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