Am 28.05.19 um 21:08 schrieb Jeff King:
> On Tue, May 28, 2019 at 08:01:43PM +0200, René Scharfe wrote:
>
>> Am 28.05.19 um 07:58 schrieb Jeff King:
>>> On Sat, May 25, 2019 at 03:26:53PM +0200, René Scharfe wrote:
>>>
>>>> We could
>>>> make git fsck report such symlinks.
>>>
>>> This is a little tricky, because fsck generally looks at individual
>>> objects, and the bad pattern is a combination of a tree and a blob
>>> together. I think you could make it work by reusing some of the code and
>>> patterns from 9e84a6d758 (Merge branch 'jk/submodule-fsck-loose' into
>>> maint, 2018-05-22).
>>
>> Actually it's super easy, barely an inconvenience (SCNR, watched a lot
>> of those rants recently).. Did I miss something?
>
> Yes. You cannot rely on calling read_object_file() in real-time when the
> fsck is being done by index-pack. The blob in question may be in the
> pack you are indexing.
It figures.
So something like the patch below?
Parsing trees with symlinks twice is not ideal, but keeps the set
structure simple -- a standard oidset suffices.
The global variables are ugly. Moving them into struct fsck_option
would be possible, but not much better, as they aren't really
options.
FSCK_MSG_MISSING_TREE_OBJECT has never been used before, it seems.
---
fsck.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
t/t1450-fsck.sh | 13 +++++++++++
2 files changed, 71 insertions(+)
diff --git a/fsck.c b/fsck.c
index 4703f55561..a6e7d0b03f 100644
--- a/fsck.c
+++ b/fsck.c
@@ -19,6 +19,7 @@
static struct oidset gitmodules_found = OIDSET_INIT;
static struct oidset gitmodules_done = OIDSET_INIT;
+static struct oidset trees_with_symlinks = OIDSET_INIT;
#define FSCK_FATAL -1
#define FSCK_INFO -2
@@ -49,6 +50,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(MISSING_PARENT, ERROR) \
FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \
FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \
+ FUNC(MISSING_SYMLINK_OBJECT, ERROR) \
FUNC(MISSING_TAG, ERROR) \
FUNC(MISSING_TAG_ENTRY, ERROR) \
FUNC(MISSING_TAG_OBJECT, ERROR) \
@@ -58,6 +60,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(MISSING_TYPE_ENTRY, ERROR) \
FUNC(MULTIPLE_AUTHORS, ERROR) \
FUNC(TAG_OBJECT_NOT_TAG, ERROR) \
+ FUNC(SYMLINK_OBJECT_NOT_BLOB, ERROR) \
FUNC(TREE_NOT_SORTED, ERROR) \
FUNC(UNKNOWN_TYPE, ERROR) \
FUNC(ZERO_PADDED_DATE, ERROR) \
@@ -78,6 +81,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(NULL_SHA1, WARN) \
FUNC(ZERO_PADDED_FILEMODE, WARN) \
FUNC(NUL_IN_COMMIT, WARN) \
+ FUNC(NUL_IN_SYMLINK_TARGET, WARN) \
/* infos (reported as warnings, but ignored by default) */ \
FUNC(GITMODULES_PARSE, INFO) \
FUNC(BAD_TAG_NAME, INFO) \
@@ -578,6 +582,33 @@ static int verify_ordered(unsigned mode1, const char
*name1, unsigned mode2, con
return c1 < c2 ? 0 : TREE_UNORDERED;
}
+static int fsck_symlink(struct tree *tree, const char *name,
+ const struct object_id *oid,
+ struct fsck_options *options)
+{
+ int ret = 0;
+ enum object_type type;
+ unsigned long size;
+ void *buffer = read_object_file(oid, &type, &size);
+
+ if (!buffer)
+ ret = report(options, &tree->object,
+ FSCK_MSG_MISSING_SYMLINK_OBJECT,
+ "cannot read blob object for symlink %s", name);
+ else if (type != OBJ_BLOB)
+ ret = report(options, &tree->object,
+ FSCK_MSG_SYMLINK_OBJECT_NOT_BLOB,
+ "expected blob got %s for symlink %s",
+ type_name(type), name);
+ else if (memchr(buffer, '\0', size))
+ ret = report(options, &tree->object,
+ FSCK_MSG_NUL_IN_SYMLINK_TARGET,
+ "NUL in target of symlink %s", name);
+
+ free(buffer);
+ return ret;
+}
+
static int fsck_tree(struct tree *item, struct fsck_options *options)
{
int retval = 0;
@@ -626,6 +657,8 @@ static int fsck_tree(struct tree *item, struct fsck_options
*options)
FSCK_MSG_GITMODULES_SYMLINK,
".gitmodules is a symbolic
link");
}
+ if (S_ISLNK(mode))
+ oidset_insert(&trees_with_symlinks, &item->object.oid);
if (update_tree_entry_gently(&desc)) {
retval += report(options, &item->object,
FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
@@ -1118,8 +1151,33 @@ int fsck_finish(struct fsck_options *options)
free(buf);
}
+ oidset_iter_init(&trees_with_symlinks, &iter);
+ while ((oid = oidset_iter_next(&iter))) {
+ struct tree *tree;
+ struct tree_desc desc;
+ struct name_entry entry;
+
+ tree = lookup_tree(the_repository, oid);
+ if (!tree) {
+ struct object *obj = lookup_unknown_object(oid->hash);
+ ret |= report(options, obj,
+ FSCK_MSG_MISSING_TREE_OBJECT,
+ "tree %s not found", oid_to_hex(oid));
+ continue;
+ }
+ if (parse_tree(tree))
+ continue;
+ if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
+ continue;
+ while (tree_entry_gently(&desc, &entry)) {
+ if (S_ISLNK(entry.mode))
+ ret |= fsck_symlink(tree, entry.path,
+ &entry.oid, options);
+ }
+ }
oidset_clear(&gitmodules_found);
oidset_clear(&gitmodules_done);
+ oidset_clear(&trees_with_symlinks);
return ret;
}
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 0f268a3664..ce9501d063 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -822,4 +822,17 @@ test_expect_success 'detect corrupt index file in fsck' '
test_i18ngrep "bad index file" errors
'
+test_expect_success 'detect NUL in symlink target' '
+ test_when_finished "git update-ref -d refs/heads/nul_in_symlink" &&
+ test_when_finished "remove_object \$commit" &&
+ test_when_finished "remove_object \$tree" &&
+ test_when_finished "remove_object \$blob" &&
+ blob=$(echo fooQbar | q_to_nul | git hash-object -w --stdin) &&
+ tree=$(echo "120000 blob $blob symlink" | git mktree) &&
+ commit=$(git commit-tree $tree) &&
+ git update-ref refs/heads/nul_in_symlink $commit &&
+ git fsck 2>out &&
+ test_i18ngrep "NUL in target of symlink" out
+'
+
test_done
--
2.21.0