File Coverage

src/b_builder.c
Criterion Covered Total %
statement 178 241 73.8
branch 93 158 58.8
condition n/a
subroutine n/a
pod n/a
total 271 399 67.9


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4             #include
5             #ifdef __GLIBC__
6             #include
7             #endif /* __GLIBC__ */
8             #include
9             #include
10             #include "match_engine.h"
11             #include "b_util.h"
12             #include "b_file.h"
13             #include "b_path.h"
14             #include "b_string.h"
15             #include "b_header.h"
16             #include "b_stack.h"
17             #include "b_buffer.h"
18             #include "b_builder.h"
19              
20             struct path_data {
21             b_string * prefix;
22             b_string * suffix;
23             int truncated;
24             };
25              
26             /*
27             * Given the value of st->st_mode & S_IFMT, return the corresponding tar header
28             * type identifier character. The hardlink type is not accounted for here as
29             * it is supplied below by header_for_file() when hardlinks are detected.
30             */
31 7069           static inline char inode_linktype(struct stat *st) {
32             /*
33             * The values in this jump table are sorted roughly in order of commonality
34             * of each inode type.
35             */
36 7069           switch (st->st_mode & S_IFMT) {
37 6981           case S_IFREG: return '0';
38 88           case S_IFDIR: return '5';
39 0           case S_IFLNK: return '2';
40 0           case S_IFIFO: return '6';
41 0           case S_IFCHR: return '3';
42 0           case S_IFBLK: return '4';
43             }
44              
45 0           return '0';
46             }
47              
48 92           b_builder *b_builder_new(size_t block_factor) {
49             b_builder *builder;
50              
51 92 50         if ((builder = malloc(sizeof(*builder))) == NULL) {
52 0           goto error_malloc;
53             }
54              
55 92 50         if ((builder->buf = b_buffer_new(block_factor? block_factor: B_BUFFER_DEFAULT_FACTOR)) == NULL) {
    50          
56 0           goto error_buffer_new;
57             }
58              
59 92 50         if ((builder->err = b_error_new()) == NULL) {
60 0           goto error_error_new;
61             }
62              
63 92           builder->total = 0;
64 92           builder->match = NULL;
65 92           builder->options = B_BUILDER_NONE;
66 92           builder->user_lookup = NULL;
67 92           builder->user_cache = NULL;
68 92           builder->hardlink_lookup = NULL;
69 92           builder->hardlink_cache = NULL;
70 92           builder->data = NULL;
71              
72 92           return builder;
73              
74             error_error_new:
75 0           b_buffer_destroy(builder->buf);
76              
77             error_buffer_new:
78 0           free(builder);
79              
80             error_malloc:
81 0           return NULL;
82             }
83              
84 6629           enum b_builder_options b_builder_get_options(b_builder *builder) {
85 6629 50         if (builder == NULL) return B_BUILDER_NONE;
86              
87 6629           return builder->options;
88             }
89              
90 92           void b_builder_set_options(b_builder *builder, enum b_builder_options options) {
91 92           builder->options = options;
92 92           }
93              
94 6715           b_error *b_builder_get_error(b_builder *builder) {
95 6715 50         if (builder == NULL) return NULL;
96              
97 6715           return builder->err;
98             }
99              
100 6680           b_buffer *b_builder_get_buffer(b_builder *builder) {
101 6680 50         if (builder == NULL) return NULL;
102              
103 6680           return builder->buf;
104             }
105              
106 0           void b_builder_set_data(b_builder *builder, void *data) {
107 0 0         if (builder == NULL) return;
108              
109 0           builder->data = data;
110             }
111              
112             /*
113             * The caller should assume responsibility for initializing and destroying the
114             * user lookup service as appropriate.
115             */
116 92           void b_builder_set_user_lookup(b_builder *builder, b_user_lookup service, void *ctx) {
117 92           builder->user_lookup = service;
118 92           builder->user_cache = ctx;
119 92           }
120              
121 10           void b_builder_set_hardlink_cache(b_builder *builder, b_hardlink_lookup lookup, void *cache) {
122 10           builder->hardlink_lookup = lookup;
123 10           builder->hardlink_cache = cache;
124 10           }
125              
126 203           int b_builder_is_excluded(b_builder *builder, const char *path) {
127 203           return lafe_excluded(builder->match, path);
128             }
129              
130 14           int b_builder_include(b_builder *builder, const char *pattern) {
131 14           return lafe_include(&builder->match, pattern);
132             }
133              
134 21           int b_builder_include_from_file(b_builder *builder, const char *file) {
135 21           return lafe_include_from_file(&builder->match, file, 0);
136             }
137              
138 14           int b_builder_exclude(b_builder *builder, const char *pattern) {
139 14           return lafe_exclude(&builder->match, pattern);
140             }
141              
142 21           int b_builder_exclude_from_file(b_builder *builder, const char *file) {
143 21           return lafe_exclude_from_file(&builder->match, file);
144             }
145              
146 5834           static int encode_longlink(b_builder *builder, b_header_block *block, b_string *path, int type, off_t *wrlen) {
147 5834           b_buffer *buf = builder->buf;
148 5834           b_error *err = builder->err;
149              
150 5834 50         if (path == NULL) {
151 0           return 0;
152             }
153              
154 5834 50         if (b_header_encode_longlink_block(block, path, type) == NULL) {
155 0           return -1;
156             }
157              
158 5834           builder->total += *wrlen;
159              
160 5834 50         if ((*wrlen = b_file_write_path_blocks(buf, path)) < 0) {
161 0 0         if (err) {
162 0           b_error_set(err, B_ERROR_FATAL, errno, "Cannot write long filename header", path);
163             }
164              
165 0           return -1;
166             }
167              
168 5834           builder->total += *wrlen;
169              
170 5834           return 0;
171             }
172              
173 7069           static struct path_data *path_split(b_string *path, struct stat *st) {
174             struct path_data *data;
175              
176             b_stack *prefix_items, *suffix_items;
177 7069           size_t prefix_size = 0, suffix_size = 0;
178 7069           int add_to_prefix = 0;
179              
180             b_stack *parts;
181             b_string *item;
182              
183 7069 50         if ((data = malloc(sizeof(*data))) == NULL) {
184 0           goto error_data_malloc;
185             }
186              
187 7069 50         if ((parts = b_path_new(path)) == NULL) {
188 0           goto error_path_new;
189             }
190              
191 7069 50         if ((prefix_items = b_stack_new(0)) == NULL) {
192 0           goto error_prefix_items;
193             }
194              
195 7069 50         if ((suffix_items = b_stack_new(0)) == NULL) {
196 0           goto error_suffix_items;
197             }
198              
199 7069           b_stack_set_destructor(parts, B_STACK_DESTRUCTOR(b_string_free));
200 7069           b_stack_set_destructor(prefix_items, B_STACK_DESTRUCTOR(b_string_free));
201 7069           b_stack_set_destructor(suffix_items, B_STACK_DESTRUCTOR(b_string_free));
202              
203 7069           data->truncated = 0;
204              
205 7069 50         if (b_stack_count(parts) == 0) {
206 0           goto error_empty_stack;
207             }
208              
209             /*
210             * Strip the leading / from the path, if present.
211             */
212 7069 100         if (b_string_len(b_stack_item_at(parts, 0)) == 0) {
213 413           b_string *leading = b_stack_shift(parts);
214              
215 413           b_string_free(leading);
216             }
217              
218 15053 100         while ((item = b_stack_pop(parts)) != NULL) {
219 7984 100         if (suffix_size && suffix_size + item->len >= B_HEADER_SUFFIX_SIZE) {
    100          
220 7           add_to_prefix = 1;
221             }
222              
223             /* directory will have a / added to the end */
224 7984 100         if ( ( (st->st_mode & S_IFMT) == S_IFDIR ) && ( suffix_size + item->len + 1 >= B_HEADER_SUFFIX_SIZE ) ) {
    100          
225 7           add_to_prefix = 1;
226             }
227              
228 7984 100         if (add_to_prefix) {
229 7 50         if (prefix_size) prefix_size++; /* Add 1 to make room for path separator */
230 7           prefix_size += item->len;
231             } else {
232 7977 100         if (suffix_size) suffix_size++; /* ^-- Ditto */
233 7977           suffix_size += item->len;
234             }
235              
236 7984 100         if (b_stack_push(add_to_prefix? prefix_items: suffix_items, item) == NULL) {
    50          
237 0           goto error_item;
238             }
239             }
240              
241 7069           b_stack_destroy(parts);
242              
243             /*
244             * Assemble the prefix and suffix strings.
245             */
246 7069 50         if ((data->prefix = b_string_join("/", b_stack_reverse(prefix_items))) == NULL) {
247 0           goto error_prefix;
248             }
249              
250 7069 50         if ((data->suffix = b_string_join("/", b_stack_reverse(suffix_items))) == NULL) {
251 0           goto error_suffix;
252             }
253              
254             /*
255             * If the item we are dealing with is a directory, then always consider the
256             * trailing slash in its representation.
257             */
258 7069 100         if ((st->st_mode & S_IFMT) == S_IFDIR) {
259 88           suffix_size++;
260 88           b_string_append_str(data->suffix, "/");
261             }
262              
263             /*
264             * If either of these cases are true, then in normal circumstances the path
265             * prefix or suffix MUST be truncated to fix into a tar header's corresponding
266             * fields.
267             *
268             * Note that this calculation MUST happen after any other path suffix or prefix
269             * size calculations are complete.
270             */
271 7069 100         if (suffix_size > B_HEADER_SUFFIX_SIZE || prefix_size > B_HEADER_PREFIX_SIZE) {
    50          
272 5842           data->truncated = 1;
273             }
274              
275 7069           b_stack_destroy(prefix_items);
276 7069           b_stack_destroy(suffix_items);
277              
278 7069           return data;
279              
280             error_suffix:
281 0           b_string_free(data->prefix);
282              
283             error_prefix:
284             error_item:
285             error_empty_stack:
286 0           b_stack_destroy(suffix_items);
287              
288             error_suffix_items:
289 0           b_stack_destroy(prefix_items);
290              
291             error_prefix_items:
292 0           b_stack_destroy(parts);
293              
294             error_path_new:
295 0           free(data);
296              
297             error_data_malloc:
298 0           return NULL;
299             }
300              
301 7069           static inline int is_hardlink(struct stat *st) {
302 7069 100         return (st->st_mode & S_IFMT) == S_IFREG && st->st_nlink > 1;
    100          
303             }
304              
305 7069           static b_header *header_for_file(b_builder *builder, b_string *path, b_string *member_name, struct stat *st) {
306             b_header *ret;
307              
308             struct path_data *path_data;
309              
310 7069 50         if ((ret = malloc(sizeof(*ret))) == NULL) {
311 0           goto error_malloc;
312             }
313              
314 7069 50         if ((path_data = path_split(member_name, st)) == NULL) {
315 0           goto error_path_data;
316             }
317              
318 7069           ret->truncated = path_data->truncated;
319 7069           ret->prefix = path_data->prefix;
320 7069           ret->suffix = path_data->suffix;
321 7069           ret->mode = st->st_mode;
322 7069           ret->uid = st->st_uid;
323 7069           ret->gid = st->st_gid;
324 7069 100         ret->size = (st->st_mode & S_IFMT) == S_IFREG? st->st_size: 0;
325 7069           ret->mtime = st->st_mtime;
326 7069           ret->major = major(st->st_dev);
327 7069           ret->minor = minor(st->st_dev);
328 7069           ret->linktype = inode_linktype(st);
329 7069           ret->linkdest = NULL;
330 7069           ret->user = NULL;
331 7069           ret->group = NULL;
332              
333 7069           ret->truncated_link = 0;
334              
335 7069 50         if ((st->st_mode & S_IFMT) == S_IFLNK) {
336 0 0         if ((ret->linkdest = b_readlink(path, st)) == NULL) {
337 0           goto error_readlink;
338             }
339 7069 100         } else if (is_hardlink(st) && builder->hardlink_lookup) {
    50          
340             b_string *linkdest;
341              
342 404 100         if (linkdest = builder->hardlink_lookup(builder->hardlink_cache, st->st_dev, st->st_ino, member_name)) {
343 202           ret->linktype = '0' + S_IF_HARDLINK;
344 202           ret->linkdest = linkdest;
345             }
346             }
347              
348 7069 100         if (ret->linkdest && b_string_len(ret->linkdest) > B_HEADER_LINKDEST_SIZE) {
    50          
349 0           ret->truncated_link = 1;
350             }
351              
352             /*
353             * free() path_data, but keep its prefix and suffix with us, as we will free() those
354             * ourselves b_header_destroy()
355             */
356 7069           free(path_data);
357              
358 7069           return ret;
359              
360             error_readlink:
361 0           b_string_free(path_data->prefix);
362 0           b_string_free(path_data->suffix);
363              
364 0           free(path_data);
365              
366             error_path_data:
367 0           free(ret);
368              
369             error_malloc:
370 0           return NULL;
371             }
372              
373 7069           int b_builder_write_file(b_builder *builder, b_string *path, b_string *member_name, struct stat *st, int fd) {
374 7069           b_buffer *buf = builder->buf;
375 7069           b_error *err = builder->err;
376              
377 7069           off_t wrlen = 0;
378              
379             b_header *header;
380             b_header_block *block;
381              
382 7069 50         if (buf == NULL) {
383 0           errno = EINVAL;
384 0           return -1;
385             }
386              
387 7069 50         if (err) {
388 7069           b_error_clear(err);
389             }
390              
391 7069 50         if ((header = header_for_file(builder, path, member_name, st)) == NULL) {
392 0 0         if (err) {
393 0           b_error_set(err, B_ERROR_FATAL, errno, "Cannot build header for file", path);
394             }
395              
396 0           goto error_header_for_file;
397             }
398              
399             /*
400             * If there is a user lookup service installed, then resolve the user and
401             * group of the current filesystem object and supply them within the
402             * b_header object.
403             */
404 7069 50         if (builder->user_lookup != NULL) {
405 7069           b_string *user = NULL, *group = NULL;
406              
407 7069 50         if (builder->user_lookup(builder->user_cache, st->st_uid, st->st_gid, &user, &group) < 0) {
408 0 0         if (err) {
409 0           b_error_set(err, B_ERROR_WARN, errno, "Cannot lookup user and group for file", path);
410             }
411              
412 0           goto error_lookup;
413             }
414              
415 7069 50         if (b_header_set_usernames(header, user, group) < 0) {
416 7069           goto error_lookup;
417             }
418             }
419              
420             /*
421             * If the header is marked to contain truncated paths, then write a GNU
422             * longlink header, followed by the blocks containing the path name to be
423             * assigned.
424             */
425 7069 100         if (header->truncated || header->truncated_link) {
    50          
426             b_string *longlink_path;
427              
428             /*
429             * GNU extensions must be explicitly enabled to encode GNU LongLink
430             * headers.
431             */
432 5842 100         if (!(builder->options & B_BUILDER_EXTENSIONS_MASK)) {
433 4           errno = ENAMETOOLONG;
434              
435 4 50         if (err) {
436 4           b_error_set(err, B_ERROR_WARN, errno, "File name too long", member_name);
437             }
438              
439 4           goto error_path_toolong;
440             }
441              
442 5838 50         if ((block = b_buffer_get_block(buf, B_HEADER_SIZE, &wrlen)) == NULL) {
443 0           goto error_get_header_block;
444             }
445              
446 5838 50         if ((longlink_path = b_string_dup(member_name)) == NULL) {
447 0           goto error_longlink_path_dup;
448             }
449              
450 5838 50         if ((st->st_mode & S_IFMT) == S_IFDIR) {
451 0 0         if ((b_string_append_str(longlink_path, "/")) == NULL) {
452 0           goto error_longlink_path_append;
453             }
454             }
455              
456 5838 100         if (builder->options & B_BUILDER_GNU_EXTENSIONS) {
457 5834 50         if (header->truncated && encode_longlink(builder, block, longlink_path, B_HEADER_LONGLINK_TYPE, &wrlen) < 0) {
    50          
458 0           goto error_header_encode;
459             }
460              
461 5834 50         if (header->truncated_link && encode_longlink(builder, block, header->linkdest, B_HEADER_LONGDEST_TYPE, &wrlen) < 0) {
    0          
462 0           goto error_header_encode;
463             }
464 4 50         } else if (builder->options & B_BUILDER_PAX_EXTENSIONS) {
465 4 50         if (b_header_encode_pax_block(block, header, longlink_path) == NULL) {
466 0           goto error_header_encode;
467             }
468              
469 4           builder->total += wrlen;
470              
471 4 50         if ((wrlen = b_file_write_pax_path_blocks(buf, longlink_path, header->linkdest)) < 0) {
472 0 0         if (err) {
473 0           b_error_set(err, B_ERROR_FATAL, errno, "Cannot write long filename header", member_name);
474             }
475              
476 0           goto error_write;
477             }
478              
479 4           builder->total += wrlen;
480             }
481             }
482              
483             /*
484             * Then, of course, encode and write the real file header block.
485             */
486 7065 50         if ((block = b_buffer_get_block(buf, B_HEADER_SIZE, &wrlen)) == NULL) {
487 0           goto error_write;
488             }
489              
490 7065 50         if (b_header_encode_block(block, header) == NULL) {
491 0           goto error_header_encode;
492             }
493              
494 7065           builder->total += wrlen;
495              
496             /*
497             * Finally, end by writing the file contents.
498             */
499 7065 100         if (B_HEADER_IS_IFREG(header) && fd > 0) {
    50          
500 6775 100         if ((wrlen = b_file_write_contents(buf, fd, header->size)) < 0) {
501 4 50         if (err) {
502 4           b_error_set(err, B_ERROR_WARN, errno, "Cannot write file to archive", path);
503             }
504              
505 4           goto error_write;
506             }
507              
508 6771           builder->total += wrlen;
509             }
510              
511 7061           b_header_destroy(header);
512              
513 7061           return 1;
514              
515             error_write:
516             error_longlink_path_append:
517             error_longlink_path_dup:
518             error_get_header_block:
519             error_path_toolong:
520             error_header_encode:
521             error_lookup:
522 8           b_header_destroy(header);
523              
524             error_header_for_file:
525 7069           return -1;
526             }
527              
528 92           void b_builder_destroy(b_builder *builder) {
529 92 50         if (builder == NULL) return;
530              
531 92 50         if (builder->buf) {
532 92           b_buffer_destroy(builder->buf);
533 92           builder->buf = NULL;
534             }
535              
536 92 50         if (builder->err) {
537 92           b_error_destroy(builder->err);
538 92           builder->err = NULL;
539             }
540              
541 92           builder->options = B_BUILDER_NONE;
542 92           builder->total = 0;
543 92           builder->data = NULL;
544              
545 92           lafe_cleanup_exclusions(&builder->match);
546              
547 92           builder->match = NULL;
548              
549 92           free(builder);
550             }