cbc373978850b25872ad350b10131c83a483860a
[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     afs_int32 newcode;
436     static char diskBuffer[BUFSIZE];    /* must be static to avoid compiler bugs for large stuff */
437     char myHostName[100];
438     struct timeval tvp[2];
439     int isDir;
440     int strip;
441     int fptr;
442     register char *tp;
443     register afs_int32 i;
444
445 #ifdef  AFS_AIX32_ENV
446     /*
447      * The following signal action for AIX is necessary so that in case of a 
448      * crash (i.e. core is generated) we can include the user's data section 
449      * in the core dump. Unfortunately, by default, only a partial core is
450      * generated which, in many cases, isn't too useful.
451      */
452     struct sigaction nsa;
453     
454     sigemptyset(&nsa.sa_mask);
455     nsa.sa_handler = SIG_DFL;
456     nsa.sa_flags = SA_FULLDUMP;
457     sigaction(SIGSEGV, &nsa, NULL);
458 #endif
459     fptr = 0;
460     rcode = 0;
461     strip = -1; /* don't know yet */
462     owner = 0;
463     setOwner = 0;
464     setMode = 0;
465     group = 0;
466     setGroup = 0;
467     isDir = -1;                         /* don't know yet */
468
469     for(i=1; i<argc; i++)
470         {tp = argv[i];
471         if (tp[0] == '-')
472             {/* a switch */
473             if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
474             else if (!strcmp(tp, "-s")) strip = 1;
475             else if (!strcmp(tp, "-ns")) strip = 0;
476             else if (!strcmp(tp, "-c")) /* nothing */;
477             else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
478             else if (!strcmp(tp, "-o"))
479                 {/* look up the dude */
480                 tpw = getpwnam(argv[++i]);
481                 if (!tpw)
482                     {printf("User %s not found in passwd database, ignored\n", argv[i]);
483                     }
484                 else
485                     {owner = tpw->pw_uid;
486                     setOwner =1;
487                     }
488                 }
489             else if (!strcmp(tp, "-g"))
490                 {/* look up the dude */
491                 tgp = getgrnam(argv[++i]);
492                 if (!tgp)
493                     {printf("Group %s not found in passwd database; ignored\n", argv[i]);
494                     }
495                 else
496                     {group = tgp->gr_gid;
497                     setGroup =1;
498                     }
499                 }
500                 else {
501                     printf("Bad switch %s\n", argv[i]);
502                     exit (1);
503                 }
504             }
505         else
506             {/* a file name */
507             if (fptr >= MAXFILES)
508                 {printf("Too many files on command line, max is %d\n", MAXFILES);
509                 exit(1);
510                 }
511             fnames[fptr++] = argv[i];
512             }
513         }
514
515     /* we've parse the commands, now *do* them */
516
517     /* otherwise we are doing a local install, so we do the work for each file
518        here the last name in the fname array is the dir in which to put all
519        this stuff */
520
521     if (fptr < 2)
522         {printf("Not enough file names\n");
523         exit(1);
524         }
525
526     /* N file usage requires last argument to be a directory.  If -f was
527        specified it is an error.  In the 2 file usage when -f is not specified
528        use a heuristic.  If the ends of the two pathnames are equal then assume
529        the target is a file, otherwise assume it is a directory. */
530
531     if ((fptr > 2) && (isDir == 0)) {
532         printf ("target must be a directory, don't use multiple source files with -f switch\n");
533         exit (1);
534     }
535     else if (fptr > 2) isDir = 1;
536     else if (isDir != 0) {
537         char *targetSuffix;
538         char *sourceSuffix;
539
540         targetSuffix = strrpbrk (fnames[1], "./");
541         sourceSuffix = strrpbrk (fnames[0], "./");
542         if (sourceSuffix == 0) {
543             sourceSuffix = fnames[0];
544             if (targetSuffix == 0) targetSuffix = fnames[1];
545             else targetSuffix++;
546         }
547         else if (targetSuffix == 0) targetSuffix = fnames[1];
548         if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
549     }
550
551     dname = fnames[--fptr];
552     if (stat(dname, &istat) < 0) {
553         if ((errno == ENOENT) || (errno == ENOTDIR)) {
554             /* create path */
555             char protopath[BUFSIZ];
556             int  i = 0;
557             char c;
558             while (dname[i]) {
559                 do {
560                     protopath[i] = dname[i];
561                     c = dname[++i];     /* next char */
562                 } while (!((c == 0) || (c == '/')));
563                 protopath[i] = 0;
564
565                 /* don't mkdir last component if target is a file */
566                 if ((c == 0) && (isDir == 0)) break;
567
568                 /* else create dir component if it doesn't exist */
569                 code = stat(protopath, &istat);
570                 if (code && (errno == ENOENT)) {
571                     code = mkdir (protopath, 0755);
572                     if (code) {
573                         printf ("Can't create destination path at %s\n",
574                                 protopath);
575                         exit (1);
576                     }
577                 }
578             } /* while dname not exhausted */
579             if (isDir == -1) isDir = 1;
580         }
581         else {
582             printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
583             exit(1);
584         }
585     } else {
586         if ((istat.st_mode & S_IFMT) == S_IFDIR)  isDir = 1;
587         else isDir = 0;
588     }
589
590     /* either can be n files and one dir, or one file and one target */
591     if (!isDir && fptr != 1) {
592         printf("target for multiple files must be a dir\n");
593         exit (1);
594     }
595
596     for (i=0;i<fptr;i++)
597         {/* figure out name to put as entry name for file */
598         tp = strrchr(fnames[i], '/');
599         if (tp) newNames[i] = tp+1;
600         else newNames[i] = fnames[i];
601         }
602     for (i=0;i<fptr;i++)
603         {/* copy newName[i] into directory dname */
604
605         /* pname is target file in either case */
606         if (isDir)
607             {strcpy(pname, dname);
608             strcat(pname, "/");
609             strcat(pname, newNames[i]);
610             }
611         else strcpy(pname, dname);
612         strcpy(pnametmp, pname);
613         /* Make up a temporary name for a destination */
614         pnamelen = strlen(pnametmp);
615         gethostname(myHostName, sizeof(myHostName)-1);  /* lv room for null */
616         if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
617         pnametmp[pnamelen] = '.';
618         strcpy(&pnametmp[pnamelen+1], myHostName);
619         if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
620
621         ifd = open(fnames[i], O_RDONLY, 0);
622         if (ifd < 0)
623             {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
624             rcode = 1;
625             continue;
626             }
627         if (fstat (ifd, &istat) < 0) {
628             printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
629             close(ifd);
630             rcode = 1;
631             continue;
632         }
633         if (lstat(pname, &ostat) == 0) {
634             if ((ostat.st_size == istat.st_size) &&
635                 (ostat.st_mtime == istat.st_mtime) &&
636                 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
637                 ((!setOwner) || (ostat.st_uid == owner)) &&
638                 ((!setGroup) || (ostat.st_gid == group))) {
639                 close(ifd);
640                 printf("No changes to %s since %s installed\n", fnames[i], pname);
641                 continue;
642             }
643         }
644 #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)
645         stripcalled = 0;
646         if (strip == 1 ||
647             ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i]))))
648             stripcalled = 1;
649         if (!stripcalled) {
650             /* Simply copy target to dest */
651             quickStrip(fnames[i], pnametmp, istat.st_size, 1);
652         } else {
653             if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
654                 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
655                 close(ifd); unlink(pnametmp);
656                 rcode = 1;
657                 continue;
658             }
659         }
660         close(ifd);
661
662         ofd = open(pnametmp, O_RDWR, 0);
663         if (ofd < 0) {
664             printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
665             close(ifd);
666             rcode = 1;
667             continue;
668         }
669         if (!setMode) mode = istat.st_mode;     /* Was 0755:> this is the default for our rcs to work */
670 #else /* AFS_AIX_ENV */
671         /* check to see if this file is hard to duplicate */
672         ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
673         if (ofd < 0)
674             {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
675             close(ifd);
676             rcode = 1;
677             continue;
678             }
679         if (!setMode) mode = istat.st_mode;     /* Was 0755:> this is the default for our rcs to work */
680         /* here both files are open and ready to go */
681         while (1)
682             {code = read(ifd, diskBuffer, BUFSIZE);
683             if (code == 0) break;
684             if (code < 0)
685                 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
686                 break;
687                 }
688             errno = 0;
689             newcode = write(ofd, diskBuffer, code);
690             if (newcode != code)
691                 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
692                 break;
693                 }
694             }
695         if (code != 0)
696             {rcode = 1; /* an error occurred copying the file */
697              printf("Warning: Error occurred writing output temp file %s; skipping it\n",
698                          pnametmp);
699             close(ifd); unlink(pnametmp); close(ofd);
700             continue;   /* to the next file */
701             }
702         /* strip the file? */
703         if (strip == 1 ||
704             (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
705                 if (quickStrip(ofd,istat.st_size) < 0) {
706                     printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
707                     close(ifd); unlink(pnametmp);
708                     rcode = 1;
709                     continue;
710                 }
711
712         /* do the chmod, etc calls before closing the file for max parallelism on store behind */
713         close(ifd);
714
715 #endif /* AFS_AIX_ENV */
716         if (fchmod(ofd, mode) < 0)
717             {printf("Couldn't chmod output temp file ``%s'': %s\n",
718                         pnametmp, ErrorString(errno));
719             unlink(pnametmp); close(ofd);
720             rcode = 1;
721             continue;
722             }
723
724         tvp[0].tv_sec = istat.st_atime;
725         tvp[0].tv_usec = 0;
726         tvp[1].tv_sec = istat.st_mtime;
727         tvp[1].tv_usec = 0;
728         if (utimes(pnametmp, tvp) < 0)
729             {printf("Couldn't utimes output temp file ``%s'': %s\n",
730                         pnametmp, ErrorString(errno));
731             unlink(pnametmp); close(ofd);
732             rcode = 1;
733             continue;
734             }
735         code = close(ofd);
736         if (code != 0)
737             {printf("Warning: Could not close output temp file %s (%s)\n",
738                         pnametmp, ErrorString(errno));
739             unlink(pnametmp);
740             rcode = 1;  /* an error occurred closing the output file */
741             continue;   /* to the next file */
742             }
743
744         /* do this later so vice doesn't see chown of unstored file */
745         if (setOwner || setGroup)
746             if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
747                 printf("Couldn't set %s for output temp file %s: %s\n",
748                         (setOwner? (setGroup? "owner and group" : "owner") : "group"),
749                         pnametmp, ErrorString(errno));
750                 unlink(pnametmp);
751                 rcode = 1;
752                 continue;
753             }
754
755         if (rename(pnametmp, pname) < 0) {
756 #if defined(AFS_HPUX_ENV)
757           if (errno == ETXTBSY) {
758             (void)strcpy(pnameBusy, pname);
759             (void)strcat(pnameBusy, ".BUSY");
760             if (rename(pname, pnameBusy) == 0) {
761               fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
762               fprintf(stdout,
763                     "Please delete this file when the program using it is finished.\n");
764               if (rename(pnametmp, pname) < 0) {
765 #endif /* AFS_HPUX_ENV */
766
767               printf("Couldn't rename temp file %s to be output file %s: %s\n",
768                      pnametmp, pname, ErrorString(errno));
769               unlink(pnametmp);
770               rcode = 1;
771               continue;
772
773 #if defined(AFS_HPUX_ENV)
774               }
775             }
776             else {
777               fprintf(stderr,
778                 "Couldn't move busy target file %s to make room for new version %s: %s\n",
779                       pname, pnametmp, ErrorString(errno));
780               if (errno == ETXTBSY) {
781                 fprintf(stderr,
782                     "Try terminating any programs using the file %s and then re-run %s.\n",
783                         pnameBusy, argv[0]);
784               }
785               unlink(pnametmp);
786               rcode = 1;
787               continue;
788             }
789           }
790 #endif /* AFS_HPUX_ENV */
791         }
792       }
793     /* all done now */
794     exit(rcode);
795 }