From de4f06c275430937026e328f48438cc799eba987 Mon Sep 17 00:00:00 2001 From: Paul Dagnelie Date: Wed, 20 May 2020 10:05:33 -0700 Subject: [PATCH] Small program that converts a dataset id and an object id to a path Small program that converts a dataset id and an object id to a path Reviewed-by: Prakash Surya Reviewed-by: Brian Behlendorf Reviewed-by: Matthew Ahrens Signed-off-by: Paul Dagnelie Closes #10204 --- cmd/Makefile.am | 2 +- cmd/zfs_ids_to_path/.gitignore | 1 + cmd/zfs_ids_to_path/Makefile.am | 9 ++ cmd/zfs_ids_to_path/zfs_ids_to_path.c | 96 +++++++++++++++++++ configure.ac | 2 + include/libzfs.h | 4 +- lib/libzfs/libzfs_pool.c | 22 ++++- man/man8/Makefile.am | 1 + man/man8/zfs_ids_to_path.8 | 50 ++++++++++ tests/runfiles/common.run | 4 + tests/zfs-tests/include/commands.cfg | 3 +- .../tests/functional/cli_root/Makefile.am | 1 + .../cli_root/zfs_ids_to_path/Makefile.am | 5 + .../cli_root/zfs_ids_to_path/cleanup.ksh | 29 ++++++ .../cli_root/zfs_ids_to_path/setup.ksh | 31 ++++++ .../zfs_ids_to_path_001_pos.ksh | 96 +++++++++++++++++++ 16 files changed, 349 insertions(+), 7 deletions(-) create mode 100644 cmd/zfs_ids_to_path/.gitignore create mode 100644 cmd/zfs_ids_to_path/Makefile.am create mode 100644 cmd/zfs_ids_to_path/zfs_ids_to_path.c create mode 100644 man/man8/zfs_ids_to_path.8 create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/Makefile.am create mode 100755 tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/cleanup.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/setup.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/zfs_ids_to_path_001_pos.ksh diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 6b152e848..88d32b1c5 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = zfs zpool zdb zhack zinject zstream zstreamdump ztest -SUBDIRS += fsck_zfs vdev_id raidz_test +SUBDIRS += fsck_zfs vdev_id raidz_test zfs_ids_to_path if USING_PYTHON SUBDIRS += arcstat arc_summary dbufstat diff --git a/cmd/zfs_ids_to_path/.gitignore b/cmd/zfs_ids_to_path/.gitignore new file mode 100644 index 000000000..f95f853e4 --- /dev/null +++ b/cmd/zfs_ids_to_path/.gitignore @@ -0,0 +1 @@ +zfs_ids_to_path diff --git a/cmd/zfs_ids_to_path/Makefile.am b/cmd/zfs_ids_to_path/Makefile.am new file mode 100644 index 000000000..c509a3862 --- /dev/null +++ b/cmd/zfs_ids_to_path/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/config/Rules.am + +sbin_PROGRAMS = zfs_ids_to_path + +zfs_ids_to_path_SOURCES = \ + zfs_ids_to_path.c + +zfs_ids_to_path_LDADD = \ + $(top_builddir)/lib/libzfs/libzfs.la diff --git a/cmd/zfs_ids_to_path/zfs_ids_to_path.c b/cmd/zfs_ids_to_path/zfs_ids_to_path.c new file mode 100644 index 000000000..48e42bb24 --- /dev/null +++ b/cmd/zfs_ids_to_path/zfs_ids_to_path.c @@ -0,0 +1,96 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2019 by Delphix. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +libzfs_handle_t *g_zfs; + +void +usage(int err) +{ + fprintf(stderr, "Usage: [-v] zfs_ids_to_path " + "\n"); + exit(err); +} + +int +main(int argc, char **argv) +{ + boolean_t verbose = B_FALSE; + char c; + while ((c = getopt(argc, argv, "v")) != -1) { + switch (c) { + case 'v': + verbose = B_TRUE; + break; + } + } + argc -= optind; + argv += optind; + + if (argc != 3) { + (void) fprintf(stderr, "Incorrect number of arguments: %d\n", + argc); + usage(1); + } + + uint64_t objset, object; + if (sscanf(argv[1], "%llu", (u_longlong_t *)&objset) != 1) { + (void) fprintf(stderr, "Invalid objset id: %s\n", argv[2]); + usage(2); + } + if (sscanf(argv[2], "%llu", (u_longlong_t *)&object) != 1) { + (void) fprintf(stderr, "Invalid object id: %s\n", argv[3]); + usage(3); + } + if ((g_zfs = libzfs_init()) == NULL) { + (void) fprintf(stderr, "%s\n", libzfs_error_init(errno)); + return (4); + } + zpool_handle_t *pool = zpool_open(g_zfs, argv[0]); + if (pool == NULL) { + fprintf(stderr, "Could not open pool %s\n", argv[1]); + libzfs_fini(g_zfs); + return (5); + } + + char pathname[PATH_MAX * 2]; + if (verbose) { + zpool_obj_to_path_ds(pool, objset, object, pathname, + sizeof (pathname)); + } else { + zpool_obj_to_path(pool, objset, object, pathname, + sizeof (pathname)); + } + printf("%s\n", pathname); + zpool_close(pool); + libzfs_fini(g_zfs); + return (0); +} diff --git a/configure.ac b/configure.ac index 9f20b2bea..0707384a7 100644 --- a/configure.ac +++ b/configure.ac @@ -76,6 +76,7 @@ AC_CONFIG_FILES([ cmd/zed/Makefile cmd/zed/zed.d/Makefile cmd/zfs/Makefile + cmd/zfs_ids_to_path/Makefile cmd/zgenhostid/Makefile cmd/zhack/Makefile cmd/zinject/Makefile @@ -258,6 +259,7 @@ AC_CONFIG_FILES([ tests/zfs-tests/tests/functional/cli_root/zfs_destroy/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_diff/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_get/Makefile + tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_inherit/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_load-key/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile diff --git a/include/libzfs.h b/include/libzfs.h index dd013ad0c..551b6ae74 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -441,8 +441,10 @@ extern int zpool_events_next(libzfs_handle_t *, nvlist_t **, int *, unsigned, int); extern int zpool_events_clear(libzfs_handle_t *, int *); extern int zpool_events_seek(libzfs_handle_t *, uint64_t, int); +extern void zpool_obj_to_path_ds(zpool_handle_t *, uint64_t, uint64_t, char *, + size_t); extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *, - size_t len); + size_t); extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *); extern int zpool_get_physpath(zpool_handle_t *, char *, size_t); extern void zpool_explain_recover(libzfs_handle_t *, const char *, int, diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 2b21787ee..f0fecd915 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -4362,9 +4362,9 @@ zpool_events_seek(libzfs_handle_t *hdl, uint64_t eid, int zevent_fd) return (error); } -void -zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, - char *pathname, size_t len) +static void +zpool_obj_to_path_impl(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, + char *pathname, size_t len, boolean_t always_unmounted) { zfs_cmd_t zc = {"\0"}; boolean_t mounted = B_FALSE; @@ -4391,7 +4391,8 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, (void) strlcpy(dsname, zc.zc_value, sizeof (dsname)); /* find out if the dataset is mounted */ - mounted = is_mounted(zhp->zpool_hdl, dsname, &mntpnt); + mounted = !always_unmounted && is_mounted(zhp->zpool_hdl, dsname, + &mntpnt); /* get the corrupted object's path */ (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name)); @@ -4412,6 +4413,19 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, free(mntpnt); } +void +zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, + char *pathname, size_t len) +{ + zpool_obj_to_path_impl(zhp, dsobj, obj, pathname, len, B_FALSE); +} + +void +zpool_obj_to_path_ds(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, + char *pathname, size_t len) +{ + zpool_obj_to_path_impl(zhp, dsobj, obj, pathname, len, B_TRUE); +} /* * Wait while the specified activity is in progress in the pool. */ diff --git a/man/man8/Makefile.am b/man/man8/Makefile.am index b7d26570e..7284ba800 100644 --- a/man/man8/Makefile.am +++ b/man/man8/Makefile.am @@ -42,6 +42,7 @@ dist_man_MANS = \ zfs-upgrade.8 \ zfs-userspace.8 \ zfs-wait.8 \ + zfs_ids_to_path.8 \ zgenhostid.8 \ zinject.8 \ zpool.8 \ diff --git a/man/man8/zfs_ids_to_path.8 b/man/man8/zfs_ids_to_path.8 new file mode 100644 index 000000000..d97071599 --- /dev/null +++ b/man/man8/zfs_ids_to_path.8 @@ -0,0 +1,50 @@ +.\" +.\" CDDL HEADER START +.\" +.\" The contents of this file are subject to the terms of the +.\" Common Development and Distribution License (the "License"). +.\" You may not use this file except in compliance with the License. +.\" +.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +.\" or http://www.opensolaris.org/os/licensing. +.\" See the License for the specific language governing permissions +.\" and limitations under the License. +.\" +.\" When distributing Covered Code, include this CDDL HEADER in each +.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. +.\" If applicable, add the following below this CDDL HEADER, with the +.\" fields enclosed by brackets "[]" replaced with your own identifying +.\" information: Portions Copyright [yyyy] [name of copyright owner] +.\" +.\" CDDL HEADER END +.\" +.\" +.\" Copyright (c) 2020 by Delphix. All rights reserved. +.Dd April 17, 2020 +.Dt ZFS_IDS_TO_PATH 8 +.Os Linux +.Sh NAME +.Nm zfs_ids_to_path +.Nd convert objset and object ids to names and paths +.Sh SYNOPSIS +.Nm +.Op Fl v +.Ar pool +.Ar objset id +.Ar object id +.Nm +.Sh DESCRIPTION +.Pp +.LP +The +.Sy zfs_ids_to_path +utility converts a provided objset and object id into a path to the file that +those ids refer to. +.Bl -tag -width "-D" +.It Fl v +Verbose. +Print the dataset name and the file path within the dataset separately. This +will work correctly even if the dataset is not mounted. +.Sh SEE ALSO +.Xr zfs 8 , +.Xr zdb 8 diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index a9bede475..01bab0870 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -169,6 +169,10 @@ tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos', 'zfs_get_008_pos', 'zfs_get_009_pos', 'zfs_get_010_neg'] tags = ['functional', 'cli_root', 'zfs_get'] +[tests/functional/cli_root/zfs_ids_to_path] +tests = ['zfs_ids_to_path_001_pos'] +tags = ['functional', 'cli_root', 'zfs_ids_to_path'] + [tests/functional/cli_root/zfs_inherit] tests = ['zfs_inherit_001_neg', 'zfs_inherit_002_neg', 'zfs_inherit_003_pos', 'zfs_inherit_mountpoint'] diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg index cf65313ac..7bd691e25 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -183,7 +183,8 @@ export ZFS_FILES='zdb zed zgenhostid zstream - zstreamdump' + zstreamdump + zfs_ids_to_path' export ZFSTEST_FILES='btree_test chg_usr_exec diff --git a/tests/zfs-tests/tests/functional/cli_root/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/Makefile.am index 8d99df09f..2595ed8c1 100644 --- a/tests/zfs-tests/tests/functional/cli_root/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/Makefile.am @@ -13,6 +13,7 @@ SUBDIRS = \ zfs_destroy \ zfs_diff \ zfs_get \ + zfs_ids_to_path \ zfs_inherit \ zfs_load-key \ zfs_mount \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/Makefile.am new file mode 100644 index 000000000..5f5e38587 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/Makefile.am @@ -0,0 +1,5 @@ +pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cli_root/zfs_ids_to_path +dist_pkgdata_SCRIPTS = \ + setup.ksh \ + cleanup.ksh \ + zfs_ids_to_path_001_pos.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/cleanup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/cleanup.ksh new file mode 100755 index 000000000..b5ff02217 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/cleanup.ksh @@ -0,0 +1,29 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2020 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +default_cleanup diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/setup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/setup.ksh new file mode 100755 index 000000000..fd6f8f8bb --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/setup.ksh @@ -0,0 +1,31 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2020 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +verify_runnable "global" + +default_setup $DISKS diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/zfs_ids_to_path_001_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/zfs_ids_to_path_001_pos.ksh new file mode 100755 index 000000000..4968e1507 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_ids_to_path/zfs_ids_to_path_001_pos.ksh @@ -0,0 +1,96 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2020 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: Identify the objset id and the object id of a file in a +# filesystem, and verify that zfs_ids_to_path behaves correctly with them. +# +# STRATEGY: +# 1. Create a dateset +# 2. Makes files in the dataset +# 3. Verify that zfs_ids_to_path outputs the correct format for each one +# + +verify_runnable "both" + +function cleanup +{ + destroy_dataset $TESTPOOL/$TESTFS + zfs create -o mountpoint=$TESTDIR $TESTPOOL/$TESTFS +} + +function test_one +{ + typeset ds_id="$1" + typeset ds_path="$2" + typeset file_path="$3" + + typeset mntpnt=$(get_prop mountpoint $ds_path) + typeset file_id=$(ls -i /$mntpnt/$file_path | sed 's/ .*//') + typeset output=$(zfs_ids_to_path $TESTPOOL $ds_id $file_id) + [[ "$output" == "$mntpnt/$file_path" ]] || \ + log_fail "Incorrect output for non-verbose while mounted: $output" + output=$(zfs_ids_to_path -v $TESTPOOL $ds_id $file_id) + [[ "$output" == "$ds_path:/$file_path" ]] || \ + log_fail "Incorrect output for verbose while mounted: $output" + log_must zfs unmount $ds_path + output=$(zfs_ids_to_path $TESTPOOL $ds_id $file_id) + [[ "$output" == "$ds_path:/$file_path" ]] || \ + log_fail "Incorrect output for non-verbose while unmounted: $output" + output=$(zfs_ids_to_path -v $TESTPOOL $ds_id $file_id) + [[ "$output" == "$ds_path:/$file_path" ]] || \ + log_fail "Incorrect output for verbose while unmounted: $output" + log_must zfs mount $ds_path +} + +log_onexit cleanup + +typeset BASE=$TESTPOOL/$TESTFS +typeset TESTFILE1=f1 +typeset TESTDIR1=d1 +typeset TESTFILE2=d1/f2 +typeset TESTDIR2=d1/d2 +typeset TESTFILE3=d1/d2/f3 +typeset TESTFILE4=d1/d2/f4 + +typeset mntpnt=$(get_prop mountpoint $BASE) + +log_must touch /$mntpnt/$TESTFILE1 +log_must mkdir /$mntpnt/$TESTDIR1 +log_must touch /$mntpnt/$TESTFILE2 +log_must mkdir /$mntpnt/$TESTDIR2 +log_must touch /$mntpnt/$TESTFILE3 +log_must touch /$mntpnt/$TESTFILE4 + +typeset ds_id=$(zdb $BASE | grep "^Dataset" | sed 's/.* ID \([0-9]*\).*/\1/') +test_one $ds_id $BASE $TESTFILE1 +test_one $ds_id $BASE $TESTFILE2 +test_one $ds_id $BASE $TESTFILE3 +test_one $ds_id $BASE $TESTFILE4 + +log_pass "zfs_ids_to_path displayed correctly"