Import Upstream version 1.8.5
[hcoop/debian/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 AFS_PIOCTL_MAXSIZE
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 if (mkdir(parent, 0777))
188 return (0);
189 if (chown(parent, owner, -1))
190 return (0);
191 }
192 return (1);
193 } /*MakeParent */
194
195
196 /*
197 * Copy
198 * This does the bulk of the work of the program. Handle one file,
199 * possibly copying subfiles if this is a directory
200 */
201 static int
202 Copy(char *file1, char *file2, short recursive, int level)
203 {
204 struct stat s1, s2; /*Stat blocks */
205 struct ViceIoctl blob;
206 char aclspace[MAXACL];
207 afs_int32 rcode = 0, code;
208 int goods2 = 1;
209
210 code = lstat(file1, &s1);
211 if (code < 0) {
212 fprintf(stderr, "Can't find %s\n", file1);
213 return 1;
214 }
215
216 code = lstat(file2, &s2);
217 if (code < 0) {
218 if (!MakeParent(file2, s1.st_uid))
219 return 0;
220 goods2 = 0;
221 }
222
223 if ((s1.st_mode & S_IFMT) == S_IFREG) {
224 /*
225 * -------------------- Copy regular file --------------------
226 */
227 int f1, f2, n;
228 char buf[4096]; /* Must be bigger than sizeof (*head) */
229 struct timeval tv[2];
230 char tmpfile[MAXPATHLEN], newName[MAXPATHLEN];
231
232 if (verbose) {
233 printf("Level %d: File %s to %s\n", level, file1, file2);
234 fflush(stdout);
235 }
236
237 /* Wonder if there is a security hole */
238 if (((s1.st_mode & 04002) == 04002) || ((s1.st_mode & 04020) == 04020)
239 || ((s1.st_mode & 02002) == 02002)) {
240 fprintf(stderr,
241 "WARNING: Mode-bits security hole in files %s and %s\n",
242 file1, file2);
243 }
244
245 if (!goods2 || (s1.st_mtime != s2.st_mtime) || (s1.st_size != s2.st_size)) { /*c */
246 /* Don't ovewrite a write protected file (unless force: -f) */
247 if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
248 fprintf(stderr,
249 "File %s is write protected against its owner; not changed\n",
250 file2);
251 return 1;
252 }
253
254 if (verbose) {
255 printf(" Copy file %s to %s (%u Bytes)\n", file1, file2,
256 (unsigned int) s1.st_size);
257 fflush(stdout);
258 }
259
260 strlcpy(tmpfile, file2, sizeof tmpfile); /* Name of temporary file */
261 strlcat(tmpfile, ".UPD", sizeof tmpfile);
262
263 /* open file1 for input */
264 f1 = open(file1, O_RDONLY);
265 if (f1 < 0) {
266 fprintf(stderr, "Unable to open input file %s: %s\n",
267 file1, strerror(errno));
268 return 1;
269 }
270
271 /* open temporary output file */
272 f2 = open(tmpfile, (O_WRONLY | O_CREAT | O_TRUNC), s1.st_mode);
273 if (f2 < 0) {
274 fprintf(stderr, "Unable to open output file %s: %s\n",
275 tmpfile, strerror(errno));
276 fflush(stdout);
277 close(f1);
278 return 1;
279 }
280
281 /* Copy file1 to temporary file */
282 while ((n = read(f1, buf, sizeof(buf))) > 0) {
283 if (write(f2, buf, n) != n) {
284 fprintf(stderr,
285 "Write failed, file %s must be copied again.\n",
286 file2);
287 }
288 }
289
290 /* preserve access and modification times: ("-x" disables) */
291 if (preserveDate) {
292 tv[0].tv_sec = s1.st_atime;
293 tv[0].tv_usec = 0;
294 tv[1].tv_sec = s1.st_mtime;
295 tv[1].tv_usec = 0;
296 utimes(tmpfile, tv);
297 }
298
299 /* Close the files */
300 code = close(f1);
301 if (code < 0) {
302 perror("close ");
303 rcode = 1;
304 }
305 code = close(f2);
306 if (code < 0) {
307 perror("close ");
308 rcode = 1;
309 }
310
311 /* Rename file2 to file2.old. [-r] */
312 if (renameTargets && goods2) {
313 strlcpy(newName, file2, sizeof newName);
314 strlcat(newName, ".old", sizeof newName);
315 if (verbose) {
316 printf(" Renaming %s to %s\n", file2, newName);
317 fflush(stdout);
318 }
319 if (rename(file2, newName) < 0) {
320 fprintf(stderr, "Rename of %s to %s failed.\n", file2,
321 newName);
322 }
323 }
324
325 /* Rename temporary file to file2 */
326 code = rename(tmpfile, file2);
327 if (code < 0) {
328 fprintf(stderr, "Rename of %s to %s failed.\n", tmpfile,
329 file2);
330 return 1;
331 }
332
333 /* Re-stat file2 and compare file sizes */
334 code = lstat(file2, &s2);
335 if (code < 0) {
336 fprintf(stderr, "WARNING: Unable to stat new file %s\n",
337 file2);
338 return 1;
339 }
340 if (s1.st_size != s2.st_size) {
341 fprintf(stderr,
342 "WARNING: New file %s is %u bytes long; should be %u\n",
343 file2, (unsigned int) s2.st_size,
344 (unsigned int) s1.st_size);
345 }
346 }
347
348 /*c */
349 /* Set the user-id */
350 if (s2.st_uid != s1.st_uid) {
351 if (verbose) {
352 printf(" Set owner-id for %s to %d\n", file2, s1.st_uid);
353 fflush(stdout);
354 }
355 code = chown(file2, s1.st_uid, -1);
356 if (code) {
357 fprintf(stderr, "Unable to set owner-id for %s to %d\n",
358 file2, s1.st_uid);
359 fflush(stdout);
360 rcode = 1;
361 s1.st_mode &= ~04000; /* Don't set suid bit */
362 }
363 }
364
365 /* Set the group-id */
366 if (s2.st_gid != s1.st_gid) {
367 if (verbose) {
368 printf(" Set group-id for %s to %d\n", file2, s1.st_gid);
369 fflush(stdout);
370 }
371 code = chown(file2, -1, s1.st_gid);
372 if (code) {
373 fprintf(stderr, "Unable to set group-id for %s to %d\n",
374 file2, s1.st_gid);
375 fflush(stdout);
376 rcode = 1;
377 s1.st_mode &= ~02000; /* Don't set sgid bit */
378 }
379 }
380
381 /* Set the mode bits */
382 if (s1.st_mode != s2.st_mode) {
383 if (verbose) {
384 printf(" Set mode-bit for %s to %o\n", file2,
385 (s1.st_mode & 07777));
386 fflush(stdout);
387 }
388 code = chmod(file2, s1.st_mode);
389 if (code) {
390 fprintf(stderr, "Unable to set mode-bits for %s to %d\n",
391 file2, s1.st_mode);
392 rcode = 1;
393 }
394 }
395 }
396 /* regular file */
397 else if ((s1.st_mode & S_IFMT) == S_IFLNK) {
398 /*
399 * --------------------- Copy symlink --------------------
400 */
401 char linkvalue[MAXPATHLEN + 1];
402 int n;
403
404 if (verbose) {
405 printf("Level %d: Symbolic link %s to %s\n", level, file1, file2);
406 fflush(stdout);
407 }
408
409 /* Don't ovewrite a write protected directory (unless force: -f) */
410 if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
411 fprintf(stderr,
412 "Link %s is write protected against its owner; not changed\n",
413 file2);
414 return 1;
415 }
416
417 if (verbose) {
418 printf(" Copy symbolic link %s->%s to %s\n", file1, linkvalue,
419 file2);
420 fflush(stdout);
421 }
422
423 n = readlink(file1, linkvalue, sizeof(linkvalue)-1);
424 if (n == -1) {
425 fprintf(stderr, "Could not read symbolic link %s\n", file1);
426 perror("read link ");
427 return 1;
428 }
429 linkvalue[n] = 0;
430
431 unlink(file2); /* Always make the new link (it was easier) */
432
433 code = symlink(linkvalue, file2);
434 if (code == -1) {
435 fprintf(stderr, "Could not create symbolic link %s\n", file2);
436 perror("create link ");
437 return 1;
438 }
439 }
440 /*Dealing with symlink */
441 else if (preserveMountPoints && (code = isMountPoint(file1, &blob))) {
442 /*
443 * --------------------- Copy mount point --------------------
444 */
445
446 if (code > 1) {
447 perror("checking for mount point ");
448 return 1;
449 }
450 if (verbose) {
451 printf("Level %d: Mount point %s to %s\n", level, file1, file2);
452 fflush(stdout);
453 }
454
455 /* Don't ovewrite a write protected directory (unless force: -f) */
456 if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
457 fprintf(stderr,
458 "Target %s is write protected against its owner; not changed\n",
459 file2);
460 return 1;
461 }
462
463 if (verbose) {
464 printf(" Copy mount point %s for vol %s to %s\n", file1,
465 blob.out, file2);
466 fflush(stdout);
467 }
468
469 unlink(file2); /* Always make the new link (it was easier) */
470
471 strcat(blob.out, "."); /* stupid convention; these end with a period */
472 code = symlink(blob.out, file2);
473 if (code == -1) {
474 fprintf(stderr, "Could not create mount point %s for vol %s\n",
475 file2, blob.out);
476 perror("create mount point ");
477 return 1;
478 }
479
480 }
481 /*Dealing with mount point */
482 else if (((s1.st_mode & S_IFMT) == S_IFDIR)
483 && (recursive || (level == 0))) {
484 /*
485 * ----------------------- Copy directory -----------------------
486 */
487 DIR *dir;
488 int tfd, code, i;
489 struct OldAcl *oacl;
490 char tacl[MAXACL];
491 char f1[MAXPATHLEN], f2[MAXPATHLEN];
492 char *p1, *p2;
493 struct dirent *d;
494 struct timeval tv[2];
495
496 if (verbose) {
497 printf("Level %d: Directory %s to %s\n", level, file1, file2);
498 fflush(stdout);
499 }
500
501 /* Don't ovewrite a write protected directory (unless force: -f) */
502 if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
503 fprintf(stderr,
504 "Directory %s is write protected against its owner; not changed\n",
505 file2);
506 return 1;
507 }
508
509 strlcpy(f1, file1, sizeof f1);
510 strlcpy(f2, file2, sizeof f2);
511 p1 = f1 + strlen(f1);
512 p2 = f2 + strlen(f2);
513 if (p1 == f1 || p1[-1] != '/')
514 *p1++ = '/';
515 if (p2 == f2 || p2[-1] != '/')
516 *p2++ = '/';
517
518 dir = opendir(file1);
519 if (dir == NULL) {
520 fprintf(stderr, "Couldn't open %s\n", file1);
521 return 1;
522 }
523
524 while ((d = readdir(dir)) != NULL) {
525 if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
526 continue;
527 strlcpy(p1, d->d_name, sizeof f1 - (p1 - f1));
528 strlcpy(p2, d->d_name, sizeof f2 - (p2 - f2));
529 code = Copy(f1, f2, recursive, level + 1);
530 if (code && !rcode)
531 rcode = 1; /* remember errors */
532 }
533
534 closedir(dir);
535
536 if (verbose) {
537 printf("Level %d: Copied directory %s to %s\n", level, file1,
538 file2);
539 fflush(stdout);
540 }
541
542 mkdir(file2, 0777); /* Handle case where MakeParent not invoked. */
543
544 if (verbose) {
545 printf(" Set owner-id for %s to %d\n", file2, s1.st_uid);
546 fflush(stdout);
547 }
548 code = chown(file2, s1.st_uid, -1);
549 if (code) {
550 fprintf(stderr, "Unable to set owner-id for %s to %d\n", file2,
551 s1.st_uid);
552 fflush(stdout);
553 s1.st_mode &= ~04000; /* Don't set suid bit */
554 }
555
556 if (verbose) {
557 printf(" Set group-id for %s to %d\n", file2, s1.st_gid);
558 fflush(stdout);
559 }
560 code = chown(file2, -1, s1.st_gid);
561 if (code) {
562 fprintf(stderr, "Unable to set group-id for %s to %d\n", file2,
563 s1.st_gid);
564 fflush(stdout);
565 s1.st_mode &= ~02000; /* Don't set sgid bit */
566 }
567
568 if (verbose) {
569 printf(" Set mode-bit for %s to %o\n", file2,
570 (s1.st_mode & 07777));
571 fflush(stdout);
572 }
573 code = chmod(file2, s1.st_mode);
574 if (code) {
575 fprintf(stderr, "Unable to set mode-bits for %s to %d\n", file2,
576 s1.st_mode);
577 fflush(stdout);
578 rcode = 1;
579 }
580
581 if (setacl == 1) {
582 if (verbose) {
583 printf(" Set acls for %s\n", file2);
584 fflush(stdout);
585 }
586
587 blob.in = aclspace;
588 blob.out = aclspace;
589 blob.in_size = 0;
590 blob.out_size = MAXACL;
591
592 if (oldAcl) {
593 /* Get an old-style ACL and convert it */
594 if (verbose) {
595 printf(" Getting old style acl\n");
596 fflush(stdout);
597 }
598
599 for (i = 1; i < strlen(file1); i++)
600 if (file1[i] == '/')
601 break;
602 strlcpy(aclspace, &file1[i], sizeof aclspace);
603
604 blob.in_size = 1 + strlen(aclspace);
605 tfd = open(file1, O_RDONLY, 0);
606 if (tfd < 0) {
607 perror("old-acl open ");
608 return 1;
609 }
610 code = ioctl(tfd, _VICEIOCTL(4), &blob);
611 close(tfd);
612 if (code < 0) {
613 if (errno == EINVAL) {
614 setacl = 0;
615 if (verbose) {
616 printf(" _VICEIOCTL(4) returns EINVAL\n");
617 fflush(stdout);
618 }
619 } else {
620 return 1;
621 }
622 }
623 /* Now convert the thing. */
624 oacl = (struct OldAcl *)(aclspace + 4);
625 sprintf(tacl, "%d\n%d\n", oacl->nplus, oacl->nminus);
626 strlcat(tacl, oacl->data, sizeof tacl);
627 strlcpy(aclspace, tacl, sizeof aclspace);
628 } /*Grab and convert old-style ACL */
629 else {
630 /* Get a new-style ACL */
631 if (verbose) {
632 printf(" Getting new style acl\n");
633 fflush(stdout);
634 }
635
636 code = pioctl(file1, _VICEIOCTL(2), &blob, 1);
637 if (code < 0) {
638 if (errno == EINVAL) {
639 setacl = 0;
640 if (verbose) {
641 printf(" _VICEIOCTL(2) returns EINVAL\n");
642 fflush(stdout);
643 }
644 } else {
645 perror("getacl ");
646 return 1;
647 }
648 }
649 } /*Grab new-style ACL */
650
651 /*
652 * Now, set the new-style ACL.
653 */
654 if (setacl == 1) {
655 if (verbose) {
656 printf(" Setting new style acl\n");
657 fflush(stdout);
658 }
659 blob.out = aclspace;
660 blob.in = aclspace;
661 blob.out_size = 0;
662 blob.in_size = 1 + strlen(aclspace);
663 code = pioctl(file2, _VICEIOCTL(1), &blob, 1);
664 if (code) {
665 if (errno == EINVAL) {
666 setacl = 0;
667 if (verbose) {
668 printf(" _VICEIOCTL(1) returns EINVAL\n");
669 fflush(stdout);
670 }
671 } else {
672 fprintf(stderr, "Couldn't set acls for %s\n", file2);
673 return 1;
674 }
675 }
676 }
677
678 if (setacl == 0) {
679 printf("Not setting acls\n");
680 }
681 }
682
683 /* preserve access and modification times: ("-x" disables) */
684 if (preserveDate) {
685 tv[0].tv_sec = s1.st_atime;
686 tv[0].tv_usec = 0;
687 tv[1].tv_sec = s1.st_mtime;
688 tv[1].tv_usec = 0;
689 utimes(file2, tv);
690 }
691 }
692
693 return rcode;
694 } /*Copy */
695
696
697 static int
698 isMountPoint(char *name, struct ViceIoctl *blob)
699 {
700 afs_int32 code;
701 char true_name[1024]; /*dirname */
702 char parent_dir[1024]; /*Parent directory of true name */
703 char *last_component; /*Last component of true name */
704
705 sprintf(true_name, "%s%s", (name[0] == '/') ? "" : "./", name);
706
707 /*
708 * Find rightmost slash, if any.
709 */
710 last_component = (char *)strrchr(true_name, '/');
711 if (last_component) {
712 /*
713 * Found it. Designate everything before it as the parent directory,
714 * everything after it as the final component.
715 */
716 strncpy(parent_dir, true_name, last_component - true_name);
717 parent_dir[last_component - true_name] = 0;
718 last_component++; /*Skip the slash */
719 } else {
720 /*
721 * No slash appears in the given file name. Set parent_dir to the current
722 * directory, and the last component as the given name.
723 */
724 strlcpy(parent_dir, ".", sizeof parent_dir);
725 last_component = true_name;
726 }
727
728 if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
729 fprintf(stderr,
730 "up: you may not use '.' or '..' as the last component\n");
731 fprintf(stderr, "up: of a name in the 'up' command.\n");
732 return 3;
733 }
734
735 blob->in = last_component;
736 blob->in_size = strlen(last_component) + 1;
737 blob->out_size = AFS_PIOCTL_MAXSIZE;
738 blob->out = space;
739 memset(space, 0, AFS_PIOCTL_MAXSIZE);
740
741 code = pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, blob, 0);
742
743 if (code == 0) {
744 printf("'%s' is a mount point for volume '%s'\n", name, space);
745 fflush(stdout);
746 return 1;
747 } else {
748 if (errno == EINVAL) {
749 /* printf( "'%s' is not a mount point.\n", name);
750 * fflush(stdout);
751 */
752 return 0;
753 } else {
754 fprintf(stderr, "problem examining '%s' in '%s'.\n",
755 last_component, parent_dir);
756 return 2;
757 /* Die(errno, (ti->data ? ti->data : parent_dir));
758 */
759 }
760 }
761 }