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