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 // class directory_entry
15 
16 // directory_entry& operator=(directory_entry const&) = default;
17 // directory_entry& operator=(directory_entry&&) noexcept = default;
18 // void assign(path const&);
19 // void replace_filename(path const&);
20 
21 #include "filesystem_include.hpp"
22 #include <type_traits>
23 #include <cassert>
24 
25 #include "test_macros.h"
26 #include "rapid-cxx-test.hpp"
27 #include "filesystem_test_helper.hpp"
28 
29 TEST_SUITE(directory_entry_mods_suite)
30 
TEST_CASE(test_refresh_method)31 TEST_CASE(test_refresh_method) {
32   using namespace fs;
33   {
34     directory_entry e;
35     static_assert(noexcept(e.refresh()) == false,
36                   "operation cannot be noexcept");
37     static_assert(std::is_same<decltype(e.refresh()), void>::value,
38                   "operation must return void");
39   }
40   {
41     directory_entry e;
42     e.refresh();
43     TEST_CHECK(!e.exists());
44   }
45 }
46 
TEST_CASE(test_refresh_ec_method)47 TEST_CASE(test_refresh_ec_method) {
48   using namespace fs;
49   {
50     directory_entry e;
51     std::error_code ec;
52     static_assert(noexcept(e.refresh(ec)), "operation should be noexcept");
53     static_assert(std::is_same<decltype(e.refresh(ec)), void>::value,
54                   "operation must return void");
55   }
56   {
57     directory_entry e;
58     std::error_code ec = GetTestEC();
59     e.refresh(ec);
60     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
61   }
62 }
63 
TEST_CASE(refresh_on_file_dne)64 TEST_CASE(refresh_on_file_dne) {
65   using namespace fs;
66   scoped_test_env env;
67   const path dir = env.create_dir("dir");
68   const path file = env.create_file("dir/file", 42);
69 
70   const perms old_perms = status(dir).permissions();
71 
72   // test file doesn't exist
73   {
74     directory_entry ent(file);
75     remove(file);
76     TEST_CHECK(ent.exists());
77 
78     ent.refresh();
79 
80     permissions(dir, perms::none);
81     TEST_CHECK(!ent.exists());
82   }
83   permissions(dir, old_perms);
84   env.create_file("dir/file", 101);
85   {
86     directory_entry ent(file);
87     remove(file);
88     TEST_CHECK(ent.exists());
89 
90     std::error_code ec = GetTestEC();
91     ent.refresh(ec);
92     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
93 
94     permissions(dir, perms::none);
95     TEST_CHECK(!ent.exists());
96   }
97 }
98 
remove_if_exists(const fs::path & p)99 void remove_if_exists(const fs::path& p) {
100   std::error_code ec;
101   remove(p, ec);
102 }
103 
TEST_CASE(refresh_on_bad_symlink)104 TEST_CASE(refresh_on_bad_symlink) {
105   using namespace fs;
106   scoped_test_env env;
107   const path dir = env.create_dir("dir");
108   const path file = env.create_file("dir/file", 42);
109   const path sym = env.create_symlink("dir/file", "sym");
110 
111   const perms old_perms = status(dir).permissions();
112 
113   // test file doesn't exist
114   {
115     directory_entry ent(sym);
116     LIBCPP_ONLY(remove(file));
117     TEST_CHECK(ent.is_symlink());
118     TEST_CHECK(ent.is_regular_file());
119     TEST_CHECK(ent.exists());
120 
121     remove_if_exists(file);
122     ent.refresh();
123 
124     LIBCPP_ONLY(permissions(dir, perms::none));
125     TEST_CHECK(ent.is_symlink());
126     TEST_CHECK(!ent.is_regular_file());
127     TEST_CHECK(!ent.exists());
128   }
129   permissions(dir, old_perms);
130   env.create_file("dir/file", 101);
131   {
132     directory_entry ent(sym);
133     LIBCPP_ONLY(remove(file));
134     TEST_CHECK(ent.is_symlink());
135     TEST_CHECK(ent.is_regular_file());
136     TEST_CHECK(ent.exists());
137 
138     remove_if_exists(file);
139 
140     std::error_code ec = GetTestEC();
141     ent.refresh(ec);
142     TEST_CHECK(!ec); // we don't report bad symlinks as an error.
143 
144     LIBCPP_ONLY(permissions(dir, perms::none));
145     TEST_CHECK(!ent.exists());
146   }
147 }
148 
TEST_CASE(refresh_cannot_resolve)149 TEST_CASE(refresh_cannot_resolve) {
150   using namespace fs;
151   scoped_test_env env;
152   const path dir = env.create_dir("dir");
153   const path file = env.create_file("dir/file", 42);
154   const path file_out_of_dir = env.create_file("file1", 99);
155   const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
156   const path sym_in_dir = env.create_symlink("file1", "dir/sym1");
157   perms old_perms = status(dir).permissions();
158 
159   {
160     directory_entry ent(file);
161     permissions(dir, perms::none);
162 
163     TEST_CHECK(ent.is_regular_file());
164 
165     std::error_code ec = GetTestEC();
166     ent.refresh(ec);
167 
168     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
169     TEST_CHECK(ent.path() == file);
170 
171     ExceptionChecker Checker(file, std::errc::permission_denied,
172                              "directory_entry::refresh");
173     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
174   }
175   permissions(dir, old_perms);
176   {
177     directory_entry ent(sym_in_dir);
178     permissions(dir, perms::none);
179     TEST_CHECK(ent.is_symlink());
180 
181     std::error_code ec = GetTestEC();
182     ent.refresh(ec);
183     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
184     TEST_CHECK(ent.path() == sym_in_dir);
185 
186     ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
187                              "directory_entry::refresh");
188     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
189   }
190   permissions(dir, old_perms);
191   {
192     directory_entry ent(sym_out_of_dir);
193     permissions(dir, perms::none);
194     TEST_CHECK(ent.is_symlink());
195 
196     // Failure to resolve the linked entity due to permissions is not
197     // reported as an error.
198     std::error_code ec = GetTestEC();
199     ent.refresh(ec);
200     TEST_CHECK(!ec);
201     TEST_CHECK(ent.is_symlink());
202 
203     ec = GetTestEC();
204     TEST_CHECK(ent.exists(ec) == false);
205     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
206     TEST_CHECK(ent.path() == sym_out_of_dir);
207   }
208   permissions(dir, old_perms);
209   {
210     directory_entry ent_file(file);
211     directory_entry ent_sym(sym_in_dir);
212     directory_entry ent_sym2(sym_out_of_dir);
213     permissions(dir, perms::none);
214     ((void)ent_file);
215     ((void)ent_sym);
216 
217     TEST_CHECK_THROW(filesystem_error, ent_file.refresh());
218     TEST_CHECK_THROW(filesystem_error, ent_sym.refresh());
219     TEST_CHECK_NO_THROW(ent_sym2);
220   }
221 }
222 
TEST_CASE(refresh_doesnt_throw_on_dne_but_reports_it)223 TEST_CASE(refresh_doesnt_throw_on_dne_but_reports_it) {
224   using namespace fs;
225   scoped_test_env env;
226 
227   const path file = env.create_file("file1", 42);
228   const path sym = env.create_symlink("file1", "sym");
229 
230   {
231     directory_entry ent(file);
232     TEST_CHECK(ent.file_size() == 42);
233 
234     remove(file);
235 
236     std::error_code ec = GetTestEC();
237     ent.refresh(ec);
238     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
239     TEST_CHECK_NO_THROW(ent.refresh());
240 
241     ec = GetTestEC();
242     TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
243     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
244 
245     // doesn't throw!
246     TEST_CHECK_THROW(filesystem_error, ent.file_size());
247   }
248   env.create_file("file1", 99);
249   {
250     directory_entry ent(sym);
251     TEST_CHECK(ent.is_symlink());
252     TEST_CHECK(ent.is_regular_file());
253     TEST_CHECK(ent.file_size() == 99);
254 
255     remove(file);
256 
257     std::error_code ec = GetTestEC();
258     ent.refresh(ec);
259     TEST_CHECK(!ec);
260 
261     ec = GetTestEC();
262     TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
263     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
264 
265     TEST_CHECK_THROW(filesystem_error, ent.file_size());
266   }
267 }
268 
TEST_CASE(access_cache_after_refresh_fails)269 TEST_CASE(access_cache_after_refresh_fails) {
270   using namespace fs;
271   scoped_test_env env;
272   const path dir = env.create_dir("dir");
273   const path file = env.create_file("dir/file", 42);
274   const path file_out_of_dir = env.create_file("file1", 101);
275   const path sym = env.create_symlink("dir/file", "sym");
276   const path sym_in_dir = env.create_symlink("dir/file", "dir/sym2");
277 
278   const perms old_perms = status(dir).permissions();
279 
280 #define CHECK_ACCESS(func, expect)                                             \
281   ec = GetTestEC();                                                            \
282   TEST_CHECK(ent.func(ec) == expect);                                          \
283   TEST_CHECK(ErrorIs(ec, std::errc::permission_denied))
284 
285   // test file doesn't exist
286   {
287     directory_entry ent(file);
288 
289     TEST_CHECK(!ent.is_symlink());
290     TEST_CHECK(ent.is_regular_file());
291     TEST_CHECK(ent.exists());
292 
293     permissions(dir, perms::none);
294     std::error_code ec = GetTestEC();
295     ent.refresh(ec);
296     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
297 
298     CHECK_ACCESS(exists, false);
299     CHECK_ACCESS(is_symlink, false);
300     CHECK_ACCESS(last_write_time, file_time_type::min());
301     CHECK_ACCESS(hard_link_count, uintmax_t(-1));
302   }
303   permissions(dir, old_perms);
304   {
305     directory_entry ent(sym_in_dir);
306     TEST_CHECK(ent.is_symlink());
307     TEST_CHECK(ent.is_regular_file());
308     TEST_CHECK(ent.exists());
309 
310     permissions(dir, perms::none);
311     std::error_code ec = GetTestEC();
312     ent.refresh(ec);
313     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
314 
315     CHECK_ACCESS(exists, false);
316     CHECK_ACCESS(is_symlink, false);
317     CHECK_ACCESS(last_write_time, file_time_type::min());
318     CHECK_ACCESS(hard_link_count, uintmax_t(-1));
319   }
320   permissions(dir, old_perms);
321   {
322     directory_entry ent(sym);
323     TEST_CHECK(ent.is_symlink());
324     TEST_CHECK(ent.is_regular_file());
325     TEST_CHECK(ent.exists());
326 
327     permissions(dir, perms::none);
328     std::error_code ec = GetTestEC();
329     ent.refresh(ec);
330     TEST_CHECK(!ec);
331     TEST_CHECK(ent.is_symlink());
332 
333     CHECK_ACCESS(exists, false);
334     CHECK_ACCESS(is_regular_file, false);
335     CHECK_ACCESS(last_write_time, file_time_type::min());
336     CHECK_ACCESS(hard_link_count, uintmax_t(-1));
337   }
338 #undef CHECK_ACCESS
339 }
340 
341 TEST_SUITE_END()
342