Index: rsync.h =================================================================== RCS file: /cvsroot/rsync/rsync.h,v retrieving revision 1.294 diff -u -r1.294 rsync.h --- rsync.h 5 Jun 2006 16:52:34 -0000 1.294 +++ rsync.h 22 Jun 2006 15:38:56 -0000 @@ -67,7 +67,7 @@ #define FLAG_MISSING (1<<6) /* generator */ /* update this if you make incompatible changes */ -#define PROTOCOL_VERSION 29 +#define PROTOCOL_VERSION 30 /* We refuse to interoperate with versions that are not in this range. * Note that we assume we'll work with later versions: the onus is on @@ -608,6 +608,7 @@ #define MATCHFLG_SENDER_SIDE (1<<16)/* rule applies to the sending side */ #define MATCHFLG_RECEIVER_SIDE (1<<17)/* rule applies to the receiving side */ #define MATCHFLG_CLEAR_LIST (1<<18)/* this item is the "!" token */ +#define MATCHFLG_PARENT_PROTECT (1<<19)/* matching files perish with their parents (modifies P) */ #define MATCHFLGS_FROM_CONTAINER (MATCHFLG_ABS_PATH | MATCHFLG_INCLUDE \ | MATCHFLG_DIRECTORY | MATCHFLG_SENDER_SIDE \ Index: generator.c =================================================================== RCS file: /cvsroot/rsync/generator.c,v retrieving revision 1.283 diff -u -r1.283 generator.c --- generator.c 5 Jun 2006 16:56:50 -0000 1.283 +++ generator.c 22 Jun 2006 15:38:54 -0000 @@ -94,9 +94,10 @@ static int deletion_count = 0; /* used to implement --max-delete */ -/* For calling delete_file() */ -#define DEL_FORCE_RECURSE (1<<1) /* recurse even w/o --force */ -#define DEL_TERSE (1<<3) +/* For calling delete_item() */ +#define DEL_PARENT_EXTRANEOUS (1<<0) /* parent-protect filters include */ +#define DEL_RECURSE (1<<1) /* recurse */ +#define DEL_REPLACE (1<<3) /* file will be replaced */ static int is_backup_file(char *fn) @@ -105,73 +106,36 @@ return k > 0 && strcmp(fn+k, backup_suffix) == 0; } +/* Declare here so empty_directory can call it */ +static int delete_item(char *fname, int mode, int flags); -/* Delete a file or directory. If DEL_FORCE_RECURSE is set in the flags, or if - * force_delete is set, this will delete recursively. - * - * Note that fname must point to a MAXPATHLEN buffer if the mode indicates it's - * a directory! (The buffer is used for recursion, but returned unchanged.) +/* The directory should be deleted. If DEL_RECURSE is given, delete unpinned + * files recursively and then signal whether the directory is pinned. If not, + * merely signal whether the directory is empty. + * + * Returns: 0 if OK, -3 if pinned, -4 if nonempty. */ -static int delete_item(char *fname, int mode, int flags) +static int empty_directory(char *fname, int flags) { struct file_list *dirlist; - int j, dlen, zap_dir, ok; + int j, dlen, result; unsigned remainder; void *save_filters; char *p; - - if (!S_ISDIR(mode)) { - if (max_delete && ++deletion_count > max_delete) - return 0; - if (make_backups && (backup_dir || !is_backup_file(fname))) - ok = make_backup(fname); - else - ok = robust_unlink(fname) == 0; - if (ok) { - if (!(flags & DEL_TERSE)) - log_delete(fname, mode); - return 0; - } - if (errno == ENOENT) { - deletion_count--; - return 0; - } - rsyserr(FERROR, errno, "delete_file: unlink %s failed", - full_fname(fname)); - return -1; - } - - zap_dir = flags & DEL_FORCE_RECURSE || force_delete; - if ((max_delete && ++deletion_count > max_delete) - || (dry_run && zap_dir)) { - ok = 0; - errno = ENOTEMPTY; - } else if (make_backups && !backup_dir && !is_backup_file(fname) - && !(flags & DEL_FORCE_RECURSE)) - ok = make_backup(fname); - else - ok = do_rmdir(fname) == 0; - if (ok) { - if (!(flags & DEL_TERSE)) - log_delete(fname, mode); - return 0; - } - if (errno == ENOENT) { - deletion_count--; - return 0; - } - if (!zap_dir) { - rsyserr(FERROR, errno, "delete_file: rmdir %s failed", - full_fname(fname)); - return -1; - } - flags |= DEL_FORCE_RECURSE; /* mark subdir dels as not "in the way" */ - deletion_count--; - + BOOL pinned = False; + dlen = strlen(fname); save_filters = push_local_filters(fname, dlen); - dirlist = get_dirlist(fname, dlen, 0); + dirlist = get_dirlist(fname, dlen, 0, &pinned); + + if (!pinned && dirlist->count == 0) { + result = 0; + goto out; + } else if (!(flags & DEL_RECURSE)) { + result = -4; + goto out; + } p = fname + dlen; if (dlen != 1 || *fname != '/') @@ -181,31 +145,106 @@ for (j = dirlist->count; j--; ) { struct file_struct *fp = dirlist->files[j]; - if (fp->flags & FLAG_MOUNT_POINT) + if (fp->flags & FLAG_MOUNT_POINT) { + pinned = True; + if (verbose > 1) + rprintf(FINFO, "mount point %s pins parent directory\n", + f_name(fp, NULL)); continue; + } strlcpy(p, fp->basename, remainder); - delete_item(fname, fp->mode, flags & ~DEL_TERSE); + result = delete_item(fname, fp->mode, + (flags | DEL_PARENT_EXTRANEOUS) & ~DEL_REPLACE); + /* as long as nothing is pinned, we can ignore deletion errors; + * if the filesystem lets us rmdir anyway, great, otherwise we + * get ENOTEMPTY and can handle it there */ + if (result == -3) { + pinned = True; + if (verbose > 1) + rprintf(FINFO, "pinned directory %s pins parent directory\n", + f_name(fp, NULL)); + } } - flist_free(dirlist); fname[dlen] = '\0'; + + if (pinned) + result = -3; + else + result = 0; +out: pop_local_filters(save_filters); + flist_free(dirlist); + return result; +} - if (max_delete && ++deletion_count > max_delete) - return 0; +/* Delete a file or directory. If DEL_RECURSE is set in the flags, this will + * delete recursively. + * + * Note that fname must point to a MAXPATHLEN buffer if the mode indicates it's + * a directory! (The buffer is used for recursion, but returned unchanged.) + * + * Returns: 0 on success, -1 on failure, -2 if deletion limit hit, -3 if pinned, + * -4 if nonempty. + */ +static int delete_item(char *fname, int mode, int flags) +{ + int ok, result; + char *what; + + if (verbose > 2) + rprintf(FINFO, "delete_item %s mode %o flags %d\n", + fname, mode, flags); + + if (S_ISDIR(mode)) { + result = empty_directory(fname, flags); + if (result == -3) { + if (!(flags & DEL_REPLACE) && verbose > 1) + rprintf(FINFO, "%s is pinned\n", fname); + return -3; + } else if (result == -4) { + rprintf(FINFO, "nonempty directory %s not deleted " + "because --force not given\n", fname); + return -4; + } + /* otherwise try to delete the directory */ + } + + if (!(flags & DEL_REPLACE)) { + if (max_delete >= 0 && ++deletion_count > max_delete) { + if (verbose > 1) + rprintf(FINFO, "%s is over --max-delete limit\n", fname); + return -2; + } + } - if (do_rmdir(fname) == 0) { - if (!(flags & DEL_TERSE)) + if (S_ISDIR(mode)) { + what = "rmdir"; + ok = do_rmdir(fname) == 0; + } else { + if (make_backups && (backup_dir || !is_backup_file(fname))) { + what = "make_backup"; + ok = make_backup(fname); + } else { + what = "unlink"; + ok = robust_unlink(fname) == 0; + } + } + + if (ok) { + if (!(flags & DEL_REPLACE)) log_delete(fname, mode); - } else if (errno != ENOTEMPTY && errno != EEXIST && errno != ENOENT) { - rsyserr(FERROR, errno, "delete_file: rmdir %s failed", - full_fname(fname)); + return 0; + } else if (errno == ENOENT) { + --deletion_count; + return 0; + } else { + rsyserr(FERROR, errno, "delete_file: %s %s failed", + what, full_fname(fname)); return -1; } - - return 0; } @@ -265,17 +304,23 @@ return; } - dirlist = get_dirlist(fbuf, dlen, 0); + dirlist = get_dirlist(fbuf, dlen, 0, NULL); /* If an item in dirlist is not found in flist, delete it * from the filesystem. */ for (i = dirlist->count; i--; ) { struct file_struct *fp = dirlist->files[i]; - if (!fp->basename || fp->flags & FLAG_MOUNT_POINT) + if (!fp->basename) continue; + if (fp->flags & FLAG_MOUNT_POINT) { + if (verbose > 1) + rprintf(FINFO, "mount point %s not deleted\n", + f_name(fp, NULL)); + continue; + } if (flist_find(flist, fp) < 0) { f_name(fp, delbuf); - delete_item(delbuf, fp->mode, DEL_FORCE_RECURSE); + delete_item(delbuf, fp->mode, DEL_RECURSE); } } @@ -802,7 +847,8 @@ char *fnamecmp, *partialptr, *backupptr = NULL; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; - int del_opts = DEL_TERSE | (delete_mode ? DEL_FORCE_RECURSE : 0); + int del_opts = DEL_REPLACE | + (delete_mode || force_delete ? DEL_RECURSE : 0); if (list_only) return; @@ -875,7 +921,7 @@ parent_dirname = dn; if (need_fuzzy_dirlist && S_ISREG(file->mode)) { - fuzzy_dirlist = get_dirlist(dn, -1, 1); + fuzzy_dirlist = get_dirlist(dn, -1, 1, NULL); need_fuzzy_dirlist = 0; } @@ -908,8 +954,10 @@ * we need to delete it. If it doesn't exist, then * (perhaps recursively) create it. */ if (statret == 0 && !S_ISDIR(st.st_mode)) { - if (delete_item(fname, st.st_mode, del_opts) < 0) + if (delete_item(fname, st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new directory at %s\n", fname); return; + } statret = -1; } if (dry_run && statret != 0 && missing_below < 0) { @@ -991,8 +1039,10 @@ } /* Not the right symlink (or not a symlink), so * delete it. */ - if (delete_item(fname, st.st_mode, del_opts) < 0) + if (delete_item(fname, st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new symlink at %s\n", fname); return; + } if (!S_ISLNK(st.st_mode)) statret = -1; } else if (basis_dir[0] != NULL) { @@ -1056,8 +1106,10 @@ || (st.st_mode & ~CHMOD_BITS) != (file->mode & ~CHMOD_BITS) || st.st_rdev != file->u.rdev) { if (statret == 0 - && delete_item(fname, st.st_mode, del_opts) < 0) + && delete_item(fname, st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new device or special at %s\n", fname); return; + } if (preserve_hard_links && file->link_u.links && hard_link_check(file, ndx, fname, -1, &st, itemizing, code, HL_SKIP)) @@ -1137,8 +1189,10 @@ fnamecmp_type = FNAMECMP_FNAME; if (statret == 0 && !S_ISREG(st.st_mode)) { - if (delete_item(fname, st.st_mode, del_opts) != 0) + if (delete_item(fname, st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new regular file at %s\n", fname); return; + } statret = -1; stat_errno = ENOENT; } @@ -1254,7 +1308,7 @@ close(fd); return; } - if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) { + if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS, NULL))) { close(fd); goto pretend_missing; } @@ -1515,7 +1569,7 @@ } recv_generator(NULL, NULL, 0, 0, 0, code, -1); - if (max_delete > 0 && deletion_count > max_delete) { + if (max_delete >= 0 && deletion_count > max_delete) { rprintf(FINFO, "Deletions stopped due to --max-delete limit (%d skipped)\n", deletion_count - max_delete); Index: compat.c =================================================================== RCS file: /cvsroot/rsync/compat.c,v retrieving revision 1.34 diff -u -r1.34 compat.c --- compat.c 25 Apr 2006 23:51:12 -0000 1.34 +++ compat.c 22 Jun 2006 15:38:51 -0000 @@ -25,6 +25,7 @@ int remote_protocol = 0; extern int verbose; +extern int am_sender; extern int am_server; extern int inplace; extern int fuzzy_basis; @@ -32,6 +33,7 @@ extern int checksum_seed; extern int basis_dir_cnt; extern int prune_empty_dirs; +extern int max_delete; extern int protocol_version; extern char *dest_option; @@ -75,6 +77,16 @@ exit_cleanup(RERR_PROTOCOL); } + if (protocol_version < 30) { + if (max_delete == 0 && am_sender) { + rprintf(FERROR, + "--max-delete=0 requires protocol 30 or higher" + " (negotiated %d).\n", + protocol_version); + exit_cleanup(RERR_PROTOCOL); + } + } + if (protocol_version < 29) { if (fuzzy_basis) { rprintf(FERROR, Index: options.c =================================================================== RCS file: /cvsroot/rsync/options.c,v retrieving revision 1.346 diff -u -r1.346 options.c --- options.c 7 Jun 2006 23:05:24 -0000 1.346 +++ options.c 22 Jun 2006 15:38:55 -0000 @@ -106,7 +106,7 @@ int ignore_existing = 0; int ignore_non_existing = 0; int need_messages_from_generator = 0; -int max_delete = 0; +int max_delete = -1; OFF_T max_size = 0; OFF_T min_size = 0; int ignore_errors = 0; @@ -1612,7 +1612,7 @@ args[ac++] = arg; } - if (max_delete && am_sender) { + if (max_delete >= 0 && am_sender) { if (asprintf(&arg, "--max-delete=%d", max_delete) < 0) goto oom; args[ac++] = arg; Index: backup.c =================================================================== RCS file: /cvsroot/rsync/backup.c,v retrieving revision 1.56 diff -u -r1.56 backup.c --- backup.c 25 Apr 2006 23:51:12 -0000 1.56 +++ backup.c 22 Jun 2006 15:38:51 -0000 @@ -180,7 +180,7 @@ if (do_lstat(fname, &st) < 0) return 1; - if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) + if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS, NULL))) return 1; /* the file could have disappeared */ if (!(buf = get_backup_name(fname))) Index: flist.c =================================================================== RCS file: /cvsroot/rsync/flist.c,v retrieving revision 1.349 diff -u -r1.349 flist.c --- flist.c 9 May 2006 18:31:13 -0000 1.349 +++ flist.c 22 Jun 2006 15:38:53 -0000 @@ -201,13 +201,16 @@ /* This function is used to check if a file should be included/excluded * from the list of files based on its name and type etc. The value of - * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. */ -static int is_excluded(char *fname, int is_dir, int filter_level) + * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. If + * parent_extraneous is true, parent-protect counts as included. */ +static int is_excluded(char *fname, int is_dir, int filter_level, + BOOL parent_extraneous) { #if 0 /* This currently never happens, so avoid a useless compare. */ if (filter_level == NO_FILTERS) return 0; #endif + int fresult; if (fname) { /* never exclude '.', even if somebody does --exclude '*' */ if (fname[0] == '.' && !fname[1]) @@ -220,12 +223,14 @@ } } if (server_filter_list.head - && check_filter(&server_filter_list, fname, is_dir) < 0) + && (fresult = check_filter(&server_filter_list, fname, is_dir)) < 0 + && (!parent_extraneous || fresult != -2)) return 1; if (filter_level != ALL_FILTERS) return 0; if (filter_list.head - && check_filter(&filter_list, fname, is_dir) < 0) + && (fresult = check_filter(&filter_list, fname, is_dir)) < 0 + && (!parent_extraneous || fresult != -2)) return 1; return 0; } @@ -247,7 +252,7 @@ } static void send_directory(int f, struct file_list *flist, - char *fbuf, int len); + char *fbuf, int len, BOOL *pinned); static char *flist_dir; static int flist_dir_len; @@ -713,7 +718,7 @@ **/ struct file_struct *make_file(char *fname, struct file_list *flist, STRUCT_STAT *stp, unsigned short flags, - int filter_level) + int filter_level, BOOL *protected_p) { static char *lastdir; static int lastdir_len = -1; @@ -724,6 +729,7 @@ char linkname[MAXPATHLEN]; int alloc_len, basename_len, dirname_len, linkname_len, sum_len; char *basename, *dirname, *bp; + BOOL excluded; if (!flist || !flist->count) /* Ignore lastdir when invalid. */ lastdir_len = -1; @@ -745,8 +751,12 @@ int save_errno = errno; /* See if file is excluded before reporting an error. */ if (filter_level != NO_FILTERS - && is_excluded(thisname, 0, filter_level)) + && is_excluded(thisname, 0, filter_level, protected_p != NULL) + && is_excluded(thisname, 1, filter_level, protected_p != NULL)) { + if (protected_p) + *protected_p = True; return NULL; + } if (save_errno == ENOENT) { #ifdef SUPPORT_LINKS /* Avoid "vanished" error if symlink points nowhere. */ @@ -796,7 +806,11 @@ flags |= FLAG_MOUNT_POINT; } - if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) + excluded = is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level, + protected_p != NULL); + if (protected_p) + *protected_p = excluded; + if (excluded) return NULL; if (lp_ignore_nonreadable(module_id)) { @@ -939,12 +953,12 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, char *fname, STRUCT_STAT *stp, - unsigned short flags) + unsigned short flags, BOOL *protected_p) { struct file_struct *file; file = make_file(fname, flist, stp, flags, - f == -2 ? SERVER_FILTERS : ALL_FILTERS); + f == -2 ? SERVER_FILTERS : ALL_FILTERS, protected_p); if (!file) return NULL; @@ -981,7 +995,7 @@ return; } save_filters = push_local_filters(fbuf, len); - send_directory(f, flist, fbuf, len); + send_directory(f, flist, fbuf, len, NULL); pop_local_filters(save_filters); fbuf[ol] = '\0'; if (is_dot_dir) @@ -995,13 +1009,14 @@ * might call this with f set to -2, which also indicates that local filter * rules should be ignored. */ static void send_directory(int f, struct file_list *flist, - char *fbuf, int len) + char *fbuf, int len, BOOL *pinned_p) { struct dirent *di; unsigned remainder; char *p; DIR *d; int start = flist->count; + BOOL protected; if (!(d = opendir(fbuf))) { io_error |= IOERR_GENERAL; @@ -1015,6 +1030,9 @@ *p = '\0'; remainder = MAXPATHLEN - (p - fbuf); + if (pinned_p) + *pinned_p = False; + for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) { char *dname = d_name(di); if (dname[0] == '.' && (dname[1] == '\0' @@ -1028,7 +1046,17 @@ continue; } - send_file_name(f, flist, fbuf, NULL, 0); + if (pinned_p == NULL) + send_file_name(f, flist, fbuf, NULL, 0, NULL); + else { + send_file_name(f, flist, fbuf, NULL, 0, &protected); + if (protected) { + *pinned_p = True; + if (verbose > 1) + rprintf(FINFO, "protected file %s pins parent directory\n", + fbuf); + } + } } fbuf[len] = '\0'; @@ -1245,7 +1273,7 @@ xfer_dirs = 1; while ((slash = strchr(slash+1, '/')) != 0) { *slash = '\0'; - send_file_name(f, flist, fbuf, NULL, 0); + send_file_name(f, flist, fbuf, NULL, 0, NULL); *slash = '/'; } copy_links = save_copy_links; @@ -1261,11 +1289,11 @@ if (recurse || (xfer_dirs && is_dot_dir)) { struct file_struct *file; - file = send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR); + file = send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR, NULL); if (file) send_if_directory(f, flist, file, fbuf, len); } else - send_file_name(f, flist, fbuf, &st, 0); + send_file_name(f, flist, fbuf, &st, 0, NULL); if (olddir[0]) { flist_dir = NULL; @@ -1637,7 +1665,7 @@ } prev_depth = file->dir.depth; if (is_excluded(f_name(file, fbuf), 1, - ALL_FILTERS)) { + ALL_FILTERS, False)) { /* Keep dirs through this dir. */ for (j = prev_depth-1; ; j--) { fp = flist->files[prev_i]; @@ -1889,9 +1917,10 @@ * exclude rules except for the daemon's. If "dlen" is >=0, it is the length * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN * buffer (the functions we call will append names onto the end, but the old - * dir value will be restored on exit). */ + * dir value will be restored on exit). If pinned is not NULL, store in + * *pinned whether the directory is pinned by a protected file. */ struct file_list *get_dirlist(char *dirname, int dlen, - int ignore_filter_rules) + int ignore_filter_rules, BOOL *pinned) { struct file_list *dirlist; char dirbuf[MAXPATHLEN]; @@ -1909,7 +1938,7 @@ recurse = 0; xfer_dirs = 1; - send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen); + send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen, pinned); xfer_dirs = save_xfer_dirs; recurse = save_recurse; if (do_progress) Index: proto.h =================================================================== RCS file: /cvsroot/rsync/proto.h,v retrieving revision 1.307 diff -u -r1.307 proto.h --- proto.h 7 Jun 2006 23:05:11 -0000 1.307 +++ proto.h 22 Jun 2006 15:38:55 -0000 @@ -72,7 +72,7 @@ void flist_expand(struct file_list *flist); struct file_struct *make_file(char *fname, struct file_list *flist, STRUCT_STAT *stp, unsigned short flags, - int filter_level); + int filter_level, BOOL *protected_p); struct file_list *send_file_list(int f, int argc, char *argv[]); struct file_list *recv_file_list(int f); int flist_find(struct file_list *flist, struct file_struct *f); @@ -82,7 +82,7 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2); char *f_name(struct file_struct *f, char *fbuf); struct file_list *get_dirlist(char *dirname, int dlen, - int ignore_filter_rules); + int ignore_filter_rules, BOOL *pinned); int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st); void itemize(struct file_struct *file, int ndx, int statret, STRUCT_STAT *st, int32 iflags, uchar fnamecmp_type, char *xname); Index: exclude.c =================================================================== RCS file: /cvsroot/rsync/exclude.c,v retrieving revision 1.134 diff -u -r1.134 exclude.c --- exclude.c 11 May 2006 07:54:33 -0000 1.134 +++ exclude.c 22 Jun 2006 15:38:52 -0000 @@ -51,8 +51,8 @@ #define MAX_RULE_PREFIX (16) #define MODIFIERS_MERGE_FILE "-+Cenw" -#define MODIFIERS_INCL_EXCL "/!Crs" -#define MODIFIERS_HIDE_PROTECT "/!" +#define MODIFIERS_INCL_EXCL "/!Crsp" +#define MODIFIERS_HIDE_PROTECT "/!p" /* The dirbuf is set by push_local_filters() to the current subdirectory * relative to curr_dir that is being processed. The path always has a @@ -595,7 +595,8 @@ /* * Return -1 if file "name" is defined to be excluded by the specified - * exclude list, 1 if it is included, and 0 if it was not matched. + * exclude list, -2 if it is excluded but parent protected, 1 if it is included, + * and 0 if it was not matched. */ int check_filter(struct filter_list_struct *listp, char *name, int name_is_dir) { @@ -619,7 +620,8 @@ if (rule_matches(name, ent, name_is_dir)) { report_filter_result(name, ent, name_is_dir, listp->debug_type); - return ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1; + return ent->match_flags & MATCHFLG_PARENT_PROTECT ? -2 : + ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1; } } @@ -809,6 +811,13 @@ case 'n': new_mflags |= MATCHFLG_NO_INHERIT; break; + case 'p': + if (new_mflags & MATCHFLG_INCLUDE + || new_mflags & MATCHFLG_SENDER_SIDE + || (delete_excluded && !(new_mflags & MATCHFLG_RECEIVER_SIDE))) + goto invalid; + new_mflags |= MATCHFLG_PARENT_PROTECT; + break; case 'r': new_mflags |= MATCHFLG_RECEIVER_SIDE; break; @@ -1079,6 +1088,12 @@ && (!for_xfer || protocol_version >= 29 || (delete_excluded && am_sender))) *op++ = 'r'; + if (match_flags & MATCHFLG_PARENT_PROTECT) { + if (!for_xfer || protocol_version >= 30) + *op++ = 'p'; + else if (am_sender) + return NULL; + } if (op - buf > legal_len) return NULL; if (legal_len) @@ -1124,7 +1139,7 @@ if (!p) { rprintf(FERROR, "filter rules are too modern for remote rsync.\n"); - exit_cleanup(RERR_SYNTAX); + exit_cleanup(RERR_PROTOCOL); } if (f_out < 0) continue; Index: patches/acls.diff =================================================================== RCS file: /cvsroot/rsync/patches/acls.diff,v retrieving revision 1.142 diff -u -r1.142 acls.diff --- patches/acls.diff 5 Jun 2006 20:57:03 -0000 1.142 +++ patches/acls.diff 22 Jun 2006 15:39:00 -0000 @@ -1159,7 +1159,7 @@ +#ifdef SUPPORT_ACLS + sx.acc_acl = sx.def_acl = NULL; +#endif -+ if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS))) ++ if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS, NULL))) + continue; +#ifdef SUPPORT_ACLS + if (preserve_acls) { @@ -1191,7 +1191,7 @@ + sx.acc_acl = sx.def_acl = NULL; +#endif - if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) + if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS, NULL))) return 1; /* the file could have disappeared */ @@ -186,6 +202,13 @@ static int keep_backup(char *fname) if (!(buf = get_backup_name(fname))) @@ -1218,7 +1218,7 @@ robust_unlink(fname); /* Just in case... */ --- old/configure.in +++ new/configure.in -@@ -482,6 +482,11 @@ if test x"$ac_cv_func_strcasecmp" = x"no +@@ -490,6 +490,11 @@ if test x"$ac_cv_func_strcasecmp" = x"no AC_CHECK_LIB(resolv, strcasecmp) fi @@ -1230,7 +1230,7 @@ dnl At the moment we don't test for a broken memcmp(), because all we dnl need to do is test for equality, not comparison, and it seems that dnl every platform has a memcmp that can do at least that. -@@ -746,6 +751,78 @@ AC_SUBST(OBJ_RESTORE) +@@ -754,6 +759,78 @@ AC_SUBST(OBJ_RESTORE) AC_SUBST(CC_SHOBJ_FLAG) AC_SUBST(BUILD_POPT) @@ -1328,7 +1328,7 @@ #ifdef SUPPORT_LINKS if (preserve_links && S_ISLNK(f->mode)) { rprintf(FINFO, "%s %11.0f %s %s -> %s\n", -@@ -491,6 +494,9 @@ static struct file_struct *receive_file_ +@@ -496,6 +499,9 @@ static struct file_struct *receive_file_ char thisname[MAXPATHLEN]; unsigned int l1 = 0, l2 = 0; int alloc_len, basename_len, dirname_len, linkname_len, sum_len; @@ -1338,7 +1338,7 @@ OFF_T file_length; char *basename, *dirname, *bp; struct file_struct *file; -@@ -594,13 +600,27 @@ static struct file_struct *receive_file_ +@@ -599,13 +605,27 @@ static struct file_struct *receive_file_ sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0; @@ -1366,7 +1366,7 @@ file->modtime = modtime; file->length = file_length; -@@ -693,6 +713,11 @@ static struct file_struct *receive_file_ +@@ -698,6 +718,11 @@ static struct file_struct *receive_file_ read_buf(f, sum, checksum_len); } @@ -1378,8 +1378,8 @@ return file; } -@@ -942,6 +967,9 @@ static struct file_struct *send_file_nam - unsigned short flags) +@@ -956,6 +981,9 @@ static struct file_struct *send_file_nam + unsigned short flags, BOOL *protected_p) { struct file_struct *file; +#ifdef SUPPORT_ACLS @@ -1387,8 +1387,8 @@ +#endif file = make_file(fname, flist, stp, flags, - f == -2 ? SERVER_FILTERS : ALL_FILTERS); -@@ -951,6 +979,15 @@ static struct file_struct *send_file_nam + f == -2 ? SERVER_FILTERS : ALL_FILTERS, protected_p); +@@ -965,6 +993,15 @@ static struct file_struct *send_file_nam if (chmod_modes && !S_ISLNK(file->mode)) file->mode = tweak_mode(file->mode, chmod_modes); @@ -1404,7 +1404,7 @@ maybe_emit_filelist_progress(flist->count + flist_count_offset); flist_expand(flist); -@@ -958,6 +995,15 @@ static struct file_struct *send_file_nam +@@ -972,6 +1009,15 @@ static struct file_struct *send_file_nam if (file->basename[0]) { flist->files[flist->count++] = file; send_file_entry(file, f); @@ -1438,7 +1438,7 @@ extern struct stats stats; extern dev_t filesystem_dev; extern char *backup_dir; -@@ -316,22 +318,27 @@ static void do_delete_pass(struct file_l +@@ -361,22 +363,27 @@ static void do_delete_pass(struct file_l rprintf(FINFO, " \r"); } @@ -1471,7 +1471,7 @@ int32 iflags, uchar fnamecmp_type, char *xname) { if (statret >= 0) { /* A from-dest-dir statret can == 1! */ -@@ -339,19 +346,23 @@ void itemize(struct file_struct *file, i +@@ -384,19 +391,23 @@ void itemize(struct file_struct *file, i : S_ISDIR(file->mode) ? !omit_dir_times : !S_ISLNK(file->mode); @@ -1500,7 +1500,7 @@ } else iflags |= ITEM_IS_NEW; -@@ -604,7 +615,7 @@ void check_for_finished_hlinks(int itemi +@@ -649,7 +660,7 @@ void check_for_finished_hlinks(int itemi * handling the file, -1 if no dest-linking occurred, or a non-negative * value if we found an alternate basis file. */ static int try_dests_reg(struct file_struct *file, char *fname, int ndx, @@ -1509,7 +1509,7 @@ int maybe_ATTRS_REPORT, enum logcode code) { int save_ignore_times = ignore_times; -@@ -618,7 +629,7 @@ static int try_dests_reg(struct file_str +@@ -663,7 +674,7 @@ static int try_dests_reg(struct file_str do { pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); @@ -1518,7 +1518,7 @@ continue; switch (match_level) { case 0: -@@ -626,16 +637,20 @@ static int try_dests_reg(struct file_str +@@ -671,16 +682,20 @@ static int try_dests_reg(struct file_str match_level = 1; /* FALL THROUGH */ case 1: @@ -1542,7 +1542,7 @@ continue; best_match = j; match_level = 3; -@@ -652,14 +667,14 @@ static int try_dests_reg(struct file_str +@@ -697,14 +712,14 @@ static int try_dests_reg(struct file_str if (j != best_match) { j = best_match; pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); @@ -1559,7 +1559,7 @@ cmpbuf, 1, itemizing && verbose > 1, code) < 0) -@@ -668,8 +683,13 @@ static int try_dests_reg(struct file_str +@@ -713,8 +728,13 @@ static int try_dests_reg(struct file_str hard_link_cluster(file, ndx, itemizing, code); } else #endif @@ -1575,7 +1575,7 @@ if (verbose > 1 && maybe_ATTRS_REPORT) { rprintf(FCLIENT, "%s is uptodate\n", fname); } -@@ -685,8 +705,13 @@ static int try_dests_reg(struct file_str +@@ -730,8 +750,13 @@ static int try_dests_reg(struct file_str } return -1; } @@ -1591,7 +1591,7 @@ set_file_attrs(fname, file, NULL, 0); if (maybe_ATTRS_REPORT && ((!itemizing && verbose && match_level == 2) -@@ -710,13 +735,18 @@ static int try_dests_non(struct file_str +@@ -755,13 +780,18 @@ static int try_dests_non(struct file_str enum logcode code) { char fnamebuf[MAXPATHLEN]; @@ -1613,7 +1613,7 @@ continue; if (S_ISLNK(file->mode)) { #ifdef SUPPORT_LINKS -@@ -729,10 +759,10 @@ static int try_dests_non(struct file_str +@@ -774,10 +804,10 @@ static int try_dests_non(struct file_str #endif continue; } else if (IS_SPECIAL(file->mode)) { @@ -1626,7 +1626,7 @@ continue; } else { rprintf(FERROR, -@@ -763,7 +793,15 @@ static int try_dests_non(struct file_str +@@ -808,7 +838,15 @@ static int try_dests_non(struct file_str int changes = compare_dest ? 0 : ITEM_LOCAL_CHANGE + (link_dest ? ITEM_XNAME_FOLLOWS : 0); char *lp = link_dest ? "" : NULL; @@ -1643,7 +1643,7 @@ } if (verbose > 1 && maybe_ATTRS_REPORT) { rprintf(FCLIENT, "%s is uptodate\n", fname); -@@ -775,6 +813,7 @@ static int try_dests_non(struct file_str +@@ -820,6 +858,7 @@ static int try_dests_non(struct file_str } static int phase = 0; @@ -1651,7 +1651,7 @@ /* Acts on the_file_list->file's ndx'th item, whose name is fname. If a dir, * make sure it exists, and has the right permissions/timestamp info. For -@@ -796,7 +835,8 @@ static void recv_generator(char *fname, +@@ -841,7 +880,8 @@ static void recv_generator(char *fname, static int need_fuzzy_dirlist = 0; struct file_struct *fuzzy_file = NULL; int fd = -1, f_copy = -1; @@ -1661,7 +1661,7 @@ struct file_struct *back_file = NULL; int statret, real_ret, stat_errno; char *fnamecmp, *partialptr, *backupptr = NULL; -@@ -852,6 +892,9 @@ static void recv_generator(char *fname, +@@ -898,6 +938,9 @@ static void recv_generator(char *fname, } else if (!dry_run) return; } @@ -1671,7 +1671,7 @@ if (dry_run > 1) { statret = -1; stat_errno = ENOENT; -@@ -859,7 +902,7 @@ static void recv_generator(char *fname, +@@ -905,7 +948,7 @@ static void recv_generator(char *fname, char *dn = file->dirname ? file->dirname : "."; if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { if (relative_paths && !implied_dirs @@ -1680,7 +1680,7 @@ && create_directory_path(fname) < 0) { rsyserr(FERROR, errno, "recv_generator: mkdir %s failed", -@@ -871,6 +914,10 @@ static void recv_generator(char *fname, +@@ -917,6 +960,10 @@ static void recv_generator(char *fname, } if (fuzzy_basis) need_fuzzy_dirlist = 1; @@ -1691,7 +1691,7 @@ } parent_dirname = dn; -@@ -879,7 +926,7 @@ static void recv_generator(char *fname, +@@ -925,7 +972,7 @@ static void recv_generator(char *fname, need_fuzzy_dirlist = 0; } @@ -1700,7 +1700,7 @@ keep_dirlinks && S_ISDIR(file->mode)); stat_errno = errno; } -@@ -897,8 +944,9 @@ static void recv_generator(char *fname, +@@ -943,8 +990,9 @@ static void recv_generator(char *fname, * mode based on the local permissions and some heuristics. */ if (!preserve_perms) { int exists = statret == 0 @@ -1712,18 +1712,18 @@ } if (S_ISDIR(file->mode)) { -@@ -907,8 +955,8 @@ static void recv_generator(char *fname, +@@ -953,8 +1001,8 @@ static void recv_generator(char *fname, * file of that name and it is *not* a directory, then * we need to delete it. If it doesn't exist, then * (perhaps recursively) create it. */ - if (statret == 0 && !S_ISDIR(st.st_mode)) { -- if (delete_item(fname, st.st_mode, del_opts) < 0) +- if (delete_item(fname, st.st_mode, del_opts) < 0) { + if (statret == 0 && !S_ISDIR(sx.st.st_mode)) { -+ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) ++ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new directory at %s\n", fname); return; - statret = -1; - } -@@ -917,7 +965,11 @@ static void recv_generator(char *fname, + } +@@ -965,7 +1013,11 @@ static void recv_generator(char *fname, dry_run++; } if (itemizing && f_out != -1) { @@ -1736,7 +1736,7 @@ statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL); } if (statret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) { -@@ -937,19 +989,19 @@ static void recv_generator(char *fname, +@@ -985,19 +1037,19 @@ static void recv_generator(char *fname, return; } } @@ -1761,7 +1761,7 @@ if (preserve_links && S_ISLNK(file->mode)) { #ifdef SUPPORT_LINKS -@@ -967,7 +1019,7 @@ static void recv_generator(char *fname, +@@ -1015,7 +1067,7 @@ static void recv_generator(char *fname, char lnk[MAXPATHLEN]; int len; @@ -1770,7 +1770,7 @@ && (len = readlink(fname, lnk, MAXPATHLEN-1)) > 0) { lnk[len] = 0; /* A link already pointing to the -@@ -975,10 +1027,10 @@ static void recv_generator(char *fname, +@@ -1023,10 +1075,10 @@ static void recv_generator(char *fname, * required. */ if (strcmp(lnk, file->u.link) == 0) { if (itemizing) { @@ -1783,19 +1783,21 @@ maybe_ATTRS_REPORT); if (preserve_hard_links && file->link_u.links) { -@@ -991,9 +1043,9 @@ static void recv_generator(char *fname, +@@ -1039,11 +1091,11 @@ static void recv_generator(char *fname, } /* Not the right symlink (or not a symlink), so * delete it. */ -- if (delete_item(fname, st.st_mode, del_opts) < 0) -+ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) +- if (delete_item(fname, st.st_mode, del_opts) < 0) { ++ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new symlink at %s\n", fname); return; + } - if (!S_ISLNK(st.st_mode)) + if (!S_ISLNK(sx.st.st_mode)) statret = -1; } else if (basis_dir[0] != NULL) { if (try_dests_non(file, fname, ndx, itemizing, -@@ -1009,7 +1061,7 @@ static void recv_generator(char *fname, +@@ -1059,7 +1111,7 @@ static void recv_generator(char *fname, } } if (preserve_hard_links && file->link_u.links @@ -1804,7 +1806,7 @@ itemizing, code, HL_SKIP)) return; if (do_symlink(file->u.link,fname) != 0) { -@@ -1018,7 +1070,7 @@ static void recv_generator(char *fname, +@@ -1068,7 +1120,7 @@ static void recv_generator(char *fname, } else { set_file_attrs(fname, file, NULL, 0); if (itemizing) { @@ -1813,7 +1815,7 @@ ITEM_LOCAL_CHANGE, 0, NULL); } if (code && verbose) { -@@ -1052,18 +1104,22 @@ static void recv_generator(char *fname, +@@ -1102,20 +1154,24 @@ static void recv_generator(char *fname, itemizing = code = 0; } } @@ -1827,10 +1829,12 @@ + || (sx.st.st_mode & ~CHMOD_BITS) != (file->mode & ~CHMOD_BITS) + || sx.st.st_rdev != file->u.rdev) { if (statret == 0 -- && delete_item(fname, st.st_mode, del_opts) < 0) +- && delete_item(fname, st.st_mode, del_opts) < 0) { ++ && delete_item(fname, sx.st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new device or special at %s\n", fname); - return; -+ && delete_item(fname, sx.st.st_mode, del_opts) < 0) + goto cleanup; + } if (preserve_hard_links && file->link_u.links - && hard_link_check(file, ndx, fname, -1, &st, + && hard_link_check(file, ndx, fname, -1, &sx, @@ -1844,7 +1848,7 @@ statret = -1; if (verbose > 2) { rprintf(FINFO,"mknod(%s,0%o,0x%x)\n", -@@ -1076,7 +1132,7 @@ static void recv_generator(char *fname, +@@ -1128,7 +1184,7 @@ static void recv_generator(char *fname, } else { set_file_attrs(fname, file, NULL, 0); if (itemizing) { @@ -1853,7 +1857,7 @@ ITEM_LOCAL_CHANGE, 0, NULL); } if (code && verbose) -@@ -1088,12 +1144,12 @@ static void recv_generator(char *fname, +@@ -1140,12 +1196,12 @@ static void recv_generator(char *fname, } } else { if (itemizing) @@ -1869,7 +1873,7 @@ } if (!S_ISREG(file->mode)) { -@@ -1127,7 +1183,7 @@ static void recv_generator(char *fname, +@@ -1179,7 +1235,7 @@ static void recv_generator(char *fname, } if (update_only && statret == 0 @@ -1878,17 +1882,18 @@ if (verbose > 1) rprintf(FINFO, "%s is newer\n", fname); return; -@@ -1136,18 +1192,18 @@ static void recv_generator(char *fname, +@@ -1188,8 +1244,8 @@ static void recv_generator(char *fname, fnamecmp = fname; fnamecmp_type = FNAMECMP_FNAME; - if (statret == 0 && !S_ISREG(st.st_mode)) { -- if (delete_item(fname, st.st_mode, del_opts) != 0) +- if (delete_item(fname, st.st_mode, del_opts) < 0) { + if (statret == 0 && !S_ISREG(sx.st.st_mode)) { -+ if (delete_item(fname, sx.st.st_mode, del_opts) != 0) ++ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) { + rprintf(FERROR, "could not make way for new regular file at %s\n", fname); return; - statret = -1; - stat_errno = ENOENT; + } +@@ -1198,10 +1254,10 @@ static void recv_generator(char *fname, } if (statret != 0 && basis_dir[0] != NULL) { @@ -1901,7 +1906,7 @@ if (j != -1) { fnamecmp = fnamecmpbuf; fnamecmp_type = j; -@@ -1156,7 +1212,7 @@ static void recv_generator(char *fname, +@@ -1210,7 +1266,7 @@ static void recv_generator(char *fname, } real_ret = statret; @@ -1910,7 +1915,7 @@ if (partial_dir && (partialptr = partial_dir_fname(fname)) != NULL && link_stat(partialptr, &partial_st, 0) == 0 -@@ -1175,7 +1231,7 @@ static void recv_generator(char *fname, +@@ -1229,7 +1285,7 @@ static void recv_generator(char *fname, rprintf(FINFO, "fuzzy basis selected for %s: %s\n", fname, fnamecmpbuf); } @@ -1919,7 +1924,7 @@ statret = 0; fnamecmp = fnamecmpbuf; fnamecmp_type = FNAMECMP_FUZZY; -@@ -1184,7 +1240,7 @@ static void recv_generator(char *fname, +@@ -1238,7 +1294,7 @@ static void recv_generator(char *fname, if (statret != 0) { if (preserve_hard_links && file->link_u.links @@ -1928,7 +1933,7 @@ itemizing, code, HL_SKIP)) return; if (stat_errno == ENOENT) -@@ -1194,31 +1250,44 @@ static void recv_generator(char *fname, +@@ -1248,31 +1304,44 @@ static void recv_generator(char *fname, return; } @@ -1979,7 +1984,7 @@ fnamecmp = partialptr; fnamecmp_type = FNAMECMP_PARTIAL_DIR; statret = 0; -@@ -1242,17 +1311,21 @@ static void recv_generator(char *fname, +@@ -1296,17 +1365,21 @@ static void recv_generator(char *fname, pretend_missing: /* pretend the file didn't exist */ if (preserve_hard_links && file->link_u.links @@ -2002,9 +2007,9 @@ - return; + goto cleanup; } - if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) { + if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS, NULL))) { close(fd); -@@ -1263,7 +1336,7 @@ static void recv_generator(char *fname, +@@ -1317,7 +1390,7 @@ static void recv_generator(char *fname, full_fname(backupptr)); free(back_file); close(fd); @@ -2013,7 +2018,7 @@ } if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) { -@@ -1271,14 +1344,14 @@ static void recv_generator(char *fname, +@@ -1325,14 +1398,14 @@ static void recv_generator(char *fname, full_fname(backupptr)); free(back_file); close(fd); @@ -2030,7 +2035,7 @@ } if (verbose > 2) -@@ -1296,24 +1369,32 @@ static void recv_generator(char *fname, +@@ -1350,24 +1423,32 @@ static void recv_generator(char *fname, iflags |= ITEM_BASIS_TYPE_FOLLOWS; if (fnamecmp_type == FNAMECMP_FUZZY) iflags |= ITEM_XNAME_FOLLOWS; @@ -2068,7 +2073,7 @@ if (f_copy >= 0) { close(f_copy); -@@ -1326,6 +1407,13 @@ static void recv_generator(char *fname, +@@ -1380,6 +1461,13 @@ static void recv_generator(char *fname, } close(fd); @@ -2082,7 +2087,7 @@ } void generate_files(int f_out, struct file_list *flist, char *local_name) -@@ -1385,6 +1473,8 @@ void generate_files(int f_out, struct fi +@@ -1439,6 +1527,8 @@ void generate_files(int f_out, struct fi * notice that and let us know via the redo pipe (or its closing). */ ignore_timeout = 1; @@ -5505,7 +5510,7 @@ +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype); --- old/log.c +++ new/log.c -@@ -582,8 +582,10 @@ static void log_formatted(enum logcode c +@@ -609,8 +609,10 @@ static void log_formatted(enum logcode c n[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p'; n[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o'; n[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g'; @@ -5869,7 +5874,7 @@ #define GID_NONE ((gid_t)-1) #define HL_CHECK_MASTER 0 -@@ -646,6 +655,17 @@ struct stats { +@@ -647,6 +656,17 @@ struct stats { struct chmod_mode_struct; @@ -5887,7 +5892,7 @@ #include "byteorder.h" #include "lib/mdfour.h" #include "lib/wildmatch.h" -@@ -661,6 +681,16 @@ struct chmod_mode_struct; +@@ -662,6 +682,16 @@ struct chmod_mode_struct; #define UNUSED(x) x __attribute__((__unused__)) @@ -5965,7 +5970,7 @@ dit(bf(--chmod)) This option tells rsync to apply one or more comma-separated "chmod" strings to the permission of the files in the transfer. The resulting value is treated as though it was the permissions -@@ -1378,8 +1392,8 @@ if the receiving rsync is at least versi +@@ -1379,8 +1393,8 @@ if the receiving rsync is at least versi with older versions of rsync, but that also turns on the output of other verbose messages). @@ -5976,7 +5981,7 @@ type of update being done, bf(X) is replaced by the file-type, and the other letters represent attributes that may be output if they are being modified. -@@ -1428,7 +1442,11 @@ quote(itemize( +@@ -1429,7 +1443,11 @@ quote(itemize( sender's value (requires bf(--owner) and super-user privileges). it() A bf(g) means the group is different and is being updated to the sender's value (requires bf(--group) and the authority to set the group).