builtin / unpack-objects.con commit Merge tag 'l10n-2.10.0-rnd2' of git://github.com/git-l10n/git-po (934b1ca)
   1#include "builtin.h"
   2#include "cache.h"
   3#include "object.h"
   4#include "delta.h"
   5#include "pack.h"
   6#include "blob.h"
   7#include "commit.h"
   8#include "tag.h"
   9#include "tree.h"
  10#include "tree-walk.h"
  11#include "progress.h"
  12#include "decorate.h"
  13#include "fsck.h"
  14
  15static int dry_run, quiet, recover, has_errors, strict;
  16static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]";
  17
  18/* We always read in 4kB chunks. */
  19static unsigned char buffer[4096];
  20static unsigned int offset, len;
  21static off_t consumed_bytes;
  22static git_SHA_CTX ctx;
  23static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
  24
  25/*
  26 * When running under --strict mode, objects whose reachability are
  27 * suspect are kept in core without getting written in the object
  28 * store.
  29 */
  30struct obj_buffer {
  31        char *buffer;
  32        unsigned long size;
  33};
  34
  35static struct decoration obj_decorate;
  36
  37static struct obj_buffer *lookup_object_buffer(struct object *base)
  38{
  39        return lookup_decoration(&obj_decorate, base);
  40}
  41
  42static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
  43{
  44        struct obj_buffer *obj;
  45        obj = xcalloc(1, sizeof(struct obj_buffer));
  46        obj->buffer = buffer;
  47        obj->size = size;
  48        if (add_decoration(&obj_decorate, object, obj))
  49                die("object %s tried to add buffer twice!", oid_to_hex(&object->oid));
  50}
  51
  52/*
  53 * Make sure at least "min" bytes are available in the buffer, and
  54 * return the pointer to the buffer.
  55 */
  56static void *fill(int min)
  57{
  58        if (min <= len)
  59                return buffer + offset;
  60        if (min > sizeof(buffer))
  61                die("cannot fill %d bytes", min);
  62        if (offset) {
  63                git_SHA1_Update(&ctx, buffer, offset);
  64                memmove(buffer, buffer + offset, len);
  65                offset = 0;
  66        }
  67        do {
  68                ssize_t ret = xread(0, buffer + len, sizeof(buffer) - len);
  69                if (ret <= 0) {
  70                        if (!ret)
  71                                die("early EOF");
  72                        die_errno("read error on input");
  73                }
  74                len += ret;
  75        } while (len < min);
  76        return buffer;
  77}
  78
  79static void use(int bytes)
  80{
  81        if (bytes > len)
  82                die("used more bytes than were available");
  83        len -= bytes;
  84        offset += bytes;
  85
  86        /* make sure off_t is sufficiently large not to wrap */
  87        if (signed_add_overflows(consumed_bytes, bytes))
  88                die("pack too large for current definition of off_t");
  89        consumed_bytes += bytes;
  90}
  91
  92static void *get_data(unsigned long size)
  93{
  94        git_zstream stream;
  95        void *buf = xmallocz(size);
  96
  97        memset(&stream, 0, sizeof(stream));
  98
  99        stream.next_out = buf;
 100        stream.avail_out = size;
 101        stream.next_in = fill(1);
 102        stream.avail_in = len;
 103        git_inflate_init(&stream);
 104
 105        for (;;) {
 106                int ret = git_inflate(&stream, 0);
 107                use(len - stream.avail_in);
 108                if (stream.total_out == size && ret == Z_STREAM_END)
 109                        break;
 110                if (ret != Z_OK) {
 111                        error("inflate returned %d", ret);
 112                        free(buf);
 113                        buf = NULL;
 114                        if (!recover)
 115                                exit(1);
 116                        has_errors = 1;
 117                        break;
 118                }
 119                stream.next_in = fill(1);
 120                stream.avail_in = len;
 121        }
 122        git_inflate_end(&stream);
 123        return buf;
 124}
 125
 126struct delta_info {
 127        unsigned char base_sha1[20];
 128        unsigned nr;
 129        off_t base_offset;
 130        unsigned long size;
 131        void *delta;
 132        struct delta_info *next;
 133};
 134
 135static struct delta_info *delta_list;
 136
 137static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
 138                              off_t base_offset,
 139                              void *delta, unsigned long size)
 140{
 141        struct delta_info *info = xmalloc(sizeof(*info));
 142
 143        hashcpy(info->base_sha1, base_sha1);
 144        info->base_offset = base_offset;
 145        info->size = size;
 146        info->delta = delta;
 147        info->nr = nr;
 148        info->next = delta_list;
 149        delta_list = info;
 150}
 151
 152struct obj_info {
 153        off_t offset;
 154        unsigned char sha1[20];
 155        struct object *obj;
 156};
 157
 158#define FLAG_OPEN (1u<<20)
 159#define FLAG_WRITTEN (1u<<21)
 160
 161static struct obj_info *obj_list;
 162static unsigned nr_objects;
 163
 164/*
 165 * Called only from check_object() after it verified this object
 166 * is Ok.
 167 */
 168static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
 169{
 170        unsigned char sha1[20];
 171
 172        if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
 173                die("failed to write object %s", oid_to_hex(&obj->oid));
 174        obj->flags |= FLAG_WRITTEN;
 175}
 176
 177/*
 178 * At the very end of the processing, write_rest() scans the objects
 179 * that have reachability requirements and calls this function.
 180 * Verify its reachability and validity recursively and write it out.
 181 */
 182static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)
 183{
 184        struct obj_buffer *obj_buf;
 185
 186        if (!obj)
 187                return 1;
 188
 189        if (obj->flags & FLAG_WRITTEN)
 190                return 0;
 191
 192        if (type != OBJ_ANY && obj->type != type)
 193                die("object type mismatch");
 194
 195        if (!(obj->flags & FLAG_OPEN)) {
 196                unsigned long size;
 197                int type = sha1_object_info(obj->oid.hash, &size);
 198                if (type != obj->type || type <= 0)
 199                        die("object of unexpected type");
 200                obj->flags |= FLAG_WRITTEN;
 201                return 0;
 202        }
 203
 204        obj_buf = lookup_object_buffer(obj);
 205        if (!obj_buf)
 206                die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid));
 207        if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))
 208                die("Error in object");
 209        fsck_options.walk = check_object;
 210        if (fsck_walk(obj, NULL, &fsck_options))
 211                die("Error on reachable objects of %s", oid_to_hex(&obj->oid));
 212        write_cached_object(obj, obj_buf);
 213        return 0;
 214}
 215
 216static void write_rest(void)
 217{
 218        unsigned i;
 219        for (i = 0; i < nr_objects; i++) {
 220                if (obj_list[i].obj)
 221                        check_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);
 222        }
 223}
 224
 225static void added_object(unsigned nr, enum object_type type,
 226                         void *data, unsigned long size);
 227
 228/*
 229 * Write out nr-th object from the list, now we know the contents
 230 * of it.  Under --strict, this buffers structured objects in-core,
 231 * to be checked at the end.
 232 */
 233static void write_object(unsigned nr, enum object_type type,
 234                         void *buf, unsigned long size)
 235{
 236        if (!strict) {
 237                if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
 238                        die("failed to write object");
 239                added_object(nr, type, buf, size);
 240                free(buf);
 241                obj_list[nr].obj = NULL;
 242        } else if (type == OBJ_BLOB) {
 243                struct blob *blob;
 244                if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
 245                        die("failed to write object");
 246                added_object(nr, type, buf, size);
 247                free(buf);
 248
 249                blob = lookup_blob(obj_list[nr].sha1);
 250                if (blob)
 251                        blob->object.flags |= FLAG_WRITTEN;
 252                else
 253                        die("invalid blob object");
 254                obj_list[nr].obj = NULL;
 255        } else {
 256                struct object *obj;
 257                int eaten;
 258                hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
 259                added_object(nr, type, buf, size);
 260                obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
 261                if (!obj)
 262                        die("invalid %s", typename(type));
 263                add_object_buffer(obj, buf, size);
 264                obj->flags |= FLAG_OPEN;
 265                obj_list[nr].obj = obj;
 266        }
 267}
 268
 269static void resolve_delta(unsigned nr, enum object_type type,
 270                          void *base, unsigned long base_size,
 271                          void *delta, unsigned long delta_size)
 272{
 273        void *result;
 274        unsigned long result_size;
 275
 276        result = patch_delta(base, base_size,
 277                             delta, delta_size,
 278                             &result_size);
 279        if (!result)
 280                die("failed to apply delta");
 281        free(delta);
 282        write_object(nr, type, result, result_size);
 283}
 284
 285/*
 286 * We now know the contents of an object (which is nr-th in the pack);
 287 * resolve all the deltified objects that are based on it.
 288 */
 289static void added_object(unsigned nr, enum object_type type,
 290                         void *data, unsigned long size)
 291{
 292        struct delta_info **p = &delta_list;
 293        struct delta_info *info;
 294
 295        while ((info = *p) != NULL) {
 296                if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
 297                    info->base_offset == obj_list[nr].offset) {
 298                        *p = info->next;
 299                        p = &delta_list;
 300                        resolve_delta(info->nr, type, data, size,
 301                                      info->delta, info->size);
 302                        free(info);
 303                        continue;
 304                }
 305                p = &info->next;
 306        }
 307}
 308
 309static void unpack_non_delta_entry(enum object_type type, unsigned long size,
 310                                   unsigned nr)
 311{
 312        void *buf = get_data(size);
 313
 314        if (!dry_run && buf)
 315                write_object(nr, type, buf, size);
 316        else
 317                free(buf);
 318}
 319
 320static int resolve_against_held(unsigned nr, const unsigned char *base,
 321                                void *delta_data, unsigned long delta_size)
 322{
 323        struct object *obj;
 324        struct obj_buffer *obj_buffer;
 325        obj = lookup_object(base);
 326        if (!obj)
 327                return 0;
 328        obj_buffer = lookup_object_buffer(obj);
 329        if (!obj_buffer)
 330                return 0;
 331        resolve_delta(nr, obj->type, obj_buffer->buffer,
 332                      obj_buffer->size, delta_data, delta_size);
 333        return 1;
 334}
 335
 336static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
 337                               unsigned nr)
 338{
 339        void *delta_data, *base;
 340        unsigned long base_size;
 341        unsigned char base_sha1[20];
 342
 343        if (type == OBJ_REF_DELTA) {
 344                hashcpy(base_sha1, fill(20));
 345                use(20);
 346                delta_data = get_data(delta_size);
 347                if (dry_run || !delta_data) {
 348                        free(delta_data);
 349                        return;
 350                }
 351                if (has_sha1_file(base_sha1))
 352                        ; /* Ok we have this one */
 353                else if (resolve_against_held(nr, base_sha1,
 354                                              delta_data, delta_size))
 355                        return; /* we are done */
 356                else {
 357                        /* cannot resolve yet --- queue it */
 358                        hashclr(obj_list[nr].sha1);
 359                        add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
 360                        return;
 361                }
 362        } else {
 363                unsigned base_found = 0;
 364                unsigned char *pack, c;
 365                off_t base_offset;
 366                unsigned lo, mid, hi;
 367
 368                pack = fill(1);
 369                c = *pack;
 370                use(1);
 371                base_offset = c & 127;
 372                while (c & 128) {
 373                        base_offset += 1;
 374                        if (!base_offset || MSB(base_offset, 7))
 375                                die("offset value overflow for delta base object");
 376                        pack = fill(1);
 377                        c = *pack;
 378                        use(1);
 379                        base_offset = (base_offset << 7) + (c & 127);
 380                }
 381                base_offset = obj_list[nr].offset - base_offset;
 382                if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
 383                        die("offset value out of bound for delta base object");
 384
 385                delta_data = get_data(delta_size);
 386                if (dry_run || !delta_data) {
 387                        free(delta_data);
 388                        return;
 389                }
 390                lo = 0;
 391                hi = nr;
 392                while (lo < hi) {
 393                        mid = (lo + hi)/2;
 394                        if (base_offset < obj_list[mid].offset) {
 395                                hi = mid;
 396                        } else if (base_offset > obj_list[mid].offset) {
 397                                lo = mid + 1;
 398                        } else {
 399                                hashcpy(base_sha1, obj_list[mid].sha1);
 400                                base_found = !is_null_sha1(base_sha1);
 401                                break;
 402                        }
 403                }
 404                if (!base_found) {
 405                        /*
 406                         * The delta base object is itself a delta that
 407                         * has not been resolved yet.
 408                         */
 409                        hashclr(obj_list[nr].sha1);
 410                        add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
 411                        return;
 412                }
 413        }
 414
 415        if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
 416                return;
 417
 418        base = read_sha1_file(base_sha1, &type, &base_size);
 419        if (!base) {
 420                error("failed to read delta-pack base object %s",
 421                      sha1_to_hex(base_sha1));
 422                if (!recover)
 423                        exit(1);
 424                has_errors = 1;
 425                return;
 426        }
 427        resolve_delta(nr, type, base, base_size, delta_data, delta_size);
 428        free(base);
 429}
 430
 431static void unpack_one(unsigned nr)
 432{
 433        unsigned shift;
 434        unsigned char *pack;
 435        unsigned long size, c;
 436        enum object_type type;
 437
 438        obj_list[nr].offset = consumed_bytes;
 439
 440        pack = fill(1);
 441        c = *pack;
 442        use(1);
 443        type = (c >> 4) & 7;
 444        size = (c & 15);
 445        shift = 4;
 446        while (c & 0x80) {
 447                pack = fill(1);
 448                c = *pack;
 449                use(1);
 450                size += (c & 0x7f) << shift;
 451                shift += 7;
 452        }
 453
 454        switch (type) {
 455        case OBJ_COMMIT:
 456        case OBJ_TREE:
 457        case OBJ_BLOB:
 458        case OBJ_TAG:
 459                unpack_non_delta_entry(type, size, nr);
 460                return;
 461        case OBJ_REF_DELTA:
 462        case OBJ_OFS_DELTA:
 463                unpack_delta_entry(type, size, nr);
 464                return;
 465        default:
 466                error("bad object type %d", type);
 467                has_errors = 1;
 468                if (recover)
 469                        return;
 470                exit(1);
 471        }
 472}
 473
 474static void unpack_all(void)
 475{
 476        int i;
 477        struct progress *progress = NULL;
 478        struct pack_header *hdr = fill(sizeof(struct pack_header));
 479
 480        nr_objects = ntohl(hdr->hdr_entries);
 481
 482        if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
 483                die("bad pack file");
 484        if (!pack_version_ok(hdr->hdr_version))
 485                die("unknown pack file version %"PRIu32,
 486                        ntohl(hdr->hdr_version));
 487        use(sizeof(struct pack_header));
 488
 489        if (!quiet)
 490                progress = start_progress(_("Unpacking objects"), nr_objects);
 491        obj_list = xcalloc(nr_objects, sizeof(*obj_list));
 492        for (i = 0; i < nr_objects; i++) {
 493                unpack_one(i);
 494                display_progress(progress, i + 1);
 495        }
 496        stop_progress(&progress);
 497
 498        if (delta_list)
 499                die("unresolved deltas left after unpacking");
 500}
 501
 502int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
 503{
 504        int i;
 505        unsigned char sha1[20];
 506
 507        check_replace_refs = 0;
 508
 509        git_config(git_default_config, NULL);
 510
 511        quiet = !isatty(2);
 512
 513        for (i = 1 ; i < argc; i++) {
 514                const char *arg = argv[i];
 515
 516                if (*arg == '-') {
 517                        if (!strcmp(arg, "-n")) {
 518                                dry_run = 1;
 519                                continue;
 520                        }
 521                        if (!strcmp(arg, "-q")) {
 522                                quiet = 1;
 523                                continue;
 524                        }
 525                        if (!strcmp(arg, "-r")) {
 526                                recover = 1;
 527                                continue;
 528                        }
 529                        if (!strcmp(arg, "--strict")) {
 530                                strict = 1;
 531                                continue;
 532                        }
 533                        if (skip_prefix(arg, "--strict=", &arg)) {
 534                                strict = 1;
 535                                fsck_set_msg_types(&fsck_options, arg);
 536                                continue;
 537                        }
 538                        if (starts_with(arg, "--pack_header=")) {
 539                                struct pack_header *hdr;
 540                                char *c;
 541
 542                                hdr = (struct pack_header *)buffer;
 543                                hdr->hdr_signature = htonl(PACK_SIGNATURE);
 544                                hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
 545                                if (*c != ',')
 546                                        die("bad %s", arg);
 547                                hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
 548                                if (*c)
 549                                        die("bad %s", arg);
 550                                len = sizeof(*hdr);
 551                                continue;
 552                        }
 553                        usage(unpack_usage);
 554                }
 555
 556                /* We don't take any non-flag arguments now.. Maybe some day */
 557                usage(unpack_usage);
 558        }
 559        git_SHA1_Init(&ctx);
 560        unpack_all();
 561        git_SHA1_Update(&ctx, buffer, offset);
 562        git_SHA1_Final(sha1, &ctx);
 563        if (strict)
 564                write_rest();
 565        if (hashcmp(fill(20), sha1))
 566                die("final sha1 did not match");
 567        use(20);
 568
 569        /* Write the last part of the buffer to stdout */
 570        while (len) {
 571                int ret = xwrite(1, buffer + offset, len);
 572                if (ret <= 0)
 573                        break;
 574                len -= ret;
 575                offset += ret;
 576        }
 577
 578        /* All done */
 579        return has_errors;
 580}