Skip to content

Commit 3c993de

Browse files
committed
Merge branch 'mk/unpack-careful'
* mk/unpack-careful: t5300: add test for "index-pack --strict" receive-pack: allow using --strict mode for unpacking objects unpack-objects: fix --strict handling t5300: add test for "unpack-objects --strict" unpack-objects: prevent writing of inconsistent objects
2 parents 769f60a + 38a5739 commit 3c993de

File tree

5 files changed

+284
-33
lines changed

5 files changed

+284
-33
lines changed

Documentation/config.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,12 @@ imap::
991991
The configuration variables in the 'imap' section are described
992992
in linkgit:git-imap-send[1].
993993

994+
receive.fsckObjects::
995+
If it is set to true, git-receive-pack will check all received
996+
objects. It will abort in the case of a malformed object or a
997+
broken link. The result of an abort are only dangling objects.
998+
Defaults to false.
999+
9941000
receive.unpackLimit::
9951001
If the number of objects received in a push is below this
9961002
limit then the objects will be unpacked into loose object

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: 157 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,25 @@
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];
1820
static unsigned int offset, len;
1921
static off_t consumed_bytes;
2022
static SHA_CTX ctx;
2123

24+
/*
25+
* When running under --strict mode, objects whose reachability are
26+
* suspect are kept in core without getting written in the object
27+
* store.
28+
*/
2229
struct obj_buffer {
2330
char *buffer;
2431
unsigned long size;
@@ -31,6 +38,16 @@ static struct obj_buffer *lookup_object_buffer(struct object *base)
3138
return lookup_decoration(&obj_decorate, base);
3239
}
3340

41+
static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
42+
{
43+
struct obj_buffer *obj;
44+
obj = xcalloc(1, sizeof(struct obj_buffer));
45+
obj->buffer = buffer;
46+
obj->size = size;
47+
if (add_decoration(&obj_decorate, object, obj))
48+
die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
49+
}
50+
3451
/*
3552
* Make sure at least "min" bytes are available in the buffer, and
3653
* return the pointer to the buffer.
@@ -134,19 +151,110 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
134151
struct obj_info {
135152
off_t offset;
136153
unsigned char sha1[20];
154+
struct object *obj;
137155
};
138156

157+
#define FLAG_OPEN (1u<<20)
158+
#define FLAG_WRITTEN (1u<<21)
159+
139160
static struct obj_info *obj_list;
161+
unsigned nr_objects;
162+
163+
/*
164+
* Called only from check_object() after it verified this object
165+
* is Ok.
166+
*/
167+
static void write_cached_object(struct object *obj)
168+
{
169+
unsigned char sha1[20];
170+
struct obj_buffer *obj_buf = lookup_object_buffer(obj);
171+
if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
172+
die("failed to write object %s", sha1_to_hex(obj->sha1));
173+
obj->flags |= FLAG_WRITTEN;
174+
}
175+
176+
/*
177+
* At the very end of the processing, write_rest() scans the objects
178+
* that have reachability requirements and calls this function.
179+
* Verify its reachability and validity recursively and write it out.
180+
*/
181+
static int check_object(struct object *obj, int type, void *data)
182+
{
183+
if (!obj)
184+
return 0;
185+
186+
if (obj->flags & FLAG_WRITTEN)
187+
return 1;
188+
189+
if (type != OBJ_ANY && obj->type != type)
190+
die("object type mismatch");
191+
192+
if (!(obj->flags & FLAG_OPEN)) {
193+
unsigned long size;
194+
int type = sha1_object_info(obj->sha1, &size);
195+
if (type != obj->type || type <= 0)
196+
die("object of unexpected type");
197+
obj->flags |= FLAG_WRITTEN;
198+
return 1;
199+
}
200+
201+
if (fsck_object(obj, 1, fsck_error_function))
202+
die("Error in object");
203+
if (!fsck_walk(obj, check_object, 0))
204+
die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
205+
write_cached_object(obj);
206+
return 1;
207+
}
208+
209+
static void write_rest(void)
210+
{
211+
unsigned i;
212+
for (i = 0; i < nr_objects; i++)
213+
check_object(obj_list[i].obj, OBJ_ANY, 0);
214+
}
140215

141216
static void added_object(unsigned nr, enum object_type type,
142217
void *data, unsigned long size);
143218

