Link hard linked files together with a circular linked list
The directory sizes are now incorrect as hard links will be counted twice again (as if there wasn't any detection in the first place), but this will get fixed by adding a shared size field. This method of keeping track of hard links is a lot faster and allows adding an interface which lists the found links.
Этот коммит содержится в:
родитель
101731ed13
Коммит
88cd199d94
@ -176,7 +176,7 @@ void browse_draw_item(struct dir *n, int row, off_t max, int ispar) {
|
||||
n->flags & FF_ERR ? '!' :
|
||||
n->flags & FF_SERR ? '.' :
|
||||
n->flags & FF_OTHFS ? '>' :
|
||||
n->flags & FF_HLNK ? 'H' :
|
||||
n->flags & FF_HLNKC ? 'H' :
|
||||
!(n->flags & FF_FILE
|
||||
|| n->flags & FF_DIR) ? '@' :
|
||||
n->flags & FF_DIR
|
||||
|
80
src/calc.c
80
src/calc.c
@ -55,6 +55,9 @@ dev_t curdev; /* current device we're calculating on */
|
||||
int anpos; /* position of the animation string */
|
||||
int curpathl = 0, lasterrl = 0;
|
||||
|
||||
struct dir **links = NULL;
|
||||
int linksl, linkst;
|
||||
|
||||
|
||||
|
||||
/* adds name to curpath */
|
||||
@ -84,6 +87,63 @@ void calc_leavepath() {
|
||||
}
|
||||
|
||||
|
||||
/* looks in the links list and adds the file when not found */
|
||||
int calc_hlink_add(struct dir *d) {
|
||||
int i;
|
||||
/* check the list */
|
||||
for(i=0; i<linkst; i++)
|
||||
if(links[i]->dev == d->dev && links[i]->ino == d->ino)
|
||||
break;
|
||||
/* found, do nothing and return the index */
|
||||
if(i != linkst)
|
||||
return i;
|
||||
/* otherwise, add to list and return -1 */
|
||||
if(++linkst > linksl) {
|
||||
linksl *= 2;
|
||||
if(!linksl) {
|
||||
linksl = 64;
|
||||
links = malloc(linksl*sizeof(struct dir *));
|
||||
} else
|
||||
links = realloc(links, linksl*sizeof(struct dir *));
|
||||
}
|
||||
links[linkst-1] = d;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* recursively checks a dir structure for hard links and fills the lookup array */
|
||||
void calc_hlink_init(struct dir *d) {
|
||||
struct dir *t;
|
||||
|
||||
for(t=d->sub; t!=NULL; t=t->next)
|
||||
calc_hlink_init(t);
|
||||
|
||||
if(!(d->flags & FF_HLNKC))
|
||||
return;
|
||||
calc_hlink_add(d);
|
||||
}
|
||||
|
||||
|
||||
/* checks an individual file and updates the flags and cicrular linked list */
|
||||
void calc_hlink_check(struct dir *d) {
|
||||
struct dir *t;
|
||||
int i;
|
||||
|
||||
d->flags |= FF_HLNKC;
|
||||
i = calc_hlink_add(d);
|
||||
|
||||
/* found in the list? update hlnk */
|
||||
if(i >= 0) {
|
||||
t = d->hlnk = links[i];
|
||||
if(t->hlnk != NULL)
|
||||
for(t=t->hlnk; t->hlnk!=d->hlnk; t=t->hlnk)
|
||||
;
|
||||
t->hlnk = d;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int calc_item(struct dir *par, char *name) {
|
||||
struct dir *t, *d;
|
||||
struct stat fs;
|
||||
@ -131,11 +191,11 @@ int calc_item(struct dir *par, char *name) {
|
||||
for(t=d->parent; t!=NULL; t=t->parent)
|
||||
t->items++;
|
||||
|
||||
/* Provide the necessary information for hard link checking */
|
||||
/* Hard link checking */
|
||||
d->ino = fs.st_ino;
|
||||
d->dev = fs.st_dev;
|
||||
if(!S_ISDIR(fs.st_mode) && fs.st_nlink > 1)
|
||||
d->flags |= FF_HLNKC;
|
||||
calc_hlink_check(d);
|
||||
|
||||
/* count the size */
|
||||
if(!(d->flags & FF_EXL || d->flags & FF_OTHFS)) {
|
||||
@ -309,6 +369,14 @@ int calc_process() {
|
||||
struct dir *t;
|
||||
int n;
|
||||
|
||||
/* create initial links array */
|
||||
linksl = linkst = 0;
|
||||
if(orig) {
|
||||
for(t=orig; t->parent!=NULL; t=t->parent)
|
||||
;
|
||||
calc_hlink_init(t);
|
||||
}
|
||||
|
||||
/* check root directory */
|
||||
if((path = path_real(curpath)) == NULL) {
|
||||
failed = 1;
|
||||
@ -384,6 +452,9 @@ int calc_process() {
|
||||
free(name);
|
||||
free(path);
|
||||
|
||||
if(links != NULL)
|
||||
free(links);
|
||||
|
||||
/* success */
|
||||
if(!n && !failed) {
|
||||
if(root->sub == NULL) {
|
||||
@ -414,8 +485,6 @@ int calc_process() {
|
||||
}
|
||||
freedir(orig);
|
||||
}
|
||||
|
||||
link_del(root);
|
||||
browse_init(root->sub);
|
||||
return 0;
|
||||
}
|
||||
@ -436,8 +505,7 @@ calc_fail:
|
||||
|
||||
void calc_init(char *dir, struct dir *org) {
|
||||
failed = anpos = 0;
|
||||
if((orig = org) != NULL)
|
||||
link_add(orig);
|
||||
orig = org;
|
||||
if(curpathl == 0) {
|
||||
curpathl = strlen(dir)+1;
|
||||
curpath = malloc(curpathl);
|
||||
|
10
src/delete.c
10
src/delete.c
@ -222,19 +222,13 @@ void delete_process() {
|
||||
return;
|
||||
}
|
||||
|
||||
/* temporarily re-add hard links, so we won't lose sizes in case we delete
|
||||
a file of which another file outside this directory was marked as duplicate */
|
||||
link_add(root);
|
||||
|
||||
/* chdir */
|
||||
if(path_chdir(getpath(root->parent)) < 0) {
|
||||
state = DS_FAILED;
|
||||
lasterrno = errno;
|
||||
while(state == DS_FAILED)
|
||||
if(input_handle(0)) {
|
||||
link_del(root);
|
||||
if(input_handle(0))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* delete */
|
||||
@ -247,8 +241,6 @@ void delete_process() {
|
||||
nextsel->flags |= FF_BSEL;
|
||||
browse_init(n);
|
||||
}
|
||||
if(n != NULL)
|
||||
link_del(n);
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,9 +38,8 @@
|
||||
#define FF_OTHFS 0x08 /* excluded because it was an other filesystem */
|
||||
#define FF_EXL 0x10 /* excluded using exlude patterns */
|
||||
#define FF_SERR 0x20 /* error in subdirectory */
|
||||
#define FF_HLNK 0x40 /* hard link (same file already encountered before) */
|
||||
#define FF_HLNKC 0x80 /* hard link candidate (file with st_nlink > 1) */
|
||||
#define FF_BSEL 0x100 /* selected */
|
||||
#define FF_HLNKC 0x40 /* hard link candidate (file with st_nlink > 1) */
|
||||
#define FF_BSEL 0x80 /* selected */
|
||||
|
||||
/* Program states */
|
||||
#define ST_CALC 0
|
||||
@ -51,11 +50,11 @@
|
||||
|
||||
/* structure representing a file or directory */
|
||||
struct dir {
|
||||
struct dir *parent, *next, *sub;
|
||||
struct dir *parent, *next, *sub, *hlnk;
|
||||
char *name;
|
||||
off_t size, asize;
|
||||
unsigned long items;
|
||||
unsigned short flags;
|
||||
unsigned char flags;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
};
|
||||
|
92
src/util.c
92
src/util.c
@ -169,10 +169,23 @@ void ncprint(int r, int c, char *fmt, ...) {
|
||||
}
|
||||
|
||||
|
||||
/* removes item from the hlnk circular linked list */
|
||||
void freedir_hlnk(struct dir *d) {
|
||||
struct dir *t;
|
||||
if(!d->hlnk)
|
||||
return;
|
||||
for(t=d->hlnk; t->hlnk!=d; t=t->hlnk)
|
||||
;
|
||||
t->hlnk = d->hlnk;
|
||||
}
|
||||
|
||||
|
||||
void freedir_rec(struct dir *dr) {
|
||||
struct dir *tmp, *tmp2;
|
||||
tmp2 = dr;
|
||||
while((tmp = tmp2) != NULL) {
|
||||
freedir_hlnk(tmp);
|
||||
/* remove item */
|
||||
if(tmp->sub) freedir_rec(tmp->sub);
|
||||
free(tmp->name);
|
||||
tmp2 = tmp->next;
|
||||
@ -208,6 +221,7 @@ void freedir(struct dir *dr) {
|
||||
tmp->next = dr->next;
|
||||
}
|
||||
|
||||
freedir_hlnk(dr);
|
||||
free(dr->name);
|
||||
free(dr);
|
||||
}
|
||||
@ -249,81 +263,3 @@ char *getpath(struct dir *cur) {
|
||||
return getpathdat;
|
||||
}
|
||||
|
||||
|
||||
/* act = 0 -> just fill the links array
|
||||
act = 1 -> fill array and remove duplicates
|
||||
act = -1 -> use array to re-add duplicates */
|
||||
void link_list_rec(struct dir *d, int act) {
|
||||
struct dir *t;
|
||||
int i;
|
||||
|
||||
/* recursion, check sub directories */
|
||||
for(t=d->sub; t!=NULL; t=t->next)
|
||||
link_list_rec(t, act);
|
||||
|
||||
/* not a link candidate? ignore */
|
||||
if(!(d->flags & FF_HLNKC))
|
||||
return;
|
||||
|
||||
/* check against what we've found so far */
|
||||
for(i=0; i<linkst; i++)
|
||||
if(links[i]->dev == d->dev && links[i]->ino == d->ino)
|
||||
break;
|
||||
|
||||
/* found in the list, set link flag and set size to zero */
|
||||
if(act == 1 && i != linkst) {
|
||||
d->flags |= FF_HLNK;
|
||||
for(t=d->parent; t!=NULL; t=t->parent) {
|
||||
t->size -= d->size;
|
||||
t->asize -= d->asize;
|
||||
}
|
||||
d->size = d->asize = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* found in the list, reset flag and re-add size */
|
||||
if(act == -1 && i != linkst && d->flags & FF_HLNK) {
|
||||
d->flags -= FF_HLNK;
|
||||
d->size = links[i]->size;
|
||||
d->asize = links[i]->asize;
|
||||
for(t=d->parent; t!=NULL; t=t->parent) {
|
||||
t->size += d->size;
|
||||
t->asize += d->asize;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found, add to the list */
|
||||
if(act == 1 || (act == 0 && !(d->flags & FF_HLNK))) {
|
||||
if(++linkst > linksl) {
|
||||
linksl *= 2;
|
||||
if(!linksl) {
|
||||
linksl = 64;
|
||||
links = malloc(linksl*sizeof(struct dir *));
|
||||
} else
|
||||
links = realloc(links, linksl*sizeof(struct dir *));
|
||||
}
|
||||
links[i] = d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void link_del(struct dir *par) {
|
||||
while(par->parent != NULL)
|
||||
par = par->parent;
|
||||
link_list_rec(par, 1);
|
||||
linkst = 0;
|
||||
}
|
||||
|
||||
|
||||
void link_add(struct dir *par) {
|
||||
while(par->parent != NULL)
|
||||
par = par->parent;
|
||||
/* In order to correctly re-add the duplicates, we'll have to pass the entire
|
||||
tree twice, one time to get a list of all links, second time to re-add them */
|
||||
link_list_rec(par, 0);
|
||||
link_list_rec(par, -1);
|
||||
linkst = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -78,11 +78,5 @@ void freedir(struct dir *);
|
||||
returned pointer will be overwritten with a subsequent call */
|
||||
char *getpath(struct dir *);
|
||||
|
||||
/* removes all hard links from a tree */
|
||||
void link_del(struct dir *);
|
||||
|
||||
/* re-adds all hard links in a tree */
|
||||
void link_add(struct dir *);
|
||||
|
||||
#endif
|
||||
|
||||
|
Загрузка…
Ссылка в новой задаче
Block a user