Home | History | Annotate | Download | only in cgpt
      1 // Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <string.h>
      6 #include <sys/stat.h>
      7 #include <sys/types.h>
      8 #include <unistd.h>
      9 
     10 #include "cgpt.h"
     11 #include "cgpt_nor.h"
     12 #include "cgptlib_internal.h"
     13 #include "vboot_host.h"
     14 
     15 #define BUFSIZE 1024
     16 // FIXME: currently we only support 512-byte sectors.
     17 #define LBA_SIZE 512
     18 
     19 
     20 // fill comparebuf with the data to be examined, returning true on success.
     21 static int FillBuffer(CgptFindParams *params, int fd, uint64_t pos,
     22                        uint64_t count) {
     23   uint8_t *bufptr = params->comparebuf;
     24 
     25   if (-1 == lseek(fd, pos, SEEK_SET))
     26     return 0;
     27 
     28   // keep reading until done or error
     29   while (count) {
     30     ssize_t bytes_read = read(fd, bufptr, count);
     31     // negative means error, 0 means (unexpected) EOF
     32     if (bytes_read <= 0)
     33       return 0;
     34     count -= bytes_read;
     35     bufptr += bytes_read;
     36   }
     37 
     38   return 1;
     39 }
     40 
     41 // check partition data content. return true for match, 0 for no match or error
     42 static int match_content(CgptFindParams *params, struct drive *drive,
     43                              GptEntry *entry) {
     44   uint64_t part_size;
     45 
     46   if (!params->matchlen)
     47     return 1;
     48 
     49   // Ensure that the region we want to match against is inside the partition.
     50   part_size = LBA_SIZE * (entry->ending_lba - entry->starting_lba + 1);
     51   if (params->matchoffset + params->matchlen > part_size) {
     52     return 0;
     53   }
     54 
     55   // Read the partition data.
     56   if (!FillBuffer(params,
     57                   drive->fd,
     58                   (LBA_SIZE * entry->starting_lba) + params->matchoffset,
     59                   params->matchlen)) {
     60     Error("unable to read partition data\n");
     61     return 0;
     62   }
     63 
     64   // Compare it
     65   if (0 == memcmp(params->matchbuf, params->comparebuf, params->matchlen)) {
     66     return 1;
     67   }
     68 
     69   // Nope.
     70   return 0;
     71 }
     72 
     73 // This needs to handle /dev/mmcblk0 -> /dev/mmcblk0p3, /dev/sda -> /dev/sda3
     74 static void showmatch(CgptFindParams *params, char *filename,
     75                       int partnum, GptEntry *entry) {
     76   char * format = "%s%d\n";
     77   if (strncmp("/dev/mmcblk", filename, 11) == 0)
     78     format = "%sp%d\n";
     79 
     80   if (params->numeric) {
     81     printf("%d\n", partnum);
     82   } else {
     83     if (params->show_fn) {
     84       params->show_fn(params, filename, partnum, entry);
     85     } else {
     86       printf(format, filename, partnum);
     87     }
     88   }
     89   if (params->verbose > 0)
     90     EntryDetails(entry, partnum - 1, params->numeric);
     91 }
     92 
     93 // This handles the MTD devices. ChromeOS uses /dev/mtdX for kernel partitions,
     94 // /dev/ubiblockX_0 for root partitions, and /dev/ubiX for stateful partition.
     95 static void chromeos_mtd_show(CgptFindParams *params, char *filename,
     96                               int partnum, GptEntry *entry) {
     97   if (GuidEqual(&guid_chromeos_kernel, &entry->type)) {
     98     printf("/dev/mtd%d\n", partnum);
     99   } else if (GuidEqual(&guid_chromeos_rootfs, &entry->type)) {
    100     printf("/dev/ubiblock%d_0\n", partnum);
    101   } else {
    102     printf("/dev/ubi%d_0\n", partnum);
    103   }
    104 }
    105 
    106 // This returns true if a GPT partition matches the search criteria. If a match
    107 // isn't found (or if the file doesn't contain a GPT), it returns false. The
    108 // filename and partition number that matched is left in a global, since we
    109 // could have multiple hits.
    110 static int gpt_search(CgptFindParams *params, struct drive *drive,
    111                       char *filename) {
    112   int i;
    113   GptEntry *entry;
    114   int retval = 0;
    115   char partlabel[GPT_PARTNAME_LEN];
    116 
    117   if (GPT_SUCCESS != GptSanityCheck(&drive->gpt)) {
    118     return 0;
    119   }
    120 
    121   for (i = 0; i < GetNumberOfEntries(drive); ++i) {
    122     entry = GetEntry(&drive->gpt, ANY_VALID, i);
    123 
    124     if (GuidIsZero(&entry->type))
    125       continue;
    126 
    127     int found = 0;
    128     if ((params->set_unique && GuidEqual(&params->unique_guid, &entry->unique))
    129         || (params->set_type && GuidEqual(&params->type_guid, &entry->type))) {
    130       found = 1;
    131     } else if (params->set_label) {
    132       if (CGPT_OK != UTF16ToUTF8(entry->name,
    133                                  sizeof(entry->name) / sizeof(entry->name[0]),
    134                                  (uint8_t *)partlabel, sizeof(partlabel))) {
    135         Error("The label cannot be converted from UTF16, so abort.\n");
    136         return 0;
    137       }
    138       if (!strncmp(params->label, partlabel, sizeof(partlabel)))
    139         found = 1;
    140     }
    141     if (found && match_content(params, drive, entry)) {
    142       params->hits++;
    143       retval++;
    144       showmatch(params, filename, i+1, entry);
    145       if (!params->match_partnum)
    146         params->match_partnum = i+1;
    147     }
    148   }
    149 
    150   return retval;
    151 }
    152 
    153 static int do_search(CgptFindParams *params, char *fileName) {
    154   int retval;
    155   struct drive drive;
    156 
    157   if (CGPT_OK != DriveOpen(fileName, &drive, O_RDONLY, params->drive_size))
    158     return 0;
    159 
    160   retval = gpt_search(params, &drive, fileName);
    161 
    162   (void) DriveClose(&drive, 0);
    163 
    164   return retval;
    165 }
    166 
    167 
    168 #define PROC_MTD "/proc/mtd"
    169 #define PROC_PARTITIONS "/proc/partitions"
    170 #define DEV_DIR "/dev"
    171 #define SYS_BLOCK_DIR "/sys/block"
    172 
    173 static const char *devdirs[] = { "/dev", "/devices", "/devfs", 0 };
    174 
    175 // Given basename "foo", see if we can find a whole, real device by that name.
    176 // This is copied from the logic in the linux utility 'findfs', although that
    177 // does more exhaustive searching.
    178 static char *is_wholedev(const char *basename) {
    179   int i;
    180   struct stat statbuf;
    181   static char pathname[BUFSIZE];        // we'll return this.
    182   char tmpname[BUFSIZE];
    183 
    184   // It should be a block device under /dev/,
    185   for (i = 0; devdirs[i]; i++) {
    186     sprintf(pathname, "%s/%s", devdirs[i], basename);
    187 
    188     if (0 != stat(pathname, &statbuf))
    189       continue;
    190 
    191     if (!S_ISBLK(statbuf.st_mode))
    192       continue;
    193 
    194     // It should have a symlink called /sys/block/*/device
    195     sprintf(tmpname, "%s/%s/device", SYS_BLOCK_DIR, basename);
    196 
    197     if (0 != lstat(tmpname, &statbuf))
    198       continue;
    199 
    200     if (!S_ISLNK(statbuf.st_mode))
    201       continue;
    202 
    203     // found it
    204     return pathname;
    205   }
    206 
    207   return 0;
    208 }
    209 
    210 // This scans all the physical devices it can find, looking for a match. It
    211 // returns true if any matches were found, false otherwise.
    212 static int scan_real_devs(CgptFindParams *params) {
    213   int found = 0;
    214   char partname[128];                   // max size for /proc/partition lines?
    215   FILE *fp;
    216   char *pathname;
    217 
    218   fp = fopen(PROC_PARTITIONS, "re");
    219   if (!fp) {
    220     perror("can't read " PROC_PARTITIONS);
    221     return found;
    222   }
    223 
    224   size_t line_length = 0;
    225   char *line = NULL;
    226   while (getline(&line, &line_length, fp) != -1) {
    227     int ma, mi;
    228     long long unsigned int sz;
    229 
    230     if (sscanf(line, " %d %d %llu %127[^\n ]", &ma, &mi, &sz, partname) != 4)
    231       continue;
    232 
    233     if ((pathname = is_wholedev(partname))) {
    234       if (do_search(params, pathname)) {
    235         found++;
    236       }
    237     }
    238   }
    239 
    240   fclose(fp);
    241 
    242   fp = fopen(PROC_MTD, "re");
    243   if (!fp) {
    244     free(line);
    245     return found;
    246   }
    247 
    248   while (getline(&line, &line_length, fp) != -1) {
    249     uint64_t sz;
    250     uint32_t erasesz;
    251     char name[128];
    252     // dev:  size  erasesize  name
    253     if (sscanf(line, "%64[^:]: %" PRIx64 " %x \"%127[^\"]\"",
    254                partname, &sz, &erasesz, name) != 4)
    255       continue;
    256     if (strcmp(partname, "mtd0") == 0) {
    257       char temp_dir[] = "/tmp/cgpt_find.XXXXXX";
    258       if (params->drive_size == 0) {
    259         if (GetMtdSize("/dev/mtd0", &params->drive_size) != 0) {
    260           perror("GetMtdSize");
    261           goto cleanup;
    262         }
    263       }
    264       if (ReadNorFlash(temp_dir) != 0) {
    265         perror("ReadNorFlash");
    266         goto cleanup;
    267       }
    268       char nor_file[64];
    269       if (snprintf(nor_file, sizeof(nor_file), "%s/rw_gpt", temp_dir) > 0) {
    270         params->show_fn = chromeos_mtd_show;
    271         if (do_search(params, nor_file)) {
    272           found++;
    273         }
    274         params->show_fn = NULL;
    275       }
    276       RemoveDir(temp_dir);
    277       break;
    278     }
    279   }
    280 cleanup:
    281   fclose(fp);
    282   free(line);
    283   return found;
    284 }
    285 
    286 
    287 void CgptFind(CgptFindParams *params) {
    288   if (params == NULL)
    289     return;
    290 
    291   if (params->drive_name != NULL)
    292     do_search(params, params->drive_name);
    293   else
    294     scan_real_devs(params);
    295 }
    296