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

164
main.c
View File

@ -247,6 +247,10 @@ struct ovl_data
int fast_ino_check;
int writeback;
int disable_xattrs;
/* current uid/gid*/
uid_t uid;
uid_t gid;
};
static double
@ -1093,6 +1097,9 @@ safe_read_xattr (char **ret, int sfd, const char *name, size_t initial_size)
buffer[s] == '\0';
if (s <= 0)
return s;
/* Change owner. */
*ret = buffer;
buffer = NULL;
@ -1237,9 +1244,9 @@ insert_node (struct ovl_node *parent, struct ovl_node *item, bool replace)
old = hash_delete (parent->children, item);
if (old)
{
node_free (old);
if (node_dirp (old))
parent->nlinks--;
node_free (old);
}
}
@ -1416,6 +1423,7 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
break;
}
if (get_timeout (lo) > 0)
n->loaded = 1;
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_free char *buf = NULL;
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 ());
@ -2177,9 +2206,12 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct
if (ret < 0)
goto out;
if (uid != lo->uid || gid != lo->gid)
{
ret = fchown (dfd, uid, gid);
if (ret < 0)
goto out;
}
if (times)
{
@ -2370,9 +2402,12 @@ copyup (struct ovl_data *lo, struct ovl_node *node)
if (dfd < 0)
goto exit;
if (st.st_uid != lo->uid || st.st_gid != lo->gid)
{
ret = fchown (dfd, st.st_uid, st.st_gid);
if (ret < 0)
goto exit;
}
buf = malloc (buf_size);
if (buf == NULL)
@ -2813,7 +2848,49 @@ ovl_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name)
}
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_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_close int fd = -1;
const struct fuse_ctx *ctx = fuse_req_ctx (req);
uid_t uid;
gid_t gid;
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;
const struct fuse_ctx *ctx = fuse_req_ctx (req);
char wd_tmp_file_name[32];
bool need_delete_whiteout = true;
struct stat st_tmp;
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)
return -1;
if (p->loaded && n == NULL)
need_delete_whiteout = false;
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);
if (ret < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return ret;
}
if (unlinkat (get_upper_layer (lo)->fd, path, 0) < 0 && errno != ENOENT)
uid = get_uid (lo, ctx->uid);
gid = get_gid (lo, ctx->gid);
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;
if (delete_whiteout (lo, -1, p, name) < 0)
if (st == NULL)
st = &st_tmp;
if (fstat (fd, st) < 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;
}
n = make_ovl_node (path, get_upper_layer (lo), name, 0, false, p, lo->fast_ino_check);
n = make_ovl_node (path, get_upper_layer (lo), name, st->st_ino, false, p, lo->fast_ino_check);
if (n == NULL)
{
errno = ENOMEM;
close (fd);
return -1;
}
n = insert_node (p, n, true);
if (n == NULL)
{
errno = ENOMEM;
close (fd);
return -1;
}
ret = fd;
fd = -1; /* We use a temporary variable so we don't close it at cleanup. */
if (retnode)
*retnode = n;
return ret;
}
/* readonly, we can use both lowerdir and upperdir. */
if (readonly)
{
if (retnode)
*retnode = n;
return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode));
}
else
{
n = get_node_up (lo, n);
if (n == NULL)
return -1;
if (retnode)
*retnode = n;
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;
struct fuse_entry_param e;
struct ovl_data *lo = ovl_data (req);
struct ovl_node *node;
struct ovl_node *node = NULL;
struct stat st;
if (UNLIKELY (ovl_debug (req)))
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;
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)
{
fuse_reply_err (req, errno);
return;
}
node = do_lookup_file (lo, parent, name);
if (node == NULL || do_getattr (req, &e, node, fd, NULL) < 0)
{
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)))
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)
{
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);
char wd_tmp_file_name[32];
bool need_delete_whiteout = true;
uid_t uid;
gid_t gid;
if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_symlink(link=%s, ino=%" PRIu64 "s, name=%s)\n", link, parent, name);
@ -3435,12 +3520,17 @@ ovl_symlink (fuse_req_t req, const char *link, fuse_ino_t parent, const char *na
return;
}
uid = get_uid (lo, ctx->uid);
gid = get_uid (lo, ctx->gid);
if (uid != lo->uid || gid != lo->gid)
{
if (fchownat (lo->workdir_fd, wd_tmp_file_name, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), AT_SYMLINK_NOFOLLOW) < 0)
{
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)
{
@ -4061,7 +4151,7 @@ ovl_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
struct stat st;
ino_t ino = 0;
int ret = 0;
char *path;
cleanup_free char *path = NULL;
bool need_delete_whiteout = true;
cleanup_lock int l = enter_big_lock ();
@ -4132,6 +4222,7 @@ ovl_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
if (parent_upperdir_only)
{
node->last_layer = pnode->last_layer;
if (get_timeout (lo) > 0)
node->loaded = 1;
node->no_security_capability = 1;
}
@ -4493,6 +4584,10 @@ fuse_opt_proc (void *data, const char *arg, int key, struct fuse_args *outargs)
return 1;
if (strcmp (arg, "noatime") == 0)
return 1;
if (strcmp (arg, "diratime") == 0)
return 1;
if (strcmp (arg, "nodiratime") == 0)
return 1;
if (strcmp (arg, "splice_write") == 0)
return 1;
if (strcmp (arg, "splice_read") == 0)
@ -4526,9 +4621,9 @@ get_new_args (int *argc, char **argv)
char **newargv = malloc (sizeof (char *) * (*argc + 2));
newargv[0] = argv[0];
if (geteuid() == 0)
newargv[1] = "-odefault_permissions,allow_other,suid";
newargv[1] = "-odefault_permissions,allow_other,suid,noatime,lazytime";
else
newargv[1] = "-odefault_permissions";
newargv[1] = "-odefault_permissions,noatime=1";
for (i = 1; i < *argc; i++)
newargv[i + 1] = argv[i];
(*argc)++;
@ -4610,6 +4705,9 @@ main (int argc, char *argv[])
exit (EXIT_SUCCESS);
}
lo.uid = geteuid ();
lo.gid = getegid ();
if (lo.redirect_dir && strcmp (lo.redirect_dir, "off"))
error (EXIT_FAILURE, 0, "fuse-overlayfs only supports redirect_dir=off");