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