+/**
+ * Read @p len bytes at @p ptr from descriptor @p desc, retrying if
+ * interrupted.
+ *
+ * @retval >0 the actual number of bytes read
+ *
+ * @retval 0 for EOF
+ *
+ * @retval <0 for an error.
+ *
+ * Derived from GNU C's cccp.c. */
+static int safe_read(int desc, char *ptr, size_t len)
+{
+ int n_chars;
+
+ if (len == 0)
+ return len;
+
+ do {
+ n_chars = read(desc, ptr, len);
+ } while (n_chars < 0 && errno == EINTR);
+
+ return n_chars;
+}
+
+
+/** Copy a file.
+ *
+ * This is used in conjunction with the --temp-dir option */
+int copy_file(char *source, char *dest, mode_t mode)
+{
+ int ifd;
+ int ofd;
+ char buf[1024 * 8];
+ int len; /* Number of bytes read into `buf'. */
+
+ ifd = do_open(source, O_RDONLY, 0);
+ if (ifd == -1) {
+ rprintf(FERROR,"open %s: %s\n",
+ source,strerror(errno));
+ return -1;
+ }
+
+ if (robust_unlink(dest) && errno != ENOENT) {
+ rprintf(FERROR,"unlink %s: %s\n",
+ dest,strerror(errno));
+ return -1;
+ }
+
+ ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
+ if (ofd == -1) {
+ rprintf(FERROR,"open %s: %s\n",
+ dest,strerror(errno));
+ close(ifd);
+ return -1;
+ }
+
+ while ((len = safe_read(ifd, buf, sizeof(buf))) > 0) {
+ if (full_write(ofd, buf, len) < 0) {
+ rprintf(FERROR,"write %s: %s\n",
+ dest,strerror(errno));
+ close(ifd);
+ close(ofd);
+ return -1;
+ }
+ }
+
+ close(ifd);
+ close(ofd);
+
+ if (len < 0) {
+ rprintf(FERROR,"read %s: %s\n",
+ source,strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
+#define MAX_RENAMES_DIGITS 3
+#define MAX_RENAMES 1000
+
+/**
+ * Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
+ * rename to <path>/.rsyncNNN instead.
+ *
+ * Note that successive rsync runs will shuffle the filenames around a
+ * bit as long as the file is still busy; this is because this function
+ * does not know if the unlink call is due to a new file coming in, or
+ * --delete trying to remove old .rsyncNNN files, hence it renames it
+ * each time.
+ **/
+int robust_unlink(char *fname)
+{
+#ifndef ETXTBSY
+ return do_unlink(fname);
+#else
+ static int counter = 1;
+ int rc, pos, start;
+ char path[MAXPATHLEN];
+
+ rc = do_unlink(fname);
+ if ((rc == 0) || (errno != ETXTBSY))
+ return rc;
+
+ strlcpy(path, fname, MAXPATHLEN);
+
+ pos = strlen(path);
+ while((path[--pos] != '/') && (pos >= 0))
+ ;
+ ++pos;
+ strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos);
+ pos += sizeof(".rsync")-1;
+
+ if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
+ errno = ETXTBSY;
+ return -1;
+ }
+
+ /* start where the last one left off to reduce chance of clashes */
+ start = counter;
+ do {
+ sprintf(&path[pos], "%03d", counter);
+ if (++counter >= MAX_RENAMES)
+ counter = 1;
+ } while (((rc = access(path, 0)) == 0) && (counter != start));
+
+ if (verbose > 0) {
+ rprintf(FINFO,"renaming %s to %s because of text busy\n",
+ fname, path);
+ }
+
+ /* maybe we should return rename()'s exit status? Nah. */
+ if (do_rename(fname, path) != 0) {
+ errno = ETXTBSY;
+ return -1;
+ }
+ return 0;
+#endif
+}