[PATCH] --links-depth for rsync

Antti Tapaninen aet at cc.hut.fi
Sat Jan 24 21:09:46 GMT 2004


Hello,

about a year ago I ran into situation where there's a "metadirectory"
containing directories and symlinks to files. There was a need to mirror
the contents of files and directories gathered via symlinks to this
metadirectory, regular mirroring of the tree wouldn't do any good.

The attached patch gives the user ability to define how many symbolic
links rsync should follow before actually adding it to the file list.

Any code behaviour without the new parameter is unchanged, I've used the
previous versions of the patch with rsync 2.5.x for the last year and
might just as well share the patch. Consider merging the patch for the
next release, please reply to me directly for additional feedback or
issues.

Cheers,
-Antti
-------------- next part --------------
Index: flist.c
===================================================================
RCS file: /cvsroot/rsync/flist.c,v
retrieving revision 1.169
diff -u -r1.169 flist.c
--- flist.c	22 Jan 2004 18:39:32 -0000	1.169
+++ flist.c	24 Jan 2004 20:30:08 -0000
@@ -50,6 +50,7 @@
 extern int one_file_system;
 extern int make_backups;
 extern int preserve_links;
+extern int follow_links_depth;
 extern int preserve_hard_links;
 extern int preserve_perms;
 extern int preserve_devices;
@@ -725,6 +726,65 @@
 /* IRIX cc cares that the operands to the ternary have the same type. */
 #define MALLOC(ap, i)	(ap ? (void*) string_area_malloc(ap, i) : malloc(i))
 
+void make_file_stat(struct file_struct * file, STRUCT_STAT *st)
+{
+	file->modtime = st->st_mtime;
+	file->length = st->st_size;
+	file->mode = st->st_mode;
+	file->uid = st->st_uid;
+	file->gid = st->st_gid;
+	if (preserve_hard_links) {
+		if (protocol_version < 28 ? S_ISREG(st->st_mode)
+		    : !S_ISDIR(st->st_mode) && st->st_nlink > 1) {
+			if (!file->link_u.idev) {
+				if (!(file->link_u.idev = new(struct idev)))
+					out_of_memory("file inode data");
+			}
+			file->F_DEV = st->st_dev;
+			file->F_INODE = st->st_ino;
+		}
+	}
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+	if (IS_DEVICE(st->st_mode))
+		file->u.rdev = st->st_rdev;
+#endif
+}
+
+#if SUPPORT_LINKS
+void links_depth(struct string_area **ap, struct file_struct * file)
+{
+	char currbuf[MAXPATHLEN], linkbuf[MAXPATHLEN];
+	STRUCT_STAT st;
+	int i;
+
+	memset(currbuf, 0, MAXPATHLEN);
+	memset(linkbuf, 0, MAXPATHLEN);
+	strncpy(currbuf, file->u.link, MAXPATHLEN);
+	for (i = 0; i < follow_links_depth; i++) {
+		if (link_stat(currbuf, &st) != 0) {
+			break;
+		}
+		if (S_ISLNK(st.st_mode)) {
+			int buflen = readlink(currbuf, linkbuf, MAXPATHLEN - 1);
+			if (buflen > 0) {
+				linkbuf[buflen] = '\0';
+				strncpy(currbuf, linkbuf, MAXPATHLEN);
+			}
+		} else {
+			/* Not a symlink, quit */
+			break;
+		}
+#if 0
+		fprintf(stderr, "\n%s:%i [#%i] %s -> %s\n", __FILE__, __LINE__, i, file->u.link, currbuf);
+#endif
+	}
+	make_file_stat(file, &st);
+	if (file->u.link && S_ISLNK(st.st_mode)) {
+		file->u.link = STRDUP(ap, currbuf);
+	}
+}
+#endif
+
 /**
  * Create a file_struct for a named file by reading its stat()
  * information and performing extensive checks against global
@@ -825,28 +885,15 @@
 		file->basename = STRDUP(ap, fname);
 	}
 
-	file->modtime = st.st_mtime;
-	file->length = st.st_size;
-	file->mode = st.st_mode;
-	file->uid = st.st_uid;
-	file->gid = st.st_gid;
-	if (preserve_hard_links) {
-		if (protocol_version < 28 ? S_ISREG(st.st_mode)
-		    : !S_ISDIR(st.st_mode) && st.st_nlink > 1) {
-			if (!(file->link_u.idev = new(struct idev)))
-				out_of_memory("file inode data");
-			file->F_DEV = st.st_dev;
-			file->F_INODE = st.st_ino;
-		}
-	}
-#ifdef HAVE_STRUCT_STAT_ST_RDEV
-	if (IS_DEVICE(st.st_mode))
-		file->u.rdev = st.st_rdev;
-#endif
+	make_file_stat(file, &st);
 
 #if SUPPORT_LINKS
-	if (S_ISLNK(st.st_mode))
+	if (S_ISLNK(st.st_mode)) {
 		file->u.link = STRDUP(ap, linkbuf);
+		if ((follow_links_depth >= 1) && preserve_links) {
+			links_depth(ap, file);
+		}
+	}
 #endif
 
 	if (always_checksum && S_ISREG(st.st_mode)) {
Index: options.c
===================================================================
RCS file: /cvsroot/rsync/options.c,v
retrieving revision 1.127
diff -u -r1.127 options.c
--- options.c	23 Jan 2004 09:32:50 -0000	1.127
+++ options.c	24 Jan 2004 20:30:08 -0000
@@ -38,6 +38,7 @@
 int archive_mode = 0;
 int copy_links = 0;
 int preserve_links = 0;
+int follow_links_depth = 0;
 int preserve_hard_links = 0;
 int preserve_perms = 0;
 int preserve_devices = 0;
@@ -224,6 +225,7 @@
   rprintf(F,"     --suffix=SUFFIX         backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
   rprintf(F," -u, --update                update only (don't overwrite newer files)\n");
   rprintf(F," -l, --links                 copy symlinks as symlinks\n");
+  rprintf(F,"     --links-depth=NUM       follow symlinks up to NUM depth\n");
   rprintf(F," -L, --copy-links            copy the referent of symlinks\n");
   rprintf(F,"     --copy-unsafe-links     copy links outside the source tree\n");
   rprintf(F,"     --safe-links            ignore links outside the destination tree\n");
@@ -328,6 +330,7 @@
   {"cvs-exclude",     'C', POPT_ARG_NONE,   &cvs_exclude, 0, 0, 0 },
   {"update",          'u', POPT_ARG_NONE,   &update_only, 0, 0, 0 },
   {"links",           'l', POPT_ARG_NONE,   &preserve_links, 0, 0, 0 },
+  {"links-depth",      0,  POPT_ARG_INT,    &follow_links_depth , 0, 0, 0 },
   {"copy-links",      'L', POPT_ARG_NONE,   &copy_links, 0, 0, 0 },
   {"whole-file",      'W', POPT_ARG_VAL,    &whole_file, 1, 0, 0 },
   {"no-whole-file",    0,  POPT_ARG_VAL,    &whole_file, 0, 0, 0 },


More information about the rsync mailing list