Code

Add --verbose to git-archive
[git.git] / builtin-tar-tree.c
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 #include "archive.h"
14 #define RECORDSIZE      (512)
15 #define BLOCKSIZE       (RECORDSIZE * 20)
17 static const char tar_tree_usage[] =
18 "git-tar-tree [--remote=<repo>] <tree-ish> [basedir]";
20 static char block[BLOCKSIZE];
21 static unsigned long offset;
23 static time_t archive_time;
24 static int tar_umask;
25 static int verbose;
27 /* writes out the whole block, but only if it is full */
28 static void write_if_needed(void)
29 {
30         if (offset == BLOCKSIZE) {
31                 write_or_die(1, block, BLOCKSIZE);
32                 offset = 0;
33         }
34 }
36 /*
37  * queues up writes, so that all our write(2) calls write exactly one
38  * full block; pads writes to RECORDSIZE
39  */
40 static void write_blocked(const void *data, unsigned long size)
41 {
42         const char *buf = data;
43         unsigned long tail;
45         if (offset) {
46                 unsigned long chunk = BLOCKSIZE - offset;
47                 if (size < chunk)
48                         chunk = size;
49                 memcpy(block + offset, buf, chunk);
50                 size -= chunk;
51                 offset += chunk;
52                 buf += chunk;
53                 write_if_needed();
54         }
55         while (size >= BLOCKSIZE) {
56                 write_or_die(1, buf, BLOCKSIZE);
57                 size -= BLOCKSIZE;
58                 buf += BLOCKSIZE;
59         }
60         if (size) {
61                 memcpy(block + offset, buf, size);
62                 offset += size;
63         }
64         tail = offset % RECORDSIZE;
65         if (tail)  {
66                 memset(block + offset, 0, RECORDSIZE - tail);
67                 offset += RECORDSIZE - tail;
68         }
69         write_if_needed();
70 }
72 /*
73  * The end of tar archives is marked by 2*512 nul bytes and after that
74  * follows the rest of the block (if any).
75  */
76 static void write_trailer(void)
77 {
78         int tail = BLOCKSIZE - offset;
79         memset(block + offset, 0, tail);
80         write_or_die(1, block, BLOCKSIZE);
81         if (tail < 2 * RECORDSIZE) {
82                 memset(block, 0, offset);
83                 write_or_die(1, block, BLOCKSIZE);
84         }
85 }
87 static void strbuf_append_string(struct strbuf *sb, const char *s)
88 {
89         int slen = strlen(s);
90         int total = sb->len + slen;
91         if (total > sb->alloc) {
92                 sb->buf = xrealloc(sb->buf, total);
93                 sb->alloc = total;
94         }
95         memcpy(sb->buf + sb->len, s, slen);
96         sb->len = total;
97 }
99 /*
100  * pax extended header records have the format "%u %s=%s\n".  %u contains
101  * the size of the whole string (including the %u), the first %s is the
102  * keyword, the second one is the value.  This function constructs such a
103  * string and appends it to a struct strbuf.
104  */
105 static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
106                                      const char *value, unsigned int valuelen)
108         char *p;
109         int len, total, tmp;
111         /* "%u %s=%s\n" */
112         len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
113         for (tmp = len; tmp > 9; tmp /= 10)
114                 len++;
116         total = sb->len + len;
117         if (total > sb->alloc) {
118                 sb->buf = xrealloc(sb->buf, total);
119                 sb->alloc = total;
120         }
122         p = sb->buf;
123         p += sprintf(p, "%u %s=", len, keyword);
124         memcpy(p, value, valuelen);
125         p += valuelen;
126         *p = '\n';
127         sb->len = total;
130 static unsigned int ustar_header_chksum(const struct ustar_header *header)
132         char *p = (char *)header;
133         unsigned int chksum = 0;
134         while (p < header->chksum)
135                 chksum += *p++;
136         chksum += sizeof(header->chksum) * ' ';
137         p += sizeof(header->chksum);
138         while (p < (char *)header + sizeof(struct ustar_header))
139                 chksum += *p++;
140         return chksum;
143 static int get_path_prefix(const struct strbuf *path, int maxlen)
145         int i = path->len;
146         if (i > maxlen)
147                 i = maxlen;
148         do {
149                 i--;
150         } while (i > 0 && path->buf[i] != '/');
151         return i;
154 static void write_entry(const unsigned char *sha1, struct strbuf *path,
155                         unsigned int mode, void *buffer, unsigned long size)
157         struct ustar_header header;
158         struct strbuf ext_header;
160         memset(&header, 0, sizeof(header));
161         ext_header.buf = NULL;
162         ext_header.len = ext_header.alloc = 0;
164         if (!sha1) {
165                 *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
166                 mode = 0100666;
167                 strcpy(header.name, "pax_global_header");
168         } else if (!path) {
169                 *header.typeflag = TYPEFLAG_EXT_HEADER;
170                 mode = 0100666;
171                 sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
172         } else {
173                 if (verbose)
174                         fprintf(stderr, "%.*s\n", path->len, path->buf);
175                 if (S_ISDIR(mode)) {
176                         *header.typeflag = TYPEFLAG_DIR;
177                         mode = (mode | 0777) & ~tar_umask;
178                 } else if (S_ISLNK(mode)) {
179                         *header.typeflag = TYPEFLAG_LNK;
180                         mode |= 0777;
181                 } else if (S_ISREG(mode)) {
182                         *header.typeflag = TYPEFLAG_REG;
183                         mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
184                 } else {
185                         error("unsupported file mode: 0%o (SHA1: %s)",
186                               mode, sha1_to_hex(sha1));
187                         return;
188                 }
189                 if (path->len > sizeof(header.name)) {
190                         int plen = get_path_prefix(path, sizeof(header.prefix));
191                         int rest = path->len - plen - 1;
192                         if (plen > 0 && rest <= sizeof(header.name)) {
193                                 memcpy(header.prefix, path->buf, plen);
194                                 memcpy(header.name, path->buf + plen + 1, rest);
195                         } else {
196                                 sprintf(header.name, "%s.data",
197                                         sha1_to_hex(sha1));
198                                 strbuf_append_ext_header(&ext_header, "path",
199                                                          path->buf, path->len);
200                         }
201                 } else
202                         memcpy(header.name, path->buf, path->len);
203         }
205         if (S_ISLNK(mode) && buffer) {
206                 if (size > sizeof(header.linkname)) {
207                         sprintf(header.linkname, "see %s.paxheader",
208                                 sha1_to_hex(sha1));
209                         strbuf_append_ext_header(&ext_header, "linkpath",
210                                                  buffer, size);
211                 } else
212                         memcpy(header.linkname, buffer, size);
213         }
215         sprintf(header.mode, "%07o", mode & 07777);
216         sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
217         sprintf(header.mtime, "%011lo", archive_time);
219         /* XXX: should we provide more meaningful info here? */
220         sprintf(header.uid, "%07o", 0);
221         sprintf(header.gid, "%07o", 0);
222         strlcpy(header.uname, "git", sizeof(header.uname));
223         strlcpy(header.gname, "git", sizeof(header.gname));
224         sprintf(header.devmajor, "%07o", 0);
225         sprintf(header.devminor, "%07o", 0);
227         memcpy(header.magic, "ustar", 6);
228         memcpy(header.version, "00", 2);
230         sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
232         if (ext_header.len > 0) {
233                 write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
234                 free(ext_header.buf);
235         }
236         write_blocked(&header, sizeof(header));
237         if (S_ISREG(mode) && buffer && size > 0)
238                 write_blocked(buffer, size);
241 static void write_global_extended_header(const unsigned char *sha1)
243         struct strbuf ext_header;
244         ext_header.buf = NULL;
245         ext_header.len = ext_header.alloc = 0;
246         strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
247         write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
248         free(ext_header.buf);
251 static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
253         int pathlen = path->len;
254         struct name_entry entry;
256         while (tree_entry(tree, &entry)) {
257                 void *eltbuf;
258                 char elttype[20];
259                 unsigned long eltsize;
261                 eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize);
262                 if (!eltbuf)
263                         die("cannot read %s", sha1_to_hex(entry.sha1));
265                 path->len = pathlen;
266                 strbuf_append_string(path, entry.path);
267                 if (S_ISDIR(entry.mode))
268                         strbuf_append_string(path, "/");
270                 write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize);
272                 if (S_ISDIR(entry.mode)) {
273                         struct tree_desc subtree;
274                         subtree.buf = eltbuf;
275                         subtree.size = eltsize;
276                         traverse_tree(&subtree, path);
277                 }
278                 free(eltbuf);
279         }
282 static int git_tar_config(const char *var, const char *value)
284         if (!strcmp(var, "tar.umask")) {
285                 if (!strcmp(value, "user")) {
286                         tar_umask = umask(0);
287                         umask(tar_umask);
288                 } else {
289                         tar_umask = git_config_int(var, value);
290                 }
291                 return 0;
292         }
293         return git_default_config(var, value);
296 static int generate_tar(int argc, const char **argv, const char *prefix)
298         unsigned char sha1[20], tree_sha1[20];
299         struct commit *commit;
300         struct tree_desc tree;
301         struct strbuf current_path;
302         void *buffer;
304         current_path.buf = xmalloc(PATH_MAX);
305         current_path.alloc = PATH_MAX;
306         current_path.len = current_path.eof = 0;
308         git_config(git_tar_config);
310         switch (argc) {
311         case 3:
312                 strbuf_append_string(&current_path, argv[2]);
313                 strbuf_append_string(&current_path, "/");
314                 /* FALLTHROUGH */
315         case 2:
316                 if (get_sha1(argv[1], sha1))
317                         die("Not a valid object name %s", argv[1]);
318                 break;
319         default:
320                 usage(tar_tree_usage);
321         }
323         commit = lookup_commit_reference_gently(sha1, 1);
324         if (commit) {
325                 write_global_extended_header(commit->object.sha1);
326                 archive_time = commit->date;
327         } else
328                 archive_time = time(NULL);
330         tree.buf = buffer = read_object_with_reference(sha1, tree_type,
331                                                        &tree.size, tree_sha1);
332         if (!tree.buf)
333                 die("not a reference to a tag, commit or tree object: %s",
334                     sha1_to_hex(sha1));
336         if (current_path.len > 0)
337                 write_entry(tree_sha1, &current_path, 040777, NULL, 0);
338         traverse_tree(&tree, &current_path);
339         write_trailer();
340         free(buffer);
341         free(current_path.buf);
342         return 0;
345 static int write_tar_entry(const unsigned char *sha1,
346                            const char *base, int baselen,
347                            const char *filename, unsigned mode, int stage)
349         static struct strbuf path;
350         int filenamelen = strlen(filename);
351         void *buffer;
352         char type[20];
353         unsigned long size;
355         if (!path.alloc) {
356                 path.buf = xmalloc(PATH_MAX);
357                 path.alloc = PATH_MAX;
358                 path.len = path.eof = 0;
359         }
360         if (path.alloc < baselen + filenamelen) {
361                 free(path.buf);
362                 path.buf = xmalloc(baselen + filenamelen);
363                 path.alloc = baselen + filenamelen;
364         }
365         memcpy(path.buf, base, baselen);
366         memcpy(path.buf + baselen, filename, filenamelen);
367         path.len = baselen + filenamelen;
368         if (S_ISDIR(mode)) {
369                 strbuf_append_string(&path, "/");
370                 buffer = NULL;
371                 size = 0;
372         } else {
373                 buffer = read_sha1_file(sha1, type, &size);
374                 if (!buffer)
375                         die("cannot read %s", sha1_to_hex(sha1));
376         }
378         write_entry(sha1, &path, mode, buffer, size);
379         free(buffer);
381         return READ_TREE_RECURSIVE;
384 int write_tar_archive(struct archiver_args *args)
386         int plen = strlen(args->base);
388         git_config(git_tar_config);
390         archive_time = args->time;
391         verbose = args->verbose;
393         if (args->commit_sha1)
394                 write_global_extended_header(args->commit_sha1);
396         if (args->base && plen > 0 && args->base[plen - 1] == '/') {
397                 char *base = xstrdup(args->base);
398                 int baselen = strlen(base);
400                 while (baselen > 0 && base[baselen - 1] == '/')
401                         base[--baselen] = '\0';
402                 write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
403                 free(base);
404         }
405         read_tree_recursive(args->tree, args->base, plen, 0,
406                             args->pathspec, write_tar_entry);
407         write_trailer();
409         return 0;
412 static const char *exec = "git-upload-tar";
414 static int remote_tar(int argc, const char **argv)
416         int fd[2], ret, len;
417         pid_t pid;
418         char buf[1024];
419         char *url;
421         if (argc < 3 || 4 < argc)
422                 usage(tar_tree_usage);
424         /* --remote=<repo> */
425         url = xstrdup(argv[1]+9);
426         pid = git_connect(fd, url, exec);
427         if (pid < 0)
428                 return 1;
430         packet_write(fd[1], "want %s\n", argv[2]);
431         if (argv[3])
432                 packet_write(fd[1], "base %s\n", argv[3]);
433         packet_flush(fd[1]);
435         len = packet_read_line(fd[0], buf, sizeof(buf));
436         if (!len)
437                 die("git-tar-tree: expected ACK/NAK, got EOF");
438         if (buf[len-1] == '\n')
439                 buf[--len] = 0;
440         if (strcmp(buf, "ACK")) {
441                 if (5 < len && !strncmp(buf, "NACK ", 5))
442                         die("git-tar-tree: NACK %s", buf + 5);
443                 die("git-tar-tree: protocol error");
444         }
445         /* expect a flush */
446         len = packet_read_line(fd[0], buf, sizeof(buf));
447         if (len)
448                 die("git-tar-tree: expected a flush");
450         /* Now, start reading from fd[0] and spit it out to stdout */
451         ret = copy_fd(fd[0], 1);
452         close(fd[0]);
454         ret |= finish_connect(pid);
455         return !!ret;
458 int cmd_tar_tree(int argc, const char **argv, const char *prefix)
460         if (argc < 2)
461                 usage(tar_tree_usage);
462         if (!strncmp("--remote=", argv[1], 9))
463                 return remote_tar(argc, argv);
464         return generate_tar(argc, argv, prefix);
467 /* ustar header + extended global header content */
468 #define HEADERSIZE (2 * RECORDSIZE)
470 int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
472         char buffer[HEADERSIZE];
473         struct ustar_header *header = (struct ustar_header *)buffer;
474         char *content = buffer + RECORDSIZE;
475         ssize_t n;
477         n = xread(0, buffer, HEADERSIZE);
478         if (n < HEADERSIZE)
479                 die("git-get-tar-commit-id: read error");
480         if (header->typeflag[0] != 'g')
481                 return 1;
482         if (memcmp(content, "52 comment=", 11))
483                 return 1;
485         n = xwrite(1, content + 11, 41);
486         if (n < 41)
487                 die("git-get-tar-commit-id: write error");
489         return 0;