]>
git.saurik.com Git - apple/file_cmds.git/blob - install/xinstall.c
fa51babaa2b071a1645d2bfcd296b921b2f33cfc
1 /* $NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh Exp $ */
4 * Copyright (c) 1987, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/cdefs.h>
38 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\
39 The Regents of the University of California. All rights reserved.\n");
44 static char sccsid
[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
46 __RCSID("$NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh Exp $");
50 #include <sys/param.h>
67 #include "pathnames.h"
69 #define STRIP_ARGS_MAX 32
73 int docopy
=0, dodir
=0, dostrip
=0, dolink
=0, dopreserve
=0;
74 int mode
= S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
;
75 char pathbuf
[MAXPATHLEN
];
80 #define LN_ABSOLUTE 0x01
81 #define LN_RELATIVE 0x02
83 #define LN_SYMBOLIC 0x08
86 #define DIRECTORY 0x01 /* Tell install it's a directory. */
87 #define SETFLAGS 0x02 /* Tell install to set flags. */
89 void copy
__P((int, char *, int, char *, off_t
));
90 void makelink
__P((char *, char *));
91 void install
__P((char *, char *, u_long
, u_int
));
92 void install_dir
__P((char *));
93 u_long string_to_flags
__P((char **, u_long
*, u_long
*));
94 void strip
__P((char *));
95 void usage
__P((void));
96 int main
__P((int, char *[]));
103 struct stat from_sb
, to_sb
;
109 char *flags
= NULL
, *to_name
, *group
= NULL
, *owner
= NULL
;
112 while ((ch
= getopt(argc
, argv
, "cdf:g:l:m:o:psS:")) != -1)
122 if (string_to_flags(&flags
, &fset
, NULL
))
123 errx(1, "%s: invalid flag", flags
);
130 for (p
= optarg
; *p
; p
++)
133 dolink
&= ~(LN_HARD
|LN_MIXED
);
134 dolink
|= LN_SYMBOLIC
;
137 dolink
&= ~(LN_SYMBOLIC
|LN_MIXED
);
141 dolink
&= ~(LN_SYMBOLIC
|LN_HARD
);
145 dolink
&= ~LN_RELATIVE
;
146 dolink
|= LN_ABSOLUTE
;
149 dolink
&= ~LN_ABSOLUTE
;
150 dolink
|= LN_RELATIVE
;
153 errx(1, "%c: invalid link type", *p
);
158 if (!(set
= setmode(optarg
)))
159 errx(1, "%s: invalid file mode", optarg
);
160 mode
= getmode(set
, 0);
169 stripArgs
= (char*)malloc(sizeof(char)*(strlen(optarg
)+1));
170 strcpy(stripArgs
,optarg
);
171 /* fall through; -S implies -s */
182 /* strip and link options make no sense when creating directories */
183 if ((dostrip
|| dolink
) && dodir
)
186 /* strip and flags make no sense with links */
187 if ((dostrip
|| flags
) && dolink
)
190 /* must have at least two arguments, except when creating directories */
191 if (argc
< 2 && !dodir
)
194 /* get group and owner id's */
195 if (group
&& !(gp
= getgrnam(group
)) && !isdigit(*group
))
196 errx(1, "unknown group %s", group
);
197 gid
= (group
) ? ((gp
) ? gp
->gr_gid
: atoi(group
)) : -1;
198 if (owner
&& !(pp
= getpwnam(owner
)) && !isdigit(*owner
))
199 errx(1, "unknown user %s", owner
);
200 uid
= (owner
) ? ((pp
) ? pp
->pw_uid
: atoi(owner
)) : -1;
203 for (; *argv
!= NULL
; ++argv
)
208 no_target
= stat(to_name
= argv
[argc
- 1], &to_sb
);
209 if (!no_target
&& S_ISDIR(to_sb
.st_mode
)) {
210 for (; *argv
!= to_name
; ++argv
)
211 install(*argv
, to_name
, fset
, iflags
| DIRECTORY
);
215 /* can't do file1 file2 directory/file */
220 if (stat(*argv
, &from_sb
))
222 if (!S_ISREG(to_sb
.st_mode
))
223 errx(1, "%s: %s", to_name
, strerror(EFTYPE
));
224 if (!dolink
&& to_sb
.st_dev
== from_sb
.st_dev
&&
225 to_sb
.st_ino
== from_sb
.st_ino
)
226 errx(1, "%s and %s are the same file", *argv
, to_name
);
228 * Unlink now... avoid ETXTBSY errors later. Try and turn
229 * off the append/immutable bits -- if we fail, go ahead,
232 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
233 if (to_sb
.st_flags
& NOCHANGEBITS
)
234 (void)chflags(to_name
,
235 to_sb
.st_flags
& ~(NOCHANGEBITS
));
236 (void)unlink(to_name
);
238 install(*argv
, to_name
, fset
, iflags
);
244 * make a link from source to destination
247 makelink(from_name
, to_name
)
251 char src
[MAXPATHLEN
], dst
[MAXPATHLEN
], lnk
[MAXPATHLEN
];
253 /* Try hard links first */
254 if (dolink
& (LN_HARD
|LN_MIXED
)) {
255 if (link(from_name
, to_name
) == -1) {
256 if ((dolink
& LN_HARD
) || errno
!= EXDEV
)
257 err(1, "link %s -> %s", from_name
, to_name
);
264 if (dolink
& LN_ABSOLUTE
) {
265 /* Convert source path to absolute */
266 if (realpath(from_name
, src
) == NULL
)
268 if (symlink(src
, to_name
) == -1)
269 err(1, "symlink %s -> %s", src
, to_name
);
273 if (dolink
& LN_RELATIVE
) {
276 /* Resolve pathnames */
277 if (realpath(from_name
, src
) == NULL
)
279 if (realpath(to_name
, dst
) == NULL
)
282 /* trim common path components */
283 for (s
= src
, d
= dst
; *s
== *d
; s
++, d
++)
288 /* count the number of directories we need to backtrack */
289 for (++d
, lnk
[0] = '\0'; *d
; d
++)
291 (void) strcat(lnk
, "../");
293 (void) strcat(lnk
, ++s
);
295 if (symlink(lnk
, dst
) == -1)
296 err(1, "symlink %s -> %s", lnk
, dst
);
301 * If absolute or relative was not specified,
302 * try the names the user provided
304 if (symlink(from_name
, to_name
) == -1)
305 err(1, "symlink %s -> %s", from_name
, to_name
);
311 * build a path name and install the file
314 install(from_name
, to_name
, fset
, flags
)
315 char *from_name
, *to_name
;
319 struct stat from_sb
, to_sb
;
320 int devnull
, from_fd
, to_fd
, serrno
;
323 /* If try to install NULL file to a directory, fails. */
324 if (flags
& DIRECTORY
|| strcmp(from_name
, _PATH_DEVNULL
)) {
325 if (stat(from_name
, &from_sb
))
326 err(1, "%s", from_name
);
327 if (!S_ISREG(from_sb
.st_mode
))
328 errx(1, "%s: %s", from_name
, strerror(EFTYPE
));
329 /* Build the target path. */
330 if (flags
& DIRECTORY
) {
331 (void)snprintf(pathbuf
, sizeof(pathbuf
), "%s/%s",
333 (p
= strrchr(from_name
, '/')) ? ++p
: from_name
);
338 from_sb
.st_flags
= 0; /* XXX */
343 * Unlink now... avoid ETXTBSY errors later. Try and turn
344 * off the append/immutable bits -- if we fail, go ahead,
347 if (stat(to_name
, &to_sb
) == 0 &&
348 to_sb
.st_flags
& (NOCHANGEBITS
))
349 (void)chflags(to_name
, to_sb
.st_flags
& ~(NOCHANGEBITS
));
350 (void)unlink(to_name
);
353 makelink(from_name
, to_name
);
358 if ((to_fd
= open(to_name
,
359 O_CREAT
| O_WRONLY
| O_TRUNC
, S_IRUSR
| S_IWUSR
)) < 0)
360 err(1, "%s", to_name
);
362 if ((from_fd
= open(from_name
, O_RDONLY
, 0)) < 0) {
363 (void)unlink(to_name
);
364 err(1, "%s", from_name
);
366 copy(from_fd
, from_name
, to_fd
, to_name
, from_sb
.st_size
);
367 (void)close(from_fd
);
374 * Re-open our fd on the target, in case we used a strip
375 * that does not work in-place -- like gnu binutils strip.
378 if ((to_fd
= open(to_name
, O_RDONLY
, S_IRUSR
| S_IWUSR
)) < 0)
379 err(1, "stripping %s", to_name
);
383 * Set owner, group, mode for target; do the chown first,
384 * chown may lose the setuid bits.
386 if ((gid
!= -1 || uid
!= -1) && fchown(to_fd
, uid
, gid
)) {
388 (void)unlink(to_name
);
389 errx(1, "%s: chown/chgrp: %s", to_name
, strerror(serrno
));
391 if (fchmod(to_fd
, mode
)) {
393 (void)unlink(to_name
);
394 errx(1, "%s: chmod: %s", to_name
, strerror(serrno
));
398 * If provided a set of flags, set them, otherwise, preserve the
399 * flags, except for the dump flag.
402 flags
& SETFLAGS
? fset
: from_sb
.st_flags
& ~UF_NODUMP
)) {
403 if (errno
!= EOPNOTSUPP
|| (from_sb
.st_flags
& ~UF_NODUMP
) != 0)
404 warn("%s: chflags", to_name
);
408 * Preserve the date of the source file.
411 struct timeval tv
[2];
413 TIMESPEC_TO_TIMEVAL(&tv
[0], &from_sb
.st_atimespec
);
414 TIMESPEC_TO_TIMEVAL(&tv
[1], &from_sb
.st_mtimespec
);
416 if (futimes(to_fd
, tv
) == -1)
417 warn("%s: futimes", to_name
);
419 if (utimes(to_name
, tv
) == -1)
420 warn("%s: utimes", to_name
);
425 if (!docopy
&& !devnull
&& unlink(from_name
))
426 err(1, "%s", from_name
);
431 * copy from one file to another
434 copy(from_fd
, from_name
, to_fd
, to_name
, size
)
436 char *from_name
, *to_name
;
445 * There's no reason to do anything other than close the file
446 * now if it's empty, so let's not bother.
450 * Mmap and write if less than 8M (the limit is so we don't totally
451 * trash memory on big files). This is really a minor hack, but it
452 * wins some CPU back.
454 if (size
<= 8 * 1048576) {
455 if ((p
= mmap(NULL
, (size_t)size
, PROT_READ
,
456 MAP_FILE
|MAP_SHARED
, from_fd
, (off_t
)0)) == (char *)-1)
457 err(1, "%s", from_name
);
458 if (write(to_fd
, p
, size
) != size
)
459 err(1, "%s", to_name
);
461 while ((nr
= read(from_fd
, buf
, sizeof(buf
))) > 0)
462 if ((nw
= write(to_fd
, buf
, nr
)) != nr
) {
464 (void)unlink(to_name
);
466 to_name
, strerror(nw
> 0 ? EIO
: serrno
));
470 (void)unlink(to_name
);
471 errx(1, "%s: %s", from_name
, strerror(serrno
));
479 * use strip(1) to strip the target file
491 (void)unlink(to_name
);
492 errx(1, "vfork: %s", strerror(serrno
));
494 stripprog
= getenv("STRIP");
495 if (stripprog
== NULL
)
496 stripprog
= _PATH_STRIP
;
499 /* build up a command line and let /bin/sh parse the arguments */
500 char* cmd
= (char*)malloc(sizeof(char)*
501 (3+strlen(stripprog
)+
505 sprintf(cmd
, "%s %s %s", stripprog
, stripArgs
, to_name
);
507 execl(_PATH_BSHELL
, "sh", "-c", cmd
, NULL
);
509 execl(stripprog
, "strip", to_name
, NULL
);
511 warn("%s", stripprog
);
514 if (wait(&status
) == -1 || status
)
515 (void)unlink(to_name
);
521 * build directory heirarchy
532 if (!*p
|| (p
!= path
&& *p
== '/')) {
535 if (stat(path
, &sb
)) {
536 if (errno
!= ENOENT
|| mkdir(path
, 0777) < 0) {
545 if (((gid
!= -1 || uid
!= -1) && chown(path
, uid
, gid
)) ||
553 * print a usage message and die
558 (void)fprintf(stderr
, "\
559 usage: install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 file2\n\
560 install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 ... fileN directory\n\
561 install -pd [-m mode] [-o owner] [-g group] directory ...\n");