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