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