Diff for /embedaddon/rsync/backup.c between versions 1.1.1.1 and 1.1.1.2

version 1.1.1.1, 2012/02/17 15:09:30 version 1.1.1.2, 2013/10/14 07:51:14
Line 2 Line 2
  * Backup handling code.   * Backup handling code.
  *   *
  * Copyright (C) 1999 Andrew Tridgell   * Copyright (C) 1999 Andrew Tridgell
 * Copyright (C) 2003-2009 Wayne Davison * Copyright (C) 2003-2013 Wayne Davison
  *   *
  * This program is free software; you can redistribute it and/or modify   * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by   * it under the terms of the GNU General Public License as published by
Line 19 Line 19
  */   */
   
 #include "rsync.h"  #include "rsync.h"
   #include "ifuncs.h"
   
 extern int verbose;  
 extern int am_root;  extern int am_root;
 extern int preserve_acls;  extern int preserve_acls;
 extern int preserve_xattrs;  extern int preserve_xattrs;
Line 34  extern char backup_dir_buf[MAXPATHLEN]; Line 34  extern char backup_dir_buf[MAXPATHLEN];
 extern char *backup_suffix;  extern char *backup_suffix;
 extern char *backup_dir;  extern char *backup_dir;
   
/* make a complete pathname for backup file *//* Returns -1 on error, 0 on missing dir, and 1 on present dir. */
char *get_backup_name(const char *fname)static int validate_backup_dir(void)
 {  {
        if (backup_dir) {        STRUCT_STAT st;
                if (stringjoin(backup_dir_buf + backup_dir_len, backup_dir_remainder, 
                               fname, backup_suffix, NULL) < backup_dir_remainder) 
                        return backup_dir_buf; 
        } else { 
                if (stringjoin(backup_dir_buf, MAXPATHLEN, 
                               fname, backup_suffix, NULL) < MAXPATHLEN) 
                        return backup_dir_buf; 
        } 
   
        rprintf(FERROR, "backup filename too long\n");        if (do_lstat(backup_dir_buf, &st) < 0) {
        return NULL;                if (errno == ENOENT)
                         return 0;
                 rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
                 return -1;
         }
         if (!S_ISDIR(st.st_mode)) {
                 int flags = get_del_for_flag(st.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
                 if (delete_item(backup_dir_buf, st.st_mode, flags) == 0)
                         return 0;
                 return -1;
         }
         return 1;
 }  }
   
/* simple backup creates a backup with a suffix in the same directory *//* Create a backup path from the given fname, putting the result into
static int make_simple_backup(const char *fname) * backup_dir_buf.  Any new directories (compared to the prior backup
  * path) are ensured to exist as directories, replacing anything else
  * that may be in the way (e.g. a symlink). */
 static BOOL copy_valid_path(const char *fname)
 {  {
        int rename_errno;        const char *f;
        const char *fnamebak = get_backup_name(fname);        int val;
         BOOL ret = True;
         stat_x sx;
         char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel;
   
        if (!fnamebak)        for (f = fname, b = rel; *f && *f == *b; f++, b++) {
                return 0;                if (*b == '/')
                         name = b + 1;
         }
   
        while (1) {        if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) {
                if (do_rename(fname, fnamebak) == 0) {                rprintf(FERROR, "backup filename too long\n");
                        if (verbose > 1) {                *name = '\0';
                                rprintf(FINFO, "backed up %s to %s\n",                return False;
                                        fname, fnamebak); 
                        } 
                        break; 
                } 
                /* cygwin (at least version b19) reports EINVAL */ 
                if (errno == ENOENT || errno == EINVAL) 
                        break; 
 
                rename_errno = errno; 
                if (errno == EISDIR && do_rmdir(fnamebak) == 0) 
                        continue; 
                if (errno == ENOTDIR && do_unlink(fnamebak) == 0) 
                        continue; 
 
                rsyserr(FERROR, rename_errno, "rename %s to backup %s", 
                        fname, fnamebak); 
                errno = rename_errno; 
                return 0; 
         }          }
   
        return 1;        for ( ; ; name = b + 1) {
}                if ((b = strchr(name, '/')) == NULL)
                         return True;
                 *b = '\0';
   
                   val = validate_backup_dir();
                   if (val == 0)
                           break;
                   if (val < 0) {
                           *name = '\0';
                           return False;
                   }
   
/****************************************************************************                *b = '/';
Create a directory given an absolute path, perms based upon another directory 
path 
****************************************************************************/ 
int make_bak_dir(const char *fullpath) 
{ 
        char fbuf[MAXPATHLEN], *rel, *end, *p; 
        struct file_struct *file; 
        int len = backup_dir_len; 
        stat_x sx; 
 
        while (*fullpath == '.' && fullpath[1] == '/') { 
                fullpath += 2; 
                len -= 2; 
         }          }
   
        if (strlcpy(fbuf, fullpath, sizeof fbuf) >= sizeof fbuf)        init_stat_x(&sx);
                return -1; 
   
        rel = fbuf + len;        for ( ; b; name = b + 1, b = strchr(name, '/')) {
        end = p = rel + strlen(rel);                *b = '\0';
   
        /* Try to find an existing dir, starting from the deepest dir. */                while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) {
        while (1) {                        if (errno == EEXIST) {
                if (--p == fbuf)                                val = validate_backup_dir();
                        return -1;                                if (val > 0)
                if (*p == '/') {                                        break;
                        *p = '\0';                                if (val == 0)
                        if (mkdir_defmode(fbuf) == 0)                                        continue;
                                break;                        } else
                        if (errno != ENOENT) {                                rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf);
                                rsyserr(FERROR, errno,                        *name = '\0';
                                        "make_bak_dir mkdir %s failed",                        ret = False;
                                        full_fname(fbuf));                        goto cleanup;
                                return -1; 
                        } 
                 }                  }
         }  
   
        /* Make all the dirs that we didn't find on the way here. */                /* Try to transfer the directory settings of the actual dir
        while (1) {                 * that the files are coming from. */
                if (p >= rel) {                if (x_stat(rel, &sx.st, NULL) < 0)
                        /* Try to transfer the directory settings of the                        rsyserr(FERROR, errno, "backup stat %s failed", full_fname(rel));
                         * actual dir that the files are coming from. */                else {
                        if (x_stat(rel, &sx.st, NULL) < 0) {                        struct file_struct *file;
                                rsyserr(FERROR, errno,                        if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
                                        "make_bak_dir stat %s failed",                                continue;
                                        full_fname(rel)); 
                        } else { 
 #ifdef SUPPORT_ACLS  #ifdef SUPPORT_ACLS
                                sx.acc_acl = sx.def_acl = NULL;                        if (preserve_acls && !S_ISLNK(file->mode)) {
                                 get_acl(rel, &sx);
                                 cache_tmp_acl(file, &sx);
                                 free_acl(&sx);
                         }
 #endif  #endif
 #ifdef SUPPORT_XATTRS  #ifdef SUPPORT_XATTRS
                                sx.xattr = NULL;                        if (preserve_xattrs) {
                                 get_xattr(rel, &sx);
                                 cache_tmp_xattr(file, &sx);
                                 free_xattr(&sx);
                         }
 #endif  #endif
                                if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))                        set_file_attrs(backup_dir_buf, file, NULL, NULL, 0);
                                        continue;                        unmake_file(file);
                 }
 
                 *b = '/';
         }
 
   cleanup:
 
 #ifdef SUPPORT_ACLS  #ifdef SUPPORT_ACLS
                                if (preserve_acls && !S_ISLNK(file->mode)) {        uncache_tmp_acls();
                                        get_acl(rel, &sx); 
                                        cache_tmp_acl(file, &sx); 
                                        free_acl(&sx); 
                                } 
 #endif  #endif
 #ifdef SUPPORT_XATTRS  #ifdef SUPPORT_XATTRS
                                if (preserve_xattrs) {        uncache_tmp_xattrs();
                                        get_xattr(rel, &sx); 
                                        cache_tmp_xattr(file, &sx); 
                                        free_xattr(&sx); 
                                } 
 #endif  #endif
                                set_file_attrs(fbuf, file, NULL, NULL, 0);
                                unmake_file(file);        return ret;
