mirror of
https://github.com/troglobit/libite.git
synced 2026-11-14 19:03:05 +00:00
Make rsync() recreate symlinks only on request
Fixing LITE_FOPT_COPYFILE_SYM in copyfile() also changed rsync(), which has always passed that flag. Every caller would start getting links in the destination where they used to get a copy of the file. Keep following links by default and add LITE_FOPT_RSYNC_LINKS for callers that want them recreated. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
5 files changed
+42
-12
No files matched your search
@@ -7,7 +7,14 @@ All notable changes to the project are documented in this file.
|
|||||||
[UNRELEASED][]
|
[UNRELEASED][]
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Add `LITE_FOPT_RSYNC_LINKS` option to `rsync()`, recreates symlinks to
|
||||||
|
files in the destination instead of copying what they point to
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
- Fix #23: `copyfile()` ignored `LITE_FOPT_COPYFILE_SYM` and always
|
||||||
|
copied what a symlink points to. It now recreates the symlink, also
|
||||||
|
when it is dangling or points to a directory
|
||||||
- Fix `copyfile()` not preserving the source file mode when the process
|
- Fix `copyfile()` not preserving the source file mode when the process
|
||||||
umask is more restrictive
|
umask is more restrictive
|
||||||
- Fix `mkpath()`, and `fmkpath()`, not applying the given mode when the
|
- Fix `mkpath()`, and `fmkpath()`, not applying the given mode when the
|
||||||
|
|||||||
@@ -244,6 +244,12 @@ Generic Functions
|
|||||||
|
|
||||||
- `LITE_FOPT_RSYNC_DELETE (0x01)`
|
- `LITE_FOPT_RSYNC_DELETE (0x01)`
|
||||||
- `LITE_FOPT_KEEP_MTIME (0x02)`
|
- `LITE_FOPT_KEEP_MTIME (0x02)`
|
||||||
|
- `LITE_FOPT_RSYNC_LINKS (0x04)`
|
||||||
|
|
||||||
|
Symlinks in the source tree are followed by default, the destination
|
||||||
|
gets a copy of what they point to. With `LITE_FOPT_RSYNC_LINKS` a
|
||||||
|
symlink to a file, or a dangling symlink, is recreated in the
|
||||||
|
destination instead. Symlinks to directories are always followed.
|
||||||
|
|
||||||
In releases prior to v2.0 the argument controlling pruning was called
|
In releases prior to v2.0 the argument controlling pruning was called
|
||||||
`delete`, it is now called `opt`. The APIs are 100% compatible if the
|
`delete`, it is now called `opt`. The APIs are 100% compatible if the
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ extern "C"
|
|||||||
#define LITE_FOPT_RSYNC_DELETE 0x01 /**< Prune files from destination that are not in source */
|
#define LITE_FOPT_RSYNC_DELETE 0x01 /**< Prune files from destination that are not in source */
|
||||||
#define LITE_FOPT_COPYFILE_SYM 0x01 /**< Recreate symlink or follow to copy target */
|
#define LITE_FOPT_COPYFILE_SYM 0x01 /**< Recreate symlink or follow to copy target */
|
||||||
#define LITE_FOPT_KEEP_MTIME 0x02 /**< Preserve modification time */
|
#define LITE_FOPT_KEEP_MTIME 0x02 /**< Preserve modification time */
|
||||||
|
#define LITE_FOPT_RSYNC_LINKS 0x04 /**< Recreate symlinks to files instead of copying their target */
|
||||||
|
|
||||||
typedef struct lfile lfile_t; /**< Opqaue context struct for lfile APIs */
|
typedef struct lfile lfile_t; /**< Opqaue context struct for lfile APIs */
|
||||||
typedef struct sdbuf sdbuf_t; /**< Opqaue context struct for telnet APIs */
|
typedef struct sdbuf sdbuf_t; /**< Opqaue context struct for telnet APIs */
|
||||||
|
|||||||
+17
-5
@@ -45,7 +45,8 @@ static int set_mtime(char *fn, struct stat *st);
|
|||||||
* Synchronize contents and optionally remove non-existing backups
|
* Synchronize contents and optionally remove non-existing backups
|
||||||
* @param src Source directory
|
* @param src Source directory
|
||||||
* @param dst Destination directory
|
* @param dst Destination directory
|
||||||
* @param opt An option mask of ::LITE_FOPT_RSYNC_DELETE, ::LITE_FOPT_KEEP_MTIME
|
* @param opt An option mask of ::LITE_FOPT_RSYNC_DELETE, ::LITE_FOPT_KEEP_MTIME,
|
||||||
|
* ::LITE_FOPT_RSYNC_LINKS
|
||||||
* @param filter Optional filtering function for source directory.
|
* @param filter Optional filtering function for source directory.
|
||||||
*
|
*
|
||||||
* This is a miniature implementation of the famous rsync for local use
|
* This is a miniature implementation of the famous rsync for local use
|
||||||
@@ -60,6 +61,13 @@ static int set_mtime(char *fn, struct stat *st);
|
|||||||
*
|
*
|
||||||
* %LITE_FOPT_RSYNC_DELETE: Prune files from @p dst that no longer exist in @p src.
|
* %LITE_FOPT_RSYNC_DELETE: Prune files from @p dst that no longer exist in @p src.
|
||||||
* %LITE_FOPT_KEEP_MTIME: Preserve modification time
|
* %LITE_FOPT_KEEP_MTIME: Preserve modification time
|
||||||
|
* %LITE_FOPT_RSYNC_LINKS: Recreate symlinks to files in @p dst
|
||||||
|
*
|
||||||
|
* Symlinks in @p src are followed by default, so @p dst gets a copy of
|
||||||
|
* what they point to. With ::LITE_FOPT_RSYNC_LINKS a symlink to a file,
|
||||||
|
* or a dangling symlink, is recreated in @p dst with the same target
|
||||||
|
* text, replacing any file or symlink already there. Symlinks to
|
||||||
|
* directories in @p src are always followed.
|
||||||
*
|
*
|
||||||
* The filter callback, @p filter, if provided, is used to determine
|
* The filter callback, @p filter, if provided, is used to determine
|
||||||
* what files to include from the source directory when backing up. If
|
* what files to include from the source directory when backing up. If
|
||||||
@@ -87,7 +95,7 @@ int rsync(char *src, char *dst, int opt, int (*filter)(const char *file))
|
|||||||
if (!fexist(src))
|
if (!fexist(src))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (copy(src, dst, keep_mtim))
|
if (copy(src, dst, opt))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -137,7 +145,7 @@ int rsync(char *src, char *dst, int opt, int (*filter)(const char *file))
|
|||||||
continue; /* Next file/dir in @src to copy... */
|
continue; /* Next file/dir in @src to copy... */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copy(source, dst, keep_mtim))
|
if (copy(source, dst, opt))
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +170,12 @@ static int copy(char *src, char *dst, int opt)
|
|||||||
{
|
{
|
||||||
char *dest = NULL, *temp = NULL, *file = NULL;
|
char *dest = NULL, *temp = NULL, *file = NULL;
|
||||||
int result = 1, saved_errno, made_temp = 0;
|
int result = 1, saved_errno, made_temp = 0;
|
||||||
|
int copt = opt & LITE_FOPT_KEEP_MTIME;
|
||||||
|
|
||||||
copyfile(src, dst, 0, opt | LITE_FOPT_COPYFILE_SYM);
|
if (opt & LITE_FOPT_RSYNC_LINKS)
|
||||||
|
copt |= LITE_FOPT_COPYFILE_SYM;
|
||||||
|
|
||||||
|
copyfile(src, dst, 0, copt);
|
||||||
if (errno != EEXIST)
|
if (errno != EEXIST)
|
||||||
return errno ? 1 : 0;
|
return errno ? 1 : 0;
|
||||||
|
|
||||||
@@ -186,7 +198,7 @@ static int copy(char *src, char *dst, int opt)
|
|||||||
if (asprintf(&file, "%s/link", temp) < 0)
|
if (asprintf(&file, "%s/link", temp) < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
copyfile(src, file, 0, opt | LITE_FOPT_COPYFILE_SYM);
|
copyfile(src, file, 0, copt);
|
||||||
if (errno)
|
if (errno)
|
||||||
goto exit;
|
goto exit;
|
||||||
result = rename(file, dest) != 0;
|
result = rename(file, dest) != 0;
|
||||||
|
|||||||
+11
-7
@@ -65,29 +65,33 @@ int main(void)
|
|||||||
check_link("copied", "file");
|
check_link("copied", "file");
|
||||||
fail_unless(lstat("copied", &dst_st) == 0 && dst_st.st_mtime == times[1].tv_sec);
|
fail_unless(lstat("copied", &dst_st) == 0 && dst_st.st_mtime == times[1].tv_sec);
|
||||||
fail_unless(unlink("copied") == 0);
|
fail_unless(unlink("copied") == 0);
|
||||||
/* Exercise the existing rsync file caller, which requests SYM. */
|
/* rsync() follows links unless asked to recreate them. */
|
||||||
fail_unless(rsync("link", "copied", 0, NULL) == 0);
|
fail_unless(rsync("link", "copied", 0, NULL) == 0);
|
||||||
|
fail_unless(lstat("copied", &dst_st) == 0 && S_ISREG(dst_st.st_mode));
|
||||||
|
fail_unless(dst_st.st_size == sizeof(payload));
|
||||||
|
fail_unless(unlink("copied") == 0);
|
||||||
|
fail_unless(rsync("link", "copied", LITE_FOPT_RSYNC_LINKS, NULL) == 0);
|
||||||
check_link("copied", "file");
|
check_link("copied", "file");
|
||||||
/* Reuse the destination after changing the source link. */
|
/* Reuse the destination after changing the source link. */
|
||||||
fail_unless(unlink("link") == 0 && symlink("file", "link") == 0);
|
fail_unless(unlink("link") == 0 && symlink("file", "link") == 0);
|
||||||
fail_unless(rsync("link", "out", 0, NULL) == 0);
|
fail_unless(rsync("link", "out", LITE_FOPT_RSYNC_LINKS, NULL) == 0);
|
||||||
check_link("out/link", "file");
|
check_link("out/link", "file");
|
||||||
fail_unless(unlink("link") == 0 && symlink("copied", "link") == 0);
|
fail_unless(unlink("link") == 0 && symlink("copied", "link") == 0);
|
||||||
fail_unless(rsync("link", "out", 0, NULL) == 0);
|
fail_unless(rsync("link", "out", LITE_FOPT_RSYNC_LINKS, NULL) == 0);
|
||||||
check_link("out/link", "copied");
|
check_link("out/link", "copied");
|
||||||
/* A same-path copy must not unlink its source. */
|
/* A same-path copy must not unlink its source. */
|
||||||
fail_unless(rsync("link", "link", 0, NULL) == 0);
|
fail_unless(rsync("link", "link", LITE_FOPT_RSYNC_LINKS, NULL) == 0);
|
||||||
check_link("link", "copied");
|
check_link("link", "copied");
|
||||||
fail_unless(unlink("out/link") == 0);
|
fail_unless(unlink("out/link") == 0);
|
||||||
/* Replacing a regular destination must also retain the source link. */
|
/* Replacing a regular destination must also retain the source link. */
|
||||||
fail_unless(touch("out/link") == 0);
|
fail_unless(touch("out/link") == 0);
|
||||||
fail_unless(rsync("link", "out", 0, NULL) == 0);
|
fail_unless(rsync("link", "out", LITE_FOPT_RSYNC_LINKS, NULL) == 0);
|
||||||
check_link("out/link", "copied");
|
check_link("out/link", "copied");
|
||||||
fail_unless(unlink("out/link") == 0);
|
fail_unless(unlink("out/link") == 0);
|
||||||
/* A failed replacement must keep both the destination and source. */
|
/* A failed replacement must keep both the destination and source. */
|
||||||
fail_unless(mkdir("out/link", 0700) == 0);
|
fail_unless(mkdir("out/link", 0700) == 0);
|
||||||
fail_unless(touch("out/link/keep") == 0);
|
fail_unless(touch("out/link/keep") == 0);
|
||||||
fail_unless(rsync("link", "out", 0, NULL) != 0);
|
fail_unless(rsync("link", "out", LITE_FOPT_RSYNC_LINKS, NULL) != 0);
|
||||||
fail_unless(fexist("out/link/keep"));
|
fail_unless(fexist("out/link/keep"));
|
||||||
check_link("link", "copied");
|
check_link("link", "copied");
|
||||||
fail_unless(unlink("out/link/keep") == 0 && rmdir("out/link") == 0);
|
fail_unless(unlink("out/link/keep") == 0 && rmdir("out/link") == 0);
|
||||||
@@ -95,7 +99,7 @@ int main(void)
|
|||||||
fail_unless(mkdir("protected", 0700) == 0);
|
fail_unless(mkdir("protected", 0700) == 0);
|
||||||
fail_unless(symlink("original", "protected/link") == 0);
|
fail_unless(symlink("original", "protected/link") == 0);
|
||||||
fail_unless(chmod("protected", 0500) == 0);
|
fail_unless(chmod("protected", 0500) == 0);
|
||||||
fail_unless(rsync("link", "protected", 0, NULL) != 0);
|
fail_unless(rsync("link", "protected", LITE_FOPT_RSYNC_LINKS, NULL) != 0);
|
||||||
check_link("protected/link", "original");
|
check_link("protected/link", "original");
|
||||||
check_link("link", "copied");
|
check_link("link", "copied");
|
||||||
fail_unless(chmod("protected", 0700) == 0);
|
fail_unless(chmod("protected", 0700) == 0);
|
||||||
|
|||||||
Reference in new issue
Block a user