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

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