Skip to content

Commit

Permalink
More backup improvements:
Browse files Browse the repository at this point in the history
- Changed get_backup_name() to verify the backup path, and make any
  missing directories.  This avoids accidental use of a symlink as a dir
  in a backup path, and gets rid of any other non-dirs that are in the
  way.  It also avoids the need for various operations to retry after
  calling make_bak_dir(), simplifying several pices of code.
- Changed create_directory_path() to make_path(), giving it flags that
  lets the caller decide if it should skip a leading slash or drop the
  trailing filename.
- Mention when we create the backup directory, so the user is not caught
  unaware when rsync uses a directory they didn't expect.
- Got rid of some dir-moving backup code that is not used.
- Added a little more backup-debug output.
  • Loading branch information
Wayne Davison committed Apr 11, 2009
1 parent 5e2d51e commit 3696674
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 213 deletions.
267 changes: 112 additions & 155 deletions backup.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,111 +34,119 @@ extern char backup_dir_buf[MAXPATHLEN];
extern char *backup_suffix;
extern char *backup_dir;

/* make a complete pathname for backup file */
char *get_backup_name(const char *fname)
{
if (backup_dir) {
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");
return NULL;
}

/****************************************************************************
Create a directory given an absolute path, perms based upon another directory
path
****************************************************************************/
int make_bak_dir(const char *fullpath)
/* Create a backup path from the given fname, putting the result into
* 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)
{
char fbuf[MAXPATHLEN], *rel, *end, *p;
struct file_struct *file;
int len = backup_dir_len;
const char *f;
int flags;
BOOL ret = True;
stat_x sx;
char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel;

while (*fullpath == '.' && fullpath[1] == '/') {
fullpath += 2;
len -= 2;
for (f = fname, b = rel; *f && *f == *b; f++, b++) {
if (*b == '/')
name = b + 1;
}

if (strlcpy(fbuf, fullpath, sizeof fbuf) >= sizeof fbuf)
return -1;
if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) {
rprintf(FERROR, "backup filename too long\n");
*name = '\0';
return False;
}

rel = fbuf + len;
end = p = rel + strlen(rel);
for ( ; ; name = b + 1) {
if ((b = strchr(name, '/')) == NULL)
return True;
*b = '\0';

/* Try to find an existing dir, starting from the deepest dir. */
while (1) {
if (--p == fbuf)
return -1;
if (*p == '/') {
*p = '\0';
if (mkdir_defmode(fbuf) == 0)
if (do_lstat(backup_dir_buf, &sx.st) < 0) {
if (errno == ENOENT)
break;
if (errno != ENOENT) {
rsyserr(FERROR, errno,
"make_bak_dir mkdir %s failed",
full_fname(fbuf));
return -1;
}
rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
*name = '\0';
return False;
}
if (!S_ISDIR(sx.st.st_mode)) {
flags = get_del_for_flag(sx.st.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
if (delete_item(backup_dir_buf, sx.st.st_mode, flags) == 0)
break;
*name = '\0';
return False;
}

*b = '/';
}

/* Make all the dirs that we didn't find on the way here. */
while (1) {
if (p >= rel) {
/* Try to transfer the directory settings of the
* actual dir that the files are coming from. */
init_stat_x(&sx);
if (x_stat(rel, &sx.st, NULL) < 0) {
rsyserr(FERROR, errno,
"make_bak_dir stat %s failed",
full_fname(rel));
} else {
if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
continue;
init_stat_x(&sx);

for ( ; b; name = b + 1, b = strchr(name, '/')) {
*b = '\0';

if (mkdir_defmode(backup_dir_buf) < 0) {
rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf);
*name = '\0';
ret = False;
break;
}

/* Try to transfer the directory settings of the actual dir
* that the files are coming from. */
if (x_stat(rel, &sx.st, NULL) < 0)
rsyserr(FERROR, errno, "backup stat %s failed", full_fname(rel));
else {
struct file_struct *file;
if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
continue;
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
get_acl(rel, &sx);
cache_tmp_acl(file, &sx);
free_acl(&sx);
}
if (preserve_acls && !S_ISLNK(file->mode)) {
get_acl(rel, &sx);
cache_tmp_acl(file, &sx);
free_acl(&sx);
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(rel, &sx);
cache_tmp_xattr(file, &sx);
free_xattr(&sx);
}
if (preserve_xattrs) {
get_xattr(rel, &sx);
cache_tmp_xattr(file, &sx);
free_xattr(&sx);
}
#endif
set_file_attrs(fbuf, file, NULL, NULL, 0);
unmake_file(file);
set_file_attrs(backup_dir_buf, file, NULL, NULL, 0);
unmake_file(file);
}

*b = '/';
}

#ifdef SUPPORT_ACLS
uncache_tmp_acls();
uncache_tmp_acls();
#endif
#ifdef SUPPORT_XATTRS
uncache_tmp_xattrs();
uncache_tmp_xattrs();
#endif
}
}
*p = '/';
p += strlen(p);
if (p == end)
break;
if (mkdir_defmode(fbuf) < 0) {
rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed",
full_fname(fbuf));
return -1;
}

return ret;
}

