* Copyright (c) 1987 Regents of the University of California.
* %sccs.include.redist.c%
"@(#) Copyright (c) 1987 Regents of the University of California.\n\
static char sccsid
[] = "@(#)xinstall.c 5.30 (Berkeley) %G%";
int mode
= S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
;
char *group
, *owner
, pathbuf
[MAXPATHLEN
];
#define DIRECTORY 0x01 /* Tell install it's a directory. */
#define SETFLAGS 0x02 /* Tell install to set flags. */
void copy
__P((int, char *, int, char *, off_t
));
void err
__P((const char *, ...));
void install
__P((char *, char *, u_long
, u_int
));
u_long string_to_flags
__P((char **, u_long
*, u_long
*));
void strip
__P((char *));
struct stat from_sb
, to_sb
;
while ((ch
= getopt(argc
, argv
, "cf:g:m:o:s")) != EOF
)
if (string_to_flags(&flags
, &fset
, NULL
))
err("%s: invalid flag", flags
);
if (!(set
= setmode(optarg
)))
err("%s: invalid file mode", optarg
);
/* get group and owner id's */
if (group
&& !(gp
= getgrnam(group
)))
err("unknown group %s", group
);
if (owner
&& !(pp
= getpwnam(owner
)))
err("unknown user %s", owner
);
no_target
= stat(to_name
= argv
[argc
- 1], &to_sb
);
if (!no_target
&& (to_sb
.st_mode
& S_IFMT
) == S_IFDIR
) {
for (; *argv
!= to_name
; ++argv
)
install(*argv
, to_name
, fset
, iflags
| DIRECTORY
);
/* can't do file1 file2 directory/file */
if (stat(*argv
, &from_sb
))
err("%s: %s", *argv
, strerror(errno
));
if (!S_ISREG(to_sb
.st_mode
))
err("%s: %s", to_name
, strerror(EFTYPE
));
if (to_sb
.st_dev
== from_sb
.st_dev
&&
to_sb
.st_ino
== from_sb
.st_ino
)
err("%s and %s are the same file", *argv
, to_name
);
* Unlink now... avoid ETXTBSY errors later. Try and turn
* off the append/immutable bits -- if we fail, go ahead,
if (to_sb
.st_mode
& (IMMUTABLE
| APPEND
))
to_sb
.st_flags
& ~(APPEND
| IMMUTABLE
));
install(*argv
, to_name
, fset
, iflags
);
* build a path name and install the file
install(from_name
, to_name
, fset
, flags
)
char *from_name
, *to_name
;
struct stat from_sb
, to_sb
;
int devnull
, from_fd
, to_fd
, serrno
;
/* If try to install NULL file to a directory, fails. */
if (flags
& DIRECTORY
|| strcmp(from_name
, _PATH_DEVNULL
)) {
if (stat(from_name
, &from_sb
))
err("%s: %s", from_name
, strerror(errno
));
if (!S_ISREG(from_sb
.st_mode
))
err("%s: %s", from_name
, strerror(EFTYPE
));
/* Build the target path. */
(void)snprintf(pathbuf
, sizeof(pathbuf
), "%s/%s",
(p
= rindex(from_name
, '/')) ? ++p
: from_name
);
* Unlink now... avoid ETXTBSY errors later. Try and turn
* off the append/immutable bits -- if we fail, go ahead,
if (stat(to_name
, &to_sb
) == 0 && to_sb
.st_flags
& (APPEND
| IMMUTABLE
))
(void)chflags(to_name
, to_sb
.st_flags
& ~(APPEND
| IMMUTABLE
));
if ((to_fd
= open(to_name
,
O_CREAT
| O_WRONLY
| O_TRUNC
, S_IRUSR
| S_IWUSR
)) < 0)
err("%s: %s", to_name
, strerror(errno
));
if ((from_fd
= open(from_name
, O_RDONLY
, 0)) < 0) {
err("%s: %s", from_name
, strerror(errno
));
copy(from_fd
, from_name
, to_fd
, to_name
, from_sb
.st_size
);
* Set owner, group, mode for target; do the chown first,
* chown may lose the setuid bits.
fchown(to_fd
, owner
? pp
->pw_uid
: -1, group
? gp
->gr_gid
: -1)) {
err("%s: chown/chgrp: %s", to_name
, strerror(serrno
));
if (fchmod(to_fd
, mode
)) {
err("%s: chmod: %s", to_name
, strerror(serrno
));
* If provided a set of flags, set them, otherwise, preserve the
* flags, except for the dump flag.
flags
& SETFLAGS
? fset
: from_sb
.st_flags
& ~NODUMP
)) {
err("%s: chflags: %s", to_name
, strerror(serrno
));
if (!docopy
&& !devnull
&& unlink(from_name
))
err("%s: %s", from_name
, strerror(errno
));
* copy from one file to another
copy(from_fd
, from_name
, to_fd
, to_name
, size
)
register int from_fd
, to_fd
;
char *from_name
, *to_name
;
* Mmap and write if less than 8M (the limit is so we don't totally
* trash memory on big files. This is really a minor hack, but it
if (size
<= 8 * 1048576) {
if ((p
= mmap(NULL
, (size_t)size
, PROT_READ
,
0, from_fd
, (off_t
)0)) == (char *)-1)
err("%s: %s", from_name
, strerror(errno
));
if (write(to_fd
, p
, size
) != size
)
err("%s: %s", to_name
, strerror(errno
));
while ((nr
= read(from_fd
, buf
, sizeof(buf
))) > 0)
if ((nw
= write(to_fd
, buf
, nr
)) != nr
) {
to_name
, strerror(nw
> 0 ? EIO
: serrno
));
err("%s: %s", from_name
, strerror(serrno
));
* use strip(1) to strip the target file
err("forks: %s", strerror(errno
));
execl(_PATH_STRIP
, "strip", to_name
, NULL
);
err("%s: %s", _PATH_STRIP
, strerror(errno
));
if (wait(&status
) == -1 || status
)
* print a usage message and die
"usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n");
err(const char *fmt
, ...)
(void)fprintf(stderr
, "install: ");
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, "\n");