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(¶ms->unique_guid, &entry->unique)) 129 || (params->set_type && GuidEqual(¶ms->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", ¶ms->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