fs.cpp revision 275988
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
32extern "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
47extern "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
58namespace impl = atf::fs;
59#define IMPL_NAME "atf::fs"
60
61// ------------------------------------------------------------------------
62// Auxiliary functions.
63// ------------------------------------------------------------------------
64
65static 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//!
74static
75bool
76safe_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
108impl::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
115impl::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
122impl::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
129impl::path::~path(void)
130{
131    atf_fs_path_fini(&m_path);
132}
133
134const char*
135impl::path::c_str(void)
136    const
137{
138    return atf_fs_path_cstring(&m_path);
139}
140
141const atf_fs_path_t*
142impl::path::c_path(void)
143    const
144{
145    return &m_path;
146}
147
148std::string
149impl::path::str(void)
150    const
151{
152    return c_str();
153}
154
155bool
156impl::path::is_absolute(void)
157    const
158{
159    return atf_fs_path_is_absolute(&m_path);
160}
161
162bool
163impl::path::is_root(void)
164    const
165{
166    return atf_fs_path_is_root(&m_path);
167}
168
169impl::path
170impl::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
185std::string
186impl::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
201impl::path
202impl::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
216impl::path&
217impl::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
232bool
233impl::path::operator==(const path& p)
234    const
235{
236    return atf_equal_fs_path_fs_path(&m_path, &p.m_path);
237}
238
239bool
240impl::path::operator!=(const path& p)
241    const
242{
243    return !atf_equal_fs_path_fs_path(&m_path, &p.m_path);
244}
245
246impl::path
247impl::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
259impl::path
260impl::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
273bool
274impl::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
286const int impl::file_info::blk_type = atf_fs_stat_blk_type;
287const int impl::file_info::chr_type = atf_fs_stat_chr_type;
288const int impl::file_info::dir_type = atf_fs_stat_dir_type;
289const int impl::file_info::fifo_type = atf_fs_stat_fifo_type;
290const int impl::file_info::lnk_type = atf_fs_stat_lnk_type;
291const int impl::file_info::reg_type = atf_fs_stat_reg_type;
292const int impl::file_info::sock_type = atf_fs_stat_sock_type;
293const int impl::file_info::wht_type = atf_fs_stat_wht_type;
294
295impl::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
304impl::file_info::file_info(const file_info& fi)
305{
306    atf_fs_stat_copy(&m_stat, &fi.m_stat);
307}
308
309impl::file_info::~file_info(void)
310{
311    atf_fs_stat_fini(&m_stat);
312}
313
314dev_t
315impl::file_info::get_device(void)
316    const
317{
318    return atf_fs_stat_get_device(&m_stat);
319}
320
321ino_t
322impl::file_info::get_inode(void)
323    const
324{
325    return atf_fs_stat_get_inode(&m_stat);
326}
327
328mode_t
329impl::file_info::get_mode(void)
330    const
331{
332    return atf_fs_stat_get_mode(&m_stat);
333}
334
335off_t
336impl::file_info::get_size(void)
337    const
338{
339    return atf_fs_stat_get_size(&m_stat);
340}
341
342int
343impl::file_info::get_type(void)
344    const
345{
346    return atf_fs_stat_get_type(&m_stat);
347}
348
349bool
350impl::file_info::is_owner_readable(void)
351    const
352{
353    return atf_fs_stat_is_owner_readable(&m_stat);
354}
355
356bool
357impl::file_info::is_owner_writable(void)
358    const
359{
360    return atf_fs_stat_is_owner_writable(&m_stat);
361}
362
363bool
364impl::file_info::is_owner_executable(void)
365    const
366{
367    return atf_fs_stat_is_owner_executable(&m_stat);
368}
369
370bool
371impl::file_info::is_group_readable(void)
372    const
373{
374    return atf_fs_stat_is_group_readable(&m_stat);
375}
376
377bool
378impl::file_info::is_group_writable(void)
379    const
380{
381    return atf_fs_stat_is_group_writable(&m_stat);
382}
383
384bool
385impl::file_info::is_group_executable(void)
386    const
387{
388    return atf_fs_stat_is_group_executable(&m_stat);
389}
390
391bool
392impl::file_info::is_other_readable(void)
393    const
394{
395    return atf_fs_stat_is_other_readable(&m_stat);
396}
397
398bool
399impl::file_info::is_other_writable(void)
400    const
401{
402    return atf_fs_stat_is_other_writable(&m_stat);
403}
404
405bool
406impl::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
416impl::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
434std::set< std::string >
435impl::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
450bool
451impl::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
463bool
464impl::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
486bool
487impl::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
494void
495impl::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
507void
508impl::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