xref: /freebsd/contrib/atf/atf-c++/detail/fs.cpp (revision 02e9120893770924227138ba49df1edb3896112a)
1 // Copyright (c) 2007 The NetBSD Foundation, Inc.
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
6 // are met:
7 // 1. Redistributions of source code must retain the above copyright
8 //    notice, this list of conditions and the following disclaimer.
9 // 2. Redistributions in binary form must reproduce the above copyright
10 //    notice, this list of conditions and the following disclaimer in the
11 //    documentation and/or other materials provided with the distribution.
12 //
13 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #include "atf-c++/detail/fs.hpp"
27 
28 #if defined(HAVE_CONFIG_H)
29 #include "config.h"
30 #endif
31 
32 extern "C" {
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/mount.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include <dirent.h>
39 #include <libgen.h>
40 #include <unistd.h>
41 }
42 
43 #include <cerrno>
44 #include <cstdlib>
45 #include <cstring>
46 
47 extern "C" {
48 #include "atf-c/error.h"
49 }
50 
51 #include "atf-c++/detail/env.hpp"
52 #include "atf-c++/detail/exceptions.hpp"
53 #include "atf-c++/detail/process.hpp"
54 #include "atf-c++/detail/sanity.hpp"
55 #include "atf-c++/detail/text.hpp"
56 #include "atf-c++/utils.hpp"
57 
58 namespace impl = atf::fs;
59 #define IMPL_NAME "atf::fs"
60 
61 // ------------------------------------------------------------------------
62 // Auxiliary functions.
63 // ------------------------------------------------------------------------
64 
65 static bool safe_access(const impl::path&, int, int);
66 
67 //!
68 //! \brief A controlled version of access(2).
69 //!
70 //! This function reimplements the standard access(2) system call to
71 //! safely control its exit status and raise an exception in case of
72 //! failure.
73 //!
74 static
75 bool
76 safe_access(const impl::path& p, int mode, int experr)
77 {
78     bool ok;
79 
80     atf_error_t err = atf_fs_eaccess(p.c_path(), mode);
81     if (atf_is_error(err)) {
82         if (atf_error_is(err, "libc")) {
83             if (atf_libc_error_code(err) == experr) {
84                 atf_error_free(err);
85                 ok = false;
86             } else {
87                 atf::throw_atf_error(err);
88                 // XXX Silence warning; maybe throw_atf_error should be
89                 // an exception and not a function.
90                 ok = false;
91             }
92         } else {
93             atf::throw_atf_error(err);
94             // XXX Silence warning; maybe throw_atf_error should be
95             // an exception and not a function.
96             ok = false;
97         }
98     } else
99         ok = true;
100 
101     return ok;
102 }
103 
104 // ------------------------------------------------------------------------
105 // The "path" class.
106 // ------------------------------------------------------------------------
107 
108 impl::path::path(const std::string& s)
109 {
110     atf_error_t err = atf_fs_path_init_fmt(&m_path, "%s", s.c_str());
111     if (atf_is_error(err))
112         throw_atf_error(err);
113 }
114 
115 impl::path::path(const path& p)
116 {
117     atf_error_t err = atf_fs_path_copy(&m_path, &p.m_path);
118     if (atf_is_error(err))
119         throw_atf_error(err);
120 }
121 
122 impl::path::path(const atf_fs_path_t *p)
123 {
124     atf_error_t err = atf_fs_path_copy(&m_path, p);
125     if (atf_is_error(err))
126         throw_atf_error(err);
127 }
128 
129 impl::path::~path(void)
130 {
131     atf_fs_path_fini(&m_path);
132 }
133 
134 const char*
135 impl::path::c_str(void)
136     const
137 {
138     return atf_fs_path_cstring(&m_path);
139 }
140 
141 const atf_fs_path_t*
142 impl::path::c_path(void)
143     const
144 {
145     return &m_path;
146 }
147 
148 std::string
149 impl::path::str(void)
150     const
151 {
152     return c_str();
153 }
154 
155 bool
156 impl::path::is_absolute(void)
157     const
158 {
159     return atf_fs_path_is_absolute(&m_path);
160 }
161 
162 bool
163 impl::path::is_root(void)
164     const
165 {
166     return atf_fs_path_is_root(&m_path);
167 }
168 
169 impl::path
170 impl::path::branch_path(void)
171     const
172 {
173     atf_fs_path_t bp;
174     atf_error_t err;
175 
176     err = atf_fs_path_branch_path(&m_path, &bp);
177     if (atf_is_error(err))
178         throw_atf_error(err);
179 
180     path p(atf_fs_path_cstring(&bp));
181     atf_fs_path_fini(&bp);
182     return p;
183 }
184 
185 std::string
186 impl::path::leaf_name(void)
187     const
188 {
189     atf_dynstr_t ln;
190     atf_error_t err;
191 
192     err = atf_fs_path_leaf_name(&m_path, &ln);
193     if (atf_is_error(err))
194         throw_atf_error(err);
195 
196     std::string s(atf_dynstr_cstring(&ln));
197     atf_dynstr_fini(&ln);
198     return s;
199 }
200 
201 impl::path
202 impl::path::to_absolute(void)
203     const
204 {
205     atf_fs_path_t pa;
206 
207     atf_error_t err = atf_fs_path_to_absolute(&m_path, &pa);
208     if (atf_is_error(err))
209         throw_atf_error(err);
210 
211     path p(atf_fs_path_cstring(&pa));
212     atf_fs_path_fini(&pa);
213     return p;
214 }
215 
216 impl::path&
217 impl::path::operator=(const path& p)
218 {
219     atf_fs_path_t tmp;
220 
221     atf_error_t err = atf_fs_path_init_fmt(&tmp, "%s", p.c_str());
222     if (atf_is_error(err))
223         throw_atf_error(err);
224     else {
225         atf_fs_path_fini(&m_path);
226         m_path = tmp;
227     }
228 
229     return *this;
230 }
231 
232 bool
233 impl::path::operator==(const path& p)
234     const
235 {
236     return atf_equal_fs_path_fs_path(&m_path, &p.m_path);
237 }
238 
239 bool
240 impl::path::operator!=(const path& p)
241     const
242 {
243     return !atf_equal_fs_path_fs_path(&m_path, &p.m_path);
244 }
245 
246 impl::path
247 impl::path::operator/(const std::string& p)
248     const
249 {
250     path p2 = *this;
251 
252     atf_error_t err = atf_fs_path_append_fmt(&p2.m_path, "%s", p.c_str());
253     if (atf_is_error(err))
254         throw_atf_error(err);
255 
256     return p2;
257 }
258 
259 impl::path
260 impl::path::operator/(const path& p)
261     const
262 {
263     path p2 = *this;
264 
265     atf_error_t err = atf_fs_path_append_fmt(&p2.m_path, "%s",
266                                              atf_fs_path_cstring(&p.m_path));
267     if (atf_is_error(err))
268         throw_atf_error(err);
269 
270     return p2;
271 }
272 
273 bool
274 impl::path::operator<(const path& p)
275     const
276 {
277     const char *s1 = atf_fs_path_cstring(&m_path);
278     const char *s2 = atf_fs_path_cstring(&p.m_path);
279     return std::strcmp(s1, s2) < 0;
280 }
281 
282 // ------------------------------------------------------------------------
283 // The "file_info" class.
284 // ------------------------------------------------------------------------
285 
286 const int impl::file_info::blk_type = atf_fs_stat_blk_type;
287 const int impl::file_info::chr_type = atf_fs_stat_chr_type;
288 const int impl::file_info::dir_type = atf_fs_stat_dir_type;
289 const int impl::file_info::fifo_type = atf_fs_stat_fifo_type;
290 const int impl::file_info::lnk_type = atf_fs_stat_lnk_type;
291 const int impl::file_info::reg_type = atf_fs_stat_reg_type;
292 const int impl::file_info::sock_type = atf_fs_stat_sock_type;
293 const int impl::file_info::wht_type = atf_fs_stat_wht_type;
294 
295 impl::file_info::file_info(const path& p)
296 {
297     atf_error_t err;
298 
299     err = atf_fs_stat_init(&m_stat, p.c_path());
300     if (atf_is_error(err))
301         throw_atf_error(err);
302 }
303 
304 impl::file_info::file_info(const file_info& fi)
305 {
306     atf_fs_stat_copy(&m_stat, &fi.m_stat);
307 }
308 
309 impl::file_info::~file_info(void)
310 {
311     atf_fs_stat_fini(&m_stat);
312 }
313 
314 dev_t
315 impl::file_info::get_device(void)
316     const
317 {
318     return atf_fs_stat_get_device(&m_stat);
319 }
320 
321 ino_t
322 impl::file_info::get_inode(void)
323     const
324 {
325     return atf_fs_stat_get_inode(&m_stat);
326 }
327 
328 mode_t
329 impl::file_info::get_mode(void)
330     const
331 {
332     return atf_fs_stat_get_mode(&m_stat);
333 }
334 
335 off_t
336 impl::file_info::get_size(void)
337     const
338 {
339     return atf_fs_stat_get_size(&m_stat);
340 }
341 
342 int
343 impl::file_info::get_type(void)
344     const
345 {
346     return atf_fs_stat_get_type(&m_stat);
347 }
348 
349 bool
350 impl::file_info::is_owner_readable(void)
351     const
352 {
353     return atf_fs_stat_is_owner_readable(&m_stat);
354 }
355 
356 bool
357 impl::file_info::is_owner_writable(void)
358     const
359 {
360     return atf_fs_stat_is_owner_writable(&m_stat);
361 }
362 
363 bool
364 impl::file_info::is_owner_executable(void)
365     const
366 {
367     return atf_fs_stat_is_owner_executable(&m_stat);
368 }
369 
370 bool
371 impl::file_info::is_group_readable(void)
372     const
373 {
374     return atf_fs_stat_is_group_readable(&m_stat);
375 }
376 
377 bool
378 impl::file_info::is_group_writable(void)
379     const
380 {
381     return atf_fs_stat_is_group_writable(&m_stat);
382 }
383 
384 bool
385 impl::file_info::is_group_executable(void)
386     const
387 {
388     return atf_fs_stat_is_group_executable(&m_stat);
389 }
390 
391 bool
392 impl::file_info::is_other_readable(void)
393     const
394 {
395     return atf_fs_stat_is_other_readable(&m_stat);
396 }
397 
398 bool
399 impl::file_info::is_other_writable(void)
400     const
401 {
402     return atf_fs_stat_is_other_writable(&m_stat);
403 }
404 
405 bool
406 impl::file_info::is_other_executable(void)
407     const
408 {
409     return atf_fs_stat_is_other_executable(&m_stat);
410 }
411 
412 // ------------------------------------------------------------------------
413 // The "directory" class.
414 // ------------------------------------------------------------------------
415 
416 impl::directory::directory(const path& p)
417 {
418     DIR* dp = ::opendir(p.c_str());
419     if (dp == NULL)
420         throw system_error(IMPL_NAME "::directory::directory(" +
421                            p.str() + ")", "opendir(3) failed", errno);
422 
423     struct dirent* dep;
424     while ((dep = ::readdir(dp)) != NULL) {
425         path entryp = p / dep->d_name;
426         insert(value_type(dep->d_name, file_info(entryp)));
427     }
428 
429     if (::closedir(dp) == -1)
430         throw system_error(IMPL_NAME "::directory::directory(" +
431                            p.str() + ")", "closedir(3) failed", errno);
432 }
433 
434 std::set< std::string >
435 impl::directory::names(void)
436     const
437 {
438     std::set< std::string > ns;
439 
440     for (const_iterator iter = begin(); iter != end(); iter++)
441         ns.insert((*iter).first);
442 
443     return ns;
444 }
445 
446 // ------------------------------------------------------------------------
447 // Free functions.
448 // ------------------------------------------------------------------------
449 
450 bool
451 impl::exists(const path& p)
452 {
453     atf_error_t err;
454     bool b;
455 
456     err = atf_fs_exists(p.c_path(), &b);
457     if (atf_is_error(err))
458         throw_atf_error(err);
459 
460     return b;
461 }
462 
463 bool
464 impl::have_prog_in_path(const std::string& prog)
465 {
466     PRE(prog.find('/') == std::string::npos);
467 
468     // Do not bother to provide a default value for PATH.  If it is not
469     // there something is broken in the user's environment.
470     if (!atf::env::has("PATH"))
471         throw std::runtime_error("PATH not defined in the environment");
472     std::vector< std::string > dirs =
473         atf::text::split(atf::env::get("PATH"), ":");
474 
475     bool found = false;
476     for (std::vector< std::string >::const_iterator iter = dirs.begin();
477          !found && iter != dirs.end(); iter++) {
478         const path& dir = path(*iter);
479 
480         if (is_executable(dir / prog))
481             found = true;
482     }
483     return found;
484 }
485 
486 bool
487 impl::is_executable(const path& p)
488 {
489     if (!exists(p))
490         return false;
491     return safe_access(p, atf_fs_access_x, EACCES);
492 }
493 
494 void
495 impl::remove(const path& p)
496 {
497     if (file_info(p).get_type() == file_info::dir_type)
498         throw atf::system_error(IMPL_NAME "::remove(" + p.str() + ")",
499                                 "Is a directory",
500                                 EPERM);
501     if (::unlink(p.c_str()) == -1)
502         throw atf::system_error(IMPL_NAME "::remove(" + p.str() + ")",
503                                 "unlink(" + p.str() + ") failed",
504                                 errno);
505 }
506 
507 void
508 impl::rmdir(const path& p)
509 {
510     atf_error_t err = atf_fs_rmdir(p.c_path());
511     if (atf_is_error(err))
512         throw_atf_error(err);
513 }
514