cgpt_find.c revision fa6b35c1ffa33833b3250a6515869ccd4cb59121
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 "cgptlib_internal.h" 12#include "vboot_host.h" 13 14#define BUFSIZE 1024 15// FIXME: currently we only support 512-byte sectors. 16#define LBA_SIZE 512 17 18 19// fill comparebuf with the data to be examined, returning true on success. 20static int FillBuffer(CgptFindParams *params, int fd, uint64_t pos, 21 uint64_t count) { 22 uint8_t *bufptr = params->comparebuf; 23 24 if (-1 == lseek(fd, pos, SEEK_SET)) 25 return 0; 26 27 // keep reading until done or error 28 while (count) { 29 ssize_t bytes_read = read(fd, bufptr, count); 30 // negative means error, 0 means (unexpected) EOF 31 if (bytes_read <= 0) 32 return 0; 33 count -= bytes_read; 34 bufptr += bytes_read; 35 } 36 37 return 1; 38} 39 40// check partition data content. return true for match, 0 for no match or error 41static int match_content(CgptFindParams *params, struct drive *drive, 42 GptEntry *entry) { 43 uint64_t part_size; 44 45 if (!params->matchlen) 46 return 1; 47 48 // Ensure that the region we want to match against is inside the partition. 49 part_size = LBA_SIZE * (entry->ending_lba - entry->starting_lba + 1); 50 if (params->matchoffset + params->matchlen > part_size) { 51 return 0; 52 } 53 54 // Read the partition data. 55 if (!FillBuffer(params, 56 drive->fd, 57 (LBA_SIZE * entry->starting_lba) + params->matchoffset, 58 params->matchlen)) { 59 Error("unable to read partition data\n"); 60 return 0; 61 } 62 63 // Compare it 64 if (0 == memcmp(params->matchbuf, params->comparebuf, params->matchlen)) { 65 return 1; 66 } 67 68 // Nope. 69 return 0; 70} 71 72// This needs to handle /dev/mmcblk0 -> /dev/mmcblk0p3, /dev/sda -> /dev/sda3 73static void showmatch(CgptFindParams *params, char *filename, 74 int partnum, GptEntry *entry) { 75 char * format = "%s%d\n"; 76 if (strncmp("/dev/mmcblk", filename, 11) == 0) 77 format = "%sp%d\n"; 78 if (params->numeric) 79 printf("%d\n", partnum); 80 else 81 printf(format, filename, partnum); 82 if (params->verbose > 0) 83 EntryDetails(entry, partnum - 1, params->numeric); 84} 85 86// This returns true if a GPT partition matches the search criteria. If a match 87// isn't found (or if the file doesn't contain a GPT), it returns false. The 88// filename and partition number that matched is left in a global, since we 89// could have multiple hits. 90static int do_search(CgptFindParams *params, char *fileName) { 91 int retval = 0; 92 int i; 93 struct drive drive; 94 GptEntry *entry; 95 char partlabel[GPT_PARTNAME_LEN]; 96 97 if (CGPT_OK != DriveOpen(fileName, &drive, O_RDONLY)) 98 return 0; 99 100 if (GPT_SUCCESS != GptSanityCheck(&drive.gpt)) { 101 (void) DriveClose(&drive, 0); 102 return 0; 103 } 104 105 for (i = 0; i < GetNumberOfEntries(&drive); ++i) { 106 entry = GetEntry(&drive.gpt, ANY_VALID, i); 107 108 if (GuidIsZero(&entry->type)) 109 continue; 110 111 int found = 0; 112 if ((params->set_unique && GuidEqual(¶ms->unique_guid, &entry->unique)) 113 || (params->set_type && GuidEqual(¶ms->type_guid, &entry->type))) { 114 found = 1; 115 } else if (params->set_label) { 116 if (CGPT_OK != UTF16ToUTF8(entry->name, 117 sizeof(entry->name) / sizeof(entry->name[0]), 118 (uint8_t *)partlabel, sizeof(partlabel))) { 119 Error("The label cannot be converted from UTF16, so abort.\n"); 120 return 0; 121 } 122 if (!strncmp(params->label, partlabel, sizeof(partlabel))) 123 found = 1; 124 } 125 if (found && match_content(params, &drive, entry)) { 126 params->hits++; 127 retval++; 128 showmatch(params, fileName, i+1, entry); 129 if (!params->match_partnum) 130 params->match_partnum = i+1; 131 } 132 } 133 134 (void) DriveClose(&drive, 0); 135 136 return retval; 137} 138 139 140#define PROC_PARTITIONS "/proc/partitions" 141#define DEV_DIR "/dev" 142#define SYS_BLOCK_DIR "/sys/block" 143 144static const char *devdirs[] = { "/dev", "/devices", "/devfs", 0 }; 145 146// Given basename "foo", see if we can find a whole, real device by that name. 147// This is copied from the logic in the linux utility 'findfs', although that 148// does more exhaustive searching. 149static char *is_wholedev(const char *basename) { 150 int i; 151 struct stat statbuf; 152 static char pathname[BUFSIZE]; // we'll return this. 153 char tmpname[BUFSIZE]; 154 155 // It should be a block device under /dev/, 156 for (i = 0; devdirs[i]; i++) { 157 sprintf(pathname, "%s/%s", devdirs[i], basename); 158 159 if (0 != stat(pathname, &statbuf)) 160 continue; 161 162 if (!S_ISBLK(statbuf.st_mode)) 163 continue; 164 165 // It should have a symlink called /sys/block/*/device 166 sprintf(tmpname, "%s/%s/device", SYS_BLOCK_DIR, basename); 167 168 if (0 != lstat(tmpname, &statbuf)) 169 continue; 170 171 if (!S_ISLNK(statbuf.st_mode)) 172 continue; 173 174 // found it 175 return pathname; 176 } 177 178 return 0; 179} 180 181 182// This scans all the physical devices it can find, looking for a match. It 183// returns true if any matches were found, false otherwise. 184static int scan_real_devs(CgptFindParams *params) { 185 int found = 0; 186 char line[BUFSIZE]; 187 char partname[128]; // max size for /proc/partition lines? 188 FILE *fp; 189 char *pathname; 190 191 fp = fopen(PROC_PARTITIONS, "r"); 192 if (!fp) { 193 perror("can't read " PROC_PARTITIONS); 194 return found; 195 } 196 197 while (fgets(line, sizeof(line), fp)) { 198 int ma, mi; 199 long long unsigned int sz; 200 201 if (sscanf(line, " %d %d %llu %127[^\n ]", &ma, &mi, &sz, partname) != 4) 202 continue; 203 204 if ((pathname = is_wholedev(partname))) { 205 if (do_search(params, pathname)) { 206 found++; 207 } 208 } 209 } 210 211 fclose(fp); 212 return found; 213} 214 215 216void CgptFind(CgptFindParams *params) { 217 if (params == NULL) 218 return; 219 220 if (params->drive_name != NULL) 221 do_search(params, params->drive_name); 222 else 223 scan_real_devs(params); 224} 225