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

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