Index: share/man/man5/style.Makefile.5 =================================================================== --- share/man/man5/style.Makefile.5 +++ share/man/man5/style.Makefile.5 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 21, 2015 +.Dd March 29, 2018 .Dt STYLE.MAKEFILE 5 .Os .Sh NAME @@ -73,6 +73,7 @@ .Va PROG Ns / Ns Oo Va SH Oc Ns Va LIB Ns / Ns Va SCRIPTS .Va FILES .Va LINKS +.Va LINKS_OR_COPIES .Oo Va NO_ Oc Ns Va MAN .Va MLINKS .Va INCS @@ -93,6 +94,7 @@ .Va INCS .Va FILES .Va LINKS +.Va LINKS_OR_COPIES .Oo Va NO_ Oc Ns Va MAN .Va MLINKS . .It Index: share/mk/bsd.README =================================================================== --- share/mk/bsd.README +++ share/mk/bsd.README @@ -318,6 +318,9 @@ LINKS= /bin/test /bin/[ +LINKS_OR_COPIES Like LINKS, but fallback to copying if the target path does not + reside on the same filesystem as the source path. + MAN Manual pages. If no MAN variable is defined, "MAN=${PROG}.1" is assumed. See bsd.man.mk for more details. Index: share/mk/bsd.links.mk =================================================================== --- share/mk/bsd.links.mk +++ share/mk/bsd.links.mk @@ -17,6 +17,9 @@ .for s t in ${LINKS} ${INSTALL_LINK} ${TAG_ARGS} ${DESTDIR}${s} ${DESTDIR}${t} .endfor +.for s t in ${LINKS_OR_COPIES} + ${INSTALL_LINK_OR_COPY} ${TAG_ARGS} ${DESTDIR}${s} ${DESTDIR}${t} +.endfor .for s t in ${SYMLINKS} ${INSTALL_SYMLINK} ${TAG_ARGS} ${s} ${DESTDIR}${t} .endfor Index: share/mk/bsd.own.mk =================================================================== --- share/mk/bsd.own.mk +++ share/mk/bsd.own.mk @@ -207,10 +207,12 @@ # install(1) parameters. # HRDLINK?= -l h +CHRDLINK?= -l c SYMLINK?= -l s RSYMLINK?= -l rs INSTALL_LINK?= ${INSTALL} ${HRDLINK} +INSTALL_LINK_OR_COPY?= ${INSTALL} ${CHRDLINK} INSTALL_SYMLINK?= ${INSTALL} ${SYMLINK} INSTALL_RSYMLINK?= ${INSTALL} ${RSYMLINK} Index: share/mk/bsd.progs.mk =================================================================== --- share/mk/bsd.progs.mk +++ share/mk/bsd.progs.mk @@ -25,7 +25,7 @@ PROG_OVERRIDE_VARS += BINDIR BINGRP BINOWN BINMODE DPSRCS MAN NO_WERROR \ PROGNAME SRCS STRIP WARNS PROG_VARS += CFLAGS CXXFLAGS DEBUG_FLAGS DPADD INTERNALPROG LDADD LIBADD \ - LINKS LDFLAGS MLINKS ${PROG_OVERRIDE_VARS} + LINKS LINKS_OR_COPIES LDFLAGS MLINKS ${PROG_OVERRIDE_VARS} .for v in ${PROG_VARS:O:u} .if empty(${PROG_OVERRIDE_VARS:M$v}) .if defined(${v}.${PROG}) Index: usr.bin/xinstall/install.1 =================================================================== --- usr.bin/xinstall/install.1 +++ usr.bin/xinstall/install.1 @@ -28,7 +28,7 @@ .\" From: @(#)install.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd January 18, 2013 +.Dd March 29, 2018 .Dt INSTALL 1 .Os .Sh NAME @@ -187,6 +187,8 @@ (relative), .Ar h (hard), +.Ar c +(copy), .Ar s (symbolic), .Ar m @@ -194,6 +196,8 @@ Absolute and relative have effect only for symbolic links. Mixed links are hard links for files on the same filesystem, symbolic otherwise. +Copy links are hard links for files on the same filesystem, copies +otherwise. .It Fl M Ar metalog Write the metadata associated with each item installed to .Ar metalog Index: usr.bin/xinstall/xinstall.c =================================================================== --- usr.bin/xinstall/xinstall.c +++ usr.bin/xinstall/xinstall.c @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,7 @@ #define LN_HARD 0x04 #define LN_SYMBOLIC 0x08 #define LN_MIXED 0x10 +#define LN_COPY 0x20 #define DIRECTORY 0x01 /* Tell install it's a directory. */ #define SETFLAGS 0x02 /* Tell install to set flags. */ @@ -130,7 +132,7 @@ static char *digest_end(DIGEST_CTX *, char *); 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 bool 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 void metadata_log(const char *, const char *, struct timespec *, @@ -190,15 +192,19 @@ for (p = optarg; *p != '\0'; p++) switch (*p) { case 's': - dolink &= ~(LN_HARD|LN_MIXED); + dolink &= ~(LN_HARD|LN_MIXED|LN_COPY); dolink |= LN_SYMBOLIC; break; + case 'c': + dolink &= ~(LN_SYMBOLIC|LN_HARD|LN_MIXED); + dolink |= LN_COPY; + break; case 'h': - dolink &= ~(LN_SYMBOLIC|LN_MIXED); + dolink &= ~(LN_SYMBOLIC|LN_MIXED|LN_COPY); dolink |= LN_HARD; break; case 'm': - dolink &= ~(LN_SYMBOLIC|LN_HARD); + dolink &= ~(LN_SYMBOLIC|LN_HARD|LN_COPY); dolink |= LN_MIXED; break; case 'a': @@ -606,8 +612,11 @@ /* * makelink -- * make a link from source to destination + * + * return bool for 'finished'. If false, caller should attempt to install + * normally (nonlink). */ -static void +static bool makelink(const char *from_name, const char *to_name, const struct stat *target_sb) { @@ -615,10 +624,12 @@ struct stat to_sb; /* Try hard links first. */ - if (dolink & (LN_HARD|LN_MIXED)) { + if (dolink & (LN_HARD|LN_MIXED|LN_COPY)) { if (do_link(from_name, to_name, target_sb) == -1) { if ((dolink & LN_HARD) || errno != EXDEV) err(EX_OSERR, "link %s -> %s", from_name, to_name); + if (dolink & LN_COPY) + return (false); } else { if (stat(to_name, &to_sb)) err(EX_OSERR, "%s: stat", to_name); @@ -657,7 +668,7 @@ group = ogroup; fflags = offlags; } - return; + return (true); } } @@ -669,7 +680,7 @@ do_symlink(src, to_name, target_sb); /* XXX: src may point outside of destdir */ metadata_log(to_name, "link", NULL, src, NULL, 0); - return; + return (true); } if (dolink & LN_RELATIVE) { @@ -680,7 +691,7 @@ do_symlink(from_name, to_name, target_sb); /* XXX: from_name may point outside of destdir. */ metadata_log(to_name, "link", NULL, from_name, NULL, 0); - return; + return (true); } /* Resolve pathnames. */ @@ -724,7 +735,7 @@ do_symlink(lnk, to_name, target_sb); /* XXX: Link may point outside of destdir. */ metadata_log(to_name, "link", NULL, lnk, NULL, 0); - return; + return (true); } /* @@ -734,6 +745,7 @@ do_symlink(from_name, to_name, target_sb); /* XXX: from_name may point outside of destdir. */ metadata_log(to_name, "link", NULL, from_name, NULL, 0); + return (true); } /* @@ -790,8 +802,8 @@ #endif unlink(to_name); } - makelink(from_name, to_name, target ? &to_sb : NULL); - return; + if (makelink(from_name, to_name, target ? &to_sb : NULL)) + return; } if (target && !S_ISREG(to_sb.st_mode) && !S_ISLNK(to_sb.st_mode)) { Index: usr.sbin/chown/Makefile =================================================================== --- usr.sbin/chown/Makefile +++ usr.sbin/chown/Makefile @@ -4,7 +4,7 @@ .include PROG= chown -LINKS= ${BINDIR}/chown /usr/bin/chgrp +LINKS_OR_COPIES= ${BINDIR}/chown /usr/bin/chgrp MAN= chgrp.1 chown.8 HAS_TESTS=