diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile index 7c76c3954ef1..227c7046e618 100644 --- a/usr.bin/xinstall/Makefile +++ b/usr.bin/xinstall/Makefile @@ -1,14 +1,13 @@ -# from: @(#)Makefile 5.5 (Berkeley) 6/24/90 -# $Id: Makefile,v 1.5 1993/07/30 22:14:18 mycroft Exp $ +# $NetBSD: Makefile,v 1.6 1994/11/14 04:57:16 jtc Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 PROG= xinstall +SRCS= stat_flags.c xinstall.c MAN1= install.0 -CLEANFILES=xinstall +.PATH: ${.CURDIR}/../../bin/ls realinstall: install ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${PROG} ${DESTDIR}${BINDIR}/install .include - - diff --git a/usr.bin/xinstall/install.1 b/usr.bin/xinstall/install.1 index 4223ef2d4342..77f695d088bd 100644 --- a/usr.bin/xinstall/install.1 +++ b/usr.bin/xinstall/install.1 @@ -1,5 +1,7 @@ -.\" Copyright (c) 1987, 1990 The Regents of the University of California. -.\" All rights reserved. +.\" $NetBSD: install.1,v 1.4 1994/11/14 04:57:17 jtc Exp $ +.\" +.\" Copyright (c) 1987, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -29,10 +31,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" from: @(#)install.1 6.11 (Berkeley) 5/31/91 -.\" $Id: install.1,v 1.3 1993/08/01 07:25:49 mycroft Exp $ +.\" @(#)install.1 8.1 (Berkeley) 6/6/93 .\" -.Dd May 31, 1991 +.Dd June 6, 1993 .Dt INSTALL 1 .Os BSD 4.2 .Sh NAME @@ -41,15 +42,17 @@ .Sh SYNOPSIS .Nm install .Op Fl cs +.Op Fl f Ar flags +.Op Fl g Ar group .Op Fl m Ar mode .Op Fl o Ar owner -.Op Fl g Ar group .Ar file1 file2 .Nm install .Op Fl cs +.Op Fl f Ar flags +.Op Fl g Ar group .Op Fl m Ar mode .Op Fl o Ar owner -.Op Fl g Ar group .Ar file1 \&... .Ar fileN directory @@ -73,16 +76,26 @@ If the target file already exists, it is overwritten if permissions allow. .Pp .Bl -tag -width Ds +.It Fl c +Copy the file. +This flag turns off the default behavior of +.Nm install +where it deletes the original file after creating the target. +.It Fl f +Specify the target's file flags. +(See +.Xr chflags 1 +for a list of possible flags and their meanings.) +.It Fl g +Specify a group. .It Fl m Specify an alternate mode. -The default mode is set to 755. +The default mode is set to rwxr-xr-x (0755). The specified mode may be either an octal or symbolic value; see .Xr chmod 1 for a description of possible mode values. .It Fl o Specify an owner. -.It Fl g -Specify a group. .It Fl s .Nm Install exec's the command @@ -94,8 +107,13 @@ Create directories. Missing parent directories are created as required. .El .Pp -.Nm Install -refuses to move a file onto itself. +By default, +.Nm install +preserves all file flags, with the exception of the ``nodump'' flag. +.Pp +The +.Nm install +utility attempts to prevent moving a file onto itself. .Pp Installing .Pa /dev/null @@ -104,12 +122,12 @@ creates an empty file. Upon successful completion a value of 0 is returned. Otherwise, a value of 1 is returned. .Sh SEE ALSO +.Xr chflags 1 , .Xr chgrp 1 , .Xr chmod 1 , .Xr cp 1 , .Xr mv 1 , .Xr strip 1 , -.Xr a.out 5 , .Xr chown 8 .Sh HISTORY The diff --git a/usr.bin/xinstall/pathnames.h b/usr.bin/xinstall/pathnames.h index 5eec1c2e43f4..56843d95fc8e 100644 --- a/usr.bin/xinstall/pathnames.h +++ b/usr.bin/xinstall/pathnames.h @@ -1,6 +1,8 @@ +/* $NetBSD: pathnames.h,v 1.3 1994/11/14 04:57:18 jtc Exp $ */ + /* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,8 +32,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90 - * $Id: pathnames.h,v 1.2 1993/08/01 18:00:57 mycroft Exp $ + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: pathnames.h,v 1.3 1994/11/14 04:57:18 jtc Exp $ */ #define _PATH_STRIP "/usr/bin/strip" diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c index 3ed8440ddeb4..2e4179426b18 100644 --- a/usr.bin/xinstall/xinstall.c +++ b/usr.bin/xinstall/xinstall.c @@ -1,6 +1,8 @@ +/* $NetBSD: xinstall.c,v 1.5 1994/11/14 04:57:19 jtc Exp $ */ + /* - * Copyright (c) 1987 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,57 +34,82 @@ */ #ifndef lint -char copyright[] = -"@(#) Copyright (c) 1987 Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -/*static char sccsid[] = "from: @(#)xinstall.c 5.24 (Berkeley) 7/1/90";*/ -static char rcsid[] = "$Id: xinstall.c,v 1.4 1994/10/02 21:32:31 cgd Exp $"; +#if 0 +static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; +#endif +static char rcsid[] = "$NetBSD: xinstall.c,v 1.5 1994/11/14 04:57:19 jtc Exp $"; #endif /* not lint */ #include +#include +#include #include -#include -#include -#include -#include + #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include + #include "pathnames.h" -static struct passwd *pp; -static struct group *gp; -static int docopy, dostrip, dodir, mode = 0755; -static char *group, *owner, pathbuf[MAXPATHLEN]; +struct passwd *pp; +struct group *gp; +int docopy, dodir, dostrip; +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 install __P((char *, char *, u_long, u_int)); +u_long string_to_flags __P((char **, u_long *, u_long *)); +void strip __P((char *)); +void usage __P((void)); + +int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern char *optarg; - extern int optind; struct stat from_sb, to_sb; - mode_t *set, *setmode(); + mode_t *set; + u_long fset; + u_int iflags; int ch, no_target; - char *to_name; + char *flags, *to_name; - while ((ch = getopt(argc, argv, "cg:m:o:sd")) != EOF) + iflags = 0; + while ((ch = getopt(argc, argv, "cf:g:m:o:sd")) != EOF) switch((char)ch) { case 'c': docopy = 1; break; + case 'f': + flags = optarg; + if (string_to_flags(&flags, &fset, NULL)) + errx(1, "%s: invalid flag", flags); + iflags |= SETFLAGS; + break; case 'g': group = optarg; break; case 'm': - if (!(set = setmode(optarg))) { - (void)fprintf(stderr, - "install: invalid file mode.\n"); - exit(1); - } + if (!(set = setmode(optarg))) + errx(1, "%s: invalid file mode", optarg); mode = getmode(set, 0); break; case 'o': @@ -110,26 +137,22 @@ main(argc, argv) usage(); /* get group and owner id's */ - if (group && !(gp = getgrnam(group))) { - fprintf(stderr, "install: unknown group %s.\n", group); - exit(1); - } - if (owner && !(pp = getpwnam(owner))) { - fprintf(stderr, "install: unknown user %s.\n", owner); - exit(1); - } + if (group && !(gp = getgrnam(group))) + errx(1, "unknown group %s", group); + if (owner && !(pp = getpwnam(owner))) + errx(1, "unknown user %s", owner); if (dodir) { for (; *argv != NULL; ++argv) - build(*argv); + install_dir(*argv); exit (0); /* NOTREACHED */ - } + } no_target = stat(to_name = argv[argc - 1], &to_sb); if (!no_target && S_ISDIR(to_sb.st_mode)) { for (; *argv != to_name; ++argv) - install(*argv, to_name, 1); + install(*argv, to_name, fset, iflags | DIRECTORY); exit(0); } @@ -138,22 +161,25 @@ main(argc, argv) usage(); if (!no_target) { - if (stat(*argv, &from_sb)) { - fprintf(stderr, "install: can't find %s.\n", *argv); - exit(1); - } - if (!S_ISREG(to_sb.st_mode)) { - fprintf(stderr, "install: %s isn't a regular file.\n", to_name); - exit(1); - } - if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino) { - fprintf(stderr, "install: %s and %s are the same file.\n", *argv, to_name); - exit(1); - } - /* unlink now... avoid ETXTBSY errors later */ + if (stat(*argv, &from_sb)) + err(1, "%s", *argv); + if (!S_ISREG(to_sb.st_mode)) + errx(1, "%s: %s", to_name, strerror(EFTYPE)); + if (to_sb.st_dev == from_sb.st_dev && + to_sb.st_ino == from_sb.st_ino) + errx(1, "%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, + * it might work. + */ +#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) + if (to_sb.st_flags & NOCHANGEBITS) + (void)chflags(to_name, + to_sb.st_flags & ~(NOCHANGEBITS)); (void)unlink(to_name); } - install(*argv, to_name, 0); + install(*argv, to_name, fset, iflags); exit(0); } @@ -161,88 +187,129 @@ main(argc, argv) * install -- * build a path name and install the file */ -install(from_name, to_name, isdir) +void +install(from_name, to_name, fset, flags) char *from_name, *to_name; - int isdir; + u_long fset; + u_int flags; { - struct stat from_sb; - int devnull, from_fd, to_fd; - char *C, *rindex(); + struct stat from_sb, to_sb; + int devnull, from_fd, to_fd, serrno; + char *p; - /* if try to install NULL file to a directory, fails */ - if (isdir || strcmp(from_name, _PATH_DEVNULL)) { - if (stat(from_name, &from_sb)) { - fprintf(stderr, "install: can't find %s.\n", from_name); - exit(1); - } - if (!S_ISREG(from_sb.st_mode)) { - fprintf(stderr, "install: %s isn't a regular file.\n", from_name); - exit(1); - } - /* build the target path */ - if (isdir) { - (void)sprintf(pathbuf, "%s/%s", to_name, (C = rindex(from_name, '/')) ? ++C : from_name); + /* 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(1, "%s", from_name); + if (!S_ISREG(from_sb.st_mode)) + errx(1, "%s: %s", from_name, strerror(EFTYPE)); + /* Build the target path. */ + if (flags & DIRECTORY) { + (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + to_name, + (p = rindex(from_name, '/')) ? ++p : from_name); to_name = pathbuf; } devnull = 0; - } else + } else { + from_sb.st_flags = 0; /* XXX */ devnull = 1; + } - /* unlink now... avoid ETXTBSY errors later */ + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ + if (stat(to_name, &to_sb) == 0 && + to_sb.st_flags & (NOCHANGEBITS)) + (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); (void)unlink(to_name); - /* create target */ - if ((to_fd = open(to_name, O_CREAT|O_WRONLY|O_TRUNC, 0600)) < 0) { - error(to_name); - exit(1); - } + /* Create target. */ + if ((to_fd = open(to_name, + O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) + err(1, "%s", to_name); if (!devnull) { if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { (void)unlink(to_name); - error(from_name); - exit(1); + err(1, "%s", from_name); } - copy(from_fd, from_name, to_fd, to_name); + copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); (void)close(from_fd); } if (dostrip) strip(to_name); /* - * set owner, group, mode for target; do the chown first, + * Set owner, group, mode for target; do the chown first, * chown may lose the setuid bits. */ if ((group || owner) && - fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1) || - fchmod(to_fd, mode)) { - error(to_name); - bad(to_name); + fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); } + if (fchmod(to_fd, mode)) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chmod: %s", to_name, strerror(serrno)); + } + + /* + * If provided a set of flags, set them, otherwise, preserve the + * flags, except for the dump flag. + */ + if (fchflags(to_fd, + flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chflags: %s", to_name, strerror(serrno)); + } + (void)close(to_fd); - if (!docopy && !devnull && unlink(from_name)) { - error(from_name); - exit(1); - } + if (!docopy && !devnull && unlink(from_name)) + err(1, "%s", from_name); } /* * copy -- * copy from one file to another */ -copy(from_fd, from_name, to_fd, to_name) +void +copy(from_fd, from_name, to_fd, to_name, size) register int from_fd, to_fd; char *from_name, *to_name; + off_t size; { - register int n; - char buf[MAXBSIZE]; + register int nr, nw; + int serrno; + char *p, buf[MAXBSIZE]; - while ((n = read(from_fd, buf, sizeof(buf))) > 0) - if (write(to_fd, buf, n) != n) { - error(to_name); - bad(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 + * wins some CPU back. + */ + if (size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)size, PROT_READ, + 0, from_fd, (off_t)0)) == (char *)-1) + err(1, "%s", from_name); + if (write(to_fd, p, size) != size) + err(1, "%s", to_name); + } else { + while ((nr = read(from_fd, buf, sizeof(buf))) > 0) + if ((nw = write(to_fd, buf, nr)) != nr) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: %s", + to_name, strerror(nw > 0 ? EIO : serrno)); + } + if (nr != 0) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: %s", from_name, strerror(serrno)); } - if (n == -1) { - error(from_name); - bad(to_name); } } @@ -250,30 +317,32 @@ copy(from_fd, from_name, to_fd, to_name) * strip -- * use strip(1) to strip the target file */ +void strip(to_name) char *to_name; { - int status; + int serrno, status; switch (vfork()) { case -1: - error("fork"); - bad(to_name); + serrno = errno; + (void)unlink(to_name); + errx(1, "forks: %s", strerror(errno)); case 0: - execl(_PATH_STRIP, "strip", to_name, (char *)NULL); - error(_PATH_STRIP); - _exit(1); + execl(_PATH_STRIP, "strip", to_name, NULL); + err(1, "%s", _PATH_STRIP); default: if (wait(&status) == -1 || status) - bad(to_name); + (void)unlink(to_name); } } /* - * build -- + * install_dir -- * build directory heirarchy */ -build(path) +int +install_dir(path) char *path; { register char *p; @@ -286,7 +355,7 @@ build(path) *p = '\0'; if (stat(path, &sb)) { if (errno != ENOENT || mkdir(path, 0777) < 0) { - error(path); + warn("%s", path); return(1); } } @@ -297,45 +366,21 @@ build(path) if ((group || owner) && chown(path, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1) || chmod(path, mode)) { - error(path); + warn("%s", path); } return(0); } -/* - * error -- - * print out an error message - */ -error(s) - char *s; -{ - extern int errno; - char *strerror(); - - (void)fprintf(stderr, "install: %s: %s\n", s, strerror(errno)); -} - -/* - * bad -- - * remove created target and die - */ -bad(fname) - char *fname; -{ - (void)unlink(fname); - exit(1); -} - /* * usage -- * print a usage message and die */ +void usage() { (void)fprintf(stderr, "\ -usage: install [-cs] [-g group] [-m mode] [-o owner] file1 file2\n\ - install [-cs] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\ +usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\ + install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\ install -d [-g group] [-m mode] [-o owner] directory ...\n"); - exit(1); }