Merge pull request #422 from akihikodaki/containers

Prefer user.containers.override_stat over user.fuseoverlayfs.
This commit is contained in:
Giuseppe Scrivano 2024-06-17 20:33:21 +02:00 committed by GitHub
commit 4217e1c160
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 211 additions and 102 deletions

View File

@ -4,23 +4,23 @@ on: [push, pull_request]
jobs: jobs:
build_job: build_job:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
name: Build on ${{ matrix.arch }} name: Build on ${{ matrix.arch }}
strategy: strategy:
matrix: matrix:
include: include:
- arch: armv7 - arch: armv7
distro: ubuntu20.04 distro: ubuntu22.04
- arch: aarch64 - arch: aarch64
distro: ubuntu20.04 distro: ubuntu22.04
- arch: s390x - arch: s390x
distro: ubuntu20.04 distro: ubuntu22.04
- arch: ppc64le - arch: ppc64le
distro: ubuntu20.04 distro: ubuntu22.04
steps: steps:
- uses: actions/checkout@v2.1.0 - uses: actions/checkout@v2.1.0
- uses: uraimo/run-on-arch-action@v2.0.5 - uses: uraimo/run-on-arch-action@v2.7.2
name: Build name: Build
id: build id: build
with: with:
@ -34,7 +34,7 @@ jobs:
install: | install: |
apt-get update -q -y apt-get update -q -y
apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3.8 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel libfuse3-dev bats apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel libfuse3-dev bats
run: | run: |
./autogen.sh ./autogen.sh
@ -49,7 +49,7 @@ jobs:
fuse-overlayfs fuse-overlayfs
Test: Test:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -66,7 +66,7 @@ jobs:
- name: install dependencies - name: install dependencies
run: | run: |
sudo apt-get update -q -y sudo apt-get update -q -y
sudo apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3.8 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel wget libfuse3-dev bats sudo apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel wget libfuse3-dev bats
sudo mkdir -p /lower /upper /mnt $GOPATH/src/github.com/containers sudo mkdir -p /lower /upper /mnt $GOPATH/src/github.com/containers
sudo sh -c "cd $GOPATH/src/github.com/containers; git clone --depth=1 https://github.com/containers/storage" sudo sh -c "cd $GOPATH/src/github.com/containers; git clone --depth=1 https://github.com/containers/storage"
@ -90,7 +90,7 @@ jobs:
- name: Archive build artifacts - name: Archive build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: fuse-overlayfs-x86_64-ubuntu20.04 name: fuse-overlayfs-x86_64-ubuntu22.04
path: | path: |
fuse-overlayfs fuse-overlayfs
if: ${{ matrix.test == 'ovl-whiteouts' }} if: ${{ matrix.test == 'ovl-whiteouts' }}

View File

@ -6,12 +6,12 @@ import stat
import errno import errno
XATTR_OVERRIDE_STAT_PRIVILEGED = "security.fuseoverlayfs.override_stat" XATTR_OVERRIDE_STAT_PRIVILEGED = "security.fuseoverlayfs.override_stat"
XATTR_OVERRIDE_STAT = "user.fuseoverlayfs.override_stat" XATTR_OVERRIDE_CONTAINERS_STAT = "user.fuseoverlayfs.override_stat"
if os.geteuid() == 0: if os.geteuid() == 0:
xattr_name = XATTR_OVERRIDE_STAT_PRIVILEGED xattr_name = XATTR_OVERRIDE_STAT_PRIVILEGED
else: else:
xattr_name = XATTR_OVERRIDE_STAT xattr_name = XATTR_OVERRIDE_CONTAINERS_STAT
cwd_fd = os.open(".", os.O_PATH) cwd_fd = os.open(".", os.O_PATH)
@ -23,7 +23,7 @@ def fix_path(path):
os.setxattr(path, xattr_name, str.encode(content), flags=os.XATTR_CREATE, follow_symlinks=False) os.setxattr(path, xattr_name, str.encode(content), flags=os.XATTR_CREATE, follow_symlinks=False)
except Exception as e: except Exception as e:
if e.errno == errno.EEXIST: if e.errno == errno.EEXIST:
print("attr %s already present for %s: %s" % (XATTR_OVERRIDE_STAT, path, e.errno)) print("attr %s already present for %s: %s" % (xattr_name, path, e.errno))
return return
raise e raise e

