Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 9 Jul 2005 01:06:11 +0200 (CEST)
From:      Dan Lukes <dan@obluda.cz>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   bin/83170: [ PATCH ] Allow 'install' to compare files by mtime instead of content
Message-ID:  <200507082306.j68N6Bfv092939@kulesh.obluda.cz>
Resent-Message-ID: <200507082310.j68NAGQc064343@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         83170
>Category:       bin
>Synopsis:       [ PATCH ] Allow 'install' to compare files by mtime instead of content
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jul 08 23:10:14 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Dan Lukes
>Release:        FreeBSD 5.4-STABLE i386
>Organization:
Obludarium
>Environment:
System: FreeBSD 5.4-STABLE #6: Mon Jul 4 04:55:52 CEST 2005 i386
usr.bin/xinstall/xinstall.c,v 1.65 2004/03/17 11:06:40

>Description:
	Imagine large number of similar computers (for example border
routers of company branches, workstations within computer lab or so). The
compile options used during 'buildworld' are identical for all computers
within such group. When SA issued and OS patched, we should update source
tree on all machines then 'buildworld'/'installworld'. It's time expensive.

	The other way is 'buildworld' on the selected machine, export
/usr/src & /usr/obj via NFS, mount'em on other computers then do
'installworld'.

	The standard install transfer every file despite only few files has
been changed (assuming use of -DNOCLEAN during 'buildworld'). 

	Unnecesarry transfers of large amount of data may disturb during updates of
remote computers connected via slow links. Even on fast network the reading
of large amount of data can be time consuming.

	-C option of current 'install' didn't help - the source file is transfered
during comparsion.

	I would like to suggest to create option '-q' (quick) which allow user to modify
the comparsion alghoritm. It skip comparsion of content of files but use
modification time of files instead.

	It may substantially reduce the amount of transferred data and
transfer time during such update.

	It's usefull when mtime on destination files follows the mtime on
sources only - so '-q' imply '-p' ('-p' imply '-C' already).

>How-To-Repeat:
	N/A
>Fix:

	I suggest to add '-q' option modifying compare alghoritm when used. The
most changes are related to compare(). The 'struct stat' of both files are
passed to it so mtimes are avaiable to function if necesarry (current code
pass st_size only to it).

	The patch of manual page should be reviewed by someone who speak
english better than me.

--- xinstall.patch begins here ---
--- usr.bin/xinstall/xinstall.c.ORIG	Fri Jul  8 19:54:34 2005
+++ usr.bin/xinstall/xinstall.c	Fri Jul  8 22:29:32 2005
@@ -82,12 +82,13 @@
 struct group *gp;
 gid_t gid;
 uid_t uid;
-int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose;
+int dobackup, docompare, dodir, dopreserve, dostrip, nommap, quickcompare, 
+    safecopy, verbose;
 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
 const char *suffix = BACKUP_SUFFIX;
 
 void	copy(int, const char *, int, const char *, off_t);
-int	compare(int, const char *, size_t, int, const char *, size_t);
+int	compare(int, const char *, struct stat *, int, const char *, struct stat *);
 int	create_newfile(const char *, int, struct stat *);
 int	create_tempfile(const char *, char *, size_t);
 void	install(const char *, const char *, u_long, u_int);
@@ -110,7 +111,7 @@
 
 	iflags = 0;
 	group = owner = NULL;
-	while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1)
+	while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pqSsv")) != -1)
 		switch((char)ch) {
 		case 'B':
 			suffix = optarg;
@@ -152,6 +153,9 @@
 		case 'p':
 			docompare = dopreserve = 1;
 			break;
+		case 'q':
+			quickcompare = docompare = dopreserve = 1;
+			break;
 		case 'S':
 			safecopy = 1;
 			break;
@@ -270,6 +274,7 @@
 	char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
 
 	files_match = 0;
+	from_fd=-1;
 
 	/* If try to install NULL file to a directory, fails. */
 	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
@@ -314,8 +319,7 @@
 			files_match = to_sb.st_size == 0;
 		else
 			files_match = !(compare(from_fd, from_name,
-			    (size_t)from_sb.st_size, to_fd,
-			    to_name, (size_t)to_sb.st_size));
+			    &from_sb, to_fd, to_name, &to_sb));
 
 		/* Close "to" file unless we match. */
 		if (!files_match)
@@ -371,8 +375,11 @@
 			err(EX_OSERR, "%s", tempfile);
 		}
 
