• 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          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, 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              LOGE("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          LOGE("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          LOGE("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          LOGE("Could not read config file %s", fn);
164          goto fail;
165      }
166  
167      if (!(devroot = config_find(root, "device"))) {
168          LOGE("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          LOGE("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          LOGE("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          LOGE("'gpt' partition scheme not supported yet.");
187          goto fail;
188      } else {
189          LOGE("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          LOGE("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          LOGE("start_lba must be provided");
204          goto fail;
205      }
206  
207      if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
208          LOGE("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          LOGE("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          LOGE("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         LOGE("Cannot stat, errno=%d.", errno);
248         return -1;
249      }
250  
251      if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
252          LOGE("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          LOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
285          return -1;
286      }
287  
288      if (fstat(fd, &stat)) {
289          LOGE("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              LOGE("Cannot get sector size (errno=%d)", errno);
303              goto fail;
304          }
305  
306          if (!sect_sz || sect_sz != dinfo->sect_size) {
307              LOGE("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                  LOGE("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          LOGI("Requesting operation on a regular file, not block device.");
323          if (!dinfo->sect_size) {
324              LOGE("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          LOGE("Device does not refer to a regular file or a block device!");
335          goto fail;
336      }
337  
338  #if 1
339      LOGV("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              LOGE("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                  LOGE("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              LOGE("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          LOGE("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              LOGE("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          LOGE("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              LOGE("GPT is presently not supported");
527              break;
528          default:
529              LOGE("Unknown partition table scheme");
530              break;
531      }
532  
533      return NULL;
534  }
535  
536  
537