diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3cf96ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +.deps/ +.dirstamp + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.cache +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile + + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Project specific +*.swp +*.swo +cfgfile_parser.c +cfgfile_parser.h +cfgfile_scanner.c +config.h +csync2 diff --git a/check.c b/check.c index 1477314..f9dc856 100644 --- a/check.c +++ b/check.c @@ -98,7 +98,13 @@ void csync_mark(const char *file, const char *thispeer, const char *peerfilter) } csync_debug(1, "Marking file as dirty: %s\n", file); - for (pl_idx=0; pl[pl_idx].peername; pl_idx++) + for (pl_idx=0; pl[pl_idx].peername; pl_idx++) { + // In case of -P flag, don't mark files as dirty + if (active_peerlist && !strstr(active_peerlist, pl[pl_idx].peername)) { + csync_debug(1, "Not marking host %s as dirty because -P flag was specified\n", pl[pl_idx].peername); + continue; + } + if (!peerfilter || !strcmp(peerfilter, pl[pl_idx].peername)) { SQL("Deleting old dirty file entries", "DELETE FROM dirty WHERE filename = '%s' AND peername = '%s'", @@ -113,6 +119,7 @@ void csync_mark(const char *file, const char *thispeer, const char *peerfilter) url_encode(pl[pl_idx].myname), url_encode(pl[pl_idx].peername)); } + } free(pl); } diff --git a/checktxt.c b/checktxt.c index 9a522d5..3baf816 100644 --- a/checktxt.c +++ b/checktxt.c @@ -19,6 +19,7 @@ */ #include "csync2.h" +#include #include #include #include @@ -48,8 +49,10 @@ const char *csync_genchecktxt(const struct stat *st, const char *filename, int i /* version 1 of this check text */ xxprintf("v1"); - if ( !S_ISLNK(st->st_mode) && !S_ISDIR(st->st_mode) ) - xxprintf(":mtime=%lld", ign_mtime ? (long long)0 : (long long)st->st_mtime); + if ( !S_ISLNK(st->st_mode) && !S_ISDIR(st->st_mode) ) { + int64_t timestamp = st->st_mtime * 1000000000 + st->st_mtim.tv_nsec; + xxprintf(":mtime=%lld", ign_mtime ? (long long)0 : (long long)timestamp); + } if ( !csync_ignore_mod ) xxprintf(":mode=%d", (int)st->st_mode); diff --git a/csync2.c b/csync2.c index 8eac868..678bdb9 100644 --- a/csync2.c +++ b/csync2.c @@ -67,6 +67,7 @@ int csync_error_count = 0; int csync_debug_level = 0; FILE *csync_debug_out = 0; int csync_syslog = 0; +int csync_atomic_patch = 1; //TODO - make an inverse flag. int csync_server_child_pid = 0; int csync_timestamps = 0; @@ -436,7 +437,7 @@ int main(int argc, char ** argv) return 1; } - while ( (opt = getopt(argc, argv, "W:s:Ftp:G:P:C:D:N:HBAIXULlSTMRvhcuoimfxrd")) != -1 ) { + while ( (opt = getopt(argc, argv, "W:s:Ftp:G:P:C:D:N:O::HBAIXULlSTMRavhcuoimfxrd")) != -1 ) { switch (opt) { case 'W': @@ -451,6 +452,15 @@ int main(int argc, char ** argv) csync_fatal("Can't open timestanp file `%s': %s\n", optarg, strerror(errno)); break; + case 'O': + { + char *logname = optarg ? optarg : "/tmp/csync2_full_log.log"; + if((debug_file = fopen(logname, "w+")) == NULL) { + fprintf(stderr, "Could not open full log file: %s\n", logname); + exit(1); + } + } + break; case 'F': csync_new_force = 1; break; @@ -463,6 +473,9 @@ int main(int argc, char ** argv) case 'G': active_grouplist = optarg; break; + case 'a': + csync_atomic_patch = 1; + break; case 'P': active_peerlist = optarg; break; @@ -684,9 +697,57 @@ int main(int argc, char ** argv) if (!csync_database || !csync_database[0] || csync_database[0] == '/') csync_database = db_default_database(csync_database); + + + // If local hostname is not set, try to guess it by getting the addrinfo of every hostname in the + // group and try to bind on that address. If bind is successful set that host as local hostname. + { + struct csync_group *g; + struct csync_group_host *h; + struct csync_group_host *prev = 0; + struct addrinfo *rp, *result; + int sfd; + int bind_status; + + for (g=csync_group; g; g=g->next) { + if ( !g->myname ) { + h = g->host; + while(h && !g->myname) { + getaddrinfo(h->hostname, NULL, NULL, &result); + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + bind_status = bind(sfd, rp->ai_addr, rp->ai_addrlen); + close(sfd); + + if (bind_status == 0) { + g->myname = h->hostname; + snprintf(myhostname, 256, "%s", h->hostname); + g->local_slave = h->slave; + + if (!prev) { + g->host = h->next; + } else { + prev->next = h->next; + } + free(h); + csync_debug(1, "My hostname guessed as: %s\n", g->myname); + break; + } + } + freeaddrinfo(result); + prev = h; + h = h->next; + } + } + } + } + csync_debug(2, "My hostname is %s.\n", myhostname); csync_debug(2, "Database-File: %s\n", csync_database); + { const struct csync_group *g; for (g=csync_group; g; g=g->next) diff --git a/csync2.h b/csync2.h index 840565e..146a0cd 100644 --- a/csync2.h +++ b/csync2.h @@ -78,6 +78,7 @@ extern int csync_perm(const char *filename, const char *key, const char *hostnam /* error.c */ +extern FILE* debug_file; extern void csync_printtime(); extern void csync_printtotaltime(); extern void csync_fatal(const char *fmt, ...); @@ -233,7 +234,7 @@ extern int db_sync_mode; extern int csync_rs_check(const char *filename, int isreg); extern void csync_rs_sig(const char *filename); extern int csync_rs_delta(const char *filename); -extern int csync_rs_patch(const char *filename); +extern int csync_rs_patch(const char *filename, struct stat *atomic_stats); extern int mkpath(const char *path, mode_t mode); extern void split_dirname_basename(char *dirname, char* basename, const char *filepath); @@ -435,6 +436,7 @@ extern int csync_messages_printed; extern int csync_server_child_pid; extern int csync_timestamps; extern int csync_new_force; +extern int csync_atomic_patch; extern char myhostname[]; extern int bind_to_myhostname; diff --git a/daemon.c b/daemon.c index 7fb53bb..05149f5 100644 --- a/daemon.c +++ b/daemon.c @@ -290,38 +290,39 @@ struct csync_command { }; enum { - A_SIG, A_FLUSH, A_MARK, A_TYPE, A_GETTM, A_GETSZ, A_DEL, A_PATCH, - A_MKDIR, A_MKCHR, A_MKBLK, A_MKFIFO, A_MKLINK, A_MKSOCK, + A_SIG, A_FLUSH, A_MARK, A_TYPE, A_GETTM, A_GETSZ, A_DEL, A_ATOMIC, + A_PATCH, A_MKDIR, A_MKCHR, A_MKBLK, A_MKFIFO, A_MKLINK, A_MKSOCK, A_SETOWN, A_SETMOD, A_SETIME, A_LIST, A_GROUP, A_DEBUG, A_HELLO, A_BYE }; struct csync_command cmdtab[] = { - { "sig", 1, 0, 0, 0, 1, A_SIG }, - { "mark", 1, 0, 0, 0, 1, A_MARK }, - { "type", 2, 0, 0, 0, 1, A_TYPE }, - { "gettm", 1, 0, 0, 0, 1, A_GETTM }, - { "getsz", 1, 0, 0, 0, 1, A_GETSZ }, - { "flush", 1, 1, 0, 0, 1, A_FLUSH }, - { "del", 1, 1, 0, 1, 1, A_DEL }, - { "patch", 1, 1, 2, 1, 1, A_PATCH }, - { "mkdir", 1, 1, 1, 1, 1, A_MKDIR }, - { "mkchr", 1, 1, 1, 1, 1, A_MKCHR }, - { "mkblk", 1, 1, 1, 1, 1, A_MKBLK }, - { "mkfifo", 1, 1, 1, 1, 1, A_MKFIFO }, - { "mklink", 1, 1, 1, 1, 1, A_MKLINK }, - { "mksock", 1, 1, 1, 1, 1, A_MKSOCK }, - { "setown", 1, 1, 0, 2, 1, A_SETOWN }, - { "setmod", 1, 1, 0, 2, 1, A_SETMOD }, - { "setime", 1, 0, 0, 2, 1, A_SETIME }, - { "list", 0, 0, 0, 0, 1, A_LIST }, + { "sig", 1, 0, 0, 0, 1, A_SIG }, + { "mark", 1, 0, 0, 0, 1, A_MARK }, + { "type", 2, 0, 0, 0, 1, A_TYPE }, + { "gettm", 1, 0, 0, 0, 1, A_GETTM }, + { "getsz", 1, 0, 0, 0, 1, A_GETSZ }, + { "flush", 1, 1, 0, 0, 1, A_FLUSH }, + { "del", 1, 1, 0, 1, 1, A_DEL }, + { "atomicpatch", 1, 1, 2, 1, 1, A_ATOMIC }, + { "patch", 1, 1, 2, 1, 1, A_PATCH }, + { "mkdir", 1, 1, 1, 1, 1, A_MKDIR }, + { "mkchr", 1, 1, 1, 1, 1, A_MKCHR }, + { "mkblk", 1, 1, 1, 1, 1, A_MKBLK }, + { "mkfifo", 1, 1, 1, 1, 1, A_MKFIFO }, + { "mklink", 1, 1, 1, 1, 1, A_MKLINK }, + { "mksock", 1, 1, 1, 1, 1, A_MKSOCK }, + { "setown", 1, 1, 0, 2, 1, A_SETOWN }, + { "setmod", 1, 1, 0, 2, 1, A_SETMOD }, + { "setime", 1, 0, 0, 2, 1, A_SETIME }, + { "list", 0, 0, 0, 0, 1, A_LIST }, #if 0 - { "debug", 0, 0, 0, 0, 1, A_DEBUG }, + { "debug", 0, 0, 0, 0, 1, A_DEBUG }, #endif - { "group", 0, 0, 0, 0, 0, A_GROUP }, - { "hello", 0, 0, 0, 0, 0, A_HELLO }, - { "bye", 0, 0, 0, 0, 0, A_BYE }, - { 0, 0, 0, 0, 0, 0, 0 } + { "group", 0, 0, 0, 0, 0, A_GROUP }, + { "hello", 0, 0, 0, 0, 0, A_HELLO }, + { "bye", 0, 0, 0, 0, 0, A_BYE }, + { 0, 0, 0, 0, 0, 0, 0 } }; typedef union address { @@ -467,12 +468,39 @@ static int setup_tag(char *tag[32], char *line) return 1; } +void csync_dir_update(char* tag[]) { + const char *directory = prefixsubst(tag[2]); + uid_t uid = atoll(tag[3]); + gid_t gid = atoll(tag[4]); + + if (mkdir(directory, atoll(tag[5]) )) return; + + if (!csync_atomic_patch) return; + + + if (chown(directory, uid, gid)) + csync_debug(3, "Error '%s' for chown(%s,%d,%d)\n", + strerror(errno), directory, uid, gid); + + struct timespec tsp[2]; + long long timestamp = atoll(tag[6]); + tsp[0].tv_sec = tsp[1].tv_sec = (int) (timestamp / 1000000000); + tsp[0].tv_nsec = tsp[1].tv_nsec = timestamp % 1000000000; + + if(utimensat(0, directory, tsp, 0)) + csync_debug(3, "Error '%s' for utimensat\n", + strerror(errno), directory); + + +} + static void destroy_tag(char *tag[32]) { int i = 0; for (i = 0; i < 32; i++) free(tag[i]); } + void csync_daemon_session() { static char line[4 * 4096]; @@ -481,6 +509,7 @@ void csync_daemon_session() socklen_t peerlen = sizeof(peername); char *peer=0, *tag[32]; int i; + struct stat atomic_stats; if (fstat(0, &sb)) @@ -506,9 +535,20 @@ void csync_daemon_session() if (!setup_tag(tag, line)) continue; + for (cmdnr=0; cmdtab[cmdnr].text; cmdnr++) if ( !strcasecmp(cmdtab[cmdnr].text, tag[0]) ) break; + // Print command and its arguments fully + csync_debug(1, "START COMMAND -> %s\n", tag[0]); + for (i = 1; i < 32; i++){ + if (!(*tag[i])) + break; + + csync_debug(1, "[Arg %d] -> %s\n", i, tag[i]); + } + csync_debug(1, "FINISH COMMAND -> %s\n", tag[0]); + if ( !cmdtab[cmdnr].text ) { cmd_error = conn_response(CR_ERR_UNKNOWN_COMMAND); goto abort_cmd; @@ -628,11 +668,32 @@ void csync_daemon_session() if (!csync_file_backup(tag[2])) csync_unlink(tag[2], 0); break; + case A_ATOMIC: + if (!csync_file_backup(tag[2])) { + conn_resp(CR_OK_SEND_DATA); + csync_rs_sig(tag[2]); + + memset(&atomic_stats, 0, sizeof(atomic_stats)); + atomic_stats.st_uid = atoll(tag[3]); + atomic_stats.st_gid = atoll(tag[4]); + atomic_stats.st_mode = atoll(tag[5]); + long long timestamp = atoll(tag[6]); + struct timespec tsp; + tsp.tv_sec = (int) (timestamp / 1000000000); + tsp.tv_nsec = timestamp % 1000000000; + atomic_stats.st_mtim = tsp; + + + if (csync_rs_patch(tag[2], &atomic_stats)) + cmd_error = strerror(errno); + } + break; case A_PATCH: if (!csync_file_backup(tag[2])) { conn_resp(CR_OK_SEND_DATA); csync_rs_sig(tag[2]); - if (csync_rs_patch(tag[2])) + + if (csync_rs_patch(tag[2], NULL)) cmd_error = strerror(errno); } break; @@ -660,11 +721,11 @@ void csync_daemon_session() } } #else - if ( mkdir(prefixsubst(tag[2]), 0700) ) { - struct stat st; - if ( lstat_strict((prefixsubst(tag[2])), &st) != 0 || !S_ISDIR(st.st_mode)) - cmd_error = strerror(errno); - } + csync_dir_update(tag); + + struct stat st; + if ( lstat_strict((prefixsubst(tag[2])), &st) != 0 || !S_ISDIR(st.st_mode)) + cmd_error = strerror(errno); #endif break; case A_MKCHR: @@ -702,11 +763,13 @@ void csync_daemon_session() break; case A_SETIME: { - struct utimbuf utb; - utb.actime = atoll(tag[3]); - utb.modtime = atoll(tag[3]); - if ( utime(prefixsubst(tag[2]), &utb) ) + struct timespec tsp[2]; + long long timestamp = atoll(tag[3]); + tsp[0].tv_sec = tsp[1].tv_sec = (int) (timestamp / 1000000000); + tsp[0].tv_nsec = tsp[1].tv_nsec = timestamp % 1000000000; + if(utimensat(0, prefixsubst(tag[2]), tsp, 0)) cmd_error = strerror(errno); + } break; case A_LIST: @@ -805,3 +868,4 @@ found_asactive: ; destroy_tag(tag); } } + diff --git a/error.c b/error.c index 59b654f..b3dc880 100644 --- a/error.c +++ b/error.c @@ -34,6 +34,8 @@ int csync_messages_printed = 0; time_t csync_startup_time = 0; +FILE *debug_file; + void csync_printtime() { if (csync_timestamps || csync_timestamp_out) @@ -102,6 +104,13 @@ static int csync_log_level_to_sys_log_level(int lv) void csync_vdebug(int lv, const char *fmt, va_list ap) { + + va_list debug_file_va; + if (debug_file) { + va_copy(debug_file_va, ap); + vfprintf(debug_file, fmt, debug_file_va); + } + if (csync_debug_level < lv) return; diff --git a/rsync.c b/rsync.c index 8317274..e985254 100644 --- a/rsync.c +++ b/rsync.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -704,12 +705,18 @@ int csync_rs_delta(const char *filename) * mtime, ACLs and other meta data as context information before starting to * act on it on the receiving side, I don't see how. */ -static void clone_ownership_and_permissions(const char *newfname, const char *oldfname) +static void clone_ownership_and_permissions(const char *newfname, const char *oldfname, struct stat *atomic_stats) { struct stat sbuf; int uid, gid; - if (stat(oldfname, &sbuf)) - return; /* At least we tried */ + + if (atomic_stats) { + sbuf = *atomic_stats; + } else { + if (stat(oldfname, &sbuf)) + return; /* At least we tried */ + } + uid = csync_ignore_uid ? -1 : sbuf.st_uid; gid = csync_ignore_gid ? -1 : sbuf.st_gid; csync_debug(3, "Cloning ownership and permissions to tmp file: 0o%03o %d:%d %s [%s]\n", @@ -725,7 +732,7 @@ static void clone_ownership_and_permissions(const char *newfname, const char *ol * as long as csync2 is no acl aware, there is no point, though */ } -int csync_rs_patch(const char *filename) +int csync_rs_patch(const char *filename, struct stat *atomic_stats) { FILE *basis_file = 0, *delta_file = 0, *new_file = 0; int backup_errno; @@ -797,7 +804,18 @@ int csync_rs_patch(const char *filename) } #endif - clone_ownership_and_permissions(newfname, prefixsubst(filename)); + clone_ownership_and_permissions(newfname, prefixsubst(filename), atomic_stats); + + // Set modification time + fflush(new_file); + if (atomic_stats) { + struct timespec tsp[2]; + tsp[0].tv_sec = tsp[1].tv_sec = atomic_stats->st_mtim.tv_sec; + tsp[0].tv_nsec = tsp[1].tv_nsec = atomic_stats->st_mtim.tv_nsec; + if(utimensat(0, newfname, tsp, 0)) + csync_debug(1, "Could not change the modification date\n"); + + } if (rename(newfname, prefixsubst(filename))) { char buffer[512]; diff --git a/update.c b/update.c index 306a5de..52584ff 100644 --- a/update.c +++ b/update.c @@ -316,6 +316,8 @@ enum connection_response csync_update_file_mod(const char *peername, goto got_error; } + long long nano_timestamp = st.st_mtime * 1000000000 + st.st_mtim.tv_nsec; + if ( force ) { if ( dry_run ) { printf("!M: %-15s %s\n", peername, filename); @@ -378,8 +380,16 @@ enum connection_response csync_update_file_mod(const char *peername, } if ( S_ISREG(st.st_mode) ) { - conn_printf("PATCH %s %s\n", - url_encode(key), url_encode(filename)); + if (csync_atomic_patch) { + conn_printf("ATOMICPATCH %s %s %d %d %d %lld\n", + url_encode(key), url_encode(filename), + st.st_uid, st.st_gid, + st.st_mode, + nano_timestamp); + } else { + conn_printf("PATCH %s %s\n", + url_encode(key), url_encode(filename)); + } last_conn_status = read_conn_status(filename, peername); /* FIXME be more specific? * (last_conn_status != CR_OK_SEND_DATA) ?? @@ -397,8 +407,16 @@ enum connection_response csync_update_file_mod(const char *peername, goto got_error; } else if ( S_ISDIR(st.st_mode) ) { - conn_printf("MKDIR %s %s\n", - url_encode(key), url_encode(filename)); + if (csync_atomic_patch) { + conn_printf("MKDIR %s %s %d %d %d %lld\n", + url_encode(key), url_encode(filename), + st.st_uid, st.st_gid, + st.st_mode, + nano_timestamp); + } else { + conn_printf("MKDIR %s %s\n", + url_encode(key), url_encode(filename)); + } last_conn_status = read_conn_status(filename, peername); if (!is_ok_response(last_conn_status)) goto maybe_auto_resolve; @@ -452,29 +470,32 @@ enum connection_response csync_update_file_mod(const char *peername, goto got_error; } - conn_printf("SETOWN %s %s %d %d\n", - url_encode(key), url_encode(filename), - st.st_uid, st.st_gid); - last_conn_status = read_conn_status(filename, peername); - if (!is_ok_response(last_conn_status)) - goto got_error; + if (!csync_atomic_patch || (!S_ISREG(st.st_mode) && S_ISDIR(st.st_mode))) { - if ( !S_ISLNK(st.st_mode) ) { - conn_printf("SETMOD %s %s %d\n", url_encode(key), - url_encode(filename), st.st_mode); + conn_printf("SETOWN %s %s %d %d\n", + url_encode(key), url_encode(filename), + st.st_uid, st.st_gid); last_conn_status = read_conn_status(filename, peername); if (!is_ok_response(last_conn_status)) goto got_error; - } + + if ( !S_ISLNK(st.st_mode) ) { + conn_printf("SETMOD %s %s %d\n", url_encode(key), + url_encode(filename), st.st_mode); + last_conn_status = read_conn_status(filename, peername); + if (!is_ok_response(last_conn_status)) + goto got_error; + } skip_action: - if ( !S_ISLNK(st.st_mode) ) { - conn_printf("SETIME %s %s %lld\n", - url_encode(key), url_encode(filename), - (long long)st.st_mtime); - last_conn_status = read_conn_status(filename, peername); - if (!is_ok_response(last_conn_status)) - goto got_error; + if ( !S_ISLNK(st.st_mode) ) { + conn_printf("SETIME %s %s %lld\n", + url_encode(key), url_encode(filename), + nano_timestamp); + last_conn_status = read_conn_status(filename, peername); + if (!is_ok_response(last_conn_status)) + goto got_error; + } } SQL("Remove dirty-file entry.",