svn commit: r244944 - projects/mtree/usr.bin/xinstall

Brooks Davis brooks at FreeBSD.org
Wed Jan 2 01:07:10 UTC 2013


Author: brooks
Date: Wed Jan  2 01:07:08 2013
New Revision: 244944
URL: http://svnweb.freebsd.org/changeset/base/244944

Log:
  Add a -l option that allows files to be linked (either hard or soft)
  rather than copied.
  
  Obtained from:	NetBSD

Modified:
  projects/mtree/usr.bin/xinstall/install.1
  projects/mtree/usr.bin/xinstall/xinstall.c

Modified: projects/mtree/usr.bin/xinstall/install.1
==============================================================================
--- projects/mtree/usr.bin/xinstall/install.1	Wed Jan  2 00:38:01 2013	(r244943)
+++ projects/mtree/usr.bin/xinstall/install.1	Wed Jan  2 01:07:08 2013	(r244944)
@@ -40,6 +40,7 @@
 .Op Fl B Ar suffix
 .Op Fl f Ar flags
 .Op Fl g Ar group
+.Op Fl l Ar linkflags
 .Op Fl m Ar mode
 .Op Fl N Ar dbdir
 .Op Fl o Ar owner
@@ -49,6 +50,7 @@
 .Op Fl B Ar suffix
 .Op Fl f Ar flags
 .Op Fl g Ar group
+.Op Fl l Ar linkflags
 .Op Fl m Ar mode
 .Op Fl N Ar dbdir
 .Op Fl o Ar owner
@@ -63,7 +65,9 @@
 .Ar directory ...
 .Sh DESCRIPTION
 The file(s) are copied
-to the target file or directory.
+(or linked if the
+.Fl l
+option is specified) to the target file or directory.
 If the destination is a directory, then the
 .Ar file
 is copied into
@@ -118,6 +122,27 @@ for a list of possible flags and their m
 .It Fl g
 Specify a group.
 A numeric GID is allowed.
+.It Fl l Ar linkflags
+Instead of copying the file make a link to the source.
+The type of the link is determined by the
+.Ar linkflags
+argument.
+Valid
+.Ar linkflags
+are:
+.Ar a
+(absolute),
+.Ar r
+(relative),
+.Ar h
+(hard),
+.Ar s
+(symbolic),
+.Ar m
+(mixed).
+Absolute and relative have effect only for symbolic links.
+Mixed links
+are hard links for files on the same filesystem, symbolic otherwise.
 .It Fl M
 Disable all use of
 .Xr mmap 2 .

Modified: projects/mtree/usr.bin/xinstall/xinstall.c
==============================================================================
--- projects/mtree/usr.bin/xinstall/xinstall.c	Wed Jan  2 00:38:01 2013	(r244943)
+++ projects/mtree/usr.bin/xinstall/xinstall.c	Wed Jan  2 01:07:08 2013	(r244944)
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
+#include <libgen.h>
 #include <paths.h>
 #include <pwd.h>
 #include <stdint.h>
@@ -71,6 +72,12 @@ __FBSDID("$FreeBSD$");
 
 #define MAX_CMP_SIZE	(16 * 1024 * 1024)
 
+#define	LN_ABSOLUTE	0x01
+#define	LN_RELATIVE	0x02
+#define	LN_HARD		0x04
+#define	LN_SYMBOLIC	0x08
+#define	LN_MIXED	0x10
+
 #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)
@@ -78,7 +85,7 @@ __FBSDID("$FreeBSD$");
 
 static gid_t gid;
 static uid_t uid;
-static int dobackup, docompare, dodir, dopreserve, dostrip, dounpriv,
+static int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv,
     nommap, safecopy, verbose;
 static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
 static const char *suffix = BACKUP_SUFFIX;
