int do_rename(const char *fname1, const char *fname2) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; if (rename(fname1, fname2) == 0) return 0; #ifdef SUPPORT_FORCE_CHANGE if (force_change && errno == EPERM) { STRUCT_STAT st1, st2; int became_mutable; if (x_lstat(fname1, &st1, NULL) != 0) goto failed; became_mutable = make_mutable(fname1, st1.st_mode, st1.st_flags, force_change) > 0; if (became_mutable && rename(fname1, fname2) == 0) goto success; if (x_lstat(fname2, &st2, NULL) == 0 && make_mutable(fname2, st2.st_mode, st2.st_flags, force_change) > 0) { if (rename(fname1, fname2) == 0) { success: if (became_mutable) /* Yes, use fname2 and st1! */ undo_make_mutable(fname2, st1.st_flags); return 0; } undo_make_mutable(fname2, st2.st_flags); } /* TODO: handle immutable directories */ if (became_mutable) undo_make_mutable(fname1, st1.st_flags); failed: errno = EPERM; } #endif return -1; }
int do_lchown(const char *path, uid_t owner, gid_t group, mode_t mode, uint32 fileflags) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; #ifndef HAVE_LCHOWN #define lchown chown #endif if (lchown(path, owner, group) == 0) return 0; #ifdef SUPPORT_FORCE_CHANGE if (force_change && errno == EPERM) { if (fileflags == NO_FFLAGS) { STRUCT_STAT st; if (x_lstat(path, &st, NULL) == 0) { mode = st.st_mode; fileflags = st.st_flags; } } if (fileflags != NO_FFLAGS && make_mutable(path, mode, fileflags, force_change) > 0) { int ret = lchown(path, owner, group); undo_make_mutable(path, fileflags); if (ret == 0) return 0; } errno = EPERM; } #else mode = fileflags = 0; /* avoid compiler warning */ #endif return -1; }
int do_chmod(const char *path, mode_t mode, uint32 fileflags) { int code; if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; #ifdef HAVE_LCHMOD code = lchmod(path, mode & CHMOD_BITS); #else if (S_ISLNK(mode)) { # if defined HAVE_SETATTRLIST struct attrlist attrList; uint32_t m = mode & CHMOD_BITS; /* manpage is wrong: not mode_t! */ memset(&attrList, 0, sizeof attrList); attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.commonattr = ATTR_CMN_ACCESSMASK; code = setattrlist(path, &attrList, &m, sizeof m, FSOPT_NOFOLLOW); # else code = 1; # endif } else code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */ #endif /* !HAVE_LCHMOD */ #ifdef SUPPORT_FORCE_CHANGE if (code < 0 && force_change && errno == EPERM && !S_ISLNK(mode)) { if (fileflags == NO_FFLAGS) { STRUCT_STAT st; if (x_lstat(path, &st, NULL) == 0) fileflags = st.st_flags; } if (fileflags != NO_FFLAGS && make_mutable(path, mode, fileflags, force_change) > 0) { code = chmod(path, mode & CHMOD_BITS); undo_make_mutable(path, fileflags); if (code == 0) return 0; } errno = EPERM; } #else fileflags = 0; /* avoid compiler warning */ #endif if (code != 0 && (preserve_perms || preserve_executability)) return code; return 0; }
int do_rmdir(const char *pathname) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; if (rmdir(pathname) == 0) return 0; #ifdef SUPPORT_FORCE_CHANGE if (force_change && errno == EPERM) { STRUCT_STAT st; if (x_lstat(pathname, &st, NULL) == 0 && make_mutable(pathname, st.st_mode, st.st_flags, force_change) > 0) { if (rmdir(pathname) == 0) return 0; undo_make_mutable(pathname, st.st_flags); } errno = EPERM; } #endif return -1; }
int do_unlink(const char *fname) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; if (unlink(fname) == 0) return 0; #ifdef SUPPORT_FORCE_CHANGE if (force_change && errno == EPERM) { STRUCT_STAT st; if (x_lstat(fname, &st, NULL) == 0 && make_mutable(fname, st.st_mode, st.st_flags, force_change) > 0) { if (unlink(fname) == 0) return 0; undo_make_mutable(fname, st.st_flags); } /* TODO: handle immutable directories */ errno = EPERM; } #endif return -1; }
/* If we have a --backup-dir, then we get here from make_backup(). * We will move the file to be deleted into a parallel directory tree. */ static int keep_backup(const char *fname) { stat_x sx; struct file_struct *file; char *buf; int save_preserve_xattrs = preserve_xattrs; int kept = 0; int ret_code; /* return if no file to keep */ if (x_lstat(fname, &sx.st, NULL) < 0) return 1; #ifdef SUPPORT_ACLS sx.acc_acl = sx.def_acl = NULL; #endif #ifdef SUPPORT_XATTRS sx.xattr = NULL; #endif if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) return 1; /* the file could have disappeared */ if (!(buf = get_backup_name(fname))) { unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 0; } #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(fname, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(fname, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif /* Check to see if this is a device file, or link */ if ((am_root && preserve_devices && IS_DEVICE(file->mode)) || (preserve_specials && IS_SPECIAL(file->mode))) { int save_errno; do_unlink(buf); if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) { save_errno = errno ? errno : EINVAL; /* 0 paranoia */ if (errno == ENOENT && make_bak_dir(buf) == 0) { if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) save_errno = errno ? errno : save_errno; else save_errno = 0; } if (save_errno) { rsyserr(FERROR, save_errno, "mknod %s failed", full_fname(buf)); } } else save_errno = 0; if (verbose > 2 && save_errno == 0) { rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname); } kept = 1; do_unlink(fname); } if (!kept && S_ISDIR(file->mode)) { /* make an empty directory */ if (do_mkdir(buf, file->mode) < 0) { int save_errno = errno ? errno : EINVAL; /* 0 paranoia */ if (errno == ENOENT && make_bak_dir(buf) == 0) { if (do_mkdir(buf, file->mode) < 0) save_errno = errno ? errno : save_errno; else save_errno = 0; } if (save_errno) { rsyserr(FINFO, save_errno, "mkdir %s failed", full_fname(buf)); } } ret_code = do_rmdir(fname); if (verbose > 2) { rprintf(FINFO, "make_backup: RMDIR %s returns %i\n", full_fname(fname), ret_code); } kept = 1; } #ifdef SUPPORT_LINKS if (!kept && preserve_links && S_ISLNK(file->mode)) { const char *sl = F_SYMLINK(file); if (safe_symlinks && unsafe_symlink(sl, fname)) { if (verbose) { rprintf(FINFO, "ignoring unsafe symlink %s -> %s\n", full_fname(buf), sl); } kept = 1; } else { do_unlink(buf); if (do_symlink(sl, buf) < 0) { int save_errno = errno ? errno : EINVAL; /* 0 paranoia */ if (errno == ENOENT && make_bak_dir(buf) == 0) { if (do_symlink(sl, buf) < 0) save_errno = errno ? errno : save_errno; else save_errno = 0; } if (save_errno) { rsyserr(FERROR, save_errno, "link %s -> \"%s\"", full_fname(buf), sl); } } do_unlink(fname); kept = 1; } } #endif if (!kept && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 1; } /* move to keep tree if a file */ if (!kept) { if (robust_move(fname, buf) != 0) { rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", full_fname(fname), buf); } else if (sx.st.st_nlink > 1) { /* If someone has hard-linked the file into the backup * dir, rename() might return success but do nothing! */ robust_unlink(fname); /* Just in case... */ } } preserve_xattrs = 0; set_file_attrs(buf, file, NULL, fname, 0); preserve_xattrs = save_preserve_xattrs; unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif if (verbose > 1) { rprintf(FINFO, "backed up %s to %s\n", fname, buf); } return 1; }
/* Hard-link, rename, or copy an item to the backup name. Returns 0 for * failure, 1 if item was moved, 2 if item was duplicated or hard linked * into backup area, or 3 if item doesn't exist or isn't a regular file. */ int make_backup(const char *fname, BOOL prefer_rename) { stat_x sx; struct file_struct *file; int save_preserve_xattrs; char *buf; int ret = 0; init_stat_x(&sx); /* Return success if no file to keep. */ if (x_lstat(fname, &sx.st, NULL) < 0) return 3; if (!(buf = get_backup_name(fname))) return 0; /* Try a hard-link or a rename first. Using rename is not atomic, but * is more efficient than forcing a copy for larger files when no hard- * linking is possible. */ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0) goto success; if (errno == EEXIST || errno == EISDIR) { STRUCT_STAT bakst; if (do_lstat(buf, &bakst) == 0) { int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE; if (delete_item(buf, bakst.st_mode, flags) != 0) return 0; } if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0) goto success; } /* Fall back to making a copy. */ if (!(file = make_file(fname, NULL, &sx.st, 0, NO_FILTERS))) return 3; /* the file could have disappeared */ #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(fname, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(fname, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif /* Check to see if this is a device file, or link */ if ((am_root && preserve_devices && IS_DEVICE(file->mode)) || (preserve_specials && IS_SPECIAL(file->mode))) { if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf)); else if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname); ret = 2; } #ifdef SUPPORT_LINKS if (!ret && preserve_links && S_ISLNK(file->mode)) { const char *sl = F_SYMLINK(file); if (safe_symlinks && unsafe_symlink(sl, fname)) { if (INFO_GTE(SYMSAFE, 1)) { rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n", fname, sl); } ret = 2; } else { if (do_symlink(sl, buf) < 0) rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl); else if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname); ret = 2; } } #endif if (!ret && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 3; } /* Copy to backup tree if a file. */ if (!ret) { if (copy_file(fname, buf, -1, file->mode) < 0) { rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", full_fname(fname), buf); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 0; } if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: COPY %s successful.\n", fname); ret = 2; } save_preserve_xattrs = preserve_xattrs; preserve_xattrs = 0; set_file_attrs(buf, file, NULL, fname, ATTRS_SET_NANO); preserve_xattrs = save_preserve_xattrs; unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif success: if (INFO_GTE(BACKUP, 1)) rprintf(FINFO, "backed up %s to %s\n", fname, buf); return ret; }