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
34extern "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
49extern "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
62namespace impl = atf::fs;
63#define IMPL_NAME "atf::fs"
64
65// ------------------------------------------------------------------------
66// Auxiliary functions.
67// ------------------------------------------------------------------------
68
69static 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//!
78static
79bool
80safe_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
112impl::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
119impl::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
126impl::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
133impl::path::~path(void)
134{
135    atf_fs_path_fini(&m_path);
136}
137
138const char*
139impl::path::c_str(void)
140    const
141{
142    return atf_fs_path_cstring(&m_path);
143}
144
145const atf_fs_path_t*
146impl::path::c_path(void)
147    const
148{
149    return &m_path;
150}
151
152std::string
153impl::path::str(void)
154    const
155{
156    return c_str();
157}
158
159bool
160impl::path::is_absolute(void)
161    const
162{
163    return atf_fs_path_is_absolute(&m_path);
164}
165
166bool
167impl::path::is_root(void)
168    const
169{
170    return atf_fs_path_is_root(&m_path);
171}
172
173impl::path
174impl::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
189std::string
190impl::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
205impl::path
206impl::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
220impl::path&
221impl::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
236bool
237impl::path::operator==(const path& p)
238    const
239{
240    return atf_equal_fs_path_fs_path(&m_path, &p.m_path);
241}
242
243bool
244impl::path::operator!=(const path& p)
245    const
246{
247    return !atf_equal_fs_path_fs_path(&m_path, &p.m_path);
248}
249
250impl::path
251impl::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
263impl::path
264impl::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
277bool
278impl::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
290const int impl::file_info::blk_type = atf_fs_stat_blk_type;
291const int impl::file_info::chr_type = atf_fs_stat_chr_type;
292const int impl::file_info::dir_type = atf_fs_stat_dir_type;
293const int impl::file_info::fifo_type = atf_fs_stat_fifo_type;
294const int impl::file_info::lnk_type = atf_fs_stat_lnk_type;
295const int impl::file_info::reg_type = atf_fs_stat_reg_type;
296const int impl::file_info::sock_type = atf_fs_stat_sock_type;
297const int impl::file_info::wht_type = atf_fs_stat_wht_type;
298
299impl::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
308impl::file_info::file_info(const file_info& fi)
309{
310    atf_fs_stat_copy(&m_stat, &fi.m_stat);
311}
312
313impl::file_info::~file_info(void)
314{
315    atf_fs_stat_fini(&m_stat);
316}
317
318dev_t
319impl::file_info::get_device(void)
320    const
321{
322    return atf_fs_stat_get_device(&m_stat);
323}
324
325ino_t
326impl::file_info::get_inode(void)
327    const
328{
329    return atf_fs_stat_get_inode(&m_stat);
330}
331
332mode_t
333impl::file_info::get_mode(void)
334    const
335{
336    return atf_fs_stat_get_mode(&m_stat);
337}
338
339off_t
340impl::file_info::get_size(void)
341    const
342{
343    return atf_fs_stat_get_size(&m_stat);
344}
345
346int
347impl::file_info::get_type(void)
348    const
349{
350    return atf_fs_stat_get_type(&m_stat);
351}
352
353bool
354impl::file_info::is_owner_readable(void)
355    const
356{
357    return atf_fs_stat_is_owner_readable(&m_stat);
358}
359
360bool
361impl::file_info::is_owner_writable(void)
362    const
363{
364    return atf_fs_stat_is_owner_writable(&m_stat);
365}
366
367bool
368impl::file_info::is_owner_executable(void)
369    const
370{
371    return atf_fs_stat_is_owner_executable(&m_stat);
372}
373
374bool
375impl::file_info::is_group_readable(void)
376    const
377{
378    return atf_fs_stat_is_group_readable(&m_stat);
379}
380
381bool
382impl::file_info::is_group_writable(void)
383    const
384{
385    return atf_fs_stat_is_group_writable(&m_stat);
386}
387
388bool
389impl::file_info::is_group_executable(void)
390    const
391{
392    return atf_fs_stat_is_group_executable(&m_stat);
393}
394
395bool
396impl::file_info::is_other_readable(void)
397    const
398{
399    return atf_fs_stat_is_other_readable(&m_stat);
400}
401
402bool
403impl::file_info::is_other_writable(void)
404    const
405{
406    return atf_fs_stat_is_other_writable(&m_stat);
407}
408
409bool
410impl::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
420impl::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
438std::set< std::string >
439impl::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
454bool
455impl::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
467bool
468impl::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
490bool
491impl::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
498void
499impl::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
511void
512impl::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