Skip to content

Commit b174a3c

Browse files
committed
Merge branch 'hn/allow-bogus-oid-in-ref-tests'
The test helper for refs subsystem learned to write bogus and/or nonexistent object name to refs to simulate error situations we want to test Git in. * hn/allow-bogus-oid-in-ref-tests: t1430: create valid symrefs using test-helper t1430: remove refs using test-tool refs: introduce REF_SKIP_REFNAME_VERIFICATION flag refs: introduce REF_SKIP_OID_VERIFICATION flag refs: update comment. test-ref-store: plug memory leak in cmd_delete_refs test-ref-store: parse symbolic flag constants test-ref-store: remove force-create argument for create-reflog
2 parents bc32aa1 + 9912391 commit b174a3c

File tree

8 files changed

+169
-90
lines changed

8 files changed

+169
-90
lines changed

refs.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,9 +1083,10 @@ int ref_transaction_update(struct ref_transaction *transaction,
10831083
{
10841084
assert(err);
10851085

1086-
if ((new_oid && !is_null_oid(new_oid)) ?
1087-
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
1088-
!refname_is_safe(refname)) {
1086+
if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
1087+
((new_oid && !is_null_oid(new_oid)) ?
1088+
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
1089+
!refname_is_safe(refname))) {
10891090
strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
10901091
refname);
10911092
return -1;

refs.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,12 +637,24 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
637637
*/
638638
#define REF_FORCE_CREATE_REFLOG (1 << 1)
639639

640+
/*
641+
* Blindly write an object_id. This is useful for testing data corruption
642+
* scenarios.
643+
*/
644+
#define REF_SKIP_OID_VERIFICATION (1 << 10)
645+
646+
/*
647+
* Skip verifying refname. This is useful for testing data corruption scenarios.
648+
*/
649+
#define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
650+
640651
/*
641652
* Bitmask of all of the flags that are allowed to be passed in to
642653
* ref_transaction_update() and friends:
643654
*/
644-
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
645-
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG)
655+
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
656+
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
657+
REF_SKIP_REFNAME_VERIFICATION)
646658

647659
/*
648660
* Add a reference update to transaction. `new_oid` is the value that

refs/files-backend.c

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
* This backend uses the following flags in `ref_update::flags` for
1717
* internal bookkeeping purposes. Their numerical values must not
1818
* conflict with REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, REF_HAVE_NEW,
19-
* REF_HAVE_OLD, or REF_IS_PRUNING, which are also stored in
20-
* `ref_update::flags`.
19+
* or REF_HAVE_OLD, which are also stored in `ref_update::flags`.
2120
*/
2221

2322
/*
@@ -1354,7 +1353,8 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
13541353
}
13551354

13561355
static int write_ref_to_lockfile(struct ref_lock *lock,
1357-
const struct object_id *oid, struct strbuf *err);
1356+
const struct object_id *oid,
1357+
int skip_oid_verification, struct strbuf *err);
13581358
static int commit_ref_update(struct files_ref_store *refs,
13591359
struct ref_lock *lock,
13601360
const struct object_id *oid, const char *logmsg,
@@ -1501,7 +1501,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
15011501
}
15021502
oidcpy(&lock->old_oid, &orig_oid);
15031503

1504-
if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
1504+
if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
15051505
commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
15061506
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
15071507
strbuf_release(&err);
@@ -1521,7 +1521,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
15211521

15221522
flag = log_all_ref_updates;
15231523
log_all_ref_updates = LOG_REFS_NONE;
1524-
if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
1524+
if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
15251525
commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
15261526
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
15271527
strbuf_release(&err);
@@ -1756,26 +1756,31 @@ static int files_log_ref_write(struct files_ref_store *refs,
17561756
* errors, rollback the lockfile, fill in *err and return -1.
17571757
*/
17581758
static int write_ref_to_lockfile(struct ref_lock *lock,
1759-
const struct object_id *oid, struct strbuf *err)
1759+
const struct object_id *oid,
1760+
int skip_oid_verification, struct strbuf *err)
17601761
{
17611762
static char term = '\n';
17621763
struct object *o;
17631764
int fd;
17641765

1765-
o = parse_object(the_repository, oid);
1766-
if (!o) {
1767-
strbuf_addf(err,
1768-
"trying to write ref '%s' with nonexistent object %s",
1769-
lock->ref_name, oid_to_hex(oid));
1770-
unlock_ref(lock);
1771-
return -1;
1772-
}
1773-
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
1774-
strbuf_addf(err,
1775-
"trying to write non-commit object %s to branch '%s'",
1776-
oid_to_hex(oid), lock->ref_name);
1777-
unlock_ref(lock);
1778-
return -1;
1766+
if (!skip_oid_verification) {
1767+
o = parse_object(the_repository, oid);
1768+
if (!o) {
1769+
strbuf_addf(
1770+
err,
1771+
"trying to write ref '%s' with nonexistent object %s",
1772+
lock->ref_name, oid_to_hex(oid));
1773+
unlock_ref(lock);
1774+
return -1;
1775+
}
1776+
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
1777+
strbuf_addf(
1778+
err,
1779+
"trying to write non-commit object %s to branch '%s'",
1780+
oid_to_hex(oid), lock->ref_name);
1781+
unlock_ref(lock);
1782+
return -1;
1783+
}
17791784
}
17801785
fd = get_lock_file_fd(&lock->lk);
17811786
if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
@@ -2189,7 +2194,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
21892194
}
21902195

