/* We're either updating the basis file or an identical copy: */
static int updating_basis_or_equiv;
-/*
- * get_tmpname() - create a tmp filename for a given filename
+#define TMPNAME_SUFFIX ".XXXXXX"
+#define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)
+#define MAX_UNIQUE_NUMBER 999999
+#define MAX_UNIQUE_LOOP 100
+
+/* get_tmpname() - create a tmp filename for a given filename
*
- * If a tmpdir is defined, use that as the directory to
- * put it in. Otherwise, the tmp filename is in the same
- * directory as the given name. Note that there may be no
- * directory at all in the given name!
+ * If a tmpdir is defined, use that as the directory to put it in. Otherwise,
+ * the tmp filename is in the same directory as the given name. Note that
+ * there may be no directory at all in the given name!
*
- * The tmp filename is basically the given filename with a
- * dot prepended, and .XXXXXX appended (for mkstemp() to
- * put its unique gunk in). Take care to not exceed
- * either the MAXPATHLEN or NAME_MAX, esp. the last, as
- * the basename basically becomes 8 chars longer. In that
- * case, the original name is shortened sufficiently to
- * make it all fit.
+ * The tmp filename is basically the given filename with a dot prepended, and
+ * .XXXXXX appended (for mkstemp() to put its unique gunk in). We take care
+ * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as
+ * the basename basically becomes 8 characters longer. In such a case, the
+ * original name is shortened sufficiently to make it all fit.
*
- * Of course, there's no real reason for the tmp name to
- * look like the original, except to satisfy us humans.
- * As long as it's unique, rsync will work.
- */
-
-int get_tmpname(char *fnametmp, const char *fname)
+ * If the make_unique arg is True, the XXXXXX string is replaced with a unique
+ * string that doesn't exist at the time of the check. This is intended to be
+ * used for creating hard links, symlinks, devices, and special files, since
+ * normal files should be handled by mkstemp() for safety.
+ *
+ * Of course, the only reason the file is based on the original name is to
+ * make it easier to figure out what purpose a temp file is serving when a
+ * transfer is in progress. */
+int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique)
{
int maxname, added, length = 0;
const char *f;
+ char *suf;
if (tmpdir) {
/* Note: this can't overflow, so the return value is safe */
fnametmp[length++] = '.';
/* The maxname value is bufsize, and includes space for the '\0'.
- * (Note that NAME_MAX get -8 for the leading '.' above.) */
- maxname = MIN(MAXPATHLEN - 7 - length, NAME_MAX - 8);
+ * 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) {
rprintf(FERROR_XFER, "temporary filename too long: %s\n", fname);
added = strlcpy(fnametmp + length, f, maxname);
if (added >= maxname)
added = maxname - 1;
- memcpy(fnametmp + length + added, ".XXXXXX", 8);
+ suf = fnametmp + length + added;
+
+ if (make_unique) {
+ static unsigned counter_limit;
+ unsigned counter;
+
+ 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;
}
{
int fd;
- if (!get_tmpname(fnametmp, fname))
+ if (!get_tmpname(fnametmp, fname, False))
return -1;
/* We initially set the perms without the setuid/setgid bits or group
if (fd == -1 && relative_paths && errno == ENOENT
&& make_path(fnametmp, MKP_SKIP_SLASH | MKP_DROP_NAME) == 0) {
/* Get back to name with XXXXXX in it. */
- get_tmpname(fnametmp, fname);
+ get_tmpname(fnametmp, fname, False);
fd = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS);
}
#endif
if (got_eof)
return 0;
+ /* TODO: integrate gen-reading I/O into perform_io() so this is not needed? */
+ io_flush(FULL_FLUSH);
+
while (next_ndx < desired_ndx) {
if (inc_recurse && flist_num <= done_cnt)
return 0;
* main routine for receiver process.
*
* Receiver process runs on the same host as the generator process. */
-int recv_files(int f_in, char *local_name)
+int recv_files(int f_in, int f_out, char *local_name)
{
int fd1,fd2;
STRUCT_STAT st;
char fnamecmpbuf[MAXPATHLEN];
uchar fnamecmp_type;
struct file_struct *file;
- struct stats initial_stats;
int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
enum logcode log_code = log_before_transfer ? FLOG : FINFO;
int max_phase = protocol_version >= 29 ? 2 : 1;
cleanup_disable();
/* This call also sets cur_flist. */
- ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
+ ndx = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type,
xname, &xlen);
if (ndx == NDX_DONE) {
if (!am_server && INFO_GTE(PROGRESS, 2) && cur_flist) {
rprintf(FINFO, "recv_files phase=%d\n", phase);
if (phase == 2 && delay_updates)
handle_delayed_updates(local_name);
- send_msg(MSG_DONE, "", 0, 0);
+ write_int(f_out, NDX_DONE);
continue;
}
}
}
+ if (!log_before_transfer)
+ remember_initial_stats();
+
if (!do_xfers) { /* log the transfer */
- log_item(FCLIENT, file, &stats, iflags, NULL);
+ log_item(FCLIENT, file, iflags, NULL);
if (read_batch)
discard_receive_data(f_in, F_LENGTH(file));
continue;
}
if (write_batch < 0) {
- log_item(FCLIENT, file, &stats, iflags, NULL);
+ log_item(FCLIENT, file, iflags, NULL);
if (!am_server)
discard_receive_data(f_in, F_LENGTH(file));
continue;
fnamecmp = fname;
}
- initial_stats = stats;
-
/* open the file */
fd1 = do_open(fnamecmp, O_RDONLY, 0);
/* log the transfer */
if (log_before_transfer)
- log_item(FCLIENT, file, &initial_stats, iflags, NULL);
+ log_item(FCLIENT, file, iflags, NULL);
else if (!am_server && INFO_GTE(NAME, 1) && INFO_EQ(PROGRESS, 1))
rprintf(FINFO, "%s\n", fname);
recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
fname, fd2, F_LENGTH(file));
- log_item(log_code, file, &initial_stats, iflags, NULL);
+ log_item(log_code, file, iflags, NULL);
if (fd1 != -1)
close(fd1);