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

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