1*3e78bf84SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*3e78bf84SChristian Brauner /*
3*3e78bf84SChristian Brauner * A mount that another namespace's rmdir detached, or that went down with
4*3e78bf84SChristian Brauner * the detached tree it was in when the tree's last fd was closed, keeps
5*3e78bf84SChristian Brauner * its submounts connected, and a connected submount is put by its
6*3e78bf84SChristian Brauner * parent's final mntput(). A submount whose filesystem keeps a file open
7*3e78bf84SChristian Brauner * on the parent then holds the parent's count above zero for good:
8*3e78bf84SChristian Brauner * nothing in userspace refers to either mount any more and nothing can
9*3e78bf84SChristian Brauner * release them. A loop device is the simplest such filesystem, its
10*3e78bf84SChristian Brauner * backing file sits on the parent.
11*3e78bf84SChristian Brauner */
12*3e78bf84SChristian Brauner #define _GNU_SOURCE
13*3e78bf84SChristian Brauner #include <dirent.h>
14*3e78bf84SChristian Brauner #include <errno.h>
15*3e78bf84SChristian Brauner #include <fcntl.h>
16*3e78bf84SChristian Brauner #include <sched.h>
17*3e78bf84SChristian Brauner #include <stdio.h>
18*3e78bf84SChristian Brauner #include <stdlib.h>
19*3e78bf84SChristian Brauner #include <string.h>
20*3e78bf84SChristian Brauner #include <sys/ioctl.h>
21*3e78bf84SChristian Brauner #include <sys/mount.h>
22*3e78bf84SChristian Brauner #include <sys/stat.h>
23*3e78bf84SChristian Brauner #include <stdbool.h>
24*3e78bf84SChristian Brauner #include <sys/wait.h>
25*3e78bf84SChristian Brauner #include <unistd.h>
26*3e78bf84SChristian Brauner #include <linux/fuse.h>
27*3e78bf84SChristian Brauner #include <linux/keyctl.h>
28*3e78bf84SChristian Brauner #include <linux/major.h>
29*3e78bf84SChristian Brauner #include <linux/raid/md_u.h>
30*3e78bf84SChristian Brauner #include <linux/raid/md_p.h>
31*3e78bf84SChristian Brauner #include <linux/loop.h>
32*3e78bf84SChristian Brauner #include <linux/magic.h>
33*3e78bf84SChristian Brauner #include <sys/syscall.h>
34*3e78bf84SChristian Brauner #include <sys/sysmacros.h>
35*3e78bf84SChristian Brauner #include <sys/uio.h>
36*3e78bf84SChristian Brauner
37*3e78bf84SChristian Brauner #include "../wrappers.h"
38*3e78bf84SChristian Brauner #include "../../kselftest_harness.h"
39*3e78bf84SChristian Brauner
40*3e78bf84SChristian Brauner #define IMAGE_SIZE (1440 * 1024)
41*3e78bf84SChristian Brauner #define SECTOR 512
42*3e78bf84SChristian Brauner
43*3e78bf84SChristian Brauner /*
44*3e78bf84SChristian Brauner * The tmpfs of the fixture. A directory of its own from mkdtemp(), so that
45*3e78bf84SChristian Brauner * the test needs no writable root directory and two instances don't take
46*3e78bf84SChristian Brauner * each other's mounts down.
47*3e78bf84SChristian Brauner */
48*3e78bf84SChristian Brauner static char base[64];
49*3e78bf84SChristian Brauner
50*3e78bf84SChristian Brauner /* The configfs directory of the gadget, named after the test process. */
51*3e78bf84SChristian Brauner static char gadget[96];
52*3e78bf84SChristian Brauner
53*3e78bf84SChristian Brauner /* A path below the base directory. Eight of them can be in use at a time. */
at(const char * rel)54*3e78bf84SChristian Brauner static const char *at(const char *rel)
55*3e78bf84SChristian Brauner {
56*3e78bf84SChristian Brauner static char buf[8][PATH_MAX];
57*3e78bf84SChristian Brauner static unsigned int next;
58*3e78bf84SChristian Brauner char *p = buf[next++ % 8];
59*3e78bf84SChristian Brauner
60*3e78bf84SChristian Brauner snprintf(p, PATH_MAX, "%s/%s", base, rel);
61*3e78bf84SChristian Brauner return p;
62*3e78bf84SChristian Brauner }
63*3e78bf84SChristian Brauner
64*3e78bf84SChristian Brauner /* A path below the gadget's configfs directory. */
gat(const char * rel)65*3e78bf84SChristian Brauner static const char *gat(const char *rel)
66*3e78bf84SChristian Brauner {
67*3e78bf84SChristian Brauner static char buf[4][PATH_MAX];
68*3e78bf84SChristian Brauner static unsigned int next;
69*3e78bf84SChristian Brauner char *p = buf[next++ % 4];
70*3e78bf84SChristian Brauner
71*3e78bf84SChristian Brauner snprintf(p, PATH_MAX, "%s%s", gadget, rel);
72*3e78bf84SChristian Brauner return p;
73*3e78bf84SChristian Brauner }
74*3e78bf84SChristian Brauner
75*3e78bf84SChristian Brauner /* The loop devices this process has bound, to release them on every path. */
76*3e78bf84SChristian Brauner #define MAX_LOOPS 4
77*3e78bf84SChristian Brauner static int bound[MAX_LOOPS];
78*3e78bf84SChristian Brauner static int nr_bound;
79*3e78bf84SChristian Brauner
80*3e78bf84SChristian Brauner /* What a child tells its parent: the loop devices it has bound, -1 for none. */
81*3e78bf84SChristian Brauner struct report {
82*3e78bf84SChristian Brauner int n[2];
83*3e78bf84SChristian Brauner };
84*3e78bf84SChristian Brauner
85*3e78bf84SChristian Brauner /* A blank FAT12 floppy image: boot sector, two FATs, an empty root directory. */
write_fat12(int fd)86*3e78bf84SChristian Brauner static int write_fat12(int fd)
87*3e78bf84SChristian Brauner {
88*3e78bf84SChristian Brauner struct stat st;
89*3e78bf84SChristian Brauner unsigned char sector[SECTOR] = {
90*3e78bf84SChristian Brauner 0xeb, 0x3c, 0x90, 'M', 'S', 'W', 'I', 'N', '4', '.', '1',
91*3e78bf84SChristian Brauner [11] = 0x00, 0x02, /* bytes per sector: 512 */
92*3e78bf84SChristian Brauner [13] = 1, /* sectors per cluster */
93*3e78bf84SChristian Brauner [14] = 1, 0, /* reserved sectors */
94*3e78bf84SChristian Brauner [16] = 2, /* FATs */
95*3e78bf84SChristian Brauner [17] = 0xe0, 0x00, /* root directory entries: 224 */
96*3e78bf84SChristian Brauner [19] = 0x40, 0x0b, /* total sectors: 2880 */
97*3e78bf84SChristian Brauner [21] = 0xf0, /* media descriptor */
98*3e78bf84SChristian Brauner [22] = 9, 0, /* sectors per FAT */
99*3e78bf84SChristian Brauner [24] = 18, 0, /* sectors per track */
100*3e78bf84SChristian Brauner [26] = 2, 0, /* heads */
101*3e78bf84SChristian Brauner [38] = 0x29, /* extended boot signature */
102*3e78bf84SChristian Brauner [39] = 0x12, 0x34, 0x56, 0x78,
103*3e78bf84SChristian Brauner [43] = 'N', 'O', ' ', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ',
104*3e78bf84SChristian Brauner [54] = 'F', 'A', 'T', '1', '2', ' ', ' ', ' ',
105*3e78bf84SChristian Brauner [510] = 0x55, 0xaa,
106*3e78bf84SChristian Brauner };
107*3e78bf84SChristian Brauner unsigned char fat[SECTOR] = { 0xf0, 0xff, 0xff };
108*3e78bf84SChristian Brauner
109*3e78bf84SChristian Brauner if (pwrite(fd, sector, SECTOR, 0) != SECTOR)
110*3e78bf84SChristian Brauner return -1;
111*3e78bf84SChristian Brauner /* the first FAT and the second one, one sector each is enough */
112*3e78bf84SChristian Brauner if (pwrite(fd, fat, SECTOR, 1 * SECTOR) != SECTOR ||
113*3e78bf84SChristian Brauner pwrite(fd, fat, SECTOR, 10 * SECTOR) != SECTOR)
114*3e78bf84SChristian Brauner return -1;
115*3e78bf84SChristian Brauner if (fstat(fd, &st) || S_ISBLK(st.st_mode))
116*3e78bf84SChristian Brauner return 0;
117*3e78bf84SChristian Brauner return ftruncate(fd, IMAGE_SIZE);
118*3e78bf84SChristian Brauner }
119*3e78bf84SChristian Brauner
120*3e78bf84SChristian Brauner /*
121*3e78bf84SChristian Brauner * A blank FAT12 image with 4 KiB sectors and @sectors of them, for a device
122*3e78bf84SChristian Brauner * with 4 KiB logical blocks (zram) or for a bigger image than a floppy.
123*3e78bf84SChristian Brauner */
write_fat12_4k(int fd,unsigned int sectors)124*3e78bf84SChristian Brauner static int write_fat12_4k(int fd, unsigned int sectors)
125*3e78bf84SChristian Brauner {
126*3e78bf84SChristian Brauner unsigned int fat_sectors = (sectors * 3 / 2 + 4095) / 4096;
127*3e78bf84SChristian Brauner unsigned char sector[4096] = {
128*3e78bf84SChristian Brauner 0xeb, 0x3c, 0x90, 'M', 'S', 'W', 'I', 'N', '4', '.', '1',
129*3e78bf84SChristian Brauner [11] = 0x00, 0x10, /* bytes per sector: 4096 */
130*3e78bf84SChristian Brauner [13] = 1, /* sectors per cluster */
131*3e78bf84SChristian Brauner [14] = 1, 0, /* reserved sectors */
132*3e78bf84SChristian Brauner [16] = 2, /* FATs */
133*3e78bf84SChristian Brauner [17] = 128, 0, /* root directory entries: one sector */
134*3e78bf84SChristian Brauner [19] = sectors & 0xff, sectors >> 8,
135*3e78bf84SChristian Brauner [21] = 0xf8, /* media descriptor */
136*3e78bf84SChristian Brauner [22] = fat_sectors, 0,
137*3e78bf84SChristian Brauner [24] = 63, 0, /* sectors per track */
138*3e78bf84SChristian Brauner [26] = 255, 0, /* heads */
139*3e78bf84SChristian Brauner [38] = 0x29, /* extended boot signature */
140*3e78bf84SChristian Brauner [39] = 0x12, 0x34, 0x56, 0x78,
141*3e78bf84SChristian Brauner [43] = 'N', 'O', ' ', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ',
142*3e78bf84SChristian Brauner [54] = 'F', 'A', 'T', '1', '2', ' ', ' ', ' ',
143*3e78bf84SChristian Brauner [510] = 0x55, 0xaa,
144*3e78bf84SChristian Brauner };
145*3e78bf84SChristian Brauner unsigned char fat[4096] = { 0xf8, 0xff, 0xff };
146*3e78bf84SChristian Brauner struct stat st;
147*3e78bf84SChristian Brauner
148*3e78bf84SChristian Brauner if (pwrite(fd, sector, sizeof(sector), 0) != sizeof(sector))
149*3e78bf84SChristian Brauner return -1;
150*3e78bf84SChristian Brauner if (pwrite(fd, fat, sizeof(fat), 1 * 4096) != sizeof(fat) ||
151*3e78bf84SChristian Brauner pwrite(fd, fat, sizeof(fat), (1 + fat_sectors) * 4096) != sizeof(fat))
152*3e78bf84SChristian Brauner return -1;
153*3e78bf84SChristian Brauner if (fstat(fd, &st) || S_ISBLK(st.st_mode))
154*3e78bf84SChristian Brauner return 0;
155*3e78bf84SChristian Brauner return ftruncate(fd, (off_t)sectors * 4096);
156*3e78bf84SChristian Brauner }
157*3e78bf84SChristian Brauner
158*3e78bf84SChristian Brauner #define MINIX_BLOCK 1024
159*3e78bf84SChristian Brauner #define MINIX_BLOCKS 4096 /* a 4 MiB image */
160*3e78bf84SChristian Brauner #define MINIX_INODES 512
161*3e78bf84SChristian Brauner #define MINIX_ITABLE (MINIX_INODES * 32 / MINIX_BLOCK)
162*3e78bf84SChristian Brauner #define MINIX_FIRSTDATA (2 + 1 + 1 + MINIX_ITABLE) /* boot, super, imap, zmap, inodes */
163*3e78bf84SChristian Brauner
164*3e78bf84SChristian Brauner /*
165*3e78bf84SChristian Brauner * A blank minix v1 image, for the holders that need a FIFO or a device
166*3e78bf84SChristian Brauner * node on the dying mount, which vfat can't hold. Superblock in block 1,
167*3e78bf84SChristian Brauner * one block each for the inode and zone bitmaps, the inode table, and
168*3e78bf84SChristian Brauner * the root directory in the first data zone.
169*3e78bf84SChristian Brauner */
write_minix(int fd)170*3e78bf84SChristian Brauner static int write_minix(int fd)
171*3e78bf84SChristian Brauner {
172*3e78bf84SChristian Brauner struct {
173*3e78bf84SChristian Brauner __u16 s_ninodes, s_nzones, s_imap_blocks, s_zmap_blocks;
174*3e78bf84SChristian Brauner __u16 s_firstdatazone, s_log_zone_size;
175*3e78bf84SChristian Brauner __u32 s_max_size;
176*3e78bf84SChristian Brauner __u16 s_magic, s_state;
177*3e78bf84SChristian Brauner } sb = {
178*3e78bf84SChristian Brauner .s_ninodes = MINIX_INODES,
179*3e78bf84SChristian Brauner .s_nzones = MINIX_BLOCKS,
180*3e78bf84SChristian Brauner .s_imap_blocks = 1,
181*3e78bf84SChristian Brauner .s_zmap_blocks = 1,
182*3e78bf84SChristian Brauner .s_firstdatazone = MINIX_FIRSTDATA,
183*3e78bf84SChristian Brauner .s_max_size = (7 + 512 + 512 * 512) * MINIX_BLOCK,
184*3e78bf84SChristian Brauner .s_magic = MINIX_SUPER_MAGIC,
185*3e78bf84SChristian Brauner .s_state = 1, /* MINIX_VALID_FS */
186*3e78bf84SChristian Brauner };
187*3e78bf84SChristian Brauner struct {
188*3e78bf84SChristian Brauner __u16 i_mode, i_uid;
189*3e78bf84SChristian Brauner __u32 i_size, i_time;
190*3e78bf84SChristian Brauner __u8 i_gid, i_nlinks;
191*3e78bf84SChristian Brauner __u16 i_zone[9];
192*3e78bf84SChristian Brauner } root = {
193*3e78bf84SChristian Brauner .i_mode = S_IFDIR | 0755,
194*3e78bf84SChristian Brauner .i_size = 2 * 16,
195*3e78bf84SChristian Brauner .i_nlinks = 2,
196*3e78bf84SChristian Brauner .i_zone = { MINIX_FIRSTDATA },
197*3e78bf84SChristian Brauner };
198*3e78bf84SChristian Brauner unsigned char imap[MINIX_BLOCK], zmap[MINIX_BLOCK], dir[MINIX_BLOCK] = {};
199*3e78bf84SChristian Brauner int i;
200*3e78bf84SChristian Brauner
201*3e78bf84SChristian Brauner /* bit 0 is reserved in both maps, the root inode and its zone are in use */
202*3e78bf84SChristian Brauner memset(imap, 0xff, sizeof(imap));
203*3e78bf84SChristian Brauner for (i = 2; i <= MINIX_INODES; i++)
204*3e78bf84SChristian Brauner imap[i / 8] &= ~(1 << (i % 8));
205*3e78bf84SChristian Brauner memset(zmap, 0xff, sizeof(zmap));
206*3e78bf84SChristian Brauner for (i = 2; i <= MINIX_BLOCKS - MINIX_FIRSTDATA; i++)
207*3e78bf84SChristian Brauner zmap[i / 8] &= ~(1 << (i % 8));
208*3e78bf84SChristian Brauner dir[0] = 1;
209*3e78bf84SChristian Brauner dir[2] = '.';
210*3e78bf84SChristian Brauner dir[16] = 1;
211*3e78bf84SChristian Brauner dir[18] = '.';
212*3e78bf84SChristian Brauner dir[19] = '.';
213*3e78bf84SChristian Brauner
214*3e78bf84SChristian Brauner if (pwrite(fd, &sb, sizeof(sb), 1 * MINIX_BLOCK) != sizeof(sb) ||
215*3e78bf84SChristian Brauner pwrite(fd, imap, sizeof(imap), 2 * MINIX_BLOCK) != sizeof(imap) ||
216*3e78bf84SChristian Brauner pwrite(fd, zmap, sizeof(zmap), 3 * MINIX_BLOCK) != sizeof(zmap) ||
217*3e78bf84SChristian Brauner pwrite(fd, &root, sizeof(root), 4 * MINIX_BLOCK) != sizeof(root) ||
218*3e78bf84SChristian Brauner pwrite(fd, dir, sizeof(dir), MINIX_FIRSTDATA * MINIX_BLOCK) != sizeof(dir))
219*3e78bf84SChristian Brauner return -1;
220*3e78bf84SChristian Brauner return ftruncate(fd, (off_t)MINIX_BLOCKS * MINIX_BLOCK);
221*3e78bf84SChristian Brauner }
222*3e78bf84SChristian Brauner
read_sysfs(const char * path,char * buf,size_t size)223*3e78bf84SChristian Brauner static int read_sysfs(const char *path, char *buf, size_t size)
224*3e78bf84SChristian Brauner {
225*3e78bf84SChristian Brauner ssize_t n;
226*3e78bf84SChristian Brauner int fd;
227*3e78bf84SChristian Brauner
228*3e78bf84SChristian Brauner fd = open(path, O_RDONLY);
229*3e78bf84SChristian Brauner if (fd < 0)
230*3e78bf84SChristian Brauner return -1;
231*3e78bf84SChristian Brauner n = read(fd, buf, size - 1);
232*3e78bf84SChristian Brauner close(fd);
233*3e78bf84SChristian Brauner if (n < 0)
234*3e78bf84SChristian Brauner return -1;
235*3e78bf84SChristian Brauner buf[n] = '\0';
236*3e78bf84SChristian Brauner return 0;
237*3e78bf84SChristian Brauner }
238*3e78bf84SChristian Brauner
239*3e78bf84SChristian Brauner /* Is @dev the mount source of this line of mountinfo? loop1 is not loop10. */
line_has_source(const char * line,const char * dev)240*3e78bf84SChristian Brauner static bool line_has_source(const char *line, const char *dev)
241*3e78bf84SChristian Brauner {
242*3e78bf84SChristian Brauner const char *sep, *src, *end;
243*3e78bf84SChristian Brauner
244*3e78bf84SChristian Brauner sep = strstr(line, " - ");
245*3e78bf84SChristian Brauner if (!sep)
246*3e78bf84SChristian Brauner return false;
247*3e78bf84SChristian Brauner src = strchr(sep + 3, ' '); /* skip the filesystem type */
248*3e78bf84SChristian Brauner if (!src)
249*3e78bf84SChristian Brauner return false;
250*3e78bf84SChristian Brauner src++;
251*3e78bf84SChristian Brauner end = strchr(src, ' ');
252*3e78bf84SChristian Brauner if (!end)
253*3e78bf84SChristian Brauner return false;
254*3e78bf84SChristian Brauner return (size_t)(end - src) == strlen(dev) && !strncmp(src, dev, end - src);
255*3e78bf84SChristian Brauner }
256*3e78bf84SChristian Brauner
257*3e78bf84SChristian Brauner /*
258*3e78bf84SChristian Brauner * Does a mount namespace of a process that /proc shows have a mount of @dev?
259*3e78bf84SChristian Brauner * The mounts under test are in no namespace at this point on any kernel, so
260*3e78bf84SChristian Brauner * this only checks that the test got as far as it thinks.
261*3e78bf84SChristian Brauner */
mounted_anywhere(const char * dev)262*3e78bf84SChristian Brauner static bool mounted_anywhere(const char *dev)
263*3e78bf84SChristian Brauner {
264*3e78bf84SChristian Brauner char path[PATH_MAX], line[4096];
265*3e78bf84SChristian Brauner struct dirent *de;
266*3e78bf84SChristian Brauner bool found = false;
267*3e78bf84SChristian Brauner DIR *proc;
268*3e78bf84SChristian Brauner FILE *f;
269*3e78bf84SChristian Brauner
270*3e78bf84SChristian Brauner proc = opendir("/proc");
271*3e78bf84SChristian Brauner if (!proc)
272*3e78bf84SChristian Brauner return false;
273*3e78bf84SChristian Brauner while (!found && (de = readdir(proc))) {
274*3e78bf84SChristian Brauner if (de->d_name[0] < '0' || de->d_name[0] > '9')
275*3e78bf84SChristian Brauner continue;
276*3e78bf84SChristian Brauner snprintf(path, sizeof(path), "/proc/%s/mountinfo", de->d_name);
277*3e78bf84SChristian Brauner f = fopen(path, "re");
278*3e78bf84SChristian Brauner if (!f)
279*3e78bf84SChristian Brauner continue;
280*3e78bf84SChristian Brauner while (fgets(line, sizeof(line), f)) {
281*3e78bf84SChristian Brauner if (line_has_source(line, dev)) {
282*3e78bf84SChristian Brauner found = true;
283*3e78bf84SChristian Brauner break;
284*3e78bf84SChristian Brauner }
285*3e78bf84SChristian Brauner }
286*3e78bf84SChristian Brauner fclose(f);
287*3e78bf84SChristian Brauner }
288*3e78bf84SChristian Brauner closedir(proc);
289*3e78bf84SChristian Brauner return found;
290*3e78bf84SChristian Brauner }
291*3e78bf84SChristian Brauner
292*3e78bf84SChristian Brauner /* Wait up to @ms milliseconds for the loop device to give up its backing file. */
loop_released(const char * sysfs,int ms)293*3e78bf84SChristian Brauner static bool loop_released(const char *sysfs, int ms)
294*3e78bf84SChristian Brauner {
295*3e78bf84SChristian Brauner char buf[PATH_MAX];
296*3e78bf84SChristian Brauner
297*3e78bf84SChristian Brauner for (; ms > 0; ms -= 100) {
298*3e78bf84SChristian Brauner if (read_sysfs(sysfs, buf, sizeof(buf)) < 0)
299*3e78bf84SChristian Brauner return errno == ENOENT;
300*3e78bf84SChristian Brauner usleep(100000);
301*3e78bf84SChristian Brauner }
302*3e78bf84SChristian Brauner return read_sysfs(sysfs, buf, sizeof(buf)) < 0 && errno == ENOENT;
303*3e78bf84SChristian Brauner }
304*3e78bf84SChristian Brauner
305*3e78bf84SChristian Brauner /* Tell the loop device to give its file up, now or when its last user is gone. */
loop_clear(int n)306*3e78bf84SChristian Brauner static void loop_clear(int n)
307*3e78bf84SChristian Brauner {
308*3e78bf84SChristian Brauner char dev[32];
309*3e78bf84SChristian Brauner int lfd;
310*3e78bf84SChristian Brauner
311*3e78bf84SChristian Brauner if (n < 0)
312*3e78bf84SChristian Brauner return;
313*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", n);
314*3e78bf84SChristian Brauner lfd = open(dev, O_RDWR);
315*3e78bf84SChristian Brauner if (lfd < 0)
316*3e78bf84SChristian Brauner return;
317*3e78bf84SChristian Brauner ioctl(lfd, LOOP_CLR_FD);
318*3e78bf84SChristian Brauner close(lfd);
319*3e78bf84SChristian Brauner }
320*3e78bf84SChristian Brauner
FIXTURE(loop_cycle)321*3e78bf84SChristian Brauner FIXTURE(loop_cycle) {
322*3e78bf84SChristian Brauner char dev[32]; /* the loop device the child set up */
323*3e78bf84SChristian Brauner char sysfs[64]; /* its backing_file attribute */
324*3e78bf84SChristian Brauner int loops[MAX_LOOPS]; /* every loop device the case has bound */
325*3e78bf84SChristian Brauner int nr_loops;
326*3e78bf84SChristian Brauner bool gadget; /* the gadget's configfs directory exists */
327*3e78bf84SChristian Brauner };
328*3e78bf84SChristian Brauner
remember_loop(FIXTURE_DATA (loop_cycle)* self,int n)329*3e78bf84SChristian Brauner static void remember_loop(FIXTURE_DATA(loop_cycle) *self, int n)
330*3e78bf84SChristian Brauner {
331*3e78bf84SChristian Brauner if (n >= 0 && self->nr_loops < MAX_LOOPS)
332*3e78bf84SChristian Brauner self->loops[self->nr_loops++] = n;
333*3e78bf84SChristian Brauner }
334*3e78bf84SChristian Brauner
FIXTURE_SETUP(loop_cycle)335*3e78bf84SChristian Brauner FIXTURE_SETUP(loop_cycle)
336*3e78bf84SChristian Brauner {
337*3e78bf84SChristian Brauner if (geteuid() != 0)
338*3e78bf84SChristian Brauner SKIP(return, "test requires CAP_SYS_ADMIN");
339*3e78bf84SChristian Brauner if (access("/dev/loop-control", R_OK | W_OK))
340*3e78bf84SChristian Brauner SKIP(return, "test requires loop devices");
341*3e78bf84SChristian Brauner
342*3e78bf84SChristian Brauner ASSERT_EQ(unshare(CLONE_NEWNS), 0);
343*3e78bf84SChristian Brauner ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
344*3e78bf84SChristian Brauner
345*3e78bf84SChristian Brauner snprintf(base, sizeof(base), "/tmp/loop_cycle.XXXXXX");
346*3e78bf84SChristian Brauner ASSERT_NE(mkdtemp(base), NULL);
347*3e78bf84SChristian Brauner /* a failed assertion in the setup does not run the teardown */
348*3e78bf84SChristian Brauner ASSERT_EQ(mount("tmpfs", base, "tmpfs", 0, NULL), 0)
349*3e78bf84SChristian Brauner rmdir(base);
350*3e78bf84SChristian Brauner snprintf(gadget, sizeof(gadget),
351*3e78bf84SChristian Brauner "/sys/kernel/config/usb_gadget/kselftest_cycle_%d", getpid());
352*3e78bf84SChristian Brauner self->dev[0] = '\0';
353*3e78bf84SChristian Brauner self->nr_loops = 0;
354*3e78bf84SChristian Brauner self->gadget = false;
355*3e78bf84SChristian Brauner }
356*3e78bf84SChristian Brauner
357*3e78bf84SChristian Brauner static int mount_configfs(void);
358*3e78bf84SChristian Brauner static void gadget_remove(void);
359*3e78bf84SChristian Brauner
FIXTURE_TEARDOWN(loop_cycle)360*3e78bf84SChristian Brauner FIXTURE_TEARDOWN(loop_cycle)
361*3e78bf84SChristian Brauner {
362*3e78bf84SChristian Brauner if (self->gadget)
363*3e78bf84SChristian Brauner gadget_remove();
364*3e78bf84SChristian Brauner /* whatever a failed assertion has left bound */
365*3e78bf84SChristian Brauner for (int i = 0; i < self->nr_loops; i++)
366*3e78bf84SChristian Brauner loop_clear(self->loops[i]);
367*3e78bf84SChristian Brauner umount2(base, MNT_DETACH);
368*3e78bf84SChristian Brauner rmdir(base);
369*3e78bf84SChristian Brauner }
370*3e78bf84SChristian Brauner
371*3e78bf84SChristian Brauner /* Child exit codes. */
372*3e78bf84SChristian Brauner enum {
373*3e78bf84SChristian Brauner CHILD_OK,
374*3e78bf84SChristian Brauner CHILD_NS, /* could not set up the namespace or the tmpfs */
375*3e78bf84SChristian Brauner CHILD_IMAGE, /* could not write the image */
376*3e78bf84SChristian Brauner CHILD_LOOP, /* could not set up the loop device */
377*3e78bf84SChristian Brauner CHILD_MOUNT, /* could not mount it (vfat and msdos both refused) */
378*3e78bf84SChristian Brauner CHILD_PIPE, /* the parent went away */
379*3e78bf84SChristian Brauner CHILD_HOLDER, /* could not set the holder up below the mount */
380*3e78bf84SChristian Brauner CHILD_SKIP, /* the kernel lacks what the holder needs */
381*3e78bf84SChristian Brauner CHILD_NOFS, /* the kernel lacks the filesystem of the image */
382*3e78bf84SChristian Brauner };
383*3e78bf84SChristian Brauner
384*3e78bf84SChristian Brauner /* Bind the loop device that LOOP_CTL_GET_FREE names to @ifd; the device number. */
loop_bind_free(int ifd)385*3e78bf84SChristian Brauner static int loop_bind_free(int ifd)
386*3e78bf84SChristian Brauner {
387*3e78bf84SChristian Brauner int cfd, lfd, n;
388*3e78bf84SChristian Brauner char dev[32];
389*3e78bf84SChristian Brauner
390*3e78bf84SChristian Brauner cfd = open("/dev/loop-control", O_RDWR);
391*3e78bf84SChristian Brauner if (cfd < 0)
392*3e78bf84SChristian Brauner return -1;
393*3e78bf84SChristian Brauner n = ioctl(cfd, LOOP_CTL_GET_FREE);
394*3e78bf84SChristian Brauner close(cfd);
395*3e78bf84SChristian Brauner if (n < 0)
396*3e78bf84SChristian Brauner return -1;
397*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", n);
398*3e78bf84SChristian Brauner lfd = open(dev, O_RDWR);
399*3e78bf84SChristian Brauner if (lfd < 0)
400*3e78bf84SChristian Brauner return -1;
401*3e78bf84SChristian Brauner if (ioctl(lfd, LOOP_SET_FD, ifd))
402*3e78bf84SChristian Brauner n = -1;
403*3e78bf84SChristian Brauner close(lfd);
404*3e78bf84SChristian Brauner return n;
405*3e78bf84SChristian Brauner }
406*3e78bf84SChristian Brauner
407*3e78bf84SChristian Brauner /*
408*3e78bf84SChristian Brauner * Bind a free loop device to the open image @ifd; the device number. The
409*3e78bf84SChristian Brauner * device is free when LOOP_CTL_GET_FREE names it and may be somebody else's
410*3e78bf84SChristian Brauner * a moment later, so try again when it is busy.
411*3e78bf84SChristian Brauner */
loop_bind(int ifd)412*3e78bf84SChristian Brauner static int loop_bind(int ifd)
413*3e78bf84SChristian Brauner {
414*3e78bf84SChristian Brauner int n = -1;
415*3e78bf84SChristian Brauner
416*3e78bf84SChristian Brauner for (int i = 0; i < 64 && n < 0; i++) {
417*3e78bf84SChristian Brauner n = loop_bind_free(ifd);
418*3e78bf84SChristian Brauner if (n < 0 && errno != EBUSY)
419*3e78bf84SChristian Brauner return -1;
420*3e78bf84SChristian Brauner }
421*3e78bf84SChristian Brauner if (n >= 0 && nr_bound < MAX_LOOPS)
422*3e78bf84SChristian Brauner bound[nr_bound++] = n;
423*3e78bf84SChristian Brauner return n;
424*3e78bf84SChristian Brauner }
425*3e78bf84SChristian Brauner
426*3e78bf84SChristian Brauner /* Mount a FAT image, as vfat or as msdos; -1 with ENODEV if the kernel has neither. */
mount_fat(const char * dev,const char * mp)427*3e78bf84SChristian Brauner static int mount_fat(const char *dev, const char *mp)
428*3e78bf84SChristian Brauner {
429*3e78bf84SChristian Brauner int err;
430*3e78bf84SChristian Brauner
431*3e78bf84SChristian Brauner if (!mount(dev, mp, "vfat", 0, NULL))
432*3e78bf84SChristian Brauner return 0;
433*3e78bf84SChristian Brauner err = errno;
434*3e78bf84SChristian Brauner if (!mount(dev, mp, "msdos", 0, NULL))
435*3e78bf84SChristian Brauner return 0;
436*3e78bf84SChristian Brauner if (err != ENODEV)
437*3e78bf84SChristian Brauner errno = err;
438*3e78bf84SChristian Brauner return -1;
439*3e78bf84SChristian Brauner }
440*3e78bf84SChristian Brauner
441*3e78bf84SChristian Brauner /*
442*3e78bf84SChristian Brauner * A child that gives up has to take down what it has set up: nobody else
443*3e78bf84SChristian Brauner * knows about it. Unmount, then tell the loop devices to let go. The mounts
444*3e78bf84SChristian Brauner * are released after this process is gone and the devices follow them.
445*3e78bf84SChristian Brauner */
child_fails(int ret)446*3e78bf84SChristian Brauner static int child_fails(int ret)
447*3e78bf84SChristian Brauner {
448*3e78bf84SChristian Brauner umount2(at("vol"), MNT_DETACH);
449*3e78bf84SChristian Brauner umount2(at("vol2"), MNT_DETACH);
450*3e78bf84SChristian Brauner umount2(at("p"), MNT_DETACH);
451*3e78bf84SChristian Brauner umount2(at("img"), MNT_DETACH);
452*3e78bf84SChristian Brauner for (int i = 0; i < nr_bound; i++)
453*3e78bf84SChristian Brauner loop_clear(bound[i]);
454*3e78bf84SChristian Brauner return ret;
455*3e78bf84SChristian Brauner }
456*3e78bf84SChristian Brauner
457*3e78bf84SChristian Brauner /* Write an image to @img, bind a loop device to it and mount that at @mp; the device number. */
loop_mount(const char * img,const char * mp)458*3e78bf84SChristian Brauner static int loop_mount(const char *img, const char *mp)
459*3e78bf84SChristian Brauner {
460*3e78bf84SChristian Brauner char dev[32];
461*3e78bf84SChristian Brauner int ifd, n;
462*3e78bf84SChristian Brauner
463*3e78bf84SChristian Brauner ifd = open(img, O_RDWR | O_CREAT | O_EXCL, 0600);
464*3e78bf84SChristian Brauner if (ifd < 0 || write_fat12(ifd))
465*3e78bf84SChristian Brauner return -CHILD_IMAGE;
466*3e78bf84SChristian Brauner n = loop_bind(ifd);
467*3e78bf84SChristian Brauner close(ifd); /* the loop device holds the file from now on */
468*3e78bf84SChristian Brauner if (n < 0)
469*3e78bf84SChristian Brauner return -CHILD_LOOP;
470*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", n);
471*3e78bf84SChristian Brauner if (mkdir(mp, 0755))
472*3e78bf84SChristian Brauner return -CHILD_MOUNT;
473*3e78bf84SChristian Brauner if (mount_fat(dev, mp))
474*3e78bf84SChristian Brauner return errno == ENODEV ? -CHILD_NOFS : -CHILD_MOUNT;
475*3e78bf84SChristian Brauner return n;
476*3e78bf84SChristian Brauner }
477*3e78bf84SChristian Brauner
478*3e78bf84SChristian Brauner /*
479*3e78bf84SChristian Brauner * Two tmpfs mounts, each carrying the image of the loop mount below the
480*3e78bf84SChristian Brauner * other: the loop mount below vol has its image on vol2 and the other way
481*3e78bf84SChristian Brauner * round.
482*3e78bf84SChristian Brauner */
crossed_child(int to_parent,int from_parent)483*3e78bf84SChristian Brauner static int crossed_child(int to_parent, int from_parent)
484*3e78bf84SChristian Brauner {
485*3e78bf84SChristian Brauner struct report r;
486*3e78bf84SChristian Brauner char c;
487*3e78bf84SChristian Brauner
488*3e78bf84SChristian Brauner if (unshare(CLONE_NEWNS) || mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL))
489*3e78bf84SChristian Brauner return CHILD_NS;
490*3e78bf84SChristian Brauner if (mkdir(at("vol"), 0755) || mount("tmpfs", at("vol"), "tmpfs", 0, NULL) ||
491*3e78bf84SChristian Brauner mkdir(at("vol2"), 0755) || mount("tmpfs", at("vol2"), "tmpfs", 0, NULL))
492*3e78bf84SChristian Brauner return child_fails(CHILD_NS);
493*3e78bf84SChristian Brauner r.n[0] = loop_mount(at("vol2/img"), at("vol/mnt"));
494*3e78bf84SChristian Brauner if (r.n[0] < 0)
495*3e78bf84SChristian Brauner return child_fails(-r.n[0]);
496*3e78bf84SChristian Brauner r.n[1] = loop_mount(at("vol/img"), at("vol2/mnt"));
497*3e78bf84SChristian Brauner if (r.n[1] < 0)
498*3e78bf84SChristian Brauner return child_fails(-r.n[1]);
499*3e78bf84SChristian Brauner if (write(to_parent, &r, sizeof(r)) != sizeof(r))
500*3e78bf84SChristian Brauner return child_fails(CHILD_PIPE);
501*3e78bf84SChristian Brauner if (read(from_parent, &c, 1) != 1)
502*3e78bf84SChristian Brauner return child_fails(CHILD_PIPE);
503*3e78bf84SChristian Brauner return CHILD_OK;
504*3e78bf84SChristian Brauner }
505*3e78bf84SChristian Brauner
506*3e78bf84SChristian Brauner /*
507*3e78bf84SChristian Brauner * In its own mount namespace the child mounts a tmpfs on vol,
508*3e78bf84SChristian Brauner * puts a filesystem image on it, binds a loop device to the image and
509*3e78bf84SChristian Brauner * mounts that loop device below. The loop device's backing file is a
510*3e78bf84SChristian Brauner * reference on the mount the image is on, held by the loop device, held
511*3e78bf84SChristian Brauner * by the mounted filesystem, held by the mount below.
512*3e78bf84SChristian Brauner */
loop_child(int to_parent,int from_parent)513*3e78bf84SChristian Brauner static int loop_child(int to_parent, int from_parent)
514*3e78bf84SChristian Brauner {
515*3e78bf84SChristian Brauner struct report r = { { -1, -1 } };
516*3e78bf84SChristian Brauner char c;
517*3e78bf84SChristian Brauner
518*3e78bf84SChristian Brauner if (unshare(CLONE_NEWNS) || mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL))
519*3e78bf84SChristian Brauner return CHILD_NS;
520*3e78bf84SChristian Brauner if (mkdir(at("vol"), 0755) || mount("tmpfs", at("vol"), "tmpfs", 0, NULL))
521*3e78bf84SChristian Brauner return child_fails(CHILD_NS);
522*3e78bf84SChristian Brauner r.n[0] = loop_mount(at("vol/img"), at("vol/mnt"));
523*3e78bf84SChristian Brauner if (r.n[0] < 0)
524*3e78bf84SChristian Brauner return child_fails(-r.n[0]);
525*3e78bf84SChristian Brauner
526*3e78bf84SChristian Brauner if (write(to_parent, &r, sizeof(r)) != sizeof(r))
527*3e78bf84SChristian Brauner return child_fails(CHILD_PIPE);
528*3e78bf84SChristian Brauner /* keep the namespace alive while the parent removes the directory */
529*3e78bf84SChristian Brauner if (read(from_parent, &c, 1) != 1)
530*3e78bf84SChristian Brauner return child_fails(CHILD_PIPE);
531*3e78bf84SChristian Brauner return CHILD_OK;
532*3e78bf84SChristian Brauner }
533*3e78bf84SChristian Brauner
534*3e78bf84SChristian Brauner /*
535*3e78bf84SChristian Brauner * The child did not report. Skip if the kernel lacks something, fail with
536*3e78bf84SChristian Brauner * what the child said otherwise. A child that a signal killed has failed.
537*3e78bf84SChristian Brauner */
538*3e78bf84SChristian Brauner #define CHILD_GAVE_UP(pid) do { \
539*3e78bf84SChristian Brauner int __status; \
540*3e78bf84SChristian Brauner \
541*3e78bf84SChristian Brauner ASSERT_EQ(waitpid(pid, &__status, 0), pid); \
542*3e78bf84SChristian Brauner if (WIFEXITED(__status) && WEXITSTATUS(__status) == CHILD_NOFS) \
543*3e78bf84SChristian Brauner SKIP(return, "test requires the filesystem of the image (FAT or minix)"); \
544*3e78bf84SChristian Brauner if (WIFEXITED(__status) && WEXITSTATUS(__status) == CHILD_SKIP) \
545*3e78bf84SChristian Brauner SKIP(return, "the kernel lacks what this holder needs"); \
546*3e78bf84SChristian Brauner ASSERT_TRUE(false) \
547*3e78bf84SChristian Brauner TH_LOG("child failed to set up: %s %d", \
548*3e78bf84SChristian Brauner WIFEXITED(__status) ? "exit status" : "signal", \
549*3e78bf84SChristian Brauner WIFEXITED(__status) ? WEXITSTATUS(__status) : WTERMSIG(__status)); \
550*3e78bf84SChristian Brauner } while (0)
551*3e78bf84SChristian Brauner
552*3e78bf84SChristian Brauner /* The child has to leave by itself and with nothing to complain about. */
553*3e78bf84SChristian Brauner #define CHILD_LEFT(pid) do { \
554*3e78bf84SChristian Brauner int __status; \
555*3e78bf84SChristian Brauner \
556*3e78bf84SChristian Brauner ASSERT_EQ(waitpid(pid, &__status, 0), pid); \
557*3e78bf84SChristian Brauner ASSERT_TRUE(WIFEXITED(__status)); \
558*3e78bf84SChristian Brauner ASSERT_EQ(WEXITSTATUS(__status), CHILD_OK); \
559*3e78bf84SChristian Brauner } while (0)
560*3e78bf84SChristian Brauner
561*3e78bf84SChristian Brauner /*
562*3e78bf84SChristian Brauner * An exclusive open of the device fails while a filesystem holds it, and
563*3e78bf84SChristian Brauner * the only filesystem that ever did is the one mounted below the dead
564*3e78bf84SChristian Brauner * mount. Give a release in flight a moment. Then the device must clear
565*3e78bf84SChristian Brauner * right away rather than only be marked for autoclear.
566*3e78bf84SChristian Brauner */
assert_loop_released(struct __test_metadata * _metadata,FIXTURE_DATA (loop_cycle)* self)567*3e78bf84SChristian Brauner static void assert_loop_released(struct __test_metadata *_metadata,
568*3e78bf84SChristian Brauner FIXTURE_DATA(loop_cycle) *self)
569*3e78bf84SChristian Brauner {
570*3e78bf84SChristian Brauner int lfd;
571*3e78bf84SChristian Brauner
572*3e78bf84SChristian Brauner for (int i = 0; i < 20; i++) {
573*3e78bf84SChristian Brauner lfd = open(self->dev, O_RDONLY | O_EXCL);
574*3e78bf84SChristian Brauner if (lfd >= 0)
575*3e78bf84SChristian Brauner break;
576*3e78bf84SChristian Brauner usleep(100000);
577*3e78bf84SChristian Brauner }
578*3e78bf84SChristian Brauner EXPECT_GE(lfd, 0)
579*3e78bf84SChristian Brauner TH_LOG("%s is still held by the loop mount below the dead mount: nothing refers to either mount and nothing can release them",
580*3e78bf84SChristian Brauner self->dev);
581*3e78bf84SChristian Brauner if (lfd >= 0)
582*3e78bf84SChristian Brauner close(lfd);
583*3e78bf84SChristian Brauner
584*3e78bf84SChristian Brauner lfd = open(self->dev, O_RDWR);
585*3e78bf84SChristian Brauner ASSERT_GE(lfd, 0);
586*3e78bf84SChristian Brauner ASSERT_EQ(ioctl(lfd, LOOP_CLR_FD), 0);
587*3e78bf84SChristian Brauner close(lfd);
588*3e78bf84SChristian Brauner ASSERT_TRUE(loop_released(self->sysfs, 5000))
589*3e78bf84SChristian Brauner TH_LOG("%s kept its backing file after LOOP_CLR_FD: the filesystem on it is still mounted somewhere nobody can reach",
590*3e78bf84SChristian Brauner self->dev);
591*3e78bf84SChristian Brauner /* somebody else may bind it from now on, so the teardown leaves it alone */
592*3e78bf84SChristian Brauner for (int i = 0; i < self->nr_loops; i++) {
593*3e78bf84SChristian Brauner char dev[32];
594*3e78bf84SChristian Brauner
595*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", self->loops[i]);
596*3e78bf84SChristian Brauner if (!strcmp(dev, self->dev))
597*3e78bf84SChristian Brauner self->loops[i] = -1;
598*3e78bf84SChristian Brauner }
599*3e78bf84SChristian Brauner }
600*3e78bf84SChristian Brauner
601*3e78bf84SChristian Brauner /*
602*3e78bf84SChristian Brauner * rmdir of vol from here, where it is not a mountpoint, detaches
603*3e78bf84SChristian Brauner * the child's tmpfs with the loop mount connected below it. Once the child
604*3e78bf84SChristian Brauner * is gone nothing refers to either mount. The loop device must then be
605*3e78bf84SChristian Brauner * free to give up its backing file, which only happens when the mounted
606*3e78bf84SChristian Brauner * filesystem below the detached tmpfs has been released.
607*3e78bf84SChristian Brauner */
TEST_F(loop_cycle,detached_loop_mount_released)608*3e78bf84SChristian Brauner TEST_F(loop_cycle, detached_loop_mount_released)
609*3e78bf84SChristian Brauner {
610*3e78bf84SChristian Brauner int to_parent[2], to_child[2];
611*3e78bf84SChristian Brauner struct report r = { { -1, -1 } };
612*3e78bf84SChristian Brauner char buf[PATH_MAX];
613*3e78bf84SChristian Brauner pid_t pid;
614*3e78bf84SChristian Brauner
615*3e78bf84SChristian Brauner ASSERT_EQ(pipe(to_parent), 0);
616*3e78bf84SChristian Brauner ASSERT_EQ(pipe(to_child), 0);
617*3e78bf84SChristian Brauner
618*3e78bf84SChristian Brauner pid = fork();
619*3e78bf84SChristian Brauner ASSERT_GE(pid, 0);
620*3e78bf84SChristian Brauner if (pid == 0) {
621*3e78bf84SChristian Brauner close(to_parent[0]);
622*3e78bf84SChristian Brauner close(to_child[1]);
623*3e78bf84SChristian Brauner _exit(loop_child(to_parent[1], to_child[0]));
624*3e78bf84SChristian Brauner }
625*3e78bf84SChristian Brauner close(to_parent[1]);
626*3e78bf84SChristian Brauner close(to_child[0]);
627*3e78bf84SChristian Brauner
628*3e78bf84SChristian Brauner if (read(to_parent[0], &r, sizeof(r)) != sizeof(r))
629*3e78bf84SChristian Brauner CHILD_GAVE_UP(pid);
630*3e78bf84SChristian Brauner remember_loop(self, r.n[0]);
631*3e78bf84SChristian Brauner snprintf(self->dev, sizeof(self->dev), "/dev/loop%d", r.n[0]);
632*3e78bf84SChristian Brauner snprintf(self->sysfs, sizeof(self->sysfs), "/sys/block/loop%d/loop/backing_file", r.n[0]);
633*3e78bf84SChristian Brauner ASSERT_EQ(read_sysfs(self->sysfs, buf, sizeof(buf)), 0);
634*3e78bf84SChristian Brauner ASSERT_NE(strstr(buf, "/vol/img"), NULL);
635*3e78bf84SChristian Brauner
636*3e78bf84SChristian Brauner /* not a mountpoint in this namespace, so the directory can go */
637*3e78bf84SChristian Brauner ASSERT_EQ(rmdir(at("vol")), 0);
638*3e78bf84SChristian Brauner
639*3e78bf84SChristian Brauner /* the child leaves: its namespace and every reference it held are gone */
640*3e78bf84SChristian Brauner ASSERT_EQ(write(to_child[1], "", 1), 1);
641*3e78bf84SChristian Brauner CHILD_LEFT(pid);
642*3e78bf84SChristian Brauner close(to_parent[0]);
643*3e78bf84SChristian Brauner close(to_child[1]);
644*3e78bf84SChristian Brauner
645*3e78bf84SChristian Brauner /* nothing can reach the two mounts any more */
646*3e78bf84SChristian Brauner ASSERT_EQ(access(at("vol"), F_OK), -1);
647*3e78bf84SChristian Brauner ASSERT_FALSE(mounted_anywhere(self->dev));
648*3e78bf84SChristian Brauner
649*3e78bf84SChristian Brauner assert_loop_released(_metadata, self);
650*3e78bf84SChristian Brauner }
651*3e78bf84SChristian Brauner
652*3e78bf84SChristian Brauner /*
653*3e78bf84SChristian Brauner * The same two mounts in a detached tree: a clone of vol from
654*3e78bf84SChristian Brauner * open_tree(), the image opened through the clone so that the loop device
655*3e78bf84SChristian Brauner * holds the clone, and the loop mount moved below the clone. The last
656*3e78bf84SChristian Brauner * close of the tree's fd dissolves the tree with the loop mount left
657*3e78bf84SChristian Brauner * connected below the dead clone, and nothing refers to either afterwards.
658*3e78bf84SChristian Brauner */
TEST_F(loop_cycle,dissolved_tree_loop_mount_released)659*3e78bf84SChristian Brauner TEST_F(loop_cycle, dissolved_tree_loop_mount_released)
660*3e78bf84SChristian Brauner {
661*3e78bf84SChristian Brauner int tfd, ifd, fsfd, mfd, n;
662*3e78bf84SChristian Brauner char buf[PATH_MAX];
663*3e78bf84SChristian Brauner
664*3e78bf84SChristian Brauner fsfd = sys_fsopen("vfat", 0);
665*3e78bf84SChristian Brauner if (fsfd < 0)
666*3e78bf84SChristian Brauner fsfd = sys_fsopen("msdos", 0);
667*3e78bf84SChristian Brauner if (fsfd < 0)
668*3e78bf84SChristian Brauner SKIP(return, "test requires a FAT filesystem");
669*3e78bf84SChristian Brauner
670*3e78bf84SChristian Brauner ASSERT_EQ(mkdir(at("vol"), 0755), 0);
671*3e78bf84SChristian Brauner ASSERT_EQ(mount("tmpfs", at("vol"), "tmpfs", 0, NULL), 0);
672*3e78bf84SChristian Brauner tfd = sys_open_tree(AT_FDCWD, at("vol"), OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
673*3e78bf84SChristian Brauner ASSERT_GE(tfd, 0);
674*3e78bf84SChristian Brauner
675*3e78bf84SChristian Brauner /* the image, opened through the clone: the loop device holds the clone */
676*3e78bf84SChristian Brauner ifd = openat(tfd, "img", O_RDWR | O_CREAT | O_EXCL, 0600);
677*3e78bf84SChristian Brauner ASSERT_GE(ifd, 0);
678*3e78bf84SChristian Brauner ASSERT_EQ(write_fat12(ifd), 0);
679*3e78bf84SChristian Brauner n = loop_bind(ifd);
680*3e78bf84SChristian Brauner close(ifd);
681*3e78bf84SChristian Brauner ASSERT_GE(n, 0);
682*3e78bf84SChristian Brauner remember_loop(self, n);
683*3e78bf84SChristian Brauner snprintf(self->dev, sizeof(self->dev), "/dev/loop%d", n);
684*3e78bf84SChristian Brauner snprintf(self->sysfs, sizeof(self->sysfs), "/sys/block/loop%d/loop/backing_file", n);
685*3e78bf84SChristian Brauner
686*3e78bf84SChristian Brauner /* the loop mount, moved below the clone */
687*3e78bf84SChristian Brauner ASSERT_EQ(mkdirat(tfd, "mnt", 0755), 0);
688*3e78bf84SChristian Brauner ASSERT_EQ(sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", self->dev, 0), 0);
689*3e78bf84SChristian Brauner ASSERT_EQ(sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
690*3e78bf84SChristian Brauner mfd = sys_fsmount(fsfd, 0, 0);
691*3e78bf84SChristian Brauner ASSERT_GE(mfd, 0);
692*3e78bf84SChristian Brauner close(fsfd);
693*3e78bf84SChristian Brauner ASSERT_EQ(sys_move_mount(mfd, "", tfd, "mnt", MOVE_MOUNT_F_EMPTY_PATH), 0);
694*3e78bf84SChristian Brauner close(mfd);
695*3e78bf84SChristian Brauner
696*3e78bf84SChristian Brauner ASSERT_EQ(read_sysfs(self->sysfs, buf, sizeof(buf)), 0);
697*3e78bf84SChristian Brauner ASSERT_NE(strstr(buf, "img"), NULL);
698*3e78bf84SChristian Brauner
699*3e78bf84SChristian Brauner /* the last fd of the tree: both mounts die, the loop mount connected */
700*3e78bf84SChristian Brauner close(tfd);
701*3e78bf84SChristian Brauner
702*3e78bf84SChristian Brauner /* nothing can reach the two mounts any more */
703*3e78bf84SChristian Brauner ASSERT_FALSE(mounted_anywhere(self->dev));
704*3e78bf84SChristian Brauner
705*3e78bf84SChristian Brauner assert_loop_released(_metadata, self);
706*3e78bf84SChristian Brauner }
707*3e78bf84SChristian Brauner
708*3e78bf84SChristian Brauner /*
709*3e78bf84SChristian Brauner * The cycle in two steps: rmdir of vol leaves the loop mount below it
710*3e78bf84SChristian Brauner * connected while its image's mount, vol2, is alive; then rmdir of vol2
711*3e78bf84SChristian Brauner * takes that one with the loop mount whose image is on the dead vol. Each
712*3e78bf84SChristian Brauner * dead mount now owns a loop mount whose filesystem pins the other.
713*3e78bf84SChristian Brauner */
TEST_F(loop_cycle,crossed_images_released)714*3e78bf84SChristian Brauner TEST_F(loop_cycle, crossed_images_released)
715*3e78bf84SChristian Brauner {
716*3e78bf84SChristian Brauner int to_parent[2], to_child[2];
717*3e78bf84SChristian Brauner struct report r = { { -1, -1 } };
718*3e78bf84SChristian Brauner char sysfs[2][64], dev[2][32];
719*3e78bf84SChristian Brauner pid_t pid;
720*3e78bf84SChristian Brauner
721*3e78bf84SChristian Brauner ASSERT_EQ(pipe(to_parent), 0);
722*3e78bf84SChristian Brauner ASSERT_EQ(pipe(to_child), 0);
723*3e78bf84SChristian Brauner
724*3e78bf84SChristian Brauner pid = fork();
725*3e78bf84SChristian Brauner ASSERT_GE(pid, 0);
726*3e78bf84SChristian Brauner if (pid == 0) {
727*3e78bf84SChristian Brauner close(to_parent[0]);
728*3e78bf84SChristian Brauner close(to_child[1]);
729*3e78bf84SChristian Brauner _exit(crossed_child(to_parent[1], to_child[0]));
730*3e78bf84SChristian Brauner }
731*3e78bf84SChristian Brauner close(to_parent[1]);
732*3e78bf84SChristian Brauner close(to_child[0]);
733*3e78bf84SChristian Brauner
734*3e78bf84SChristian Brauner if (read(to_parent[0], &r, sizeof(r)) != sizeof(r))
735*3e78bf84SChristian Brauner CHILD_GAVE_UP(pid);
736*3e78bf84SChristian Brauner for (int i = 0; i < 2; i++) {
737*3e78bf84SChristian Brauner remember_loop(self, r.n[i]);
738*3e78bf84SChristian Brauner snprintf(dev[i], sizeof(dev[i]), "/dev/loop%d", r.n[i]);
739*3e78bf84SChristian Brauner snprintf(sysfs[i], sizeof(sysfs[i]), "/sys/block/loop%d/loop/backing_file", r.n[i]);
740*3e78bf84SChristian Brauner }
741*3e78bf84SChristian Brauner
742*3e78bf84SChristian Brauner /* step one: the mount with the first loop mount below it goes */
743*3e78bf84SChristian Brauner ASSERT_EQ(rmdir(at("vol")), 0);
744*3e78bf84SChristian Brauner
745*3e78bf84SChristian Brauner /* step two: the other one, with the loop mount whose image is on the first */
746*3e78bf84SChristian Brauner ASSERT_EQ(rmdir(at("vol2")), 0);
747*3e78bf84SChristian Brauner
748*3e78bf84SChristian Brauner /* the child leaves: its namespace and every reference it held are gone */
749*3e78bf84SChristian Brauner ASSERT_EQ(write(to_child[1], "", 1), 1);
750*3e78bf84SChristian Brauner CHILD_LEFT(pid);
751*3e78bf84SChristian Brauner close(to_parent[0]);
752*3e78bf84SChristian Brauner close(to_child[1]);
753*3e78bf84SChristian Brauner
754*3e78bf84SChristian Brauner /* nothing can reach the four mounts any more */
755*3e78bf84SChristian Brauner for (int i = 0; i < 2; i++) {
756*3e78bf84SChristian Brauner ASSERT_FALSE(mounted_anywhere(dev[i]));
757*3e78bf84SChristian Brauner strcpy(self->dev, dev[i]);
758*3e78bf84SChristian Brauner strcpy(self->sysfs, sysfs[i]);
759*3e78bf84SChristian Brauner assert_loop_released(_metadata, self);
760*3e78bf84SChristian Brauner }
761*3e78bf84SChristian Brauner }
762*3e78bf84SChristian Brauner
763*3e78bf84SChristian Brauner /*
764*3e78bf84SChristian Brauner * The holders. Each keeps a file or a path on a mount P for as long as
765*3e78bf84SChristian Brauner * its own filesystem or device lives, and each has that filesystem or
766*3e78bf84SChristian Brauner * device mounted at C below P. Once another namespace's rmdir has
767*3e78bf84SChristian Brauner * detached P with C connected below it and the child is gone, P is owned
768*3e78bf84SChristian Brauner * by nobody, C by P, and P is kept by whatever the holder still holds.
769*3e78bf84SChristian Brauner *
770*3e78bf84SChristian Brauner * P is a loop mount so that its death can be observed: the loop device
771*3e78bf84SChristian Brauner * gives its backing file up when P's superblock goes. The image sits on
772*3e78bf84SChristian Brauner * a tmpfs next to P, not above it, so the loop device's own reference is
773*3e78bf84SChristian Brauner * not part of the picture.
774*3e78bf84SChristian Brauner */
775*3e78bf84SChristian Brauner enum holder {
776*3e78bf84SChristian Brauner HOLDER_AUTOFS, /* a FIFO on P as the daemon's pipe */
777*3e78bf84SChristian Brauner HOLDER_ZRAM, /* a device node on P as the writeback device */
778*3e78bf84SChristian Brauner HOLDER_ECRYPTFS, /* a directory on P as the lower directory */
779*3e78bf84SChristian Brauner HOLDER_BINFMT_MISC, /* an executable on P as an 'F' interpreter */
780*3e78bf84SChristian Brauner HOLDER_FUSE, /* a file on P as a passthrough backing file */
781*3e78bf84SChristian Brauner HOLDER_ZLOOP, /* a directory on P for the zone files */
782*3e78bf84SChristian Brauner HOLDER_GADGET, /* a file on P as a mass storage LUN, over dummy_hcd */
783*3e78bf84SChristian Brauner HOLDER_MD, /* a file on P as an array's bitmap file */
784*3e78bf84SChristian Brauner };
785*3e78bf84SChristian Brauner
786*3e78bf84SChristian Brauner #define HOLDER_IMG at("img/p.img")
787*3e78bf84SChristian Brauner #define HOLDER_MNT at("p")
788*3e78bf84SChristian Brauner #define HOLDER_BELOW at("p/c")
789*3e78bf84SChristian Brauner
write_file(const char * path,const char * s)790*3e78bf84SChristian Brauner static int write_file(const char *path, const char *s)
791*3e78bf84SChristian Brauner {
792*3e78bf84SChristian Brauner int fd = open(path, O_WRONLY);
793*3e78bf84SChristian Brauner ssize_t n;
794*3e78bf84SChristian Brauner
795*3e78bf84SChristian Brauner if (fd < 0)
796*3e78bf84SChristian Brauner return -1;
797*3e78bf84SChristian Brauner n = write(fd, s, strlen(s));
798*3e78bf84SChristian Brauner close(fd);
799*3e78bf84SChristian Brauner return n == (ssize_t)strlen(s) ? 0 : -1;
800*3e78bf84SChristian Brauner }
801*3e78bf84SChristian Brauner
802*3e78bf84SChristian Brauner /* Bind a free loop device to @img; the device number. */
loop_attach(const char * img)803*3e78bf84SChristian Brauner static int loop_attach(const char *img)
804*3e78bf84SChristian Brauner {
805*3e78bf84SChristian Brauner int ifd, n;
806*3e78bf84SChristian Brauner
807*3e78bf84SChristian Brauner ifd = open(img, O_RDWR);
808*3e78bf84SChristian Brauner if (ifd < 0)
809*3e78bf84SChristian Brauner return -1;
810*3e78bf84SChristian Brauner n = loop_bind(ifd);
811*3e78bf84SChristian Brauner close(ifd);
812*3e78bf84SChristian Brauner return n;
813*3e78bf84SChristian Brauner }
814*3e78bf84SChristian Brauner
holder_autofs(void)815*3e78bf84SChristian Brauner static int holder_autofs(void)
816*3e78bf84SChristian Brauner {
817*3e78bf84SChristian Brauner char opts[64];
818*3e78bf84SChristian Brauner int pfd;
819*3e78bf84SChristian Brauner
820*3e78bf84SChristian Brauner if (mkfifo(at("p/pipe"), 0600))
821*3e78bf84SChristian Brauner return CHILD_HOLDER;
822*3e78bf84SChristian Brauner pfd = open(at("p/pipe"), O_RDWR);
823*3e78bf84SChristian Brauner if (pfd < 0)
824*3e78bf84SChristian Brauner return CHILD_HOLDER;
825*3e78bf84SChristian Brauner snprintf(opts, sizeof(opts), "fd=%d,minproto=5,maxproto=5", pfd);
826*3e78bf84SChristian Brauner if (mount("autofs", HOLDER_BELOW, "autofs", 0, opts))
827*3e78bf84SChristian Brauner return errno == ENODEV ? CHILD_SKIP : CHILD_HOLDER;
828*3e78bf84SChristian Brauner close(pfd); /* the mount keeps its own */
829*3e78bf84SChristian Brauner return CHILD_OK;
830*3e78bf84SChristian Brauner }
831*3e78bf84SChristian Brauner
832*3e78bf84SChristian Brauner /*
833*3e78bf84SChristian Brauner * zram's writeback device has to be a block device node, and that node has
834*3e78bf84SChristian Brauner * to be on P. Point it at a second loop device.
835*3e78bf84SChristian Brauner */
836*3e78bf84SChristian Brauner static void zram_reset(void);
837*3e78bf84SChristian Brauner
holder_zram(void)838*3e78bf84SChristian Brauner static int holder_zram(void)
839*3e78bf84SChristian Brauner {
840*3e78bf84SChristian Brauner char dev[32], buf[64];
841*3e78bf84SChristian Brauner struct stat st;
842*3e78bf84SChristian Brauner int fd, n;
843*3e78bf84SChristian Brauner
844*3e78bf84SChristian Brauner if (access("/sys/block/zram0/backing_dev", W_OK))
845*3e78bf84SChristian Brauner return CHILD_SKIP;
846*3e78bf84SChristian Brauner /* somebody else's device, leave it alone */
847*3e78bf84SChristian Brauner if (read_sysfs("/sys/block/zram0/initstate", buf, sizeof(buf)) || buf[0] != '0')
848*3e78bf84SChristian Brauner return CHILD_SKIP;
849*3e78bf84SChristian Brauner fd = open(at("img/wb.img"), O_RDWR | O_CREAT | O_EXCL, 0600);
850*3e78bf84SChristian Brauner if (fd < 0 || ftruncate(fd, IMAGE_SIZE))
851*3e78bf84SChristian Brauner return CHILD_HOLDER;
852*3e78bf84SChristian Brauner close(fd);
853*3e78bf84SChristian Brauner n = loop_attach(at("img/wb.img"));
854*3e78bf84SChristian Brauner if (n < 0)
855*3e78bf84SChristian Brauner return CHILD_HOLDER;
856*3e78bf84SChristian Brauner /* the minor is not the number of the device when loop has partitions */
857*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", n);
858*3e78bf84SChristian Brauner if (stat(dev, &st) || mknod(at("p/wbdev"), S_IFBLK | 0600, st.st_rdev))
859*3e78bf84SChristian Brauner return CHILD_HOLDER;
860*3e78bf84SChristian Brauner if (write_file("/sys/block/zram0/backing_dev", at("p/wbdev")))
861*3e78bf84SChristian Brauner return CHILD_HOLDER;
862*3e78bf84SChristian Brauner snprintf(buf, sizeof(buf), "%d", 4 * 1024 * 1024);
863*3e78bf84SChristian Brauner if (write_file("/sys/block/zram0/disksize", buf))
864*3e78bf84SChristian Brauner goto undo;
865*3e78bf84SChristian Brauner fd = open("/dev/zram0", O_RDWR);
866*3e78bf84SChristian Brauner if (fd < 0)
867*3e78bf84SChristian Brauner goto undo;
868*3e78bf84SChristian Brauner n = write_fat12_4k(fd, 1024); /* zram has 4 KiB blocks */
869*3e78bf84SChristian Brauner close(fd); /* the reset in undo is refused while the device is open */
870*3e78bf84SChristian Brauner if (n)
871*3e78bf84SChristian Brauner goto undo;
872*3e78bf84SChristian Brauner if (mount_fat("/dev/zram0", HOLDER_BELOW)) {
873*3e78bf84SChristian Brauner n = errno == ENODEV ? CHILD_NOFS : CHILD_HOLDER;
874*3e78bf84SChristian Brauner zram_reset();
875*3e78bf84SChristian Brauner return n;
876*3e78bf84SChristian Brauner }
877*3e78bf84SChristian Brauner return CHILD_OK;
878*3e78bf84SChristian Brauner undo:
879*3e78bf84SChristian Brauner zram_reset();
880*3e78bf84SChristian Brauner return CHILD_HOLDER;
881*3e78bf84SChristian Brauner }
882*3e78bf84SChristian Brauner
883*3e78bf84SChristian Brauner /* Is @alg in /proc/crypto? A module is listed once a request has loaded it. */
crypto_has(const char * alg)884*3e78bf84SChristian Brauner static bool crypto_has(const char *alg)
885*3e78bf84SChristian Brauner {
886*3e78bf84SChristian Brauner char line[256], name[64];
887*3e78bf84SChristian Brauner bool found = false;
888*3e78bf84SChristian Brauner FILE *f;
889*3e78bf84SChristian Brauner
890*3e78bf84SChristian Brauner f = fopen("/proc/crypto", "r");
891*3e78bf84SChristian Brauner if (!f)
892*3e78bf84SChristian Brauner return true; /* no way to tell, assume it is */
893*3e78bf84SChristian Brauner while (!found && fgets(line, sizeof(line), f))
894*3e78bf84SChristian Brauner found = sscanf(line, "name : %63s", name) == 1 && !strcmp(name, alg);
895*3e78bf84SChristian Brauner fclose(f);
896*3e78bf84SChristian Brauner return found;
897*3e78bf84SChristian Brauner }
898*3e78bf84SChristian Brauner
899*3e78bf84SChristian Brauner /* The kernel's auth token layout, which userspace has to match byte for byte. */
900*3e78bf84SChristian Brauner struct ecryptfs_auth_tok {
901*3e78bf84SChristian Brauner __u16 version;
902*3e78bf84SChristian Brauner __u16 token_type;
903*3e78bf84SChristian Brauner __u32 flags;
904*3e78bf84SChristian Brauner struct {
905*3e78bf84SChristian Brauner __u32 flags, encrypted_key_size, decrypted_key_size;
906*3e78bf84SChristian Brauner __u8 encrypted_key[512], decrypted_key[64];
907*3e78bf84SChristian Brauner } session_key;
908*3e78bf84SChristian Brauner __u8 reserved[32];
909*3e78bf84SChristian Brauner struct {
910*3e78bf84SChristian Brauner __u32 password_bytes;
911*3e78bf84SChristian Brauner __s32 hash_algo;
912*3e78bf84SChristian Brauner __u32 hash_iterations, session_key_encryption_key_bytes, flags;
913*3e78bf84SChristian Brauner __u8 session_key_encryption_key[64];
914*3e78bf84SChristian Brauner __u8 signature[17];
915*3e78bf84SChristian Brauner __u8 salt[8];
916*3e78bf84SChristian Brauner } password;
917*3e78bf84SChristian Brauner } __attribute__((packed));
918*3e78bf84SChristian Brauner
919*3e78bf84SChristian Brauner #define ECRYPTFS_SIG "0123456789abcdef"
920*3e78bf84SChristian Brauner
holder_ecryptfs(void)921*3e78bf84SChristian Brauner static int holder_ecryptfs(void)
922*3e78bf84SChristian Brauner {
923*3e78bf84SChristian Brauner struct ecryptfs_auth_tok tok = {
924*3e78bf84SChristian Brauner .version = 0x0004,
925*3e78bf84SChristian Brauner .token_type = 0, /* ECRYPTFS_PASSWORD */
926*3e78bf84SChristian Brauner .password.session_key_encryption_key_bytes = 16,
927*3e78bf84SChristian Brauner .password.flags = 0x02, /* ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET */
928*3e78bf84SChristian Brauner .password.signature = ECRYPTFS_SIG,
929*3e78bf84SChristian Brauner };
930*3e78bf84SChristian Brauner
931*3e78bf84SChristian Brauner /* a session keyring of this child's own, so that the key goes with it */
932*3e78bf84SChristian Brauner if (syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL) < 0)
933*3e78bf84SChristian Brauner return errno == ENOSYS ? CHILD_SKIP : CHILD_HOLDER;
934*3e78bf84SChristian Brauner if (syscall(__NR_add_key, "user", ECRYPTFS_SIG, &tok, sizeof(tok),
935*3e78bf84SChristian Brauner KEY_SPEC_SESSION_KEYRING) < 0)
936*3e78bf84SChristian Brauner return CHILD_HOLDER;
937*3e78bf84SChristian Brauner if (mkdir(at("p/lower"), 0755))
938*3e78bf84SChristian Brauner return CHILD_HOLDER;
939*3e78bf84SChristian Brauner if (mount(at("p/lower"), HOLDER_BELOW, "ecryptfs", 0,
940*3e78bf84SChristian Brauner "ecryptfs_sig=" ECRYPTFS_SIG ",ecryptfs_cipher=aes,ecryptfs_key_bytes=16")) {
941*3e78bf84SChristian Brauner /* EINVAL without the cipher; a module is loaded by the attempt */
942*3e78bf84SChristian Brauner if (errno == ENODEV || (errno == EINVAL && !crypto_has("aes")))
943*3e78bf84SChristian Brauner return CHILD_SKIP;
944*3e78bf84SChristian Brauner return CHILD_HOLDER;
945*3e78bf84SChristian Brauner }
946*3e78bf84SChristian Brauner return CHILD_OK;
947*3e78bf84SChristian Brauner }
948*3e78bf84SChristian Brauner
949*3e78bf84SChristian Brauner /*
950*3e78bf84SChristian Brauner * binfmt_misc instances are per user namespace, so the mount below P is
951*3e78bf84SChristian Brauner * made from a new one, which gets a copy of P.
952*3e78bf84SChristian Brauner */
holder_binfmt_misc(void)953*3e78bf84SChristian Brauner static int holder_binfmt_misc(void)
954*3e78bf84SChristian Brauner {
955*3e78bf84SChristian Brauner static const char interp[] = "#!/bin/true\n";
956*3e78bf84SChristian Brauner char reg[PATH_MAX];
957*3e78bf84SChristian Brauner int out;
958*3e78bf84SChristian Brauner
959*3e78bf84SChristian Brauner /* 'F' opens the interpreter at registration, nothing runs it here */
960*3e78bf84SChristian Brauner out = open(at("p/interp"), O_WRONLY | O_CREAT | O_EXCL, 0755);
961*3e78bf84SChristian Brauner if (out < 0 || write(out, interp, sizeof(interp) - 1) != sizeof(interp) - 1)
962*3e78bf84SChristian Brauner return CHILD_HOLDER;
963*3e78bf84SChristian Brauner close(out);
964*3e78bf84SChristian Brauner
965*3e78bf84SChristian Brauner /* ENOSPC: user.max_user_namespaces is 0, EPERM: an LSM says no */
966*3e78bf84SChristian Brauner if (unshare(CLONE_NEWUSER | CLONE_NEWNS)) {
967*3e78bf84SChristian Brauner if (errno == EINVAL || errno == ENOSPC || errno == EPERM)
968*3e78bf84SChristian Brauner return CHILD_SKIP;
969*3e78bf84SChristian Brauner return CHILD_HOLDER;
970*3e78bf84SChristian Brauner }
971*3e78bf84SChristian Brauner if (write_file("/proc/self/setgroups", "deny") ||
972*3e78bf84SChristian Brauner write_file("/proc/self/uid_map", "0 0 1") ||
973*3e78bf84SChristian Brauner write_file("/proc/self/gid_map", "0 0 1"))
974*3e78bf84SChristian Brauner return CHILD_HOLDER;
975*3e78bf84SChristian Brauner if (mount("binfmt_misc", HOLDER_BELOW, "binfmt_misc", 0, NULL))
976*3e78bf84SChristian Brauner return errno == ENODEV ? CHILD_SKIP : CHILD_HOLDER;
977*3e78bf84SChristian Brauner snprintf(reg, sizeof(reg), ":cycle:E::cyc::%s:F", at("p/interp"));
978*3e78bf84SChristian Brauner if (write_file(at("p/c/register"), reg))
979*3e78bf84SChristian Brauner return CHILD_HOLDER;
980*3e78bf84SChristian Brauner return CHILD_OK;
981*3e78bf84SChristian Brauner }
982*3e78bf84SChristian Brauner
983*3e78bf84SChristian Brauner /*
984*3e78bf84SChristian Brauner * A fuse server that only ever answers FUSE_INIT, with passthrough on, and
985*3e78bf84SChristian Brauner * then registers a file on P as a backing file. The registration alone
986*3e78bf84SChristian Brauner * makes the fuse superblock hold the file.
987*3e78bf84SChristian Brauner */
holder_fuse(void)988*3e78bf84SChristian Brauner static int holder_fuse(void)
989*3e78bf84SChristian Brauner {
990*3e78bf84SChristian Brauner struct fuse_backing_map map = {};
991*3e78bf84SChristian Brauner struct fuse_in_header *ih;
992*3e78bf84SChristian Brauner struct fuse_init_out init = {
993*3e78bf84SChristian Brauner .major = FUSE_KERNEL_VERSION,
994*3e78bf84SChristian Brauner .minor = FUSE_KERNEL_MINOR_VERSION,
995*3e78bf84SChristian Brauner .flags = FUSE_INIT_EXT,
996*3e78bf84SChristian Brauner .flags2 = FUSE_PASSTHROUGH >> 32,
997*3e78bf84SChristian Brauner .max_write = 4096,
998*3e78bf84SChristian Brauner .max_stack_depth = 1,
999*3e78bf84SChristian Brauner };
1000*3e78bf84SChristian Brauner struct fuse_out_header oh = { .len = sizeof(oh) + sizeof(init) };
1001*3e78bf84SChristian Brauner struct iovec iov[2] = { { &oh, sizeof(oh) }, { &init, sizeof(init) } };
1002*3e78bf84SChristian Brauner char opts[64], buf[8192];
1003*3e78bf84SChristian Brauner int ffd, bfd;
1004*3e78bf84SChristian Brauner ssize_t n;
1005*3e78bf84SChristian Brauner
1006*3e78bf84SChristian Brauner bfd = open(at("p/backing"), O_RDWR | O_CREAT | O_EXCL, 0600);
1007*3e78bf84SChristian Brauner if (bfd < 0)
1008*3e78bf84SChristian Brauner return CHILD_HOLDER;
1009*3e78bf84SChristian Brauner ffd = open("/dev/fuse", O_RDWR);
1010*3e78bf84SChristian Brauner if (ffd < 0)
1011*3e78bf84SChristian Brauner return CHILD_SKIP;
1012*3e78bf84SChristian Brauner snprintf(opts, sizeof(opts), "fd=%d,rootmode=40000,user_id=0,group_id=0", ffd);
1013*3e78bf84SChristian Brauner if (mount("fuse", HOLDER_BELOW, "fuse", 0, opts))
1014*3e78bf84SChristian Brauner return errno == ENODEV ? CHILD_SKIP : CHILD_HOLDER;
1015*3e78bf84SChristian Brauner
1016*3e78bf84SChristian Brauner n = read(ffd, buf, sizeof(buf));
1017*3e78bf84SChristian Brauner ih = (void *)buf;
1018*3e78bf84SChristian Brauner if (n < (ssize_t)sizeof(*ih) || ih->opcode != FUSE_INIT)
1019*3e78bf84SChristian Brauner return CHILD_HOLDER;
1020*3e78bf84SChristian Brauner oh.unique = ih->unique;
1021*3e78bf84SChristian Brauner if (writev(ffd, iov, 2) != (ssize_t)oh.len)
1022*3e78bf84SChristian Brauner return CHILD_HOLDER;
1023*3e78bf84SChristian Brauner
1024*3e78bf84SChristian Brauner map.fd = bfd;
1025*3e78bf84SChristian Brauner if (ioctl(ffd, FUSE_DEV_IOC_BACKING_OPEN, &map) < 0) {
1026*3e78bf84SChristian Brauner /* EOPNOTSUPP: no FUSE_PASSTHROUGH, ENOTTY: no such ioctl */
1027*3e78bf84SChristian Brauner if (errno == EPERM || errno == EOPNOTSUPP || errno == ENOTTY)
1028*3e78bf84SChristian Brauner return CHILD_SKIP;
1029*3e78bf84SChristian Brauner return CHILD_HOLDER;
1030*3e78bf84SChristian Brauner }
1031*3e78bf84SChristian Brauner close(bfd); /* the connection keeps its own */
1032*3e78bf84SChristian Brauner return CHILD_OK; /* ffd stays open until the child exits */
1033*3e78bf84SChristian Brauner }
1034*3e78bf84SChristian Brauner
1035*3e78bf84SChristian Brauner /* Wait for a device node the kernel is about to create. */
open_when_there(const char * dev,int flags,int ms)1036*3e78bf84SChristian Brauner static int open_when_there(const char *dev, int flags, int ms)
1037*3e78bf84SChristian Brauner {
1038*3e78bf84SChristian Brauner int fd;
1039*3e78bf84SChristian Brauner
1040*3e78bf84SChristian Brauner for (; ms > 0; ms -= 100) {
1041*3e78bf84SChristian Brauner fd = open(dev, flags);
1042*3e78bf84SChristian Brauner if (fd >= 0)
1043*3e78bf84SChristian Brauner return fd;
1044*3e78bf84SChristian Brauner usleep(100000);
1045*3e78bf84SChristian Brauner }
1046*3e78bf84SChristian Brauner return -1;
1047*3e78bf84SChristian Brauner }
1048*3e78bf84SChristian Brauner
1049*3e78bf84SChristian Brauner /*
1050*3e78bf84SChristian Brauner * zloop keeps every zone file open. One conventional zone is enough for a
1051*3e78bf84SChristian Brauner * FAT image, the sequential one stays empty.
1052*3e78bf84SChristian Brauner */
holder_zloop(void)1053*3e78bf84SChristian Brauner static int holder_zloop(void)
1054*3e78bf84SChristian Brauner {
1055*3e78bf84SChristian Brauner char cmd[PATH_MAX];
1056*3e78bf84SChristian Brauner int fd, ret = CHILD_HOLDER;
1057*3e78bf84SChristian Brauner
1058*3e78bf84SChristian Brauner if (access("/dev/zloop-control", W_OK))
1059*3e78bf84SChristian Brauner return CHILD_SKIP;
1060*3e78bf84SChristian Brauner /* somebody else's device, leave it alone */
1061*3e78bf84SChristian Brauner if (!access("/sys/block/zloop0", F_OK))
1062*3e78bf84SChristian Brauner return CHILD_SKIP;
1063*3e78bf84SChristian Brauner if (mkdir(at("p/zl"), 0755) || mkdir(at("p/zl/0"), 0755))
1064*3e78bf84SChristian Brauner return CHILD_HOLDER;
1065*3e78bf84SChristian Brauner snprintf(cmd, sizeof(cmd),
1066*3e78bf84SChristian Brauner "add id=0,capacity_mb=8,zone_size_mb=4,conv_zones=1,base_dir=%s",
1067*3e78bf84SChristian Brauner at("p/zl"));
1068*3e78bf84SChristian Brauner if (write_file("/dev/zloop-control", cmd))
1069*3e78bf84SChristian Brauner return CHILD_HOLDER;
1070*3e78bf84SChristian Brauner fd = open_when_there("/dev/zloop0", O_RDWR, 5000);
1071*3e78bf84SChristian Brauner if (fd < 0 || write_fat12_4k(fd, 1024)) /* 4 KiB blocks, like P */
1072*3e78bf84SChristian Brauner goto undo;
1073*3e78bf84SChristian Brauner close(fd);
1074*3e78bf84SChristian Brauner fd = -1;
1075*3e78bf84SChristian Brauner if (mount_fat("/dev/zloop0", HOLDER_BELOW)) {
1076*3e78bf84SChristian Brauner ret = errno == ENODEV ? CHILD_NOFS : CHILD_HOLDER;
1077*3e78bf84SChristian Brauner goto undo;
1078*3e78bf84SChristian Brauner }
1079*3e78bf84SChristian Brauner return CHILD_OK;
1080*3e78bf84SChristian Brauner undo:
1081*3e78bf84SChristian Brauner if (fd >= 0)
1082*3e78bf84SChristian Brauner close(fd);
1083*3e78bf84SChristian Brauner write_file("/dev/zloop-control", "remove id=0");
1084*3e78bf84SChristian Brauner return ret;
1085*3e78bf84SChristian Brauner }
1086*3e78bf84SChristian Brauner
mount_configfs(void)1087*3e78bf84SChristian Brauner static int mount_configfs(void)
1088*3e78bf84SChristian Brauner {
1089*3e78bf84SChristian Brauner if (access("/sys/kernel/config", F_OK))
1090*3e78bf84SChristian Brauner return -1;
1091*3e78bf84SChristian Brauner if (mount("configfs", "/sys/kernel/config", "configfs", 0, NULL) && errno != EBUSY)
1092*3e78bf84SChristian Brauner return -1;
1093*3e78bf84SChristian Brauner return 0;
1094*3e78bf84SChristian Brauner }
1095*3e78bf84SChristian Brauner
1096*3e78bf84SChristian Brauner /* Take the gadget out of configfs again, in the reverse order of its creation. */
gadget_remove(void)1097*3e78bf84SChristian Brauner static void gadget_remove(void)
1098*3e78bf84SChristian Brauner {
1099*3e78bf84SChristian Brauner if (mount_configfs())
1100*3e78bf84SChristian Brauner return;
1101*3e78bf84SChristian Brauner write_file(gat("/functions/mass_storage.0/lun.0/file"), "\n");
1102*3e78bf84SChristian Brauner write_file(gat("/UDC"), "\n");
1103*3e78bf84SChristian Brauner unlink(gat("/configs/c.1/mass_storage.0"));
1104*3e78bf84SChristian Brauner rmdir(gat("/configs/c.1/strings/0x409"));
1105*3e78bf84SChristian Brauner rmdir(gat("/configs/c.1"));
1106*3e78bf84SChristian Brauner rmdir(gat("/functions/mass_storage.0"));
1107*3e78bf84SChristian Brauner rmdir(gat("/strings/0x409"));
1108*3e78bf84SChristian Brauner rmdir(gat(""));
1109*3e78bf84SChristian Brauner }
1110*3e78bf84SChristian Brauner
1111*3e78bf84SChristian Brauner /* The disk usb-storage created for the gadget, by the LUN's inquiry string. */
find_gadget_disk(char * dev,size_t len,int ms)1112*3e78bf84SChristian Brauner static int find_gadget_disk(char *dev, size_t len, int ms)
1113*3e78bf84SChristian Brauner {
1114*3e78bf84SChristian Brauner char path[PATH_MAX], model[64];
1115*3e78bf84SChristian Brauner struct dirent *de;
1116*3e78bf84SChristian Brauner DIR *d;
1117*3e78bf84SChristian Brauner
1118*3e78bf84SChristian Brauner for (; ms > 0; ms -= 100, usleep(100000)) {
1119*3e78bf84SChristian Brauner d = opendir("/sys/block");
1120*3e78bf84SChristian Brauner if (!d)
1121*3e78bf84SChristian Brauner return -1;
1122*3e78bf84SChristian Brauner while ((de = readdir(d))) {
1123*3e78bf84SChristian Brauner if (strncmp(de->d_name, "sd", 2))
1124*3e78bf84SChristian Brauner continue;
1125*3e78bf84SChristian Brauner snprintf(path, sizeof(path), "/sys/block/%s/device/model", de->d_name);
1126*3e78bf84SChristian Brauner if (read_sysfs(path, model, sizeof(model)) ||
1127*3e78bf84SChristian Brauner strncmp(model, "File-Stor Gadget", 16))
1128*3e78bf84SChristian Brauner continue;
1129*3e78bf84SChristian Brauner snprintf(dev, len, "/dev/%s", de->d_name);
1130*3e78bf84SChristian Brauner closedir(d);
1131*3e78bf84SChristian Brauner return 0;
1132*3e78bf84SChristian Brauner }
1133*3e78bf84SChristian Brauner closedir(d);
1134*3e78bf84SChristian Brauner }
1135*3e78bf84SChristian Brauner return -1;
1136*3e78bf84SChristian Brauner }
1137*3e78bf84SChristian Brauner
1138*3e78bf84SChristian Brauner /*
1139*3e78bf84SChristian Brauner * A mass storage gadget bound to the dummy UDC, so that this kernel is
1140*3e78bf84SChristian Brauner * also the USB host that sees the LUN as a SCSI disk. sd locks the
1141*3e78bf84SChristian Brauner * medium on open, which is what keeps the LUN's file from being ejected.
1142*3e78bf84SChristian Brauner */
1143*3e78bf84SChristian Brauner #define GADGET_STEP(x) do { \
1144*3e78bf84SChristian Brauner if (x) { \
1145*3e78bf84SChristian Brauner fprintf(stderr, "gadget: %s failed: %s\n", #x, strerror(errno)); \
1146*3e78bf84SChristian Brauner gadget_remove(); \
1147*3e78bf84SChristian Brauner return CHILD_HOLDER; \
1148*3e78bf84SChristian Brauner } \
1149*3e78bf84SChristian Brauner } while (0)
1150*3e78bf84SChristian Brauner
holder_gadget(void)1151*3e78bf84SChristian Brauner static int holder_gadget(void)
1152*3e78bf84SChristian Brauner {
1153*3e78bf84SChristian Brauner char dev[PATH_MAX];
1154*3e78bf84SChristian Brauner int fd;
1155*3e78bf84SChristian Brauner
1156*3e78bf84SChristian Brauner if (mount_configfs())
1157*3e78bf84SChristian Brauner return CHILD_SKIP;
1158*3e78bf84SChristian Brauner if (access("/sys/kernel/config/usb_gadget", F_OK) ||
1159*3e78bf84SChristian Brauner access("/sys/class/udc/dummy_udc.0", F_OK))
1160*3e78bf84SChristian Brauner return CHILD_SKIP;
1161*3e78bf84SChristian Brauner
1162*3e78bf84SChristian Brauner fd = open(at("p/lun.img"), O_RDWR | O_CREAT | O_EXCL, 0600);
1163*3e78bf84SChristian Brauner if (fd < 0 || write_fat12(fd))
1164*3e78bf84SChristian Brauner return CHILD_HOLDER;
1165*3e78bf84SChristian Brauner close(fd);
1166*3e78bf84SChristian Brauner
1167*3e78bf84SChristian Brauner GADGET_STEP(mkdir(gat(""), 0755));
1168*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/idVendor"), "0x1d6b"));
1169*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/idProduct"), "0x0104"));
1170*3e78bf84SChristian Brauner GADGET_STEP(mkdir(gat("/strings/0x409"), 0755));
1171*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/strings/0x409/serialnumber"), "1"));
1172*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/strings/0x409/manufacturer"), "kselftest"));
1173*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/strings/0x409/product"), "cycle"));
1174*3e78bf84SChristian Brauner GADGET_STEP(mkdir(gat("/configs/c.1"), 0755));
1175*3e78bf84SChristian Brauner GADGET_STEP(mkdir(gat("/configs/c.1/strings/0x409"), 0755));
1176*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/configs/c.1/strings/0x409/configuration"), "c"));
1177*3e78bf84SChristian Brauner GADGET_STEP(mkdir(gat("/functions/mass_storage.0"), 0755));
1178*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/functions/mass_storage.0/lun.0/removable"), "1"));
1179*3e78bf84SChristian Brauner GADGET_STEP(write_file(gat("/functions/mass_storage.0/lun.0/file"), at("p/lun.img")));
1180*3e78bf84SChristian Brauner GADGET_STEP(symlink(gat("/functions/mass_storage.0"), gat("/configs/c.1/mass_storage.0")));
1181*3e78bf84SChristian Brauner /* EBUSY: the controller is somebody else's, leave it alone */
1182*3e78bf84SChristian Brauner if (write_file(gat("/UDC"), "dummy_udc.0")) {
1183*3e78bf84SChristian Brauner fd = errno == EBUSY ? CHILD_SKIP : CHILD_HOLDER;
1184*3e78bf84SChristian Brauner gadget_remove();
1185*3e78bf84SChristian Brauner return fd;
1186*3e78bf84SChristian Brauner }
1187*3e78bf84SChristian Brauner
1188*3e78bf84SChristian Brauner /* usb-storage waits a second before it scans the device */
1189*3e78bf84SChristian Brauner if (find_gadget_disk(dev, sizeof(dev), 15000)) {
1190*3e78bf84SChristian Brauner /* without usb-storage and sd the LUN never shows up as a disk */
1191*3e78bf84SChristian Brauner fd = (access("/sys/bus/usb/drivers/usb-storage", F_OK) ||
1192*3e78bf84SChristian Brauner access("/sys/bus/scsi/drivers/sd", F_OK)) ? CHILD_SKIP : CHILD_HOLDER;
1193*3e78bf84SChristian Brauner gadget_remove();
1194*3e78bf84SChristian Brauner return fd;
1195*3e78bf84SChristian Brauner }
1196*3e78bf84SChristian Brauner fd = open_when_there(dev, O_RDONLY, 5000);
1197*3e78bf84SChristian Brauner GADGET_STEP(fd < 0);
1198*3e78bf84SChristian Brauner close(fd);
1199*3e78bf84SChristian Brauner if (mount_fat(dev, HOLDER_BELOW)) {
1200*3e78bf84SChristian Brauner fd = errno == ENODEV ? CHILD_NOFS : CHILD_HOLDER;
1201*3e78bf84SChristian Brauner gadget_remove();
1202*3e78bf84SChristian Brauner return fd;
1203*3e78bf84SChristian Brauner }
1204*3e78bf84SChristian Brauner return CHILD_OK;
1205*3e78bf84SChristian Brauner }
1206*3e78bf84SChristian Brauner
1207*3e78bf84SChristian Brauner #define BITMAP_MAGIC 0x6d746962
1208*3e78bf84SChristian Brauner
1209*3e78bf84SChristian Brauner /*
1210*3e78bf84SChristian Brauner * A RAID1 of one loop device, not persistent, with its bitmap in a file
1211*3e78bf84SChristian Brauner * on P. The bitmap file needs a superblock the kernel accepts; sync_size,
1212*3e78bf84SChristian Brauner * uuid and events are not looked at for a non-persistent array.
1213*3e78bf84SChristian Brauner */
holder_md(void)1214*3e78bf84SChristian Brauner static int holder_md(void)
1215*3e78bf84SChristian Brauner {
1216*3e78bf84SChristian Brauner struct {
1217*3e78bf84SChristian Brauner __u32 magic, version;
1218*3e78bf84SChristian Brauner __u8 uuid[16];
1219*3e78bf84SChristian Brauner __u64 events, events_cleared, sync_size;
1220*3e78bf84SChristian Brauner __u32 state, chunksize, daemon_sleep, write_behind;
1221*3e78bf84SChristian Brauner } bsb = {
1222*3e78bf84SChristian Brauner .magic = BITMAP_MAGIC,
1223*3e78bf84SChristian Brauner .version = 4,
1224*3e78bf84SChristian Brauner .chunksize = 64 * 1024,
1225*3e78bf84SChristian Brauner .daemon_sleep = 5,
1226*3e78bf84SChristian Brauner };
1227*3e78bf84SChristian Brauner mdu_array_info_t info = {
1228*3e78bf84SChristian Brauner .level = 1,
1229*3e78bf84SChristian Brauner .raid_disks = 1,
1230*3e78bf84SChristian Brauner .size = 8 * 1024, /* KiB */
1231*3e78bf84SChristian Brauner .not_persistent = 1,
1232*3e78bf84SChristian Brauner };
1233*3e78bf84SChristian Brauner mdu_disk_info_t disk = {
1234*3e78bf84SChristian Brauner .major = 7,
1235*3e78bf84SChristian Brauner .state = (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC),
1236*3e78bf84SChristian Brauner };
1237*3e78bf84SChristian Brauner int fd, mdfd, bfd, n, ret = CHILD_HOLDER;
1238*3e78bf84SChristian Brauner char dev[32], buf[4096];
1239*3e78bf84SChristian Brauner struct stat st;
1240*3e78bf84SChristian Brauner
1241*3e78bf84SChristian Brauner fd = open(at("img/md.img"), O_RDWR | O_CREAT | O_EXCL, 0600);
1242*3e78bf84SChristian Brauner if (fd < 0 || ftruncate(fd, 8 * 1024 * 1024))
1243*3e78bf84SChristian Brauner return CHILD_HOLDER;
1244*3e78bf84SChristian Brauner close(fd);
1245*3e78bf84SChristian Brauner n = loop_attach(at("img/md.img"));
1246*3e78bf84SChristian Brauner if (n < 0)
1247*3e78bf84SChristian Brauner return CHILD_HOLDER;
1248*3e78bf84SChristian Brauner /* the minor is not the number of the device when loop has partitions */
1249*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", n);
1250*3e78bf84SChristian Brauner if (stat(dev, &st))
1251*3e78bf84SChristian Brauner return CHILD_HOLDER;
1252*3e78bf84SChristian Brauner disk.major = major(st.st_rdev);
1253*3e78bf84SChristian Brauner disk.minor = minor(st.st_rdev);
1254*3e78bf84SChristian Brauner
1255*3e78bf84SChristian Brauner bfd = open(at("p/bitmap"), O_RDWR | O_CREAT | O_EXCL, 0600);
1256*3e78bf84SChristian Brauner if (bfd < 0 || write(bfd, &bsb, sizeof(bsb)) != sizeof(bsb) || ftruncate(bfd, 4096))
1257*3e78bf84SChristian Brauner return CHILD_HOLDER;
1258*3e78bf84SChristian Brauner
1259*3e78bf84SChristian Brauner if (read_sysfs("/proc/mdstat", buf, sizeof(buf))) {
1260*3e78bf84SChristian Brauner fprintf(stderr, "md: no /proc/mdstat\n");
1261*3e78bf84SChristian Brauner return CHILD_SKIP;
1262*3e78bf84SChristian Brauner }
1263*3e78bf84SChristian Brauner if (!strstr(buf, "[raid1]")) {
1264*3e78bf84SChristian Brauner fprintf(stderr, "md: no raid1 personality: %s\n", buf);
1265*3e78bf84SChristian Brauner return CHILD_SKIP;
1266*3e78bf84SChristian Brauner }
1267*3e78bf84SChristian Brauner /* a node of our own: /dev may not have one and is not ours to change */
1268*3e78bf84SChristian Brauner if (mknod(at("img/md0"), S_IFBLK | 0600, makedev(MD_MAJOR, 0)))
1269*3e78bf84SChristian Brauner return CHILD_HOLDER;
1270*3e78bf84SChristian Brauner mdfd = open(at("img/md0"), O_RDWR);
1271*3e78bf84SChristian Brauner if (mdfd < 0)
1272*3e78bf84SChristian Brauner return CHILD_SKIP;
1273*3e78bf84SChristian Brauner /* somebody else's array: SET_ARRAY_INFO would say EINVAL, not EBUSY */
1274*3e78bf84SChristian Brauner if (!read_sysfs("/sys/block/md0/md/array_state", buf, sizeof(buf)) &&
1275*3e78bf84SChristian Brauner strncmp(buf, "clear", 5)) {
1276*3e78bf84SChristian Brauner fprintf(stderr, "md: md0 is in use: %s", buf);
1277*3e78bf84SChristian Brauner close(mdfd);
1278*3e78bf84SChristian Brauner return CHILD_SKIP;
1279*3e78bf84SChristian Brauner }
1280*3e78bf84SChristian Brauner /* the bitmap ops are only installed once a bitmap type is chosen */
1281*3e78bf84SChristian Brauner write_file("/sys/block/md0/md/bitmap_type", "bitmap");
1282*3e78bf84SChristian Brauner /* EBUSY: somebody else's array, leave it alone */
1283*3e78bf84SChristian Brauner if (ioctl(mdfd, SET_ARRAY_INFO, &info))
1284*3e78bf84SChristian Brauner return errno == EBUSY ? CHILD_SKIP : CHILD_HOLDER;
1285*3e78bf84SChristian Brauner /* the array is ours from here on and has to be stopped on every path */
1286*3e78bf84SChristian Brauner if (ioctl(mdfd, ADD_NEW_DISK, &disk))
1287*3e78bf84SChristian Brauner goto undo;
1288*3e78bf84SChristian Brauner /* attach the bitmap file before the array runs, the way mdadm does */
1289*3e78bf84SChristian Brauner if (ioctl(mdfd, SET_BITMAP_FILE, bfd)) {
1290*3e78bf84SChristian Brauner fprintf(stderr, "md: SET_BITMAP_FILE: %s\n", strerror(errno));
1291*3e78bf84SChristian Brauner if (errno == EINVAL)
1292*3e78bf84SChristian Brauner ret = CHILD_SKIP;
1293*3e78bf84SChristian Brauner goto undo;
1294*3e78bf84SChristian Brauner }
1295*3e78bf84SChristian Brauner close(bfd); /* the array keeps its own */
1296*3e78bf84SChristian Brauner if (ioctl(mdfd, RUN_ARRAY, NULL)) {
1297*3e78bf84SChristian Brauner fprintf(stderr, "md: RUN_ARRAY: %s\n", strerror(errno));
1298*3e78bf84SChristian Brauner goto undo;
1299*3e78bf84SChristian Brauner }
1300*3e78bf84SChristian Brauner if (write_fat12(mdfd))
1301*3e78bf84SChristian Brauner goto undo;
1302*3e78bf84SChristian Brauner close(mdfd);
1303*3e78bf84SChristian Brauner if (mount_fat(at("img/md0"), HOLDER_BELOW)) {
1304*3e78bf84SChristian Brauner ret = errno == ENODEV ? CHILD_NOFS : CHILD_HOLDER;
1305*3e78bf84SChristian Brauner mdfd = open(at("img/md0"), O_RDWR);
1306*3e78bf84SChristian Brauner goto undo;
1307*3e78bf84SChristian Brauner }
1308*3e78bf84SChristian Brauner return CHILD_OK;
1309*3e78bf84SChristian Brauner undo:
1310*3e78bf84SChristian Brauner if (mdfd >= 0) {
1311*3e78bf84SChristian Brauner ioctl(mdfd, STOP_ARRAY);
1312*3e78bf84SChristian Brauner close(mdfd);
1313*3e78bf84SChristian Brauner }
1314*3e78bf84SChristian Brauner return ret;
1315*3e78bf84SChristian Brauner }
1316*3e78bf84SChristian Brauner
1317*3e78bf84SChristian Brauner /*
1318*3e78bf84SChristian Brauner * A device keeps its file for as long as it is configured, so once nothing
1319*3e78bf84SChristian Brauner * below the dead mount is left the device has to be told to let go. With
1320*3e78bf84SChristian Brauner * the cycle unbroken C's filesystem still holds the device and every one
1321*3e78bf84SChristian Brauner * of these refuses.
1322*3e78bf84SChristian Brauner */
holder_let_go_once(enum holder holder)1323*3e78bf84SChristian Brauner static int holder_let_go_once(enum holder holder)
1324*3e78bf84SChristian Brauner {
1325*3e78bf84SChristian Brauner int fd, ret;
1326*3e78bf84SChristian Brauner
1327*3e78bf84SChristian Brauner switch (holder) {
1328*3e78bf84SChristian Brauner case HOLDER_ZRAM:
1329*3e78bf84SChristian Brauner return write_file("/sys/block/zram0/reset", "1");
1330*3e78bf84SChristian Brauner case HOLDER_ZLOOP:
1331*3e78bf84SChristian Brauner return write_file("/dev/zloop-control", "remove id=0");
1332*3e78bf84SChristian Brauner case HOLDER_GADGET:
1333*3e78bf84SChristian Brauner if (mount_configfs())
1334*3e78bf84SChristian Brauner return -1;
1335*3e78bf84SChristian Brauner /* a zero-length write is a no-op for configfs; a newline ejects */
1336*3e78bf84SChristian Brauner return write_file(gat("/functions/mass_storage.0/lun.0/file"), "\n");
1337*3e78bf84SChristian Brauner case HOLDER_MD:
1338*3e78bf84SChristian Brauner fd = open(at("img/md0"), O_RDONLY);
1339*3e78bf84SChristian Brauner if (fd < 0) {
1340*3e78bf84SChristian Brauner /* the child's node went with its tmpfs */
1341*3e78bf84SChristian Brauner mknod(at("md0"), S_IFBLK | 0600, makedev(MD_MAJOR, 0));
1342*3e78bf84SChristian Brauner fd = open(at("md0"), O_RDONLY);
1343*3e78bf84SChristian Brauner }
1344*3e78bf84SChristian Brauner if (fd < 0)
1345*3e78bf84SChristian Brauner return -1;
1346*3e78bf84SChristian Brauner ret = ioctl(fd, STOP_ARRAY);
1347*3e78bf84SChristian Brauner close(fd);
1348*3e78bf84SChristian Brauner return ret;
1349*3e78bf84SChristian Brauner default:
1350*3e78bf84SChristian Brauner return 0;
1351*3e78bf84SChristian Brauner }
1352*3e78bf84SChristian Brauner }
1353*3e78bf84SChristian Brauner
zram_reset(void)1354*3e78bf84SChristian Brauner static void zram_reset(void)
1355*3e78bf84SChristian Brauner {
1356*3e78bf84SChristian Brauner holder_let_go_once(HOLDER_ZRAM);
1357*3e78bf84SChristian Brauner }
1358*3e78bf84SChristian Brauner
1359*3e78bf84SChristian Brauner /*
1360*3e78bf84SChristian Brauner * In its own mount namespace the child puts the image on a tmpfs next to
1361*3e78bf84SChristian Brauner * P, mounts P from a loop device, and sets the holder up with its own
1362*3e78bf84SChristian Brauner * filesystem or device mounted at C below P.
1363*3e78bf84SChristian Brauner */
holder_child(int to_parent,int from_parent,enum holder holder)1364*3e78bf84SChristian Brauner static int holder_child(int to_parent, int from_parent, enum holder holder)
1365*3e78bf84SChristian Brauner {
1366*3e78bf84SChristian Brauner bool fifo = holder == HOLDER_AUTOFS || holder == HOLDER_ZRAM;
1367*3e78bf84SChristian Brauner /* room for a 4 MiB zone file or a floppy image on P */
1368*3e78bf84SChristian Brauner bool big = holder == HOLDER_ZLOOP || holder == HOLDER_GADGET;
1369*3e78bf84SChristian Brauner struct report r = { { -1, -1 } };
1370*3e78bf84SChristian Brauner char dev[32], c;
1371*3e78bf84SChristian Brauner int ifd, n, ret;
1372*3e78bf84SChristian Brauner
1373*3e78bf84SChristian Brauner if (unshare(CLONE_NEWNS) || mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL))
1374*3e78bf84SChristian Brauner return CHILD_NS;
1375*3e78bf84SChristian Brauner if (mkdir(at("img"), 0755) || mount("tmpfs", at("img"), "tmpfs", 0, NULL))
1376*3e78bf84SChristian Brauner return child_fails(CHILD_NS);
1377*3e78bf84SChristian Brauner
1378*3e78bf84SChristian Brauner ifd = open(HOLDER_IMG, O_RDWR | O_CREAT | O_EXCL, 0600);
1379*3e78bf84SChristian Brauner if (ifd < 0 || (fifo ? write_minix(ifd) :
1380*3e78bf84SChristian Brauner big ? write_fat12_4k(ifd, 3072) : write_fat12(ifd)))
1381*3e78bf84SChristian Brauner return child_fails(CHILD_IMAGE);
1382*3e78bf84SChristian Brauner close(ifd);
1383*3e78bf84SChristian Brauner n = loop_attach(HOLDER_IMG);
1384*3e78bf84SChristian Brauner if (n < 0)
1385*3e78bf84SChristian Brauner return child_fails(CHILD_LOOP);
1386*3e78bf84SChristian Brauner snprintf(dev, sizeof(dev), "/dev/loop%d", n);
1387*3e78bf84SChristian Brauner if (mkdir(HOLDER_MNT, 0755))
1388*3e78bf84SChristian Brauner return child_fails(CHILD_MOUNT);
1389*3e78bf84SChristian Brauner if (fifo ? mount(dev, HOLDER_MNT, "minix", 0, NULL) : mount_fat(dev, HOLDER_MNT))
1390*3e78bf84SChristian Brauner return child_fails(errno == ENODEV ? CHILD_NOFS : CHILD_MOUNT);
1391*3e78bf84SChristian Brauner if (mkdir(HOLDER_BELOW, 0755))
1392*3e78bf84SChristian Brauner return child_fails(CHILD_MOUNT);
1393*3e78bf84SChristian Brauner
1394*3e78bf84SChristian Brauner switch (holder) {
1395*3e78bf84SChristian Brauner case HOLDER_AUTOFS:
1396*3e78bf84SChristian Brauner ret = holder_autofs();
1397*3e78bf84SChristian Brauner break;
1398*3e78bf84SChristian Brauner case HOLDER_ZRAM:
1399*3e78bf84SChristian Brauner ret = holder_zram();
1400*3e78bf84SChristian Brauner break;
1401*3e78bf84SChristian Brauner case HOLDER_ECRYPTFS:
1402*3e78bf84SChristian Brauner ret = holder_ecryptfs();
1403*3e78bf84SChristian Brauner break;
1404*3e78bf84SChristian Brauner case HOLDER_BINFMT_MISC:
1405*3e78bf84SChristian Brauner ret = holder_binfmt_misc();
1406*3e78bf84SChristian Brauner break;
1407*3e78bf84SChristian Brauner case HOLDER_FUSE:
1408*3e78bf84SChristian Brauner ret = holder_fuse();
1409*3e78bf84SChristian Brauner break;
1410*3e78bf84SChristian Brauner case HOLDER_ZLOOP:
1411*3e78bf84SChristian Brauner ret = holder_zloop();
1412*3e78bf84SChristian Brauner break;
1413*3e78bf84SChristian Brauner case HOLDER_GADGET:
1414*3e78bf84SChristian Brauner ret = holder_gadget();
1415*3e78bf84SChristian Brauner break;
1416*3e78bf84SChristian Brauner case HOLDER_MD:
1417*3e78bf84SChristian Brauner ret = holder_md();
1418*3e78bf84SChristian Brauner break;
1419*3e78bf84SChristian Brauner default:
1420*3e78bf84SChristian Brauner ret = CHILD_HOLDER;
1421*3e78bf84SChristian Brauner }
1422*3e78bf84SChristian Brauner if (ret != CHILD_OK)
1423*3e78bf84SChristian Brauner return child_fails(ret);
1424*3e78bf84SChristian Brauner
1425*3e78bf84SChristian Brauner /* P's device first, then the one the holder has bound for itself */
1426*3e78bf84SChristian Brauner r.n[0] = n;
1427*3e78bf84SChristian Brauner if (nr_bound > 1)
1428*3e78bf84SChristian Brauner r.n[1] = bound[1];
1429*3e78bf84SChristian Brauner if (write(to_parent, &r, sizeof(r)) != sizeof(r) ||
1430*3e78bf84SChristian Brauner read(from_parent, &c, 1) != 1) {
1431*3e78bf84SChristian Brauner /* the parent went away and will not tell the devices to let go */
1432*3e78bf84SChristian Brauner umount2(HOLDER_BELOW, MNT_DETACH);
1433*3e78bf84SChristian Brauner for (int i = 0; i < 50 && holder_let_go_once(holder); i++)
1434*3e78bf84SChristian Brauner usleep(100000);
1435*3e78bf84SChristian Brauner if (holder == HOLDER_GADGET)
1436*3e78bf84SChristian Brauner gadget_remove();
1437*3e78bf84SChristian Brauner return child_fails(CHILD_PIPE);
1438*3e78bf84SChristian Brauner }
1439*3e78bf84SChristian Brauner return CHILD_OK;
1440*3e78bf84SChristian Brauner }
1441*3e78bf84SChristian Brauner
1442*3e78bf84SChristian Brauner /*
1443*3e78bf84SChristian Brauner * The release of C's filesystem may still be in flight when the child is
1444*3e78bf84SChristian Brauner * gone, and a device that is still held refuses to let go, so try for a
1445*3e78bf84SChristian Brauner * while. With the cycle unbroken it refuses for good.
1446*3e78bf84SChristian Brauner */
holder_let_go(enum holder holder)1447*3e78bf84SChristian Brauner static void holder_let_go(enum holder holder)
1448*3e78bf84SChristian Brauner {
1449*3e78bf84SChristian Brauner for (int i = 0; i < 50; i++) {
1450*3e78bf84SChristian Brauner if (!holder_let_go_once(holder))
1451*3e78bf84SChristian Brauner return;
1452*3e78bf84SChristian Brauner usleep(100000);
1453*3e78bf84SChristian Brauner }
1454*3e78bf84SChristian Brauner }
1455*3e78bf84SChristian Brauner
1456*3e78bf84SChristian Brauner /*
1457*3e78bf84SChristian Brauner * rmdir of p from here, where it is a plain directory, detaches
1458*3e78bf84SChristian Brauner * P in the child's namespace with C connected below it. Once the child is
1459*3e78bf84SChristian Brauner * gone the holder's file on P is the only thing left that refers to P,
1460*3e78bf84SChristian Brauner * and it is dropped only when C's filesystem dies, which waits for P.
1461*3e78bf84SChristian Brauner * The loop device backing P tells whether that resolved.
1462*3e78bf84SChristian Brauner */
holder_cycle(struct __test_metadata * _metadata,FIXTURE_DATA (loop_cycle)* self,enum holder holder)1463*3e78bf84SChristian Brauner static void holder_cycle(struct __test_metadata *_metadata,
1464*3e78bf84SChristian Brauner FIXTURE_DATA(loop_cycle) *self, enum holder holder)
1465*3e78bf84SChristian Brauner {
1466*3e78bf84SChristian Brauner struct report r = { { -1, -1 } };
1467*3e78bf84SChristian Brauner int to_parent[2], from_parent[2];
1468*3e78bf84SChristian Brauner pid_t pid;
1469*3e78bf84SChristian Brauner
1470*3e78bf84SChristian Brauner ASSERT_EQ(pipe(to_parent), 0);
1471*3e78bf84SChristian Brauner ASSERT_EQ(pipe(from_parent), 0);
1472*3e78bf84SChristian Brauner pid = fork();
1473*3e78bf84SChristian Brauner ASSERT_GE(pid, 0);
1474*3e78bf84SChristian Brauner if (pid == 0) {
1475*3e78bf84SChristian Brauner close(to_parent[0]);
1476*3e78bf84SChristian Brauner close(from_parent[1]);
1477*3e78bf84SChristian Brauner _exit(holder_child(to_parent[1], from_parent[0], holder));
1478*3e78bf84SChristian Brauner }
1479*3e78bf84SChristian Brauner close(to_parent[1]);
1480*3e78bf84SChristian Brauner close(from_parent[0]);
1481*3e78bf84SChristian Brauner
1482*3e78bf84SChristian Brauner if (read(to_parent[0], &r, sizeof(r)) != sizeof(r))
1483*3e78bf84SChristian Brauner CHILD_GAVE_UP(pid);
1484*3e78bf84SChristian Brauner self->gadget = holder == HOLDER_GADGET;
1485*3e78bf84SChristian Brauner remember_loop(self, r.n[0]);
1486*3e78bf84SChristian Brauner remember_loop(self, r.n[1]);
1487*3e78bf84SChristian Brauner snprintf(self->dev, sizeof(self->dev), "/dev/loop%d", r.n[0]);
1488*3e78bf84SChristian Brauner snprintf(self->sysfs, sizeof(self->sysfs), "/sys/block/loop%d/loop/backing_file", r.n[0]);
1489*3e78bf84SChristian Brauner
1490*3e78bf84SChristian Brauner ASSERT_EQ(rmdir(HOLDER_MNT), 0);
1491*3e78bf84SChristian Brauner ASSERT_EQ(write(from_parent[1], "x", 1), 1);
1492*3e78bf84SChristian Brauner CHILD_LEFT(pid);
1493*3e78bf84SChristian Brauner close(to_parent[0]);
1494*3e78bf84SChristian Brauner close(from_parent[1]);
1495*3e78bf84SChristian Brauner
1496*3e78bf84SChristian Brauner holder_let_go(holder);
1497*3e78bf84SChristian Brauner assert_loop_released(_metadata, self);
1498*3e78bf84SChristian Brauner /* the teardown takes the gadget and the holder's own loop device away */
1499*3e78bf84SChristian Brauner }
1500*3e78bf84SChristian Brauner
TEST_F(loop_cycle,autofs_pipe_on_dead_mount_released)1501*3e78bf84SChristian Brauner TEST_F(loop_cycle, autofs_pipe_on_dead_mount_released)
1502*3e78bf84SChristian Brauner {
1503*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_AUTOFS);
1504*3e78bf84SChristian Brauner }
1505*3e78bf84SChristian Brauner
TEST_F(loop_cycle,zram_writeback_node_on_dead_mount_released)1506*3e78bf84SChristian Brauner TEST_F(loop_cycle, zram_writeback_node_on_dead_mount_released)
1507*3e78bf84SChristian Brauner {
1508*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_ZRAM);
1509*3e78bf84SChristian Brauner }
1510*3e78bf84SChristian Brauner
TEST_F(loop_cycle,ecryptfs_lower_on_dead_mount_released)1511*3e78bf84SChristian Brauner TEST_F(loop_cycle, ecryptfs_lower_on_dead_mount_released)
1512*3e78bf84SChristian Brauner {
1513*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_ECRYPTFS);
1514*3e78bf84SChristian Brauner }
1515*3e78bf84SChristian Brauner
TEST_F(loop_cycle,binfmt_misc_interpreter_on_dead_mount_released)1516*3e78bf84SChristian Brauner TEST_F(loop_cycle, binfmt_misc_interpreter_on_dead_mount_released)
1517*3e78bf84SChristian Brauner {
1518*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_BINFMT_MISC);
1519*3e78bf84SChristian Brauner }
1520*3e78bf84SChristian Brauner
TEST_F(loop_cycle,fuse_backing_file_on_dead_mount_released)1521*3e78bf84SChristian Brauner TEST_F(loop_cycle, fuse_backing_file_on_dead_mount_released)
1522*3e78bf84SChristian Brauner {
1523*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_FUSE);
1524*3e78bf84SChristian Brauner }
1525*3e78bf84SChristian Brauner
TEST_F(loop_cycle,zloop_zone_files_on_dead_mount_released)1526*3e78bf84SChristian Brauner TEST_F(loop_cycle, zloop_zone_files_on_dead_mount_released)
1527*3e78bf84SChristian Brauner {
1528*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_ZLOOP);
1529*3e78bf84SChristian Brauner }
1530*3e78bf84SChristian Brauner
1531*3e78bf84SChristian Brauner /* up to 15 s for the disk to show up and 12 s for a device that is held */
1532*3e78bf84SChristian Brauner TEST_F_TIMEOUT(loop_cycle, mass_storage_lun_on_dead_mount_released, 120)
1533*3e78bf84SChristian Brauner {
1534*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_GADGET);
1535*3e78bf84SChristian Brauner }
1536*3e78bf84SChristian Brauner
TEST_F(loop_cycle,md_bitmap_file_on_dead_mount_released)1537*3e78bf84SChristian Brauner TEST_F(loop_cycle, md_bitmap_file_on_dead_mount_released)
1538*3e78bf84SChristian Brauner {
1539*3e78bf84SChristian Brauner holder_cycle(_metadata, self, HOLDER_MD);
1540*3e78bf84SChristian Brauner }
1541*3e78bf84SChristian Brauner
1542*3e78bf84SChristian Brauner TEST_HARNESS_MAIN
1543