mirror of
https://github.com/containers/fuse-overlayfs.git
synced 2025-08-04 02:15:58 -04:00
main: support writing uid/gid/mode to xattr
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
5c7fc2856a
commit
b8086b58fa
8
direct.c
8
direct.c
@ -90,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;
|
||||||
@ -101,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
|
||||||
@ -118,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;
|
||||||
@ -128,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 *
|
||||||
|
@ -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;
|
||||||
|
148
main.c
148
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
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -464,10 +466,54 @@ 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
|
static int
|
||||||
do_fchown (struct ovl_data *lo, int fd, uid_t uid, gid_t gid, mode_t mode)
|
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);
|
return fchown (fd, uid, gid);
|
||||||
}
|
}
|
||||||
/* Make sure it is not used anymore. */
|
/* Make sure it is not used anymore. */
|
||||||
@ -476,6 +522,8 @@ do_fchown (struct ovl_data *lo, int fd, uid_t uid, gid_t gid, mode_t mode)
|
|||||||
static int
|
static int
|
||||||
do_chown (struct ovl_data *lo, const char *path, uid_t uid, gid_t gid, mode_t mode)
|
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);
|
return chown (path, uid, gid);
|
||||||
}
|
}
|
||||||
/* Make sure it is not used anymore. */
|
/* Make sure it is not used anymore. */
|
||||||
@ -484,6 +532,16 @@ do_chown (struct ovl_data *lo, const char *path, uid_t uid, gid_t gid, mode_t mo
|
|||||||
static int
|
static int
|
||||||
do_fchownat (struct ovl_data *lo, int dfd, const char *path, uid_t uid, gid_t gid, mode_t mode, int flags)
|
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);
|
return fchownat (dfd, path, uid, gid, 0);
|
||||||
}
|
}
|
||||||
/* Make sure it is not used anymore. */
|
/* Make sure it is not used anymore. */
|
||||||
@ -492,6 +550,24 @@ do_fchownat (struct ovl_data *lo, int dfd, const char *path, uid_t uid, gid_t gi
|
|||||||
static int
|
static int
|
||||||
do_fchmod (struct ovl_data *lo, int fd, mode_t mode)
|
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);
|
return fchmod (fd, mode);
|
||||||
}
|
}
|
||||||
/* Make sure it is not used anymore. */
|
/* Make sure it is not used anymore. */
|
||||||
@ -500,6 +576,24 @@ do_fchmod (struct ovl_data *lo, int fd, mode_t mode)
|
|||||||
static int
|
static int
|
||||||
do_chmod (struct ovl_data *lo, const char *path, mode_t mode)
|
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);
|
return chmod (path, mode);
|
||||||
}
|
}
|
||||||
/* Make sure it is not used anymore. */
|
/* Make sure it is not used anymore. */
|
||||||
@ -2540,7 +2634,7 @@ 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 = do_fchown (lo, dfd, uid, gid, mode);
|
ret = do_fchown (lo, dfd, uid, gid, mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -2764,7 +2858,7 @@ 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 = do_fchown (lo, dfd, st.st_uid, st.st_gid, st.st_mode);
|
ret = do_fchown (lo, dfd, st.st_uid, st.st_gid, st.st_mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -3220,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)
|
||||||
@ -3234,7 +3328,7 @@ 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 (do_fchown (lo, fd, uid, gid, mode) < 0)
|
if (do_fchown (lo, fd, uid, gid, mode) < 0)
|
||||||
{
|
{
|
||||||
@ -3877,7 +3971,7 @@ 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 = do_fchownat (lo, lo->workdir_fd, wd_tmp_file_name, uid, gid, AT_SYMLINK_NOFOLLOW, 0755);
|
ret = do_fchownat (lo, lo->workdir_fd, wd_tmp_file_name, uid, gid, AT_SYMLINK_NOFOLLOW, 0755);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -5146,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,
|
||||||
@ -5255,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, ".", "");
|
||||||
|
12
utils.c
12
utils.c
@ -225,7 +225,7 @@ open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st)
|
override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
@ -246,6 +246,12 @@ override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
else if (abs_path)
|
||||||
|
{
|
||||||
|
ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char full_path[PATH_MAX];
|
char full_path[PATH_MAX];
|
||||||
@ -259,7 +265,11 @@ override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st)
|
|||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
|
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1);
|
ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1);
|
||||||
|
if (ret < 0 && errno == ENODATA)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
2
utils.h
2
utils.h
@ -58,6 +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 *path, struct stat *st);
|
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