Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site allegra.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!jpl From: jpl@allegra.UUCP (John P. Linderman) Newsgroups: net.bugs.4bsd,net.bugs.usg,net.unix-wizards Subject: find(1) mishandles multiple -newer options - fix included Message-ID: <5275@allegra.UUCP> Date: Mon, 21-Oct-85 10:30:31 EDT Article-I.D.: allegra.5275 Posted: Mon Oct 21 10:30:31 1985 Date-Received: Tue, 22-Oct-85 05:47:15 EDT Distribution: net.bugs.4bsd,net.bugs.usg,net.unix-wizards Organization: AT&T Bell Laboratories, Murray Hill Lines: 144 Keywords: find, incremental backup Xref: watmath net.bugs.4bsd:1817 net.bugs.usg:366 net.unix-wizards:15355 Index: usr.bin/find.c 4.2BSD Description: Only one -newer option will be correctly processed on a find. Repeat-By: # The following script demonstrates the problem (which also # exists on System V and Version 8) and the effect of the fix. # The fix also adds the ability to compare on access and # inode modification times as well as file modification time, # as is also demonstrated in the script. $ touch 1 $ touch 2 $ touch 3 $ find . \( -newer 2 -o -newer 3 \) -print . $ /usr/5bin/find . \( -newer 2 -o -newer 3 \) -print . $ find . \( -newer 3 -o -newer 2 \) -print . ./3 $ ./find . \( -newer 2 -o -newer 3 \) -print . ./3 $ mv 1 4 $ find . -newer 2 -print . ./3 $ ./find . -newer 2 -print . ./3 $ ./find . -newerc 2 -print . ./3 ./4 $ Fix: The following diffs to the BSD 4.2 source correct the problem, and add a dozen options. (Only a few options are genuinely useful, but it was cleaner to add them all than to prune out the useless ones.) -newer can be followed by one or two occurrences of the letters [acm] to specify which time from the stat structure (st_atime, st_ctime or st_mtime -- see stat(2)) will be used in the comparison. The first letter, if any, determines the time used for the files the find command is searching. The second, if any, determines the time from the file that follows the -newer option. Both default to m, so -newer foo, -newerm foo, and -newermm foo are identical. Note that -newerc causes the INODE modification time of the found files to be compared to the FILE (not inode) modification time of the specified target. This was done deliberately, because it works correctly with the following incremental backup scheme touch startstamp find ... -newerc laststamp ... mv startstamp laststamp If the dump dies midstream, laststamp is not changed, so the next dump will get all the files this dump would have. If the dump does run to completion, the mv changes the inode modification time of startstamp but not the file modification time, so the next incremental dump will pick up all the files changed after OR DURING this dump, including those whose modes or owners were changed or those renamed. I don't know if the System V and Version 8 sources are identical, but (except for the MAXPATHLEN change), the changes appear to be analogous. The new features are particularly useful in conjunction with the System V touch command, which allows one to set the modification dates of a file to an arbitrary time. These give greater precision and cleaner semantics than the -mtime and -atime options (one day since when??). John P. Linderman Department of find bug finders allegra!jpl 11c11 < char Pathname[200]; --- > char Pathname[MAXPATHLEN + 1]; 30c30,32 < long Newer; --- > #define NNEW 50 > int Nnewer; > time_t Newer[NNEW]; 230c232,234 < else if(EQ(a, "-newer")) { --- > else if(strncmp(a, "-newer", 6) == 0) { > char *p = a + 6; > time_t *t1p, *t2p; 235,236c239,278 < Newer = Statb.st_mtime; < return mk(newer, (struct anode *)0, (struct anode *)0); --- > if(Nnewer >= NNEW) { > fprintf(stderr, "find: too many -newer constructs\n"); > exit(1); > } > t1p = t2p = &(Statb.st_mtime); > switch (*p) { > case 'm': > p++; > break; > case '\0': > break; > case 'a': > t1p = &(Statb.st_atime); > p++; > break; > case 'c': > t1p = &(Statb.st_ctime); > p++; > break; > } > switch (*p) { > case 'm': > p++; > break; > case '\0': > break; > case 'a': > t2p = &(Statb.st_atime); > p++; > break; > case 'c': > t2p = &(Statb.st_ctime); > p++; > break; > } > if (*p == '\0') { > Newer[Nnewer] = *t2p; > return mk(newer, (struct anode *)t1p, > (struct anode *)(&Newer[Nnewer++])); > } 428c470,471 < newer() --- > newer(p) > register struct { int f; time_t *t1, *t2; } *p; 430c473 < return Statb.st_mtime > Newer; --- > return *(p->t1) > *(p->t2);