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;
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;
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);
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 *,
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);
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));
tree_close(tree);
}
+/*
+ * Backend for write_entry.
+ */
+static void
+write_entry_backend(struct bsdtar *bsdtar, struct archive *a,
+ struct archive_entry *entry, int *fd)
+{
+ int e;
+
+ 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);
+ *fd = -1;
+ }
+}
+
/*
* Add a single filesystem object to the archive.
*/
write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
const char *pathname, const char *accpath)
{
- struct archive_entry *entry;
+ struct archive_entry *entry, *sparse_entry;
int e;
int fd;
#ifdef __linux
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));
if (!S_ISREG(st->st_mode))
archive_entry_set_size(entry, 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));
- }
-
- if (e == ARCHIVE_FATAL)
- exit(1);
+ archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
- /*
- * 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);
+ while (entry != NULL) {
+ write_entry_backend(bsdtar, a, entry, &fd);
+ archive_entry_free(entry);
+ entry = sparse_entry;
+ sparse_entry = NULL;
+ }
cleanup:
if (bsdtar->verbose)
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;
- if (le->name != NULL)
- 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,