reindent-20030715
[openafs.git] / src / pinstall / install.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 /*
11                       All Rights Reserved
12
13 Permission to use, copy, modify, and distribute this software and its 
14 documentation for any purpose and without fee is hereby granted, 
15 provided that the above copyright notice appear in all copies and that
16 both that copyright notice and this permission notice appear in 
17 supporting documentation, and that the name of IBM not be
18 used in advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.  
20
21 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27 SOFTWARE.
28 ******************************************************************/
29 /* $ACIS: $ */
30
31 /* ALSO utimes and strip the file
32
33 Generic install command.  Options are:
34         -s              strip the file  (default for executable files with no extension)
35         -ns             do not strip the file   (default for other files)
36         -c              ignored for compatability
37         -m <mode>       chmod to this value
38         -o <user>       chown to this user
39         -g <group>      chgrp to this group
40         -f              target path is a file
41         -q              be very, very quick and quiet
42         -l <envcwd>     attempt symbolic link back from destination to source
43                         with the current directory in the specified environment
44                         variable
45 */
46
47 #define MAXFILES 200
48 #define BUFSIZE 32768
49 #include <afsconfig.h>
50 #include <afs/param.h>
51
52 RCSID
53     ("$Header$");
54
55 #include <stdio.h>
56 #include <pwd.h>
57 #include <grp.h>
58 #include <errno.h>
59 #ifdef  AFS_AIX32_ENV
60 #include <signal.h>
61 #endif
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <sys/file.h>
65 #include <sys/time.h>
66 #ifdef  AFS_SUN5_ENV
67 #include <fcntl.h>
68 #include <string.h>
69 #include <elf.h>
70 #else
71 #ifdef AFS_DARWIN_ENV
72 #include <fcntl.h>
73 #include <string.h>
74 #else
75 #include <strings.h>
76 #include <a.out.h>
77 #endif
78 #endif
79 #ifdef  AFS_HPUX_ENV
80 #include <utime.h>
81 #endif
82 #include <unistd.h>
83 #include <string.h>
84 #include <sys/wait.h>
85 #include <stdlib.h>
86 #ifdef HAVE_PWD_H
87 #include <pwd.h>
88 #endif
89 #include <stdio.h>
90
91 static struct stat istat, ostat;
92 static int stripcalled = 0;
93
94 #if !defined(AFS_DARWIN60_ENV) && !defined(AFS_FBSD50_ENV)
95 extern int sys_nerr;
96 #endif
97 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
98 extern char *sys_errlist[];
99 #endif
100
101 /* static prototypes */
102 char *ErrorString(int aerrno);
103 int stripName(char *aname);
104 int atoo(register char *astr);
105
106
107 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
108 int
109 utimes(char *file, struct timeval tvp[2])
110 {
111     struct utimbuf times;
112
113     times.actime = tvp[0].tv_sec;
114     times.modtime = tvp[1].tv_sec;
115     return (utime(file, &times));
116 }
117 #endif
118
119 static char *
120 strrpbrk(char *s, char *set)
121 {
122     char sets[256];
123     int i;
124
125     memset(sets, 0, sizeof(sets));
126     while (*set)
127         sets[(int)*set++] = 1;
128     i = strlen(s);
129     while (i > 0)
130         if (sets[(int)s[--i]])
131             return &s[i];
132     return 0;
133 }
134
135 char *
136 ErrorString(int aerrno)
137 {
138     static char tbuffer[100];
139     if (aerrno < 0 || aerrno >= sys_nerr) {
140         sprintf(tbuffer, "undefined error code %d", aerrno);
141     } else {
142         strcpy(tbuffer, sys_errlist[aerrno]);
143     }
144     return tbuffer;
145 }
146
147 int
148 stripName(char *aname)
149 {
150     if (strrchr(aname, '.') == 0)
151         return 1;
152     else
153         return 0;
154 }
155
156 int
157 atoo(register char *astr)
158 {
159     register afs_int32 value;
160     register char tc;
161     value = 0;
162     while ((tc = *astr++)) {
163         value <<= 3;
164         value += tc - '0';
165     }
166     return value;
167 }
168
169 #if     defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DECOSF_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV) || defined(AFS_NBSD_ENV)
170 /*
171  * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
172  * reason why it wouldn't work.
173  */
174 static int
175 quickStrip(char *iname, char *oname, int ignored, int copy_only)
176 {
177     int pid;
178     pid_t status;
179     static char *strip[] = {
180         "strip", 0, 0,
181     };
182     static char *copy[] = {
183         "cp", 0, 0, 0,
184     };
185
186     /*
187      * first, copy the `iname' to the `oname'
188      */
189     switch (pid = fork()) {
190     case -1:                    /* error        */
191         perror("fork");
192         return -1;
193
194     case 0:                     /* child        */
195         copy[1] = iname;
196         copy[2] = oname;
197         execve("/bin/cp", copy, NULL);
198         perror("/bin/cp");
199         exit(1);
200
201     default:                    /* parent       */
202         if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
203             perror("waitpid");
204             return -1;
205         }
206     }
207
208     if (status != 0) {
209         fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
210         return -1;
211     }
212
213     /*
214      * need to do a chmod to guarantee that the perms will permit
215      * the strip.  Perms are fixed up later.
216      */
217     if (chmod(oname, 0700)) {
218         perror("chmod");
219         return -1;
220     }
221 #if !defined(AFS_OBSD_ENV) && !defined(AFS_NBSD_ENV)
222     /*
223      * done the copy, now strip if desired.
224      */
225     if (copy_only)
226         return 0;
227
228     switch (pid = fork()) {
229     case -1:                    /* error        */
230         perror("fork");
231         return -1;
232
233     case 0:                     /* child        */
234         strip[1] = oname;
235 #ifdef  AFS_SUN5_ENV
236 #define STRIP_BIN       "/usr/ccs/bin/strip"
237 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
238 #define STRIP_BIN       "/usr/bin/strip"
239 #else
240 #define STRIP_BIN       "/bin/strip"
241 #endif
242         execve(STRIP_BIN, strip, NULL);
243         perror(STRIP_BIN);
244         exit(1);
245
246     default:                    /* parent       */
247         if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
248             perror("waitpid");
249             return -1;
250         }
251     }
252 #endif
253
254     return status;
255 }
256
257 #else
258 #ifdef AFS_AIX_ENV
259 #ifdef AFS_AIX32_ENV
260 /*
261  * whoa! back up and be a little more rational (every little bit helps in
262  * aix_31).
263  */
264 static int
265 quickStrip(char *iname, char *oname, int ignored, int copy_only)
266 {
267     int pid, status;
268     static char *strip[] = {
269         "strip", 0, 0,
270     };
271     static char *copy[] = {
272         "cp", 0, 0, 0,
273     };
274
275     /*
276      * first, copy the `iname' to the `oname'
277      */
278     switch (pid = fork()) {
279     case -1:                    /* error        */
280         perror("fork");
281         return -1;
282
283     case 0:                     /* child        */
284         copy[1] = iname;
285         copy[2] = oname;
286         execve("/bin/cp", copy, 0);
287         perror("/bin/cp");
288         exit(1);
289
290     default:                    /* parent       */
291         if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
292             perror("waitpid");
293             return -1;
294         }
295     }
296
297     if (status != 0) {
298         fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
299         return -1;
300     }
301
302     /*
303      * need to do a chmod to guarantee that the perms will permit
304      * the strip.  Perms are fixed up later.
305      */
306     if (chmod(oname, 0700)) {
307         perror("chmod");
308         return -1;
309     }
310
311     /*
312      * done the copy, now strip if desired.
313      */
314     if (copy_only)
315         return 0;
316
317     switch (pid = fork()) {
318     case -1:                    /* error        */
319         perror("fork");
320         return -1;
321
322     case 0:                     /* child        */
323         strip[1] = oname;
324         execve("/bin/strip", strip, 0);
325         perror("/bin/strip");
326         exit(1);
327
328     default:                    /* parent       */
329         if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
330             perror("waitpid");
331             return -1;
332         }
333     }
334
335     return status;
336 }
337
338 #endif /* AFS_AIX32_ENV        */
339 #else /* !AFS_AIX_ENV         */
340
341 #ifdef  mips
342 #include "sex.h"
343 int
344 quickStrip(int fd, afs_int32 asize)
345 {
346     FILHDR fheader;
347     int dum, newlen;
348     int mysex, swapheader;
349
350     /* Read the file header, if it is one. */
351     if (lseek(fd, 0, L_SET) == -1) {
352         printf("Initial lseek failed while stripping file: %s\n",
353                ErrorString(errno));
354         return -1;
355     }
356     dum = read(fd, (char *)&fheader, sizeof(fheader));
357     /* Fail on I/O error */
358     if (dum < 0) {
359         printf("Initial read failed while stripping: %s\n",
360                ErrorString(errno));
361         return -1;
362     }
363     /* If the file is smaller than a file header, forget it. */
364     if (dum != sizeof(fheader))
365         return 0;
366 #ifdef AFS_DECOSF_ENV
367     mysex = LITTLEENDIAN;
368 #else
369     mysex = gethostsex();
370     if (mysex != BIGENDIAN && mysex != LITTLEENDIAN)
371         return 0;
372 #endif /* DEC OSF */
373     swapheader = 0;
374     if (fheader.f_magic == MIPSELMAGIC) {
375         if (mysex == BIGENDIAN)
376             swapheader = 1;
377     } else if (fheader.f_magic == MIPSEBMAGIC) {
378         if (mysex == LITTLEENDIAN)
379             swapheader = 1;
380     } else
381         return 0;               /* not executable */
382 #ifdef AFS_DECOSF_ENV
383     if (swapheader)
384         return 0;
385 #else
386     if (swapheader)
387         swap_filehdr(&fheader, gethostsex());
388 #endif /* DEC OSF */
389     /* Already stripped? */
390     if (fheader.f_symptr == 0 || fheader.f_nsyms == 0)
391         return 0;
392     /* Strip it.  Zero out the symbol pointers. */
393     newlen = fheader.f_symptr;
394     fheader.f_symptr = 0;
395     fheader.f_nsyms = 0;
396 #ifndef AFS_DECOSF_ENV
397     if (swapheader)
398         swap_filehdr(&fheader, gethostsex());
399 #endif /* DEC OSF */
400     if (lseek(fd, 0, L_SET) == -1)
401         return -1;
402     if (write(fd, (char *)&fheader, sizeof(fheader)) != sizeof(fheader))
403         return -1;
404 /* Now truncate the file itself. */
405     if (ftruncate(fd, newlen) != 0)
406         return -1;
407     return 0;
408 }
409 #else /* !mips */
410 static int
411 quickStrip(int afd, afs_int32 asize)
412 {
413     int n, bytesLeft;
414     struct exec buf;
415     struct exec *head;
416     n = lseek(afd, 0, 0);
417     if (n < 0) {
418         printf("Initial lseek failed while stripping file: %s\n",
419                ErrorString(errno));
420         return -1;
421     }
422     n = read(afd, &buf, sizeof(buf));
423     if (n < 0) {
424         printf("Initial read failed while stripping: %s\n",
425                ErrorString(errno));
426         return -1;
427     }
428     head = &buf;
429     if (n >= sizeof(*head) && !N_BADMAG(*head)) {       /* This code lifted from strip.c. */
430         bytesLeft = (afs_int32) head->a_text + head->a_data;
431         head->a_syms = head->a_trsize = head->a_drsize = 0;
432         if (head->a_magic == ZMAGIC)
433             bytesLeft += N_TXTOFF(*head) - sizeof(*head);
434         /* also include size of header */
435         bytesLeft += sizeof(*head);
436         n = lseek(afd, 0, 0);
437         if (n < 0) {
438             printf("lseek failed while stripping file: %s\n",
439                    ErrorString(errno));
440             return -1;
441         }
442         n = write(afd, &buf, sizeof(buf));
443         if (n < 0) {
444             printf("write failed while stripping file: %s\n",
445                    ErrorString(errno));
446             return -1;
447         }
448     } else
449         bytesLeft = 0;
450
451     /* check if size of stripped file is same as existing file */
452     if (bytesLeft != 0 && bytesLeft != asize) {
453         if (ftruncate(afd, bytesLeft) < 0) {
454             printf("ftruncate failed after stripping file: %s\n",
455                    ErrorString(errno));
456             return -1;
457         }
458     }
459     return 0;
460 }
461 #endif /* mips */
462 #endif
463 #endif /* AFS_HPUX_ENV */
464
465 #include "AFS_component_version_number.c"
466
467 int
468 main(int argc, char *argv[])
469 {
470     int setOwner, setMode, setGroup, ifd, ofd;
471     afs_int32 mode = 0, owner, group;
472     struct passwd *tpw;
473     struct group *tgp;
474     char *fnames[MAXFILES], *newNames[MAXFILES];
475     afs_int32 rcode, code;
476     char *dname;
477     char pname[1024];
478 #if defined (AFS_HPUX_ENV)
479     char pnameBusy[1024];
480 #endif /* AFS_HPUX_ENV */
481     char pnametmp[1024];
482     int pnamelen;
483     afs_int32 newcode;
484     static char diskBuffer[BUFSIZE];    /* must be static to avoid compiler bugs for large stuff */
485     char myHostName[100];
486     struct timeval tvp[2];
487     int isDir;
488     int strip;
489     int fptr;
490     register char *tp;
491     register afs_int32 i;
492
493 #ifdef  AFS_AIX32_ENV
494     /*
495      * The following signal action for AIX is necessary so that in case of a 
496      * crash (i.e. core is generated) we can include the user's data section 
497      * in the core dump. Unfortunately, by default, only a partial core is
498      * generated which, in many cases, isn't too useful.
499      */
500     struct sigaction nsa;
501
502     sigemptyset(&nsa.sa_mask);
503     nsa.sa_handler = SIG_DFL;
504     nsa.sa_flags = SA_FULLDUMP;
505     sigaction(SIGSEGV, &nsa, NULL);
506 #endif
507     fptr = 0;
508     rcode = 0;
509     strip = -1;                 /* don't know yet */
510     owner = 0;
511     setOwner = 0;
512     setMode = 0;
513     group = 0;
514     setGroup = 0;
515     isDir = -1;                 /* don't know yet */
516
517     for (i = 1; i < argc; i++) {
518         tp = argv[i];
519         if (tp[0] == '-') {     /* a switch */
520             if (!strcmp(tp, "-m"))
521                 mode = atoo(argv[++i]), setMode = 1;
522             else if (!strcmp(tp, "-s"))
523                 strip = 1;
524             else if (!strcmp(tp, "-ns"))
525                 strip = 0;
526             else if (!strcmp(tp, "-c")) /* nothing */
527                 ;
528             else if (!strcmp(tp, "-f"))
529                 isDir = 0;      /* => dest is file */
530             else if (!strcmp(tp, "-o")) {       /* look up the dude */
531                 tpw = getpwnam(argv[++i]);
532                 if (!tpw) {
533                     printf("User %s not found in passwd database, ignored\n",
534                            argv[i]);
535                 } else {
536                     owner = tpw->pw_uid;
537                     setOwner = 1;
538                 }
539             } else if (!strcmp(tp, "-g")) {     /* look up the dude */
540                 tgp = getgrnam(argv[++i]);
541                 if (!tgp) {
542                     printf("Group %s not found in passwd database; ignored\n",
543                            argv[i]);
544                 } else {
545                     group = tgp->gr_gid;
546                     setGroup = 1;
547                 }
548             } else {
549                 printf("Bad switch %s\n", argv[i]);
550                 exit(1);
551             }
552         } else {                /* a file name */
553             if (fptr >= MAXFILES) {
554                 printf("Too many files on command line, max is %d\n",
555                        MAXFILES);
556                 exit(1);
557             }
558             fnames[fptr++] = argv[i];
559         }
560     }
561
562     /* we've parse the commands, now *do* them */
563
564     /* otherwise we are doing a local install, so we do the work for each file
565      * here the last name in the fname array is the dir in which to put all
566      * this stuff */
567
568     if (fptr < 2) {
569         printf("Not enough file names\n");
570         exit(1);
571     }
572
573     /* N file usage requires last argument to be a directory.  If -f was
574      * specified it is an error.  In the 2 file usage when -f is not specified
575      * use a heuristic.  If the ends of the two pathnames are equal then assume
576      * the target is a file, otherwise assume it is a directory. */
577
578     if ((fptr > 2) && (isDir == 0)) {
579         printf
580             ("target must be a directory, don't use multiple source files with -f switch\n");
581         exit(1);
582     } else if (fptr > 2)
583         isDir = 1;
584     else if (isDir != 0) {
585         char *targetSuffix;
586         char *sourceSuffix;
587
588         targetSuffix = strrpbrk(fnames[1], "./");
589         sourceSuffix = strrpbrk(fnames[0], "./");
590         if (sourceSuffix == 0) {
591             sourceSuffix = fnames[0];
592             if (targetSuffix == 0)
593                 targetSuffix = fnames[1];
594             else
595                 targetSuffix++;
596         } else if (targetSuffix == 0)
597             targetSuffix = fnames[1];
598         if (strcmp(targetSuffix, sourceSuffix) == 0)
599             isDir = 0;
600     }
601
602     dname = fnames[--fptr];
603     if (stat(dname, &istat) < 0) {
604         if ((errno == ENOENT) || (errno == ENOTDIR)) {
605             /* create path */
606             char protopath[BUFSIZ];
607             int i = 0;
608             char c;
609             while (dname[i]) {
610                 do {
611                     protopath[i] = dname[i];
612                     c = dname[++i];     /* next char */
613                 } while (!((c == 0) || (c == '/')));
614                 protopath[i] = 0;
615
616                 /* don't mkdir last component if target is a file */
617                 if ((c == 0) && (isDir == 0))
618                     break;
619
620                 /* else create dir component if it doesn't exist */
621                 code = stat(protopath, &istat);
622                 if (code && (errno == ENOENT)) {
623                     code = mkdir(protopath, 0755);
624                     if (code) {
625                         printf("Can't create destination path at %s\n",
626                                protopath);
627                         exit(1);
628                     }
629                 }
630             }                   /* while dname not exhausted */
631             if (isDir == -1)
632                 isDir = 1;
633         } else {
634             printf("Can't stat destination ``%s'': %s\n", dname,
635                    ErrorString(errno));
636             exit(1);
637         }
638     } else {
639         if ((istat.st_mode & S_IFMT) == S_IFDIR)
640             isDir = 1;
641         else
642             isDir = 0;
643     }
644
645     /* either can be n files and one dir, or one file and one target */
646     if (!isDir && fptr != 1) {
647         printf("target for multiple files must be a dir\n");
648         exit(1);
649     }
650
651     for (i = 0; i < fptr; i++) {        /* figure out name to put as entry name for file */
652         tp = strrchr(fnames[i], '/');
653         if (tp)
654             newNames[i] = tp + 1;
655         else
656             newNames[i] = fnames[i];
657     }
658     for (i = 0; i < fptr; i++) {        /* copy newName[i] into directory dname */
659
660         /* pname is target file in either case */
661         if (isDir) {
662             strcpy(pname, dname);
663             strcat(pname, "/");
664             strcat(pname, newNames[i]);
665         } else
666             strcpy(pname, dname);
667         strcpy(pnametmp, pname);
668         /* Make up a temporary name for a destination */
669         pnamelen = strlen(pnametmp);
670         gethostname(myHostName, sizeof(myHostName) - 1);        /* lv room for null */
671         if (pnamelen > 1020 - strlen(myHostName))
672             pnamelen = 1020 - strlen(myHostName);
673         pnametmp[pnamelen] = '.';
674         strcpy(&pnametmp[pnamelen + 1], myHostName);
675         if (strcmp(fnames[i], pnametmp) == 0)
676             strcpy(&pnametmp[pnamelen], ".NeW");
677
678         ifd = open(fnames[i], O_RDONLY, 0);
679         if (ifd < 0) {
680             printf("Can't open source file ``%s'': %s\n", fnames[i],
681                    ErrorString(errno));
682             rcode = 1;
683             continue;
684         }
685         if (fstat(ifd, &istat) < 0) {
686             printf("Cound not fstat input file ``%s'': %s; skipping it\n",
687                    fnames[i], ErrorString(errno));
688             close(ifd);
689             rcode = 1;
690             continue;
691         }
692         if (lstat(pname, &ostat) == 0) {
693             if ((ostat.st_size == istat.st_size)
694                 && (ostat.st_mtime == istat.st_mtime) && ((!setMode)
695                                                           ||
696                                                           ((ostat.
697                                                             st_mode & S_IFMT)
698                                                            == mode))
699                 && ((!setOwner) || (ostat.st_uid == owner)) && ((!setGroup)
700                                                                 || (ostat.
701                                                                     st_gid ==
702                                                                     group))) {
703                 close(ifd);
704                 printf("No changes to %s since %s installed\n", fnames[i],
705                        pname);
706                 continue;
707             }
708         }
709 #if     defined(AFS_AIX_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DECOSF_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV) || defined(AFS_NBSD_ENV)
710         stripcalled = 0;
711         if (strip == 1 || ((strip == -1 && ((istat.st_mode & 0111) == 0111)
712                             && stripName(newNames[i]))))
713             stripcalled = 1;
714         if (!stripcalled) {
715             /* Simply copy target to dest */
716             quickStrip(fnames[i], pnametmp, istat.st_size, 1);
717         } else {
718             if (quickStrip(fnames[i], pnametmp, istat.st_size, 0) < 0) {
719                 printf
720                     ("...strip failed for output temp file ``%s''; skipping it\n",
721                      pnametmp);
722                 close(ifd);
723                 unlink(pnametmp);
724                 rcode = 1;
725                 continue;
726             }
727         }
728         close(ifd);
729
730         ofd = open(pnametmp, O_RDWR, 0);
731         if (ofd < 0) {
732             printf("Could not open output temp file ``%s'': %s\n", pnametmp,
733                    ErrorString(errno));
734             close(ifd);
735             rcode = 1;
736             continue;
737         }
738         if (!setMode)
739             mode = istat.st_mode;       /* Was 0755:> this is the default for our rcs to work */
740 #else /* AFS_AIX_ENV */
741         /* check to see if this file is hard to duplicate */
742         ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
743         if (ofd < 0) {
744             printf("Could not create output temp file ``%s'': %s\n", pnametmp,
745                    ErrorString(errno));
746             close(ifd);
747             rcode = 1;
748             continue;
749         }
750         if (!setMode)
751             mode = istat.st_mode;       /* Was 0755:> this is the default for our rcs to work */
752         /* here both files are open and ready to go */
753         while (1) {
754             code = read(ifd, diskBuffer, BUFSIZE);
755             if (code == 0)
756                 break;
757             if (code < 0) {
758                 printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
759                 break;
760             }
761             errno = 0;
762             newcode = write(ofd, diskBuffer, code);
763             if (newcode != code) {
764                 printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
765                 break;
766             }
767         }
768         if (code != 0) {
769             rcode = 1;          /* an error occurred copying the file */
770             printf
771                 ("Warning: Error occurred writing output temp file %s; skipping it\n",
772                  pnametmp);
773             close(ifd);
774             unlink(pnametmp);
775             close(ofd);
776             continue;           /* to the next file */
777         }
778         /* strip the file? */
779         if (strip == 1 || (strip == -1 && ((istat.st_mode & 0111) == 0111)
780                            && stripName(newNames[i])))
781             if (quickStrip(ofd, istat.st_size) < 0) {
782                 printf
783                     ("...strip failed for output temp file ``%s''; skipping it\n",
784                      pnametmp);
785                 close(ifd);
786                 unlink(pnametmp);
787                 rcode = 1;
788                 continue;
789             }
790
791         /* do the chmod, etc calls before closing the file for max parallelism on store behind */
792         close(ifd);
793
794 #endif /* AFS_AIX_ENV */
795         if (fchmod(ofd, mode) < 0) {
796             printf("Couldn't chmod output temp file ``%s'': %s\n", pnametmp,
797                    ErrorString(errno));
798             unlink(pnametmp);
799             close(ofd);
800             rcode = 1;
801             continue;
802         }
803
804         tvp[0].tv_sec = istat.st_atime;
805         tvp[0].tv_usec = 0;
806         tvp[1].tv_sec = istat.st_mtime;
807         tvp[1].tv_usec = 0;
808         if (utimes(pnametmp, tvp) < 0) {
809             printf("Couldn't utimes output temp file ``%s'': %s\n", pnametmp,
810                    ErrorString(errno));
811             unlink(pnametmp);
812             close(ofd);
813             rcode = 1;
814             continue;
815         }
816         code = close(ofd);
817         if (code != 0) {
818             printf("Warning: Could not close output temp file %s (%s)\n",
819                    pnametmp, ErrorString(errno));
820             unlink(pnametmp);
821             rcode = 1;          /* an error occurred closing the output file */
822             continue;           /* to the next file */
823         }
824
825         /* do this later so vice doesn't see chown of unstored file */
826         if (setOwner || setGroup)
827             if (chown
828                 (pnametmp, (setOwner ? owner : -1),
829                  (setGroup ? group : -1)) < 0) {
830                 printf("Couldn't set %s for output temp file %s: %s\n",
831                        (setOwner ? (setGroup ? "owner and group" : "owner") :
832                         "group"), pnametmp, ErrorString(errno));
833                 unlink(pnametmp);
834                 rcode = 1;
835                 continue;
836             }
837
838         if (rename(pnametmp, pname) < 0) {
839 #if defined(AFS_HPUX_ENV)
840             if (errno == ETXTBSY) {
841                 (void)strcpy(pnameBusy, pname);
842                 (void)strcat(pnameBusy, ".BUSY");
843                 if (rename(pname, pnameBusy) == 0) {
844                     fprintf(stdout, "Had to leave old file: %s.\n",
845                             pnameBusy);
846                     fprintf(stdout,
847                             "Please delete this file when the program using it is finished.\n");
848                     if (rename(pnametmp, pname) < 0) {
849 #endif /* AFS_HPUX_ENV */
850
851                         printf
852                             ("Couldn't rename temp file %s to be output file %s: %s\n",
853                              pnametmp, pname, ErrorString(errno));
854                         unlink(pnametmp);
855                         rcode = 1;
856                         continue;
857
858 #if defined(AFS_HPUX_ENV)
859                     }
860                 } else {
861                     fprintf(stderr,
862                             "Couldn't move busy target file %s to make room for new version %s: %s\n",
863                             pname, pnametmp, ErrorString(errno));
864                     if (errno == ETXTBSY) {
865                         fprintf(stderr,
866                                 "Try terminating any programs using the file %s and then re-run %s.\n",
867                                 pnameBusy, argv[0]);
868                     }
869                     unlink(pnametmp);
870                     rcode = 1;
871                     continue;
872                 }
873             }
874 #endif /* AFS_HPUX_ENV */
875         }
876     }
877     /* all done now */
878     exit(rcode);
879 }