version 1.15, 2004/06/02 14:58:46 |
version 1.16, 2004/06/14 18:21:31 |
|
|
#include <err.h> |
#include <err.h> |
#include <errno.h> |
#include <errno.h> |
#include <fts.h> |
#include <fts.h> |
#include <math.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
|
#include <util.h> |
|
|
typedef enum { NONE = 0, KILO, MEGA, GIGA, TERA, PETA /* , EXA */ } unit_t; |
|
|
|
int linkchk(FTSENT *); |
int linkchk(FTSENT *); |
void prtout(quad_t, char *, int); |
void prtout(quad_t, char *, int); |
void usage(void); |
void usage(void); |
unit_t unit_adjust(double *); |
|
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
|
|
exit(rval); |
exit(rval); |
} |
} |
|
|
typedef struct _ID { |
|
dev_t dev; |
|
ino_t inode; |
|
} ID; |
|
|
|
int |
int |
linkchk(FTSENT *p) |
linkchk(FTSENT *p) |
{ |
{ |
static ID *files; |
struct links_entry { |
static int maxfiles, nfiles; |
struct links_entry *next; |
ID *fp, *start; |
struct links_entry *previous; |
ino_t ino; |
int links; |
dev_t dev; |
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; |
|
size_t i, new_size; |
|
int count, hash; |
|
|
ino = p->fts_statp->st_ino; |
st = p->fts_statp; |
dev = p->fts_statp->st_dev; |
|
if ((start = files) != NULL) |
|
for (fp = start + nfiles - 1; fp >= start; --fp) |
|
if (ino == fp->inode && dev == fp->dev) |
|
return (1); |
|
|
|
if (nfiles == maxfiles && (files = realloc((char *)files, |
/* If necessary, initialize the hash table. */ |
(u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) |
if (buckets == NULL) { |
err(1, NULL); |
number_buckets = links_hash_initial_size; |
files[nfiles].inode = ino; |
buckets = malloc(number_buckets * sizeof(buckets[0])); |
files[nfiles].dev = dev; |
if (buckets == NULL) |
++nfiles; |
errx(1, "No memory for hardlink detection"); |
return (0); |
for (i = 0; i < number_buckets; i++) |
} |
buckets[i] = NULL; |
|
} |
|
|
/* |
/* If the hash table is getting too full, enlarge it. */ |
* "human-readable" output: use 3 digits max.--put unit suffixes at |
if (number_entries > number_buckets * 10 && !stop_allocating) { |
* the end. Makes output compact and easy-to-read. |
new_size = number_buckets * 2; |
*/ |
new_buckets = malloc(new_size * sizeof(struct links_entry *)); |
|
count = 0; |
|
|
unit_t |
/* Try releasing the free list to see if that helps. */ |
unit_adjust(double *val) |
if (new_buckets == NULL && free_list != NULL) { |
{ |
while (free_list != NULL) { |
double abval; |
le = free_list; |
unit_t unit; |
free_list = le->next; |
|
free(le); |
|
} |
|
new_buckets = malloc(new_size * sizeof(new_buckets[0])); |
|
} |
|
|
abval = fabs(*val); |
if (new_buckets == NULL) { |
if (abval < 1024) |
stop_allocating = 1; |
unit = NONE; |
warnx("No more memory for tracking hard links"); |
else if (abval < 1048576ULL) { |
} else { |
unit = KILO; |
memset(new_buckets, 0, |
*val /= 1024; |
new_size * sizeof(struct links_entry *)); |
} else if (abval < 1073741824ULL) { |
for (i = 0; i < number_buckets; i++) { |
unit = MEGA; |
while (buckets[i] != NULL) { |
*val /= 1048576; |
/* Remove entry from old bucket. */ |
} else if (abval < 1099511627776ULL) { |
le = buckets[i]; |
unit = GIGA; |
buckets[i] = le->next; |
*val /= 1073741824ULL; |
|
} else if (abval < 1125899906842624ULL) { |
/* Add entry to new bucket. */ |
unit = TERA; |
hash = (le->dev ^ le->ino) % new_size; |
*val /= 1099511627776ULL; |
|
} else /* if (abval < 1152921504606846976ULL) */ { |
if (new_buckets[hash] != NULL) |
unit = PETA; |
new_buckets[hash]->previous = |
*val /= 1125899906842624ULL; |
le; |
|
le->next = new_buckets[hash]; |
|
le->previous = NULL; |
|
new_buckets[hash] = le; |
|
} |
|
} |
|
free(buckets); |
|
buckets = new_buckets; |
|
number_buckets = new_size; |
|
} |
} |
} |
return (unit); |
|
|
/* 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) |
|
return (0); |
|
|
|
/* Add this entry to the links cache. */ |
|
if (free_list != NULL) { |
|
/* Pull a node from the free list if we can. */ |
|
le = free_list; |
|
free_list = le->next; |
|
} else |
|
/* Malloc one if we have to. */ |
|
le = malloc(sizeof(struct links_entry)); |
|
if (le == NULL) { |
|
stop_allocating = 1; |
|
warnx("No more memory for tracking hard links"); |
|
return (0); |
|
} |
|
le->dev = st->st_dev; |
|
le->ino = st->st_ino; |
|
le->links = st->st_nlink - 1; |
|
number_entries++; |
|
le->next = buckets[hash]; |
|
le->previous = NULL; |
|
if (buckets[hash] != NULL) |
|
buckets[hash]->previous = le; |
|
buckets[hash] = le; |
|
return (0); |
} |
} |
|
|
void |
void |
prtout(quad_t size, char *path, int hflag) |
prtout(quad_t size, char *path, int hflag) |
{ |
{ |
unit_t unit; |
|
double bytes; |
|
|
|
if (!hflag) |
if (!hflag) |
(void)printf("%lld\t%s\n", (long long)size, path); |
(void)printf("%lld\t%s\n", (long long)size, path); |
else { |
else { |
bytes = (double)size * 512.0; |
char buf[FMT_SCALED_STRSIZE]; |
unit = unit_adjust(&bytes); |
|
|
|
if (bytes == 0) |
if (fmt_scaled(size * 512, buf) == 0) |
(void)printf("0B\t%s\n", path); |
(void)printf("%s\t%s\n", buf, path); |
else if (bytes > 10) |
|
(void)printf("%.0f%c\t%s\n", bytes, "BKMGTPE"[unit], path); |
|
else |
else |
(void)printf("%.1f%c\t%s\n", bytes, "BKMGTPE"[unit], path); |
(void)printf("%lld\t%s\n", (long long)size, path); |
} |
} |
} |
} |
|
|