1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fs-management/mount.h>
6
7 #include <errno.h>
8 #include <dirent.h>
9 #include <fcntl.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 #include <fbl/alloc_checker.h>
14 #include <fbl/auto_call.h>
15 #include <fbl/unique_fd.h>
16 #include <fbl/unique_ptr.h>
17 #include <fs-management/fvm.h>
18 #include <fvm/fvm.h>
19 #include <lib/fdio/limits.h>
20 #include <lib/fdio/util.h>
21 #include <lib/fdio/vfs.h>
22 #include <lib/fdio/watcher.h>
23 #include <fs/client.h>
24 #include <zircon/compiler.h>
25 #include <zircon/device/block.h>
26 #include <zircon/device/vfs.h>
27 #include <zircon/processargs.h>
28 #include <zircon/syscalls.h>
29
30 #include <utility>
31
32 namespace {
33 // Checks that |fd| is a partition which matches |uniqueGUID| and |typeGUID|.
34 // If either is null, it doesn't compare |fd| with that guid.
35 // At least one of the GUIDs must be non-null.
is_partition(int fd,const uint8_t * uniqueGUID,const uint8_t * typeGUID)36 static bool is_partition(int fd, const uint8_t* uniqueGUID, const uint8_t* typeGUID) {
37 ZX_ASSERT(uniqueGUID || typeGUID);
38 uint8_t buf[GUID_LEN];
39 if (fd < 0) {
40 return false;
41 }
42 if (typeGUID) {
43 if (ioctl_block_get_type_guid(fd, buf, sizeof(buf)) < 0) {
44 return false;
45 } else if (memcmp(buf, typeGUID, GUID_LEN) != 0) {
46 return false;
47 }
48 }
49 if (uniqueGUID) {
50 if (ioctl_block_get_partition_guid(fd, buf, sizeof(buf)) < 0) {
51 return false;
52 } else if (memcmp(buf, uniqueGUID, GUID_LEN) != 0) {
53 return false;
54 }
55 }
56 return true;
57 }
58
59 constexpr char kBlockDevPath[] = "/dev/class/block/";
60
61 } // namespace anonymous
62
fvm_init(int fd,size_t slice_size)63 zx_status_t fvm_init(int fd, size_t slice_size) {
64 if (slice_size % FVM_BLOCK_SIZE != 0) {
65 // Alignment
66 return ZX_ERR_INVALID_ARGS;
67 } else if ((slice_size * VSLICE_MAX) / VSLICE_MAX != slice_size) {
68 // Overflow
69 return ZX_ERR_INVALID_ARGS;
70 }
71
72 // The metadata layout of the FVM is dependent on the
73 // size of the FVM's underlying partition.
74 block_info_t block_info;
75 ssize_t rc = ioctl_block_get_info(fd, &block_info);
76
77 if (rc < 0) {
78 return static_cast<zx_status_t>(rc);
79 } else if (rc != sizeof(block_info)) {
80 return ZX_ERR_BAD_STATE;
81 } else if (slice_size == 0 || slice_size % block_info.block_size) {
82 return ZX_ERR_BAD_STATE;
83 }
84
85 size_t disk_size = block_info.block_count * block_info.block_size;
86 size_t metadata_size = fvm::MetadataSize(disk_size, slice_size);
87
88 fbl::unique_ptr<uint8_t[]> mvmo(new uint8_t[metadata_size * 2]);
89 // Clear entire primary copy of metadata
90 memset(mvmo.get(), 0, metadata_size);
91
92 // Superblock
93 fvm::fvm_t* sb = reinterpret_cast<fvm::fvm_t*>(mvmo.get());
94 sb->magic = FVM_MAGIC;
95 sb->version = FVM_VERSION;
96 sb->pslice_count = (disk_size - metadata_size * 2) / slice_size;
97 sb->slice_size = slice_size;
98 sb->fvm_partition_size = disk_size;
99 sb->vpartition_table_size = fvm::kVPartTableLength;
100 sb->allocation_table_size = fvm::AllocTableLength(disk_size, slice_size);
101 sb->generation = 0;
102
103 if (sb->pslice_count == 0) {
104 return ZX_ERR_NO_SPACE;
105 }
106
107 fvm_update_hash(mvmo.get(), metadata_size);
108
109 const void* backup = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(mvmo.get()) +
110 metadata_size);
111 zx_status_t status = fvm_validate_header(mvmo.get(), backup, metadata_size, nullptr);
112 if (status != ZX_OK) {
113 return status;
114 }
115
116 if (lseek(fd, 0, SEEK_SET) < 0) {
117 return ZX_ERR_BAD_STATE;
118 }
119 // Write to primary copy.
120 if (write(fd, mvmo.get(), metadata_size) != static_cast<ssize_t>(metadata_size)) {
121 return ZX_ERR_BAD_STATE;
122 }
123 // Write to secondary copy, to overwrite any previous FVM metadata copy that
124 // could be here.
125 if (write(fd, mvmo.get(), metadata_size) != static_cast<ssize_t>(metadata_size)) {
126 return ZX_ERR_BAD_STATE;
127 }
128
129 return ZX_OK;
130 }
131
132 // Helper function to overwrite FVM given the slice_size
fvm_overwrite(const char * path,size_t slice_size)133 zx_status_t fvm_overwrite(const char* path, size_t slice_size) {
134 int fd = open(path, O_RDWR);
135
136 if (fd <= 0) {
137 fprintf(stderr, "fvm_destroy: Failed to open block device\n");
138 return -1;
139 }
140
141 block_info_t block_info;
142 ssize_t rc = ioctl_block_get_info(fd, &block_info);
143
144 if (rc < 0 || rc != sizeof(block_info)) {
145 printf("fvm_destroy: Failed to query block device\n");
146 return -1;
147 }
148
149 size_t disk_size = block_info.block_count * block_info.block_size;
150 size_t metadata_size = fvm::MetadataSize(disk_size, slice_size);
151
152 fbl::AllocChecker ac;
153 fbl::unique_ptr<uint8_t[]> buf(new (&ac) uint8_t[metadata_size]);
154 if (!ac.check()) {
155 printf("fvm_destroy: Failed to allocate buffer\n");
156 return -1;
157 }
158
159 memset(buf.get(), 0, metadata_size);
160
161 if (lseek(fd, 0, SEEK_SET) < 0) {
162 return -1;
163 }
164
165 // Write to primary copy.
166 if (write(fd, buf.get(), metadata_size) != static_cast<ssize_t>(metadata_size)) {
167 fprintf(stderr, "fvm_overwrite: Failed to write metadata\n");
168 return -1;
169 }
170
171 // Write to backup copy
172 if (write(fd, buf.get(), metadata_size) != static_cast<ssize_t>(metadata_size)) {
173 fprintf(stderr, "fvm_overwrite: Failed to write metadata (secondary)\n");
174 return -1;
175 }
176
177 if (ioctl_block_rr_part(fd) != 0) {
178 return -1;
179 }
180
181 close(fd);
182 return ZX_OK;
183 }
184
185 // Helper function to destroy FVM
fvm_destroy(const char * path)186 zx_status_t fvm_destroy(const char* path) {
187 char driver_path[PATH_MAX];
188 if (strlcpy(driver_path, path, sizeof(driver_path)) >= sizeof(driver_path)) {
189 return ZX_ERR_BAD_PATH;
190 }
191 if (strlcat(driver_path, "/fvm", sizeof(driver_path)) >= sizeof(driver_path)) {
192 return ZX_ERR_BAD_PATH;
193 }
194 fbl::unique_fd driver_fd(open(driver_path, O_RDWR));
195
196 if (!driver_fd) {
197 fprintf(stderr, "fvm_destroy: Failed to open fvm driver: %s\n", driver_path);
198 return -1;
199 }
200
201 fvm_info_t fvm_info;
202 ssize_t r;
203 if ((r = ioctl_block_fvm_query(driver_fd.get(), &fvm_info)) <= 0) {
204 fprintf(stderr, "fvm_destroy: Failed to query fvm: %ld\n", r);
205 return -1;
206 }
207
208 return fvm_overwrite(path, fvm_info.slice_size);
209 }
210
211 // Helper function to allocate, find, and open VPartition.
fvm_allocate_partition(int fvm_fd,const alloc_req_t * request)212 int fvm_allocate_partition(int fvm_fd, const alloc_req_t* request) {
213 ssize_t r;
214 if ((r = ioctl_block_fvm_alloc_partition(fvm_fd, request)) != ZX_OK) {
215 return -1;
216 }
217
218 return open_partition(request->guid, request->type, ZX_SEC(10), nullptr);
219 }
220
open_partition(const uint8_t * uniqueGUID,const uint8_t * typeGUID,zx_duration_t timeout,char * out_path)221 int open_partition(const uint8_t* uniqueGUID, const uint8_t* typeGUID,
222 zx_duration_t timeout, char* out_path) {
223 ZX_ASSERT(uniqueGUID || typeGUID);
224
225 typedef struct {
226 const uint8_t* guid;
227 const uint8_t* type;
228 char* out_path;
229 fbl::unique_fd out_partition;
230 } alloc_helper_info_t;
231
232 alloc_helper_info_t info;
233 info.guid = uniqueGUID;
234 info.type = typeGUID;
235 info.out_path = out_path;
236 info.out_partition.reset();
237
238 auto cb = [](int dirfd, int event, const char* fn, void* cookie) {
239 if (event != WATCH_EVENT_ADD_FILE) {
240 return ZX_OK;
241 } else if ((strcmp(fn, ".") == 0) || strcmp(fn, "..") == 0) {
242 return ZX_OK;
243 }
244 auto info = static_cast<alloc_helper_info_t*>(cookie);
245 fbl::unique_fd devfd(openat(dirfd, fn, O_RDWR));
246 if (!devfd) {
247 return ZX_OK;
248 }
249 if (is_partition(devfd.get(), info->guid, info->type)) {
250 info->out_partition = std::move(devfd);
251 if (info->out_path) {
252 strcpy(info->out_path, kBlockDevPath);
253 strcat(info->out_path, fn);
254 }
255 return ZX_ERR_STOP;
256 }
257 return ZX_OK;
258 };
259
260 DIR* dir = opendir(kBlockDevPath);
261 if (dir == nullptr) {
262 return -1;
263 }
264
265 zx_time_t deadline = zx_deadline_after(timeout);
266 if (fdio_watch_directory(dirfd(dir), cb, deadline, &info) != ZX_ERR_STOP) {
267 return -1;
268 }
269 closedir(dir);
270 return info.out_partition.release();
271 }
272
destroy_partition(const uint8_t * uniqueGUID,const uint8_t * typeGUID)273 zx_status_t destroy_partition(const uint8_t* uniqueGUID, const uint8_t* typeGUID) {
274 char path[PATH_MAX];
275 fbl::unique_fd fd(open_partition(uniqueGUID, typeGUID, 0, path));
276
277 if (!fd) {
278 return ZX_ERR_IO;
279 }
280
281 return static_cast<zx_status_t>(ioctl_block_fvm_destroy_partition(fd.get()));
282 }
283