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). Also, if kernel
supports new ioctls (BTRFS_IOC_GET_SUBVOL_INFO/BTRFS_IOC_GET_ROOTREF/
BTRFS_IOC_INO_LOOKUP_USER),
  - the specified path can be non-subvolume directory.
  - non-privileged user can also call it.

Note that root user can list all the subvolume in the fs with -a option
(the same behavior as before).

Signed-off-by: Tomohiro Misono <misono.tomoh...@jp.fujitsu.com>
---
 Documentation/btrfs-subvolume.asciidoc |  2 +
 cmds-subvolume.c                       | 90 +++++++++++++++++++++++++++++-----
 2 files changed, 81 insertions(+), 11 deletions(-)

diff --git a/Documentation/btrfs-subvolume.asciidoc 
b/Documentation/btrfs-subvolume.asciidoc
index a8c4af4b..e03d4a6e 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -92,6 +92,7 @@ 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 lists the subvolume under the specified path.
 +
 For every subvolume the following information is shown by default. +
 ID <ID> top level <ID> path <path> +
@@ -109,6 +110,7 @@ print parent ID.
 -a::::
 print all the subvolumes in the filesystem and distinguish between
 absolute and relative path with respect to the given <path>.
+This requires root privileges.
 -c::::
 print the ogeneration of the subvolume, aliases: ogen or origin generation.
 -g::::
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 06686943..c3952172 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1126,6 +1126,7 @@ out:
 }
 
 static struct subvol_list *btrfs_list_subvols(int fd,
+                                             int is_list_all,
                                              struct btrfs_list_filter_set_v2 
*filter_set)
 {
        struct subvol_list *subvols;
@@ -1133,6 +1134,7 @@ static struct subvol_list *btrfs_list_subvols(int fd,
        struct btrfs_util_subvolume_iterator *iter;
        enum btrfs_util_error err;
        int ret = -1;
+       int tree_id = 0;
 
        subvols = malloc(sizeof(*subvols));
        if (!subvols) {
@@ -1141,8 +1143,11 @@ static struct subvol_list *btrfs_list_subvols(int fd,
        }
        subvols->num = 0;
 
+       if (is_list_all)
+               tree_id = BTRFS_FS_TREE_OBJECTID;
+
        err = btrfs_util_create_subvolume_iterator_fd(fd,
-                                                     BTRFS_FS_TREE_OBJECTID, 0,
+                                                     tree_id, 0,
                                                      &iter);
        if (err) {
                iter = NULL;
@@ -1189,6 +1194,60 @@ static struct subvol_list *btrfs_list_subvols(int fd,
                subvols->num++;
        }
 
+       /*
+        * Subvolume iterator does not include the information of the
+        * specified path/fd. So, add it here.
+        */
+       if (!is_list_all) {
+               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 out;
+               }
+               err = btrfs_util_subvolume_id_fd(fd, &id);
+               if (err)
+                       goto out;
+               if (id == BTRFS_FS_TREE_OBJECTID) {
+                       /* Skip top level subvolume */
+                       ret = 0;
+                       goto out;
+               }
+
+               err = btrfs_util_subvolume_info_fd(fd, 0, &subvol.info);
+               if (err)
+                       goto out;
+
+               subvol.path = strdup(".");
+               if (!filters_match(&subvol, filter_set)) {
+                       free(subvol.path);
+               } else {
+                       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)
@@ -1202,20 +1261,14 @@ 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 *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, filter_set);
        if (!subvols)
                return -1;
 
@@ -1311,6 +1364,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
@@ -1319,12 +1380,13 @@ 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 subvolume under the specified 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 +1547,12 @@ static int cmd_subvol_list(int argc, char **argv)
        if (ret)
                goto out;
 
+       if (is_list_all && !is_root()) {
+               ret = -1;
+               error("Only root can use -a option");
+               goto out;
+       }
+
        if (is_list_all)
                btrfs_list_setup_filter_v2(&filter_set,
                                        BTRFS_LIST_FILTER_FULL_PATH,
@@ -1501,7 +1569,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, NULL);
 
 out:
        close_file_or_dir(fd, dirstream);
-- 
2.14.3


--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to