]>
Commit | Line | Data |
---|---|---|
453b82a3 DK |
1 | #include <config.h> |
2 | ||
488011fa MV |
3 | #include <apt-pkg/error.h> |
4 | #include <apt-pkg/fileutl.h> | |
f00832cc | 5 | #include <apt-pkg/strutl.h> |
f7feb041 | 6 | #include <apt-pkg/aptconfiguration.h> |
124e6916 | 7 | #include <apt-pkg/configuration.h> |
488011fa | 8 | |
124e6916 | 9 | #include <algorithm> |
488011fa MV |
10 | #include <string> |
11 | #include <vector> | |
488011fa | 12 | #include <stdlib.h> |
f7feb041 | 13 | #include <string.h> |
488011fa | 14 | |
f00832cc | 15 | #include <gtest/gtest.h> |
488011fa | 16 | |
f00832cc | 17 | #include "file-helpers.h" |
f7feb041 | 18 | |
f00832cc DK |
19 | static void TestFileFd(mode_t const a_umask, mode_t const ExpectedFilePermission, |
20 | unsigned int const filemode, APT::Configuration::Compressor const &compressor) | |
f22b65b4 | 21 | { |
f00832cc DK |
22 | std::string trace; |
23 | strprintf(trace, "TestFileFd: Compressor: %s umask: %#o permission: %#o mode: %d", compressor.Name.c_str(), a_umask, ExpectedFilePermission, filemode); | |
24 | SCOPED_TRACE(trace); | |
25 | ||
bb93178b DK |
26 | static const char* fname = "apt-filefd-test.txt"; |
27 | if (FileExists(fname) == true) | |
f00832cc | 28 | EXPECT_EQ(0, unlink(fname)); |
f22b65b4 | 29 | |
f00832cc | 30 | FileFd f; |
f22b65b4 | 31 | umask(a_umask); |
f00832cc DK |
32 | EXPECT_TRUE(f.Open(fname, filemode, compressor)); |
33 | EXPECT_TRUE(f.IsOpen()); | |
34 | EXPECT_FALSE(f.Failed()); | |
35 | EXPECT_EQ(umask(a_umask), a_umask); | |
f7feb041 | 36 | |
bb93178b | 37 | std::string test = "This is a test!\n"; |
f00832cc DK |
38 | EXPECT_TRUE(f.Write(test.c_str(), test.size())); |
39 | EXPECT_TRUE(f.IsOpen()); | |
40 | EXPECT_FALSE(f.Failed()); | |
f7feb041 | 41 | |
f22b65b4 | 42 | f.Close(); |
f00832cc DK |
43 | EXPECT_FALSE(f.IsOpen()); |
44 | EXPECT_FALSE(f.Failed()); | |
45 | ||
46 | EXPECT_TRUE(f.Open(fname, FileFd::ReadOnly, compressor)); | |
47 | EXPECT_TRUE(f.IsOpen()); | |
48 | EXPECT_FALSE(f.Failed()); | |
49 | EXPECT_FALSE(f.Eof()); | |
50 | EXPECT_NE(0, f.FileSize()); | |
51 | EXPECT_FALSE(f.Failed()); | |
52 | EXPECT_NE(0, f.ModificationTime()); | |
53 | EXPECT_FALSE(f.Failed()); | |
f7feb041 | 54 | |
bb93178b | 55 | // ensure the memory is as predictably messed up |
f00832cc | 56 | #define APT_INIT_READBACK \ |
bb93178b | 57 | char readback[20]; \ |
ad5ffe1f | 58 | memset(readback, 'D', sizeof(readback)*sizeof(readback[0])); \ |
bb93178b | 59 | readback[19] = '\0'; |
f00832cc DK |
60 | #define EXPECT_N_STR(expect, actual) \ |
61 | EXPECT_EQ(0, strncmp(expect, actual, strlen(expect))); | |
ad5ffe1f DK |
62 | { |
63 | APT_INIT_READBACK | |
64 | char const * const expect = "DDDDDDDDDDDDDDDDDDD"; | |
65 | EXPECT_STREQ(expect,readback); | |
66 | EXPECT_N_STR(expect, readback); | |
67 | } | |
f7feb041 | 68 | { |
bb93178b | 69 | APT_INIT_READBACK |
f7feb041 | 70 | char const * const expect = "This"; |
f00832cc DK |
71 | EXPECT_TRUE(f.Read(readback, strlen(expect))); |
72 | EXPECT_FALSE(f.Failed()); | |
73 | EXPECT_FALSE(f.Eof()); | |
74 | EXPECT_N_STR(expect, readback); | |
75 | EXPECT_EQ(strlen(expect), f.Tell()); | |
f7feb041 DK |
76 | } |
77 | { | |
bb93178b DK |
78 | APT_INIT_READBACK |
79 | char const * const expect = "test!\n"; | |
f00832cc DK |
80 | EXPECT_TRUE(f.Skip((test.size() - f.Tell()) - strlen(expect))); |
81 | EXPECT_TRUE(f.Read(readback, strlen(expect))); | |
82 | EXPECT_FALSE(f.Failed()); | |
83 | EXPECT_FALSE(f.Eof()); | |
84 | EXPECT_N_STR(expect, readback); | |
85 | EXPECT_EQ(test.size(), f.Tell()); | |
f7feb041 | 86 | } |
5fafaf27 JAK |
87 | // Non-zero backwards seek |
88 | { | |
89 | APT_INIT_READBACK | |
90 | char const * const expect = "is"; | |
91 | EXPECT_EQ(test.size(), f.Tell()); | |
92 | EXPECT_TRUE(f.Seek(5)); | |
93 | EXPECT_TRUE(f.Read(readback, strlen(expect))); | |
94 | EXPECT_FALSE(f.Failed()); | |
95 | EXPECT_FALSE(f.Eof()); | |
96 | EXPECT_N_STR(expect, readback); | |
97 | EXPECT_EQ(7, f.Tell()); | |
98 | } | |
bb93178b DK |
99 | { |
100 | APT_INIT_READBACK | |
f00832cc DK |
101 | EXPECT_TRUE(f.Seek(0)); |
102 | EXPECT_FALSE(f.Eof()); | |
103 | EXPECT_TRUE(f.Read(readback, 20, true)); | |
104 | EXPECT_FALSE(f.Failed()); | |
105 | EXPECT_TRUE(f.Eof()); | |
106 | EXPECT_N_STR(test.c_str(), readback); | |
107 | EXPECT_EQ(f.Size(), f.Tell()); | |
bb93178b DK |
108 | } |
109 | { | |
110 | APT_INIT_READBACK | |
f00832cc DK |
111 | EXPECT_TRUE(f.Seek(0)); |
112 | EXPECT_FALSE(f.Eof()); | |
113 | EXPECT_TRUE(f.Read(readback, test.size(), true)); | |
114 | EXPECT_FALSE(f.Failed()); | |
115 | EXPECT_FALSE(f.Eof()); | |
116 | EXPECT_N_STR(test.c_str(), readback); | |
117 | EXPECT_EQ(f.Size(), f.Tell()); | |
bb93178b DK |
118 | } |
119 | { | |
120 | APT_INIT_READBACK | |
f00832cc DK |
121 | EXPECT_TRUE(f.Seek(0)); |
122 | EXPECT_FALSE(f.Eof()); | |
bb93178b | 123 | unsigned long long actual; |
f00832cc DK |
124 | EXPECT_TRUE(f.Read(readback, 20, &actual)); |
125 | EXPECT_FALSE(f.Failed()); | |
126 | EXPECT_TRUE(f.Eof()); | |
127 | EXPECT_EQ(test.size(), actual); | |
128 | EXPECT_N_STR(test.c_str(), readback); | |
129 | EXPECT_EQ(f.Size(), f.Tell()); | |
bb93178b DK |
130 | } |
131 | { | |
132 | APT_INIT_READBACK | |
f00832cc DK |
133 | EXPECT_TRUE(f.Seek(0)); |
134 | EXPECT_FALSE(f.Eof()); | |
bb93178b | 135 | f.ReadLine(readback, 20); |
f00832cc DK |
136 | EXPECT_FALSE(f.Failed()); |
137 | EXPECT_FALSE(f.Eof()); | |
138 | EXPECT_EQ(test, readback); | |
139 | EXPECT_EQ(f.Size(), f.Tell()); | |
bb93178b DK |
140 | } |
141 | { | |
142 | APT_INIT_READBACK | |
f00832cc DK |
143 | EXPECT_TRUE(f.Seek(0)); |
144 | EXPECT_FALSE(f.Eof()); | |
bb93178b DK |
145 | char const * const expect = "This"; |
146 | f.ReadLine(readback, strlen(expect) + 1); | |
f00832cc DK |
147 | EXPECT_FALSE(f.Failed()); |
148 | EXPECT_FALSE(f.Eof()); | |
149 | EXPECT_N_STR(expect, readback); | |
150 | EXPECT_EQ(strlen(expect), f.Tell()); | |
bb93178b DK |
151 | } |
152 | #undef APT_INIT_READBACK | |
f7feb041 DK |
153 | |
154 | f.Close(); | |
f00832cc DK |
155 | EXPECT_FALSE(f.IsOpen()); |
156 | EXPECT_FALSE(f.Failed()); | |
f7feb041 DK |
157 | |
158 | // regression test for permission bug LP: #1304657 | |
f00832cc DK |
159 | struct stat buf; |
160 | EXPECT_EQ(0, stat(fname, &buf)); | |
161 | EXPECT_EQ(0, unlink(fname)); | |
162 | EXPECT_EQ(ExpectedFilePermission, buf.st_mode & 0777); | |
f22b65b4 MV |
163 | } |
164 | ||
f00832cc | 165 | static void TestFileFd(unsigned int const filemode) |
488011fa | 166 | { |
124e6916 DK |
167 | auto const compressors = APT::Configuration::getCompressors(); |
168 | EXPECT_EQ(7, compressors.size()); | |
169 | bool atLeastOneWasTested = false; | |
170 | for (auto const &c: compressors) | |
f7feb041 DK |
171 | { |
172 | if ((filemode & FileFd::ReadWrite) == FileFd::ReadWrite && | |
124e6916 | 173 | (c.Name.empty() != true && c.Binary.empty() != true)) |
f7feb041 | 174 | continue; |
124e6916 DK |
175 | atLeastOneWasTested = true; |
176 | TestFileFd(0002, 0664, filemode, c); | |
177 | TestFileFd(0022, 0644, filemode, c); | |
178 | TestFileFd(0077, 0600, filemode, c); | |
179 | TestFileFd(0026, 0640, filemode, c); | |
f7feb041 | 180 | } |
124e6916 | 181 | EXPECT_TRUE(atLeastOneWasTested); |
f7feb041 | 182 | } |
488011fa | 183 | |
f00832cc DK |
184 | TEST(FileUtlTest, FileFD) |
185 | { | |
124e6916 DK |
186 | // testing the (un)compress via pipe, as the 'real' compressors are usually built in via libraries |
187 | _config->Set("APT::Compressor::rev::Name", "rev"); | |
188 | _config->Set("APT::Compressor::rev::Extension", ".reversed"); | |
189 | _config->Set("APT::Compressor::rev::Binary", "rev"); | |
190 | _config->Set("APT::Compressor::rev::Cost", 10); | |
191 | auto const compressors = APT::Configuration::getCompressors(false); | |
192 | EXPECT_EQ(7, compressors.size()); | |
193 | EXPECT_TRUE(std::any_of(compressors.begin(), compressors.end(), [](APT::Configuration::Compressor const &c) { return c.Name == "rev"; })); | |
194 | ||
f00832cc DK |
195 | std::string const startdir = SafeGetCWD(); |
196 | EXPECT_FALSE(startdir.empty()); | |
197 | std::string tempdir; | |
198 | createTemporaryDirectory("filefd", tempdir); | |
199 | EXPECT_EQ(0, chdir(tempdir.c_str())); | |
200 | ||
201 | TestFileFd(FileFd::WriteOnly | FileFd::Create); | |
202 | TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Empty); | |
203 | TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive); | |
204 | TestFileFd(FileFd::WriteOnly | FileFd::Atomic); | |
205 | TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Atomic); | |
206 | // short-hands for ReadWrite with these modes | |
207 | TestFileFd(FileFd::WriteEmpty); | |
208 | TestFileFd(FileFd::WriteAny); | |
209 | TestFileFd(FileFd::WriteTemp); | |
210 | TestFileFd(FileFd::WriteAtomic); | |
211 | ||
212 | EXPECT_EQ(0, chdir(startdir.c_str())); | |
213 | removeDirectory(tempdir); | |
214 | } | |
215 | TEST(FileUtlTest, Glob) | |
f7feb041 | 216 | { |
f7feb041 | 217 | std::vector<std::string> files; |
488011fa | 218 | // normal match |
f00832cc DK |
219 | files = Glob("*akefile"); |
220 | EXPECT_EQ(1, files.size()); | |
488011fa MV |
221 | |
222 | // not there | |
223 | files = Glob("xxxyyyzzz"); | |
f00832cc DK |
224 | EXPECT_TRUE(files.empty()); |
225 | EXPECT_FALSE(_error->PendingError()); | |
488011fa MV |
226 | |
227 | // many matches (number is a bit random) | |
228 | files = Glob("*.cc"); | |
f00832cc DK |
229 | EXPECT_LT(10, files.size()); |
230 | } | |
231 | TEST(FileUtlTest, GetTempDir) | |
232 | { | |
233 | char const * const envtmp = getenv("TMPDIR"); | |
234 | std::string old_tmpdir; | |
235 | if (envtmp != NULL) | |
236 | old_tmpdir = envtmp; | |
488011fa | 237 | |
a077861a | 238 | unsetenv("TMPDIR"); |
f00832cc | 239 | EXPECT_EQ("/tmp", GetTempDir()); |
a077861a MV |
240 | |
241 | setenv("TMPDIR", "", 1); | |
f00832cc | 242 | EXPECT_EQ("/tmp", GetTempDir()); |
a077861a MV |
243 | |
244 | setenv("TMPDIR", "/not-there-no-really-not", 1); | |
f00832cc | 245 | EXPECT_EQ("/tmp", GetTempDir()); |
a077861a | 246 | |
dbbe1e63 PT |
247 | // root can access everything, so /usr will be accepted |
248 | if (geteuid() != 0) | |
249 | { | |
250 | // here but not accessible for non-roots | |
251 | setenv("TMPDIR", "/usr", 1); | |
252 | EXPECT_EQ("/tmp", GetTempDir()); | |
253 | } | |
0d303f17 DK |
254 | |
255 | // files are no good for tmpdirs, too | |
256 | setenv("TMPDIR", "/dev/null", 1); | |
257 | EXPECT_EQ("/tmp", GetTempDir()); | |
258 | ||
259 | setenv("TMPDIR", "/var/tmp", 1); | |
260 | EXPECT_EQ("/var/tmp", GetTempDir()); | |
a077861a | 261 | |
f00832cc DK |
262 | unsetenv("TMPDIR"); |
263 | if (old_tmpdir.empty() == false) | |
264 | setenv("TMPDIR", old_tmpdir.c_str(), 1); | |
488011fa | 265 | } |
7ad2a347 MV |
266 | TEST(FileUtlTest, Popen) |
267 | { | |
268 | FileFd Fd; | |
269 | pid_t Child; | |
270 | char buf[1024]; | |
271 | std::string s; | |
272 | unsigned long long n = 0; | |
273 | std::vector<std::string> OpenFds; | |
274 | ||
275 | // count Fds to ensure we don't have a resource leak | |
276 | if(FileExists("/proc/self/fd")) | |
277 | OpenFds = Glob("/proc/self/fd/*"); | |
278 | ||
279 | // output something | |
280 | const char* Args[10] = {"/bin/echo", "meepmeep", NULL}; | |
23fb7b2c DK |
281 | EXPECT_TRUE(Popen(Args, Fd, Child, FileFd::ReadOnly)); |
282 | EXPECT_TRUE(Fd.Read(buf, sizeof(buf)-1, &n)); | |
7ad2a347 MV |
283 | buf[n] = 0; |
284 | EXPECT_NE(n, 0); | |
7ad2a347 MV |
285 | EXPECT_STREQ(buf, "meepmeep\n"); |
286 | ||
287 | // wait for the child to exit and cleanup | |
23fb7b2c DK |
288 | EXPECT_TRUE(ExecWait(Child, "PopenRead")); |
289 | EXPECT_TRUE(Fd.Close()); | |
7ad2a347 MV |
290 | |
291 | // ensure that after a close all is good again | |
292 | if(FileExists("/proc/self/fd")) | |
293 | EXPECT_EQ(Glob("/proc/self/fd/*").size(), OpenFds.size()); | |
294 | ||
7ad2a347 | 295 | // ReadWrite is not supported |
23fb7b2c DK |
296 | _error->PushToStack(); |
297 | EXPECT_FALSE(Popen(Args, Fd, Child, FileFd::ReadWrite)); | |
298 | EXPECT_FALSE(Fd.IsOpen()); | |
299 | EXPECT_FALSE(Fd.Failed()); | |
300 | EXPECT_TRUE(_error->PendingError()); | |
301 | _error->RevertToStack(); | |
7ad2a347 MV |
302 | |
303 | // write something | |
304 | Args[0] = "/bin/bash"; | |
305 | Args[1] = "-c"; | |
306 | Args[2] = "read"; | |
307 | Args[3] = NULL; | |
23fb7b2c | 308 | EXPECT_TRUE(Popen(Args, Fd, Child, FileFd::WriteOnly)); |
7ad2a347 | 309 | s = "\n"; |
23fb7b2c DK |
310 | EXPECT_TRUE(Fd.Write(s.c_str(), s.length())); |
311 | EXPECT_TRUE(Fd.Close()); | |
312 | EXPECT_FALSE(Fd.IsOpen()); | |
313 | EXPECT_FALSE(Fd.Failed()); | |
314 | EXPECT_TRUE(ExecWait(Child, "PopenWrite")); | |
7ad2a347 | 315 | } |
53ac87ac MV |
316 | TEST(FileUtlTest, flAbsPath) |
317 | { | |
a111a024 | 318 | std::string cwd = SafeGetCWD(); |
20cf708a | 319 | int res = chdir("/etc/"); |
53ac87ac | 320 | EXPECT_EQ(res, 0); |
20cf708a JAK |
321 | std::string p = flAbsPath("passwd"); |
322 | EXPECT_EQ(p, "/etc/passwd"); | |
a111a024 MV |
323 | |
324 | res = chdir(cwd.c_str()); | |
325 | EXPECT_EQ(res, 0); | |
53ac87ac | 326 | } |
cd46d4eb DK |
327 | |
328 | static void TestDevNullFileFd(unsigned int const filemode) | |
329 | { | |
fc5db01b | 330 | SCOPED_TRACE(filemode); |
cd46d4eb DK |
331 | FileFd f("/dev/null", filemode); |
332 | EXPECT_FALSE(f.Failed()); | |
333 | EXPECT_TRUE(f.IsOpen()); | |
334 | EXPECT_TRUE(f.IsOpen()); | |
335 | ||
336 | std::string test = "This is a test!\n"; | |
337 | EXPECT_TRUE(f.Write(test.c_str(), test.size())); | |
338 | EXPECT_TRUE(f.IsOpen()); | |
339 | EXPECT_FALSE(f.Failed()); | |
340 | ||
341 | f.Close(); | |
342 | EXPECT_FALSE(f.IsOpen()); | |
343 | EXPECT_FALSE(f.Failed()); | |
344 | } | |
345 | TEST(FileUtlTest, WorkingWithDevNull) | |
346 | { | |
347 | TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create); | |
348 | TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Empty); | |
349 | TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive); | |
350 | TestDevNullFileFd(FileFd::WriteOnly | FileFd::Atomic); | |
351 | TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Atomic); | |
352 | // short-hands for ReadWrite with these modes | |
353 | TestDevNullFileFd(FileFd::WriteEmpty); | |
354 | TestDevNullFileFd(FileFd::WriteAny); | |
355 | TestDevNullFileFd(FileFd::WriteTemp); | |
356 | TestDevNullFileFd(FileFd::WriteAtomic); | |
357 | } | |
fc5db01b DK |
358 | constexpr char const * const TESTSTRING = "This is a test"; |
359 | static void TestFailingAtomicKeepsFile(char const * const label, std::string const &filename) | |
360 | { | |
361 | SCOPED_TRACE(label); | |
362 | EXPECT_TRUE(FileExists(filename)); | |
363 | FileFd fd; | |
364 | EXPECT_TRUE(fd.Open(filename, FileFd::ReadOnly)); | |
365 | char buffer[50]; | |
366 | EXPECT_NE(nullptr, fd.ReadLine(buffer, sizeof(buffer))); | |
367 | EXPECT_STREQ(TESTSTRING, buffer); | |
368 | } | |
369 | TEST(FileUtlTest, FailingAtomic) | |
370 | { | |
371 | FileFd fd; | |
372 | std::string filename; | |
373 | createTemporaryFile("failingatomic", fd, &filename, TESTSTRING); | |
374 | TestFailingAtomicKeepsFile("init", filename); | |
375 | ||
376 | FileFd f; | |
377 | EXPECT_TRUE(f.Open(filename, FileFd::ReadWrite | FileFd::Atomic)); | |
378 | f.EraseOnFailure(); | |
379 | EXPECT_FALSE(f.Failed()); | |
380 | EXPECT_TRUE(f.IsOpen()); | |
381 | TestFailingAtomicKeepsFile("before-fail", filename); | |
382 | EXPECT_TRUE(f.Write("Bad file write", 10)); | |
383 | f.OpFail(); | |
384 | EXPECT_TRUE(f.Failed()); | |
385 | TestFailingAtomicKeepsFile("after-fail", filename); | |
386 | EXPECT_TRUE(f.Close()); | |
387 | TestFailingAtomicKeepsFile("closed", filename); | |
388 | ||
389 | if (filename.empty() == false) | |
390 | unlink(filename.c_str()); | |
391 | } |