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>