/*****************************************************************************
* Backup Copy v2.0 *
* November 16, 1998 *
* Programmed by: Kevin Lindsay *
* Copyright (c) 1998 NetNation Communications Inc *
* *
* Report any Bugs to: klindsay@mkintraweb.com *
* *
* Special Thanks to: *
* *
* Michael Oswell <oswell@mkintraweb.com> - Ported Backup Copy to AIX, *
* HPUX, SUNOS, and IRIX *
* John Quinn <jquinn@soggey.ilo.dec.com> - Ported Backup Copy to Digital *
* UNIX V4.x gcc 2.8.0 *
* Karl Iorio <iorio@i-cafe.net> - Provided patches and beta testing. *
* Jeff Trawick <trawick@ibm.net> - Provided beta testing and partial *
* patches. *
* Robert Schouwenburg <rds@stack.nl> - Provided helpful support and *
* beta testing. *
* *
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <utime.h>
#include "config.h"
#include "link.h"
#include "statmalloc.h"
#include "misc.h"
#include "rmrec.h"
#include "trashbin.h"
#include "modechange.h"
#define VERSION "Backup Copy v2.0 - Released November 16, 1998\n"
/* Global Variables */
/*******************************************************************
* *MODE.value must be a 5 or greater digit number *
* *
* Note that the "*_MODE_ADD" values will be added to the current *
* permissions. These are equivalent to a chmod [u,g,o]+[r,w,x] *
*******************************************************************/
struct mode_change FILE_MODE;
struct mode_change DIR_MODE;
struct mode_change SPC_MODE;
/* File Default Mode */
unsigned short FILE_MODE_ADD = 00600;
F_MODE FILE_DEF_MODE;
/* Directory Default Mode */
unsigned short DIR_MODE_ADD = 00700;
F_MODE DIR_DEF_MODE;
/* Special File Default Mode */
unsigned short SPC_MODE_ADD = 00600;
F_MODE SPC_DEF_MODE;
/* Trash Bin Directory Default Mode */
unsigned short TRASH_DIR_DEF_MODE = 00700;
char *progname;
char *optchars = "cet";
char *SOURCE;
char *DEST;
char *EXCLUDE_BUF = "";
char *TRASHBINDIR = "";
int TRASHFD = 0;
int OPTINT = 0;
uid_t UID = 0;
int TEMP_FD1;
int TEMP_FD2;
dir_item *TEMP_PTR;
int MOD_CHECK_SEC;
char *cur_dir=NULL;
#define BUFSIZE 4096
/* Flags */
int VERBOSE = 0;
int RECURSIVE = 0;
int FIND = 0;
int CONFIG = 0;
int EXCLUDE = 0;
int SAVEEXCDIR = 0;
int NEWONLY = 0;
int NEWDIR = 0;
int TRASHBIN = 0;
int MOD_CHECK = 0;
int INODE_CHECK = 1;
int BYBYTE = 0;
int FILESIZE = 1;
int QUIET=0;
/* Usage */
void
usage()
{
printf("\n%s\n"
"Usage: %s [options] <source1>...[source2]... <destination>\n"
" or: %s -f [options] <source>\n"
" or: %s -c <conf file>\n\n"
"\tOptions:\n\n"
"\t-c <conf file> Specify a configuration file.\n"
"\t-e \"<dir1,...>\" Exclude specified directories. Note that any excluded\n"
"\t directories will be removed from an incremental backup.\n"
"\t-E \"<dir1,...>\" Exclude specified directories but do not remove them\n"
"\t from an incremental backup.\n"
"\t-f Find mode. Does not copy files. There is no need to\n"
"\t specify a destination with this option.\n"
"\t-n Incremental backup. Compare last modified time and copy\n"
"\t New files only. This will remove files and directories\n"
"\t that have been deleted from the source argument.\n"
"\t-r Recursive mode. Copy files from <source> recusively\n"
"\t to directory <destination>.\n"
"\t-t <directory> Move all old and deleted files from the last backup\n"
"\t to the specified trashbin. -n flag is automatically\n"
"\t used when -t is specified.\n"
"\t-s <seconds> Maximum time in seconds that comparison on file\n"
"\t modification time can differ between source and\n"
"\t destination files.\n"
"\t-i Toggle inode information file comparison checking OFF.\n"
"\t-z Toggle file size comparison checking OFF.\n"
"\t-q Quiet mode. Suppress all error message.\n"
"\t-v Verbose mode.\n"
"\t-V Version.\n\n"
"Report any bugs or ideas to:\n\n"
"\tKevin Lindsay\n"
"\tklindsay@mkintraweb.com\n"
"",VERSION,progname,progname,progname);
exit(1);
}
/* My fchdir() so that I can do cleaner error checking */
int
MYfchdir(int fd, char *filepath, char *func)
{
if (fchdir(fd) == -1) {
if (!QUIET)
fprintf(stderr,"%s: %s: fchdir: %s: %s\n",progname,func,filepath,strerror(errno));
return(-1);
}
return(0);
}
/* My chdir() so that I can do cleaner error checking */
int
MYchdir(char *filepath, char *func)
{
if (chdir(filepath) == -1) {
if (!QUIET)
fprintf(stderr,"%s: %s: chdir: %s: %s\n",progname,func,filepath,strerror(errno));
return(-1);
}
return(0);
}
/* Grab and check trash bin from command line */
int
trash_dir(char *directory)
{
char *ptr1;
struct stat tbfs;
struct stat tmpfs;
char *dirptr2;
if (directory[0] != '/') {
if (!QUIET)
fprintf(stderr,"%s: trash bin: %s must be an absolute directory name.\n",progname,directory);
exit(1);
}
if (strstr(directory,"..") != NULL || strstr(directory,"./") != NULL) {
if (!QUIET)
fprintf(stderr,"%s: trash bin: %s is an invalid absolute directory name.\n",progname,directory);
exit(1);
}
ptr1 = directory + (strlen(directory));
ptr1--;
if (ptr1[0] != '/') ptr1++;
TRASHBINDIR = statmalloc((ptr1-directory)+1);
memcpy(TRASHBINDIR,directory,ptr1-directory);
TRASHBINDIR[ptr1-directory] = '\0';
if (lstat(TRASHBINDIR,&tbfs) == -1) {
if (errno == 2) {
if (mkdir(TRASHBINDIR,00700) == -1) {
if (VERBOSE)
fprintf(stdout,"%s: trash bin: %s does not exist. Creating directory.\n",progname,TRASHBINDIR);
dirptr2 = TRASHBINDIR+1;
while (dirptr2 != NULL) {
if ((dirptr2 = strchr(dirptr2,'/')) != NULL) {
*dirptr2 = '\0';
}
if (lstat(TRASHBINDIR,&tmpfs) == -1) {
if (mkdir(TRASHBINDIR,00700) == -1) {
fprintf(stderr,"%s: trash bin: %s: %s\n",progname,TRASHBINDIR,strerror(errno));
exit(1);
}
}
if (dirptr2 != NULL) {
*dirptr2 = '/';
dirptr2++;
}
}
}
} else {
if (!QUIET)
fprintf(stderr,"%s: trash bin: %s: %s\n",progname,TRASHBINDIR,strerror(errno));
exit(1);
}
} else {
if (!S_ISDIR(tbfs.st_mode)) {
if (!QUIET)
fprintf(stderr,"%s: trash bin: %s already exists, but must be a directory.\n",progname,TRASHBINDIR);
exit(1);
}
if (VERBOSE)
fprintf(stdout,"%s: trash bin: %s will be used as the trash bin.\n",progname,TRASHBINDIR);
}
if ((TRASHFD = open(TRASHBINDIR,O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: trash bin: %s: %s\n",progname,TRASHBINDIR,strerror(errno));
exit(1);
}
return(0);
}
/* Parse Config File Organizer */
char **
parse_config(char **argv, int argnum)
{
char *fbuf;
char **filelist;
int f = 0;
fbuf = load_file(argv[argnum]);
/* Check to make sure Variables can be found in config file */
if (strcmp(get_config("VERBOSE",fbuf,'S'),"NULL") == 0) exit(-1);
if (strcmp(get_config("RECURSIVE",fbuf,'S'),"NULL") == 0) exit(-1);
if (strcmp(get_config("FIND",fbuf,'S'),"NULL") == 0) exit(-1);
if (strcmp(get_config("SOURCE",fbuf,'S'),"NULL") == 0) exit(-1);
/* Set flags with values from the config file */
VERBOSE = atoi(get_config("VERBOSE",fbuf,'S'));
NEWONLY = atoi(get_config("NEWONLY",fbuf,'S'));
if ((FIND = atoi(get_config("FIND",fbuf,'S')))) {
RECURSIVE = 1;
NEWONLY = 0;
}
else
RECURSIVE = atoi(get_config("RECURSIVE",fbuf,'S'));
if (!FIND && (strcmp(get_config("DEST",fbuf,'S'),"NULL") == 0)) exit(-1);
SOURCE = get_config("SOURCE",fbuf,'S');
DEST = get_config("DEST",fbuf,'S');
TRASHBIN = atoi(get_config("TRASHBIN",fbuf,'S'));
TRASHBINDIR = get_config("TRASHBINDIR",fbuf,'S');
if ((MOD_CHECK_SEC = atoi(get_config("SECONDSCHK",fbuf,'S'))) > 0)
MOD_CHECK = 1;
INODE_CHECK = atoi(get_config("INODE_CHECK",fbuf,'S'));
FILESIZE = atoi(get_config("SECONDSCHK",fbuf,'S'));
QUIET = atoi(get_config("QUITE",fbuf,'S'));
SAVEEXCDIR = atoi(get_config("SAVEEXCDIR",fbuf,'S'));
if (TRASHBIN) trash_dir(TRASHBINDIR);
if (RECURSIVE) {
/* Set the Exclude buffer */
if ((EXCLUDE = atoi(get_config("EXCLUDEDIR",fbuf,'S'))))
EXCLUDE_BUF = get_config("EXCLUDEDIR",fbuf,'M');
}
/* Add the appropriate Source and Destination arguments to filelist */
filelist = (char **)statmalloc(sizeof(char *));
filelist[f] = (char *)statmalloc(strlen(SOURCE)+1);
filelist[f][0] = '\0';
strcat(filelist[f],SOURCE);
if (!FIND) {
f++;
filelist[f] = statmalloc(strlen(DEST)+1);
filelist[f][0] = '\0';
strcat(filelist[f],DEST);
}
f++;
filelist[f] = statmalloc(1);
filelist[f][0] = '\0';
return(filelist);
}
/* Exclude Dir Command Line Entry */
int
exclude_dir(char *directory)
{
char *ptr1;
char *ptr2;
char *resval=NULL;
int grow = 1;
int jump = 0;
struct stat resfs;
/* arg_num should be the optind for the variable after -e from
* a command line entry */
ptr1 = directory;
/* Initialize the Exclude Buffer with 1 byte so we can easily
* realloc later on */
EXCLUDE_BUF = malloc(1);
EXCLUDE_BUF[0] = '\0';
while (!jump) {
/* Point ptr2 to end of current list of "," delimited dir names. */
if ((ptr2 = strchr(ptr1,',')) == NULL)
ptr2 = strchr(ptr1,'\0');
/* Move ptr2 back one and Check if '/' exists. If it doesn't then
* move back to original spot. We want to elimite and '/'s from the
* end of directories */
ptr2--;
if (ptr2[0] != '/')
ptr2++;
/* Copy the directory name into resval and check if it is a proper
* directory and only print a warning to stderr */
resval = statmalloc((ptr2-ptr1)+1);
resval[0] = '\0';
strncat(resval,ptr1,ptr2-ptr1);
if (lstat(resval,&resfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: This exclude directory is invalid: %s\n",progname,resval,strerror(errno));
}
if (!S_ISDIR(resfs.st_mode)) {
if (!QUIET)
fprintf(stderr,"%s: %s: Exclude argument is not a directory.\n",progname,resval);
}
freemalloc(resval);
/* Re size the Exclude Buffer and add the directory to it */
grow += (ptr2-ptr1)+2;
EXCLUDE_BUF = realloc(EXCLUDE_BUF,grow);
strcat(EXCLUDE_BUF,"^");
strncat(EXCLUDE_BUF,ptr1,ptr2-ptr1);
strcat(EXCLUDE_BUF,"^");
/* if ptr2[0] or ptr[1] is not equal to a ',' then jump = 1 to
* end while loop */
if (ptr2[0] == ',' || ptr2[1] == ',') {
if (ptr2[1] == ',') ptr1 = ptr2+2;
else ptr1 = ptr2+1;
} else jump = 1;
}
/* Replace any double '/'s with single '/'s in the Exclude buffer */
EXCLUDE_BUF = repasubstr("//","/",EXCLUDE_BUF);
/* Mark EXCLUDE flag */
EXCLUDE = 1;
return(1);
}
/* File Hole Checker */
int
hole(char *buffer, int bytes)
{
int res = 1;
int i = 0;
while (res == 1 && i < bytes) {
if (buffer[i] != 0x0)
res = 0;
i++;
}
return(res);
}
/* Parse Command Line */
char **
parse_cmdline(int args, char **argv, int optind)
{
char **filelist = NULL;
int i;
int f = 0;
filelist = (char **)realloc(filelist,sizeof(char *)+1);
for (i = optind; i < args; i++) {
filelist[f] = statmalloc(strlen(argv[i])+1);
filelist[f][0] = '\0';
strcat(filelist[f],argv[i]);
f++;
}
filelist[f] = statmalloc(1);
filelist[f][0] = '\0';
return(filelist);
}
/* return non-zero if symlink filename
* does not have the length or contents of symbuf - kji */
int
symlink_not_equal(char *filename,char *symbuf,int linkbytes)
{
char nsymbuf[MYPATH_MAX+1];
if ( linkbytes != readlink(filename,nsymbuf,MYPATH_MAX) ) return 1;
nsymbuf[linkbytes] = '\0';
return strcmp(nsymbuf,symbuf);
}
/* Copy Recursive files */
int
rec_file_copy(int source_fd, int dest_fd, char *filename)
{
register char *buffer = NULL;
int bytes_read = 0;
int oldf = 0;
int newf = 0;
struct stat foldfs;
char symbuf[MYPATH_MAX+1];
int linkbytes = 0;
struct utimbuf newtime;
unsigned long total_bytes = 0;
int make_holes = 0;
if (MYfchdir(source_fd,filename,"rec_file_copy 1") == -1) return(-1);
if (lstat(filename,&foldfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,filename,strerror(errno));
return(-1);
}
/* Open file if it is a regular file*/
if (S_ISREG(foldfs.st_mode) && (oldf = open(filename,O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,filename,strerror(errno));
return(-1);
}
/* If a device of some type */
/* If a Character or Block Device */
if (S_ISCHR(foldfs.st_mode) || S_ISBLK(foldfs.st_mode) || S_ISFIFO(foldfs.st_mode) || S_ISSOCK(foldfs.st_mode)) {
if ((UID != 0 && S_ISFIFO(foldfs.st_mode)) || UID == 0) {
if (MYfchdir(dest_fd,filename,"rec_file_copy 2") == -1) return(-1);
remove(filename);
if (mknod(filename, SPC_DEF_MODE, foldfs.st_rdev) == -1) {
if (!QUIET)
fprintf(stderr,"%s: mknod: %s: %s\n",progname,filename,strerror(errno));
return(-1);
}
if (UID == 0) {
chown(filename,foldfs.st_uid,foldfs.st_gid);
SPC_DEF_MODE = foldfs.st_mode;
} else
SPC_DEF_MODE = mode_adjust(foldfs.st_mode,&SPC_MODE);
chmod(filename,SPC_DEF_MODE);
/* Set time on file to same as source file */
newtime.actime = foldfs.st_atime;
newtime.modtime = foldfs.st_mtime;
if (utime(filename,&newtime) == -1) {
if (!QUIET)
fprintf(stderr,"%s: utime: %s: %s\n",progname,filename,strerror(errno));
}
if (VERBOSE) {
fprintf(stdout," -> %s\n",filename);
}
return(0);
} else {
if (!QUIET)
fprintf(stderr,"-> %s: copy only permited by root for this file type.\n",filename);
return(-1);
}
}
/* If a symlink, copy and return appropriately */
if (S_ISLNK(foldfs.st_mode)) {
if ((linkbytes = readlink(filename,symbuf,MYPATH_MAX)) < 0) {
if (!QUIET)
fprintf(stderr,"%s: readlink: %s: %s\n",progname,filename,strerror(errno));
return(-1);
}
symbuf[linkbytes] = '\0';
if (MYfchdir(dest_fd,filename,"rec_file_copy 3") == -1) {
return(-1);
}
/* copy only if not equal already - kji */
if ( symlink_not_equal(filename,symbuf,linkbytes) ) {
remove(filename);
if (symlink(symbuf,filename) == -1) {
if (!QUIET)
fprintf(stderr,"%s: symlink: %s: %s\n",progname,filename,strerror(errno));
return(-1);
}
if (UID == 0)
#ifdef FREEBSD_CPBK
if (lchown(filename,foldfs.st_uid,foldfs.st_gid) == -1) {
if (!QUIET)
fprintf(stderr,"%s: chown: %s: %s\n",progname,filename,strerror(errno));
}
#endif
#ifdef BSDOS_CPBK
if (lchown(filename,foldfs.st_uid,foldfs.st_gid) == -1) {
if (!QUIET)
fprintf(stderr,"%s: chown: %s: %s\n",progname,filename,strerror(errno));
}
#endif
#ifdef LINUX_CPBK
if (chown(filename,foldfs.st_uid,foldfs.st_gid) == -1) {
if (!QUIET)
fprintf(stderr,"%s: chown: %s: %s\n",progname,filename,strerror(errno));
}
#endif
/* no need to do anything else with symlinks:
* permissions are ignored by system and
* times cannot be set with utimes() */
if (VERBOSE) {
fprintf(stdout," -> %s\n",filename);
}
}
return(1);
}
/* If we get this far and it is not a regular file, then we cannot copy it.
* It is probably a UNIX Socket. We will get to those soon. */
if (!S_ISREG(foldfs.st_mode)) {
if (!QUIET)
fprintf(stderr,"%s: %s: Not a Regular File!\n",progname,filename);
return(-1);
}
if (MYfchdir(dest_fd,filename,"rec_file_copy 4") == -1) {
close(oldf);
return(-1);
}
/* Remove the destination file before write it. Cleaner this way. */
remove(filename);
if (UID == 0)
FILE_DEF_MODE = foldfs.st_mode;
else
FILE_DEF_MODE = mode_adjust(foldfs.st_mode,&FILE_MODE);
/* Open the New file with same perms as source */
if ((newf = open(filename,O_WRONLY+O_CREAT,FILE_DEF_MODE)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,filename,strerror(errno));
close(oldf);
return(-1);
}
if ((buffer = statmalloc(BUFSIZE)) == NULL) {
if (!QUIET)
fprintf(stderr,"%s: Could not allocate %d bytes of memory for buffer\n",progname,BUFSIZE);
close(oldf);
close(newf);
return(-1);
}
/* Check if size is what it should be compared to how many blocks
* that we have. */
if (S_ISREG (foldfs.st_mode) && (size_t) ((foldfs.st_size / foldfs.st_blksize)+1) > (size_t) foldfs.st_blocks) {
make_holes = 1;
}
/* Loop until file is copies */
while ((bytes_read = read(oldf,buffer,BUFSIZE)) > 0) {
total_bytes += bytes_read;
/* Check if data is a hole */
if (make_holes == 1 && hole(buffer,bytes_read)) {
/* lseek bytes_read to rebuild the hole */
if (bytes_read == BUFSIZE) {
if (lseek(newf,bytes_read,SEEK_CUR) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lseek: %s: %s\n",progname,filename,strerror(errno));
close(oldf);
close(newf);
freemalloc(buffer);
return(-1);
}
} else {
/* ftrunct total bytes read to rebuild the hole */
if (ftruncate(newf,total_bytes) == -1) {
if (!QUIET)
fprintf(stderr,"%s: ftruncate: %s: %s\n",progname,filename,strerror(errno));
close(oldf);
close(newf);
freemalloc(buffer);
return(-1);
}
}
} else if (write(newf,buffer,bytes_read) == -1) {
if (!QUIET)
fprintf(stderr,"%s: write: %s: %s\n",progname,filename,strerror(errno));
close(oldf);
close(newf);
freemalloc(buffer);
return(-1);
}
}
/* Error check if read returned an error */
if (bytes_read == -1) {
if (!QUIET)
fprintf(stderr,"%s: read: %s: %s\n",progname,filename,strerror(errno));
close(oldf);
close(newf);
freemalloc(buffer);
return(-1);
}
close(oldf);
/* Set perms and ownership for new file matching source file */
if (UID == 0) {
fchown(newf,foldfs.st_uid,foldfs.st_gid);
FILE_DEF_MODE = foldfs.st_mode;
} else
FILE_DEF_MODE = mode_adjust(foldfs.st_mode,&FILE_MODE);
fchmod(newf,FILE_DEF_MODE);
close(newf);
freemalloc(buffer);
if (MYfchdir(dest_fd,filename,"rec_file_copy 5") == -1) return(-1);
if (VERBOSE) {
fprintf(stdout," -> %s\n",filename);
}
/* Set time on file to same as source file */
newtime.actime = foldfs.st_atime;
newtime.modtime = foldfs.st_mtime;
if (utime(filename,&newtime) == -1) {
if (!QUIET)
fprintf(stderr,"%s: utime: %s: %s\n",progname,filename,strerror(errno));
}
return(1);
}
/* Copy regular files - I'm leaving this undocumeted. I'm going to use the
* rec_file_copy() function to do this same thing. */
int
file_copy(char *oldfile, char *newfile)
{
register char *buffer = NULL;
int bytes_read = 0;
int oldf;
int newf;
struct stat foldfs;
struct stat fnewfs;
char *p;
char *temp;
struct utimbuf newtime;
unsigned long total_bytes = 0;
if (lstat(oldfile,&foldfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,oldfile,strerror(errno));
return(-1);
}
if (S_ISDIR(foldfs.st_mode)) {
if (!QUIET)
fprintf(stderr,"%s: %s: omitting directory\n",progname,oldfile);
return(-1);
}
lstat(newfile,&fnewfs);
if (fnewfs.st_dev == foldfs.st_dev && fnewfs.st_ino == foldfs.st_ino) {
if (!QUIET)
fprintf(stderr,"%s: %s and %s are identical (not copied).\n",progname,oldfile,newfile);
return(-1);
}
if (S_ISDIR(fnewfs.st_mode)) {
temp = statmalloc(strlen(((p = rindex(oldfile,'/')) ? ++p : oldfile))+((newfile[strlen(newfile)-1] == '/') ? 0 : 1)+strlen(newfile)+1);
temp[0] = '\0';
strcat(temp,newfile);
if (newfile[strlen(newfile)-1] != '/')
strcat(temp,"/");
strcat(temp,((p = rindex(oldfile,'/')) ? ++p : oldfile));
newfile = statmalloc(strlen(temp)+1);
newfile[0] = '\0';
strcat(newfile,temp);
freemalloc(temp);
}
/* If a Character or Block Device */
if (S_ISCHR(foldfs.st_mode) || S_ISBLK(foldfs.st_mode) || S_ISFIFO(foldfs.st_mode) || S_ISSOCK(foldfs.st_mode)) {
if (mknod(newfile, foldfs.st_mode, foldfs.st_rdev) == -1) {
if (!QUIET)
fprintf(stderr,"%s: dev: %s: %s\n",progname,oldfile,strerror(errno));
return(-1);
}
chown(newfile,foldfs.st_uid,foldfs.st_gid);
chmod(newfile,foldfs.st_mode);
/* Set time on file to same as source file */
newtime.actime = foldfs.st_atime;
newtime.modtime = foldfs.st_mtime;
if (utime(newfile,&newtime) == -1) {
if (!QUIET)
fprintf(stderr,"%s: utime: %s: %s\n",progname,newfile,strerror(errno));
}
if (VERBOSE) {
fprintf(stdout," -> %s\n",newfile);
}
return(0);
}
if ((oldf = open(oldfile,O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,oldfile,strerror(errno));
return(-1);
}
if ((newf = open(newfile,O_WRONLY+O_CREAT,foldfs.st_mode)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,newfile,strerror(errno));
close(oldf);
return(-1);
}
if ((buffer = statmalloc(BUFSIZE)) == NULL) {
if (!QUIET)
fprintf(stderr,"%s: Could not allocate %d bytes of memory for buffer\n",progname,BUFSIZE);
close(oldf);
close(newf);
return(-1);
}
while ((bytes_read = read(oldf,buffer,BUFSIZE)) > 0) {
if (bytes_read > 0)
total_bytes += bytes_read;
if (hole(buffer,bytes_read)) {
if (bytes_read == BUFSIZE) {
if (lseek(newf,bytes_read,SEEK_CUR) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lseek: %s: %s\n",progname,newfile,strerror(errno));
close(oldf);
close(newf);
return(-1);
}
} else {
if (ftruncate(newf,total_bytes) == -1) {
if (!QUIET)
fprintf(stderr,"%s: ftruncate: %s: %s\n",progname,newfile,strerror(errno));
close(oldf);
close(newf);
return(-1);
}
}
} else if (write(newf,buffer,bytes_read) == -1) {
if (!QUIET)
fprintf(stderr,"%s: write: %s: %s\n",progname,newfile,strerror(errno));
close(oldf);
close(newf);
return(-1);
}
}
if (bytes_read == -1) {
if (!QUIET)
fprintf(stderr,"%s: read: %s: %s\n",progname,oldfile,strerror(errno));
close(oldf);
close(newf);
return(-1);
}
close(oldf);
fchmod(newf,foldfs.st_mode);
fchown(newf,foldfs.st_uid,foldfs.st_gid);
close(newf);
newtime.actime = foldfs.st_atime;
newtime.modtime = foldfs.st_mtime;
utime(newfile,&newtime);
if (VERBOSE) {
fprintf(stdout,"%s: %s -> %s\n",progname,oldfile,newfile);
}
if (S_ISDIR(fnewfs.st_mode)) {
freemalloc(newfile);
}
freemalloc(buffer);
return(1);
}
/* Get Current Directory */
char *
get_cur_path(dir_item *listptr, char gtype)
{
dir_item *dir_ptr;
char **dirarray;
int items=0;
int i;
int grow=0;
int tgrow;
/* gtype options:
*
* V = Verbose, do not include ^'s around dirnames
* E = Exclude, include ^'s around dirnames as delimiters */
if (cur_dir) {
free(cur_dir);
}
dir_ptr = listptr;
while (!dir_ptr->root) {
dir_ptr=dir_ptr->left;
items++;
}
dirarray = calloc(items,sizeof(char *));
dir_ptr=listptr->left;
for (i = items-1; i != -1; i--) {
dirarray[i] = malloc(strlen(dir_ptr->name)+1);
dirarray[i][0] = '\0';
strcat(dirarray[i],dir_ptr->name);
dir_ptr=dir_ptr->left;
}
if (gtype == 'V') {
cur_dir = malloc(1);
cur_dir[0] = '\0';
grow=1;
} else {
grow=2;
cur_dir = malloc(2);
cur_dir[0] = '^';
cur_dir[1] = '\0';
}
tgrow=grow;
for (i = 0; i != items; i++) {
grow+=strlen(dirarray[i])+2;
if (grow > tgrow) {
tgrow=grow+64;
cur_dir = realloc(cur_dir,tgrow);
}
strcat(cur_dir,dirarray[i]);
strcat(cur_dir,"/");
}
if (gtype == 'E') {
grow += listptr->name_len;
cur_dir = realloc(cur_dir,grow+1);
strcat(cur_dir,listptr->name);
strcat(cur_dir,"^");
}
for (i = 0; i != items; i++) {
free(dirarray[i]);
}
free(dirarray);
return(cur_dir);
}
/* Delete Files */
int
delete_files(dir_item *listptr, int dest_fd)
{
dest_item *destptr;
char *trash_src_dir;
char *ptr;
char *tmpdir=NULL;
destptr = listptr->dir_info;
/* Loop Through destptr files and remove any with a the delete flag
* set to 1 */
while (destptr != NULL) {
if (destptr->delete) {
if (destptr->type == 0) {
if (VERBOSE)
fprintf(stdout,"rmfile: %s\n",destptr->name);
if (TRASHBIN) {
trash_src_dir = get_cur_path(listptr,'V');
trashit(destptr->name,dest_fd,TRASHFD,progname,'S',trash_src_dir,TRASH_DIR_DEF_MODE);
}
remove(destptr->name);
}
else if (destptr->type == 1) {
if (SAVEEXCDIR) {
get_cur_path(listptr,'E');
ptr = strrchr(cur_dir,'^');
*ptr = '\0';
tmpdir = malloc(strlen(cur_dir)+destptr->name_len+3);
tmpdir[0] = '\0';
strcat(tmpdir,cur_dir);
strcat(tmpdir,"/");
strcat(tmpdir,destptr->name);
strcat(tmpdir,"^");
}
if (!SAVEEXCDIR || (SAVEEXCDIR && (strstr(EXCLUDE_BUF,tmpdir) == NULL))) {
if (VERBOSE)
fprintf(stdout,"removing directory: %s\n",destptr->name);
if (TRASHBIN) {
trash_src_dir = get_cur_path(listptr,'V');
trashit(destptr->name,dest_fd,TRASHFD,progname,'D',trash_src_dir,TRASH_DIR_DEF_MODE);
} else
rmrec(destptr->name);
}
if (SAVEEXCDIR)
free(tmpdir);
}
}
destptr = destptr->next;
}
return(0);
}
/* Check if File is Newer */
int
file_newer(dir_item *listptr, int source_fd, int dest_fd, char ctype)
{
struct stat sourcefs;
struct stat destfs;
dest_item *destptr;
char *trash_src_dir;
int linkbytes;
char symbuf[MYPATH_MAX+1];
destptr = listptr->left->dir_info;
/* Check if Source file exists in Destination Directory */
while (destptr != NULL && (strcmp(destptr->name,listptr->name) != 0)) {
destptr = destptr->next;
}
/* Return, Source File is newer */
if (destptr == NULL) {
return(1);
}
destptr->delete = 0;
if (MYfchdir(source_fd,"","file_newer 1") == -1) return(-1);
lstat(listptr->name,&sourcefs);
if (S_ISLNK(sourcefs.st_mode)) {
/* if not for verbose printout, always assume here that symlinks
* are newer so they will be sent to rec_file_copy. the link
* will actually be copied only if the contents are not equal - kji */
if ( ctype != 'C' ) return(1);
/* from here on is only for verbose printout:
* return "newer" if contents are not equal */
if ((linkbytes = readlink(listptr->name,symbuf,MYPATH_MAX)) < 0) {
if (!QUIET)
fprintf(stderr,"%s: readlink: %s: %s\n",progname,listptr->name,strerror(errno));
return(-1);
}
symbuf[linkbytes] = '\0';
if (MYfchdir(dest_fd,"","file_newer 2") == -1) return(-1);
return symlink_not_equal(listptr->name,symbuf,linkbytes);
}
if (MYfchdir(dest_fd,"","file_newer 3") == -1) return(-1);
lstat(destptr->name,&destfs);
if (INODE_CHECK && sourcefs.st_ctime > destfs.st_ctime) {
if (TRASHBIN && ctype == 'P') {
trash_src_dir = get_cur_path(listptr->left,'V');
trashit(listptr->name,dest_fd,TRASHFD,progname,'S',trash_src_dir,TRASH_DIR_DEF_MODE);
}
return(1);
}
if (FILESIZE && sourcefs.st_size != destfs.st_size) {
if (TRASHBIN && ctype == 'P') {
trash_src_dir = get_cur_path(listptr->left,'V');
trashit(listptr->name,dest_fd,TRASHFD,progname,'S',trash_src_dir,TRASH_DIR_DEF_MODE);
}
return(1);
}
if (sourcefs.st_mtime != destfs.st_mtime) {
if (MOD_CHECK && (sourcefs.st_mtime - destfs.st_mtime) < MOD_CHECK_SEC) {
return(0);
}
if (TRASHBIN && ctype == 'P') {
trash_src_dir = get_cur_path(listptr->left,'V');
trashit(listptr->name,dest_fd,TRASHFD,progname,'S',trash_src_dir,TRASH_DIR_DEF_MODE);
}
return(1);
}
return(0);
}
/* Load Destination Dir Contents into Linked List */
dest_item *
load_dest_dir(int dest_fd, dest_item *destptr)
{
DIR *recdir;
struct dirent *filenames = NULL;
struct stat destfs;
destptr = init_dest_list();
if (MYfchdir(dest_fd,"","load_dest_dir 1") == -1) return(NULL);
/* Open Current Directory */
if ((recdir = opendir(".")) == NULL) {
if (!QUIET)
fprintf(stderr,"%s: Opendir: %s: %s\n",progname,destptr->name,strerror(errno));
return(NULL);
}
if ((filenames = readdir(recdir)) == NULL) {
if (!QUIET)
fprintf(stderr,"%s: Readdir Error: Load Dest Dir: %s: %s\n",progname,destptr->name,strerror(errno));
closedir(recdir);
return(NULL);
}
/* Move ahead one directory */
seekdir(recdir,telldir(recdir));
while ((filenames = readdir(recdir)) != NULL) {
/* If at the top of the dir, then move ahead one file */
if (strcmp(filenames->d_name,"..") == 0) {
seekdir(recdir,telldir(recdir));
} else {
/* Add another item to destptr if not at top of dir */
destptr = add_next(destptr);
destptr = destptr->next;
}
if (strcmp(filenames->d_name,"..") != 0) {
/* Add filename or Dir to destptr */
destptr->name = statmalloc(strlen(filenames->d_name)+1);
destptr->name[0] = '\0';
strcat(destptr->name,filenames->d_name);
destptr->name_len = strlen(destptr->name);
if (lstat(filenames->d_name,&destfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,filenames->d_name,strerror(errno));
}
if (S_ISDIR(destfs.st_mode))
destptr->type=1;
else
destptr->type=0;
} else {
/* If directory is blank, setup destptr appropriately */
destptr->name = statmalloc(2);
destptr->name[0] = ' ';
destptr->name[1] = '\0';
destptr->type = 2;
destptr->name_len = 1;
}
}
while (destptr->prev != NULL)
destptr = destptr->prev;
closedir(recdir);
return(destptr);
}
/* Load Dir Contents into Linked List */
dir_item *
load_list(int source_fd, dir_item *listptr)
{
DIR *recdir;
struct dirent *filename = NULL;
struct stat statres;
int top=0;
if (MYfchdir(source_fd,listptr->name,"load_dir 1") == -1) return(NULL);
if ((recdir = opendir(".")) == NULL) {
if (!QUIET)
fprintf(stderr,"%s: Opendir: %s: %s\n",progname,listptr->name,strerror(errno));
return(NULL);
}
while ((filename = readdir(recdir)) != NULL) {
while (!strcmp(filename->d_name,".") || !strcmp(filename->d_name,"..")) {
top=1;
seekdir(recdir,telldir(recdir));
if ((filename = readdir(recdir)) == NULL)
break;
}
if (top) {
listptr = add_right(listptr);
listptr = listptr->right;
/* If directory is emtpy then add a space to listptr */
if (filename == NULL) {
listptr->name = statmalloc(1);
listptr->name[0] = '\0';
listptr->empty = 1;
listptr->type = 0;
listptr->root = 0;
listptr->name_len = 1;
closedir(recdir);
return(listptr);
}
top=0;
} else {
listptr = add_down(listptr);
listptr = listptr->down;
}
/* Add information from filenames->d_name to listptr */
listptr->name = statmalloc(strlen(filename->d_name)+1);
listptr->name[0] = '\0';
strcat(listptr->name,filename->d_name);
listptr->name_len = strlen(listptr->name);
if (lstat(filename->d_name,&statres) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,filename->d_name,strerror(errno));
}
if (S_ISDIR(statres.st_mode))
listptr->type=1;
else
listptr->type=0;
listptr->root = 0;
seekdir(recdir,telldir(recdir));
}
closedir(recdir);
return(listptr);
}
/* Fix it when the directory that we are in dies */
int
get_new_dir(dir_item *listptr, int source_fd, int dest_fd)
{
return(0);
}
/* Recurse Back if at bottom of a directory */
int
recurse_back(dir_item *listptr, int source_fd, int dest_fd)
{
struct utimbuf newtime;
struct stat oldfs;
if (MYfchdir(dest_fd,listptr->name,"recurse_copy 5") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
/* This While will move up a directory until the more items are located
* in listptr or until we hit the root directory */
while (listptr->down == NULL && listptr->root == 0) {
listptr = listptr->left;
if (!FIND) {
/* Delete files or dirs from Dest that have been removed from Source */
if (NEWONLY) {
delete_files(listptr,dest_fd);
if (MYfchdir(dest_fd,listptr->name,"recurse_copy 6") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
}
free_right(listptr);
if (MYchdir("..","recurse_copy 7") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
close(dest_fd);
if ((dest_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
}
if (MYfchdir(source_fd,listptr->name,"recurse_copy 7.3") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
if (MYchdir("..","recurse_copy 7.6") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
close(source_fd);
if ((source_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
}
if (lstat(listptr->name,&oldfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: recurse back: %s: %s\n",progname,listptr->name,strerror(errno));
}
newtime.actime = oldfs.st_atime;
newtime.modtime = oldfs.st_mtime;
if (MYfchdir(dest_fd,listptr->name,"recurse_copy 6") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
/* Set time on file to same as source file.
* This is done here because if we changed the time when
* we did the mkdir the time will reset when we write to
* this directory. */
if (utime(listptr->name,&newtime) == -1) {
if (!QUIET)
fprintf(stderr,"%s: utime: %s: %s\n",progname,listptr->name,strerror(errno));
}
}
}
if (MYfchdir(source_fd,listptr->name,"recurse_copy 5.2") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
TEMP_FD1 = source_fd;
TEMP_FD2 = dest_fd;
TEMP_PTR = listptr;
return(0);
}
/* Copy to Directories */
int
recurse_copy(char *olddir, char *newdir)
{
struct utimbuf newtime;
struct stat oldfs;
struct stat newfs;
dir_item *listptr=NULL;
dir_item *tmpptr = NULL;
int source_fd;
int dest_fd;
int load;
dest_item *destptr=NULL;
char *cur_path_ptr_E;
char *cur_path_ptr_V;
char *MYcwd;
/* Make sure there is no problems accessing the source directory */
if (lstat(olddir,&oldfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,olddir,strerror(errno));
return(-1);
}
/* Initialize 2D Linked List */
listptr = init_2D_list();
listptr->name = statmalloc(strlen(olddir)+((olddir[strlen(olddir)-1] == '/') ? -1 : 0)+1);
listptr->name[0] = '\0';
if (olddir[strlen(olddir)-1] == '/')
strncat(listptr->name,olddir,strlen(olddir)-1);
else
strcat(listptr->name,olddir);
listptr->name_len = strlen(listptr->name);
listptr->type = 1;
listptr->root = 1;
/* Open Source Directory */
if ((source_fd = open(olddir,O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,olddir,strerror(errno));
return(-1);
}
/* Open Destination Directory */
if ((dest_fd = open(newdir,O_RDONLY)) == -1 && !FIND) {
/* If directory does not exists then make one */
if (errno == 2) {
if (UID == 0)
DIR_DEF_MODE = oldfs.st_mode;
else
DIR_DEF_MODE = mode_adjust(oldfs.st_mode,&DIR_MODE);
if (mkdir(newdir,DIR_DEF_MODE) == -1) {
if (!QUIET)
fprintf(stderr,"%s: mkdir: %s: %s\n",progname,newdir,strerror(errno));
close(source_fd);
return(-1);
}
/* Set permissions, ownership and time matching the source dir */
if (UID == 0) {
chown(newdir,oldfs.st_uid,oldfs.st_gid);
DIR_DEF_MODE = oldfs.st_mode;
} else
DIR_DEF_MODE = mode_adjust(oldfs.st_mode,&DIR_MODE);
chmod(newdir,DIR_DEF_MODE);
newtime.actime = oldfs.st_atime;
newtime.modtime = oldfs.st_mtime;
if (utime(newdir,&newtime) == -1) {
if (!QUIET)
fprintf(stderr,"%s: utime: %s: %s\n",progname,newdir,strerror(errno));
}
NEWDIR = 1;
/* Open the new directory */
if ((dest_fd = open(newdir,O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
close(source_fd);
return(-1);
}
} else {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,newdir,strerror(errno));
close(source_fd);
return(-1);
}
}
/* Change CWD to source dir */
if (MYfchdir(source_fd,newdir,"recurse_copy 1") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
load = 1;
/* The BIG Recursive Loop! */
while (listptr != NULL) {
/* When load = 1 then we have hit a directory in the Source structure and need
* to load the directory contents into listptr */
if (load) {
/* If this directory is in the Exclude Buffer, then skip it, and move to the
* next item in listptr */
cur_path_ptr_E = get_cur_path(listptr,'E');
if (EXCLUDE && (strstr(EXCLUDE_BUF,cur_path_ptr_E) != NULL)
&& (strcmp(cur_path_ptr_E,"^^") != 0)) {
if (VERBOSE) {
cur_path_ptr_V = get_cur_path(listptr,'V');
fprintf(stdout,"%s: %s%s: Excluding directory.\n",progname,cur_path_ptr_V,listptr->name);
}
tmpptr = listptr;
listptr = listptr->down;
} else {
tmpptr = listptr;
/* Load directory contents in source dir to listptr */
listptr = load_list(source_fd,listptr);
if (listptr != NULL) {
/* Moves current listptr to top of current dimension */
listptr = listptr->left->right;
/* If the destination directory is not new, then load its contents
* into listptr->left->dir_info. */
if (!NEWDIR && NEWONLY) {
listptr->left->dir_info = load_dest_dir(dest_fd,listptr->left->dir_info);
}
} else {
/* If listptr returns NULL then ignore directory and move up one dir
* and go onto the next item in listptr. This is a fix when using
* smbmounted NT drives, as they currently do not display . or ..
* directories */
listptr = tmpptr;
if (MYchdir("..","recurse_copy 2") == -1) return(-1);
close(source_fd);
if ((source_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
return(-1);
}
if (MYfchdir(dest_fd,listptr->name,"recurse_copy 3") == -1) return(-1);
if (MYchdir("..","recurse_copy 4") == -1) return(-1);
close(dest_fd);
if ((dest_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
return(-1);
}
listptr = listptr->down;
}
}
}
/* Copy if necessary every file in the current directory up until we hit a directory */
while (listptr != NULL && listptr->type == 0) {
/* If directory is not empty then copy */
if (!listptr->empty) {
if (VERBOSE) {
if (!NEWONLY || (NEWONLY && file_newer(listptr,source_fd,dest_fd,'C'))) {
cur_path_ptr_V = get_cur_path(listptr,'V');
printf("%s%s",cur_path_ptr_V,listptr->name);
}
if (FIND) printf("\n");
}
if (!FIND) {
if (NEWONLY && !NEWDIR) {
/* if NEWONLY flag is set and this is not a new directory then copy if
* file in source is newer than file in dest */
if (file_newer(listptr,source_fd,dest_fd,'P')) {
if (rec_file_copy(source_fd,dest_fd,listptr->name) == -1 && VERBOSE)
printf("\n");
}
} else {
if (rec_file_copy(source_fd,dest_fd,listptr->name) == -1 && VERBOSE)
printf("\n");
}
}
}
tmpptr = listptr;
listptr = listptr->down;
}
/* If no more files in dir (listptr == NULL) Then move up a directory */
if (listptr == NULL) {
listptr = tmpptr;
if (recurse_back(listptr,source_fd,dest_fd) == -1) return(-1);
source_fd = TEMP_FD1;
dest_fd = TEMP_FD2;
listptr = TEMP_PTR;
if (!listptr->root) {
listptr = listptr->down;
while (lstat(listptr->name,&oldfs) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat3: %s: Ooops, directory has disappeared!\n",progname,listptr->name);
if (listptr->down != NULL) listptr = listptr->down;
if (recurse_back(listptr,source_fd,dest_fd) == -1) return(-1);
source_fd = TEMP_FD1;
dest_fd = TEMP_FD2;
listptr = TEMP_PTR;
}
load = 0;
} else {
freemalloc(listptr->name);
freemalloc(listptr);
listptr = NULL;
}
} else if (listptr->type == 1) {
/* If we hit a directory while trying to copy files above, the do the following... */
cur_path_ptr_E = get_cur_path(listptr,'E');
if ((EXCLUDE) && (strstr(EXCLUDE_BUF,cur_path_ptr_E) != NULL)) {
} else {
/* move source CWD to new directory and close old File Descriptor and open a new one */
if (MYfchdir(source_fd,listptr->name,"recurse_copy 10") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
if (chdir(listptr->name) == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
close(source_fd);
if ((source_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
return(-1);
}
if (source_fd == 0) {
if ((source_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
return(-1);
}
}
if (lstat(".",&oldfs) == -1) {
MYcwd = MYgetcwd();
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,MYcwd,strerror(errno));
free(MYcwd);
}
if (!FIND ) {
NEWDIR = 0;
if (MYfchdir(dest_fd,listptr->name,"recurse_copy 11") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
/* Make a directory in the Destination directory and set
* the proper permissions, ownership and times. If directory already
* exists then mark file as not deleted */
if (UID == 0)
DIR_DEF_MODE = oldfs.st_mode;
else
DIR_DEF_MODE = mode_adjust(oldfs.st_mode,&DIR_MODE);
/* Fix for when files in source change from a file to a directory which
* fails to match the backup */
if (lstat(listptr->name,&newfs) == 0) {
if (!S_ISDIR(newfs.st_mode))
remove(listptr->name);
}
if (mkdir(listptr->name,DIR_DEF_MODE) == 0) {
/* chmod(listptr->name,oldfs.st_mode); */
if (UID == 0)
chown(listptr->name,oldfs.st_uid,oldfs.st_gid);
newtime.actime = oldfs.st_atime;
newtime.modtime = oldfs.st_mtime;
if (utime(listptr->name,&newtime) == -1) {
if (!QUIET)
fprintf(stderr,"%s: utime: %s: %s\n",progname,listptr->name,strerror(errno));
}
NEWDIR = 1;
if (VERBOSE) {
cur_path_ptr_V = get_cur_path(listptr,'V');
fprintf(stdout,"%s%s -> %s\n",get_cur_path(listptr,'V'),listptr->name,listptr->name);
}
} else if (NEWONLY) {
/* Find directory name in listptr->left->dir_info */
destptr = listptr->left->dir_info;
while (destptr != NULL && strcmp(destptr->name,listptr->name) != 0) {
destptr = destptr->next;
}
/* Mark directory to not delete if dir name was found */
if (destptr == NULL) {
printf("%s: error! Could not find file to mark as not deleted! This should not happen!\n",progname);
} else {
destptr->delete = 0;
}
}
/* Close and open new file descriptor for new Destination directory */
if (MYchdir(listptr->name,"recurse_copy 12") == -1) {
MYcwd = MYgetcwd();
if (!QUIET)
fprintf(stderr,"%s: chdir 12: %s: %s\n",progname,MYcwd,strerror(errno));
free(MYcwd);
close(source_fd);
close(dest_fd);
return(-1);
}
close(dest_fd);
if ((dest_fd = open(".",O_RDONLY)) == -1) {
if (!QUIET)
fprintf(stderr,"%s: open: %s: %s\n",progname,listptr->name,strerror(errno));
}
}
/* Change CWD to source directory */
if (MYfchdir(source_fd,listptr->name,"recurse_copy 13") == -1) {
close(source_fd);
close(dest_fd);
return(-1);
}
}
load = 1;
}
}
close(source_fd);
if (!FIND)
close(dest_fd);
return(1);
}
/* Copy Organizer - Decides how to copy/display the files */
int
copy_files(char **filelist, int args)
{
struct stat fsdest;
struct stat fssource;
int i=0;
int return_code = 1;
int destres;
destres = lstat(filelist[args-1],&fsdest);
/* Check if Source argument is a proper file */
if (lstat(filelist[0],&fssource) == -1) {
if (!QUIET)
fprintf(stderr,"%s: lstat: %s: %s\n",progname,filelist[0],strerror(errno));
return(-1);
}
/* Check if the last argument (if it exists) is a directory. Incase of
* multiple file copy */
if (!S_ISDIR(fsdest.st_mode) && args > 2) {
if (!QUIET)
fprintf(stderr,"%s: Copying multiple files, but last arguent (%s) is not a directory.\n",progname,filelist[args-1]);
return(-1);
}
if (!RECURSIVE) {
/* Copy every <source> file to <destination> */
while (i < args-1) {
return_code = file_copy(filelist[i],filelist[args-1]);
i++;
}
} else {
if (args == 2 || FIND) {
/* Check if source file is a directory. Must be a dir if we are doing a recursive
* copy or find */
if (!S_ISDIR(fssource.st_mode)) {
if (!QUIET)
fprintf(stderr,"%s: %s: Source argument must be a directory.\n",progname,filelist[0]);
return(-1);
}
/* Check if Destination is a directory with only 2 file arguments */
if (destres != -1 && !S_ISDIR(fsdest.st_mode)) {
if (!QUIET)
fprintf(stderr,"%s: %s: Destinatin argument must be a directory.\n",progname,filelist[0]);
return(-1);
}
/* Recursively copy specified directory */
return_code = recurse_copy(filelist[i],filelist[args-1]);
} else if (args > 2) {
if (!QUIET)
fprintf(stderr,"%s: too many arguments\n",progname);
return(-1);
}
}
return(return_code);
}
int
main (int args, char **argv)
{
int ch;
extern char *optarg;
extern int optind, opterr, optopt;
char **filelist=NULL;
int return_code = 1;
int arg_num = 0;
char *p;
/* Grab the progname */
progname = ((p = rindex(argv[0],'/')) ? p+1 : *argv);
/* File Default Mode */
FILE_MODE.op = '+';
FILE_MODE.value = FILE_MODE_ADD;
/* Directory Default Mode */
DIR_MODE.op = '+';
DIR_MODE.value = DIR_MODE_ADD;
/* Special File Default Mode */
SPC_MODE.op = '+';
SPC_MODE.value = SPC_MODE_ADD;
UID = getuid();
if (args < 2) usage();
while ((ch = getopt(args,argv,"vct:rfqE:e:ns:iV")) != EOF) {
switch(ch) {
case 'v':
VERBOSE = 1;
break;
case 'r':
RECURSIVE = 1;
break;
case 'n':
NEWONLY = 1;
break;
case 'f':
FIND = 1;
RECURSIVE = 1;
break;
case 'c':
CONFIG = 1;
arg_num = optind;
break;
case 'e':
exclude_dir(optarg);
break;
case 'E':
exclude_dir(optarg);
SAVEEXCDIR=1;
break;
case 't':
TRASHBIN = 1;
NEWONLY = 1;
trash_dir(optarg);
break;
case 's':
MOD_CHECK = 1;
MOD_CHECK_SEC = atoi(optarg);
break;
case 'i':
INODE_CHECK = 0;
break;
case 'z':
FILESIZE=0;
break;
case 'q':
QUIET=1;
break;
case 'V':
fprintf(stdout,"%s",VERSION);
exit(0);
break;
default:
usage();
break;
}
}
/* If a Trash bin is used, we must exclude that directory from our backup
* so that we don't backup our old backups. :) */
if (TRASHBIN) {
if (!EXCLUDE) {
EXCLUDE_BUF = malloc(strlen(TRASHBINDIR)+3);
EXCLUDE_BUF[0] = '\0';
}
else
EXCLUDE_BUF = realloc(EXCLUDE_BUF,strlen(EXCLUDE_BUF)+strlen(TRASHBINDIR)+3);
strcat(EXCLUDE_BUF,"^");
strcat(EXCLUDE_BUF,TRASHBINDIR);
strcat(EXCLUDE_BUF,"^");
EXCLUDE = 1;
}
/* Set proper Flags */
if (FIND)
NEWONLY = 0;
/* Grab the approprite source and destination arguments */
if (CONFIG)
filelist = parse_config(argv,arg_num);
else
filelist = parse_cmdline(args,argv,optind);
/* Change variable args to the number of arguments in filelist[] */
for (args = 0; filelist[args][0] != '\0'; args++);
/* If there are enough proper arguments or FIND and RECURSIVE are
* set while args is > 0 then copy files */
if ((args) > 1 || (FIND && RECURSIVE && args > 0)) {
umask(0);
return_code = copy_files(filelist,args);
} else {
if (args == 1){
if (!QUIET)
fprintf(stderr,"%s: missing destination argument.\n",progname);
}
if (args == 0) {
if (!QUIET)
fprintf(stderr,"%s: missing source and destination arguments.\n",progname);
}
}
/* printf("Backup Copy Complete: %d\n",return_code); */
return(return_code);
}
syntax highlighted by Code2HTML, v. 0.9.1