diff options
context:
space:
mode:
Diffstat (limited to 'source/ui-snapshot.c')
-rw-r--r--source/ui-snapshot.c71
1 file changed, 60 insertions, 11 deletions
diff --git a/source/ui-snapshot.c b/source/ui-snapshot.c
index a9bf858..5f2d69d 100644
--- a/source/ui-snapshot.c
+++ b/source/ui-snapshot.c
@@ -15,6 +15,7 @@
#include "cgit.h"
#include "filter.h"
#include "html.h"
+#include "shared.h"
#include "ui-shared.h"
#include "ui-snapshot.h"
@@ -61,6 +62,31 @@ static int write_zip_archive(const char *hex, const char *prefix)
return write_archive_format("--format=zip", hex, prefix);
}
+static int program_on_path(const char *program)
+{
+ const char *path = getenv("PATH");
+ struct strbuf full = STRBUF_INIT;
+ int found = 0;
+
+ if (!path)
+ return 0;
+ while (!found) {
+ const char *end = strchrnul(path, ':');
+
+ strbuf_reset(&full);
+ strbuf_add(&full, path, end - path);
+ if (!full.len)
+ strbuf_addch(&full, '.');
+ strbuf_addf(&full, "/%s", program);
+ found = !access(full.buf, X_OK);
+ if (!*end)
+ break;
+ path = end + 1;
+ }
+ strbuf_release(&full);
+ return found;
+}
+
static int write_compressed_tar_archive(const char *hex, const char *prefix, char *argv[])
{
struct cgit_exec_filter filter;
@@ -76,24 +102,28 @@ static int write_compressed_tar_archive(const char *hex, const char *prefix, cha
static int write_tar_gzip_archive(const char *hex, const char *prefix)
{
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)
{
char *argv[] = { "bzip2", NULL };
+
return write_compressed_tar_archive(hex, prefix, argv);
}
static int write_tar_lzip_archive(const char *hex, const char *prefix)
{
char *argv[] = { "lzip", NULL };
+
return write_compressed_tar_archive(hex, prefix, argv);
}
static int write_tar_xz_archive(const char *hex, const char *prefix)
{
char *argv[] = { "xz", NULL };
+
return write_compressed_tar_archive(hex, prefix, argv);
}
@@ -102,20 +132,21 @@ 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.
char *argv[] = { "zstd", NULL };
+
return write_compressed_tar_archive(hex, prefix, argv);
}
// 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 },
- { ".tar.gz", "application/gzip", write_tar_gzip_archive },
- { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive },
- { ".tar.lz", "application/x-lzip", write_tar_lzip_archive },
- { ".tar.xz", "application/x-xz", write_tar_xz_archive },
- { ".tar.zst", "application/zstd", write_tar_zstd_archive },
- { ".zip", "application/zip", write_zip_archive },
- { NULL }
+ { ".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 },
+ { 0 }
};
// Each tree is read the first time a signature for that format is asked for,
@@ -142,7 +173,10 @@ static int resolves(const char *rev)
{
struct object_id oid;
- return repo_get_oid(the_repository, rev, &oid) == 0;
+ // The name goes no further than this lookup, so a leading dash is
+ // fine here, while the wider syntax is refused as in cgit_valid_rev.
+ return !check_refname_format(rev, REFNAME_ALLOW_ONELEVEL) &&
+ !repo_get_oid(the_repository, rev, &oid);
}
static const char *ref_from_filename(const struct cgit_repo *repo, const char *filename,
@@ -197,14 +231,23 @@ static int send_snapshot(const struct cgit_snapshot_format *format, const char *
return 1;
}
if (!lookup_commit_reference(the_repository, &oid)) {
- cgit_print_error_page(400, "Bad Request", "Not a commit reference: %s", hex);
+ cgit_print_error_page(404, "Not Found", "Not a commit: %s", hex);
+ return 1;
+ }
+ // 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;
}
ctx.page.mimetype = xstrdup(format->mimetype);
ctx.page.filename = xstrdup(filename);
cgit_print_http_headers();
init_archivers();
- format->write_func(hex, prefix);
+ // 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;
}
@@ -321,6 +364,12 @@ void cgit_print_snapshot(const char *head, const char *hex, const char *filename
return;
}
+ // 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;
+ }
if (!hex && dwim) {
hex = ref_from_filename(ctx.repo, filename, f);
if (!hex) {