Skip to content

Commit 2fff24a

Browse files
fangfufugoogle-labs-jules[bot]gemini-code-assist[bot]
committed
feat: add FUSE releasedir and LinkTable eviction
Add FUSE .opendir and .releasedir handlers to safely evict LinkTable directories from memory when they are no longer in use. To avoid crashing other subsystems that might currently reference files within them (like cache or stat routines), this patch augments LinkTable with an atomic reference-counting (refcount) memory management strategy. Whenever path_to_Link or path_to_LinkTable resolves a table, it increases its reference count. Transient users subsequently release the reference once finished. When the directory is no longer directly open, fs_releasedir marks it as orphaned, after which its associated structures are recursively freed at the very moment its last reference drops to zero. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: fangfufu <2323403+fangfufu@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 68dd7bb commit 2fff24a

4 files changed

Lines changed: 176 additions & 34 deletions

File tree

src/cache.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,9 +622,12 @@ static int Cache_exist(const char *fn)
622622
*/
623623
void Cache_delete(const char *fn)
624624
{
625+
Link *link = NULL;
625626
if (CONFIG.mode == SONIC) {
626-
Link *link = path_to_Link(fn);
627+
link = path_to_Link(fn);
627628
fn = link->sonic.id;
629+
} else {
630+
link = path_to_Link(fn);
628631
}
629632

630633
char *metafn = path_append(META_DIR, fn);
@@ -638,6 +641,9 @@ void Cache_delete(const char *fn)
638641
}
639642
FREE(metafn);
640643
FREE(datafn);
644+
if (link) {
645+
LinkTable_unref(link->parent_table);
646+
}
641647
}
642648

643649
/**
@@ -766,6 +772,8 @@ int Cache_create(const char *path)
766772
curl_free(fn);
767773
}
768774

775+
LinkTable_unref(this_link->parent_table);
776+
769777
return res;
770778
}
771779

@@ -791,6 +799,7 @@ Cache *Cache_open(const char *fn)
791799
lprintf(cache_lock_debug, "thread %lx: unlocking cf_lock;\n",
792800
(unsigned long)pthread_self());
793801
PTHREAD_MUTEX_UNLOCK(&cf_lock);
802+
LinkTable_unref(link->parent_table);
794803
return link->cache_ptr;
795804
}
796805

@@ -809,6 +818,7 @@ Cache *Cache_open(const char *fn)
809818
lprintf(cache_lock_debug, "thread %lx: unlocking cf_lock;\n",
810819
(unsigned long)pthread_self());
811820
PTHREAD_MUTEX_UNLOCK(&cf_lock);
821+
LinkTable_unref(link->parent_table);
812822
return NULL;
813823
}
814824
}
@@ -887,6 +897,7 @@ Cache *Cache_open(const char *fn)
887897
lprintf(cache_lock_debug, "thread %lx: unlocking cf_lock;\n",
888898
(unsigned long)pthread_self());
889899
PTHREAD_MUTEX_UNLOCK(&cf_lock);
900+
LinkTable_unref(link->parent_table);
890901
return NULL;
891902
}
892903

@@ -928,12 +939,14 @@ void Cache_close(Cache *cf)
928939
lprintf(error, "cannot close data file %s.\n", strerror(errno));
929940
}
930941

931-
cf->link->cache_ptr = NULL;
942+
Link *link = cf->link;
943+
link->cache_ptr = NULL;
932944

933945
lprintf(cache_lock_debug,
934946
"thread %lx: unlocking cf_lock, cache closed: %s\n",
935947
(unsigned long)pthread_self(), cf->path);
936948
Cache_free(cf);
949+
LinkTable_unref(link->parent_table);
937950
PTHREAD_MUTEX_UNLOCK(&cf_lock);
938951
}
939952

src/fuse_local.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ static int fs_getattr(const char *path, struct stat *stbuf,
7373
stbuf->st_blocks = (link->content_length) / 512;
7474
break;
7575
default:
76+
LinkTable_unref(link->parent_table);
7677
return -ENOENT;
7778
}
79+
LinkTable_unref(link->parent_table);
7880
}
7981
stbuf->st_uid = getuid();
8082
stbuf->st_gid = getgid();
@@ -105,6 +107,7 @@ static int fs_open(const char *path, struct fuse_file_info *fi)
105107
}
106108
lprintf(debug, "%s found.\n", path);
107109
if ((fi->flags & O_RDWR) != O_RDONLY) {
110+
LinkTable_unref(link->parent_table);
108111
return -EROFS;
109112
}
110113
if (CACHE_SYSTEM_INIT) {
@@ -115,33 +118,47 @@ static int fs_open(const char *path, struct fuse_file_info *fi)
115118
*/
116119
if (!fi->fh) {
117120
lprintf(fatal, "Cache file creation failure for %s.\n", path);
121+
LinkTable_unref(link->parent_table);
118122
return -ENOENT;
119123
}
120124
}
125+
LinkTable_unref(link->parent_table);
126+
return 0;
127+
}
128+
129+
static int fs_opendir(const char *path, struct fuse_file_info *fi)
130+
{
131+
LinkTable *linktbl = path_to_LinkTable(path);
132+
if (!linktbl) {
133+
return -ENOENT;
134+
}
135+
fi->fh = (uint64_t)linktbl;
136+
return 0;
137+
}
138+
139+
static int fs_releasedir(const char *path, struct fuse_file_info *fi)
140+
{
141+
LinkTable *linktbl = (LinkTable *)fi->fh;
142+
if (linktbl) {
143+
if (strcmp(path, "/") != 0) {
144+
LinkTable_mark_orphaned(linktbl);
145+
}
146+
LinkTable_unref(linktbl);
147+
}
121148
return 0;
122149
}
123150

