Linux-BTRFS Archive on lore.kernel.org
 help / Atom feed
* [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show"
@ 2018-11-27  5:24 Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 1/8] btrfs-progs: sub list: Use libbtrfsuitl for subvolume list Misono Tomohiro
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs

Hello,

This is basically the resend of 
  "[PATCH v2 00/20] btrfs-progs: Rework of "subvolume list/show" and relax the
	root privileges of them" [1]
which I submitted in June. The aim of this series is to allow non-privileged user
to use basic subvolume functionality (create/list/snapshot/delete; this allows "list")

They were once in devel branch with some whitespace/comment modification by david.
I rebased them to current devel branch.

github: https://github.com/t-msn/btrfs-progs/tree/rework-sub-list

Basic logic/code is the same as before. Some differences are:
 - Use latest libbtrfsutil from Omar [2] (thus drop first part of patches).
   As a result, "sub list" cannot accept an ordinary directry to be
   specified (which is allowed in previous version)
 - Drop patches which add new options to "sub list"
 - Use 'nobody' as non-privileged test user just like libbtrfsutil test
 - Update comments

Importantly, in order to make output consistent for both root and non-privileged
user, this changes the behavior of "subvolume list": 
 - (default) Only list in subvolume under the specified path.
   Path needs to be a subvolume.
 - (-a) filter is dropped. i.e. its output is the same as the
        default behavior of "sub list" in progs <= 4.19

Therefore, existent scripts may need to update to add -a option
(I believe nobody uses current -a option).
If anyone thinks this is not good, please let me know.

Behavior summary from cover letter in [1]
====
* Behavior summary of new "sub list"
  - default (no option)
    - lists subvolumes below the specified path (inc. path itself)
    - If new ioctls exists non-privileged user can call it
        (subvolumes to which the user cannot access will be skipped)

  - -a
    - updated to remove filter. i.e. the output is the same as current progs
      without option (require root privileges)

 [Example]
  $ mkfs.btrfs -f $DEV
  $ mkfs.btrfs -f $DEV2
  $ mount $DEV $MNT

  $ btrfs subvolume create $MNT/AAA
  $ btrfs subvolume create $MNT/BBB
  $ btrfs subvolume create $MNT/CCC
  $ btrfs subvolume create $MNT/DDD
  $ mkdir $MNT/AAA/bbb
  $ mkdir $MNT/AAA/ccc
  $ mkdir $MNT/AAA/other

  $ umount $MNT
  $ mount -o subvol=AAA $DEV $MNT
  $ mount -o subvol=BBB $DEV $MNT/bbb
  $ mount -o subvol=CCC $DEV $MNT/ccc
  $ mount -o $DEV2 $MNT/other

  $ btrfs subvolume list $MNT # print subvolumes below the path
  ID 256 gen 10 top level 5 path .

  $ btrfs subvolume list -a $MNT
  # print all subvolumes in the fs. the same output as progs<=4.19 without option
  ID 256 gen 10 top level 5 path AAA
  ID 258 gen 7 top level 5 path BBB
  ID 259 gen 8 top level 5 path CCC
  ID 260 gen 9 top level 5 path DDD

* Behavior summary of new "sub show"
  - No change for root's output
  - If new ioctls exists, non-privileged user can call it
    - In that case, path to be shown is absolute path
      (for root, it is relative to top-level subvolume)
      Also, snapshots to be shown are to which the user can access from current
			mount point.
      (for root, all snapshots in the fs)
===

[1] https://lore.kernel.org/linux-btrfs/cover.1529310485.git.misono.tomohiro@jp.fujitsu.com/
[2] https://lore.kernel.org/linux-btrfs/cover.1542181521.git.osandov@fb.com/

Thanks,
Misono

Misono Tomohiro (8):
  btrfs-progs: sub list: Use libbtrfsuitl for subvolume list
  btrfs-progs: sub list: factor out main part of btrfs_list_subvols
  btrfs-progs: sub list: Change the default behavior of "subvolume list"
    and allow non-privileged user to call it
  btrfs-progs: sub list: Update -a option and remove meaningless filter
  btrfs-progs: utils: Fallback to open without O_NOATIME flag in
    find_mount_root():
  btrfs-progs: sub show: Allow non-privileged user to call "subvolume
    show"
  btrfs-progs: test: Add helper function to check if test user exists
  btrfs-porgs: test: Add cli-test/009 to check subvolume list for both
    root and normal user

 Documentation/btrfs-subvolume.asciidoc     |   25 +-
 cmds-subvolume.c                           | 1149 +++++++++++++++++++-
 tests/cli-tests/009-subvolume-list/test.sh |  130 +++
 tests/common                               |   10 +
 utils.c                                    |    3 +
 5 files changed, 1266 insertions(+), 51 deletions(-)
 create mode 100755 tests/cli-tests/009-subvolume-list/test.sh

-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/8] btrfs-progs: sub list: Use libbtrfsuitl for subvolume list
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 2/8] btrfs-progs: sub list: factor out main part of btrfs_list_subvols Misono Tomohiro
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

This is a copy of non-merged following patch originally written
by Omar Sandoval:
  btrfs-progs: use libbtrfsutil for subvolume list
expect this commit keeps libbtrfs implementation which above commit
tries to remove (therefore this adds suffix _v2 for struct/function).

Original Author: Omar Sandoval <osandov@fb.com>
Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 cmds-subvolume.c | 963 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 936 insertions(+), 27 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index a8395aac..84a03fd8 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -407,6 +407,915 @@ keep_fd:
 	return ret;
 }
 
