Index: usr.bin/xinstall/install.1 =================================================================== --- usr.bin/xinstall/install.1 +++ usr.bin/xinstall/install.1 @@ -187,11 +187,25 @@ symbolic .It Sy m mixed +.It Sy c +metalog contents reference .El .Pp Absolute and relative have effect only for symbolic links. Mixed links are hard links for files on the same filesystem, symbolic otherwise. +.Pp +The +.Ar c +link type does not create a filesystem object. +Instead, the source file's absolute path is recorded using the +.Xr mtree 5 +.Cm contents +tag in the +.Ar metalog +file. +It must be used with +.Fl M Ar metalog . .It Fl M Ar metalog Write the metadata associated with each item installed to .Ar metalog Index: usr.bin/xinstall/tests/install_test.sh =================================================================== --- usr.bin/xinstall/tests/install_test.sh +++ usr.bin/xinstall/tests/install_test.sh @@ -32,6 +32,8 @@ install -s -d dir1 atf_check -s not-exit:0 -e match:"specified together" \ install -s -l s testf copyf + atf_check -s not-exit:0 -e match:"requires -M metalog" \ + install -l c testf copyf } atf_test_case copy_to_empty Index: usr.bin/xinstall/xinstall.c =================================================================== --- usr.bin/xinstall/xinstall.c +++ usr.bin/xinstall/xinstall.c @@ -95,6 +95,7 @@ #define LN_HARD 0x04 #define LN_SYMBOLIC 0x08 #define LN_MIXED 0x10 +#define LN_CONTENTS 0x20 #define DIRECTORY 0x01 /* Tell install it's a directory. */ #define SETFLAGS 0x02 /* Tell install to set flags. */ @@ -155,7 +156,8 @@ 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 *, - const char *, const char *, off_t); + const char *, const char *, off_t, const char *); +static void parse_l_args(const char *p); static int parseid(const char *, id_t *); static int strip(const char *, int, const char *, char **); static void usage(void); @@ -168,7 +170,6 @@ u_long fset; int ch, no_target; u_int iflags; - char *p; const char *to_name; uint64_t num; @@ -209,32 +210,7 @@ digest = optarg; break; case 'l': - for (p = optarg; *p != '\0'; 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 */ - } + parse_l_args(optarg); break; case 'M': metafile = optarg; @@ -299,6 +275,11 @@ usage(); } + if ((dolink & LN_CONTENTS) && metafile == NULL) { + warnx("-l c requires -M metalog"); + usage(); + } + /* * Default permissions based on whether we're a directory or not, since * an +X may mean that we need to set the execute bit. @@ -427,6 +408,42 @@ /* NOTREACHED */ } +static void +parse_l_args(const char *p) +{ + for (p = optarg; *p != '\0'; p++) { + switch (*p) { + case 'c': + dolink &= ~(LN_SYMBOLIC | LN_HARD | LN_MIXED); + dolink |= LN_CONTENTS; + break; + case 's': + dolink &= ~(LN_CONTENTS | LN_HARD | LN_MIXED); + dolink |= LN_SYMBOLIC; + break; + case 'h': + dolink &= ~(LN_CONTENTS | LN_SYMBOLIC | LN_MIXED); + dolink |= LN_HARD; + break; + case 'm': + dolink &= ~(LN_CONTENTS | 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 */ + } + } +} + static char * digest_file(const char *name) { @@ -673,7 +690,25 @@ const char *base, *dir; struct stat to_sb; - /* Try hard links first. */ + /* First check for mtree contents reference. */ + if (dolink & LN_CONTENTS) { + struct stat from_sb; + char *dres; + + if (dolink & LN_RELATIVE) + errx(EX_OSERR, "relative contents not yet supported"); + if (stat(from_name, &from_sb)) + err(EX_OSERR, "%s: stat", from_name); + if (realpath(from_name, src) == NULL) + err(EX_OSERR, "%s: realpath", from_name); + dres = digest_file(from_name); + metadata_log(to_name, "file", NULL, NULL, dres, + from_sb.st_size, src); + free(dres); + return; + } + + /* Try hard links next. */ if (dolink & (LN_HARD|LN_MIXED)) { if (do_link(from_name, to_name, target_sb) == -1) { if ((dolink & LN_HARD) || errno != EXDEV) @@ -709,7 +744,7 @@ fflags = NULL; dres = digest_file(from_name); metadata_log(to_name, "file", NULL, NULL, - dres, to_sb.st_size); + dres, to_sb.st_size, NULL); free(dres); mode = omode; owner = oowner; @@ -727,7 +762,7 @@ err(EX_OSERR, "%s: realpath", from_name); do_symlink(src, to_name, target_sb); /* XXX: src may point outside of destdir */ - metadata_log(to_name, "link", NULL, src, NULL, 0); + metadata_log(to_name, "link", NULL, src, NULL, 0, NULL); return; } @@ -736,7 +771,8 @@ /* this is already a relative link */ 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); + metadata_log(to_name, "link", NULL, from_name, NULL, 0, + NULL); return; } @@ -797,7 +833,7 @@ do_symlink(lnk, to_name, target_sb); /* XXX: Link may point outside of destdir. */ - metadata_log(to_name, "link", NULL, lnk, NULL, 0); + metadata_log(to_name, "link", NULL, lnk, NULL, 0, NULL); return; } @@ -807,7 +843,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); + metadata_log(to_name, "link", NULL, from_name, NULL, 0, NULL); } /* @@ -1084,7 +1120,8 @@ if (!devnull) (void)close(from_fd); - metadata_log(to_name, "file", tsb, NULL, digestresult, to_sb.st_size); + metadata_log(to_name, "file", tsb, NULL, digestresult, to_sb.st_size, + NULL); free(digestresult); } @@ -1392,7 +1429,7 @@ if (chmod(path, mode)) warn("chmod %o %s", mode, path); } - metadata_log(path, "dir", NULL, NULL, NULL, 0); + metadata_log(path, "dir", NULL, NULL, NULL, 0, NULL); } /* @@ -1403,7 +1440,8 @@ */ static void metadata_log(const char *path, const char *type, struct timespec *ts, - const char *slink, const char *digestresult, off_t size) + const char *slink, const char *digestresult, off_t size, + const char *contents) { static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; const char *p; @@ -1413,10 +1451,12 @@ if (!metafp) return; - /* Buffer for strsnvis(3), used for both path and slink. */ + /* Buffer for strsnvis(3), used for path, slink, and contents. */ buflen = strlen(path); if (slink && strlen(slink) > buflen) buflen = strlen(slink); + if (contents && strlen(contents) > buflen) + buflen = strlen(contents); buflen = 4 * buflen + 1; if ((buf = malloc(buflen)) == NULL) { warn(NULL); @@ -1469,6 +1509,10 @@ strsnvis(buf, buflen, slink, VIS_CSTYLE, extra); fprintf(metafp, " link=%s", buf); } + if (contents) { + strsnvis(buf, buflen, contents, VIS_OCTAL, extra); + fprintf(metafp, " contents=%s", buf); + } if (*type == 'f') /* type=file */ fprintf(metafp, " size=%llu", (unsigned long long)size); if (ts != NULL && dopreserve)