Home | History | Annotate | Line # | Download | only in libarchive
      1 /*-
      2  * Copyright (c) 2003-2007 Tim Kientzle
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
     15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
     18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "archive_platform.h"
     27 
     28 #ifdef HAVE_SYS_STAT_H
     29 #include <sys/stat.h>
     30 #endif
     31 #ifdef HAVE_ERRNO_H
     32 #include <errno.h>
     33 #endif
     34 #include <stdio.h>
     35 #ifdef HAVE_STDLIB_H
     36 #include <stdlib.h>
     37 #endif
     38 #ifdef HAVE_STRING_H
     39 #include <string.h>
     40 #endif
     41 
     42 #include "archive.h"
     43 #include "archive_entry.h"
     44 
     45 /*
     46  * This is mostly a pretty straightforward hash table implementation.
     47  * The only interesting bit is the different strategies used to
     48  * match up links.  These strategies match those used by various
     49  * archiving formats:
     50  *   tar - content stored with first link, remainder refer back to it.
     51  *       This requires us to match each subsequent link up with the
     52  *       first appearance.
     53  *   cpio - Old cpio just stored body with each link, match-ups were
     54  *       implicit.  This is trivial.
     55  *   new cpio - New cpio only stores body with last link, match-ups
     56  *       are implicit.  This is actually quite tricky; see the notes
     57  *       below.
     58  */
     59 
     60 /* Users pass us a format code, we translate that into a strategy here. */
     61 #define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR	0
     62 #define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
     63 #define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
     64 #define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
     65 
     66 /* Initial size of link cache. */
     67 #define	links_cache_initial_size 1024
     68 
     69 struct links_entry {
     70 	struct links_entry	*next;
     71 	struct links_entry	*previous;
     72 	struct archive_entry	*canonical;
     73 	struct archive_entry	*entry;
     74 	size_t			 hash;
     75 	unsigned int		 links; /* # links not yet seen */
     76 };
     77 
     78 struct archive_entry_linkresolver {
     79 	struct links_entry	**buckets;
     80 	struct links_entry	 *spare;
     81 	unsigned long		  number_entries;
     82 	size_t			  number_buckets;
     83 	int			  strategy;
     84 };
     85 
     86 #define	NEXT_ENTRY_DEFERRED	1
     87 #define	NEXT_ENTRY_PARTIAL	2
     88 #define	NEXT_ENTRY_ALL		(NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)
     89 
     90 static struct links_entry *find_entry(struct archive_entry_linkresolver *,
     91 		    struct archive_entry *);
     92 static void grow_hash(struct archive_entry_linkresolver *);
     93 static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
     94 		    struct archive_entry *);
     95 static struct links_entry *next_entry(struct archive_entry_linkresolver *,
     96     int);
     97 
     98 struct archive_entry_linkresolver *
     99 archive_entry_linkresolver_new(void)
    100 {
    101 	struct archive_entry_linkresolver *res;
    102 
    103 	/* Check for positive power-of-two */
    104 	if (links_cache_initial_size == 0 ||
    105 	    (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)
    106 		return (NULL);
    107 
    108 	res = calloc(1, sizeof(struct archive_entry_linkresolver));
    109 	if (res == NULL)
    110 		return (NULL);
    111 	res->number_buckets = links_cache_initial_size;
    112 	res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));
    113 	if (res->buckets == NULL) {
    114 		free(res);
    115 		return (NULL);
    116 	}
    117 	return (res);
    118 }
    119 
    120 void
    121 archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
    122     int fmt)
    123 {
    124 	int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
    125 
    126 	switch (fmtbase) {
    127 	case ARCHIVE_FORMAT_7ZIP:
    128 	case ARCHIVE_FORMAT_AR:
    129 	case ARCHIVE_FORMAT_ZIP:
    130 		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
    131 		break;
    132 	case ARCHIVE_FORMAT_CPIO:
    133 		switch (fmt) {
    134 		case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
    135 		case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
    136 			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
    137 			break;
    138 		default:
    139 			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
    140 			break;
    141 		}
    142 		break;
    143 	case ARCHIVE_FORMAT_MTREE:
    144 		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
    145 		break;
    146 	case ARCHIVE_FORMAT_ISO9660:
    147 	case ARCHIVE_FORMAT_SHAR:
    148 	case ARCHIVE_FORMAT_TAR:
    149 	case ARCHIVE_FORMAT_XAR:
    150 		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
    151 		break;
    152 	default:
    153 		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
    154 		break;
    155 	}
    156 }
    157 
    158 void
    159 archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
    160 {
    161 	struct links_entry *le;
    162 
    163 	if (res == NULL)
    164 		return;
    165 
    166 	while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)
    167 		archive_entry_free(le->entry);
    168 	free(res->buckets);
    169 	free(res);
    170 }
    171 
    172 void
    173 archive_entry_linkify(struct archive_entry_linkresolver *res,
    174     struct archive_entry **e, struct archive_entry **f)
    175 {
    176 	struct links_entry *le;
    177 	struct archive_entry *t;
    178 
    179 	*f = NULL; /* Default: Don't return a second entry. */
    180 
    181 	if (*e == NULL) {
    182 		le = next_entry(res, NEXT_ENTRY_DEFERRED);
    183 		if (le != NULL) {
    184 			*e = le->entry;
    185 			le->entry = NULL;
    186 		}
    187 		return;
    188 	}
    189 
    190 	/* If it has only one link, then we're done. */
    191 	if (archive_entry_nlink(*e) == 1)
    192 		return;
    193 	/* Directories, devices never have hardlinks. */
    194 	if (archive_entry_filetype(*e) == AE_IFDIR
    195 	    || archive_entry_filetype(*e) == AE_IFBLK
    196 	    || archive_entry_filetype(*e) == AE_IFCHR)
    197 		return;
    198 
    199 	switch (res->strategy) {
    200 	case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
    201 		le = find_entry(res, *e);
    202 		if (le != NULL) {
    203 			archive_entry_unset_size(*e);
    204 #if defined(_WIN32) && !defined(__CYGWIN__)
    205 			archive_entry_copy_hardlink_w(*e,
    206 			    archive_entry_pathname_w(le->canonical));
    207 #else
    208 			archive_entry_copy_hardlink(*e,
    209 			    archive_entry_pathname(le->canonical));
    210 #endif
    211 		} else
    212 			insert_entry(res, *e);
    213 		return;
    214 	case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
    215 		le = find_entry(res, *e);
    216 		if (le != NULL) {
    217 #if defined(_WIN32) && !defined(__CYGWIN__)
    218 			archive_entry_copy_hardlink_w(*e,
    219 			    archive_entry_pathname_w(le->canonical));
    220 #else
    221 			archive_entry_copy_hardlink(*e,
    222 			    archive_entry_pathname(le->canonical));
    223 #endif
    224 		} else
    225 			insert_entry(res, *e);
    226 		return;
    227 	case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
    228 		/* This one is trivial. */
    229 		return;
    230 	case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
    231 		le = find_entry(res, *e);
    232 		if (le != NULL) {
    233 			/*
    234 			 * Put the new entry in le, return the
    235 			 * old entry from le.
    236 			 */
    237 			t = *e;
    238 			*e = le->entry;
    239 			le->entry = t;
    240 			/* Make the old entry into a hardlink. */
    241 			archive_entry_unset_size(*e);
    242 #if defined(_WIN32) && !defined(__CYGWIN__)
    243 			archive_entry_copy_hardlink_w(*e,
    244 			    archive_entry_pathname_w(le->canonical));
    245 #else
    246 			archive_entry_copy_hardlink(*e,
    247 			    archive_entry_pathname(le->canonical));
    248 #endif
    249 			/* If we ran out of links, return the
    250 			 * final entry as well. */
    251 			if (le->links == 0) {
    252 				*f = le->entry;
    253 				le->entry = NULL;
    254 			}
    255 		} else {
    256 			/*
    257 			 * If we haven't seen it, tuck it away
    258 			 * for future use.
    259 			 */
    260 			le = insert_entry(res, *e);
    261 			if (le == NULL)
    262 				/* XXX We should return an error code XXX */
    263 				return;
    264 			le->entry = *e;
    265 			*e = NULL;
    266 		}
    267 		return;
    268 	default:
    269 		break;
    270 	}
    271 	return;
    272 }
    273 
    274 static struct links_entry *
    275 find_entry(struct archive_entry_linkresolver *res,
    276     struct archive_entry *entry)
    277 {
    278 	struct links_entry	*le;
    279 	size_t			 hash, bucket;
    280 	dev_t			 dev;
    281 	int64_t			 ino;
    282 
    283 	if (!archive_entry_ino_is_set(entry) || !archive_entry_dev_is_set(entry)) {
    284 		return (NULL);
    285 	}
    286 
    287 	/* Free a held entry. */
    288 	if (res->spare != NULL) {
    289 		archive_entry_free(res->spare->canonical);
    290 		archive_entry_free(res->spare->entry);
    291 		free(res->spare);
    292 		res->spare = NULL;
    293 	}
    294 
    295 	dev = archive_entry_dev(entry);
    296 	ino = archive_entry_ino64(entry);
    297 	hash = (size_t)(dev ^ ino);
    298 
    299 	/* Try to locate this entry in the links cache. */
    300 	bucket = hash & (res->number_buckets - 1);
    301 	for (le = res->buckets[bucket]; le != NULL; le = le->next) {
    302 		if (le->hash == hash
    303 		    && dev == archive_entry_dev(le->canonical)
    304 		    && ino == archive_entry_ino64(le->canonical)) {
    305 			/*
    306 			 * Decrement link count each time and release
    307 			 * the entry if it hits zero.  This saves
    308 			 * memory and is necessary for detecting
    309 			 * missed links.
    310 			 */
    311 			--le->links;
    312 			if (le->links > 0)
    313 				return (le);
    314 			/* Remove it from this hash bucket. */
    315 			if (le->previous != NULL)
    316 				le->previous->next = le->next;
    317 			if (le->next != NULL)
    318 				le->next->previous = le->previous;
    319 			if (res->buckets[bucket] == le)
    320 				res->buckets[bucket] = le->next;
    321 			res->number_entries--;
    322 			/* Defer freeing this entry. */
    323 			res->spare = le;
    324 			return (le);
    325 		}
    326 	}
    327 	return (NULL);
    328 }
    329 
    330 static struct links_entry *
    331 next_entry(struct archive_entry_linkresolver *res, int mode)
    332 {
    333 	struct links_entry	*le;
    334 	size_t			 bucket;
    335 
    336 	/* Free a held entry. */
    337 	if (res->spare != NULL) {
    338 		archive_entry_free(res->spare->canonical);
    339 		archive_entry_free(res->spare->entry);
    340 		free(res->spare);
    341 		res->spare = NULL;
    342 	}
    343 
    344 	/* Look for next non-empty bucket in the links cache. */
    345 	for (bucket = 0; bucket < res->number_buckets; bucket++) {
    346 		for (le = res->buckets[bucket]; le != NULL; le = le->next) {
    347 			if (le->entry != NULL &&
    348 			    (mode & NEXT_ENTRY_DEFERRED) == 0)
    349 				continue;
    350 			if (le->entry == NULL &&
    351 			    (mode & NEXT_ENTRY_PARTIAL) == 0)
    352 				continue;
    353 			/* Remove it from this hash bucket. */
    354 			if (le->next != NULL)
    355 				le->next->previous = le->previous;
    356 			if (le->previous != NULL)
    357 				le->previous->next = le->next;
    358 			else
    359 				res->buckets[bucket] = le->next;
    360 			res->number_entries--;
    361 			/* Defer freeing this entry. */
    362 			res->spare = le;
    363 			return (le);
    364 		}
    365 	}
    366 	return (NULL);
    367 }
    368 
    369 static struct links_entry *
    370 insert_entry(struct archive_entry_linkresolver *res,
    371     struct archive_entry *entry)
    372 {
    373 	struct links_entry *le;
    374 	size_t hash, bucket;
    375 
    376 	if (!archive_entry_ino_is_set(entry) || !archive_entry_dev_is_set(entry)) {
    377 		return (NULL);
    378 	}
    379 
    380 	/* Add this entry to the links cache. */
    381 	le = calloc(1, sizeof(struct links_entry));
    382 	if (le == NULL)
    383 		return (NULL);
    384 	le->canonical = archive_entry_clone(entry);
    385 
    386 	/* If the links cache is getting too full, enlarge the hash table. */
    387 	if (res->number_entries > res->number_buckets * 2)
    388 		grow_hash(res);
    389 
    390 	hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
    391 	bucket = hash & (res->number_buckets - 1);
    392 
    393 	/* If we could allocate the entry, record it. */
    394 	if (res->buckets[bucket] != NULL)
    395 		res->buckets[bucket]->previous = le;
    396 	res->number_entries++;
    397 	le->next = res->buckets[bucket];
    398 	le->previous = NULL;
    399 	res->buckets[bucket] = le;
    400 	le->hash = hash;
    401 	le->links = archive_entry_nlink(entry) - 1;
    402 	return (le);
    403 }
    404 
    405 static void
    406 grow_hash(struct archive_entry_linkresolver *res)
    407 {
    408 	struct links_entry *le, **new_buckets;
    409 	size_t new_size;
    410 	size_t i, bucket;
    411 
    412 	/* Try to enlarge the bucket list. */
    413 	new_size = res->number_buckets * 2;
    414 	if (new_size < res->number_buckets)
    415 		return;
    416 	new_buckets = calloc(new_size, sizeof(struct links_entry *));
    417 
    418 	if (new_buckets == NULL)
    419 		return;
    420 
    421 	for (i = 0; i < res->number_buckets; i++) {
    422 		while (res->buckets[i] != NULL) {
    423 			/* Remove entry from old bucket. */
    424 			le = res->buckets[i];
    425 			res->buckets[i] = le->next;
    426 
    427 			/* Add entry to new bucket. */
    428 			bucket = le->hash & (new_size - 1);
    429 
    430 			if (new_buckets[bucket] != NULL)
    431 				new_buckets[bucket]->previous = le;
    432 			le->next = new_buckets[bucket];
    433 			le->previous = NULL;
    434 			new_buckets[bucket] = le;
    435 		}
    436 	}
    437 	free(res->buckets);
    438 	res->buckets = new_buckets;
    439 	res->number_buckets = new_size;
    440 }
    441 
    442 struct archive_entry *
    443 archive_entry_partial_links(struct archive_entry_linkresolver *res,
    444     unsigned int *links)
    445 {
    446 	struct archive_entry	*e;
    447 	struct links_entry	*le;
    448 
    449 	/* Free a held entry. */
    450 	if (res->spare != NULL) {
    451 		archive_entry_free(res->spare->canonical);
    452 		archive_entry_free(res->spare->entry);
    453 		free(res->spare);
    454 		res->spare = NULL;
    455 	}
    456 
    457 	le = next_entry(res, NEXT_ENTRY_PARTIAL);
    458 	if (le != NULL) {
    459 		e = le->canonical;
    460 		if (links != NULL)
    461 			*links = le->links;
    462 		le->canonical = NULL;
    463 	} else {
    464 		e = NULL;
    465 		if (links != NULL)
    466 			*links = 0;
    467 	}
    468 	return (e);
    469 }
    470