Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9bb2ff1 authored by Elliott Hughes's avatar Elliott Hughes Committed by android-build-merger
Browse files

Merge "libziparchive: add zipinfo(1)." am: 23a3dcea am: 3853870e

am: c67757dc

Change-Id: I31efca36b784a7bfa5aa747eae2a6003d5042eba
parents 8a8e8fb0 c67757dc
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -175,7 +175,7 @@ cc_benchmark {
}

cc_binary {
    name: "unzip",
    name: "ziptool",
    defaults: ["libziparchive_flags"],
    srcs: ["unzip.cpp"],
    shared_libs: [
@@ -183,6 +183,12 @@ cc_binary {
        "libziparchive",
    ],
    recovery_available: true,
    host_supported: true,
    target: {
        android: {
            symlinks: ["unzip", "zipinfo"],
        },
    },
}

cc_fuzz {
+19 −0
Original line number Diff line number Diff line
@@ -79,6 +79,12 @@ struct ZipEntry {

  // The offset to the start of data for this ZipEntry.
  off64_t offset;

  // The version of zip and the host file system this came from.
  uint16_t version_made_by;

  // Whether this entry is believed to be text or binary.
  bool is_text;
};

struct ZipArchive;
@@ -125,6 +131,19 @@ int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* de
 */
void CloseArchive(ZipArchiveHandle archive);

/** See GetArchiveInfo(). */
struct ZipArchiveInfo {
  /** The size in bytes of the archive itself. Used by zipinfo. */
  off64_t archive_size;
  /** The number of entries in the archive. */
  size_t entry_count;
};

/**
 * Returns information about the given archive.
 */
ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);

/*
 * Find an entry in the Zip archive, by name. |data| must be non-null.
 *
+194 −91
Original line number Diff line number Diff line
@@ -40,12 +40,15 @@ enum OverwriteMode {
  kPrompt,
};

static bool is_unzip;
static OverwriteMode overwrite_mode = kPrompt;
static bool flag_1 = false;
static const char* flag_d = nullptr;
static bool flag_l = false;
static bool flag_p = false;
static bool flag_q = false;
static bool flag_v = false;
static bool flag_x = false;
static const char* archive_name = nullptr;
static std::set<std::string> includes;
static std::set<std::string> excludes;
@@ -88,7 +91,9 @@ static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
  return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
}

static void MaybeShowHeader() {
static void MaybeShowHeader(ZipArchiveHandle zah) {
  if (is_unzip) {
    // unzip has three formats.
    if (!flag_q) printf("Archive:  %s\n", archive_name);
    if (flag_v) {
      printf(
@@ -99,9 +104,19 @@ static void MaybeShowHeader() {
          "  Length      Date    Time    Name\n"
          "---------  ---------- -----   ----\n");
    }
  } else {
    // zipinfo.
    if (!flag_1 && includes.empty() && excludes.empty()) {
      ZipArchiveInfo info{GetArchiveInfo(zah)};
      printf("Archive:  %s\n", archive_name);
      printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
             info.entry_count);
    }
  }
}

static void MaybeShowFooter() {
  if (is_unzip) {
    if (flag_v) {
      printf(
          "--------          -------  ---                            -------\n"
@@ -115,6 +130,13 @@ static void MaybeShowFooter() {
          "%9" PRId64 "                     %zu file%s\n",
          total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
    }
  } else {
    if (!flag_1 && includes.empty() && excludes.empty()) {
      printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %3d%%\n",
             file_count, total_uncompressed_length, total_compressed_length,
             CompressionRatio(total_uncompressed_length, total_compressed_length));
    }
  }
}

static bool PromptOverwrite(const std::string& dst) {
@@ -226,7 +248,47 @@ static void ListOne(const ZipEntry& entry, const std::string& name) {
  }
}

static void InfoOne(const ZipEntry& entry, const std::string& name) {
  if (flag_1) {
    // "android-ndk-r19b/sources/android/NOTICE"
    printf("%s\n", name.c_str());
    return;
  }

  int version = entry.version_made_by & 0xff;
  int os = (entry.version_made_by >> 8) & 0xff;

  // TODO: Support suid/sgid? Non-Unix host file system attributes?
  char mode[] = "??????????";
  if (os == 3) {
    mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
    mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
    mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
    mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
    mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
    mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
    mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
    mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
    mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
    mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
  }

  // TODO: zipinfo (unlike unzip) sometimes uses time zone?
  // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
  tm t = entry.GetModificationTime();
  char time[32];
  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
           t.tm_mday, t.tm_hour, t.tm_min);

  // "-rw-r--r--  3.0 unx      577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
  printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10,
         os == 3 ? "unx" : "???", entry.uncompressed_length, entry.is_text ? 't' : 'b',
         entry.has_data_descriptor ? 'X' : 'x', entry.method == kCompressStored ? "stor" : "defX",
         time, name.c_str());
}

static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
  if (is_unzip) {
    if (flag_l || flag_v) {
      // -l or -lv or -lq or -v.
      ListOne(entry, name);
@@ -238,13 +300,17 @@ static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string&
        ExtractOne(zah, entry, name);
      }
    }
  } else {
    // zipinfo or zipinfo -1.
    InfoOne(entry, name);
  }
  total_uncompressed_length += entry.uncompressed_length;
  total_compressed_length += entry.compressed_length;
  ++file_count;
}

static void ProcessAll(ZipArchiveHandle zah) {
  MaybeShowHeader();
  MaybeShowHeader(zah);

  // libziparchive iteration order doesn't match the central directory.
  // We could sort, but that would cost extra and wouldn't match either.
@@ -267,6 +333,7 @@ static void ProcessAll(ZipArchiveHandle zah) {
}

static void ShowHelp(bool full) {
  if (is_unzip) {
    fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
    if (!full) exit(EXIT_FAILURE);

@@ -283,23 +350,59 @@ static void ShowHelp(bool full) {
        "-q	Quiet\n"
        "-v	List contents verbosely\n"
        "-x FILE	Exclude files\n");
  } else {
    fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
    if (!full) exit(EXIT_FAILURE);

    printf(
        "\n"
        "Show information about FILEs from ZIP archive. Default is all files.\n"
        "Both the include and exclude (-x) lists use shell glob patterns.\n"
        "\n"
        "-1	Show filenames only, one per line\n"
        "-x FILE	Exclude files\n");
  }
  exit(EXIT_SUCCESS);
}

static void HandleCommonOption(int opt) {
  switch (opt) {
    case 'h':
      ShowHelp(true);
      break;
    case 'x':
      flag_x = true;
      break;
    case 1:
      // -x swallows all following arguments, so we use '-' in the getopt
      // string and collect files here.
      if (!archive_name) {
        archive_name = optarg;
      } else if (flag_x) {
        excludes.insert(optarg);
      } else {
        includes.insert(optarg);
      }
      break;
    default:
      ShowHelp(false);
      break;
  }
}

int main(int argc, char* argv[]) {
  static struct option opts[] = {
      {"help", no_argument, 0, 'h'},
  };
  bool saw_x = false;

  is_unzip = !strcmp(basename(argv[0]), "unzip");
  if (is_unzip) {
    int opt;
    while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
      switch (opt) {
        case 'd':
          flag_d = optarg;
          break;
      case 'h':
        ShowHelp(true);
        break;
        case 'l':
          flag_l = true;
          break;
@@ -318,22 +421,22 @@ int main(int argc, char* argv[]) {
        case 'v':
          flag_v = true;
          break;
      case 'x':
        saw_x = true;
        default:
          HandleCommonOption(opt);
          break;
      case 1:
        // -x swallows all following arguments, so we use '-' in the getopt
        // string and collect files here.
        if (!archive_name) {
          archive_name = optarg;
        } else if (saw_x) {
          excludes.insert(optarg);
        } else {
          includes.insert(optarg);
      }
    }
  } else {
    int opt;
    while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
      switch (opt) {
        case '1':
          flag_1 = true;
          break;
        default:
        ShowHelp(false);
          HandleCommonOption(opt);
          break;
      }
    }
  }

+13 −1
Original line number Diff line number Diff line
@@ -478,6 +478,13 @@ int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* de
  return OpenArchiveInternal(archive, debug_file_name);
}

ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
  ZipArchiveInfo result;
  result.archive_size = archive->mapped_zip.GetFileLength();
  result.entry_count = archive->num_entries;
  return result;
}

/*
 * Close a ZipArchive, closing the file and freeing the contents.
 */
@@ -614,12 +621,17 @@ static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry*
  }

  // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
  if ((cdr->version_made_by >> 8) == 3) {
  data->version_made_by = cdr->version_made_by;
  if ((data->version_made_by >> 8) == 3) {
    data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
  } else {
    data->unix_mode = 0777;
  }

  // 4.4.14: the lowest bit of the internal file attributes field indicates text.
  // Currently only needed to implement zipinfo.
  data->is_text = (cdr->internal_file_attributes & 1);

  // Check that the local file header name matches the declared
  // name in the central directory.
  if (lfh->file_name_length != nameLen) {
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ phony {
        "tcpdump",
        "toolbox",
        "toybox",
        "unzip",
        "ziptool",
    ],
}