+#define BTRFS_LIST_NFILTERS_INCREASE	(2 * BTRFS_LIST_FILTER_MAX)
+#define BTRFS_LIST_NCOMPS_INCREASE	(2 * BTRFS_LIST_COMP_MAX)
+
+struct listed_subvol {
+	struct btrfs_util_subvolume_info info;
+	char *path;
+};
+
+struct subvol_list {
+	size_t num;
+	struct listed_subvol subvols[];
+};
+
+typedef int (*btrfs_list_filter_func_v2)(struct listed_subvol *, uint64_t);
+typedef int (*btrfs_list_comp_func_v2)(const struct listed_subvol *,
+				    const struct listed_subvol *,
+				    int);
+
+struct btrfs_list_filter_v2 {
+	btrfs_list_filter_func_v2 filter_func;
+	u64 data;
+};
+
+struct btrfs_list_comparer_v2 {
+	btrfs_list_comp_func_v2 comp_func;
+	int is_descending;
+};
+
+struct btrfs_list_filter_set_v2 {
+	int total;
+	int nfilters;
+	int only_deleted;
+	struct btrfs_list_filter_v2 filters[0];
+};
+
+struct btrfs_list_comparer_set_v2 {
+	int total;
+	int ncomps;
+	struct btrfs_list_comparer_v2 comps[0];
+};
+
+static struct {
+	char	*name;
+	char	*column_name;
+	int	need_print;
+} btrfs_list_columns[] = {
+	{
+		.name		= "ID",
+		.column_name	= "ID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "gen",
+		.column_name	= "Gen",
+		.need_print	= 0,
+	},
+	{
+		.name		= "cgen",
+		.column_name	= "CGen",
+		.need_print	= 0,
+	},
+	{
+		.name		= "parent",
+		.column_name	= "Parent",
+		.need_print	= 0,
+	},
+	{
+		.name		= "top level",
+		.column_name	= "Top Level",
+		.need_print	= 0,
+	},
+	{
+		.name		= "otime",
+		.column_name	= "OTime",
+		.need_print	= 0,
+	},
+	{
+		.name		= "parent_uuid",
+		.column_name	= "Parent UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "received_uuid",
+		.column_name	= "Received UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "uuid",
+		.column_name	= "UUID",
+		.need_print	= 0,
+	},
+	{
+		.name		= "path",
+		.column_name	= "Path",
+		.need_print	= 0,
+	},
+	{
+		.name		= NULL,
+		.column_name	= NULL,
+		.need_print	= 0,
+	},
+};
+
+static btrfs_list_filter_func_v2 all_filter_funcs[];
+static btrfs_list_comp_func_v2 all_comp_funcs[];
+
+static void btrfs_list_setup_print_column_v2(enum btrfs_list_column_enum column)
+{
+	int i;
+
+	ASSERT(0 <= column && column <= BTRFS_LIST_ALL);
+
+	if (column < BTRFS_LIST_ALL) {
+		btrfs_list_columns[column].need_print = 1;
+		return;
+	}
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++)
+		btrfs_list_columns[i].need_print = 1;
+}
+
+static int comp_entry_with_rootid_v2(const struct listed_subvol *entry1,
+				  const struct listed_subvol *entry2,
+				  int is_descending)
+{
+	int ret;
+
+	if (entry1->info.id > entry2->info.id)
+		ret = 1;
+	else if (entry1->info.id < entry2->info.id)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_gen_v2(const struct listed_subvol *entry1,
+			       const struct listed_subvol *entry2,
+			       int is_descending)
+{
+	int ret;
+
+	if (entry1->info.generation > entry2->info.generation)
+		ret = 1;
+	else if (entry1->info.generation < entry2->info.generation)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_ogen_v2(const struct listed_subvol *entry1,
+				const struct listed_subvol *entry2,
+				int is_descending)
+{
+	int ret;
+
+	if (entry1->info.otransid > entry2->info.otransid)
+		ret = 1;
+	else if (entry1->info.otransid < entry2->info.otransid)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_path_v2(const struct listed_subvol *entry1,
+				const struct listed_subvol *entry2,
+				int is_descending)
+{
+	int ret;
+
+	if (strcmp(entry1->path, entry2->path) > 0)
+		ret = 1;
+	else if (strcmp(entry1->path, entry2->path) < 0)
+		ret = -1;
+	else
+		ret = 0;
+
+	return is_descending ? -ret : ret;
+}
+
+static btrfs_list_comp_func_v2 all_comp_funcs[] = {
+	[BTRFS_LIST_COMP_ROOTID]	= comp_entry_with_rootid_v2,
+	[BTRFS_LIST_COMP_OGEN]		= comp_entry_with_ogen_v2,
+	[BTRFS_LIST_COMP_GEN]		= comp_entry_with_gen_v2,
+	[BTRFS_LIST_COMP_PATH]		= comp_entry_with_path_v2,
+};
+
+static char *all_sort_items[] = {
+	[BTRFS_LIST_COMP_ROOTID]	= "rootid",
+	[BTRFS_LIST_COMP_OGEN]		= "ogen",
+	[BTRFS_LIST_COMP_GEN]		= "gen",
+	[BTRFS_LIST_COMP_PATH]		= "path",
+	[BTRFS_LIST_COMP_MAX]		= NULL,
+};
+
+static int btrfs_list_get_sort_item(char *sort_name)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_COMP_MAX; i++) {
+		if (strcmp(sort_name, all_sort_items[i]) == 0)
+			return i;
+	}
+	return -1;
+}
+
+static struct btrfs_list_comparer_set_v2 *btrfs_list_alloc_comparer_set_v2(void)
+{
+	struct btrfs_list_comparer_set_v2 *set;
+	int size;
+
+	size = sizeof(struct btrfs_list_comparer_set_v2) +
+		BTRFS_LIST_NCOMPS_INCREASE *
+		sizeof(struct btrfs_list_comparer_v2);
+	set = calloc(1, size);
+	if (!set) {
+		fprintf(stderr, "memory allocation failed\n");
+		exit(1);
+	}
+
+	set->total = BTRFS_LIST_NCOMPS_INCREASE;
+
+	return set;
+}
+
+static int btrfs_list_setup_comparer_v2(struct btrfs_list_comparer_set_v2 **comp_set,
+				     enum btrfs_list_comp_enum comparer,
+				     int is_descending)
+{
+	struct btrfs_list_comparer_set_v2 *set = *comp_set;
+	int size;
+
+	ASSERT(set != NULL);
+	ASSERT(comparer < BTRFS_LIST_COMP_MAX);
+	ASSERT(set->ncomps <= set->total);
+
+	if (set->ncomps == set->total) {
+		void *tmp;
+
+		size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
+		size = sizeof(*set) +
+			size * sizeof(struct btrfs_list_comparer_v2);
+		tmp = set;
+		set = realloc(set, size);
+		if (!set) {
+			fprintf(stderr, "memory allocation failed\n");
+			free(tmp);
+			exit(1);
+		}
+
+		memset(&set->comps[set->total], 0,
+		       BTRFS_LIST_NCOMPS_INCREASE *
+		       sizeof(struct btrfs_list_comparer_v2));
+		set->total += BTRFS_LIST_NCOMPS_INCREASE;
+		*comp_set = set;
+	}
+
+	ASSERT(set->comps[set->ncomps].comp_func == NULL);
+
+	set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
+	set->comps[set->ncomps].is_descending = is_descending;
+	set->ncomps++;
+	return 0;
+}
+
+static int subvol_comp(const void *entry1, const void *entry2, void *arg)
+{
+	const struct btrfs_list_comparer_set_v2 * const set = arg;
+	int rootid_compared = 0;
+	int ret;
+	int i;
+
+	for (i = 0; set && i < set->ncomps; i++) {
+		if (!set->comps[i].comp_func)
+			break;
+
+		ret = set->comps[i].comp_func(entry1, entry2,
+					      set->comps[i].is_descending);
+		if (ret)
+			return ret;
+
+		if (set->comps[i].comp_func == comp_entry_with_rootid_v2)
+			rootid_compared = 1;
+	}
+
+	if (!rootid_compared)
+		return comp_entry_with_rootid_v2(entry1, entry2, 0);
+
+	return 0;
+}
+
+static void sort_subvols(struct btrfs_list_comparer_set_v2 *comp_set,
+			 struct subvol_list *subvols)
+{
+	qsort_r(subvols->subvols, subvols->num, sizeof(subvols->subvols[0]),
+		subvol_comp, comp_set);
+}
+
+static int filter_by_rootid(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.id == data;
+}
+
+static int filter_snapshot(struct listed_subvol *subvol, uint64_t data)
+{
+	return !uuid_is_null(subvol->info.parent_uuid);
+}
+
+static int filter_flags(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.flags & data;
+}
+
+static int filter_gen_more(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.generation >= data;
+}
+
+static int filter_gen_less(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.generation <= data;
+}
+
+static int filter_gen_equal(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.generation == data;
+}
+
+static int filter_cgen_more(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.otransid >= data;
+}
+
+static int filter_cgen_less(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.otransid <= data;
+}
+
+static int filter_cgen_equal(struct listed_subvol *subvol, uint64_t data)
+{
+	return subvol->info.otransid == data;
+}
+
+static int filter_topid_equal(struct listed_subvol *subvol, uint64_t data)
+{
+	/* See the comment in print_subvolume_column() about top level. */
+	return subvol->info.parent_id == data;
+}
+
+static int filter_full_path(struct listed_subvol *subvol, uint64_t data)
+{
+	/*
+	 * This implements the same behavior as before the conversion to
+	 * libbtrfsutil, which is mostly nonsensical.
+	 */
+	if (subvol->info.parent_id != data) {
+		char *tmp;
+		int ret;
+
+		ret = asprintf(&tmp, "<FS_TREE>/%s", subvol->path);
+		if (ret == -1) {
+			error("out of memory");
+			exit(1);
+		}
+
+		free(subvol->path);
+		subvol->path = tmp;
+	}
+	return 1;
+}
+
+static int filter_by_parent(struct listed_subvol *subvol, uint64_t data)
+{
+	return !uuid_compare(subvol->info.parent_uuid,
+			     (u8 *)(unsigned long)data);
+}
+
+static btrfs_list_filter_func_v2 all_filter_funcs[] = {
+	[BTRFS_LIST_FILTER_ROOTID]		= filter_by_rootid,
+	[BTRFS_LIST_FILTER_SNAPSHOT_ONLY]	= filter_snapshot,
+	[BTRFS_LIST_FILTER_FLAGS]		= filter_flags,
+	[BTRFS_LIST_FILTER_GEN_MORE]		= filter_gen_more,
+	[BTRFS_LIST_FILTER_GEN_LESS]		= filter_gen_less,
+	[BTRFS_LIST_FILTER_GEN_EQUAL]           = filter_gen_equal,
+	[BTRFS_LIST_FILTER_CGEN_MORE]		= filter_cgen_more,
+	[BTRFS_LIST_FILTER_CGEN_LESS]		= filter_cgen_less,
+	[BTRFS_LIST_FILTER_CGEN_EQUAL]          = filter_cgen_equal,
+	[BTRFS_LIST_FILTER_TOPID_EQUAL]		= filter_topid_equal,
+	[BTRFS_LIST_FILTER_FULL_PATH]		= filter_full_path,
+	[BTRFS_LIST_FILTER_BY_PARENT]		= filter_by_parent,
+};
+
+static struct btrfs_list_filter_set_v2 *btrfs_list_alloc_filter_set_v2(void)
+{
+	struct btrfs_list_filter_set_v2 *set;
+	int size;
+
+	size = sizeof(struct btrfs_list_filter_set_v2) +
+		BTRFS_LIST_NFILTERS_INCREASE *
+		sizeof(struct btrfs_list_filter_v2);
+	set = calloc(1, size);
+	if (!set) {
+		fprintf(stderr, "memory allocation failed\n");
+		exit(1);
+	}
+
+	set->total = BTRFS_LIST_NFILTERS_INCREASE;
+
+	return set;
+}
+
+/*
+ * Setup list filters. Exit if there's not enough memory, as we can't continue
+ * without the structures set up properly.
+ */
+static void btrfs_list_setup_filter_v2(struct btrfs_list_filter_set_v2 **filter_set,
+				    enum btrfs_list_filter_enum filter,
+				    u64 data)
+{
+	struct btrfs_list_filter_set_v2 *set = *filter_set;
+	int size;
+
+	ASSERT(set != NULL);
+	ASSERT(filter < BTRFS_LIST_FILTER_MAX);
+	ASSERT(set->nfilters <= set->total);
+
+	if (set->nfilters == set->total) {
+		void *tmp;
+
+		size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
+		size = sizeof(*set) +
+			size * sizeof(struct btrfs_list_filter_v2);
+		tmp = set;
+		set = realloc(set, size);
+		if (!set) {
+			fprintf(stderr, "memory allocation failed\n");
+			free(tmp);
+			exit(1);
+		}
+
+		memset(&set->filters[set->total], 0,
+		       BTRFS_LIST_NFILTERS_INCREASE *
+		       sizeof(struct btrfs_list_filter_v2));
+		set->total += BTRFS_LIST_NFILTERS_INCREASE;
+		*filter_set = set;
+	}
+
+	ASSERT(set->filters[set->nfilters].filter_func == NULL);
+
+	if (filter == BTRFS_LIST_FILTER_DELETED) {
+		set->only_deleted = 1;
+	} else {
+		set->filters[set->nfilters].filter_func =
+					all_filter_funcs[filter];
+		set->filters[set->nfilters].data = data;
+		set->nfilters++;
+	}
+}
+
+static int filters_match(struct listed_subvol *subvol,
+			 struct btrfs_list_filter_set_v2 *set)
+{
+	int i, ret;
+
+	if (!set)
+		return 1;
+
+	for (i = 0; i < set->nfilters; i++) {
+		if (!set->filters[i].filter_func)
+			break;
+		ret = set->filters[i].filter_func(subvol, set->filters[i].data);
+		if (!ret)
+			return 0;
+	}
+	return 1;
+}
+
+static void print_subvolume_column(struct listed_subvol *subvol,
+				   enum btrfs_list_column_enum column)
+{
+	char tstr[256];
+	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
+
+	ASSERT(0 <= column && column < BTRFS_LIST_ALL);
+
+	switch (column) {
+	case BTRFS_LIST_OBJECTID:
+		printf("%" PRIu64, subvol->info.id);
+		break;
+	case BTRFS_LIST_GENERATION:
+		printf("%" PRIu64, subvol->info.generation);
+		break;
+	case BTRFS_LIST_OGENERATION:
+		printf("%" PRIu64, subvol->info.otransid);
+		break;
+	case BTRFS_LIST_PARENT:
+	/*
+	 * Top level used to mean something else, but since 4f5ebb3ef553
+	 * ("Btrfs-progs: fix to make list specified directory's subvolumes
+	 * work") it was always set to the parent ID. See
+	 * https://www.spinics.net/lists/linux-btrfs/msg69820.html.
+	 */
+	case BTRFS_LIST_TOP_LEVEL:
+		printf("%" PRIu64, subvol->info.parent_id);
+		break;
+	case BTRFS_LIST_OTIME:
+		if (subvol->info.otime.tv_sec) {
+			struct tm tm;
+
+			localtime_r(&subvol->info.otime.tv_sec, &tm);
+			strftime(tstr, sizeof(tstr), "%Y-%m-%d %X", &tm);
+		} else
+			strcpy(tstr, "-");
+		printf("%s", tstr);
+		break;
+	case BTRFS_LIST_UUID:
+		if (uuid_is_null(subvol->info.uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subvol->info.uuid, uuidparse);
+		printf("%-36s", uuidparse);
+		break;
+	case BTRFS_LIST_PUUID:
+		if (uuid_is_null(subvol->info.parent_uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subvol->info.parent_uuid, uuidparse);
+		printf("%-36s", uuidparse);
+		break;
+	case BTRFS_LIST_RUUID:
+		if (uuid_is_null(subvol->info.received_uuid))
+			strcpy(uuidparse, "-");
+		else
+			uuid_unparse(subvol->info.received_uuid, uuidparse);
+		printf("%-36s", uuidparse);
+		break;
+	case BTRFS_LIST_PATH:
+		printf("%s", subvol->path);
+		break;
+	default:
+		break;
+	}
+}
+
+static void print_one_subvol_info_raw(struct listed_subvol *subvol,
+				      const char *raw_prefix)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		if (raw_prefix)
+			printf("%s", raw_prefix);
+
+		print_subvolume_column(subvol, i);
+	}
+	printf("\n");
+}
+
+static void print_one_subvol_info_table(struct listed_subvol *subvol)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		print_subvolume_column(subvol, i);
+
+		if (i != BTRFS_LIST_PATH)
+			printf("\t");
+
+		if (i == BTRFS_LIST_TOP_LEVEL)
+			printf("\t");
+	}
+	printf("\n");
+}
+
+static void print_one_subvol_info_default(struct listed_subvol *subvol)
+{
+	int i;
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		printf("%s ", btrfs_list_columns[i].name);
+		print_subvolume_column(subvol, i);
+
+		if (i != BTRFS_LIST_PATH)
+			printf(" ");
+	}
+	printf("\n");
+}
+
+static void print_all_subvol_info_tab_head(void)
+{
+	int i;
+	int len;
+	char barrier[20];
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (btrfs_list_columns[i].need_print)
+			printf("%s\t", btrfs_list_columns[i].name);
+
+		if (i == BTRFS_LIST_ALL-1)
+			printf("\n");
+	}
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		memset(barrier, 0, sizeof(barrier));
+
+		if (btrfs_list_columns[i].need_print) {
+			len = strlen(btrfs_list_columns[i].name);
+			while (len--)
+				strcat(barrier, "-");
+
+			printf("%s\t", barrier);
+		}
+		if (i == BTRFS_LIST_ALL-1)
+			printf("\n");
+	}
+}
+
+static void print_all_subvol_info(struct subvol_list *subvols,
+				  enum btrfs_list_layout layout,
+				  const char *raw_prefix)
+{
+	size_t i;
+
+	if (layout == BTRFS_LIST_LAYOUT_TABLE)
+		print_all_subvol_info_tab_head();
+
+	for (i = 0; i < subvols->num; i++) {
+		struct listed_subvol *subvol = &subvols->subvols[i];
+
+		switch (layout) {
+		case BTRFS_LIST_LAYOUT_DEFAULT:
+			print_one_subvol_info_default(subvol);
+			break;
+		case BTRFS_LIST_LAYOUT_TABLE:
+			print_one_subvol_info_table(subvol);
+			break;
+		case BTRFS_LIST_LAYOUT_RAW:
+			print_one_subvol_info_raw(subvol, raw_prefix);
+			break;
+		}
+	}
+}
+
+static void free_subvol_list(struct subvol_list *subvols)
+{
+	size_t i;
+
+	if (subvols) {
+		for (i = 0; i < subvols->num; i++)
+			free(subvols->subvols[i].path);
+		free(subvols);
+	}
+}
+
+static struct subvol_list *btrfs_list_deleted_subvols(int fd,
+						      struct btrfs_list_filter_set_v2 *filter_set)
+{
+	struct subvol_list *subvols = NULL;
+	uint64_t *ids = NULL;
+	size_t i, n;
+	enum btrfs_util_error err;
+	int ret = -1;
+
+	err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &n);
+	if (err) {
+		error_btrfs_util(err);
+		return NULL;
+	}
+
+	subvols = malloc(sizeof(*subvols) + n * sizeof(subvols->subvols[0]));
+	if (!subvols) {
+		error("out of memory");
+		goto out;
+	}
+
+	subvols->num = 0;
+	for (i = 0; i < n; i++) {
+		struct listed_subvol *subvol = &subvols->subvols[subvols->num];
+
+		err = btrfs_util_subvolume_info_fd(fd, ids[i], &subvol->info);
+		if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		subvol->path = strdup("DELETED");
+		if (!subvol->path)
+			goto out;
+
+		if (!filters_match(subvol, filter_set)) {
+			free(subvol->path);
+			continue;
+		}
+
+		subvols->num++;
+	}
+
+	ret = 0;
+out:
+	if (ret) {
+		free_subvol_list(subvols);
+		subvols = NULL;
+		free(ids);
+	}
+	return subvols;
+}
+
+static struct subvol_list *btrfs_list_subvols(int fd,
+					      struct btrfs_list_filter_set_v2 *filter_set)
+{
+	struct subvol_list *subvols;
+	size_t capacity = 0;
+	struct btrfs_util_subvolume_iterator *iter;
+	enum btrfs_util_error err;
+	int ret = -1;
+
+	subvols = malloc(sizeof(*subvols));
+	if (!subvols) {
+		error("out of memory");
+		return NULL;
+	}
+	subvols->num = 0;
+
+	err = btrfs_util_create_subvolume_iterator_fd(fd,
+						      BTRFS_FS_TREE_OBJECTID, 0,
+						      &iter);
+	if (err) {
+		iter = NULL;
+		error_btrfs_util(err);
+		goto out;
+	}
+
+	for (;;) {
+		struct listed_subvol subvol;
+
+		err = btrfs_util_subvolume_iterator_next_info(iter,
+							      &subvol.path,
+							      &subvol.info);
+		if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+			break;
+		} else if (err) {
+			error_btrfs_util(err);
+			goto out;
+		}
+
+		if (!filters_match(&subvol, filter_set)) {
+			free(subvol.path);
+			continue;
+		}
+
+		if (subvols->num >= capacity) {
+			struct subvol_list *new_subvols;
+			size_t new_capacity = max_t(size_t, 1, capacity * 2);
+
+			new_subvols = realloc(subvols,
+					      sizeof(*new_subvols) +
+					      new_capacity *
+					      sizeof(new_subvols->subvols[0]));
+			if (!new_subvols) {
+				error("out of memory");
+				goto out;
+			}
+
+			subvols = new_subvols;
+			capacity = new_capacity;
+		}
+
+		subvols->subvols[subvols->num] = subvol;
+		subvols->num++;
+	}
+
+	ret = 0;
+out:
+	if (iter)
+		btrfs_util_destroy_subvolume_iterator(iter);
+	if (ret) {
+		free_subvol_list(subvols);
+		subvols = NULL;
+	}
+	return subvols;
+}
+
+static int btrfs_list_subvols_print_v2(int fd,
+				    struct btrfs_list_filter_set_v2 *filter_set,
+				    struct btrfs_list_comparer_set_v2 *comp_set,
+				    enum btrfs_list_layout layout,
+				    int full_path, const char *raw_prefix)
+{
+	struct subvol_list *subvols;
+
+	/*
+	 * full_path hasn't done anything since 4f5ebb3ef553 ("Btrfs-progs: fix
+	 * to make list specified directory's subvolumes work"). See
+	 * https://www.spinics.net/lists/linux-btrfs/msg69820.html
+	 */
+
+	if (filter_set->only_deleted)
+		subvols = btrfs_list_deleted_subvols(fd, filter_set);
+	else
+		subvols = btrfs_list_subvols(fd, filter_set);
+	if (!subvols)
+		return -1;
+
+	sort_subvols(comp_set, subvols);
+
+	print_all_subvol_info(subvols, layout, raw_prefix);
+
+	free_subvol_list(subvols);
+
+	return 0;
+}
+
+static int btrfs_list_parse_sort_string_v2(char *opt_arg,
+					struct btrfs_list_comparer_set_v2 **comps)
+{
+	int order;
+	int flag;
+	char *p;
+	char **ptr_argv;
+	int what_to_sort;
+
+	while ((p = strtok(opt_arg, ",")) != NULL) {
+		flag = 0;
+		ptr_argv = all_sort_items;
+
+		while (*ptr_argv) {
+			if (strcmp(*ptr_argv, p) == 0) {
+				flag = 1;
+				break;
+			} else {
+				p++;
+				if (strcmp(*ptr_argv, p) == 0) {
+					flag = 1;
+					p--;
+					break;
+				}
+				p--;
+			}
+			ptr_argv++;
+		}
+
+		if (flag == 0)
+			return -1;
+
+		else {
+			if (*p == '+') {
+				order = 0;
+				p++;
+			} else if (*p == '-') {
+				order = 1;
+				p++;
+			} else
+				order = 0;
+
+			what_to_sort = btrfs_list_get_sort_item(p);
+			btrfs_list_setup_comparer_v2(comps,
+						what_to_sort, order);
+		}
+		opt_arg = NULL;
+	}
+
+	return 0;
+}
+
+static int btrfs_list_parse_filter_string_v2(char *opt_arg,
+					  struct btrfs_list_filter_set_v2 **filters,
+					  enum btrfs_list_filter_enum type)
+{
+
+	u64 arg;
+
+	switch (*(opt_arg++)) {
+	case '+':
+		arg = arg_strtou64(opt_arg);
+		type += 2;
+
+		btrfs_list_setup_filter_v2(filters, type, arg);
+		break;
+	case '-':
+		arg = arg_strtou64(opt_arg);
+		type += 1;
+
+		btrfs_list_setup_filter_v2(filters, type, arg);
+		break;
+	default:
+		opt_arg--;
+		arg = arg_strtou64(opt_arg);
+
+		btrfs_list_setup_filter_v2(filters, type, arg);
+		break;
+	}
+
+	return 0;
+}
+
 /*
  * Naming of options:
  * - uppercase for filters and sort options
@@ -454,8 +1363,8 @@ static const char * const cmd_subvol_list_usage[] = {
 
 static int cmd_subvol_list(int argc, char **argv)
 {
-	struct btrfs_list_filter_set *filter_set;
-	struct btrfs_list_comparer_set *comparer_set;
+	struct btrfs_list_filter_set_v2 *filter_set;
+	struct btrfs_list_comparer_set_v2 *comparer_set;
 	u64 flags = 0;
 	int fd = -1;
 	u64 top_id;
@@ -466,8 +1375,8 @@ static int cmd_subvol_list(int argc, char **argv)
 	DIR *dirstream = NULL;
 	enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
 
-	filter_set = btrfs_list_alloc_filter_set();
-	comparer_set = btrfs_list_alloc_comparer_set();
+	filter_set = btrfs_list_alloc_filter_set_v2();
+	comparer_set = btrfs_list_alloc_comparer_set_v2();
 
 	optind = 0;
 	while(1) {
@@ -484,21 +1393,21 @@ static int cmd_subvol_list(int argc, char **argv)
 
 		switch(c) {
 		case 'p':
-			btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_PARENT);
 			break;
 		case 'a':
 			is_list_all = 1;
 			break;
 		case 'c':
-			btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_OGENERATION);
 			break;
 		case 'd':
-			btrfs_list_setup_filter(&filter_set,
+			btrfs_list_setup_filter_v2(&filter_set,
 						BTRFS_LIST_FILTER_DELETED,
 						0);
 			break;
 		case 'g':
-			btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_GENERATION);
 			break;
 		case 'o':
 			is_only_in_path = 1;
@@ -507,27 +1416,27 @@ static int cmd_subvol_list(int argc, char **argv)
 			layout = BTRFS_LIST_LAYOUT_TABLE;
 			break;
 		case 's':
-			btrfs_list_setup_filter(&filter_set,
+			btrfs_list_setup_filter_v2(&filter_set,
 						BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
 						0);
-			btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
-			btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_OGENERATION);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_OTIME);
 			break;
 		case 'u':
-			btrfs_list_setup_print_column(BTRFS_LIST_UUID);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_UUID);
 			break;
 		case 'q':
-			btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_PUUID);
 			break;
 		case 'R':
-			btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_RUUID);
 			break;
 		case 'r':
 			flags |= BTRFS_ROOT_SUBVOL_RDONLY;
 			break;
 		case 'G':
-			btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
-			ret = btrfs_list_parse_filter_string(optarg,
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_GENERATION);
+			ret = btrfs_list_parse_filter_string_v2(optarg,
 							&filter_set,
 							BTRFS_LIST_FILTER_GEN);
 			if (ret) {
@@ -537,8 +1446,8 @@ static int cmd_subvol_list(int argc, char **argv)
 			break;
 
 		case 'C':
-			btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
-			ret = btrfs_list_parse_filter_string(optarg,
+			btrfs_list_setup_print_column_v2(BTRFS_LIST_OGENERATION);
+			ret = btrfs_list_parse_filter_string_v2(optarg,
 							&filter_set,
 							BTRFS_LIST_FILTER_CGEN);
 			if (ret) {
@@ -547,7 +1456,7 @@ static int cmd_subvol_list(int argc, char **argv)
 			}
 			break;
 		case 'S':
-			ret = btrfs_list_parse_sort_string(optarg,
+			ret = btrfs_list_parse_sort_string_v2(optarg,
 							   &comparer_set);
 			if (ret) {
 				uerr = 1;
@@ -575,7 +1484,7 @@ static int cmd_subvol_list(int argc, char **argv)
 	}
 
 	if (flags)
-		btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
+		btrfs_list_setup_filter_v2(&filter_set, BTRFS_LIST_FILTER_FLAGS,
 					flags);
 
 	ret = btrfs_list_get_path_rootid(fd, &top_id);
@@ -583,21 +1492,21 @@ static int cmd_subvol_list(int argc, char **argv)
 		goto out;
 
 	if (is_list_all)
-		btrfs_list_setup_filter(&filter_set,
+		btrfs_list_setup_filter_v2(&filter_set,
 					BTRFS_LIST_FILTER_FULL_PATH,
 					top_id);
 	else if (is_only_in_path)
-		btrfs_list_setup_filter(&filter_set,
+		btrfs_list_setup_filter_v2(&filter_set,
 					BTRFS_LIST_FILTER_TOPID_EQUAL,
 					top_id);
 
 	/* by default we shall print the following columns*/
