diff options
| author | Tim Kientzle <kientzle@FreeBSD.org> | 2008-05-26 17:10:10 +0000 |
|---|---|---|
| committer | Tim Kientzle <kientzle@FreeBSD.org> | 2008-05-26 17:10:10 +0000 |
| commit | eb36031906b623c41398f29df9178b5214a4c9fa (patch) | |
| tree | d94d00fec531d1439aa528d7d109ac2166ab5295 /usr.bin/tar/write.c | |
| parent | fa07de5eeb36516701b3393f13acc1e1f702c634 (diff) | |
Notes
Diffstat (limited to 'usr.bin/tar/write.c')
| -rw-r--r-- | usr.bin/tar/write.c | 309 |
1 files changed, 87 insertions, 222 deletions
diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index 9b3f39bc1c166..f2ff46a5479c5 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -87,9 +87,6 @@ __FBSDID("$FreeBSD$"); static const char * const NO_NAME = "(noname)"; -/* Initial size of link cache. */ -#define links_cache_initial_size 1024 - struct archive_dir_entry { struct archive_dir_entry *next; time_t mtime_sec; @@ -101,21 +98,6 @@ struct archive_dir { struct archive_dir_entry *head, *tail; }; -struct links_cache { - unsigned long number_entries; - size_t number_buckets; - struct links_entry **buckets; -}; - -struct links_entry { - struct links_entry *next; - struct links_entry *previous; - int links; - dev_t dev; - ino_t ino; - char *name; -}; - struct name_cache { int probes; int hits; @@ -139,13 +121,10 @@ static int archive_names_from_file_helper(struct bsdtar *bsdtar, static int copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina); static void create_cleanup(struct bsdtar *); -static void free_buckets(struct bsdtar *, struct links_cache *); static void free_cache(struct name_cache *cache); static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid); static int lookup_gname_helper(struct bsdtar *bsdtar, const char **name, id_t gid); -static void lookup_hardlink(struct bsdtar *, - struct archive_entry *entry, const struct stat *); static const char * lookup_uname(struct bsdtar *bsdtar, uid_t uid); static int lookup_uname_helper(struct bsdtar *bsdtar, const char **name, id_t uid); @@ -160,6 +139,8 @@ static void write_archive(struct archive *, struct bsdtar *); static void write_entry(struct bsdtar *, struct archive *, const struct stat *, const char *pathname, const char *accpath); +static void write_entry_backend(struct bsdtar *, struct archive *, + struct archive_entry *, int); static int write_file_data(struct bsdtar *, struct archive *, int fd); static void write_hierarchy(struct bsdtar *, struct archive *, @@ -266,6 +247,9 @@ tar_mode_r(struct bsdtar *bsdtar) /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); + /* We want to catch SIGINFO and SIGUSR1. */ + siginfo_init(bsdtar); + format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT, 0666); @@ -364,6 +348,9 @@ tar_mode_u(struct bsdtar *bsdtar) /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); + /* We want to catch SIGINFO and SIGUSR1. */ + siginfo_init(bsdtar); + bsdtar->fd = open(bsdtar->filename, O_RDWR); if (bsdtar->fd < 0) bsdtar_errc(bsdtar, 1, errno, @@ -448,6 +435,12 @@ static void write_archive(struct archive *a, struct bsdtar *bsdtar) { const char *arg; + struct archive_entry *entry, *sparse_entry; + + if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) + bsdtar_errc(bsdtar, 1, 0, "cannot create link resolver"); + archive_entry_linkresolver_set_strategy(bsdtar->resolver, + archive_format(a)); if (bsdtar->names_from_file != NULL) archive_names_from_file(bsdtar, a); @@ -480,6 +473,16 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) bsdtar->argv++; } + entry = NULL; + archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); + while (entry != NULL) { + int fd = -1; + write_entry_backend(bsdtar, a, entry, fd); + archive_entry_free(entry); + entry = NULL; + archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); + } + create_cleanup(bsdtar); if (archive_write_close(a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); @@ -597,9 +600,12 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) if (e == ARCHIVE_FATAL) exit(1); - if (e >= ARCHIVE_WARN) - if (copy_file_data(bsdtar, a, ina)) + if (e >= ARCHIVE_WARN) { + if (archive_entry_size(in_entry) == 0) + archive_read_data_skip(ina); + else if (copy_file_data(bsdtar, a, ina)) exit(1); + } if (bsdtar->verbose) fprintf(stderr, "\n"); @@ -791,15 +797,62 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) } /* + * Backend for write_entry. + */ +static void +write_entry_backend(struct bsdtar *bsdtar, struct archive *a, + struct archive_entry *entry, int fd) +{ + int e; + + if (fd == -1 && archive_entry_size(entry) > 0) { + const char *pathname = archive_entry_sourcepath(entry); + fd = open(pathname, O_RDONLY); + if (fd == -1) { + if (!bsdtar->verbose) + bsdtar_warnc(bsdtar, errno, + "%s: could not open file", pathname); + else + fprintf(stderr, ": %s", strerror(errno)); + return; + } + } + + e = archive_write_header(a, entry); + if (e != ARCHIVE_OK) { + if (!bsdtar->verbose) + bsdtar_warnc(bsdtar, 0, "%s: %s", + archive_entry_pathname(entry), + archive_error_string(a)); + else + fprintf(stderr, ": %s", archive_error_string(a)); + } + + if (e == ARCHIVE_FATAL) + exit(1); + + /* + * If we opened a file earlier, write it out now. Note that + * the format handler might have reset the size field to zero + * to inform us that the archive body won't get stored. In + * that case, just skip the write. + */ + if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) { + if (write_file_data(bsdtar, a, fd)) + exit(1); + close(fd); + } +} + +/* * Add a single filesystem object to the archive. */ static void write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, const char *pathname, const char *accpath) { - struct archive_entry *entry; - int e; - int fd; + struct archive_entry *entry, *sparse_entry; + int fd; #ifdef __linux int r; unsigned long stflags; @@ -810,6 +863,7 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, entry = archive_entry_new(); archive_entry_set_pathname(entry, pathname); + archive_entry_copy_sourcepath(entry, accpath); /* * Rewrite the pathname to be archived. If rewrite @@ -825,9 +879,6 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, if (!new_enough(bsdtar, archive_entry_pathname(entry), st)) goto abort; - if (!S_ISDIR(st->st_mode) && (st->st_nlink > 1)) - lookup_hardlink(bsdtar, entry, st); - /* Display entry as we process it. This format is required by SUSv2. */ if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); @@ -874,23 +925,6 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, setup_acls(bsdtar, entry, accpath); setup_xattrs(bsdtar, entry, accpath); - /* - * If it's a regular file (and non-zero in size) make sure we - * can open it before we start to write. In particular, note - * that we can always archive a zero-length file, even if we - * can't read it. - */ - if (S_ISREG(st->st_mode) && st->st_size > 0) { - fd = open(accpath, O_RDONLY); - if (fd < 0) { - if (!bsdtar->verbose) - bsdtar_warnc(bsdtar, errno, "%s: could not open file", pathname); - else - fprintf(stderr, ": %s", strerror(errno)); - goto cleanup; - } - } - /* Non-regular files get archived with zero size. */ if (!S_ISREG(st->st_mode)) archive_entry_set_size(entry, 0); @@ -898,32 +932,19 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, /* Record what we're doing, for the benefit of SIGINFO / SIGUSR1. */ siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry), archive_entry_size(entry)); + archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); /* Handle SIGINFO / SIGUSR1 request if one was made. */ siginfo_printinfo(bsdtar, 0); - e = archive_write_header(a, entry); - if (e != ARCHIVE_OK) { - if (!bsdtar->verbose) - bsdtar_warnc(bsdtar, 0, "%s: %s", pathname, - archive_error_string(a)); - else - fprintf(stderr, ": %s", archive_error_string(a)); + while (entry != NULL) { + write_entry_backend(bsdtar, a, entry, fd); + fd = -1; + archive_entry_free(entry); + entry = sparse_entry; + sparse_entry = NULL; } - if (e == ARCHIVE_FATAL) - exit(1); - - /* - * If we opened a file earlier, write it out now. Note that - * the format handler might have reset the size field to zero - * to inform us that the archive body won't get stored. In - * that case, just skip the write. - */ - if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) - if (write_file_data(bsdtar, a, fd)) - exit(1); - cleanup: if (bsdtar->verbose) fprintf(stderr, "\n"); @@ -974,168 +995,12 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd) static void create_cleanup(struct bsdtar *bsdtar) { - /* Free inode->pathname map used for hardlink detection. */ - if (bsdtar->links_cache != NULL) { - free_buckets(bsdtar, bsdtar->links_cache); - free(bsdtar->links_cache); - bsdtar->links_cache = NULL; - } - free_cache(bsdtar->uname_cache); bsdtar->uname_cache = NULL; free_cache(bsdtar->gname_cache); bsdtar->gname_cache = NULL; } - -static void -free_buckets(struct bsdtar *bsdtar, struct links_cache *links_cache) -{ - size_t i; - - if (links_cache->buckets == NULL) - return; - - for (i = 0; i < links_cache->number_buckets; i++) { - while (links_cache->buckets[i] != NULL) { - struct links_entry *lp = links_cache->buckets[i]->next; - if (bsdtar->option_warn_links) - bsdtar_warnc(bsdtar, 0, "Missing links to %s", - links_cache->buckets[i]->name); - if (links_cache->buckets[i]->name != NULL) - free(links_cache->buckets[i]->name); - free(links_cache->buckets[i]); - links_cache->buckets[i] = lp; - } - } - free(links_cache->buckets); - links_cache->buckets = NULL; -} - -static void -lookup_hardlink(struct bsdtar *bsdtar, struct archive_entry *entry, - const struct stat *st) -{ - struct links_cache *links_cache; - struct links_entry *le, **new_buckets; - int hash; - size_t i, new_size; - - /* If necessary, initialize the links cache. */ - links_cache = bsdtar->links_cache; - if (links_cache == NULL) { - bsdtar->links_cache = malloc(sizeof(struct links_cache)); - if (bsdtar->links_cache == NULL) - bsdtar_errc(bsdtar, 1, ENOMEM, - "No memory for hardlink detection."); - links_cache = bsdtar->links_cache; - memset(links_cache, 0, sizeof(struct links_cache)); - links_cache->number_buckets = links_cache_initial_size; - links_cache->buckets = malloc(links_cache->number_buckets * - sizeof(links_cache->buckets[0])); - if (links_cache->buckets == NULL) { - bsdtar_errc(bsdtar, 1, ENOMEM, - "No memory for hardlink detection."); - } - for (i = 0; i < links_cache->number_buckets; i++) - links_cache->buckets[i] = NULL; - } - - /* If the links cache overflowed and got flushed, don't bother. */ - if (links_cache->buckets == NULL) - return; - - /* If the links cache is getting too full, enlarge the hash table. */ - if (links_cache->number_entries > links_cache->number_buckets * 2) - { - new_size = links_cache->number_buckets * 2; - new_buckets = malloc(new_size * sizeof(struct links_entry *)); - - if (new_buckets != NULL) { - memset(new_buckets, 0, - new_size * sizeof(struct links_entry *)); - for (i = 0; i < links_cache->number_buckets; i++) { - while (links_cache->buckets[i] != NULL) { - /* Remove entry from old bucket. */ - le = links_cache->buckets[i]; - links_cache->buckets[i] = le->next; - - /* Add entry to new bucket. */ - hash = (le->dev ^ le->ino) % new_size; - - if (new_buckets[hash] != NULL) - new_buckets[hash]->previous = - le; - le->next = new_buckets[hash]; - le->previous = NULL; - new_buckets[hash] = le; - } - } - free(links_cache->buckets); - links_cache->buckets = new_buckets; - links_cache->number_buckets = new_size; - } else { - free_buckets(bsdtar, links_cache); - bsdtar_warnc(bsdtar, ENOMEM, - "No more memory for recording hard links"); - bsdtar_warnc(bsdtar, 0, - "Remaining links will be dumped as full files"); - } - } - - /* Try to locate this entry in the links cache. */ - hash = ( st->st_dev ^ st->st_ino ) % links_cache->number_buckets; - for (le = links_cache->buckets[hash]; le != NULL; le = le->next) { - if (le->dev == st->st_dev && le->ino == st->st_ino) { - archive_entry_copy_hardlink(entry, le->name); - - /* - * Decrement link count each time and release - * the entry if it hits zero. This saves - * memory and is necessary for proper -l - * implementation. - */ - if (--le->links <= 0) { - if (le->previous != NULL) - le->previous->next = le->next; - if (le->next != NULL) - le->next->previous = le->previous; - free(le->name); - if (links_cache->buckets[hash] == le) - links_cache->buckets[hash] = le->next; - links_cache->number_entries--; - free(le); - } - - return; - } - } - - /* Add this entry to the links cache. */ - le = malloc(sizeof(struct links_entry)); - if (le != NULL) - le->name = strdup(archive_entry_pathname(entry)); - if ((le == NULL) || (le->name == NULL)) { - free_buckets(bsdtar, links_cache); - bsdtar_warnc(bsdtar, ENOMEM, - "No more memory for recording hard links"); - bsdtar_warnc(bsdtar, 0, - "Remaining hard links will be dumped as full files"); - if (le != NULL) - free(le); - return; - } - if (links_cache->buckets[hash] != NULL) - links_cache->buckets[hash]->previous = le; - links_cache->number_entries++; - le->next = links_cache->buckets[hash]; - le->previous = NULL; - links_cache->buckets[hash] = le; - le->dev = st->st_dev; - le->ino = st->st_ino; - le->links = st->st_nlink - 1; -} - #ifdef HAVE_POSIX_ACL static void setup_acl(struct bsdtar *bsdtar, struct archive_entry *entry, const char *accpath, |
