Skip to content

Commit b41860b

Browse files
Martin Koeglergitster
authored andcommitted
unpack-objects: prevent writing of inconsistent objects
This patch introduces a strict mode, which ensures that: - no malformed object will be written - no object with broken links will be written The patch ensures this by delaying the write of all non blob object. These object are written, after all objects they link to are written. An error can only result in unreferenced objects. Signed-off-by: Martin Koegler <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 79b1138 commit b41860b

File tree

2 files changed

+106
-7
lines changed

2 files changed

+106
-7
lines changed

Documentation/git-unpack-objects.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ OPTIONS
4040
and make the best effort to recover as many objects as
4141
possible.
4242

43+
--strict::
44+
Don't write objects with broken content or links.
45+
4346

4447
Author
4548
------

builtin-unpack-objects.c

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
#include "commit.h"
88
#include "tag.h"
99
#include "tree.h"
10+
#include "tree-walk.h"
1011
#include "progress.h"
1112
#include "decorate.h"
13+
#include "fsck.h"
1214

13-
static int dry_run, quiet, recover, has_errors;
14-
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
15+
static int dry_run, quiet, recover, has_errors, strict;
16+
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
1517

1618
/* We always read in 4kB chunks. */
1719
static unsigned char buffer[4096];
@@ -31,6 +33,16 @@ static struct obj_buffer *lookup_object_buffer(struct object *base)
3133
return lookup_decoration(&obj_decorate, base);
3234
}
3335

36+
static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
37+
{
38+
struct obj_buffer *obj;
39+
obj = xcalloc(1, sizeof(struct obj_buffer));
40+
obj->buffer = buffer;
41+
obj->size = size;
42+
if (add_decoration(&obj_decorate, object, obj))
43+
die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
44+
}
45+
3446
/*
3547
* Make sure at least "min" bytes are available in the buffer, and
3648
* return the pointer to the buffer.
@@ -134,19 +146,95 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
134146
struct obj_info {
135147
off_t offset;
136148
unsigned char sha1[20];
149+
struct object *obj;
137150
};
138151

152+
#define FLAG_OPEN (1u<<20)
153+
#define FLAG_WRITTEN (1u<<21)
154+
139155
static struct obj_info *obj_list;
156+
unsigned nr_objects;
157+
158+
static void write_cached_object(struct object *obj)
159+
{
160+
unsigned char sha1[20];
161+
struct obj_buffer *obj_buf = lookup_object_buffer(obj);
162+
if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
163+
die("failed to write object %s", sha1_to_hex(obj->sha1));
164+
obj->flags |= FLAG_WRITTEN;
165+
}
166+
167+
static int check_object(struct object *obj, int type, void *data)
168+
{
169+
if (!obj)
170+
return 0;
171+
172+
if (obj->flags & FLAG_WRITTEN)
173+
return 1;
174+
175+
if (type != OBJ_ANY && obj->type != type)
176+
die("object type mismatch");
177+
178+
if (!(obj->flags & FLAG_OPEN)) {
179+
unsigned long size;
180+
int type = sha1_object_info(obj->sha1, &size);
181+
if (type != obj->type || type <= 0)
182+
die("object of unexpected type");
183+
obj->flags |= FLAG_WRITTEN;
184+
return 1;
185+
}
186+
187+
if (fsck_object(obj, 1, fsck_error_function))
188+
die("Error in object");
189+
if (!fsck_walk(obj, check_object, 0))
190+
die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
191+
write_cached_object(obj);
192+
return 1;
193+
}
194+
195+
static void write_rest(void)
196+
{
197+
unsigned i;
198+
for (i = 0; i < nr_objects; i++)
199+
check_object(obj_list[i].obj, OBJ_ANY, 0);
200+
}
140201

141202
static void added_object(unsigned nr, enum object_type type,
142203
void *data, unsigned long size);
143204

144205
static void write_object(unsigned nr, enum object_type type,
145206
void *buf, unsigned long size)
146207
{
147-
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
148-
die("failed to write object");
149208
added_object(nr, type, buf, size);
209+
if (!strict) {
210+
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
211+
die("failed to write object");
212+
free(buf);
213+
obj_list[nr].obj = 0;
214+
} else if (type == OBJ_BLOB) {
215+
struct blob *blob;
216+
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
217+
die("failed to write object");
218+
free(buf);
219+
220+
blob = lookup_blob(obj_list[nr].sha1);
221+
if (blob)
222+
blob->object.flags |= FLAG_WRITTEN;
223+
else
224+
die("invalid blob object");
225+
obj_list[nr].obj = 0;
226+
} else {
227+
struct object *obj;
228+
int eaten;
229+
hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
230+
obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
231+
if (!obj)
232+
die("invalid %s", typename(type));
233+
/* buf is stored via add_object_buffer and in obj, if its a tree or commit */
234+
add_object_buffer(obj, buf, size);
235+
obj->flags |= FLAG_OPEN;
236+
obj_list[nr].obj = obj;
237+
}
150238
}
151239

152240
static void resolve_delta(unsigned nr, enum object_type type,
@@ -163,7 +251,6 @@ static void resolve_delta(unsigned nr, enum object_type type,
163251
die("failed to apply delta");
164252
free(delta);
165253
write_object(nr, type, result, result_size);
166-
free(result);
167254
}
168255

169256
static void added_object(unsigned nr, enum object_type type,
@@ -193,7 +280,8 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
193280

194281
if (!dry_run && buf)
195282
write_object(nr, type, buf, size);
196-
free(buf);
283+
else
284+
free(buf);
197285
}
198286

199287
static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
@@ -336,7 +424,8 @@ static void unpack_all(void)
336424
int i;
337425
struct progress *progress = NULL;
338426
struct pack_header *hdr = fill(sizeof(struct pack_header));
339-
unsigned nr_objects = ntohl(hdr->hdr_entries);
427+
428+
nr_objects = ntohl(hdr->hdr_entries);
340429

341430
if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
342431
die("bad pack file");
@@ -347,6 +436,7 @@ static void unpack_all(void)
347436
if (!quiet)
348437
progress = start_progress("Unpacking objects", nr_objects);
349438
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
439+
memset(obj_list, 0, nr_objects * sizeof(*obj_list));
350440
for (i = 0; i < nr_objects; i++) {
351441
unpack_one(i);
352442
display_progress(progress, i + 1);
@@ -382,6 +472,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
382472
recover = 1;
383473
continue;
384474
}
475+
if (!strcmp(arg, "--strict")) {
476+
strict = 1;
477+
continue;
478+
}
385479
if (!prefixcmp(arg, "--pack_header=")) {
386480
struct pack_header *hdr;
387481
char *c;
@@ -407,6 +501,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
407501
unpack_all();
408502
SHA1_Update(&ctx, buffer, offset);
409503
SHA1_Final(sha1, &ctx);
504+
if (strict)
505+
write_rest();
410506
if (hashcmp(fill(20), sha1))
411507
die("final sha1 did not match");
412508
use(20);

0 commit comments

Comments
 (0)