Futex::futexWait returns FutexResult
[folly.git] / folly / test / FileUtilTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/FileUtil.h>
18 #include <folly/detail/FileUtilDetail.h>
19 #include <folly/experimental/TestUtil.h>
20
21 #include <deque>
22 #if defined(__linux__)
23 #include <dlfcn.h>
24 #endif
25
26 #include <glog/logging.h>
27
28 #include <folly/Exception.h>
29 #include <folly/File.h>
30 #include <folly/Range.h>
31 #include <folly/String.h>
32 #include <folly/portability/GTest.h>
33
34 namespace folly { namespace test {
35
36 using namespace fileutil_detail;
37 using namespace std;
38
39 namespace {
40
41 class Reader {
42  public:
43   Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec);
44
45   // write-like
46   ssize_t operator()(int fd, void* buf, size_t count);
47
48   // pwrite-like
49   ssize_t operator()(int fd, void* buf, size_t count, off_t offset);
50
51   // writev-like
52   ssize_t operator()(int fd, const iovec* iov, int count);
53
54   // pwritev-like
55   ssize_t operator()(int fd, const iovec* iov, int count, off_t offset);
56
57   const std::deque<ssize_t> spec() const { return spec_; }
58
59  private:
60   ssize_t nextSize();
61
62   off_t offset_;
63   StringPiece data_;
64   std::deque<ssize_t> spec_;
65 };
66
67 Reader::Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec)
68   : offset_(offset),
69     data_(data),
70     spec_(std::move(spec)) {
71 }
72
73 ssize_t Reader::nextSize() {
74   if (spec_.empty()) {
75     throw std::runtime_error("spec empty");
76   }
77   ssize_t n = spec_.front();
78   spec_.pop_front();
79   if (n <= 0) {
80     if (n == -1) {
81       errno = EIO;
82     }
83     spec_.clear();  // so we fail if called again
84   } else {
85     offset_ += n;
86   }
87   return n;
88 }
89
90 ssize_t Reader::operator()(int /* fd */, void* buf, size_t count) {
91   ssize_t n = nextSize();
92   if (n <= 0) {
93     return n;
94   }
95   if (size_t(n) > count) {
96     throw std::runtime_error("requested count too small");
97   }
98   memcpy(buf, data_.data(), n);
99   data_.advance(n);
100   return n;
101 }
102
103 ssize_t Reader::operator()(int fd, void* buf, size_t count, off_t offset) {
104   EXPECT_EQ(offset_, offset);
105   return operator()(fd, buf, count);
106 }
107
108 ssize_t Reader::operator()(int /* fd */, const iovec* iov, int count) {
109   ssize_t n = nextSize();
110   if (n <= 0) {
111     return n;
112   }
113   ssize_t remaining = n;
114   for (; count != 0 && remaining != 0; ++iov, --count) {
115     ssize_t len = std::min(remaining, ssize_t(iov->iov_len));
116     memcpy(iov->iov_base, data_.data(), len);
117     data_.advance(len);
118     remaining -= len;
119   }
120   if (remaining != 0) {
121     throw std::runtime_error("requested total size too small");
122   }
123   return n;
124 }
125
126 ssize_t Reader::operator()(int fd, const iovec* iov, int count, off_t offset) {
127   EXPECT_EQ(offset_, offset);
128   return operator()(fd, iov, count);
129 }
130
131 } // namespace
132
133 class FileUtilTest : public ::testing::Test {
134  protected:
135   FileUtilTest();
136
137   Reader reader(std::deque<ssize_t> spec);
138
139   std::string in_;
140   std::vector<std::pair<size_t, Reader>> readers_;
141 };
142
143 FileUtilTest::FileUtilTest()
144   : in_("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
145   CHECK_EQ(62, in_.size());
146
147   readers_.emplace_back(0, reader({0}));
148   readers_.emplace_back(62, reader({62}));
149   readers_.emplace_back(62, reader({62, -1}));  // error after end (not called)
150   readers_.emplace_back(61, reader({61, 0}));
151   readers_.emplace_back(-1, reader({61, -1}));  // error before end
152   readers_.emplace_back(62, reader({31, 31}));
153   readers_.emplace_back(62, reader({1, 10, 20, 10, 1, 20}));
154   readers_.emplace_back(61, reader({1, 10, 20, 10, 20, 0}));
155   readers_.emplace_back(41, reader({1, 10, 20, 10, 0}));
156   readers_.emplace_back(-1, reader({1, 10, 20, 10, 20, -1}));
157 }
158
159 Reader FileUtilTest::reader(std::deque<ssize_t> spec) {
160   return Reader(42, in_, std::move(spec));
161 }
162
163 TEST_F(FileUtilTest, read) {
164   for (auto& p : readers_) {
165     std::string out(in_.size(), '\0');
166     EXPECT_EQ(p.first, wrapFull(p.second, 0, &out[0], out.size()));
167     if (p.first != (decltype(p.first))(-1)) {
168       EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
169     }
170   }
171 }
172
173 TEST_F(FileUtilTest, pread) {
174   for (auto& p : readers_) {
175     std::string out(in_.size(), '\0');
176     EXPECT_EQ(p.first, wrapFull(p.second, 0, &out[0], out.size(), off_t(42)));
177     if (p.first != (decltype(p.first))(-1)) {
178       EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
179     }
180   }
181 }
182
183 class IovecBuffers {
184  public:
185   explicit IovecBuffers(std::initializer_list<size_t> sizes);
186   explicit IovecBuffers(std::vector<size_t> sizes);
187
188   std::vector<iovec> iov() const { return iov_; }  // yes, make a copy
189   std::string join() const { return folly::join("", buffers_); }
190   size_t size() const;
191
192  private:
193   std::vector<std::string> buffers_;
194   std::vector<iovec> iov_;
195 };
196
197 IovecBuffers::IovecBuffers(std::initializer_list<size_t> sizes) {
198   iov_.reserve(sizes.size());
199   for (auto& s : sizes) {
200     buffers_.push_back(std::string(s, '\0'));
201   }
202   for (auto& b : buffers_) {
203     iovec iov;
204     iov.iov_base = &b[0];
205     iov.iov_len = b.size();
206     iov_.push_back(iov);
207   }
208 }
209
210 IovecBuffers::IovecBuffers(std::vector<size_t> sizes) {
211   iov_.reserve(sizes.size());
212   for (auto s : sizes) {
213     buffers_.push_back(std::string(s, '\0'));
214   }
215   for (auto& b : buffers_) {
216     iovec iov;
217     iov.iov_base = &b[0];
218     iov.iov_len = b.size();
219     iov_.push_back(iov);
220   }
221 }
222
223 size_t IovecBuffers::size() const {
224   size_t s = 0;
225   for (auto& b : buffers_) {
226     s += b.size();
227   }
228   return s;
229 }
230
231 TEST_F(FileUtilTest, readv) {
232   for (auto& p : readers_) {
233     IovecBuffers buf({12, 19, 31});
234     ASSERT_EQ(62, buf.size());
235
236     auto iov = buf.iov();
237     EXPECT_EQ(p.first, wrapvFull(p.second, 0, iov.data(), iov.size()));
238     if (p.first != (decltype(p.first))(-1)) {
239       EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
240     }
241   }
242 }
243
244 TEST(FileUtilTest2, wrapv) {
245   TemporaryFile tempFile("file-util-test");
246   std::vector<size_t> sizes;
247   size_t sum = 0;
248   for (int32_t i = 0; i < 1500; ++i) {
249     sizes.push_back(i % 3 + 1);
250     sum += sizes.back();
251   }
252   IovecBuffers buf(sizes);
253   ASSERT_EQ(sum, buf.size());
254   auto iov = buf.iov();
255   EXPECT_EQ(sum, wrapvFull(writev, tempFile.fd(), iov.data(), iov.size()));
256 }
257
258 TEST_F(FileUtilTest, preadv) {
259   for (auto& p : readers_) {
260     IovecBuffers buf({12, 19, 31});
261     ASSERT_EQ(62, buf.size());
262
263     auto iov = buf.iov();
264     EXPECT_EQ(p.first,
265               wrapvFull(p.second, 0, iov.data(), iov.size(), off_t(42)));
266     if (p.first != (decltype(p.first))(-1)) {
267       EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
268     }
269   }
270 }
271
272 TEST(String, readFile) {
273   const TemporaryFile afileTemp, emptyFileTemp;
274   auto afile = afileTemp.path().string();
275   auto emptyFile = emptyFileTemp.path().string();
276
277   EXPECT_TRUE(writeFile(string(), emptyFile.c_str()));
278   EXPECT_TRUE(writeFile(StringPiece("bar"), afile.c_str()));
279
280   {
281     string contents;
282     EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
283     EXPECT_EQ(contents, "");
284     EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
285     EXPECT_EQ("", contents);
286     EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
287     EXPECT_EQ("ba", contents);
288     EXPECT_TRUE(readFile(afile.c_str(), contents));
289     EXPECT_EQ("bar", contents);
290   }
291   {
292     vector<unsigned char> contents;
293     EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
294     EXPECT_EQ(vector<unsigned char>(), contents);
295     EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
296     EXPECT_EQ(vector<unsigned char>(), contents);
297     EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
298     EXPECT_EQ(vector<unsigned char>({'b', 'a'}), contents);
299     EXPECT_TRUE(readFile(afile.c_str(), contents));
300     EXPECT_EQ(vector<unsigned char>({'b', 'a', 'r'}), contents);
301   }
302 }
303
304 class ReadFileFd : public ::testing::Test {
305  protected:
306   void SetUp() override {
307     ASSERT_TRUE(writeFile(StringPiece("bar"), aFile.path().string().c_str()));
308   }
309
310   TemporaryFile aFile;
311 };
312
313 TEST_F(ReadFileFd, ReadZeroBytes) {
314   std::string contents;
315   EXPECT_TRUE(readFile(aFile.fd(), contents, 0));
316   EXPECT_EQ("", contents);
317 }
318
319 TEST_F(ReadFileFd, ReadPartial) {
320   std::string contents;
321   EXPECT_TRUE(readFile(aFile.fd(), contents, 2));
322   EXPECT_EQ("ba", contents);
323 }
324
325 TEST_F(ReadFileFd, ReadFull) {
326   std::string contents;
327   EXPECT_TRUE(readFile(aFile.fd(), contents));
328   EXPECT_EQ("bar", contents);
329 }
330
331 TEST_F(ReadFileFd, WriteOnlyFd) {
332   File f(aFile.path().string(), O_WRONLY);
333   std::string contents;
334   EXPECT_FALSE(readFile(f.fd(), contents));
335   PLOG(INFO);
336 }
337
338 TEST_F(ReadFileFd, InvalidFd) {
339   File f(aFile.path().string());
340   f.close();
341   std::string contents;
342   msvcSuppressAbortOnInvalidParams([&] {
343     EXPECT_FALSE(readFile(f.fd(), contents));
344   });
345   PLOG(INFO);
346 }
347
348 class WriteFileAtomic : public ::testing::Test {
349  protected:
350   WriteFileAtomic() {}
351
352   std::set<std::string> listTmpDir() const {
353     std::set<std::string> entries;
354     for (auto& entry : fs::directory_iterator(tmpDir_.path())) {
355       entries.insert(entry.path().filename().string());
356     }
357     return entries;
358   }
359
360   std::string readData(const string& path) const {
361     string data;
362     if (!readFile(path.c_str(), data)) {
363       throwSystemError("failed to read ", path);
364     }
365     return data;
366   }
367
368   struct stat statFile(const string& path) const {
369     struct stat s;
370     auto rc = stat(path.c_str(), &s);
371     checkUnixError(rc, "failed to stat() ", path);
372     return s;
373   }
374
375   mode_t getPerms(const string& path) {
376     return (statFile(path).st_mode & 0777);
377   }
378
379   string tmpPath(StringPiece name) {
380     return tmpDir_.path().string() + "/" + name.str();
381   }
382
383   void setDirPerms(mode_t mode) {
384     auto rc = chmod(tmpDir_.path().string().c_str(), mode);
385     checkUnixError(rc, "failed to set permissions on tmp dir");
386   }
387
388   TemporaryDirectory tmpDir_{"folly_file_test"};
389 };
390
391 TEST_F(WriteFileAtomic, writeNew) {
392   // Call writeFileAtomic() to create a new file
393   auto path = tmpPath("foo");
394   auto contents = StringPiece{"contents\n"};
395   writeFileAtomic(path, contents);
396
397   // The directory should contain exactly 1 file now, with the correct contents
398   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
399   EXPECT_EQ(contents, readData(path));
400   EXPECT_EQ(0644, getPerms(path));
401 }
402
403 TEST_F(WriteFileAtomic, overwrite) {
404   // Call writeFileAtomic() to create a new file
405   auto path = tmpPath("foo");
406   auto contents1 = StringPiece{"contents\n"};
407   writeFileAtomic(path, contents1);
408
409   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
410   EXPECT_EQ(contents1, readData(path));
411   EXPECT_EQ(0644, getPerms(path));
412
413   // Now overwrite the file with different contents
414   auto contents2 = StringPiece{"testing"};
415   writeFileAtomic(path, contents2);
416   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
417   EXPECT_EQ(contents2, readData(path));
418   EXPECT_EQ(0644, getPerms(path));
419
420   // Test overwriting with relatively large contents, and different permissions
421   auto contents3 =
422       "asdf" + string(10240, '\n') + "foobar\n" + string(10240, 'b') + "\n";
423   writeFileAtomic(path, contents3, 0444);
424   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
425   EXPECT_EQ(contents3, readData(path));
426   EXPECT_EQ(0444, getPerms(path));
427
428   // Test overwriting with empty contents
429   //
430   // Note that the file's permissions are 0444 at this point (read-only),
431   // but we writeFileAtomic() should still replace it successfully.  Since we
432   // update it with a rename we need write permissions on the parent directory,
433   // but not the destination file.
434   auto contents4 = StringPiece("");
435   writeFileAtomic(path, contents4, 0400);
436   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
437   EXPECT_EQ(contents4, readData(path));
438   EXPECT_EQ(0400, getPerms(path));
439 }
440
441 TEST_F(WriteFileAtomic, directoryPermissions) {
442   // Test writeFileAtomic() when we do not have write permission in the target
443   // directory.
444   //
445   // Make the test directory read-only
446   setDirPerms(0555);
447   SCOPE_EXIT {
448     // Restore directory permissions before we exit, just to ensure the code
449     // will be able to clean up the directory.
450     try {
451       setDirPerms(0755);
452     } catch (const std::exception&) {
453       // Intentionally ignore errors here, in case an exception is already
454       // being thrown.
455     }
456   };
457
458   // writeFileAtomic() should fail, and the directory should still be empty
459   auto path1 = tmpPath("foo");
460   auto contents = StringPiece("testing");
461   EXPECT_THROW(writeFileAtomic(path1, contents), std::system_error);
462   EXPECT_EQ(set<string>{}, listTmpDir());
463
464   // Make the directory writable again, then create the file
465   setDirPerms(0755);
466   writeFileAtomic(path1, contents, 0400);
467   EXPECT_EQ(contents, readData(path1));
468   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
469
470   // Make the directory read-only again
471   // Creating another file now should fail and we should still have only the
472   // first file.
473   setDirPerms(0555);
474   EXPECT_THROW(
475       writeFileAtomic(tmpPath("another_file.txt"), "x\n"), std::system_error);
476   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
477 }
478
479 TEST_F(WriteFileAtomic, multipleFiles) {
480   // Test creating multiple files in the same directory
481   writeFileAtomic(tmpPath("foo.txt"), "foo");
482   writeFileAtomic(tmpPath("bar.txt"), "bar", 0400);
483   writeFileAtomic(tmpPath("foo_txt"), "underscore", 0440);
484   writeFileAtomic(tmpPath("foo.txt2"), "foo2", 0444);
485
486   auto expectedPaths = set<string>{"foo.txt", "bar.txt", "foo_txt", "foo.txt2"};
487   EXPECT_EQ(expectedPaths, listTmpDir());
488   EXPECT_EQ("foo", readData(tmpPath("foo.txt")));
489   EXPECT_EQ("bar", readData(tmpPath("bar.txt")));
490   EXPECT_EQ("underscore", readData(tmpPath("foo_txt")));
491   EXPECT_EQ("foo2", readData(tmpPath("foo.txt2")));
492   EXPECT_EQ(0644, getPerms(tmpPath("foo.txt")));
493   EXPECT_EQ(0400, getPerms(tmpPath("bar.txt")));
494   EXPECT_EQ(0440, getPerms(tmpPath("foo_txt")));
495   EXPECT_EQ(0444, getPerms(tmpPath("foo.txt2")));
496 }
497 } // namespace test
498 } // namespace folly
499
500 #if defined(__linux__)
501 namespace {
502 /**
503  * A helper class that forces our fchmod() wrapper to fail when
504  * an FChmodFailure object exists.
505  */
506 class FChmodFailure {
507  public:
508   FChmodFailure() {
509     ++forceFailure_;
510   }
511   ~FChmodFailure() {
512     --forceFailure_;
513   }
514
515   static bool shouldFail() {
516     return forceFailure_.load() > 0;
517   }
518
519  private:
520   static std::atomic<int> forceFailure_;
521 };
522
523 std::atomic<int> FChmodFailure::forceFailure_{0};
524 } // namespace
525
526 // Replace the system fchmod() function with our own stub, so we can
527 // trigger failures in the writeFileAtomic() tests.
528 int fchmod(int fd, mode_t mode) {
529   static const auto realFunction =
530       reinterpret_cast<int (*)(int, mode_t)>(dlsym(RTLD_NEXT, "fchmod"));
531   // For sanity, make sure we didn't find ourself,
532   // since that would cause infinite recursion.
533   CHECK_NE(realFunction, fchmod);
534
535   if (FChmodFailure::shouldFail()) {
536     errno = EINVAL;
537     return -1;
538   }
539   return realFunction(fd, mode);
540 }
541
542 namespace folly {
543 namespace test {
544 TEST_F(WriteFileAtomic, chmodFailure) {
545   auto path = tmpPath("foo");
546
547   // Use our stubbed out fchmod() function to force a failure when setting up
548   // the temporary file.
549   //
550   // First try when creating the file for the first time.
551   {
552     FChmodFailure fail;
553     EXPECT_THROW(writeFileAtomic(path, "foobar"), std::system_error);
554   }
555   EXPECT_EQ(set<string>{}, listTmpDir());
556
557   // Now create a file normally so we can overwrite it
558   auto contents = StringPiece("regular perms");
559   writeFileAtomic(path, contents, 0600);
560   EXPECT_EQ(contents, readData(path));
561   EXPECT_EQ(0600, getPerms(path));
562   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
563
564   // Now try overwriting the file when forcing fchmod to fail
565   {
566     FChmodFailure fail;
567     EXPECT_THROW(writeFileAtomic(path, "overwrite"), std::system_error);
568   }
569   // The file should be unchanged
570   EXPECT_EQ(contents, readData(path));
571   EXPECT_EQ(0600, getPerms(path));
572   EXPECT_EQ(set<string>{"foo"}, listTmpDir());
573 }
574 } // namespace test
575 } // namespace folly
576 #endif