@@ -87,6 +94,9 @@ static int	compare(int, const char *, si
 static void	copy(int, const char *, int, const char *, off_t);
 static int	create_newfile(const char *, int, struct stat *);
 static int	create_tempfile(const char *, char *, size_t);
+static int	do_link(const char *, const char *, const struct stat *);
+static void	do_symlink(const char *, const char *, const struct stat *);
+static void	makelink(const char *, const char *, const struct stat *);
 static void	install(const char *, const char *, u_long, u_int);
 static void	install_dir(char *);
 static int	parseid(const char *, id_t *);
@@ -102,13 +112,13 @@ main(int argc, char *argv[])
 	u_long fset;
 	int ch, no_target;
 	u_int iflags;
-	char *flags;
+	char *flags, *p;
 	const char *group, *owner, *to_name;
 
 	flags = NULL;
 	iflags = 0;
 	group = owner = NULL;
-	while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:N:o:pSsUv")) != -1)
+	while ((ch = getopt(argc, argv, "B:bCcdf:g:l:Mm:N:o:pSsUv")) != -1)
 		switch((char)ch) {
 		case 'B':
 			suffix = optarg;
@@ -131,6 +141,34 @@ main(int argc, char *argv[])
 		case 'g':
 			group = optarg;
 			break;
+		case 'l':
+			for (p = optarg; *p; p++)
+				switch (*p) {
+				case 's':
+					dolink &= ~(LN_HARD|LN_MIXED);
+					dolink |= LN_SYMBOLIC;
+					break;
+				case 'h':
+					dolink &= ~(LN_SYMBOLIC|LN_MIXED);
+					dolink |= LN_HARD;
+					break;
+				case 'm':
+					dolink &= ~(LN_SYMBOLIC|LN_HARD);
+					dolink |= LN_MIXED;
+					break;
+				case 'a':
+					dolink &= ~LN_RELATIVE;
+					dolink |= LN_ABSOLUTE;
+					break;
+				case 'r':
+					dolink &= ~LN_ABSOLUTE;
+					dolink |= LN_RELATIVE;
+					break;
+				default:
+					errx(1, "%c: invalid link type", *p);
+					/* NOTREACHED */
+				}
+			break;
 		case 'M':
 			nommap = 1;
 			break;
@@ -276,6 +314,171 @@ parseid(const char *name, id_t *id)
 }
 
 /*
+ * quiet_mktemp --
+ *	mktemp implementation used mkstemp to avoid mktemp warnings.  We
+ *	really do need mktemp semantics here as we will be creating a link.
+ */
+static char *
+quiet_mktemp(char *template)
+{
+	int fd;
+
+	if ((fd = mkstemp(template)) == -1)
+		return (NULL);
+	close (fd);
+	if (unlink(template) == -1)
+		err(1, "unlink %s", template);
+	return (template);
+}
+
+/*
+ * do_link --
+ *	make a hard link, obeying dorename if set
+ *	return -1 on failure
+ */
+static int
+do_link(const char *from_name, const char *to_name,
+    const struct stat *target_sb)
+{
+	char tmpl[MAXPATHLEN];
+	int ret;
+
+	if (safecopy && target_sb != NULL) {
+		(void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+		/* This usage is safe. */
+		if (quiet_mktemp(tmpl) == NULL)
+			err(1, "%s: mktemp", tmpl);
+		ret = link(from_name, tmpl);
+		if (ret == 0) {
+			if (target_sb->st_flags & NOCHANGEBITS)
+				(void)chflags(to_name, target_sb->st_flags &
+				     ~NOCHANGEBITS);
+			unlink(to_name);
+			ret = rename(tmpl, to_name);
+			/* If rename has posix semantics, then the temporary
+			 * file may still exist when from_name and to_name point
+			 * to the same file, so unlink it unconditionally.
+			 */
+			(void)unlink(tmpl);
+		}
+		return (ret);
+	} else
+		return (link(from_name, to_name));
+}
+
+/*
+ * do_symlink --
+ *	make a symbolic link, obeying dorename if set
+ *	exit on failure
+ */
+static void
+do_symlink(const char *from_name, const char *to_name,
+    const struct stat *target_sb)
+{
+	char tmpl[MAXPATHLEN];
+
+	if (safecopy && target_sb != NULL) {
+		(void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+		/* This usage is safe. */
+		if (quiet_mktemp(tmpl) == NULL)
+			err(1, "%s: mktemp", tmpl);
+
+		if (symlink(from_name, tmpl) == -1)
+			err(1, "symlink %s -> %s", from_name, tmpl);
+
+		if (target_sb->st_flags & NOCHANGEBITS)
+			(void)chflags(to_name, target_sb->st_flags &
+			     ~NOCHANGEBITS);
+		unlink(to_name);
+
+		if (rename(tmpl, to_name) == -1) {
+			/* remove temporary link before exiting */
+			(void)unlink(tmpl);
+			err(1, "%s: rename", to_name);
+		}
+	} else {
+		if (symlink(from_name, to_name) == -1)
+			err(1, "symlink %s -> %s", from_name, to_name);
+	}
+}
+
+/*
+ * makelink --
+ *	make a link from source to destination
+ */
+static void
+makelink(const char *from_name, const char *to_name,
+    const struct stat *target_sb)
+{
+	char	src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
+
+	/* Try hard links first */
+	if (dolink & (LN_HARD|LN_MIXED)) {
+		if (do_link(from_name, to_name, target_sb) == -1) {
+			if ((dolink & LN_HARD) || errno != EXDEV)
+				err(1, "link %s -> %s", from_name, to_name);
+		} else {
+			return;
+		}
+	}
+
+	/* Symbolic links */
+	if (dolink & LN_ABSOLUTE) {
+		/* Convert source path to absolute */
+		if (realpath(from_name, src) == NULL)
+			err(1, "%s: realpath", from_name);
+		do_symlink(src, to_name, target_sb);
+		return;
+	}
+
+	if (dolink & LN_RELATIVE) {
+		char *cp, *d, *s;
+
+		/* Resolve pathnames */
+		if (realpath(from_name, src) == NULL)
+			err(1, "%s: realpath", from_name);
+
+		/*
+		 * The last component of to_name may be a symlink,
+		 * so use realpath to resolve only the directory.
+		 */
+		cp = dirname(to_name);
+		if (realpath(cp, dst) == NULL)
+			err(1, "%s: realpath", cp);
+		/* .. and add the last component */
+		if (strcmp(dst, "/") != 0) {
+			if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
+				errx(1, "resolved pathname too long");
+		}
+		cp = basename(to_name);
+		if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
+			errx(1, "resolved pathname too long");
+
+		/* trim common path components */
+		for (s = src, d = dst; *s == *d; s++, d++)
+			continue;
+		while (*s != '/')
+			s--, d--;
+
+		/* count the number of directories we need to backtrack */
+		for (++d, lnk[0] = '\0'; *d; d++)
+			if (*d == '/')
+				(void)strlcat(lnk, "../", sizeof(lnk));
+
+		(void)strlcat(lnk, ++s, sizeof(lnk));
+
+		do_symlink(lnk, to_name, target_sb);
+		return;
+	}
+
+	/*
+	 * If absolute or relative was not specified, 
+	 * try the names the user provided
+	 */
+	do_symlink(from_name, to_name, target_sb);
+}
+
+/*
  * install --
  *	build a path name and install the file
  */
@@ -314,6 +517,24 @@ install(const char *from_name, const cha
 
 	target = stat(to_name, &to_sb) == 0;
 
+	if (dolink) {
+		if (target) {
+			if (to_sb.st_mode & S_IFDIR) {
+				errno = EFTYPE;
+				warn("%s", to_name);
+				return;
+			}
+			if (!safecopy) {
+				if (to_sb.st_flags & NOCHANGEBITS)
+					(void)chflags(to_name,
+					    to_sb.st_flags & ~NOCHANGEBITS);
+				unlink(to_name);
+			}
+		}
+		makelink(from_name, to_name, target ? &to_sb : NULL);
+		return;
+	}
+
 	/* Only install to regular files. */
 	if (target && !S_ISREG(to_sb.st_mode)) {
 		errno = EFTYPE;
@@ -805,10 +1026,10 @@ static void
 usage(void)
 {
 	(void)fprintf(stderr,
-"usage: install [-bCcMpSsUv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
-"               [-N dbdir] [-o owner] file1 file2\n"
-"       install [-bCcMpSsUv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
-"               [-N dbdir] [-o owner] file1 ... fileN directory\n"
+"usage: install [-bCcMpSsUv] [-B suffix] [-f flags] [-g group] [-l linkflags]\n"
+"               [-m mode] [-N dbdir] [-o owner] file1 file2\n"
+"       install [-bCcMpSsUv] [-B suffix] [-f flags] [-g group] [-l linkflags]\n"
+"               [-m mode] [-N dbdir] [-o owner] file1 ... fileN directory\n"
 "       install -dU [-vU] [-g group] [-m mode] [-N dbdir] [-o owner]\n"
 "               directory ...\n");
 	exit(EX_USAGE);


More information about the svn-src-projects mailing list