3131 *
3232 * [1] Portions of this software were developed by Allan Jude
3333 * under sponsorship from the FreeBSD Foundation.
34+ * Copyright (c) 2021 Allan Jude
3435 */
3536
3637#include <stdio.h>
@@ -755,13 +756,14 @@ usage(void)
755756 "\t%s -m [-AFLPX] [-e [-V] [-p <path> ...]] [-t <txg>] "
756757 "[-U <cache>]\n\t\t<poolname> [<vdev> [<metaslab> ...]]\n"
757758 "\t%s -O <dataset> <path>\n"
759+ "\t%s -r <dataset> <path> <destination>\n"
758760 "\t%s -R [-A] [-e [-V] [-p <path> ...]] [-U <cache>]\n"
759761 "\t\t<poolname> <vdev>:<offset>:<size>[:<flags>]\n"
760762 "\t%s -E [-A] word0:word1:...:word15\n"
761763 "\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] "
762764 "<poolname>\n\n" ,
763765 cmdname , cmdname , cmdname , cmdname , cmdname , cmdname , cmdname ,
764- cmdname , cmdname , cmdname );
766+ cmdname , cmdname , cmdname , cmdname );
765767
766768 (void ) fprintf (stderr , " Dataset name must include at least one "
767769 "separator character '/' or '@'\n" );
@@ -800,6 +802,7 @@ usage(void)
800802 (void ) fprintf (stderr , " -m metaslabs\n" );
801803 (void ) fprintf (stderr , " -M metaslab groups\n" );
802804 (void ) fprintf (stderr , " -O perform object lookups by path\n" );
805+ (void ) fprintf (stderr , " -r copy an object by path to file\n" );
803806 (void ) fprintf (stderr , " -R read and display block from a "
804807 "device\n" );
805808 (void ) fprintf (stderr , " -s report stats on zdb's I/O\n" );
@@ -4490,7 +4493,7 @@ static char curpath[PATH_MAX];
44904493 * for the last one.
44914494 */
44924495static int
4493- dump_path_impl (objset_t * os , uint64_t obj , char * name )
4496+ dump_path_impl (objset_t * os , uint64_t obj , char * name , uint64_t * retobj )
44944497{
44954498 int err ;
44964499 boolean_t header = B_TRUE ;
@@ -4540,10 +4543,15 @@ dump_path_impl(objset_t *os, uint64_t obj, char *name)
45404543 switch (doi .doi_type ) {
45414544 case DMU_OT_DIRECTORY_CONTENTS :
45424545 if (s != NULL && * (s + 1 ) != '\0' )
4543- return (dump_path_impl (os , child_obj , s + 1 ));
4546+ return (dump_path_impl (os , child_obj , s + 1 , retobj ));
45444547 /*FALLTHROUGH*/
45454548 case DMU_OT_PLAIN_FILE_CONTENTS :
4546- dump_object (os , child_obj , dump_opt ['v' ], & header , NULL , 0 );
4549+ if (retobj != NULL ) {
4550+ * retobj = child_obj ;
4551+ } else {
4552+ dump_object (os , child_obj , dump_opt ['v' ], & header ,
4553+ NULL , 0 );
4554+ }
45474555 return (0 );
45484556 default :
45494557 (void ) fprintf (stderr , "object %llu has non-file/directory "
@@ -4558,7 +4566,7 @@ dump_path_impl(objset_t *os, uint64_t obj, char *name)
45584566 * Dump the blocks for the object specified by path inside the dataset.
45594567 */
45604568static int
4561- dump_path (char * ds , char * path )
4569+ dump_path (char * ds , char * path , uint64_t * retobj )
45624570{
45634571 int err ;
45644572 objset_t * os ;
@@ -4578,12 +4586,89 @@ dump_path(char *ds, char *path)
45784586
45794587 (void ) snprintf (curpath , sizeof (curpath ), "dataset=%s path=/" , ds );
45804588
4581- err = dump_path_impl (os , root_obj , path );
4589+ err = dump_path_impl (os , root_obj , path , retobj );
45824590
45834591 close_objset (os , FTAG );
45844592 return (err );
45854593}
45864594
4595+ static int
4596+ zdb_copy_object (objset_t * os , uint64_t srcobj , char * destfile )
4597+ {
4598+ int err = 0 ;
4599+ uint64_t size , readsize , oursize , offset ;
4600+ ssize_t writesize ;
4601+ sa_handle_t * hdl ;
4602+
4603+ (void ) printf ("Copying object %" PRIu64 " to file %s\n" , srcobj ,
4604+ destfile );
4605+
4606+ VERIFY3P (os , = = , sa_os );
4607+ if ((err = sa_handle_get (os , srcobj , NULL , SA_HDL_PRIVATE , & hdl ))) {
4608+ (void ) printf ("Failed to get handle for SA znode\n" );
4609+ return (err );
4610+ }
4611+ if ((err = sa_lookup (hdl , sa_attr_table [ZPL_SIZE ], & size , 8 ))) {
4612+ (void ) sa_handle_destroy (hdl );
4613+ return (err );
4614+ }
4615+ (void ) sa_handle_destroy (hdl );
4616+
4617+ (void ) printf ("Object %" PRIu64 " is %" PRIu64 " bytes\n" , srcobj ,
4618+ size );
4619+ if (size == 0 ) {
4620+ return (EINVAL );
4621+ }
4622+
4623+ int fd = open (destfile , O_WRONLY | O_CREAT | O_TRUNC , 0644 );
4624+ /*
4625+ * We cap the size at 1 mebibyte here to prevent
4626+ * allocation failures and nigh-infinite printing if the
4627+ * object is extremely large.
4628+ */
4629+ oursize = MIN (size , 1 << 20 );
4630+ offset = 0 ;
4631+ char * buf = kmem_alloc (oursize , KM_NOSLEEP );
4632+ if (buf == NULL ) {
4633+ return (ENOMEM );
4634+ }
4635+
4636+ while (offset < size ) {
4637+ readsize = MIN (size - offset , 1 << 20 );
4638+ err = dmu_read (os , srcobj , offset , readsize , buf , 0 );
4639+ if (err != 0 ) {
4640+ (void ) printf ("got error %u from dmu_read\n" , err );
4641+ kmem_free (buf , oursize );
4642+ return (err );
4643+ }
4644+ if (dump_opt ['v' ] > 3 ) {
4645+ (void ) printf ("Read offset=%" PRIu64 " size=%" PRIu64
4646+ " error=%d\n" , offset , readsize , err );
4647+ }
4648+
4649+ writesize = write (fd , buf , readsize );
4650+ if (writesize < 0 ) {
4651+ err = errno ;
4652+ break ;
4653+ } else if (writesize != readsize ) {
4654+ /* Incomplete write */
4655+ (void ) fprintf (stderr , "Short write, only wrote %llu of"
4656+ " %" PRIu64 " bytes, exiting...\n" ,
4657+ (u_longlong_t )writesize , readsize );
4658+ break ;
4659+ }
4660+
4661+ offset += readsize ;
4662+ }
4663+
4664+ (void ) close (fd );
4665+
4666+ if (buf != NULL )
4667+ kmem_free (buf , oursize );
4668+
4669+ return (err );
4670+ }
4671+
45874672static int
45884673dump_label (const char * dev )
45894674{
@@ -8167,6 +8252,7 @@ main(int argc, char **argv)
81678252 nvlist_t * policy = NULL ;
81688253 uint64_t max_txg = UINT64_MAX ;
81698254 int64_t objset_id = -1 ;
8255+ uint64_t object ;
81708256 int flags = ZFS_IMPORT_MISSING_LOG ;
81718257 int rewind = ZPOOL_NEVER_REWIND ;
81728258 char * spa_config_path_env , * objset_str ;
@@ -8195,7 +8281,7 @@ main(int argc, char **argv)
81958281 zfs_btree_verify_intensity = 3 ;
81968282
81978283 while ((c = getopt (argc , argv ,
8198- "AbcCdDeEFGhiI:klLmMo:Op:PqRsSt :uU:vVx:XYyZ" )) != -1 ) {
8284+ "AbcCdDeEFGhiI:klLmMo:Op:PqrRsSt :uU:vVx:XYyZ" )) != -1 ) {
81998285 switch (c ) {
82008286 case 'b' :
82018287 case 'c' :
@@ -8210,6 +8296,7 @@ main(int argc, char **argv)
82108296 case 'm' :
82118297 case 'M' :
82128298 case 'O' :
8299+ case 'r' :
82138300 case 'R' :
82148301 case 's' :
82158302 case 'S' :
@@ -8299,7 +8386,7 @@ main(int argc, char **argv)
82998386 (void ) fprintf (stderr , "-p option requires use of -e\n" );
83008387 usage ();
83018388 }
8302- if (dump_opt ['d' ]) {
8389+ if (dump_opt ['d' ] || dump_opt [ 'r' ] ) {
83038390 /* <pool>[/<dataset | objset id> is accepted */
83048391 if (argv [2 ] && (objset_str = strchr (argv [2 ], '/' )) != NULL &&
83058392 objset_str ++ != NULL ) {
@@ -8358,7 +8445,7 @@ main(int argc, char **argv)
83588445 verbose = MAX (verbose , 1 );
83598446
83608447 for (c = 0 ; c < 256 ; c ++ ) {
8361- if (dump_all && strchr ("AeEFklLOPRSXy " , c ) == NULL )
8448+ if (dump_all && strchr ("AeEFklLOPrRSXy " , c ) == NULL )
83628449 dump_opt [c ] = 1 ;
83638450 if (dump_opt [c ])
83648451 dump_opt [c ] += verbose ;
@@ -8394,7 +8481,13 @@ main(int argc, char **argv)
83948481 if (argc != 2 )
83958482 usage ();
83968483 dump_opt ['v' ] = verbose + 3 ;
8397- return (dump_path (argv [0 ], argv [1 ]));
8484+ return (dump_path (argv [0 ], argv [1 ], NULL ));
8485+ }
8486+ if (dump_opt ['r' ]) {
8487+ if (argc != 3 )
8488+ usage ();
8489+ dump_opt ['v' ] = verbose ;
8490+ error = dump_path (argv [0 ], argv [1 ], & object );
83988491 }
83998492
84008493 if (dump_opt ['X' ] || dump_opt ['F' ])
@@ -8572,7 +8665,9 @@ main(int argc, char **argv)
85728665
85738666 argv ++ ;
85748667 argc -- ;
8575- if (!dump_opt ['R' ]) {
8668+ if (dump_opt ['r' ]) {
8669+ error = zdb_copy_object (os , object , argv [1 ]);
8670+ } else if (!dump_opt ['R' ]) {
85768671 flagbits ['d' ] = ZOR_FLAG_DIRECTORY ;
85778672 flagbits ['f' ] = ZOR_FLAG_PLAIN_FILE ;
85788673 flagbits ['m' ] = ZOR_FLAG_SPACE_MAP ;
0 commit comments