-	btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
-	btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
-	btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
-	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
+	btrfs_list_setup_print_column_v2(BTRFS_LIST_OBJECTID);
+	btrfs_list_setup_print_column_v2(BTRFS_LIST_GENERATION);
+	btrfs_list_setup_print_column_v2(BTRFS_LIST_TOP_LEVEL);
+	btrfs_list_setup_print_column_v2(BTRFS_LIST_PATH);
 
-	ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
+	ret = btrfs_list_subvols_print_v2(fd, filter_set, comparer_set,
 			layout, !is_list_all && !is_only_in_path, NULL);
 
 out:
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 2/8] btrfs-progs: sub list: factor out main part of btrfs_list_subvols
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 1/8] btrfs-progs: sub list: Use libbtrfsuitl for subvolume list Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 3/8] btrfs-progs: sub list: Change the default behavior of "subvolume list" and allow non-privileged user to call it Misono Tomohiro
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

No functional changes.
This is a preparation work for reworking "subvolume list".

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 cmds-subvolume.c | 50 +++++++++++++++++++++++++++++-------------------
 1 file changed, 30 insertions(+), 20 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 84a03fd8..40cc2687 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1128,22 +1128,15 @@ out:
 	return subvols;
 }
 
-static struct subvol_list *btrfs_list_subvols(int fd,
-					      struct btrfs_list_filter_set_v2 *filter_set)
+static void get_subvols_info(struct subvol_list **subvols,
+			     struct btrfs_list_filter_set_v2 *filter_set,
+			     int fd,
+			     size_t *capacity)
 {
-	struct subvol_list *subvols;
-	size_t capacity = 0;
 	struct btrfs_util_subvolume_iterator *iter;
 	enum btrfs_util_error err;
 	int ret = -1;
 
-	subvols = malloc(sizeof(*subvols));
-	if (!subvols) {
-		error("out of memory");
-		return NULL;
-	}
-	subvols->num = 0;
-
 	err = btrfs_util_create_subvolume_iterator_fd(fd,
 						      BTRFS_FS_TREE_OBJECTID, 0,
 						      &iter);
@@ -1171,11 +1164,11 @@ static struct subvol_list *btrfs_list_subvols(int fd,
 			continue;
 		}
 
-		if (subvols->num >= capacity) {
+		if ((*subvols)->num >= *capacity) {
 			struct subvol_list *new_subvols;
-			size_t new_capacity = max_t(size_t, 1, capacity * 2);
+			size_t new_capacity = max_t(size_t, 1, *capacity * 2);
 
-			new_subvols = realloc(subvols,
+			new_subvols = realloc(*subvols,
 					      sizeof(*new_subvols) +
 					      new_capacity *
 					      sizeof(new_subvols->subvols[0]));
@@ -1184,12 +1177,12 @@ static struct subvol_list *btrfs_list_subvols(int fd,
 				goto out;
 			}
 
-			subvols = new_subvols;
-			capacity = new_capacity;
+			*subvols = new_subvols;
+			*capacity = new_capacity;
 		}
 
-		subvols->subvols[subvols->num] = subvol;
-		subvols->num++;
+		(*subvols)->subvols[(*subvols)->num] = subvol;
+		(*subvols)->num++;
 	}
 
 	ret = 0;
@@ -1197,9 +1190,26 @@ out:
 	if (iter)
 		btrfs_util_destroy_subvolume_iterator(iter);
 	if (ret) {
-		free_subvol_list(subvols);
-		subvols = NULL;
+		free_subvol_list(*subvols);
+		*subvols = NULL;
+	}
+}
+
+static struct subvol_list *btrfs_list_subvols(int fd,
+					      struct btrfs_list_filter_set_v2 *filter_set)
+{
+	struct subvol_list *subvols;
+	size_t capacity = 0;
+
+	subvols = malloc(sizeof(*subvols));
+	if (!subvols) {
+		error("out of memory");
+		return NULL;
 	}
+	subvols->num = 0;
+
+	get_subvols_info(&subvols, filter_set, fd, &capacity);
+
 	return subvols;
 }
 
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 3/8] btrfs-progs: sub list: Change the default behavior of "subvolume list" and allow non-privileged user to call it
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 1/8] btrfs-progs: sub list: Use libbtrfsuitl for subvolume list Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 2/8] btrfs-progs: sub list: factor out main part of btrfs_list_subvols Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 4/8] btrfs-progs: sub list: Update -a option and remove meaningless filter Misono Tomohiro
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Change the default behavior of "subvolume list" and allow non-privileged
user to call it as well.

