FileUtilTest.cpp 16 KB

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