219+
/*
220+
* Write out nr-th object from the list, now we know the contents
221+
* of it. Under --strict, this buffers structured objects in-core,
222+
* to be checked at the end.
223+
*/
144224
static void write_object(unsigned nr, enum object_type type,
145225
void *buf, unsigned long size)
146226
{
147-
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
148-
die("failed to write object");
149-
added_object(nr, type, buf, size);
227+
if (!strict) {
228+
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
229+
die("failed to write object");
230+
added_object(nr, type, buf, size);
231+
free(buf);
232+
obj_list[nr].obj = NULL;
233+
} else if (type == OBJ_BLOB) {
234+
struct blob *blob;
235+
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
236+
die("failed to write object");
237+
added_object(nr, type, buf, size);
238+
free(buf);
239+
240+
blob = lookup_blob(obj_list[nr].sha1);
241+
if (blob)
242+
blob->object.flags |= FLAG_WRITTEN;
243+
else
244+
die("invalid blob object");
245+
obj_list[nr].obj = NULL;
246+
} else {
247+
struct object *obj;
248+
int eaten;
249+
hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
250+
added_object(nr, type, buf, size);
251+
obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
252+
if (!obj)
253+
die("invalid %s", typename(type));
254+
add_object_buffer(obj, buf, size);
255+
obj->flags |= FLAG_OPEN;
256+
obj_list[nr].obj = obj;
257+
}
150258
}
151259

152260
static void resolve_delta(unsigned nr, enum object_type type,
@@ -163,9 +271,12 @@ static void resolve_delta(unsigned nr, enum object_type type,
163271
die("failed to apply delta");
164272
free(delta);
165273
write_object(nr, type, result, result_size);
166-
free(result);
167274
}
168275

276+
/*
277+
* We now know the contents of an object (which is nr-th in the pack);
278+
* resolve all the deltified objects that are based on it.
279+
*/
169280
static void added_object(unsigned nr, enum object_type type,
170281
void *data, unsigned long size)
171282
{
@@ -193,7 +304,24 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
193304

194305
if (!dry_run && buf)
195306
write_object(nr, type, buf, size);
196-
free(buf);
307+
else
308+
free(buf);
309+
}
310+
311+
static int resolve_against_held(unsigned nr, const unsigned char *base,
312+
void *delta_data, unsigned long delta_size)
313+
{
314+
struct object *obj;
315+
struct obj_buffer *obj_buffer;
316+
obj = lookup_object(base);
317+
if (!obj)
318+
return 0;
319+
obj_buffer = lookup_object_buffer(obj);
320+
if (!obj_buffer)
321+
return 0;
322+
resolve_delta(nr, obj->type, obj_buffer->buffer,
323+
obj_buffer->size, delta_data, delta_size);
324+
return 1;
197325
}
198326

199327
static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
@@ -202,7 +330,6 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
202330
void *delta_data, *base;
203331
unsigned long base_size;
204332
unsigned char base_sha1[20];
205-
struct object *obj;
206333

207334
if (type == OBJ_REF_DELTA) {
208335
hashcpy(base_sha1, fill(20));
@@ -212,7 +339,13 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
212339
free(delta_data);
213340
return;
214341
}
215-
if (!has_sha1_file(base_sha1)) {
342+
if (has_sha1_file(base_sha1))
343+
; /* Ok we have this one */
344+
else if (resolve_against_held(nr, base_sha1,
345+
delta_data, delta_size))
346+
return; /* we are done */
347+
else {
348+
/* cannot resolve yet --- queue it */
216349
hashcpy(obj_list[nr].sha1, null_sha1);
217350
add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
218351
return;
@@ -258,22 +391,18 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
258391
}
259392
}
260393
if (!base_found) {
261-
/* The delta base object is itself a delta that
262-
has not been resolved yet. */
394+
/*
395+
* The delta base object is itself a delta that
396+
* has not been resolved yet.
397+
*/
263398
hashcpy(obj_list[nr].sha1, null_sha1);
264399
add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
265400
return;
266401
}
267402
}
268403

269-
obj = lookup_object(base_sha1);
270-
if (obj) {
271-
struct obj_buffer *obj_buf = lookup_object_buffer(obj);
272-
if (obj_buf) {
273-
resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
274-
return;
275-
}
276-
}
404+
if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
405+
return;
277406

