Commit 7f897b6f authored by Jeff King's avatar Jeff King Committed by Junio C Hamano

avoid using fixed PATH_MAX buffers for refs

Many functions which handle refs use a PATH_MAX-sized buffer
to do so. This is mostly reasonable as we have to write
loose refs into the filesystem, and at least on Linux the 4K
PATH_MAX is big enough that nobody would care. But:

  1. The static PATH_MAX is not always the filesystem limit.

  2. On other platforms, PATH_MAX may be much smaller.

  3. As we move to alternate ref storage, we won't be bound
     by filesystem limits.

Let's convert these to heap buffers so we don't have to
worry about truncation or size limits.

We may want to eventually constrain ref lengths for sanity
and to prevent malicious names, but we should do so
consistently across all platforms, and in a central place
(like the ref code).
Signed-off-by: default avatarJeff King <>
parent 1412f762
......@@ -908,11 +908,10 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
static const char *unique_tracking_name(const char *name, struct object_id *oid)
struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
char src_ref[PATH_MAX];
snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
cb_data.src_ref = src_ref;
cb_data.src_ref = xstrfmt("refs/heads/%s", name);
cb_data.dst_oid = oid;
for_each_remote(check_tracking_name, &cb_data);
if (cb_data.unique)
return cb_data.dst_ref;
......@@ -17,17 +17,19 @@ static const char * const ls_remote_usage[] = {
static int tail_match(const char **pattern, const char *path)
const char *p;
char pathbuf[PATH_MAX];
char *pathbuf;
if (!pattern)
return 1; /* no restriction */
if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
return error("insanely long ref %.*s...", 20, path);
pathbuf = xstrfmt("/%s", path);
while ((p = *(pattern++)) != NULL) {
if (!wildmatch(p, pathbuf, 0, NULL))
if (!wildmatch(p, pathbuf, 0, NULL)) {
return 1;
return 0;
......@@ -93,26 +93,31 @@ typedef int (*each_replace_name_fn)(const char *name, const char *ref,
static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
const char **p, *full_hex;
char ref[PATH_MAX];
struct strbuf ref = STRBUF_INIT;
size_t base_len;
int had_error = 0;
struct object_id oid;
strbuf_addstr(&ref, git_replace_ref_base);
base_len = ref.len;
for (p = argv; *p; p++) {
if (get_oid(*p, &oid)) {
error("Failed to resolve '%s' as a valid ref.", *p);
had_error = 1;
full_hex = oid_to_hex(&oid);
snprintf(ref, sizeof(ref), "%s%s", git_replace_ref_base, full_hex);
/* read_ref() may reuse the buffer */
full_hex = ref + strlen(git_replace_ref_base);
if (read_ref(ref, oid.hash)) {
strbuf_setlen(&ref, base_len);
strbuf_addstr(&ref, oid_to_hex(&oid));
full_hex = ref.buf + base_len;
if (read_ref(ref.buf, oid.hash)) {
error("replace ref '%s' not found.", full_hex);
had_error = 1;
if (fn(full_hex, ref, &oid))
if (fn(full_hex, ref.buf, &oid))
had_error = 1;
return had_error;
......@@ -129,21 +134,18 @@ static int delete_replace_ref(const char *name, const char *ref,
static void check_ref_valid(struct object_id *object,
struct object_id *prev,
char *ref,
int ref_size,
struct strbuf *ref,
int force)
if (snprintf(ref, ref_size,
"%s%s", git_replace_ref_base,
oid_to_hex(object)) > ref_size - 1)
die("replace ref name too long: %.*s...", 50, ref);
if (check_refname_format(ref, 0))
die("'%s' is not a valid ref name.", ref);
if (read_ref(ref, prev->hash))
strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object));
if (check_refname_format(ref->buf, 0))
die("'%s' is not a valid ref name.", ref->buf);
if (read_ref(ref->buf, prev->hash))
else if (!force)
die("replace ref '%s' already exists", ref);
die("replace ref '%s' already exists", ref->buf);
static int replace_object_oid(const char *object_ref,
......@@ -154,7 +156,7 @@ static int replace_object_oid(const char *object_ref,
struct object_id prev;
enum object_type obj_type, repl_type;
char ref[PATH_MAX];
struct strbuf ref = STRBUF_INIT;
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
......@@ -167,16 +169,17 @@ static int replace_object_oid(const char *object_ref,
object_ref, typename(obj_type),
replace_ref, typename(repl_type));
check_ref_valid(object, &prev, ref, sizeof(ref), force);
check_ref_valid(object, &prev, &ref, force);
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref, repl->hash, prev.hash,
ref_transaction_update(transaction, ref.buf, repl->hash, prev.hash,
0, NULL, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
return 0;
......@@ -280,7 +283,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
enum object_type type;
struct object_id old, new, prev;
char ref[PATH_MAX];
struct strbuf ref = STRBUF_INIT;
if (get_oid(object_ref, &old) < 0)
die("Not a valid object name: '%s'", object_ref);
......@@ -289,7 +292,8 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
if (type < 0)
die("unable to get object type for %s", oid_to_hex(&old));
check_ref_valid(&old, &prev, ref, sizeof(ref), force);
check_ref_valid(&old, &prev, &ref, force);
export_object(&old, type, raw, tmpfile);
if (launch_editor(tmpfile, NULL, NULL) < 0)
......@@ -72,25 +72,22 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
const void *cb_data)
const char **p;
char ref[PATH_MAX];
struct strbuf ref = STRBUF_INIT;
int had_error = 0;
unsigned char sha1[20];
for (p = argv; *p; p++) {
if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
>= sizeof(ref)) {
error(_("tag name too long: %.*s..."), 50, *p);
had_error = 1;
if (read_ref(ref, sha1)) {
strbuf_addf(&ref, "refs/tags/%s", *p);
if (read_ref(ref.buf, sha1)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
if (fn(*p, ref, sha1, cb_data))
if (fn(*p, ref.buf, sha1, cb_data))
had_error = 1;
return had_error;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment