Diff options
Diffstat (limited to '')
| -rw-r--r-- | source/ui-snapshot.c | 206 |
1 file changed, 75 insertions, 131 deletions
diff --git a/source/ui-snapshot.c b/source/ui-snapshot.c index bd5ebf2..333bce6 100644 --- a/source/ui-snapshot.c +++ b/source/ui-snapshot.c @@ -1,13 +1,13 @@ /* * Serves a commit as a downloadable archive. Writing the archive is git's - * work, so each format here is a thin wrapper that either calls write_archive - * directly or pipes its tar output through an external compressor. The table - * of formats also lives here, and since a repository's snapshots mask is one - * bit per table position, that order is part of what cgitrc means. When the - * request carries no revision the file name is worked backwards to find one, - * so that a link to cgit-1.2.tar.gz can serve the tag it was named after. A - * name ending in .asc asks for the detached signature kept under refs/notes - * instead of the archive itself. + * work, so each format here is only a row in a table, naming the archive + * format git writes and, for a compressed tar, the compressor its output is + * piped through. A repository's snapshots mask is one bit per table position, + * so that order is part of what cgitrc means. When the request carries no + * revision the file name is worked backwards to find one, so that a link to + * cgit-1.2.tar.gz can serve the tag it was named after. A name ending in .asc + * asks for the detached signature kept under refs/notes instead of the archive + * itself. */ #define USE_THE_REPOSITORY_VARIABLE @@ -19,13 +19,10 @@ #include "ui-shared.h" #include "ui-snapshot.h" -#define SIG_SUFFIX ".asc" - -static int write_archive_format(const char *format_arg, const char *hex, const char *prefix) +static void write_archive_format(const char *format_arg, const char *hex, const char *prefix) { struct strvec argv = STRVEC_INIT; const char **args; - int result; strvec_push(&argv, "snapshot"); strvec_push(&argv, format_arg); @@ -48,20 +45,25 @@ static int write_archive_format(const char *format_arg, const char *hex, const c args = xmalloc(sizeof(char *) * (argv.nr + 1)); memcpy(args, argv.v, sizeof(char *) * (argv.nr + 1)); - result = write_archive(argv.nr, args, NULL, the_repository, NULL, 0); + write_archive(argv.nr, args, NULL, the_repository, NULL, 0); strvec_clear(&argv); free(args); - return result; } -static int write_tar_archive(const char *hex, const char *prefix) +static void write_snapshot(const struct cgit_snapshot_format *format, + const char *hex, const char *prefix) { - return write_archive_format("--format=tar", hex, prefix); -} + struct cgit_exec_filter compressor; + struct cgit_filter *filter = NULL; -static int write_zip_archive(const char *hex, const char *prefix) -{ - return write_archive_format("--format=zip", hex, prefix); + if (format->compressor) { + cgit_exec_filter_init(&compressor, format->compressor[0], + format->compressor); + filter = &compressor.base; + } + cgit_open_filter(filter); + write_archive_format(format->format_arg, hex, prefix); + cgit_close_filter(filter); } static int program_on_path(const char *program) @@ -101,66 +103,25 @@ static int program_on_path(const char *program) return found; } -static int write_compressed_tar_archive(const char *hex, const char *prefix, const char *argv[]) -{ - struct cgit_exec_filter filter; - int result; - - cgit_exec_filter_init(&filter, argv[0], argv); - cgit_open_filter(&filter.base); - result = write_tar_archive(hex, prefix); - cgit_close_filter(&filter.base); - return result; -} - -static int write_tar_gzip_archive(const char *hex, const char *prefix) -{ - const char *argv[] = { "gzip", "-n", NULL }; - - return write_compressed_tar_archive(hex, prefix, argv); -} - -static int write_tar_bzip2_archive(const char *hex, const char *prefix) -{ - const char *argv[] = { "bzip2", NULL }; - - return write_compressed_tar_archive(hex, prefix, argv); -} - -static int write_tar_lzip_archive(const char *hex, const char *prefix) -{ - const char *argv[] = { "lzip", NULL }; - - return write_compressed_tar_archive(hex, prefix, argv); -} - -static int write_tar_xz_archive(const char *hex, const char *prefix) -{ - // Current xz runs a thread per core unless told otherwise. - const char *argv[] = { "xz", "-T1", NULL }; - - return write_compressed_tar_archive(hex, prefix, argv); -} - -static int write_tar_zstd_archive(const char *hex, const char *prefix) -{ - // Single-threaded like the other compressors, since -T0 would let one - // request pin every core. - const char *argv[] = { "zstd", NULL }; - - return write_compressed_tar_archive(hex, prefix, argv); -} +static const char *gzip_argv[] = { "gzip", "-n", NULL }; +static const char *bzip2_argv[] = { "bzip2", NULL }; +static const char *lzip_argv[] = { "lzip", NULL }; +// Current xz runs a thread per core unless told otherwise. +static const char *xz_argv[] = { "xz", "-T1", NULL }; +// Single-threaded like the other compressors, since -T0 would let one +// request pin every core. +static const char *zstd_argv[] = { "zstd", NULL }; // ui-shared.c reads entry zero as the tar whose signature stands in for every // tar variant, so this order cannot change. const struct cgit_snapshot_format cgit_snapshot_formats[] = { - { ".tar", "application/x-tar", write_tar_archive, NULL }, - { ".tar.gz", "application/gzip", write_tar_gzip_archive, "gzip" }, - { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, "bzip2" }, - { ".tar.lz", "application/x-lzip", write_tar_lzip_archive, "lzip" }, - { ".tar.xz", "application/x-xz", write_tar_xz_archive, "xz" }, - { ".tar.zst", "application/zstd", write_tar_zstd_archive, "zstd" }, - { ".zip", "application/zip", write_zip_archive, NULL }, + { ".tar", "application/x-tar", "--format=tar", NULL }, + { ".tar.gz", "application/gzip", "--format=tar", gzip_argv }, + { ".tar.bz2", "application/x-bzip2", "--format=tar", bzip2_argv }, + { ".tar.lz", "application/x-lzip", "--format=tar", lzip_argv }, + { ".tar.xz", "application/x-xz", "--format=tar", xz_argv }, + { ".tar.zst", "application/zstd", "--format=tar", zstd_argv }, + { ".zip", "application/zip", "--format=zip", NULL }, { 0 } }; @@ -248,50 +209,41 @@ static int candidate_resolves(const struct cgit_repo *repo, const char *stem, * any is tried as a branch, which a branch spelled like a tag's stem would * otherwise win. */ -static const char *ref_from_filename(const struct cgit_repo *repo, const char *filename, - const struct cgit_snapshot_format *format) +static char *ref_from_filename(const struct cgit_repo *repo, const char *stem) { struct strbuf rev = STRBUF_INIT; - char *stem = xstrndup(filename, strlen(filename) - strlen(format->suffix)); int found = candidate_resolves(repo, stem, &rev, 1) || candidate_resolves(repo, stem, &rev, 0); - free(stem); if (!found) strbuf_release(&rev); return found ? strbuf_detach(&rev, NULL) : NULL; } -static int send_snapshot(const struct cgit_snapshot_format *format, const char *hex, +static void send_snapshot(const struct cgit_snapshot_format *format, const char *hex, const char *prefix, const char *filename) { struct object_id oid; - if (repo_get_oid(the_repository, hex, &oid)) { - cgit_print_error_page(404, "Not Found", "Bad object id: %s", hex); - return 1; - } - if (!lookup_commit_reference(the_repository, &oid)) { - cgit_print_error_page(404, "Not Found", "Not a commit: %s", hex); - return 1; - } + if (!cgit_get_commit(hex, &oid)) + return; // A compressor that cannot run only shows up as a broken pipe once // the tar is under way, so it is looked for while a status can still // say so. - if (format->program && !program_on_path(format->program)) { - cgit_print_error_page(500, "Internal Server Error", "Unable to run %s", format->program); - return 1; + if (format->compressor && !program_on_path(format->compressor[0])) { + cgit_print_error_page(500, "Internal Server Error", "Unable to run %s", + format->compressor[0]); + return; } - ctx.page.mimetype = xstrdup(format->mimetype); - ctx.page.filename = xstrdup(filename); + ctx.page.mimetype = format->mimetype; + ctx.page.filename = filename; cgit_print_http_headers(); init_archivers(); // The archiver reads its arguments as a command line, so it gets the // resolved id and never the name. - format->write_func(oid_to_hex(&oid), prefix); - return 0; + write_snapshot(format, oid_to_hex(&oid), prefix); } -static int send_sig(const struct cgit_snapshot_format *format, const char *hex, +static void send_sig(const struct cgit_snapshot_format *format, const char *hex, const char *archive_name, const char *sig_filename) { const struct object_id *note = cgit_snapshot_get_sig(hex, format); @@ -301,34 +253,33 @@ static int send_sig(const struct cgit_snapshot_format *format, const char *hex, if (!note) { cgit_print_error_page(404, "Not Found", "No signature for %s", archive_name); - return 0; + return; } // A note is any blob a pusher stored, so it is held to the same bound // as every other blob a page inflates. if (odb_read_object_info(the_repository->objects, note, &size) != OBJ_BLOB) { cgit_print_error_page(404, "Not Found", "Not found"); - return 0; + return; } if (cgit_blob_too_large(size)) { cgit_print_blob_too_large(size); - return 0; + return; } buf = odb_read_object(the_repository->objects, note, &type, &size); if (!buf) { cgit_print_error_page(404, "Not Found", "Not found"); - return 0; + return; } ctx.page.untrusted = 1; - ctx.page.mimetype = xstrdup("application/pgp-signature"); - ctx.page.filename = xstrdup(sig_filename); + ctx.page.mimetype = "application/pgp-signature"; + ctx.page.filename = sig_filename; cgit_print_http_headers(); html_raw(buf, size); free(buf); - return 0; } const struct object_id *cgit_snapshot_get_sig(const char *ref, const struct cgit_snapshot_format *f) @@ -401,58 +352,51 @@ void cgit_print_snapshot(const char *head, const char *hex, const char *filename const char *sig_filename = NULL; char *archive_name = NULL; char *prefix = NULL; + char *ref = NULL; + size_t len; if (!filename) { cgit_print_error_page(400, "Bad Request", "No snapshot name specified"); return; } - if (ends_with(filename, SIG_SUFFIX)) { + if (strip_suffix(filename, SIG_SUFFIX, &len)) { sig_filename = filename; - archive_name = xstrdup(filename); - archive_name[strlen(archive_name) - strlen(SIG_SUFFIX)] = '\0'; - filename = archive_name; + filename = archive_name = xstrndup(filename, len); } f = find_format(filename); if (!f || (!sig_filename && !(ctx.repo->snapshots & cgit_snapshot_format_bit(f)))) { cgit_print_error_page(400, "Bad Request", "Unsupported snapshot format: %s", filename); - return; + goto done; } - // The name becomes the prefix of every member of the archive, and a - // revision expression such as :/pattern could carry a path in it. - if (!hex && dwim && strchr(filename, '/')) { - cgit_print_error_page(404, "Not Found", "Not found"); - return; - } - // A name that is all suffix names no revision, though the lookup below - // would settle on a ref called v. - if (!hex && dwim && strlen(filename) == strlen(f->suffix)) { - cgit_print_error_page(404, "Not Found", "Not found"); - return; - } if (!hex && dwim) { - hex = ref_from_filename(ctx.repo, filename, f); + strip_suffix(filename, f->suffix, &len); + prefix = xstrndup(filename, len); + // The stem becomes the prefix of every member of the archive, + // and a revision expression such as :/pattern could carry a path + // in it. An empty stem names no revision, though the lookup + // would settle on a ref called v. + if (len && !strchr(prefix, '/')) + hex = ref = ref_from_filename(ctx.repo, prefix); if (!hex) { cgit_print_error_page(404, "Not Found", "Not found"); - return; + goto done; } - prefix = xstrdup(filename); - prefix[strlen(filename) - strlen(f->suffix)] = '\0'; - } - - if (!hex) - hex = head; - - if (!prefix) + } else { prefix = xstrdup(cgit_snapshot_prefix(ctx.repo)); + if (!hex) + hex = head; + } if (sig_filename) send_sig(f, hex, filename, sig_filename); else send_snapshot(f, hex, prefix, filename); +done: free(prefix); + free(ref); free(archive_name); } |
