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(&params->unique_guid, &entry->unique))
113        || (params->set_type && GuidEqual(&params->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