MTD search commands: show & find

BUG=chromium:221745
TEST=MTD version of run_cgpt_tests.sh passes
BRANCH=none

Change-Id: I8a5c1afab238eb10f38d865405ea2828715685d8
Original-Change-Id: I3d72fecbb3ef95252aa35d56885288340537b1c5
Reviewed-on: https://gerrit.chromium.org/gerrit/47175
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Queue: Albert Chaulk <achaulk@chromium.org>
Tested-by: Albert Chaulk <achaulk@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/49794
Reviewed-by: Albert Chaulk <achaulk@chromium.org>
diff --git a/cgpt/cgpt_find.c b/cgpt/cgpt_find.c
index a8d3186..df6c4d3 100644
--- a/cgpt/cgpt_find.c
+++ b/cgpt/cgpt_find.c
@@ -83,27 +83,37 @@
     EntryDetails(entry, partnum - 1, params->numeric);
 }
 
+// This needs to handle /dev/mmcblk0 -> /dev/mmcblk0p3, /dev/sda -> /dev/sda3
+static void mtd_showmatch(CgptFindParams *params, char *filename,
+                           int partnum, MtdDiskPartition *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)
+    MtdEntryDetails(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;
+static int gpt_search(CgptFindParams *params, struct drive *drive,
+                      char *filename) {
   int i;
-  struct drive drive;
   GptEntry *entry;
+  int retval = 0;
   char partlabel[GPT_PARTNAME_LEN];
 
-  if (CGPT_OK != DriveOpen(fileName, &drive, O_RDONLY))
-    return 0;
-
-  if (GPT_SUCCESS != GptSanityCheck(&drive.gpt)) {
-    (void) DriveClose(&drive, 0);
+  if (GPT_SUCCESS != GptSanityCheck(&drive->gpt)) {
     return 0;
   }
 
-  for (i = 0; i < GetNumberOfEntries(&drive); ++i) {
-    entry = GetEntry(&drive.gpt, ANY_VALID, i);
+  for (i = 0; i < GetNumberOfEntries(drive); ++i) {
+    entry = GetEntry(&drive->gpt, ANY_VALID, i);
 
     if (GuidIsZero(&entry->type))
       continue;
@@ -122,15 +132,94 @@
       if (!strncmp(params->label, partlabel, sizeof(partlabel)))
         found = 1;
     }
-    if (found && match_content(params, &drive, entry)) {
+    if (found && match_content(params, drive, entry)) {
       params->hits++;
       retval++;
-      showmatch(params, fileName, i+1, entry);
+      showmatch(params, filename, i+1, entry);
       if (!params->match_partnum)
         params->match_partnum = i+1;
     }
   }
 
+  return retval;
+}
+
+static int mtd_match_type_to_guid(const MtdDiskPartition *e, const Guid *guid) {
+  return LookupMtdTypeForGuid(guid) == MtdGetEntryType(e);
+}
+
+static int mtd_match_content(CgptFindParams *params, struct drive *drive,
+                             MtdDiskPartition *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;
+}
+
+static int mtd_search(CgptFindParams *params, struct drive *drive,
+                      char *filename) {
+  int i;
+  int retval = 0;
+  for (i = 0; i < GetNumberOfEntries(drive); ++i) {
+    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, ANY_VALID, i);
+
+    if (IsUnused(drive, ANY_VALID, i))
+      continue;
+
+    int found = 0;
+
+    // Only searches by type are possible right now
+    if (params->set_type && mtd_match_type_to_guid(e, &params->type_guid)) {
+      found = 1;
+    }
+
+    if (found && mtd_match_content(params, drive, e)) {
+      params->hits++;
+      retval++;
+      mtd_showmatch(params, filename, i+1, e);
+      if (!params->match_partnum)
+        params->match_partnum = i+1;
+    }
+  }
+  return retval;
+}
+
+static int do_search(CgptFindParams *params, char *fileName) {
+  int retval;
+  struct drive drive;
+
+  if (CGPT_OK != DriveOpen(fileName, &drive, O_RDONLY))
+    return 0;
+
+  if (drive.is_mtd) {
+    retval = mtd_search(params, &drive, fileName);
+  } else {
+    retval = gpt_search(params, &drive, fileName);
+  }
+
   (void) DriveClose(&drive, 0);
 
   return retval;
diff --git a/cgpt/cgpt_show.c b/cgpt/cgpt_show.c
index e80567c..c3c222e 100644
--- a/cgpt/cgpt_show.c
+++ b/cgpt/cgpt_show.c
@@ -49,22 +49,37 @@
 #define PARTITION_FMT  "%12d%12d%8d  %s\n"
 #define PARTITION_MORE "%12s%12s%8s  %s%s\n", "", "", ""
 
-static void HeaderDetails(GptHeader *header, GptEntry *entries,
-                          const char *indent, int raw) {
-  int i;
-
+void PrintSignature(const char *indent, const char *sig, size_t n, int raw) {
+  size_t i;
   printf("%sSig: ", indent);
   if (!raw) {
     printf("[");
-    for (i = 0; i < sizeof(header->signature); ++i)
-      printf("%c", header->signature[i]);
+    for (i = 0; i < n; ++i)
+      printf("%c", sig[i]);
     printf("]");
   } else {
-    char buf[BUFFER_SIZE(sizeof(header->signature))];
-    RawDump((uint8_t *)header->signature, sizeof(header->signature), buf, 1);
+    char *buf = malloc(BUFFER_SIZE(n));
+    RawDump((uint8_t *)sig, n, buf, 1);
     printf("%s", buf);
+    free(buf);
   }
   printf("\n");
+}
+
+void MtdHeaderDetails(MtdDiskLayout *header, const char *indent, int raw) {
+  PrintSignature(indent, (char*)header->signature, sizeof(header->signature),
+    raw);
+
+  printf("%sSize: %d\n", indent, header->size);
+  printf("%sCRC: 0x%08x %s\n", indent, header->crc32,
+         (MtdHeaderCrc(header) != header->crc32) ? "(INVALID)" : "");
+  printf("%sFirst LBA: %u\n", indent, header->first_lba);
+  printf("%sLast LBA: %u\n", indent, header->last_lba);
+}
+
+static void HeaderDetails(GptHeader *header, GptEntry *entries,
+                          const char *indent, int raw) {
+  PrintSignature(indent, header->signature, sizeof(header->signature), raw);
 
   printf("%sRev: 0x%08x\n", indent, header->revision);
   printf("%sSize: %d\n", indent, header->size);
@@ -92,6 +107,26 @@
          );
 }
 
+void MtdEntryDetails(MtdDiskPartition *entry, uint32_t index, int raw) {
+  const Guid *guid = LookupGuidForMtdType(MtdGetEntryType(entry));
+  char buf[256];                   // scratch buffer for formatting output
+  if (guid) {
+    ResolveType(guid, buf);
+  } else {
+    snprintf(buf, sizeof(buf), "MTD partition type %d", MtdGetEntryType(entry));
+  }
+
+  if (!raw) {
+    printf(PARTITION_FMT, (int)entry->starting_lba,
+           (int)(entry->ending_lba - entry->starting_lba + 1),
+           index+1, buf);
+  } else {
+    printf(PARTITION_FMT, (int)entry->starting_lba,
+           (int)(entry->ending_lba - entry->starting_lba + 1),
+           index+1, buf);
+  }
+}
+
 void EntryDetails(GptEntry *entry, uint32_t index, int raw) {
   char contents[256];                   // scratch buffer for formatting output
   uint8_t label[GPT_PARTNAME_LEN];
@@ -149,6 +184,17 @@
   }
 }
 
+void MtdEntriesDetails(struct drive *drive, int secondary, int raw) {
+  uint32_t i;
+
+  for (i = 0; i < GetNumberOfEntries(drive); ++i) {
+    MtdDiskPartition *entry;
+    entry = MtdGetEntry(&drive->mtd, secondary, i);
+    if (IsUnused(drive, secondary, i))
+      continue;
+    MtdEntryDetails(entry, i, raw);
+  }
+}
 
 void EntriesDetails(struct drive *drive, const int secondary, int raw) {
   uint32_t i;
@@ -200,17 +246,86 @@
   return retval;
 }
 
-int CgptShow(CgptShowParams *params) {
-  struct drive drive;
+int MtdShow(struct drive *drive, CgptShowParams *params) {
+  if (params->partition) {                      // show single partition
+    if (params->partition > GetNumberOfEntries(drive)) {
+      Error("invalid partition number: %d\n", params->partition);
+      return CGPT_FAILED;
+    }
+
+    uint32_t index = params->partition - 1;
+    MtdDiskPartition *entry = MtdGetEntry(&drive->mtd, ANY_VALID, index);
+    char buf[256];                      // scratch buffer for string conversion
+    const Guid *guid;
+
+    if (params->single_item) {
+      switch(params->single_item) {
+      case 'b':
+        printf("%u\n", entry->starting_lba);
+        break;
+      case 's':
+        printf("%u\n", entry->ending_lba - entry->starting_lba + 1);
+        break;
+      case 't':
+        guid = LookupGuidForMtdType(MtdGetEntryType(entry));
+        GuidToStr(guid, buf, sizeof(buf));
+        printf("%s\n", buf);
+        break;
+      case 'S':
+        printf("%d\n", GetSuccessful(drive, ANY_VALID, index));
+        break;
+      case 'T':
+        printf("%d\n", GetTries(drive, ANY_VALID, index));
+        break;
+      case 'P':
+        printf("%d\n", GetPriority(drive, ANY_VALID, index));
+        break;
+      case 'A':
+        printf("0x%x\n", entry->flags);
+        break;
+      }
+    } else {
+      printf(TITLE_FMT, "start", "size", "part", "contents");
+      MtdEntryDetails(entry, index, params->numeric);
+    }
+  } else if (params->quick) {                   // show all partitions, quickly
+    uint32_t i;
+    char type[GUID_STRLEN];
+
+    for (i = 0; i < GetNumberOfEntries(drive); ++i) {
+      MtdDiskPartition *entry = MtdGetEntry(&drive->mtd, ANY_VALID, i);
+      const Guid *guid = LookupGuidForMtdType(MtdGetEntryType(entry));
+
+      if (IsUnused(drive, ANY_VALID, i))
+        continue;
+
+      if (!params->numeric && guid) {
+        ResolveType(guid, type);
+      } else {
+        snprintf(type, sizeof(type), "MTD partition type %d",
+                 MtdGetEntryType(entry));
+      }
+      printf(PARTITION_FMT, (int)entry->starting_lba,
+             (int)(entry->ending_lba - entry->starting_lba + 1),
+             i+1, type);
+    }
+  } else {                              // show all partitions
+    if (params->debug || params->verbose) {
+      char indent[64];
+
+      require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
+      MtdHeaderDetails(&drive->mtd.primary, indent, 0);
+    }
+
+    MtdEntriesDetails(drive, PRIMARY, params->numeric);
+  }
+
+  return CGPT_OK;
+}
+
+int GptShow(struct drive *drive, CgptShowParams *params) {
   int gpt_retval;
-
-  if (params == NULL)
-    return CGPT_FAILED;
-
-  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY))
-    return CGPT_FAILED;
-
-  if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
+  if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
     Error("GptSanityCheck() returned %d: %s\n",
           gpt_retval, GptError(gpt_retval));
     return CGPT_FAILED;
@@ -218,13 +333,13 @@
 
   if (params->partition) {                      // show single partition
 
-    if (params->partition > GetNumberOfEntries(&drive)) {
+    if (params->partition > GetNumberOfEntries(drive)) {
       Error("invalid partition number: %d\n", params->partition);
       return CGPT_FAILED;
     }
 
     uint32_t index = params->partition - 1;
-    GptEntry *entry = GetEntry(&drive.gpt, ANY_VALID, index);
+    GptEntry *entry = GetEntry(&drive->gpt, ANY_VALID, index);
     char buf[256];                      // scratch buffer for string conversion
 
     if (params->single_item) {
@@ -254,13 +369,13 @@
         printf("%s\n", buf);
         break;
       case 'S':
-        printf("%d\n", GetSuccessful(&drive, ANY_VALID, index));
+        printf("%d\n", GetSuccessful(drive, ANY_VALID, index));
         break;
       case 'T':
-        printf("%d\n", GetTries(&drive, ANY_VALID, index));
+        printf("%d\n", GetTries(drive, ANY_VALID, index));
         break;
       case 'P':
-        printf("%d\n", GetPriority(&drive, ANY_VALID, index));
+        printf("%d\n", GetPriority(drive, ANY_VALID, index));
         break;
       case 'A':
         printf("0x%x\n", entry->attrs.fields.gpt_att);
@@ -276,8 +391,8 @@
     GptEntry *entry;
     char type[GUID_STRLEN];
 
-    for (i = 0; i < GetNumberOfEntries(&drive); ++i) {
-      entry = GetEntry(&drive.gpt, ANY_VALID, i);
+    for (i = 0; i < GetNumberOfEntries(drive); ++i) {
+      entry = GetEntry(&drive->gpt, ANY_VALID, i);
 
       if (GuidIsZero(&entry->type))
         continue;
@@ -293,17 +408,17 @@
   } else {                              // show all partitions
     GptEntry *entries;
 
-    if (CGPT_OK != ReadPMBR(&drive)) {
+    if (CGPT_OK != ReadPMBR(drive)) {
       Error("Unable to read PMBR\n");
       return CGPT_FAILED;
     }
 
     printf(TITLE_FMT, "start", "size", "part", "contents");
     char buf[256];                      // buffer for formatted PMBR content
-    PMBRToStr(&drive.pmbr, buf, sizeof(buf)); // will exit if buf is too small
+    PMBRToStr(&drive->pmbr, buf, sizeof(buf)); // will exit if buf is too small
     printf(GPT_FMT, 0, GPT_PMBR_SECTOR, "", buf);
 
-    if (drive.gpt.valid_headers & MASK_PRIMARY) {
+    if (drive->gpt.valid_headers & MASK_PRIMARY) {
       printf(GPT_FMT, (int)GPT_PMBR_SECTOR,
              (int)GPT_HEADER_SECTOR, "", "Pri GPT header");
     } else {
@@ -312,30 +427,30 @@
     }
 
     if (params->debug ||
-        ((drive.gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
+        ((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
       GptHeader *header;
       char indent[64];
 
       require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
-      header = (GptHeader*)drive.gpt.primary_header;
-      entries = (GptEntry*)drive.gpt.primary_entries;
+      header = (GptHeader*)drive->gpt.primary_header;
+      entries = (GptEntry*)drive->gpt.primary_entries;
       HeaderDetails(header, entries, indent, params->numeric);
     }
 
     printf(GPT_FMT, (int)(GPT_PMBR_SECTOR + GPT_HEADER_SECTOR),
            (int)GPT_ENTRIES_SECTORS,
-           drive.gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
+           drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
            "Pri GPT table");
 
     if (params->debug ||
-        (drive.gpt.valid_entries & MASK_PRIMARY))
-      EntriesDetails(&drive, PRIMARY, params->numeric);
+        (drive->gpt.valid_entries & MASK_PRIMARY))
+      EntriesDetails(drive, PRIMARY, params->numeric);
 
     /****************************** Secondary *************************/
-    printf(GPT_FMT, (int)(drive.gpt.drive_sectors - GPT_HEADER_SECTOR -
+    printf(GPT_FMT, (int)(drive->gpt.drive_sectors - GPT_HEADER_SECTOR -
                           GPT_ENTRIES_SECTORS),
            (int)GPT_ENTRIES_SECTORS,
-           drive.gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
+           drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
            "Sec GPT table");
     /* We show secondary table details if any of following is true.
      *   1. in debug mode.
@@ -343,15 +458,15 @@
      *   3. secondary is not identical to promary.
      */
     if (params->debug ||
-        ((drive.gpt.valid_entries & MASK_SECONDARY) &&
-         (!(drive.gpt.valid_entries & MASK_PRIMARY) ||
-          memcmp(drive.gpt.primary_entries, drive.gpt.secondary_entries,
+        ((drive->gpt.valid_entries & MASK_SECONDARY) &&
+         (!(drive->gpt.valid_entries & MASK_PRIMARY) ||
+          memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries,
                  TOTAL_ENTRIES_SIZE)))) {
-      EntriesDetails(&drive, SECONDARY, params->numeric);
+      EntriesDetails(drive, SECONDARY, params->numeric);
     }
 
-    if (drive.gpt.valid_headers & MASK_SECONDARY)
-      printf(GPT_FMT, (int)(drive.gpt.drive_sectors - GPT_HEADER_SECTOR),
+    if (drive->gpt.valid_headers & MASK_SECONDARY)
+      printf(GPT_FMT, (int)(drive->gpt.drive_sectors - GPT_HEADER_SECTOR),
              (int)GPT_HEADER_SECTOR, "", "Sec GPT header");
     else
       printf(GPT_FMT, (int)GPT_PMBR_SECTOR,
@@ -362,23 +477,43 @@
      *   3. secondary is not synonymous to primary.
      */
     if (params->debug ||
-        ((drive.gpt.valid_headers & MASK_SECONDARY) &&
-         (!(drive.gpt.valid_headers & MASK_PRIMARY) ||
-          !IsSynonymous((GptHeader*)drive.gpt.primary_header,
-                        (GptHeader*)drive.gpt.secondary_header)) &&
+        ((drive->gpt.valid_headers & MASK_SECONDARY) &&
+         (!(drive->gpt.valid_headers & MASK_PRIMARY) ||
+          !IsSynonymous((GptHeader*)drive->gpt.primary_header,
+                        (GptHeader*)drive->gpt.secondary_header)) &&
          params->verbose)) {
       GptHeader *header;
       char indent[64];
 
       require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
-      header = (GptHeader*)drive.gpt.secondary_header;
-      entries = (GptEntry*)drive.gpt.secondary_entries;
+      header = (GptHeader*)drive->gpt.secondary_header;
+      entries = (GptEntry*)drive->gpt.secondary_entries;
       HeaderDetails(header, entries, indent, params->numeric);
     }
   }
 
-  CheckValid(&drive);
-  DriveClose(&drive, 0);
+  CheckValid(drive);
 
   return CGPT_OK;
 }
+
+int CgptShow(CgptShowParams *params) {
+  struct drive drive;
+
+  if (params == NULL)
+    return CGPT_FAILED;
+
+  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY))
+    return CGPT_FAILED;
+
+  if (drive.is_mtd) {
+    if (MtdShow(&drive, params))
+      return CGPT_FAILED;
+  } else {
+    if (GptShow(&drive, params))
+      return CGPT_FAILED;
+  }
+
+  DriveClose(&drive, 0);
+  return CGPT_OK;
+}