View File

@ -186,10 +186,10 @@ direct_load_data_source (struct ovl_layer *l, const char *opaque, const char *pa
if (fgetxattr (l->fd, XATTR_PRIVILEGED_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0) if (fgetxattr (l->fd, XATTR_PRIVILEGED_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_PRIVILEGED; l->stat_override_mode = STAT_OVERRIDE_PRIVILEGED;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_USER;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, tmp, sizeof (tmp)) >= 0) else if (fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_CONTAINERS; l->stat_override_mode = STAT_OVERRIDE_CONTAINERS;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_USER;
return 0; return 0;
} }

243
main.c
View File

@ -59,6 +59,7 @@
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#include <sys/xattr.h> #include <sys/xattr.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/xattr.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <pthread.h> #include <pthread.h>
@ -141,6 +142,7 @@ open_by_handle_at (int mount_fd, struct file_handle *handle, int flags)
#define ORIGIN_XATTR "user.fuseoverlayfs.origin" #define ORIGIN_XATTR "user.fuseoverlayfs.origin"
#define OPAQUE_XATTR "user.fuseoverlayfs.opaque" #define OPAQUE_XATTR "user.fuseoverlayfs.opaque"
#define XATTR_CONTAINERS_PREFIX "user.containers." #define XATTR_CONTAINERS_PREFIX "user.containers."
#define XATTR_CONTAINERS_OVERRIDE_PREFIX "user.containers.override_"
#define UNPRIVILEGED_XATTR_PREFIX "user.overlay." #define UNPRIVILEGED_XATTR_PREFIX "user.overlay."
#define UNPRIVILEGED_OPAQUE_XATTR "user.overlay.opaque" #define UNPRIVILEGED_OPAQUE_XATTR "user.overlay.opaque"
#define PRIVILEGED_XATTR_PREFIX "trusted.overlay." #define PRIVILEGED_XATTR_PREFIX "trusted.overlay."
@ -524,11 +526,46 @@ has_prefix (const char *str, const char *pref)
} }
static bool static bool
can_access_xattr (const char *name) can_access_xattr (const struct ovl_layer *l, const char *name)
{ {
return ! has_prefix (name, XATTR_PREFIX) return ! (has_prefix (name, XATTR_PREFIX)
&& ! has_prefix (name, PRIVILEGED_XATTR_PREFIX) || has_prefix (name, PRIVILEGED_XATTR_PREFIX)
&& ! has_prefix (name, UNPRIVILEGED_XATTR_PREFIX); || has_prefix (name, UNPRIVILEGED_XATTR_PREFIX)
|| (l->stat_override_mode == STAT_OVERRIDE_CONTAINERS &&
has_prefix (name, XATTR_SECURITY_PREFIX)));
}
static bool encoded_xattr_name (const struct ovl_layer *l, const char *name)
{
return has_prefix (name, XATTR_CONTAINERS_OVERRIDE_PREFIX) &&
! can_access_xattr (l, name + sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1);
}
static const char *decode_xattr_name (const struct ovl_layer *l, const char *name)
{
if (encoded_xattr_name (l, name))
return name + sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1;
if (can_access_xattr (l, name))
return name;
return NULL;
}
static const char *encode_xattr_name (const struct ovl_layer *l, char *buf,
const char *name)
{
if (can_access_xattr (l, name))
return name;
if (l->stat_override_mode != STAT_OVERRIDE_CONTAINERS ||
strlen(name) > XATTR_NAME_MAX + 1 - sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX))
return NULL;
strcpy(buf, XATTR_CONTAINERS_OVERRIDE_PREFIX);
strcpy(buf + sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1, name);
return buf;
} }
static ssize_t static ssize_t
@ -539,17 +576,21 @@ write_permission_xattr (struct ovl_data *lo, int fd, const char *path, uid_t uid
int ret; int ret;
const char *name = NULL; const char *name = NULL;
switch (lo->xattr_permissions) switch (get_upper_layer (lo)->stat_override_mode)
{ {
case 0: case STAT_OVERRIDE_NONE:
return 0; return 0;
case 1: case STAT_OVERRIDE_USER:
name = XATTR_OVERRIDE_STAT;
break;
case STAT_OVERRIDE_PRIVILEGED:
name = XATTR_PRIVILEGED_OVERRIDE_STAT; name = XATTR_PRIVILEGED_OVERRIDE_STAT;
break; break;
case 2: case STAT_OVERRIDE_CONTAINERS:
name = XATTR_OVERRIDE_STAT; name = XATTR_OVERRIDE_CONTAINERS_STAT;
break; break;
default: default:
@ -622,22 +663,32 @@ do_fchownat (struct ovl_data *lo, int dfd, const char *path, uid_t uid, gid_t gi
#define fchownat ERROR #define fchownat ERROR
static int static int
do_fchmod (struct ovl_data *lo, int fd, mode_t mode) do_stat (struct ovl_node *node, int fd, const char *path, struct stat *st)
{
struct ovl_layer *l = node->layer;
if (fd >= 0)
return l->ds->fstat (l, fd, path, STATX_BASIC_STATS, st);
if (path != NULL)
return stat (path, st);
if (node->hidden)
return fstatat (node_dirfd (node), node->path, st, AT_SYMLINK_NOFOLLOW);
return l->ds->statat (l, node->path, st, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS);
}
static int
do_fchmod (struct ovl_data *lo, struct ovl_node *node, int fd, mode_t mode)
{ {
if (lo->xattr_permissions) if (lo->xattr_permissions)
{ {
struct ovl_layer *upper = get_upper_layer (lo);
struct stat st; struct stat st;
if (upper == NULL)
{
errno = EROFS;
return -1;
}
st.st_uid = 0; st.st_uid = 0;
st.st_gid = 0; st.st_gid = 0;
if (override_mode (upper, fd, NULL, NULL, &st) < 0 && errno != ENODATA) if (do_stat (node, fd, NULL, &st) < 0)
return -1; return -1;
return write_permission_xattr (lo, fd, NULL, st.st_uid, st.st_gid, mode); return write_permission_xattr (lo, fd, NULL, st.st_uid, st.st_gid, mode);
@ -648,22 +699,15 @@ do_fchmod (struct ovl_data *lo, int fd, mode_t mode)
#define fchmod ERROR #define fchmod ERROR
static int static int
do_chmod (struct ovl_data *lo, const char *path, mode_t mode) do_chmod (struct ovl_data *lo, struct ovl_node *node, const char *path, mode_t mode)
{ {
if (lo->xattr_permissions) if (lo->xattr_permissions)
{ {
struct ovl_layer *upper = get_upper_layer (lo);
struct stat st; struct stat st;
if (upper == NULL)
{
errno = EROFS;
return -1;
}
st.st_uid = 0; st.st_uid = 0;
st.st_gid = 0; st.st_gid = 0;
if (override_mode (upper, -1, path, NULL, &st) < 0 && errno != ENODATA) if (do_stat (node, -1, path, &st) < 0)
return -1; return -1;
return write_permission_xattr (lo, -1, path, st.st_uid, st.st_gid, mode); return write_permission_xattr (lo, -1, path, st.st_uid, st.st_gid, mode);
@ -921,14 +965,8 @@ rpl_stat (fuse_req_t req, struct ovl_node *node, int fd, const char *path, struc
if (st_in) if (st_in)
memcpy (st, st_in, sizeof (*st)); memcpy (st, st_in, sizeof (*st));
else if (fd >= 0)
ret = l->ds->fstat (l, fd, path, STATX_BASIC_STATS, st);
else if (path != NULL)
ret = stat (path, st);
else if (node->hidden)
ret = fstatat (node_dirfd (node), node->path, st, AT_SYMLINK_NOFOLLOW);
else else
ret = l->ds->statat (l, node->path, st, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS); ret = do_stat (node, fd, path, st);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -2582,7 +2620,7 @@ inherit_acl (struct ovl_data *lo, struct ovl_node *parent, int targetfd, const c
/* in-place filter xattrs that cannot be accessed. */ /* in-place filter xattrs that cannot be accessed. */
static ssize_t static ssize_t
filter_xattrs_list (char *buf, ssize_t len) filter_xattrs_list (struct ovl_layer *l, char *buf, ssize_t len)
{ {
ssize_t ret = 0; ssize_t ret = 0;
char *it; char *it;
@ -2598,14 +2636,17 @@ filter_xattrs_list (char *buf, ssize_t len)
it_len = strlen (it) + 1; it_len = strlen (it) + 1;
if (can_access_xattr (it)) if (can_access_xattr (l, it))
{ {
it += it_len; it += it_len;
ret += it_len; ret += it_len;
} }
else else
{ {
char *next = it + it_len; char *next = it;
next += encoded_xattr_name (l, it) ?
sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1 : it_len;
memmove (it, next, buf + len - next); memmove (it, next, buf + len - next);
len -= it_len; len -= it_len;
@ -2665,7 +2706,7 @@ ovl_listxattr (fuse_req_t req, fuse_ino_t ino, size_t size)
return; return;
} }
len = filter_xattrs_list (buf, ret); len = filter_xattrs_list (node->layer, buf, ret);
if (size == 0) if (size == 0)
fuse_reply_xattr (req, len); fuse_reply_xattr (req, len);
@ -2680,7 +2721,8 @@ ovl_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
ssize_t len; ssize_t len;
struct ovl_node *node; struct ovl_node *node;
struct ovl_data *lo = ovl_data (req); struct ovl_data *lo = ovl_data (req);
cleanup_free char *buf = NULL; cleanup_free char *value_buf = NULL;
char name_buf[XATTR_NAME_MAX + 1];
int ret; int ret;
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
@ -2692,12 +2734,6 @@ ovl_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
return; return;
} }
if (! can_access_xattr (name))
{
fuse_reply_err (req, ENODATA);
return;
}
node = do_lookup_file (lo, ino, NULL); node = do_lookup_file (lo, ino, NULL);
if (node == NULL || node->whiteout) if (node == NULL || node->whiteout)
{ {
@ -2705,10 +2741,17 @@ ovl_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
return; return;
} }
name = encode_xattr_name (node->layer, name_buf, name);
if (!name)
{
fuse_reply_err (req, ENODATA);
return;
}
if (size > 0) if (size > 0)
{ {
buf = malloc (size); value_buf = malloc (size);
if (buf == NULL) if (value_buf == NULL)
{ {
fuse_reply_err (req, errno); fuse_reply_err (req, errno);
return; return;
@ -2716,12 +2759,12 @@ ovl_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
} }
if (! node->hidden) if (! node->hidden)
ret = node->layer->ds->getxattr (node->layer, node->path, name, buf, size); ret = node->layer->ds->getxattr (node->layer, node->path, name, value_buf, size);
else else
{ {
char path[PATH_MAX]; char path[PATH_MAX];
strconcat3 (path, PATH_MAX, lo->workdir, "/", node->path); strconcat3 (path, PATH_MAX, lo->workdir, "/", node->path);
ret = getxattr (path, name, buf, size); ret = getxattr (path, name, value_buf, size);
} }
if (ret < 0) if (ret < 0)
@ -2735,7 +2778,7 @@ ovl_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
if (size == 0) if (size == 0)
fuse_reply_xattr (req, len); fuse_reply_xattr (req, len);
else else
fuse_reply_buf (req, buf, len); fuse_reply_buf (req, value_buf, len);
} }
static void static void
@ -2756,7 +2799,8 @@ ovl_access (fuse_req_t req, fuse_ino_t ino, int mask)
} }
static int static int
copy_xattr (int sfd, int dfd, char *buf, size_t buf_size) copy_xattr (const struct ovl_layer *sl, int sfd,
const struct ovl_layer *dl, int dfd, char *buf, size_t buf_size)
{ {
ssize_t xattr_len; ssize_t xattr_len;
@ -2767,9 +2811,16 @@ copy_xattr (int sfd, int dfd, char *buf, size_t buf_size)
for (it = buf; it - buf < xattr_len; it += strlen (it) + 1) for (it = buf; it - buf < xattr_len; it += strlen (it) + 1)
{ {
cleanup_free char *v = NULL; cleanup_free char *v = NULL;
const char *decoded_name = decode_xattr_name (sl, it);
const char *encoded_name;
char buf[XATTR_NAME_MAX + 1];
ssize_t s; ssize_t s;
if (! can_access_xattr (it)) if (! decoded_name)
continue;
encoded_name = encode_xattr_name (dl, buf, decoded_name);
if (! encoded_name)
continue; continue;
s = safe_read_xattr (&v, sfd, it, 256); s = safe_read_xattr (&v, sfd, it, 256);
@ -2780,7 +2831,7 @@ copy_xattr (int sfd, int dfd, char *buf, size_t buf_size)
return -1; return -1;
} }
if (fsetxattr (dfd, it, v, s, 0) < 0) if (fsetxattr (dfd, encoded_name, v, s, 0) < 0)
{ {
if (errno == EINVAL || errno == EOPNOTSUPP) if (errno == EINVAL || errno == EOPNOTSUPP)
continue; continue;
@ -2856,7 +2907,8 @@ static int create_node_directory (struct ovl_data *lo, struct ovl_node *src);
static int static int
create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct timespec *times, create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct timespec *times,
struct ovl_node *parent, int xattr_sfd, uid_t uid, gid_t gid, mode_t mode, bool set_opaque, struct stat *st_out) struct ovl_node *parent, struct ovl_layer *sl, int xattr_sfd,
uid_t uid, gid_t gid, mode_t mode, bool set_opaque, struct stat *st_out)
{ {
int ret; int ret;
int saved_errno; int saved_errno;
@ -2920,7 +2972,7 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct
goto out; goto out;
} }
ret = copy_xattr (xattr_sfd, dfd, buf, buf_size); ret = copy_xattr (sl, xattr_sfd, get_upper_layer (lo), dfd, buf, buf_size);
if (ret < 0) if (ret < 0)
goto out; goto out;
} }
@ -3013,7 +3065,7 @@ create_node_directory (struct ovl_data *lo, struct ovl_node *src)
if (override_mode (src->layer, sfd, NULL, NULL, &st) < 0 && errno != ENODATA && errno != EOPNOTSUPP) if (override_mode (src->layer, sfd, NULL, NULL, &st) < 0 && errno != ENODATA && errno != EOPNOTSUPP)
return -1; return -1;
ret = create_directory (lo, get_upper_layer (lo)->fd, src->path, times, src->parent, sfd, st.st_uid, st.st_gid, st.st_mode, false, NULL); ret = create_directory (lo, get_upper_layer (lo)->fd, src->path, times, src->parent, src->layer, sfd, st.st_uid, st.st_gid, st.st_mode, false, NULL);
if (ret == 0) if (ret == 0)
{ {
src->layer = get_upper_layer (lo); src->layer = get_upper_layer (lo);
@ -3192,7 +3244,7 @@ copyup (struct ovl_data *lo, struct ovl_node *node)
if (ret < 0) if (ret < 0)
goto exit; goto exit;
ret = copy_xattr (sfd, dfd, buf, buf_size); ret = copy_xattr (node->layer, sfd, get_upper_layer (lo), dfd, buf, buf_size);
if (ret < 0) if (ret < 0)
goto exit; goto exit;
@ -3472,6 +3524,7 @@ ovl_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name,
cleanup_lock int l = enter_big_lock (); cleanup_lock int l = enter_big_lock ();
struct ovl_data *lo = ovl_data (req); struct ovl_data *lo = ovl_data (req);
struct ovl_node *node; struct ovl_node *node;
char name_buf[XATTR_NAME_MAX + 1];
int ret; int ret;
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
@ -3484,12 +3537,6 @@ ovl_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name,
return; return;
} }
if (has_prefix (name, PRIVILEGED_XATTR_PREFIX) || has_prefix (name, XATTR_PREFIX) || has_prefix (name, XATTR_CONTAINERS_PREFIX))
{
fuse_reply_err (req, EPERM);
return;
}
node = do_lookup_file (lo, ino, NULL); node = do_lookup_file (lo, ino, NULL);
if (node == NULL || node->whiteout) if (node == NULL || node->whiteout)
{ {
@ -3504,6 +3551,13 @@ ovl_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name,
return; return;
} }
name = encode_xattr_name (node->layer, name_buf, name);
if (!name)
{
fuse_reply_err (req, EPERM);
return;
}
if (! node->hidden) if (! node->hidden)
ret = direct_setxattr (node->layer, node->path, name, value, size, flags); ret = direct_setxattr (node->layer, node->path, name, value, size, flags);
else else
@ -3545,6 +3599,7 @@ ovl_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name)
cleanup_lock int l = enter_big_lock (); cleanup_lock int l = enter_big_lock ();
struct ovl_node *node; struct ovl_node *node;
struct ovl_data *lo = ovl_data (req); struct ovl_data *lo = ovl_data (req);
char name_buf[XATTR_NAME_MAX + 1];
int ret; int ret;
if (UNLIKELY (ovl_debug (req))) if (UNLIKELY (ovl_debug (req)))
@ -3564,6 +3619,13 @@ ovl_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name)
return; return;
} }
name = encode_xattr_name (node->layer, name_buf, name);
if (!name)
{
fuse_reply_err (req, EPERM);
return;
}
if (! node->hidden) if (! node->hidden)
ret = direct_removexattr (node->layer, node->path, name); ret = direct_removexattr (node->layer, node->path, name);
else else
@ -3823,7 +3885,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 (do_fchmod (lo, fi->fh, inode->mode) < 0) if (do_fchmod (lo, inode->node, fi->fh, inode->mode) < 0)
{ {
fuse_reply_err (req, errno); fuse_reply_err (req, errno);
return; return;
@ -4135,9 +4197,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 = do_fchmod (lo, fd, attr->st_mode); ret = do_fchmod (lo, node, fd, attr->st_mode);
else else
ret = do_chmod (lo, path, attr->st_mode); ret = do_chmod (lo, node, path, attr->st_mode);
if (ret < 0) if (ret < 0)
{ {
fuse_reply_err (req, errno); fuse_reply_err (req, errno);
@ -4161,6 +4223,24 @@ 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)
{ {
struct stat st;
if (do_stat (node, fd, NULL, &st) < 0)
{
fuse_reply_err (req, errno);
return;
}
if (uid == -1)
{
uid = st.st_uid;
}
if (gid == -1)
{
gid = st.st_gid;
}
if (fd >= 0) if (fd >= 0)
ret = do_fchown (lo, fd, uid, gid, node->ino->mode); ret = do_fchown (lo, fd, uid, gid, node->ino->mode);
else else
@ -5089,7 +5169,7 @@ ovl_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
return; return;
} }
ret = create_directory (lo, get_upper_layer (lo)->fd, path, NULL, pnode, -1, ret = create_directory (lo, get_upper_layer (lo)->fd, path, NULL, pnode, NULL, -1,
get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), mode & ~ctx->umask, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid), mode & ~ctx->umask,
true, &st); true, &st);
if (ret < 0) if (ret < 0)
@ -5777,13 +5857,22 @@ main (int argc, char *argv[])
} }
else if (lo.xattr_permissions == 2) else if (lo.xattr_permissions == 2)
{ {
get_upper_layer (&lo)->stat_override_mode = STAT_OVERRIDE_USER; get_upper_layer (&lo)->stat_override_mode = STAT_OVERRIDE_CONTAINERS;
name = XATTR_OVERRIDE_STAT; name = XATTR_OVERRIDE_CONTAINERS_STAT;
} }
else else
error (EXIT_FAILURE, 0, "invalid value for xattr_permissions"); error (EXIT_FAILURE, 0, "invalid value for xattr_permissions");
s = fgetxattr (get_upper_layer (&lo)->fd, name, data, sizeof (data)); s = fgetxattr (get_upper_layer (&lo)->fd, name, data, sizeof (data));
if (s < 0 && errno == ENODATA && lo.xattr_permissions == 2)
{
s = fgetxattr (get_upper_layer (&lo)->fd, XATTR_OVERRIDE_STAT, data, sizeof (data));
if (s >= 0)
{
get_upper_layer (&lo)->stat_override_mode = STAT_OVERRIDE_USER;
name = XATTR_OVERRIDE_STAT;
}
}
if (s < 0) if (s < 0)
{ {
bool found = false; bool found = false;
@ -5794,15 +5883,19 @@ main (int argc, char *argv[])
for (l = get_lower_layers (&lo); l; l = l->next) for (l = get_lower_layers (&lo); l; l = l->next)
{ {
s = fgetxattr (l->fd, name, data, sizeof (data)); switch (lo.xattr_permissions)
if (s < 0 && errno != ENODATA)
error (EXIT_FAILURE, errno, "fgetxattr mode from lower layer");
if (s < 0 && lo.xattr_permissions == 2)
{ {
case 1:
s = fgetxattr (l->fd, name, data, sizeof (data));
break;
case 2:
s = fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, data, sizeof (data)); s = fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, data, sizeof (data));
if (s < 0 && errno != ENODATA) if (s < 0 && errno == ENODATA)
error (EXIT_FAILURE, errno, "fgetxattr mode from lower layer"); s = fgetxattr (l->fd, XATTR_OVERRIDE_STAT, data, sizeof (data));
break;
} }
if (s > 0) if (s > 0)
{ {
ret = fsetxattr (get_upper_layer (&lo)->fd, name, data, s, 0); ret = fsetxattr (get_upper_layer (&lo)->fd, name, data, s, 0);

View File

@ -29,3 +29,27 @@ else
fi fi
fusermount -u merged || [ $? -eq "${EXPECT_UMOUNT_STATUS:-0}" ] fusermount -u merged || [ $? -eq "${EXPECT_UMOUNT_STATUS:-0}" ]
# xattr_permissions=2
rm -rf lower upper workdir merged
mkdir lower upper workdir merged
touch upper/file
unshare -r setcap cap_net_admin+ep upper/file
fuse-overlayfs -o lowerdir=lower,upperdir=upper,workdir=workdir,xattr_permissions=2 merged
# Ensure the security xattr namespace is isolated.
test "$(unshare -r getcap merged/file)" = ''
unshare -r setcap cap_net_admin+ep merged/file
test "$(unshare -r getcap merged/file)" = 'merged/file cap_net_admin=ep'
# Ensure UID is preserved with chgrp.
podman unshare chgrp 1 merged/file
test $(podman unshare stat -c %u:%g merged/file) = 0:1
# Ensure UID and GID are preserved with chmod.
chmod 600 merged/file
test $(podman unshare stat -c %u:%g merged/file) = 0:1
fusermount -u merged || [ $? -eq "${EXPECT_UMOUNT_STATUS:-0}" ]

16
utils.c
View File

@ -270,14 +270,10 @@ override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *pa
if (fd >= 0) if (fd >= 0)
{ {
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1); ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
if (ret < 0)
return ret;
} }
else if (abs_path) else if (abs_path)
{ {
ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1); ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1);
if (ret < 0)
return ret;
} }
else else
{ {
@ -292,16 +288,12 @@ override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *pa
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)
return ret;
} }
if (ret < 0)
return errno == ENODATA ? 0 : ret;
buf[ret] = '\0'; buf[ret] = '\0';
ret = sscanf (buf, "%d:%d:%o", &uid, &gid, &mode); ret = sscanf (buf, "%d:%d:%o", &uid, &gid, &mode);