• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 
29 #include <linux/fs.h>
30 
31 #include <cutils/config_utils.h>
32 #include <cutils/log.h>
33 
34 #include <diskconfig/diskconfig.h>
35 
36 
37 static int
parse_len(const char * str,uint64_t * plen)38 parse_len(const char *str, uint64_t *plen)
39 {
40     char tmp[64];
41     int len_str;
42     uint32_t multiple = 1;
43 
44     strncpy(tmp, str, sizeof(tmp));
45     tmp[sizeof(tmp)-1] = '\0';
46     len_str = strlen(tmp);
47     if (!len_str) {
48         ALOGE("Invalid disk length specified.");
49         return 1;
50     }
51 
52     switch(tmp[len_str - 1]) {
53         case 'M': case 'm':
54             /* megabyte */
55             multiple <<= 10;
56         case 'K': case 'k':
57             /* kilobytes */
58             multiple <<= 10;
59             tmp[len_str - 1] = '\0';
60             break;
61         default:
62             break;
63     }
64 
65     *plen = strtoull(tmp, NULL, 0);
66     if (!*plen) {
67         ALOGE("Invalid length specified: %s", str);
68         return 1;
69     }
70 
71     if (*plen == (uint64_t)-1) {
72         if (multiple > 1) {
73             ALOGE("Size modifier illegal when len is -1");
74             return 1;
75         }
76     } else {
77         /* convert len to kilobytes */
78         if (multiple > 1024)
79             multiple >>= 10;
80         *plen *= multiple;
81 
82         if (*plen > 0xffffffffULL) {
83             ALOGE("Length specified is too large!: %llu KB", *plen);
84             return 1;
85         }
86     }
87 
88     return 0;
89 }
90 
91 
92 static int
load_partitions(cnode * root,struct disk_info * dinfo)93 load_partitions(cnode *root, struct disk_info *dinfo)
94 {
95     cnode *partnode;
96 
97     dinfo->num_parts = 0;
98     for (partnode = root->first_child; partnode; partnode = partnode->next) {
99         struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts];
100         const char *tmp;
101 
102         /* bleh, i will leak memory here, but i DONT CARE since
103          * the only right thing to do when this function fails
104          * is to quit */
105         pinfo->name = strdup(partnode->name);
106 
107         if(config_bool(partnode, "active", 0))
108             pinfo->flags |= PART_ACTIVE_FLAG;
109 
110         if (!(tmp = config_str(partnode, "type", NULL))) {
111             ALOGE("Partition type required: %s", pinfo->name);
112             return 1;
113         }
114 
115         /* possible values are: linux, fat32 */
116         if (!strcmp(tmp, "linux")) {
117             pinfo->type = PC_PART_TYPE_LINUX;
118         } else if (!strcmp(tmp, "fat32")) {
119             pinfo->type = PC_PART_TYPE_FAT32;
120         } else {
121             ALOGE("Unsupported partition type found: %s", tmp);
122             return 1;
123         }
124 
125         if ((tmp = config_str(partnode, "len", NULL)) != NULL) {
126             uint64_t len;
127             if (parse_len(tmp, &len))
128                 return 1;
129             pinfo->len_kb = (uint32_t) len;
130         } else
131             pinfo->len_kb = 0;
132 
133         ++dinfo->num_parts;
134     }
135 
136     return 0;
137 }
138 
139 struct disk_info *
load_diskconfig(const char * fn,char * path_override)140 load_diskconfig(const char *fn, char *path_override)
141 {
142     struct disk_info *dinfo;
143     cnode *devroot;
144     cnode *partnode;
145     cnode *root = config_node("", "");
146     const char *tmp;
147 
148     if (!(dinfo = malloc(sizeof(struct disk_info)))) {
149         ALOGE("Could not malloc disk_info");
150         return NULL;
151     }
152     memset(dinfo, 0, sizeof(struct disk_info));
153 
154     if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
155         ALOGE("Could not malloc part_lst");
156         goto fail;
157     }
158     memset(dinfo->part_lst, 0,
159            (MAX_NUM_PARTS * sizeof(struct part_info)));
160 
161     config_load_file(root, fn);
162     if (root->first_child == NULL) {
163         ALOGE("Could not read config file %s", fn);
164         goto fail;
165     }
166 
167     if (!(devroot = config_find(root, "device"))) {
168         ALOGE("Could not find device section in config file '%s'", fn);
169         goto fail;
170     }
171 
172 
173     if (!(tmp = config_str(devroot, "path", path_override))) {
174         ALOGE("device path is requried");
175         goto fail;
176     }
177     dinfo->device = strdup(tmp);
178 
179     /* find the partition scheme */
180     if (!(tmp = config_str(devroot, "scheme", NULL))) {
181         ALOGE("partition scheme is required");
182         goto fail;
183     } else if (!strcmp(tmp, "mbr")) {
184         dinfo->scheme = PART_SCHEME_MBR;
185     } else if (!strcmp(tmp, "gpt")) {
186         ALOGE("'gpt' partition scheme not supported yet.");
187         goto fail;
188     } else {
189         ALOGE("Unknown partition scheme specified: %s", tmp);
190         goto fail;
191     }
192 
193     /* grab the sector size (in bytes) */
194     tmp = config_str(devroot, "sector_size", "512");
195     dinfo->sect_size = strtol(tmp, NULL, 0);
196     if (!dinfo->sect_size) {
197         ALOGE("Invalid sector size: %s", tmp);
198         goto fail;
199     }
200 
201     /* first lba where the partitions will start on disk */
202     if (!(tmp = config_str(devroot, "start_lba", NULL))) {
203         ALOGE("start_lba must be provided");
204         goto fail;
205     }
206 
207     if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
208         ALOGE("Invalid starting LBA (or zero): %s", tmp);
209         goto fail;
210     }
211 
212     /* Number of LBAs on disk */
213     if (!(tmp = config_str(devroot, "num_lba", NULL))) {
214         ALOGE("num_lba is required");
215         goto fail;
216     }
217     dinfo->num_lba = strtoul(tmp, NULL, 0);
218 
219     if (!(partnode = config_find(devroot, "partitions"))) {
220         ALOGE("Device must specify partition list");
221         goto fail;
222     }
223 
224     if (load_partitions(partnode, dinfo))
225         goto fail;
226 
227     return dinfo;
228 
229 fail:
230     if (dinfo->part_lst)
231         free(dinfo->part_lst);
232     if (dinfo->device)
233         free(dinfo->device);
234     free(dinfo);
235     return NULL;
236 }
237 
238 static int
sync_ptable(int fd)239 sync_ptable(int fd)
240 {
241     struct stat stat;
242     int rv;
243 
244     sync();
245 
246     if (fstat(fd, &stat)) {
247        ALOGE("Cannot stat, errno=%d.", errno);
248        return -1;
249     }
250 
251     if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
252         ALOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno);
253         return -1;
254     }
255 
256     return 0;
257 }
258 
259 /* This function verifies that the disk info provided is valid, and if so,
260  * returns an open file descriptor.
261  *
262  * This does not necessarily mean that it will later be successfully written
263  * though. If we use the pc-bios partitioning scheme, we must use extended
264  * partitions, which eat up some hd space. If the user manually provisioned
265  * every single partition, but did not account for the extra needed space,
266  * then we will later fail.
267  *
268  * TODO: Make validation more complete.
269  */
270 static int
validate(struct disk_info * dinfo)271 validate(struct disk_info *dinfo)
272 {
273     int fd;
274     int sect_sz;
275     uint64_t disk_size;
276     uint64_t total_size;
277     int cnt;
278     struct stat stat;
279 
280     if (!dinfo)
281         return -1;
282 
283     if ((fd = open(dinfo->device, O_RDWR)) < 0) {
284         ALOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
285         return -1;
286     }
287 
288     if (fstat(fd, &stat)) {
289         ALOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno);
290         goto fail;
291     }
292 
293 
294     /* XXX: Some of the code below is kind of redundant and should probably
295      * be refactored a little, but it will do for now. */
296 
297     /* Verify that we can operate on the device that was requested.
298      * We presently only support block devices and regular file images. */
299     if (S_ISBLK(stat.st_mode)) {
300         /* get the sector size and make sure we agree */
301         if (ioctl(fd, BLKSSZGET, &sect_sz) < 0) {
302             ALOGE("Cannot get sector size (errno=%d)", errno);
303             goto fail;
304         }
305 
306         if (!sect_sz || sect_sz != dinfo->sect_size) {
307             ALOGE("Device sector size is zero or sector sizes do not match!");
308             goto fail;
309         }
310 
311         /* allow the user override the "disk size" if they provided num_lba */
312         if (!dinfo->num_lba) {
313             if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) {
314                 ALOGE("Could not get block device size (errno=%d)", errno);
315                 goto fail;
316             }
317             /* XXX: we assume that the disk has < 2^32 sectors :-) */
318             dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size);
319         } else
320             disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
321     } else if (S_ISREG(stat.st_mode)) {
322         ALOGI("Requesting operation on a regular file, not block device.");
323         if (!dinfo->sect_size) {
324             ALOGE("Sector size for regular file images cannot be zero");
325             goto fail;
326         }
327         if (dinfo->num_lba)
328             disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
329         else {
330             dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size);
331             disk_size = (uint64_t)stat.st_size;
332         }
333     } else {
334         ALOGE("Device does not refer to a regular file or a block device!");
335         goto fail;
336     }
337 
338 #if 1
339     ALOGV("Device/file %s: size=%llu bytes, num_lba=%u, sect_size=%d",
340          dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size);
341 #endif
342 
343     /* since this is our offset into the disk, we start off with that as
344      * our size of needed partitions */
345     total_size = dinfo->skip_lba * dinfo->sect_size;
346 
347     /* add up all the partition sizes and make sure it fits */
348     for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
349         struct part_info *part = &dinfo->part_lst[cnt];
350         if (part->len_kb != (uint32_t)-1) {
351             total_size += part->len_kb * 1024;
352         } else if (part->len_kb == 0) {
353             ALOGE("Zero-size partition '%s' is invalid.", part->name);
354             goto fail;
355         } else {
356             /* the partition requests the rest of the disk. */
357             if (cnt + 1 != dinfo->num_parts) {
358                 ALOGE("Only the last partition in the list can request to fill "
359                      "the rest of disk.");
360                 goto fail;
361             }
362         }
363 
364         if ((part->type != PC_PART_TYPE_LINUX) &&
365             (part->type != PC_PART_TYPE_FAT32)) {
366             ALOGE("Unknown partition type (0x%x) encountered for partition "
367                  "'%s'\n", part->type, part->name);
368             goto fail;
369         }
370     }
371 
372     /* only matters for disks, not files */
373     if (S_ISBLK(stat.st_mode) && total_size > disk_size) {
374         ALOGE("Total requested size of partitions (%llu) is greater than disk "
375              "size (%llu).", total_size, disk_size);
376         goto fail;
377     }
378 
379     return fd;
380 
381 fail:
382     close(fd);
383     return -1;
384 }
385 
386 static int
validate_and_config(struct disk_info * dinfo,int * fd,struct write_list ** lst)387 validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst)
388 {
389     *lst = NULL;
390     *fd = -1;
391 
392     if ((*fd = validate(dinfo)) < 0)
393         return 1;
394 
395     switch (dinfo->scheme) {
396         case PART_SCHEME_MBR:
397             *lst = config_mbr(dinfo);
398             return *lst == NULL;
399         case PART_SCHEME_GPT:
400             /* not supported yet */
401         default:
402             ALOGE("Uknown partition scheme.");
403             break;
404     }
405 
406     close(*fd);
407     *lst = NULL;
408     return 1;
409 }
410 
411 /* validate and process the disk layout configuration.
412  * This will cause an update to the partitions' start lba.
413  *
414  * Basically, this does the same thing as apply_disk_config in test mode,
415  * except that wlist_commit is not called to print out the data to be
416  * written.
417  */
418 int
process_disk_config(struct disk_info * dinfo)419 process_disk_config(struct disk_info *dinfo)
420 {
421     struct write_list *lst;
422     int fd;
423 
424     if (validate_and_config(dinfo, &fd, &lst) != 0)
425         return 1;
426 
427     close(fd);
428     wlist_free(lst);
429     return 0;
430 }
431 
432 
433 int
apply_disk_config(struct disk_info * dinfo,int test)434 apply_disk_config(struct disk_info *dinfo, int test)
435 {
436     int fd;
437     struct write_list *wr_lst = NULL;
438     int rv;
439 
440     if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {
441         ALOGE("Configuration is invalid.");
442         goto fail;
443     }
444 
445     if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)
446         rv = test ? 0 : sync_ptable(fd);
447 
448     close(fd);
449     wlist_free(wr_lst);
450     return rv;
451 
452 fail:
453     close(fd);
454     if (wr_lst)
455         wlist_free(wr_lst);
456     return 1;
457 }
458 
459 int
dump_disk_config(struct disk_info * dinfo)460 dump_disk_config(struct disk_info *dinfo)
461 {
462     int cnt;
463     struct part_info *part;
464 
465     printf("Device: %s\n", dinfo->device);
466     printf("Scheme: ");
467     switch (dinfo->scheme) {
468         case PART_SCHEME_MBR:
469             printf("MBR");
470             break;
471         case PART_SCHEME_GPT:
472             printf("GPT (unsupported)");
473             break;
474         default:
475             printf("Unknown");
476             break;
477     }
478     printf ("\n");
479 
480     printf("Sector size: %d\n", dinfo->sect_size);
481     printf("Skip leading LBAs: %u\n", dinfo->skip_lba);
482     printf("Number of LBAs: %u\n", dinfo->num_lba);
483     printf("Partitions:\n");
484 
485     for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
486         part = &dinfo->part_lst[cnt];
487         printf("\tname = %s\n", part->name);
488         printf("\t\tflags = %s\n",
489                part->flags & PART_ACTIVE_FLAG ? "Active" : "None");
490         printf("\t\ttype = %s\n",
491                part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown");
492         if (part->len_kb == (uint32_t)-1)
493             printf("\t\tlen = rest of disk\n");
494         else
495             printf("\t\tlen = %uKB\n", part->len_kb);
496     }
497     printf("Total number of partitions: %d\n", cnt);
498     printf("\n");
499 
500     return 0;
501 }
502 
503 struct part_info *
find_part(struct disk_info * dinfo,const char * name)504 find_part(struct disk_info *dinfo, const char *name)
505 {
506     struct part_info *pinfo;
507     int cnt;
508 
509     for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
510         pinfo = &dinfo->part_lst[cnt];
511         if (!strcmp(pinfo->name, name))
512             return pinfo;
513     }
514 
515     return NULL;
516 }
517 
518 /* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */
519 char *
find_part_device(struct disk_info * dinfo,const char * name)520 find_part_device(struct disk_info *dinfo, const char *name)
521 {
522     switch (dinfo->scheme) {
523         case PART_SCHEME_MBR:
524             return find_mbr_part(dinfo, name);
525         case PART_SCHEME_GPT:
526             ALOGE("GPT is presently not supported");
527             break;
528         default:
529             ALOGE("Unknown partition table scheme");
530             break;
531     }
532 
533     return NULL;
534 }
535 
536 
537