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