diff --git a/src/calc.c b/src/calc.c index 8e8209b26b516ab0e5a150c34a0a31b2e599973a..5f6c9cc9dd3d0af319248b182fb7c3d4fcf95872 100644 --- a/src/calc.c +++ b/src/calc.c @@ -124,9 +124,10 @@ void calc_hlink_init(struct dir *d) { } -/* checks an individual file and updates the flags and cicrular linked list */ +/* checks an individual file and updates the flags and cicrular linked list, + * also updates the sizes of the parent dirs */ void calc_hlink_check(struct dir *d) { - struct dir *t; + struct dir *t, *pt, *par; int i; d->flags |= FF_HLNKC; @@ -139,7 +140,22 @@ void calc_hlink_check(struct dir *d) { for(t=t->hlnk; t->hlnk!=d->hlnk; t=t->hlnk) ; t->hlnk = d; - return; + } + + /* now update the sizes of the parent directories, + * This works by only counting this file in the parent directories where this + * file hasn't been counted yet, which can be determined from the hlnk list. + * XXX: This may not be the most efficient algorithm to do this */ + for(i=1,par=d->parent; i&∥ par=par->parent) { + if(d->hlnk) + for(t=d->hlnk; i&&t!=d; t=t->hlnk) + for(pt=t->parent; i&&pt; pt=pt->parent) + if(pt==par) + i=0; + if(i) { + par->size += d->size; + par->asize += d->asize; + } } } @@ -186,27 +202,29 @@ int calc_item(struct dir *par, char *name) { else if(S_ISDIR(fs.st_mode)) d->flags |= FF_DIR; - /* update parent dirs */ + /* update the items count of the parent dirs */ if(!(d->flags & FF_EXL)) for(t=d->parent; t!=NULL; t=t->parent) t->items++; - /* Hard link checking */ - d->ino = fs.st_ino; - d->dev = fs.st_dev; - if(!S_ISDIR(fs.st_mode) && fs.st_nlink > 1) - calc_hlink_check(d); - /* count the size */ if(!(d->flags & FF_EXL || d->flags & FF_OTHFS)) { d->size = fs.st_blocks * S_BLKSIZE; d->asize = fs.st_size; - for(t=d->parent; t!=NULL; t=t->parent) { - t->size += d->size; - t->asize += d->asize; - } + /* only update the sizes of the parents if it's not a hard link */ + if(S_ISDIR(fs.st_mode) || fs.st_nlink <= 1) + for(t=d->parent; t!=NULL; t=t->parent) { + t->size += d->size; + t->asize += d->asize; + } } + /* Hard link checking (also takes care of updating the sizes of the parents) */ + d->ino = fs.st_ino; + d->dev = fs.st_dev; + if(!S_ISDIR(fs.st_mode) && fs.st_nlink > 1) + calc_hlink_check(d); + return 0; } @@ -421,6 +439,7 @@ int calc_process() { if(orig) { t->name = malloc(strlen(orig->name)+1); strcpy(t->name, orig->name); + t->parent = orig->parent; } else { t->name = malloc(strlen(path)+strlen(name)+2); t->name[0] = 0; @@ -434,6 +453,14 @@ int calc_process() { root = t; curdev = fs.st_dev; + /* make sure to count this directory entry in its parents at this point */ + if(orig) + for(t=root->parent; t!=NULL; t=t->parent) { + t->size += root->size; + t->asize += root->asize; + t->items += 1; + } + /* update curpath */ if(strcmp(name, ".")) { if((int)strlen(path)+1 > curpathl) { @@ -468,14 +495,7 @@ int calc_process() { /* update references and free original item */ if(orig) { - root->parent = orig->parent; root->next = orig->next; - for(t=root->parent; t!=NULL; t=t->parent) { - t->size += root->size; - t->asize += root->asize; - t->items += root->items+1; - } - if(orig->parent) { t = orig->parent->sub; if(t == orig) diff --git a/src/global.h b/src/global.h index 47dcbe3902bf9a8dce92baad023c2c381c8a5636..0ca5d437b7b49071b22f7620f4d9f510f49c4b53 100644 --- a/src/global.h +++ b/src/global.h @@ -48,7 +48,9 @@ #define ST_HELP 3 -/* structure representing a file or directory */ +/* structure representing a file or directory + * XXX: probably a good idea to get rid of the custom _t types and use + * fixed-size integers instead, which are much more predictable */ struct dir { struct dir *parent, *next, *sub, *hlnk; char *name; diff --git a/src/util.c b/src/util.c index 8980c2fb174a2d23371af57188da66e3550e4403..ce09fbb7fcb143e8eb2ca07a853b869b811eda19 100644 --- a/src/util.c +++ b/src/util.c @@ -197,7 +197,8 @@ void freedir_rec(struct dir *dr) { void freedir(struct dir *dr) { struct dir *tmp; - /* update sizes of parent directories */ + /* update sizes of parent directories + * XXX: This breaks when the dir contains hard linked files */ tmp = dr; while((tmp = tmp->parent) != NULL) { tmp->size -= dr->size;