#ifdef SUPPORT_ACLS}
                                uncache_tmp_acls();
#endif/* Make a complete pathname for backup file and verify any new path elements. */
#ifdef SUPPORT_XATTRSchar *get_backup_name(const char *fname)
                                uncache_tmp_xattrs();{
#endif        if (backup_dir) {
                        }                /* copy fname into backup_dir_buf while validating the dirs. */
                }                if (copy_valid_path(fname))
                *p = '/';                        return backup_dir_buf;
                p += strlen(p);                /* copy_valid_path() has printed an error message. */
                if (p == end)                return NULL;
                        break; 
                if (mkdir_defmode(fbuf) < 0) { 
                        rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed", 
                                full_fname(fbuf)); 
                        return -1; 
                } 
         }          }
   
        return 0;        if (stringjoin(backup_dir_buf, MAXPATHLEN, fname, backup_suffix, NULL) < MAXPATHLEN)
                 return backup_dir_buf;
 
         rprintf(FERROR, "backup filename too long\n");
         return NULL;
 }  }
   
/* robustly move a file, creating new directory structures if necessary *//* Has same return codes as make_backup(). */
static int robust_move(const char *src, char *dst)static inline int link_or_rename(const char *from, const char *to,
                                  BOOL prefer_rename, STRUCT_STAT *stp)
 {  {
        if (robust_rename(src, dst, NULL, 0755) < 0) {#ifdef SUPPORT_HARD_LINKS
                int save_errno = errno ? errno : EINVAL; /* 0 paranoia */        if (!prefer_rename) {
                if (errno == ENOENT && make_bak_dir(dst) == 0) {#ifndef CAN_HARDLINK_SYMLINK
                        if (robust_rename(src, dst, NULL, 0755) < 0)                if (S_ISLNK(stp->st_mode))
                                save_errno = errno ? errno : save_errno;                        return 0; /* Use copy code. */
                        else#endif
                                save_errno = 0;#ifndef CAN_HARDLINK_SPECIAL
                 if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
                         return 0; /* Use copy code. */
 #endif
                 if (do_link(from, to) == 0) {
                         if (DEBUG_GTE(BACKUP, 1))
                                 rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
                         return 2;
                 }                  }
                if (save_errno) {                /* We prefer to rename a regular file rather than copy it. */
                        errno = save_errno;                if (!S_ISREG(stp->st_mode) || errno == EEXIST || errno == EISDIR)
                        return -1;                        return 0;
         }
 #endif
         if (do_rename(from, to) == 0) {
                 if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) {
                         /* If someone has hard-linked the file into the backup
                          * dir, rename() might return success but do nothing! */
                         robust_unlink(from); /* Just in case... */
                 }                  }
                   if (DEBUG_GTE(BACKUP, 1))
                           rprintf(FINFO, "make_backup: RENAME %s successful.\n", from);
                   return 1;
         }          }
         return 0;          return 0;
 }  }
   
