version 1.16, 2004/06/14 18:21:31 |
version 1.17, 2004/06/21 09:44:31 |
|
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
#include <sys/tree.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <util.h> |
#include <util.h> |
|
|
|
|
exit(rval); |
exit(rval); |
} |
} |
|
|
|
|
|
struct links_entry { |
|
RB_ENTRY(links_entry) entry; |
|
struct links_entry *fnext; |
|
int links; |
|
dev_t dev; |
|
ino_t ino; |
|
}; |
|
|
int |
int |
|
links_cmp(struct links_entry *e1, struct links_entry *e2) |
|
{ |
|
if (e1->dev == e2->dev) { |
|
if (e1->ino == e2->ino) |
|
return (0); |
|
else |
|
return (e1->ino < e2->ino ? -1 : 1); |
|
} |
|
else |
|
return (e1->dev < e2->dev ? -1 : 1); |
|
} |
|
|
|
RB_HEAD(ltree, links_entry) links = RB_INITIALIZER(&links); |
|
|
|
RB_GENERATE(ltree, links_entry, entry, links_cmp); |
|
|
|
|
|
int |
linkchk(FTSENT *p) |
linkchk(FTSENT *p) |
{ |
{ |
struct links_entry { |
static struct links_entry *free_list = NULL; |
struct links_entry *next; |
static int stop_allocating = 0; |
struct links_entry *previous; |
struct links_entry ltmp, *le; |
int links; |
|
dev_t dev; |
|
ino_t ino; |
|
}; |
|
static const size_t links_hash_initial_size = 8192; |
|
static struct links_entry **buckets; |
|
static struct links_entry *free_list; |
|
static size_t number_buckets; |
|
static unsigned long number_entries; |
|
static char stop_allocating; |
|
struct links_entry *le, **new_buckets; |
|
struct stat *st; |
struct stat *st; |
size_t i, new_size; |
|
int count, hash; |
|
|
|
st = p->fts_statp; |
st = p->fts_statp; |
|
|
/* If necessary, initialize the hash table. */ |
ltmp.ino = st->st_ino; |
if (buckets == NULL) { |
ltmp.dev = st->st_dev; |
number_buckets = links_hash_initial_size; |
|
buckets = malloc(number_buckets * sizeof(buckets[0])); |
|
if (buckets == NULL) |
|
errx(1, "No memory for hardlink detection"); |
|
for (i = 0; i < number_buckets; i++) |
|
buckets[i] = NULL; |
|
} |
|
|
|
/* If the hash table is getting too full, enlarge it. */ |
le = RB_FIND(ltree, &links, <mp); |
if (number_entries > number_buckets * 10 && !stop_allocating) { |
if (le != NULL) { |
new_size = number_buckets * 2; |
/* |
new_buckets = malloc(new_size * sizeof(struct links_entry *)); |
* Save memory by releasing an entry when we've seen |
count = 0; |
* all of it's links. |
|
*/ |
/* Try releasing the free list to see if that helps. */ |
if (--le->links <= 0) { |
if (new_buckets == NULL && free_list != NULL) { |
RB_REMOVE(ltree, &links, le); |
while (free_list != NULL) { |
/* Recycle this node through the free list */ |
le = free_list; |
if (stop_allocating) { |
free_list = le->next; |
|
free(le); |
free(le); |
|
} else { |
|
le->fnext = free_list; |
|
free_list = le; |
} |
} |
new_buckets = malloc(new_size * sizeof(new_buckets[0])); |
|
} |
} |
|
return (1); |
if (new_buckets == NULL) { |
|
stop_allocating = 1; |
|
warnx("No more memory for tracking hard links"); |
|
} else { |
|
memset(new_buckets, 0, |
|
new_size * sizeof(struct links_entry *)); |
|
for (i = 0; i < number_buckets; i++) { |
|
while (buckets[i] != NULL) { |
|
/* Remove entry from old bucket. */ |
|
le = buckets[i]; |
|
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(buckets); |
|
buckets = new_buckets; |
|
number_buckets = new_size; |
|
} |
|
} |
} |
|
|
/* Try to locate this entry in the hash table. */ |
|
hash = ( st->st_dev ^ st->st_ino ) % number_buckets; |
|
for (le = buckets[hash]; le != NULL; le = le->next) { |
|
if (le->dev == st->st_dev && le->ino == st->st_ino) { |
|
/* |
|
* Save memory by releasing an entry when we've seen |
|
* all of it's links. |
|
*/ |
|
if (--le->links <= 0) { |
|
if (le->previous != NULL) |
|
le->previous->next = le->next; |
|
if (le->next != NULL) |
|
le->next->previous = le->previous; |
|
if (buckets[hash] == le) |
|
buckets[hash] = le->next; |
|
number_entries--; |
|
/* Recycle this node through the free list */ |
|
if (stop_allocating) { |
|
free(le); |
|
} else { |
|
le->next = free_list; |
|
free_list = le; |
|
} |
|
} |
|
return (1); |
|
} |
|
} |
|
|
|
if (stop_allocating) |
if (stop_allocating) |
return (0); |
return (0); |
|
|
|
|
if (free_list != NULL) { |
if (free_list != NULL) { |
/* Pull a node from the free list if we can. */ |
/* Pull a node from the free list if we can. */ |
le = free_list; |
le = free_list; |
free_list = le->next; |
free_list = le->fnext; |
} else |
} else |
/* Malloc one if we have to. */ |
/* Malloc one if we have to. */ |
le = malloc(sizeof(struct links_entry)); |
le = malloc(sizeof(struct links_entry)); |
|
|
if (le == NULL) { |
if (le == NULL) { |
stop_allocating = 1; |
stop_allocating = 1; |
warnx("No more memory for tracking hard links"); |
warnx("No more memory for tracking hard links"); |
return (0); |
return (0); |
} |
} |
|
|
le->dev = st->st_dev; |
le->dev = st->st_dev; |
le->ino = st->st_ino; |
le->ino = st->st_ino; |
le->links = st->st_nlink - 1; |
le->links = st->st_nlink - 1; |
number_entries++; |
le->fnext = NULL; |
le->next = buckets[hash]; |
|
le->previous = NULL; |
RB_INSERT(ltree, &links, le); |
if (buckets[hash] != NULL) |
|
buckets[hash]->previous = le; |
|
buckets[hash] = le; |
|
return (0); |
return (0); |
} |
} |
|
|