diff options
author | Neal Norwitz <nnorwitz@gmail.com> | 2005-10-20 04:50:13 (GMT) |
---|---|---|
committer | Neal Norwitz <nnorwitz@gmail.com> | 2005-10-20 04:50:13 (GMT) |
commit | b0e32e2b71633211be6a62d1afaa250fb23cd3b4 (patch) | |
tree | 8c15e8edb03fba3a429fff0c5be609e6439bf745 | |
parent | 40563eddf17f072a74eba0bf7b77229f77bcc3d3 (diff) | |
download | cpython-b0e32e2b71633211be6a62d1afaa250fb23cd3b4.zip cpython-b0e32e2b71633211be6a62d1afaa250fb23cd3b4.tar.gz cpython-b0e32e2b71633211be6a62d1afaa250fb23cd3b4.tar.bz2 |
Fix SF bug # 1330039, patch # 1331635 from Lars Gustaebel (tarfile maintainer)
Problem: if two files are assigned the same inode
number by the filesystem, the second one will be added
as a hardlink to the first, which means that the
content will be lost.
The patched code checks if the file's st_nlink is
greater 1. So only for files that actually have several
links pointing to them hardlinks will be created, which
is what GNU tar does.
Will backport.
-rw-r--r-- | Lib/tarfile.py | 3 | ||||
-rw-r--r-- | Lib/test/test_tarfile.py | 49 |
2 files changed, 50 insertions, 2 deletions
diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 2f21971..c86248c 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1150,7 +1150,8 @@ class TarFile(object): stmd = statres.st_mode if stat.S_ISREG(stmd): inode = (statres.st_ino, statres.st_dev) - if inode in self.inodes and not self.dereference: + if not self.dereference and \ + statres.st_nlink > 1 and inode in self.inodes: # Is it a hardlink to an already # archived file? type = LNKTYPE diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 66409cd..b202ea5 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -372,6 +372,53 @@ class ExtractHardlinkTest(BaseTest): if e.errno == errno.ENOENT: self.fail("hardlink not extracted properly") +class CreateHardlinkTest(BaseTest): + """Test the creation of LNKTYPE (hardlink) members in an archive. + In this respect tarfile.py mimics the behaviour of GNU tar: If + a file has a st_nlink > 1, it will be added a REGTYPE member + only the first time. + """ + + def setUp(self): + self.tar = tarfile.open(tmpname(), "w") + + self.foo = os.path.join(dirname(), "foo") + self.bar = os.path.join(dirname(), "bar") + + if os.path.exists(self.foo): + os.remove(self.foo) + if os.path.exists(self.bar): + os.remove(self.bar) + + file(self.foo, "w").write("foo") + self.tar.add(self.foo) + + def test_add_twice(self): + # If st_nlink == 1 then the same file will be added as + # REGTYPE every time. + tarinfo = self.tar.gettarinfo(self.foo) + self.assertEqual(tarinfo.type, tarfile.REGTYPE, + "add file as regular failed") + + def test_add_hardlink(self): + # If st_nlink > 1 then the same file will be added as + # LNKTYPE. + os.link(self.foo, self.bar) + tarinfo = self.tar.gettarinfo(self.foo) + self.assertEqual(tarinfo.type, tarfile.LNKTYPE, + "add file as hardlink failed") + + tarinfo = self.tar.gettarinfo(self.bar) + self.assertEqual(tarinfo.type, tarfile.LNKTYPE, + "add file as hardlink failed") + + def test_dereference_hardlink(self): + self.tar.dereference = True + os.link(self.foo, self.bar) + tarinfo = self.tar.gettarinfo(self.bar) + self.assertEqual(tarinfo.type, tarfile.REGTYPE, + "dereferencing hardlink failed") + # Gzip TestCases class ReadTestGzip(ReadTest): @@ -387,7 +434,6 @@ class ReadAsteriskTestGzip(ReadAsteriskTest): class ReadStreamAsteriskTestGzip(ReadStreamAsteriskTest): comp = "gz" - # Filemode test cases class FileModeTest(unittest.TestCase): @@ -440,6 +486,7 @@ def test_main(): if hasattr(os, "link"): tests.append(ExtractHardlinkTest) + tests.append(CreateHardlinkTest) if gzip: tests.extend([ |