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