llvm.org GIT mirror llvm / 55cbcce
Fix off-by-one error in TarWriter. The tar format originally supported up to 99 byte filename. The two extensions are proposed later: Ustar or PAX. In the UStar extension, a pathanme is split at a '/' and its "prefix" and "suffix" are stored in different locations in the tar header. Since "prefix" can be up to 155 byte, it can represent up to 254 byte filename (but exact limit depends on the location of '/' character in a pathname.) Our TarWriter first attempt to use UStar extension and then fallback to PAX extension. But there's a bug in UStar header creation. "Suffix" part must be a NUL- terminated string, but we didn't handle it correctly. As a result, if your filename just 100 characters long, the last character was droppped. This patch fixes the issue. Differential Revision: https://reviews.llvm.org/D38149 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@314349 91177308-0d34-0410-b5e6-96231b3b80d8 Rui Ueyama 1 year, 11 months ago
2 changed file(s) with 83 addition(s) and 43 deletion(s). Raw diff Collapse all Expand all
115115 pad(OS);
116116 }
117117
118 // In the Ustar header, a path can be split at any '/' to store
119 // a path into UstarHeader::Name and UstarHeader::Prefix. This
120 // function splits a given path for that purpose.
121 static std::pair splitPath(StringRef Path) {
122 if (Path.size() <= sizeof(UstarHeader::Name))
123 return {"", Path};
118 // Path fits in a Ustar header if
119 //
120 // - Path is less than 100 characters long, or
121 // - Path is in the form of "/" where is less
122 // than or equal to 155 characters long and is less than 100
123 // characters long. Both and can contain extra '/'.
124 //
125 // If Path fits in a Ustar header, updates Prefix and Name and returns true.
126 // Otherwise, returns false.
127 static bool splitUstar(StringRef Path, StringRef &Prefix, StringRef &Name) {
128 if (Path.size() < sizeof(UstarHeader::Name)) {
129 Name = Path;
130 return true;
131 }
132
124133 size_t Sep = Path.rfind('/', sizeof(UstarHeader::Prefix) + 1);
125134 if (Sep == StringRef::npos)
126 return {"", Path};
127 return {Path.substr(0, Sep), Path.substr(Sep + 1)};
128 }
135 return false;
136 if (Path.size() - Sep - 1 >= sizeof(UstarHeader::Name))
137 return false;
129138
130 // Returns true if a given path can be stored to a Ustar header
131 // without the PAX extension.
132 static bool fitsInUstar(StringRef Path) {
133 StringRef Prefix;
134 StringRef Name;
135 std::tie(Prefix, Name) = splitPath(Path);
136 return Name.size() <= sizeof(UstarHeader::Name);
139 Prefix = Path.substr(0, Sep);
140 Name = Path.substr(Sep + 1);
141 return true;
137142 }
138143
139144 // The PAX header is an extended format, so a PAX header needs
140145 // to be followed by a "real" header.
141 static void writeUstarHeader(raw_fd_ostream &OS, StringRef Path, size_t Size) {
142 StringRef Prefix;
143 StringRef Name;
144 std::tie(Prefix, Name) = splitPath(Path);
145
146 static void writeUstarHeader(raw_fd_ostream &OS, StringRef Prefix,
147 StringRef Name, size_t Size) {
146148 UstarHeader Hdr = makeUstarHeader();
147149 memcpy(Hdr.Name, Name.data(), Name.size());
148150 memcpy(Hdr.Mode, "0000664", 8);
167169 // Append a given file to an archive.
168170 void TarWriter::append(StringRef Path, StringRef Data) {
169171 // Write Path and Data.
170 std::string S = BaseDir + "/" + sys::path::convert_to_slash(Path) + "\0";
171 if (fitsInUstar(S)) {
172 writeUstarHeader(OS, S, Data.size());
172 std::string Fullpath = BaseDir + "/" + sys::path::convert_to_slash(Path);
173
174 StringRef Prefix;
175 StringRef Name;
176 if (splitUstar(Fullpath, Prefix, Name)) {
177 writeUstarHeader(OS, Prefix, Name, Data.size());
173178 } else {
174 writePaxHeader(OS, S);
175 writeUstarHeader(OS, "", Data.size());
179 writePaxHeader(OS, Fullpath);
180 writeUstarHeader(OS, "", "", Data.size());
176181 }
177182
178183 OS << Data;
1010 #include "llvm/Support/FileSystem.h"
1111 #include "llvm/Support/MemoryBuffer.h"
1212 #include "gtest/gtest.h"
13 #include
1314
1415 using namespace llvm;
1516 namespace {
3637
3738 class TarWriterTest : public ::testing::Test {};
3839
39 static UstarHeader create(StringRef Base, StringRef Filename) {
40 static std::vector createTar(StringRef Base, StringRef Filename) {
4041 // Create a temporary file.
4142 SmallString<128> Path;
4243 std::error_code EC =
5455 ErrorOr> MBOrErr = MemoryBuffer::getFile(Path);
5556 EXPECT_TRUE((bool)MBOrErr);
5657 std::unique_ptr MB = std::move(*MBOrErr);
58 std::vector Buf((const uint8_t *)MB->getBufferStart(),
59 (const uint8_t *)MB->getBufferEnd());
60
61 // Windows does not allow us to remove a mmap'ed files, so
62 // unmap first and then remove the temporary file.
63 MB = nullptr;
5764 sys::fs::remove(Path);
58 return *reinterpret_cast(MB->getBufferStart());
65
66 return Buf;
67 }
68
69 static UstarHeader createUstar(StringRef Base, StringRef Filename) {
70 std::vector Buf = createTar(Base, Filename);
71 EXPECT_TRUE(Buf.size() >= sizeof(UstarHeader));
72 return *reinterpret_cast(Buf.data());
5973 }
6074
6175 TEST_F(TarWriterTest, Basics) {
62 UstarHeader Hdr = create("base", "file");
76 UstarHeader Hdr = createUstar("base", "file");
6377 EXPECT_EQ("ustar", StringRef(Hdr.Magic));
6478 EXPECT_EQ("00", StringRef(Hdr.Version, 2));
6579 EXPECT_EQ("base/file", StringRef(Hdr.Name));
6781 }
6882
6983 TEST_F(TarWriterTest, LongFilename) {
70 UstarHeader Hdr1 = create(
71 "012345678", std::string(99, 'x') + "/" + std::string(44, 'x') + "/foo");
72 EXPECT_EQ("foo", StringRef(Hdr1.Name));
73 EXPECT_EQ("012345678/" + std::string(99, 'x') + "/" + std::string(44, 'x'),
74 StringRef(Hdr1.Prefix));
84 std::string x154(154, 'x');
85 std::string x155(155, 'x');
86 std::string y99(99, 'y');
87 std::string y100(100, 'y');
7588
76 UstarHeader Hdr2 = create(
77 "012345678", std::string(99, 'x') + "/" + std::string(45, 'x') + "/foo");
78 EXPECT_EQ("foo", StringRef(Hdr2.Name));
79 EXPECT_EQ("012345678/" + std::string(99, 'x') + "/" + std::string(45, 'x'),
80 StringRef(Hdr2.Prefix));
89 UstarHeader Hdr1 = createUstar("", x154 + "/" + y99);
90 EXPECT_EQ("/" + x154, StringRef(Hdr1.Prefix));
91 EXPECT_EQ(y99, StringRef(Hdr1.Name));
8192
82 UstarHeader Hdr3 = create(
83 "012345678", std::string(99, 'x') + "/" + std::string(46, 'x') + "/foo");
84 EXPECT_EQ(std::string(46, 'x') + "/foo", StringRef(Hdr3.Name));
85 EXPECT_EQ("012345678/" + std::string(99, 'x'), StringRef(Hdr3.Prefix));
93 UstarHeader Hdr2 = createUstar("", x155 + "/" + y99);
94 EXPECT_EQ("", StringRef(Hdr2.Prefix));
95 EXPECT_EQ("", StringRef(Hdr2.Name));
96
97 UstarHeader Hdr3 = createUstar("", x154 + "/" + y100);
98 EXPECT_EQ("", StringRef(Hdr3.Prefix));
99 EXPECT_EQ("", StringRef(Hdr3.Name));
100
101 UstarHeader Hdr4 = createUstar("", x155 + "/" + y100);
102 EXPECT_EQ("", StringRef(Hdr4.Prefix));
103 EXPECT_EQ("", StringRef(Hdr4.Name));
104
105 std::string yz = "yyyyyyyyyyyyyyyyyyyy/zzzzzzzzzzzzzzzzzzzz";
106 UstarHeader Hdr5 = createUstar("", x154 + "/" + yz);
107 EXPECT_EQ("/" + x154, StringRef(Hdr5.Prefix));
108 EXPECT_EQ(yz, StringRef(Hdr5.Name));
109 }
110
111 TEST_F(TarWriterTest, Pax) {
112 std::vector Buf = createTar("", std::string(200, 'x'));
113 EXPECT_TRUE(Buf.size() >= 1024);
114
115 auto *Hdr = reinterpret_cast(Buf.data());
116 EXPECT_EQ("", StringRef(Hdr->Prefix));
117 EXPECT_EQ("", StringRef(Hdr->Name));
118
119 StringRef Pax = StringRef((char *)(Buf.data() + 512), 512);
120 EXPECT_TRUE(Pax.startswith("211 path=/" + std::string(200, 'x')));
86121 }
87122 }