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