int csync_db_next(void *vmx, const char *err, int *pN, const char ***pazValue, const char ***pazColName) { db_stmt_p stmt = vmx; int rc, busyc = 0; csync_debug(4, "Trying to fetch a row from the database.\n"); while (1) { rc = db_stmt_next(stmt); if ( rc != DB_BUSY ) break; if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); } csync_debug(2, "Database is busy, sleeping a sec.\n"); sleep(1); } if ( rc != DB_OK && rc != DB_ROW && rc != DB_DONE && err ) csync_fatal("Database Error: %s [%d]: %s\n", err, rc, db_errmsg(db)); return rc == DB_ROW; }
void csync_db_fin(void *vmx, const char *err) { db_stmt_p stmt = (db_stmt_p) vmx; int rc, busyc = 0; if (vmx == NULL) return; csync_debug(2, "SQL Query finished.\n"); while (1) { rc = db_stmt_close(stmt); if ( rc != DB_BUSY ) break; if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); } csync_debug(2, "Database is busy, sleeping a sec.\n"); sleep(1); } if ( rc != DB_OK && err ) csync_fatal("Database Error: %s [%d]: %s\n", err, rc, db_errmsg(db)); csync_db_maycommit(); in_sql_query--; }
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"); } csync_debug(3, "Receiving %ld bytes ..\n", size); while ( size > 0 ) { chunk = size > 512 ? 512 : size; rc = conn_read(buffer, chunk); 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"); size -= chunk; csync_debug(3, "Got %d bytes, %ld bytes left ..\n", chunk, size); } fflush(out); rewind(out); return 0; }
void* csync_db_begin(const char *err, const char *fmt, ...) { db_stmt_p stmt = NULL; char *sql; va_list ap; int rc, busyc = 0; char *ppTail; va_start(ap, fmt); VASPRINTF(&sql, fmt, ap); va_end(ap); in_sql_query++; csync_db_maybegin(); csync_debug(2, "SQL: %s\n", sql); while (1) { rc = db_prepare_stmt(db, sql, &stmt, &ppTail); if ( rc != DB_BUSY ) break; if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); } csync_debug(2, "Database is busy, sleeping a sec.\n"); sleep(1); } if ( rc != DB_OK && err ) csync_fatal("Database Error: %s [%d]: %s on executing %s\n", err, rc, db_errmsg(db), sql); free(sql); return stmt; }
static int get_tmpname(char *fnametmp, const char *fname) { int maxname, added, length = 0; const char *f; char *suf; static unsigned counter_limit; unsigned counter; if ((f = strrchr(fname, '/')) != NULL) { ++f; length = f - fname; /* copy up to and including the slash */ strlcpy(fnametmp, fname, length + 1); } else f = fname; fnametmp[length++] = '.'; /* The maxname value is bufsize, and includes space for the '\0'. * NAME_MAX needs an extra -1 for the name's leading dot. */ maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN, NAME_MAX - 1 - TMPNAME_SUFFIX_LEN); if (maxname < 1) { csync_debug(1, "temporary filename too long: %s\n", fname); fnametmp[0] = '\0'; return 0; } added = strlcpy(fnametmp + length, f, maxname); if (added >= maxname) added = maxname - 1; suf = fnametmp + 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. */ while (1) { snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter); if (access(fnametmp, 0) < 0) break; if (++counter >= counter_limit) return 0; } } else memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1); return 1; }
static FILE *open_temp_file(char *fnametmp, const char *fname) { 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; } f = NULL; fd = open(fnametmp, 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; } return f; }
void csync_db_alarmhandler(int signum) { if ( in_sql_query || begin_commit_recursion ) alarm(2); if (tqueries_counter <= 0) return; begin_commit_recursion++; csync_debug(2, "Database idle in transaction. Forcing COMMIT.\n"); SQL("COMMIT ", "COMMIT "); tqueries_counter = -10; begin_commit_recursion--; }
void csync_db_maycommit() { time_t now; if ( !db_blocking_mode || begin_commit_recursion ) return; begin_commit_recursion++; if (tqueries_counter <= 0) { begin_commit_recursion--; return; } now = time(0); if ((now - last_wait_cycle) > 10) { SQL("COMMIT", "COMMIT "); if (wait) { csync_debug(2, "Waiting %d secs so others can lock the database (%d - %d)...\n", wait, (int)now, (int)last_wait_cycle); sleep(wait); } last_wait_cycle = 0; tqueries_counter = -10; begin_commit_recursion--; return; } if ((tqueries_counter > 1000) || ((now - transaction_begin) > 3)) { SQL("COMMIT ", "COMMIT "); tqueries_counter = 0; begin_commit_recursion--; return; } signal(SIGALRM, csync_db_alarmhandler); alarm(10); begin_commit_recursion--; return; }
static FILE *paranoid_tmpfile() { 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); } 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; }
void csync_rs_sig(const char *filename) { FILE *basis_file = 0, *sig_file = 0; rs_stats_t stats; rs_result result; char tmpfname[MAXPATHLEN]; csync_debug(3, "Csync2 / Librsync: csync_rs_sig('%s')\n", 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; 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"); result = rs_sig_file(basis_file, sig_file, RS_DEFAULT_BLOCK_LEN, RS_DEFAULT_STRONG_LEN, &stats); if (result != RS_DONE) csync_fatal("Got an error from librsync, too bad!\n"); csync_debug(3, "Sending sig_file to peer..\n"); csync_send_file(sig_file); csync_debug(3, "Signature has been created successfully.\n"); fclose(basis_file); fclose(sig_file); return; io_error: csync_debug(0, "I/O Error '%s' in rsync-sig: %s\n", strerror(errno), prefixsubst(filename)); if (basis_file) fclose(basis_file); if (sig_file) fclose(sig_file); }
int csync_rs_patch(const char *filename) { FILE *basis_file = 0, *delta_file = 0, *new_file = 0; int backup_errno; rs_stats_t stats; rs_result result; char *errstr = "?"; char tmpfname[MAXPATHLEN], newfname[MAXPATHLEN]; csync_debug(3, "Csync2 / Librsync: csync_rs_patch('%s')\n", filename); csync_debug(3, "Receiving delta_file from peer..\n"); delta_file = open_temp_file(tmpfname, prefixsubst(filename)); 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"); if ( !basis_file ) { basis_file = open_temp_file(tmpfname, prefixsubst(filename)); 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)); if ( !new_file ) { errstr="creating new data temp file"; goto io_error; } csync_debug(3, "Running rs_patch_file() from librsync..\n"); result = rs_patch_file(basis_file, delta_file, new_file, &stats); if (result != RS_DONE) { csync_debug(0, "Internal error from rsync library!\n"); goto error; } csync_debug(3, "Renaming tmp file to data file..\n"); fclose(basis_file); #ifdef __CYGWIN__ /* TODO: needed? */ // This creates the file using the native windows API, bypassing // the cygwin wrappers and so making sure that we do not mess up the // permissions.. { char winfilename[MAX_PATH]; HANDLE winfh; cygwin_conv_to_win32_path(prefixsubst(filename), winfilename); winfh = CreateFile(TEXT(winfilename), GENERIC_WRITE, // open for writing 0, // do not share NULL, // default security CREATE_ALWAYS, // overwrite existing FILE_ATTRIBUTE_NORMAL | // normal file FILE_FLAG_OVERLAPPED, // asynchronous I/O NULL); // no attr. template if (winfh == INVALID_HANDLE_VALUE) { csync_debug(0, "Win32 I/O Error %d in rsync-patch: %s\n", (int)GetLastError(), winfilename); errno = EACCES; goto error; } CloseHandle(winfh); } #endif if (rename(newfname, prefixsubst(filename)) < 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); fclose(new_file); return 0; io_error: csync_debug(0, "I/O Error '%s' while %s in rsync-patch: %s\n", strerror(errno), errstr, prefixsubst(filename)); error:; backup_errno = errno; if ( delta_file ) fclose(delta_file); if ( basis_file ) fclose(basis_file); if ( new_file ) fclose(new_file); errno = backup_errno; return -1; }
int csync_rs_delta(const char *filename) { FILE *sig_file = 0, *new_file = 0, *delta_file = 0; rs_result result; rs_signature_t *sumset; rs_stats_t stats; char tmpfname[MAXPATHLEN]; 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)); if ( !sig_file ) goto io_error; if (unlink(tmpfname) < 0) goto io_error; if ( csync_recv_file(sig_file) ) { fclose(sig_file); return -1; } result = rs_loadsig_file(sig_file, &sumset, &stats); if (result != RS_DONE) csync_fatal("Got an error from librsync, too bad!\n"); fclose(sig_file); csync_debug(3, "Opening new_file and delta_file..\n"); new_file = fopen(prefixsubst(filename), "rb"); if ( !new_file ) { int backup_errno = errno; csync_debug(0, "I/O Error '%s' while %s in rsync-delta: %s\n", strerror(errno), "opening data file for reading", filename); csync_send_error(); fclose(new_file); errno = backup_errno; return -1; } delta_file = open_temp_file(tmpfname, prefixsubst(filename)); if ( !delta_file ) goto io_error; if (unlink(tmpfname) < 0) goto io_error; csync_debug(3, "Running rs_build_hash_table() from librsync..\n"); result = rs_build_hash_table(sumset); if (result != RS_DONE) csync_fatal("Got an error from librsync, too bad!\n"); csync_debug(3, "Running rs_delta_file() from librsync..\n"); result = rs_delta_file(sumset, new_file, delta_file, &stats); if (result != RS_DONE) csync_fatal("Got an error from librsync, too bad!\n"); csync_debug(3, "Sending delta_file to peer..\n"); csync_send_file(delta_file); csync_debug(3, "Delta has been created successfully.\n"); rs_free_sumset(sumset); fclose(delta_file); fclose(new_file); return 0; io_error: csync_debug(0, "I/O Error '%s' in rsync-delta: %s\n", strerror(errno), prefixsubst(filename)); if (new_file) fclose(new_file); if (delta_file) fclose(delta_file); if (sig_file) fclose(sig_file); return -1; }
int csync_rs_check(const char *filename, int isreg) { FILE *basis_file = 0, *sig_file = 0; char buffer1[512], buffer2[512]; int rc, chunk, found_diff = 0; int backup_errno; rs_stats_t stats; rs_result result; long size; char tmpfname[MAXPATHLEN]; csync_debug(3, "Csync2 / Librsync: csync_rs_check('%s', %d [%s])\n", filename, isreg, isreg ? "regular file" : "non-regular file"); 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; 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; } if ( isreg ) { csync_debug(3, "Running rs_sig_file() from librsync....\n"); 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); basis_file = 0; { char line[100]; csync_debug(3, "Reading signature size from peer....\n"); if ( !conn_gets(line, 100) || sscanf(line, "octet-stream %ld\n", &size) != 1 ) 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; } rewind(sig_file); csync_debug(3, "Receiving %ld bytes ..\n", size); while ( size > 0 ) { chunk = size > 512 ? 512 : size; rc = conn_read(buffer1, chunk); if ( rc <= 0 ) 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; } size -= chunk; csync_debug(3, "Got %d bytes, %ld bytes left ..\n", chunk, size); } csync_debug(3, "File has been checked successfully (%s).\n", found_diff ? "difference found" : "files are equal"); fclose(sig_file); return found_diff; io_error: csync_debug(0, "I/O Error '%s' in rsync-check: %s\n", strerror(errno), prefixsubst(filename)); error:; backup_errno = errno; if ( basis_file ) fclose(basis_file); if ( sig_file ) fclose(sig_file); errno = backup_errno; return -1; }