summaryrefslogtreecommitdiff
path: root/tar
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@gmail.com>2011-11-05 01:29:29 -0400
committerTim Kientzle <kientzle@gmail.com>2011-11-05 01:29:29 -0400
commitbe20a49001ac1421a3b3fe7cde70ea86f65dde7c (patch)
tree65f7585638bc76c1f6f17b8d88a66cf21dd1e1e2 /tar
parent44a85834ef84b95eec7fc830edd7f4d9ed4a6f54 (diff)
downloadlibarchive-be20a49001ac1421a3b3fe7cde70ea86f65dde7c.tar.gz
Issue 165: Use GNU tar-style hHrRsS trailing flags to
control hardlink, regular filename, and symlink substitutions. SVN-Revision: 3748
Diffstat (limited to 'tar')
-rw-r--r--tar/bsdtar.138
-rw-r--r--tar/subst.c35
-rw-r--r--tar/test/test_option_s.c107
-rw-r--r--tar/test/test_option_s.tar.Z.uu16
4 files changed, 159 insertions, 37 deletions
diff --git a/tar/bsdtar.1 b/tar/bsdtar.1
index 13fd39cc..a9683604 100644
--- a/tar/bsdtar.1
+++ b/tar/bsdtar.1
@@ -496,24 +496,11 @@ Extract files as sparse files.
For every block on disk, check first if it contains only NULL bytes and seek
over it otherwise.
This works similar to the conv=sparse option of dd.
-.It Fl Fl same-owner
-(x mode only)
-Extract owner and group IDs.
-This is the reverse of
-.Fl Fl no-same-owner
-and the default behavior if
-.Nm
-is run as root.
-.It Fl Fl strip-components Ar count
-Remove the specified number of leading path elements.
-Pathnames with fewer elements will be silently skipped.
-Note that the pathname is edited after checking inclusion/exclusion patterns
-but before security checks.
.It Fl s Ar pattern
Modify file or archive member names according to
.Pa pattern .
The pattern has the format
-.Ar /old/new/ Ns Op gps
+.Ar /old/new/ Ns Op ghHprRsS
where
.Ar old
is a basic regular expression,
@@ -535,6 +522,29 @@ of symbolic links.
The optional trailing p specifies that after a successful substitution
the original path name and the new path name should be printed to
standard error.
+Optional trailing H, R, or S characters suppress substitutions
+for hardlink targets, regular filenames, or symlink targets,
+respectively.
+Optional trailing h, r, or s characters enable substitutions
+for hardlink targets, regular filenames, or symlink targets,
+respectively.
+The default is
+.Ar hrs
+which applies substitutions to all names.
+In particular, it is never necessary to specify h, r, or s.
+.It Fl Fl same-owner
+(x mode only)
+Extract owner and group IDs.
+This is the reverse of
+.Fl Fl no-same-owner
+and the default behavior if
+.Nm
+is run as root.
+.It Fl Fl strip-components Ar count
+Remove the specified number of leading path elements.
+Pathnames with fewer elements will be silently skipped.
+Note that the pathname is edited after checking inclusion/exclusion patterns
+but before security checks.
.It Fl T Ar filename , Fl Fl files-from Ar filename
In x or t mode,
.Nm
diff --git a/tar/subst.c b/tar/subst.c
index 018d77af..0ef95b91 100644
--- a/tar/subst.c
+++ b/tar/subst.c
@@ -44,7 +44,7 @@ struct subst_rule {
struct subst_rule *next;
regex_t re;
char *result;
- unsigned int global:1, print:1, symlink:1, hardlink:1;
+ unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1;
};
struct substitution {
@@ -120,6 +120,7 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text)
/* Defaults */
rule->global = 0; /* Don't do multiple replacements. */
rule->print = 0; /* Don't print. */
+ rule->regular = 1; /* Rewrite regular filenames. */
rule->symlink = 1; /* Rewrite symlink targets. */
rule->hardlink = 1; /* Rewrite hardlink targets. */
@@ -129,14 +130,27 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text)
case 'G':
rule->global = 1;
break;
+ case 'h':
+ rule->hardlink = 1;
+ break;
+ case 'H':
+ rule->hardlink = 0;
+ break;
case 'p':
case 'P':
rule->print = 1;
break;
+ case 'r':
+ rule->regular = 1;
+ break;
+ case 'R':
+ rule->regular = 0;
+ break;
case 's':
+ rule->symlink = 1;
+ break;
case 'S':
rule->symlink = 0;
- rule->hardlink = 0;
break;
default:
lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
@@ -189,7 +203,7 @@ realloc_strcat(char **str, const char *append)
int
apply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
- int hardlink_target, int symlink_target)
+ int symlink_target, int hardlink_target)
{
const char *path = name;
regmatch_t matches[10];
@@ -207,10 +221,17 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
print_match = 0;
for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
- if (symlink_target && !rule->symlink)
- continue;
- if (hardlink_target && !rule->hardlink)
- continue;
+ if (symlink_target) {
+ if (!rule->symlink)
+ continue;
+ } else if (hardlink_target) {
+ if (!rule->hardlink)
+ continue;
+ } else { /* Regular filename. */
+ if (!rule->regular)
+ continue;
+ }
+
if (regexec(&rule->re, name, 10, matches, 0))
continue;
diff --git a/tar/test/test_option_s.c b/tar/test/test_option_s.c
index df2550c2..f643f187 100644
--- a/tar/test/test_option_s.c
+++ b/tar/test/test_option_s.c
@@ -152,27 +152,102 @@ DEFINE_TEST(test_option_s)
/*
* Test 9: selective renaming of hardlink target
*/
+ /* At extraction. (assuming hardlink2 is the hardlink entry) */
+ assertMakeDir("test9a", 0755);
+ systemf("%s -cf - in/d1 | %s -xf - -s /hardlink1/hardlink1-renamed/ -C test9a",
+ testprog, testprog);
+ assertIsHardlink("test9a/in/d1/hardlink1-renamed", "test9a/in/d1/hardlink2");
+ /* At extraction. (assuming hardlink1 is the hardlink entry) */
+ assertMakeDir("test9b", 0755);
+ systemf("%s -cf - in/d1 | %s -xf - -s /hardlink2/hardlink2-renamed/ -C test9b",
+ testprog, testprog);
+ assertIsHardlink("test9b/in/d1/hardlink1", "test9b/in/d1/hardlink2-renamed");
+ /* At creation. (assuming hardlink2 is the hardlink entry) */
+ assertMakeDir("test9c", 0755);
+ systemf("%s -cf - -s /hardlink1/hardlink1-renamed/ in/d1 | %s -xf - -C test9c",
+ testprog, testprog);
+ assertIsHardlink("test9c/in/d1/hardlink1-renamed", "test9c/in/d1/hardlink2");
+ /* At creation. (assuming hardlink1 is the hardlink entry) */
+ assertMakeDir("test9d", 0755);
+ systemf("%s -cf - -s /hardlink2/hardlink2-renamed/ in/d1 | %s -xf - -C test9d",
+ testprog, testprog);
+ assertIsHardlink("test9d/in/d1/hardlink1", "test9d/in/d1/hardlink2-renamed");
+
+ /*
+ * Test 10: renaming symlink target without repointing symlink
+ */
if (canSymlink()) {
- /* At extraction. (assuming hardlink2 is the hardlink entry) */
- assertMakeDir("test9a", 0755);
- systemf("%s -cf - in/d1 | %s -xf - -s /hardlink1/hardlink1-renamed/ -C test9a",
+ /* At extraction. */
+ assertMakeDir("test10a", 0755);
+ systemf("%s -cf - in/d1 | %s -xf - -s /realfile/foo/S -s /foo/realfile/ -C test10a",
testprog, testprog);
- assertIsHardlink("test9a/in/d1/hardlink1-renamed", "test9a/in/d1/hardlink2");
- /* At extraction. (assuming hardlink1 is the hardlink entry) */
- assertMakeDir("test9b", 0755);
- systemf("%s -cf - in/d1 | %s -xf - -s /hardlink2/hardlink2-renamed/ -C test9b",
+ assertFileContents("realfile", 8, "test10a/in/d1/foo");
+ assertFileContents("foo", 3, "test10a/in/d1/realfile");
+ assertFileContents("foo", 3, "test10a/in/d1/symlink");
+ assertIsSymlink("test10a/in/d1/symlink", "realfile");
+ /* At creation. */
+ assertMakeDir("test10b", 0755);
+ systemf("%s -cf - -s /realfile/foo/S -s /foo/realfile/ in/d1 | %s -xf - -C test10b",
testprog, testprog);
- assertIsHardlink("test9b/in/d1/hardlink1", "test9b/in/d1/hardlink2-renamed");
- /* At creation. (assuming hardlink2 is the hardlink entry) */
- assertMakeDir("test9c", 0755);
- systemf("%s -cf - -s /hardlink1/hardlink1-renamed/ in/d1 | %s -xf - -C test9c",
+ assertFileContents("realfile", 8, "test10b/in/d1/foo");
+ assertFileContents("foo", 3, "test10b/in/d1/realfile");
+ assertFileContents("foo", 3, "test10b/in/d1/symlink");
+ assertIsSymlink("test10b/in/d1/symlink", "realfile");
+ }
+
+ /*
+ * Test 11: repointing symlink without renaming file
+ */
+ if (canSymlink()) {
+ /* At extraction. */
+ assertMakeDir("test11a", 0755);
+ systemf("%s -cf - in/d1 | %s -xf - -s /realfile/foo/sR -C test11a",
testprog, testprog);
- assertIsHardlink("test9c/in/d1/hardlink1-renamed", "test9c/in/d1/hardlink2");
- /* At creation. (assuming hardlink1 is the hardlink entry) */
- assertMakeDir("test9d", 0755);
- systemf("%s -cf - -s /hardlink2/hardlink2-renamed/ in/d1 | %s -xf - -C test9d",
+ assertFileContents("foo", 3, "test11a/in/d1/foo");
+ assertFileContents("realfile", 8, "test11a/in/d1/realfile");
+ assertFileContents("foo", 3, "test11a/in/d1/symlink");
+ assertIsSymlink("test11a/in/d1/symlink", "foo");
+ /* At creation. */
+ assertMakeDir("test11b", 0755);
+ systemf("%s -cf - -s /realfile/foo/R in/d1 | %s -xf - -C test11b",
testprog, testprog);
- assertIsHardlink("test9d/in/d1/hardlink1", "test9d/in/d1/hardlink2-renamed");
+ assertFileContents("foo", 3, "test11b/in/d1/foo");
+ assertFileContents("realfile", 8, "test11b/in/d1/realfile");
+ assertFileContents("foo", 3, "test11b/in/d1/symlink");
+ assertIsSymlink("test11b/in/d1/symlink", "foo");
}
+ /*
+ * Test 12: renaming hardlink target without changing hardlink.
+ * (Requires a pre-built archive, since we otherwise can't know
+ * which element will be stored as the hardlink.)
+ */
+ extract_reference_file("test_option_s.tar.Z");
+ assertMakeDir("test12a", 0755);
+ systemf("%s -xf test_option_s.tar.Z -s /hardlink1/foo/H -s /foo/hardlink1/ -C test12a",
+ testprog);
+ assertFileContents("foo", 3, "test12a/in/d1/hardlink1");
+ assertFileContents("hardlinkedfile", 14, "test12a/in/d1/foo");
+ assertFileContents("foo", 3, "test12a/in/d1/hardlink2");
+ assertIsHardlink("test12a/in/d1/hardlink1", "test12a/in/d1/hardlink2");
+ /* TODO: Expand this test to verify creation as well.
+ * Since either hardlink1 or hardlink2 might get stored as a hardlink,
+ * this will either requiring testing both cases and accepting either
+ * pass, or some very creative renames that can be tested regardless.
+ */
+
+ /*
+ * Test 13: repoint hardlink without changing files
+ * (Requires a pre-built archive, since we otherwise can't know
+ * which element will be stored as the hardlink.)
+ */
+ extract_reference_file("test_option_s.tar.Z");
+ assertMakeDir("test13a", 0755);
+ systemf("%s -xf test_option_s.tar.Z -s /hardlink1/foo/Rh -s /foo/hardlink1/Rh -C test13a",
+ testprog);
+ assertFileContents("foo", 3, "test13a/in/d1/foo");
+ assertFileContents("hardlinkedfile", 14, "test13a/in/d1/hardlink1");
+ assertFileContents("foo", 3, "test13a/in/d1/hardlink2");
+ assertIsHardlink("test13a/in/d1/foo", "test13a/in/d1/hardlink2");
+ /* TODO: See above; expand this test to verify renames at creation. */
}
diff --git a/tar/test/test_option_s.tar.Z.uu b/tar/test/test_option_s.tar.Z.uu
new file mode 100644
index 00000000..df690bfa
--- /dev/null
+++ b/tar/test/test_option_s.tar.Z.uu
@@ -0,0 +1,16 @@
+begin 644 test_option_s.tar.Z
+M'YV0:=R\(!/C!8"#"!,J7,BPH<.'$"-*1`BCXHT:-4``J`CCAHV,&SG*H*&1
+MHTF3(&+$^%@C!HT8,&C(``%#Y0T9,P"`J#&QI\^?0(,"J#.'3A@Y(>FD:2/4
+M8=$P9LPT77@21LF3&J=JW<JUJU>A`0<6%'/TJ]FS"3G:H$$R9,>/5RN.C%L5
+MQHR4*S&ZA"F39HR1=G7"0$NX<$*B1I%65,KTZ].H7JO2K9C5L.7+F+>219JY
+ML^?/H$.+'DVZM&G+80F^,//FS6G0:MG&]0C2Y%RW=>^J9+DWYLR:(V7($/SZ
+M->*RBY>:?2RUJV3<E(M+GQZ1M6OJV+-KW\Z]NW?3J0NB.4J&3<`U,;YKC=V6
+M(VVZM^M67(F7]TO??F?4D#&8IGK,QRD&`V/+&069<UA!9]5_#'8VGASEG5<&
+M&6:DP489#6:HX88<=NAA3^&]\&"$;JPQW(<0L3<;7+C%)Q]EN^EU7U\UV2!<
+M3BF%.*)Y)::'XD,!)J6<8P8VQ]5SS_VHI$\ARE%&&&Q4>.&2#JGHUGLMMB<?
+M3/7)R-=O,<P@4W^#4=E4D,DUYA5SD268I)EP,N0DE%)B&.>=>.:IYYX'A3A'
+M'FWPN`:?[F&T8FTB:?EBERW-"&8-=N$HPYQ16F@GGPBA.>"0:Q;9)DH*5H;I
+MJ*26:NJIJ*:JZJJLMNKJJ[#&*NNLM-9JZZVXYJKKKKSVZNNOP`8K[+#$%FOL
+)L<@FJ^RRS.X)
+`
+end