1 //===----------------------------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++98, c++03
11 
12 // <filesystem>
13 
14 // file_status symlink_status(const path& p);
15 // file_status symlink_status(const path& p, error_code& ec) noexcept;
16 
17 #include "filesystem_include.hpp"
18 
19 #include "test_macros.h"
20 #include "rapid-cxx-test.hpp"
21 #include "filesystem_test_helper.hpp"
22 
23 using namespace fs;
24 
25 TEST_SUITE(filesystem_symlink_status_test_suite)
26 
TEST_CASE(signature_test)27 TEST_CASE(signature_test)
28 {
29     const path p; ((void)p);
30     std::error_code ec; ((void)ec);
31     ASSERT_NOT_NOEXCEPT(symlink_status(p));
32     ASSERT_NOEXCEPT(symlink_status(p, ec));
33 }
34 
TEST_CASE(test_symlink_status_not_found)35 TEST_CASE(test_symlink_status_not_found)
36 {
37     const std::error_code expect_ec =
38         std::make_error_code(std::errc::no_such_file_or_directory);
39     const path cases[] {
40         StaticEnv::DNE
41     };
42     for (auto& p : cases) {
43         std::error_code ec = std::make_error_code(std::errc::address_in_use);
44         // test non-throwing overload.
45         file_status st = symlink_status(p, ec);
46         TEST_CHECK(ec == expect_ec);
47         TEST_CHECK(st.type() == file_type::not_found);
48         TEST_CHECK(st.permissions() == perms::unknown);
49         // test throwing overload. It should not throw even though it reports
50         // that the file was not found.
51         TEST_CHECK_NO_THROW(st = status(p));
52         TEST_CHECK(st.type() == file_type::not_found);
53         TEST_CHECK(st.permissions() == perms::unknown);
54     }
55 }
56 
TEST_CASE(test_symlink_status_cannot_resolve)57 TEST_CASE(test_symlink_status_cannot_resolve)
58 {
59     scoped_test_env env;
60     const path dir = env.create_dir("dir");
61     const path file_in_dir = env.create_file("dir/file", 42);
62     const path sym_in_dir = env.create_symlink("dir/file", "dir/bad_sym");
63     const path sym_points_in_dir = env.create_symlink("dir/file", "sym");
64     permissions(dir, perms::none);
65 
66     const std::error_code set_ec =
67         std::make_error_code(std::errc::address_in_use);
68     const std::error_code expect_ec =
69         std::make_error_code(std::errc::permission_denied);
70 
71     const path fail_cases[] = {
72         file_in_dir, sym_in_dir
73     };
74     for (auto& p : fail_cases)
75     {
76         { // test non-throwing case
77             std::error_code ec = set_ec;
78             file_status st = symlink_status(p, ec);
79             TEST_CHECK(ec == expect_ec);
80             TEST_CHECK(st.type() == file_type::none);
81             TEST_CHECK(st.permissions() == perms::unknown);
82         }
83 #ifndef TEST_HAS_NO_EXCEPTIONS
84         { // test throwing case
85             try {
86                 symlink_status(p);
87             } catch (filesystem_error const& err) {
88                 TEST_CHECK(err.path1() == p);
89                 TEST_CHECK(err.path2() == "");
90                 TEST_CHECK(err.code() == expect_ec);
91             }
92         }
93 #endif
94     }
95     // Test that a symlink that points into a directory without read perms
96     // can be stat-ed using symlink_status
97     {
98         std::error_code ec = set_ec;
99         file_status st = symlink_status(sym_points_in_dir, ec);
100         TEST_CHECK(!ec);
101         TEST_CHECK(st.type() == file_type::symlink);
102         TEST_CHECK(st.permissions() != perms::unknown);
103         // test non-throwing version
104         TEST_REQUIRE_NO_THROW(st = symlink_status(sym_points_in_dir));
105         TEST_CHECK(st.type() == file_type::symlink);
106         TEST_CHECK(st.permissions() != perms::unknown);
107     }
108 }
109 
110 
TEST_CASE(symlink_status_file_types_test)111 TEST_CASE(symlink_status_file_types_test)
112 {
113     scoped_test_env env;
114     struct TestCase {
115       path p;
116       file_type expect_type;
117     } cases[] = {
118         {StaticEnv::BadSymlink, file_type::symlink},
119         {StaticEnv::File, file_type::regular},
120         {StaticEnv::SymlinkToFile, file_type::symlink},
121         {StaticEnv::Dir, file_type::directory},
122         {StaticEnv::SymlinkToDir, file_type::symlink},
123         // Block files tested elsewhere
124         {StaticEnv::CharFile, file_type::character},
125 #if !defined(__APPLE__) && !defined(__FreeBSD__) // No support for domain sockets
126         {env.create_socket("socket"), file_type::socket},
127 #endif
128         {env.create_fifo("fifo"), file_type::fifo}
129     };
130     for (const auto& TC : cases) {
131         // test non-throwing case
132         std::error_code ec = std::make_error_code(std::errc::address_in_use);
133         file_status st = symlink_status(TC.p, ec);
134         TEST_CHECK(!ec);
135         TEST_CHECK(st.type() == TC.expect_type);
136         TEST_CHECK(st.permissions() != perms::unknown);
137         // test throwing case
138         TEST_REQUIRE_NO_THROW(st = symlink_status(TC.p));
139         TEST_CHECK(st.type() == TC.expect_type);
140         TEST_CHECK(st.permissions() != perms::unknown);
141     }
142 }
143 
TEST_CASE(test_block_file)144 TEST_CASE(test_block_file)
145 {
146     const path possible_paths[] = {
147         "/dev/drive0", // Apple
148         "/dev/sda",    // Linux
149         "/dev/loop0"   // Linux
150         // No FreeBSD files known
151     };
152     path p;
153     for (const path& possible_p : possible_paths) {
154         std::error_code ec;
155         if (exists(possible_p, ec)) {
156             p = possible_p;
157             break;
158         }
159     }
160     if (p == path{}) {
161         TEST_UNSUPPORTED();
162     }
163     scoped_test_env env;
164     { // test block file
165         // test non-throwing case
166         std::error_code ec = std::make_error_code(std::errc::address_in_use);
167         file_status st = symlink_status(p, ec);
168         TEST_CHECK(!ec);
169         TEST_CHECK(st.type() == file_type::block);
170         TEST_CHECK(st.permissions() != perms::unknown);
171         // test throwing case
172         TEST_REQUIRE_NO_THROW(st = symlink_status(p));
173         TEST_CHECK(st.type() == file_type::block);
174         TEST_CHECK(st.permissions() != perms::unknown);
175     }
176     const path sym = env.make_env_path("sym");
177     create_symlink(p, sym);
178     { // test symlink to block file
179         // test non-throwing case
180         std::error_code ec = std::make_error_code(std::errc::address_in_use);
181         file_status st = symlink_status(sym, ec);
182         TEST_CHECK(!ec);
183         TEST_CHECK(st.type() == file_type::symlink);
184         TEST_CHECK(st.permissions() != perms::unknown);
185         // test throwing case
186         TEST_REQUIRE_NO_THROW(st = symlink_status(sym));
187         TEST_CHECK(st.type() == file_type::symlink);
188         TEST_CHECK(st.permissions() != perms::unknown);
189     }
190 }
191 
192 TEST_SUITE_END()
193