mirror of
https://github.com/containers/fuse-overlayfs.git
synced 2025-09-11 08:16:05 -04:00
Merge pull request #229 from giuseppe/remap-writeable
main: support writing uid/gid/mode to xattr
This commit is contained in:
commit
938d9d4b1f
76
direct.c
76
direct.c
@ -34,9 +34,6 @@
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#define XATTR_OVERRIDE_STAT "user.fuseoverlayfs.override_stat"
|
|
||||||
#define XATTR_PRIVILEGED_OVERRIDE_STAT "security.fuseoverlayfs.override_stat"
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
direct_file_exists (struct ovl_layer *l, const char *pathname)
|
direct_file_exists (struct ovl_layer *l, const char *pathname)
|
||||||
{
|
{
|
||||||
@ -79,64 +76,6 @@ direct_getxattr (struct ovl_layer *l, const char *path, const char *name, char *
|
|||||||
return lgetxattr (full_path, name, buf, size);
|
return lgetxattr (full_path, name, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
mode_t mode;
|
|
||||||
char buf[64];
|
|
||||||
cleanup_close int cleanup_fd = -1;
|
|
||||||
const char *xattr_name;
|
|
||||||
|
|
||||||
if (l->has_stat_override == 0 && l->has_privileged_stat_override == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
xattr_name = l->has_privileged_stat_override ? XATTR_PRIVILEGED_OVERRIDE_STAT : XATTR_OVERRIDE_STAT;
|
|
||||||
|
|
||||||
if (fd >= 0)
|
|
||||||
{
|
|
||||||
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char full_path[PATH_MAX];
|
|
||||||
|
|
||||||
full_path[0] = '\0';
|
|
||||||
ret = open_fd_or_get_path (l, path, full_path, &cleanup_fd, O_RDONLY);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
fd = cleanup_fd;
|
|
||||||
|
|
||||||
if (fd >= 0)
|
|
||||||
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
|
|
||||||
else
|
|
||||||
ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1);
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[ret] = '\0';
|
|
||||||
|
|
||||||
ret = sscanf (buf, "%d:%d:%o", &uid, &gid, &mode);
|
|
||||||
if (ret != 3)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
st->st_uid = uid;
|
|
||||||
st->st_gid = gid;
|
|
||||||
st->st_mode = (st->st_mode & S_IFMT) | mode;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask, struct stat *st)
|
direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask, struct stat *st)
|
||||||
{
|
{
|
||||||
@ -151,7 +90,7 @@ direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask,
|
|||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
statx_to_stat (&stx, st);
|
statx_to_stat (&stx, st);
|
||||||
return override_mode (l, fd, path, st);
|
return override_mode (l, fd, NULL, path, st);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -162,7 +101,7 @@ direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask,
|
|||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return override_mode (l, fd, path, st);
|
return override_mode (l, fd, NULL, path, st);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -179,7 +118,7 @@ direct_statat (struct ovl_layer *l, const char *path, struct stat *st, int flags
|
|||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
statx_to_stat (&stx, st);
|
statx_to_stat (&stx, st);
|
||||||
return override_mode (l, -1, path, st);
|
return override_mode (l, -1, NULL, path, st);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -189,7 +128,7 @@ direct_statat (struct ovl_layer *l, const char *path, struct stat *st, int flags
|
|||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return override_mode (l, -1, path, st);
|
return override_mode (l, -1, NULL, path, st);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dirent *
|
static struct dirent *
|
||||||
@ -274,6 +213,12 @@ direct_num_of_layers (const char *opaque, const char *path)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
direct_must_be_remapped (struct ovl_layer *l)
|
||||||
|
{
|
||||||
|
return l->has_privileged_stat_override == 0 && l->has_stat_override == 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct data_source direct_access_ds =
|
struct data_source direct_access_ds =
|
||||||
{
|
{
|
||||||
.num_of_layers = direct_num_of_layers,
|
.num_of_layers = direct_num_of_layers,
|
||||||
@ -289,4 +234,5 @@ struct data_source direct_access_ds =
|
|||||||
.getxattr = direct_getxattr,
|
.getxattr = direct_getxattr,
|
||||||
.listxattr = direct_listxattr,
|
.listxattr = direct_listxattr,
|
||||||
.readlinkat = direct_readlinkat,
|
.readlinkat = direct_readlinkat,
|
||||||
|
.must_be_remapped = direct_must_be_remapped,
|
||||||
};
|
};
|
||||||
|
@ -94,6 +94,7 @@ struct ovl_data
|
|||||||
int fast_ino_check;
|
int fast_ino_check;
|
||||||
int writeback;
|
int writeback;
|
||||||
int disable_xattrs;
|
int disable_xattrs;
|
||||||
|
int xattr_permissions;
|
||||||
|
|
||||||
/* current uid/gid*/
|
/* current uid/gid*/
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
@ -132,6 +133,7 @@ struct data_source
|
|||||||
int (*listxattr)(struct ovl_layer *l, const char *path, char *buf, size_t size);
|
int (*listxattr)(struct ovl_layer *l, const char *path, char *buf, size_t size);
|
||||||
int (*getxattr)(struct ovl_layer *l, const char *path, const char *name, char *buf, size_t size);
|
int (*getxattr)(struct ovl_layer *l, const char *path, const char *name, char *buf, size_t size);
|
||||||
ssize_t (*readlinkat)(struct ovl_layer *l, const char *path, char *buf, size_t bufsiz);
|
ssize_t (*readlinkat)(struct ovl_layer *l, const char *path, char *buf, size_t bufsiz);
|
||||||
|
bool (*must_be_remapped)(struct ovl_layer *l);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* passtrough to the file system. */
|
/* passtrough to the file system. */
|
||||||
|
217
main.c
217
main.c
@ -203,6 +203,8 @@ static const struct fuse_opt ovl_opts[] = {
|
|||||||
offsetof (struct ovl_data, disable_xattrs), 1},
|
offsetof (struct ovl_data, disable_xattrs), 1},
|
||||||
{"plugins=%s",
|
{"plugins=%s",
|
||||||
offsetof (struct ovl_data, plugins), 0},
|
offsetof (struct ovl_data, plugins), 0},
|
||||||
|
{"xattr_permissions=%d",
|
||||||
|
offsetof (struct ovl_data, xattr_permissions), 0},
|
||||||
FUSE_OPT_END
|
FUSE_OPT_END
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,7 +277,6 @@ check_can_mknod (struct ovl_data *lo)
|
|||||||
can_mknod = false;
|
can_mknod = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct ovl_mapping *
|
static struct ovl_mapping *
|
||||||
read_mappings (const char *str)
|
read_mappings (const char *str)
|
||||||
{
|
{
|
||||||
@ -465,6 +466,139 @@ can_access_xattr (const char *name)
|
|||||||
&& !has_prefix (name, PRIVILEGED_XATTR_PREFIX);
|
&& !has_prefix (name, PRIVILEGED_XATTR_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
write_permission_xattr (struct ovl_data *lo, int fd, const char *path, uid_t uid, gid_t gid, mode_t mode)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
size_t len;
|
||||||
|
int ret;
|
||||||
|
const char *name = NULL;
|
||||||
|
|
||||||
|
switch (lo->xattr_permissions)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
name = XATTR_PRIVILEGED_OVERRIDE_STAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
name = XATTR_OVERRIDE_STAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == NULL && fd < 0)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = sprintf (buf, "%d:%d:%o", uid, gid, mode);
|
||||||
|
if (fd >= 0)
|
||||||
|
return fsetxattr (fd, name, buf, len, 0);
|
||||||
|
|
||||||
|
ret = lsetxattr (path, name, buf, len, 0);
|
||||||
|
/* Ignore EPERM in unprivileged mode. */
|
||||||
|
if (ret < 0 && lo->xattr_permissions == 2 && errno == EPERM)
|
||||||
|
return 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_fchown (struct ovl_data *lo, int fd, uid_t uid, gid_t gid, mode_t mode)
|
||||||
|
{
|
||||||
|
if (lo->xattr_permissions)
|
||||||
|
return write_permission_xattr (lo, fd, NULL, uid, gid, mode);
|
||||||
|
return fchown (fd, uid, gid);
|
||||||
|
}
|
||||||
|
/* Make sure it is not used anymore. */
|
||||||
|
#define fchown ERROR
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_chown (struct ovl_data *lo, const char *path, uid_t uid, gid_t gid, mode_t mode)
|
||||||
|
{
|
||||||
|
if (lo->xattr_permissions)
|
||||||
|
return write_permission_xattr (lo, -1, path, uid, gid, mode);
|
||||||
|
return chown (path, uid, gid);
|
||||||
|
}
|
||||||
|
/* Make sure it is not used anymore. */
|
||||||
|
#define chown ERROR
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_fchownat (struct ovl_data *lo, int dfd, const char *path, uid_t uid, gid_t gid, mode_t mode, int flags)
|
||||||
|
{
|
||||||
|
if (lo->xattr_permissions)
|
||||||
|
{
|
||||||
|
char proc_path[32];
|
||||||
|
cleanup_close int fd = openat (dfd, path, O_NOFOLLOW|O_PATH);
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
sprintf (proc_path, "/proc/self/fd/%d", fd);
|
||||||
|
return write_permission_xattr (lo, -1, proc_path, uid, gid, mode);
|
||||||
|
}
|
||||||
|
return fchownat (dfd, path, uid, gid, 0);
|
||||||
|
}
|
||||||
|
/* Make sure it is not used anymore. */
|
||||||
|
#define fchownat ERROR
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_fchmod (struct ovl_data *lo, int fd, mode_t mode)
|
||||||
|
{
|
||||||
|
if (lo->xattr_permissions)
|
||||||
|
{
|
||||||
|
struct ovl_layer *upper = get_upper_layer (lo);
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (upper == NULL)
|
||||||
|
{
|
||||||
|
errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
st.st_uid = 0;
|
||||||
|
st.st_gid = 0;
|
||||||
|
if (override_mode (upper, fd, NULL, NULL, &st) < 0 && errno != ENODATA)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return write_permission_xattr (lo, fd, NULL, st.st_uid, st.st_gid, mode);
|
||||||
|
}
|
||||||
|
return fchmod (fd, mode);
|
||||||
|
}
|
||||||
|
/* Make sure it is not used anymore. */
|
||||||
|
#define fchmod ERROR
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_chmod (struct ovl_data *lo, const char *path, mode_t mode)
|
||||||
|
{
|
||||||
|
if (lo->xattr_permissions)
|
||||||
|
{
|
||||||
|
struct ovl_layer *upper = get_upper_layer (lo);
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (upper == NULL)
|
||||||
|
{
|
||||||
|
errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
st.st_uid = 0;
|
||||||
|
st.st_gid = 0;
|
||||||
|
if (override_mode (upper, -1, path, NULL, &st) < 0 && errno != ENODATA)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return write_permission_xattr (lo, -1, path, st.st_uid, st.st_gid, mode);
|
||||||
|
}
|
||||||
|
return chmod (path, mode);
|
||||||
|
}
|
||||||
|
/* Make sure it is not used anymore. */
|
||||||
|
#define chmod ERROR
|
||||||
|
|
||||||
static int
|
static int
|
||||||
set_fd_origin (int fd, const char *origin)
|
set_fd_origin (int fd, const char *origin)
|
||||||
{
|
{
|
||||||
@ -698,8 +832,11 @@ rpl_stat (fuse_req_t req, struct ovl_node *node, int fd, const char *path, struc
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (l->ds->must_be_remapped && l->ds->must_be_remapped (l))
|
||||||
|
{
|
||||||
st->st_uid = find_mapping (st->st_uid, data->uid_mappings, true, true);
|
st->st_uid = find_mapping (st->st_uid, data->uid_mappings, true, true);
|
||||||
st->st_gid = find_mapping (st->st_gid, data->gid_mappings, true, false);
|
st->st_gid = find_mapping (st->st_gid, data->gid_mappings, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
st->st_ino = node->tmp_ino;
|
st->st_ino = node->tmp_ino;
|
||||||
st->st_dev = node->tmp_dev;
|
st->st_dev = node->tmp_dev;
|
||||||
@ -2497,9 +2634,9 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (uid != lo->uid || gid != lo->gid)
|
if (uid != lo->uid || gid != lo->gid || get_upper_layer (lo)->has_stat_override || get_upper_layer (lo)->has_privileged_stat_override)
|
||||||
{
|
{
|
||||||
ret = fchown (dfd, uid, gid);
|
ret = do_fchown (lo, dfd, uid, gid, mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -2721,9 +2858,9 @@ copyup (struct ovl_data *lo, struct ovl_node *node)
|
|||||||
if (dfd < 0)
|
if (dfd < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
if (st.st_uid != lo->uid || st.st_gid != lo->gid)
|
if (st.st_uid != lo->uid || st.st_gid != lo->gid || get_upper_layer (lo)->has_stat_override || get_upper_layer (lo)->has_privileged_stat_override)
|
||||||
{
|
{
|
||||||
ret = fchown (dfd, st.st_uid, st.st_gid);
|
ret = do_fchown (lo, dfd, st.st_uid, st.st_gid, st.st_mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -3177,7 +3314,7 @@ direct_create_file (struct ovl_layer *l, int dirfd, const char *path, uid_t uid,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* try to create directly the file if it doesn't need to be chowned. */
|
/* try to create directly the file if it doesn't need to be chowned. */
|
||||||
if (uid == lo->uid && gid == lo->gid)
|
if (uid == lo->uid && gid == lo->gid && !l->has_stat_override && !l->has_privileged_stat_override)
|
||||||
{
|
{
|
||||||
ret = TEMP_FAILURE_RETRY (safe_openat (get_upper_layer (lo)->fd, path, flags, mode));
|
ret = TEMP_FAILURE_RETRY (safe_openat (get_upper_layer (lo)->fd, path, flags, mode));
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
@ -3191,9 +3328,9 @@ direct_create_file (struct ovl_layer *l, int dirfd, const char *path, uid_t uid,
|
|||||||
fd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, flags, mode));
|
fd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, flags, mode));
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (uid != lo->uid || gid != lo->gid)
|
if (uid != lo->uid || gid != lo->gid || l->has_stat_override || l->has_privileged_stat_override)
|
||||||
{
|
{
|
||||||
if (fchown (fd, uid, gid) < 0)
|
if (do_fchown (lo, fd, uid, gid, mode) < 0)
|
||||||
{
|
{
|
||||||
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
|
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
|
||||||
return -1;
|
return -1;
|
||||||
@ -3400,7 +3537,7 @@ ovl_write_buf (fuse_req_t req, fuse_ino_t ino,
|
|||||||
/* if it is a writepage request, make sure to restore the setuid bit. */
|
/* if it is a writepage request, make sure to restore the setuid bit. */
|
||||||
if (fi->writepage && (inode->mode & (S_ISUID|S_ISGID)))
|
if (fi->writepage && (inode->mode & (S_ISUID|S_ISGID)))
|
||||||
{
|
{
|
||||||
if (fchmod (fi->fh, inode->mode) < 0)
|
if (do_fchmod (lo, fi->fh, inode->mode) < 0)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, errno);
|
fuse_reply_err (req, errno);
|
||||||
return;
|
return;
|
||||||
@ -3665,9 +3802,9 @@ ovl_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, stru
|
|||||||
if (to_set & FUSE_SET_ATTR_MODE)
|
if (to_set & FUSE_SET_ATTR_MODE)
|
||||||
{
|
{
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
ret = fchmod (fd, attr->st_mode);
|
ret = do_fchmod (lo, fd, attr->st_mode);
|
||||||
else
|
else
|
||||||
ret = chmod (path, attr->st_mode);
|
ret = do_chmod (lo, path, attr->st_mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, errno);
|
fuse_reply_err (req, errno);
|
||||||
@ -3692,9 +3829,9 @@ ovl_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, stru
|
|||||||
if (uid != -1 || gid != -1)
|
if (uid != -1 || gid != -1)
|
||||||
{
|
{
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
ret = fchown (fd, uid, gid);
|
ret = do_fchown (lo, fd, uid, gid, node->ino->mode);
|
||||||
else
|
else
|
||||||
ret = chown (path, uid, gid);
|
ret = do_chown (lo, path, uid, gid, node->ino->mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, errno);
|
fuse_reply_err (req, errno);
|
||||||
@ -3834,9 +3971,9 @@ direct_symlinkat (struct ovl_layer *l, const char *target, const char *linkpath,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (uid != lo->uid || gid != lo->gid)
|
if (uid != lo->uid || gid != lo->gid || l->has_stat_override || l->has_privileged_stat_override)
|
||||||
{
|
{
|
||||||
ret = fchownat (lo->workdir_fd, wd_tmp_file_name, uid, gid, AT_SYMLINK_NOFOLLOW);
|
ret = do_fchownat (lo, lo->workdir_fd, wd_tmp_file_name, uid, gid, AT_SYMLINK_NOFOLLOW, 0755);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
|
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
|
||||||
@ -4414,6 +4551,8 @@ ovl_mknod (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev
|
|||||||
fprintf (stderr, "ovl_mknod(ino=%" PRIu64 ", name=%s, mode=%d, rdev=%lu)\n",
|
fprintf (stderr, "ovl_mknod(ino=%" PRIu64 ", name=%s, mode=%d, rdev=%lu)\n",
|
||||||
parent, name, mode, rdev);
|
parent, name, mode, rdev);
|
||||||
|
|
||||||
|
mode = mode & ~ctx->umask;
|
||||||
|
|
||||||
node = do_lookup_file (lo, parent, name);
|
node = do_lookup_file (lo, parent, name);
|
||||||
if (node != NULL && !node->whiteout)
|
if (node != NULL && !node->whiteout)
|
||||||
{
|
{
|
||||||
@ -4435,14 +4574,14 @@ ovl_mknod (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
|
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
|
||||||
ret = mknodat (lo->workdir_fd, wd_tmp_file_name, mode & ~ctx->umask, rdev);
|
ret = mknodat (lo->workdir_fd, wd_tmp_file_name, mode, rdev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, errno);
|
fuse_reply_err (req, errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fchownat (lo->workdir_fd, wd_tmp_file_name, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), 0) < 0)
|
if (do_fchownat (lo, lo->workdir_fd, wd_tmp_file_name, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), 0, mode) < 0)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, errno);
|
fuse_reply_err (req, errno);
|
||||||
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
|
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
|
||||||
@ -5101,6 +5240,7 @@ main (int argc, char *argv[])
|
|||||||
.redirect_dir = NULL,
|
.redirect_dir = NULL,
|
||||||
.mountpoint = NULL,
|
.mountpoint = NULL,
|
||||||
.fsync = 1,
|
.fsync = 1,
|
||||||
|
.xattr_permissions = 0,
|
||||||
.timeout = 1000000000.0,
|
.timeout = 1000000000.0,
|
||||||
.timeout_str = NULL,
|
.timeout_str = NULL,
|
||||||
.writeback = 1,
|
.writeback = 1,
|
||||||
@ -5210,8 +5350,51 @@ main (int argc, char *argv[])
|
|||||||
error (EXIT_FAILURE, errno, "cannot read upper dir");
|
error (EXIT_FAILURE, errno, "cannot read upper dir");
|
||||||
layers = tmp_layer;
|
layers = tmp_layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
lo.layers = layers;
|
lo.layers = layers;
|
||||||
|
|
||||||
|
if (lo.upperdir)
|
||||||
|
{
|
||||||
|
if (lo.xattr_permissions)
|
||||||
|
{
|
||||||
|
const char *name = NULL;
|
||||||
|
char data[64];
|
||||||
|
ssize_t s;
|
||||||
|
if (lo.xattr_permissions == 1)
|
||||||
|
{
|
||||||
|
get_upper_layer (&lo)->has_privileged_stat_override = 1;
|
||||||
|
name = XATTR_PRIVILEGED_OVERRIDE_STAT;
|
||||||
|
}
|
||||||
|
else if (lo.xattr_permissions == 2)
|
||||||
|
{
|
||||||
|
get_upper_layer (&lo)->has_stat_override = 1;
|
||||||
|
name = XATTR_OVERRIDE_STAT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error (EXIT_FAILURE, 0, "invalid value for xattr_permissions");
|
||||||
|
|
||||||
|
s = fgetxattr (get_upper_layer (&lo)->fd, name, data, sizeof (data));
|
||||||
|
if (s < 0)
|
||||||
|
{
|
||||||
|
if (errno != ENODATA)
|
||||||
|
error (EXIT_FAILURE, errno, "read xattr `%s` from upperdir", name);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
ret = fstat (get_upper_layer (&lo)->fd, &st);
|
||||||
|
if (ret < 0)
|
||||||
|
error (EXIT_FAILURE, errno, "stat upperdir");
|
||||||
|
|
||||||
|
ret = write_permission_xattr (&lo, get_upper_layer (&lo)->fd,
|
||||||
|
lo.upperdir,
|
||||||
|
st.st_uid, st.st_gid, st.st_mode);
|
||||||
|
if (ret < 0)
|
||||||
|
error (EXIT_FAILURE, errno, "write xattr `%s` to upperdir", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lo.inodes = hash_initialize (2048, NULL, node_inode_hasher, node_inode_compare, inode_free);
|
lo.inodes = hash_initialize (2048, NULL, node_inode_hasher, node_inode_compare, inode_free);
|
||||||
|
|
||||||
lo.root = load_dir (&lo, NULL, lo.layers, ".", "");
|
lo.root = load_dir (&lo, NULL, lo.layers, ".", "");
|
||||||
|
68
utils.c
68
utils.c
@ -29,6 +29,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
|
||||||
#ifndef TEMP_FAILURE_RETRY
|
#ifndef TEMP_FAILURE_RETRY
|
||||||
#define TEMP_FAILURE_RETRY(expression) \
|
#define TEMP_FAILURE_RETRY(expression) \
|
||||||
@ -222,3 +223,70 @@ open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd,
|
|||||||
|
|
||||||
return *fd;
|
return *fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
mode_t mode;
|
||||||
|
char buf[64];
|
||||||
|
cleanup_close int cleanup_fd = -1;
|
||||||
|
const char *xattr_name;
|
||||||
|
|
||||||
|
if (l->has_stat_override == 0 && l->has_privileged_stat_override == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
xattr_name = l->has_privileged_stat_override ? XATTR_PRIVILEGED_OVERRIDE_STAT : XATTR_OVERRIDE_STAT;
|
||||||
|
|
||||||
|
if (fd >= 0)
|
||||||
|
{
|
||||||
|
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else if (abs_path)
|
||||||
|
{
|
||||||
|
ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char full_path[PATH_MAX];
|
||||||
|
|
||||||
|
full_path[0] = '\0';
|
||||||
|
ret = open_fd_or_get_path (l, path, full_path, &cleanup_fd, O_RDONLY);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
fd = cleanup_fd;
|
||||||
|
|
||||||
|
if (fd >= 0)
|
||||||
|
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1);
|
||||||
|
if (ret < 0 && errno == ENODATA)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[ret] = '\0';
|
||||||
|
|
||||||
|
ret = sscanf (buf, "%d:%d:%o", &uid, &gid, &mode);
|
||||||
|
if (ret != 3)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->st_uid = uid;
|
||||||
|
st->st_gid = gid;
|
||||||
|
st->st_mode = (st->st_mode & S_IFMT) | mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
5
utils.h
5
utils.h
@ -31,6 +31,9 @@
|
|||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include "fuse-overlayfs.h"
|
# include "fuse-overlayfs.h"
|
||||||
|
|
||||||
|
# define XATTR_OVERRIDE_STAT "user.fuseoverlayfs.override_stat"
|
||||||
|
# define XATTR_PRIVILEGED_OVERRIDE_STAT "security.fuseoverlayfs.override_stat"
|
||||||
|
|
||||||
void cleanup_freep (void *p);
|
void cleanup_freep (void *p);
|
||||||
void cleanup_filep (FILE **f);
|
void cleanup_filep (FILE **f);
|
||||||
void cleanup_closep (void *p);
|
void cleanup_closep (void *p);
|
||||||
@ -55,4 +58,6 @@ void statx_to_stat (struct statx *stx, struct stat *st);
|
|||||||
|
|
||||||
int safe_openat (int dirfd, const char *pathname, int flags, mode_t mode);
|
int safe_openat (int dirfd, const char *pathname, int flags, mode_t mode);
|
||||||
|
|
||||||
|
int override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user