1*1ec284f3SChristian Brauner // SPDX-License-Identifier: GPL-2.0 2*1ec284f3SChristian Brauner #define _GNU_SOURCE 3*1ec284f3SChristian Brauner 4*1ec284f3SChristian Brauner #include <fcntl.h> 5*1ec284f3SChristian Brauner #include <limits.h> 6*1ec284f3SChristian Brauner #include <sched.h> 7*1ec284f3SChristian Brauner #include <stdio.h> 8*1ec284f3SChristian Brauner #include <unistd.h> 9*1ec284f3SChristian Brauner #include <sys/stat.h> 10*1ec284f3SChristian Brauner #include <sys/syscall.h> 11*1ec284f3SChristian Brauner 12*1ec284f3SChristian Brauner #include <linux/mount.h> 13*1ec284f3SChristian Brauner #include <linux/types.h> 14*1ec284f3SChristian Brauner 15*1ec284f3SChristian Brauner #include "kselftest_harness.h" 16*1ec284f3SChristian Brauner #include "../wrappers.h" 17*1ec284f3SChristian Brauner #include "../utils.h" 18*1ec284f3SChristian Brauner 19*1ec284f3SChristian Brauner /* 20*1ec284f3SChristian Brauner * An idmapping that maps the mount-visible id range [0, ID_RANGE) onto the 21*1ec284f3SChristian Brauner * host/overlay-final id range [ID_HOST, ID_HOST + ID_RANGE). Through such an 22*1ec284f3SChristian Brauner * idmapped overlay mount, an overlay-final id of ID_HOST + n is reported as n, 23*1ec284f3SChristian Brauner * and an id of n requested through the mount is stored as ID_HOST + n. 24*1ec284f3SChristian Brauner */ 25*1ec284f3SChristian Brauner #define ID_NS 0 26*1ec284f3SChristian Brauner #define ID_HOST 10000 27*1ec284f3SChristian Brauner #define ID_RANGE 10000 28*1ec284f3SChristian Brauner 29*1ec284f3SChristian Brauner /* 30*1ec284f3SChristian Brauner * For the composition test the lower layer's on-disk ids live in a 31*1ec284f3SChristian Brauner * separate range and are mapped by an idmapped lower layer onto the 32*1ec284f3SChristian Brauner * overlay-final range [ID_HOST, ID_HOST + ID_RANGE). 33*1ec284f3SChristian Brauner */ 34*1ec284f3SChristian Brauner #define LAYER_HOST 20000 35*1ec284f3SChristian Brauner 36*1ec284f3SChristian Brauner #ifndef MOUNT_ATTR_IDMAP 37*1ec284f3SChristian Brauner #define MOUNT_ATTR_IDMAP 0x00100000 38*1ec284f3SChristian Brauner #endif 39*1ec284f3SChristian Brauner 40*1ec284f3SChristian Brauner #ifndef __NR_mount_setattr 41*1ec284f3SChristian Brauner #define __NR_mount_setattr 442 42*1ec284f3SChristian Brauner #endif 43*1ec284f3SChristian Brauner 44*1ec284f3SChristian Brauner static inline int sys_mount_setattr(int dfd, const char *path, 45*1ec284f3SChristian Brauner unsigned int flags, 46*1ec284f3SChristian Brauner struct mount_attr *attr, size_t size) 47*1ec284f3SChristian Brauner { 48*1ec284f3SChristian Brauner return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); 49*1ec284f3SChristian Brauner } 50*1ec284f3SChristian Brauner 51*1ec284f3SChristian Brauner static bool ovl_supported(void) 52*1ec284f3SChristian Brauner { 53*1ec284f3SChristian Brauner int fd = sys_fsopen("overlay", 0); 54*1ec284f3SChristian Brauner 55*1ec284f3SChristian Brauner if (fd < 0) 56*1ec284f3SChristian Brauner return false; 57*1ec284f3SChristian Brauner close(fd); 58*1ec284f3SChristian Brauner return true; 59*1ec284f3SChristian Brauner } 60*1ec284f3SChristian Brauner 61*1ec284f3SChristian Brauner /* base/{l,u,w} owned by ID_HOST so they map to ID_NS through the idmap. */ 62*1ec284f3SChristian Brauner static int setup_layers(const char *base) 63*1ec284f3SChristian Brauner { 64*1ec284f3SChristian Brauner static const char *sub[] = { "", "/l", "/u", "/w" }; 65*1ec284f3SChristian Brauner char path[PATH_MAX]; 66*1ec284f3SChristian Brauner 67*1ec284f3SChristian Brauner for (size_t i = 0; i < ARRAY_SIZE(sub); i++) { 68*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s%s", base, sub[i]); 69*1ec284f3SChristian Brauner if (mkdir(path, 0755) && errno != EEXIST) 70*1ec284f3SChristian Brauner return -1; 71*1ec284f3SChristian Brauner if (i && chown(path, ID_HOST, ID_HOST)) 72*1ec284f3SChristian Brauner return -1; 73*1ec284f3SChristian Brauner } 74*1ec284f3SChristian Brauner return 0; 75*1ec284f3SChristian Brauner } 76*1ec284f3SChristian Brauner 77*1ec284f3SChristian Brauner static int ovl_mount(const char *base, bool nfs_export) 78*1ec284f3SChristian Brauner { 79*1ec284f3SChristian Brauner char lower[PATH_MAX], upper[PATH_MAX], work[PATH_MAX]; 80*1ec284f3SChristian Brauner int fsfd, ovl; 81*1ec284f3SChristian Brauner 82*1ec284f3SChristian Brauner snprintf(lower, sizeof(lower), "%s/l", base); 83*1ec284f3SChristian Brauner snprintf(upper, sizeof(upper), "%s/u", base); 84*1ec284f3SChristian Brauner snprintf(work, sizeof(work), "%s/w", base); 85*1ec284f3SChristian Brauner 86*1ec284f3SChristian Brauner fsfd = sys_fsopen("overlay", 0); 87*1ec284f3SChristian Brauner if (fsfd < 0) 88*1ec284f3SChristian Brauner return -1; 89*1ec284f3SChristian Brauner 90*1ec284f3SChristian Brauner if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) || 91*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", lower, 0) || 92*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", upper, 0) || 93*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", work, 0)) 94*1ec284f3SChristian Brauner goto err; 95*1ec284f3SChristian Brauner if (nfs_export && 96*1ec284f3SChristian Brauner (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "index", "on", 0) || 97*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "nfs_export", "on", 0))) 98*1ec284f3SChristian Brauner goto err; 99*1ec284f3SChristian Brauner if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) 100*1ec284f3SChristian Brauner goto err; 101*1ec284f3SChristian Brauner 102*1ec284f3SChristian Brauner ovl = sys_fsmount(fsfd, 0, 0); 103*1ec284f3SChristian Brauner close(fsfd); 104*1ec284f3SChristian Brauner return ovl; 105*1ec284f3SChristian Brauner err: 106*1ec284f3SChristian Brauner close(fsfd); 107*1ec284f3SChristian Brauner return -1; 108*1ec284f3SChristian Brauner } 109*1ec284f3SChristian Brauner 110*1ec284f3SChristian Brauner /* Idmap the (still detached, not yet visible) overlay mount @mfd. */ 111*1ec284f3SChristian Brauner static int ovl_idmap(int mfd) 112*1ec284f3SChristian Brauner { 113*1ec284f3SChristian Brauner struct mount_attr attr = { 114*1ec284f3SChristian Brauner .attr_set = MOUNT_ATTR_IDMAP, 115*1ec284f3SChristian Brauner }; 116*1ec284f3SChristian Brauner int ret, userns_fd; 117*1ec284f3SChristian Brauner 118*1ec284f3SChristian Brauner /* 119*1ec284f3SChristian Brauner * get_userns_fd(fs_id, mount_id, range): a file whose filesystem id 120*1ec284f3SChristian Brauner * is fs_id + n is shown through the idmapped mount as mount_id + n. 121*1ec284f3SChristian Brauner * Here the overlay-final (fs side) range is [ID_HOST, ..) and the 122*1ec284f3SChristian Brauner * caller-visible (mount side) range is [ID_NS, ..). 123*1ec284f3SChristian Brauner */ 124*1ec284f3SChristian Brauner userns_fd = get_userns_fd(ID_HOST, ID_NS, ID_RANGE); 125*1ec284f3SChristian Brauner if (userns_fd < 0) 126*1ec284f3SChristian Brauner return -1; 127*1ec284f3SChristian Brauner 128*1ec284f3SChristian Brauner attr.userns_fd = userns_fd; 129*1ec284f3SChristian Brauner ret = sys_mount_setattr(mfd, "", AT_EMPTY_PATH, &attr, sizeof(attr)); 130*1ec284f3SChristian Brauner close(userns_fd); 131*1ec284f3SChristian Brauner return ret; 132*1ec284f3SChristian Brauner } 133*1ec284f3SChristian Brauner 134*1ec284f3SChristian Brauner /* Clone @path into a detached, idmapped mount usable as an overlay layer. */ 135*1ec284f3SChristian Brauner static int idmapped_layer_fd(const char *path, int nsid, int hostid, int range) 136*1ec284f3SChristian Brauner { 137*1ec284f3SChristian Brauner struct mount_attr attr = { 138*1ec284f3SChristian Brauner .attr_set = MOUNT_ATTR_IDMAP, 139*1ec284f3SChristian Brauner }; 140*1ec284f3SChristian Brauner int fd_tree, userns_fd; 141*1ec284f3SChristian Brauner 142*1ec284f3SChristian Brauner fd_tree = sys_open_tree(AT_FDCWD, path, 143*1ec284f3SChristian Brauner OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 144*1ec284f3SChristian Brauner if (fd_tree < 0) 145*1ec284f3SChristian Brauner return -1; 146*1ec284f3SChristian Brauner userns_fd = get_userns_fd(nsid, hostid, range); 147*1ec284f3SChristian Brauner if (userns_fd < 0) { 148*1ec284f3SChristian Brauner close(fd_tree); 149*1ec284f3SChristian Brauner return -1; 150*1ec284f3SChristian Brauner } 151*1ec284f3SChristian Brauner attr.userns_fd = userns_fd; 152*1ec284f3SChristian Brauner if (sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH, &attr, 153*1ec284f3SChristian Brauner sizeof(attr))) { 154*1ec284f3SChristian Brauner close(userns_fd); 155*1ec284f3SChristian Brauner close(fd_tree); 156*1ec284f3SChristian Brauner return -1; 157*1ec284f3SChristian Brauner } 158*1ec284f3SChristian Brauner close(userns_fd); 159*1ec284f3SChristian Brauner return fd_tree; 160*1ec284f3SChristian Brauner } 161*1ec284f3SChristian Brauner 162*1ec284f3SChristian Brauner /* Overlay with a layer passed by fd (idmapped) plus a plain upper/work. */ 163*1ec284f3SChristian Brauner static int ovl_mount_lower_fd(const char *upper, const char *work, int fd_lower) 164*1ec284f3SChristian Brauner { 165*1ec284f3SChristian Brauner int fsfd, ovl; 166*1ec284f3SChristian Brauner 167*1ec284f3SChristian Brauner fsfd = sys_fsopen("overlay", 0); 168*1ec284f3SChristian Brauner if (fsfd < 0) 169*1ec284f3SChristian Brauner return -1; 170*1ec284f3SChristian Brauner 171*1ec284f3SChristian Brauner if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) || 172*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", upper, 0) || 173*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", work, 0) || 174*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower) || 175*1ec284f3SChristian Brauner sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) 176*1ec284f3SChristian Brauner goto err; 177*1ec284f3SChristian Brauner 178*1ec284f3SChristian Brauner ovl = sys_fsmount(fsfd, 0, 0); 179*1ec284f3SChristian Brauner close(fsfd); 180*1ec284f3SChristian Brauner return ovl; 181*1ec284f3SChristian Brauner err: 182*1ec284f3SChristian Brauner close(fsfd); 183*1ec284f3SChristian Brauner return -1; 184*1ec284f3SChristian Brauner } 185*1ec284f3SChristian Brauner 186*1ec284f3SChristian Brauner /* 187*1ec284f3SChristian Brauner * Mount an overlay inside user namespace @u1 (so the overlay sb's s_user_ns is 188*1ec284f3SChristian Brauner * not the initial namespace) and idmap that overlay mount with @u2. Runs in a 189*1ec284f3SChristian Brauner * child that joins @u1; returns 0 on success. 190*1ec284f3SChristian Brauner */ 191*1ec284f3SChristian Brauner static int userns_overlay_child(int u1) 192*1ec284f3SChristian Brauner { 193*1ec284f3SChristian Brauner struct mount_attr attr = { 194*1ec284f3SChristian Brauner .attr_set = MOUNT_ATTR_IDMAP, 195*1ec284f3SChristian Brauner }; 196*1ec284f3SChristian Brauner struct stat st; 197*1ec284f3SChristian Brauner int ovl, u2; 198*1ec284f3SChristian Brauner 199*1ec284f3SChristian Brauner /* Become root in the overlay sb's user namespace u1. */ 200*1ec284f3SChristian Brauner if (!switch_userns(u1, 0, 0, false)) 201*1ec284f3SChristian Brauner return fprintf(stderr, "userns: switch_userns: %m\n"), -1; 202*1ec284f3SChristian Brauner if (unshare(CLONE_NEWNS) || 203*1ec284f3SChristian Brauner sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) 204*1ec284f3SChristian Brauner return fprintf(stderr, "userns: unshare/slave: %m\n"), -1; 205*1ec284f3SChristian Brauner if (sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL)) 206*1ec284f3SChristian Brauner return fprintf(stderr, "userns: mount tmpfs: %m\n"), -1; 207*1ec284f3SChristian Brauner if (setup_layers("/tmp/ovl")) 208*1ec284f3SChristian Brauner return fprintf(stderr, "userns: setup_layers: %m\n"), -1; 209*1ec284f3SChristian Brauner if (mknod("/tmp/ovl/l/file", S_IFREG | 0644, 0) || 210*1ec284f3SChristian Brauner chown("/tmp/ovl/l/file", ID_HOST + 5, ID_HOST + 5)) 211*1ec284f3SChristian Brauner return fprintf(stderr, "userns: lower file: %m\n"), -1; 212*1ec284f3SChristian Brauner 213*1ec284f3SChristian Brauner ovl = ovl_mount("/tmp/ovl", false); 214*1ec284f3SChristian Brauner if (ovl < 0) 215*1ec284f3SChristian Brauner return fprintf(stderr, "userns: ovl_mount: %m\n"), -1; 216*1ec284f3SChristian Brauner 217*1ec284f3SChristian Brauner /* 218*1ec284f3SChristian Brauner * mount_setattr() requires CAP_SYS_ADMIN over the idmap user 219*1ec284f3SChristian Brauner * namespace, so it must be a child of u1. Create it now, from 220*1ec284f3SChristian Brauner * inside u1. 221*1ec284f3SChristian Brauner */ 222*1ec284f3SChristian Brauner u2 = get_userns_fd(ID_HOST, ID_NS, ID_RANGE); 223*1ec284f3SChristian Brauner if (u2 < 0) 224*1ec284f3SChristian Brauner return fprintf(stderr, "userns: get_userns_fd: %m\n"), -1; 225*1ec284f3SChristian Brauner attr.userns_fd = u2; 226*1ec284f3SChristian Brauner if (sys_mount_setattr(ovl, "", AT_EMPTY_PATH, &attr, sizeof(attr))) 227*1ec284f3SChristian Brauner return fprintf(stderr, "userns: mount_setattr: %m\n"), -1; 228*1ec284f3SChristian Brauner close(u2); 229*1ec284f3SChristian Brauner 230*1ec284f3SChristian Brauner if (fstatat(ovl, "file", &st, 0)) 231*1ec284f3SChristian Brauner return fprintf(stderr, "userns: fstatat: %m\n"), -1; 232*1ec284f3SChristian Brauner if (st.st_uid != ID_NS + 5 || st.st_gid != ID_NS + 5) { 233*1ec284f3SChristian Brauner fprintf(stderr, "userns: got %u:%u expected %u:%u\n", 234*1ec284f3SChristian Brauner st.st_uid, st.st_gid, ID_NS + 5, ID_NS + 5); 235*1ec284f3SChristian Brauner return -1; 236*1ec284f3SChristian Brauner } 237*1ec284f3SChristian Brauner return 0; 238*1ec284f3SChristian Brauner } 239*1ec284f3SChristian Brauner 240*1ec284f3SChristian Brauner FIXTURE(idmapped_overlay) { 241*1ec284f3SChristian Brauner char base[64]; 242*1ec284f3SChristian Brauner }; 243*1ec284f3SChristian Brauner 244*1ec284f3SChristian Brauner FIXTURE_SETUP(idmapped_overlay) 245*1ec284f3SChristian Brauner { 246*1ec284f3SChristian Brauner /* Private mount namespace so test mounts need no cleanup. */ 247*1ec284f3SChristian Brauner ASSERT_EQ(unshare(CLONE_NEWNS), 0); 248*1ec284f3SChristian Brauner ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0); 249*1ec284f3SChristian Brauner 250*1ec284f3SChristian Brauner /* tmpfs for the layers so we can chown them to arbitrary ids. */ 251*1ec284f3SChristian Brauner ASSERT_EQ(sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL), 0); 252*1ec284f3SChristian Brauner 253*1ec284f3SChristian Brauner snprintf(self->base, sizeof(self->base), "/tmp/ovl"); 254*1ec284f3SChristian Brauner ASSERT_EQ(setup_layers(self->base), 0); 255*1ec284f3SChristian Brauner } 256*1ec284f3SChristian Brauner 257*1ec284f3SChristian Brauner FIXTURE_TEARDOWN(idmapped_overlay) 258*1ec284f3SChristian Brauner { 259*1ec284f3SChristian Brauner } 260*1ec284f3SChristian Brauner 261*1ec284f3SChristian Brauner /* A file owned by ID_HOST + 5 is reported as ID_NS + 5 through the idmap. */ 262*1ec284f3SChristian Brauner TEST_F(idmapped_overlay, getattr) 263*1ec284f3SChristian Brauner { 264*1ec284f3SChristian Brauner char path[PATH_MAX]; 265*1ec284f3SChristian Brauner struct stat st; 266*1ec284f3SChristian Brauner int ovl; 267*1ec284f3SChristian Brauner 268*1ec284f3SChristian Brauner if (!ovl_supported()) 269*1ec284f3SChristian Brauner SKIP(return, "overlayfs not supported"); 270*1ec284f3SChristian Brauner 271*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/l/file", self->base); 272*1ec284f3SChristian Brauner ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0); 273*1ec284f3SChristian Brauner ASSERT_EQ(chown(path, ID_HOST + 5, ID_HOST + 5), 0); 274*1ec284f3SChristian Brauner 275*1ec284f3SChristian Brauner ovl = ovl_mount(self->base, false); 276*1ec284f3SChristian Brauner ASSERT_GE(ovl, 0); 277*1ec284f3SChristian Brauner ASSERT_EQ(ovl_idmap(ovl), 0); 278*1ec284f3SChristian Brauner 279*1ec284f3SChristian Brauner ASSERT_EQ(fstatat(ovl, "file", &st, 0), 0); 280*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_NS + 5); 281*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_NS + 5); 282*1ec284f3SChristian Brauner 283*1ec284f3SChristian Brauner EXPECT_EQ(close(ovl), 0); 284*1ec284f3SChristian Brauner } 285*1ec284f3SChristian Brauner 286*1ec284f3SChristian Brauner /* 287*1ec284f3SChristian Brauner * Every creation path initializes the new owner through the mount idmap: 288*1ec284f3SChristian Brauner * created as caller id ID_NS, stored on the upper layer as overlay-final 289*1ec284f3SChristian Brauner * ID_HOST. Covers ovl_create() (regular file), ovl_mkdir(), ovl_mknod() 290*1ec284f3SChristian Brauner * and ovl_symlink() (which share ovl_create_object()), plus the separate 291*1ec284f3SChristian Brauner * ovl_tmpfile() path. 292*1ec284f3SChristian Brauner */ 293*1ec284f3SChristian Brauner TEST_F(idmapped_overlay, create) 294*1ec284f3SChristian Brauner { 295*1ec284f3SChristian Brauner static const char *names[] = { "reg", "dir", "fifo", "lnk" }; 296*1ec284f3SChristian Brauner char path[PATH_MAX]; 297*1ec284f3SChristian Brauner struct stat st; 298*1ec284f3SChristian Brauner int ovl, fd; 299*1ec284f3SChristian Brauner 300*1ec284f3SChristian Brauner if (!ovl_supported()) 301*1ec284f3SChristian Brauner SKIP(return, "overlayfs not supported"); 302*1ec284f3SChristian Brauner 303*1ec284f3SChristian Brauner ovl = ovl_mount(self->base, false); 304*1ec284f3SChristian Brauner ASSERT_GE(ovl, 0); 305*1ec284f3SChristian Brauner ASSERT_EQ(ovl_idmap(ovl), 0); 306*1ec284f3SChristian Brauner 307*1ec284f3SChristian Brauner /* One object per creation operation, all as caller id ID_NS. */ 308*1ec284f3SChristian Brauner fd = openat(ovl, "reg", O_CREAT | O_WRONLY | O_EXCL, 0644); 309*1ec284f3SChristian Brauner ASSERT_GE(fd, 0); 310*1ec284f3SChristian Brauner EXPECT_EQ(close(fd), 0); 311*1ec284f3SChristian Brauner ASSERT_EQ(mkdirat(ovl, "dir", 0755), 0); 312*1ec284f3SChristian Brauner ASSERT_EQ(mknodat(ovl, "fifo", S_IFIFO | 0644, 0), 0); 313*1ec284f3SChristian Brauner ASSERT_EQ(symlinkat("target", ovl, "lnk"), 0); 314*1ec284f3SChristian Brauner 315*1ec284f3SChristian Brauner for (size_t i = 0; i < ARRAY_SIZE(names); i++) { 316*1ec284f3SChristian Brauner /* Reported as ID_NS through the idmapped mount ... */ 317*1ec284f3SChristian Brauner ASSERT_EQ(fstatat(ovl, names[i], &st, AT_SYMLINK_NOFOLLOW), 0); 318*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_NS); 319*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_NS); 320*1ec284f3SChristian Brauner /* ... and stored as ID_HOST on the upper layer. */ 321*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/u/%s", self->base, names[i]); 322*1ec284f3SChristian Brauner ASSERT_EQ(lstat(path, &st), 0); 323*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_HOST); 324*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_HOST); 325*1ec284f3SChristian Brauner } 326*1ec284f3SChristian Brauner 327*1ec284f3SChristian Brauner /* O_TMPFILE goes through the separate ovl_tmpfile() path. */ 328*1ec284f3SChristian Brauner fd = openat(ovl, ".", O_TMPFILE | O_WRONLY, 0644); 329*1ec284f3SChristian Brauner ASSERT_GE(fd, 0); 330*1ec284f3SChristian Brauner /* Inside the mount: caller id ID_NS. */ 331*1ec284f3SChristian Brauner ASSERT_EQ(fstat(fd, &st), 0); 332*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_NS); 333*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_NS); 334*1ec284f3SChristian Brauner /* Link it in so the upper backing file can be inspected too. */ 335*1ec284f3SChristian Brauner ASSERT_EQ(linkat(fd, "", ovl, "tmp", AT_EMPTY_PATH), 0); 336*1ec284f3SChristian Brauner EXPECT_EQ(close(fd), 0); 337*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/u/tmp", self->base); 338*1ec284f3SChristian Brauner ASSERT_EQ(lstat(path, &st), 0); 339*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_HOST); 340*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_HOST); 341*1ec284f3SChristian Brauner 342*1ec284f3SChristian Brauner EXPECT_EQ(close(ovl), 0); 343*1ec284f3SChristian Brauner } 344*1ec284f3SChristian Brauner 345*1ec284f3SChristian Brauner /* chown through the idmapped mount round-trips: ID_NS + 5 <-> ID_HOST + 5. */ 346*1ec284f3SChristian Brauner TEST_F(idmapped_overlay, chown) 347*1ec284f3SChristian Brauner { 348*1ec284f3SChristian Brauner char path[PATH_MAX]; 349*1ec284f3SChristian Brauner struct stat st; 350*1ec284f3SChristian Brauner int ovl, fd; 351*1ec284f3SChristian Brauner 352*1ec284f3SChristian Brauner if (!ovl_supported()) 353*1ec284f3SChristian Brauner SKIP(return, "overlayfs not supported"); 354*1ec284f3SChristian Brauner 355*1ec284f3SChristian Brauner ovl = ovl_mount(self->base, false); 356*1ec284f3SChristian Brauner ASSERT_GE(ovl, 0); 357*1ec284f3SChristian Brauner ASSERT_EQ(ovl_idmap(ovl), 0); 358*1ec284f3SChristian Brauner 359*1ec284f3SChristian Brauner fd = openat(ovl, "f", O_CREAT | O_WRONLY | O_EXCL, 0644); 360*1ec284f3SChristian Brauner ASSERT_GE(fd, 0); 361*1ec284f3SChristian Brauner EXPECT_EQ(close(fd), 0); 362*1ec284f3SChristian Brauner 363*1ec284f3SChristian Brauner ASSERT_EQ(fchownat(ovl, "f", ID_NS + 5, ID_NS + 5, 0), 0); 364*1ec284f3SChristian Brauner 365*1ec284f3SChristian Brauner ASSERT_EQ(fstatat(ovl, "f", &st, 0), 0); 366*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_NS + 5); 367*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_NS + 5); 368*1ec284f3SChristian Brauner 369*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/u/f", self->base); 370*1ec284f3SChristian Brauner ASSERT_EQ(stat(path, &st), 0); 371*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_HOST + 5); 372*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_HOST + 5); 373*1ec284f3SChristian Brauner 374*1ec284f3SChristian Brauner EXPECT_EQ(close(ovl), 0); 375*1ec284f3SChristian Brauner } 376*1ec284f3SChristian Brauner 377*1ec284f3SChristian Brauner /* 378*1ec284f3SChristian Brauner * Composition: an idmapped lower layer underneath an idmapped overlay mount. 379*1ec284f3SChristian Brauner * An on-disk id is mapped by the layer idmap into the overlay-final range and 380*1ec284f3SChristian Brauner * then by the mount idmap into the caller's range: 381*1ec284f3SChristian Brauner * 382*1ec284f3SChristian Brauner * on-disk LAYER_HOST+7 --layer--> ID_HOST+7 --mount--> ID_NS+7 383*1ec284f3SChristian Brauner */ 384*1ec284f3SChristian Brauner TEST_F(idmapped_overlay, composition) 385*1ec284f3SChristian Brauner { 386*1ec284f3SChristian Brauner char lower[PATH_MAX], upper[PATH_MAX], work[PATH_MAX], path[PATH_MAX]; 387*1ec284f3SChristian Brauner struct stat st; 388*1ec284f3SChristian Brauner int ovl, fd_lower; 389*1ec284f3SChristian Brauner 390*1ec284f3SChristian Brauner if (!ovl_supported()) 391*1ec284f3SChristian Brauner SKIP(return, "overlayfs not supported"); 392*1ec284f3SChristian Brauner 393*1ec284f3SChristian Brauner snprintf(lower, sizeof(lower), "%s/l", self->base); 394*1ec284f3SChristian Brauner snprintf(upper, sizeof(upper), "%s/u", self->base); 395*1ec284f3SChristian Brauner snprintf(work, sizeof(work), "%s/w", self->base); 396*1ec284f3SChristian Brauner 397*1ec284f3SChristian Brauner /* Put the lower layer's ids in the on-disk [LAYER_HOST, ..) range. */ 398*1ec284f3SChristian Brauner ASSERT_EQ(chown(lower, LAYER_HOST, LAYER_HOST), 0); 399*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/l/file", self->base); 400*1ec284f3SChristian Brauner ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0); 401*1ec284f3SChristian Brauner ASSERT_EQ(chown(path, LAYER_HOST + 7, LAYER_HOST + 7), 0); 402*1ec284f3SChristian Brauner 403*1ec284f3SChristian Brauner /* Idmapped lower: on-disk LAYER_HOST <-> overlay-final ID_HOST. */ 404*1ec284f3SChristian Brauner fd_lower = idmapped_layer_fd(lower, LAYER_HOST, ID_HOST, ID_RANGE); 405*1ec284f3SChristian Brauner ASSERT_GE(fd_lower, 0); 406*1ec284f3SChristian Brauner 407*1ec284f3SChristian Brauner ovl = ovl_mount_lower_fd(upper, work, fd_lower); 408*1ec284f3SChristian Brauner ASSERT_GE(ovl, 0); 409*1ec284f3SChristian Brauner EXPECT_EQ(close(fd_lower), 0); 410*1ec284f3SChristian Brauner 411*1ec284f3SChristian Brauner /* Idmap the overlay mount: overlay-final ID_HOST <-> caller ID_NS. */ 412*1ec284f3SChristian Brauner ASSERT_EQ(ovl_idmap(ovl), 0); 413*1ec284f3SChristian Brauner 414*1ec284f3SChristian Brauner ASSERT_EQ(fstatat(ovl, "file", &st, 0), 0); 415*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_NS + 7); 416*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_NS + 7); 417*1ec284f3SChristian Brauner 418*1ec284f3SChristian Brauner EXPECT_EQ(close(ovl), 0); 419*1ec284f3SChristian Brauner } 420*1ec284f3SChristian Brauner 421*1ec284f3SChristian Brauner /* An idmapped overlay mount whose sb lives inside a user namespace. */ 422*1ec284f3SChristian Brauner TEST_F(idmapped_overlay, userns) 423*1ec284f3SChristian Brauner { 424*1ec284f3SChristian Brauner int u1; 425*1ec284f3SChristian Brauner pid_t pid; 426*1ec284f3SChristian Brauner 427*1ec284f3SChristian Brauner if (!ovl_supported()) 428*1ec284f3SChristian Brauner SKIP(return, "overlayfs not supported"); 429*1ec284f3SChristian Brauner 430*1ec284f3SChristian Brauner /* u1 backs the overlay sb: identity-mapped, but not the init ns. */ 431*1ec284f3SChristian Brauner u1 = get_userns_fd(0, 0, 65536); 432*1ec284f3SChristian Brauner if (u1 < 0) 433*1ec284f3SChristian Brauner SKIP(return, "user namespaces not available"); 434*1ec284f3SChristian Brauner 435*1ec284f3SChristian Brauner pid = fork(); 436*1ec284f3SChristian Brauner ASSERT_GE(pid, 0); 437*1ec284f3SChristian Brauner if (pid == 0) { 438*1ec284f3SChristian Brauner int ret = userns_overlay_child(u1); 439*1ec284f3SChristian Brauner 440*1ec284f3SChristian Brauner _exit(ret ? EXIT_FAILURE : EXIT_SUCCESS); 441*1ec284f3SChristian Brauner } 442*1ec284f3SChristian Brauner EXPECT_EQ(wait_for_pid(pid), 0); 443*1ec284f3SChristian Brauner 444*1ec284f3SChristian Brauner EXPECT_EQ(close(u1), 0); 445*1ec284f3SChristian Brauner } 446*1ec284f3SChristian Brauner 447*1ec284f3SChristian Brauner /* 448*1ec284f3SChristian Brauner * An nfs_export overlay can be idmapped, and decodable file handles round-trip 449*1ec284f3SChristian Brauner * through the idmapped mount with correctly mapped ownership. Overlay file 450*1ec284f3SChristian Brauner * handles encode object identity, not ownership, so the mount idmap does not 451*1ec284f3SChristian Brauner * affect them; it only maps the owner reported once a handle is reopened. 452*1ec284f3SChristian Brauner */ 453*1ec284f3SChristian Brauner TEST_F(idmapped_overlay, nfs_export_handles) 454*1ec284f3SChristian Brauner { 455*1ec284f3SChristian Brauner char path[PATH_MAX], mnt[128]; 456*1ec284f3SChristian Brauner union { 457*1ec284f3SChristian Brauner struct file_handle fh; 458*1ec284f3SChristian Brauner char buf[sizeof(struct file_handle) + MAX_HANDLE_SZ]; 459*1ec284f3SChristian Brauner } fhu; 460*1ec284f3SChristian Brauner struct file_handle *fh = &fhu.fh; 461*1ec284f3SChristian Brauner struct stat st; 462*1ec284f3SChristian Brauner int ovl, mfd, fd, mount_id; 463*1ec284f3SChristian Brauner 464*1ec284f3SChristian Brauner if (!ovl_supported()) 465*1ec284f3SChristian Brauner SKIP(return, "overlayfs not supported"); 466*1ec284f3SChristian Brauner 467*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/l/file", self->base); 468*1ec284f3SChristian Brauner ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0); 469*1ec284f3SChristian Brauner ASSERT_EQ(chown(path, ID_HOST + 7, ID_HOST + 7), 0); 470*1ec284f3SChristian Brauner 471*1ec284f3SChristian Brauner /* nfs_export=on gives decodable overlay file handles. */ 472*1ec284f3SChristian Brauner ovl = ovl_mount(self->base, true); 473*1ec284f3SChristian Brauner if (ovl < 0) 474*1ec284f3SChristian Brauner SKIP(return, "overlayfs nfs_export not supported"); 475*1ec284f3SChristian Brauner ASSERT_EQ(ovl_idmap(ovl), 0); 476*1ec284f3SChristian Brauner 477*1ec284f3SChristian Brauner /* Attach the idmapped mount so handles can be resolved against it. */ 478*1ec284f3SChristian Brauner snprintf(mnt, sizeof(mnt), "%s/mnt", self->base); 479*1ec284f3SChristian Brauner ASSERT_EQ(mkdir(mnt, 0755), 0); 480*1ec284f3SChristian Brauner ASSERT_EQ(sys_move_mount(ovl, "", AT_FDCWD, mnt, 481*1ec284f3SChristian Brauner MOVE_MOUNT_F_EMPTY_PATH), 0); 482*1ec284f3SChristian Brauner 483*1ec284f3SChristian Brauner snprintf(path, sizeof(path), "%s/file", mnt); 484*1ec284f3SChristian Brauner fh->handle_bytes = MAX_HANDLE_SZ; 485*1ec284f3SChristian Brauner ASSERT_EQ(name_to_handle_at(AT_FDCWD, path, fh, &mount_id, 0), 0); 486*1ec284f3SChristian Brauner 487*1ec284f3SChristian Brauner mfd = open(mnt, O_RDONLY | O_DIRECTORY); 488*1ec284f3SChristian Brauner ASSERT_GE(mfd, 0); 489*1ec284f3SChristian Brauner fd = open_by_handle_at(mfd, fh, O_RDONLY); 490*1ec284f3SChristian Brauner EXPECT_EQ(close(mfd), 0); 491*1ec284f3SChristian Brauner ASSERT_GE(fd, 0); 492*1ec284f3SChristian Brauner 493*1ec284f3SChristian Brauner ASSERT_EQ(fstat(fd, &st), 0); 494*1ec284f3SChristian Brauner EXPECT_EQ(st.st_uid, ID_NS + 7); 495*1ec284f3SChristian Brauner EXPECT_EQ(st.st_gid, ID_NS + 7); 496*1ec284f3SChristian Brauner 497*1ec284f3SChristian Brauner EXPECT_EQ(close(fd), 0); 498*1ec284f3SChristian Brauner EXPECT_EQ(close(ovl), 0); 499*1ec284f3SChristian Brauner } 500*1ec284f3SChristian Brauner 501*1ec284f3SChristian Brauner TEST_HARNESS_MAIN 502