a209c3ae12
This patch fixes most of current reasons to generate compiler warnings. The changes consist of: - adding missing casts - hiding or unhiding function declarations - including headers where missing - add __UNCONST when assigning a const char * to a char * - adding missing return statements - changing some types from unsigned to signed, as the code seems to want signed ints - converting old-style function definitions to current style (i.e., void func(param1, param2) short param1, param2; {...} to void func (short param1, short param2) {...}) - making the compiler silent about signed vs unsigned comparisons. We have too many of those in the new libc to fix. A number of bugs in the test set were fixed. These bugs were never triggered with our old libc. Consequently, these tests are now forced to link with the new libc or they will generate errors (in particular tests 43 and 55). Most changes in NetBSD libc are limited to moving aroudn "#ifndef __minix" or stuff related to Minix-specific things (code in sys-minix or gen/minix).
432 lines
11 KiB
C
432 lines
11 KiB
C
/*-
|
|
* 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: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $");
|
|
|
|
#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.h"
|
|
#include "archive_entry.h"
|
|
|
|
/*
|
|
* This is mostly a pretty straightforward hash table implementation.
|
|
* The only interesting bit is the different strategies used to
|
|
* match up links. These strategies match those used by various
|
|
* archiving formats:
|
|
* tar - content stored with first link, remainder refer back to it.
|
|
* This requires us to match each subsequent link up with the
|
|
* first appearance.
|
|
* cpio - Old cpio just stored body with each link, match-ups were
|
|
* implicit. This is trivial.
|
|
* new cpio - New cpio only stores body with last link, match-ups
|
|
* are implicit. This is actually quite tricky; see the notes
|
|
* below.
|
|
*/
|
|
|
|
/* Users pass us a format code, we translate that into a strategy here. */
|
|
#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0
|
|
#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
|
|
#ifndef __minix
|
|
#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
|
|
#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
|
|
#endif
|
|
|
|
/* Initial size of link cache. */
|
|
#define links_cache_initial_size 1024
|
|
|
|
struct links_entry {
|
|
struct links_entry *next;
|
|
struct links_entry *previous;
|
|
int links; /* # links not yet seen */
|
|
int hash;
|
|
struct archive_entry *canonical;
|
|
struct archive_entry *entry;
|
|
};
|
|
|
|
struct archive_entry_linkresolver {
|
|
struct links_entry **buckets;
|
|
struct links_entry *spare;
|
|
unsigned long number_entries;
|
|
size_t number_buckets;
|
|
int strategy;
|
|
};
|
|
|
|
static struct links_entry *find_entry(struct archive_entry_linkresolver *,
|
|
struct archive_entry *);
|
|
static void grow_hash(struct archive_entry_linkresolver *);
|
|
static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
|
|
struct archive_entry *);
|
|
static struct links_entry *next_entry(struct archive_entry_linkresolver *);
|
|
|
|
struct archive_entry_linkresolver *
|
|
archive_entry_linkresolver_new(void)
|
|
{
|
|
struct archive_entry_linkresolver *res;
|
|
size_t i;
|
|
|
|
res = malloc(sizeof(struct archive_entry_linkresolver));
|
|
if (res == NULL)
|
|
return (NULL);
|
|
memset(res, 0, sizeof(struct archive_entry_linkresolver));
|
|
res->number_buckets = links_cache_initial_size;
|
|
res->buckets = malloc(res->number_buckets *
|
|
sizeof(res->buckets[0]));
|
|
if (res->buckets == NULL) {
|
|
free(res);
|
|
return (NULL);
|
|
}
|
|
for (i = 0; i < res->number_buckets; i++)
|
|
res->buckets[i] = NULL;
|
|
return (res);
|
|
}
|
|
|
|
void
|
|
archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
|
|
int fmt)
|
|
{
|
|
int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
|
|
|
|
switch (fmtbase) {
|
|
#ifndef __minix
|
|
case ARCHIVE_FORMAT_CPIO:
|
|
switch (fmt) {
|
|
case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
|
|
case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
|
|
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
|
|
break;
|
|
default:
|
|
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
|
|
break;
|
|
}
|
|
break;
|
|
#endif
|
|
case ARCHIVE_FORMAT_MTREE:
|
|
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
|
|
break;
|
|
case ARCHIVE_FORMAT_TAR:
|
|
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
|
|
break;
|
|
default:
|
|
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
|
|
{
|
|
struct links_entry *le;
|
|
|
|
if (res == NULL)
|
|
return;
|
|
|
|
if (res->buckets != NULL) {
|
|
while ((le = next_entry(res)) != NULL)
|
|
archive_entry_free(le->entry);
|
|
free(res->buckets);
|
|
res->buckets = NULL;
|
|
}
|
|
free(res);
|
|
}
|
|
|
|
void
|
|
archive_entry_linkify(struct archive_entry_linkresolver *res,
|
|
struct archive_entry **e, struct archive_entry **f)
|
|
{
|
|
struct links_entry *le;
|
|
#ifndef __minix
|
|
struct archive_entry *t;
|
|
#endif
|
|
|
|
*f = NULL; /* Default: Don't return a second entry. */
|
|
|
|
if (*e == NULL) {
|
|
le = next_entry(res);
|
|
if (le != NULL) {
|
|
*e = le->entry;
|
|
le->entry = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* If it has only one link, then we're done. */
|
|
if (archive_entry_nlink(*e) == 1)
|
|
return;
|
|
/* Directories, devices never have hardlinks. */
|
|
if (archive_entry_filetype(*e) == AE_IFDIR
|
|
|| archive_entry_filetype(*e) == AE_IFBLK
|
|
|| archive_entry_filetype(*e) == AE_IFCHR)
|
|
return;
|
|
|
|
switch (res->strategy) {
|
|
case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
|
|
le = find_entry(res, *e);
|
|
if (le != NULL) {
|
|
archive_entry_unset_size(*e);
|
|
archive_entry_copy_hardlink(*e,
|
|
archive_entry_pathname(le->canonical));
|
|
} else
|
|
insert_entry(res, *e);
|
|
return;
|
|
case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
|
|
le = find_entry(res, *e);
|
|
if (le != NULL) {
|
|
archive_entry_copy_hardlink(*e,
|
|
archive_entry_pathname(le->canonical));
|
|
} else
|
|
insert_entry(res, *e);
|
|
return;
|
|
#ifndef __minix
|
|
case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
|
|
/* This one is trivial. */
|
|
return;
|
|
case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
|
|
le = find_entry(res, *e);
|
|
if (le != NULL) {
|
|
/*
|
|
* Put the new entry in le, return the
|
|
* old entry from le.
|
|
*/
|
|
t = *e;
|
|
*e = le->entry;
|
|
le->entry = t;
|
|
/* Make the old entry into a hardlink. */
|
|
archive_entry_unset_size(*e);
|
|
archive_entry_copy_hardlink(*e,
|
|
archive_entry_pathname(le->canonical));
|
|
/* If we ran out of links, return the
|
|
* final entry as well. */
|
|
if (le->links == 0) {
|
|
*f = le->entry;
|
|
le->entry = NULL;
|
|
}
|
|
} else {
|
|
/*
|
|
* If we haven't seen it, tuck it away
|
|
* for future use.
|
|
*/
|
|
le = insert_entry(res, *e);
|
|
le->entry = *e;
|
|
*e = NULL;
|
|
}
|
|
return;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static struct links_entry *
|
|
find_entry(struct archive_entry_linkresolver *res,
|
|
struct archive_entry *entry)
|
|
{
|
|
struct links_entry *le;
|
|
int hash, bucket;
|
|
dev_t dev;
|
|
#ifndef __minix
|
|
int64_t ino;
|
|
#else
|
|
int32_t ino;
|
|
#endif
|
|
|
|
/* Free a held entry. */
|
|
if (res->spare != NULL) {
|
|
archive_entry_free(res->spare->canonical);
|
|
archive_entry_free(res->spare->entry);
|
|
free(res->spare);
|
|
res->spare = NULL;
|
|
}
|
|
|
|
/* If the links cache overflowed and got flushed, don't bother. */
|
|
if (res->buckets == NULL)
|
|
return (NULL);
|
|
|
|
dev = archive_entry_dev(entry);
|
|
#ifndef __minix
|
|
ino = archive_entry_ino64(entry);
|
|
#else
|
|
ino = archive_entry_ino(entry);
|
|
#endif
|
|
hash = (int)(dev ^ ino);
|
|
|
|
/* Try to locate this entry in the links cache. */
|
|
bucket = hash % res->number_buckets;
|
|
for (le = res->buckets[bucket]; le != NULL; le = le->next) {
|
|
#ifndef __minix
|
|
if (le->hash == hash
|
|
&& dev == archive_entry_dev(le->canonical)
|
|
&& ino == archive_entry_ino64(le->canonical)) {
|
|
#else
|
|
if (le->hash == hash
|
|
&& dev == archive_entry_dev(le->canonical)
|
|
&& ino == archive_entry_ino(le->canonical)) {
|
|
#endif
|
|
/*
|
|
* 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);
|
|
/* Remove it from this hash bucket. */
|
|
if (le->previous != NULL)
|
|
le->previous->next = le->next;
|
|
if (le->next != NULL)
|
|
le->next->previous = le->previous;
|
|
if (res->buckets[bucket] == le)
|
|
res->buckets[bucket] = le->next;
|
|
res->number_entries--;
|
|
/* Defer freeing this entry. */
|
|
res->spare = le;
|
|
return (le);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
static struct links_entry *
|
|
next_entry(struct archive_entry_linkresolver *res)
|
|
{
|
|
struct links_entry *le;
|
|
size_t bucket;
|
|
|
|
/* Free a held entry. */
|
|
if (res->spare != NULL) {
|
|
archive_entry_free(res->spare->canonical);
|
|
free(res->spare);
|
|
res->spare = NULL;
|
|
}
|
|
|
|
/* If the links cache overflowed and got flushed, don't bother. */
|
|
if (res->buckets == NULL)
|
|
return (NULL);
|
|
|
|
/* Look for next non-empty bucket in the links cache. */
|
|
for (bucket = 0; bucket < res->number_buckets; bucket++) {
|
|
le = res->buckets[bucket];
|
|
if (le != NULL) {
|
|
/* Remove it from this hash bucket. */
|
|
if (le->next != NULL)
|
|
le->next->previous = le->previous;
|
|
res->buckets[bucket] = le->next;
|
|
res->number_entries--;
|
|
/* Defer freeing this entry. */
|
|
res->spare = le;
|
|
return (le);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
static struct links_entry *
|
|
insert_entry(struct archive_entry_linkresolver *res,
|
|
struct archive_entry *entry)
|
|
{
|
|
struct links_entry *le;
|
|
int hash, bucket;
|
|
|
|
/* Add this entry to the links cache. */
|
|
le = malloc(sizeof(struct links_entry));
|
|
if (le == NULL)
|
|
return (NULL);
|
|
memset(le, 0, sizeof(*le));
|
|
le->canonical = archive_entry_clone(entry);
|
|
|
|
/* If the links cache is getting too full, enlarge the hash table. */
|
|
if (res->number_entries > res->number_buckets * 2)
|
|
grow_hash(res);
|
|
|
|
#ifndef __minix
|
|
hash = archive_entry_dev(entry) ^ archive_entry_ino64(entry);
|
|
#else
|
|
hash = ((int)archive_entry_dev(entry)) ^ ((int)archive_entry_ino(entry));
|
|
#endif
|
|
bucket = hash % res->number_buckets;
|
|
|
|
/* If we could allocate the entry, record it. */
|
|
if (res->buckets[bucket] != NULL)
|
|
res->buckets[bucket]->previous = le;
|
|
res->number_entries++;
|
|
le->next = res->buckets[bucket];
|
|
le->previous = NULL;
|
|
res->buckets[bucket] = le;
|
|
le->hash = hash;
|
|
le->links = archive_entry_nlink(entry) - 1;
|
|
return (le);
|
|
}
|
|
|
|
static void
|
|
grow_hash(struct archive_entry_linkresolver *res)
|
|
{
|
|
struct links_entry *le, **new_buckets;
|
|
size_t new_size;
|
|
size_t i, bucket;
|
|
|
|
/* Try to enlarge the bucket list. */
|
|
new_size = res->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 < res->number_buckets; i++) {
|
|
while (res->buckets[i] != NULL) {
|
|
/* Remove entry from old bucket. */
|
|
le = res->buckets[i];
|
|
res->buckets[i] = le->next;
|
|
|
|
/* Add entry to new bucket. */
|
|
bucket = le->hash % new_size;
|
|
|
|
if (new_buckets[bucket] != NULL)
|
|
new_buckets[bucket]->previous =
|
|
le;
|
|
le->next = new_buckets[bucket];
|
|
le->previous = NULL;
|
|
new_buckets[bucket] = le;
|
|
}
|
|
}
|
|
free(res->buckets);
|
|
res->buckets = new_buckets;
|
|
res->number_buckets = new_size;
|
|
}
|
|
}
|