3737#include <assert.h>
3838#include <ctype.h>
3939#include <sys/debug.h>
40+ #include <dirent.h>
4041#include <errno.h>
4142#include <getopt.h>
4243#include <libgen.h>
@@ -121,6 +122,7 @@ static int zfs_do_change_key(int argc, char **argv);
121122static int zfs_do_project (int argc , char * * argv );
122123static int zfs_do_version (int argc , char * * argv );
123124static int zfs_do_redact (int argc , char * * argv );
125+ static int zfs_do_rewrite (int argc , char * * argv );
124126static int zfs_do_wait (int argc , char * * argv );
125127
126128#ifdef __FreeBSD__
@@ -193,6 +195,7 @@ typedef enum {
193195 HELP_CHANGE_KEY ,
194196 HELP_VERSION ,
195197 HELP_REDACT ,
198+ HELP_REWRITE ,
196199 HELP_JAIL ,
197200 HELP_UNJAIL ,
198201 HELP_WAIT ,
@@ -227,7 +230,7 @@ static zfs_command_t command_table[] = {
227230 { "promote" , zfs_do_promote , HELP_PROMOTE },
228231 { "rename" , zfs_do_rename , HELP_RENAME },
229232 { "bookmark" , zfs_do_bookmark , HELP_BOOKMARK },
230- { "program" , zfs_do_channel_program , HELP_CHANNEL_PROGRAM },
233+ { "diff" , zfs_do_diff , HELP_DIFF },
231234 { NULL },
232235 { "list" , zfs_do_list , HELP_LIST },
233236 { NULL },
@@ -249,27 +252,31 @@ static zfs_command_t command_table[] = {
249252 { NULL },
250253 { "send" , zfs_do_send , HELP_SEND },
251254 { "receive" , zfs_do_receive , HELP_RECEIVE },
255+ { "redact" , zfs_do_redact , HELP_REDACT },
252256 { NULL },
253257 { "allow" , zfs_do_allow , HELP_ALLOW },
254- { NULL },
255258 { "unallow" , zfs_do_unallow , HELP_UNALLOW },
256259 { NULL },
257260 { "hold" , zfs_do_hold , HELP_HOLD },
258261 { "holds" , zfs_do_holds , HELP_HOLDS },
259262 { "release" , zfs_do_release , HELP_RELEASE },
260- { "diff" , zfs_do_diff , HELP_DIFF },
263+ { NULL },
261264 { "load-key" , zfs_do_load_key , HELP_LOAD_KEY },
262265 { "unload-key" , zfs_do_unload_key , HELP_UNLOAD_KEY },
263266 { "change-key" , zfs_do_change_key , HELP_CHANGE_KEY },
264- { "redact" , zfs_do_redact , HELP_REDACT },
267+ { NULL },
268+ { "program" , zfs_do_channel_program , HELP_CHANNEL_PROGRAM },
269+ { "rewrite" , zfs_do_rewrite , HELP_REWRITE },
265270 { "wait" , zfs_do_wait , HELP_WAIT },
266271
267272#ifdef __FreeBSD__
273+ { NULL },
268274 { "jail" , zfs_do_jail , HELP_JAIL },
269275 { "unjail" , zfs_do_unjail , HELP_UNJAIL },
270276#endif
271277
272278#ifdef __linux__
279+ { NULL },
273280 { "zone" , zfs_do_zone , HELP_ZONE },
274281 { "unzone" , zfs_do_unzone , HELP_UNZONE },
275282#endif
@@ -432,6 +439,9 @@ get_usage(zfs_help_t idx)
432439 case HELP_REDACT :
433440 return (gettext ("\tredact <snapshot> <bookmark> "
434441 "<redaction_snapshot> ...\n" ));
442+ case HELP_REWRITE :
443+ return (gettext ("\trewrite [-rx] [-o <offset>] [-l <length>] "
444+ "<directory|file ...>\n" ));
435445 case HELP_JAIL :
436446 return (gettext ("\tjail <jailid|jailname> <filesystem>\n" ));
437447 case HELP_UNJAIL :
@@ -9016,6 +9026,186 @@ zfs_do_project(int argc, char **argv)
90169026 return (ret );
90179027}
90189028
9029+ static int
9030+ zfs_rewrite_file (const char * path , zfs_rewrite_args_t * args )
9031+ {
9032+ int fd , ret = 0 ;
9033+
9034+ fd = open (path , O_WRONLY );
9035+ if (fd < 0 ) {
9036+ ret = errno ;
9037+ (void ) fprintf (stderr , gettext ("failed to open %s: %s\n" ),
9038+ path , strerror (errno ));
9039+ return (ret );
9040+ }
9041+
9042+ if (ioctl (fd , ZFS_IOC_REWRITE , args ) < 0 ) {
9043+ ret = errno ;
9044+ (void ) fprintf (stderr , gettext ("failed to rewrite %s: %s\n" ),
9045+ path , strerror (errno ));
9046+ }
9047+
9048+ close (fd );
9049+ return (ret );
9050+ }
9051+
9052+ static int
9053+ zfs_rewrite_dir (const char * path , boolean_t xdev , dev_t dev ,
9054+ zfs_rewrite_args_t * args , nvlist_t * dirs )
9055+ {
9056+ struct dirent * ent ;
9057+ DIR * dir ;
9058+ int ret = 0 , err ;
9059+
9060+ dir = opendir (path );
9061+ if (dir == NULL ) {
9062+ if (errno == ENOENT )
9063+ return (0 );
9064+ ret = errno ;
9065+ (void ) fprintf (stderr , gettext ("failed to opendir %s: %s\n" ),
9066+ path , strerror (errno ));
9067+ return (ret );
9068+ }
9069+
9070+ size_t plen = strlen (path ) + 1 ;
9071+ while ((ent = readdir (dir )) != NULL ) {
9072+ char * fullname ;
9073+ struct stat st ;
9074+
9075+ if (ent -> d_type != DT_REG && ent -> d_type != DT_DIR )
9076+ continue ;
9077+
9078+ if (strcmp (ent -> d_name , "." ) == 0 ||
9079+ strcmp (ent -> d_name , ".." ) == 0 )
9080+ continue ;
9081+
9082+ if (plen + strlen (ent -> d_name ) >= PATH_MAX ) {
9083+ (void ) fprintf (stderr , gettext ("path too long %s/%s\n" ),
9084+ path , ent -> d_name );
9085+ ret = ENAMETOOLONG ;
9086+ continue ;
9087+ }
9088+
9089+ if (asprintf (& fullname , "%s/%s" , path , ent -> d_name ) == -1 ) {
9090+ (void ) fprintf (stderr ,
9091+ gettext ("failed to allocate memory\n" ));
9092+ ret = ENOMEM ;
9093+ continue ;
9094+ }
9095+
9096+ if (xdev ) {
9097+ if (stat (fullname , & st ) < 0 ) {
9098+ ret = errno ;
9099+ (void ) fprintf (stderr ,
9100+ gettext ("failed to stat %s: %s\n" ),
9101+ fullname , strerror (errno ));
9102+ free (fullname );
9103+ continue ;
9104+ }
9105+ if (st .st_dev != dev ) {
9106+ free (fullname );
9107+ continue ;
9108+ }
9109+ }
9110+
9111+ if (ent -> d_type == DT_REG ) {
9112+ err = zfs_rewrite_file (fullname , args );
9113+ if (err )
9114+ ret = err ;
9115+ } else { /* DT_DIR */
9116+ fnvlist_add_uint64 (dirs , fullname , dev );
9117+ }
9118+
9119+ free (fullname );
9120+ }
9121+
9122+ closedir (dir );
9123+ return (ret );
9124+ }
9125+
9126+ static int
9127+ zfs_rewrite_path (const char * path , boolean_t recurse , boolean_t xdev ,
9128+ zfs_rewrite_args_t * args , nvlist_t * dirs )
9129+ {
9130+ struct stat st ;
9131+ int ret = 0 ;
9132+
9133+ if (stat (path , & st ) < 0 ) {
9134+ ret = errno ;
9135+ (void ) fprintf (stderr , gettext ("failed to stat %s: %s\n" ),
9136+ path , strerror (errno ));
9137+ return (ret );
9138+ }
9139+
9140+ if (S_ISREG (st .st_mode ))
9141+ ret = zfs_rewrite_file (path , args );
9142+ else if (S_ISDIR (st .st_mode ) && recurse )
9143+ ret = zfs_rewrite_dir (path , xdev , st .st_dev , args , dirs );
9144+ return (ret );
9145+ }
9146+
9147+ static int
9148+ zfs_do_rewrite (int argc , char * * argv )
9149+ {
9150+ int ret = 0 , err , c ;
9151+ boolean_t recurse = B_FALSE , xdev = B_FALSE ;
9152+
9153+ if (argc < 2 )
9154+ usage (B_FALSE );
9155+
9156+ zfs_rewrite_args_t args ;
9157+ args .off = 0 ;
9158+ args .len = 0 ;
9159+ args .flags = 0 ;
9160+
9161+ while ((c = getopt (argc , argv , "l:o:rx" )) != -1 ) {
9162+ switch (c ) {
9163+ case 'l' :
9164+ args .len = strtoll (optarg , NULL , 0 );
9165+ break ;
9166+ case 'o' :
9167+ args .off = strtoll (optarg , NULL , 0 );
9168+ break ;
9169+ case 'r' :
9170+ recurse = B_TRUE ;
9171+ break ;
9172+ case 'x' :
9173+ xdev = B_TRUE ;
9174+ break ;
9175+ default :
9176+ (void ) fprintf (stderr , gettext ("invalid option '%c'\n" ),
9177+ optopt );
9178+ usage (B_FALSE );
9179+ }
9180+ }
9181+
9182+ argv += optind ;
9183+ argc -= optind ;
9184+ if (argc == 0 ) {
9185+ (void ) fprintf (stderr ,
9186+ gettext ("missing file or directory target(s)\n" ));
9187+ usage (B_FALSE );
9188+ }
9189+
9190+ nvlist_t * dirs = fnvlist_alloc ();
9191+ for (int i = 0 ; i < argc ; i ++ ) {
9192+ err = zfs_rewrite_path (argv [i ], recurse , xdev , & args , dirs );
9193+ if (err )
9194+ ret = err ;
9195+ }
9196+ nvpair_t * dir ;
9197+ while ((dir = nvlist_next_nvpair (dirs , NULL )) != NULL ) {
9198+ err = zfs_rewrite_dir (nvpair_name (dir ), xdev ,
9199+ fnvpair_value_uint64 (dir ), & args , dirs );
9200+ if (err )
9201+ ret = err ;
9202+ fnvlist_remove_nvpair (dirs , dir );
9203+ }
9204+ fnvlist_free (dirs );
9205+
9206+ return (ret );
9207+ }
9208+
90199209static int
90209210zfs_do_wait (int argc , char * * argv )
90219211{
0 commit comments