• 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 <inttypes.h>
23  #include <linux/fs.h>
24  #include <stdio.h>
25  #include <stdlib.h>
26  #include <string.h>
27  #include <sys/ioctl.h>
28  #include <sys/stat.h>
29  #include <unistd.h>
30  
31  #include <cutils/config_utils.h>
32  #include <log/log.h>
33  
34  #include <diskconfig/diskconfig.h>
35  
36  static int
parse_len(const char * str,uint64_t * plen)37  parse_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) {
47          ALOGE("Invalid disk length specified.");
48          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) {
66          ALOGE("Invalid length specified: %s", str);
67          return 1;
68      }
69  
70      if (*plen == (uint64_t)-1) {
71          if (multiple > 1) {
72              ALOGE("Size modifier illegal when len is -1");
73              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) {
82              ALOGE("Length specified is too large!: %"PRIu64" KB", *plen);
83              return 1;
84          }
85      }
86  
87      return 0;
88  }
89  
90  
91  static int
load_partitions(cnode * root,struct disk_info * dinfo)92  load_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))) {
110              ALOGE("Partition type required: %s", pinfo->name);
111              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 {
120              ALOGE("Unsupported partition type found: %s", tmp);
121              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  
138  struct disk_info *
load_diskconfig(const char * fn,char * path_override)139  load_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)))) {
148          ALOGE("Could not malloc disk_info");
149          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)))) {
154          ALOGE("Could not malloc part_lst");
155          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) {
162          ALOGE("Could not read config file %s", fn);
163          goto fail;
164      }
165  
166      if (!(devroot = config_find(root, "device"))) {
167          ALOGE("Could not find device section in config file '%s'", fn);
168          goto fail;
169      }
170  
171  
172      if (!(tmp = config_str(devroot, "path", path_override))) {
173          ALOGE("device path is requried");
174          goto fail;
175      }
176      dinfo->device = strdup(tmp);
177  
178      /* find the partition scheme */
179      if (!(tmp = config_str(devroot, "scheme", NULL))) {
180          ALOGE("partition scheme is required");
181          goto fail;
182      } else if (!strcmp(tmp, "mbr")) {
183          dinfo->scheme = PART_SCHEME_MBR;
184      } else if (!strcmp(tmp, "gpt")) {
185          ALOGE("'gpt' partition scheme not supported yet.");
186          goto fail;
187      } else {
188          ALOGE("Unknown partition scheme specified: %s", tmp);
189          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) {
196          ALOGE("Invalid sector size: %s", tmp);
197          goto fail;
198      }
199  
200      /* first lba where the partitions will start on disk */
201      if (!(tmp = config_str(devroot, "start_lba", NULL))) {
202          ALOGE("start_lba must be provided");
203          goto fail;
204      }
205  
206      if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
207          ALOGE("Invalid starting LBA (or zero): %s", tmp);
208          goto fail;
209      }
210  
211      /* Number of LBAs on disk */
212      if (!(tmp = config_str(devroot, "num_lba", NULL))) {
213          ALOGE("num_lba is required");
214          goto fail;
215      }
216      dinfo->num_lba = strtoul(tmp, NULL, 0);
217  
218      if (!(partnode = config_find(devroot, "partitions"))) {
219          ALOGE("Device must specify partition list");
220          goto fail;
221      }
222  
223      if (load_partitions(partnode, dinfo))
224          goto fail;
225  
226      return dinfo;
227  
228  fail:
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  
237  static int
sync_ptable(int fd)238  sync_ptable(int fd)
239  {
240      struct stat stat;
241      int rv;
242  
243      sync();
244  
245      if (fstat(fd, &stat)) {
246         ALOGE("Cannot stat, errno=%d.", errno);
247         return -1;
248      }
249  
250      if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
251          ALOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno);
252          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   */
269  static int
validate(struct disk_info * dinfo)270  validate(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) {
283          ALOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
284          return -1;
285      }
286  
287      if (fstat(fd, &stat)) {
288          ALOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno);
289          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) {
301              ALOGE("Cannot get sector size (errno=%d)", errno);
302              goto fail;
303          }
304  
305          if (!sect_sz || sect_sz != dinfo->sect_size) {
306              ALOGE("Device sector size is zero or sector sizes do not match!");
307              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) {
313                  ALOGE("Could not get block device size (errno=%d)", errno);
314                  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)) {
321          ALOGI("Requesting operation on a regular file, not block device.");
322          if (!dinfo->sect_size) {
323              ALOGE("Sector size for regular file images cannot be zero");
324              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 {
333          ALOGE("Device does not refer to a regular file or a block device!");
334          goto fail;
335      }
336  
337  #if 1
338      ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d",
339           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) {
352              ALOGE("Zero-size partition '%s' is invalid.", part->name);
353              goto fail;
354          } else {
355              /* the partition requests the rest of the disk. */
356              if (cnt + 1 != dinfo->num_parts) {
357                  ALOGE("Only the last partition in the list can request to fill "
358                       "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)) {
365              ALOGE("Unknown partition type (0x%x) encountered for partition "
366                   "'%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) {
373          ALOGE("Total requested size of partitions (%"PRIu64") is greater than disk "
374               "size (%"PRIu64").", total_size, disk_size);
375          goto fail;
376      }
377  
378      return fd;
379  
380  fail:
381      close(fd);
382      return -1;
383  }
384  
385  static int
validate_and_config(struct disk_info * dinfo,int * fd,struct write_list ** lst)386  validate_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:
401              ALOGE("Uknown partition scheme.");
402              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   */
417  int
process_disk_config(struct disk_info * dinfo)418  process_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  
432  int
apply_disk_config(struct disk_info * dinfo,int test)433  apply_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) {
440          ALOGE("Configuration is invalid.");
441          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  
451  fail:
452      close(fd);
453      if (wr_lst)
454          wlist_free(wr_lst);
455      return 1;
456  }
457  
458  int
dump_disk_config(struct disk_info * dinfo)459  dump_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  
502  struct part_info *
find_part(struct disk_info * dinfo,const char * name)503  find_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. */
518  char *
find_part_device(struct disk_info * dinfo,const char * name)519  find_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:
525              ALOGE("GPT is presently not supported");
526              break;
527          default:
528              ALOGE("Unknown partition table scheme");
529              break;
530      }
531  
532      return NULL;
533  }
534  
535  
536