Diff options
Diffstat (limited to '')
-rw-r--r--source/ui-snapshot.c206
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);
}