-		if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
-			    to_name, (size_t)to_sb.st_size) == 0) {
+		/* Use mtime of original file during quick comparsion */
+		temp_sb.st_mtime = to_sb.st_mtime;
+
+		if (compare(temp_fd, tempfile, &temp_sb, to_fd,
+			    to_name, &to_sb) == 0) {
 			/*
 			 * If target has more than one link we need to
 			 * replace it in order to snap the extra links.
@@ -513,61 +520,64 @@
  *	compare two files; non-zero means files differ
  */
 int
-compare(int from_fd, const char *from_name __unused, size_t from_len,
-	int to_fd, const char *to_name __unused, size_t to_len)
+compare(int from_fd, const char *from_name __unused, struct stat *from_sb,
+	int to_fd, const char *to_name __unused, struct stat *to_sb)
 {
-	char *p, *q;
 	int rv;
-	int done_compare;
 
-	rv = 0;
-	if (from_len != to_len)
+	if (from_sb->st_size != to_sb->st_size)
 		return 1;
 
-	if (from_len <= MAX_CMP_SIZE) {
-		done_compare = 0;
-		if (trymmap(from_fd) && trymmap(to_fd)) {
-			p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
-			if (p == (char *)MAP_FAILED)
-				goto out;
-			q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
-			if (q == (char *)MAP_FAILED) {
-				munmap(p, from_len);
-				goto out;
-			}
+	if (quickcompare == 1)
+		return (from_sb->st_mtime != to_sb->st_mtime);
+		
+	if (from_sb->st_size > MAX_CMP_SIZE)
+		return 1;
 
-			rv = memcmp(p, q, from_len);
-			munmap(p, from_len);
-			munmap(q, from_len);
-			done_compare = 1;
-		}
-	out:
-		if (!done_compare) {
-			char buf1[MAXBSIZE];
-			char buf2[MAXBSIZE];
-			int n1, n2;
-
-			rv = 0;
-			lseek(from_fd, 0, SEEK_SET);
-			lseek(to_fd, 0, SEEK_SET);
-			while (rv == 0) {
-				n1 = read(from_fd, buf1, sizeof(buf1));
-				if (n1 == 0)
-					break;		/* EOF */
-				else if (n1 > 0) {
-					n2 = read(to_fd, buf2, n1);
-					if (n2 == n1)
-						rv = memcmp(buf1, buf2, n1);
-					else
-						rv = 1;	/* out of sync */
-				} else
-					rv = 1;		/* read failure */
-			}
-			lseek(from_fd, 0, SEEK_SET);
-			lseek(to_fd, 0, SEEK_SET);
+	if (trymmap(from_fd) && trymmap(to_fd)) {
+		char *p, *q;
+
+		p = mmap(NULL, from_sb->st_size, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
+		if (p == (char *)MAP_FAILED)
+			goto out;
+		q = mmap(NULL, from_sb->st_size, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+		if (q == (char *)MAP_FAILED) {
+			munmap(p, from_sb->st_size);
+			goto out;
+		}
+
+		rv = memcmp(p, q, from_sb->st_size);
+		munmap(p, from_sb->st_size);
+		munmap(q, from_sb->st_size);
+		return rv;
+	}
+
+	/* mmap disabled or failed - fallback to I/O mode */
+out:
+	{
+		char buf1[MAXBSIZE];
+		char buf2[MAXBSIZE];
+		int n1, n2;
+
+		rv = 0;
+		lseek(from_fd, 0, SEEK_SET);
+		lseek(to_fd, 0, SEEK_SET);
+		while (rv == 0) {
+			n1 = read(from_fd, buf1, sizeof(buf1));
+			if (n1 == 0)
+				break;		/* EOF */
+			else if (n1 > 0) {
+				n2 = read(to_fd, buf2, n1);
+				if (n2 == n1)
+					rv = memcmp(buf1, buf2, n1);
+				else
+					rv = 1;	/* out of sync */
+			} else
+				rv = 1;		/* read failure */
 		}
-	} else
-		rv = 1;	/* don't bother in this case */
+		lseek(from_fd, 0, SEEK_SET);
+		lseek(to_fd, 0, SEEK_SET);
+	}
 
 	return rv;
 }
@@ -762,9 +772,9 @@
 usage()
 {
 	(void)fprintf(stderr,
-"usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
+"usage: install [-bCcpqSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
 "               [-o owner] file1 file2\n"
-"       install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
+"       install [-bCcpqSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
 "               [-o owner] file1 ... fileN directory\n"
 "       install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n");
 	exit(EX_USAGE);
--- usr.bin/xinstall/install.1.ORIG	Sun Aug  8 21:13:08 2004
+++ usr.bin/xinstall/install.1	Fri Jul  8 22:32:09 2005
@@ -40,7 +40,7 @@
 .Nd install binaries
 .Sh SYNOPSIS
 .Nm
-.Op Fl bCcMpSsv
+.Op Fl bCcMpqSsv
 .Op Fl B Ar suffix
 .Op Fl f Ar flags
 .Op Fl g Ar group
@@ -48,7 +48,7 @@
 .Op Fl o Ar owner
 .Ar file1 file2
 .Nm
-.Op Fl bCcMpSsv
+.Op Fl bCcMpqSsv
 .Op Fl B Ar suffix
 .Op Fl f Ar flags
 .Op Fl g Ar group
@@ -138,6 +138,20 @@
 (compare and copy) option is specified,
 except if the target file doesn't already exist or is different,
 then preserve the access and modification times of the source file.
+.It Fl q
+Do quick comparsion.
+Normally, files are considered identical when size and content of files 
+match.
+During quick comparsion, the files are considered identical when size and 
+mtime match. 
+It may be usefull during updates of large software packages where the only
+few files has been changed between versions. Especially when source or 
+destination is remote filesystem on computer connected via slow link.
+The
+.Fl q
+imply 
+.Fl p
+.
 .It Fl S
 Safe copy.
 Normally,
--- xinstall.patch ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200507082306.j68N6Bfv092939>