1// Copyright 2010 The Kyua Authors.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "utils/fs/operations.hpp"
30
31extern "C" {
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/wait.h>
35
36#include <dirent.h>
37#include <signal.h>
38#include <unistd.h>
39}
40
41#include <cerrno>
42#include <cstdlib>
43#include <cstring>
44#include <iostream>
45#include <stdexcept>
46#include <string>
47#include <vector>
48
49#include <atf-c++.hpp>
50
51#include "utils/env.hpp"
52#include "utils/format/containers.ipp"
53#include "utils/format/macros.hpp"
54#include "utils/fs/directory.hpp"
55#include "utils/fs/exceptions.hpp"
56#include "utils/fs/path.hpp"
57#include "utils/optional.ipp"
58#include "utils/passwd.hpp"
59#include "utils/stream.hpp"
60#include "utils/units.hpp"
61
62namespace fs = utils::fs;
63namespace passwd = utils::passwd;
64namespace units = utils::units;
65
66using utils::optional;
67
68
69namespace {
70
71
72/// Checks if a directory entry exists and matches a specific type.
73///
74/// \param dir The directory in which to look for the entry.
75/// \param name The name of the entry to look up.
76/// \param expected_type The expected type of the file as given by dir(5).
77///
78/// \return True if the entry exists and matches the given type; false
79/// otherwise.
80static bool
81lookup(const char* dir, const char* name, const unsigned int expected_type)
82{
83    DIR* dirp = ::opendir(dir);
84    ATF_REQUIRE(dirp != NULL);
85
86    bool found = false;
87    struct dirent* dp;
88    while (!found && (dp = readdir(dirp)) != NULL) {
89        if (std::strcmp(dp->d_name, name) == 0) {
90            struct ::stat s;
91            const fs::path lookup_path = fs::path(dir) / name;
92            ATF_REQUIRE(::stat(lookup_path.c_str(), &s) != -1);
93            if ((s.st_mode & S_IFMT) == expected_type) {
94                found = true;
95            }
96        }
97    }
98    ::closedir(dirp);
99    return found;
100}
101
102
103}  // anonymous namespace
104
105
106ATF_TEST_CASE_WITHOUT_HEAD(copy__ok);
107ATF_TEST_CASE_BODY(copy__ok)
108{
109    const fs::path source("f1.txt");
110    const fs::path target("f2.txt");
111
112    atf::utils::create_file(source.str(), "This is the input");
113    fs::copy(source, target);
114    ATF_REQUIRE(atf::utils::compare_file(target.str(), "This is the input"));
115}
116
117
118ATF_TEST_CASE_WITHOUT_HEAD(copy__fail_open);
119ATF_TEST_CASE_BODY(copy__fail_open)
120{
121    const fs::path source("f1.txt");
122    const fs::path target("f2.txt");
123
124    ATF_REQUIRE_THROW_RE(fs::error, "Cannot open copy source f1.txt",
125                         fs::copy(source, target));
126}
127
128
129ATF_TEST_CASE(copy__fail_create);
130ATF_TEST_CASE_HEAD(copy__fail_create)
131{
132    set_md_var("require.user", "unprivileged");
133}
134ATF_TEST_CASE_BODY(copy__fail_create)
135{
136    const fs::path source("f1.txt");
137    const fs::path target("f2.txt");
138
139    atf::utils::create_file(target.str(), "Do not override");
140    ATF_REQUIRE(::chmod(target.c_str(), 0444) != -1);
141
142    atf::utils::create_file(source.str(), "This is the input");
143    ATF_REQUIRE_THROW_RE(fs::error, "Cannot create copy target f2.txt",
144                         fs::copy(source, target));
145}
146
147
148ATF_TEST_CASE_WITHOUT_HEAD(current_path__ok);
149ATF_TEST_CASE_BODY(current_path__ok)
150{
151    const fs::path previous = fs::current_path();
152    fs::mkdir(fs::path("root"), 0755);
153    ATF_REQUIRE(::chdir("root") != -1);
154    const fs::path cwd = fs::current_path();
155    ATF_REQUIRE_EQ(cwd.str().length() - 5, cwd.str().find("/root"));
156    ATF_REQUIRE_EQ(previous / "root", cwd);
157}
158
159
160ATF_TEST_CASE_WITHOUT_HEAD(current_path__enoent);
161ATF_TEST_CASE_BODY(current_path__enoent)
162{
163    const fs::path previous = fs::current_path();
164    fs::mkdir(fs::path("root"), 0755);
165    ATF_REQUIRE(::chdir("root") != -1);
166    ATF_REQUIRE(::rmdir("../root") != -1);
167    try {
168        (void)fs::current_path();
169        fail("system_errpr not raised");
170    } catch (const fs::system_error& e) {
171        ATF_REQUIRE_EQ(ENOENT, e.original_errno());
172    }
173}
174
175
176ATF_TEST_CASE_WITHOUT_HEAD(exists);
177ATF_TEST_CASE_BODY(exists)
178{
179    const fs::path dir("dir");
180    ATF_REQUIRE(!fs::exists(dir));
181    fs::mkdir(dir, 0755);
182    ATF_REQUIRE(fs::exists(dir));
183}
184
185
186ATF_TEST_CASE_WITHOUT_HEAD(find_in_path__no_path);
187ATF_TEST_CASE_BODY(find_in_path__no_path)
188{
189    utils::unsetenv("PATH");
190    ATF_REQUIRE(!fs::find_in_path("ls"));
191    atf::utils::create_file("ls", "");
192    ATF_REQUIRE(!fs::find_in_path("ls"));
193}
194
195
196ATF_TEST_CASE_WITHOUT_HEAD(find_in_path__empty_path);
197ATF_TEST_CASE_BODY(find_in_path__empty_path)
198{
199    utils::setenv("PATH", "");
200    ATF_REQUIRE(!fs::find_in_path("ls"));
201    atf::utils::create_file("ls", "");
202    ATF_REQUIRE(!fs::find_in_path("ls"));
203}
204
205
206ATF_TEST_CASE_WITHOUT_HEAD(find_in_path__one_component);
207ATF_TEST_CASE_BODY(find_in_path__one_component)
208{
209    const fs::path dir = fs::current_path() / "bin";
210    fs::mkdir(dir, 0755);
211    utils::setenv("PATH", dir.str());
212
213    ATF_REQUIRE(!fs::find_in_path("ls"));
214    atf::utils::create_file((dir / "ls").str(), "");
215    ATF_REQUIRE_EQ(dir / "ls", fs::find_in_path("ls").get());
216}
217
218
219ATF_TEST_CASE_WITHOUT_HEAD(find_in_path__many_components);
220ATF_TEST_CASE_BODY(find_in_path__many_components)
221{
222    const fs::path dir1 = fs::current_path() / "dir1";
223    const fs::path dir2 = fs::current_path() / "dir2";
224    fs::mkdir(dir1, 0755);
225    fs::mkdir(dir2, 0755);
226    utils::setenv("PATH", dir1.str() + ":" + dir2.str());
227
228    ATF_REQUIRE(!fs::find_in_path("ls"));
229    atf::utils::create_file((dir2 / "ls").str(), "");
230    ATF_REQUIRE_EQ(dir2 / "ls", fs::find_in_path("ls").get());
231    atf::utils::create_file((dir1 / "ls").str(), "");
232    ATF_REQUIRE_EQ(dir1 / "ls", fs::find_in_path("ls").get());
233}
234
235
236ATF_TEST_CASE_WITHOUT_HEAD(find_in_path__current_directory);
237ATF_TEST_CASE_BODY(find_in_path__current_directory)
238{
239    utils::setenv("PATH", "bin:");
240
241    ATF_REQUIRE(!fs::find_in_path("foo-bar"));
242    atf::utils::create_file("foo-bar", "");
243    ATF_REQUIRE_EQ(fs::path("foo-bar").to_absolute(),
244                   fs::find_in_path("foo-bar").get());
245}
246
247
248ATF_TEST_CASE_WITHOUT_HEAD(find_in_path__always_absolute);
249ATF_TEST_CASE_BODY(find_in_path__always_absolute)
250{
251    fs::mkdir(fs::path("my-bin"), 0755);
252    utils::setenv("PATH", "my-bin");
253
254    ATF_REQUIRE(!fs::find_in_path("abcd"));
255    atf::utils::create_file("my-bin/abcd", "");
256    ATF_REQUIRE_EQ(fs::path("my-bin/abcd").to_absolute(),
257                   fs::find_in_path("abcd").get());
258}
259
260
261ATF_TEST_CASE_WITHOUT_HEAD(free_disk_space__ok__smoke);
262ATF_TEST_CASE_BODY(free_disk_space__ok__smoke)
263{
264    const units::bytes space = fs::free_disk_space(fs::path("."));
265    ATF_REQUIRE(space > units::MB);  // Simple test that should always pass.
266}
267
268
269/// Unmounts a directory without raising errors.
270///
271/// \param cookie Name of a file that exists while the mount point is still
272///     mounted.  Used to prevent a double-unmount, which would print a
273///     misleading error message.
274/// \param mount_point Path to the mount point to unmount.
275static void
276cleanup_mount_point(const fs::path& cookie, const fs::path& mount_point)
277{
278    try {
279        if (fs::exists(cookie)) {
280            fs::unmount(mount_point);
281        }
282    } catch (const std::runtime_error& e) {
283        std::cerr << "Failed trying to unmount " + mount_point.str() +
284            " during cleanup: " << e.what() << '\n';
285    }
286}
287
288
289ATF_TEST_CASE_WITH_CLEANUP(free_disk_space__ok__real);
290ATF_TEST_CASE_HEAD(free_disk_space__ok__real)
291{
292    set_md_var("require.user", "root");
293}
294ATF_TEST_CASE_BODY(free_disk_space__ok__real)
295{
296    try {
297        const fs::path mount_point("mount_point");
298        fs::mkdir(mount_point, 0755);
299        fs::mount_tmpfs(mount_point, units::bytes(32 * units::MB));
300        atf::utils::create_file("mounted", "");
301        const units::bytes space = fs::free_disk_space(fs::path(mount_point));
302        fs::unmount(mount_point);
303        fs::unlink(fs::path("mounted"));
304        ATF_REQUIRE(space < 35 * units::MB);
305        ATF_REQUIRE(space > 28 * units::MB);
306    } catch (const fs::unsupported_operation_error& e) {
307        ATF_SKIP(e.what());
308    }
309}
310ATF_TEST_CASE_CLEANUP(free_disk_space__ok__real)
311{
312    cleanup_mount_point(fs::path("mounted"), fs::path("mount_point"));
313}
314
315
316ATF_TEST_CASE_WITHOUT_HEAD(free_disk_space__fail);
317ATF_TEST_CASE_BODY(free_disk_space__fail)
318{
319    ATF_REQUIRE_THROW_RE(fs::error, "Failed to stat file system for missing",
320                         fs::free_disk_space(fs::path("missing")));
321}
322
323
324ATF_TEST_CASE_WITHOUT_HEAD(is_directory__ok);
325ATF_TEST_CASE_BODY(is_directory__ok)
326{
327    const fs::path file("file");
328    atf::utils::create_file(file.str(), "");
329    ATF_REQUIRE(!fs::is_directory(file));
330
331    const fs::path dir("dir");
332    fs::mkdir(dir, 0755);
333    ATF_REQUIRE(fs::is_directory(dir));
334}
335
336
337ATF_TEST_CASE_WITH_CLEANUP(is_directory__fail);
338ATF_TEST_CASE_HEAD(is_directory__fail)
339{
340    set_md_var("require.user", "unprivileged");
341}
342ATF_TEST_CASE_BODY(is_directory__fail)
343{
344    fs::mkdir(fs::path("dir"), 0000);
345    ATF_REQUIRE_THROW(fs::error, fs::is_directory(fs::path("dir/foo")));
346}
347ATF_TEST_CASE_CLEANUP(is_directory__fail)
348{
349    if (::chmod("dir", 0755) == -1) {
350        // If we cannot restore the original permissions, we cannot do much
351        // more.  However, leaving an unwritable directory behind will cause the
352        // runtime engine to report us as broken.
353    }
354}
355
356
357ATF_TEST_CASE_WITHOUT_HEAD(mkdir__ok);
358ATF_TEST_CASE_BODY(mkdir__ok)
359{
360    fs::mkdir(fs::path("dir"), 0755);
361    ATF_REQUIRE(lookup(".", "dir", S_IFDIR));
362}
363
364
365ATF_TEST_CASE_WITHOUT_HEAD(mkdir__enoent);
366ATF_TEST_CASE_BODY(mkdir__enoent)
367{
368    try {
369        fs::mkdir(fs::path("dir1/dir2"), 0755);
370        fail("system_error not raised");
371    } catch (const fs::system_error& e) {
372        ATF_REQUIRE_EQ(ENOENT, e.original_errno());
373    }
374    ATF_REQUIRE(!lookup(".", "dir1", S_IFDIR));
375    ATF_REQUIRE(!lookup(".", "dir2", S_IFDIR));
376}
377
378
379ATF_TEST_CASE_WITHOUT_HEAD(mkdir_p__one_component);
380ATF_TEST_CASE_BODY(mkdir_p__one_component)
381{
382    ATF_REQUIRE(!lookup(".", "new-dir", S_IFDIR));
383    fs::mkdir_p(fs::path("new-dir"), 0755);
384    ATF_REQUIRE(lookup(".", "new-dir", S_IFDIR));
385}
386
387
388ATF_TEST_CASE_WITHOUT_HEAD(mkdir_p__many_components);
389ATF_TEST_CASE_BODY(mkdir_p__many_components)
390{
391    ATF_REQUIRE(!lookup(".", "a", S_IFDIR));
392    fs::mkdir_p(fs::path("a/b/c"), 0755);
393    ATF_REQUIRE(lookup(".", "a", S_IFDIR));
394    ATF_REQUIRE(lookup("a", "b", S_IFDIR));
395    ATF_REQUIRE(lookup("a/b", "c", S_IFDIR));
396}
397
398
399ATF_TEST_CASE_WITHOUT_HEAD(mkdir_p__already_exists);
400ATF_TEST_CASE_BODY(mkdir_p__already_exists)
401{
402    fs::mkdir(fs::path("a"), 0755);
403    fs::mkdir(fs::path("a/b"), 0755);
404    fs::mkdir_p(fs::path("a/b"), 0755);
405}
406
407
408ATF_TEST_CASE(mkdir_p__eacces)
409ATF_TEST_CASE_HEAD(mkdir_p__eacces)
410{
411    set_md_var("require.user", "unprivileged");
412}
413ATF_TEST_CASE_BODY(mkdir_p__eacces)
414{
415    fs::mkdir(fs::path("a"), 0755);
416    fs::mkdir(fs::path("a/b"), 0755);
417    ATF_REQUIRE(::chmod("a/b", 0555) != -1);
418    try {
419        fs::mkdir_p(fs::path("a/b/c/d"), 0755);
420        fail("system_error not raised");
421    } catch (const fs::system_error& e) {
422        ATF_REQUIRE_EQ(EACCES, e.original_errno());
423    }
424    ATF_REQUIRE(lookup(".", "a", S_IFDIR));
425    ATF_REQUIRE(lookup("a", "b", S_IFDIR));
426    ATF_REQUIRE(!lookup(".", "c", S_IFDIR));
427    ATF_REQUIRE(!lookup("a", "c", S_IFDIR));
428    ATF_REQUIRE(!lookup("a/b", "c", S_IFDIR));
429}
430
431
432ATF_TEST_CASE_WITHOUT_HEAD(mkdtemp_public)
433ATF_TEST_CASE_BODY(mkdtemp_public)
434{
435    const fs::path tmpdir = fs::current_path() / "tmp";
436    utils::setenv("TMPDIR", tmpdir.str());
437    fs::mkdir(tmpdir, 0755);
438
439    const std::string dir_template("tempdir.XXXXXX");
440    const fs::path tempdir = fs::mkdtemp_public(dir_template);
441    ATF_REQUIRE(!lookup("tmp", dir_template.c_str(), S_IFDIR));
442    ATF_REQUIRE(lookup("tmp", tempdir.leaf_name().c_str(), S_IFDIR));
443}
444
445
446ATF_TEST_CASE(mkdtemp_public__getcwd_as_non_root)
447ATF_TEST_CASE_HEAD(mkdtemp_public__getcwd_as_non_root)
448{
449    set_md_var("require.config", "unprivileged-user");
450    set_md_var("require.user", "root");
451}
452ATF_TEST_CASE_BODY(mkdtemp_public__getcwd_as_non_root)
453{
454    const std::string dir_template("dir.XXXXXX");
455    const fs::path dir = fs::mkdtemp_public(dir_template);
456    const fs::path subdir = dir / "subdir";
457    fs::mkdir(subdir, 0755);
458
459    const uid_t old_euid = ::geteuid();
460    const gid_t old_egid = ::getegid();
461
462    const passwd::user unprivileged_user = passwd::find_user_by_name(
463        get_config_var("unprivileged-user"));
464    ATF_REQUIRE(::setegid(unprivileged_user.gid) != -1);
465    ATF_REQUIRE(::seteuid(unprivileged_user.uid) != -1);
466
467    // The next code block runs as non-root.  We cannot use any ATF macros nor
468    // functions in it because a failure would cause the test to attempt to
469    // write to the ATF result file which may not be writable as non-root.
470    bool failed = false;
471    {
472        try {
473            if (::chdir(subdir.c_str()) == -1) {
474                std::cerr << "Cannot enter directory\n";
475                failed |= true;
476            } else {
477                fs::current_path();
478            }
479        } catch (const fs::error& e) {
480            failed |= true;
481            std::cerr << "Failed to query current path in: " << subdir << '\n';
482        }
483
484        if (::seteuid(old_euid) == -1) {
485            std::cerr << "Failed to restore euid; cannot continue\n";
486            std::abort();
487        }
488        if (::setegid(old_egid) == -1) {
489            std::cerr << "Failed to restore egid; cannot continue\n";
490            std::abort();
491        }
492    }
493
494    if (failed)
495        fail("Test failed; see stdout for details");
496}
497
498
499ATF_TEST_CASE(mkdtemp_public__search_permissions_as_non_root)
500ATF_TEST_CASE_HEAD(mkdtemp_public__search_permissions_as_non_root)
501{
502    set_md_var("require.config", "unprivileged-user");
503    set_md_var("require.user", "root");
504}
505ATF_TEST_CASE_BODY(mkdtemp_public__search_permissions_as_non_root)
506{
507    const std::string dir_template("dir.XXXXXX");
508    const fs::path dir = fs::mkdtemp_public(dir_template);
509    const fs::path cookie = dir / "not-secret";
510    atf::utils::create_file(cookie.str(), "this is readable");
511
512    // We are running as root so there is no reason to assume that our current
513    // work directory is accessible by non-root.  Weaken the permissions so that
514    // our code below works.
515    ATF_REQUIRE(::chmod(".", 0755) != -1);
516
517    const uid_t old_euid = ::geteuid();
518    const gid_t old_egid = ::getegid();
519
520    const passwd::user unprivileged_user = passwd::find_user_by_name(
521        get_config_var("unprivileged-user"));
522    ATF_REQUIRE(::setegid(unprivileged_user.gid) != -1);
523    ATF_REQUIRE(::seteuid(unprivileged_user.uid) != -1);
524
525    // The next code block runs as non-root.  We cannot use any ATF macros nor
526    // functions in it because a failure would cause the test to attempt to
527    // write to the ATF result file which may not be writable as non-root.
528    bool failed = false;
529    {
530        try {
531            const std::string contents = utils::read_file(cookie);
532            std::cerr << "Read contents: " << contents << '\n';
533            failed |= (contents != "this is readable");
534        } catch (const std::runtime_error& e) {
535            failed |= true;
536            std::cerr << "Failed to read " << cookie << '\n';
537        }
538
539        if (::seteuid(old_euid) == -1) {
540            std::cerr << "Failed to restore euid; cannot continue\n";
541            std::abort();
542        }
543        if (::setegid(old_egid) == -1) {
544            std::cerr << "Failed to restore egid; cannot continue\n";
545            std::abort();
546        }
547    }
548
549    if (failed)
550        fail("Test failed; see stdout for details");
551}
552
553
554ATF_TEST_CASE_WITHOUT_HEAD(mkstemp)
555ATF_TEST_CASE_BODY(mkstemp)
556{
557    const fs::path tmpdir = fs::current_path() / "tmp";
558    utils::setenv("TMPDIR", tmpdir.str());
559    fs::mkdir(tmpdir, 0755);
560
561    const std::string file_template("tempfile.XXXXXX");
562    const fs::path tempfile = fs::mkstemp(file_template);
563    ATF_REQUIRE(!lookup("tmp", file_template.c_str(), S_IFREG));
564    ATF_REQUIRE(lookup("tmp", tempfile.leaf_name().c_str(), S_IFREG));
565}
566
567
568static void
569test_mount_tmpfs_ok(const units::bytes& size)
570{
571    const fs::path mount_point("mount_point");
572    fs::mkdir(mount_point, 0755);
573
574    try {
575        atf::utils::create_file("outside", "");
576        fs::mount_tmpfs(mount_point, size);
577        atf::utils::create_file("mounted", "");
578        atf::utils::create_file((mount_point / "inside").str(), "");
579
580        struct ::stat outside, inside;
581        ATF_REQUIRE(::stat("outside", &outside) != -1);
582        ATF_REQUIRE(::stat((mount_point / "inside").c_str(), &inside) != -1);
583        ATF_REQUIRE(outside.st_dev != inside.st_dev);
584        fs::unmount(mount_point);
585    } catch (const fs::unsupported_operation_error& e) {
586        ATF_SKIP(e.what());
587    }
588}
589
590
591ATF_TEST_CASE_WITH_CLEANUP(mount_tmpfs__ok__default_size)
592ATF_TEST_CASE_HEAD(mount_tmpfs__ok__default_size)
593{
594    set_md_var("require.user", "root");
595}
596ATF_TEST_CASE_BODY(mount_tmpfs__ok__default_size)
597{
598    test_mount_tmpfs_ok(units::bytes());
599}
600ATF_TEST_CASE_CLEANUP(mount_tmpfs__ok__default_size)
601{
602    cleanup_mount_point(fs::path("mounted"), fs::path("mount_point"));
603}
604
605
606ATF_TEST_CASE_WITH_CLEANUP(mount_tmpfs__ok__explicit_size)
607ATF_TEST_CASE_HEAD(mount_tmpfs__ok__explicit_size)
608{
609    set_md_var("require.user", "root");
610}
611ATF_TEST_CASE_BODY(mount_tmpfs__ok__explicit_size)
612{
613    test_mount_tmpfs_ok(units::bytes(10 * units::MB));
614}
615ATF_TEST_CASE_CLEANUP(mount_tmpfs__ok__explicit_size)
616{
617    cleanup_mount_point(fs::path("mounted"), fs::path("mount_point"));
618}
619
620
621ATF_TEST_CASE(mount_tmpfs__fail)
622ATF_TEST_CASE_HEAD(mount_tmpfs__fail)
623{
624    set_md_var("require.user", "root");
625}
626ATF_TEST_CASE_BODY(mount_tmpfs__fail)
627{
628    try {
629        fs::mount_tmpfs(fs::path("non-existent"));
630    } catch (const fs::unsupported_operation_error& e) {
631        ATF_SKIP(e.what());
632    } catch (const fs::error& e) {
633        // Expected.
634    }
635}
636
637
638ATF_TEST_CASE_WITHOUT_HEAD(rm_r__empty);
639ATF_TEST_CASE_BODY(rm_r__empty)
640{
641    fs::mkdir(fs::path("root"), 0755);
642    ATF_REQUIRE(lookup(".", "root", S_IFDIR));
643    fs::rm_r(fs::path("root"));
644    ATF_REQUIRE(!lookup(".", "root", S_IFDIR));
645}
646
647
648ATF_TEST_CASE_WITHOUT_HEAD(rm_r__files_and_directories);
649ATF_TEST_CASE_BODY(rm_r__files_and_directories)
650{
651    fs::mkdir(fs::path("root"), 0755);
652    atf::utils::create_file("root/.hidden_file", "");
653    fs::mkdir(fs::path("root/.hidden_dir"), 0755);
654    atf::utils::create_file("root/.hidden_dir/a", "");
655    atf::utils::create_file("root/file", "");
656    atf::utils::create_file("root/with spaces", "");
657    fs::mkdir(fs::path("root/dir1"), 0755);
658    fs::mkdir(fs::path("root/dir1/dir2"), 0755);
659    atf::utils::create_file("root/dir1/dir2/file", "");
660    fs::mkdir(fs::path("root/dir1/dir3"), 0755);
661    ATF_REQUIRE(lookup(".", "root", S_IFDIR));
662    fs::rm_r(fs::path("root"));
663    ATF_REQUIRE(!lookup(".", "root", S_IFDIR));
664}
665
666
667ATF_TEST_CASE_WITHOUT_HEAD(rmdir__ok)
668ATF_TEST_CASE_BODY(rmdir__ok)
669{
670    ATF_REQUIRE(::mkdir("foo", 0755) != -1);
671    ATF_REQUIRE(::access("foo", X_OK) == 0);
672    fs::rmdir(fs::path("foo"));
673    ATF_REQUIRE(::access("foo", X_OK) == -1);
674}
675
676
677ATF_TEST_CASE_WITHOUT_HEAD(rmdir__fail)
678ATF_TEST_CASE_BODY(rmdir__fail)
679{
680    ATF_REQUIRE_THROW_RE(fs::system_error, "Removal of foo failed",
681                         fs::rmdir(fs::path("foo")));
682}
683
684
685ATF_TEST_CASE_WITHOUT_HEAD(scan_directory__ok)
686ATF_TEST_CASE_BODY(scan_directory__ok)
687{
688    fs::mkdir(fs::path("dir"), 0755);
689    atf::utils::create_file("dir/foo", "");
690    atf::utils::create_file("dir/.hidden", "");
691
692    const std::set< fs::directory_entry > contents = fs::scan_directory(
693        fs::path("dir"));
694
695    std::set< fs::directory_entry > exp_contents;
696    exp_contents.insert(fs::directory_entry("."));
697    exp_contents.insert(fs::directory_entry(".."));
698    exp_contents.insert(fs::directory_entry(".hidden"));
699    exp_contents.insert(fs::directory_entry("foo"));
700
701    ATF_REQUIRE_EQ(exp_contents, contents);
702}
703
704
705ATF_TEST_CASE_WITHOUT_HEAD(scan_directory__fail)
706ATF_TEST_CASE_BODY(scan_directory__fail)
707{
708    ATF_REQUIRE_THROW_RE(fs::system_error, "opendir(.*missing.*) failed",
709                         fs::scan_directory(fs::path("missing")));
710}
711
712
713ATF_TEST_CASE_WITHOUT_HEAD(unlink__ok)
714ATF_TEST_CASE_BODY(unlink__ok)
715{
716    atf::utils::create_file("foo", "");
717    ATF_REQUIRE(::access("foo", R_OK) == 0);
718    fs::unlink(fs::path("foo"));
719    ATF_REQUIRE(::access("foo", R_OK) == -1);
720}
721
722
723ATF_TEST_CASE_WITHOUT_HEAD(unlink__fail)
724ATF_TEST_CASE_BODY(unlink__fail)
725{
726    ATF_REQUIRE_THROW_RE(fs::system_error, "Removal of foo failed",
727                         fs::unlink(fs::path("foo")));
728}
729
730
731ATF_TEST_CASE(unmount__ok)
732ATF_TEST_CASE_HEAD(unmount__ok)
733{
734    set_md_var("require.user", "root");
735}
736ATF_TEST_CASE_BODY(unmount__ok)
737{
738    const fs::path mount_point("mount_point");
739    fs::mkdir(mount_point, 0755);
740
741    atf::utils::create_file((mount_point / "test1").str(), "");
742    try {
743        fs::mount_tmpfs(mount_point);
744    } catch (const fs::unsupported_operation_error& e) {
745        ATF_SKIP(e.what());
746    }
747
748    atf::utils::create_file((mount_point / "test2").str(), "");
749
750    ATF_REQUIRE(!fs::exists(mount_point / "test1"));
751    ATF_REQUIRE( fs::exists(mount_point / "test2"));
752    fs::unmount(mount_point);
753    ATF_REQUIRE( fs::exists(mount_point / "test1"));
754    ATF_REQUIRE(!fs::exists(mount_point / "test2"));
755}
756
757
758ATF_TEST_CASE(unmount__fail)
759ATF_TEST_CASE_HEAD(unmount__fail)
760{
761    set_md_var("require.user", "root");
762}
763ATF_TEST_CASE_BODY(unmount__fail)
764{
765    ATF_REQUIRE_THROW(fs::error, fs::unmount(fs::path("non-existent")));
766}
767
768
769ATF_INIT_TEST_CASES(tcs)
770{
771    ATF_ADD_TEST_CASE(tcs, copy__ok);
772    ATF_ADD_TEST_CASE(tcs, copy__fail_open);
773    ATF_ADD_TEST_CASE(tcs, copy__fail_create);
774
775    ATF_ADD_TEST_CASE(tcs, current_path__ok);
776    ATF_ADD_TEST_CASE(tcs, current_path__enoent);
777
778    ATF_ADD_TEST_CASE(tcs, exists);
779
780    ATF_ADD_TEST_CASE(tcs, find_in_path__no_path);
781    ATF_ADD_TEST_CASE(tcs, find_in_path__empty_path);
782    ATF_ADD_TEST_CASE(tcs, find_in_path__one_component);
783    ATF_ADD_TEST_CASE(tcs, find_in_path__many_components);
784    ATF_ADD_TEST_CASE(tcs, find_in_path__current_directory);
785    ATF_ADD_TEST_CASE(tcs, find_in_path__always_absolute);
786
787    ATF_ADD_TEST_CASE(tcs, free_disk_space__ok__smoke);
788    ATF_ADD_TEST_CASE(tcs, free_disk_space__ok__real);
789    ATF_ADD_TEST_CASE(tcs, free_disk_space__fail);
790
791    ATF_ADD_TEST_CASE(tcs, is_directory__ok);
792    ATF_ADD_TEST_CASE(tcs, is_directory__fail);
793
794    ATF_ADD_TEST_CASE(tcs, mkdir__ok);
795    ATF_ADD_TEST_CASE(tcs, mkdir__enoent);
796
797    ATF_ADD_TEST_CASE(tcs, mkdir_p__one_component);
798    ATF_ADD_TEST_CASE(tcs, mkdir_p__many_components);
799    ATF_ADD_TEST_CASE(tcs, mkdir_p__already_exists);
800    ATF_ADD_TEST_CASE(tcs, mkdir_p__eacces);
801
802    ATF_ADD_TEST_CASE(tcs, mkdtemp_public);
803    ATF_ADD_TEST_CASE(tcs, mkdtemp_public__getcwd_as_non_root);
804    ATF_ADD_TEST_CASE(tcs, mkdtemp_public__search_permissions_as_non_root);
805
806    ATF_ADD_TEST_CASE(tcs, mkstemp);
807
808    ATF_ADD_TEST_CASE(tcs, mount_tmpfs__ok__default_size);
809    ATF_ADD_TEST_CASE(tcs, mount_tmpfs__ok__explicit_size);
810    ATF_ADD_TEST_CASE(tcs, mount_tmpfs__fail);
811
812    ATF_ADD_TEST_CASE(tcs, rm_r__empty);
813    ATF_ADD_TEST_CASE(tcs, rm_r__files_and_directories);
814
815    ATF_ADD_TEST_CASE(tcs, rmdir__ok);
816    ATF_ADD_TEST_CASE(tcs, rmdir__fail);
817
818    ATF_ADD_TEST_CASE(tcs, scan_directory__ok);
819    ATF_ADD_TEST_CASE(tcs, scan_directory__fail);
820
821    ATF_ADD_TEST_CASE(tcs, unlink__ok);
822    ATF_ADD_TEST_CASE(tcs, unlink__fail);
823
824    ATF_ADD_TEST_CASE(tcs, unmount__ok);
825    ATF_ADD_TEST_CASE(tcs, unmount__fail);
826}
827