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