summaryrefslogtreecommitdiffstats
path: root/unit/atf-src/atf-c/detail/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'unit/atf-src/atf-c/detail/fs.c')
-rw-r--r--unit/atf-src/atf-c/detail/fs.c884
1 files changed, 884 insertions, 0 deletions
diff --git a/unit/atf-src/atf-c/detail/fs.c b/unit/atf-src/atf-c/detail/fs.c
new file mode 100644
index 0000000..5ff7648
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs.c
@@ -0,0 +1,884 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/fs.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/detail/user.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Prototypes for auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static bool check_umask(const mode_t, const mode_t);
+static atf_error_t copy_contents(const atf_fs_path_t *, char **);
+static mode_t current_umask(void);
+static atf_error_t do_mkdtemp(char *);
+static atf_error_t normalize(atf_dynstr_t *, char *);
+static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
+static void replace_contents(atf_fs_path_t *, const char *);
+static const char *stat_type_to_string(const int);
+
+/* ---------------------------------------------------------------------
+ * The "invalid_umask" error type.
+ * --------------------------------------------------------------------- */
+
+struct invalid_umask_error_data {
+ /* One of atf_fs_stat_*_type. */
+ int m_type;
+
+ /* The original path causing the error. */
+ /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
+ * from the error constructor, we cannot delete the path later on.
+ * Can't remember why atf_error_new does not take a hook for
+ * deletion. */
+ char m_path[1024];
+
+ /* The umask that caused the error. */
+ mode_t m_umask;
+};
+typedef struct invalid_umask_error_data invalid_umask_error_data_t;
+
+static
+void
+invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const invalid_umask_error_data_t *data;
+
+ PRE(atf_error_is(err, "invalid_umask"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "Could not create the temporary %s %s because "
+ "it will not have enough access rights due to the current "
+ "umask %05o", stat_type_to_string(data->m_type),
+ data->m_path, (unsigned int)data->m_umask);
+}
+
+static
+atf_error_t
+invalid_umask_error(const atf_fs_path_t *path, const int type,
+ const mode_t failing_mask)
+{
+ atf_error_t err;
+ invalid_umask_error_data_t data;
+
+ data.m_type = type;
+
+ strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
+ data.m_path[sizeof(data.m_path) - 1] = '\0';
+
+ data.m_umask = failing_mask;
+
+ err = atf_error_new("invalid_umask", &data, sizeof(data),
+ invalid_umask_format);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "unknown_file_type" error type.
+ * --------------------------------------------------------------------- */
+
+struct unknown_type_error_data {
+ const char *m_path;
+ int m_type;
+};
+typedef struct unknown_type_error_data unknown_type_error_data_t;
+
+static
+void
+unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const unknown_type_error_data_t *data;
+
+ PRE(atf_error_is(err, "unknown_type"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
+ data->m_path);
+}
+
+static
+atf_error_t
+unknown_type_error(const char *path, int type)
+{
+ atf_error_t err;
+ unknown_type_error_data_t data;
+
+ data.m_path = path;
+ data.m_type = type;
+
+ err = atf_error_new("unknown_type", &data, sizeof(data),
+ unknown_type_format);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+bool
+check_umask(const mode_t exp_mode, const mode_t min_mode)
+{
+ const mode_t actual_mode = (~current_umask() & exp_mode);
+ return (actual_mode & min_mode) == min_mode;
+}
+
+static
+atf_error_t
+copy_contents(const atf_fs_path_t *p, char **buf)
+{
+ atf_error_t err;
+ char *str;
+
+ str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
+ if (str == NULL)
+ err = atf_no_memory_error();
+ else {
+ strcpy(str, atf_dynstr_cstring(&p->m_data));
+ *buf = str;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+mode_t
+current_umask(void)
+{
+ const mode_t current = umask(0);
+ (void)umask(current);
+ return current;
+}
+
+static
+atf_error_t
+do_mkdtemp(char *tmpl)
+{
+ atf_error_t err;
+
+ PRE(strstr(tmpl, "XXXXXX") != NULL);
+
+ if (mkdtemp(tmpl) == NULL)
+ err = atf_libc_error(errno, "Cannot create temporary directory "
+ "with template '%s'", tmpl);
+ else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+do_mkstemp(char *tmpl, int *fdout)
+{
+ atf_error_t err;
+
+ PRE(strstr(tmpl, "XXXXXX") != NULL);
+
+ *fdout = mkstemp(tmpl);
+ if (*fdout == -1)
+ err = atf_libc_error(errno, "Cannot create temporary file "
+ "with template '%s'", tmpl);
+
+ else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+normalize(atf_dynstr_t *d, char *p)
+{
+ const char *ptr;
+ char *last;
+ atf_error_t err;
+ bool first;
+
+ PRE(strlen(p) > 0);
+ PRE(atf_dynstr_length(d) == 0);
+
+ if (p[0] == '/')
+ err = atf_dynstr_append_fmt(d, "/");
+ else
+ err = atf_no_error();
+
+ first = true;
+ last = NULL; /* Silence GCC warning. */
+ ptr = strtok_r(p, "/", &last);
+ while (!atf_is_error(err) && ptr != NULL) {
+ if (strlen(ptr) > 0) {
+ err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
+ first = false;
+ }
+
+ ptr = strtok_r(NULL, "/", &last);
+ }
+
+ return err;
+}
+
+static
+atf_error_t
+normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
+{
+ char *str;
+ atf_error_t err;
+ va_list ap2;
+
+ err = atf_dynstr_init(d);
+ if (atf_is_error(err))
+ goto out;
+
+ va_copy(ap2, ap);
+ err = atf_text_format_ap(&str, p, ap2);
+ va_end(ap2);
+ if (atf_is_error(err))
+ atf_dynstr_fini(d);
+ else {
+ err = normalize(d, str);
+ free(str);
+ }
+
+out:
+ return err;
+}
+
+static
+void
+replace_contents(atf_fs_path_t *p, const char *buf)
+{
+ atf_error_t err;
+
+ PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
+
+ atf_dynstr_clear(&p->m_data);
+ err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
+
+ INV(!atf_is_error(err));
+}
+
+static
+const char *
+stat_type_to_string(const int type)
+{
+ const char *str;
+
+ if (type == atf_fs_stat_blk_type)
+ str = "block device";
+ else if (type == atf_fs_stat_chr_type)
+ str = "character device";
+ else if (type == atf_fs_stat_dir_type)
+ str = "directory";
+ else if (type == atf_fs_stat_fifo_type)
+ str = "named pipe";
+ else if (type == atf_fs_stat_lnk_type)
+ str = "symbolic link";
+ else if (type == atf_fs_stat_reg_type)
+ str = "regular file";
+ else if (type == atf_fs_stat_sock_type)
+ str = "socket";
+ else if (type == atf_fs_stat_wht_type)
+ str = "whiteout";
+ else {
+ UNREACHABLE;
+ str = NULL;
+ }
+
+ return str;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = normalize_ap(&p->m_data, fmt, ap2);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_fs_path_init_ap(p, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
+{
+ return atf_dynstr_copy(&dest->m_data, &src->m_data);
+}
+
+void
+atf_fs_path_fini(atf_fs_path_t *p)
+{
+ atf_dynstr_fini(&p->m_data);
+}
+
+/*
+ * Getters.
+ */
+
+atf_error_t
+atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
+{
+ const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
+ atf_error_t err;
+
+ if (endpos == atf_dynstr_npos)
+ err = atf_fs_path_init_fmt(bp, ".");
+ else if (endpos == 0)
+ err = atf_fs_path_init_fmt(bp, "/");
+ else
+ err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
+
+#if defined(HAVE_CONST_DIRNAME)
+ INV(atf_equal_dynstr_cstring(&bp->m_data,
+ dirname(atf_dynstr_cstring(&p->m_data))));
+#endif /* defined(HAVE_CONST_DIRNAME) */
+
+ return err;
+}
+
+const char *
+atf_fs_path_cstring(const atf_fs_path_t *p)
+{
+ return atf_dynstr_cstring(&p->m_data);
+}
+
+atf_error_t
+atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
+{
+ size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
+ atf_error_t err;
+
+ if (begpos == atf_dynstr_npos)
+ begpos = 0;
+ else
+ begpos++;
+
+ err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
+
+#if defined(HAVE_CONST_BASENAME)
+ INV(atf_equal_dynstr_cstring(ln,
+ basename(atf_dynstr_cstring(&p->m_data))));
+#endif /* defined(HAVE_CONST_BASENAME) */
+
+ return err;
+}
+
+bool
+atf_fs_path_is_absolute(const atf_fs_path_t *p)
+{
+ return atf_dynstr_cstring(&p->m_data)[0] == '/';
+}
+
+bool
+atf_fs_path_is_root(const atf_fs_path_t *p)
+{
+ return atf_equal_dynstr_cstring(&p->m_data, "/");
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
+{
+ atf_dynstr_t aux;
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = normalize_ap(&aux, fmt, ap2);
+ va_end(ap2);
+ if (!atf_is_error(err)) {
+ const char *auxstr = atf_dynstr_cstring(&aux);
+ const bool needslash = auxstr[0] != '/';
+
+ err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
+ needslash ? "/" : "", auxstr);
+
+ atf_dynstr_fini(&aux);
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_fs_path_append_ap(p, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
+{
+ return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
+}
+
+atf_error_t
+atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
+{
+ atf_error_t err;
+
+ PRE(!atf_fs_path_is_absolute(p));
+
+ err = atf_fs_getcwd(pa);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_path_append_path(pa, p);
+ if (atf_is_error(err))
+ atf_fs_path_fini(pa);
+
+out:
+ return err;
+}
+
+/*
+ * Operators.
+ */
+
+bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
+ const atf_fs_path_t *p2)
+{
+ return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constants.
+ */
+
+const int atf_fs_stat_blk_type = 1;
+const int atf_fs_stat_chr_type = 2;
+const int atf_fs_stat_dir_type = 3;
+const int atf_fs_stat_fifo_type = 4;
+const int atf_fs_stat_lnk_type = 5;
+const int atf_fs_stat_reg_type = 6;
+const int atf_fs_stat_sock_type = 7;
+const int atf_fs_stat_wht_type = 8;
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
+{
+ atf_error_t err;
+ const char *pstr = atf_fs_path_cstring(p);
+
+ if (lstat(pstr, &st->m_sb) == -1) {
+ err = atf_libc_error(errno, "Cannot get information of %s; "
+ "lstat(2) failed", pstr);
+ } else {
+ int type = st->m_sb.st_mode & S_IFMT;
+ err = atf_no_error();
+ switch (type) {
+ case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break;
+ case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break;
+ case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break;
+ case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break;
+ case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break;
+ case S_IFREG: st->m_type = atf_fs_stat_reg_type; break;
+ case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
+#if defined(S_IFWHT)
+ case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
+#endif
+ default:
+ err = unknown_type_error(pstr, type);
+ }
+ }
+
+ return err;
+}
+
+void
+atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
+{
+ dest->m_type = src->m_type;
+ dest->m_sb = src->m_sb;
+}
+
+void
+atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+/*
+ * Getters.
+ */
+
+dev_t
+atf_fs_stat_get_device(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_dev;
+}
+
+ino_t
+atf_fs_stat_get_inode(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_ino;
+}
+
+mode_t
+atf_fs_stat_get_mode(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & ~S_IFMT;
+}
+
+off_t
+atf_fs_stat_get_size(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_size;
+}
+
+int
+atf_fs_stat_get_type(const atf_fs_stat_t *st)
+{
+ return st->m_type;
+}
+
+bool
+atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IRUSR;
+}
+
+bool
+atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWUSR;
+}
+
+bool
+atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXUSR;
+}
+
+bool
+atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IRGRP;
+}
+
+bool
+atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWGRP;
+}
+
+bool
+atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXGRP;
+}
+
+bool
+atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IROTH;
+}
+
+bool
+atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWOTH;
+}
+
+bool
+atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXOTH;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+const int atf_fs_access_f = 1 << 0;
+const int atf_fs_access_r = 1 << 1;
+const int atf_fs_access_w = 1 << 2;
+const int atf_fs_access_x = 1 << 3;
+
+/*
+ * An implementation of access(2) but using the effective user value
+ * instead of the real one. Also avoids false positives for root when
+ * asking for execute permissions, which appear in SunOS.
+ */
+atf_error_t
+atf_fs_eaccess(const atf_fs_path_t *p, int mode)
+{
+ atf_error_t err;
+ struct stat st;
+ bool ok;
+
+ PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
+ mode & atf_fs_access_w || mode & atf_fs_access_x);
+
+ if (lstat(atf_fs_path_cstring(p), &st) == -1) {
+ err = atf_libc_error(errno, "Cannot get information from file %s",
+ atf_fs_path_cstring(p));
+ goto out;
+ }
+
+ err = atf_no_error();
+
+ /* Early return if we are only checking for existence and the file
+ * exists (stat call returned). */
+ if (mode & atf_fs_access_f)
+ goto out;
+
+ ok = false;
+ if (atf_user_is_root()) {
+ if (!ok && !(mode & atf_fs_access_x)) {
+ /* Allow root to read/write any file. */
+ ok = true;
+ }
+
+ if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+ /* Allow root to execute the file if any of its execution bits
+ * are set. */
+ ok = true;
+ }
+ } else {
+ if (!ok && (atf_user_euid() == st.st_uid)) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
+ }
+ if (!ok && atf_user_is_member_of_group(st.st_gid)) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
+ }
+ if (!ok && ((atf_user_euid() != st.st_uid) &&
+ !atf_user_is_member_of_group(st.st_gid))) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
+ }
+ }
+
+ if (!ok)
+ err = atf_libc_error(EACCES, "Access check failed");
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_exists(const atf_fs_path_t *p, bool *b)
+{
+ atf_error_t err;
+
+ err = atf_fs_eaccess(p, atf_fs_access_f);
+ if (atf_is_error(err)) {
+ if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
+ atf_error_free(err);
+ err = atf_no_error();
+ *b = false;
+ }
+ } else
+ *b = true;
+
+ return err;
+}
+
+atf_error_t
+atf_fs_getcwd(atf_fs_path_t *p)
+{
+ atf_error_t err;
+ char *cwd;
+
+#if defined(HAVE_GETCWD_DYN)
+ cwd = getcwd(NULL, 0);
+#else
+ cwd = getcwd(NULL, MAXPATHLEN);
+#endif
+ if (cwd == NULL) {
+ err = atf_libc_error(errno, "Cannot determine current directory");
+ goto out;
+ }
+
+ err = atf_fs_path_init_fmt(p, "%s", cwd);
+ free(cwd);
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_mkdtemp(atf_fs_path_t *p)
+{
+ atf_error_t err;
+ char *buf;
+
+ if (!check_umask(S_IRWXU, S_IRWXU)) {
+ err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
+ goto out;
+ }
+
+ err = copy_contents(p, &buf);
+ if (atf_is_error(err))
+ goto out;
+
+ err = do_mkdtemp(buf);
+ if (atf_is_error(err))
+ goto out_buf;
+
+ replace_contents(p, buf);
+
+ INV(!atf_is_error(err));
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
+{
+ atf_error_t err;
+ char *buf;
+ int fd;
+
+ if (!check_umask(S_IRWXU, S_IRWXU)) {
+ err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
+ goto out;
+ }
+
+ err = copy_contents(p, &buf);
+ if (atf_is_error(err))
+ goto out;
+
+ err = do_mkstemp(buf, &fd);
+ if (atf_is_error(err))
+ goto out_buf;
+
+ replace_contents(p, buf);
+ *fdout = fd;
+
+ INV(!atf_is_error(err));
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_rmdir(const atf_fs_path_t *p)
+{
+ atf_error_t err;
+
+ if (rmdir(atf_fs_path_cstring(p))) {
+ if (errno == EEXIST) {
+ /* Some operating systems (e.g. OpenSolaris 200906) return
+ * EEXIST instead of ENOTEMPTY for non-empty directories.
+ * Homogenize the return value so that callers don't need
+ * to bother about differences in operating systems. */
+ errno = ENOTEMPTY;
+ }
+ err = atf_libc_error(errno, "Cannot remove directory");
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+atf_error_t
+atf_fs_unlink(const atf_fs_path_t *p)
+{
+ atf_error_t err;
+ const char *path;
+
+ path = atf_fs_path_cstring(p);
+
+ if (unlink(path) != 0)
+ err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
+ else
+ err = atf_no_error();
+
+ return err;
+}