source: trunk/athena/etc/track/stamp.c @ 12350

Revision 12350, 14.7 KB checked in by ghudson, 26 years ago (diff)
Some RCS ID cleanup: delete $Log$ and replace other RCS keywords with $Id$.
Line 
1/*
2 *      $Id: stamp.c,v 4.12 1999-01-22 23:16:02 ghudson Exp $
3 */
4
5#ifndef lint
6static char *rcsid_header_h = "$Id: stamp.c,v 4.12 1999-01-22 23:16:02 ghudson Exp $";
7#endif lint
8
9#include "mit-copyright.h"
10
11#include "track.h"
12
13extern char *mode_to_char(), *mode_to_fmt(), *mode_to_rfmt();
14
15char type_char[] = " cdbfls*89ABCDEF";
16
17/*
18 * Place a time-stamp line in the format:
19 *       type file uid.gid.mode.time
20 * or a suitable derivate thereof
21 */
22
23write_statline( path, c)
24char **path; struct currentness *c;
25{
26        char  *format, *linebuf, *name;
27        int same_name;
28        unsigned int type;
29        struct stat fromstat, *s;
30        unsigned size, curr1 = 0, extra = 0;
31
32        if ( cur_line >= maxlines) {
33                maxlines += MAXLINES;
34                size = maxlines * sizeof statfilebufs[0];
35                statfilebufs =
36                        (Statline *) ( cur_line ?
37                                       realloc( (char *) statfilebufs, size)
38                                      : malloc( size));
39                if ( ! statfilebufs) {
40                        sprintf( errmsg, "alloc failed: %d statfile lines\n",
41                                 cur_line);
42                        do_panic();
43                }
44        }
45        /*
46         * set up type-dependent currency data:
47         */
48
49        s = &c->sbuf;
50
51        switch( type = TYPE( *s)) {
52        case S_IFREG:
53                curr1 = c->cksum;
54                extra = TIME( *s);
55                break;
56        case S_IFLNK:
57                curr1 = (unsigned int) c->link;
58                break;
59        case S_IFDIR:
60                curr1 = 0;
61                break;
62        case S_IFBLK:
63                curr1 = RDEV( *s);
64                break;
65        case S_IFCHR:
66                curr1 = RDEV( *s);
67                break;
68        case S_IFSOCK:
69                sprintf( errmsg, "can't track socket %s.\n", path[ ROOT]);
70                do_gripe();
71                return( type);
72        case S_IFMT:
73        default:
74                sprintf( errmsg,
75                "bad type for inode %d, pathname %s.\n\tapparent type = %c\n",
76                    c->sbuf.st_ino, path[ ROOT], mode_to_string(type));
77                do_panic();
78        }
79        /* set up name & sortkey:
80         * root is a special case:
81         * its "relative path" is "", which dec_statfile() can't read.
82         */
83        name = path[ NAME];
84        if ( !*name) name = "/";
85
86        KEYCPY( statfilebufs[ cur_line].sortkey, name);
87
88        linebuf = statfilebufs[ cur_line].line;
89
90        /* if this entry's fromfile != cmpfile,
91         * the subscribing machine needs to know:
92         */
93        same_name = ! strcmp( path[ NAME], c->name);
94
95        /* to choose printing format, convert type-bits to array-index:
96         * the formats specify 3-7 arguments, according to type:
97         */
98        format = mode_to_fmt(type);
99
100        sprintf( linebuf, format, name, same_name ? '=' : '~' , curr1,
101                 UID( *s), GID( *s), MODE( *s), extra);
102
103        cur_line++;
104
105        if ( verboseflag)
106                fputs( linebuf, stderr);
107
108        if ( same_name);
109        else if ( ! lstat( path[ ROOT], &fromstat))
110                type = TYPE( fromstat);
111        else {
112                sprintf( errmsg, "(write_statline) can't lstat %s\n",
113                         path[ ROOT]);
114                do_panic();
115        }
116        return( type);
117}
118
119fake_link( root, name, c) char *root, *name; struct currentness *c; {
120
121        /* it is difficult to fool write_statline(),
122         * update_file(), and curr_diff() all at once.
123         * write_statline() can't take a normal currency in this case,
124         * because it can't know the subscriber's fromroot.
125         * curr_diff() needs to see a normal link's currency,
126         * or else unneccessary link-updates will occur.
127         * update_file() can use a normal currency to make a link.
128         */
129        if ( name != c->name)           /* speed hack for dec_statfile() */
130                strcpy( c->name, name);
131
132        if ( *root)
133                sprintf( c->link, "%s/%s", root, name);
134        else    *c->link = '\0';        /* special case for write_statline() */
135
136        c->cksum = 0;
137        clear_stat( &c->sbuf);
138        c->sbuf.st_mode = S_IFLNK;
139}
140
141sort_stat() {
142        int i;
143
144        /* NOTE: this qsort call assumes that each statfilebufs[] element
145         * begins with a sortkey as its first field.
146         */
147        qsort( (char *) statfilebufs, cur_line,
148                sizeof( statfilebufs[ 0]), strcmp);
149
150        for ( i = 0; i < cur_line; i++) {
151                fputs( statfilebufs[ i].line, statfile);
152        }
153}
154
155sort_entries() {
156        char *tail;
157        Table list;
158        Entry *A, *C;
159        int i, j;
160
161        list.table = NULL;
162        list.shift = 0;
163
164        /* NOTE: we assume that each entry begins with a sortkey string.
165         * don't include entries[ 0] in the sort:
166         */
167        qsort( (char *)& entries[ 1], entrycnt - 1,
168               sizeof(   entries[ 1]), strcmp);
169
170        /* for each entry's fromfile (call it A),
171         * look for A's children amongst the subsequent entries,
172         * and add any that you find to A's exception-table.
173         * note that this may overfill the hash-table; in this event,
174         * we accept the performance-hit, and don't try to rehash.
175         */
176        for (     A = &entries[ i = 1  ]; i < entrycnt; A = &entries[ ++i]) {
177            for ( C = &entries[ j = i+1]; j < entrycnt; C = &entries[ ++j]) {
178                switch(  keyncmp( C->sortkey, i)) {
179                case 1:  break;                 /* get next A */
180                case 0:  tail = C->fromfile + A->keylen;
181                         while( '/' == *tail) tail++;
182                         if ( A->names.table)
183                             store( add_list_elt( tail, DONT_TRACK, NULL),
184                                    &A->names);
185                         else add_list_elt( tail, DONT_TRACK, LIST( list));
186                case -1: continue;      /* unlikely */
187                }
188                break; /* get next A */
189            }
190            /* if A doesn't already have an exception-list of names,
191             * then we've accumulated the list of descendant-entries
192             * in the list{} structure; convert it to a hash-table for A:
193             */
194            if ( ! list.table);
195            else if ( ! A->names.table) {
196                     A->names.table = list.table;
197                     A->names.shift = list.shift;
198                     list.table = NULL;
199                     list.shift = 0;
200                     list2hashtable( &A->names);
201            }
202            else {
203                     sprintf(errmsg, "sort_entries: internal error\n");
204                     do_panic();
205            }
206        }
207}
208
209/*
210 * Decode a statfile line into its individual fields.
211 * setup TYPE(), UID(), GID(), MODE(), TIME(), & RDEV() contents.
212 */
213
214char *
215dec_statfile( line, c)
216char *line; struct currentness *c;
217{
218        struct stat *s;
219        int *extra = 0;
220        char *end, *format, *name, same_name, type;
221        int dummy, *curr1 = &dummy, d = 0, u = 0, g = 0, m = 0;
222
223        /* these long-int temps are necessary for pc/rt compatibility:
224         * sscanf cannot scan into a short, though it may sometimes succeed
225         * in doing so. the difficulty is that it can't know about the
226         * target-integer's length, so it assumes that it's long.
227         * since the rt will truncate a short's addr, in order to treat
228         * it as a long, sscanf's data will often get lost.
229         * the solution is to give scanf longs, and then to convert these
230         * longs to shorts, explicitly.
231         */
232
233        /* for speed, we laboriously parse the type-independent part
234         * of the statline without calling sscanf().
235         * thus, we avoid copying the pathname strings around,
236         * since the caller can re-use the originals,
237         * once they're broken out of the line-format.
238         */
239        type = *line++;
240        name = line;
241        line = strchr( line, ' ');
242        if ( ! line) {
243                sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line);
244                sprintf( errmsg, "line has only one field\n");
245                do_panic();
246        }
247        *line++ = '\0';
248
249        /* in the  statfile, which contains only relative pathnames,
250         * "/" is the only pathname which can begin with a slash.
251         * in entries[], the root appears as "", which is more natural,
252         * because "" is "/"'s pathname relative to the mount-point fromroot.
253         * and because pushpath() prepends slashes in the right places, anyway.
254         * "" is hard to read with sscanf(), so we handle "/" specially:
255         */
256        if ( '/' != *name);
257        else if ( ! name[ 1]) *name = '\0';
258        else {
259                sprintf(errmsg, "statfile passed an absolute pathname: %s\n",
260                        name);
261                do_gripe();
262        }
263        /* the subscriber needs to know whether the currency-data
264         * came from the remote fromfile or from the remote cmpfile.
265         */
266        switch( same_name = *line++) {
267        case '=': strcpy( c->name, name);
268                  break;
269        case '~': *c->name = '\0';
270                  break;
271        default:
272                sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line);
273                sprintf( errmsg, "bad equality flag = %c\n", same_name);
274                do_panic();
275        }
276        *c->link = '\0';
277        c->cksum = 0;
278
279        /*
280         * set up scanf arg's for type-dependent currency-data:
281         */
282
283        s = &c->sbuf;
284        clear_stat( s);
285
286        switch( type) {
287        case 'f':
288                s->st_mode = S_IFREG;
289                curr1 = ( int*)&c->cksum;
290                extra = ( int*)&s->st_mtime;
291                break;
292        case 'l':       /* more common than dir's */
293                s->st_mode = S_IFLNK;
294                if ( end = strchr( line, '\n'))
295                        *end = '\0';
296                else {
297                        sprintf( errmsg, "garbled statfile: bad line =\n%s\n",
298                                line);
299                        sprintf( errmsg, "line doesn't end with a newline\n");
300                        do_panic();
301                }
302                if ( !*line) fake_link( fromroot, c->name, c);
303                else strcpy( c->link, line);
304                break;
305        case 'd':
306                s->st_mode = S_IFDIR;
307                curr1 = &dummy;
308                break;
309        case 'b':
310                s->st_mode = S_IFBLK;
311                curr1 = &d;
312                break;
313        case 'c':
314                s->st_mode = S_IFCHR;
315                curr1 = &d;
316                break;
317        default:
318                sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line);
319                sprintf( errmsg, "first char isn't a file-type [fldbc]\n");
320                do_panic();
321        }
322        /* if we've already parsed the line,
323         * as in S_IFLNK case, skip the sscanf call:
324         */
325        if ( *(format = mode_to_rfmt(s->st_mode)))
326                sscanf( line, format, curr1, &u, &g, &m, extra);
327
328        s->st_uid   = (short) u;
329        s->st_gid   = (short) g;
330        s->st_mode |= (short) m & 07777;
331        s->st_rdev  = (short) d;
332        return( name);
333}
334
335/* the match abstractions implement a synchronized traversal
336 * of the entries[] array & the statfile. both sets of data
337 * are sorted by strcmp() on their sortkeys, which are made
338 * via the KEYCPY macro.
339 */
340
341/* XXX: not the best, but not the worst approach either.
342 * this  allows main() to call readstat() twice, with correct initialization.
343 */
344int prev_ent = 0;;
345
346int cur_ent = 0;        /* not global! index into entries[]. */
347
348init_next_match() {
349        prev_ent = 0;
350        cur_ent = 1;
351}
352
353int
354get_next_match( name)
355char *name;
356{
357        char key[ LINELEN];
358
359        KEYCPY( key, name);
360
361        for ( ; cur_ent < entrycnt; cur_ent++)
362                switch ( keyncmp( key, cur_ent)) {
363                case 0:  return( cur_ent);
364                case -1: return( 0);            /* advance name's    key */
365                case 1:  continue;              /* advance cur_ent's key */
366                }
367
368        return( -1); /* out of entries */
369}
370
371/* last_match:
372 * we know that path begins with entnum's fromfile,
373 * but path may also match other entries.
374 * for example, the path /a/b/c matches the entries /a & /a/b.
375 * we want the longest entry that matches.
376 * look-ahead to find last sub-list entry that matches
377 * the current pathname: this works because by keyncmp,
378 *          /   is greater than         /Z...
379 *         |    matches                 /a
380 *         |    is greater than         /a/a
381 *         \    matches                 /a/b
382 * /a/b/c  <    is greater than         /a/b/a
383 *         /    matches                 /a/b/c
384 *         |    is less than            /a/b/c/a
385 *         |    is less than            /a/b/d
386 *          \   is less than            /a/c...
387 * NOTE that the right-hand column is sorted by key,
388 * and that the last match is the longest.
389 */
390int
391last_match( path, entnum) char *path; int entnum; {
392        int i;
393        char key[ LINELEN];
394
395        KEYCPY( key, path);
396        for ( i = entnum + 1; i < entrycnt; i++) {
397                switch ( keyncmp( key, i)) {
398                case -1: break;         /* quit at   /a/b/d */
399                case 0:  entnum = i;    /* remember  /a or /a/b */
400                case 1:  continue;      /* skip over /a/a */
401                }
402                break; /* quit loop */
403        }
404        return( entnum);
405}
406
407/* compare the path r with i's sortkey in the following way:
408 * if the key is a subpath of r, return 0, as a match.
409 * if the key is < or > r, return 1 or -1  respectively.
410 */
411int
412keyncmp( r, i) char *r; int i; {
413        char *l;
414        int diff, n;
415
416        l = entries[ i].sortkey;
417        n = entries[ i].keylen;
418
419        diff = SIGN( strncmp( r, l, n));
420
421        /* if diff == 0 & n != 0 ( l isn't root) &
422         * r[n] == '\0' or '\001', we have a match.
423         * if n == 0, l is root, which matches everything.
424         */
425        return( diff? diff: n ? ( (unsigned) r[n] > '\001') : 0);   /* XXX */
426}
427
428struct currentness *
429dec_entry( entnum, fr, to, cmp, tail)
430int entnum; char *fr[], *to[], *cmp[], *tail; {
431        static struct currentness currency_buf, *entry_currency;
432        int i;
433        Entry *e;
434        static int xref_flag = 0;
435
436        /* this routine's main purpose is to transfer entnum's contents
437         * to the paths fr, to, & cmp.
438         * for efficiency and data-hiding reasons, we maintain various static
439         * data, including the currentness data for readstat's last update.
440         */
441
442        /* we avoid calling get_currentness(cmp) redundantly, but not just
443         * for efficiency reasons: this helps our update-simulation for
444         * nopullflag-support. see update_file().
445         * each cmpfile may get updated at most once;
446         * if it does, update_file() will mark its currency "out-of-date".
447         * xref_flag is set if any entry uses another entry's file
448         * as a cmpfile. in this case, we have to search entries[]
449         * to see if what we've modified is another entry's cmpfile.
450         * if so, propagate the "out-of-date" mark to that entry.
451         */
452        if ( xref_flag && updated( &currency_buf, NULL)) {
453                for ( e = &entries[ i = 1]; i <= entrycnt; e = &entries[ ++i]) {
454                        if ( updated( &e->currency, NULL) ||
455                             strcmp(   e->cmpfile,   currency_buf.name));
456                        else updated( &e->currency, &currency_buf);
457                }
458        }
459        currency_buf.sbuf.st_mode = S_IFMT;     /* kill short-term data. */
460
461        if (    prev_ent != entnum) {
462                prev_ent =  entnum;
463
464                /* a subtler, longer search would set this flag less often.
465                 */
466                if ( ! writeflag)
467                        xref_flag = strncmp( entries[ entnum].cmpfile,
468                                             entries[ entnum].tofile,
469                                     strlen( entries[ entnum].tofile));
470
471                poppath( fr); pushpath( fr,  entries[ entnum].fromfile);
472                poppath( to); pushpath( to,  entries[ entnum].tofile);
473                poppath(cmp); pushpath( cmp, entries[ entnum].cmpfile);
474               
475                entry_currency = &entries[ entnum].currency;
476        }
477        if ( updated( entry_currency, NULL))
478                get_currentness( cmp, entry_currency);
479
480        if ( ! tail || ! *tail )
481                return( entry_currency);
482
483        if ( S_IFDIR == TYPE( entry_currency->sbuf)) { /* usual case */
484                pushpath( cmp, tail);
485                get_currentness( cmp, &currency_buf);
486                poppath( cmp);
487                return( &currency_buf);
488        }
489        /* rarely, a directory may have a non-dir as its cmpfile.
490         * if the entry's cmpfile isn't a dir, then cmp[ ROOT],
491         * is the comparison-file for each of the tofile's dependents.
492         */
493        return( entry_currency);
494}
495
496/* these routines handle a stack of pointers into a character-string,
497 * which typically is a UNIX pathname.
498 * the first element CNT of the stack points to a depth-counter.
499 * the second element ROOT points to the beginning of the pathname string.
500 * subsequent elements point to terminal substrings of the pathname.
501 * a slash precedes each element.
502 */
503#define COUNT(p) (*(int*)p[CNT])
504char **
505initpath( name) char *name; {
506        char **p;
507
508        /* for each stack-element, alloc a pointer
509         * and 15 chars for a filename:
510         */
511        p =      (char **) malloc( stackmax * sizeof NULL);
512        p[ CNT] = (char *) malloc( stackmax * 15 + sizeof ((int) 0));
513        COUNT( p) = 1;
514        p[ ROOT] = p[ CNT] + sizeof ((int) 1);
515        strcpy( p[ ROOT], name);
516        p[ NAME] = p[ ROOT] + strlen( name);
517        return( p);
518}
519int
520pushpath( p, name) char **p; char *name; {
521        char *top;
522
523        if ( ! p) return( -1);
524        if ( ++COUNT( p) >= stackmax) {
525                sprintf( errmsg, "%s\n%s\n%s %d.\n",
526                        "path stack overflow: directory too deep:", p[ ROOT],
527                        "use -S option, with value >", stackmax);
528                do_panic();
529        }
530        if ( *name) *p[ COUNT( p)]++ = '/';
531        top = p[ COUNT( p)];
532        strcpy( top, name);
533        p[ COUNT( p) + 1] = top + strlen( top);
534        return( COUNT( p));
535}
536poppath( p) char **p; {
537        if ( ! p) return;
538        else if ( 1 >= COUNT( p)) {
539                sprintf(errmsg,"can't pop root from path-stack");
540                do_panic();
541        }
542        else if ( *p[ COUNT( p)]) p[ COUNT( p)]--;
543                /* non-null last elt; remove its initial slash */
544        *p[  COUNT( p)--] = '\0';
545        return;
546}
Note: See TracBrowser for help on using the repository browser.