summaryrefslogtreecommitdiffstats
path: root/lib/libarchive/archive_entry_link_resolver.c
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2007-12-30 04:58:22 +0000
committerkientzle <kientzle@FreeBSD.org>2007-12-30 04:58:22 +0000
commit2b8395148ff378e52a5e00c0d2ac0123258b759b (patch)
tree743c28601a2502c30676ab2adfbbb665fa3f38b7 /lib/libarchive/archive_entry_link_resolver.c
parentb1a3a611be764b44dbdd47a17ec5076bcfbe6e88 (diff)
downloadFreeBSD-src-2b8395148ff378e52a5e00c0d2ac0123258b759b.zip
FreeBSD-src-2b8395148ff378e52a5e00c0d2ac0123258b759b.tar.gz
Update libarchive to 2.4.10. This includes a number of improvements
that I've been working on but put off committing until after the RELENG_7 branch, including: * New manpages: cpio.5 mtree.5 * New archive_entry_strmode() * New archive_entry_link_resolver() * New read support: mtree format * Internal API change: read format auction only runs once * Running the auction only once allowed simplifying a lot of bid logic. * Cpio robustness: search for next header after a sync error * Support device nodes on ISO9660 images * Eliminate a lot of unnecessary copies for uncompressed archives * Corrected handling of new GNU --sparse --posix formats * Correctly handle a zero-byte write to a compressed archive * Fixed memory leaks Many of these improvements were motivated by the upcoming bsdcpio front-end. There have also been extensive improvements to the libarchive_test test harness, which I'll commit separately.
Diffstat (limited to 'lib/libarchive/archive_entry_link_resolver.c')
-rw-r--r--lib/libarchive/archive_entry_link_resolver.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/lib/libarchive/archive_entry_link_resolver.c b/lib/libarchive/archive_entry_link_resolver.c
new file mode 100644
index 0000000..0f07e06
--- /dev/null
+++ b/lib/libarchive/archive_entry_link_resolver.c
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive_entry.h"
+
+/* Initial size of link cache. */
+#define links_cache_initial_size 1024
+
+struct archive_entry_linkresolver {
+ char *last_name;
+ 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 archive_entry_linkresolver *
+archive_entry_linkresolver_new(void)
+{
+ struct archive_entry_linkresolver *links_cache;
+ size_t i;
+
+ links_cache = malloc(sizeof(struct archive_entry_linkresolver));
+ if (links_cache == NULL)
+ return (NULL);
+ memset(links_cache, 0, sizeof(struct archive_entry_linkresolver));
+ 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) {
+ free(links_cache);
+ return (NULL);
+ }
+ for (i = 0; i < links_cache->number_buckets; i++)
+ links_cache->buckets[i] = NULL;
+ return (links_cache);
+}
+
+void
+archive_entry_linkresolver_free(struct archive_entry_linkresolver *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 (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;
+}
+
+const char *
+archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache,
+ struct archive_entry *entry)
+{
+ struct links_entry *le, **new_buckets;
+ int hash;
+ size_t i, new_size;
+ dev_t dev;
+ ino_t ino;
+ int nlinks;
+
+
+ /* Free a held name. */
+ free(links_cache->last_name);
+ links_cache->last_name = NULL;
+
+ /* If the links cache overflowed and got flushed, don't bother. */
+ if (links_cache->buckets == NULL)
+ return (NULL);
+
+ dev = archive_entry_dev(entry);
+ ino = archive_entry_ino(entry);
+ nlinks = archive_entry_nlink(entry);
+
+ /* An entry with one link can't be a hard link. */
+ if (nlinks == 1)
+ return (NULL);
+
+ /* If the links cache is getting too full, enlarge the hash table. */
+ if (links_cache->number_entries > links_cache->number_buckets * 2)
+ {
+ /* Try to enlarge the bucket list. */
+ 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;
+ }
+ }
+
+ /* Try to locate this entry in the links cache. */
+ hash = ( dev ^ ino ) % links_cache->number_buckets;
+ for (le = links_cache->buckets[hash]; le != NULL; le = le->next) {
+ if (le->dev == dev && le->ino == ino) {
+ /*
+ * Decrement link count each time and release
+ * the entry if it hits zero. This saves
+ * memory and is necessary for detecting
+ * missed links.
+ */
+ --le->links;
+ if (le->links > 0)
+ return (le->name);
+ /*
+ * When we release the entry, save the name
+ * until the next call.
+ */
+ links_cache->last_name = le->name;
+ /*
+ * Release the entry.
+ */
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (links_cache->buckets[hash] == le)
+ links_cache->buckets[hash] = le->next;
+ links_cache->number_entries--;
+ free(le);
+ return (links_cache->last_name);
+ }
+ }
+
+ /* Add this entry to the links cache. */
+ le = malloc(sizeof(struct links_entry));
+ if (le == NULL)
+ return (NULL);
+ le->name = strdup(archive_entry_pathname(entry));
+ if (le->name == NULL) {
+ free(le);
+ return (NULL);
+ }
+
+ /* If we could allocate the entry, record it. */
+ 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 = dev;
+ le->ino = ino;
+ le->links = nlinks - 1;
+ return (NULL);
+}
OpenPOWER on IntegriCloud