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