124151
/**
125152
* \brief read the directory indicated by the path
126-
* \note
127-
* - releasedir() is not implemented, because I don't see why anybody want
128-
* the LinkTables to be evicted from the memory during the runtime of this
129-
* program. If you want to evict LinkTables, just unmount the filesystem.
130-
* - There is no real need to associate the LinkTable with the fi of each
131-
* directory data structure. If you want a deep level directory, you need to
132-
* generate the LinkTables for previous level directories. We might
133-
* as well maintain our own tree structure.
134153
*/
135154
static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add,
136155
off_t offset, struct fuse_file_info *fi,
137156
enum fuse_readdir_flags fr_flags)
138157
{
158+
(void)path;
139159
(void)offset;
140-
(void)fi;
141160
(void)fr_flags;
142-
LinkTable *linktbl;
143-
144-
linktbl = path_to_LinkTable(path);
161+
LinkTable *linktbl = (LinkTable *)fi->fh;
145162

146163
if (!linktbl) {
147164
lprintf(debug, "linktbl empty!\n");
@@ -166,7 +183,9 @@ static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add,
166183
}
167184

168185
static struct fuse_operations fs_oper = {.getattr = fs_getattr,
186+
.opendir = fs_opendir,
169187
.readdir = fs_readdir,
188+
.releasedir = fs_releasedir,
170189
.open = fs_open,
171190
.read = fs_read,
172191
.init = fs_init,

src/link.c

Lines changed: 109 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ void LinkTable_add(LinkTable *linktbl, Link *link)
367367
linktbl->links = (Link **)REALLOC(
368368
(void *)linktbl->links, ((size_t)linktbl->size + 1) * sizeof(Link *));
369369
linktbl->links[linktbl->size] = link;
370+
link->parent_table = linktbl;
370371
linktbl->size++;
371372
}
372373

@@ -572,6 +573,52 @@ static void LinkTable_fill(LinkTable *linktbl)
572573
LinkTable_uninitialised_fill(linktbl);
573574
}
574575

