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