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