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