Merge pull request #91 from giuseppe/perf-improvements-3

perf: improve performance for mkdir and open(O_CREAT)
This commit is contained in:
Daniel J Walsh 2019-07-25 11:42:34 -04:00 committed by GitHub
commit 9c77da7d73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

192
main.c
View File

@ -247,6 +247,10 @@ struct ovl_data
int fast_ino_check; int fast_ino_check;
int writeback; int writeback;
int disable_xattrs; int disable_xattrs;
/* current uid/gid*/
uid_t uid;
uid_t gid;
}; };
static double static double
@ -1093,6 +1097,9 @@ safe_read_xattr (char **ret, int sfd, const char *name, size_t initial_size)
buffer[s] == '\0'; buffer[s] == '\0';
if (s <= 0)
return s;
/* Change owner. */ /* Change owner. */
*ret = buffer; *ret = buffer;
buffer = NULL; buffer = NULL;
@ -1237,9 +1244,9 @@ insert_node (struct ovl_node *parent, struct ovl_node *item, bool replace)
old = hash_delete (parent->children, item); old = hash_delete (parent->children, item);
if (old) if (old)
{ {
node_free (old);
if (node_dirp (old)) if (node_dirp (old))
parent->nlinks--; parent->nlinks--;
node_free (old);
} }
} }
@ -1416,7 +1423,8 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
break; break;
} }
n->loaded = 1; if (get_timeout (lo) > 0)
n->loaded = 1;
return n; return n;
} }
@ -2166,6 +2174,27 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct
cleanup_close int dfd = -1; cleanup_close int dfd = -1;
cleanup_free char *buf = NULL; cleanup_free char *buf = NULL;
char wd_tmp_file_name[32]; char wd_tmp_file_name[32];
bool need_rename;
need_rename = times || xattr_sfd >= 0 || uid != lo->uid || gid != lo->gid;
if (!need_rename)
{
/* mkdir can be used directly without a temporary directory in the working directory. */
ret = mkdirat (dirfd, name, mode);
if (ret < 0)
{
if (errno == EEXIST)
{
unlinkat (dirfd, name, 0);
ret = mkdirat (dirfd, name, mode);
}
if (ret < 0)
return ret;
}
if (st_out)
return fstatat (dirfd, name, st_out, AT_SYMLINK_NOFOLLOW);
return 0;
}
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ()); sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
@ -2177,9 +2206,12 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = fchown (dfd, uid, gid); if (uid != lo->uid || gid != lo->gid)
if (ret < 0) {
goto out; ret = fchown (dfd, uid, gid);
if (ret < 0)
goto out;
}
if (times) if (times)
{ {
@ -2370,9 +2402,12 @@ copyup (struct ovl_data *lo, struct ovl_node *node)
if (dfd < 0) if (dfd < 0)
goto exit; goto exit;
ret = fchown (dfd, st.st_uid, st.st_gid); if (st.st_uid != lo->uid || st.st_gid != lo->gid)
if (ret < 0) {
goto exit; ret = fchown (dfd, st.st_uid, st.st_gid);
if (ret < 0)
goto exit;
}
buf = malloc (buf_size); buf = malloc (buf_size);
if (buf == NULL) if (buf == NULL)
@ -2813,7 +2848,49 @@ ovl_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name)
} }
static int static int
ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mode_t mode) create_file (struct ovl_data *lo, int dirfd, const char *path, uid_t uid, gid_t gid, int flags, mode_t mode)
{
cleanup_close int fd = -1;
char wd_tmp_file_name[32];
int ret;
/* try to create directly the file if it doesn't need to be chowned. */
if (uid == lo->uid && gid == lo->gid)
{
ret = TEMP_FAILURE_RETRY (openat (get_upper_layer (lo)->fd, path, flags, mode));
if (ret == 0)
return ret;
/* if it fails (e.g. there is a whiteout) then fallback to create it in
the working dir + rename. */
}
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
fd = TEMP_FAILURE_RETRY (openat (lo->workdir_fd, wd_tmp_file_name, flags, mode));
if (fd < 0)
return -1;
if (uid != lo->uid || gid != lo->gid)
{
if (fchown (fd, uid, gid) < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return -1;
}
}
if (renameat (lo->workdir_fd, wd_tmp_file_name, get_upper_layer (lo)->fd, path) < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return -1;
}
ret = fd;
fd = -1;
return ret;
}
static int
ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mode_t mode, struct ovl_node **retnode, struct stat *st)
{ {
struct ovl_data *lo = ovl_data (req); struct ovl_data *lo = ovl_data (req);
struct ovl_node *n; struct ovl_node *n;
@ -2821,6 +2898,8 @@ ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mod
cleanup_free char *path = NULL; cleanup_free char *path = NULL;
cleanup_close int fd = -1; cleanup_close int fd = -1;
const struct fuse_ctx *ctx = fuse_req_ctx (req); const struct fuse_ctx *ctx = fuse_req_ctx (req);
uid_t uid;
gid_t gid;
flags |= O_NOFOLLOW; flags |= O_NOFOLLOW;
@ -2858,6 +2937,8 @@ ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mod
struct ovl_node *p; struct ovl_node *p;
const struct fuse_ctx *ctx = fuse_req_ctx (req); const struct fuse_ctx *ctx = fuse_req_ctx (req);
char wd_tmp_file_name[32]; char wd_tmp_file_name[32];
bool need_delete_whiteout = true;
struct stat st_tmp;
if ((flags & O_CREAT) == 0) if ((flags & O_CREAT) == 0)
{ {
@ -2876,64 +2957,66 @@ ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mod
if (p == NULL) if (p == NULL)
return -1; return -1;
if (p->loaded && n == NULL)
need_delete_whiteout = false;
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ()); sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
fd = TEMP_FAILURE_RETRY (openat (lo->workdir_fd, wd_tmp_file_name, flags, mode & ~ctx->umask));
if (fd < 0)
return -1;
if (fchown (fd, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid)) < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return -1;
}
ret = asprintf (&path, "%s/%s", p->path, name); ret = asprintf (&path, "%s/%s", p->path, name);
if (ret < 0) if (ret < 0)
{ return ret;
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return ret; uid = get_uid (lo, ctx->uid);
} gid = get_gid (lo, ctx->gid);
if (unlinkat (get_upper_layer (lo)->fd, path, 0) < 0 && errno != ENOENT)
fd = create_file (lo, get_upper_layer (lo)->fd, path, uid, gid, flags, mode & ~ctx->umask);
if (fd < 0)
return fd;
if (need_delete_whiteout && delete_whiteout (lo, -1, p, name) < 0)
return -1; return -1;
if (delete_whiteout (lo, -1, p, name) < 0) if (st == NULL)
st = &st_tmp;
if (fstat (fd, st) < 0)
return -1; return -1;
if (renameat (lo->workdir_fd, wd_tmp_file_name, get_upper_layer (lo)->fd, path) < 0) n = make_ovl_node (path, get_upper_layer (lo), name, st->st_ino, false, p, lo->fast_ino_check);
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return -1;
}
n = make_ovl_node (path, get_upper_layer (lo), name, 0, false, p, lo->fast_ino_check);
if (n == NULL) if (n == NULL)
{ {
errno = ENOMEM; errno = ENOMEM;
close (fd);
return -1; return -1;
} }
n = insert_node (p, n, true); n = insert_node (p, n, true);
if (n == NULL) if (n == NULL)
{ {
errno = ENOMEM; errno = ENOMEM;
close (fd);
return -1; return -1;
} }
ret = fd; ret = fd;
fd = -1; /* We use a temporary variable so we don't close it at cleanup. */ fd = -1; /* We use a temporary variable so we don't close it at cleanup. */
if (retnode)
*retnode = n;
return ret; return ret;
} }
/* readonly, we can use both lowerdir and upperdir. */ /* readonly, we can use both lowerdir and upperdir. */
if (readonly) if (readonly)
return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode)); {
if (retnode)
*retnode = n;
return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode));
}
else else
{ {
n = get_node_up (lo, n); n = get_node_up (lo, n);
if (n == NULL) if (n == NULL)
return -1; return -1;
if (retnode)
*retnode = n;
return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode)); return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode));
} }
} }
@ -3016,7 +3099,8 @@ ovl_create (fuse_req_t req, fuse_ino_t parent, const char *name,
cleanup_close int fd = -1; cleanup_close int fd = -1;
struct fuse_entry_param e; struct fuse_entry_param e;
struct ovl_data *lo = ovl_data (req); struct ovl_data *lo = ovl_data (req);
struct ovl_node *node; struct ovl_node *node = NULL;
struct stat st;
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_create(parent=%" PRIu64 ", name=%s)\n", fprintf (stderr, "ovl_create(parent=%" PRIu64 ", name=%s)\n",
@ -3024,14 +3108,13 @@ ovl_create (fuse_req_t req, fuse_ino_t parent, const char *name,
fi->flags = fi->flags | O_CREAT; fi->flags = fi->flags | O_CREAT;
fd = ovl_do_open (req, parent, name, fi->flags, mode); fd = ovl_do_open (req, parent, name, fi->flags, mode, &node, &st);
if (fd < 0) if (fd < 0)
{ {
fuse_reply_err (req, errno); fuse_reply_err (req, errno);
return; return;
} }
node = do_lookup_file (lo, parent, name);
if (node == NULL || do_getattr (req, &e, node, fd, NULL) < 0) if (node == NULL || do_getattr (req, &e, node, fd, NULL) < 0)
{ {
fuse_reply_err (req, errno); fuse_reply_err (req, errno);
@ -3054,7 +3137,7 @@ ovl_open (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_open(ino=%" PRIu64 "s)\n", ino); fprintf (stderr, "ovl_open(ino=%" PRIu64 "s)\n", ino);
fd = ovl_do_open (req, ino, NULL, fi->flags, 0700); fd = ovl_do_open (req, ino, NULL, fi->flags, 0700, NULL, NULL);
if (fd < 0) if (fd < 0)
{ {
fuse_reply_err (req, errno); fuse_reply_err (req, errno);
@ -3397,6 +3480,8 @@ ovl_symlink (fuse_req_t req, const char *link, fuse_ino_t parent, const char *na
const struct fuse_ctx *ctx = fuse_req_ctx (req); const struct fuse_ctx *ctx = fuse_req_ctx (req);
char wd_tmp_file_name[32]; char wd_tmp_file_name[32];
bool need_delete_whiteout = true; bool need_delete_whiteout = true;
uid_t uid;
gid_t gid;
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_symlink(link=%s, ino=%" PRIu64 "s, name=%s)\n", link, parent, name); fprintf (stderr, "ovl_symlink(link=%s, ino=%" PRIu64 "s, name=%s)\n", link, parent, name);
@ -3435,11 +3520,16 @@ ovl_symlink (fuse_req_t req, const char *link, fuse_ino_t parent, const char *na
return; return;
} }
if (fchownat (lo->workdir_fd, wd_tmp_file_name, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), AT_SYMLINK_NOFOLLOW) < 0) uid = get_uid (lo, ctx->uid);
gid = get_uid (lo, ctx->gid);
if (uid != lo->uid || gid != lo->gid)
{ {
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0); if (fchownat (lo->workdir_fd, wd_tmp_file_name, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), AT_SYMLINK_NOFOLLOW) < 0)
fuse_reply_err (req, errno); {
return; unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
fuse_reply_err (req, errno);
return;
}
} }
if (need_delete_whiteout && delete_whiteout (lo, -1, pnode, name) < 0) if (need_delete_whiteout && delete_whiteout (lo, -1, pnode, name) < 0)
@ -4061,7 +4151,7 @@ ovl_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
struct stat st; struct stat st;
ino_t ino = 0; ino_t ino = 0;
int ret = 0; int ret = 0;
char *path; cleanup_free char *path = NULL;
bool need_delete_whiteout = true; bool need_delete_whiteout = true;
cleanup_lock int l = enter_big_lock (); cleanup_lock int l = enter_big_lock ();
@ -4132,7 +4222,8 @@ ovl_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
if (parent_upperdir_only) if (parent_upperdir_only)
{ {
node->last_layer = pnode->last_layer; node->last_layer = pnode->last_layer;
node->loaded = 1; if (get_timeout (lo) > 0)
node->loaded = 1;
node->no_security_capability = 1; node->no_security_capability = 1;
} }
else else
@ -4493,6 +4584,10 @@ fuse_opt_proc (void *data, const char *arg, int key, struct fuse_args *outargs)
return 1; return 1;
if (strcmp (arg, "noatime") == 0) if (strcmp (arg, "noatime") == 0)
return 1; return 1;
if (strcmp (arg, "diratime") == 0)
return 1;
if (strcmp (arg, "nodiratime") == 0)
return 1;
if (strcmp (arg, "splice_write") == 0) if (strcmp (arg, "splice_write") == 0)
return 1; return 1;
if (strcmp (arg, "splice_read") == 0) if (strcmp (arg, "splice_read") == 0)
@ -4526,9 +4621,9 @@ get_new_args (int *argc, char **argv)
char **newargv = malloc (sizeof (char *) * (*argc + 2)); char **newargv = malloc (sizeof (char *) * (*argc + 2));
newargv[0] = argv[0]; newargv[0] = argv[0];
if (geteuid() == 0) if (geteuid() == 0)
newargv[1] = "-odefault_permissions,allow_other,suid"; newargv[1] = "-odefault_permissions,allow_other,suid,noatime,lazytime";
else else
newargv[1] = "-odefault_permissions"; newargv[1] = "-odefault_permissions,noatime=1";
for (i = 1; i < *argc; i++) for (i = 1; i < *argc; i++)
newargv[i + 1] = argv[i]; newargv[i + 1] = argv[i];
(*argc)++; (*argc)++;
@ -4610,6 +4705,9 @@ main (int argc, char *argv[])
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
} }
lo.uid = geteuid ();
lo.gid = getegid ();
if (lo.redirect_dir && strcmp (lo.redirect_dir, "off")) if (lo.redirect_dir && strcmp (lo.redirect_dir, "off"))
error (EXIT_FAILURE, 0, "fuse-overlayfs only supports redirect_dir=off"); error (EXIT_FAILURE, 0, "fuse-overlayfs only supports redirect_dir=off");