Blame libarchive/archive_entry_link_resolver.c

Packit Service 1d0348
/*-
Packit Service 1d0348
 * Copyright (c) 2003-2007 Tim Kientzle
Packit Service 1d0348
 * All rights reserved.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Redistribution and use in source and binary forms, with or without
Packit Service 1d0348
 * modification, are permitted provided that the following conditions
Packit Service 1d0348
 * are met:
Packit Service 1d0348
 * 1. Redistributions of source code must retain the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer.
Packit Service 1d0348
 * 2. Redistributions in binary form must reproduce the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer in the
Packit Service 1d0348
 *    documentation and/or other materials provided with the distribution.
Packit Service 1d0348
 *
Packit Service 1d0348
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit Service 1d0348
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit Service 1d0348
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit Service 1d0348
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 1d0348
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit Service 1d0348
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit Service 1d0348
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit Service 1d0348
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit Service 1d0348
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit Service 1d0348
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 1d0348
 */
Packit Service 1d0348
Packit Service 1d0348
#include "archive_platform.h"
Packit Service 1d0348
__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $");
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_SYS_STAT_H
Packit Service 1d0348
#include <sys/stat.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_ERRNO_H
Packit Service 1d0348
#include <errno.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#include <stdio.h>
Packit Service 1d0348
#ifdef HAVE_STDLIB_H
Packit Service 1d0348
#include <stdlib.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_STRING_H
Packit Service 1d0348
#include <string.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
Packit Service 1d0348
#include "archive.h"
Packit Service 1d0348
#include "archive_entry.h"
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * This is mostly a pretty straightforward hash table implementation.
Packit Service 1d0348
 * The only interesting bit is the different strategies used to
Packit Service 1d0348
 * match up links.  These strategies match those used by various
Packit Service 1d0348
 * archiving formats:
Packit Service 1d0348
 *   tar - content stored with first link, remainder refer back to it.
Packit Service 1d0348
 *       This requires us to match each subsequent link up with the
Packit Service 1d0348
 *       first appearance.
Packit Service 1d0348
 *   cpio - Old cpio just stored body with each link, match-ups were
Packit Service 1d0348
 *       implicit.  This is trivial.
Packit Service 1d0348
 *   new cpio - New cpio only stores body with last link, match-ups
Packit Service 1d0348
 *       are implicit.  This is actually quite tricky; see the notes
Packit Service 1d0348
 *       below.
Packit Service 1d0348
 */
Packit Service 1d0348
Packit Service 1d0348
/* Users pass us a format code, we translate that into a strategy here. */
Packit Service 1d0348
#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR	0
Packit Service 1d0348
#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
Packit Service 1d0348
#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
Packit Service 1d0348
#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
Packit Service 1d0348
Packit Service 1d0348
/* Initial size of link cache. */
Packit Service 1d0348
#define	links_cache_initial_size 1024
Packit Service 1d0348
Packit Service 1d0348
struct links_entry {
Packit Service 1d0348
	struct links_entry	*next;
Packit Service 1d0348
	struct links_entry	*previous;
Packit Service 1d0348
	struct archive_entry	*canonical;
Packit Service 1d0348
	struct archive_entry	*entry;
Packit Service 1d0348
	size_t			 hash;
Packit Service 1d0348
	unsigned int		 links; /* # links not yet seen */
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
struct archive_entry_linkresolver {
Packit Service 1d0348
	struct links_entry	**buckets;
Packit Service 1d0348
	struct links_entry	 *spare;
Packit Service 1d0348
	unsigned long		  number_entries;
Packit Service 1d0348
	size_t			  number_buckets;
Packit Service 1d0348
	int			  strategy;
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
#define	NEXT_ENTRY_DEFERRED	1
Packit Service 1d0348
#define	NEXT_ENTRY_PARTIAL	2
Packit Service 1d0348
#define	NEXT_ENTRY_ALL		(NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)
Packit Service 1d0348
Packit Service 1d0348
static struct links_entry *find_entry(struct archive_entry_linkresolver *,
Packit Service 1d0348
		    struct archive_entry *);
Packit Service 1d0348
static void grow_hash(struct archive_entry_linkresolver *);
Packit Service 1d0348
static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
Packit Service 1d0348
		    struct archive_entry *);
Packit Service 1d0348
static struct links_entry *next_entry(struct archive_entry_linkresolver *,
Packit Service 1d0348
    int);
Packit Service 1d0348
Packit Service 1d0348
struct archive_entry_linkresolver *
Packit Service 1d0348
archive_entry_linkresolver_new(void)
Packit Service 1d0348
{
Packit Service 1d0348
	struct archive_entry_linkresolver *res;
Packit Service 1d0348
Packit Service 1d0348
	/* Check for positive power-of-two */
Packit Service 1d0348
	if (links_cache_initial_size == 0 ||
Packit Service 1d0348
	    (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)
Packit Service 1d0348
		return (NULL);
Packit Service 1d0348
Packit Service 1d0348
	res = calloc(1, sizeof(struct archive_entry_linkresolver));
Packit Service 1d0348
	if (res == NULL)
Packit Service 1d0348
		return (NULL);
Packit Service 1d0348
	res->number_buckets = links_cache_initial_size;
Packit Service 1d0348
	res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));
Packit Service 1d0348
	if (res->buckets == NULL) {
Packit Service 1d0348
		free(res);
Packit Service 1d0348
		return (NULL);
Packit Service 1d0348
	}
Packit Service 1d0348
	return (res);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
void
Packit Service 1d0348
archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
Packit Service 1d0348
    int fmt)
