File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / patches / fileflags.diff
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:32:36 2021 UTC (4 years ago) by misho
Branches: rsync, MAIN
CVS tags: v3_2_3, HEAD
rsync 3.2.3

    1: This patch provides --fileflags, which preserves the st_flags stat() field.
    2: Modified from a patch that was written by Rolf Grossmann.
    3: 
    4: To use this patch, run these commands for a successful build:
    5: 
    6:     patch -p1 <patches/fileflags.diff
    7:     ./configure                         (optional if already run)
    8:     make
    9: 
   10: based-on: e94bad1c156fc3910f24e2b3b71a81b0b0bdeb70
   11: diff --git a/compat.c b/compat.c
   12: --- a/compat.c
   13: +++ b/compat.c
   14: @@ -39,6 +39,7 @@ extern int checksum_seed;
   15:  extern int basis_dir_cnt;
   16:  extern int prune_empty_dirs;
   17:  extern int protocol_version;
   18: +extern int force_change;
   19:  extern int protect_args;
   20:  extern int preserve_uid;
   21:  extern int preserve_gid;
   22: @@ -46,6 +47,7 @@ extern int preserve_atimes;
   23:  extern int preserve_crtimes;
   24:  extern int preserve_acls;
   25:  extern int preserve_xattrs;
   26: +extern int preserve_fileflags;
   27:  extern int xfer_flags_as_varint;
   28:  extern int need_messages_from_generator;
   29:  extern int delete_mode, delete_before, delete_during, delete_after;
   30: @@ -77,7 +79,7 @@ int do_negotiated_strings = 0;
   31:  int xmit_id0_names = 0;
   32:  
   33:  /* These index values are for the file-list's extra-attribute array. */
   34: -int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
   35: +int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
   36:  
   37:  int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
   38:  int sender_symlink_iconv = 0;	/* sender should convert symlink content */
   39: @@ -566,6 +568,8 @@ void setup_protocol(int f_out,int f_in)
   40:  		uid_ndx = ++file_extra_cnt;
   41:  	if (preserve_gid)
   42:  		gid_ndx = ++file_extra_cnt;
   43: +	if (preserve_fileflags || (force_change && !am_sender))
   44: +		fileflags_ndx = ++file_extra_cnt;
   45:  	if (preserve_acls && !am_sender)
   46:  		acls_ndx = ++file_extra_cnt;
   47:  	if (preserve_xattrs)
   48: @@ -726,6 +730,10 @@ void setup_protocol(int f_out,int f_in)
   49:  			fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
   50:  			exit_cleanup(RERR_PROTOCOL);
   51:  		}
   52: +		if (!xfer_flags_as_varint && preserve_fileflags) {
   53: +			fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --fileflags.\n");
   54: +			exit_cleanup(RERR_PROTOCOL);
   55: +		}
   56:  		if (am_sender) {
   57:  			receiver_symlink_times = am_server
   58:  			    ? strchr(client_info, 'L') != NULL
   59: diff --git a/delete.c b/delete.c
   60: --- a/delete.c
   61: +++ b/delete.c
   62: @@ -25,6 +25,7 @@
   63:  extern int am_root;
   64:  extern int make_backups;
   65:  extern int max_delete;
   66: +extern int force_change;
   67:  extern char *backup_dir;
   68:  extern char *backup_suffix;
   69:  extern int backup_suffix_len;
   70: @@ -97,8 +98,12 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
   71:  		}
   72:  
   73:  		strlcpy(p, fp->basename, remainder);
   74: +#ifdef SUPPORT_FORCE_CHANGE
   75: +		if (force_change)
   76: +			make_mutable(fname, fp->mode, F_FFLAGS(fp), force_change);
   77: +#endif
   78:  		if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
   79: -			do_chmod(fname, fp->mode | S_IWUSR);
   80: +			do_chmod(fname, fp->mode | S_IWUSR, NO_FFLAGS);
   81:  		/* Save stack by recursing to ourself directly. */
   82:  		if (S_ISDIR(fp->mode)) {
   83:  			if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
   84: @@ -139,11 +144,18 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
   85:  	}
   86:  
   87:  	if (flags & DEL_NO_UID_WRITE)
   88: -		do_chmod(fbuf, mode | S_IWUSR);
   89: +		do_chmod(fbuf, mode | S_IWUSR, NO_FFLAGS);
   90:  
   91:  	if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
   92:  		/* This only happens on the first call to delete_item() since
   93:  		 * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
   94: +#ifdef SUPPORT_FORCE_CHANGE
   95: +		if (force_change) {
   96: +			STRUCT_STAT st;
   97: +			if (x_lstat(fbuf, &st, NULL) == 0)
   98: +				make_mutable(fbuf, st.st_mode, st.st_flags, force_change);
   99: +		}
  100: +#endif
  101:  		ignore_perishable = 1;
  102:  		/* If DEL_RECURSE is not set, this just reports emptiness. */
  103:  		ret = delete_dir_contents(fbuf, flags);
  104: diff --git a/flist.c b/flist.c
  105: --- a/flist.c
  106: +++ b/flist.c
  107: @@ -52,6 +52,7 @@ extern int preserve_links;
  108:  extern int preserve_hard_links;
  109:  extern int preserve_devices;
  110:  extern int preserve_specials;
  111: +extern int preserve_fileflags;
  112:  extern int delete_during;
  113:  extern int missing_args;
  114:  extern int eol_nulls;
  115: @@ -383,6 +384,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
  116:  	static time_t crtime;
  117:  #endif
  118:  	static mode_t mode;
  119: +#ifdef SUPPORT_FILEFLAGS
  120: +	static uint32 fileflags;
  121: +#endif
  122:  #ifdef SUPPORT_HARD_LINKS
  123:  	static int64 dev;
  124:  #endif
  125: @@ -426,6 +430,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
  126:  		xflags |= XMIT_SAME_MODE;
  127:  	else
  128:  		mode = file->mode;
  129: +#ifdef SUPPORT_FILEFLAGS
  130: +	if (preserve_fileflags) {
  131: +		if (F_FFLAGS(file) == fileflags)
  132: +			xflags |= XMIT_SAME_FLAGS;
  133: +		else
  134: +			fileflags = F_FFLAGS(file);
  135: +	}
  136: +#endif
  137:  
  138:  	if (preserve_devices && IS_DEVICE(mode)) {
  139:  		if (protocol_version < 28) {
  140: @@ -587,6 +599,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
  141:  #endif
  142:  	if (!(xflags & XMIT_SAME_MODE))
  143:  		write_int(f, to_wire_mode(mode));
  144: +#ifdef SUPPORT_FILEFLAGS
  145: +	if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
  146: +		write_int(f, (int)fileflags);
  147: +#endif
  148:  	if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
  149:  		write_varlong(f, atime, 4);
  150:  	if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
  151: @@ -681,6 +697,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
  152:  	static time_t crtime;
  153:  #endif
  154:  	static mode_t mode;
  155: +#ifdef SUPPORT_FILEFLAGS
  156: +	static uint32 fileflags;
  157: +#endif
  158:  #ifdef SUPPORT_HARD_LINKS
  159:  	static int64 dev;
  160:  #endif
  161: @@ -797,6 +816,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
  162:  #ifdef SUPPORT_CRTIMES
  163:  			if (crtimes_ndx)
  164:  				crtime = F_CRTIME(first);
  165: +#endif
  166: +#ifdef SUPPORT_FILEFLAGS
  167: +			if (preserve_fileflags)
  168: +				fileflags = F_FFLAGS(first);
  169:  #endif
  170:  			if (preserve_uid)
  171:  				uid = F_OWNER(first);
  172: @@ -869,6 +892,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
  173:  
  174:  	if (chmod_modes && !S_ISLNK(mode) && mode)
  175:  		mode = tweak_mode(mode, chmod_modes);
  176: +#ifdef SUPPORT_FILEFLAGS
  177: +	if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
  178: +		fileflags = (uint32)read_int(f);
  179: +#endif
  180:  
  181:  	if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
  182:  		if (protocol_version < 30)
  183: @@ -1027,6 +1054,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
  184:  	}
  185:  #endif
  186:  	file->mode = mode;
  187: +#ifdef SUPPORT_FILEFLAGS
  188: +	if (preserve_fileflags)
  189: +		F_FFLAGS(file) = fileflags;
  190: +#endif
  191:  	if (preserve_uid)
  192:  		F_OWNER(file) = uid;
  193:  	if (preserve_gid) {
  194: @@ -1428,6 +1459,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
  195:  	}
  196:  #endif
  197:  	file->mode = st.st_mode;
  198: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
  199: +	if (fileflags_ndx)
  200: +		F_FFLAGS(file) = st.st_flags;
  201: +#endif
  202:  	if (preserve_uid)
  203:  		F_OWNER(file) = st.st_uid;
  204:  	if (preserve_gid)
  205: diff --git a/generator.c b/generator.c
  206: --- a/generator.c
  207: +++ b/generator.c
  208: @@ -43,8 +43,10 @@ extern int write_devices;
  209:  extern int preserve_specials;
  210:  extern int preserve_hard_links;
  211:  extern int preserve_executability;
  212: +extern int preserve_fileflags;
  213:  extern int preserve_perms;
  214:  extern int preserve_times;
  215: +extern int force_change;
  216:  extern int delete_mode;
  217:  extern int delete_before;
  218:  extern int delete_during;
  219: @@ -487,6 +489,10 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
  220:  			return 0;
  221:  		if (perms_differ(file, sxp))
  222:  			return 0;
  223: +#ifdef SUPPORT_FILEFLAGS
  224: +		if (preserve_fileflags && sxp->st.st_flags != F_FFLAGS(file))
  225: +			return 0;
  226: +#endif
  227:  		if (ownership_differs(file, sxp))
  228:  			return 0;
  229:  #ifdef SUPPORT_ACLS
  230: @@ -548,6 +554,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
  231:  			iflags |= ITEM_REPORT_OWNER;
  232:  		if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
  233:  			iflags |= ITEM_REPORT_GROUP;
  234: +#ifdef SUPPORT_FILEFLAGS
  235: +		if (preserve_fileflags && !S_ISLNK(file->mode)
  236: +		 && sxp->st.st_flags != F_FFLAGS(file))
  237: +			iflags |= ITEM_REPORT_FFLAGS;
  238: +#endif
  239:  #ifdef SUPPORT_ACLS
  240:  		if (preserve_acls && !S_ISLNK(file->mode)) {
  241:  			if (!ACL_READY(*sxp))
  242: @@ -1442,6 +1453,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
  243:  		if (!preserve_perms) { /* See comment in non-dir code below. */
  244:  			file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, statret == 0);
  245:  		}
  246: +#ifdef SUPPORT_FORCE_CHANGE
  247: +		if (force_change && !preserve_fileflags)
  248: +			F_FFLAGS(file) = sx.st.st_flags;
  249: +#endif
  250:  		if (statret != 0 && basis_dir[0] != NULL) {
  251:  			int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code);
  252:  			if (j == -2) {
  253: @@ -1484,10 +1499,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
  254:  		 * readable and writable permissions during the time we are
  255:  		 * putting files within them.  This is then restored to the
  256:  		 * former permissions after the transfer is done. */
  257: +#ifdef SUPPORT_FORCE_CHANGE
  258: +		if (force_change && F_FFLAGS(file) & force_change
  259: +		 && make_mutable(fname, file->mode, F_FFLAGS(file), force_change))
  260: +			need_retouch_dir_perms = 1;
  261: +#endif
  262:  #ifdef HAVE_CHMOD
  263:  		if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
  264:  			mode_t mode = file->mode | S_IRWXU;
  265: -			if (do_chmod(fname, mode) < 0) {
  266: +			if (do_chmod(fname, mode, 0) < 0) {
  267:  				rsyserr(FERROR_XFER, errno,
  268:  					"failed to modify permissions on %s",
  269:  					full_fname(fname));
  270: @@ -1522,6 +1542,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
  271:  		int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
  272:  		file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, exists);
  273:  	}
  274: +#ifdef SUPPORT_FORCE_CHANGE
  275: +	if (force_change && !preserve_fileflags)
  276: +		F_FFLAGS(file) = sx.st.st_flags;
  277: +#endif
  278:  
  279:  #ifdef SUPPORT_HARD_LINKS
  280:  	if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
  281: @@ -2102,17 +2126,25 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
  282:  			continue;
  283:  		fname = f_name(file, NULL);
  284:  		if (fix_dir_perms)
  285: -			do_chmod(fname, file->mode);
  286: +			do_chmod(fname, file->mode, 0);
  287:  		if (need_retouch_dir_times) {
  288:  			STRUCT_STAT st;
  289:  			if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
  290:  				st.st_mtime = file->modtime;
  291:  #ifdef ST_MTIME_NSEC
  292:  				st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
  293: +#endif
  294: +#ifdef SUPPORT_FORCE_CHANGE
  295: +				st.st_mode = file->mode;
  296: +				st.st_flags = 0;
  297:  #endif
  298:  				set_times(fname, &st);
  299:  			}
  300:  		}
  301: +#ifdef SUPPORT_FORCE_CHANGE
  302: +		if (force_change && F_FFLAGS(file) & force_change)
  303: +			undo_make_mutable(fname, F_FFLAGS(file));
  304: +#endif
  305:  		if (counter >= loopchk_limit) {
  306:  			if (allowed_lull)
  307:  				maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
  308: diff --git a/log.c b/log.c
  309: --- a/log.c
  310: +++ b/log.c
  311: @@ -725,7 +725,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
  312:  			     : iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
  313:  			c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
  314:  			c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
  315: -			c[11] = '\0';
  316: +			c[11] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
  317: +			c[12] = '\0';
  318:  
  319:  			if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
  320:  				char ch = iflags & ITEM_IS_NEW ? '+' : '?';
  321: diff --git a/main.c b/main.c
  322: --- a/main.c
  323: +++ b/main.c
  324: @@ -31,6 +31,9 @@
  325:  #ifdef __TANDEM
  326:  #include <floss.h(floss_execlp)>
  327:  #endif
  328: +#ifdef SUPPORT_FORCE_CHANGE
  329: +#include <sys/sysctl.h>
  330: +#endif
  331:  
  332:  extern int dry_run;
  333:  extern int list_only;
  334: @@ -48,6 +51,7 @@ extern int called_from_signal_handler;
  335:  extern int need_messages_from_generator;
  336:  extern int kluge_around_eof;
  337:  extern int got_xfer_error;
  338: +extern int force_change;
  339:  extern int msgs2stderr;
  340:  extern int module_id;
  341:  extern int read_only;
  342: @@ -970,6 +974,22 @@ static int do_recv(int f_in, int f_out, char *local_name)
  343:  	 * points to an identical file won't be replaced by the referent. */
  344:  	copy_links = copy_dirlinks = copy_unsafe_links = 0;
  345:  
  346: +#ifdef SUPPORT_FORCE_CHANGE
  347: +	if (force_change & SYS_IMMUTABLE) {
  348: +		/* Determine whether we'll be able to unlock a system immutable item. */
  349: +		int mib[2];
  350: +		int securityLevel = 0;
  351: +		size_t len = sizeof securityLevel;
  352: +
  353: +		mib[0] = CTL_KERN;
  354: +		mib[1] = KERN_SECURELVL;
  355: +		if (sysctl(mib, 2, &securityLevel, &len, NULL, 0) == 0 && securityLevel > 0) {
  356: +			rprintf(FERROR, "System security level is too high to force mutability on system immutable files and directories.\n");
  357: +			exit_cleanup(RERR_UNSUPPORTED);
  358: +		}
  359: +	}
  360: +#endif
  361: +
  362:  #ifdef SUPPORT_HARD_LINKS
  363:  	if (preserve_hard_links && !inc_recurse)
  364:  		match_hard_links(first_flist);
  365: diff --git a/options.c b/options.c
  366: --- a/options.c
  367: +++ b/options.c
  368: @@ -53,6 +53,7 @@ int preserve_hard_links = 0;
  369:  int preserve_acls = 0;
  370:  int preserve_xattrs = 0;
  371:  int preserve_perms = 0;
  372: +int preserve_fileflags = 0;
  373:  int preserve_executability = 0;
  374:  int preserve_devices = 0;
  375:  int preserve_specials = 0;
  376: @@ -90,6 +91,7 @@ int numeric_ids = 0;
  377:  int msgs2stderr = 2; /* Default: send errors to stderr for local & remote-shell transfers */
  378:  int allow_8bit_chars = 0;
  379:  int force_delete = 0;
  380: +int force_change = 0;
  381:  int io_timeout = 0;
  382:  int prune_empty_dirs = 0;
  383:  int use_qsort = 0;
  384: @@ -614,6 +616,8 @@ static struct poptOption long_options[] = {
  385:    {"perms",           'p', POPT_ARG_VAL,    &preserve_perms, 1, 0, 0 },
  386:    {"no-perms",         0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
  387:    {"no-p",             0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
  388: +  {"fileflags",        0,  POPT_ARG_VAL,    &preserve_fileflags, 1, 0, 0 },
  389: +  {"no-fileflags",     0,  POPT_ARG_VAL,    &preserve_fileflags, 0, 0, 0 },
  390:    {"executability",   'E', POPT_ARG_NONE,   &preserve_executability, 0, 0, 0 },
  391:    {"acls",            'A', POPT_ARG_NONE,   0, 'A', 0, 0 },
  392:    {"no-acls",          0,  POPT_ARG_VAL,    &preserve_acls, 0, 0, 0 },
  393: @@ -711,6 +715,12 @@ static struct poptOption long_options[] = {
  394:    {"remove-source-files",0,POPT_ARG_VAL,    &remove_source_files, 1, 0, 0 },
  395:    {"force",            0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
  396:    {"no-force",         0,  POPT_ARG_VAL,    &force_delete, 0, 0, 0 },
  397: +  {"force-delete",     0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
  398: +  {"no-force-delete",  0,  POPT_ARG_VAL,    &force_delete, 0, 0, 0 },
  399: +  {"force-change",     0,  POPT_ARG_VAL,    &force_change, ALL_IMMUTABLE, 0, 0 },
  400: +  {"no-force-change",  0,  POPT_ARG_VAL,    &force_change, 0, 0, 0 },
  401: +  {"force-uchange",    0,  POPT_ARG_VAL,    &force_change, USR_IMMUTABLE, 0, 0 },
  402: +  {"force-schange",    0,  POPT_ARG_VAL,    &force_change, SYS_IMMUTABLE, 0, 0 },
  403:    {"ignore-errors",    0,  POPT_ARG_VAL,    &ignore_errors, 1, 0, 0 },
  404:    {"no-ignore-errors", 0,  POPT_ARG_VAL,    &ignore_errors, 0, 0, 0 },
  405:    {"max-delete",       0,  POPT_ARG_INT,    &max_delete, 0, 0, 0 },
  406: @@ -998,6 +1008,14 @@ static void set_refuse_options(void)
  407:  #ifndef SUPPORT_CRTIMES
  408:  	parse_one_refuse_match(0, "crtimes", list_end);
  409:  #endif
  410: +#ifndef SUPPORT_FILEFLAGS
  411: +	parse_one_refuse_match(0, "fileflags", list_end);
  412: +#endif
  413: +#ifndef SUPPORT_FORCE_CHANGE
  414: +	parse_one_refuse_match(0, "force-change", list_end);
  415: +	parse_one_refuse_match(0, "force-uchange", list_end);
  416: +	parse_one_refuse_match(0, "force-schange", list_end);
  417: +#endif
  418:  
  419:  	/* Now we use the descrip values to actually mark the options for refusal. */
  420:  	for (op = long_options; op != list_end; op++) {
  421: @@ -2646,6 +2664,9 @@ void server_options(char **args, int *argc_p)
  422:  	if (xfer_dirs && !recurse && delete_mode && am_sender)
  423:  		args[ac++] = "--no-r";
  424:  
  425: +	if (preserve_fileflags)
  426: +		args[ac++] = "--fileflags";
  427: +
  428:  	if (do_compression && do_compression_level != CLVL_NOT_SPECIFIED) {
  429:  		if (asprintf(&arg, "--compress-level=%d", do_compression_level) < 0)
  430:  			goto oom;
  431: @@ -2754,6 +2775,16 @@ void server_options(char **args, int *argc_p)
  432:  			args[ac++] = "--delete-excluded";
  433:  		if (force_delete)
  434:  			args[ac++] = "--force";
  435: +#ifdef SUPPORT_FORCE_CHANGE
  436: +		if (force_change) {
  437: +			if (force_change == ALL_IMMUTABLE)
  438: +				args[ac++] = "--force-change";
  439: +			else if (force_change == USR_IMMUTABLE)
  440: +				args[ac++] = "--force-uchange";
  441: +			else if (force_change == SYS_IMMUTABLE)
  442: +				args[ac++] = "--force-schange";
  443: +		}
  444: +#endif
  445:  		if (write_batch < 0)
  446:  			args[ac++] = "--only-write-batch=X";
  447:  		if (am_root > 1)
  448: diff --git a/rsync.1.md b/rsync.1.md
  449: --- a/rsync.1.md
  450: +++ b/rsync.1.md
  451: @@ -361,6 +361,7 @@ detailed description below for a complete description.
  452:  --keep-dirlinks, -K      treat symlinked dir on receiver as dir
  453:  --hard-links, -H         preserve hard links
  454:  --perms, -p              preserve permissions
  455: +--fileflags              preserve file-flags (aka chflags)
  456:  --executability, -E      preserve executability
  457:  --chmod=CHMOD            affect file and/or directory permissions
  458:  --acls, -A               preserve ACLs (implies --perms)
  459: @@ -401,7 +402,10 @@ detailed description below for a complete description.
  460:  --ignore-missing-args    ignore missing source args without error
  461:  --delete-missing-args    delete missing source args from destination
  462:  --ignore-errors          delete even if there are I/O errors
  463: ---force                  force deletion of dirs even if not empty
  464: +--force-delete           force deletion of directories even if not empty
  465: +--force-change           affect user-/system-immutable files/dirs
  466: +--force-uchange          affect user-immutable files/dirs
  467: +--force-schange          affect system-immutable files/dirs
  468:  --max-delete=NUM         don't delete more than NUM files
  469:  --max-size=SIZE          don't transfer any file larger than SIZE
  470:  --min-size=SIZE          don't transfer any file smaller than SIZE
  471: @@ -717,6 +721,8 @@ your home directory (remove the '=' for that).
  472:  
  473:      Note that `-a` **does not preserve hardlinks**, because finding
  474:      multiply-linked files is expensive.  You must separately specify `-H`.
  475: +    Note also that for backward compatibility, `-a` currently does **not**
  476: +    imply the `--fileflags` option.
  477:  
  478:  0.  `--no-OPTION`
  479:  
  480: @@ -1076,7 +1082,7 @@ your home directory (remove the '=' for that).
  481:      Without this option, if the sending side has replaced a directory with a
  482:      symlink to a directory, the receiving side will delete anything that is in
  483:      the way of the new symlink, including a directory hierarchy (as long as
  484: -    `--force` or `--delete` is in effect).
  485: +    `--force-delete` or `--delete` is in effect).
  486:  
  487:      See also `--keep-dirlinks` for an analogous option for the receiving side.
  488:  
  489: @@ -1262,6 +1268,29 @@ your home directory (remove the '=' for that).
  490:      those used by `--fake-super`) unless you repeat the option (e.g. `-XX`).
  491:      This "copy all xattrs" mode cannot be used with `--fake-super`.
  492:  
  493: +0.  `--fileflags` This option causes rsync to update the file-flags to be the
  494: +    same as the source files and directories (if your OS supports the
  495: +    **chflags**(2) system call).   Some flags can only be altered by the
  496: +    super-user and some might only be unset below a certain secure-level
  497: +    (usually single-user mode). It will not make files alterable that are set
  498: +    to immutable on the receiver.  To do that, see `--force-change`,
  499: +    `--force-uchange`, and `--force-schange`.
  500: +
  501: +0.  `--force-change` This option causes rsync to disable both user-immutable
  502: +    and system-immutable flags on files and directories that are being updated
  503: +    or deleted on the receiving side.  This option overrides `--force-uchange`
  504: +    and `--force-schange`.
  505: +
  506: +0.  `--force-uchange` This option causes rsync to disable user-immutable flags
  507: +    on files and directories that are being updated or deleted on the receiving
  508: +    side.  It does not try to affect system flags.  This option overrides
  509: +    `--force-change` and `--force-schange`.
  510: +
  511: +0.  `--force-schange` This option causes rsync to disable system-immutable
  512: +    flags on files and directories that are being updated or deleted on the
  513: +    receiving side.  It does not try to affect user flags.  This option
  514: +    overrides `--force-change` and `--force-uchange`.
  515: +
  516:  0.  `--chmod=CHMOD`
  517:  
  518:      This option tells rsync to apply one or more comma-separated "chmod" modes
  519: @@ -1729,7 +1758,7 @@ your home directory (remove the '=' for that).
  520:      option a step farther: each missing arg will become a deletion request of
  521:      the corresponding destination file on the receiving side (should it exist).
  522:      If the destination file is a non-empty directory, it will only be
  523: -    successfully deleted if `--force` or `--delete` are in effect.  Other than
  524: +    successfully deleted if `--force-delete` or `--delete` are in effect.  Other than
  525:      that, this option is independent of any other type of delete processing.
  526:  
  527:      The missing source files are represented by special file-list entries which
  528: @@ -1740,15 +1769,16 @@ your home directory (remove the '=' for that).
  529:      Tells `--delete` to go ahead and delete files even when there are I/O
  530:      errors.
  531:  
  532: -0.  `--force`
  533: +0.  `--force-delete`
  534:  
  535:      This option tells rsync to delete a non-empty directory when it is to be
  536:      replaced by a non-directory.  This is only relevant if deletions are not
  537:      active (see `--delete` for details).
  538:  
  539: -    Note for older rsync versions: `--force` used to still be required when
  540: -    using `--delete-after`, and it used to be non-functional unless the
  541: -    `--recursive` option was also enabled.
  542: +    This option can be abbreviated `--force` for backward compatibility.  Note
  543: +    that some older rsync versions used to still require `--force` when using
  544: +    `--delete-after`, and it used to be non-functional unless the `--recursive`
  545: +    option was also enabled.
  546:  
  547:  0.  `--max-delete=NUM`
  548:  
  549: @@ -2700,7 +2730,7 @@ your home directory (remove the '=' for that).
  550:      output of other verbose messages).
  551:  
  552:      The "%i" escape has a cryptic output that is 11 letters long.  The general
  553: -    format is like the string `YXcstpoguax`, where **Y** is replaced by the type
  554: +    format is like the string `YXcstpoguaxf`, where **Y** is replaced by the type
  555:      of update being done, **X** is replaced by the file-type, and the other
  556:      letters represent attributes that may be output if they are being modified.
  557:  
  558: diff --git a/rsync.c b/rsync.c
  559: --- a/rsync.c
  560: +++ b/rsync.c
  561: @@ -31,6 +31,7 @@ extern int dry_run;
  562:  extern int preserve_acls;
  563:  extern int preserve_xattrs;
  564:  extern int preserve_perms;
  565: +extern int preserve_fileflags;
  566:  extern int preserve_executability;
  567:  extern int preserve_times;
  568:  extern int am_root;
  569: @@ -467,6 +468,39 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
  570:  	return new_mode;
  571:  }
  572:  
  573: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
  574: +/* Set a file's st_flags. */
  575: +static int set_fileflags(const char *fname, uint32 fileflags)
  576: +{
  577: +	if (do_chflags(fname, fileflags) != 0) {
  578: +		rsyserr(FERROR_XFER, errno,
  579: +			"failed to set file flags on %s",
  580: +			full_fname(fname));
  581: +		return 0;
  582: +	}
  583: +
  584: +	return 1;
  585: +}
  586: +
  587: +/* Remove immutable flags from an object, so it can be altered/removed. */
  588: +int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags)
  589: +{
  590: +	if (S_ISLNK(mode) || !(fileflags & iflags))
  591: +		return 0;
  592: +	if (!set_fileflags(fname, fileflags & ~iflags))
  593: +		return -1;
  594: +	return 1;
  595: +}
  596: +
  597: +/* Undo a prior make_mutable() call that returned a 1. */
  598: +int undo_make_mutable(const char *fname, uint32 fileflags)
  599: +{
  600: +	if (!set_fileflags(fname, fileflags))
  601: +		return -1;
  602: +	return 1;
  603: +}
  604: +#endif
  605: +
  606:  static int same_mtime(struct file_struct *file, STRUCT_STAT *st, int extra_accuracy)
  607:  {
  608:  #ifdef ST_MTIME_NSEC
  609: @@ -543,7 +577,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
  610:  		if (am_root >= 0) {
  611:  			uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
  612:  			gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
  613: -			if (do_lchown(fname, uid, gid) != 0) {
  614: +			if (do_lchown(fname, uid, gid, sxp->st.st_mode, ST_FLAGS(sxp->st)) != 0) {
  615:  				/* We shouldn't have attempted to change uid
  616:  				 * or gid unless have the privilege. */
  617:  				rsyserr(FERROR_XFER, errno, "%s %s failed",
  618: @@ -642,7 +676,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
  619:  
  620:  #ifdef HAVE_CHMOD
  621:  	if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
  622: -		int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
  623: +		int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode, ST_FLAGS(sxp->st));
  624:  		if (ret < 0) {
  625:  			rsyserr(FERROR_XFER, errno,
  626:  				"failed to set permissions on %s",
  627: @@ -654,6 +688,19 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
  628:  	}
  629:  #endif
  630:  
  631: +#ifdef SUPPORT_FILEFLAGS
  632: +	if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode)
  633: +	 && sxp->st.st_flags != F_FFLAGS(file)) {
  634: +		uint32 fileflags = F_FFLAGS(file);
  635: +		if (flags & ATTRS_DELAY_IMMUTABLE)
  636: +			fileflags &= ~ALL_IMMUTABLE;
  637: +		if (sxp->st.st_flags != fileflags
  638: +		 && !set_fileflags(fname, fileflags))
  639: +			goto cleanup;
  640: +		updated = 1;
  641: +	}
  642: +#endif
  643: +
  644:  	if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
  645:  		if (updated)
  646:  			rprintf(FCLIENT, "%s\n", fname);
  647: @@ -731,7 +778,8 @@ int finish_transfer(const char *fname, const char *fnametmp,
  648:  
  649:  	/* Change permissions before putting the file into place. */
  650:  	set_file_attrs(fnametmp, file, NULL, fnamecmp,
  651: -		       ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME);
  652: +		       ATTRS_DELAY_IMMUTABLE
  653: +		       | (ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME));
  654:  
  655:  	/* move tmp file over real file */
  656:  	if (DEBUG_GTE(RECV, 1))
  657: @@ -748,6 +796,10 @@ int finish_transfer(const char *fname, const char *fnametmp,
  658:  	}
  659:  	if (ret == 0) {
  660:  		/* The file was moved into place (not copied), so it's done. */
  661: +#ifdef SUPPORT_FILEFLAGS
  662: +		if (preserve_fileflags && F_FFLAGS(file) & ALL_IMMUTABLE)
  663: +			set_fileflags(fname, F_FFLAGS(file));
  664: +#endif
  665:  		return 1;
  666:  	}
  667:  	/* The file was copied, so tweak the perms of the copied file.  If it
  668: diff --git a/rsync.h b/rsync.h
  669: --- a/rsync.h
  670: +++ b/rsync.h
  671: @@ -69,7 +69,7 @@
  672:  
  673:  /* The following XMIT flags require an rsync that uses a varint for the flag values */
  674:  
  675: -#define XMIT_RESERVED_16 (1<<16) 	/* reserved for future fileflags use */
  676: +#define XMIT_SAME_FLAGS (1<<16) 	/* any protocol - restricted by command-line option */
  677:  #define XMIT_CRTIME_EQ_MTIME (1<<17)	/* any protocol - restricted by command-line option */
  678:  
  679:  /* These flags are used in the live flist data. */
  680: @@ -182,6 +182,7 @@
  681:  #define ATTRS_SKIP_MTIME	(1<<1)
  682:  #define ATTRS_ACCURATE_TIME	(1<<2)
  683:  #define ATTRS_SKIP_ATIME	(1<<3)
  684: +#define ATTRS_DELAY_IMMUTABLE	(1<<4)
  685:  #define ATTRS_SKIP_CRTIME	(1<<5)
  686:  
  687:  #define MSG_FLUSH	2
  688: @@ -210,6 +211,7 @@
  689:  #define ITEM_REPORT_GROUP (1<<6)
  690:  #define ITEM_REPORT_ACL (1<<7)
  691:  #define ITEM_REPORT_XATTR (1<<8)
  692: +#define ITEM_REPORT_FFLAGS (1<<9)
  693:  #define ITEM_REPORT_CRTIME (1<<10)
  694:  #define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
  695:  #define ITEM_XNAME_FOLLOWS (1<<12)
  696: @@ -578,6 +580,31 @@ typedef unsigned int size_t;
  697:  #define SUPPORT_CRTIMES 1
  698:  #endif
  699:  
  700: +#define NO_FFLAGS ((uint32)-1)
  701: +
  702: +#ifdef HAVE_CHFLAGS
  703: +#define SUPPORT_FILEFLAGS 1
  704: +#define SUPPORT_FORCE_CHANGE 1
  705: +#endif
  706: +
  707: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
  708: +#ifndef UF_NOUNLINK
  709: +#define UF_NOUNLINK 0
  710: +#endif
  711: +#ifndef SF_NOUNLINK
  712: +#define SF_NOUNLINK 0
  713: +#endif
  714: +#define USR_IMMUTABLE (UF_IMMUTABLE|UF_NOUNLINK|UF_APPEND)
  715: +#define SYS_IMMUTABLE (SF_IMMUTABLE|SF_NOUNLINK|SF_APPEND)
  716: +#define ALL_IMMUTABLE (USR_IMMUTABLE|SYS_IMMUTABLE)
  717: +#define ST_FLAGS(st) ((st).st_flags)
  718: +#else
  719: +#define USR_IMMUTABLE 0
  720: +#define SYS_IMMUTABLE 0
  721: +#define ALL_IMMUTABLE 0
  722: +#define ST_FLAGS(st) NO_FFLAGS
  723: +#endif
  724: +
  725:  /* Find a variable that is either exactly 32-bits or longer.
  726:   * If some code depends on 32-bit truncation, it will need to
  727:   * take special action in a "#if SIZEOF_INT32 > 4" section. */
  728: @@ -804,6 +831,7 @@ extern int pathname_ndx;
  729:  extern int depth_ndx;
  730:  extern int uid_ndx;
  731:  extern int gid_ndx;
  732: +extern int fileflags_ndx;
  733:  extern int acls_ndx;
  734:  extern int xattrs_ndx;
  735:  
  736: @@ -858,6 +886,11 @@ extern int xattrs_ndx;
  737:  /* When the associated option is on, all entries will have these present: */
  738:  #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
  739:  #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
  740: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
  741: +#define F_FFLAGS(f) REQ_EXTRA(f, fileflags_ndx)->unum
  742: +#else
  743: +#define F_FFLAGS(f) NO_FFLAGS
  744: +#endif
  745:  #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
  746:  #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
  747:  #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
  748: diff --git a/syscall.c b/syscall.c
  749: --- a/syscall.c
  750: +++ b/syscall.c
  751: @@ -38,6 +38,7 @@ extern int am_root;
  752:  extern int am_sender;
  753:  extern int read_only;
  754:  extern int list_only;
  755: +extern int force_change;
  756:  extern int inplace;
  757:  extern int preallocate_files;
  758:  extern int preserve_perms;
  759: @@ -77,7 +78,23 @@ int do_unlink(const char *fname)
  760:  {
  761:  	if (dry_run) return 0;
  762:  	RETURN_ERROR_IF_RO_OR_LO;
  763: -	return unlink(fname);
  764: +	if (unlink(fname) == 0)
  765: +		return 0;
  766: +#ifdef SUPPORT_FORCE_CHANGE
  767: +	if (force_change && errno == EPERM) {
  768: +		STRUCT_STAT st;
  769: +
  770: +		if (x_lstat(fname, &st, NULL) == 0
  771: +		 && make_mutable(fname, st.st_mode, st.st_flags, force_change) > 0) {
  772: +			if (unlink(fname) == 0)
  773: +				return 0;
  774: +			undo_make_mutable(fname, st.st_flags);
  775: +		}
  776: +		/* TODO: handle immutable directories */
  777: +		errno = EPERM;
  778: +	}
  779: +#endif
  780: +	return -1;
  781:  }
  782:  
  783:  #ifdef SUPPORT_LINKS
  784: @@ -142,14 +159,35 @@ int do_link(const char *old_path, const char *new_path)
  785:  }
  786:  #endif
  787:  
  788: -int do_lchown(const char *path, uid_t owner, gid_t group)
  789: +int do_lchown(const char *path, uid_t owner, gid_t group, UNUSED(mode_t mode), UNUSED(uint32 fileflags))
  790:  {
  791:  	if (dry_run) return 0;
  792:  	RETURN_ERROR_IF_RO_OR_LO;
  793:  #ifndef HAVE_LCHOWN
  794:  #define lchown chown
  795:  #endif
  796: -	return lchown(path, owner, group);
  797: +	if (lchown(path, owner, group) == 0)
  798: +		return 0;
  799: +#ifdef SUPPORT_FORCE_CHANGE
  800: +	if (force_change && errno == EPERM) {
  801: +		if (fileflags == NO_FFLAGS) {
  802: +			STRUCT_STAT st;
  803: +			if (x_lstat(path, &st, NULL) == 0) {
  804: +				mode = st.st_mode;
  805: +				fileflags = st.st_flags;
  806: +			}
  807: +		}
  808: +		if (fileflags != NO_FFLAGS
  809: +		 && make_mutable(path, mode, fileflags, force_change) > 0) {
  810: +			int ret = lchown(path, owner, group);
  811: +			undo_make_mutable(path, fileflags);
  812: +			if (ret == 0)
  813: +				return 0;
  814: +		}
  815: +		errno = EPERM;
  816: +	}
  817: +#endif
  818: +	return -1;
  819:  }
  820:  
  821:  int do_mknod(const char *pathname, mode_t mode, dev_t dev)
  822: @@ -189,7 +227,7 @@ int do_mknod(const char *pathname, mode_t mode, dev_t dev)
  823:  			return -1;
  824:  		close(sock);
  825:  #ifdef HAVE_CHMOD
  826: -		return do_chmod(pathname, mode);
  827: +		return do_chmod(pathname, mode, 0);
  828:  #else
  829:  		return 0;
  830:  #endif
  831: @@ -206,7 +244,22 @@ int do_rmdir(const char *pathname)
  832:  {
  833:  	if (dry_run) return 0;
  834:  	RETURN_ERROR_IF_RO_OR_LO;
  835: -	return rmdir(pathname);
  836: +	if (rmdir(pathname) == 0)
  837: +		return 0;
  838: +#ifdef SUPPORT_FORCE_CHANGE
  839: +	if (force_change && errno == EPERM) {
  840: +		STRUCT_STAT st;
  841: +
  842: +		if (x_lstat(pathname, &st, NULL) == 0
  843: +		 && make_mutable(pathname, st.st_mode, st.st_flags, force_change) > 0) {
  844: +			if (rmdir(pathname) == 0)
  845: +				return 0;
  846: +			undo_make_mutable(pathname, st.st_flags);
  847: +		}
  848: +		errno = EPERM;
  849: +	}
  850: +#endif
  851: +	return -1;
  852:  }
  853:  
  854:  int do_open(const char *pathname, int flags, mode_t mode)
  855: @@ -225,7 +278,7 @@ int do_open(const char *pathname, int flags, mode_t mode)
  856:  }
  857:  
  858:  #ifdef HAVE_CHMOD
  859: -int do_chmod(const char *path, mode_t mode)
  860: +int do_chmod(const char *path, mode_t mode, UNUSED(uint32 fileflags))
  861:  {
  862:  	int code;
  863:  	if (dry_run) return 0;
  864: @@ -248,17 +301,72 @@ int do_chmod(const char *path, mode_t mode)
  865:  	} else
  866:  		code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
  867:  #endif /* !HAVE_LCHMOD */
  868: +#ifdef SUPPORT_FORCE_CHANGE
  869: +	if (code < 0 && force_change && errno == EPERM && !S_ISLNK(mode)) {
  870: +		if (fileflags == NO_FFLAGS) {
  871: +			STRUCT_STAT st;
  872: +			if (x_lstat(path, &st, NULL) == 0)
  873: +				fileflags = st.st_flags;
  874: +		}
  875: +		if (fileflags != NO_FFLAGS
  876: +		 && make_mutable(path, mode, fileflags, force_change) > 0) {
  877: +			code = chmod(path, mode & CHMOD_BITS);
  878: +			undo_make_mutable(path, fileflags);
  879: +			if (code == 0)
  880: +				return 0;
  881: +		}
  882: +		errno = EPERM;
  883: +	}
  884: +#endif
  885:  	if (code != 0 && (preserve_perms || preserve_executability))
  886:  		return code;
  887:  	return 0;
  888:  }
  889:  #endif
  890:  
  891: +#ifdef HAVE_CHFLAGS
  892: +int do_chflags(const char *path, uint32 fileflags)
  893: +{
  894: +	if (dry_run) return 0;
  895: +	RETURN_ERROR_IF_RO_OR_LO;
  896: +	return chflags(path, fileflags);
  897: +}
  898: +#endif
  899: +
  900:  int do_rename(const char *old_path, const char *new_path)
  901:  {
  902:  	if (dry_run) return 0;
  903:  	RETURN_ERROR_IF_RO_OR_LO;
  904: -	return rename(old_path, new_path);
  905: +	if (rename(old_path, new_path) == 0)
  906: +		return 0;
  907: +#ifdef SUPPORT_FORCE_CHANGE
  908: +	if (force_change && errno == EPERM) {
  909: +		STRUCT_STAT st1, st2;
  910: +		int became_mutable;
  911: +
  912: +		if (x_lstat(old_path, &st1, NULL) != 0)
  913: +			goto failed;
  914: +		became_mutable = make_mutable(old_path, st1.st_mode, st1.st_flags, force_change) > 0;
  915: +		if (became_mutable && rename(old_path, new_path) == 0)
  916: +			goto success;
  917: +		if (x_lstat(new_path, &st2, NULL) == 0
  918: +		 && make_mutable(new_path, st2.st_mode, st2.st_flags, force_change) > 0) {
  919: +			if (rename(old_path, new_path) == 0) {
  920: +			  success:
  921: +				if (became_mutable) /* Yes, use new_path and st1! */
  922: +					undo_make_mutable(new_path, st1.st_flags);
  923: +				return 0;
  924: +			}
  925: +			undo_make_mutable(new_path, st2.st_flags);
  926: +		}
  927: +		/* TODO: handle immutable directories */
  928: +		if (became_mutable)
  929: +			undo_make_mutable(old_path, st1.st_flags);
  930: +	  failed:
  931: +		errno = EPERM;
  932: +	}
  933: +#endif
  934: +	return -1;
  935:  }
  936:  
  937:  #ifdef HAVE_FTRUNCATE
  938: diff --git a/t_stub.c b/t_stub.c
  939: --- a/t_stub.c
  940: +++ b/t_stub.c
  941: @@ -28,6 +28,8 @@ int protect_args = 0;
  942:  int module_id = -1;
  943:  int relative_paths = 0;
  944:  int module_dirlen = 0;
  945: +int force_change = 0;
  946: +int preserve_acls = 0;
  947:  int preserve_times = 0;
  948:  int preserve_xattrs = 0;
  949:  int preserve_perms = 0;
  950: @@ -110,3 +112,23 @@ filter_rule_list daemon_filter_list;
  951:  {
  952:  	return cst ? 0 : 0;
  953:  }
  954: +
  955: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
  956: + int make_mutable(UNUSED(const char *fname), UNUSED(mode_t mode), UNUSED(uint32 fileflags), UNUSED(uint32 iflags))
  957: +{
  958: +	return 0;
  959: +}
  960: +
  961: +/* Undo a prior make_mutable() call that returned a 1. */
  962: + int undo_make_mutable(UNUSED(const char *fname), UNUSED(uint32 fileflags))
  963: +{
  964: +	return 0;
  965: +}
  966: +#endif
  967: +
  968: +#ifdef SUPPORT_XATTRS
  969: + int x_lstat(UNUSED(const char *fname), UNUSED(STRUCT_STAT *fst), UNUSED(STRUCT_STAT *xst))
  970: +{
  971: +	return -1;
  972: +}
  973: +#endif
  974: diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
  975: --- a/testsuite/rsync.fns
  976: +++ b/testsuite/rsync.fns
  977: @@ -23,9 +23,9 @@ todir="$tmpdir/to"
  978:  chkdir="$tmpdir/chk"
  979:  
  980:  # For itemized output:
  981: -all_plus='+++++++++'
  982: -allspace='         '
  983: -dots='.....' # trailing dots after changes
  984: +all_plus='++++++++++'
  985: +allspace='          '
  986: +dots='......' # trailing dots after changes
  987:  tab_ch='	' # a single tab character
  988:  
  989:  # Berkley's nice.
  990: diff --git a/usage.c b/usage.c
  991: --- a/usage.c
  992: +++ b/usage.c
  993: @@ -131,6 +131,11 @@ static void print_info_flags(enum logcode f)
  994:  #endif
  995:  			"crtimes",
  996:  
  997: +#ifndef SUPPORT_FILEFLAGS
  998: +		"no "
  999: +#endif
 1000: +			"file-flags",
 1001: +
 1002:  	"*Optimizations",
 1003:  
 1004:  #ifndef HAVE_SIMD
 1005: diff --git a/util.c b/util.c
 1006: --- a/util.c
 1007: +++ b/util.c
 1008: @@ -33,6 +33,7 @@ extern int relative_paths;
 1009:  extern int preserve_times;
 1010:  extern int preserve_xattrs;
 1011:  extern int preallocate_files;
 1012: +extern int force_change;
 1013:  extern char *module_dir;
 1014:  extern unsigned int module_dirlen;
 1015:  extern char *partial_dir;
 1016: @@ -115,6 +116,33 @@ void print_child_argv(const char *prefix, char **cmd)
 1017:  	rprintf(FCLIENT, " (%d args)\n", cnt);
 1018:  }
 1019:  
 1020: +#ifdef SUPPORT_FORCE_CHANGE
 1021: +static int try_a_force_change(const char *fname, STRUCT_STAT *stp)
 1022: +{
 1023: +	uint32 fileflags = ST_FLAGS(*stp);
 1024: +	if (fileflags == NO_FFLAGS) {
 1025: +		STRUCT_STAT st;
 1026: +		if (x_lstat(fname, &st, NULL) == 0)
 1027: +			fileflags = st.st_flags;
 1028: +	}
 1029: +	if (fileflags != NO_FFLAGS && make_mutable(fname, stp->st_mode, fileflags, force_change) > 0) {
 1030: +		int ret, save_force_change = force_change;
 1031: +
 1032: +		force_change = 0; /* Make certain we can't come back here. */
 1033: +		ret = set_times(fname, stp);
 1034: +		force_change = save_force_change;
 1035: +
 1036: +		undo_make_mutable(fname, fileflags);
 1037: +
 1038: +		return ret;
 1039: +	}
 1040: +
 1041: +	errno = EPERM;
 1042: +
 1043: +	return -1;
 1044: +}
 1045: +#endif
 1046: +
 1047:  /* This returns 0 for success, 1 for a symlink if symlink time-setting
 1048:   * is not possible, or -1 for any other error. */
 1049:  int set_times(const char *fname, STRUCT_STAT *stp)
 1050: @@ -142,6 +170,10 @@ int set_times(const char *fname, STRUCT_STAT *stp)
 1051:  #include "case_N.h"
 1052:  		if (do_utimensat(fname, stp) == 0)
 1053:  			break;
 1054: +#ifdef SUPPORT_FORCE_CHANGE
 1055: +		if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0)
 1056: +			break;
 1057: +#endif
 1058:  		if (errno != ENOSYS)
 1059:  			return -1;
 1060:  		switch_step++;
 1061: @@ -151,6 +183,10 @@ int set_times(const char *fname, STRUCT_STAT *stp)
 1062:  #include "case_N.h"
 1063:  		if (do_lutimes(fname, stp) == 0)
 1064:  			break;
 1065: +#ifdef SUPPORT_FORCE_CHANGE
 1066: +		if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0)
 1067: +			break;
 1068: +#endif
 1069:  		if (errno != ENOSYS)
 1070:  			return -1;
 1071:  		switch_step++;
 1072: @@ -172,6 +208,10 @@ int set_times(const char *fname, STRUCT_STAT *stp)
 1073:  		if (do_utime(fname, stp) == 0)
 1074:  			break;
 1075:  #endif
 1076: +#ifdef SUPPORT_FORCE_CHANGE
 1077: +		if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0)
 1078: +			break;
 1079: +#endif
 1080:  
 1081:  		return -1;
 1082:  	}
 1083: diff --git a/xattrs.c b/xattrs.c
 1084: --- a/xattrs.c
 1085: +++ b/xattrs.c
 1086: @@ -1202,7 +1202,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
 1087:  	mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
 1088:  	     | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
 1089:  	if (fst.st_mode != mode)
 1090: -		do_chmod(fname, mode);
 1091: +		do_chmod(fname, mode, ST_FLAGS(fst));
 1092:  	if (!IS_DEVICE(fst.st_mode))
 1093:  		fst.st_rdev = 0; /* just in case */
 1094:  
 1095: diff -Nurp a/rsync.1 b/rsync.1
 1096: --- a/rsync.1
 1097: +++ b/rsync.1
 1098: @@ -437,6 +437,7 @@ detailed description below for a complet
 1099:  --keep-dirlinks, -K      treat symlinked dir on receiver as dir
 1100:  --hard-links, -H         preserve hard links
 1101:  --perms, -p              preserve permissions
 1102: +--fileflags              preserve file-flags (aka chflags)
 1103:  --executability, -E      preserve executability
 1104:  --chmod=CHMOD            affect file and/or directory permissions
 1105:  --acls, -A               preserve ACLs (implies --perms)
 1106: @@ -477,7 +478,10 @@ detailed description below for a complet
 1107:  --ignore-missing-args    ignore missing source args without error
 1108:  --delete-missing-args    delete missing source args from destination
 1109:  --ignore-errors          delete even if there are I/O errors
 1110: ---force                  force deletion of dirs even if not empty
 1111: +--force-delete           force deletion of directories even if not empty
 1112: +--force-change           affect user-/system-immutable files/dirs
 1113: +--force-uchange          affect user-immutable files/dirs
 1114: +--force-schange          affect system-immutable files/dirs
 1115:  --max-delete=NUM         don't delete more than NUM files
 1116:  --max-size=SIZE          don't transfer any file larger than SIZE
 1117:  --min-size=SIZE          don't transfer any file smaller than SIZE
 1118: @@ -783,6 +787,8 @@ omission).  The only exception to the ab
 1119:  .IP
 1120:  Note that \fB\-a\fP \fBdoes not preserve hardlinks\fP, because finding
 1121:  multiply-linked files is expensive.  You must separately specify \fB\-H\fP.
 1122: +Note also that for backward compatibility, \fB\-a\fP currently does \fBnot\fP
 1123: +imply the \fB\-\-fileflags\fP option.
 1124:  .IP "\fB\-\-no-OPTION\fP"
 1125:  You may turn off one or more implied options by prefixing the option name
 1126:  with "no-".  Not all options may be prefixed with a "no-": only options that
 1127: @@ -1139,7 +1145,7 @@ to non-directories to be affected, as th
 1128:  Without this option, if the sending side has replaced a directory with a
 1129:  symlink to a directory, the receiving side will delete anything that is in
 1130:  the way of the new symlink, including a directory hierarchy (as long as
 1131: -\fB\-\-force\fP or \fB\-\-delete\fP is in effect).
 1132: +\fB\-\-force-delete\fP or \fB\-\-delete\fP is in effect).
 1133:  .IP
 1134:  See also \fB\-\-keep-dirlinks\fP for an analogous option for the receiving side.
 1135:  .IP
 1136: @@ -1348,6 +1354,25 @@ receiver-only rule that excludes all nam
 1137:  Note that the \fB\-X\fP option does not copy rsync's special xattr values (e.g.
 1138:  those used by \fB\-\-fake-super\fP) unless you repeat the option (e.g. \fB\-XX\fP).
 1139:  This "copy all xattrs" mode cannot be used with \fB\-\-fake-super\fP.
 1140: +.IP "\fB\-\-fileflags\fP This option causes rsync to update the file-flags to be the
 1141: +same as the source files and directories (if your OS supports the
 1142: +\fBchflags\fP(2) system call).   Some flags can only be altered by the
 1143: +super-user and some might only be unset below a certain secure-level
 1144: +(usually single-user mode). It will not make files alterable that are set
 1145: +to immutable on the receiver.  To do that, see \fB\-\-force-change\fP,
 1146: +\fB\-\-force-uchange\fP, and \fB\-\-force-schange\fP."
 1147: +.IP "\fB\-\-force-change\fP This option causes rsync to disable both user-immutable
 1148: +and system-immutable flags on files and directories that are being updated
 1149: +or deleted on the receiving side.  This option overrides \fB\-\-force-uchange\fP
 1150: +and \fB\-\-force-schange\fP."
 1151: +.IP "\fB\-\-force-uchange\fP This option causes rsync to disable user-immutable flags
 1152: +on files and directories that are being updated or deleted on the receiving
 1153: +side.  It does not try to affect system flags.  This option overrides
 1154: +\fB\-\-force-change\fP and \fB\-\-force-schange\fP."
 1155: +.IP "\fB\-\-force-schange\fP This option causes rsync to disable system-immutable
 1156: +flags on files and directories that are being updated or deleted on the
 1157: +receiving side.  It does not try to affect user flags.  This option
 1158: +overrides \fB\-\-force-change\fP and \fB\-\-force-uchange\fP."
 1159:  .IP "\fB\-\-chmod=CHMOD\fP"
 1160:  This option tells rsync to apply one or more comma-separated "chmod" modes
 1161:  to the permission of the files in the transfer.  The resulting value is
 1162: @@ -1773,7 +1798,7 @@ This option takes the behavior of (the i
 1163:  option a step farther: each missing arg will become a deletion request of
 1164:  the corresponding destination file on the receiving side (should it exist).
 1165:  If the destination file is a non-empty directory, it will only be
 1166: -successfully deleted if \fB\-\-force\fP or \fB\-\-delete\fP are in effect.  Other than
 1167: +successfully deleted if \fB\-\-force-delete\fP or \fB\-\-delete\fP are in effect.  Other than
 1168:  that, this option is independent of any other type of delete processing.
 1169:  .IP
 1170:  The missing source files are represented by special file-list entries which
 1171: @@ -1781,14 +1806,15 @@ display as a "\fB*missing\fP" entry in t
 1172:  .IP "\fB\-\-ignore-errors\fP"
 1173:  Tells \fB\-\-delete\fP to go ahead and delete files even when there are I/O
 1174:  errors.
 1175: -.IP "\fB\-\-force\fP"
 1176: +.IP "\fB\-\-force-delete\fP"
 1177:  This option tells rsync to delete a non-empty directory when it is to be
 1178:  replaced by a non-directory.  This is only relevant if deletions are not
 1179:  active (see \fB\-\-delete\fP for details).
 1180:  .IP
 1181: -Note for older rsync versions: \fB\-\-force\fP used to still be required when
 1182: -using \fB\-\-delete-after\fP, and it used to be non-functional unless the
 1183: -\fB\-\-recursive\fP option was also enabled.
 1184: +This option can be abbreviated \fB\-\-force\fP for backward compatibility.  Note
 1185: +that some older rsync versions used to still require \fB\-\-force\fP when using
 1186: +\fB\-\-delete-after\fP, and it used to be non-functional unless the \fB\-\-recursive\fP
 1187: +option was also enabled.
 1188:  .IP "\fB\-\-max-delete=NUM\fP"
 1189:  This tells rsync not to delete more than NUM files or directories.  If that
 1190:  limit is exceeded, all further deletions are skipped through the end of the
 1191: @@ -2735,7 +2761,7 @@ also be output, but only if the receivin
 1192:  output of other verbose messages).
 1193:  .IP
 1194:  The "%i" escape has a cryptic output that is 11 letters long.  The general
 1195: -format is like the string \fBYXcstpoguax\fP, where \fBY\fP is replaced by the type
 1196: +format is like the string \fBYXcstpoguaxf\fP, where \fBY\fP is replaced by the type
 1197:  of update being done, \fBX\fP is replaced by the file-type, and the other
 1198:  letters represent attributes that may be output if they are being modified.
 1199:  .IP
 1200: diff -Nurp a/rsync.1.html b/rsync.1.html
 1201: --- a/rsync.1.html
 1202: +++ b/rsync.1.html
 1203: @@ -352,6 +352,7 @@ detailed description below for a complet
 1204:  --keep-dirlinks, -K      treat symlinked dir on receiver as dir
 1205:  --hard-links, -H         preserve hard links
 1206:  --perms, -p              preserve permissions
 1207: +--fileflags              preserve file-flags (aka chflags)
 1208:  --executability, -E      preserve executability
 1209:  --chmod=CHMOD            affect file and/or directory permissions
 1210:  --acls, -A               preserve ACLs (implies --perms)
 1211: @@ -392,7 +393,10 @@ detailed description below for a complet
 1212:  --ignore-missing-args    ignore missing source args without error
 1213:  --delete-missing-args    delete missing source args from destination
 1214:  --ignore-errors          delete even if there are I/O errors
 1215: ---force                  force deletion of dirs even if not empty
 1216: +--force-delete           force deletion of directories even if not empty
 1217: +--force-change           affect user-/system-immutable files/dirs
 1218: +--force-uchange          affect user-immutable files/dirs
 1219: +--force-schange          affect system-immutable files/dirs
 1220:  --max-delete=NUM         don't delete more than NUM files
 1221:  --max-size=SIZE          don't transfer any file larger than SIZE
 1222:  --min-size=SIZE          don't transfer any file smaller than SIZE
 1223: @@ -697,7 +701,9 @@ recursion and want to preserve almost ev
 1224:  omission).  The only exception to the above equivalence is when
 1225:  <code>--files-from</code> is specified, in which case <code>-r</code> is not implied.</p>
 1226:  <p>Note that <code>-a</code> <strong>does not preserve hardlinks</strong>, because finding
 1227: -multiply-linked files is expensive.  You must separately specify <code>-H</code>.</p>
 1228: +multiply-linked files is expensive.  You must separately specify <code>-H</code>.
 1229: +Note also that for backward compatibility, <code>-a</code> currently does <strong>not</strong>
 1230: +imply the <code>--fileflags</code> option.</p>
 1231:  </dd>
 1232:  
 1233:  <dt><code>--no-OPTION</code></dt><dd>
 1234: @@ -1039,7 +1045,7 @@ to non-directories to be affected, as th
 1235:  <p>Without this option, if the sending side has replaced a directory with a
 1236:  symlink to a directory, the receiving side will delete anything that is in
 1237:  the way of the new symlink, including a directory hierarchy (as long as
 1238: -<code>--force</code> or <code>--delete</code> is in effect).</p>
 1239: +<code>--force-delete</code> or <code>--delete</code> is in effect).</p>
 1240:  <p>See also <code>--keep-dirlinks</code> for an analogous option for the receiving side.</p>
 1241:  <p><code>--copy-dirlinks</code> applies to all symlinks to directories in the source.  If
 1242:  you want to follow only a few specified symlinks, a trick you can use is to
 1243: @@ -1219,6 +1225,33 @@ those used by <code>--fake-super</code>)
 1244:  This &quot;copy all xattrs&quot; mode cannot be used with <code>--fake-super</code>.</p>
 1245:  </dd>
 1246:  
 1247: +<dt><code>--fileflags</code> This option causes rsync to update the file-flags to be the
 1248: +same as the source files and directories (if your OS supports the
 1249: +<strong>chflags</strong>(2) system call).   Some flags can only be altered by the
 1250: +super-user and some might only be unset below a certain secure-level
 1251: +(usually single-user mode). It will not make files alterable that are set
 1252: +to immutable on the receiver.  To do that, see <code>--force-change</code>,
 1253: +<code>--force-uchange</code>, and <code>--force-schange</code>.</dt><dd>
 1254: +</dd>
 1255: +
 1256: +<dt><code>--force-change</code> This option causes rsync to disable both user-immutable
 1257: +and system-immutable flags on files and directories that are being updated
 1258: +or deleted on the receiving side.  This option overrides <code>--force-uchange</code>
 1259: +and <code>--force-schange</code>.</dt><dd>
 1260: +</dd>
 1261: +
 1262: +<dt><code>--force-uchange</code> This option causes rsync to disable user-immutable flags
 1263: +on files and directories that are being updated or deleted on the receiving
 1264: +side.  It does not try to affect system flags.  This option overrides
 1265: +<code>--force-change</code> and <code>--force-schange</code>.</dt><dd>
 1266: +</dd>
 1267: +
 1268: +<dt><code>--force-schange</code> This option causes rsync to disable system-immutable
 1269: +flags on files and directories that are being updated or deleted on the
 1270: +receiving side.  It does not try to affect user flags.  This option
 1271: +overrides <code>--force-change</code> and <code>--force-uchange</code>.</dt><dd>
 1272: +</dd>
 1273: +
 1274:  <dt><code>--chmod=CHMOD</code></dt><dd>
 1275:  <p>This option tells rsync to apply one or more comma-separated &quot;chmod&quot; modes
 1276:  to the permission of the files in the transfer.  The resulting value is
 1277: @@ -1652,7 +1685,7 @@ is no longer there.</p>
 1278:  option a step farther: each missing arg will become a deletion request of
 1279:  the corresponding destination file on the receiving side (should it exist).
 1280:  If the destination file is a non-empty directory, it will only be
 1281: -successfully deleted if <code>--force</code> or <code>--delete</code> are in effect.  Other than
 1282: +successfully deleted if <code>--force-delete</code> or <code>--delete</code> are in effect.  Other than
 1283:  that, this option is independent of any other type of delete processing.</p>
 1284:  <p>The missing source files are represented by special file-list entries which
 1285:  display as a &quot;<code>*missing</code>&quot; entry in the <code>--list-only</code> output.</p>
 1286: @@ -1663,13 +1696,14 @@ display as a &quot;<code>*missing</code>
 1287:  errors.</p>
 1288:  </dd>
 1289:  
 1290: -<dt><code>--force</code></dt><dd>
 1291: +<dt><code>--force-delete</code></dt><dd>
 1292:  <p>This option tells rsync to delete a non-empty directory when it is to be
 1293:  replaced by a non-directory.  This is only relevant if deletions are not
 1294:  active (see <code>--delete</code> for details).</p>
 1295: -<p>Note for older rsync versions: <code>--force</code> used to still be required when
 1296: -using <code>--delete-after</code>, and it used to be non-functional unless the
 1297: -<code>--recursive</code> option was also enabled.</p>
 1298: +<p>This option can be abbreviated <code>--force</code> for backward compatibility.  Note
 1299: +that some older rsync versions used to still require <code>--force</code> when using
 1300: +<code>--delete-after</code>, and it used to be non-functional unless the <code>--recursive</code>
 1301: +option was also enabled.</p>
 1302:  </dd>
 1303:  
 1304:  <dt><code>--max-delete=NUM</code></dt><dd>
 1305: @@ -2552,7 +2586,7 @@ also be output, but only if the receivin
 1306:  (you can use <code>-vv</code> with older versions of rsync, but that also turns on the
 1307:  output of other verbose messages).</p>
 1308:  <p>The &quot;%i&quot; escape has a cryptic output that is 11 letters long.  The general
 1309: -format is like the string <code>YXcstpoguax</code>, where <strong>Y</strong> is replaced by the type
 1310: +format is like the string <code>YXcstpoguaxf</code>, where <strong>Y</strong> is replaced by the type
 1311:  of update being done, <strong>X</strong> is replaced by the file-type, and the other
 1312:  letters represent attributes that may be output if they are being modified.</p>
 1313:  <p>The update types that replace the <strong>Y</strong> are as follows:</p>

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>