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