Merge pull request #239 from giuseppe/leak-fix

main: fix a potential leak and be more aggressive on releasing inodes
This commit is contained in:
Daniel J Walsh 2020-10-07 11:01:54 -04:00 committed by GitHub
commit 3c4eb36f7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 20 deletions

View File

@ -55,7 +55,7 @@ reading/writing files to the system.
.PP .PP
The fuse\-overlayfs dynamic mapping is an alternative and cheaper way The fuse\-overlayfs dynamic mapping is an alternative and cheaper way
to chown'ing the files on the host to accomodate the user namespace to chown'ing the files on the host to accommodate the user namespace
settings. settings.
.PP .PP

94
main.c
View File

@ -50,23 +50,18 @@
#include <hash.h> #include <hash.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <sys/file.h> #include <sys/file.h>
#include <signal.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#include <sys/xattr.h> #include <sys/xattr.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <utils.h>
#include <pthread.h> #include <pthread.h>
#include <utils.h>
#include <plugin.h> #include <plugin.h>
#ifndef TEMP_FAILURE_RETRY #ifndef TEMP_FAILURE_RETRY
@ -169,6 +164,23 @@ static gid_t overflow_gid;
static struct ovl_ino dummy_ino; static struct ovl_ino dummy_ino;
struct stats_s
{
size_t nodes;
size_t inodes;
};
static volatile struct stats_s stats;
static void
print_stats (int sig)
{
char fmt[128];
int l = snprintf (fmt, sizeof (fmt) - 1, "# INODES: %zu\n# NODES: %zu\n", stats.inodes, stats.nodes);
fmt[l] = '\0';
write (STDERR_FILENO, fmt, l + 1);
}
static double static double
get_timeout (struct ovl_data *lo) get_timeout (struct ovl_data *lo)
{ {
@ -906,6 +918,7 @@ node_free (void *p)
if (n->do_rmdir) if (n->do_rmdir)
unlinkat (n->hidden_dirfd, n->path, AT_REMOVEDIR); unlinkat (n->hidden_dirfd, n->path, AT_REMOVEDIR);
stats.nodes--;
free (n->name); free (n->name);
free (n->path); free (n->path);
free (n); free (n);
@ -927,6 +940,7 @@ inode_free (void *p)
node_free (tmp); node_free (tmp);
} }
stats.inodes--;
free (i); free (i);
} }
@ -935,18 +949,21 @@ drop_node_from_ino (Hash_table *inodes, struct ovl_node *node)
{ {
struct ovl_ino *ino; struct ovl_ino *ino;
struct ovl_node *it, *prev = NULL; struct ovl_node *it, *prev = NULL;
size_t len = 0;
ino = node->ino; ino = node->ino;
for (it = ino->node; it; it = it->next_link) if (ino->lookups == 0)
len++; {
hash_delete (inodes, ino);
inode_free (ino);
return;
}
if (len == 1 && node->ino->lookups > 0) /* If it is the only node referenced by the inode, do not destroy it. */
if (ino->node == node && node->next_link == NULL)
return; return;
node->ino = NULL; node->ino = NULL;
ino->lookups -= node->node_lookups;
for (it = ino->node; it; it = it->next_link) for (it = ino->node; it; it = it->next_link)
{ {
@ -1144,20 +1161,21 @@ register_inode (struct ovl_data *lo, struct ovl_node *n, mode_t mode)
return NULL; return NULL;
} }
stats.inodes++;
return ino->node; return ino->node;
} }
static void static bool
do_forget (struct ovl_data *lo, fuse_ino_t ino, uint64_t nlookup) do_forget (struct ovl_data *lo, fuse_ino_t ino, uint64_t nlookup)
{ {
struct ovl_ino *i; struct ovl_ino *i;
if (ino == FUSE_ROOT_ID || ino == 0) if (ino == FUSE_ROOT_ID || ino == 0)
return; return false;
i = lookup_inode (lo, ino); i = lookup_inode (lo, ino);
if (i == NULL) if (i == NULL || i == &dummy_ino)
return; return false;
i->lookups -= nlookup; i->lookups -= nlookup;
if (i->lookups <= 0) if (i->lookups <= 0)
@ -1165,6 +1183,38 @@ do_forget (struct ovl_data *lo, fuse_ino_t ino, uint64_t nlookup)
hash_delete (lo->inodes, i); hash_delete (lo->inodes, i);
inode_free (i); inode_free (i);
} }
return true;
}
/* cleanup any inode that has 0 lookups. */
static void
cleanup_inodes (struct ovl_data *lo)
{
cleanup_free struct ovl_ino **to_cleanup = NULL;
size_t no_lookups = 0;
struct ovl_ino *it;
size_t i;
/* Also attempt to cleanup any inode that has 0 lookups. */
for (it = hash_get_first (lo->inodes); it; it = hash_get_next (lo->inodes, it))
{
if (it->lookups == 0)
no_lookups++;
}
if (no_lookups > 0)
{
to_cleanup = malloc (sizeof (*to_cleanup) * no_lookups);
if (! to_cleanup)
return;
for (i = 0, it = hash_get_first (lo->inodes); it; it = hash_get_next (lo->inodes, it))
{
if (it->lookups == 0)
to_cleanup[i++] = it;
}
for (i = 0; i < no_lookups; i++)
do_forget (lo, (fuse_ino_t) to_cleanup[i], 0);
}
} }
static void static void
@ -1194,6 +1244,8 @@ ovl_forget_multi (fuse_req_t req, size_t count, struct fuse_forget_data *forgets
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
do_forget (lo, forgets[i].ino, forgets[i].nlookup); do_forget (lo, forgets[i].ino, forgets[i].nlookup);
cleanup_inodes (lo);
fuse_reply_none (req); fuse_reply_none (req);
} }
@ -1249,6 +1301,7 @@ make_whiteout_node (const char *path, const char *name)
ret_xchg = ret; ret_xchg = ret;
ret = NULL; ret = NULL;
stats.nodes++;
return ret_xchg; return ret_xchg;
} }
@ -1461,6 +1514,7 @@ no_fd:
ret_xchg = ret; ret_xchg = ret;
ret = NULL; ret = NULL;
stats.nodes++;
return register_inode (lo, ret_xchg, mode); return register_inode (lo, ret_xchg, mode);
} }
@ -2322,6 +2376,7 @@ ovl_releasedir (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
cleanup_lock int l = enter_big_lock (); cleanup_lock int l = enter_big_lock ();
size_t s; size_t s;
struct ovl_dirp *d = ovl_dirp (fi); struct ovl_dirp *d = ovl_dirp (fi);
struct ovl_data *lo = ovl_data (req);
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_releasedir(ino=%" PRIu64 ")\n", ino); fprintf (stderr, "ovl_releasedir(ino=%" PRIu64 ")\n", ino);
@ -2329,9 +2384,7 @@ ovl_releasedir (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
for (s = 2; s < d->tbl_size; s++) for (s = 2; s < d->tbl_size; s++)
{ {
d->tbl[s]->node_lookups--; d->tbl[s]->node_lookups--;
if (d->tbl[s]->ino) if (! do_forget (lo, (fuse_ino_t) d->tbl[s]->ino, 1))
d->tbl[s]->ino->lookups--;
else
{ {
if (d->tbl[s]->node_lookups == 0) if (d->tbl[s]->node_lookups == 0)
node_free (d->tbl[s]); node_free (d->tbl[s]);
@ -5520,6 +5573,9 @@ main (int argc, char *argv[])
error (0, errno, "cannot set signal handler"); error (0, errno, "cannot set signal handler");
goto err_out2; goto err_out2;
} }
signal (SIGUSR1, print_stats);
if (fuse_session_mount (se, lo.mountpoint) != 0) if (fuse_session_mount (se, lo.mountpoint) != 0)
{ {
error (0, errno, "cannot mount"); error (0, errno, "cannot mount");