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