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