source: trunk/athena/etc/track/update.c @ 1471

Revision 1471, 16.6 KB checked in by don, 36 years ago (diff)
bellcore copyright.
Line 
1/*
2 *      $Source: /afs/dev.mit.edu/source/repository/athena/etc/track/update.c,v $
3 *      $Header: /afs/dev.mit.edu/source/repository/athena/etc/track/update.c,v 4.5 1988-09-19 20:27:49 don Exp $
4 *
5 *      $Log: not supported by cvs2svn $
6 * Revision 4.4  88/06/13  16:27:24  don
7 * fixed a bug in device-handling: if device major/minor #'s are wrong,
8 * have to delete the node & call mknode().
9 *
10 * Revision 4.3  88/06/10  15:55:29  don
11 * fixed a bug in device-handling: update  was triggered by differences in
12 * st_dev, rather than st_rdev.
13 *
14 * Revision 4.2  88/05/27  20:15:21  don
15 * fixed two bugs at once: the nopulflag bug, and -I wasn't preserving
16 * mode-bits at all. see update().
17 *
18 * Revision 4.1  88/05/25  21:42:09  don
19 * added -I option. needs compatible track.h & track.c.
20 *
21 * Revision 4.0  88/04/14  16:43:30  don
22 * this version is not compatible with prior versions.
23 * it offers, chiefly, link-exporting, i.e., "->" systax in exception-lists.
24 * it also offers sped-up exception-checking, via hash-tables.
25 * a bug remains in -nopullflag support: if the entry's to-name top-level
26 * dir doesn't exist, update_file doesn't get over it.
27 * the fix should be put into the updated() routine, or possibly dec_entry().
28 *
29 * Revision 3.0  88/03/09  13:17:18  don
30 * this version is incompatible with prior versions. it offers:
31 * 1) checksum-handling for regular files, to detect filesystem corruption.
32 * 2) more concise & readable "updating" messages & error messages.
33 * 3) better update-simulation when nopullflag is set.
34 * 4) more support for non-default comparison-files.
35 * finally, the "currentness" data-structure has replaced the statbufs
36 * used before, so that the notion of currency is more readily extensible.
37 * note: the statfile format has been changed.
38 *
39 * Revision 2.2  88/01/29  18:24:18  don
40 * bug fixes. also, now track can update the root.
41 *
42 * Revision 2.1  87/12/03  17:33:18  don
43 * fixed lint warnings.
44 *
45 * Revision 2.1  87/12/01  20:56:26  don
46 * robustified file-updating, so that more failure-checking is done,
47 * and so that if either utimes() or  set_prots() fails, the other will
48 * still run.  made double-sure that protections & time don't
49 * change, unless copy-file succeeds.
50 *
51 * Revision 2.0  87/11/30  15:19:35  don
52 * general rewrite; got rid of stamp data-type, with its attendant garbage,
53 * cleaned up pathname-handling. readstat & writestat now sort overything
54 * by pathname, which simplifies traversals/lookup. should be comprehensible
55 * now.
56 *
57 * Revision 1.2  87/09/02  17:44:20  shanzer
58 * Aborts if We get a write error when copying a file..
59 *
60 * Revision 1.1  87/02/12  21:16:00  rfrench
61 * Initial revision
62 *
63 */
64
65#ifndef lint
66static char
67*rcsid_header_h = "$Header: /afs/dev.mit.edu/source/repository/athena/etc/track/update.c,v 4.5 1988-09-19 20:27:49 don Exp $";
68#endif lint
69
70#include "bellcore-copyright.h"
71#include "mit-copyright.h"
72
73#include "track.h"
74#include <sys/errno.h>
75
76/* this array converts stat()'s type-bits to a character string:
77 * to make the index, right-shift the st_mode field by 13 bits.
78 */
79static char *type_str[] = { "ERROR", "char-device", "directory",
80        "block-device", "file", "symlink", "socket(ERROR)", "nonexistent"
81};
82
83#define DIFF( l, r, field) (short)(((l).sbuf.field) != ((r).sbuf.field))
84
85struct currentness *
86currency_diff( l, r) struct currentness *l, *r; {
87        static struct currentness d;
88        struct stat *s;
89        int diff;
90
91        /* we fill the difference structure d with boolean flags,
92         * a field being set indicating that l & r differ in that field.
93         */
94        s = &d.sbuf;
95
96        if ( ignore_prots) s->st_mode = UID( *s) = GID( *s) = 0;
97        else {
98            s->st_mode  = DIFF( *l, *r, st_mode & ~S_IFMT);     /* prot bits */
99            UID( *s)    = DIFF( *l, *r, st_uid);
100            GID( *s)    = DIFF( *l, *r, st_gid);
101        }
102        s->st_mode |= DIFF( *l, *r, st_mode & S_IFMT) << 12;    /* type bits */
103
104        diff = UID( *s) || GID( *s) || s->st_mode;
105
106        TIME( *s) = 0;
107        RDEV( *s) = 0;
108        *d.link = '\0';
109        d.cksum = 0;
110
111        switch ( TYPE( r->sbuf)) {
112
113        case S_IFREG:
114                TIME( *s) = DIFF( *l, *r, st_mtime);
115                diff |= (uflag == DO_CLOBBER) || TIME( *s);
116                diff |= d.cksum = ( cksumflag && l->cksum != r->cksum);
117        case S_IFDIR: break;
118
119        case S_IFLNK:
120                *d.link = (char)( 0 != strcmp( r->link, l->link));
121                diff = (int) *d.link;
122                break;
123
124        case S_IFCHR:
125        case S_IFBLK:
126                RDEV( *s) = DIFF( *l, *r, st_rdev);
127                diff |= RDEV( *s);
128                break;
129
130        case S_IFMT:
131                return ( NULL);
132        default:
133                sprintf(errmsg,"bad string passed to update\n");
134                do_panic();
135                break;
136        }
137
138        return( diff? &d : NULL);
139}
140int
141update_file(l, lpath,
142            r, rpath)
143char **lpath, **rpath;
144struct currentness *r, *l;
145{
146        char *remotename = rpath[ ROOT], *localname = lpath[ ROOT];
147        struct currentness *diff;
148        struct stat lstat, *local_statp;
149        struct timeval *timevec;
150        static List_element *missing_dirs;
151        List_element *p;
152        unsigned int oumask;
153        unsigned int exists, local_type, remote_type, same_name;
154
155        diff = currency_diff( l, r);
156
157        /* if the cmpfile doesn't exist, and tofile != cmpfile,
158         * give up, since we want to be conservative about updating.
159         */
160        same_name = ! strcmp( l->name, lpath[ NAME]);
161
162        if ( S_IFMT == TYPE( l->sbuf) && ! same_name) {
163                sprintf( errmsg, "%s%s.\n%s%s.\n",
164                         "nonexistent local comparison-file ", l->name,
165                         "trying to update from ", rpath[ ROOT]);
166                errno = 0;
167                do_gripe();
168                return( -1);
169        }
170        /* either: cmpfile != tofile, and cmpfile exists,
171         *     or: cmpfile == tofile, & the file might exist or not.
172         */
173        local_statp = same_name ? &l->sbuf :
174                     (*statf)( lpath[ ROOT], &lstat) ? NULL : &lstat;
175        local_type = local_statp ? TYPE( *local_statp) : S_IFMT;
176        exists = local_type != S_IFMT;
177
178        /* that diff == NULL doesn't mean localname exists,
179         * since l may represent a different file.
180         */
181        if ( ! diff && exists) return( -1);
182
183        if ( verboseflag) banner( remotename, localname, r, l, diff);
184
185        /* speed hack: when we can, we avoid re-extracting remote currentness.
186         * reasons that we can't:
187         * 1) if fromfile != the remote cmpfile,
188         * we need to extract fromfile's real currency info.
189         * 2) if nopullflag is set, we need to simulate real update of files.
190         * 3) if fromfile is a dir or device, we need to ensure that it's real.
191         * XXX: for detecting case 1, dec_statfile() makes
192         *      strcmp( r->name, rpath[ NAME]) unnecessary:
193         *      if '~' was in statline, r->name contains "".
194         *      if '=' was in statline, r->name contains rpath[ NAME].
195         */
196        remote_type = TYPE( r->sbuf);
197        do {
198            if ( ! *r->name); /* remote_type isn't necessarily rpath's type. */
199            else switch( remote_type) {
200                 case S_IFREG: if ( nopullflag) break;   /* call get_curr(). */
201                 case S_IFLNK: continue; /* leave do block, skip get_curr(). */
202                 default:      break;    /* dir's & dev's must exist. */
203            }
204            if ( get_currentness( rpath, r)) {
205                sprintf( errmsg,
206                         "master-copy doesn't exist:\n\t%s should be a %s.\n",
207                         rpath[ ROOT], type_str[ remote_type >> 13]);
208                do_gripe();
209
210                /* for nopullflag,
211                 * maintain list of dir's whose creation would fail,
212                 * so we can see whether lpath's parent "exists".
213                 * the only predictable reason that we wouldn't create a dir
214                 * is if the corresponding remote-dir doesn't exist.
215                 */
216                if ( remote_type == S_IFDIR) {
217                         pushpath( lpath, "");  /* append slash */
218                         add_list_elt( lpath[ ROOT], 0, &missing_dirs);
219                         poppath( lpath);       /* remove slash */
220                }
221                return( -1);
222            }
223            else remote_type = TYPE( r->sbuf);
224        } while( 0); /* just once */
225
226        /* if cmpfile == tofile, then we're updating the cmpfile.
227         * just in case this cmpfile is some entry's top-level cmpfile,
228         * we need to update that entry's currency-info for dec_entry().
229         * because dec_entry() reuses each entry's currencies repeatedly.
230         */
231        if ( same_name) updated( l, r);
232
233        if ( ! nopullflag);
234        else if ( S_IFDIR == remote_type || exists)
235            return(-1);
236        else {
237            /* simulate findparent():
238             * search missing_dirs list for ancestors of lpath.
239             * we must search the whole list,
240             * because we can't assume that the list is in recognizable order:
241             * the list contains tofile's, sorted in order of decreasing
242             * fromfile-name.
243             */
244            for ( p = missing_dirs; p; p = NEXT( p))
245                if ( ! strncmp( lpath[ ROOT], TEXT( p), strlen( TEXT( p)))) {
246                    sprintf(errmsg,"%s %s,\n\t%s %s.\n",
247                            "would't find parent directory for", localname,
248                            "because track previously failed to create",
249                            TEXT( p));
250                    do_gripe();
251                    break;
252                }
253            return(-1);
254        }
255        /* if tofile is supposed to be a dir,
256         * we can create its whole path if necessary;
257         * otherwise, its parent must exist:
258         */
259        if ( S_IFDIR == remote_type || exists);
260        else if ( findparent( localname)) {
261                sprintf(errmsg,"can't find parent directory for %s",
262                        localname);
263                do_gripe();
264                return(-1);
265        }
266        /* if fromfile & tofile aren't of the same type,
267         * delete tofile, and record the deed.
268         */
269        if ( local_type == remote_type);
270        else if ( exists && removeit( localname, local_type)) return( -1);
271        else exists = 0;
272
273        /* at this stage, we know that if localfile still exists,
274         * it has the same type as its remote counterpart.
275         */
276
277        switch ( remote_type) {
278
279        case S_IFREG:
280                /* the stat structure happens to contain
281                 * a timevec structure, because of the spare integers
282                 * that follow each of the time fields:
283                 */
284                r->sbuf.st_atime = TIME( r->sbuf);
285                timevec = (struct timeval *) &r->sbuf.st_atime; /* XXX */
286
287                /* only transfer the file if the contents seem to differ.
288                 * only call utimes() if copy_file() succeeds.
289                 * it's important to call utimes before set_prots.
290                 * if the file doesn't exist, always use remote protections.
291                 */
292                if ( !( diff->cksum || TIME( diff->sbuf)));
293                else if ( copy_file( remotename, localname)) return( -1);
294                else      utimes(    localname, timevec);
295
296                /* if we're ignoring protections (-I option),
297                 * restore the old local file-protections,
298                 * unless this update created the file.
299                 */
300                return( exists && ignore_prots ?
301                        set_prots( localname, local_statp) :
302                        set_prots( localname, &r->sbuf));
303
304        case S_IFLNK:
305                if ( exists && removeit( localname, S_IFLNK))
306                        return( -1);
307
308                oumask = umask(0); /* Symlinks don't really have modes */
309                if ( symlink( r->link, localname)) {
310                        sprintf(errmsg,"can't create symbolic link %s -> %s\n",
311                                localname, r->link);
312                        do_gripe();
313                        umask(oumask);
314                        return(-1);
315                }
316                umask(oumask);
317                return( 0);
318
319        case S_IFDIR:
320                /* if we're ignoring protections (-I option),
321                 * keep the old local file-protections,
322                 * unless this update creates the dir.
323                 */
324                return(   ! exists ?       makepath(  localname, &r->sbuf)
325                        : ! ignore_prots ? set_prots( localname, &r->sbuf)
326                        : 0 );
327        case S_IFBLK:
328        case S_IFCHR:
329                /* need to recompute the device#-difference,
330                 * in case r or l has been recomputed since currency_diff()
331                 * first computed it.
332                 */
333                if ( ! exists || ! DIFF( *r, *l, st_rdev));
334                else if ( exists = removeit( localname, local_type))
335                        return( -1);
336
337                if ( !exists && mknod( localname, remote_type,RDEV( r->sbuf))){
338                        sprintf(errmsg, "can't make device %s\n", localname);
339                        do_gripe();
340                        return(-1);
341                }
342                /* if we're ignoring protections (-I option),
343                 * keep the old local file-protections,
344                 * unless this update creates the device.
345                 */
346                return( exists && ignore_prots ?
347                        0 : set_prots( localname, &r->sbuf));
348
349        case S_IFMT: /* should have been caught already. */
350                sprintf( errmsg, "fromfile %s doesn't exist.\n", remotename);
351                do_gripe();
352                return( -1);
353        default:
354                sprintf(errmsg, "unknown file-type in update_file()\n");
355                do_gripe();
356                return(-1);
357        }
358        /*NOTREACHED*/
359        /*
360        sprintf( errmsg, "ERROR (update_file): internal error.\n");
361        do_panic();
362        return( -1);
363        */
364}
365
366updated( cmp, fr) struct currentness *cmp, *fr; {
367
368        if ( ! fr) return( ! TYPE( cmp->sbuf));
369
370        else if ( nopullflag) {
371                /* simulate cmpfile's update for dec_entry().
372                 * we do this for every cmpfile, because we can't
373                 * tell whether this cmpfile represents an entry.
374                 */
375                cmp->sbuf.st_mode  = fr->sbuf.st_mode;
376                cmp->sbuf.st_uid   = fr->sbuf.st_uid;
377                cmp->sbuf.st_gid   = fr->sbuf.st_gid;
378                cmp->sbuf.st_rdev  = fr->sbuf.st_rdev;
379                cmp->sbuf.st_mtime = fr->sbuf.st_mtime;
380                cmp->cksum         = fr->cksum;
381                strcpy( cmp->link,   fr->link);
382        }
383        else    /* usual case. mark the currency as "out-of-date",
384                 * dec_entry() will refresh the entry's currency,
385                 * if it sees this mark.
386                 * this an efficiency hack; dec_entry() will seldom
387                 * see this mark, because most cmpfiles aren't at top-level.
388                 */
389                cmp->sbuf.st_mode &= ~S_IFMT;
390
391        return( 0);
392}
393
394get_currentness( path, c) char **path; struct currentness *c; {
395        strcpy( c->name, path[ NAME]);
396        c->cksum = 0;
397        *c->link = '\0';
398        if ( (*statf)( path[ ROOT], &c->sbuf)) {
399                clear_stat( &c->sbuf);
400                c->sbuf.st_mode = S_IFMT;       /* XXX */
401                if ( errno != ENOENT) {
402                        sprintf( errmsg,"can't %s comparison-file %s\n",
403                                 statn, path[ ROOT]);
404                        do_gripe();
405                }
406                return( -1);
407        }
408        switch ( TYPE( c->sbuf)) {
409        case S_IFREG:
410                if ( writeflag || cksumflag)
411                        c->cksum = in_cksum( path[ ROOT], &c->sbuf);
412                break;
413        case S_IFLNK:
414                if ( follow_link( path[ ROOT], c->link)) {
415                        *c->link = '\0';
416                        return( -1);
417                }
418                break;
419        default:
420                break;
421        }
422        return( 0);
423}
424
425clear_stat( sp) struct stat *sp; {
426        int *p;
427
428        /* it happens that  a stat is 16 long integers;
429         * we exploit this fact for speed.
430         */
431        for ( p = (int *)sp + sizeof( struct stat) / 4; --p >= (int *)sp;)
432                *p = 0;
433}
434
435int
436set_prots( name, r)
437char *name;
438struct stat *r;
439{
440        struct stat sbuf;
441        int error = 0;
442
443        if ( (*statf)( name, &sbuf)) {
444                sprintf( errmsg, "(set_prots) can't %s &s\n", statn, name);
445                do_gripe();
446                return(-1);
447        }
448        if (( UID( sbuf) != UID( *r) || GID( sbuf) != GID( *r)) &&
449             chown( name,   UID( *r),   GID( *r)) ) {
450                sprintf( errmsg, "can't chown file %s %d %d\n",
451                         name, UID( *r), GID( *r));
452                do_gripe();
453                error = 1;
454        }
455        if ( MODE( sbuf) != MODE( *r) && chmod( name,  MODE( *r)) ) {
456                sprintf( errmsg, "can't chmod file %s %o\n", name, MODE( *r));
457                do_gripe();
458                error = 1;
459        }
460        return( error);
461}
462
463copy_file(from,to)
464char *from,*to;
465{
466        char buf[MAXBSIZE],temp[LINELEN];
467        int cc, fdf, fdt;
468
469        fdf = open(from,O_RDONLY);
470        if ( 0 > fdf) {
471                sprintf(errmsg,"can't open input file %s\n",from);
472                do_gripe();
473                return(-1);
474        }
475        sprintf( temp,"%s_trk.tmp",to);
476
477        if ( 0 <= ( fdt = open( temp, O_WRONLY | O_CREAT)));
478
479        else if ( errno == ENOSPC) {
480                /* creates will fail before writes do,
481                 * when the disk is full.
482                 */
483                sprintf( errmsg, "no room for temp file %s\n", temp);
484                do_panic();
485        }
486        else {
487                sprintf(errmsg,"can't open temporary file %s\n",temp);
488                do_gripe();
489                close( fdf);
490                return(-1);
491        }
492        while ( 0 < ( cc  = read(  fdf, buf, sizeof buf)))
493                if  ( cc != write( fdt, buf, cc))
494                        break;
495
496        close(fdf);
497        close(fdt);
498
499        switch( SIGN( cc)) {
500        case 0: break;
501        case -1:
502                unlink( temp);
503                sprintf( errmsg,"error while reading file %s\n",from);
504                do_gripe();
505                return(-1);
506        case 1:
507                unlink( temp);
508                sprintf( errmsg,"error while writing file %s\n",temp);
509                do_panic();
510        }
511        if ( rename( temp, to)) { /* atomic! */
512                sprintf( errmsg, "rename( %s, %s) failed!\n", temp, to);
513                do_panic();
514        }
515        return (0);
516}
517
518banner( rname, lname, r, l, d)
519char *rname, *lname;
520struct currentness *r, *l, *d;
521{
522        unsigned int n = 0, m = 0;
523        struct stat *ds = &d->sbuf, *ls = &l->sbuf, *rs = &r->sbuf;
524        char *format, *ltype, *rtype, *p;
525        char fill[ LINELEN], *lfill = "", *rfill = "";
526        int dlen;
527
528        ltype = type_str[ TYPE( *ls) >> 13];
529        rtype = type_str[ TYPE( *rs) >> 13];
530
531        dlen =  strlen( lname) - strlen( rname) +
532                strlen( ltype) - strlen( rtype);
533       
534        /* we need to align the ends of the filenames,
535         * "Updating ... lname" &
536         * "    from ... rname".
537         */
538       
539        if ( dlen < 0) {
540                dlen *= -1;
541                lfill = fill;
542        }
543        else    rfill = fill;
544
545        for ( p = fill ; dlen > 0; --dlen) *p++ = ' ';
546        *p = '\0';
547
548        if        ( TYPE( *ds)) {
549                format =        "%s %s%s %s\n"; }
550        else if   ( TIME( *ds)) {
551                n = TIME( *ls);
552                m = TIME( *rs);
553                format =        "%s %s%s %s ( mod-time=%d)\n"; }
554        else if   (     *d->link) {
555                n = (int)l->link;
556                m = (int)r->link;
557                format =        "%s %s%s %s ( symlink -> %s)\n"; }
558        else if   ( d->cksum) {
559                n = l->cksum;
560                m = r->cksum;
561                format =        "%s %s%s %s ( file-cksum=%4.x)\n"; }
562        else if   (  RDEV( *ds)) {
563                n =  RDEV( *ls);
564                m =  RDEV( *rs);
565                format =        "%s %s%s %s ( device-type=%d)\n"; }
566        else if   ( MODE( *ds)) {
567                n = MODE( *ls);
568                m = MODE( *rs);
569                format =        "%s %s%s %s ( mode-bits=%4.o)\n"; }
570        else if   (  UID( *ds)) {
571                n =  UID( *ls);
572                m =  UID( *rs);
573                format =        "%s %s%s %s ( user-id=%d)\n"; }
574        else if   (  GID( *ds)) {
575                n =  GID( *ls);
576                m =  GID( *rs);
577                format =        "%s %s%s %s ( group-id=%d)\n"; }
578
579        fprintf( stderr, format, "Updating", ltype, lfill, lname, n);
580        fprintf( stderr, format, "    from", rtype, rfill, rname, m);
581}
Note: See TracBrowser for help on using the repository browser.