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