/* Make a complete pathname for backup file and verify any new path elements. */
char *get_backup_name(const char *fname)
{
if (backup_dir) {
/* copy fname into backup_dir_buf while validating the dirs. */
if (copy_valid_path(fname))
return backup_dir_buf;
return NULL;
} else {
if (stringjoin(backup_dir_buf, MAXPATHLEN,
fname, backup_suffix, NULL) < MAXPATHLEN)
return backup_dir_buf;
}

return 0;
rprintf(FERROR, "backup filename too long\n");
return NULL;
}

/* Has same return codes as make_backup(). */
Expand All @@ -155,8 +163,11 @@ static inline int link_or_rename(const char *from, const char *to,
if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
return 0; /* Use copy code. */
#endif
if (do_link(from, to) == 0)
if (do_link(from, to) == 0) {
if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
return 2;
}
/* We prefer to rename a regular file rather than copy it. */
if (!S_ISREG(stp->st_mode) || errno == EEXIST || errno == EISDIR)
return 0;
Expand All @@ -168,6 +179,8 @@ static inline int link_or_rename(const char *from, const char *to,
* 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;
Expand Down Expand Up @@ -205,12 +218,6 @@ int make_backup(const char *fname, BOOL prefer_rename)
}
if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
goto success;
} else if (backup_dir && errno == ENOENT) {
/* If the backup dir is missing, try again after making it. */
if (make_bak_dir(buf) != 0)
return 0;
if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
goto success;
}

/* Fall back to making a copy. */
Expand All @@ -235,50 +242,10 @@ int make_backup(const char *fname, BOOL prefer_rename)
/* 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;
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 (DEBUG_GTE(BACKUP, 1) && save_errno == 0) {
rprintf(FINFO, "make_backup: DEVICE %s successful.\n",
fname);
}
ret = 2;
}

if (!ret && S_ISDIR(file->mode)) {
int ret_code;
/* 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 (DEBUG_GTE(BACKUP, 1)) {
rprintf(FINFO, "make_backup: RMDIR %s returns %i\n",
full_fname(fname), ret_code);
}
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;
}

Expand All @@ -292,27 +259,17 @@ int make_backup(const char *fname, BOOL prefer_rename)
}
ret = 2;
} else {
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);
}
}
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);
rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
unmake_file(file);
#ifdef SUPPORT_ACLS
uncache_tmp_acls();
Expand All @@ -325,7 +282,7 @@ int make_backup(const char *fname, BOOL prefer_rename)

/* Copy to backup tree if a file. */
if (!ret) {
if (copy_file(fname, buf, -1, file->mode, 1) < 0) {
if (copy_file(fname, buf, -1, file->mode) < 0) {
rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
full_fname(fname), buf);
unmake_file(file);
Expand All @@ -337,6 +294,8 @@ int make_backup(const char *fname, BOOL prefer_rename)
#endif
return 0;
}
if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: COPY %s successful.\n", fname);
ret = 2;
}

Expand All @@ -354,9 +313,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
#endif

success:
if (INFO_GTE(BACKUP, 1)) {
rprintf(FINFO, "backed up %s to %s\n",
fname, buf);
}
if (INFO_GTE(BACKUP, 1))
rprintf(FINFO, "backed up %s to %s\n", fname, buf);
return ret;
}
Loading

0 comments on commit 3696674

Please sign in to comment.