blob: c7e1b437457ca0772ac945fc720d23ac25f87de2 [file] [log] [blame]
San Mehata6391f12010-03-10 12:46:00 -08001/* libs/diskconfig/diskconfig.c
2 *
3 * Copyright 2008, The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#define LOG_TAG "diskconfig"
19
20#include <errno.h>
21#include <fcntl.h>
Elliott Hughesccecf142014-01-16 10:53:11 -080022#include <inttypes.h>
Mark Salyzynff2dcd92016-09-28 15:54:45 -070023#include <linux/fs.h>
San Mehata6391f12010-03-10 12:46:00 -080024#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
San Mehata6391f12010-03-10 12:46:00 -080027#include <sys/ioctl.h>
28#include <sys/stat.h>
Mark Salyzynff2dcd92016-09-28 15:54:45 -070029#include <unistd.h>
San Mehata6391f12010-03-10 12:46:00 -080030
San Mehata6391f12010-03-10 12:46:00 -080031#include <cutils/config_utils.h>
Mark Salyzyn30f991f2017-01-10 13:19:54 -080032#include <log/log.h>
San Mehata6391f12010-03-10 12:46:00 -080033
34#include <diskconfig/diskconfig.h>
35
San Mehata6391f12010-03-10 12:46:00 -080036static int
37parse_len(const char *str, uint64_t *plen)
38{
39 char tmp[64];
40 int len_str;
41 uint32_t multiple = 1;
42
43 strncpy(tmp, str, sizeof(tmp));
44 tmp[sizeof(tmp)-1] = '\0';
45 len_str = strlen(tmp);
46 if (!len_str) {
Steve Block01dda202012-01-06 14:13:42 +000047 ALOGE("Invalid disk length specified.");
San Mehata6391f12010-03-10 12:46:00 -080048 return 1;
49 }
50
51 switch(tmp[len_str - 1]) {
52 case 'M': case 'm':
53 /* megabyte */
54 multiple <<= 10;
55 case 'K': case 'k':
56 /* kilobytes */
57 multiple <<= 10;
58 tmp[len_str - 1] = '\0';
59 break;
60 default:
61 break;
62 }
63
64 *plen = strtoull(tmp, NULL, 0);
65 if (!*plen) {
Steve Block01dda202012-01-06 14:13:42 +000066 ALOGE("Invalid length specified: %s", str);
San Mehata6391f12010-03-10 12:46:00 -080067 return 1;
68 }
69
70 if (*plen == (uint64_t)-1) {
71 if (multiple > 1) {
Steve Block01dda202012-01-06 14:13:42 +000072 ALOGE("Size modifier illegal when len is -1");
San Mehata6391f12010-03-10 12:46:00 -080073 return 1;
74 }
75 } else {
76 /* convert len to kilobytes */
77 if (multiple > 1024)
78 multiple >>= 10;
79 *plen *= multiple;
80
81 if (*plen > 0xffffffffULL) {
Elliott Hughesccecf142014-01-16 10:53:11 -080082 ALOGE("Length specified is too large!: %"PRIu64" KB", *plen);
San Mehata6391f12010-03-10 12:46:00 -080083 return 1;
84 }
85 }
86
87 return 0;
88}
89
90
91static int
92load_partitions(cnode *root, struct disk_info *dinfo)
93{
94 cnode *partnode;
95
96 dinfo->num_parts = 0;
97 for (partnode = root->first_child; partnode; partnode = partnode->next) {
98 struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts];
99 const char *tmp;
100
101 /* bleh, i will leak memory here, but i DONT CARE since
102 * the only right thing to do when this function fails
103 * is to quit */
104 pinfo->name = strdup(partnode->name);
105
106 if(config_bool(partnode, "active", 0))
107 pinfo->flags |= PART_ACTIVE_FLAG;
108
109 if (!(tmp = config_str(partnode, "type", NULL))) {
Steve Block01dda202012-01-06 14:13:42 +0000110 ALOGE("Partition type required: %s", pinfo->name);
San Mehata6391f12010-03-10 12:46:00 -0800111 return 1;
112 }
113
114 /* possible values are: linux, fat32 */
115 if (!strcmp(tmp, "linux")) {
116 pinfo->type = PC_PART_TYPE_LINUX;
117 } else if (!strcmp(tmp, "fat32")) {
118 pinfo->type = PC_PART_TYPE_FAT32;
119 } else {
Steve Block01dda202012-01-06 14:13:42 +0000120 ALOGE("Unsupported partition type found: %s", tmp);
San Mehata6391f12010-03-10 12:46:00 -0800121 return 1;
122 }
123
124 if ((tmp = config_str(partnode, "len", NULL)) != NULL) {
125 uint64_t len;
126 if (parse_len(tmp, &len))
127 return 1;
128 pinfo->len_kb = (uint32_t) len;
129 } else
130 pinfo->len_kb = 0;
131
132 ++dinfo->num_parts;
133 }
134
135 return 0;
136}
137
138struct disk_info *
139load_diskconfig(const char *fn, char *path_override)
140{
141 struct disk_info *dinfo;
142 cnode *devroot;
143 cnode *partnode;
144 cnode *root = config_node("", "");
145 const char *tmp;
146
147 if (!(dinfo = malloc(sizeof(struct disk_info)))) {
Steve Block01dda202012-01-06 14:13:42 +0000148 ALOGE("Could not malloc disk_info");
San Mehata6391f12010-03-10 12:46:00 -0800149 return NULL;
150 }
151 memset(dinfo, 0, sizeof(struct disk_info));
152
153 if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
Steve Block01dda202012-01-06 14:13:42 +0000154 ALOGE("Could not malloc part_lst");
San Mehata6391f12010-03-10 12:46:00 -0800155 goto fail;
156 }
157 memset(dinfo->part_lst, 0,
158 (MAX_NUM_PARTS * sizeof(struct part_info)));
159
160 config_load_file(root, fn);
161 if (root->first_child == NULL) {
Steve Block01dda202012-01-06 14:13:42 +0000162 ALOGE("Could not read config file %s", fn);
San Mehata6391f12010-03-10 12:46:00 -0800163 goto fail;
164 }
165
166 if (!(devroot = config_find(root, "device"))) {
Steve Block01dda202012-01-06 14:13:42 +0000167 ALOGE("Could not find device section in config file '%s'", fn);
San Mehata6391f12010-03-10 12:46:00 -0800168 goto fail;
169 }
170
171
172 if (!(tmp = config_str(devroot, "path", path_override))) {
Steve Block01dda202012-01-06 14:13:42 +0000173 ALOGE("device path is requried");
San Mehata6391f12010-03-10 12:46:00 -0800174 goto fail;
175 }
176 dinfo->device = strdup(tmp);
177
178 /* find the partition scheme */
179 if (!(tmp = config_str(devroot, "scheme", NULL))) {
Steve Block01dda202012-01-06 14:13:42 +0000180 ALOGE("partition scheme is required");
San Mehata6391f12010-03-10 12:46:00 -0800181 goto fail;
182 } else if (!strcmp(tmp, "mbr")) {
183 dinfo->scheme = PART_SCHEME_MBR;
184 } else if (!strcmp(tmp, "gpt")) {
Steve Block01dda202012-01-06 14:13:42 +0000185 ALOGE("'gpt' partition scheme not supported yet.");
San Mehata6391f12010-03-10 12:46:00 -0800186 goto fail;
187 } else {
Steve Block01dda202012-01-06 14:13:42 +0000188 ALOGE("Unknown partition scheme specified: %s", tmp);
San Mehata6391f12010-03-10 12:46:00 -0800189 goto fail;
190 }
191
192 /* grab the sector size (in bytes) */
193 tmp = config_str(devroot, "sector_size", "512");
194 dinfo->sect_size = strtol(tmp, NULL, 0);
195 if (!dinfo->sect_size) {
Steve Block01dda202012-01-06 14:13:42 +0000196 ALOGE("Invalid sector size: %s", tmp);
San Mehata6391f12010-03-10 12:46:00 -0800197 goto fail;
198 }
199
200 /* first lba where the partitions will start on disk */
201 if (!(tmp = config_str(devroot, "start_lba", NULL))) {
Steve Block01dda202012-01-06 14:13:42 +0000202 ALOGE("start_lba must be provided");
San Mehata6391f12010-03-10 12:46:00 -0800203 goto fail;
204 }
205
206 if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
Steve Block01dda202012-01-06 14:13:42 +0000207 ALOGE("Invalid starting LBA (or zero): %s", tmp);
San Mehata6391f12010-03-10 12:46:00 -0800208 goto fail;
209 }
210
211 /* Number of LBAs on disk */
212 if (!(tmp = config_str(devroot, "num_lba", NULL))) {
Steve Block01dda202012-01-06 14:13:42 +0000213 ALOGE("num_lba is required");
San Mehata6391f12010-03-10 12:46:00 -0800214 goto fail;
215 }
216 dinfo->num_lba = strtoul(tmp, NULL, 0);
217
218 if (!(partnode = config_find(devroot, "partitions"))) {
Steve Block01dda202012-01-06 14:13:42 +0000219 ALOGE("Device must specify partition list");
San Mehata6391f12010-03-10 12:46:00 -0800220 goto fail;
221 }
222
223 if (load_partitions(partnode, dinfo))
224 goto fail;
225
226 return dinfo;
227
228fail:
229 if (dinfo->part_lst)
230 free(dinfo->part_lst);
231 if (dinfo->device)
232 free(dinfo->device);
233 free(dinfo);
234 return NULL;
235}
236
237static int
238sync_ptable(int fd)
239{
240 struct stat stat;
241 int rv;
242
243 sync();
244
245 if (fstat(fd, &stat)) {
Steve Block01dda202012-01-06 14:13:42 +0000246 ALOGE("Cannot stat, errno=%d.", errno);
San Mehata6391f12010-03-10 12:46:00 -0800247 return -1;
248 }
249
250 if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
Steve Block01dda202012-01-06 14:13:42 +0000251 ALOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno);
San Mehata6391f12010-03-10 12:46:00 -0800252 return -1;
253 }
254
255 return 0;
256}
257
258/* This function verifies that the disk info provided is valid, and if so,
259 * returns an open file descriptor.
260 *
261 * This does not necessarily mean that it will later be successfully written
262 * though. If we use the pc-bios partitioning scheme, we must use extended
263 * partitions, which eat up some hd space. If the user manually provisioned
264 * every single partition, but did not account for the extra needed space,
265 * then we will later fail.
266 *
267 * TODO: Make validation more complete.
268 */
269static int
270validate(struct disk_info *dinfo)
271{
272 int fd;
273 int sect_sz;
274 uint64_t disk_size;
275 uint64_t total_size;
276 int cnt;
277 struct stat stat;
278
279 if (!dinfo)
280 return -1;
281
282 if ((fd = open(dinfo->device, O_RDWR)) < 0) {
Steve Block01dda202012-01-06 14:13:42 +0000283 ALOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
San Mehata6391f12010-03-10 12:46:00 -0800284 return -1;
285 }
286
287 if (fstat(fd, &stat)) {
Steve Block01dda202012-01-06 14:13:42 +0000288 ALOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno);
San Mehata6391f12010-03-10 12:46:00 -0800289 goto fail;
290 }
291
292
293 /* XXX: Some of the code below is kind of redundant and should probably
294 * be refactored a little, but it will do for now. */
295
296 /* Verify that we can operate on the device that was requested.
297 * We presently only support block devices and regular file images. */
298 if (S_ISBLK(stat.st_mode)) {
299 /* get the sector size and make sure we agree */
300 if (ioctl(fd, BLKSSZGET, &sect_sz) < 0) {
Steve Block01dda202012-01-06 14:13:42 +0000301 ALOGE("Cannot get sector size (errno=%d)", errno);
San Mehata6391f12010-03-10 12:46:00 -0800302 goto fail;
303 }
304
305 if (!sect_sz || sect_sz != dinfo->sect_size) {
Steve Block01dda202012-01-06 14:13:42 +0000306 ALOGE("Device sector size is zero or sector sizes do not match!");
San Mehata6391f12010-03-10 12:46:00 -0800307 goto fail;
308 }
309
310 /* allow the user override the "disk size" if they provided num_lba */
311 if (!dinfo->num_lba) {
312 if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) {
Steve Block01dda202012-01-06 14:13:42 +0000313 ALOGE("Could not get block device size (errno=%d)", errno);
San Mehata6391f12010-03-10 12:46:00 -0800314 goto fail;
315 }
316 /* XXX: we assume that the disk has < 2^32 sectors :-) */
317 dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size);
318 } else
319 disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
320 } else if (S_ISREG(stat.st_mode)) {
Steve Blockfe71a612012-01-04 19:19:03 +0000321 ALOGI("Requesting operation on a regular file, not block device.");
San Mehata6391f12010-03-10 12:46:00 -0800322 if (!dinfo->sect_size) {
Steve Block01dda202012-01-06 14:13:42 +0000323 ALOGE("Sector size for regular file images cannot be zero");
San Mehata6391f12010-03-10 12:46:00 -0800324 goto fail;
325 }
326 if (dinfo->num_lba)
327 disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
328 else {
329 dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size);
330 disk_size = (uint64_t)stat.st_size;
331 }
332 } else {
Steve Block01dda202012-01-06 14:13:42 +0000333 ALOGE("Device does not refer to a regular file or a block device!");
San Mehata6391f12010-03-10 12:46:00 -0800334 goto fail;
335 }
336
337#if 1
Mark Salyzyn1874e652014-03-13 16:38:43 -0700338 ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d",
San Mehata6391f12010-03-10 12:46:00 -0800339 dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size);
340#endif
341
342 /* since this is our offset into the disk, we start off with that as
343 * our size of needed partitions */
344 total_size = dinfo->skip_lba * dinfo->sect_size;
345
346 /* add up all the partition sizes and make sure it fits */
347 for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
348 struct part_info *part = &dinfo->part_lst[cnt];
349 if (part->len_kb != (uint32_t)-1) {
350 total_size += part->len_kb * 1024;
351 } else if (part->len_kb == 0) {
Steve Block01dda202012-01-06 14:13:42 +0000352 ALOGE("Zero-size partition '%s' is invalid.", part->name);
San Mehata6391f12010-03-10 12:46:00 -0800353 goto fail;
354 } else {
355 /* the partition requests the rest of the disk. */
356 if (cnt + 1 != dinfo->num_parts) {
Steve Block01dda202012-01-06 14:13:42 +0000357 ALOGE("Only the last partition in the list can request to fill "
San Mehata6391f12010-03-10 12:46:00 -0800358 "the rest of disk.");
359 goto fail;
360 }
361 }
362
363 if ((part->type != PC_PART_TYPE_LINUX) &&
364 (part->type != PC_PART_TYPE_FAT32)) {
Steve Block01dda202012-01-06 14:13:42 +0000365 ALOGE("Unknown partition type (0x%x) encountered for partition "
San Mehata6391f12010-03-10 12:46:00 -0800366 "'%s'\n", part->type, part->name);
367 goto fail;
368 }
369 }
370
371 /* only matters for disks, not files */
372 if (S_ISBLK(stat.st_mode) && total_size > disk_size) {
Elliott Hughesccecf142014-01-16 10:53:11 -0800373 ALOGE("Total requested size of partitions (%"PRIu64") is greater than disk "
374 "size (%"PRIu64").", total_size, disk_size);
San Mehata6391f12010-03-10 12:46:00 -0800375 goto fail;
376 }
377
378 return fd;
379
380fail:
381 close(fd);
382 return -1;
383}
384
385static int
386validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst)
387{
388 *lst = NULL;
389 *fd = -1;
390
391 if ((*fd = validate(dinfo)) < 0)
392 return 1;
393
394 switch (dinfo->scheme) {
395 case PART_SCHEME_MBR:
396 *lst = config_mbr(dinfo);
397 return *lst == NULL;
398 case PART_SCHEME_GPT:
399 /* not supported yet */
400 default:
Steve Block01dda202012-01-06 14:13:42 +0000401 ALOGE("Uknown partition scheme.");
San Mehata6391f12010-03-10 12:46:00 -0800402 break;
403 }
404
405 close(*fd);
406 *lst = NULL;
407 return 1;
408}
409
410/* validate and process the disk layout configuration.
411 * This will cause an update to the partitions' start lba.
412 *
413 * Basically, this does the same thing as apply_disk_config in test mode,
414 * except that wlist_commit is not called to print out the data to be
415 * written.
416 */
417int
418process_disk_config(struct disk_info *dinfo)
419{
420 struct write_list *lst;
421 int fd;
422
423 if (validate_and_config(dinfo, &fd, &lst) != 0)
424 return 1;
425
426 close(fd);
427 wlist_free(lst);
428 return 0;
429}
430
431
432int
433apply_disk_config(struct disk_info *dinfo, int test)
434{
435 int fd;
436 struct write_list *wr_lst = NULL;
437 int rv;
438
439 if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {
Steve Block01dda202012-01-06 14:13:42 +0000440 ALOGE("Configuration is invalid.");
San Mehata6391f12010-03-10 12:46:00 -0800441 goto fail;
442 }
443
444 if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)
445 rv = test ? 0 : sync_ptable(fd);
446
447 close(fd);
448 wlist_free(wr_lst);
449 return rv;
450
451fail:
452 close(fd);
453 if (wr_lst)
454 wlist_free(wr_lst);
455 return 1;
456}
457
458int
459dump_disk_config(struct disk_info *dinfo)
460{
461 int cnt;
462 struct part_info *part;
463
464 printf("Device: %s\n", dinfo->device);
465 printf("Scheme: ");
466 switch (dinfo->scheme) {
467 case PART_SCHEME_MBR:
468 printf("MBR");
469 break;
470 case PART_SCHEME_GPT:
471 printf("GPT (unsupported)");
472 break;
473 default:
474 printf("Unknown");
475 break;
476 }
477 printf ("\n");
478
479 printf("Sector size: %d\n", dinfo->sect_size);
480 printf("Skip leading LBAs: %u\n", dinfo->skip_lba);
481 printf("Number of LBAs: %u\n", dinfo->num_lba);
482 printf("Partitions:\n");
483
484 for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
485 part = &dinfo->part_lst[cnt];
486 printf("\tname = %s\n", part->name);
487 printf("\t\tflags = %s\n",
488 part->flags & PART_ACTIVE_FLAG ? "Active" : "None");
489 printf("\t\ttype = %s\n",
490 part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown");
491 if (part->len_kb == (uint32_t)-1)
492 printf("\t\tlen = rest of disk\n");
493 else
494 printf("\t\tlen = %uKB\n", part->len_kb);
495 }
496 printf("Total number of partitions: %d\n", cnt);
497 printf("\n");
498
499 return 0;
500}
501
502struct part_info *
503find_part(struct disk_info *dinfo, const char *name)
504{
505 struct part_info *pinfo;
506 int cnt;
507
508 for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
509 pinfo = &dinfo->part_lst[cnt];
510 if (!strcmp(pinfo->name, name))
511 return pinfo;
512 }
513
514 return NULL;
515}
516
517/* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */
518char *
519find_part_device(struct disk_info *dinfo, const char *name)
520{
521 switch (dinfo->scheme) {
522 case PART_SCHEME_MBR:
523 return find_mbr_part(dinfo, name);
524 case PART_SCHEME_GPT:
Steve Block01dda202012-01-06 14:13:42 +0000525 ALOGE("GPT is presently not supported");
San Mehata6391f12010-03-10 12:46:00 -0800526 break;
527 default:
Steve Block01dda202012-01-06 14:13:42 +0000528 ALOGE("Unknown partition table scheme");
San Mehata6391f12010-03-10 12:46:00 -0800529 break;
530 }
531
532 return NULL;
533}
534
535