From this commit, by default it only lists subvolumes under the specified
path (incl. the path itself except top-level subvolume. the path needs
to be a subvolume). Also, if kernel supports new ioctls
(BTRFS_IOC_GET_SUBVOL_INFO/BTRFS_IOC_GET_SUBVOL_ROOTREF and
BTRFS_IOC_INO_LOOKUP_USER, which are avilable from 4.18), non-privileged
user can also use "sub list" command (subvolumes which the user does not
have access right will be skiped).

Note that root user can list all the subvolume in the fs with -a option.

[Example]
 $ mkfs.btrfs -f $DEV
 $ mount $DEV /mnt

 $ btrfs subvolume create /mnt/AAA
 $ btrfs subvolume create /mnt/AAA/BBB
 $ mkdir /mnt/AAA/BBB/dir
 $ btrfs subvolume create /mnt/AAA/BBB/dir/CCC
 $ btrfs subvolume create /mnt/ZZZ

 $ umount /mnt
 $ mount -o subvol=AAA $DEV /mnt

 $ btrfs subvolume list /mnt
 ID 256 gen 11 top level 5 path .
 ID 257 gen 8 top level 256 path BBB
 ID 258 gen 8 top level 257 path BBB/dir/CCC

 $ btrfs subvolume list /mnt/BBB
 ID 257 gen 8 top level 256 path .
 ID 258 gen 8 top level 257 path dir/CCC

 ** output of progs <= 4.19
 $ mount -o subvol=AAA $DEV /mnt
 $ btrfs subvolume list /mnt
 ID 256 gen 11 top level 5 path AAA
 ID 257 gen 8 top level 256 path BBB
 ID 258 gen 8 top level 257 path BBB/dir/CCC
 ID 259 gen 11 top level 256 path ZZZ

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 Documentation/btrfs-subvolume.asciidoc |   8 +-
 cmds-subvolume.c                       | 144 +++++++++++++++++++------
 2 files changed, 119 insertions(+), 33 deletions(-)

diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index f3eb4e26..99fff977 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -95,6 +95,12 @@ The output format is similar to *subvolume list* command.
 
 *list* [options] [-G [\+|-]<value>] [-C [+|-]<value>] [--sort=rootid,gen,ogen,path] <path>::
 List the subvolumes present in the filesystem <path>.
+By default, this only lists the subvolumes under <path>,
+including <path> itself (except top-level subvolume).
++
+This command had required root privileges. From kernel 4.18,
+non privileged user can call this too. Also from kernel 4.18,
+It is possible to specify non-subvolume directory as <path>.
 +
 For every subvolume the following information is shown by default:
 +
@@ -102,7 +108,7 @@ ID <ID> gen <generation> top level <ID> path <path>
 +
 where ID is subvolume's id, gen is an internal counter which is updated
 every transaction, top level is the same as parent subvolume's id, and
-path is the relative path of the subvolume to the top level subvolume.
+path is the relative path of the subvolume to the specified path.
 The subvolume's ID may be used by the subvolume set-default command,
 or at mount time via the subvolid= option.
 +
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 40cc2687..ef613662 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1128,9 +1128,37 @@ out:
 	return subvols;
 }
 
