diff options
Diffstat (limited to 'cgpt/cgpt_find.c')
-rw-r--r-- | cgpt/cgpt_find.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/cgpt/cgpt_find.c b/cgpt/cgpt_find.c new file mode 100644 index 00000000..30d0e8b0 --- /dev/null +++ b/cgpt/cgpt_find.c @@ -0,0 +1,224 @@ +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cgpt.h" + +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "cgptlib_internal.h" +#include "cgpt_params.h" + +#define BUFSIZE 1024 +// FIXME: currently we only support 512-byte sectors. +#define LBA_SIZE 512 + + +// fill comparebuf with the data to be examined, returning true on success. +static int FillBuffer(CgptFindParams *params, int fd, uint64_t pos, + uint64_t count) { + uint8_t *bufptr = params->comparebuf; + + if (-1 == lseek(fd, pos, SEEK_SET)) + return 0; + + // keep reading until done or error + while (count) { + ssize_t bytes_read = read(fd, bufptr, count); + // negative means error, 0 means (unexpected) EOF + if (bytes_read <= 0) + return 0; + count -= bytes_read; + bufptr += bytes_read; + } + + return 1; +} + +// check partition data content. return true for match, 0 for no match or error +static int match_content(CgptFindParams *params, struct drive *drive, + GptEntry *entry) { + uint64_t part_size; + + if (!params->matchlen) + return 1; + + // Ensure that the region we want to match against is inside the partition. + part_size = LBA_SIZE * (entry->ending_lba - entry->starting_lba + 1); + if (params->matchoffset + params->matchlen > part_size) { + return 0; + } + + // Read the partition data. + if (!FillBuffer(params, + drive->fd, + (LBA_SIZE * entry->starting_lba) + params->matchoffset, + params->matchlen)) { + Error("unable to read partition data\n"); + return 0; + } + + // Compare it + if (0 == memcmp(params->matchbuf, params->comparebuf, params->matchlen)) { + return 1; + } + + // Nope. + return 0; +} + +// This needs to handle /dev/mmcblk0 -> /dev/mmcblk0p3, /dev/sda -> /dev/sda3 +static void showmatch(CgptFindParams *params, char *filename, + int partnum, GptEntry *entry) { + char * format = "%s%d\n"; + if (strncmp("/dev/mmcblk", filename, 11) == 0) + format = "%sp%d\n"; + if (params->numeric) + printf("%d\n", partnum); + else + printf(format, filename, partnum); + if (params->verbose > 0) + EntryDetails(entry, partnum - 1, params->numeric); +} + +// This returns true if a GPT partition matches the search criteria. If a match +// isn't found (or if the file doesn't contain a GPT), it returns false. The +// filename and partition number that matched is left in a global, since we +// could have multiple hits. +static int do_search(CgptFindParams *params, char *fileName) { + int retval = 0; + int i; + struct drive drive; + GptEntry *entry; + char partlabel[GPT_PARTNAME_LEN]; + + if (CGPT_OK != DriveOpen(fileName, &drive)) + return 0; + + if (GPT_SUCCESS != GptSanityCheck(&drive.gpt)) { + (void) DriveClose(&drive, 0); + return 0; + } + + for (i = 0; i < GetNumberOfEntries(&drive.gpt); ++i) { + entry = GetEntry(&drive.gpt, ANY_VALID, i); + + if (IsZero(&entry->type)) + continue; + + int found = 0; + if ((params->set_unique && GuidEqual(¶ms->unique_guid, &entry->unique)) + || (params->set_type && GuidEqual(¶ms->type_guid, &entry->type))) { + found = 1; + } else if (params->set_label) { + if (CGPT_OK != UTF16ToUTF8(entry->name, + sizeof(entry->name) / sizeof(entry->name[0]), + (uint8_t *)partlabel, sizeof(partlabel))) { + Error("The label cannot be converted from UTF16, so abort.\n"); + return 0; + } + if (!strncmp(params->label, partlabel, sizeof(partlabel))) + found = 1; + } + if (found && match_content(params, &drive, entry)) { + params->hits++; + retval++; + showmatch(params, fileName, i+1, entry); + if (!params->match_partnum) + params->match_partnum = i+1; + } + } + + (void) DriveClose(&drive, 0); + + return retval; +} + + +#define PROC_PARTITIONS "/proc/partitions" +#define DEV_DIR "/dev" +#define SYS_BLOCK_DIR "/sys/block" + +static const char *devdirs[] = { "/dev", "/devices", "/devfs", 0 }; + +// Given basename "foo", see if we can find a whole, real device by that name. +// This is copied from the logic in the linux utility 'findfs', although that +// does more exhaustive searching. +static char *is_wholedev(const char *basename) { + int i; + struct stat statbuf; + static char pathname[BUFSIZE]; // we'll return this. + char tmpname[BUFSIZE]; + + // It should be a block device under /dev/, + for (i = 0; devdirs[i]; i++) { + sprintf(pathname, "%s/%s", devdirs[i], basename); + + if (0 != stat(pathname, &statbuf)) + continue; + + if (!S_ISBLK(statbuf.st_mode)) + continue; + + // It should have a symlink called /sys/block/*/device + sprintf(tmpname, "%s/%s/device", SYS_BLOCK_DIR, basename); + + if (0 != lstat(tmpname, &statbuf)) + continue; + + if (!S_ISLNK(statbuf.st_mode)) + continue; + + // found it + return pathname; + } + + return 0; +} + + +// This scans all the physical devices it can find, looking for a match. It +// returns true if any matches were found, false otherwise. +static int scan_real_devs(CgptFindParams *params) { + int found = 0; + char line[BUFSIZE]; + char partname[128]; // max size for /proc/partition lines? + FILE *fp; + char *pathname; + + fp = fopen(PROC_PARTITIONS, "r"); + if (!fp) { + perror("can't read " PROC_PARTITIONS); + return found; + } + + while (fgets(line, sizeof(line), fp)) { + int ma, mi; + long long unsigned int sz; + + if (sscanf(line, " %d %d %llu %127[^\n ]", &ma, &mi, &sz, partname) != 4) + continue; + + if ((pathname = is_wholedev(partname))) { + if (do_search(params, pathname)) { + found++; + } + } + } + + fclose(fp); + return found; +} + + +void cgpt_find(CgptFindParams *params) { + if (params == NULL) + return; + + if (params->driveName != NULL) + do_search(params, params->driveName); + else + scan_real_devs(params); +} |