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