+static int add_subvol(struct subvol_list **subvols,
+		      struct listed_subvol *subvol,
+		      size_t *capacity)
+{
+	if ((*subvols)->num >= *capacity) {
+		struct subvol_list *new_subvols;
+		size_t new_capacity = max_t(size_t, 1, *capacity * 2);
+
+		new_subvols = realloc(*subvols,
+				      sizeof(*new_subvols) +
+				      new_capacity *
+				      sizeof(new_subvols->subvols[0]));
+		if (!new_subvols) {
+			error("out of memory");
+			return -1;
+		}
+
+		*subvols = new_subvols;
+		*capacity = new_capacity;
+	}
+
+	(*subvols)->subvols[(*subvols)->num] = *subvol;
+	(*subvols)->num++;
+
+	return 0;
+}
+
 static void get_subvols_info(struct subvol_list **subvols,
 			     struct btrfs_list_filter_set_v2 *filter_set,
 			     int fd,
+			     int tree_id,
 			     size_t *capacity)
 {
 	struct btrfs_util_subvolume_iterator *iter;
@@ -1138,7 +1166,7 @@ static void get_subvols_info(struct subvol_list **subvols,
 	int ret = -1;
 
 	err = btrfs_util_create_subvolume_iterator_fd(fd,
-						      BTRFS_FS_TREE_OBJECTID, 0,
+						      tree_id, 0,
 						      &iter);
 	if (err) {
 		iter = NULL;
@@ -1146,6 +1174,52 @@ static void get_subvols_info(struct subvol_list **subvols,
 		goto out;
 	}
 
+	/*
+	 * Subvolume iterator does not include the information of the
+	 * specified path/fd. So, add it first.
+	 */
+	if (!tree_id) {
+		uint64_t id;
+		struct listed_subvol subvol;
+
+		err = btrfs_util_is_subvolume_fd(fd);
+		if (err != BTRFS_UTIL_OK) {
+			if (err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
+				ret = 0;
+				goto skip;
+			} else {
+				ret = -1;
+				goto out;
+			}
+		}
+		err = btrfs_util_subvolume_id_fd(fd, &id);
+		if (err) {
+			ret = -1;
+			goto out;
+		}
+		if (id == BTRFS_FS_TREE_OBJECTID) {
+			/* Skip top level subvolume */
+			ret = 0;
+			goto skip;
+		}
+
+		err = btrfs_util_subvolume_info_fd(fd, 0, &subvol.info);
+		if (err) {
+			ret = -1;
+			goto out;
+		}
+
+		subvol.path = strdup(".");
+		if (!filters_match(&subvol, filter_set)) {
+			free(subvol.path);
+		} else {
+			ret = add_subvol(subvols, &subvol, capacity);
+			if (ret)
+				goto out;
+		}
+	}
+
+skip:
 	for (;;) {
 		struct listed_subvol subvol;
 
@@ -1156,33 +1230,17 @@ static void get_subvols_info(struct subvol_list **subvols,
 			break;
 		} else if (err) {
 			error_btrfs_util(err);
+			ret = -1;
 			goto out;
 		}
 
 		if (!filters_match(&subvol, filter_set)) {
 			free(subvol.path);
-			continue;
-		}
-
-		if ((*subvols)->num >= *capacity) {
-			struct subvol_list *new_subvols;
-			size_t new_capacity = max_t(size_t, 1, *capacity * 2);
-
-			new_subvols = realloc(*subvols,
-					      sizeof(*new_subvols) +
-					      new_capacity *
-					      sizeof(new_subvols->subvols[0]));
-			if (!new_subvols) {
-				error("out of memory");
+		} else {
+			ret = add_subvol(subvols, &subvol, capacity);
+			if (ret)
 				goto out;
-			}
-
-			*subvols = new_subvols;
-			*capacity = new_capacity;
 		}
-
-		(*subvols)->subvols[(*subvols)->num] = subvol;
-		(*subvols)->num++;
 	}
 
 	ret = 0;
@@ -1196,6 +1254,8 @@ out:
 }
 
 static struct subvol_list *btrfs_list_subvols(int fd,
+					      int is_list_all,
+					      const char *path,
 					      struct btrfs_list_filter_set_v2 *filter_set)
 {
 	struct subvol_list *subvols;
@@ -1208,7 +1268,11 @@ static struct subvol_list *btrfs_list_subvols(int fd,
 	}
 	subvols->num = 0;
 
-	get_subvols_info(&subvols, filter_set, fd, &capacity);
+	if (is_list_all)
+		get_subvols_info(&subvols, filter_set, fd,
+				BTRFS_FS_TREE_OBJECTID, &capacity);
+	else
+		get_subvols_info(&subvols, filter_set, fd, 0, &capacity);
 
 	return subvols;
 }
@@ -1217,20 +1281,16 @@ static int btrfs_list_subvols_print_v2(int fd,
 				    struct btrfs_list_filter_set_v2 *filter_set,
 				    struct btrfs_list_comparer_set_v2 *comp_set,
 				    enum btrfs_list_layout layout,
-				    int full_path, const char *raw_prefix)
+				    int is_list_all,
+				    const char *path,
+				    const char *raw_prefix)
 {
 	struct subvol_list *subvols;
 
-	/*
-	 * full_path hasn't done anything since 4f5ebb3ef553 ("Btrfs-progs: fix
-	 * to make list specified directory's subvolumes work"). See
-	 * https://www.spinics.net/lists/linux-btrfs/msg69820.html
-	 */
-
 	if (filter_set->only_deleted)
 		subvols = btrfs_list_deleted_subvols(fd, filter_set);
 	else
-		subvols = btrfs_list_subvols(fd, filter_set);
+		subvols = btrfs_list_subvols(fd, is_list_all, path, filter_set);
 	if (!subvols)
 		return -1;
 
@@ -1326,6 +1386,14 @@ static int btrfs_list_parse_filter_string_v2(char *opt_arg,
 	return 0;
 }
 
+static bool is_root(void)
+{
+	uid_t uid;
+
+	uid = geteuid();
+	return (uid == 0);
+}
+
 /*
  * Naming of options:
  * - uppercase for filters and sort options
@@ -1334,12 +1402,18 @@ static int btrfs_list_parse_filter_string_v2(char *opt_arg,
 static const char * const cmd_subvol_list_usage[] = {
 	"btrfs subvolume list [options] <path>",
 	"List subvolumes and snapshots in the filesystem.",
+	"By default, this only lists the subvolumes under <path>,",
+	"including <path> itself (except top-level subvolume).",
+	"",
+	"This command had required root privileges. From kernel 4.18,",
+	"non privileged user can call this too. Also from kernel 4.18,",
+	"It is possible to specify non-subvolume directory as <path>.",
 	"",
 	"Path filtering:",
 	"-o           print only subvolumes below specified path",
 	"-a           print all the subvolumes in the filesystem and",
 	"             distinguish absolute and relative path with respect",
-	"             to the given <path>",
+	"             to the given <path> (require root privileges)",
 	"",
 	"Field selection:",
 	"-p           print parent ID",
@@ -1485,6 +1559,12 @@ static int cmd_subvol_list(int argc, char **argv)
 		goto out;
 	}
 
+	if (is_list_all && !is_root()) {
+		ret = -1;
+		error("only root can use -a option");
+		goto out;
+	}
+
 	subvol = argv[optind];
 	fd = btrfs_open_dir(subvol, &dirstream, 1);
 	if (fd < 0) {
@@ -1517,7 +1597,7 @@ static int cmd_subvol_list(int argc, char **argv)
 	btrfs_list_setup_print_column_v2(BTRFS_LIST_PATH);
 
 	ret = btrfs_list_subvols_print_v2(fd, filter_set, comparer_set,
-			layout, !is_list_all && !is_only_in_path, NULL);
+			layout, is_list_all, subvol, NULL);
 
 out:
 	close_file_or_dir(fd, dirstream);
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 4/8] btrfs-progs: sub list: Update -a option and remove meaningless filter
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (2 preceding siblings ...)
  2018-11-27  5:24 ` [PATCH 3/8] btrfs-progs: sub list: Change the default behavior of "subvolume list" and allow non-privileged user to call it Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 5/8] btrfs-progs: utils: Fallback to open without O_NOATIME flag in find_mount_root(): Misono Tomohiro
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Currently, -a option add filter and change subvolume path as follows:
  - If a subvolume is a child of the specified path, nothing changes
  - otherwise, adds <FS_TREE> to head

This is rather meaningless, so let's remove this filter.

As a result, the behavior of -a option becomes the same as
default behavior of sub list in progs <= 4.19

[Example]
 $ mkfs.btrfs -f $DEV
 $ mount $DEV /mnt

 $ btrfs subvolume create /mnt/AAA
 $ btrfs subvolume create /mnt/AAA/BBB
 $ btrfs subvolume create /mnt/ZZZ

 $ btrfs subvolume list -a /mnt
 ID 256 gen 9 top level 5 path AAA
 ID 257 gen 9 top level 256 path AAA/BBB
 ID 258 gen 10 top level 5 path ZZZ

 ** output of progs <= 4.19
 $ btrfs subvolume list -a /mnt
 ID 256 gen 9 top level 5 path AAA
 ID 257 gen 9 top level 256 path <FS_TREE>/AAA/BBB
 ID 258 gen 10 top level 5 path ZZZ

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 Documentation/btrfs-subvolume.asciidoc |  6 +++--
 cmds-subvolume.c                       | 35 +++-----------------------
 2 files changed, 8 insertions(+), 33 deletions(-)

diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index 99fff977..428a2faa 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -118,8 +118,10 @@ Path filtering;;
 -o::::
 print only subvolumes below specified <path>.
 -a::::
-print all the subvolumes in the filesystem and distinguish between
-absolute and relative path with respect to the given <path>.
+print all the subvolumes in the filesystem, including subvolumes
+which cannot be accessed from current mount point.
+path to be shown is relative to the top-level subvolume
+(require root privileges).
 
 Field selection;;
 -p::::
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index ef613662..cd2e4425 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -761,28 +761,6 @@ static int filter_topid_equal(struct listed_subvol *subvol, uint64_t data)
 	return subvol->info.parent_id == data;
 }
 
-static int filter_full_path(struct listed_subvol *subvol, uint64_t data)
-{
-	/*
-	 * This implements the same behavior as before the conversion to
-	 * libbtrfsutil, which is mostly nonsensical.
-	 */
-	if (subvol->info.parent_id != data) {
-		char *tmp;
-		int ret;
-
-		ret = asprintf(&tmp, "<FS_TREE>/%s", subvol->path);
-		if (ret == -1) {
-			error("out of memory");
-			exit(1);
-		}
-
-		free(subvol->path);
-		subvol->path = tmp;
-	}
-	return 1;
-}
-
 static int filter_by_parent(struct listed_subvol *subvol, uint64_t data)
 {
 	return !uuid_compare(subvol->info.parent_uuid,
@@ -800,7 +778,6 @@ static btrfs_list_filter_func_v2 all_filter_funcs[] = {
 	[BTRFS_LIST_FILTER_CGEN_LESS]		= filter_cgen_less,
 	[BTRFS_LIST_FILTER_CGEN_EQUAL]          = filter_cgen_equal,
 	[BTRFS_LIST_FILTER_TOPID_EQUAL]		= filter_topid_equal,
-	[BTRFS_LIST_FILTER_FULL_PATH]		= filter_full_path,
 	[BTRFS_LIST_FILTER_BY_PARENT]		= filter_by_parent,
 };
 
@@ -1411,9 +1388,9 @@ static const char * const cmd_subvol_list_usage[] = {
 	"",
 	"Path filtering:",
 	"-o           print only subvolumes below specified path",
-	"-a           print all the subvolumes in the filesystem and",
-	"             distinguish absolute and relative path with respect",
-	"             to the given <path> (require root privileges)",
+	"-a           print all the subvolumes in the filesystem.",
+	"             path to be shown is relative to the top-level",
+	"             subvolume (require root privileges)",
 	"",
 	"Field selection:",
 	"-p           print parent ID",
@@ -1581,11 +1558,7 @@ static int cmd_subvol_list(int argc, char **argv)
 	if (ret)
 		goto out;
 
-	if (is_list_all)
-		btrfs_list_setup_filter_v2(&filter_set,
-					BTRFS_LIST_FILTER_FULL_PATH,
-					top_id);
-	else if (is_only_in_path)
+	if (is_only_in_path)
 		btrfs_list_setup_filter_v2(&filter_set,
 					BTRFS_LIST_FILTER_TOPID_EQUAL,
 					top_id);
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/8] btrfs-progs: utils: Fallback to open without O_NOATIME flag in find_mount_root():
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (3 preceding siblings ...)
  2018-11-27  5:24 ` [PATCH 4/8] btrfs-progs: sub list: Update -a option and remove meaningless filter Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 6/8] btrfs-progs: sub show: Allow non-privileged user to call "subvolume show" Misono Tomohiro
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

O_NOATIME flag requires effective UID of process matches file's owner
or has CAP_FOWNER capabilities. Fallback to open without O_NOATIME flag
so that non-privileged user can also call find_mount_root().

This is a preparation work to allow non-privileged user to call
"subvolume show".

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 utils.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/utils.c b/utils.c
index 3a4bc92a..8ce8417d 100644
--- a/utils.c
+++ b/utils.c
@@ -2054,6 +2054,9 @@ int find_mount_root(const char *path, char **mount_root)
 	char *longest_match = NULL;
 
 	fd = open(path, O_RDONLY | O_NOATIME);
+	if (fd < 0 && errno == EPERM)
+		fd = open(path, O_RDONLY);
+
 	if (fd < 0)
 		return -errno;
 	close(fd);
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 6/8] btrfs-progs: sub show: Allow non-privileged user to call "subvolume show"
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (4 preceding siblings ...)
  2018-11-27  5:24 ` [PATCH 5/8] btrfs-progs: utils: Fallback to open without O_NOATIME flag in find_mount_root(): Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 7/8] btrfs-progs: test: Add helper function to check if test user exists Misono Tomohiro
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Allow non-privileged user to call subvolume show if new ioctls
(BTRFS_IOC_GET_SUBVOL_INFO/BTRFS_IOC_GET_SUBVOL_ROOTREF,
BTRFS_IOC_INO_LOOKUP_USER, from kernel 4.18) are available.
Non-privileged user still cannot use -r or -u option.

The behavior for root user is the same as before.

There are some output differences between root and user:
  root ... subvolume path is from top-level subvolume
           list all snapshots in the fs (inc. non-accessible ones)
  user ... subvolume path is absolute path
           list snapshots under the mountpoint
	   (only to which the user has appropriate access right)

[Example]
 $ sudo mkfs.btrfs -f $DEV
 $ sudo mount $DEV /mnt

 $ sudo btrfs subvolume create /mnt/AAA
 $ sudo btrfs subvolume snapshot /mnt/AAA /mnt/snap1
 $ sudo btrfs subvolume snapshot /mnt/AAA /mnt/AAA/snap2

 $ sudo umount /mnt
 $ sudo mount -o subvol=AAA $DEV /mnt

 # root
 $ sudo btrfs subvolume show /mnt
 AAA
      Name:        AAA
      UUID:        15e80697-2ffb-0b4b-8e1e-e0873a7cf944
      ...
      Snapshot(s):
                   AAA/snap2
                   snap1

 # non-privileged user
 $ btrfs subvolume show /mnt
 /mnt
      Name:        AAA
      UUID:        15e80697-2ffb-0b4b-8e1e-e0873a7cf944
      ...
      Snapshot(s):
                   /mnt/snap2

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 Documentation/btrfs-subvolume.asciidoc |  11 ++-
 cmds-subvolume.c                       | 107 ++++++++++++++++++++++---
 2 files changed, 105 insertions(+), 13 deletions(-)

diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index 428a2faa..ea8e9554 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -182,12 +182,19 @@ The id can be obtained from *btrfs subvolume list*, *btrfs subvolume show* or
 *show* [options] <path>|<mnt>::
 Show information of a given subvolume in the <path>.
 +
+This command had required root privileges. From kernel 4.18,
+non-privileged user can call this unless -r/-u option is not used.
+Note that for root, output path is relative to the top-level subvolume
+while absolute path is shown for non-privileged user.
+Also for root, snapshots filed lists all the snapshots in the fs while
+only snapshots under mount point are shown for non-privileged user.
++
 `Options`
 +
 -r|--rootid::::
-rootid of the subvolume.
+rootid of the subvolume (require root privileges).
 -u|--uuid:::
-UUID of the subvolume.
+UUID of the subvolume (require root privileges).
 
 +
 If no option is specified, subvolume information of <path> is shown,
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index cd2e4425..ab1f14a2 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1886,8 +1886,12 @@ static int cmd_subvol_find_new(int argc, char **argv)
 static const char * const cmd_subvol_show_usage[] = {
 	"btrfs subvolume show [options] <subvol-path>|<mnt>",
 	"Show more information about the subvolume",
-	"-r|--rootid   rootid of the subvolume",
-	"-u|--uuid     uuid of the subvolume",
+	"",
+	"This command had required root privileges. From kernel 4.18,",
+	"non-privileged user can call this unless -r/-u option is not used.",
+	"",
+	"-r|--rootid   rootid of the subvolume (require root privileges)",
+	"-u|--uuid     uuid of the subvolume   (require root privileges)",
 	"",
 	"If no option is specified, <subvol-path> will be shown, otherwise",
 	"the rootid or uuid are resolved relative to the <mnt> path.",
@@ -1900,8 +1904,10 @@ static int cmd_subvol_show(int argc, char **argv)
 	char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
 	char *fullpath = NULL;
 	int fd = -1;
+	int fd_mnt = -1;
 	int ret = 1;
 	DIR *dirstream1 = NULL;
+	DIR *dirstream_mnt = NULL;
 	int by_rootid = 0;
 	int by_uuid = 0;
 	u64 rootid_arg = 0;
@@ -1909,7 +1915,10 @@ static int cmd_subvol_show(int argc, char **argv)
 	struct btrfs_util_subvolume_iterator *iter;
 	struct btrfs_util_subvolume_info subvol;
 	char *subvol_path = NULL;
+	char *subvol_name = NULL;
+	char *mount_point = NULL;
 	enum btrfs_util_error err;
+	bool root;
 
 	optind = 0;
 	while (1) {
@@ -1947,6 +1956,12 @@ static int cmd_subvol_show(int argc, char **argv)
 		usage(cmd_subvol_show_usage);
 	}
 
+	root = is_root();
+	if (!root && (by_rootid || by_uuid)) {
+		error("only root can use -r or -u options");
+		return -1;
+	}
+
 	fullpath = realpath(argv[optind], NULL);
 	if (!fullpath) {
 		error("cannot find real path for '%s': %m", argv[optind]);
@@ -2001,19 +2016,53 @@ static int cmd_subvol_show(int argc, char **argv)
 			goto out;
 		}
 
-		err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
-		if (err) {
-			error_btrfs_util(err);
-			goto out;
+		if (root) {
+			/* Construct path relative to top-level subvolume */
+			err = btrfs_util_subvolume_path_fd(fd, subvol.id,
+								&subvol_path);
+			if (err) {
+				error_btrfs_util(err);
+				goto out;
+			}
+			subvol_name = strdup(basename(subvol_path));
+		} else {
+			/* Show absolute path */
+			subvol_path = strdup(fullpath);
+
+			ret = find_mount_root(fullpath, &mount_point);
+			if (ret < 0) {
+				error("cannot get mount point");
+				goto out;
+			}
+			fd_mnt = open_file_or_dir(mount_point, &dirstream_mnt);
+			if (fd_mnt < 0) {
+				error("cannot open mount point");
+				goto out;
+			}
+			/* Get real name if the path is mount point */
+			if (strlen(fullpath) == strlen(mount_point)) {
+				struct btrfs_ioctl_get_subvol_info_args arg;
+
+				ret = ioctl(fd_mnt, BTRFS_IOC_GET_SUBVOL_INFO,
+							&arg);
+				if (ret < 0) {
+					error("cannot get subvolume info");
+					goto out;
+				}
+				subvol_name = strdup(arg.name);
+			} else {
+				subvol_name = strdup(basename(subvol_path));
+			}
 		}
 
 	}
 
 	/* print the info */
-	printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
+	printf("%s\n", (subvol.id == BTRFS_FS_TREE_OBJECTID && root) ?
+			"/" : subvol_path);
 	printf("\tName: \t\t\t%s\n",
 	       (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
-		basename(subvol_path)));
+					subvol_name));
 
 	if (uuid_is_null(subvol.uuid))
 		strcpy(uuidparse, "-");
@@ -2056,9 +2105,18 @@ static int cmd_subvol_show(int argc, char **argv)
 	/* print the snapshots of the given subvol if any*/
 	printf("\tSnapshot(s):\n");
 
-	err = btrfs_util_create_subvolume_iterator_fd(fd,
-						      BTRFS_FS_TREE_OBJECTID, 0,
-						      &iter);
+	/*
+	 * For root, show all snapshots in the filesystem.
+	 * For non-privileged user, show all snapshots under mount point.
+	 */
+	if (root)
+		err = btrfs_util_create_subvolume_iterator_fd(fd,
+					      BTRFS_FS_TREE_OBJECTID, 0,
+					      &iter);
+	else
+		err = btrfs_util_create_subvolume_iterator_fd(fd_mnt,
+					      0, 0,
+					      &iter);
 
 	for (;;) {
 		struct btrfs_util_subvolume_info subvol2;
@@ -2070,9 +2128,33 @@ static int cmd_subvol_show(int argc, char **argv)
 		} else if (err) {
 			error_btrfs_util(err);
 			btrfs_util_destroy_subvolume_iterator(iter);
+			ret = -1;
 			goto out;
 		}
 
+		if (!root) {
+			/* Make path absolute */
+			char *temp = malloc(strlen(mount_point) +
+						   strlen(path) + 2);
+
+			if (!temp) {
+				error("out of memory");
+				ret = -1;
+				goto out;
+			}
+
+			strcpy(temp, mount_point);
+			if (strlen(mount_point) == 1) {
+				strcpy(temp + 1, path);
+			} else {
+				temp[strlen(mount_point)] = '/';
+				strcpy(temp + strlen(mount_point) + 1, path);
+			}
+
+			free(path);
+			path = temp;
+		}
+
 		if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
 			printf("\t\t\t\t%s\n", path);
 
@@ -2083,8 +2165,11 @@ static int cmd_subvol_show(int argc, char **argv)
 	ret = 0;
 out:
 	free(subvol_path);
+	free(subvol_name);
 	close_file_or_dir(fd, dirstream1);
+	close_file_or_dir(fd_mnt, dirstream_mnt);
 	free(fullpath);
+	free(mount_point);
 	return !!ret;
 }
 
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 7/8] btrfs-progs: test: Add helper function to check if test user exists
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (5 preceding siblings ...)
  2018-11-27  5:24 ` [PATCH 6/8] btrfs-progs: sub show: Allow non-privileged user to call "subvolume show" Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  5:24 ` [PATCH 8/8] btrfs-porgs: test: Add cli-test/009 to check subvolume list for both root and normal user Misono Tomohiro
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Add helper function to check that if user 'nobody' exists or not.
Note that 'nobody' should not have root privileges as it will be used
to test the behavior of non-privileged user.

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 tests/common | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/tests/common b/tests/common
index 1575ae38..16693ca7 100644
--- a/tests/common
+++ b/tests/common
@@ -314,6 +314,16 @@ check_global_prereq()
 	fi
 }
 
