+ if (preserve_links && S_ISLNK(f->mode)) {
+ rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
+ perms,
+ (double)f->length, timestring(f->modtime),
+ f_name(f), f->u.link);
+ } else
+#endif
+ {
+ rprintf(FINFO, "%s %11.0f %s %s\n",
+ perms,
+ (double)f->length, timestring(f->modtime),
+ f_name(f));
+ }
+}
+
+
+/**
+ * Stat either a symlink or its referent, depending on the settings of
+ * copy_links, copy_unsafe_links, etc.
+ *
+ * @retval -1 on error
+ *
+ * @retval 0 for success
+ *
+ * @post If @p path is a symlink, then @p linkbuf (of size @c
+ * MAXPATHLEN) contains the symlink target.
+ *
+ * @post @p buffer contains information about the link or the
+ * referrent as appropriate, if they exist.
+ **/
+static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf)
+{
+#if SUPPORT_LINKS
+ if (copy_links)
+ return do_stat(path, buffer);
+ if (link_stat(path, buffer, 0) < 0)
+ return -1;
+ if (S_ISLNK(buffer->st_mode)) {
+ int l = readlink((char *)path, linkbuf, MAXPATHLEN - 1);
+ if (l == -1)
+ return -1;
+ linkbuf[l] = 0;
+ if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) {
+ if (verbose > 1) {
+ rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
+ path, linkbuf);
+ }
+ return do_stat(path, buffer);
+ }
+ }
+ return 0;
+#else
+ return do_stat(path, buffer);
+#endif
+}
+
+int link_stat(const char *path, STRUCT_STAT *buffer, int follow_dirlinks)
+{
+#if SUPPORT_LINKS
+ if (copy_links)
+ return do_stat(path, buffer);
+ if (do_lstat(path, buffer) < 0)
+ return -1;
+ if (follow_dirlinks && S_ISLNK(buffer->st_mode)) {
+ STRUCT_STAT st;
+ if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+ *buffer = st;
+ }
+ return 0;