/* Hard-link, rename, or copy an item to the backup name.  Returns 2 if item
/* If we have a --backup-dir, then we get here from make_backup(). * was duplicated into backup area, 1 if item was moved, or 0 for failure.*/
 * We will move the file to be deleted into a parallel directory tree. */int make_backup(const char *fname, BOOL prefer_rename)
static int keep_backup(const char *fname) 
 {  {
         stat_x sx;          stat_x sx;
         struct file_struct *file;          struct file_struct *file;
        char *buf;        int save_preserve_xattrs;
        int save_preserve_xattrs = preserve_xattrs;        char *buf = get_backup_name(fname);
        int kept = 0;        int ret = 0;
        int ret_code; 
   
        /* return if no file to keep */        if (!buf)
                 return 0;
 
         init_stat_x(&sx);
         /* Return success if no file to keep. */
         if (x_lstat(fname, &sx.st, NULL) < 0)          if (x_lstat(fname, &sx.st, NULL) < 0)
                 return 1;                  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)))        /* 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 1; /* the file could have disappeared */                  return 1; /* the file could have disappeared */
   
         if (!(buf = get_backup_name(fname))) {  
                 unmake_file(file);  
 #ifdef SUPPORT_ACLS  #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)) {          if (preserve_acls && !S_ISLNK(file->mode)) {
                 get_acl(fname, &sx);                  get_acl(fname, &sx);
                 cache_tmp_acl(file, &sx);                  cache_tmp_acl(file, &sx);
Line 256  static int keep_backup(const char *fname) Line 264  static int keep_backup(const char *fname)
         /* Check to see if this is a device file, or link */          /* Check to see if this is a device file, or link */
         if ((am_root && preserve_devices && IS_DEVICE(file->mode))          if ((am_root && preserve_devices && IS_DEVICE(file->mode))
          || (preserve_specials && IS_SPECIAL(file->mode))) {           || (preserve_specials && IS_SPECIAL(file->mode))) {
                int save_errno;                if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
                do_unlink(buf);                        rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf));
                if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) {                else if (DEBUG_GTE(BACKUP, 1))
                        save_errno = errno ? errno : EINVAL; /* 0 paranoia */                        rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname);
                        if (errno == ENOENT && make_bak_dir(buf) == 0) {                ret = 2;
                                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  #ifdef SUPPORT_LINKS
        if (!kept && preserve_links && S_ISLNK(file->mode)) {        if (!ret && preserve_links && S_ISLNK(file->mode)) {
                 const char *sl = F_SYMLINK(file);                  const char *sl = F_SYMLINK(file);
                 if (safe_symlinks && unsafe_symlink(sl, fname)) {                  if (safe_symlinks && unsafe_symlink(sl, fname)) {
                        if (verbose) {                        if (INFO_GTE(SYMSAFE, 1)) {
                                 rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",                                  rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",
                                         fname, sl);                                          fname, sl);
                         }                          }
                        kept = 1;                        ret = 2;
                 } else {                  } else {
                        do_unlink(buf);                        if (do_symlink(sl, buf) < 0)
                        if (do_symlink(sl, buf) < 0) {                                rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl);
                                int save_errno = errno ? errno : EINVAL; /* 0 paranoia */                        else if (DEBUG_GTE(BACKUP, 1))
                                if (errno == ENOENT && make_bak_dir(buf) == 0) {                                rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname);
                                        if (do_symlink(sl, buf) < 0)                        ret = 2;
                                                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  #endif
   
        if (!kept && !S_ISREG(file->mode)) {        if (!ret && !S_ISREG(file->mode)) {
                rprintf(FINFO, "make_bak: skipping non-regular file %s\n",                rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
                        fname); 
                 unmake_file(file);                  unmake_file(file);
 #ifdef SUPPORT_ACLS  #ifdef SUPPORT_ACLS
                 uncache_tmp_acls();                  uncache_tmp_acls();
Line 344  static int keep_backup(const char *fname) Line 299  static int keep_backup(const char *fname)
 #ifdef SUPPORT_XATTRS  #ifdef SUPPORT_XATTRS
                 uncache_tmp_xattrs();                  uncache_tmp_xattrs();
 #endif  #endif
                return 1;                return 2;
         }          }
   
        /* move to keep tree if a file */        /* Copy to backup tree if a file. */
        if (!kept) {        if (!ret) {
                if (robust_move(fname, buf) != 0) {                if (copy_file(fname, buf, -1, file->mode) < 0) {
                         rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",                          rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
                                 full_fname(fname), buf);                                  full_fname(fname), buf);
                } else if (sx.st.st_nlink > 1) {                        unmake_file(file);
                        /* If someone has hard-linked the file into the backup#ifdef SUPPORT_ACLS
                         * dir, rename() might return success but do nothing! */                        uncache_tmp_acls();
                        robust_unlink(fname); /* Just in case... */#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;          preserve_xattrs = 0;
         set_file_attrs(buf, file, NULL, fname, 0);          set_file_attrs(buf, file, NULL, fname, 0);
         preserve_xattrs = save_preserve_xattrs;          preserve_xattrs = save_preserve_xattrs;
   
         unmake_file(file);          unmake_file(file);
 #ifdef SUPPORT_ACLS  #ifdef SUPPORT_ACLS
         uncache_tmp_acls();          uncache_tmp_acls();
Line 369  static int keep_backup(const char *fname) Line 334  static int keep_backup(const char *fname)
         uncache_tmp_xattrs();          uncache_tmp_xattrs();
 #endif  #endif
   
        if (verbose > 1) {  success:
                rprintf(FINFO, "backed up %s to %s\n",        if (INFO_GTE(BACKUP, 1))
                        fname, buf);                rprintf(FINFO, "backed up %s to %s\n", fname, buf);
        }        return ret;
        return 1; 
} 
 
 
/* main backup switch routine */ 
int make_backup(const char *fname) 
{ 
        if (backup_dir) 
                return keep_backup(fname); 
        return make_simple_backup(fname); 
 }  }

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.2


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