All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christian Brauner <christian.brauner@ubuntu.com>
To: Eryu Guan <guan@eryu.me>
Cc: Christoph Hellwig <hch@lst.de>,
	"Darrick J . Wong" <djwong@kernel.org>,
	fstests@vger.kernel.org, David Howells <dhowells@redhat.com>
Subject: Re: [PATCH v9 3/4] xfs/529: quotas and idmapped mounts
Date: Mon, 22 Mar 2021 11:11:24 +0100	[thread overview]
Message-ID: <20210322101124.yllhcg3qovi635df@wittgenstein> (raw)
In-Reply-To: <YFdbPtFPdyyZiL/F@desktop>

On Sun, Mar 21, 2021 at 10:42:06PM +0800, Eryu Guan wrote:
> On Tue, Mar 16, 2021 at 11:36:26AM +0100, Christian Brauner wrote:
> > Test that xfs quota behave correctly on idmapped mounts.
> > Mount a scratch device with user and group quota support enabled. Create
> > directories "unmapped" and "idmapped". Create files in the unampped
> > mount and verify quota behavior. Create files through the idmapped mount
> > and verify identical behavior.
> > 
> > Cc: Christoph Hellwig <hch@lst.de>
> > Cc: Darrick J. Wong <djwong@kernel.org>
> > Cc: fstests@vger.kernel.org
> > Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
> > ---
> > /* v1 */
> > patch not present
> > 
> > /* v2 */
> > patch not present
> > 
> > /* v3 */
> > patch not present
> > 
> > /* v4 */
> > patch not present
> > 
> > /* v5 */
> > patch not present
> > 
> > /* v6 */
> > patch not present
> > 
> > /* v7 */
> > patch not present
> > 
> > /* v8 */
> > patch introduced
> > 
> > /* v9 */
> > - Christian Brauner <christian.brauner@ubuntu.com>:
> >   - Rebased on current master.
> > ---
> >  .gitignore                           |   1 +
> >  src/idmapped-mounts/Makefile         |  14 +-
> >  src/idmapped-mounts/mount-idmapped.c | 428 +++++++++++++++++
> >  src/idmapped-mounts/utils.c          |   2 +-
> >  src/idmapped-mounts/utils.h          |   1 +
> >  tests/xfs/529                        | 373 +++++++++++++++
> >  tests/xfs/529.out                    | 657 +++++++++++++++++++++++++++
> >  tests/xfs/group                      |   1 +
> >  8 files changed, 1472 insertions(+), 5 deletions(-)
> >  create mode 100644 src/idmapped-mounts/mount-idmapped.c
> >  create mode 100644 tests/xfs/529
> >  create mode 100644 tests/xfs/529.out
> > 
> > diff --git a/.gitignore b/.gitignore
> > index 3229bb26..4cc9c807 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -179,6 +179,7 @@
> >  /src/aio-dio-regress/aiocp
> >  /src/aio-dio-regress/aiodio_sparse2
> >  /src/idmapped-mounts/idmapped-mounts
> > +/src/idmapped-mounts/mount-idmapped
> >  /src/log-writes/replay-log
> >  /src/perf/*.pyc
> >  
> > diff --git a/src/idmapped-mounts/Makefile b/src/idmapped-mounts/Makefile
> > index 6a934146..40bf9164 100644
> > --- a/src/idmapped-mounts/Makefile
> > +++ b/src/idmapped-mounts/Makefile
> > @@ -3,9 +3,10 @@
> >  TOPDIR = ../..
> >  include $(TOPDIR)/include/builddefs
> >  
> > -TARGETS = idmapped-mounts
> > +TARGETS = idmapped-mounts mount-idmapped
> > +CFILES_IDMAPPED_MOUNTS = idmapped-mounts.c utils.c
> > +CFILES_MOUNT_IDMAPPED = mount-idmapped.c utils.c
> >  
> > -CFILES = idmapped-mounts.c utils.c
> >  HFILES = missing.h utils.h
> >  LLDLIBS += -pthread
> >  LDIRT = $(TARGETS)
> > @@ -24,12 +25,17 @@ depend: .dep
> >  
> >  include $(BUILDRULES)
> >  
> > -$(TARGETS): $(CFILES)
> > +idmapped-mounts:
> >  	@echo "    [CC]    $@"
> > -	$(Q)$(LTLINK) $(CFILES) -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
> > +	$(Q)$(LTLINK) $(CFILES_IDMAPPED_MOUNTS) -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
> > +
> > +mount-idmapped:
> > +	@echo "    [CC]    $@"
> > +	$(Q)$(LTLINK) $(CFILES_MOUNT_IDMAPPED) -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
> >  
> >  install:
> >  	$(INSTALL) -m 755 -d $(PKG_LIB_DIR)/src/idmapped-mounts
> >  	$(INSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/src/idmapped-mounts
> > +	$(INSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/src/mount-idmapped
> >  
> >  -include .dep
> > diff --git a/src/idmapped-mounts/mount-idmapped.c b/src/idmapped-mounts/mount-idmapped.c
> > new file mode 100644
> > index 00000000..f127cdc7
> > --- /dev/null
> > +++ b/src/idmapped-mounts/mount-idmapped.c
> > @@ -0,0 +1,428 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#ifndef _GNU_SOURCE
> > +#define _GNU_SOURCE
> > +#endif
> > +
> > +#include "../global.h"
> > +
> > +#include <dirent.h>
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <getopt.h>
> > +#include <libgen.h>
> > +#include <limits.h>
> > +#include <linux/bpf.h>
> > +#include <linux/sched.h>
> > +#include <linux/seccomp.h>
> > +#include <sched.h>
> > +#include <signal.h>
> > +#include <stdbool.h>
> > +#include <stdint.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <sys/mman.h>
> > +#include <sys/stat.h>
> > +#include <sys/syscall.h>
> > +#include <sys/types.h>
> > +#include <sys/wait.h>
> > +#include <unistd.h>
> > +
> > +#include "missing.h"
> > +#include "utils.h"
> > +
> > +/* A few helpful macros. */
> > +#define STRLITERALLEN(x) (sizeof(""x"") - 1)
> > +
> > +#define INTTYPE_TO_STRLEN(type)             \
> > +	(2 + (sizeof(type) <= 1             \
> > +		  ? 3                       \
> > +		  : sizeof(type) <= 2       \
> > +			? 5                 \
> > +			: sizeof(type) <= 4 \
> > +			      ? 10          \
> > +			      : sizeof(type) <= 8 ? 20 : sizeof(int[-2 * (sizeof(type) > 8)])))
> > +
> > +#define syserror(format, ...)                           \
> > +	({                                              \
> > +		fprintf(stderr, format, ##__VA_ARGS__); \
> > +		(-errno);                               \
> > +	})
> > +
> > +#define syserror_set(__ret__, format, ...)                    \
> > +	({                                                    \
> > +		typeof(__ret__) __internal_ret__ = (__ret__); \
> > +		errno = labs(__ret__);                        \
> > +		fprintf(stderr, format, ##__VA_ARGS__);       \
> > +		__internal_ret__;                             \
> > +	})
> > +
> > +struct list {
> > +	void *elem;
> > +	struct list *next;
> > +	struct list *prev;
> > +};
> > +
> > +#define list_for_each(__iterator, __list) \
> > +	for (__iterator = (__list)->next; __iterator != __list; __iterator = __iterator->next)
> > +
> > +static inline void list_init(struct list *list)
> > +{
> > +	list->elem = NULL;
> > +	list->next = list->prev = list;
> > +}
> > +
> > +static inline int list_empty(const struct list *list)
> > +{
> > +	return list == list->next;
> > +}
> > +
> > +static inline void __list_add(struct list *new, struct list *prev, struct list *next)
> > +{
> > +	next->prev = new;
> > +	new->next = next;
> > +	new->prev = prev;
> > +	prev->next = new;
> > +}
> > +
> > +static inline void list_add_tail(struct list *head, struct list *list)
> > +{
> > +	__list_add(list, head->prev, head);
> > +}
> > +
> > +typedef enum idmap_type_t {
> > +	ID_TYPE_UID,
> > +	ID_TYPE_GID
> > +} idmap_type_t;
> > +
> > +struct id_map {
> > +	idmap_type_t map_type;
> > +	__u32 nsid;
> > +	__u32 hostid;
> > +	__u32 range;
> > +};
> > +
> > +static struct list active_map;
> > +
> > +static int add_map_entry(__u32 id_host,
> > +			 __u32 id_ns,
> > +			 __u32 range,
> > +			 idmap_type_t map_type)
> > +{
> > +	struct list *new_list = NULL;
> > +	struct id_map *newmap = NULL;
> > +
> > +	newmap = malloc(sizeof(*newmap));
> > +	if (!newmap)
> > +		return -ENOMEM;
> > +
> > +	new_list = malloc(sizeof(struct list));
> > +	if (!new_list) {
> > +		free(newmap);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	*newmap = (struct id_map){
> > +		.hostid		= id_host,
> > +		.nsid		= id_ns,
> > +		.range		= range,
> > +		.map_type	= map_type,
> > +	};
> > +
> > +	new_list->elem = newmap;
> > +	list_add_tail(&active_map, new_list);
> > +	return 0;
> > +}
> > +
> > +static int parse_map(char *map)
> > +{
> > +	char types[2] = {'u', 'g'};
> > +	int ret;
> > +	__u32 id_host, id_ns, range;
> > +	char which;
> > +
> > +	if (!map)
> > +		return -1;
> > +
> > +	ret = sscanf(map, "%c:%u:%u:%u", &which, &id_ns, &id_host, &range);
> > +	if (ret != 4)
> > +		return -1;
> > +
> > +	if (which != 'b' && which != 'u' && which != 'g')
> > +		return -1;
> > +
> > +	for (int i = 0; i < 2; i++) {
> > +		idmap_type_t map_type;
> > +
> > +		if (which != types[i] && which != 'b')
> > +			continue;
> > +
> > +		if (types[i] == 'u')
> > +			map_type = ID_TYPE_UID;
> > +		else
> > +			map_type = ID_TYPE_GID;
> > +
> > +		ret = add_map_entry(id_host, id_ns, range, map_type);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int write_id_mapping(idmap_type_t map_type, pid_t pid, const char *buf, size_t buf_size)
> > +{
> > +	int fd = -EBADF, setgroups_fd = -EBADF;
> > +	int fret = -1;
> > +	int ret;
> > +	char path[STRLITERALLEN("/proc") + INTTYPE_TO_STRLEN(pid_t) +
> > +		  STRLITERALLEN("/setgroups") + 1];
> > +
> > +	if (geteuid() != 0 && map_type == ID_TYPE_GID) {
> > +		ret = snprintf(path, sizeof(path), "/proc/%d/setgroups", pid);
> > +		if (ret < 0 || ret >= sizeof(path))
> > +			goto out;
> > +
> > +		setgroups_fd = open(path, O_WRONLY | O_CLOEXEC);
> > +		if (setgroups_fd < 0 && errno != ENOENT) {
> > +			syserror("Failed to open \"%s\"", path);
> > +			goto out;
> > +		}
> > +
> > +		if (setgroups_fd >= 0) {
> > +			ret = write_nointr(setgroups_fd, "deny\n", STRLITERALLEN("deny\n"));
> > +			if (ret != STRLITERALLEN("deny\n")) {
> > +				syserror("Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid);
> > +				goto out;
> > +			}
> > +		}
> > +	}
> > +
> > +	ret = snprintf(path, sizeof(path), "/proc/%d/%cid_map", pid, map_type == ID_TYPE_UID ? 'u' : 'g');
> > +	if (ret < 0 || ret >= sizeof(path))
> > +		goto out;
> > +
> > +	fd = open(path, O_WRONLY | O_CLOEXEC);
> > +	if (fd < 0) {
> > +		syserror("Failed to open \"%s\"", path);
> > +		goto out;
> > +	}
> > +
> > +	ret = write_nointr(fd, buf, buf_size);
> > +	if (ret != buf_size) {
> > +		syserror("Failed to write %cid mapping to \"%s\"",
> > +			 map_type == ID_TYPE_UID ? 'u' : 'g', path);
> > +		goto out;
> > +	}
> > +
> > +	fret = 0;
> > +out:
> > +	if (fd >= 0)
> > +		close(fd);
> > +	if (setgroups_fd >= 0)
> > +		close(setgroups_fd);
> > +
> > +	return fret;
> > +}
> > +
> > +static int map_ids_from_idmap(struct list *idmap, pid_t pid)
> > +{
> > +	int fill, left;
> > +	char mapbuf[4096] = {};
> > +	bool had_entry = false;
> > +
> > +	for (idmap_type_t map_type = ID_TYPE_UID, u_or_g = 'u';
> > +	     map_type <= ID_TYPE_GID; map_type++, u_or_g = 'g') {
> > +		char *pos = mapbuf;
> > +		int ret;
> > +		struct list *iterator;
> > +
> > +
> > +		list_for_each(iterator, idmap) {
> > +			struct id_map *map = iterator->elem;
> > +			if (map->map_type != map_type)
> > +				continue;
> > +
> > +			had_entry = true;
> > +
> > +			left = 4096 - (pos - mapbuf);
> > +			fill = snprintf(pos, left, "%u %u %u\n", map->nsid, map->hostid, map->range);
> > +			/*
> > +			 * The kernel only takes <= 4k for writes to
> > +			 * /proc/<pid>/{g,u}id_map
> > +			 */
> > +			if (fill <= 0 || fill >= left)
> > +				return syserror_set(-E2BIG, "Too many %cid mappings defined", u_or_g);
> > +
> > +			pos += fill;
> > +		}
> > +		if (!had_entry)
> > +			continue;
> > +
> > +		ret = write_id_mapping(map_type, pid, mapbuf, pos - mapbuf);
> > +		if (ret < 0)
> > +			return syserror("Failed to write mapping: %s", mapbuf);
> > +
> > +		memset(mapbuf, 0, sizeof(mapbuf));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int get_userns_fd_from_idmap(struct list *idmap)
> > +{
> > +	int ret;
> > +	pid_t pid;
> > +	char path_ns[STRLITERALLEN("/proc") + INTTYPE_TO_STRLEN(pid_t) +
> > +		  STRLITERALLEN("/ns/user") + 1];
> > +
> > +	pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER | CLONE_NEWNS);
> > +	if (pid < 0)
> > +		return -errno;
> > +
> > +	ret = map_ids_from_idmap(idmap, pid);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = snprintf(path_ns, sizeof(path_ns), "/proc/%d/ns/user", pid);
> > +	if (ret < 0 || (size_t)ret >= sizeof(path_ns))
> > +		ret = -EIO;
> > +	else
> > +		ret = open(path_ns, O_RDONLY | O_CLOEXEC | O_NOCTTY);
> > +
> > +	(void)kill(pid, SIGKILL);
> > +	(void)wait_for_pid(pid);
> > +	return ret;
> > +}
> > +
> > +static inline bool strnequal(const char *str, const char *eq, size_t len)
> > +{
> > +	return strncmp(str, eq, len) == 0;
> > +}
> > +
> > +static void usage(void)
> > +{
> > +	const char *text = "\
> > +mount-idmapped --map-mount=<idmap> <source> <target>\n\
> > +\n\
> > +Create an idmapped mount of <source> at <target>\n\
> > +Options:\n\
> > +  --map-mount=<idmap>\n\
> > +	Specify an idmap for the <target> mount in the format\n\
> > +	<idmap-type>:<id-from>:<id-to>:<id-range>\n\
> > +	The <idmap-type> can be:\n\
> > +	\"b\" or \"both\"	-> map both uids and gids\n\
> > +	\"u\" or \"uid\"	-> map uids\n\
> > +	\"g\" or \"gid\"	-> map gids\n\
> > +	For example, specifying:\n\
> > +	both:1000:1001:1	-> map uid and gid 1000 to uid and gid 1001 in <target> and no other ids\n\
> > +	uid:20000:100000:1000	-> map uid 20000 to uid 100000, uid 20001 to uid 100001 [...] in <target>\n\
> > +	Currently up to 340 separate idmappings may be specified.\n\n\
> > +  --map-mount=/proc/<pid>/ns/user\n\
> > +	Specify a path to a user namespace whose idmap is to be used.\n\n\
> > +  --recursive\n\
> > +	Copy the whole mount tree from <source> and apply the idmap to everyone at <target>.\n\n\
> > +Examples:\n\
> > +  - Create an idmapped mount of /source on /target with both ('b') uids and gids mapped:\n\
> > +	mount-idmapped --map-mount b:0:10000:10000 /source /target\n\n\
> > +  - Create an idmapped mount of /source on /target with uids ('u') and gids ('g') mapped separately:\n\
> > +	mount-idmapped --map-mount u:0:10000:10000 g:0:20000:20000 /source /target\n\n\
> > +";
> > +	fprintf(stderr, "%s", text);
> > +	_exit(EXIT_SUCCESS);
> > +}
> > +
> > +#define exit_usage(format, ...)                         \
> > +	({                                              \
> > +		fprintf(stderr, format, ##__VA_ARGS__); \
> > +		usage();                                \
> > +	})
> > +
> > +#define exit_log(format, ...)                           \
> > +	({                                              \
> > +		fprintf(stderr, format, ##__VA_ARGS__); \
> > +		exit(EXIT_FAILURE);                     \
> > +	})
> > +
> > +static const struct option longopts[] = {
> > +	{"map-mount",	required_argument,	0,	'a'},
> > +	{"help",	no_argument,		0,	'c'},
> > +	{"recursive",	no_argument,		0,	'd'},
> > +	{ NULL,		0,			0,	0  },
> > +};
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +	int fd_userns = -EBADF;
> > +	int index = 0;
> > +	const char *source = NULL, *target = NULL;
> > +	bool recursive = false;
> > +	int fd_tree, new_argc, ret;
> > +	char *const *new_argv;
> > +
> > +	list_init(&active_map);
> > +	while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
> > +		switch (ret) {
> > +		case 'a':
> > +			if (strnequal(optarg, "/proc", STRLITERALLEN("/proc/"))) {
> > +				fd_userns = open(optarg, O_RDONLY | O_CLOEXEC);
> > +				if (fd_userns < 0)
> > +					exit_log("%m - Failed top open user namespace path %s\n", optarg);
> > +				break;
> > +			}
> > +
> > +			ret = parse_map(optarg);
> > +			if (ret < 0)
> > +				exit_log("Failed to parse idmaps for mount\n");
> > +			break;
> > +		case 'd':
> > +			recursive = true;
> > +			break;
> > +		case 'c':
> > +			/* fallthrough */
> > +		default:
> > +			usage();
> > +		}
> > +	}
> > +
> > +	new_argv = &argv[optind];
> > +	new_argc = argc - optind;
> > +	if (new_argc < 2)
> > +		exit_usage("Missing source or target mountpoint\n\n");
> > +	source = new_argv[0];
> > +	target = new_argv[1];
> > +
> > +	fd_tree = sys_open_tree(-EBADF, source,
> > +			        OPEN_TREE_CLONE |
> > +			        OPEN_TREE_CLOEXEC |
> > +			        AT_EMPTY_PATH |
> > +			        (recursive ? AT_RECURSIVE : 0));
> > +	if (fd_tree < 0) {
> > +		exit_log("%m - Failed to open %s\n", source);
> > +		exit(EXIT_FAILURE);
> > +	}
> > +
> > +	if (!list_empty(&active_map)) {
> > +		struct mount_attr attr = {
> > +			.attr_set = MOUNT_ATTR_IDMAP,
> > +		};
> > +
> > +		attr.userns_fd = get_userns_fd_from_idmap(&active_map);
> > +		if (attr.userns_fd < 0)
> > +			exit_log("%m - Failed to create user namespace\n");
> > +
> > +		ret = sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH | AT_RECURSIVE,
> > +					&attr, sizeof(attr));
> > +		if (ret < 0)
> > +			exit_log("%m - Failed to change mount attributes\n");
> > +		close(attr.userns_fd);
> > +	}
> > +
> > +	ret = sys_move_mount(fd_tree, "", -EBADF, target,
> > +			     MOVE_MOUNT_F_EMPTY_PATH);
> > +	if (ret < 0)
> > +		exit_log("%m - Failed to attach mount to %s\n", target);
> > +	close(fd_tree);
> > +
> > +	exit(EXIT_SUCCESS);
> > +}
> > diff --git a/src/idmapped-mounts/utils.c b/src/idmapped-mounts/utils.c
> > index b27ba445..977443f1 100644
> > --- a/src/idmapped-mounts/utils.c
> > +++ b/src/idmapped-mounts/utils.c
> > @@ -88,7 +88,7 @@ pid_t do_clone(int (*fn)(void *), void *arg, int flags)
> >  #endif
> >  }
> >  
> > -static int get_userns_fd_cb(void *data)
> > +int get_userns_fd_cb(void *data)
> >  {
> >  	return kill(getpid(), SIGSTOP);
> >  }
> > diff --git a/src/idmapped-mounts/utils.h b/src/idmapped-mounts/utils.h
> > index 93425731..efbf3bc3 100644
> > --- a/src/idmapped-mounts/utils.h
> > +++ b/src/idmapped-mounts/utils.h
> > @@ -20,6 +20,7 @@
> >  #include "missing.h"
> >  
> >  extern pid_t do_clone(int (*fn)(void *), void *arg, int flags);
> > +extern int get_userns_fd_cb(void *data);
> >  extern int get_userns_fd(unsigned long nsid, unsigned long hostid,
> >  			 unsigned long range);
> >  extern ssize_t read_nointr(int fd, void *buf, size_t count);
> > diff --git a/tests/xfs/529 b/tests/xfs/529
> > new file mode 100644
> > index 00000000..67251818
> > --- /dev/null
> > +++ b/tests/xfs/529
> > @@ -0,0 +1,373 @@
> > +#! /bin/bash
> > +# SPDX-License-Identifier: GPL-2.0+
> > +#
> > +# Copyright (c) 2021 Christian Brauner <christian.brauner@ubuntu.com>
> > +# All Rights Reserved.
> > +#
> > +# FS QA Test No. 529
> > +#
> > +# Exercise basic xfs_quota functionality (user/group/project quota)
> > +# Use of "sync" mount option here is an attempt to get deterministic
> > +# allocator behaviour.
> > +#
> > +seq=`basename $0`
> > +seqres=$RESULT_DIR/$seq
> > +echo "QA output created by $seq"
> > +
> > +here=`pwd`
> > +tmp=/tmp/$$
> > +status=1	# failure is the default!
> > +trap "_cleanup; exit \$status" 0 1 2 3 15
> > +
> > +_cleanup()
> > +{
> > +	cd /
> > +	rm -f $tmp.*
> 
> Seems we should do wipe_mounts in _cleanup as well, in case test exits
> in the middle of the test with idmapped mount still mounted.

added

> 
> > +}
> > +
> > +# get standard environment, filters and checks
> > +. ./common/rc
> > +. ./common/filter
> > +. ./common/quota
> > +
> > +# remove previous $seqres.full before test
> > +rm -f $seqres.full
> > +
> > +# real QA test starts here
> > +_supported_fs xfs
> > +_require_idmapped_mounts
> > +_require_scratch
> > +_require_xfs_quota
> > +_require_user fsgqa
> > +_require_user fsgqa2
> > +_require_group fsgqa
> > +_require_group fsgqa2
> > +
> > +_scratch_mkfs_xfs >>$seqres.full 2>&1
> > +
> > +uqid=`id -u fsgqa`
> > +gqid=`id -g fsgqa`
> > +
> > +uqid2=`id -u fsgqa2`
> > +gqid2=`id -g fsgqa2`
> > +
> > +pqid=10
> > +cat >$tmp.projects <<EOF
> > +$pqid:$SCRATCH_MNT
> > +EOF
> > +
> > +cat >$tmp.projid <<EOF
> > +root:0
> > +fsgqa:$pqid
> > +EOF
> > +
> > +create_files_unmapped()
> > +{
> > +	local bs=$1
> > +	local inum=$2
> > +
> > +	echo "Using type=$type id=$id" >> $seqres.full
> > +
> > +	for ((i=0; i<$((inum-1)); i++)); do
> > +		_file_as_id $SCRATCH_MNT/unmapped/inode$i $id $type 1024 0
> > +	done
> > +
> > +	_file_as_id $SCRATCH_MNT/unmapped/block $id $type $bs 1
> > +}
> > +
> > +create_files_idmapped()
> > +{
> > +	local bs=$1
> > +	local inum=$2
> > +
> > +	echo "Using type=$type id=$id2" >> $seqres.full
> > +
> > +	for ((i=0; i<$((inum-1)); i++)); do
> > +		_file_as_id $SCRATCH_MNT/idmapped/inode$i $id2 $type 1024 0
> > +	done
> > +
> > +	_file_as_id $SCRATCH_MNT/idmapped/block $id2 $type $bs 1
> > +}
> > +
> > +clean_files()
> > +{
> > +	rm -rf $SCRATCH_MNT/unmapped 2>/dev/null
> > +	rm -rf $SCRATCH_MNT/idmapped 2>/dev/null
> > +	rm -rf $tmp.quot 2>/dev/null
> > +	rm -rf $tmp.quota 2>/dev/null
> > +}
> > +
> > +filter_quot()
> > +{
> > +	_filter_quota | grep -v "root \|\#0 " \
> > +		| sed -e '/#[0-9]*/s/#[0-9]*/#ID/g'
> > +}
> > +
> > +filter_report()
> > +{
> > +	_filter_quota | grep -v "^root \|^\#0 " \
> > +		| sed -e '/^#[0-9]*/s/^#[0-9]*/#ID/g'
> > +}
> > +
> > +filter_quota()
> > +{
> > +	_filter_quota | sed -e "/Disk quotas for/s/([0-9]*)/(ID)/g" \
> > +			    -e "/Disk quotas for/s/#[0-9]*/#ID/g"
> > +}
> > +
> > +filter_state()
> > +{
> > +	_filter_quota | sed -e "s/Inode: #[0-9]* (0 blocks, 0 extents)/Inode: #[INO] (0 blocks, 0 extents)/g" \
> > +			    -e "s/Inode: #[0-9]* ([0-9]* blocks, [0-9]* extents)/Inode: #[INO] (X blocks, Y extents)/g" \
> > +			    -e "/[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/s/ [0-9][0-9]:[0-9][0-9]:[0-9][0-9]//g" \
> > +			    -e '/max warnings:/d'
> > +}
> > +
> > +test_quot()
> > +{
> > +	local opt="$*"
> > +
> > +	echo "checking quot command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "quot -$type $opt -bi" $SCRATCH_MNT | filter_quot
> > +}
> > +
> > +test_report()
> > +{
> > +	local opt="$*"
> > +
> > +	echo "checking report command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "report -$type $opt -bi" \
> > +			$SCRATCH_MNT | filter_report
> > +}
> > +
> > +test_quota()
> > +{
> > +	local opt="$*"
> > +
> > +	echo "checking quota command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "quota -$type $opt -bi $id" \
> > +			$SCRATCH_MNT | filter_quota
> > +}
> > +
> > +test_limit()
> > +{
> > +	local bs=$1
> > +	local bh=$2
> > +	local is=$3
> > +	local ih=$4
> > +
> > +	echo "checking limit command (type=$type, bsoft=$bs, bhard=$bh, isoft=$is, ihard=$ih)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "limit -$type bsoft=$bs bhard=$bh fsgqa" \
> > +			-c "limit -$type isoft=$is ihard=$ih fsgqa" \
> > +			$SCRATCH_MNT
> > +
> > +	# let the timer day transition happen
> > +	sleep 2
> > +}
> > +
> > +test_timer()
> > +{
> > +	echo "checking timer command (type=$type)"
> > +	# set 3days+1h for time won't become 2days soon
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "timer -$type -bi 73h" \
> > +			$SCRATCH_MNT | _filter_scratch
> > +}
> > +
> > +test_disable()
> > +{
> > +	echo "checking disable command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "disable -$type -v" \
> > +			$SCRATCH_MNT | filter_state
> > +}
> > +
> > +test_enable()
> > +{
> > +	echo "checking enable command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "enable -$type -v" $SCRATCH_MNT | filter_state
> > +}
> > +
> > +test_off()
> > +{
> > +	echo "checking off command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "off -$type -v" $SCRATCH_MNT | _filter_scratch
> > +}
> > +
> > +test_remove()
> > +{
> > +	echo "checking remove command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "remove -$type -v" \
> > +			$SCRATCH_MNT | _filter_scratch
> > +}
> > +
> > +test_state()
> > +{
> > +	echo "checking state command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "state -$type" $SCRATCH_MNT | filter_state
> > +}
> > +
> > +test_dump()
> > +{
> > +	echo "checking dump command (type=$type)"
> > +	rm -f $tmp.backup 2>>/dev/null
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "dump -$type -f $tmp.backup" \
> > +			$SCRATCH_MNT | _filter_scratch
> > +}
> > +
> > +test_restore()
> > +{
> > +	echo "checking restore command (type=$type)"
> > +	$XFS_QUOTA_PROG -D $tmp.projects -P $tmp.projid -x \
> > +			-c "restore -$type -f $tmp.backup" \
> > +			$SCRATCH_MNT | _filter_scratch
> > +}
> > +
> > +_wipe_mounts()
> 
> Local functions don't need the leading "_" in function name.

Ok, removed the leading "_" from _wipe_mounts() and _wipe_scratch().

> 
> > +{
> > +	umount -l -q "${SCRATCH_MNT}/idmapped" >/dev/null 2>&1
> 
> $UMOUNT_PROG, and you already dump stderr and stdout to /dev/null, -q
> seems not necessary.

Removed "-q" option.

> 
> > +	_scratch_unmount >/dev/null 2>&1
> > +}
> > +
> > +_wipe_scratch()
> > +{
> > +	_wipe_mounts
> > +	_scratch_mkfs_xfs >>$seqres.full 2>&1
> > +}
> > +
> > +_qmount_idmapped()
> > +{
> > +	_wipe_mounts
> > +	_try_scratch_mount || _fail "qmount failed"
> > +
> > +	mkdir -p "${SCRATCH_MNT}/unmapped"
> > +	mkdir -p "${SCRATCH_MNT}/idmapped"
> > +
> > +	$here/src/idmapped-mounts/mount-idmapped --map-mount b:$id:$id2:1 --map-mount b:0:0:1 "$SCRATCH_MNT/unmapped" "$SCRATCH_MNT/idmapped" || _fail "mount-idmapped failed"
> > +
> > +	chmod ugo+rwx $SCRATCH_MNT
> > +	chmod ugo+rwx $SCRATCH_MNT/unmapped
> > +	chmod ugo+rwx $SCRATCH_MNT/idmapped
> > +}
> > +
> > +test_xfs_quota()
> > +{
> > +	# init quota
> > +	echo "init quota limit and timer, and dump it"
> > +	if [ "$idmapped" -eq 1 ]; then
> > +		echo "create_files_idmapped 1024k 15"; create_files_idmapped 1024k 15
> > +	else
> > +		echo "create_files_unmapped 1024k 15"; create_files_unmapped 1024k 15
> > +	fi
> > +	echo "quota remount"; _qmount_idmapped
> > +	echo ; test_quot
> > +	echo ; test_timer
> > +	echo ; test_limit 512k 2048k 10 20
> > +	echo ; test_dump
> > +
> > +	# report options test
> > +	echo "report options test"
> > +	echo ; test_report
> > +	echo "-N option"; test_report -N
> > +	echo "-L -U options"; test_report -L $id -U $id
> > +	echo "-t option"; test_report -t
> > +	echo "-n option"; test_report -n
> > +	echo "-h option"; test_report -h
> > +
> > +	# quot options test
> > +	echo "quot options test"
> > +	echo ; test_quot
> > +	echo "-f option"; test_quot -f $tmp.quot
> > +	cat $tmp.quot | filter_quot
> > +	echo "-n option"; test_quot -n
> > +
> > +	# quota options test
> > +	echo ; test_quota
> > +	echo "-f option"; test_quota -f $tmp.quota
> > +	cat $tmp.quota | filter_quota
> > +	echo "-N option"; test_quota -N
> > +	echo "-n option"; test_quota -n
> > +	echo "-h option"; test_quota -h
> > +
> > +	# disable/enable test
> > +	echo "disable quota"
> > +	echo ; test_disable
> > +	echo ; test_report -N
> > +	echo "expect a remove error at here"; test_remove
> > +	echo ; test_enable
> > +	echo ; test_report -N
> > +
> > +	# off and remove test
> > +	echo "off and remove test"
> > +	echo ; test_limit 100m 100m 100 100
> > +	echo ; test_quota -N
> > +	echo ; test_off
> > +	echo ; test_state
> > +	echo ; test_remove
> > +	echo ; test_report -N
> > +	echo "quota remount"; _qmount_idmapped
> > +	echo ; test_report -N
> > +
> > +	# restore test
> > +	echo "restore quota"
> > +	echo ; test_restore
> > +	echo ; test_report -N
> > +	echo ; test_state
> > +	echo "cleanup files"; clean_files
> > +}
> > +
> > +echo "----------------------- uquota,sync,unmapped ---------------------------"
> > +_wipe_scratch
> > +_qmount_option "uquota,sync"
> > +type=u
> > +id=$uqid
> > +id2=$uqid2
> > +idmapped=0
> > +_qmount_idmapped
> > +test_xfs_quota
> > +
> > +echo "----------------------- uquota,sync,idmapped ---------------------------"
> > +_wipe_scratch
> > +_qmount_option "uquota,sync"
> > +type=u
> > +id=$uqid
> > +id2=$uqid2
> > +idmapped=1
> > +_qmount_idmapped
> > +test_xfs_quota
> > +
> > +echo "----------------------- gquota,sync,unmapped ---------------------------"
> > +_wipe_scratch
> > +_qmount_option "gquota,sync"
> > +type=g
> > +id=$gqid
> > +id2=$gqid2
> > +idmapped=0
> > +_qmount_idmapped
> > +test_xfs_quota
> > +
> > +echo "----------------------- gquota,sync,idmapped ---------------------------"
> > +_wipe_scratch
> > +_qmount_option "gquota,sync"
> > +type=g
> > +id=$gqid
> > +id2=$gqid2
> > +idmapped=1
> > +_qmount_idmapped
> > +test_xfs_quota
> > +
> > +umount -l -q "${SCRATCH_MNT}/idmapped" >/dev/null 2>&1
> > +_scratch_unmount
> 
> Could use wipe_mounts here?

Yes, added.

Christian

  reply	other threads:[~2021-03-22 10:12 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-16 10:36 [PATCH v9 0/4 REBASED] fstests: add idmapped mounts tests Christian Brauner
2021-03-16 10:36 ` [PATCH v9 1/4] generic/631: add test for detached mount propagation Christian Brauner
2021-03-18  6:23   ` Christoph Hellwig
2021-03-18 15:02     ` Darrick J. Wong
2021-03-18 15:14       ` Christian Brauner
2021-03-18 15:57         ` Darrick J. Wong
2021-03-18 16:31           ` Darrick J. Wong
2021-03-21 14:28   ` Eryu Guan
2021-03-22 10:00     ` Christian Brauner
2021-03-16 10:36 ` [PATCH v9 3/4] xfs/529: quotas and idmapped mounts Christian Brauner
2021-03-18  6:24   ` Christoph Hellwig
2021-03-21 14:42   ` Eryu Guan
2021-03-22 10:11     ` Christian Brauner [this message]
2021-03-16 10:36 ` [PATCH v9 4/4] xfs/530: quotas on " Christian Brauner
2021-03-18  6:24   ` Christoph Hellwig
2021-03-21 14:51   ` Eryu Guan
2021-03-22 12:25     ` Christian Brauner
     [not found] ` <20210316103627.2954121-3-christian.brauner@ubuntu.com>
2021-03-18  6:23   ` [PATCH v9 2/4] generic/632: add fstests for " Christoph Hellwig
2021-03-21 14:26 ` [PATCH v9 0/4 REBASED] fstests: add idmapped mounts tests Eryu Guan
2021-03-21 15:32   ` Christian Brauner
2021-03-22  2:37     ` Eryu Guan
2021-03-22  9:49       ` Christian Brauner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210322101124.yllhcg3qovi635df@wittgenstein \
    --to=christian.brauner@ubuntu.com \
    --cc=dhowells@redhat.com \
    --cc=djwong@kernel.org \
    --cc=fstests@vger.kernel.org \
    --cc=guan@eryu.me \
    --cc=hch@lst.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.