summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
authorbde <bde@FreeBSD.org>1995-10-09 07:21:00 +0000
committerbde <bde@FreeBSD.org>1995-10-09 07:21:00 +0000
commit3d363c44cc566458210a38f09a6fa8fb6e2cd5ae (patch)
treead43e8fb2214e95e8a80546f032ecc191c13d771 /usr.bin
parent37ad3411f67cdb127ebfc11fabb9afcf7a8542dc (diff)
downloadFreeBSD-src-3d363c44cc566458210a38f09a6fa8fb6e2cd5ae.zip
FreeBSD-src-3d363c44cc566458210a38f09a6fa8fb6e2cd5ae.tar.gz
Add options -C (same as -c except for preserving the modification
time of the target if the target file is the same as the source), -d (debug), and -p (same as -C except for preserving the modification time of the source if the target doesn't exists or is different from the source. Use library err() functions instead of our own and pass them better exit codes. Submitted by: wollman (and changed a lot by me)
Diffstat (limited to 'usr.bin')
-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