summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.bin/xinstall/install.147
-rw-r--r--usr.bin/xinstall/xinstall.c296
2 files changed, 265 insertions, 78 deletions
diff --git a/usr.bin/xinstall/install.1 b/usr.bin/xinstall/install.1
index c35c7a5..84ec638 100644
--- a/usr.bin/xinstall/install.1
+++ b/usr.bin/xinstall/install.1
@@ -29,9 +29,10 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)install.1 8.1 (Berkeley) 6/6/93
+.\" From: @(#)install.1 8.1 (Berkeley) 6/6/93
+.\" $Id$
.\"
-.Dd June 6, 1993
+.Dd October 9, 1995
.Dt INSTALL 1
.Os BSD 4.2
.Sh NAME
@@ -46,7 +47,7 @@
.Op Fl o Ar owner
.Ar file1 file2
.Nm install
-.Op Fl cs
+.Op Fl Ccdps
.Op Fl f Ar flags
.Op Fl g Ar group
.Op Fl m Ar mode
@@ -67,11 +68,29 @@ If the target file already exists, it is overwritten if permissions
allow.
.Pp
.Bl -tag -width Ds
+.It Fl C .
+Copy the file, as if the
+.Fl c
+option is specifed,
+except if the target file already exists and the files are the same,
+then don't change the modification time of the target
.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 d
+Print debugging information.
+If
+.Fl d
+is specified one or more times,
+then print the renaming steps for
+.Fl C .
+If
+.Fl d
+is specified two or more times,
+then warn about files that aren't installed with
+.Fl C .
.It Fl f
Specify the target's file flags.
(See
@@ -87,6 +106,13 @@ The specified mode may be either an octal or symbolic value; see
for a description of possible mode values.
.It Fl o
Specify an owner.
+.It Fl p
+Preserve the modification time.
+Copy the file, as if the
+.Fl C
+(Compare and copy) option is specifed,
+except if the target file doesn't already exist or is different,
+then preserve the modification time of the file.
.It Fl s
.Nm Install
exec's the command
@@ -109,6 +135,17 @@ creates an empty file.
.Pp
Upon successful completion a value of 0 is returned.
Otherwise, a value of 1 is returned.
+.Sh FILES
+.Bl -tag -width INS@XXXX -compact
+.It Pa INS@XXXX
+If the
+.Fl C
+or
+.Fl p
+option is used, then temporary files named INS@XXXX,
+where XXXX is decided by
+.Xr mkstemp 3 ,
+are created in the target directory.
.Sh SEE ALSO
.Xr chflags 1 ,
.Xr chgrp 1 ,
@@ -122,3 +159,7 @@ The
.Nm install
utility appeared in
.Bx 4.2 .
+.Sh BUGS
+Temporary files may be left in the target directory if
+.Nm install
+exits abnormally.
diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c
index 4c41fa8..eeae7da 100644
--- a/usr.bin/xinstall/xinstall.c
+++ b/usr.bin/xinstall/xinstall.c
@@ -32,21 +32,39 @@
*/
#ifndef lint
-static char copyright[] =
+static const 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[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
+/*static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93";*/
+static const char rcsid[] =
+ "$Id$";
#endif /* not lint */
+/*-
+ * Todo:
+ * o for -C, compare original files except in -s case.
+ * o for -C, don't change anything if nothing needs be changed. In
+ * particular, don't toggle the immutable flags just to allow null
+ * attribute changes and don't clear the dump flag. (I think inode
+ * ctimes are not updated for null attribute changes, but this is a
+ * bug.)
+ * o independent of -C, if a copy must be made, then copy to a tmpfile,
+ * set all attributes except the immutable flags, then rename, then
+ * set the immutable flags. It's annoying that the immutable flags
+ * defeat the atomicicity of rename - it seems that there must be
+ * o a window where the target is not immutable.
+ */
+
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <ctype.h>
+#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
@@ -55,21 +73,26 @@ static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sysexits.h>
#include <unistd.h>
+#include <utime.h>
#include "pathnames.h"
struct passwd *pp;
struct group *gp;
-int docopy, dostrip;
+int debug, docompare, docopy, dopreserve, dostrip;
int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
char *group, *owner, pathbuf[MAXPATHLEN];
+char pathbuf2[MAXPATHLEN];
#define DIRECTORY 0x01 /* Tell install it's a directory. */
#define SETFLAGS 0x02 /* Tell install to set flags. */
+#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
void copy __P((int, char *, int, char *, off_t));
-void err __P((const char *, ...));
+int compare __P((int, const char *, int, const char *,
+ const struct stat *, const struct stat *));
void install __P((char *, char *, u_long, u_int));
u_long string_to_flags __P((char **, u_long *, u_long *));
void strip __P((char *));
@@ -88,15 +111,21 @@ main(argc, argv)
char *flags, *to_name;
iflags = 0;
- while ((ch = getopt(argc, argv, "cf:g:m:o:s")) != EOF)
+ while ((ch = getopt(argc, argv, "Ccdf:g:m:o:ps")) != EOF)
switch((char)ch) {
+ case 'C':
+ docompare = docopy = 1;
+ break;
case 'c':
docopy = 1;
break;
+ case 'd':
+ debug++;
+ break;
case 'f':
flags = optarg;
if (string_to_flags(&flags, &fset, NULL))
- err("%s: invalid flag", flags);
+ errx(EX_USAGE, "%s: invalid flag", flags);
iflags |= SETFLAGS;
break;
case 'g':
@@ -104,12 +133,16 @@ main(argc, argv)
break;
case 'm':
if (!(set = setmode(optarg)))
- err("%s: invalid file mode", optarg);
+ errx(EX_USAGE, "invalid file mode: %s",
+ optarg);
mode = getmode(set, 0);
break;
case 'o':
owner = optarg;
break;
+ case 'p':
+ docompare = docopy = dopreserve = 1;
+ break;
case 's':
dostrip = 1;
break;
@@ -124,9 +157,9 @@ main(argc, argv)
/* get group and owner id's */
if (group && !(gp = getgrnam(group)))
- err("unknown group %s", group);
+ errx(EX_NOUSER, "unknown group %s", group);
if (owner && !(pp = getpwnam(owner)))
- err("unknown user %s", owner);
+ errx(EX_NOUSER, "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) {
@@ -141,22 +174,31 @@ main(argc, argv)
if (!no_target) {
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));
+ err(EX_OSERR, "%s", *argv);
+ if (!S_ISREG(to_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", to_name);
+ }
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);
+ errx(EX_USAGE,
+ "%s and %s are the same file", *argv, to_name);
+/*
+ * XXX - It's not at all clear why this code was here, since it completely
+ * duplicates code install(). The version in install() handles the -C flag
+ * correctly, so we'll just disable this for now.
+ */
+#if 0
/*
* 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);
+#endif
}
install(*argv, to_name, fset, iflags);
exit(0);
@@ -174,19 +216,25 @@ install(from_name, to_name, fset, flags)
{
struct stat from_sb, to_sb;
int devnull, from_fd, to_fd, serrno;
- char *p;
+ char *p, *old_to_name = 0;
+
+ if (debug >= 2 && !docompare)
+ fprintf(stderr, "install: invoked without -C for %s to %s\n",
+ from_name, to_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("%s: %s", from_name, strerror(errno));
- if (!S_ISREG(from_sb.st_mode))
- err("%s: %s", from_name, strerror(EFTYPE));
+ err(EX_OSERR, "%s", from_name);
+ if (!S_ISREG(from_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", from_name);
+ }
/* Build the target path. */
if (flags & DIRECTORY) {
(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
to_name,
- (p = rindex(from_name, '/')) ? ++p : from_name);
+ (p = strrchr(from_name, '/')) ? ++p : from_name);
to_name = pathbuf;
}
devnull = 0;
@@ -195,30 +243,118 @@ install(from_name, to_name, fset, flags)
devnull = 1;
}
- /*
- * 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, S_IRUSR | S_IWUSR)) < 0)
- err("%s: %s", to_name, strerror(errno));
+ if (docompare) {
+ old_to_name = to_name;
+ /*
+ * Make a new temporary file in the same file system
+ * (actually, in in the same directory) as the target so
+ * that the temporary file can be renamed to the target.
+ */
+ snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name);
+ p = strrchr(pathbuf2, '/');
+ p = (p == NULL ? pathbuf2 : p + 1);
+ snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX");
+ to_fd = mkstemp(pathbuf2);
+ if (to_fd < 0)
+ /* XXX should fall back to not comparing. */
+ err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name);
+ to_name = pathbuf2;
+ } else {
+ /*
+ * Unlink now... avoid errors later. Try to 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);
+ unlink(to_name);
+
+ /* Create target. */
+ to_fd = open(to_name,
+ O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (to_fd < 0)
+ err(EX_OSERR, "%s", to_name);
+ }
+
if (!devnull) {
if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
+ serrno = errno;
(void)unlink(to_name);
- err("%s: %s", from_name, strerror(errno));
+ errno = serrno;
+ err(EX_OSERR, "%s", from_name);
}
copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
(void)close(from_fd);
}
+
if (dostrip)
strip(to_name);
+
+ /*
+ * Unfortunately, because we strip the installed file and not the
+ * original one, it is impossible to do the comparison without
+ * first laboriously copying things over and then comparing.
+ * It may be possible to better optimize the !dostrip case, however.
+ * For further study.
+ */
+ if (docompare) {
+ struct stat old_sb, new_sb, timestamp_sb;
+ int old_fd;
+ struct utimbuf utb;
+
+ old_fd = open(old_to_name, O_RDONLY, 0);
+ if (old_fd < 0 && errno == ENOENT)
+ goto different;
+ if (old_fd < 0)
+ err(EX_OSERR, "%s", old_to_name);
+ fstat(old_fd, &old_sb);
+ if (old_sb.st_flags & NOCHANGEBITS)
+ (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS);
+ fstat(to_fd, &new_sb);
+ if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb,
+ &new_sb)) {
+different:
+ if (debug != 0)
+ fprintf(stderr,
+ "install: renaming for %s: %s to %s\n",
+ from_name, to_name, old_to_name);
+ if (dopreserve && stat(from_name, &timestamp_sb) == 0) {
+ utb.actime = from_sb.st_atime;
+ utb.modtime = from_sb.st_mtime;
+ (void)utime(to_name, &utb);
+ }
+moveit:
+ if (rename(to_name, old_to_name) < 0) {
+ serrno = errno;
+ unlink(to_name);
+ unlink(old_to_name);
+ errno = serrno;
+ err(EX_OSERR, "rename: %s to %s", to_name,
+ old_to_name);
+ }
+ close(old_fd);
+ } else {
+ if (old_sb.st_nlink != 1) {
+ /*
+ * Replace the target, although it hasn't
+ * changed, to snap the extra links. But
+ * preserve the target file times.
+ */
+ if (fstat(old_fd, &timestamp_sb) == 0) {
+ utb.actime = timestamp_sb.st_atime;
+ utb.modtime = timestamp_sb.st_mtime;
+ (void)utime(to_name, &utb);
+ }
+ goto moveit;
+ }
+ if (unlink(to_name) < 0)
+ err(EX_OSERR, "unlink: %s", to_name);
+ close(to_fd);
+ to_fd = old_fd;
+ }
+ to_name = old_to_name;
+ }
+
/*
* Set owner, group, mode for target; do the chown first,
* chown may lose the setuid bits.
@@ -227,12 +363,14 @@ install(from_name, to_name, fset, flags)
fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
serrno = errno;
(void)unlink(to_name);
- err("%s: chown/chgrp: %s", to_name, strerror(serrno));
+ errno = serrno;
+ err(EX_OSERR,"%s: chown/chgrp", to_name);
}
if (fchmod(to_fd, mode)) {
serrno = errno;
(void)unlink(to_name);
- err("%s: chmod: %s", to_name, strerror(serrno));
+ errno = serrno;
+ err(EX_OSERR, "%s: chmod", to_name);
}
/*
@@ -243,12 +381,47 @@ install(from_name, to_name, fset, flags)
flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
serrno = errno;
(void)unlink(to_name);
- err("%s: chflags: %s", to_name, strerror(serrno));
+ errno = serrno;
+ err(EX_OSERR, "%s: chflags", to_name);
}
(void)close(to_fd);
if (!docopy && !devnull && unlink(from_name))
- err("%s: %s", from_name, strerror(errno));
+ err(EX_OSERR, "%s", from_name);
+}
+
+/*
+ * compare --
+ * compare two files; non-zero means files differ
+ */
+int
+compare(int from_fd, const char *from_name, int to_fd, const char *to_name,
+ const struct stat *from_sb, const struct stat *to_sb)
+{
+ char *p, *q;
+ int rv;
+ size_t tsize;
+
+ if (from_sb->st_size != to_sb->st_size)
+ return 1;
+
+ tsize = (size_t)from_sb->st_size;
+
+ if (tsize <= 8 * 1024 * 1024) {
+ p = mmap(NULL, tsize, PROT_READ, 0, from_fd, (off_t)0);
+ if ((long)p == -1)
+ err(EX_OSERR, "mmap %s", from_name);
+ q = mmap(NULL, tsize, PROT_READ, 0, to_fd, (off_t)0);
+ if ((long)q == -1)
+ err(EX_OSERR, "mmap %s", to_name);
+
+ rv = memcmp(p, q, tsize);
+ munmap(p, tsize);
+ munmap(q, tsize);
+ } else {
+ rv = 1; /* don't bother in this case */
+ }
+ return rv;
}
/*
@@ -273,21 +446,22 @@ copy(from_fd, from_name, to_fd, to_name, size)
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));
+ err(EX_OSERR, "mmap %s", from_name);
if (write(to_fd, p, size) != size)
- err("%s: %s", to_name, strerror(errno));
+ err(EX_OSERR, "%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);
- err("%s: %s",
- to_name, strerror(nw > 0 ? EIO : serrno));
+ errno = nw > 0 ? EIO : serrno;
+ err(EX_OSERR, "%s", to_name);
}
if (nr != 0) {
serrno = errno;
(void)unlink(to_name);
- err("%s: %s", from_name, strerror(serrno));
+ errno = serrno;
+ err(EX_OSERR, "%s", from_name);
}
}
}
@@ -306,10 +480,11 @@ strip(to_name)
case -1:
serrno = errno;
(void)unlink(to_name);
- err("forks: %s", strerror(errno));
+ errno = serrno;
+ err(EX_TEMPFAIL, "fork");
case 0:
execl(_PATH_STRIP, "strip", to_name, NULL);
- err("%s: %s", _PATH_STRIP, strerror(errno));
+ err(EX_OSERR, "exec(" _PATH_STRIP ")");
default:
if (wait(&status) == -1 || status)
(void)unlink(to_name);
@@ -324,35 +499,6 @@ void
usage()
{
(void)fprintf(stderr,
-"usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n");
- exit(1);
-}
-
-#if __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
-
-void
-#if __STDC__
-err(const char *fmt, ...)
-#else
-err(fmt, va_alist)
- char *fmt;
- va_dcl
-#endif
-{
- va_list ap;
-#if __STDC__
- va_start(ap, fmt);
-#else
- va_start(ap);
-#endif
- (void)fprintf(stderr, "install: ");
- (void)vfprintf(stderr, fmt, ap);
- va_end(ap);
- (void)fprintf(stderr, "\n");
+"usage: install [-Ccdps] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n");
exit(1);
- /* NOTREACHED */
}
OpenPOWER on IntegriCloud