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