afsconfig-and-rcsid-all-around-20010705
[openafs.git] / src / venus / up.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afsconfig.h>
12
13 RCSID("$Header$");
14
15 /* missing type from C language */
16 #define Boolean short
17 #define true 1
18 #define false 0
19
20 #include <errno.h>
21 #ifdef  AFS_AIX32_ENV
22 #include <signal.h>
23 #undef  _NONSTD_TYPES
24 #endif
25 #include <stdio.h>
26 #include <afs/afs_args.h>
27 #include <sys/param.h>
28 #ifdef  AFS_SUN5_ENV
29 #include <fcntl.h>
30 #endif
31 #include <sys/file.h>
32 #include <sys/stat.h>
33 #include <dirent.h>
34 #include <sys/time.h>
35 #define VIRTUE
36 #define VICE
37 #include <sys/ioctl.h>
38 #include <afs/vice.h>
39 #undef VIRTUE
40 #undef VICE
41 #include <sys/ioctl.h>
42 #include <netdb.h>      
43 #ifdef HAVE_NETINET_IN_H
44 #include <netinet/in.h>
45 #endif
46 #include <afs/venus.h>
47
48 /* ************************************************************* */
49
50 #define MAXACL 400
51
52 extern char *index ();
53 extern char *rindex ();
54 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
55 extern sys_nerr;
56 extern char *sys_errlist[];
57 #endif
58
59 Boolean   verbose = false;
60 Boolean   renameTargets = false;
61 Boolean   oneLevel = false;
62 Boolean   preserveDate = true;  
63 Boolean   preserveMountPoints = false;
64 Boolean   forceOverwrite = false;
65
66 int   pageSize;
67 Boolean setacl = true;
68 Boolean oldAcl = false;
69 char file1[MAXPATHLEN], file2[MAXPATHLEN];
70
71 #define MAXSIZE 2048
72 static char space[MAXSIZE];
73
74 struct OldAcl {
75     int nplus;
76     int nminus;
77     int offset;
78     char data[1];
79 };
80
81 /* ************************************************************ */
82 /*                                                               */
83 /* main program                                                  */
84 /*                                                               */
85 /* ************************************************************ */
86
87 #include "AFS_component_version_number.c"
88
89 main(argc, argv)
90     int argc;
91     char *argv[];
92
93 {
94 #ifdef  AFS_AIX32_ENV
95     /*
96      * The following signal action for AIX is necessary so that in case of a 
97      * crash (i.e. core is generated) we can include the user's data section 
98      * in the core dump. Unfortunately, by default, only a partial core is
99      * generated which, in many cases, isn't too useful.
100      */
101     struct sigaction nsa;
102     
103     sigemptyset(&nsa.sa_mask);
104     nsa.sa_handler = SIG_DFL;
105     nsa.sa_flags = SA_FULLDUMP;
106     sigaction(SIGSEGV, &nsa, NULL);
107 #endif
108 #if !defined (AFS_AIX_ENV) && !defined (AFS_HPUX_ENV)
109     pageSize = getpagesize();
110 #endif
111     ScanArgs(argc, argv);
112     
113     /* now read each line of the CopyList */
114     if (Copy(file1, file2, !oneLevel, 0))
115         exit(1);        /* some type of failure */
116
117     exit(0);
118 }
119
120
121 ScanArgs(argc, argv)
122     int argc;
123     char *argv[];
124
125 {
126     /* skip program name */
127     argc--, argv++;
128     
129     /* check for -flag options */
130     while (argc > 0 && *argv[0] == '-') {
131         char *cp = *argv;
132
133         switch (*++cp) {
134           case 'v':
135             verbose = true;
136             break;
137             
138           case '1':
139             oneLevel = true;
140             break;
141             
142           case 'r':
143             renameTargets = true;
144             break;
145             
146           case 'f':
147             forceOverwrite = true;
148             break;
149             
150           case 'x':
151             preserveDate = false;
152             break;
153             
154           case 'm':
155             preserveMountPoints = true;
156             break;
157             
158           default: 
159             fprintf(stderr, "Unknown option: '%c'\n", *cp);
160             fprintf(stderr, "usage: up [-v1frxm] from to\n");
161             exit(1);
162         }
163         argc--, argv++;
164     }
165
166     if (argc != 2) {
167         fprintf(stderr, "usage: up [-v1frx] from to\n");
168         exit(1);
169     }
170
171     strncpy(file1, argv[0], MAXPATHLEN);
172     strncpy(file2, argv[1], MAXPATHLEN);
173
174 } /*ScanArgs*/
175
176
177
178 /*
179  * MakeParent
180  *      Make sure the parent directory of this file exists.  Returns
181  *      true if it exists, false otherwise.  Note: the owner argument
182  *      is a hack.  All directories made will have this owner.
183  */
184 Boolean MakeParent(file, owner)
185     char *file;
186     afs_int32 owner;
187 {
188     char  parent[MAXPATHLEN];
189     char *p;
190     struct stat s;
191     
192     strcpy(parent, file);
193     
194     p = rindex(parent, '/');
195     if (!p) {
196         strcpy(parent, ".");
197     }
198     else if (p > parent) {
199         *p = '\0';
200     }
201     else {
202         p[1] = '\0';
203     }
204     
205     if (stat(parent, &s) < 0) {
206         if (!MakeParent(parent, owner))
207             return(false);
208
209         if (verbose) {
210             printf("Creating directory %s\n", parent);
211             fflush(stdout);
212         }
213
214         mkdir(parent, 0777);
215         chown(parent, owner, -1);
216     }
217     return(true);
218 } /*MakeParent*/
219
220
221 /*
222  * Copy
223  *      This does the bulk of the work of the program.  Handle one file,
224  *      possibly copying subfiles if this is a directory
225  */
226 Copy(file1, file2, recursive, level)
227     char *file1;        /* input file name */
228     char *file2;        /* output file name */
229     Boolean recursive;  /* true if directory should be copied */
230     int level;          /* level of recursion: 0, 1, ... */
231
232 {
233     struct stat s1, s2; /*Stat blocks*/
234     struct ViceIoctl blob;
235     char aclspace[MAXACL];
236     afs_int32 rcode = 0, code;
237     int   goods2 = 1;
238
239     code = lstat(file1, &s1);
240     if (code < 0) {
241        fprintf(stderr,"Can't find %s\n",file1);
242        return 1;
243     }
244
245     code = lstat(file2, &s2);
246     if (code < 0) {
247        if (!MakeParent(file2,s1.st_uid))
248           return 0;
249        goods2 = 0;
250     }
251
252     if ((s1.st_mode & S_IFMT) == S_IFREG) {
253        /*
254         * -------------------- Copy regular file --------------------
255         */
256        int    f1, f2, n;
257        char   buf[4096];                        /* Must be bigger than sizeof (*head) */
258        struct timeval tv[2];
259        char   tmpfile[MAXPATHLEN], newName[MAXPATHLEN];
260         
261        if (verbose) {
262           printf("Level %d: File %s to %s\n", level, file1, file2);
263           fflush(stdout);
264        }
265
266        /* Wonder if there is a security hole */
267        if ( ((s1.st_mode & 04002) == 04002) ||
268             ((s1.st_mode & 04020) == 04020) ||
269             ((s1.st_mode & 02002) == 02002) ) {
270           fprintf(stderr, "WARNING: Mode-bits security hole in files %s and %s\n",
271                   file1, file2);
272        }
273
274        if (!goods2 || (s1.st_mtime != s2.st_mtime) || (s1.st_size != s2.st_size)) { /*c*/
275           /* Don't ovewrite a write protected file (unless force: -f) */
276           if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
277              fprintf(stderr,
278                      "File %s is write protected against its owner; not changed\n",
279                      file2);
280              return 1;
281           }
282
283           if (verbose) {
284              printf("  Copy file %s to %s (%u Bytes)\n", file1, file2, s1.st_size);
285              fflush(stdout);
286           }
287
288           strcpy(tmpfile, file2);        /* Name of temporary file */
289           strcat(tmpfile,".UPD");
290
291           /* open file1 for input */
292           f1 = open(file1, O_RDONLY);
293           if (f1 < 0) {
294              fprintf(stderr, "Unable to open input file %s, ", file1);
295              if (errno >= sys_nerr)
296                 fprintf(stderr, "error code = %d\n", errno);
297              else
298                 fprintf(stderr, "%s\n", sys_errlist[errno]);
299              return 1;
300           }
301
302           /* open temporary output file */
303           f2 = open(tmpfile, (O_WRONLY | O_CREAT | O_TRUNC), s1.st_mode);
304           if (f2 < 0) {
305              fprintf(stderr, "Unable to open output file %s, ", tmpfile);
306              if (errno >= sys_nerr)
307                 fprintf(stderr, "error code = %d\n", errno);
308              else
309                 fprintf(stderr, "%s\n", sys_errlist[errno]);
310              fflush(stdout);
311              close(f1);
312              return 1;
313            }
314         
315           /* Copy file1 to temporary file */
316           while ((n = read(f1, buf, sizeof(buf))) > 0) {
317              if (write(f2, buf, n) != n) {
318                 fprintf(stderr,"Write failed, file %s must be copied again.\n", file2);
319              }
320           }
321
322           /* preserve access and modification times: ("-x" disables)*/
323           if (preserveDate) {
324              tv[0].tv_sec = s1.st_atime;
325              tv[0].tv_usec = 0;
326              tv[1].tv_sec = s1.st_mtime;
327              tv[1].tv_usec = 0;
328              utimes(tmpfile, tv);
329           }
330
331           /* Close the files */
332           code = close(f1);
333           code = close(f2);
334           if (code < 0) {
335              perror("close ");
336              rcode = 1;
337           }
338
339           /* Rename file2 to file2.old. [-r] */
340           if (renameTargets && goods2) {
341              strcpy(newName, file2);
342              strcat(newName, ".old");
343              if (verbose) {
344                 printf("  Renaming %s to %s\n", file2, newName);
345                 fflush(stdout);
346              }
347              if (rename(file2, newName) < 0) {
348                 fprintf(stderr, "Rename of %s to %s failed.\n", file2, newName);
349              }
350           }
351
352           /* Rename temporary file to file2 */
353           code = rename(tmpfile, file2);
354           if (code < 0) {
355              fprintf(stderr, "Rename of %s to %s failed.\n", tmpfile, file2);
356              return 1;
357           }
358
359           /* Re-stat file2 and compare file sizes */
360           code = lstat(file2, &s2);
361           if (code < 0) {
362              fprintf(stderr, "WARNING: Unable to stat new file %s\n", file2);
363              return 1;
364           }
365           if (s1.st_size != s2.st_size) {
366              fprintf(stderr, "WARNING: New file %s is %u bytes long; should be %u\n",
367                     file2, s2.st_size, s1.st_size);
368           }
369        } /*c*/
370
371        /* Set the user-id */
372        if (s2.st_uid != s1.st_uid) {
373           if (verbose) {
374              printf("  Set owner-id for %s to %d\n", file2, s1.st_uid);
375              fflush(stdout);
376           }
377           code = chown(file2, s1.st_uid, -1);
378           if (code) {
379              fprintf(stderr, "Unable to set owner-id for %s to %d\n", file2, s1.st_uid);
380              fflush(stdout);
381              rcode = 1;
382              s1.st_mode &= ~04000;       /* Don't set suid bit */
383           }
384        }
385
386        /* Set the group-id */
387        if (s2.st_gid != s1.st_gid) {
388           if (verbose) {
389              printf("  Set group-id for %s to %d\n", file2, s1.st_gid);
390              fflush(stdout);
391           }
392           code = chown(file2, -1, s1.st_gid);
393           if (code) {
394              fprintf(stderr, "Unable to set group-id for %s to %d\n", file2, s1.st_gid);
395              fflush(stdout);
396              rcode = 1;
397              s1.st_mode &= ~02000;       /* Don't set sgid bit */
398           }
399        }
400
401        /* Set the mode bits */
402        if (s1.st_mode != s2.st_mode) {
403           if (verbose) {
404              printf("  Set mode-bit for %s to %o\n", file2, (s1.st_mode & 07777));
405              fflush(stdout);
406           }
407           code = chmod(file2, s1.st_mode);
408           if (code) {
409              fprintf(stderr, "Unable to set mode-bits for %s to %d\n", file2, s1.st_mode);
410              rcode = 1;
411           }
412        }
413     } /* regular file */
414
415     else if ((s1.st_mode & S_IFMT) == S_IFLNK) {
416        /*
417         * --------------------- Copy symlink  --------------------
418         */
419        char linkvalue[MAXPATHLEN+1];
420        int  n;
421
422        if (verbose) {
423           printf("Level %d: Symbolic link %s to %s\n", level, file1, file2);
424           fflush(stdout);
425        }
426        
427        /* Don't ovewrite a write protected directory (unless force: -f) */
428        if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
429           fprintf(stderr,
430                   "Link %s is write protected against its owner; not changed\n",
431                   file2);
432           return 1;
433        }
434        
435        if (verbose) {
436           printf("  Copy symbolic link %s->%s to %s\n", file1, linkvalue, file2);
437           fflush(stdout);
438        }
439        
440        n = readlink(file1, linkvalue, sizeof(linkvalue));
441        if (n == -1) {
442           fprintf(stderr, "Could not read symbolic link %s\n", file1);
443           perror("read link ");
444           return 1;
445        }
446        linkvalue[n] = 0;
447
448        unlink(file2);   /* Always make the new link (it was easier) */
449
450        code = symlink(linkvalue, file2);
451        if (code == -1) {
452           fprintf(stderr, "Could not create symbolic link %s\n", file2);
453           perror("create link ");
454           return 1;
455        }
456     } /*Dealing with symlink*/
457     
458     else if ( preserveMountPoints && (code=isMountPoint( file1, &blob )) ) {
459        /*
460         * --------------------- Copy mount point  --------------------
461         */
462
463        if ( code > 1 ) {
464           perror("checking for mount point ");
465           return 1;
466        }
467        if (verbose) {
468           printf("Level %d: Mount point %s to %s\n", level, file1, file2);
469           fflush(stdout);
470        }
471        
472        /* Don't ovewrite a write protected directory (unless force: -f) */
473        if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
474           fprintf(stderr,
475                   "Target %s is write protected against its owner; not changed\n",
476                   file2);
477           return 1;
478        }
479        
480        if (verbose) {
481           printf("  Copy mount point %s for vol %s to %s\n", file1, blob.out, file2);
482           fflush(stdout);
483        }
484        
485        unlink(file2);   /* Always make the new link (it was easier) */
486
487        strcat(blob.out, ".");           /* stupid convention; these end with a period */
488        code = symlink(blob.out, file2);
489        if (code == -1) {
490           fprintf(stderr, "Could not create mount point %s for vol %s\n", file2, blob.out);
491           perror("create mount point ");
492           return 1;
493        }
494         
495     } /*Dealing with mount point*/
496
497     else if (((s1.st_mode & S_IFMT) == S_IFDIR) && (recursive || (level == 0))) {
498        /*
499         * ----------------------- Copy directory -----------------------
500         */
501        DIR           *dir;
502        int           tfd, code, i;
503        struct OldAcl *oacl;
504        char          tacl[MAXACL];
505        char          f1[MAXPATHLEN], f2[MAXPATHLEN];
506        char          *p1, *p2;
507        struct dirent *d;
508
509        if (verbose) {
510           printf("Level %d: Directory %s to %s\n", level, file1, file2);
511           fflush(stdout);
512        }
513
514        /* Don't ovewrite a write protected directory (unless force: -f) */
515        if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
516           fprintf(stderr,
517                   "Directory %s is write protected against its owner; not changed\n",
518                   file2);
519           return 1;
520        }
521
522        strcpy(f1, file1);
523        strcpy(f2, file2);
524        p1 = f1 + strlen(f1);
525        p2 = f2 + strlen(f2);
526        if (p1 == f1 || p1[-1] != '/')
527           *p1++ = '/';
528        if (p2 == f2 || p2[-1] != '/')
529           *p2++ = '/';
530
531        dir = opendir(file1);
532        if (dir == NULL) {
533           fprintf(stderr, "Couldn't open %s\n", file1);
534           return 1;
535        }
536
537        while ((d = readdir(dir)) != NULL) {
538           if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
539              continue;
540           strcpy(p1, d->d_name);
541           strcpy(p2, d->d_name);
542           code = Copy(f1, f2, recursive, level + 1);
543           if (code && !rcode) rcode = 1;                 /* remember errors */
544        }
545
546        closedir(dir);
547
548        if (verbose) {
549           printf("Level %d: Copied directory %s to %s\n", level, file1, file2);
550           fflush(stdout);
551        }
552
553        mkdir(file2, 0777);      /* Handle case where MakeParent not invoked. */
554
555        if (verbose) {
556           printf("  Set owner-id for %s to %d\n", file2, s1.st_uid);
557           fflush(stdout);
558        }
559        code = chown(file2, s1.st_uid, -1);
560        if (code) {
561           fprintf(stderr, "Unable to set owner-id for %s to %d\n", file2, s1.st_uid);
562           fflush(stdout);
563           s1.st_mode &= ~04000;       /* Don't set suid bit */
564        }
565
566        if (verbose) {
567           printf("  Set group-id for %s to %d\n", file2, s1.st_gid);
568           fflush(stdout);
569        }
570        code = chown(file2, -1, s1.st_gid);
571        if (code) {
572           fprintf(stderr, "Unable to set group-id for %s to %d\n", file2, s1.st_gid);
573           fflush(stdout);
574           s1.st_mode &= ~02000;       /* Don't set sgid bit */
575        }
576
577        if (verbose) {
578           printf("  Set mode-bit for %s to %o\n", file2, (s1.st_mode & 07777));
579           fflush(stdout);
580        }
581        code = chmod(file2, s1.st_mode);
582        if (code) {
583           fprintf(stderr, "Unable to set mode-bits for %s to %d\n", file2, s1.st_mode);
584           fflush(stdout);
585           rcode = 1;
586        }
587
588        if (setacl == true) {
589           if (verbose) {
590              printf("  Set acls for %s\n", file2);
591              fflush(stdout);
592           }
593
594           blob.in       = aclspace;
595           blob.out      = aclspace;
596           blob.in_size  = 0;
597           blob.out_size = MAXACL;
598
599           if (oldAcl) {
600              /* Get an old-style ACL and convert it */
601              for (i=1; i<strlen(file1); i++)
602                 if (file1[i] == '/') break;
603              strcpy(aclspace, &file1[i]);
604
605              blob.in_size = 1+strlen(aclspace);
606              tfd = open(file1, O_RDONLY, 0);
607              if (tfd < 0) {
608                 perror("old-acl open ");
609                 return 1;
610              }
611              code = ioctl(tfd, _VICEIOCTL(4), &blob);
612              close(tfd);
613              if (code < 0) {
614                 if (errno == EINVAL) {
615                    setacl = false;
616                 }
617                 else {
618                    return 1;
619                 }
620              }
621              /* Now convert the thing. */
622              oacl = (struct OldAcl *) (aclspace+4);
623              sprintf(tacl, "%d\n%d\n", oacl->nplus, oacl->nminus);
624              strcat(tacl, oacl->data);
625              strcpy(aclspace, tacl);
626           } /*Grab and convert old-style ACL*/
627           else {
628              /* Get a new-style ACL */
629              code = pioctl(file1, _VICEIOCTL(2), &blob, 1);
630              if (code < 0) {
631                 if (errno == EINVAL) {
632                    setacl = false;
633                 }
634                 else {
635                    perror("getacl ");
636                    return 1;
637                 }
638              }
639           } /*Grab new-style ACL*/
640
641           /*
642            * Now, set the new-style ACL.
643            */
644           if (setacl == true) {
645              blob.out      = aclspace;
646              blob.in       = aclspace;
647              blob.out_size = 0;
648              blob.in_size  = 1+strlen(aclspace);
649              code = pioctl(file2, _VICEIOCTL(1), &blob, 1);
650              if (code) {
651                 if (errno == EINVAL) {
652                    setacl = false;
653                 }
654                 else {
655                    fprintf(stderr, "Couldn't set acls for %s\n", file2);
656                    return 1;
657                 }
658              }
659           }
660
661           if (setacl == false) {
662              printf("Not setting acls\n");
663           }
664        }
665     }
666
667     return rcode;
668 } /*Copy*/
669
670
671 int isMountPoint( name, blob )
672     char *name;
673     struct ViceIoctl *blob;
674 {
675     afs_int32  code;
676     char true_name[1024];               /*dirname*/
677     char parent_dir[1024];              /*Parent directory of true name*/
678     char *last_component;               /*Last component of true name*/
679
680     sprintf(true_name, "%s%s",
681             (name[0] == '/') ? "" : "./",
682             name);
683
684     /*
685      * Find rightmost slash, if any.
686      */
687     last_component = (char *) rindex(true_name, '/');
688     if (last_component) {
689         /*
690          * Found it.  Designate everything before it as the parent directory,
691          * everything after it as the final component.
692          */
693         strncpy(parent_dir, true_name, last_component - true_name);
694         parent_dir[last_component - true_name] = 0;
695         last_component++;   /*Skip the slash*/
696     }
697     else {
698         /*
699          * No slash appears in the given file name.  Set parent_dir to the current
700          * directory, and the last component as the given name.
701          */
702         strcpy(parent_dir, ".");
703         last_component = true_name;
704     }
705
706     if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
707         fprintf(stderr, "up: you may not use '.' or '..' as the last component\n");
708         fprintf(stderr, "up: of a name in the 'up' command.\n");
709         return 3;
710     }
711
712     blob->in = last_component;
713     blob->in_size = strlen(last_component)+1;
714     blob->out_size = MAXSIZE;
715     blob->out = space;
716     bzero(space, MAXSIZE);
717
718     code = pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, blob, 0);
719
720     if (code == 0) {
721         printf("'%s' is a mount point for volume '%s'\n", name, space);
722         fflush(stdout);
723         return 1;
724     }
725     else {
726         if (errno == EINVAL) {
727             /* printf( "'%s' is not a mount point.\n", name);
728              * fflush(stdout);
729              */
730             return 0;
731         }
732         else {
733             fprintf( stderr, "problem examining '%s' in '%s'.\n", last_component, parent_dir );
734             return 2;
735             /* Die(errno, (ti->data ? ti->data : parent_dir));
736              */
737         }
738     }
739     return 4;
740 }