1d780102eSjmmv //
2d780102eSjmmv // Automated Testing Framework (atf)
3d780102eSjmmv //
4d780102eSjmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
5d780102eSjmmv // All rights reserved.
6d780102eSjmmv //
7d780102eSjmmv // Redistribution and use in source and binary forms, with or without
8d780102eSjmmv // modification, are permitted provided that the following conditions
9d780102eSjmmv // are met:
10d780102eSjmmv // 1. Redistributions of source code must retain the above copyright
11d780102eSjmmv // notice, this list of conditions and the following disclaimer.
12d780102eSjmmv // 2. Redistributions in binary form must reproduce the above copyright
13d780102eSjmmv // notice, this list of conditions and the following disclaimer in the
14d780102eSjmmv // documentation and/or other materials provided with the distribution.
15d780102eSjmmv //
16d780102eSjmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17d780102eSjmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18d780102eSjmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19d780102eSjmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20d780102eSjmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21d780102eSjmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d780102eSjmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23d780102eSjmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24d780102eSjmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25d780102eSjmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26d780102eSjmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27d780102eSjmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28d780102eSjmmv //
29d780102eSjmmv
30d780102eSjmmv extern "C" {
31d780102eSjmmv #include <sys/param.h>
32d780102eSjmmv #include <sys/types.h>
33d780102eSjmmv #include <sys/mount.h>
34d780102eSjmmv #include <sys/stat.h>
35d780102eSjmmv #include <sys/wait.h>
36d780102eSjmmv
37d780102eSjmmv #include <dirent.h>
38d780102eSjmmv #include <libgen.h>
39d780102eSjmmv #include <unistd.h>
40d780102eSjmmv }
41d780102eSjmmv
42d780102eSjmmv #include <cassert>
43d780102eSjmmv #include <cerrno>
44d780102eSjmmv #include <cstdlib>
45d780102eSjmmv #include <cstring>
46d780102eSjmmv
47d780102eSjmmv #include "auto_array.hpp"
48d780102eSjmmv #include "env.hpp"
49d780102eSjmmv #include "exceptions.hpp"
50d780102eSjmmv #include "fs.hpp"
51d780102eSjmmv #include "text.hpp"
52d780102eSjmmv #include "user.hpp"
53d780102eSjmmv
54d780102eSjmmv namespace impl = tools::fs;
55d780102eSjmmv #define IMPL_NAME "tools::fs"
56d780102eSjmmv
57d780102eSjmmv // ------------------------------------------------------------------------
58d780102eSjmmv // Auxiliary functions.
59d780102eSjmmv // ------------------------------------------------------------------------
60d780102eSjmmv
61d780102eSjmmv static void cleanup_aux(const impl::path&, dev_t, bool);
62d780102eSjmmv static void cleanup_aux_dir(const impl::path&, const impl::file_info&,
63d780102eSjmmv bool);
64d780102eSjmmv static void do_unmount(const impl::path&);
65d780102eSjmmv static bool safe_access(const impl::path&, int, int);
66d780102eSjmmv
67d780102eSjmmv static const int access_f = 1 << 0;
68d780102eSjmmv static const int access_r = 1 << 1;
69d780102eSjmmv static const int access_w = 1 << 2;
70d780102eSjmmv static const int access_x = 1 << 3;
71d780102eSjmmv
72d780102eSjmmv //!
73d780102eSjmmv //! An implementation of access(2) but using the effective user value
74d780102eSjmmv //! instead of the real one. Also avoids false positives for root when
75d780102eSjmmv //! asking for execute permissions, which appear in SunOS.
76d780102eSjmmv //!
77d780102eSjmmv static
78d780102eSjmmv void
eaccess(const tools::fs::path & p,int mode)79d780102eSjmmv eaccess(const tools::fs::path& p, int mode)
80d780102eSjmmv {
81d780102eSjmmv assert(mode & access_f || mode & access_r ||
82d780102eSjmmv mode & access_w || mode & access_x);
83d780102eSjmmv
84d780102eSjmmv struct stat st;
85d780102eSjmmv if (lstat(p.c_str(), &st) == -1)
86d780102eSjmmv throw tools::system_error(IMPL_NAME "::eaccess",
87d780102eSjmmv "Cannot get information from file " +
88d780102eSjmmv p.str(), errno);
89d780102eSjmmv
90d780102eSjmmv /* Early return if we are only checking for existence and the file
91d780102eSjmmv * exists (stat call returned). */
92d780102eSjmmv if (mode & access_f)
93d780102eSjmmv return;
94d780102eSjmmv
95d780102eSjmmv bool ok = false;
96d780102eSjmmv if (tools::user::is_root()) {
97d780102eSjmmv if (!ok && !(mode & access_x)) {
98d780102eSjmmv /* Allow root to read/write any file. */
99d780102eSjmmv ok = true;
100d780102eSjmmv }
101d780102eSjmmv
102d780102eSjmmv if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
103d780102eSjmmv /* Allow root to execute the file if any of its execution bits
104d780102eSjmmv * are set. */
105d780102eSjmmv ok = true;
106d780102eSjmmv }
107d780102eSjmmv } else {
108d780102eSjmmv if (!ok && (tools::user::euid() == st.st_uid)) {
109d780102eSjmmv ok = ((mode & access_r) && (st.st_mode & S_IRUSR)) ||
110d780102eSjmmv ((mode & access_w) && (st.st_mode & S_IWUSR)) ||
111d780102eSjmmv ((mode & access_x) && (st.st_mode & S_IXUSR));
112d780102eSjmmv }
113d780102eSjmmv if (!ok && tools::user::is_member_of_group(st.st_gid)) {
114d780102eSjmmv ok = ((mode & access_r) && (st.st_mode & S_IRGRP)) ||
115d780102eSjmmv ((mode & access_w) && (st.st_mode & S_IWGRP)) ||
116d780102eSjmmv ((mode & access_x) && (st.st_mode & S_IXGRP));
117d780102eSjmmv }
118d780102eSjmmv if (!ok && ((tools::user::euid() != st.st_uid) &&
119d780102eSjmmv !tools::user::is_member_of_group(st.st_gid))) {
120d780102eSjmmv ok = ((mode & access_r) && (st.st_mode & S_IROTH)) ||
121d780102eSjmmv ((mode & access_w) && (st.st_mode & S_IWOTH)) ||
122d780102eSjmmv ((mode & access_x) && (st.st_mode & S_IXOTH));
123d780102eSjmmv }
124d780102eSjmmv }
125d780102eSjmmv
126d780102eSjmmv if (!ok)
127d780102eSjmmv throw tools::system_error(IMPL_NAME "::eaccess", "Access check failed",
128d780102eSjmmv EACCES);
129d780102eSjmmv }
130d780102eSjmmv
131d780102eSjmmv //!
132d780102eSjmmv //! \brief A controlled version of access(2).
133d780102eSjmmv //!
134d780102eSjmmv //! This function reimplements the standard access(2) system call to
135d780102eSjmmv //! safely control its exit status and raise an exception in case of
136d780102eSjmmv //! failure.
137d780102eSjmmv //!
138d780102eSjmmv static
139d780102eSjmmv bool
safe_access(const impl::path & p,int mode,int experr)140d780102eSjmmv safe_access(const impl::path& p, int mode, int experr)
141d780102eSjmmv {
142d780102eSjmmv try {
143d780102eSjmmv eaccess(p, mode);
144d780102eSjmmv return true;
145d780102eSjmmv } catch (const tools::system_error& e) {
146d780102eSjmmv if (e.code() == experr)
147d780102eSjmmv return false;
148d780102eSjmmv else
149d780102eSjmmv throw e;
150d780102eSjmmv }
151d780102eSjmmv }
152d780102eSjmmv
153d780102eSjmmv // The cleanup routines below are tricky: they are executed immediately after
154d780102eSjmmv // a test case's death, and after we have forcibly killed any stale processes.
155d780102eSjmmv // However, even if the processes are dead, this does not mean that the file
156d780102eSjmmv // system we are scanning is stable. In particular, if the test case has
157d780102eSjmmv // mounted file systems through fuse/puffs, the fact that the processes died
158d780102eSjmmv // does not mean that the file system is truly unmounted.
159d780102eSjmmv //
160d780102eSjmmv // The code below attempts to cope with this by catching errors and either
161d780102eSjmmv // ignoring them or retrying the actions on the same file/directory a few times
162d780102eSjmmv // before giving up.
163d780102eSjmmv static const int max_retries = 5;
164d780102eSjmmv static const int retry_delay_in_seconds = 1;
165d780102eSjmmv
166d780102eSjmmv // The erase parameter in this routine is to control nested mount points.
167d780102eSjmmv // We want to descend into a mount point to unmount anything that is
168d780102eSjmmv // mounted under it, but we do not want to delete any files while doing
169d780102eSjmmv // this traversal. In other words, we erase files until we cross the
170d780102eSjmmv // first mount point, and after that point we only scan and unmount.
171d780102eSjmmv static
172d780102eSjmmv void
cleanup_aux(const impl::path & p,dev_t parent_device,bool erase)173d780102eSjmmv cleanup_aux(const impl::path& p, dev_t parent_device, bool erase)
174d780102eSjmmv {
175d780102eSjmmv try {
176d780102eSjmmv impl::file_info fi(p);
177d780102eSjmmv
178d780102eSjmmv if (fi.get_type() == impl::file_info::dir_type)
179d780102eSjmmv cleanup_aux_dir(p, fi, fi.get_device() == parent_device);
180d780102eSjmmv
181d780102eSjmmv if (fi.get_device() != parent_device)
182d780102eSjmmv do_unmount(p);
183d780102eSjmmv
184d780102eSjmmv if (erase) {
185d780102eSjmmv if (fi.get_type() == impl::file_info::dir_type)
186d780102eSjmmv impl::rmdir(p);
187d780102eSjmmv else
188d780102eSjmmv impl::remove(p);
189d780102eSjmmv }
190d780102eSjmmv } catch (const tools::system_error& e) {
191d780102eSjmmv if (e.code() != ENOENT && e.code() != ENOTDIR)
192d780102eSjmmv throw e;
193d780102eSjmmv }
194d780102eSjmmv }
195d780102eSjmmv
196d780102eSjmmv static
197d780102eSjmmv void
cleanup_aux_dir(const impl::path & p,const impl::file_info & fi,bool erase)198d780102eSjmmv cleanup_aux_dir(const impl::path& p, const impl::file_info& fi,
199d780102eSjmmv bool erase)
200d780102eSjmmv {
201d780102eSjmmv if (erase && ((fi.get_mode() & S_IRWXU) != S_IRWXU)) {
202d780102eSjmmv int retries = max_retries;
203d780102eSjmmv retry_chmod:
204d780102eSjmmv if (chmod(p.c_str(), fi.get_mode() | S_IRWXU) == -1) {
205d780102eSjmmv if (retries > 0) {
206d780102eSjmmv retries--;
207d780102eSjmmv ::sleep(retry_delay_in_seconds);
208d780102eSjmmv goto retry_chmod;
209d780102eSjmmv } else {
210d780102eSjmmv throw tools::system_error(IMPL_NAME "::cleanup(" +
211d780102eSjmmv p.str() + ")", "chmod(2) failed",
212d780102eSjmmv errno);
213d780102eSjmmv }
214d780102eSjmmv }
215d780102eSjmmv }
216d780102eSjmmv
217d780102eSjmmv std::set< std::string > subdirs;
218d780102eSjmmv {
219d780102eSjmmv bool ok = false;
220d780102eSjmmv int retries = max_retries;
221d780102eSjmmv while (!ok) {
222d780102eSjmmv assert(retries > 0);
223d780102eSjmmv try {
224d780102eSjmmv const impl::directory d(p);
225d780102eSjmmv subdirs = d.names();
226d780102eSjmmv ok = true;
227d780102eSjmmv } catch (const tools::system_error& e) {
228d780102eSjmmv retries--;
229d780102eSjmmv if (retries == 0)
230d780102eSjmmv throw e;
231d780102eSjmmv ::sleep(retry_delay_in_seconds);
232d780102eSjmmv }
233d780102eSjmmv }
234d780102eSjmmv assert(ok);
235d780102eSjmmv }
236d780102eSjmmv
237d780102eSjmmv for (std::set< std::string >::const_iterator iter = subdirs.begin();
238d780102eSjmmv iter != subdirs.end(); iter++) {
239d780102eSjmmv const std::string& name = *iter;
240d780102eSjmmv if (name != "." && name != "..")
241d780102eSjmmv cleanup_aux(p / name, fi.get_device(), erase);
242d780102eSjmmv }
243d780102eSjmmv }
244d780102eSjmmv
245d780102eSjmmv static
246d780102eSjmmv void
do_unmount(const impl::path & in_path)247d780102eSjmmv do_unmount(const impl::path& in_path)
248d780102eSjmmv {
249d780102eSjmmv // At least, FreeBSD's unmount(2) requires the path to be absolute.
250d780102eSjmmv // Let's make it absolute in all cases just to be safe that this does
251d780102eSjmmv // not affect other systems.
252d780102eSjmmv const impl::path& abs_path = in_path.is_absolute() ?
253d780102eSjmmv in_path : in_path.to_absolute();
254d780102eSjmmv
255d780102eSjmmv int retries = max_retries;
256d780102eSjmmv retry_unmount:
257d780102eSjmmv if (unmount(abs_path.c_str(), 0) == -1) {
258d780102eSjmmv if (errno == EBUSY && retries > 0) {
259d780102eSjmmv retries--;
260d780102eSjmmv ::sleep(retry_delay_in_seconds);
261d780102eSjmmv goto retry_unmount;
262d780102eSjmmv } else {
263d780102eSjmmv throw tools::system_error(IMPL_NAME "::cleanup(" + in_path.str() +
264d780102eSjmmv ")", "unmount(2) failed", errno);
265d780102eSjmmv }
266d780102eSjmmv }
267d780102eSjmmv }
268d780102eSjmmv
269d780102eSjmmv static
270d780102eSjmmv std::string
normalize(const std::string & in)271d780102eSjmmv normalize(const std::string& in)
272d780102eSjmmv {
273d780102eSjmmv assert(!in.empty());
274d780102eSjmmv
275d780102eSjmmv std::string out;
276d780102eSjmmv
277d780102eSjmmv std::string::size_type pos = 0;
278d780102eSjmmv do {
279d780102eSjmmv const std::string::size_type next_pos = in.find('/', pos);
280d780102eSjmmv
281d780102eSjmmv const std::string component = in.substr(pos, next_pos - pos);
282d780102eSjmmv if (!component.empty()) {
283d780102eSjmmv if (pos == 0)
284d780102eSjmmv out += component;
285d780102eSjmmv else if (component != ".")
286d780102eSjmmv out += "/" + component;
287d780102eSjmmv }
288d780102eSjmmv
289d780102eSjmmv if (next_pos == std::string::npos)
290d780102eSjmmv pos = next_pos;
291d780102eSjmmv else
292d780102eSjmmv pos = next_pos + 1;
293d780102eSjmmv } while (pos != std::string::npos);
294d780102eSjmmv
295d780102eSjmmv return out.empty() ? "/" : out;
296d780102eSjmmv }
297d780102eSjmmv
298d780102eSjmmv // ------------------------------------------------------------------------
299d780102eSjmmv // The "path" class.
300d780102eSjmmv // ------------------------------------------------------------------------
301d780102eSjmmv
path(const std::string & s)302d780102eSjmmv impl::path::path(const std::string& s) :
303d780102eSjmmv m_data(normalize(s))
304d780102eSjmmv {
305d780102eSjmmv }
306d780102eSjmmv
~path(void)307d780102eSjmmv impl::path::~path(void)
308d780102eSjmmv {
309d780102eSjmmv }
310d780102eSjmmv
311d780102eSjmmv const char*
c_str(void) const312d780102eSjmmv impl::path::c_str(void)
313d780102eSjmmv const
314d780102eSjmmv {
315d780102eSjmmv return m_data.c_str();
316d780102eSjmmv }
317d780102eSjmmv
318d780102eSjmmv std::string
str(void) const319d780102eSjmmv impl::path::str(void)
320d780102eSjmmv const
321d780102eSjmmv {
322d780102eSjmmv return m_data;
323d780102eSjmmv }
324d780102eSjmmv
325d780102eSjmmv bool
is_absolute(void) const326d780102eSjmmv impl::path::is_absolute(void)
327d780102eSjmmv const
328d780102eSjmmv {
329d780102eSjmmv return !m_data.empty() && m_data[0] == '/';
330d780102eSjmmv }
331d780102eSjmmv
332d780102eSjmmv bool
is_root(void) const333d780102eSjmmv impl::path::is_root(void)
334d780102eSjmmv const
335d780102eSjmmv {
336d780102eSjmmv return m_data == "/";
337d780102eSjmmv }
338d780102eSjmmv
339d780102eSjmmv impl::path
branch_path(void) const340d780102eSjmmv impl::path::branch_path(void)
341d780102eSjmmv const
342d780102eSjmmv {
343d780102eSjmmv const std::string::size_type endpos = m_data.rfind('/');
344d780102eSjmmv if (endpos == std::string::npos)
345d780102eSjmmv return path(".");
346d780102eSjmmv else if (endpos == 0)
347d780102eSjmmv return path("/");
348d780102eSjmmv else
349d780102eSjmmv return path(m_data.substr(0, endpos));
350d780102eSjmmv }
351d780102eSjmmv
352d780102eSjmmv std::string
leaf_name(void) const353d780102eSjmmv impl::path::leaf_name(void)
354d780102eSjmmv const
355d780102eSjmmv {
356d780102eSjmmv std::string::size_type begpos = m_data.rfind('/');
357d780102eSjmmv if (begpos == std::string::npos)
358d780102eSjmmv begpos = 0;
359d780102eSjmmv else
360d780102eSjmmv begpos++;
361d780102eSjmmv
362d780102eSjmmv return m_data.substr(begpos);
363d780102eSjmmv }
364d780102eSjmmv
365d780102eSjmmv impl::path
to_absolute(void) const366d780102eSjmmv impl::path::to_absolute(void)
367d780102eSjmmv const
368d780102eSjmmv {
369d780102eSjmmv assert(!is_absolute());
370d780102eSjmmv return get_current_dir() / m_data;
371d780102eSjmmv }
372d780102eSjmmv
373d780102eSjmmv bool
operator ==(const path & p) const374d780102eSjmmv impl::path::operator==(const path& p)
375d780102eSjmmv const
376d780102eSjmmv {
377d780102eSjmmv return m_data == p.m_data;
378d780102eSjmmv }
379d780102eSjmmv
380d780102eSjmmv bool
operator !=(const path & p) const381d780102eSjmmv impl::path::operator!=(const path& p)
382d780102eSjmmv const
383d780102eSjmmv {
384d780102eSjmmv return m_data != p.m_data;
385d780102eSjmmv }
386d780102eSjmmv
387d780102eSjmmv impl::path
operator /(const std::string & p) const388d780102eSjmmv impl::path::operator/(const std::string& p)
389d780102eSjmmv const
390d780102eSjmmv {
391d780102eSjmmv return path(m_data + "/" + normalize(p));
392d780102eSjmmv }
393d780102eSjmmv
394d780102eSjmmv impl::path
operator /(const path & p) const395d780102eSjmmv impl::path::operator/(const path& p)
396d780102eSjmmv const
397d780102eSjmmv {
398d780102eSjmmv return path(m_data) / p.m_data;
399d780102eSjmmv }
400d780102eSjmmv
401d780102eSjmmv bool
operator <(const path & p) const402d780102eSjmmv impl::path::operator<(const path& p)
403d780102eSjmmv const
404d780102eSjmmv {
405d780102eSjmmv return std::strcmp(m_data.c_str(), p.m_data.c_str()) < 0;
406d780102eSjmmv }
407d780102eSjmmv
408d780102eSjmmv // ------------------------------------------------------------------------
409d780102eSjmmv // The "file_info" class.
410d780102eSjmmv // ------------------------------------------------------------------------
411d780102eSjmmv
412d780102eSjmmv const int impl::file_info::blk_type = 1;
413d780102eSjmmv const int impl::file_info::chr_type = 2;
414d780102eSjmmv const int impl::file_info::dir_type = 3;
415d780102eSjmmv const int impl::file_info::fifo_type = 4;
416d780102eSjmmv const int impl::file_info::lnk_type = 5;
417d780102eSjmmv const int impl::file_info::reg_type = 6;
418d780102eSjmmv const int impl::file_info::sock_type = 7;
419d780102eSjmmv const int impl::file_info::wht_type = 8;
420d780102eSjmmv
file_info(const path & p)421d780102eSjmmv impl::file_info::file_info(const path& p)
422d780102eSjmmv {
423d780102eSjmmv if (lstat(p.c_str(), &m_sb) == -1)
424d780102eSjmmv throw system_error(IMPL_NAME "::file_info",
425d780102eSjmmv "Cannot get information of " + p.str() + "; " +
426d780102eSjmmv "lstat(2) failed", errno);
427d780102eSjmmv
428d780102eSjmmv int type = m_sb.st_mode & S_IFMT;
429d780102eSjmmv switch (type) {
430d780102eSjmmv case S_IFBLK: m_type = blk_type; break;
431d780102eSjmmv case S_IFCHR: m_type = chr_type; break;
432d780102eSjmmv case S_IFDIR: m_type = dir_type; break;
433d780102eSjmmv case S_IFIFO: m_type = fifo_type; break;
434d780102eSjmmv case S_IFLNK: m_type = lnk_type; break;
435d780102eSjmmv case S_IFREG: m_type = reg_type; break;
436d780102eSjmmv case S_IFSOCK: m_type = sock_type; break;
437d780102eSjmmv case S_IFWHT: m_type = wht_type; break;
438d780102eSjmmv default:
439d780102eSjmmv throw system_error(IMPL_NAME "::file_info", "Unknown file type "
440d780102eSjmmv "error", EINVAL);
441d780102eSjmmv }
442d780102eSjmmv }
443d780102eSjmmv
~file_info(void)444d780102eSjmmv impl::file_info::~file_info(void)
445d780102eSjmmv {
446d780102eSjmmv }
447d780102eSjmmv
448d780102eSjmmv dev_t
get_device(void) const449d780102eSjmmv impl::file_info::get_device(void)
450d780102eSjmmv const
451d780102eSjmmv {
452d780102eSjmmv return m_sb.st_dev;
453d780102eSjmmv }
454d780102eSjmmv
455d780102eSjmmv ino_t
get_inode(void) const456d780102eSjmmv impl::file_info::get_inode(void)
457d780102eSjmmv const
458d780102eSjmmv {
459d780102eSjmmv return m_sb.st_ino;
460d780102eSjmmv }
461d780102eSjmmv
462d780102eSjmmv mode_t
get_mode(void) const463d780102eSjmmv impl::file_info::get_mode(void)
464d780102eSjmmv const
465d780102eSjmmv {
466d780102eSjmmv return m_sb.st_mode & ~S_IFMT;
467d780102eSjmmv }
468d780102eSjmmv
469d780102eSjmmv off_t
get_size(void) const470d780102eSjmmv impl::file_info::get_size(void)
471d780102eSjmmv const
472d780102eSjmmv {
473d780102eSjmmv return m_sb.st_size;
474d780102eSjmmv }
475d780102eSjmmv
476d780102eSjmmv int
get_type(void) const477d780102eSjmmv impl::file_info::get_type(void)
478d780102eSjmmv const
479d780102eSjmmv {
480d780102eSjmmv return m_type;
481d780102eSjmmv }
482d780102eSjmmv
483d780102eSjmmv bool
is_owner_readable(void) const484d780102eSjmmv impl::file_info::is_owner_readable(void)
485d780102eSjmmv const
486d780102eSjmmv {
487d780102eSjmmv return m_sb.st_mode & S_IRUSR;
488d780102eSjmmv }
489d780102eSjmmv
490d780102eSjmmv bool
is_owner_writable(void) const491d780102eSjmmv impl::file_info::is_owner_writable(void)
492d780102eSjmmv const
493d780102eSjmmv {
494d780102eSjmmv return m_sb.st_mode & S_IWUSR;
495d780102eSjmmv }
496d780102eSjmmv
497d780102eSjmmv bool
is_owner_executable(void) const498d780102eSjmmv impl::file_info::is_owner_executable(void)
499d780102eSjmmv const
500d780102eSjmmv {
501d780102eSjmmv return m_sb.st_mode & S_IXUSR;
502d780102eSjmmv }
503d780102eSjmmv
504d780102eSjmmv bool
is_group_readable(void) const505d780102eSjmmv impl::file_info::is_group_readable(void)
506d780102eSjmmv const
507d780102eSjmmv {
508d780102eSjmmv return m_sb.st_mode & S_IRGRP;
509d780102eSjmmv }
510d780102eSjmmv
511d780102eSjmmv bool
is_group_writable(void) const512d780102eSjmmv impl::file_info::is_group_writable(void)
513d780102eSjmmv const
514d780102eSjmmv {
515d780102eSjmmv return m_sb.st_mode & S_IWGRP;
516d780102eSjmmv }
517d780102eSjmmv
518d780102eSjmmv bool
is_group_executable(void) const519d780102eSjmmv impl::file_info::is_group_executable(void)
520d780102eSjmmv const
521d780102eSjmmv {
522d780102eSjmmv return m_sb.st_mode & S_IXGRP;
523d780102eSjmmv }
524d780102eSjmmv
525d780102eSjmmv bool
is_other_readable(void) const526d780102eSjmmv impl::file_info::is_other_readable(void)
527d780102eSjmmv const
528d780102eSjmmv {
529d780102eSjmmv return m_sb.st_mode & S_IROTH;
530d780102eSjmmv }
531d780102eSjmmv
532d780102eSjmmv bool
is_other_writable(void) const533d780102eSjmmv impl::file_info::is_other_writable(void)
534d780102eSjmmv const
535d780102eSjmmv {
536d780102eSjmmv return m_sb.st_mode & S_IWOTH;
537d780102eSjmmv }
538d780102eSjmmv
539d780102eSjmmv bool
is_other_executable(void) const540d780102eSjmmv impl::file_info::is_other_executable(void)
541d780102eSjmmv const
542d780102eSjmmv {
543d780102eSjmmv return m_sb.st_mode & S_IXOTH;
544d780102eSjmmv }
545d780102eSjmmv
546d780102eSjmmv // ------------------------------------------------------------------------
547d780102eSjmmv // The "directory" class.
548d780102eSjmmv // ------------------------------------------------------------------------
549d780102eSjmmv
directory(const path & p)550d780102eSjmmv impl::directory::directory(const path& p)
551d780102eSjmmv {
552d780102eSjmmv DIR* dp = ::opendir(p.c_str());
553d780102eSjmmv if (dp == NULL)
554d780102eSjmmv throw system_error(IMPL_NAME "::directory::directory(" +
555d780102eSjmmv p.str() + ")", "opendir(3) failed", errno);
556d780102eSjmmv
557d780102eSjmmv struct dirent* dep;
558d780102eSjmmv while ((dep = ::readdir(dp)) != NULL) {
559d780102eSjmmv path entryp = p / dep->d_name;
560d780102eSjmmv insert(value_type(dep->d_name, file_info(entryp)));
561d780102eSjmmv }
562d780102eSjmmv
563d780102eSjmmv if (::closedir(dp) == -1)
564d780102eSjmmv throw system_error(IMPL_NAME "::directory::directory(" +
565d780102eSjmmv p.str() + ")", "closedir(3) failed", errno);
566d780102eSjmmv }
567d780102eSjmmv
568d780102eSjmmv std::set< std::string >
names(void) const569d780102eSjmmv impl::directory::names(void)
570d780102eSjmmv const
571d780102eSjmmv {
572d780102eSjmmv std::set< std::string > ns;
573d780102eSjmmv
574d780102eSjmmv for (const_iterator iter = begin(); iter != end(); iter++)
575d780102eSjmmv ns.insert((*iter).first);
576d780102eSjmmv
577d780102eSjmmv return ns;
578d780102eSjmmv }
579d780102eSjmmv
580d780102eSjmmv // ------------------------------------------------------------------------
581d780102eSjmmv // The "temp_dir" class.
582d780102eSjmmv // ------------------------------------------------------------------------
583d780102eSjmmv
temp_dir(const path & p)584d780102eSjmmv impl::temp_dir::temp_dir(const path& p)
585d780102eSjmmv {
586d780102eSjmmv tools::auto_array< char > buf(new char[p.str().length() + 1]);
587d780102eSjmmv std::strcpy(buf.get(), p.c_str());
588d780102eSjmmv if (::mkdtemp(buf.get()) == NULL)
589d780102eSjmmv throw tools::system_error(IMPL_NAME "::temp_dir::temp_dir(" +
590d780102eSjmmv p.str() + ")", "mkdtemp(3) failed",
591d780102eSjmmv errno);
592d780102eSjmmv
593d780102eSjmmv m_path.reset(new path(buf.get()));
594d780102eSjmmv }
595d780102eSjmmv
~temp_dir(void)596d780102eSjmmv impl::temp_dir::~temp_dir(void)
597d780102eSjmmv {
598d780102eSjmmv cleanup(*m_path);
599d780102eSjmmv }
600d780102eSjmmv
601d780102eSjmmv const impl::path&
get_path(void) const602d780102eSjmmv impl::temp_dir::get_path(void)
603d780102eSjmmv const
604d780102eSjmmv {
605d780102eSjmmv return *m_path;
606d780102eSjmmv }
607d780102eSjmmv
608d780102eSjmmv // ------------------------------------------------------------------------
609d780102eSjmmv // Free functions.
610d780102eSjmmv // ------------------------------------------------------------------------
611d780102eSjmmv
612d780102eSjmmv bool
exists(const path & p)613d780102eSjmmv impl::exists(const path& p)
614d780102eSjmmv {
615d780102eSjmmv try {
616d780102eSjmmv eaccess(p, access_f);
617d780102eSjmmv return true;
618d780102eSjmmv } catch (const system_error& e) {
619d780102eSjmmv if (e.code() == ENOENT)
620d780102eSjmmv return false;
621d780102eSjmmv else
622d780102eSjmmv throw;
623d780102eSjmmv }
624d780102eSjmmv }
625d780102eSjmmv
626d780102eSjmmv bool
have_prog_in_path(const std::string & prog)627d780102eSjmmv impl::have_prog_in_path(const std::string& prog)
628d780102eSjmmv {
629d780102eSjmmv assert(prog.find('/') == std::string::npos);
630d780102eSjmmv
631d780102eSjmmv // Do not bother to provide a default value for PATH. If it is not
632d780102eSjmmv // there something is broken in the user's environment.
633d780102eSjmmv if (!tools::env::has("PATH"))
634d780102eSjmmv throw std::runtime_error("PATH not defined in the environment");
635d780102eSjmmv std::vector< std::string > dirs =
636d780102eSjmmv tools::text::split(tools::env::get("PATH"), ":");
637d780102eSjmmv
638d780102eSjmmv bool found = false;
639d780102eSjmmv for (std::vector< std::string >::const_iterator iter = dirs.begin();
640d780102eSjmmv !found && iter != dirs.end(); iter++) {
641d780102eSjmmv const path& dir = path(*iter);
642d780102eSjmmv
643d780102eSjmmv if (is_executable(dir / prog))
644d780102eSjmmv found = true;
645d780102eSjmmv }
646d780102eSjmmv return found;
647d780102eSjmmv }
648d780102eSjmmv
649d780102eSjmmv bool
is_executable(const path & p)650d780102eSjmmv impl::is_executable(const path& p)
651d780102eSjmmv {
652d780102eSjmmv if (!exists(p))
653d780102eSjmmv return false;
654d780102eSjmmv return safe_access(p, access_x, EACCES);
655d780102eSjmmv }
656d780102eSjmmv
657d780102eSjmmv void
remove(const path & p)658d780102eSjmmv impl::remove(const path& p)
659d780102eSjmmv {
660d780102eSjmmv if (file_info(p).get_type() == file_info::dir_type)
661d780102eSjmmv throw tools::system_error(IMPL_NAME "::remove(" + p.str() + ")",
662d780102eSjmmv "Is a directory",
663d780102eSjmmv EPERM);
664d780102eSjmmv if (::unlink(p.c_str()) == -1)
665d780102eSjmmv throw tools::system_error(IMPL_NAME "::remove(" + p.str() + ")",
666d780102eSjmmv "unlink(" + p.str() + ") failed",
667d780102eSjmmv errno);
668d780102eSjmmv }
669d780102eSjmmv
670d780102eSjmmv void
rmdir(const path & p)671d780102eSjmmv impl::rmdir(const path& p)
672d780102eSjmmv {
673d780102eSjmmv if (::rmdir(p.c_str())) {
674d780102eSjmmv if (errno == EEXIST) {
675d780102eSjmmv /* Some operating systems (e.g. OpenSolaris 200906) return
676d780102eSjmmv * EEXIST instead of ENOTEMPTY for non-empty directories.
677d780102eSjmmv * Homogenize the return value so that callers don't need
678d780102eSjmmv * to bother about differences in operating systems. */
679d780102eSjmmv errno = ENOTEMPTY;
680d780102eSjmmv }
681d780102eSjmmv throw system_error(IMPL_NAME "::rmdir", "Cannot remove directory",
682d780102eSjmmv errno);
683d780102eSjmmv }
684d780102eSjmmv }
685d780102eSjmmv
686*6d3e616aSchristos void
change_ownership(const path & p,const std::pair<int,int> & user)687*6d3e616aSchristos impl::change_ownership(const path& p, const std::pair < int, int >& user)
688*6d3e616aSchristos {
689*6d3e616aSchristos if (::chown(p.c_str(), user.first, user.second) == -1) {
690*6d3e616aSchristos std::stringstream ss;
691*6d3e616aSchristos ss << IMPL_NAME "::chown(" << p.str() << ", " << user.first << ", "
692*6d3e616aSchristos << user.second << ")";
693*6d3e616aSchristos throw tools::system_error(ss.str(), "chown(2) failed", errno);
694*6d3e616aSchristos }
695*6d3e616aSchristos }
696*6d3e616aSchristos
697d780102eSjmmv impl::path
change_directory(const path & dir)698d780102eSjmmv impl::change_directory(const path& dir)
699d780102eSjmmv {
700d780102eSjmmv path olddir = get_current_dir();
701d780102eSjmmv
702d780102eSjmmv if (olddir != dir) {
703d780102eSjmmv if (::chdir(dir.c_str()) == -1)
704d780102eSjmmv throw tools::system_error(IMPL_NAME "::chdir(" + dir.str() + ")",
705d780102eSjmmv "chdir(2) failed", errno);
706d780102eSjmmv }
707d780102eSjmmv
708d780102eSjmmv return olddir;
709d780102eSjmmv }
710d780102eSjmmv
711d780102eSjmmv void
cleanup(const path & p)712d780102eSjmmv impl::cleanup(const path& p)
713d780102eSjmmv {
714d780102eSjmmv impl::file_info fi(p);
715d780102eSjmmv cleanup_aux(p, fi.get_device(), true);
716d780102eSjmmv }
717d780102eSjmmv
718d780102eSjmmv impl::path
get_current_dir(void)719d780102eSjmmv impl::get_current_dir(void)
720d780102eSjmmv {
7210bb4b538Smaya char *cwd = getcwd(NULL, 0);
7220bb4b538Smaya if (cwd == NULL)
723d780102eSjmmv throw tools::system_error(IMPL_NAME "::get_current_dir()",
724d780102eSjmmv "getcwd() failed", errno);
725d780102eSjmmv
7260bb4b538Smaya try {
7270bb4b538Smaya impl::path p(cwd);
7280bb4b538Smaya free(cwd);
7290bb4b538Smaya return p;
7300bb4b538Smaya } catch(...) {
7310bb4b538Smaya free(cwd);
7320bb4b538Smaya throw;
7330bb4b538Smaya }
734d780102eSjmmv }
735