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