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