]>
Commit | Line | Data |
---|---|---|
1 | #include <config.h> | |
2 | ||
3 | #include <apt-pkg/error.h> | |
4 | #include <apt-pkg/fileutl.h> | |
5 | #include <apt-pkg/strutl.h> | |
6 | #include <apt-pkg/aptconfiguration.h> | |
7 | ||
8 | #include <string> | |
9 | #include <vector> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | ||
13 | #include <gtest/gtest.h> | |
14 | ||
15 | #include "file-helpers.h" | |
16 | ||
17 | static void TestFileFd(mode_t const a_umask, mode_t const ExpectedFilePermission, | |
18 | unsigned int const filemode, APT::Configuration::Compressor const &compressor) | |
19 | { | |
20 | std::string trace; | |
21 | strprintf(trace, "TestFileFd: Compressor: %s umask: %#o permission: %#o mode: %d", compressor.Name.c_str(), a_umask, ExpectedFilePermission, filemode); | |
22 | SCOPED_TRACE(trace); | |
23 | ||
24 | static const char* fname = "apt-filefd-test.txt"; | |
25 | if (FileExists(fname) == true) | |
26 | EXPECT_EQ(0, unlink(fname)); | |
27 | ||
28 | FileFd f; | |
29 | umask(a_umask); | |
30 | EXPECT_TRUE(f.Open(fname, filemode, compressor)); | |
31 | EXPECT_TRUE(f.IsOpen()); | |
32 | EXPECT_FALSE(f.Failed()); | |
33 | EXPECT_EQ(umask(a_umask), a_umask); | |
34 | ||
35 | std::string test = "This is a test!\n"; | |
36 | EXPECT_TRUE(f.Write(test.c_str(), test.size())); | |
37 | EXPECT_TRUE(f.IsOpen()); | |
38 | EXPECT_FALSE(f.Failed()); | |
39 | ||
40 | f.Close(); | |
41 | EXPECT_FALSE(f.IsOpen()); | |
42 | EXPECT_FALSE(f.Failed()); | |
43 | ||
44 | EXPECT_TRUE(f.Open(fname, FileFd::ReadOnly, compressor)); | |
45 | EXPECT_TRUE(f.IsOpen()); | |
46 | EXPECT_FALSE(f.Failed()); | |
47 | EXPECT_FALSE(f.Eof()); | |
48 | EXPECT_NE(0, f.FileSize()); | |
49 | EXPECT_FALSE(f.Failed()); | |
50 | EXPECT_NE(0, f.ModificationTime()); | |
51 | EXPECT_FALSE(f.Failed()); | |
52 | ||
53 | // ensure the memory is as predictably messed up | |
54 | #define APT_INIT_READBACK \ | |
55 | char readback[20]; \ | |
56 | memset(readback, 'D', sizeof(readback)/sizeof(readback[0])); \ | |
57 | readback[19] = '\0'; | |
58 | #define EXPECT_N_STR(expect, actual) \ | |
59 | EXPECT_EQ(0, strncmp(expect, actual, strlen(expect))); | |
60 | ||
61 | { | |
62 | APT_INIT_READBACK | |
63 | char const * const expect = "This"; | |
64 | EXPECT_TRUE(f.Read(readback, strlen(expect))); | |
65 | EXPECT_FALSE(f.Failed()); | |
66 | EXPECT_FALSE(f.Eof()); | |
67 | EXPECT_N_STR(expect, readback); | |
68 | EXPECT_EQ(strlen(expect), f.Tell()); | |
69 | } | |
70 | { | |
71 | APT_INIT_READBACK | |
72 | char const * const expect = "test!\n"; | |
73 | EXPECT_TRUE(f.Skip((test.size() - f.Tell()) - strlen(expect))); | |
74 | EXPECT_TRUE(f.Read(readback, strlen(expect))); | |
75 | EXPECT_FALSE(f.Failed()); | |
76 | EXPECT_FALSE(f.Eof()); | |
77 | EXPECT_N_STR(expect, readback); | |
78 | EXPECT_EQ(test.size(), f.Tell()); | |
79 | } | |
80 | { | |
81 | APT_INIT_READBACK | |
82 | EXPECT_TRUE(f.Seek(0)); | |
83 | EXPECT_FALSE(f.Eof()); | |
84 | EXPECT_TRUE(f.Read(readback, 20, true)); | |
85 | EXPECT_FALSE(f.Failed()); | |
86 | EXPECT_TRUE(f.Eof()); | |
87 | EXPECT_N_STR(test.c_str(), readback); | |
88 | EXPECT_EQ(f.Size(), f.Tell()); | |
89 | } | |
90 | { | |
91 | APT_INIT_READBACK | |
92 | EXPECT_TRUE(f.Seek(0)); | |
93 | EXPECT_FALSE(f.Eof()); | |
94 | EXPECT_TRUE(f.Read(readback, test.size(), true)); | |
95 | EXPECT_FALSE(f.Failed()); | |
96 | EXPECT_FALSE(f.Eof()); | |
97 | EXPECT_N_STR(test.c_str(), readback); | |
98 | EXPECT_EQ(f.Size(), f.Tell()); | |
99 | } | |
100 | { | |
101 | APT_INIT_READBACK | |
102 | EXPECT_TRUE(f.Seek(0)); | |
103 | EXPECT_FALSE(f.Eof()); | |
104 | unsigned long long actual; | |
105 | EXPECT_TRUE(f.Read(readback, 20, &actual)); | |
106 | EXPECT_FALSE(f.Failed()); | |
107 | EXPECT_TRUE(f.Eof()); | |
108 | EXPECT_EQ(test.size(), actual); | |
109 | EXPECT_N_STR(test.c_str(), readback); | |
110 | EXPECT_EQ(f.Size(), f.Tell()); | |
111 | } | |
112 | { | |
113 | APT_INIT_READBACK | |
114 | EXPECT_TRUE(f.Seek(0)); | |
115 | EXPECT_FALSE(f.Eof()); | |
116 | f.ReadLine(readback, 20); | |
117 | EXPECT_FALSE(f.Failed()); | |
118 | EXPECT_FALSE(f.Eof()); | |
119 | EXPECT_EQ(test, readback); | |
120 | EXPECT_EQ(f.Size(), f.Tell()); | |
121 | } | |
122 | { | |
123 | APT_INIT_READBACK | |
124 | EXPECT_TRUE(f.Seek(0)); | |
125 | EXPECT_FALSE(f.Eof()); | |
126 | char const * const expect = "This"; | |
127 | f.ReadLine(readback, strlen(expect) + 1); | |
128 | EXPECT_FALSE(f.Failed()); | |
129 | EXPECT_FALSE(f.Eof()); | |
130 | EXPECT_N_STR(expect, readback); | |
131 | EXPECT_EQ(strlen(expect), f.Tell()); | |
132 | } | |
133 | #undef APT_INIT_READBACK | |
134 | ||
135 | f.Close(); | |
136 | EXPECT_FALSE(f.IsOpen()); | |
137 | EXPECT_FALSE(f.Failed()); | |
138 | ||
139 | // regression test for permission bug LP: #1304657 | |
140 | struct stat buf; | |
141 | EXPECT_EQ(0, stat(fname, &buf)); | |
142 | EXPECT_EQ(0, unlink(fname)); | |
143 | EXPECT_EQ(ExpectedFilePermission, buf.st_mode & 0777); | |
144 | } | |
145 | ||
146 | static void TestFileFd(unsigned int const filemode) | |
147 | { | |
148 | std::vector<APT::Configuration::Compressor> compressors = APT::Configuration::getCompressors(); | |
149 | ||
150 | // testing the (un)compress via pipe, as the 'real' compressors are usually built in via libraries | |
151 | compressors.push_back(APT::Configuration::Compressor("rev", ".reversed", "rev", NULL, NULL, 42)); | |
152 | //compressors.push_back(APT::Configuration::Compressor("cat", ".ident", "cat", NULL, NULL, 42)); | |
153 | ||
154 | for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin(); c != compressors.end(); ++c) | |
155 | { | |
156 | if ((filemode & FileFd::ReadWrite) == FileFd::ReadWrite && | |
157 | (c->Name.empty() != true && c->Binary.empty() != true)) | |
158 | continue; | |
159 | TestFileFd(0002, 0664, filemode, *c); | |
160 | TestFileFd(0022, 0644, filemode, *c); | |
161 | TestFileFd(0077, 0600, filemode, *c); | |
162 | TestFileFd(0026, 0640, filemode, *c); | |
163 | } | |
164 | } | |
165 | ||
166 | TEST(FileUtlTest, FileFD) | |
167 | { | |
168 | std::string const startdir = SafeGetCWD(); | |
169 | EXPECT_FALSE(startdir.empty()); | |
170 | std::string tempdir; | |
171 | createTemporaryDirectory("filefd", tempdir); | |
172 | EXPECT_EQ(0, chdir(tempdir.c_str())); | |
173 | ||
174 | TestFileFd(FileFd::WriteOnly | FileFd::Create); | |
175 | TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Empty); | |
176 | TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive); | |
177 | TestFileFd(FileFd::WriteOnly | FileFd::Atomic); | |
178 | TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Atomic); | |
179 | // short-hands for ReadWrite with these modes | |
180 | TestFileFd(FileFd::WriteEmpty); | |
181 | TestFileFd(FileFd::WriteAny); | |
182 | TestFileFd(FileFd::WriteTemp); | |
183 | TestFileFd(FileFd::WriteAtomic); | |
184 | ||
185 | EXPECT_EQ(0, chdir(startdir.c_str())); | |
186 | removeDirectory(tempdir); | |
187 | } | |
188 | TEST(FileUtlTest, Glob) | |
189 | { | |
190 | std::vector<std::string> files; | |
191 | // normal match | |
192 | files = Glob("*akefile"); | |
193 | EXPECT_EQ(1, files.size()); | |
194 | ||
195 | // not there | |
196 | files = Glob("xxxyyyzzz"); | |
197 | EXPECT_TRUE(files.empty()); | |
198 | EXPECT_FALSE(_error->PendingError()); | |
199 | ||
200 | // many matches (number is a bit random) | |
201 | files = Glob("*.cc"); | |
202 | EXPECT_LT(10, files.size()); | |
203 | } | |
204 | TEST(FileUtlTest, GetTempDir) | |
205 | { | |
206 | char const * const envtmp = getenv("TMPDIR"); | |
207 | std::string old_tmpdir; | |
208 | if (envtmp != NULL) | |
209 | old_tmpdir = envtmp; | |
210 | ||
211 | unsetenv("TMPDIR"); | |
212 | EXPECT_EQ("/tmp", GetTempDir()); | |
213 | ||
214 | setenv("TMPDIR", "", 1); | |
215 | EXPECT_EQ("/tmp", GetTempDir()); | |
216 | ||
217 | setenv("TMPDIR", "/not-there-no-really-not", 1); | |
218 | EXPECT_EQ("/tmp", GetTempDir()); | |
219 | ||
220 | setenv("TMPDIR", "/usr", 1); | |
221 | EXPECT_EQ("/usr", GetTempDir()); | |
222 | ||
223 | unsetenv("TMPDIR"); | |
224 | if (old_tmpdir.empty() == false) | |
225 | setenv("TMPDIR", old_tmpdir.c_str(), 1); | |
226 | } | |
227 | TEST(FileUtlTest, Popen) | |
228 | { | |
229 | FileFd Fd; | |
230 | pid_t Child; | |
231 | char buf[1024]; | |
232 | std::string s; | |
233 | unsigned long long n = 0; | |
234 | std::vector<std::string> OpenFds; | |
235 | ||
236 | // count Fds to ensure we don't have a resource leak | |
237 | if(FileExists("/proc/self/fd")) | |
238 | OpenFds = Glob("/proc/self/fd/*"); | |
239 | ||
240 | // output something | |
241 | const char* Args[10] = {"/bin/echo", "meepmeep", NULL}; | |
242 | bool res = Popen(Args, Fd, Child, FileFd::ReadOnly); | |
243 | Fd.Read(buf, sizeof(buf)-1, &n); | |
244 | buf[n] = 0; | |
245 | EXPECT_NE(n, 0); | |
246 | EXPECT_EQ(res, true); | |
247 | EXPECT_STREQ(buf, "meepmeep\n"); | |
248 | ||
249 | // wait for the child to exit and cleanup | |
250 | ExecWait(Child, "PopenRead"); | |
251 | Fd.Close(); | |
252 | ||
253 | // ensure that after a close all is good again | |
254 | if(FileExists("/proc/self/fd")) | |
255 | EXPECT_EQ(Glob("/proc/self/fd/*").size(), OpenFds.size()); | |
256 | ||
257 | ||
258 | // ReadWrite is not supported | |
259 | res = Popen(Args, Fd, Child, FileFd::ReadWrite); | |
260 | EXPECT_EQ(res, false); | |
261 | _error->Discard(); | |
262 | ||
263 | // write something | |
264 | Args[0] = "/bin/bash"; | |
265 | Args[1] = "-c"; | |
266 | Args[2] = "read"; | |
267 | Args[3] = NULL; | |
268 | res = Popen(Args, Fd, Child, FileFd::WriteOnly); | |
269 | s = "\n"; | |
270 | Fd.Write(s.c_str(), s.size()); | |
271 | Fd.Close(); | |
272 | ExecWait(Child, "PopenWrite"); | |
273 | } | |
274 | TEST(FileUtlTest, flAbsPath) | |
275 | { | |
276 | std::string cwd = SafeGetCWD(); | |
277 | int res = chdir("/bin/"); | |
278 | EXPECT_EQ(res, 0); | |
279 | std::string p = flAbsPath("ls"); | |
280 | EXPECT_EQ(p, "/bin/ls"); | |
281 | ||
282 | res = chdir(cwd.c_str()); | |
283 | EXPECT_EQ(res, 0); | |
284 | } |