21912196
static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator,
2192-
struct object_id *peeled)
2197+
struct object_id *peeled)
21932198
{
21942199
BUG("ref_iterator_peel() called for reflog_iterator");
21952200
}
@@ -2575,8 +2580,10 @@ static int lock_ref_for_update(struct files_ref_store *refs,
25752580
* The reference already has the desired
25762581
* value, so we don't need to write it.
25772582
*/
2578-
} else if (write_ref_to_lockfile(lock, &update->new_oid,
2579-
err)) {
2583+
} else if (write_ref_to_lockfile(
2584+
lock, &update->new_oid,
2585+
update->flags & REF_SKIP_OID_VERIFICATION,
2586+
err)) {
25802587
char *write_err = strbuf_detach(err, NULL);
25812588

25822589
/*

t/helper/test-ref-store.c

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,59 @@
55
#include "object-store.h"
66
#include "repository.h"
77

8+
struct flag_definition {
9+
const char *name;
10+
uint64_t mask;
11+
};
12+
13+
#define FLAG_DEF(x) \
14+
{ \
15+
#x, (x) \
16+
}
17+
18+
static unsigned int parse_flags(const char *str, struct flag_definition *defs)
19+
{
20+
struct string_list masks = STRING_LIST_INIT_DUP;
21+
int i = 0;
22+
unsigned int result = 0;
23+
24+
if (!strcmp(str, "0"))
25+
return 0;
26+
27+
string_list_split(&masks, str, ',', 64);
28+
for (; i < masks.nr; i++) {
29+
const char *name = masks.items[i].string;
30+
struct flag_definition *def = defs;
31+
int found = 0;
32+
while (def->name) {
33+
if (!strcmp(def->name, name)) {
34+
result |= def->mask;
35+
found = 1;
36+
break;
37+
}
38+
def++;
39+
}
40+
if (!found)
41+
die("unknown flag \"%s\"", name);
42+
}
43+
44+
string_list_clear(&masks, 0);
45+
return result;
46+
}
47+
48+
static struct flag_definition empty_flags[] = { { NULL, 0 } };
49+
850
static const char *notnull(const char *arg, const char *name)
951
{
1052
if (!arg)
1153
die("%s required", name);
1254
return arg;
1355
}
1456

15-
static unsigned int arg_flags(const char *arg, const char *name)
57+
static unsigned int arg_flags(const char *arg, const char *name,
58+
struct flag_definition *defs)
1659
{
17-
return atoi(notnull(arg, name));
60+
return parse_flags(notnull(arg, name), defs);
1861
}
1962

2063
static const char **get_store(const char **argv, struct ref_store **refs)
@@ -64,10 +107,13 @@ static const char **get_store(const char **argv, struct ref_store **refs)
64107
return argv + 1;
65108
}
66109

110+
static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
111+
FLAG_DEF(PACK_REFS_ALL),
112+
{ NULL, 0 } };
67113

68114
static int cmd_pack_refs(struct ref_store *refs, const char **argv)
69115
{
70-
unsigned int flags = arg_flags(*argv++, "flags");
116+
unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
71117

72118
return refs_pack_refs(refs, flags);
73119
}
@@ -81,16 +127,27 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
81127
return refs_create_symref(refs, refname, target, logmsg);
82128
}
83129

130+
static struct flag_definition transaction_flags[] = {
131+
FLAG_DEF(REF_NO_DEREF),
132+
FLAG_DEF(REF_FORCE_CREATE_REFLOG),
133+
FLAG_DEF(REF_SKIP_OID_VERIFICATION),
134+
FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION),
135+
{ NULL, 0 }
136+
};
137+
84138
static int cmd_delete_refs(struct ref_store *refs, const char **argv)
85139
{
86-
unsigned int flags = arg_flags(*argv++, "flags");
140+
unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
87141
const char *msg = *argv++;
88142
struct string_list refnames = STRING_LIST_INIT_NODUP;
143+
int result;
89144

90145
while (*argv)
91146
string_list_append(&refnames, *argv++);
92147

93-
return refs_delete_refs(refs, msg, &refnames, flags);
148+
result = refs_delete_refs(refs, msg, &refnames, flags);
149+
string_list_clear(&refnames, 0);
150+
return result;
94151
}
95152

96153
static int cmd_rename_ref(struct ref_store *refs, const char **argv)
@@ -120,7 +177,7 @@ static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
120177
{
121178
struct object_id oid = *null_oid();
122179
const char *refname = notnull(*argv++, "refname");
123-
int resolve_flags = arg_flags(*argv++, "resolve-flags");
180+
int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags);
124181
int flags;
125182
const char *ref;
126183
int ignore_errno;
@@ -208,7 +265,7 @@ static int cmd_delete_ref(struct ref_store *refs, const char **argv)
208265
const char *msg = notnull(*argv++, "msg");
209266
const char *refname = notnull(*argv++, "refname");
210267
const char *sha1_buf = notnull(*argv++, "old-sha1");
211-
unsigned int flags = arg_flags(*argv++, "flags");
268+
unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
212269
struct object_id old_oid;
213270

214271
if (get_oid_hex(sha1_buf, &old_oid))
@@ -223,7 +280,7 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
223280
const char *refname = notnull(*argv++, "refname");
224281
const char *new_sha1_buf = notnull(*argv++, "new-sha1");
225282
const char *old_sha1_buf = notnull(*argv++, "old-sha1");
226-
unsigned int flags = arg_flags(*argv++, "flags");
283+
unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
227284
struct object_id old_oid;
228285
struct object_id new_oid;
229286

t/t1006-cat-file.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,8 @@ test_expect_success 'the --allow-unknown-type option does not consider replaceme
452452
# Create it manually, as "git replace" will die on bogus
453453
# types.
454454
head=$(git rev-parse --verify HEAD) &&
455-
test_when_finished "rm -rf .git/refs/replace" &&
456-
mkdir -p .git/refs/replace &&
457-
echo $head >.git/refs/replace/$bogus_short_sha1 &&
455+
test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
456+
test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
458457
459458
cat >expect <<-EOF &&
460459
commit

t/t1405-main-ref-store.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ test_expect_success 'setup' '
1717
test_expect_success REFFILES 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
1818
N=`find .git/refs -type f | wc -l` &&
1919
test "$N" != 0 &&
20-
ALL_OR_PRUNE_FLAG=3 &&
21-
$RUN pack-refs ${ALL_OR_PRUNE_FLAG} &&
20+
$RUN pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
2221
N=`find .git/refs -type f` &&
2322
test -z "$N"
2423
'
@@ -35,8 +34,7 @@ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
3534
git rev-parse FOO -- &&
3635
git rev-parse refs/tags/new-tag -- &&
3736
m=$(git rev-parse main) &&
38-
REF_NO_DEREF=1 &&
39-
$RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
37+
$RUN delete-refs REF_NO_DEREF nothing FOO refs/tags/new-tag &&
4038
test_must_fail git rev-parse --symbolic-full-name FOO &&
4139
test_must_fail git rev-parse FOO -- &&
4240
test_must_fail git rev-parse refs/tags/new-tag --

0 commit comments

Comments
 (0)