builtin-tar-tree.con commit Merge branch 'jc/lockfile' (9941afc)
   1/*
   2 * Copyright (c) 2005, 2006 Rene Scharfe
   3 */
   4#include <time.h>
   5#include "cache.h"
   6#include "tree-walk.h"
   7#include "commit.h"
   8#include "strbuf.h"
   9#include "tar.h"
  10#include "builtin.h"
  11#include "pkt-line.h"
  12
  13#define RECORDSIZE      (512)
  14#define BLOCKSIZE       (RECORDSIZE * 20)
  15
  16static const char tar_tree_usage[] =
  17"git-tar-tree [--remote=<repo>] <ent> [basedir]";
  18
  19static char block[BLOCKSIZE];
  20static unsigned long offset;
  21
  22static time_t archive_time;
  23
  24/* tries hard to write, either succeeds or dies in the attempt */
  25static void reliable_write(void *buf, unsigned long size)
  26{
  27        while (size > 0) {
  28                long ret = xwrite(1, buf, size);
  29                if (ret < 0) {
  30                        if (errno == EPIPE)
  31                                exit(0);
  32                        die("git-tar-tree: %s", strerror(errno));
  33                } else if (!ret) {
  34                        die("git-tar-tree: disk full?");
  35                }
  36                size -= ret;
  37                buf += ret;
  38        }
  39}
  40
  41/* writes out the whole block, but only if it is full */
  42static void write_if_needed(void)
  43{
  44        if (offset == BLOCKSIZE) {
  45                reliable_write(block, BLOCKSIZE);
  46                offset = 0;
  47        }
  48}
  49
  50/* acquire the next record from the buffer; user must call write_if_needed() */
  51static char *get_record(void)
  52{
  53        char *p = block + offset;
  54        memset(p, 0, RECORDSIZE);
  55        offset += RECORDSIZE;
  56        return p;
  57}
  58
  59/*
  60 * The end of tar archives is marked by 1024 nul bytes and after that
  61 * follows the rest of the block (if any).
  62 */
  63static void write_trailer(void)
  64{
  65        get_record();
  66        write_if_needed();
  67        get_record();
  68        write_if_needed();
  69        while (offset) {
  70                get_record();
  71                write_if_needed();
  72        }
  73}
  74
  75/*
  76 * queues up writes, so that all our write(2) calls write exactly one
  77 * full block; pads writes to RECORDSIZE
  78 */
  79static void write_blocked(void *buf, unsigned long size)
  80{
  81        unsigned long tail;
  82
  83        if (offset) {
  84                unsigned long chunk = BLOCKSIZE - offset;
  85                if (size < chunk)
  86                        chunk = size;
  87                memcpy(block + offset, buf, chunk);
  88                size -= chunk;
  89                offset += chunk;
  90                buf += chunk;
  91                write_if_needed();
  92        }
  93        while (size >= BLOCKSIZE) {
  94                reliable_write(buf, BLOCKSIZE);
  95                size -= BLOCKSIZE;
  96                buf += BLOCKSIZE;
  97        }
  98        if (size) {
  99                memcpy(block + offset, buf, size);
 100                offset += size;
 101        }
 102        tail = offset % RECORDSIZE;
 103        if (tail)  {
 104                memset(block + offset, 0, RECORDSIZE - tail);
 105                offset += RECORDSIZE - tail;
 106        }
 107        write_if_needed();
 108}
 109
 110static void strbuf_append_string(struct strbuf *sb, const char *s)
 111{
 112        int slen = strlen(s);
 113        int total = sb->len + slen;
 114        if (total > sb->alloc) {
 115                sb->buf = xrealloc(sb->buf, total);
 116                sb->alloc = total;
 117        }
 118        memcpy(sb->buf + sb->len, s, slen);
 119        sb->len = total;
 120}
 121
 122/*
 123 * pax extended header records have the format "%u %s=%s\n".  %u contains
 124 * the size of the whole string (including the %u), the first %s is the
 125 * keyword, the second one is the value.  This function constructs such a
 126 * string and appends it to a struct strbuf.
 127 */
 128static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
 129                                     const char *value, unsigned int valuelen)
 130{
 131        char *p;
 132        int len, total, tmp;
 133
 134        /* "%u %s=%s\n" */
 135        len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
 136        for (tmp = len; tmp > 9; tmp /= 10)
 137                len++;
 138
 139        total = sb->len + len;
 140        if (total > sb->alloc) {
 141                sb->buf = xrealloc(sb->buf, total);
 142                sb->alloc = total;
 143        }
 144
 145        p = sb->buf;
 146        p += sprintf(p, "%u %s=", len, keyword);
 147        memcpy(p, value, valuelen);
 148        p += valuelen;
 149        *p = '\n';
 150        sb->len = total;
 151}
 152
 153static unsigned int ustar_header_chksum(const struct ustar_header *header)
 154{
 155        char *p = (char *)header;
 156        unsigned int chksum = 0;
 157        while (p < header->chksum)
 158                chksum += *p++;
 159        chksum += sizeof(header->chksum) * ' ';
 160        p += sizeof(header->chksum);
 161        while (p < (char *)header + sizeof(struct ustar_header))
 162                chksum += *p++;
 163        return chksum;
 164}
 165
 166static int get_path_prefix(const struct strbuf *path, int maxlen)
 167{
 168        int i = path->len;
 169        if (i > maxlen)
 170                i = maxlen;
 171        while (i > 0 && path->buf[i] != '/')
 172                i--;
 173        return i;
 174}
 175
 176static void write_entry(const unsigned char *sha1, struct strbuf *path,
 177                        unsigned int mode, void *buffer, unsigned long size)
 178{
 179        struct ustar_header header;
 180        struct strbuf ext_header;
 181
 182        memset(&header, 0, sizeof(header));
 183        ext_header.buf = NULL;
 184        ext_header.len = ext_header.alloc = 0;
 185
 186        if (!sha1) {
 187                *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
 188                mode = 0100666;
 189                strcpy(header.name, "pax_global_header");
 190        } else if (!path) {
 191                *header.typeflag = TYPEFLAG_EXT_HEADER;
 192                mode = 0100666;
 193                sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
 194        } else {
 195                if (S_ISDIR(mode)) {
 196                        *header.typeflag = TYPEFLAG_DIR;
 197                        mode |= 0777;
 198                } else if (S_ISLNK(mode)) {
 199                        *header.typeflag = TYPEFLAG_LNK;
 200                        mode |= 0777;
 201                } else if (S_ISREG(mode)) {
 202                        *header.typeflag = TYPEFLAG_REG;
 203                        mode |= (mode & 0100) ? 0777 : 0666;
 204                } else {
 205                        error("unsupported file mode: 0%o (SHA1: %s)",
 206                              mode, sha1_to_hex(sha1));
 207                        return;
 208                }
 209                if (path->len > sizeof(header.name)) {
 210                        int plen = get_path_prefix(path, sizeof(header.prefix));
 211                        int rest = path->len - plen - 1;
 212                        if (plen > 0 && rest <= sizeof(header.name)) {
 213                                memcpy(header.prefix, path->buf, plen);
 214                                memcpy(header.name, path->buf + plen + 1, rest);
 215                        } else {
 216                                sprintf(header.name, "%s.data",
 217                                        sha1_to_hex(sha1));
 218                                strbuf_append_ext_header(&ext_header, "path",
 219                                                         path->buf, path->len);
 220                        }
 221                } else
 222                        memcpy(header.name, path->buf, path->len);
 223        }
 224
 225        if (S_ISLNK(mode) && buffer) {
 226                if (size > sizeof(header.linkname)) {
 227                        sprintf(header.linkname, "see %s.paxheader",
 228                                sha1_to_hex(sha1));
 229                        strbuf_append_ext_header(&ext_header, "linkpath",
 230                                                 buffer, size);
 231                } else
 232                        memcpy(header.linkname, buffer, size);
 233        }
 234
 235        sprintf(header.mode, "%07o", mode & 07777);
 236        sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
 237        sprintf(header.mtime, "%011lo", archive_time);
 238
 239        /* XXX: should we provide more meaningful info here? */
 240        sprintf(header.uid, "%07o", 0);
 241        sprintf(header.gid, "%07o", 0);
 242        strncpy(header.uname, "git", 31);
 243        strncpy(header.gname, "git", 31);
 244        sprintf(header.devmajor, "%07o", 0);
 245        sprintf(header.devminor, "%07o", 0);
 246
 247        memcpy(header.magic, "ustar", 6);
 248        memcpy(header.version, "00", 2);
 249
 250        sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
 251
 252        if (ext_header.len > 0) {
 253                write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
 254                free(ext_header.buf);
 255        }
 256        write_blocked(&header, sizeof(header));
 257        if (S_ISREG(mode) && buffer && size > 0)
 258                write_blocked(buffer, size);
 259}
 260
 261static void write_global_extended_header(const unsigned char *sha1)
 262{
 263        struct strbuf ext_header;
 264        ext_header.buf = NULL;
 265        ext_header.len = ext_header.alloc = 0;
 266        strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
 267        write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
 268        free(ext_header.buf);
 269}
 270
 271static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
 272{
 273        int pathlen = path->len;
 274        struct name_entry entry;
 275
 276        while (tree_entry(tree, &entry)) {
 277                void *eltbuf;
 278                char elttype[20];
 279                unsigned long eltsize;
 280
 281                eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize);
 282                if (!eltbuf)
 283                        die("cannot read %s", sha1_to_hex(entry.sha1));
 284
 285                path->len = pathlen;
 286                strbuf_append_string(path, entry.path);
 287                if (S_ISDIR(entry.mode))
 288                        strbuf_append_string(path, "/");
 289
 290                write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize);
 291
 292                if (S_ISDIR(entry.mode)) {
 293                        struct tree_desc subtree;
 294                        subtree.buf = eltbuf;
 295                        subtree.size = eltsize;
 296                        traverse_tree(&subtree, path);
 297                }
 298                free(eltbuf);
 299        }
 300}
 301
 302static int generate_tar(int argc, const char **argv, char** envp)
 303{
 304        unsigned char sha1[20], tree_sha1[20];
 305        struct commit *commit;
 306        struct tree_desc tree;
 307        struct strbuf current_path;
 308
 309        current_path.buf = xmalloc(PATH_MAX);
 310        current_path.alloc = PATH_MAX;
 311        current_path.len = current_path.eof = 0;
 312
 313        setup_git_directory();
 314        git_config(git_default_config);
 315
 316        switch (argc) {
 317        case 3:
 318                strbuf_append_string(&current_path, argv[2]);
 319                strbuf_append_string(&current_path, "/");
 320                /* FALLTHROUGH */
 321        case 2:
 322                if (get_sha1(argv[1], sha1))
 323                        die("Not a valid object name %s", argv[1]);
 324                break;
 325        default:
 326                usage(tar_tree_usage);
 327        }
 328
 329        commit = lookup_commit_reference_gently(sha1, 1);
 330        if (commit) {
 331                write_global_extended_header(commit->object.sha1);
 332                archive_time = commit->date;
 333        } else
 334                archive_time = time(NULL);
 335
 336        tree.buf = read_object_with_reference(sha1, tree_type, &tree.size,
 337                                              tree_sha1);
 338        if (!tree.buf)
 339                die("not a reference to a tag, commit or tree object: %s",
 340                    sha1_to_hex(sha1));
 341
 342        if (current_path.len > 0)
 343                write_entry(tree_sha1, &current_path, 040777, NULL, 0);
 344        traverse_tree(&tree, &current_path);
 345        write_trailer();
 346        free(current_path.buf);
 347        return 0;
 348}
 349
 350static const char *exec = "git-upload-tar";
 351
 352static int remote_tar(int argc, const char **argv)
 353{
 354        int fd[2], ret, len;
 355        pid_t pid;
 356        char buf[1024];
 357        char *url;
 358
 359        if (argc < 3 || 4 < argc)
 360                usage(tar_tree_usage);
 361
 362        /* --remote=<repo> */
 363        url = strdup(argv[1]+9);
 364        pid = git_connect(fd, url, exec);
 365        if (pid < 0)
 366                return 1;
 367
 368        packet_write(fd[1], "want %s\n", argv[2]);
 369        if (argv[3])
 370                packet_write(fd[1], "base %s\n", argv[3]);
 371        packet_flush(fd[1]);
 372
 373        len = packet_read_line(fd[0], buf, sizeof(buf));
 374        if (!len)
 375                die("git-tar-tree: expected ACK/NAK, got EOF");
 376        if (buf[len-1] == '\n')
 377                buf[--len] = 0;
 378        if (strcmp(buf, "ACK")) {
 379                if (5 < len && !strncmp(buf, "NACK ", 5))
 380                        die("git-tar-tree: NACK %s", buf + 5);
 381                die("git-tar-tree: protocol error");
 382        }
 383        /* expect a flush */
 384        len = packet_read_line(fd[0], buf, sizeof(buf));
 385        if (len)
 386                die("git-tar-tree: expected a flush");
 387
 388        /* Now, start reading from fd[0] and spit it out to stdout */
 389        ret = copy_fd(fd[0], 1);
 390        close(fd[0]);
 391
 392        ret |= finish_connect(pid);
 393        return !!ret;
 394}
 395
 396int cmd_tar_tree(int argc, const char **argv, char **envp)
 397{
 398        if (argc < 2)
 399                usage(tar_tree_usage);
 400        if (!strncmp("--remote=", argv[1], 9))
 401                return remote_tar(argc, argv);
 402        return generate_tar(argc, argv, envp);
 403}