de24918264c5293d63ca79b091165d991749f0a5
[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 /* missing type from C language */
11 #define Boolean short
12 #define true 1
13 #define false 0
14
15 /* ************************************************************* */
16
17 #include <afs/param.h>
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 <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
39 /* ************************************************************* */
40
41 #define MAXACL 400
42
43 extern char *index ();
44 extern char *rindex ();
45 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
46 extern sys_nerr;
47 extern char *sys_errlist[];
48 #endif
49
50 Boolean   verbose = false;
51 Boolean   renameTargets = false;
52 Boolean   oneLevel = false;
53 Boolean   preserveDate = true;  
54 Boolean   forceOverwrite = false;
55
56 int   pageSize;
57 Boolean setacl = true;
58 Boolean oldAcl = false;
59 char file1[MAXPATHLEN], file2[MAXPATHLEN];
60
61 struct OldAcl {
62     int nplus;
63     int nminus;
64     int offset;
65     char data[1];
66 };
67
68 /* ************************************************************ */
69 /*                                                               */
70 /* main program                                                  */
71 /*                                                               */
72 /* ************************************************************ */
73
74 #include "AFS_component_version_number.c"
75
76 main(argc, argv)
77     int argc;
78     char *argv[];
79
80 {
81 #ifdef  AFS_AIX32_ENV
82     /*
83      * The following signal action for AIX is necessary so that in case of a 
84      * crash (i.e. core is generated) we can include the user's data section 
85      * in the core dump. Unfortunately, by default, only a partial core is
86      * generated which, in many cases, isn't too useful.
87      */
88     struct sigaction nsa;
89     
90     sigemptyset(&nsa.sa_mask);
91     nsa.sa_handler = SIG_DFL;
92     nsa.sa_flags = SA_FULLDUMP;
93     sigaction(SIGSEGV, &nsa, NULL);
94 #endif
95 #if !defined (AFS_AIX_ENV) && !defined (AFS_HPUX_ENV)
96     pageSize = getpagesize();
97 #endif
98     ScanArgs(argc, argv);
99     
100     /* now read each line of the CopyList */
101     if (Copy(file1, file2, !oneLevel, 0))
102         exit(1);        /* some type of failure */
103
104     exit(0);
105 }
106
107
108 ScanArgs(argc, argv)
109     int argc;
110     char *argv[];
111
112 {
113     /* skip program name */
114     argc--, argv++;
115     
116     /* check for -flag options */
117     while (argc > 0 && *argv[0] == '-') {
118         char *cp = *argv;
119
120         switch (*++cp) {
121           case 'v':
122             verbose = true;
123             break;
124             
125           case '1':
126             oneLevel = true;
127             break;
128             
129           case 'r':
130             renameTargets = true;
131             break;
132             
133           case 'f':
134             forceOverwrite = true;
135             break;
136             
137           case 'x':
138             preserveDate = false;
139             break;
140             
141           default: 
142             fprintf(stderr, "Unknown option: '%c'\n", *cp);
143             fprintf(stderr, "usage: up [-v1frx] from to\n");
144             exit(1);
145         }
146         argc--, argv++;
147     }
148
149     if (argc != 2) {
150         fprintf(stderr, "usage: up [-v1frx] from to\n");
151         exit(1);
152     }
153
154     strcpy(file1, argv[0]);
155     strcpy(file2, argv[1]);
156
157 } /*ScanArgs*/
158
159
160
161 /*
162  * MakeParent
163  *      Make sure the parent directory of this file exists.  Returns
164  *      true if it exists, false otherwise.  Note: the owner argument
165  *      is a hack.  All directories made will have this owner.
166  */
167 Boolean MakeParent(file, owner)
168     char *file;
169     afs_int32 owner;
170 {
171     char  parent[MAXPATHLEN];
172     char *p;
173     struct stat s;
174     
175     strcpy(parent, file);
176     
177     p = rindex(parent, '/');
178     if (!p) {
179         strcpy(parent, ".");
180     }
181     else if (p > parent) {
182         *p = '\0';
183     }
184     else {
185         p[1] = '\0';
186     }
187     
188     if (stat(parent, &s) < 0) {
189         if (!MakeParent(parent, owner))
190             return(false);
191
192         if (verbose) {
193             printf("Creating directory %s\n", parent);
194             fflush(stdout);
195         }
196
197         mkdir(parent, 0777);
198         chown(parent, owner, -1);
199     }
200     return(true);
201 } /*MakeParent*/
202
203
204 /*
205  * Copy
206  *      This does the bulk of the work of the program.  Handle one file,
207  *      possibly copying subfiles if this is a directory
208  */
209 Copy(file1, file2, recursive, level)
210     char *file1;        /* input file name */
211     char *file2;        /* output file name */
212     Boolean recursive;  /* true if directory should be copied */
213     int level;          /* level of recursion: 0, 1, ... */
214
215 {
216     struct stat s1, s2; /*Stat blocks*/
217     struct ViceIoctl blob;
218     char aclspace[MAXACL];
219     afs_int32 rcode = 0, code;
220     int   goods2 = 1;
221
222     code = lstat(file1, &s1);
223     if (code < 0) {
224        fprintf(stderr,"Can't find %s\n",file1);
225        return 1;
226     }
227
228     code = lstat(file2, &s2);
229     if (code < 0) {
230        if (!MakeParent(file2,s1.st_uid))
231           return 0;
232        goods2 = 0;
233     }
234
235     if ((s1.st_mode & S_IFMT) == S_IFREG) {
236        /*
237         * -------------------- Copy regular file --------------------
238         */
239        int    f1, f2, n;
240        char   buf[4096];                        /* Must be bigger than sizeof (*head) */
241        struct timeval tv[2];
242        char   tmpfile[MAXPATHLEN], newName[MAXPATHLEN];
243         
244        if (verbose) {
245           printf("Level %d: File %s to %s\n", level, file1, file2);
246           fflush(stdout);
247        }
248
249        /* Wonder if there is a security hole */
250        if ( ((s1.st_mode & 04002) == 04002) ||
251             ((s1.st_mode & 04020) == 04020) ||
252             ((s1.st_mode & 02002) == 02002) ) {
253           fprintf(stderr, "WARNING: Mode-bits security hole in files %s and %s\n",
254                   file1, file2);
255        }
256
257        if (!goods2 || (s1.st_mtime != s2.st_mtime) || (s1.st_size != s2.st_size)) { /*c*/
258           /* Don't ovewrite a write protected file (unless force: -f) */
259           if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
260              fprintf(stderr,
261                      "File %s is write protected against its owner; not changed\n",
262                      file2);
263              return 1;
264           }
265
266           if (verbose) {
267              printf("  Copy file %s to %s (%u Bytes)\n", file1, file2, s1.st_size);
268              fflush(stdout);
269           }
270
271           strcpy(tmpfile, file2);        /* Name of temporary file */
272           strcat(tmpfile,".UPD");
273
274           /* open file1 for input */
275           f1 = open(file1, O_RDONLY);
276           if (f1 < 0) {
277              fprintf(stderr, "Unable to open input file %s, ", file1);
278              if (errno >= sys_nerr)
279                 fprintf(stderr, "error code = %d\n", errno);
280              else
281                 fprintf(stderr, "%s\n", sys_errlist[errno]);
282              return 1;
283           }
284
285           /* open temporary output file */
286           f2 = open(tmpfile, (O_WRONLY | O_CREAT | O_TRUNC), s1.st_mode);
287           if (f2 < 0) {
288              fprintf(stderr, "Unable to open output file %s, ", tmpfile);
289              if (errno >= sys_nerr)
290                 fprintf(stderr, "error code = %d\n", errno);
291              else
292                 fprintf(stderr, "%s\n", sys_errlist[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,"Write failed, file %s must be copied again.\n", file2);
302              }
303           }
304
305           /* preserve access and modification times: ("-x" disables)*/
306           if (preserveDate) {
307              tv[0].tv_sec = s1.st_atime;
308              tv[0].tv_usec = 0;
309              tv[1].tv_sec = s1.st_mtime;
310              tv[1].tv_usec = 0;
311              utimes(tmpfile, tv);
312           }
313
314           /* Close the files */
315           code = close(f1);
316           code = close(f2);
317           if (code < 0) {
318              perror("close ");
319              rcode = 1;
320           }
321
322           /* Rename file2 to file2.old. [-r] */
323           if (renameTargets && goods2) {
324              strcpy(newName, file2);
325              strcat(newName, ".old");
326              if (verbose) {
327                 printf("  Renaming %s to %s\n", file2, newName);
328                 fflush(stdout);
329              }
330              if (rename(file2, newName) < 0) {
331                 fprintf(stderr, "Rename of %s to %s failed.\n", file2, newName);
332              }
333           }
334
335           /* Rename temporary file to file2 */
336           code = rename(tmpfile, file2);
337           if (code < 0) {
338              fprintf(stderr, "Rename of %s to %s failed.\n", tmpfile, file2);
339              return 1;
340           }
341
342           /* Re-stat file2 and compare file sizes */
343           code = lstat(file2, &s2);
344           if (code < 0) {
345              fprintf(stderr, "WARNING: Unable to stat new file %s\n", file2);
346              return 1;
347           }
348           if (s1.st_size != s2.st_size) {
349              fprintf(stderr, "WARNING: New file %s is %u bytes long; should be %u\n",
350                     file2, s2.st_size, s1.st_size);
351           }
352        } /*c*/
353
354        /* Set the user-id */
355        if (s2.st_uid != s1.st_uid) {
356           if (verbose) {
357              printf("  Set owner-id for %s to %d\n", file2, s1.st_uid);
358              fflush(stdout);
359           }
360           code = chown(file2, s1.st_uid, -1);
361           if (code) {
362              fprintf(stderr, "Unable to set owner-id for %s to %d\n", file2, s1.st_uid);
363              fflush(stdout);
364              rcode = 1;
365              s1.st_mode &= ~04000;       /* Don't set suid bit */
366           }
367        }
368
369        /* Set the group-id */
370        if (s2.st_gid != s1.st_gid) {
371           if (verbose) {
372              printf("  Set group-id for %s to %d\n", file2, s1.st_gid);
373              fflush(stdout);
374           }
375           code = chown(file2, -1, s1.st_gid);
376           if (code) {
377              fprintf(stderr, "Unable to set group-id for %s to %d\n", file2, s1.st_gid);
378              fflush(stdout);
379              rcode = 1;
380              s1.st_mode &= ~02000;       /* Don't set sgid bit */
381           }
382        }
383
384        /* Set the mode bits */
385        if (s1.st_mode != s2.st_mode) {
386           if (verbose) {
387              printf("  Set mode-bit for %s to %o\n", file2, (s1.st_mode & 07777));
388              fflush(stdout);
389           }
390           code = chmod(file2, s1.st_mode);
391           if (code) {
392              fprintf(stderr, "Unable to set mode-bits for %s to %d\n", file2, s1.st_mode);
393              rcode = 1;
394           }
395        }
396     } /* regular file */
397
398     else if ((s1.st_mode & S_IFMT) == S_IFLNK) {
399        /*
400         * --------------------- Copy symlink  --------------------
401         */
402        char linkvalue[MAXPATHLEN+1];
403        int  n;
404
405        if (verbose) {
406           printf("Level %d: Symbolic link %s to %s\n", level, file1, file2);
407           fflush(stdout);
408        }
409        
410        /* Don't ovewrite a write protected directory (unless force: -f) */
411        if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
412           fprintf(stderr,
413                   "Link %s is write protected against its owner; not changed\n",
414                   file2);
415           return 1;
416        }
417        
418        if (verbose) {
419           printf("  Copy symbolic link %s->%s to %s\n", file1, linkvalue, file2);
420           fflush(stdout);
421        }
422        
423        n = readlink(file1, linkvalue, sizeof(linkvalue));
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     } /*Dealing with symlink*/
440
441     else if (((s1.st_mode & S_IFMT) == S_IFDIR) && (recursive || (level == 0))) {
442        /*
443         * ----------------------- Copy directory -----------------------
444         */
445        DIR           *dir;
446        int           tfd, code, i;
447        struct OldAcl *oacl;
448        char          tacl[MAXACL];
449        char          f1[MAXPATHLEN], f2[MAXPATHLEN];
450        char          *p1, *p2;
451        struct dirent *d;
452
453        if (verbose) {
454           printf("Level %d: Directory %s to %s\n", level, file1, file2);
455           fflush(stdout);
456        }
457
458        /* Don't ovewrite a write protected directory (unless force: -f) */
459        if (!forceOverwrite && goods2 && (s2.st_mode & 0200) == 0) {
460           fprintf(stderr,
461                   "Directory %s is write protected against its owner; not changed\n",
462                   file2);
463           return 1;
464        }
465
466        strcpy(f1, file1);
467        strcpy(f2, file2);
468        p1 = f1 + strlen(f1);
469        p2 = f2 + strlen(f2);
470        if (p1 == f1 || p1[-1] != '/')
471           *p1++ = '/';
472        if (p2 == f2 || p2[-1] != '/')
473           *p2++ = '/';
474
475        dir = opendir(file1);
476        if (dir == NULL) {
477           fprintf(stderr, "Couldn't open %s\n", file1);
478           return 1;
479        }
480
481        while ((d = readdir(dir)) != NULL) {
482           if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
483              continue;
484           strcpy(p1, d->d_name);
485           strcpy(p2, d->d_name);
486           code = Copy(f1, f2, recursive, level + 1);
487           if (code && !rcode) rcode = 1;                 /* remember errors */
488        }
489
490        closedir(dir);
491
492        if (verbose) {
493           printf("Level %d: Copied directory %s to %s\n", level, file1, file2);
494           fflush(stdout);
495        }
496
497        mkdir(file2, 0777);      /* Handle case where MakeParent not invoked. */
498
499        if (verbose) {
500           printf("  Set owner-id for %s to %d\n", file2, s1.st_uid);
501           fflush(stdout);
502        }
503        code = chown(file2, s1.st_uid, -1);
504        if (code) {
505           fprintf(stderr, "Unable to set owner-id for %s to %d\n", file2, s1.st_uid);
506           fflush(stdout);
507           s1.st_mode &= ~04000;       /* Don't set suid bit */
508        }
509
510        if (verbose) {
511           printf("  Set group-id for %s to %d\n", file2, s1.st_gid);
512           fflush(stdout);
513        }
514        code = chown(file2, -1, s1.st_gid);
515        if (code) {
516           fprintf(stderr, "Unable to set group-id for %s to %d\n", file2, s1.st_gid);
517           fflush(stdout);
518           s1.st_mode &= ~02000;       /* Don't set sgid bit */
519        }
520
521        if (verbose) {
522           printf("  Set mode-bit for %s to %o\n", file2, (s1.st_mode & 07777));
523           fflush(stdout);
524        }
525        code = chmod(file2, s1.st_mode);
526        if (code) {
527           fprintf(stderr, "Unable to set mode-bits for %s to %d\n", file2, s1.st_mode);
528           fflush(stdout);
529           rcode = 1;
530        }
531
532        if (setacl == true) {
533           if (verbose) {
534              printf("  Set acls for %s\n", file2);
535              fflush(stdout);
536           }
537
538           blob.in       = aclspace;
539           blob.out      = aclspace;
540           blob.in_size  = 0;
541           blob.out_size = MAXACL;
542
543           if (oldAcl) {
544              /* Get an old-style ACL and convert it */
545              for (i=1; i<strlen(file1); i++)
546                 if (file1[i] == '/') break;
547              strcpy(aclspace, &file1[i]);
548
549              blob.in_size = 1+strlen(aclspace);
550              tfd = open(file1, O_RDONLY, 0);
551              if (tfd < 0) {
552                 perror("old-acl open ");
553                 return 1;
554              }
555              code = ioctl(tfd, _VICEIOCTL(4), &blob);
556              close(tfd);
557              if (code < 0) {
558                 if (errno == EINVAL) {
559                    setacl = false;
560                 }
561                 else {
562                    return 1;
563                 }
564              }
565              /* Now convert the thing. */
566              oacl = (struct OldAcl *) (aclspace+4);
567              sprintf(tacl, "%d\n%d\n", oacl->nplus, oacl->nminus);
568              strcat(tacl, oacl->data);
569              strcpy(aclspace, tacl);
570           } /*Grab and convert old-style ACL*/
571           else {
572              /* Get a new-style ACL */
573              code = pioctl(file1, _VICEIOCTL(2), &blob, 1);
574              if (code < 0) {
575                 if (errno == EINVAL) {
576                    setacl = false;
577                 }
578                 else {
579                    perror("getacl ");
580                    return 1;
581                 }
582              }
583           } /*Grab new-style ACL*/
584
585           /*
586            * Now, set the new-style ACL.
587            */
588           if (setacl == true) {
589              blob.out      = aclspace;
590              blob.in       = aclspace;
591              blob.out_size = 0;
592              blob.in_size  = 1+strlen(aclspace);
593              code = pioctl(file2, _VICEIOCTL(1), &blob, 1);
594              if (code) {
595                 if (errno == EINVAL) {
596                    setacl = false;
597                 }
598                 else {
599                    fprintf(stderr, "Couldn't set acls for %s\n", file2);
600                    return 1;
601                 }
602              }
603           }
604
605           if (setacl == false) {
606              printf("Not setting acls\n");
607           }
608        }
609     }
610
611     return rcode;
612 } /*Copy*/