diff --git a/csync2.xinetd b/csync2.xinetd index 146d356..842a8f8 100644 --- a/csync2.xinetd +++ b/csync2.xinetd @@ -8,7 +8,7 @@ service csync2 user = root group = root server = /usr/sbin/csync2 - server_args = -i + server_args = -i -l #log_on_failure += USERID disable = no # only_from = 192.168.199.3 192.168.199.4 diff --git a/daemon.c b/daemon.c index 2c054ed..c8ede49 100644 --- a/daemon.c +++ b/daemon.c @@ -103,23 +103,30 @@ void csync_file_flush(const char *filename) url_encode(filename)); } -int csync_file_backup(const char *filename) +int csync_file_backup(const char *filepath) { static char error_buffer[1024]; const struct csync_group *g = NULL; struct stat buf; int rc; - while ( (g=csync_find_next(g, filename)) ) { - if (g->backup_directory && g->backup_generations > 1) { + /* ============================================================================================== + * As of now, filepath may only contain prefixes but we may need to resolve other + * dynamic references like environment variables, symbolic links, etc in future + * if we plan to support those in later releases + *==============================================================================================*/ + char *filename=prefixsubst(filepath); + int filename_len = strlen(filename);//can filename be null? + while ( (g=csync_find_next(g, filepath)) ) { + + if (g->backup_directory && g->backup_generations > 1) { int bak_dir_len = strlen(g->backup_directory); - int filename_len = strlen(filename); char backup_filename[bak_dir_len + filename_len + 10]; char backup_otherfilename[bak_dir_len + filename_len + 10]; int fd_in, fd_out, i; int lastSlash = 0; mode_t mode; - csync_debug(1, "backup\n"); + csync_debug(1, "backup %s \n", filename); // Skip generation of directories rc = stat(filename, &buf); if (S_ISDIR(buf.st_mode)) { @@ -207,7 +214,6 @@ int csync_file_backup(const char *filename) // return 1; } csync_setBackupFileStatus(backup_filename, bak_dir_len); - csync_debug(1, "csync_backup loop end\n"); } } csync_debug(1, "csync_backup end\n"); @@ -428,6 +434,36 @@ void set_peername_from_env(address_t *p, const char *env) freeaddrinfo(result); } +static int setup_tag(char *tag[32], char *line) { + int i = 0; + char *context; + + tag[0] = strtok_r(line, "\t \r\n", &context); + + while ( tag[i] && i < 31 ) { + tag[++i] = strtok_r(NULL, "\t \r\n", &context); + } + while ( i < 32 ) { + tag[i++] = ""; + } + + if ( !tag[0][0] ) { + return 0; + } + + for (i=0; i<32; i++) { + tag[i] = strdup(url_decode(tag[i])); + } + + return 1; +} + +static void destroy_tag(char *tag[32]) { + int i = 0; + for (i=0; i<32; i++) + free(tag[i]); +} + void csync_daemon_session() { struct stat sb; @@ -456,18 +492,10 @@ void csync_daemon_session() while ( conn_gets(line, 4096) ) { int cmdnr; - - tag[i=0] = strtok(line, "\t \r\n"); - while ( tag[i] && i < 31 ) - tag[++i] = strtok(0, "\t \r\n"); - while ( i < 32 ) - tag[i++] = ""; - - if ( !tag[0][0] ) continue; - - for (i=0; i<32; i++) - tag[i] = strdup(url_decode(tag[i])); - + + if (!setup_tag(tag, line)) + continue; + for (cmdnr=0; cmdtab[cmdnr].text; cmdnr++) if ( !strcasecmp(cmdtab[cmdnr].text, tag[0]) ) break; @@ -756,7 +784,6 @@ abort_cmd: conn_printf("OK (cmd_finished).\n"); next_cmd: - for (i=0; i<32; i++) - free(tag[i]); + destroy_tag(tag); } } diff --git a/rsync.c b/rsync.c index 86482ee..25c49cd 100644 --- a/rsync.c +++ b/rsync.c @@ -62,6 +62,54 @@ static size_t strlcpy(char *d, const char *s, size_t bufsize) return ret; } +static void get_parent_child_from_path(char *dirname, char* filename,const char *filepath) { + char *temp; + if ((temp = strrchr(filepath, '/')) != NULL) { + /* copy up to and excluding the slash */ + strlcpy( dirname, filepath, ( (strlen(filepath)-strlen(temp)) + 1) ); + ++temp;//forward one character to skip '/' in the filename + strlcpy( filename, temp, (strlen(temp) + 1) ); + } else { + strlcpy( filename, filepath, (strlen(filepath) + 1) ); + } +} + +static int get_tempdir_name(char * tempdir_name, const char* currentdir_name) +{ + int found=0; int length; + if ( access(currentdir_name, R_OK|W_OK|X_OK) >= 0 ) { + strlcpy(tempdir_name, currentdir_name, strlen(currentdir_name)+ 1); + found=1; + } else { + csync_debug(2,"the directory '%s' seems to have been deleted, hence creating the temp file under 'tempdir'!\n", currentdir_name); + if(csync_tempdir!=NULL) { + if ( access(csync_tempdir, R_OK|W_OK|X_OK) >= 0 ) { + strlcpy(tempdir_name, csync_tempdir, strlen(csync_tempdir)+1); + found=1; + } else { + csync_debug(1,"csync configuration option 'tempdir' is set to %s but that directory does not exit; ensure that it exists and is accessible to the user running 'csync2' process",csync_tempdir); + } + } + if( !found ) { + csync_debug(2,"falling back to system temp dir : %s",P_tmpdir); + if ( access(P_tmpdir, R_OK|W_OK|X_OK) >= 0 ) { + strlcpy(tempdir_name, P_tmpdir, strlen(P_tmpdir)+1); + found=1; + } else { + csync_debug(0,"could not find a usable temporary directory"); + } + } + } + if(found) { + length=strlen(tempdir_name); + if(length= maxname) - added = maxname - 1; - suf = fnametmp + length + added; - + added = maxname - 1; + suffix = tempfile_name + length + added; if (!counter_limit) { counter_limit = (unsigned)getpid() + MAX_UNIQUE_LOOP; if (counter_limit > MAX_UNIQUE_NUMBER || counter_limit < MAX_UNIQUE_LOOP) counter_limit = MAX_UNIQUE_LOOP; counter = counter_limit - MAX_UNIQUE_LOOP; - /* This doesn't have to be very good because we don't need - * to worry about someone trying to guess the values: all - * a conflict will do is cause a device, special file, hard - * link, or symlink to fail to be created. Also: avoid - * using mktemp() due to gcc's annoying warning. */ + * to worry about someone trying to guess the values: all + * a conflict will do is cause a device, special file, hard + * link, or symlink to fail to be created. Also: avoid + * using mktemp() due to gcc's annoying warning. */ while (1) { - snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter); - if (access(fnametmp, 0) < 0) + snprintf(suffix, TMPNAME_SUFFIX_LEN+1, ".%d", counter); + if (access(tempfile_name, 0) < 0) break; if (++counter >= counter_limit) return 0; } - } else - memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1); - + } else { + //memcpy(suffix, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1); + snprintf(suffix, TMPNAME_SUFFIX_LEN+1, ".%d", counter_limit); + } return 1; } +/* + * Recursively creates the given path, with the given mode + * Note that path argument is not directory name here but rather + * a path to a file that you are going to create after calling mkpath(). + * Works with relative paths as well. + * Shamelessly copied from + * Stackoverlow.com#http://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix + * Returns: 0 on success and -1 on error + */ + +static int mkpath(const char *path, mode_t mode) { + char temp[MAXPATHLEN]; + char *remaining; + + if(!mode) { + mode=S_IRWXU; + } + if(!path){ + csync_debug(2,"invalid path"); + return -1; + } + + strlcpy(temp,path,strlen(path)); + csync_debug(0,"full path : %s",temp); + for( remaining=strchr(temp+1, '/'); remaining!=NULL; remaining=strchr(remaining+1, '/') ){ + *remaining='\0'; + if(mkdir(temp, mode)==-1) { //strchr keeps the parent in temp and child[ren] in remaining + if(errno != EEXIST) { + *remaining='/'; + csync_debug(1,"error occured while creating path %s; cause : %s",temp,strerror(errno)); + return -1; + } + } + csync_debug(0,"parent dir : %s",temp); + *remaining='/'; + } + return 0; +} /* Returns open file handle for a temp file that resides in the - same directory as file fname. The file must be removed after + same directory as file filepath. The file must be removed after usage. */ -static FILE *open_temp_file(char *fnametmp, const char *fname) +#ifdef _SVID_SOURCE +static FILE *paranoid_tempfile(const char *filepath) { FILE *f; - int fd; - - if (get_tmpname(fnametmp, fname) == 0) { - csync_debug(1, "ERROR: Couldn't find tempname for file %s\n", fname); - return NULL; + int fd; int tempdir_length; int filename_length; + char template[MAXPATHLEN]; + char filename[MAXPATHLEN]; + char currentdir_name[MAXPATHLEN]; + char tempdir_name[MAXPATHLEN]; + + get_parent_child_from_path(currentdir_name,filename,filepath); + + if(get_tempdir_name(tempdir_name,currentdir_name)>0){ + tempdir_length=strlen(tempdir_name); + filename_length=strlen(filename); + csync_debug(3,"creating paranoid tempfile (SYSV) ==> tempdir_name : %s ; filename : %s",tempdir_name,filename); + + if( tempdir_length!=0 && filename_length!=0 && (tempdir_length < ( MAXPATHLEN-filename_length+20) ) ) { + snprintf(template,MAXPATHLEN,"%s.csync2_%s_XXXXXX",tempdir_name,filename); + } else { + csync_debug(2,"could not create tempfile for %s under the tempdir %s because of MAXPATHLEN restiction; leaving upto the system for deciding where to create the tempfile",filepath,tempdir_name); + strcpy(template,".csync2_XXXXXX"); + } + } else { + csync_debug(2,"could not find a usable tempdir to create tempfile for file %s; leaving upto the system for deciding where to create the tempfile",filepath); + strcpy(template,".csync2_XXXXXX"); } - - f = NULL; - fd = open(fnametmp, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR); + + fd = mkstemp(template); + unlink(template); + + if (fd < 0 || f==NULL) { + csync_fatal("ERROR: Could not create temporary file using mkstemp(%s)!\n", template); + } + //fd = open(name, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR); */ if (fd >= 0) { f = fdopen(fd, "wb+"); - /* not unlinking since rename wouldn't work then */ - } - if (fd < 0 || !f) { - csync_debug(1, "ERROR: Could not open result from tempnam(%s)!\n", fnametmp); - return NULL; } - + csync_debug(3, "Tempfilename is %s\n", template); return f; } - - - -#ifdef _SVID_SOURCE -static FILE *paranoid_tmpfile() +#else +static FILE *paranoid_tempfile(const char *filepath) { - char *name; FILE *f; - int fd; - - name = tempnam(csync_tempdir, "csync2"); - if (!name) - csync_fatal("ERROR: tempnam() didn't return a valid filename!\n"); - - f = NULL; - fd = open(name, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR); - if (fd >= 0) { - f = fdopen(fd, "wb+"); - unlink(name); + char filename[MAXPATHLEN]; + char tempdir_name[MAXPATHLEN]: + get_parent_child_from_path(tempdir_name,filename,filepath); + csync_debug(3,"creating paranoid tempfile ==> tempdir_name : %s ; filename : %s",tempdir_name,filename); + if ( !(f = tempnam(tempdir_name,filename)) ) { + csync_fatal("ERROR: tempnam(%s,%s) didn't return a valid file handle!\n",tempdir_name,filename); } - if (fd < 0 || !f) - csync_fatal("ERROR: Could not open result from tempnam(%s)!\n", name); - - csync_debug(3, "Tempfilename is %s\n", name); - free(name); return f; } -#else -static FILE *paranoid_tmpfile() +#endif + +/*==================================================================================================== + * @param filepath prefixes must be resolved before passing in + *====================================================================================================*/ +static FILE *open_temp_file(char *tempfile_name, const char *filepath, int paranoid) { FILE *f; - - if ( access(P_tmpdir, R_OK|W_OK|X_OK) < 0 ) - csync_fatal("Temp directory '%s' does not exist!\n", P_tmpdir); - - if ( !(f = tmpfile()) ) - csync_fatal("ERROR: tmpfile() didn't return a valid file handle!\n"); - + int fd; + + if(paranoid) { + f=paranoid_tempfile(filepath); + } else { + if (get_tempfile_name(tempfile_name, filepath) == 0) { + csync_debug(1, "ERROR: Couldn't find tempname for file %s\n", filepath); + return NULL; + } + fd = open(tempfile_name, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR); + if (fd >= 0) { + f = fdopen(fd, "wb+"); + /* not unlinking since rename wouldn't work then */ + } + } + if (fd < 0 || !f) { + csync_debug(1, "ERROR: Could not open result from tempnam(%s)!\n", tempfile_name); + return NULL; + } return f; } -#endif void csync_send_file(FILE *in) { @@ -259,7 +361,7 @@ int csync_recv_file(FILE *out) char buffer[512]; int rc, chunk; long size; - + if ( !conn_gets(buffer, 100) || sscanf(buffer, "octet-stream %ld\n", &size) != 1 ) { if (!strcmp(buffer, "ERROR\n")) { errno=EIO; return -1; } csync_fatal("Format-error while receiving data.\n"); @@ -274,7 +376,7 @@ int csync_recv_file(FILE *out) if ( rc <= 0 ) csync_fatal("Read-error while receiving data.\n"); chunk = rc; - + rc = fwrite(buffer, chunk, 1, out); if ( rc != 1 ) csync_fatal("Write-error while receiving data.\n"); @@ -305,28 +407,26 @@ int csync_rs_check(const char *filename, int isreg) csync_debug(3, "Opening basis_file and sig_file..\n"); - sig_file = open_temp_file(tmpfname, prefixsubst(filename)); + sig_file = open_temp_file(tmpfname, prefixsubst(filename),1); + //sig_file = paranoid_tmpfile(); if ( !sig_file ) goto io_error; - if (unlink(tmpfname) < 0) goto io_error; - - basis_file = fopen(prefixsubst(filename), "rb"); - if ( !basis_file ) { /* ?? why a tmp file? */ - basis_file = open_temp_file(tmpfname, prefixsubst(filename)); - if ( !basis_file ) goto io_error; - if (unlink(tmpfname) < 0) goto io_error; - } + unlink(tmpfname); if ( isreg ) { + basis_file = fopen(prefixsubst(filename), "rb"); + if ( !basis_file ) { + basis_file = fopen("/dev/null", "rb"); + } csync_debug(3, "Running rs_sig_file() from librsync....\n"); + if (basis_file) result = rs_sig_file(basis_file, sig_file, RS_DEFAULT_BLOCK_LEN, RS_DEFAULT_STRONG_LEN, &stats); if (result != RS_DONE) { csync_debug(0, "Internal error from rsync library!\n"); goto error; } + fclose(basis_file); } - - fclose(basis_file); basis_file = 0; { @@ -336,14 +436,18 @@ int csync_rs_check(const char *filename, int isreg) csync_fatal("Format-error while receiving data.\n"); } - fflush(sig_file); - if ( size != ftell(sig_file) ) { - csync_debug(2, "Signature size differs: local=%d, peer=%d\n", - ftell(sig_file), size); - found_diff = 1; + if (sig_file) { + fflush(sig_file); + if ( size != ftell(sig_file) ) { + csync_debug(2, "Signature size differs: local=%d, peer=%d\n",ftell(sig_file), size); + found_diff = 1; + } + rewind(sig_file); + } + else { + csync_debug(2, "Signature size differs: local don't exist, peer=%d\n", size); + found_diff = 1; } - rewind(sig_file); - csync_debug(3, "Receiving %ld bytes ..\n", size); while ( size > 0 ) { @@ -354,16 +458,17 @@ int csync_rs_check(const char *filename, int isreg) csync_fatal("Read-error while receiving data.\n"); chunk = rc; - if ( fread(buffer2, chunk, 1, sig_file) != 1 ) { - csync_debug(2, "Found EOF in local sig file.\n"); - found_diff = 1; - } - if ( memcmp(buffer1, buffer2, chunk) ) { - csync_debug(2, "Found diff in sig at -%d:-%d\n", - size, size-chunk); - found_diff = 1; + if (sig_file) { + if ( fread(buffer2, chunk, 1, sig_file) != 1 ) { + csync_debug(2, "Found EOF in local sig file.\n"); + found_diff = 1; + } + if ( memcmp(buffer1, buffer2, chunk) ) { + csync_debug(2, "Found diff in sig at -%d:-%d\n", + size, size-chunk); + found_diff = 1; + } } - size -= chunk; csync_debug(3, "Got %d bytes, %ld bytes left ..\n", chunk, size); @@ -371,7 +476,8 @@ int csync_rs_check(const char *filename, int isreg) csync_debug(3, "File has been checked successfully (%s).\n", found_diff ? "difference found" : "files are equal"); - fclose(sig_file); + if (sig_file) + fclose(sig_file); return found_diff; io_error: @@ -397,9 +503,11 @@ void csync_rs_sig(const char *filename) csync_debug(3, "Opening basis_file and sig_file..\n"); - sig_file = open_temp_file(tmpfname, prefixsubst(filename)); - if ( !sig_file ) goto io_error; - if (unlink(tmpfname) < 0) goto io_error; + sig_file = open_temp_file(tmpfname, prefixsubst(filename),0); + if ( !sig_file ) + goto io_error; + if (unlink(tmpfname) < 0) + goto io_error; basis_file = fopen(prefixsubst(filename), "rb"); if ( !basis_file ) basis_file = fopen("/dev/null", "rb"); @@ -440,7 +548,7 @@ int csync_rs_delta(const char *filename) csync_debug(3, "Csync2 / Librsync: csync_rs_delta('%s')\n", filename); csync_debug(3, "Receiving sig_file from peer..\n"); - sig_file = open_temp_file(tmpfname, prefixsubst(filename)); + sig_file = open_temp_file(tmpfname, prefixsubst(filename),0); if ( !sig_file ) goto io_error; if (unlink(tmpfname) < 0) goto io_error; @@ -465,7 +573,7 @@ int csync_rs_delta(const char *filename) return -1; } - delta_file = open_temp_file(tmpfname, prefixsubst(filename)); + delta_file = open_temp_file(tmpfname, prefixsubst(filename),0); if ( !delta_file ) goto io_error; if (unlink(tmpfname) < 0) goto io_error; @@ -500,33 +608,47 @@ io_error: return -1; } -int csync_rs_patch(const char *filename) +int csync_rs_patch(const char *filepath) { FILE *basis_file = 0, *delta_file = 0, *new_file = 0; int backup_errno; rs_stats_t stats; rs_result result; char *errstr = "?"; + struct stat sb; + char filename[MAXPATHLEN]; + char dirname[MAXPATHLEN]; char tmpfname[MAXPATHLEN], newfname[MAXPATHLEN]; - csync_debug(3, "Csync2 / Librsync: csync_rs_patch('%s')\n", filename); + csync_debug(3, "Csync2 / Librsync: csync_rs_patch('%s')\n", filepath); csync_debug(3, "Receiving delta_file from peer..\n"); - delta_file = open_temp_file(tmpfname, prefixsubst(filename)); + + get_parent_child_from_path(dirname,filename,prefixsubst(filepath)); + + if (stat (dirname, &sb) != 0) { + csync_debug(2,"one or more subdirectories in path %s seems to have deleted",filepath); + if(mkpath(prefixsubst(filepath),0755)!=0) { + csync_debug(1,"failed to create path %s",dirname); + goto io_error; + } + } + + delta_file = open_temp_file(tmpfname, prefixsubst(filepath),0); if ( !delta_file ) { errstr="creating delta temp file"; goto io_error; } if (unlink(tmpfname) < 0) { errstr="removing delta temp file"; goto io_error; } if ( csync_recv_file(delta_file) ) goto error; csync_debug(3, "Opening to be patched file on local host..\n"); - basis_file = fopen(prefixsubst(filename), "rb"); + basis_file = fopen(prefixsubst(filepath), "rb"); if ( !basis_file ) { - basis_file = open_temp_file(tmpfname, prefixsubst(filename)); + basis_file = open_temp_file(tmpfname, prefixsubst(filepath),0); if ( !basis_file ) { errstr="opening data file for reading"; goto io_error; } if (unlink(tmpfname) < 0) { errstr="removing data temp file"; goto io_error; } } csync_debug(3, "Opening temp file for new data on local host..\n"); - new_file = open_temp_file(newfname, prefixsubst(filename)); + new_file = open_temp_file(newfname, prefixsubst(filepath),0); if ( !new_file ) { errstr="creating new data temp file"; goto io_error; } csync_debug(3, "Running rs_patch_file() from librsync..\n"); @@ -549,7 +671,7 @@ int csync_rs_patch(const char *filename) char winfilename[MAX_PATH]; HANDLE winfh; - cygwin_conv_to_win32_path(prefixsubst(filename), winfilename); + cygwin_conv_to_win32_path(prefixsubst(filepath), winfilename); winfh = CreateFile(TEXT(winfilename), GENERIC_WRITE, // open for writing @@ -570,7 +692,7 @@ int csync_rs_patch(const char *filename) } #endif - if (rename(newfname, prefixsubst(filename)) < 0) { errstr="renaming tmp file to to be patched file"; goto io_error; } + if (rename(newfname, prefixsubst(filepath)) < 0) { errstr="renaming tmp file to to be patched file"; goto io_error; } csync_debug(3, "File has been patched successfully.\n"); fclose(delta_file); @@ -580,7 +702,7 @@ int csync_rs_patch(const char *filename) io_error: csync_debug(0, "I/O Error '%s' while %s in rsync-patch: %s\n", - strerror(errno), errstr, prefixsubst(filename)); + strerror(errno), errstr, prefixsubst(filepath)); error:; backup_errno = errno;