main: support writing uid/gid/mode to xattr

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2020-08-21 19:02:56 +02:00
parent 5c7fc2856a
commit b8086b58fa
No known key found for this signature in database
GPG Key ID: E4730F97F60286ED
5 changed files with 160 additions and 11 deletions

View File

@ -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 *

View File

@ -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
View File

@ -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
View File

@ -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;

View File

@ -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