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