diff --git a/src/libduc/index.c b/src/libduc/index.c index 5c71f1e..8d71174 100644 --- a/src/libduc/index.c +++ b/src/libduc/index.c @@ -698,6 +698,178 @@ static void read_mounts(duc_index_req *req) } +static int split_parent_child(const char *path, char *parent, size_t parent_len, const char **child_name) +{ + const char *slash = strrchr(path, '/'); + if(slash == NULL || slash[1] == '\0') { + return 0; + } + + if(slash == path) { + snprintf(parent, parent_len, "/"); + } else { + size_t len = slash - path; + if(len >= parent_len) { + len = parent_len - 1; + } + memcpy(parent, path, len); + parent[len] = '\0'; + } + *child_name = slash + 1; + return 1; +} + + +static int update_dir_buffer_entry( + duc *duc, + const char *parent_path, + const char *child_name, + const struct duc_size *size_exact, + const struct duc_size *size_delta, + const struct duc_devino *devino_exact, + struct duc_size *old_size) +{ + struct stat st; + if(lstat(parent_path, &st) != 0) { + return 0; + } + + struct duc_devino parent_devino; + st_to_devino(&st, &parent_devino); + + char key[64]; + int ret = snprintf(key, sizeof(key), "%jx/%jx", + (uintmax_t)parent_devino.dev, (uintmax_t)parent_devino.ino); + if(ret < 0) { + return 0; + } + size_t keyl = (ret >= (int)sizeof(key)) ? (sizeof(key) - 1U) : (size_t)ret; + + size_t vall; + char *val = db_get(duc->db, key, keyl, &vall); + if(val == NULL) { + return 0; + } + + struct buffer *src = buffer_new(val, vall); + char *dst_data = NULL; + if (vall > 0) { + dst_data = malloc(vall); + if (dst_data == NULL) { + buffer_free(src); + return 0; + } + } + struct buffer *dst = buffer_new(dst_data, vall); + dst->len = 0; + dst->ptr = 0; + struct duc_devino devino_parent; + time_t mtime; + int updated = 0; + + if(old_size) { + memset(old_size, 0, sizeof(*old_size)); + } + + buffer_get_dir(src, &devino_parent, &mtime); + buffer_put_dir(dst, &devino_parent, mtime); + + while(src->ptr < src->len) { + struct duc_dirent ent; + memset(&ent, 0, sizeof(ent)); + buffer_get_dirent(src, &ent); + + if(strcmp(ent.name, child_name) == 0 && ent.type == DUC_FILE_TYPE_DIR) { + if(old_size) { + *old_size = ent.size; + } + if(size_exact) { + ent.size = *size_exact; + } else if(size_delta) { + ent.size.apparent += size_delta->apparent; + ent.size.actual += size_delta->actual; + ent.size.count += size_delta->count; + } + if(devino_exact) { + ent.devino = *devino_exact; + } + updated = 1; + } + + buffer_put_dirent(dst, &ent); + duc_free(ent.name); + } + + buffer_free(src); + + if(updated) { + duc_errno err = db_put(duc->db, key, keyl, dst->data, dst->len); + if(err != DUC_OK) { + duc->err = err; + updated = 0; + } + } + + buffer_free(dst); + return updated; +} + + +static void update_parent_buffers(duc *duc, const char *path, const struct duc_index_report *report) +{ + char parent[DUC_PATH_MAX]; + const char *child_name = NULL; + struct duc_size old_size; + + if(!split_parent_child(path, parent, sizeof(parent), &child_name)) { + return; + } + + int updated = update_dir_buffer_entry( + duc, + parent, + child_name, + &report->size, + NULL, + &report->devino, + &old_size + ); + if(!updated) { + return; + } + + struct duc_size delta = { + .actual = report->size.actual - old_size.actual, + .apparent = report->size.apparent - old_size.apparent, + .count = report->size.count - old_size.count, + }; + + while(delta.actual || delta.apparent || delta.count) { + char ancestor[DUC_PATH_MAX]; + const char *ancestor_child_name = NULL; + + if(!split_parent_child(parent, ancestor, sizeof(ancestor), &ancestor_child_name)) { + break; + } + + updated = update_dir_buffer_entry( + duc, + ancestor, + ancestor_child_name, + NULL, + &delta, + NULL, + NULL + ); + if(!updated) { + break; + } + + snprintf(parent, sizeof(parent), "%s", ancestor); + } +} + + struct duc_index_report *duc_index(duc_index_req *req, const char *path, duc_index_flags flags) { duc *duc = req->duc; @@ -759,6 +931,7 @@ struct duc_index_report *duc_index(duc_index_req *req, const char *path, duc_ind if(!(req->flags & DUC_INDEX_DRY_RUN)) { gettimeofday(&report->time_stop, NULL); db_write_report(duc, report); + update_parent_buffers(duc, report->path, report); } free(path_canon); @@ -777,4 +950,4 @@ int duc_index_report_free(struct duc_index_report *rep) /* * End */ - + \ No newline at end of file diff --git a/test.sh b/test.sh index 7668499..a6b66da 100755 --- a/test.sh +++ b/test.sh @@ -201,5 +201,26 @@ else echo "Type checks: failed - got $typecount failures instead of expected 4 out of $typemax." fi -# end +# Re-indexing a subtree should refresh the cached size shown by its parent. +truncate -s 200 ${DUC_TEST_DIR}/tree/sub1/alpha +./duc index --bytes ${DUC_TEST_DIR}/tree/sub1 > ${DUC_TEST_DIR}.out 2>&1 + +if [ "$?" != "0" ]; then + echo "subtree reindex failed" + cat ${DUC_TEST_DIR}.out + exit 1 +fi + +./duc ls -ab ${DUC_TEST_DIR}/tree > ${DUC_TEST_DIR}.out 2>&1 +grep -q "^[[:space:]]*12200 sub1$" ${DUC_TEST_DIR}.out + +if [ "$?" = "0" ]; then + echo "subtree reindex propagation: ok" +else + echo "subtree reindex propagation: failed" + cat ${DUC_TEST_DIR}.out + exit 1 +fi + +# end