Compare commits

..

10 Commits

3 changed files with 66 additions and 60 deletions

View File

@ -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

View File

@ -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).

View File

@ -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;
} }