576+
void LinkTable_ref(LinkTable *tbl)
577+
{
578+
if (!tbl) {
579+
return;
580+
}
581+
PTHREAD_MUTEX_LOCK(&link_lock);
582+
tbl->refcount++;
583+
tbl->orphaned = 0;
584+
PTHREAD_MUTEX_UNLOCK(&link_lock);
585+
}
586+
587+
void LinkTable_mark_orphaned(LinkTable *tbl)
588+
{
589+
if (!tbl) {
590+
return;
591+
}
592+
PTHREAD_MUTEX_LOCK(&link_lock);
593+
tbl->orphaned = 1;
594+
PTHREAD_MUTEX_UNLOCK(&link_lock);
595+
}
596+
597+
void LinkTable_unref(LinkTable *tbl)
598+
{
599+
if (!tbl) {
600+
return;
601+
}
602+
PTHREAD_MUTEX_LOCK(&link_lock);
603+
tbl->refcount--;
604+
if (tbl->refcount == 0 && tbl->orphaned) {
605+
LinkTable *parent = tbl->parent_tbl;
606+
Link *parent_link = tbl->parent_link;
607+
if (parent_link) {
608+
parent_link->next_table = NULL;
609+
}
610+
PTHREAD_MUTEX_UNLOCK(&link_lock);
611+
612+
LinkTable_free(tbl);
613+
614+
if (parent) {
615+
LinkTable_unref(parent);
616+
}
617+
return;
618+
}
619+
PTHREAD_MUTEX_UNLOCK(&link_lock);
620+
}
621+
575622
void LinkTable_free(LinkTable *linktbl)
576623
{
577624
if (linktbl) {
@@ -838,6 +885,7 @@ LinkTable *LinkTable_disk_open(const char *dirn)
838885

839886
for (int i = 0; i < sz; i++) {
840887
linktbl->links[i] = CALLOC(1, sizeof(Link));
888+
linktbl->links[i]->parent_table = linktbl;
841889
if (fread(linktbl->links[i]->linkname, sizeof(char), NAME_MAX, fp)
842890
!= NAME_MAX
843891
|| fread(linktbl->links[i]->f_url, sizeof(char), PATH_MAX, fp)
@@ -866,47 +914,67 @@ LinkTable *path_to_LinkTable(const char *path)
866914
{
867915
Link *link = NULL;
868916
Link *tmp_link = NULL;
869-
Link link_cpy = {0};
870917
LinkTable *next_table = NULL;
871918

872919
if (!strcmp(path, "/")) {
873920
next_table = ROOT_LINK_TBL;
874-
link_cpy = *next_table->links[0];
875-
tmp_link = &link_cpy;
921+
LinkTable_ref(next_table);
922+
return next_table;
876923
} else {
877924
link = path_to_Link(path);
878925
if (!link) {
879926
return NULL;
880927
}
881928
tmp_link = link;
929+
930+
PTHREAD_MUTEX_LOCK(&link_lock);
882931
next_table = link->next_table;
932+
PTHREAD_MUTEX_UNLOCK(&link_lock);
883933
}
884934

885935
if (!next_table) {
936+
LinkTable *new_table = NULL;
886937
if (CONFIG.mode == NORMAL) {
887-
next_table = LinkTable_new(tmp_link->f_url);
938+
new_table = LinkTable_new(tmp_link->f_url);
888939
} else if (CONFIG.mode == SINGLE) {
889-
next_table = single_LinkTable_new(tmp_link->f_url);
940+
new_table = single_LinkTable_new(tmp_link->f_url);
890941
} else if (CONFIG.mode == SONIC) {
891942
if (!CONFIG.sonic_id3) {
892-
next_table = sonic_LinkTable_new_index(tmp_link->sonic.id);
943+
new_table = sonic_LinkTable_new_index(tmp_link->sonic.id);
893944
} else {
894-
next_table = sonic_LinkTable_new_id3(tmp_link->sonic.depth,
895-
tmp_link->sonic.id);
945+
new_table = sonic_LinkTable_new_id3(tmp_link->sonic.depth,
946+
tmp_link->sonic.id);
896947
}
897948
} else {
898949
lprintf(fatal, "Invalid CONFIG.mode: %d\n", CONFIG.mode);
899950
}
900-
}
901951

902-
if (link) {
903-
link->next_table = next_table;
904-
} else {
905-
ROOT_LINK_TBL = next_table;
952+
PTHREAD_MUTEX_LOCK(&link_lock);
953+
if (!link->next_table) {
954+
link->next_table = new_table;
955+
new_table->parent_tbl = link->parent_table;
956+
new_table->parent_link = link;
957+
if (new_table->parent_tbl) {
958+
new_table->parent_tbl->refcount++;
959+
}
960+
next_table = new_table;
961+
} else {
962+
PTHREAD_MUTEX_UNLOCK(&link_lock);
963+
LinkTable_free(new_table);
964+
PTHREAD_MUTEX_LOCK(&link_lock);
965+
next_table = link->next_table;
966+
}
967+
PTHREAD_MUTEX_UNLOCK(&link_lock);
968+
969+
if (CONFIG.invalid_refresh) {
970+
LinkTable_uninitialised_fill(next_table);
971+
}
906972
}
907973

908-
if (CONFIG.invalid_refresh) {
909-
LinkTable_uninitialised_fill(next_table);
974+
LinkTable_ref(next_table);
975+
976+
if (link) {
977+
LinkTable_unref(link->parent_table);
910978
}
911979

912980
return next_table;
@@ -971,22 +1039,36 @@ static Link *path_to_Link_recursive(char *path, LinkTable *linktbl)
9711039
*/
9721040
LinkTable *next_table = linktbl->links[i]->next_table;
9731041
if (!next_table) {
1042+
PTHREAD_MUTEX_UNLOCK(&link_lock);
1043+
LinkTable *new_table = NULL;
9741044
if (CONFIG.mode == NORMAL) {
975-
next_table = LinkTable_new(linktbl->links[i]->f_url);
1045+
new_table = LinkTable_new(linktbl->links[i]->f_url);
9761046
} else if (CONFIG.mode == SONIC) {
9771047
if (!CONFIG.sonic_id3) {
978-
next_table = sonic_LinkTable_new_index(
1048+
new_table = sonic_LinkTable_new_index(
9791049
linktbl->links[i]->sonic.id);
9801050
} else {
981-
next_table = sonic_LinkTable_new_id3(
1051+
new_table = sonic_LinkTable_new_id3(
9821052
linktbl->links[i]->sonic.depth,
9831053
linktbl->links[i]->sonic.id);
9841054
}
9851055
} else {
9861056
lprintf(fatal, "Invalid CONFIG.mode\n");
9871057
}
1058+
PTHREAD_MUTEX_LOCK(&link_lock);
1059+
if (!linktbl->links[i]->next_table) {
1060+
linktbl->links[i]->next_table = new_table;
1061+
new_table->parent_tbl = linktbl;
1062+
new_table->parent_link = linktbl->links[i];
1063+
linktbl->refcount++;
1064+
next_table = new_table;
1065+
} else {
1066+
PTHREAD_MUTEX_UNLOCK(&link_lock);
1067+
LinkTable_free(new_table);
1068+
PTHREAD_MUTEX_LOCK(&link_lock);
1069+
next_table = linktbl->links[i]->next_table;
1070+
}
9881071
}
989-
linktbl->links[i]->next_table = next_table;
9901072
return path_to_Link_recursive(next_path, next_table);
9911073
}
9921074
}
@@ -1007,6 +1089,11 @@ Link *path_to_Link(const char *path)
10071089
Link *link = path_to_Link_recursive(new_path, ROOT_LINK_TBL);
10081090
FREE(new_path);
10091091

1092+
if (link && link->parent_table) {
1093+
link->parent_table->refcount++;
1094+
link->parent_table->orphaned = 0;
1095+
}
1096+
10101097
lprintf(link_lock_debug, "thread %lx: unlocking link_lock;\n",
10111098
(unsigned long)pthread_self());
10121099
PTHREAD_MUTEX_UNLOCK(&link_lock);
@@ -1227,7 +1314,9 @@ long path_download(const char *path, char *output_buf, size_t req_size,
12271314
return -ENOENT;
12281315
}
12291316

1230-
return Link_download(link, output_buf, req_size, offset);
1317+
long res = Link_download(link, output_buf, req_size, offset);
1318+
LinkTable_unref(link->parent_table);
1319+
return res;
12311320
}
12321321

12331322
static void make_link_relative(const char *page_url, char *link_url)

0 commit comments

Comments
 (0)