2012-08-26 04:41:25 -08:00
|
|
|
/* ncdu - NCurses Disk Usage
|
|
|
|
|
|
2019-02-04 07:30:18 -09:00
|
|
|
Copyright (c) 2007-2019 Yoran Heling
|
2012-08-26 04:41:25 -08:00
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
|
"Software"), to deal in the Software without restriction, including
|
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
|
the following conditions:
|
|
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
|
|
|
in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "global.h"
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
2013-04-24 22:11:39 -08:00
|
|
|
#include <khash.h>
|
|
|
|
|
|
2012-08-26 04:41:25 -08:00
|
|
|
|
|
|
|
|
static struct dir *root; /* root directory struct we're scanning */
|
|
|
|
|
static struct dir *curdir; /* directory item that we're currently adding items to */
|
|
|
|
|
static struct dir *orig; /* original directory, when refreshing an already scanned dir */
|
|
|
|
|
|
|
|
|
|
/* Table of struct dir items with more than one link (in order to detect hard links) */
|
|
|
|
|
#define hlink_hash(d) (kh_int64_hash_func((khint64_t)d->dev) ^ kh_int64_hash_func((khint64_t)d->ino))
|
|
|
|
|
#define hlink_equal(a, b) ((a)->dev == (b)->dev && (a)->ino == (b)->ino)
|
|
|
|
|
KHASH_INIT(hl, struct dir *, char, 0, hlink_hash, hlink_equal);
|
|
|
|
|
static khash_t(hl) *links = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* recursively checks a dir structure for hard links and fills the lookup array */
|
|
|
|
|
static void hlink_init(struct dir *d) {
|
|
|
|
|
struct dir *t;
|
|
|
|
|
|
|
|
|
|
for(t=d->sub; t!=NULL; t=t->next)
|
|
|
|
|
hlink_init(t);
|
|
|
|
|
|
|
|
|
|
if(!(d->flags & FF_HLNKC))
|
|
|
|
|
return;
|
|
|
|
|
int r;
|
|
|
|
|
kh_put(hl, links, d, &r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* checks an individual file for hard links and updates its cicrular linked
|
|
|
|
|
* list, also updates the sizes of the parent dirs */
|
|
|
|
|
static void hlink_check(struct dir *d) {
|
|
|
|
|
struct dir *t, *pt, *par;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* add to links table */
|
|
|
|
|
khiter_t k = kh_put(hl, links, d, &i);
|
|
|
|
|
|
|
|
|
|
/* found in the table? update hlnk */
|
|
|
|
|
if(!i) {
|
2018-11-30 21:53:48 -09:00
|
|
|
t = kh_key(links, k);
|
|
|
|
|
d->hlnk = t->hlnk == NULL ? t : t->hlnk;
|
2012-08-26 04:41:25 -08:00
|
|
|
t->hlnk = d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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) {
|
2012-11-22 03:33:32 -09:00
|
|
|
par->size = adds64(par->size, d->size);
|
|
|
|
|
par->asize = adds64(par->size, d->asize);
|
2012-08-26 04:41:25 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Add item to the correct place in the memory structure */
|
|
|
|
|
static void item_add(struct dir *item) {
|
|
|
|
|
if(!root) {
|
|
|
|
|
root = item;
|
|
|
|
|
/* Make sure that the *root appears to be part of the same dir structure as
|
|
|
|
|
* *orig, otherwise the directory size calculation will be incorrect in the
|
|
|
|
|
* case of hard links. */
|
|
|
|
|
if(orig)
|
|
|
|
|
root->parent = orig->parent;
|
|
|
|
|
} else {
|
|
|
|
|
item->parent = curdir;
|
|
|
|
|
item->next = curdir->sub;
|
|
|
|
|
if(item->next)
|
|
|
|
|
item->next->prev = item;
|
|
|
|
|
curdir->sub = item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-01-23 03:17:06 -09:00
|
|
|
static int item(struct dir *dir, const char *name, struct dir_ext *ext) {
|
|
|
|
|
struct dir *t, *item;
|
2012-08-26 04:41:25 -08:00
|
|
|
|
|
|
|
|
/* Go back to parent dir */
|
2018-01-23 03:17:06 -09:00
|
|
|
if(!dir) {
|
2012-08-26 04:41:25 -08:00
|
|
|
curdir = curdir->parent;
|
2012-08-29 00:27:12 -08:00
|
|
|
return 0;
|
2012-08-26 04:41:25 -08:00
|
|
|
}
|
|
|
|
|
|
2018-01-23 03:17:06 -09:00
|
|
|
if(!root && orig)
|
|
|
|
|
name = orig->name;
|
|
|
|
|
|
2018-01-23 04:59:38 -09:00
|
|
|
if(!extended_info)
|
|
|
|
|
dir->flags &= ~FF_EXT;
|
2019-07-23 01:01:48 -08:00
|
|
|
item = xmalloc(dir->flags & FF_EXT ? dir_ext_memsize(name) : dir_memsize(name));
|
2018-01-23 03:17:06 -09:00
|
|
|
memcpy(item, dir, offsetof(struct dir, name));
|
|
|
|
|
strcpy(item->name, name);
|
2018-01-23 04:59:38 -09:00
|
|
|
if(dir->flags & FF_EXT)
|
2018-01-23 03:17:06 -09:00
|
|
|
memcpy(dir_ext_ptr(item), ext, sizeof(struct dir_ext));
|
|
|
|
|
|
2012-08-26 04:41:25 -08:00
|
|
|
item_add(item);
|
|
|
|
|
|
|
|
|
|
/* Ensure that any next items will go to this directory */
|
|
|
|
|
if(item->flags & FF_DIR)
|
|
|
|
|
curdir = item;
|
|
|
|
|
|
2012-08-26 06:53:37 -08:00
|
|
|
/* Special-case the name of the root item to be empty instead of "/". This is
|
|
|
|
|
* what getpath() expects. */
|
|
|
|
|
if(item == root && strcmp(item->name, "/") == 0)
|
|
|
|
|
item->name[0] = 0;
|
|
|
|
|
|
2012-08-26 04:41:25 -08:00
|
|
|
/* Update stats of parents. Don't update the size/asize fields if this is a
|
|
|
|
|
* possible hard link, because hlnk_check() will take care of it in that
|
|
|
|
|
* case. */
|
|
|
|
|
if(item->flags & FF_HLNKC) {
|
2019-01-20 11:03:38 -09:00
|
|
|
addparentstats(item->parent, 0, 0, 0, 1);
|
2012-08-26 04:41:25 -08:00
|
|
|
hlink_check(item);
|
2019-01-20 11:03:38 -09:00
|
|
|
} else if(item->flags & FF_EXT) {
|
|
|
|
|
addparentstats(item->parent, item->size, item->asize, dir_ext_ptr(item)->mtime, 1);
|
|
|
|
|
} else {
|
|
|
|
|
addparentstats(item->parent, item->size, item->asize, 0, 1);
|
|
|
|
|
}
|
2012-08-26 04:41:25 -08:00
|
|
|
|
|
|
|
|
/* propagate ERR and SERR back up to the root */
|
|
|
|
|
if(item->flags & FF_SERR || item->flags & FF_ERR)
|
|
|
|
|
for(t=item->parent; t; t=t->parent)
|
|
|
|
|
t->flags |= FF_SERR;
|
2012-08-26 06:53:37 -08:00
|
|
|
|
|
|
|
|
dir_output.size = root->size;
|
|
|
|
|
dir_output.items = root->items;
|
2012-08-29 00:27:12 -08:00
|
|
|
|
|
|
|
|
return 0;
|
2012-08-26 04:41:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int final(int fail) {
|
|
|
|
|
kh_destroy(hl, links);
|
|
|
|
|
links = NULL;
|
|
|
|
|
|
|
|
|
|
if(fail) {
|
|
|
|
|
freedir(root);
|
|
|
|
|
if(orig) {
|
|
|
|
|
browse_init(orig);
|
|
|
|
|
return 0;
|
|
|
|
|
} else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* success, update references and free original item */
|
|
|
|
|
if(orig) {
|
|
|
|
|
root->next = orig->next;
|
|
|
|
|
root->prev = orig->prev;
|
|
|
|
|
if(root->parent && root->parent->sub == orig)
|
|
|
|
|
root->parent->sub = root;
|
|
|
|
|
if(root->prev)
|
|
|
|
|
root->prev->next = root;
|
|
|
|
|
if(root->next)
|
|
|
|
|
root->next->prev = root;
|
|
|
|
|
orig->next = orig->prev = NULL;
|
|
|
|
|
freedir(orig);
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-22 03:30:51 -09:00
|
|
|
browse_init(root);
|
2012-08-26 04:41:25 -08:00
|
|
|
dirlist_top(-3);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void dir_mem_init(struct dir *_orig) {
|
|
|
|
|
orig = _orig;
|
|
|
|
|
root = curdir = NULL;
|
|
|
|
|
pstate = ST_CALC;
|
|
|
|
|
|
|
|
|
|
dir_output.item = item;
|
|
|
|
|
dir_output.final = final;
|
2012-08-26 06:53:37 -08:00
|
|
|
dir_output.size = 0;
|
|
|
|
|
dir_output.items = 0;
|
2012-08-26 04:41:25 -08:00
|
|
|
|
|
|
|
|
/* Init hash table for hard link detection */
|
|
|
|
|
links = kh_init(hl);
|
|
|
|
|
if(orig)
|
|
|
|
|
hlink_init(getroot(orig));
|
|
|
|
|
}
|
|
|
|
|
|