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