Packit Service 1d0348
{
Packit Service 1d0348
	int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
Packit Service 1d0348
Packit Service 1d0348
	switch (fmtbase) {
Packit Service 1d0348
	case ARCHIVE_FORMAT_7ZIP:
Packit Service 1d0348
	case ARCHIVE_FORMAT_AR:
Packit Service 1d0348
	case ARCHIVE_FORMAT_ZIP:
Packit Service 1d0348
		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
Packit Service 1d0348
		break;
Packit Service 1d0348
	case ARCHIVE_FORMAT_CPIO:
Packit Service 1d0348
		switch (fmt) {
Packit Service 1d0348
		case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
Packit Service 1d0348
		case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
Packit Service 1d0348
			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
Packit Service 1d0348
			break;
Packit Service 1d0348
		default:
Packit Service 1d0348
			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
Packit Service 1d0348
			break;
Packit Service 1d0348
		}
Packit Service 1d0348
		break;
Packit Service 1d0348
	case ARCHIVE_FORMAT_MTREE:
Packit Service 1d0348
		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
Packit Service 1d0348
		break;
Packit Service 1d0348
	case ARCHIVE_FORMAT_ISO9660:
Packit Service 1d0348
	case ARCHIVE_FORMAT_SHAR:
Packit Service 1d0348
	case ARCHIVE_FORMAT_TAR:
Packit Service 1d0348
	case ARCHIVE_FORMAT_XAR:
Packit Service 1d0348
		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
Packit Service 1d0348
		break;
Packit Service 1d0348
	default:
Packit Service 1d0348
		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
Packit Service 1d0348
		break;
Packit Service 1d0348
	}
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
void
Packit Service 1d0348
archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
Packit Service 1d0348
{
Packit Service 1d0348
	struct links_entry *le;
Packit Service 1d0348
Packit Service 1d0348
	if (res == NULL)
Packit Service 1d0348
		return;
Packit Service 1d0348
Packit Service 1d0348
	while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)
Packit Service 1d0348
		archive_entry_free(le->entry);
Packit Service 1d0348
	free(res->buckets);
Packit Service 1d0348
	free(res);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
void
Packit Service 1d0348
archive_entry_linkify(struct archive_entry_linkresolver *res,
Packit Service 1d0348
    struct archive_entry **e, struct archive_entry **f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct links_entry *le;
Packit Service 1d0348
	struct archive_entry *t;
Packit Service 1d0348
Packit Service 1d0348
	*f = NULL; /* Default: Don't return a second entry. */
Packit Service 1d0348
Packit Service 1d0348
	if (*e == NULL) {
Packit Service 1d0348
		le = next_entry(res, NEXT_ENTRY_DEFERRED);
Packit Service 1d0348
		if (le != NULL) {
Packit Service 1d0348
			*e = le->entry;
Packit Service 1d0348
			le->entry = NULL;
Packit Service 1d0348
		}
Packit Service 1d0348
		return;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* If it has only one link, then we're done. */
Packit Service 1d0348
	if (archive_entry_nlink(*e) == 1)
Packit Service 1d0348
		return;
Packit Service 1d0348
	/* Directories, devices never have hardlinks. */
Packit Service 1d0348
	if (archive_entry_filetype(*e) == AE_IFDIR
Packit Service 1d0348
	    || archive_entry_filetype(*e) == AE_IFBLK
Packit Service 1d0348
	    || archive_entry_filetype(*e) == AE_IFCHR)
Packit Service 1d0348
		return;
Packit Service 1d0348
Packit Service 1d0348
	switch (res->strategy) {
Packit Service 1d0348
	case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
Packit Service 1d0348
		le = find_entry(res, *e);
Packit Service 1d0348
		if (le != NULL) {
Packit Service 1d0348
			archive_entry_unset_size(*e);
Packit Service 1d0348
			archive_entry_copy_hardlink(*e,
Packit Service 1d0348
			    archive_entry_pathname(le->canonical));
Packit Service 1d0348
		} else
Packit Service 1d0348
			insert_entry(res, *e);
Packit Service 1d0348
		return;
Packit Service 1d0348
	case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
Packit Service 1d0348
		le = find_entry(res, *e);
Packit Service 1d0348
		if (le != NULL) {
Packit Service 1d0348
			archive_entry_copy_hardlink(*e,
Packit Service 1d0348
			    archive_entry_pathname(le->canonical));
Packit Service 1d0348
		} else
Packit Service 1d0348
			insert_entry(res, *e);
Packit Service 1d0348
		return;
Packit Service 1d0348
	case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
Packit Service 1d0348
		/* This one is trivial. */
Packit Service 1d0348
		return;
Packit Service 1d0348
	case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
Packit Service 1d0348
		le = find_entry(res, *e);
Packit Service 1d0348
		if (le != NULL) {
Packit Service 1d0348
			/*
Packit Service 1d0348
			 * Put the new entry in le, return the
Packit Service 1d0348
			 * old entry from le.
Packit Service 1d0348
			 */
Packit Service 1d0348
			t = *e;
Packit Service 1d0348
			*e = le->entry;
Packit Service 1d0348
			le->entry = t;
Packit Service 1d0348
			/* Make the old entry into a hardlink. */
Packit Service 1d0348
			archive_entry_unset_size(*e);
Packit Service 1d0348
			archive_entry_copy_hardlink(*e,
Packit Service 1d0348
			    archive_entry_pathname(le->canonical));
Packit Service 1d0348
			/* If we ran out of links, return the
Packit Service 1d0348
			 * final entry as well. */
Packit Service 1d0348
			if (le->links == 0) {
Packit Service 1d0348
				*f = le->entry;
Packit Service 1d0348
				le->entry = NULL;
Packit Service 1d0348
			}
Packit Service 1d0348
		} else {
Packit Service 1d0348
			/*
Packit Service 1d0348
			 * If we haven't seen it, tuck it away
Packit Service 1d0348
			 * for future use.
Packit Service 1d0348
			 */
Packit Service 1d0348
			le = insert_entry(res, *e);
Packit Service 1d0348
			if (le == NULL)
Packit Service 1d0348
				/* XXX We should return an error code XXX */
Packit Service 1d0348
				return;
Packit Service 1d0348
			le->entry = *e;
Packit Service 1d0348
			*e = NULL;
Packit Service 1d0348
		}
Packit Service 1d0348
		return;
Packit Service 1d0348
	default:
Packit Service 1d0348
		break;
Packit Service 1d0348
	}
Packit Service 1d0348
	return;
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static struct links_entry *
Packit Service 1d0348
find_entry(struct archive_entry_linkresolver *res,
Packit Service 1d0348
    struct archive_entry *entry)
Packit Service 1d0348
{
Packit Service 1d0348
	struct links_entry	*le;
Packit Service 1d0348
	size_t			 hash, bucket;
Packit Service 1d0348
	dev_t			 dev;
Packit Service 1d0348
	int64_t			 ino;
Packit Service 1d0348
Packit Service 1d0348
	/* Free a held entry. */
Packit Service 1d0348
	if (res->spare != NULL) {
Packit Service 1d0348
		archive_entry_free(res->spare->canonical);
Packit Service 1d0348
		archive_entry_free(res->spare->entry);
Packit Service 1d0348
		free(res->spare);
Packit Service 1d0348
		res->spare = NULL;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	dev = archive_entry_dev(entry);
Packit Service 1d0348
	ino = archive_entry_ino64(entry);
Packit Service 1d0348
	hash = (size_t)(dev ^ ino);
Packit Service 1d0348
Packit Service 1d0348
	/* Try to locate this entry in the links cache. */
Packit Service 1d0348
	bucket = hash & (res->number_buckets - 1);
Packit Service 1d0348
	for (le = res->buckets[bucket]; le != NULL; le = le->next) {
Packit Service 1d0348
		if (le->hash == hash
Packit Service 1d0348
		    && dev == archive_entry_dev(le->canonical)
Packit Service 1d0348
		    && ino == archive_entry_ino64(le->canonical)) {
Packit Service 1d0348
			/*
Packit Service 1d0348
			 * Decrement link count each time and release
Packit Service 1d0348
			 * the entry if it hits zero.  This saves
Packit Service 1d0348
			 * memory and is necessary for detecting
Packit Service 1d0348
			 * missed links.
Packit Service 1d0348
			 */
Packit Service 1d0348
			--le->links;
Packit Service 1d0348
			if (le->links > 0)
Packit Service 1d0348
				return (le);
Packit Service 1d0348
			/* Remove it from this hash bucket. */
Packit Service 1d0348
			if (le->previous != NULL)
Packit Service 1d0348
				le->previous->next = le->next;
Packit Service 1d0348
			if (le->next != NULL)
Packit Service 1d0348
				le->next->previous = le->previous;
Packit Service 1d0348
			if (res->buckets[bucket] == le)
Packit Service 1d0348
				res->buckets[bucket] = le->next;
Packit Service 1d0348
			res->number_entries--;
Packit Service 1d0348
			/* Defer freeing this entry. */
Packit Service 1d0348
			res->spare = le;
Packit Service 1d0348
			return (le);
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	return (NULL);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static struct links_entry *
Packit Service 1d0348
next_entry(struct archive_entry_linkresolver *res, int mode)
Packit Service 1d0348
{
Packit Service 1d0348
	struct links_entry	*le;
Packit Service 1d0348
	size_t			 bucket;
Packit Service 1d0348
Packit Service 1d0348
	/* Free a held entry. */
Packit Service 1d0348
	if (res->spare != NULL) {
Packit Service 1d0348
		archive_entry_free(res->spare->canonical);
Packit Service 1d0348
		archive_entry_free(res->spare->entry);
Packit Service 1d0348
		free(res->spare);
Packit Service 1d0348
		res->spare = NULL;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Look for next non-empty bucket in the links cache. */
Packit Service 1d0348
	for (bucket = 0; bucket < res->number_buckets; bucket++) {
Packit Service 1d0348
		for (le = res->buckets[bucket]; le != NULL; le = le->next) {
Packit Service 1d0348
			if (le->entry != NULL &&
Packit Service 1d0348
			    (mode & NEXT_ENTRY_DEFERRED) == 0)
Packit Service 1d0348
				continue;
Packit Service 1d0348
			if (le->entry == NULL &&
Packit Service 1d0348
			    (mode & NEXT_ENTRY_PARTIAL) == 0)
Packit Service 1d0348
				continue;
Packit Service 1d0348
			/* Remove it from this hash bucket. */
Packit Service 1d0348
			if (le->next != NULL)
Packit Service 1d0348
				le->next->previous = le->previous;
Packit Service 1d0348
			if (le->previous != NULL)
Packit Service 1d0348
				le->previous->next = le->next;
Packit Service 1d0348
			else
Packit Service 1d0348
				res->buckets[bucket] = le->next;
Packit Service 1d0348
			res->number_entries--;
Packit Service 1d0348
			/* Defer freeing this entry. */
Packit Service 1d0348
			res->spare = le;
Packit Service 1d0348
			return (le);
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	return (NULL);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static struct links_entry *
Packit Service 1d0348
insert_entry(struct archive_entry_linkresolver *res,
Packit Service 1d0348
    struct archive_entry *entry)
Packit Service 1d0348
{
Packit Service 1d0348
	struct links_entry *le;
Packit Service 1d0348
	size_t hash, bucket;
Packit Service 1d0348
Packit Service 1d0348
	/* Add this entry to the links cache. */
Packit Service 1d0348
	le = calloc(1, sizeof(struct links_entry));
Packit Service 1d0348
	if (le == NULL)
Packit Service 1d0348
		return (NULL);
Packit Service 1d0348
	le->canonical = archive_entry_clone(entry);
Packit Service 1d0348
Packit Service 1d0348
	/* If the links cache is getting too full, enlarge the hash table. */
Packit Service 1d0348
	if (res->number_entries > res->number_buckets * 2)
Packit Service 1d0348
		grow_hash(res);
Packit Service 1d0348
Packit Service 1d0348
	hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
Packit Service 1d0348
	bucket = hash & (res->number_buckets - 1);
Packit Service 1d0348
Packit Service 1d0348
	/* If we could allocate the entry, record it. */
Packit Service 1d0348
	if (res->buckets[bucket] != NULL)
Packit Service 1d0348
		res->buckets[bucket]->previous = le;
Packit Service 1d0348
	res->number_entries++;
Packit Service 1d0348
	le->next = res->buckets[bucket];
Packit Service 1d0348
	le->previous = NULL;
Packit Service 1d0348
	res->buckets[bucket] = le;
Packit Service 1d0348
	le->hash = hash;
Packit Service 1d0348
	le->links = archive_entry_nlink(entry) - 1;
Packit Service 1d0348
	return (le);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static void
Packit Service 1d0348
grow_hash(struct archive_entry_linkresolver *res)
Packit Service 1d0348
{
Packit Service 1d0348
	struct links_entry *le, **new_buckets;
Packit Service 1d0348
	size_t new_size;
Packit Service 1d0348
	size_t i, bucket;
Packit Service 1d0348
Packit Service 1d0348
	/* Try to enlarge the bucket list. */
Packit Service 1d0348
	new_size = res->number_buckets * 2;
Packit Service 1d0348
	if (new_size < res->number_buckets)
Packit Service 1d0348
		return;
Packit Service 1d0348
	new_buckets = calloc(new_size, sizeof(struct links_entry *));
Packit Service 1d0348
Packit Service 1d0348
	if (new_buckets == NULL)
Packit Service 1d0348
		return;
Packit Service 1d0348
Packit Service 1d0348
	for (i = 0; i < res->number_buckets; i++) {
Packit Service 1d0348
		while (res->buckets[i] != NULL) {
Packit Service 1d0348
			/* Remove entry from old bucket. */
Packit Service 1d0348
			le = res->buckets[i];
Packit Service 1d0348
			res->buckets[i] = le->next;
Packit Service 1d0348
Packit Service 1d0348
			/* Add entry to new bucket. */
Packit Service 1d0348
			bucket = le->hash & (new_size - 1);
Packit Service 1d0348
Packit Service 1d0348
			if (new_buckets[bucket] != NULL)
Packit Service 1d0348
				new_buckets[bucket]->previous = le;
Packit Service 1d0348
			le->next = new_buckets[bucket];
Packit Service 1d0348
			le->previous = NULL;
Packit Service 1d0348
			new_buckets[bucket] = le;
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	free(res->buckets);
Packit Service 1d0348
	res->buckets = new_buckets;
Packit Service 1d0348
	res->number_buckets = new_size;
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
struct archive_entry *
Packit Service 1d0348
archive_entry_partial_links(struct archive_entry_linkresolver *res,
Packit Service 1d0348
    unsigned int *links)
Packit Service 1d0348
{
Packit Service 1d0348
	struct archive_entry	*e;
Packit Service 1d0348
	struct links_entry	*le;
Packit Service 1d0348
Packit Service 1d0348
	/* Free a held entry. */
Packit Service 1d0348
	if (res->spare != NULL) {
Packit Service 1d0348
		archive_entry_free(res->spare->canonical);
Packit Service 1d0348
		archive_entry_free(res->spare->entry);
Packit Service 1d0348
		free(res->spare);
Packit Service 1d0348
		res->spare = NULL;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	le = next_entry(res, NEXT_ENTRY_PARTIAL);
Packit Service 1d0348
	if (le != NULL) {
Packit Service 1d0348
		e = le->canonical;
Packit Service 1d0348
		if (links != NULL)
Packit Service 1d0348
			*links = le->links;
Packit Service 1d0348
		le->canonical = NULL;
Packit Service 1d0348
	} else {
Packit Service 1d0348
		e = NULL;
Packit Service 1d0348
		if (links != NULL)
Packit Service 1d0348
			*links = 0;
Packit Service 1d0348
	}
Packit Service 1d0348
	return (e);
Packit Service 1d0348
}