Annotation of embedaddon/rsync/backup.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Backup handling code.
! 3: *
! 4: * Copyright (C) 1999 Andrew Tridgell
! 5: * Copyright (C) 2003-2009 Wayne Davison
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify
! 8: * it under the terms of the GNU General Public License as published by
! 9: * the Free Software Foundation; either version 3 of the License, or
! 10: * (at your option) any later version.
! 11: *
! 12: * This program is distributed in the hope that it will be useful,
! 13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 15: * GNU General Public License for more details.
! 16: *
! 17: * You should have received a copy of the GNU General Public License along
! 18: * with this program; if not, visit the http://fsf.org website.
! 19: */
! 20:
! 21: #include "rsync.h"
! 22:
! 23: extern int verbose;
! 24: extern int am_root;
! 25: extern int preserve_acls;
! 26: extern int preserve_xattrs;
! 27: extern int preserve_devices;
! 28: extern int preserve_specials;
! 29: extern int preserve_links;
! 30: extern int safe_symlinks;
! 31: extern int backup_dir_len;
! 32: extern unsigned int backup_dir_remainder;
! 33: extern char backup_dir_buf[MAXPATHLEN];
! 34: extern char *backup_suffix;
! 35: extern char *backup_dir;
! 36:
! 37: /* make a complete pathname for backup file */
! 38: char *get_backup_name(const char *fname)
! 39: {
! 40: if (backup_dir) {
! 41: if (stringjoin(backup_dir_buf + backup_dir_len, backup_dir_remainder,
! 42: fname, backup_suffix, NULL) < backup_dir_remainder)
! 43: return backup_dir_buf;
! 44: } else {
! 45: if (stringjoin(backup_dir_buf, MAXPATHLEN,
! 46: fname, backup_suffix, NULL) < MAXPATHLEN)
! 47: return backup_dir_buf;
! 48: }
! 49:
! 50: rprintf(FERROR, "backup filename too long\n");
! 51: return NULL;
! 52: }
! 53:
! 54: /* simple backup creates a backup with a suffix in the same directory */
! 55: static int make_simple_backup(const char *fname)
! 56: {
! 57: int rename_errno;
! 58: const char *fnamebak = get_backup_name(fname);
! 59:
! 60: if (!fnamebak)
! 61: return 0;
! 62:
! 63: while (1) {
! 64: if (do_rename(fname, fnamebak) == 0) {
! 65: if (verbose > 1) {
! 66: rprintf(FINFO, "backed up %s to %s\n",
! 67: fname, fnamebak);
! 68: }
! 69: break;
! 70: }
! 71: /* cygwin (at least version b19) reports EINVAL */
! 72: if (errno == ENOENT || errno == EINVAL)
! 73: break;
! 74:
! 75: rename_errno = errno;
! 76: if (errno == EISDIR && do_rmdir(fnamebak) == 0)
! 77: continue;
! 78: if (errno == ENOTDIR && do_unlink(fnamebak) == 0)
! 79: continue;
! 80:
! 81: rsyserr(FERROR, rename_errno, "rename %s to backup %s",
! 82: fname, fnamebak);
! 83: errno = rename_errno;
! 84: return 0;
! 85: }
! 86:
! 87: return 1;
! 88: }
! 89:
! 90:
! 91: /****************************************************************************
! 92: Create a directory given an absolute path, perms based upon another directory
! 93: path
! 94: ****************************************************************************/
! 95: int make_bak_dir(const char *fullpath)
! 96: {
! 97: char fbuf[MAXPATHLEN], *rel, *end, *p;
! 98: struct file_struct *file;
! 99: int len = backup_dir_len;
! 100: stat_x sx;
! 101:
! 102: while (*fullpath == '.' && fullpath[1] == '/') {
! 103: fullpath += 2;
! 104: len -= 2;
! 105: }
! 106:
! 107: if (strlcpy(fbuf, fullpath, sizeof fbuf) >= sizeof fbuf)
! 108: return -1;
! 109:
! 110: rel = fbuf + len;
! 111: end = p = rel + strlen(rel);
! 112:
! 113: /* Try to find an existing dir, starting from the deepest dir. */
! 114: while (1) {
! 115: if (--p == fbuf)
! 116: return -1;
! 117: if (*p == '/') {
! 118: *p = '\0';
! 119: if (mkdir_defmode(fbuf) == 0)
! 120: break;
! 121: if (errno != ENOENT) {
! 122: rsyserr(FERROR, errno,
! 123: "make_bak_dir mkdir %s failed",
! 124: full_fname(fbuf));
! 125: return -1;
! 126: }
! 127: }
! 128: }
! 129:
! 130: /* Make all the dirs that we didn't find on the way here. */
! 131: while (1) {
! 132: if (p >= rel) {
! 133: /* Try to transfer the directory settings of the
! 134: * actual dir that the files are coming from. */
! 135: if (x_stat(rel, &sx.st, NULL) < 0) {
! 136: rsyserr(FERROR, errno,
! 137: "make_bak_dir stat %s failed",
! 138: full_fname(rel));
! 139: } else {
! 140: #ifdef SUPPORT_ACLS
! 141: sx.acc_acl = sx.def_acl = NULL;
! 142: #endif
! 143: #ifdef SUPPORT_XATTRS
! 144: sx.xattr = NULL;
! 145: #endif
! 146: if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
! 147: continue;
! 148: #ifdef SUPPORT_ACLS
! 149: if (preserve_acls && !S_ISLNK(file->mode)) {
! 150: get_acl(rel, &sx);
! 151: cache_tmp_acl(file, &sx);
! 152: free_acl(&sx);
! 153: }
! 154: #endif
! 155: #ifdef SUPPORT_XATTRS
! 156: if (preserve_xattrs) {
! 157: get_xattr(rel, &sx);
! 158: cache_tmp_xattr(file, &sx);
! 159: free_xattr(&sx);
! 160: }
! 161: #endif
! 162: set_file_attrs(fbuf, file, NULL, NULL, 0);
! 163: unmake_file(file);
! 164: #ifdef SUPPORT_ACLS
! 165: uncache_tmp_acls();
! 166: #endif
! 167: #ifdef SUPPORT_XATTRS
! 168: uncache_tmp_xattrs();
! 169: #endif
! 170: }
! 171: }
! 172: *p = '/';
! 173: p += strlen(p);
! 174: if (p == end)
! 175: break;
! 176: if (mkdir_defmode(fbuf) < 0) {
! 177: rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed",
! 178: full_fname(fbuf));
! 179: return -1;
! 180: }
! 181: }
! 182:
! 183: return 0;
! 184: }
! 185:
! 186: /* robustly move a file, creating new directory structures if necessary */
! 187: static int robust_move(const char *src, char *dst)
! 188: {
! 189: if (robust_rename(src, dst, NULL, 0755) < 0) {
! 190: int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
! 191: if (errno == ENOENT && make_bak_dir(dst) == 0) {
! 192: if (robust_rename(src, dst, NULL, 0755) < 0)
! 193: save_errno = errno ? errno : save_errno;
! 194: else
! 195: save_errno = 0;
! 196: }
! 197: if (save_errno) {
! 198: errno = save_errno;
! 199: return -1;
! 200: }
! 201: }
! 202: return 0;
! 203: }
! 204:
! 205:
! 206: /* If we have a --backup-dir, then we get here from make_backup().
! 207: * We will move the file to be deleted into a parallel directory tree. */
! 208: static int keep_backup(const char *fname)
! 209: {
! 210: stat_x sx;
! 211: struct file_struct *file;
! 212: char *buf;
! 213: int save_preserve_xattrs = preserve_xattrs;
! 214: int kept = 0;
! 215: int ret_code;
! 216:
! 217: /* return if no file to keep */
! 218: if (x_lstat(fname, &sx.st, NULL) < 0)
! 219: return 1;
! 220: #ifdef SUPPORT_ACLS
! 221: sx.acc_acl = sx.def_acl = NULL;
! 222: #endif
! 223: #ifdef SUPPORT_XATTRS
! 224: sx.xattr = NULL;
! 225: #endif
! 226:
! 227: if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
! 228: return 1; /* the file could have disappeared */
! 229:
! 230: if (!(buf = get_backup_name(fname))) {
! 231: unmake_file(file);
! 232: #ifdef SUPPORT_ACLS
! 233: uncache_tmp_acls();
! 234: #endif
! 235: #ifdef SUPPORT_XATTRS
! 236: uncache_tmp_xattrs();
! 237: #endif
! 238: return 0;
! 239: }
! 240:
! 241: #ifdef SUPPORT_ACLS
! 242: if (preserve_acls && !S_ISLNK(file->mode)) {
! 243: get_acl(fname, &sx);
! 244: cache_tmp_acl(file, &sx);
! 245: free_acl(&sx);
! 246: }
! 247: #endif
! 248: #ifdef SUPPORT_XATTRS
! 249: if (preserve_xattrs) {
! 250: get_xattr(fname, &sx);
! 251: cache_tmp_xattr(file, &sx);
! 252: free_xattr(&sx);
! 253: }
! 254: #endif
! 255:
! 256: /* Check to see if this is a device file, or link */
! 257: if ((am_root && preserve_devices && IS_DEVICE(file->mode))
! 258: || (preserve_specials && IS_SPECIAL(file->mode))) {
! 259: int save_errno;
! 260: do_unlink(buf);
! 261: if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) {
! 262: save_errno = errno ? errno : EINVAL; /* 0 paranoia */
! 263: if (errno == ENOENT && make_bak_dir(buf) == 0) {
! 264: if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
! 265: save_errno = errno ? errno : save_errno;
! 266: else
! 267: save_errno = 0;
! 268: }
! 269: if (save_errno) {
! 270: rsyserr(FERROR, save_errno, "mknod %s failed",
! 271: full_fname(buf));
! 272: }
! 273: } else
! 274: save_errno = 0;
! 275: if (verbose > 2 && save_errno == 0) {
! 276: rprintf(FINFO, "make_backup: DEVICE %s successful.\n",
! 277: fname);
! 278: }
! 279: kept = 1;
! 280: do_unlink(fname);
! 281: }
! 282:
! 283: if (!kept && S_ISDIR(file->mode)) {
! 284: /* make an empty directory */
! 285: if (do_mkdir(buf, file->mode) < 0) {
! 286: int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
! 287: if (errno == ENOENT && make_bak_dir(buf) == 0) {
! 288: if (do_mkdir(buf, file->mode) < 0)
! 289: save_errno = errno ? errno : save_errno;
! 290: else
! 291: save_errno = 0;
! 292: }
! 293: if (save_errno) {
! 294: rsyserr(FINFO, save_errno, "mkdir %s failed",
! 295: full_fname(buf));
! 296: }
! 297: }
! 298:
! 299: ret_code = do_rmdir(fname);
! 300: if (verbose > 2) {
! 301: rprintf(FINFO, "make_backup: RMDIR %s returns %i\n",
! 302: full_fname(fname), ret_code);
! 303: }
! 304: kept = 1;
! 305: }
! 306:
! 307: #ifdef SUPPORT_LINKS
! 308: if (!kept && preserve_links && S_ISLNK(file->mode)) {
! 309: const char *sl = F_SYMLINK(file);
! 310: if (safe_symlinks && unsafe_symlink(sl, fname)) {
! 311: if (verbose) {
! 312: rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",
! 313: fname, sl);
! 314: }
! 315: kept = 1;
! 316: } else {
! 317: do_unlink(buf);
! 318: if (do_symlink(sl, buf) < 0) {
! 319: int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
! 320: if (errno == ENOENT && make_bak_dir(buf) == 0) {
! 321: if (do_symlink(sl, buf) < 0)
! 322: save_errno = errno ? errno : save_errno;
! 323: else
! 324: save_errno = 0;
! 325: }
! 326: if (save_errno) {
! 327: rsyserr(FERROR, save_errno, "link %s -> \"%s\"",
! 328: full_fname(buf), sl);
! 329: }
! 330: }
! 331: do_unlink(fname);
! 332: kept = 1;
! 333: }
! 334: }
! 335: #endif
! 336:
! 337: if (!kept && !S_ISREG(file->mode)) {
! 338: rprintf(FINFO, "make_bak: skipping non-regular file %s\n",
! 339: fname);
! 340: unmake_file(file);
! 341: #ifdef SUPPORT_ACLS
! 342: uncache_tmp_acls();
! 343: #endif
! 344: #ifdef SUPPORT_XATTRS
! 345: uncache_tmp_xattrs();
! 346: #endif
! 347: return 1;
! 348: }
! 349:
! 350: /* move to keep tree if a file */
! 351: if (!kept) {
! 352: if (robust_move(fname, buf) != 0) {
! 353: rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
! 354: full_fname(fname), buf);
! 355: } else if (sx.st.st_nlink > 1) {
! 356: /* If someone has hard-linked the file into the backup
! 357: * dir, rename() might return success but do nothing! */
! 358: robust_unlink(fname); /* Just in case... */
! 359: }
! 360: }
! 361: preserve_xattrs = 0;
! 362: set_file_attrs(buf, file, NULL, fname, 0);
! 363: preserve_xattrs = save_preserve_xattrs;
! 364: unmake_file(file);
! 365: #ifdef SUPPORT_ACLS
! 366: uncache_tmp_acls();
! 367: #endif
! 368: #ifdef SUPPORT_XATTRS
! 369: uncache_tmp_xattrs();
! 370: #endif
! 371:
! 372: if (verbose > 1) {
! 373: rprintf(FINFO, "backed up %s to %s\n",
! 374: fname, buf);
! 375: }
! 376: return 1;
! 377: }
! 378:
! 379:
! 380: /* main backup switch routine */
! 381: int make_backup(const char *fname)
! 382: {
! 383: if (backup_dir)
! 384: return keep_backup(fname);
! 385: return make_simple_backup(fname);
! 386: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>