Archive-Tar-Builder
view release on metacpan or search on metacpan
src/b_builder.c view on Meta::CPAN
/*
* If either of these cases are true, then in normal circumstances the path
* prefix or suffix MUST be truncated to fix into a tar header's corresponding
* fields.
*
* Note that this calculation MUST happen after any other path suffix or prefix
* size calculations are complete.
*/
if (suffix_size > B_HEADER_SUFFIX_SIZE || prefix_size > B_HEADER_PREFIX_SIZE) {
data->truncated = 1;
}
b_stack_destroy(prefix_items);
b_stack_destroy(suffix_items);
return data;
error_suffix:
b_string_free(data->prefix);
error_prefix:
error_item:
error_empty_stack:
b_stack_destroy(suffix_items);
error_suffix_items:
b_stack_destroy(prefix_items);
error_prefix_items:
b_stack_destroy(parts);
error_path_new:
free(data);
error_data_malloc:
return NULL;
}
static inline int is_hardlink(struct stat *st) {
return (st->st_mode & S_IFMT) == S_IFREG && st->st_nlink > 1;
}
static b_header *header_for_file(b_builder *builder, b_string *path, b_string *member_name, struct stat *st) {
b_header *ret;
struct path_data *path_data;
if ((ret = malloc(sizeof(*ret))) == NULL) {
goto error_malloc;
}
if ((path_data = path_split(member_name, st)) == NULL) {
goto error_path_data;
}
ret->truncated = path_data->truncated;
ret->prefix = path_data->prefix;
ret->suffix = path_data->suffix;
ret->mode = st->st_mode;
ret->uid = st->st_uid;
ret->gid = st->st_gid;
ret->size = (st->st_mode & S_IFMT) == S_IFREG? st->st_size: 0;
ret->mtime = st->st_mtime;
ret->major = major(st->st_dev);
ret->minor = minor(st->st_dev);
ret->linktype = inode_linktype(st);
ret->linkdest = NULL;
ret->user = NULL;
ret->group = NULL;
ret->truncated_link = 0;
if ((st->st_mode & S_IFMT) == S_IFLNK) {
if ((ret->linkdest = b_readlink(path, st)) == NULL) {
goto error_readlink;
}
} else if (is_hardlink(st) && builder->hardlink_lookup) {
b_string *linkdest;
if (linkdest = builder->hardlink_lookup(builder->hardlink_cache, st->st_dev, st->st_ino, member_name)) {
ret->linktype = '0' + S_IF_HARDLINK;
ret->linkdest = linkdest;
}
}
if (ret->linkdest && b_string_len(ret->linkdest) > B_HEADER_LINKDEST_SIZE) {
ret->truncated_link = 1;
}
/*
* free() path_data, but keep its prefix and suffix with us, as we will free() those
* ourselves b_header_destroy()
*/
free(path_data);
return ret;
error_readlink:
b_string_free(path_data->prefix);
b_string_free(path_data->suffix);
free(path_data);
error_path_data:
free(ret);
error_malloc:
return NULL;
}
int b_builder_write_file(b_builder *builder, b_string *path, b_string *member_name, struct stat *st, int fd) {
b_buffer *buf = builder->buf;
b_error *err = builder->err;
off_t wrlen = 0;
b_header *header;
b_header_block *block;
if (buf == NULL) {
errno = EINVAL;
return -1;
}
if (err) {
b_error_clear(err);
}
if ((header = header_for_file(builder, path, member_name, st)) == NULL) {
if (err) {
b_error_set(err, B_ERROR_FATAL, errno, "Cannot build header for file", path);
}
goto error_header_for_file;
}
/*
* If there is a user lookup service installed, then resolve the user and
* group of the current filesystem object and supply them within the
* b_header object.
*/
if (builder->user_lookup != NULL) {
b_string *user = NULL, *group = NULL;
if (builder->user_lookup(builder->user_cache, st->st_uid, st->st_gid, &user, &group) < 0) {
if (err) {
b_error_set(err, B_ERROR_WARN, errno, "Cannot lookup user and group for file", path);
}
goto error_lookup;
}
if (b_header_set_usernames(header, user, group) < 0) {
goto error_lookup;
}
}
/*
* If the header is marked to contain truncated paths, then write a GNU
* longlink header, followed by the blocks containing the path name to be
* assigned.
*/
if (header->truncated || header->truncated_link) {
b_string *longlink_path;
/*
* GNU extensions must be explicitly enabled to encode GNU LongLink
* headers.
*/
if (!(builder->options & B_BUILDER_EXTENSIONS_MASK)) {
errno = ENAMETOOLONG;
if (err) {
b_error_set(err, B_ERROR_WARN, errno, "File name too long", member_name);
}
goto error_path_toolong;
}
if ((block = b_buffer_get_block(buf, B_HEADER_SIZE, &wrlen)) == NULL) {
goto error_get_header_block;
}
if ((longlink_path = b_string_dup(member_name)) == NULL) {
goto error_longlink_path_dup;
}
if ((st->st_mode & S_IFMT) == S_IFDIR) {
if ((b_string_append_str(longlink_path, "/")) == NULL) {
goto error_longlink_path_append;
}
}
if (builder->options & B_BUILDER_GNU_EXTENSIONS) {
if (header->truncated && encode_longlink(builder, block, longlink_path, B_HEADER_LONGLINK_TYPE, &wrlen) < 0) {
goto error_header_encode;
} else if ( header->truncated && header->truncated_link ) {
// With encoding two links side by side, we need to call the get block again to update the internals of the buffer to avoid
// the next encode_longlink from overwriting parts of the previous link.
if ((block = b_buffer_get_block(buf, B_HEADER_SIZE, &wrlen)) == NULL) {
goto error_get_header_block;
}
}
( run in 0.609 second using v1.01-cache-2.11-cpan-5735350b133 )