builtin-tar-tree.con commit Merge git://git.kernel.org/pub/scm/gitk/gitk (0825de8)
   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
 275        while (tree->size) {
 276                const char *name;
 277                const unsigned char *sha1;
 278                unsigned mode;
 279                void *eltbuf;
 280                char elttype[20];
 281                unsigned long eltsize;
 282
 283                sha1 = tree_entry_extract(tree, &name, &mode);
 284                update_tree_entry(tree);
 285
 286                eltbuf = read_sha1_file(sha1, elttype, &eltsize);
 287                if (!eltbuf)
 288                        die("cannot read %s", sha1_to_hex(sha1));
 289
 290                path->len = pathlen;
 291                strbuf_append_string(path, name);
 292                if (S_ISDIR(mode))
 293                        strbuf_append_string(path, "/");
 294
 295                write_entry(sha1, path, mode, eltbuf, eltsize);
 296
 297                if (S_ISDIR(mode)) {
 298                        struct tree_desc subtree;
 299                        subtree.buf = eltbuf;
 300                        subtree.size = eltsize;
 301                        traverse_tree(&subtree, path);
 302                }
 303                free(eltbuf);
 304        }
 305}
 306
 307static int generate_tar(int argc, const char **argv, char** envp)
 308{
 309        unsigned char sha1[20], tree_sha1[20];
 310        struct commit *commit;
 311        struct tree_desc tree;
 312        struct strbuf current_path;
 313
 314        current_path.buf = xmalloc(PATH_MAX);
 315        current_path.alloc = PATH_MAX;
 316        current_path.len = current_path.eof = 0;
 317
 318        setup_git_directory();
 319        git_config(git_default_config);
 320
 321        switch (argc) {
 322        case 3:
 323                strbuf_append_string(&current_path, argv[2]);
 324                strbuf_append_string(&current_path, "/");
 325                /* FALLTHROUGH */
 326        case 2:
 327                if (get_sha1(argv[1], sha1))
 328                        die("Not a valid object name %s", argv[1]);
 329                break;
 330        default:
 331                usage(tar_tree_usage);
 332        }
 333
 334        commit = lookup_commit_reference_gently(sha1, 1);
 335        if (commit) {
 336                write_global_extended_header(commit->object.sha1);
 337                archive_time = commit->date;
 338        } else
 339                archive_time = time(NULL);
 340
 341        tree.buf = read_object_with_reference(sha1, tree_type, &tree.size,
 342                                              tree_sha1);
 343        if (!tree.buf)
 344                die("not a reference to a tag, commit or tree object: %s",
 345                    sha1_to_hex(sha1));
 346
 347        if (current_path.len > 0)
 348                write_entry(tree_sha1, &current_path, 040777, NULL, 0);
 349        traverse_tree(&tree, &current_path);
 350        write_trailer();
 351        free(current_path.buf);
 352        return 0;
 353}
 354
 355static const char *exec = "git-upload-tar";
 356
 357static int remote_tar(int argc, const char **argv)
 358{
 359        int fd[2], ret, len;
 360        pid_t pid;
 361        char buf[1024];
 362        char *url;
 363
 364        if (argc < 3 || 4 < argc)
 365                usage(tar_tree_usage);
 366
 367        /* --remote=<repo> */
 368        url = strdup(argv[1]+9);
 369        pid = git_connect(fd, url, exec);
 370        if (pid < 0)
 371                return 1;
 372
 373        packet_write(fd[1], "want %s\n", argv[2]);
 374        if (argv[3])
 375                packet_write(fd[1], "base %s\n", argv[3]);
 376        packet_flush(fd[1]);
 377
 378        len = packet_read_line(fd[0], buf, sizeof(buf));
 379        if (!len)
 380                die("git-tar-tree: expected ACK/NAK, got EOF");
 381        if (buf[len-1] == '\n')
 382                buf[--len] = 0;
 383        if (strcmp(buf, "ACK")) {
 384                if (5 < len && !strncmp(buf, "NACK ", 5))
 385                        die("git-tar-tree: NACK %s", buf + 5);
 386                die("git-tar-tree: protocol error");
 387        }
 388        /* expect a flush */
 389        len = packet_read_line(fd[0], buf, sizeof(buf));
 390        if (len)
 391                die("git-tar-tree: expected a flush");
 392
 393        /* Now, start reading from fd[0] and spit it out to stdout */
 394        ret = copy_fd(fd[0], 1);
 395        close(fd[0]);
 396
 397        ret |= finish_connect(pid);
 398        return !!ret;
 399}
 400
 401int cmd_tar_tree(int argc, const char **argv, char **envp)
 402{
 403        if (argc < 2)
 404                usage(tar_tree_usage);
 405        if (!strncmp("--remote=", argv[1], 9))
 406                return remote_tar(argc, argv);
 407        return generate_tar(argc, argv, envp);
 408}