Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 174 additions & 1 deletion src/libduc/index.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -777,4 +950,4 @@ int duc_index_report_free(struct duc_index_report *rep)
/*
* End
*/

23 changes: 22 additions & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down