summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2021-05-12 11:28:00 +0200
committerYorhel <git@yorhel.nl>2021-05-12 11:28:26 +0200
commitca51d4ed1a0f61042fc43d2a7ae8732351431654 (patch)
treed8b7d56696094f4cd3bd55b4701c7a188cabe7b2
parent9337cdc99e8c205e6e75de25de29a5be53ea858c (diff)
JSON Export: only export ino for hlinks and include hlink count
Bumping the minor version of the file format to '2'. The "ino" field is only interesting for hardlinks, so we can save space by not exporting it for other entries. The hlink count will be interesting later on when I implement tracking of shared data between directories. It's currently ignored on import. The "nlink" field makes the "hlnkc" field redundant, but let's keep including that field anyway for backwards compatibility.
-rw-r--r--src/dir.h2
-rw-r--r--src/dir_export.c18
-rw-r--r--src/dir_import.c15
-rw-r--r--src/dir_mem.c3
-rw-r--r--src/dir_scan.c24
5 files changed, 39 insertions, 23 deletions
diff --git a/src/dir.h b/src/dir.h
index fc33eb0..39bb41f 100644
--- a/src/dir.h
+++ b/src/dir.h
@@ -69,7 +69,7 @@ struct dir_output {
* The function should return non-zero on error, at which point errno is
* assumed to be set to something sensible.
*/
- int (*item)(struct dir *, const char *, struct dir_ext *);
+ int (*item)(struct dir *, const char *, struct dir_ext *, unsigned int);
/* Finalizes the output to go to the next program state or exit ncdu. Called
* after item(NULL) has been called for the root item or before any item()
diff --git a/src/dir_export.c b/src/dir_export.c
index dd96721..83590d4 100644
--- a/src/dir_export.c
+++ b/src/dir_export.c
@@ -74,7 +74,7 @@ static void output_int(uint64_t n) {
}
-static void output_info(struct dir *d, const char *name, struct dir_ext *e) {
+static void output_info(struct dir *d, const char *name, struct dir_ext *e, unsigned int nlink) {
if(!extended_info || !(d->flags & FF_EXT))
e = NULL;
@@ -96,8 +96,6 @@ static void output_info(struct dir *d, const char *name, struct dir_ext *e) {
fputs(",\"dev\":", stream);
output_int(d->dev);
}
- fputs(",\"ino\":", stream);
- output_int(d->ino);
if(e) {
fputs(",\"uid\":", stream);
@@ -111,8 +109,12 @@ static void output_info(struct dir *d, const char *name, struct dir_ext *e) {
}
/* TODO: Including the actual number of links would be nicer. */
- if(d->flags & FF_HLNKC)
- fputs(",\"hlnkc\":true", stream);
+ if(d->flags & FF_HLNKC) {
+ fputs(",\"ino\":", stream);
+ output_int(d->ino);
+ fputs(",\"hlnkc\":true,\"nlink\":", stream);
+ output_int(nlink);
+ }
if(d->flags & FF_ERR)
fputs(",\"read_error\":true", stream);
/* excluded/error'd files are "unknown" with respect to the "notreg" field. */
@@ -136,7 +138,7 @@ static void output_info(struct dir *d, const char *name, struct dir_ext *e) {
* item() call do we check for ferror(). This greatly simplifies the code, but
* assumes that calls to fwrite()/fput./etc don't do any weird stuff when
* called with a stream that's in an error state. */
-static int item(struct dir *item, const char *name, struct dir_ext *ext) {
+static int item(struct dir *item, const char *name, struct dir_ext *ext, unsigned int nlink) {
if(!item) {
nstack_pop(&stack);
if(!stack.top) { /* closing of the root item */
@@ -152,7 +154,7 @@ static int item(struct dir *item, const char *name, struct dir_ext *ext) {
/* File header.
* TODO: Add scan options? */
if(!stack.top) {
- fputs("[1,1,{\"progname\":\""PACKAGE"\",\"progver\":\""PACKAGE_VERSION"\",\"timestamp\":", stream);
+ fputs("[1,2,{\"progname\":\""PACKAGE"\",\"progver\":\""PACKAGE_VERSION"\",\"timestamp\":", stream);
output_int((uint64_t)time(NULL));
fputc('}', stream);
}
@@ -161,7 +163,7 @@ static int item(struct dir *item, const char *name, struct dir_ext *ext) {
if(item->flags & FF_DIR)
fputc('[', stream);
- output_info(item, name, ext);
+ output_info(item, name, ext, nlink);
if(item->flags & FF_DIR)
nstack_push(&stack, item->dev);
diff --git a/src/dir_import.c b/src/dir_import.c
index bde822c..cdb94a0 100644
--- a/src/dir_import.c
+++ b/src/dir_import.c
@@ -72,6 +72,7 @@ static struct ctx {
/* scratch space */
struct dir *buf_dir;
struct dir_ext buf_ext[1];
+ unsigned int nlink;
char buf_name[MAX_VAL];
char val[MAX_VAL];
@@ -421,7 +422,7 @@ static int itemdir(uint64_t dev) {
/* Reads a JSON object representing a struct dir/dir_ext item. Writes to
- * ctx->buf_dir, ctx->buf_ext and ctx->buf_name. */
+ * ctx->buf_dir, ctx->buf_ext, ctx->buf_name and ctx->nlink. */
static int iteminfo(void) {
uint64_t iv;
@@ -470,6 +471,11 @@ static int iteminfo(void) {
ctx->buf_dir->flags |= FF_HLNKC;
} else
C(rlit("false", 5));
+ } else if(strcmp(ctx->val, "nlink") == 0) { /* nlink */
+ C(rint64(&iv, UINT32_MAX));
+ if(iv > 1)
+ ctx->buf_dir->flags |= FF_HLNKC;
+ ctx->nlink = iv;
} else if(strcmp(ctx->val, "read_error") == 0) { /* read_error */
if(*ctx->buf == 't') {
C(rlit("true", 4));
@@ -526,6 +532,7 @@ static int item(uint64_t dev) {
memset(ctx->buf_dir, 0, offsetof(struct dir, name));
memset(ctx->buf_ext, 0, sizeof(struct dir_ext));
+ ctx->nlink = 0;
*ctx->buf_name = 0;
ctx->buf_dir->flags |= isdir ? FF_DIR : FF_FILE;
ctx->buf_dir->dev = dev;
@@ -539,16 +546,16 @@ static int item(uint64_t dev) {
dir_curpath_enter(ctx->buf_name);
if(isdir) {
- if(dir_output.item(ctx->buf_dir, ctx->buf_name, ctx->buf_ext)) {
+ if(dir_output.item(ctx->buf_dir, ctx->buf_name, ctx->buf_ext, ctx->nlink)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
C(itemdir(dev));
- if(dir_output.item(NULL, 0, NULL)) {
+ if(dir_output.item(NULL, 0, NULL, 0)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
- } else if(dir_output.item(ctx->buf_dir, ctx->buf_name, ctx->buf_ext)) {
+ } else if(dir_output.item(ctx->buf_dir, ctx->buf_name, ctx->buf_ext, ctx->nlink)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
diff --git a/src/dir_mem.c b/src/dir_mem.c
index d1dce8d..f513ab7 100644
--- a/src/dir_mem.c
+++ b/src/dir_mem.c
@@ -109,8 +109,9 @@ static void item_add(struct dir *item) {
}
-static int item(struct dir *dir, const char *name, struct dir_ext *ext) {
+static int item(struct dir *dir, const char *name, struct dir_ext *ext, unsigned int nlink) {
struct dir *t, *item;
+ (void)nlink;
/* Go back to parent dir */
if(!dir) {
diff --git a/src/dir_scan.c b/src/dir_scan.c
index 03a582b..9629f32 100644
--- a/src/dir_scan.c
+++ b/src/dir_scan.c
@@ -57,6 +57,7 @@ static uint64_t curdev; /* current device we're scanning on */
/* scratch space */
static struct dir *buf_dir;
static struct dir_ext buf_ext[1];
+static unsigned int buf_nlink;
#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
@@ -120,8 +121,11 @@ static void stat_to_dir(struct stat *fs) {
else if(S_ISDIR(fs->st_mode))
buf_dir->flags |= FF_DIR;
- if(!S_ISDIR(fs->st_mode) && fs->st_nlink > 1)
+ if(!S_ISDIR(fs->st_mode) && fs->st_nlink > 1) {
buf_dir->flags |= FF_HLNKC;
+ buf_nlink = fs->st_nlink;
+ } else
+ buf_nlink = 0;
if(dir_scan_smfs && curdev != buf_dir->dev)
buf_dir->flags |= FF_OTHFS;
@@ -193,7 +197,7 @@ static int dir_scan_recurse(const char *name) {
if(chdir(name)) {
dir_setlasterr(dir_curpath);
buf_dir->flags |= FF_ERR;
- if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) {
+ if(dir_output.item(buf_dir, name, buf_ext, buf_nlink) || dir_output.item(NULL, 0, NULL, 0)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
@@ -203,7 +207,7 @@ static int dir_scan_recurse(const char *name) {
if((dir = dir_read(&fail)) == NULL) {
dir_setlasterr(dir_curpath);
buf_dir->flags |= FF_ERR;
- if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) {
+ if(dir_output.item(buf_dir, name, buf_ext, buf_nlink) || dir_output.item(NULL, 0, NULL, 0)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
@@ -218,12 +222,12 @@ static int dir_scan_recurse(const char *name) {
if(fail)
buf_dir->flags |= FF_ERR;
- if(dir_output.item(buf_dir, name, buf_ext)) {
+ if(dir_output.item(buf_dir, name, buf_ext, buf_nlink)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
fail = dir_walk(dir);
- if(dir_output.item(NULL, 0, NULL)) {
+ if(dir_output.item(NULL, 0, NULL, 0)) {
dir_seterr("Output error: %s", strerror(errno));
return 1;
}
@@ -308,11 +312,11 @@ static int dir_scan_item(const char *name) {
if(buf_dir->flags & FF_DIR && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK)))
fail = dir_scan_recurse(name);
else if(buf_dir->flags & FF_DIR) {
- if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) {
+ if(dir_output.item(buf_dir, name, buf_ext, 0) || dir_output.item(NULL, 0, NULL, 0)) {
dir_seterr("Output error: %s", strerror(errno));
fail = 1;
}
- } else if(dir_output.item(buf_dir, name, buf_ext)) {
+ } else if(dir_output.item(buf_dir, name, buf_ext, buf_nlink)) {
dir_seterr("Output error: %s", strerror(errno));
fail = 1;
}
@@ -333,6 +337,7 @@ static int dir_walk(char *dir) {
dir_curpath_enter(cur);
memset(buf_dir, 0, offsetof(struct dir, name));
memset(buf_ext, 0, sizeof(struct dir_ext));
+ buf_nlink = 0;
fail = dir_scan_item(cur);
dir_curpath_leave();
}
@@ -350,6 +355,7 @@ static int process(void) {
memset(buf_dir, 0, offsetof(struct dir, name));
memset(buf_ext, 0, sizeof(struct dir_ext));
+ buf_nlink = 0;
if((path = path_real(dir_curpath)) == NULL)
dir_seterr("Error obtaining full path: %s", strerror(errno));
@@ -376,13 +382,13 @@ static int process(void) {
buf_dir->flags |= FF_ERR;
stat_to_dir(&fs);
- if(dir_output.item(buf_dir, dir_curpath, buf_ext)) {
+ if(dir_output.item(buf_dir, dir_curpath, buf_ext, buf_nlink)) {
dir_seterr("Output error: %s", strerror(errno));
fail = 1;
}
if(!fail)
fail = dir_walk(dir);
- if(!fail && dir_output.item(NULL, 0, NULL)) {
+ if(!fail && dir_output.item(NULL, 0, NULL, 0)) {
dir_seterr("Output error: %s", strerror(errno));
fail = 1;
}