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