278407
base = read_sha1_file(base_sha1, &type, &base_size);
279408
if (!base) {
@@ -336,7 +465,8 @@ static void unpack_all(void)
336465
int i;
337466
struct progress *progress = NULL;
338467
struct pack_header *hdr = fill(sizeof(struct pack_header));
339-
unsigned nr_objects = ntohl(hdr->hdr_entries);
468+
469+
nr_objects = ntohl(hdr->hdr_entries);
340470

341471
if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
342472
die("bad pack file");
@@ -347,6 +477,7 @@ static void unpack_all(void)
347477
if (!quiet)
348478
progress = start_progress("Unpacking objects", nr_objects);
349479
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
480+
memset(obj_list, 0, nr_objects * sizeof(*obj_list));
350481
for (i = 0; i < nr_objects; i++) {
351482
unpack_one(i);
352483
display_progress(progress, i + 1);
@@ -382,6 +513,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
382513
recover = 1;
383514
continue;
384515
}
516+
if (!strcmp(arg, "--strict")) {
517+
strict = 1;
518+
continue;
519+
}
385520
if (!prefixcmp(arg, "--pack_header=")) {
386521
struct pack_header *hdr;
387522
char *c;
@@ -407,6 +542,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
407542
unpack_all();
408543
SHA1_Update(&ctx, buffer, offset);
409544
SHA1_Final(sha1, &ctx);
545+
if (strict)
546+
write_rest();
410547
if (hashcmp(fill(20), sha1))
411548
die("final sha1 did not match");
412549
use(20);

receive-pack.c

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
1111

1212
static int deny_non_fast_forwards = 0;
13+
static int receive_fsck_objects;
1314
static int receive_unpack_limit = -1;
1415
static int transfer_unpack_limit = -1;
1516
static int unpack_limit = 100;
@@ -35,6 +36,11 @@ static int receive_pack_config(const char *var, const char *value)
3536
return 0;
3637
}
3738

39+
if (strcmp(var, "receive.fsckobjects") == 0) {
40+
receive_fsck_objects = git_config_bool(var, value);
41+
return 0;
42+
}
43+
3844
return git_default_config(var, value);
3945
}
4046

@@ -368,11 +374,13 @@ static const char *unpack(void)
368374
ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
369375

370376
if (ntohl(hdr.hdr_entries) < unpack_limit) {
371-
int code;
372-
const char *unpacker[3];
373-
unpacker[0] = "unpack-objects";
374-
unpacker[1] = hdr_arg;
375-
unpacker[2] = NULL;
377+
int code, i = 0;
378+
const char *unpacker[4];
379+
unpacker[i++] = "unpack-objects";
380+
if (receive_fsck_objects)
381+
unpacker[i++] = "--strict";
382+
unpacker[i++] = hdr_arg;
383+
unpacker[i++] = NULL;
376384
code = run_command_v_opt(unpacker, RUN_GIT_CMD);
377385
switch (code) {
378386
case 0:
@@ -393,21 +401,23 @@ static const char *unpack(void)
393401
return "unpacker exited with error code";
394402
}
395403
} else {
396-
const char *keeper[6];
397-
int s, status;
404+
const char *keeper[7];
405+
int s, status, i = 0;
398406
char keep_arg[256];
399407
struct child_process ip;
400408

401409
s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
402410
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
403411
strcpy(keep_arg + s, "localhost");
404412

405-
keeper[0] = "index-pack";
406-
keeper[1] = "--stdin";
407-
keeper[2] = "--fix-thin";
408-
keeper[3] = hdr_arg;
409-
keeper[4] = keep_arg;
410-
keeper[5] = NULL;
413+
keeper[i++] = "index-pack";
414+
keeper[i++] = "--stdin";
415+
if (receive_fsck_objects)
416+
keeper[i++] = "--strict";
417+
keeper[i++] = "--fix-thin";
418+
keeper[i++] = hdr_arg;
419+
keeper[i++] = keep_arg;
420+
keeper[i++] = NULL;
411421
memset(&ip, 0, sizeof(ip));
412422
ip.argv = keeper;
413423
ip.out = -1;

0 commit comments

Comments
 (0)