cgpt_find.c 8.1 KB

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