+check_testuser()
+{
+	id -u nobody > /dev/null 2>&1
+	if [ $? -ne 0 ]; then
+		_not_run "Need to add user \"nobody\""
+	fi
+	# Note that nobody should not have root privileges
+	# otherwise test may not run as expected
+}
+
 check_image()
 {
 	local image
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 8/8] btrfs-porgs: test: Add cli-test/009 to check subvolume list for both root and normal user
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (6 preceding siblings ...)
  2018-11-27  5:24 ` [PATCH 7/8] btrfs-progs: test: Add helper function to check if test user exists Misono Tomohiro
@ 2018-11-27  5:24 ` Misono Tomohiro
  2018-11-27  9:48 ` [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Martin Steigerwald
  2018-12-07  1:02 ` Omar Sandoval
  9 siblings, 0 replies; 13+ messages in thread
From: Misono Tomohiro @ 2018-11-27  5:24 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
---
 tests/cli-tests/009-subvolume-list/test.sh | 130 +++++++++++++++++++++
 1 file changed, 130 insertions(+)
 create mode 100755 tests/cli-tests/009-subvolume-list/test.sh

diff --git a/tests/cli-tests/009-subvolume-list/test.sh b/tests/cli-tests/009-subvolume-list/test.sh
new file mode 100755
index 00000000..50b7eb6b
--- /dev/null
+++ b/tests/cli-tests/009-subvolume-list/test.sh
@@ -0,0 +1,130 @@
+#!/bin/bash
+# test for "subvolume list" both for root and normal user
+
+source "$TEST_TOP/common"
+
+check_testuser
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev
+
+
+# test if the ids returned by "sub list" match expected ids
+# $1  ... indicate run as root or test user
+# $2  ... PATH to be specified by sub list command
+# $3~ ... expected return ids
+test_list()
+{
+	local SUDO
+	if [ $1 -eq 1 ]; then
+		SUDO=$SUDO_HELPER
+	else
+		SUDO="sudo -u progs-test"
+	fi
+
+	result=$(run_check_stdout $SUDO "$TOP/btrfs" subvolume list "$2" | \
+		awk '{print $2}' | xargs | sort -n)
+
+	shift
+	shift
+	expected=($(echo "$@" | tr " " "\n" | sort -n))
+	expected=$(IFS=" "; echo "${expected[*]}")
+
+	if [ "$result" != "$expected" ]; then
+		echo "result  : $result"
+		echo "expected: $expected"
+		_fail "ids returned by sub list does not match expected ids"
+	fi
+}
+
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+run_check_mount_test_dev
+cd "$TEST_MNT"
+
+# create subvolumes and directories and make some non-readable
+# by user 'progs-test'
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub1
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub1/subsub1
+run_check $SUDO_HELPER mkdir sub1/dir
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub2
+run_check $SUDO_HELPER mkdir -p sub2/dir/dirdir
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub2/dir/subsub2
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub2/dir/dirdir/subsubX
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub3
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub3/subsub3
+run_check $SUDO_HELPER mkdir sub3/dir
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub3/dir/subsubY
+run_check $SUDO_HELPER chmod o-r sub3
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub4
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub4/subsub4
+run_check $SUDO_HELPER mkdir sub4/dir
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create sub4/dir/subsubZ
+run_check $SUDO_HELPER setfacl -m u:progs-test:- sub4/dir
+
+run_check $SUDO_HELPER touch "file"
+
+# expected result for root at mount point:
+#
+# ID 256 gen 8 top level 5 path sub1
+# ID 258 gen 7 top level 256 path sub1/subsub1
+# ID 259 gen 10 top level 5 path sub2
+# ID 260 gen 9 top level 259 path sub2/dir/subsub2
+# ID 261 gen 10 top level 259 path sub2/dir/dirdir/subsubX
+# ID 262 gen 14 top level 5 path sub3
+# ID 263 gen 12 top level 262 path sub3/subsub3
+# ID 264 gen 13 top level 262 path sub3/dir/subsubY
+# ID 265 gen 17 top level 5 path sub4
+# ID 266 gen 15 top level 265 path sub4/subsub4
+# ID 267 gen 16 top level 265 path sub4/dir/subsubZ
+
+# check for root for both absolute/relative path
+all=(256 258 259 260 261 262 263 264 265 266 267)
+test_list 1 "$TEST_MNT" "${all[@]}"
+test_list 1 "$TEST_MNT/sub1" "256 258"
+run_mustfail "should raise invalid argument error" \
+	sudo "$TOP/btrfs" subvolume list "$TEST_MNT/sub1/dir"
+test_list 1 "$TEST_MNT/sub2" "259 260 261"
+test_list 1 "$TEST_MNT/sub3" "262 263 264"
+test_list 1 "$TEST_MNT/sub4" "265 266 267"
+run_mustfail "should fail for file" \
+	$SUDO_HELPER "$TOP/btrfs" subvolume list "$TEST_MNT/file"
+
+test_list 1 "." "${all[@]}"
+test_list 1 "sub1" "256 258"
+run_mustfail "should raise invalid argument error" \
+	sudo "$TOP/btrfs" subvolume list "sub1/dir"
+test_list 1 "sub2" "259 260 261"
+test_list 1 "sub3" "262 263 264"
+test_list 1 "sub4" "265 266 267"
+run_mustfail "should fail for file" \
+	$SUDO_HELPER "$TOP/btrfs" subvolume list "file"
+
+# check for normal user for both absolute/relative path
+test_list 0 "$TEST_MNT" "256 258 259 260 261 265 266"
+test_list 0 "$TEST_MNT/sub1" "256 258"
+run_mustfail "should raise invalid argument error" \
+	sudo "$TOP/btrfs" -u nobody subvolume list "$TEST_MNT/sub1/dir"
+test_list 0 "$TEST_MNT/sub2" "259 260 261"
+run_mustfail "should raise permission error" \
+	sudo -u nobody "$TOP/btrfs" subvolume list "$TEST_MNT/sub3"
+test_list 0 "$TEST_MNT/sub4" "265 266"
+run_mustfail "should fail for file" \
+	sudo -u nobody "$TOP/btrfs" subvolume list "$TEST_MNT/file"
+
+test_list 0 "." "256 258 259 260 261 265 266"
+run_mustfail "should raise permission error" \
+	sudo "$TOP/btrfs" -u nobody subvolume list "sub1/dir"
+test_list 0 "sub2" "259 260 261"
+run_mustfail "should raise permission error" \
+	sudo -u nobody "$TOP/btrfs" subvolume list "sub3"
+test_list 0 "sub4" "265 266"
+run_mustfail "should fail for file" \
+	sudo -u nobody "$TOP/btrfs" subvolume list "file"
+
+cd ..
+run_check_umount_test_dev
-- 
2.19.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show"
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (7 preceding siblings ...)
  2018-11-27  5:24 ` [PATCH 8/8] btrfs-porgs: test: Add cli-test/009 to check subvolume list for both root and normal user Misono Tomohiro
@ 2018-11-27  9:48 ` Martin Steigerwald
  2018-11-28  1:26   ` misono.tomohiro
  2018-12-07  1:02 ` Omar Sandoval
  9 siblings, 1 reply; 13+ messages in thread
