Compare commits
10 Commits
67906a920a
...
ee8c693f3b
Author | SHA1 | Date | |
---|---|---|---|
ee8c693f3b | |||
|
b7c052b39a | ||
b65901c151 | |||
2cb8f3cc7f | |||
0ca6ba656d | |||
3063272f1f | |||
1dad5cda13 | |||
a1e80eb09a | |||
b9f7caabe1 | |||
098d6d1c44 |
4
Makefile
4
Makefile
@ -3,14 +3,14 @@ DESTDIR ?=
|
|||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
# Build flags
|
# Build flags
|
||||||
FLAGS = -std=gnu99
|
OPTS = -std=gnu99
|
||||||
|
|
||||||
# Build rules
|
# Build rules
|
||||||
all: backly
|
all: backly
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean
|
||||||
|
|
||||||
backly: backly.c Makefile
|
backly: backly.c Makefile
|
||||||
gcc $(FLAGS) -o backly backly.c
|
gcc $(OPTS) $(CFLAGS) -o backly backly.c
|
||||||
|
|
||||||
install: backly
|
install: backly
|
||||||
install -Dm 0755 backly $(DESTDIR)/$(PREFIX)/bin/backly
|
install -Dm 0755 backly $(DESTDIR)/$(PREFIX)/bin/backly
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
About backly
|
# backly
|
||||||
============
|
|
||||||
|
A simple directory cloner
|
||||||
|
|
||||||
|
## About backly
|
||||||
|
|
||||||
backly is a simple rsync-like tool for recursively copying entire directories.
|
backly is a simple rsync-like tool for recursively copying entire directories.
|
||||||
The intended use case is for making verionless incremental backups; however, it
|
The intended use case is for making verionless incremental backups; however, it
|
||||||
@ -8,31 +11,25 @@ provided you have read access to the source and write access to the
|
|||||||
destination. Like rsync, backly uses a combination of file size and
|
destination. Like rsync, backly uses a combination of file size and
|
||||||
modification date to determine if it needs to be copied over.
|
modification date to determine if it needs to be copied over.
|
||||||
|
|
||||||
This program will not work properly if source is a subdirectory
|
This program will **not** work properly if source is a subdirectory
|
||||||
or destination or vice-versa.
|
or destination or vice-versa.
|
||||||
|
|
||||||
|
## Required packages
|
||||||
Required packages
|
|
||||||
=================
|
|
||||||
|
|
||||||
backly does not depend on any packages other than what typically ships with any
|
backly does not depend on any packages other than what typically ships with any
|
||||||
Linux distribution.
|
Linux distribution.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
Installation
|
The usual `make` and `make install` is sufficient for compiling and installing
|
||||||
============
|
backly. The default prefix is `/usr/local`, which means the binary will be
|
||||||
|
installed to `/usr/local/bin`. The prefix can be changed by setting it in the
|
||||||
|
install command; for example, by running `make PREFIX=/usr install`.
|
||||||
|
|
||||||
The usual 'make' and 'make install' is sufficient for compiling and installing
|
## How to report bugs?
|
||||||
backly. The default prefix /usr/local, which means the binary will be installed
|
|
||||||
to /usr/local/bin. The prefix can be changed by setting it in the install
|
|
||||||
command; for example, by running 'make PREFIX=/usr install'.
|
|
||||||
|
|
||||||
|
Open an issue on the issue tracker.
|
||||||
How to report bugs?
|
|
||||||
===================
|
|
||||||
|
|
||||||
Bugs should be reported to L. Bradley LaBoon <me@bradleylaboon.com>
|
|
||||||
|
|
||||||
Please indicate what OS and architecture you are using, as well as output from
|
Please indicate what OS and architecture you are using, as well as output from
|
||||||
the program showing the bug, if possible (hint: run backly with --test to avoid
|
the program showing the bug, if possible (hint: run backly with `--test` to
|
||||||
destroying any files that you care about).
|
avoid destroying any files that you care about).
|
87
backly.c
87
backly.c
@ -37,11 +37,12 @@ void printHelp()
|
|||||||
{
|
{
|
||||||
printUsage(stdout);
|
printUsage(stdout);
|
||||||
printf("Turn destdir into a clone of srcdir.\n\n");
|
printf("Turn destdir into a clone of srcdir.\n\n");
|
||||||
|
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" --test\tRun in test mode.\n");
|
printf(" --test\t\tRun in test mode\n");
|
||||||
printf(" --help\tPrint this help and quit.\n\n");
|
printf(" --noremove\tDon't remove any files in destdir\n");
|
||||||
|
printf(" --help\t\tPrint this help and quit\n\n");
|
||||||
|
|
||||||
printf("Report bugs to L. Bradley LaBoon <me@bradleylaboon.com>\n");
|
printf("Report bugs to L. Bradley LaBoon <me@bradleylaboon.com>\n");
|
||||||
printf("backly home page: <http://git.bradleylaboon.com/backly.git>\n");
|
printf("backly home page: <http://git.bradleylaboon.com/backly.git>\n");
|
||||||
}
|
}
|
||||||
@ -53,13 +54,13 @@ void removeMissing(char *src, int srcPrefix, char *dest, int destPrefix, int tes
|
|||||||
srcLen++;
|
srcLen++;
|
||||||
while (dest[destLen])
|
while (dest[destLen])
|
||||||
destLen++;
|
destLen++;
|
||||||
|
|
||||||
DIR *destDir = opendir(dest);
|
DIR *destDir = opendir(dest);
|
||||||
if (destDir == NULL) {
|
if (destDir == NULL) {
|
||||||
fprintf(stderr, "Could not open %s: %s\n", dest, strerror(errno));
|
fprintf(stderr, "Could not open %s: %s\n", dest, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the entire directory doesn't exist in src, remove it from dest
|
// If the entire directory doesn't exist in src, remove it from dest
|
||||||
DIR *srcDir = opendir(src);
|
DIR *srcDir = opendir(src);
|
||||||
if (srcDir == NULL) {
|
if (srcDir == NULL) {
|
||||||
@ -88,24 +89,24 @@ void removeMissing(char *src, int srcPrefix, char *dest, int destPrefix, int tes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(srcDir);
|
closedir(srcDir);
|
||||||
|
|
||||||
// Look at each item in the folder
|
// Look at each item in the folder
|
||||||
struct dirent *item;
|
struct dirent *item;
|
||||||
while ((item = readdir(destDir)) != NULL) {
|
while ((item = readdir(destDir)) != NULL) {
|
||||||
// Ignore . and .. references
|
// Ignore . and .. references
|
||||||
if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0)
|
if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int itemLen = 0;
|
int itemLen = 0;
|
||||||
while (item->d_name[itemLen])
|
while (item->d_name[itemLen])
|
||||||
itemLen++;
|
itemLen++;
|
||||||
|
|
||||||
// +2 because a trailing slash might be added
|
// +2 because a trailing slash might be added
|
||||||
char itemSrc[srcLen + itemLen + 2];
|
char itemSrc[srcLen + itemLen + 2];
|
||||||
char itemDest[destLen + itemLen + 2];
|
char itemDest[destLen + itemLen + 2];
|
||||||
sprintf(itemSrc, "%s%s", src, item->d_name);
|
sprintf(itemSrc, "%s%s", src, item->d_name);
|
||||||
sprintf(itemDest, "%s%s", dest, item->d_name);
|
sprintf(itemDest, "%s%s", dest, item->d_name);
|
||||||
|
|
||||||
// Test if item is a directory
|
// Test if item is a directory
|
||||||
int itemIsDir = 1;
|
int itemIsDir = 1;
|
||||||
DIR *testDir = opendir(itemDest);
|
DIR *testDir = opendir(itemDest);
|
||||||
@ -113,7 +114,7 @@ void removeMissing(char *src, int srcPrefix, char *dest, int destPrefix, int tes
|
|||||||
itemIsDir = 0;
|
itemIsDir = 0;
|
||||||
else if (testDir != NULL)
|
else if (testDir != NULL)
|
||||||
closedir(testDir);
|
closedir(testDir);
|
||||||
|
|
||||||
if (itemIsDir == 1) {
|
if (itemIsDir == 1) {
|
||||||
// If it's a directory, append trailing slashes and recurse
|
// If it's a directory, append trailing slashes and recurse
|
||||||
itemSrc[srcLen + itemLen] = '/';
|
itemSrc[srcLen + itemLen] = '/';
|
||||||
@ -150,7 +151,7 @@ void removeMissing(char *src, int srcPrefix, char *dest, int destPrefix, int tes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(destDir);
|
closedir(destDir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -162,13 +163,13 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
srcLen++;
|
srcLen++;
|
||||||
while(dest[destLen])
|
while(dest[destLen])
|
||||||
destLen++;
|
destLen++;
|
||||||
|
|
||||||
DIR *srcDir = opendir(src);
|
DIR *srcDir = opendir(src);
|
||||||
if (srcDir == NULL) {
|
if (srcDir == NULL) {
|
||||||
fprintf(stderr, "Could not open %s: %s\n", src, strerror(errno));
|
fprintf(stderr, "Could not open %s: %s\n", src, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the dest directory doesn't exist, create it
|
// If the dest directory doesn't exist, create it
|
||||||
DIR *destDir = opendir(dest);
|
DIR *destDir = opendir(dest);
|
||||||
if (destDir == NULL) {
|
if (destDir == NULL) {
|
||||||
@ -195,22 +196,22 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(destDir);
|
closedir(destDir);
|
||||||
|
|
||||||
// Look at each item in the folder
|
// Look at each item in the folder
|
||||||
struct dirent *item;
|
struct dirent *item;
|
||||||
while ((item = readdir(srcDir)) != NULL) {
|
while ((item = readdir(srcDir)) != NULL) {
|
||||||
if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0)
|
if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int itemLen = 0;
|
int itemLen = 0;
|
||||||
while (item->d_name[itemLen])
|
while (item->d_name[itemLen])
|
||||||
itemLen++;
|
itemLen++;
|
||||||
|
|
||||||
char itemSrc[srcLen + itemLen + 2];
|
char itemSrc[srcLen + itemLen + 2];
|
||||||
char itemDest[destLen + itemLen + 2];
|
char itemDest[destLen + itemLen + 2];
|
||||||
sprintf(itemSrc, "%s%s", src, item->d_name);
|
sprintf(itemSrc, "%s%s", src, item->d_name);
|
||||||
sprintf(itemDest, "%s%s", dest, item->d_name);
|
sprintf(itemDest, "%s%s", dest, item->d_name);
|
||||||
|
|
||||||
// Test if item is a directory
|
// Test if item is a directory
|
||||||
int itemIsDir = 1;
|
int itemIsDir = 1;
|
||||||
DIR *testDir = opendir(itemSrc);
|
DIR *testDir = opendir(itemSrc);
|
||||||
@ -218,7 +219,7 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
itemIsDir = 0;
|
itemIsDir = 0;
|
||||||
else if (testDir != NULL)
|
else if (testDir != NULL)
|
||||||
closedir(testDir);
|
closedir(testDir);
|
||||||
|
|
||||||
if (itemIsDir == 1) {
|
if (itemIsDir == 1) {
|
||||||
// If it's a directory, recurse
|
// If it's a directory, recurse
|
||||||
itemSrc[srcLen + itemLen] = '/';
|
itemSrc[srcLen + itemLen] = '/';
|
||||||
@ -239,7 +240,7 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fclose(destFile);
|
fclose(destFile);
|
||||||
|
|
||||||
// Check if file size or modified time is different
|
// Check if file size or modified time is different
|
||||||
struct stat srcStat, destStat;
|
struct stat srcStat, destStat;
|
||||||
if (stat(itemSrc, &srcStat) == -1) {
|
if (stat(itemSrc, &srcStat) == -1) {
|
||||||
@ -250,13 +251,13 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
fprintf(stderr, "Could not stat %s\n", itemDest);
|
fprintf(stderr, "Could not stat %s\n", itemDest);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcStat.st_size != destStat.st_size)
|
if (srcStat.st_size != destStat.st_size)
|
||||||
needToCopy = 1;
|
needToCopy = 1;
|
||||||
else if (srcStat.st_mtime > destStat.st_mtime)
|
else if (srcStat.st_mtime > destStat.st_mtime)
|
||||||
needToCopy = 1;
|
needToCopy = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only copy file if it doesn't exist or has changed
|
// Only copy file if it doesn't exist or has changed
|
||||||
if (needToCopy == 1) {
|
if (needToCopy == 1) {
|
||||||
printf("+ %s ", itemSrc + srcPrefix);
|
printf("+ %s ", itemSrc + srcPrefix);
|
||||||
@ -267,7 +268,7 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
fprintf(stderr, "Could not fork: %s\n", strerror(errno));
|
fprintf(stderr, "Could not fork: %s\n", strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
} else if (pid == 0) {
|
} else if (pid == 0) {
|
||||||
execlp("cp", "cp", "-fp", itemSrc, itemDest, (char *) NULL);
|
execlp("cp", "cp", "-f", "-P", "--preserve=timestamps,links", itemSrc, itemDest, (char *) NULL);
|
||||||
} else {
|
} else {
|
||||||
struct stat srcStat, destStat;
|
struct stat srcStat, destStat;
|
||||||
double pDone = 0;
|
double pDone = 0;
|
||||||
@ -276,22 +277,27 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
while (waitpid(pid, NULL, WNOHANG) == 0) {
|
while (waitpid(pid, NULL, WNOHANG) == 0) {
|
||||||
if (stat(itemDest, &destStat) == -1)
|
if (stat(itemDest, &destStat) == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
pDone = (double)destStat.st_size / (double)srcStat.st_size;
|
pDone = (double)destStat.st_size / (double)srcStat.st_size;
|
||||||
|
|
||||||
for (int i = 0; i < numPrinted; i++)
|
for (int i = 0; i < numPrinted; i++)
|
||||||
printf("\b \b");
|
printf("\b \b");
|
||||||
numPrinted = printf("%.2lf%%", pDone * 100);
|
numPrinted = printf("%.2lf%%", pDone * 100);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
usleep(50000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < numPrinted; i++)
|
||||||
|
printf("\b \b");
|
||||||
|
printf("100.00%%");
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(srcDir);
|
closedir(srcDir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -299,8 +305,8 @@ void copyNew(char *src, int srcPrefix, char *dest, int destPrefix, int testMode)
|
|||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int srcArg = 0, destArg = 0;
|
int srcArg = 0, destArg = 0;
|
||||||
int testMode = 0;
|
int testMode = 0, remove = 1;
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "--help") == 0) {
|
if (strcmp(argv[i], "--help") == 0) {
|
||||||
@ -309,39 +315,41 @@ int main(int argc, char **argv)
|
|||||||
} else if (strcmp(argv[i], "--test") == 0) {
|
} else if (strcmp(argv[i], "--test") == 0) {
|
||||||
testMode = 1;
|
testMode = 1;
|
||||||
printf("***Operating in test mode.***\n");
|
printf("***Operating in test mode.***\n");
|
||||||
|
} else if (strcmp(argv[i], "--noremove") == 0) {
|
||||||
|
remove = 0;
|
||||||
} else if (srcArg == 0) {
|
} else if (srcArg == 0) {
|
||||||
srcArg = i;
|
srcArg = i;
|
||||||
} else {
|
} else {
|
||||||
destArg = i;
|
destArg = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcArg == 0 || destArg == 0) {
|
if (srcArg == 0 || destArg == 0) {
|
||||||
printUsage(stderr);
|
printUsage(stderr);
|
||||||
fprintf(stderr, "Run 'backly --help' for more information.\n");
|
fprintf(stderr, "Run 'backly --help' for more information.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine argument lengths
|
// Determine argument lengths
|
||||||
int srcLen = 0, destLen = 0;
|
int srcLen = 0, destLen = 0;
|
||||||
while (argv[srcArg][srcLen])
|
while (argv[srcArg][srcLen])
|
||||||
srcLen++;
|
srcLen++;
|
||||||
while (argv[destArg][destLen])
|
while (argv[destArg][destLen])
|
||||||
destLen++;
|
destLen++;
|
||||||
|
|
||||||
// Add trailing slashes to directory arguments if necessary
|
// Add trailing slashes to directory arguments if necessary
|
||||||
char srcDir[srcLen + 2], destDir[destLen + 2];
|
char srcDir[srcLen + 2], destDir[destLen + 2];
|
||||||
|
|
||||||
if (argv[srcArg][srcLen - 1] == '/')
|
if (argv[srcArg][srcLen - 1] == '/')
|
||||||
sprintf(srcDir, "%s", argv[srcArg]);
|
sprintf(srcDir, "%s", argv[srcArg]);
|
||||||
else
|
else
|
||||||
sprintf(srcDir, "%s/", argv[srcArg]);
|
sprintf(srcDir, "%s/", argv[srcArg]);
|
||||||
|
|
||||||
if (argv[destArg][destLen - 1] == '/')
|
if (argv[destArg][destLen - 1] == '/')
|
||||||
sprintf(destDir, "%s", argv[destArg]);
|
sprintf(destDir, "%s", argv[destArg]);
|
||||||
else
|
else
|
||||||
sprintf(destDir, "%s/", argv[destArg]);
|
sprintf(destDir, "%s/", argv[destArg]);
|
||||||
|
|
||||||
// Check if directories exist and can be accessed
|
// Check if directories exist and can be accessed
|
||||||
DIR *dir = opendir(srcDir);
|
DIR *dir = opendir(srcDir);
|
||||||
if (dir == NULL) {
|
if (dir == NULL) {
|
||||||
@ -349,19 +357,20 @@ int main(int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
|
||||||
dir = opendir(destDir);
|
dir = opendir(destDir);
|
||||||
if (dir == NULL) {
|
if (dir == NULL) {
|
||||||
fprintf(stderr, "Could not open destination: %s\n", strerror(errno));
|
fprintf(stderr, "Could not open destination: %s\n", strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
|
||||||
// Remove files from the destination that don't exist in the source
|
// Remove files from the destination that don't exist in the source
|
||||||
removeMissing(srcDir, strlen(srcDir), destDir, strlen(destDir), testMode);
|
if (remove)
|
||||||
|
removeMissing(srcDir, strlen(srcDir), destDir, strlen(destDir), testMode);
|
||||||
|
|
||||||
// Copy new files and overwrite existing files if different
|
// Copy new files and overwrite existing files if different
|
||||||
copyNew(srcDir, strlen(srcDir), destDir, strlen(destDir), testMode);
|
copyNew(srcDir, strlen(srcDir), destDir, strlen(destDir), testMode);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user