4 #define RECORDSIZE (512)
5 #define BLOCKSIZE (RECORDSIZE * 20)
7 #define TYPEFLAG_AUTO '\0'
8 #define TYPEFLAG_REG '0'
9 #define TYPEFLAG_DIR '5'
10 #define TYPEFLAG_GLOBAL_HEADER 'g'
11 #define TYPEFLAG_EXT_HEADER 'x'
13 static const char *tar_tree_usage
= "tar-tree <key> [basedir]";
15 static char block
[BLOCKSIZE
];
16 static unsigned long offset
;
18 static const char *basedir
;
19 static time_t archive_time
;
22 struct path_prefix
*prev
;
26 /* tries hard to write, either succeeds or dies in the attempt */
27 static void reliable_write(void *buf
, unsigned long size
)
30 long ret
= write(1, buf
, size
);
36 die("tar-tree: %s", strerror(errno
));
38 die("tar-tree: disk full?");
45 /* writes out the whole block, but only if it is full */
46 static void write_if_needed(void)
48 if (offset
== BLOCKSIZE
) {
49 reliable_write(block
, BLOCKSIZE
);
54 /* acquire the next record from the buffer; user must call write_if_needed() */
55 static char *get_record(void)
57 char *p
= block
+ offset
;
58 memset(p
, 0, RECORDSIZE
);
64 * The end of tar archives is marked by 1024 nul bytes and after that
65 * follows the rest of the block (if any).
67 static void write_trailer(void)
69 memset(block
+ offset
, 0, RECORDSIZE
);
72 memset(block
+ offset
, 0, RECORDSIZE
);
76 memset(block
+ offset
, 0, BLOCKSIZE
- offset
);
77 reliable_write(block
, BLOCKSIZE
);
83 * queues up writes, so that all our write(2) calls write exactly one
84 * full block; pads writes to RECORDSIZE
86 static void write_blocked(void *buf
, unsigned long size
)
91 unsigned long chunk
= BLOCKSIZE
- offset
;
94 memcpy(block
+ offset
, buf
, chunk
);
100 while (size
>= BLOCKSIZE
) {
101 reliable_write(buf
, BLOCKSIZE
);
106 memcpy(block
+ offset
, buf
, size
);
110 tail
= offset
% RECORDSIZE
;
112 memset(block
+ offset
, 0, RECORDSIZE
- tail
);
113 offset
+= RECORDSIZE
- tail
;
118 static void append_string(char **p
, const char *s
)
120 unsigned int len
= strlen(s
);
125 static void append_char(char **p
, char c
)
131 static void append_long(char **p
, long n
)
133 int len
= sprintf(*p
, "%ld", n
);
137 static void append_path_prefix(char **buffer
, struct path_prefix
*prefix
)
141 append_path_prefix(buffer
, prefix
->prev
);
142 append_string(buffer
, prefix
->name
);
143 append_char(buffer
, '/');
146 static unsigned int path_prefix_len(struct path_prefix
*prefix
)
150 return path_prefix_len(prefix
->prev
) + strlen(prefix
->name
) + 1;
153 static void append_path(char **p
, int is_dir
, const char *basepath
,
154 struct path_prefix
*prefix
, const char *path
)
157 append_string(p
, basepath
);
160 append_path_prefix(p
, prefix
);
161 append_string(p
, path
);
166 static unsigned int path_len(int is_dir
, const char *basepath
,
167 struct path_prefix
*prefix
, const char *path
)
169 unsigned int len
= 0;
171 len
+= strlen(basepath
) + 1;
172 len
+= path_prefix_len(prefix
) + strlen(path
);
178 static void write_header(const char *, char, const char *, struct path_prefix
*,
179 const char *, unsigned int, unsigned long);
181 /* stores a pax extended header directly in the block buffer */
182 static void write_extended_header(const char *headerfilename
, int is_dir
,
183 const char *basepath
,
184 struct path_prefix
*prefix
,
185 const char *path
, unsigned int namelen
)
188 unsigned int size
= 1 + 6 + namelen
+ 1;
193 if (size
> RECORDSIZE
)
194 die("tar-tree: extended header too big, wtf?");
195 write_header(NULL
, TYPEFLAG_EXT_HEADER
, NULL
, NULL
, headerfilename
,
198 append_long(&p
, size
);
199 append_string(&p
, " path=");
200 append_path(&p
, is_dir
, basepath
, prefix
, path
);
201 append_char(&p
, '\n');
205 static void write_global_extended_header(const char *sha1
)
208 write_header(NULL
, TYPEFLAG_GLOBAL_HEADER
, NULL
, NULL
,
209 "pax_global_header", 0100600, 52);
211 append_long(&p
, 52); /* 2 + 9 + 40 + 1 */
212 append_string(&p
, " comment=");
213 append_string(&p
, sha1_to_hex(sha1
));
214 append_char(&p
, '\n');
218 /* stores a ustar header directly in the block buffer */
219 static void write_header(const char *sha1
, char typeflag
, const char *basepath
,
220 struct path_prefix
*prefix
, const char *path
,
221 unsigned int mode
, unsigned long size
)
223 unsigned int namelen
;
224 char *p
, *header
= NULL
;
225 unsigned int checksum
= 0;
228 if (typeflag
== TYPEFLAG_AUTO
) {
230 typeflag
= TYPEFLAG_DIR
;
232 typeflag
= TYPEFLAG_REG
;
235 namelen
= path_len(S_ISDIR(mode
), basepath
, prefix
, path
);
237 die("tar-tree: name too log of object %s\n", sha1_to_hex(sha1
));
238 } else if (namelen
> 100) {
239 char *sha1_hex
= sha1_to_hex(sha1
);
240 char headerfilename
[51];
241 sprintf(headerfilename
, "%s.paxheader", sha1_hex
);
242 /* the extended header must be written before the normal one */
243 write_extended_header(headerfilename
, S_ISDIR(mode
), basepath
,
244 prefix
, path
, namelen
);
246 header
= get_record();
247 sprintf(header
, "%s.data", sha1_hex
);
249 p
= header
= get_record();
250 append_path(&p
, S_ISDIR(mode
), basepath
, prefix
, path
);
254 mode
|= 0755; /* GIT doesn't store permissions of dirs */
255 sprintf(&header
[100], "%07o", mode
& 07777);
257 /* XXX: should we provide more meaningful info here? */
258 sprintf(&header
[108], "%07o", 0); /* uid */
259 sprintf(&header
[116], "%07o", 0); /* gid */
260 strncpy(&header
[265], "git", 31); /* uname */
261 strncpy(&header
[297], "git", 31); /* gname */
263 sprintf(&header
[124], "%011lo", S_ISDIR(mode
) ? 0 : size
);
264 sprintf(&header
[136], "%011lo", archive_time
);
266 header
[156] = typeflag
;
268 memcpy(&header
[257], "ustar", 6);
269 memcpy(&header
[263], "00", 2);
271 printf(&header
[329], "%07o", 0); /* devmajor */
272 printf(&header
[337], "%07o", 0); /* devminor */
274 memset(&header
[148], ' ', 8);
275 for (i
= 0; i
< RECORDSIZE
; i
++)
276 checksum
+= header
[i
];
277 sprintf(&header
[148], "%07o", checksum
& 0x1fffff);
282 static void traverse_tree(void *buffer
, unsigned long size
,
283 struct path_prefix
*prefix
)
285 struct path_prefix this_prefix
;
286 this_prefix
.prev
= prefix
;
289 int namelen
= strlen(buffer
)+1;
292 unsigned long eltsize
;
293 unsigned char *sha1
= buffer
+ namelen
;
294 char *path
= strchr(buffer
, ' ') + 1;
297 if (size
< namelen
+ 20 || sscanf(buffer
, "%o", &mode
) != 1)
298 die("corrupt 'tree' file");
300 size
-= namelen
+ 20;
302 eltbuf
= read_sha1_file(sha1
, elttype
, &eltsize
);
304 die("cannot read %s", sha1_to_hex(sha1
));
305 write_header(sha1
, TYPEFLAG_AUTO
, basedir
, prefix
, path
,
307 if (!strcmp(elttype
, "tree")) {
308 this_prefix
.name
= path
;
309 traverse_tree(eltbuf
, eltsize
, &this_prefix
);
310 } else if (!strcmp(elttype
, "blob")) {
311 write_blocked(eltbuf
, eltsize
);
317 /* get commit time from committer line of commit object */
318 time_t commit_time(void * buffer
, unsigned long size
)
324 char *endp
= memchr(p
, '\n', size
);
325 if (!endp
|| endp
== p
)
328 if (endp
- p
> 10 && !memcmp(p
, "committer ", 10)) {
329 char *nump
= strrchr(p
, '>');
333 result
= strtoul(nump
, &endp
, 10);
338 size
-= endp
- p
- 1;
344 int main(int argc
, char **argv
)
346 unsigned char sha1
[20];
347 unsigned char commit_sha1
[20];
356 if (get_sha1(argv
[1], sha1
) < 0)
357 usage(tar_tree_usage
);
360 usage(tar_tree_usage
);
363 sha1_file_directory
= getenv(DB_ENVIRONMENT
);
364 if (!sha1_file_directory
)
365 sha1_file_directory
= DEFAULT_DB_ENVIRONMENT
;
367 buffer
= read_object_with_reference(sha1
, "commit", &size
, commit_sha1
);
369 write_global_extended_header(commit_sha1
);
370 archive_time
= commit_time(buffer
, size
);
373 buffer
= read_object_with_reference(sha1
, "tree", &size
, NULL
);
375 die("not a reference to a tag, commit or tree object: %s",
378 archive_time
= time(NULL
);
380 write_header("0", TYPEFLAG_DIR
, NULL
, NULL
, basedir
, 040755, 0);
381 traverse_tree(buffer
, size
, NULL
);