1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "brillo/file_utils.h"
6
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 #include <string>
12
13 #include <base/files/file_util.h>
14 #include <base/files/scoped_temp_dir.h>
15 #include <base/rand_util.h>
16 #include <base/stl_util.h>
17 #include <base/strings/string_number_conversions.h>
18 #include <gtest/gtest.h>
19
20 namespace brillo {
21
22 namespace {
23
24 constexpr int kPermissions600 =
25 base::FILE_PERMISSION_READ_BY_USER | base::FILE_PERMISSION_WRITE_BY_USER;
26 constexpr int kPermissions700 = base::FILE_PERMISSION_USER_MASK;
27 constexpr int kPermissions777 = base::FILE_PERMISSION_MASK;
28 constexpr int kPermissions755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
29
GetRandomSuffix()30 std::string GetRandomSuffix() {
31 const int kBufferSize = 6;
32 unsigned char buffer[kBufferSize];
33 base::RandBytes(buffer, base::size(buffer));
34 return base::HexEncode(buffer, base::size(buffer));
35 }
36
IsNonBlockingFD(int fd)37 bool IsNonBlockingFD(int fd) {
38 return fcntl(fd, F_GETFL) & O_NONBLOCK;
39 }
40
41 } // namespace
42
43 class FileUtilsTest : public testing::Test {
44 public:
FileUtilsTest()45 FileUtilsTest() {
46 CHECK(temp_dir_.CreateUniqueTempDir());
47 file_path_ = temp_dir_.GetPath().Append("test.temp");
48 }
49
50 protected:
51 base::FilePath file_path_;
52 base::ScopedTempDir temp_dir_;
53
54 // Writes |contents| to |file_path_|. Pulled into a separate function just
55 // to improve readability of tests.
WriteFile(const std::string & contents)56 void WriteFile(const std::string& contents) {
57 EXPECT_EQ(contents.length(),
58 base::WriteFile(file_path_, contents.c_str(), contents.length()));
59 }
60
61 // Verifies that the file at |file_path_| exists and contains |contents|.
ExpectFileContains(const std::string & contents)62 void ExpectFileContains(const std::string& contents) {
63 EXPECT_TRUE(base::PathExists(file_path_));
64 std::string new_contents;
65 EXPECT_TRUE(base::ReadFileToString(file_path_, &new_contents));
66 EXPECT_EQ(contents, new_contents);
67 }
68
69 // Verifies that the file at |file_path_| has |permissions|.
ExpectFilePermissions(int permissions)70 void ExpectFilePermissions(int permissions) {
71 int actual_permissions;
72 EXPECT_TRUE(base::GetPosixFilePermissions(file_path_, &actual_permissions));
73 EXPECT_EQ(permissions, actual_permissions);
74 }
75
76 // Creates a file with a random name in the temporary directory.
GetTempName()77 base::FilePath GetTempName() {
78 return temp_dir_.GetPath().Append(GetRandomSuffix());
79 }
80 };
81
TEST_F(FileUtilsTest,TouchFileCreate)82 TEST_F(FileUtilsTest, TouchFileCreate) {
83 EXPECT_TRUE(TouchFile(file_path_));
84 ExpectFileContains("");
85 ExpectFilePermissions(kPermissions600);
86 }
87
TEST_F(FileUtilsTest,TouchFileCreateThroughUmask)88 TEST_F(FileUtilsTest, TouchFileCreateThroughUmask) {
89 mode_t old_umask = umask(kPermissions777);
90 EXPECT_TRUE(TouchFile(file_path_));
91 umask(old_umask);
92 ExpectFileContains("");
93 ExpectFilePermissions(kPermissions600);
94 }
95
TEST_F(FileUtilsTest,TouchFileCreateDirectoryStructure)96 TEST_F(FileUtilsTest, TouchFileCreateDirectoryStructure) {
97 file_path_ = temp_dir_.GetPath().Append("foo/bar/baz/test.temp");
98 EXPECT_TRUE(TouchFile(file_path_));
99 ExpectFileContains("");
100 }
101
TEST_F(FileUtilsTest,TouchFileExisting)102 TEST_F(FileUtilsTest, TouchFileExisting) {
103 WriteFile("abcd");
104 EXPECT_TRUE(TouchFile(file_path_));
105 ExpectFileContains("abcd");
106 }
107
TEST_F(FileUtilsTest,TouchFileReplaceDirectory)108 TEST_F(FileUtilsTest, TouchFileReplaceDirectory) {
109 EXPECT_TRUE(base::CreateDirectory(file_path_));
110 EXPECT_TRUE(TouchFile(file_path_));
111 EXPECT_FALSE(base::DirectoryExists(file_path_));
112 ExpectFileContains("");
113 }
114
TEST_F(FileUtilsTest,TouchFileReplaceSymlink)115 TEST_F(FileUtilsTest, TouchFileReplaceSymlink) {
116 base::FilePath symlink_target = temp_dir_.GetPath().Append("target.temp");
117 EXPECT_TRUE(base::CreateSymbolicLink(symlink_target, file_path_));
118 EXPECT_TRUE(TouchFile(file_path_));
119 EXPECT_FALSE(base::IsLink(file_path_));
120 ExpectFileContains("");
121 }
122
TEST_F(FileUtilsTest,TouchFileReplaceOtherUser)123 TEST_F(FileUtilsTest, TouchFileReplaceOtherUser) {
124 WriteFile("abcd");
125 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid() + 1, getegid()));
126 ExpectFileContains("");
127 }
128
TEST_F(FileUtilsTest,TouchFileReplaceOtherGroup)129 TEST_F(FileUtilsTest, TouchFileReplaceOtherGroup) {
130 WriteFile("abcd");
131 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid() + 1));
132 ExpectFileContains("");
133 }
134
TEST_F(FileUtilsTest,TouchFileCreateWithAllPermissions)135 TEST_F(FileUtilsTest, TouchFileCreateWithAllPermissions) {
136 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid()));
137 ExpectFileContains("");
138 ExpectFilePermissions(kPermissions777);
139 }
140
TEST_F(FileUtilsTest,TouchFileCreateWithOwnerPermissions)141 TEST_F(FileUtilsTest, TouchFileCreateWithOwnerPermissions) {
142 EXPECT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
143 ExpectFileContains("");
144 ExpectFilePermissions(kPermissions700);
145 }
146
TEST_F(FileUtilsTest,TouchFileExistingPermissionsUnchanged)147 TEST_F(FileUtilsTest, TouchFileExistingPermissionsUnchanged) {
148 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid()));
149 EXPECT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
150 ExpectFileContains("");
151 ExpectFilePermissions(kPermissions777);
152 }
153
154 // Other parts of OpenSafely are tested in Arcsetup.TestInstallDirectory*.
TEST_F(FileUtilsTest,TestOpenSafelyWithoutNonblocking)155 TEST_F(FileUtilsTest, TestOpenSafelyWithoutNonblocking) {
156 ASSERT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
157 base::ScopedFD fd(OpenSafely(file_path_, O_RDONLY, 0));
158 EXPECT_TRUE(fd.is_valid());
159 EXPECT_FALSE(IsNonBlockingFD(fd.get()));
160 }
161
TEST_F(FileUtilsTest,TestOpenSafelyWithNonblocking)162 TEST_F(FileUtilsTest, TestOpenSafelyWithNonblocking) {
163 ASSERT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
164 base::ScopedFD fd = OpenSafely(file_path_, O_RDONLY | O_NONBLOCK, 0);
165 EXPECT_TRUE(fd.is_valid());
166 EXPECT_TRUE(IsNonBlockingFD(fd.get()));
167 }
168
TEST_F(FileUtilsTest,TestOpenFifoSafelySuccess)169 TEST_F(FileUtilsTest, TestOpenFifoSafelySuccess) {
170 ASSERT_EQ(0, mkfifo(file_path_.value().c_str(), kPermissions700));
171 base::ScopedFD fd(OpenFifoSafely(file_path_, O_RDONLY, 0));
172 EXPECT_TRUE(fd.is_valid());
173 EXPECT_FALSE(IsNonBlockingFD(fd.get()));
174 }
175
TEST_F(FileUtilsTest,TestOpenFifoSafelyRegularFile)176 TEST_F(FileUtilsTest, TestOpenFifoSafelyRegularFile) {
177 ASSERT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
178 base::ScopedFD fd = OpenFifoSafely(file_path_, O_RDONLY, 0);
179 EXPECT_FALSE(fd.is_valid());
180 }
181
TEST_F(FileUtilsTest,TestMkdirRecursivelyRoot)182 TEST_F(FileUtilsTest, TestMkdirRecursivelyRoot) {
183 // Try to create an existing directory ("/") should still succeed.
184 EXPECT_TRUE(
185 MkdirRecursively(base::FilePath("/"), kPermissions755).is_valid());
186 }
187
TEST_F(FileUtilsTest,TestMkdirRecursivelySuccess)188 TEST_F(FileUtilsTest, TestMkdirRecursivelySuccess) {
189 // Set |temp_directory| to 0707.
190 EXPECT_TRUE(base::SetPosixFilePermissions(temp_dir_.GetPath(), 0707));
191
192 EXPECT_TRUE(
193 MkdirRecursively(temp_dir_.GetPath().Append("a/b/c"), kPermissions755)
194 .is_valid());
195 // Confirm the 3 directories are there.
196 EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a")));
197 EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a/b")));
198 EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a/b/c")));
199
200 // Confirm that the newly created directories have 0755 mode.
201 int mode = 0;
202 EXPECT_TRUE(
203 base::GetPosixFilePermissions(temp_dir_.GetPath().Append("a"), &mode));
204 EXPECT_EQ(kPermissions755, mode);
205 mode = 0;
206 EXPECT_TRUE(
207 base::GetPosixFilePermissions(temp_dir_.GetPath().Append("a/b"), &mode));
208 EXPECT_EQ(kPermissions755, mode);
209 mode = 0;
210 EXPECT_TRUE(base::GetPosixFilePermissions(temp_dir_.GetPath().Append("a/b/c"),
211 &mode));
212 EXPECT_EQ(kPermissions755, mode);
213
214 // Confirm that the existing directory |temp_directory| still has 0707 mode.
215 mode = 0;
216 EXPECT_TRUE(base::GetPosixFilePermissions(temp_dir_.GetPath(), &mode));
217 EXPECT_EQ(0707, mode);
218
219 // Call the API again which should still succeed.
220 EXPECT_TRUE(
221 MkdirRecursively(temp_dir_.GetPath().Append("a/b/c"), kPermissions755)
222 .is_valid());
223 EXPECT_TRUE(
224 MkdirRecursively(temp_dir_.GetPath().Append("a/b/c/d"), kPermissions755)
225 .is_valid());
226 EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a/b/c/d")));
227 mode = 0;
228 EXPECT_TRUE(base::GetPosixFilePermissions(
229 temp_dir_.GetPath().Append("a/b/c/d"), &mode));
230 EXPECT_EQ(kPermissions755, mode);
231
232 // Call the API again which should still succeed.
233 EXPECT_TRUE(
234 MkdirRecursively(temp_dir_.GetPath().Append("a/b"), kPermissions755)
235 .is_valid());
236 EXPECT_TRUE(MkdirRecursively(temp_dir_.GetPath().Append("a"), kPermissions755)
237 .is_valid());
238 }
239
TEST_F(FileUtilsTest,TestMkdirRecursivelyRelativePath)240 TEST_F(FileUtilsTest, TestMkdirRecursivelyRelativePath) {
241 // Try to pass a relative or empty directory. They should all fail.
242 EXPECT_FALSE(
243 MkdirRecursively(base::FilePath("foo"), kPermissions755).is_valid());
244 EXPECT_FALSE(
245 MkdirRecursively(base::FilePath("bar/"), kPermissions755).is_valid());
246 EXPECT_FALSE(MkdirRecursively(base::FilePath(), kPermissions755).is_valid());
247 }
248
TEST_F(FileUtilsTest,WriteFileCanBeReadBack)249 TEST_F(FileUtilsTest, WriteFileCanBeReadBack) {
250 const base::FilePath filename(GetTempName());
251 const std::string content("blablabla");
252 EXPECT_TRUE(WriteStringToFile(filename, content));
253 std::string output;
254 EXPECT_TRUE(ReadFileToString(filename, &output));
255 EXPECT_EQ(content, output);
256 }
257
TEST_F(FileUtilsTest,WriteFileSets0666)258 TEST_F(FileUtilsTest, WriteFileSets0666) {
259 const mode_t mask = 0000;
260 const mode_t mode = 0666;
261 const base::FilePath filename(GetTempName());
262 const std::string content("blablabla");
263 const mode_t old_mask = umask(mask);
264 EXPECT_TRUE(WriteStringToFile(filename, content));
265 int file_mode = 0;
266 EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
267 EXPECT_EQ(mode & ~mask, file_mode & 0777);
268 umask(old_mask);
269 }
270
TEST_F(FileUtilsTest,WriteFileCreatesMissingParentDirectoriesWith0700)271 TEST_F(FileUtilsTest, WriteFileCreatesMissingParentDirectoriesWith0700) {
272 const mode_t mask = 0000;
273 const mode_t mode = 0700;
274 const base::FilePath dirname(GetTempName());
275 const base::FilePath subdirname(dirname.Append(GetRandomSuffix()));
276 const base::FilePath filename(subdirname.Append(GetRandomSuffix()));
277 const std::string content("blablabla");
278 EXPECT_TRUE(WriteStringToFile(filename, content));
279 int dir_mode = 0;
280 int subdir_mode = 0;
281 EXPECT_TRUE(base::GetPosixFilePermissions(dirname, &dir_mode));
282 EXPECT_TRUE(base::GetPosixFilePermissions(subdirname, &subdir_mode));
283 EXPECT_EQ(mode & ~mask, dir_mode & 0777);
284 EXPECT_EQ(mode & ~mask, subdir_mode & 0777);
285 const mode_t old_mask = umask(mask);
286 umask(old_mask);
287 }
288
TEST_F(FileUtilsTest,WriteToFileAtomicCanBeReadBack)289 TEST_F(FileUtilsTest, WriteToFileAtomicCanBeReadBack) {
290 const base::FilePath filename(GetTempName());
291 const std::string content("blablabla");
292 EXPECT_TRUE(
293 WriteToFileAtomic(filename, content.data(), content.size(), 0644));
294 std::string output;
295 EXPECT_TRUE(ReadFileToString(filename, &output));
296 EXPECT_EQ(content, output);
297 }
298
TEST_F(FileUtilsTest,WriteToFileAtomicHonorsMode)299 TEST_F(FileUtilsTest, WriteToFileAtomicHonorsMode) {
300 const mode_t mask = 0000;
301 const mode_t mode = 0616;
302 const base::FilePath filename(GetTempName());
303 const std::string content("blablabla");
304 const mode_t old_mask = umask(mask);
305 EXPECT_TRUE(
306 WriteToFileAtomic(filename, content.data(), content.size(), mode));
307 int file_mode = 0;
308 EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
309 EXPECT_EQ(mode & ~mask, file_mode & 0777);
310 umask(old_mask);
311 }
312
TEST_F(FileUtilsTest,WriteToFileAtomicHonorsUmask)313 TEST_F(FileUtilsTest, WriteToFileAtomicHonorsUmask) {
314 const mode_t mask = 0073;
315 const mode_t mode = 0777;
316 const base::FilePath filename(GetTempName());
317 const std::string content("blablabla");
318 const mode_t old_mask = umask(mask);
319 EXPECT_TRUE(
320 WriteToFileAtomic(filename, content.data(), content.size(), mode));
321 int file_mode = 0;
322 EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
323 EXPECT_EQ(mode & ~mask, file_mode & 0777);
324 umask(old_mask);
325 }
326
TEST_F(FileUtilsTest,WriteToFileAtomicCreatesMissingParentDirectoriesWith0700)327 TEST_F(FileUtilsTest,
328 WriteToFileAtomicCreatesMissingParentDirectoriesWith0700) {
329 const mode_t mask = 0000;
330 const mode_t mode = 0700;
331 const base::FilePath dirname(GetTempName());
332 const base::FilePath subdirname(dirname.Append(GetRandomSuffix()));
333 const base::FilePath filename(subdirname.Append(GetRandomSuffix()));
334 const std::string content("blablabla");
335 EXPECT_TRUE(
336 WriteToFileAtomic(filename, content.data(), content.size(), 0777));
337 int dir_mode = 0;
338 int subdir_mode = 0;
339 EXPECT_TRUE(base::GetPosixFilePermissions(dirname, &dir_mode));
340 EXPECT_TRUE(base::GetPosixFilePermissions(subdirname, &subdir_mode));
341 EXPECT_EQ(mode & ~mask, dir_mode & 0777);
342 EXPECT_EQ(mode & ~mask, subdir_mode & 0777);
343 const mode_t old_mask = umask(mask);
344 umask(old_mask);
345 }
346
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageNormalRandomFile)347 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageNormalRandomFile) {
348 // 2MB test file.
349 constexpr size_t kFileSize = 2 * 1024 * 1024;
350
351 const base::FilePath dirname(GetTempName());
352 EXPECT_TRUE(base::CreateDirectory(dirname));
353 const base::FilePath filename = dirname.Append("test.temp");
354
355 std::string file_content = base::RandBytesAsString(kFileSize);
356 EXPECT_TRUE(WriteStringToFile(filename, file_content));
357
358 int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
359 int64_t result_size = base::ComputeDirectorySize(dirname);
360
361 // result_usage (what we are testing here) should be within +/-10% of ground
362 // truth. The variation is to account for filesystem overhead variations.
363 EXPECT_GT(result_usage, kFileSize / 10 * 9);
364 EXPECT_LT(result_usage, kFileSize / 10 * 11);
365
366 // result_usage should be close to result_size, because the test file is
367 // random so it's disk usage should be similar to apparent size.
368 EXPECT_GT(result_usage, result_size / 10 * 9);
369 EXPECT_LT(result_usage, result_size / 10 * 11);
370 }
371
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageDeepRandomFile)372 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageDeepRandomFile) {
373 // 2MB test file.
374 constexpr size_t kFileSize = 2 * 1024 * 1024;
375
376 const base::FilePath dirname(GetTempName());
377 EXPECT_TRUE(base::CreateDirectory(dirname));
378 base::FilePath currentlevel = dirname;
379 for (int i = 0; i < 10; i++) {
380 base::FilePath nextlevel = currentlevel.Append("test.dir");
381 EXPECT_TRUE(base::CreateDirectory(nextlevel));
382 currentlevel = nextlevel;
383 }
384 const base::FilePath filename = currentlevel.Append("test.temp");
385
386 std::string file_content = base::RandBytesAsString(kFileSize);
387 EXPECT_TRUE(WriteStringToFile(filename, file_content));
388
389 int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
390 int64_t result_size = base::ComputeDirectorySize(dirname);
391
392 // result_usage (what we are testing here) should be within +/-10% of ground
393 // truth. The variation is to account for filesystem overhead variations.
394 EXPECT_GT(result_usage, kFileSize / 10 * 9);
395 EXPECT_LT(result_usage, kFileSize / 10 * 11);
396
397 // result_usage should be close to result_size, because the test file is
398 // random so it's disk usage should be similar to apparent size.
399 EXPECT_GT(result_usage, result_size / 10 * 9);
400 EXPECT_LT(result_usage, result_size / 10 * 11);
401 }
402
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageHiddenRandomFile)403 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageHiddenRandomFile) {
404 // 2MB test file.
405 constexpr size_t kFileSize = 2 * 1024 * 1024;
406
407 const base::FilePath dirname(GetTempName());
408 EXPECT_TRUE(base::CreateDirectory(dirname));
409 // File name starts with a dot, so it's a hidden file.
410 const base::FilePath filename = dirname.Append(".test.temp");
411
412 std::string file_content = base::RandBytesAsString(kFileSize);
413 EXPECT_TRUE(WriteStringToFile(filename, file_content));
414
415 int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
416 int64_t result_size = base::ComputeDirectorySize(dirname);
417
418 // result_usage (what we are testing here) should be within +/-10% of ground
419 // truth. The variation is to account for filesystem overhead variations.
420 EXPECT_GT(result_usage, kFileSize / 10 * 9);
421 EXPECT_LT(result_usage, kFileSize / 10 * 11);
422
423 // result_usage should be close to result_size, because the test file is
424 // random so it's disk usage should be similar to apparent size.
425 EXPECT_GT(result_usage, result_size / 10 * 9);
426 EXPECT_LT(result_usage, result_size / 10 * 11);
427 }
428
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageSparseFile)429 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageSparseFile) {
430 // 128MB sparse test file.
431 constexpr size_t kFileSize = 128 * 1024 * 1024;
432 constexpr size_t kFileSizeThreshold = 64 * 1024;
433
434 const base::FilePath dirname(GetTempName());
435 EXPECT_TRUE(base::CreateDirectory(dirname));
436 const base::FilePath filename = dirname.Append("test.temp");
437
438 int fd =
439 open(filename.value().c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
440 EXPECT_NE(fd, -1);
441 // Calling ftruncate on an empty file will create a sparse file.
442 EXPECT_EQ(0, ftruncate(fd, kFileSize));
443
444 int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
445 int64_t result_size = base::ComputeDirectorySize(dirname);
446
447 // result_usage (what we are testing here) should be less than
448 // kFileSizeThreshold, the threshold is to account for filesystem overhead
449 // variations.
450 EXPECT_LT(result_usage, kFileSizeThreshold);
451
452 // Since we are dealing with sparse files here, the apparent size should be
453 // much much larger than the actual disk usage.
454 EXPECT_LT(result_usage, result_size / 1000);
455 }
456
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageSymlinkFile)457 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageSymlinkFile) {
458 // 2MB test file.
459 constexpr size_t kFileSize = 2 * 1024 * 1024;
460
461 const base::FilePath dirname(GetTempName());
462 EXPECT_TRUE(base::CreateDirectory(dirname));
463 const base::FilePath filename = dirname.Append("test.temp");
464 const base::FilePath linkname = dirname.Append("test.link");
465
466 std::string file_content = base::RandBytesAsString(kFileSize);
467 EXPECT_TRUE(WriteStringToFile(filename, file_content));
468
469 // Create a symlink.
470 EXPECT_TRUE(base::CreateSymbolicLink(filename, linkname));
471
472 int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
473
474 // result_usage (what we are testing here) should be within +/-10% of ground
475 // truth. The variation is to account for filesystem overhead variations.
476 // Note that it's not 2x kFileSize because symblink is not counted twice.
477 EXPECT_GT(result_usage, kFileSize / 10 * 9);
478 EXPECT_LT(result_usage, kFileSize / 10 * 11);
479 }
480
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageSymlinkDir)481 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageSymlinkDir) {
482 // 2MB test file.
483 constexpr size_t kFileSize = 2 * 1024 * 1024;
484
485 const base::FilePath parentname(GetTempName());
486 EXPECT_TRUE(base::CreateDirectory(parentname));
487 const base::FilePath dirname = parentname.Append("target.dir");
488 EXPECT_TRUE(base::CreateDirectory(dirname));
489 const base::FilePath linkname = parentname.Append("link.dir");
490
491 const base::FilePath filename = dirname.Append("test.temp");
492
493 std::string file_content = base::RandBytesAsString(kFileSize);
494 EXPECT_TRUE(WriteStringToFile(filename, file_content));
495
496 // Create a symlink.
497 EXPECT_TRUE(base::CreateSymbolicLink(dirname, linkname));
498
499 int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
500
501 // result_usage (what we are testing here) should be within +/-10% of ground
502 // truth. The variation is to account for filesystem overhead variations.
503 // Note that it's not 2x kFileSize because symblink is not counted twice.
504 EXPECT_GT(result_usage, kFileSize / 10 * 9);
505 EXPECT_LT(result_usage, kFileSize / 10 * 11);
506 }
507
508 } // namespace brillo
509