From patchwork Tue Nov 27 05:24:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699737 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BB72715A7 for ; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A60BC2A23B for ; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 995572A735; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2EC8E2A23B for ; Tue, 27 Nov 2018 05:31:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728671AbeK0Q2V (ORCPT ); Tue, 27 Nov 2018 11:28:21 -0500 Received: from mgwym04.jp.fujitsu.com ([211.128.242.43]:47094 "EHLO mgwym04.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728654AbeK0Q2U (ORCPT ); Tue, 27 Nov 2018 11:28:20 -0500 Received: from yt-mxauth.gw.nic.fujitsu.com (unknown [192.168.229.68]) by mgwym04.jp.fujitsu.com with smtp id 0563_bb75_730019d6_1f07_426c_822d_562bdb9611db; Tue, 27 Nov 2018 14:20:23 +0900 Received: from g01jpfmpwyt02.exch.g01.fujitsu.local (g01jpfmpwyt02.exch.g01.fujitsu.local [10.128.193.56]) by yt-mxauth.gw.nic.fujitsu.com (Postfix) with ESMTP id 898BBAC0104 for ; Tue, 27 Nov 2018 14:20:23 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt02.exch.g01.fujitsu.local (Postfix) with ESMTP id A3EF3584295; Tue, 27 Nov 2018 14:20:22 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:22 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 1/8] btrfs-progs: sub list: Use libbtrfsuitl for subvolume list Date: Tue, 27 Nov 2018 14:24:42 +0900 Message-ID: X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: Misono Tomohiro Signed-off-by: David Sterba --- 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, "/%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: From patchwork Tue Nov 27 05:24:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699731 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2A0041869 for ; Tue, 27 Nov 2018 05:31:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 167B42A23B for ; Tue, 27 Nov 2018 05:31:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0AAA62A3D6; Tue, 27 Nov 2018 05:31:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A5AED2A2D2 for ; Tue, 27 Nov 2018 05:31:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728642AbeK0Q2P (ORCPT ); Tue, 27 Nov 2018 11:28:15 -0500 Received: from mgwkm02.jp.fujitsu.com ([202.219.69.169]:14838 "EHLO mgwkm02.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728624AbeK0Q2P (ORCPT ); Tue, 27 Nov 2018 11:28:15 -0500 X-Greylist: delayed 666 seconds by postgrey-1.27 at vger.kernel.org; Tue, 27 Nov 2018 11:28:13 EST Received: from kw-mxoi2.gw.nic.fujitsu.com (unknown [192.168.231.133]) by mgwkm02.jp.fujitsu.com with smtp id 7647_8b25_e2f3972e_611d_4d56_9986_bc484b0beca1; Tue, 27 Nov 2018 14:20:23 +0900 Received: from g01jpfmpwyt03.exch.g01.fujitsu.local (g01jpfmpwyt03.exch.g01.fujitsu.local [10.128.193.57]) by kw-mxoi2.gw.nic.fujitsu.com (Postfix) with ESMTP id A8F6FAC00DD for ; Tue, 27 Nov 2018 14:20:23 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt03.exch.g01.fujitsu.local (Postfix) with ESMTP id BB7F546E6C3; Tue, 27 Nov 2018 14:20:22 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:22 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 2/8] btrfs-progs: sub list: factor out main part of btrfs_list_subvols Date: Tue, 27 Nov 2018 14:24:43 +0900 Message-ID: <6b60008a654c82495140a928747c117d27663243.1543294426.git.misono.tomohiro@jp.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP No functional changes. This is a preparation work for reworking "subvolume list". Signed-off-by: Misono Tomohiro Signed-off-by: David Sterba --- 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; } From patchwork Tue Nov 27 05:24:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699729 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A056F15A7 for ; Tue, 27 Nov 2018 05:31:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8CE6E2A23B for ; Tue, 27 Nov 2018 05:31:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8111A2A3D6; Tue, 27 Nov 2018 05:31:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F34E62A2D2 for ; Tue, 27 Nov 2018 05:31:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728652AbeK0Q2Q (ORCPT ); Tue, 27 Nov 2018 11:28:16 -0500 Received: from mgwym01.jp.fujitsu.com ([211.128.242.40]:36633 "EHLO mgwym01.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728516AbeK0Q2P (ORCPT ); Tue, 27 Nov 2018 11:28:15 -0500 X-Greylist: delayed 664 seconds by postgrey-1.27 at vger.kernel.org; Tue, 27 Nov 2018 11:28:12 EST Received: from yt-mxoi1.gw.nic.fujitsu.com (unknown [192.168.229.67]) by mgwym01.jp.fujitsu.com with smtp id 4226_7b17_bcc0966b_0df7_451c_8176_912e7dded632; Tue, 27 Nov 2018 14:20:24 +0900 Received: from g01jpfmpwyt02.exch.g01.fujitsu.local (g01jpfmpwyt02.exch.g01.fujitsu.local [10.128.193.56]) by yt-mxoi1.gw.nic.fujitsu.com (Postfix) with ESMTP id C9E8DAC0115 for ; Tue, 27 Nov 2018 14:20:23 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt02.exch.g01.fujitsu.local (Postfix) with ESMTP id F41DF584296; Tue, 27 Nov 2018 14:20:22 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:22 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 3/8] btrfs-progs: sub list: Change the default behavior of "subvolume list" and allow non-privileged user to call it Date: Tue, 27 Nov 2018 14:24:44 +0900 Message-ID: X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: David Sterba --- 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 [\+|-]] [-C [+|-]] [--sort=rootid,gen,ogen,path] :: List the subvolumes present in the filesystem . +By default, this only lists the subvolumes under , +including 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 . + For every subvolume the following information is shown by default: + @@ -102,7 +108,7 @@ ID gen top level 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] ", "List subvolumes and snapshots in the filesystem.", + "By default, this only lists the subvolumes under ,", + "including 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 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 ", + " to the given (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); From patchwork Tue Nov 27 05:24:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699727 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 49D1513AD for ; Tue, 27 Nov 2018 05:31:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 36FAF2A23B for ; Tue, 27 Nov 2018 05:31:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2AB582A755; Tue, 27 Nov 2018 05:31:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D21362A23B for ; Tue, 27 Nov 2018 05:31:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728635AbeK0Q2O (ORCPT ); Tue, 27 Nov 2018 11:28:14 -0500 Received: from mgwkm04.jp.fujitsu.com ([202.219.69.171]:21610 "EHLO mgwkm04.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728625AbeK0Q2O (ORCPT ); Tue, 27 Nov 2018 11:28:14 -0500 Received: from kw-mxoi2.gw.nic.fujitsu.com (unknown [192.168.231.133]) by mgwkm04.jp.fujitsu.com with smtp id 2b29_2613_5eaa3c12_e565_489c_ad52_883efd5fbcea; Tue, 27 Nov 2018 14:20:24 +0900 Received: from g01jpfmpwyt03.exch.g01.fujitsu.local (g01jpfmpwyt03.exch.g01.fujitsu.local [10.128.193.57]) by kw-mxoi2.gw.nic.fujitsu.com (Postfix) with ESMTP id 03700AC00DD for ; Tue, 27 Nov 2018 14:20:24 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt03.exch.g01.fujitsu.local (Postfix) with ESMTP id 1D01746E6D1; Tue, 27 Nov 2018 14:20:23 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:23 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 4/8] btrfs-progs: sub list: Update -a option and remove meaningless filter Date: Tue, 27 Nov 2018 14:24:45 +0900 Message-ID: <708c725e63d16289257ee0a44a925e5a8d2572ac.1543294426.git.misono.tomohiro@jp.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 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 /AAA/BBB ID 258 gen 10 top level 5 path ZZZ Signed-off-by: Misono Tomohiro Signed-off-by: David Sterba --- 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 . -a:::: -print all the subvolumes in the filesystem and distinguish between -absolute and relative path with respect to the given . +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, "/%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 (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); From patchwork Tue Nov 27 05:24:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699739 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E735E1869 for ; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D56772A23B for ; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C93EC2A2D2; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7BC9B2A3D6 for ; Tue, 27 Nov 2018 05:31:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728678AbeK0Q2W (ORCPT ); Tue, 27 Nov 2018 11:28:22 -0500 Received: from mgwym04.jp.fujitsu.com ([211.128.242.43]:47094 "EHLO mgwym04.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728516AbeK0Q2W (ORCPT ); Tue, 27 Nov 2018 11:28:22 -0500 Received: from yt-mxoi1.gw.nic.fujitsu.com (unknown [192.168.229.67]) by mgwym04.jp.fujitsu.com with smtp id 54a9_ddea_195c8337_7766_430d_946b_9c5613616c8f; Tue, 27 Nov 2018 14:20:25 +0900 Received: from g01jpfmpwyt02.exch.g01.fujitsu.local (g01jpfmpwyt02.exch.g01.fujitsu.local [10.128.193.56]) by yt-mxoi1.gw.nic.fujitsu.com (Postfix) with ESMTP id 658D1AC0115 for ; Tue, 27 Nov 2018 14:20:24 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt02.exch.g01.fujitsu.local (Postfix) with ESMTP id 8A582584295; Tue, 27 Nov 2018 14:20:23 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:23 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 5/8] btrfs-progs: utils: Fallback to open without O_NOATIME flag in find_mount_root(): Date: Tue, 27 Nov 2018 14:24:46 +0900 Message-ID: <97ed64c94563e960eb3443bcbc164ce167662e2e.1543294426.git.misono.tomohiro@jp.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: David Sterba --- 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); From patchwork Tue Nov 27 05:24:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699743 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2A8E615A7 for ; Tue, 27 Nov 2018 05:31:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1674A2A23B for ; Tue, 27 Nov 2018 05:31:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 09DB82A735; Tue, 27 Nov 2018 05:31:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3E3E72A23B for ; Tue, 27 Nov 2018 05:31:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728553AbeK0Q2i (ORCPT ); Tue, 27 Nov 2018 11:28:38 -0500 Received: from mgwym02.jp.fujitsu.com ([211.128.242.41]:39421 "EHLO mgwym02.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728267AbeK0Q2i (ORCPT ); Tue, 27 Nov 2018 11:28:38 -0500 Received: from yt-mxoi2.gw.nic.fujitsu.com (unknown [192.168.229.69]) by mgwym02.jp.fujitsu.com with smtp id 0238_95b3_edefa8bc_83bc_42e3_96d7_4a85b7208d69; Tue, 27 Nov 2018 14:20:24 +0900 Received: from g01jpfmpwyt01.exch.g01.fujitsu.local (g01jpfmpwyt01.exch.g01.fujitsu.local [10.128.193.38]) by yt-mxoi2.gw.nic.fujitsu.com (Postfix) with ESMTP id 87603AC00E3 for ; Tue, 27 Nov 2018 14:20:24 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt01.exch.g01.fujitsu.local (Postfix) with ESMTP id A6F276D656B; Tue, 27 Nov 2018 14:20:23 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:23 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 6/8] btrfs-progs: sub show: Allow non-privileged user to call "subvolume show" Date: Tue, 27 Nov 2018 14:24:47 +0900 Message-ID: X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: David Sterba --- 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] |:: Show information of a given subvolume in the . + +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 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] |", "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, will be shown, otherwise", "the rootid or uuid are resolved relative to the 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 ? "" : - 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; } From patchwork Tue Nov 27 05:24:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699735 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9555E1869 for ; Tue, 27 Nov 2018 05:31:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 81E642A23B for ; Tue, 27 Nov 2018 05:31:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 75F4C2A3D6; Tue, 27 Nov 2018 05:31:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 94BF02A2D2 for ; Tue, 27 Nov 2018 05:31:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728667AbeK0Q2T (ORCPT ); Tue, 27 Nov 2018 11:28:19 -0500 Received: from mgwkm03.jp.fujitsu.com ([202.219.69.170]:62548 "EHLO mgwkm03.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728516AbeK0Q2T (ORCPT ); Tue, 27 Nov 2018 11:28:19 -0500 Received: from kw-mxauth.gw.nic.fujitsu.com (unknown [192.168.231.132]) by mgwkm03.jp.fujitsu.com with smtp id 01eb_8a3d_2e1293e3_8c3c_43cd_b112_811dd67470e7; Tue, 27 Nov 2018 14:20:24 +0900 Received: from g01jpfmpwyt03.exch.g01.fujitsu.local (g01jpfmpwyt03.exch.g01.fujitsu.local [10.128.193.57]) by kw-mxauth.gw.nic.fujitsu.com (Postfix) with ESMTP id ED2EFAC00C1 for ; Tue, 27 Nov 2018 14:20:24 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt03.exch.g01.fujitsu.local (Postfix) with ESMTP id 1B3AF46E6C0; Tue, 27 Nov 2018 14:20:24 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:23 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 7/8] btrfs-progs: test: Add helper function to check if test user exists Date: Tue, 27 Nov 2018 14:24:48 +0900 Message-ID: <452bdc64251360e5e31209d19ea84921152f4517.1543294426.git.misono.tomohiro@jp.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: David Sterba --- 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 From patchwork Tue Nov 27 05:24:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Misono Tomohiro X-Patchwork-Id: 10699741 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4A49013AD for ; Tue, 27 Nov 2018 05:31:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 36F762A23B for ; Tue, 27 Nov 2018 05:31:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2B75A2A3D6; Tue, 27 Nov 2018 05:31:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A51752A23B for ; Tue, 27 Nov 2018 05:31:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728695AbeK0Q2X (ORCPT ); Tue, 27 Nov 2018 11:28:23 -0500 Received: from mgwym04.jp.fujitsu.com ([211.128.242.43]:47094 "EHLO mgwym04.jp.fujitsu.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728516AbeK0Q2X (ORCPT ); Tue, 27 Nov 2018 11:28:23 -0500 Received: from yt-mxoi1.gw.nic.fujitsu.com (unknown [192.168.229.67]) by mgwym04.jp.fujitsu.com with smtp id 0563_bbbf_02780552_6680_4b05_a649_3a852ed6a150; Tue, 27 Nov 2018 14:20:25 +0900 Received: from g01jpfmpwyt01.exch.g01.fujitsu.local (g01jpfmpwyt01.exch.g01.fujitsu.local [10.128.193.38]) by yt-mxoi1.gw.nic.fujitsu.com (Postfix) with ESMTP id 2FD9FAC014E for ; Tue, 27 Nov 2018 14:20:25 +0900 (JST) Received: from g01jpexchyt37.g01.fujitsu.local (unknown [10.128.193.4]) by g01jpfmpwyt01.exch.g01.fujitsu.local (Postfix) with ESMTP id 4CE476D66F4; Tue, 27 Nov 2018 14:20:24 +0900 (JST) Received: from luna3.soft.fujitsu.com (10.124.196.199) by g01jpexchyt37.g01.fujitsu.local (10.128.193.67) with Microsoft SMTP Server id 14.3.352.0; Tue, 27 Nov 2018 14:20:24 +0900 From: Misono Tomohiro To: CC: David Sterba Subject: [PATCH 8/8] btrfs-porgs: test: Add cli-test/009 to check subvolume list for both root and normal user Date: Tue, 27 Nov 2018 14:24:49 +0900 Message-ID: <0ed1cc0c1afd4447571680f848c0cc5d4ef8b96b.1543294426.git.misono.tomohiro@jp.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 X-SecurityPolicyCheck-GC: OK by FENCE-Mail X-TM-AS-MML: disable Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Misono Tomohiro Signed-off-by: David Sterba --- 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