From: Martin Steigerwald @ 2018-11-27  9:48 UTC (permalink / raw)
  To: Misono Tomohiro; +Cc: linux-btrfs

Misono Tomohiro - 27.11.18, 06:24:
> Importantly, in order to make output consistent for both root and
> non-privileged user, this changes the behavior of "subvolume list":
>  - (default) Only list in subvolume under the specified path.
>    Path needs to be a subvolume.

Does that work recursively?

I wound find it quite unexpected if I did btrfs subvol list in or on the 
root directory of a BTRFS filesystem would not display any subvolumes on 
that filesystem no matter where they are.

Thanks,
-- 
Martin



^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show"
  2018-11-27  9:48 ` [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Martin Steigerwald
@ 2018-11-28  1:26   ` misono.tomohiro
  0 siblings, 0 replies; 13+ messages in thread
From: misono.tomohiro @ 2018-11-28  1:26 UTC (permalink / raw)
  To: Martin Steigerwald; +Cc: linux-btrfs

Hi,

> -----Original Message-----
> From: Martin Steigerwald [mailto:martin@lichtvoll.de]
> Sent: Tuesday, November 27, 2018 6:48 PM
> To: Misono, Tomohiro <misono.tomohiro@fujitsu.com>
> Cc: linux-btrfs@vger.kernel.org
> Subject: Re: [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges
> of "subvolume list/show"
> 
> Misono Tomohiro - 27.11.18, 06:24:
> > Importantly, in order to make output consistent for both root and
> > non-privileged user, this changes the behavior of "subvolume list":
> >  - (default) Only list in subvolume under the specified path.
> >    Path needs to be a subvolume.
> 
> Does that work recursively?

Not in this version.

Previous version has -f option which recursively search and list subbvolumes
(only if they have the same btrfs fsid):
https://lore.kernel.org/linux-btrfs/84d06767762b4285ddefec0392ee16e2d7e06f62.1529310485.git.misono.tomohiro@jp.fujitsu.com/

However, current "sub list" command already has many options and we cannot
add new one randomly, I drop the patches which add new options in this version.
(In other word, previous version can be divided in two parts: 
  1. Relax the privileges of sub list/show
  2. Add new option to sub list
And this version contains only 1).

> 
> I wound find it quite unexpected if I did btrfs subvol list in or on the
> root directory of a BTRFS filesystem would not display any subvolumes
> on
> that filesystem no matter where they are.

Yes, I think output of -f option is more readable in such case.
If agreement has been made about how "sub list" should really work,
I could update/send patches again.

Thanks,
Misono

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show"
  2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
                   ` (8 preceding siblings ...)
  2018-11-27  9:48 ` [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Martin Steigerwald
@ 2018-12-07  1:02 ` Omar Sandoval
  2018-12-11  9:06   ` misono.tomohiro
  9 siblings, 1 reply; 13+ messages in thread
From: Omar Sandoval @ 2018-12-07  1:02 UTC (permalink / raw)
  To: Misono Tomohiro; +Cc: linux-btrfs

On Tue, Nov 27, 2018 at 02:24:41PM +0900, Misono Tomohiro wrote:
> Hello,
> 
> This is basically the resend of 
>   "[PATCH v2 00/20] btrfs-progs: Rework of "subvolume list/show" and relax the
> 	root privileges of them" [1]
> which I submitted in June. The aim of this series is to allow non-privileged user
> to use basic subvolume functionality (create/list/snapshot/delete; this allows "list")
> 
> They were once in devel branch with some whitespace/comment modification by david.
> I rebased them to current devel branch.
> 
> github: https://github.com/t-msn/btrfs-progs/tree/rework-sub-list
> 
> Basic logic/code is the same as before. Some differences are:
>  - Use latest libbtrfsutil from Omar [2] (thus drop first part of patches).
>    As a result, "sub list" cannot accept an ordinary directry to be
>    specified (which is allowed in previous version)
>  - Drop patches which add new options to "sub list"
>  - Use 'nobody' as non-privileged test user just like libbtrfsutil test
>  - Update comments
> 
> Importantly, in order to make output consistent for both root and non-privileged
> user, this changes the behavior of "subvolume list": 
>  - (default) Only list in subvolume under the specified path.
>    Path needs to be a subvolume.
>  - (-a) filter is dropped. i.e. its output is the same as the
>         default behavior of "sub list" in progs <= 4.19
> 
> Therefore, existent scripts may need to update to add -a option
> (I believe nobody uses current -a option).
> If anyone thinks this is not good, please let me know.

I think there are a few options in the case that the path isn't a
subvolume:

1. List all subvolumes in the filesystem with randomly mangled paths,
   which is what we currently do.
2. Error out, which is what this version of the series does.
3. List all subvolumes under the containing subvolume, which is what the
   previous version does.
4. List all subvolumes under the containing subvolume that are
   underneath the given path.

Option 1 won't work well for unprivileged users. Option 2 (this series)
is definitely going to break people's workflows/scripts. Option 3 is
unintuitive. In my opinion, option 4 is the nicest, but it may also
break scripts that expect all subvolumes to be printed.

There's also an option 5, which is to keep the behavior the same for
root (like what my previous patch [1] did) and implement option 4 for
unprivileged users.

I think 4 and 5 are the two main choices: do we want to preserve
backwards compatibility as carefully as possible (at the cost of
consistency), or do we want to risk it and improve the interface?

1: https://github.com/osandov/btrfs-progs/commit/fb61c21aeb998b12c1d02532639083d7f40c41e0

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show"
  2018-12-07  1:02 ` Omar Sandoval
@ 2018-12-11  9:06   ` misono.tomohiro
  0 siblings, 0 replies; 13+ messages in thread
From: misono.tomohiro @ 2018-12-11  9:06 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: linux-btrfs

> -----Original Message-----
> From: Omar Sandoval [mailto:osandov@osandov.com]
> Sent: Friday, December 7, 2018 10:02 AM
> To: Misono, Tomohiro/味曽野 智礼 <misono.tomohiro@fujitsu.com>
> Cc: linux-btrfs@vger.kernel.org
> Subject: Re: [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of
> "subvolume list/show"
> 
> On Tue, Nov 27, 2018 at 02:24:41PM +0900, Misono Tomohiro wrote:
> > Hello,
> >
> > This is basically the resend of
> >   "[PATCH v2 00/20] btrfs-progs: Rework of "subvolume list/show" and relax
> the
> > 	root privileges of them" [1]
> > which I submitted in June. The aim of this series is to allow non-privileged
> user
> > to use basic subvolume functionality (create/list/snapshot/delete; this
> allows "list")
> >
> > They were once in devel branch with some whitespace/comment modification by
> david.
> > I rebased them to current devel branch.
> >
> > github: https://github.com/t-msn/btrfs-progs/tree/rework-sub-list
> >
> > Basic logic/code is the same as before. Some differences are:
> >  - Use latest libbtrfsutil from Omar [2] (thus drop first part of patches).
> >    As a result, "sub list" cannot accept an ordinary directry to be
> >    specified (which is allowed in previous version)
> >  - Drop patches which add new options to "sub list"
> >  - Use 'nobody' as non-privileged test user just like libbtrfsutil test
> >  - Update comments
> >
> > Importantly, in order to make output consistent for both root and non-privileged
> > user, this changes the behavior of "subvolume list":
> >  - (default) Only list in subvolume under the specified path.
> >    Path needs to be a subvolume.
> >  - (-a) filter is dropped. i.e. its output is the same as the
> >         default behavior of "sub list" in progs <= 4.19
> >
> > Therefore, existent scripts may need to update to add -a option
> > (I believe nobody uses current -a option).
> > If anyone thinks this is not good, please let me know.
> 
> I think there are a few options in the case that the path isn't a
> subvolume:
> 
> 1. List all subvolumes in the filesystem with randomly mangled paths,
>    which is what we currently do.
> 2. Error out, which is what this version of the series does.
> 3. List all subvolumes under the containing subvolume, which is what the
>    previous version does.
> 4. List all subvolumes under the containing subvolume that are
>    underneath the given path.
> 
> Option 1 won't work well for unprivileged users. Option 2 (this series)
> is definitely going to break people's workflows/scripts. Option 3 is
> unintuitive. In my opinion, option 4 is the nicest, but it may also
> break scripts that expect all subvolumes to be printed.

I think my previous version is 4, or am I misunderstanding?
In previous version, output could be:
 $ mkfs.btrfs -f $DEV
 $ mount $DEV /mnt

 $ btrfs subvolume create /mnt/sub1
 $ btrfs subvolume create /mnt/sub1/sub2
 $ mkdir /mnt/sub1/dir
 $ btrfs subvolume create /mnt/sub1/dir/sub3

 $ btrfs subvolume list /mnt
  ID 256 gen 8 top level 5 path sub1
  ID 258 gen 7 top level 256 path sub1/sub2
  ID 259 gen 8 top level 256 path sub1/dir/sub3
 
 $ btrfs subvolume list /mnt/sub1
  ID 256 gen 8 top level 5 path .
  ID 258 gen 7 top level 256 path sub2
  ID 259 gen 8 top level 256 path dir/sub3

 $ btrfs subvolume list /mnt/sub1/dir
  ID 259 gen 8 top level 256 path sub3

https://lore.kernel.org/linux-btrfs/6af6dac8829d3ba405e3c53baffd828c9c428ef6.1529310485.git.misono.tomohiro@jp.fujitsu.com/

> 
> There's also an option 5, which is to keep the behavior the same for
> root (like what my previous patch [1] did) and implement option 4 for
> unprivileged users.
> 
> I think 4 and 5 are the two main choices: do we want to preserve
> backwards compatibility as carefully as possible (at the cost of
> consistency), or do we want to risk it and improve the interface?
> 
> 1:
> https://github.com/osandov/btrfs-progs/commit/fb61c21aeb998b12c1d0253263908
> 3d7f40c41e0


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, back to index

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-27  5:24 [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Misono Tomohiro
2018-11-27  5:24 ` [PATCH 1/8] btrfs-progs: sub list: Use libbtrfsuitl for subvolume list Misono Tomohiro
2018-11-27  5:24 ` [PATCH 2/8] btrfs-progs: sub list: factor out main part of btrfs_list_subvols Misono Tomohiro
2018-11-27  5:24 ` [PATCH 3/8] btrfs-progs: sub list: Change the default behavior of "subvolume list" and allow non-privileged user to call it Misono Tomohiro
2018-11-27  5:24 ` [PATCH 4/8] btrfs-progs: sub list: Update -a option and remove meaningless filter Misono Tomohiro
2018-11-27  5:24 ` [PATCH 5/8] btrfs-progs: utils: Fallback to open without O_NOATIME flag in find_mount_root(): Misono Tomohiro
2018-11-27  5:24 ` [PATCH 6/8] btrfs-progs: sub show: Allow non-privileged user to call "subvolume show" Misono Tomohiro
2018-11-27  5:24 ` [PATCH 7/8] btrfs-progs: test: Add helper function to check if test user exists Misono Tomohiro
2018-11-27  5:24 ` [PATCH 8/8] btrfs-porgs: test: Add cli-test/009 to check subvolume list for both root and normal user Misono Tomohiro
2018-11-27  9:48 ` [PATCH RESEND 0/8] btrfs-progs: sub: Relax the privileges of "subvolume list/show" Martin Steigerwald
2018-11-28  1:26   ` misono.tomohiro
2018-12-07  1:02 ` Omar Sandoval
2018-12-11  9:06   ` misono.tomohiro

Linux-BTRFS Archive on lore.kernel.org

Archives are clonable: git clone --mirror https://lore.kernel.org/linux-btrfs/0 linux-btrfs/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-btrfs linux-btrfs/ https://lore.kernel.org/linux-btrfs \
		linux-btrfs@vger.kernel.org linux-btrfs@archiver.kernel.org
	public-inbox-index linux-btrfs


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-btrfs


AGPL code for this site: git clone https://public-inbox.org/ public-inbox