Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / unit / atf-src / atf-c / detail / fs.c
blobfffd5f13695c43df88bb9b8fe6a6cda04ae50ac9
1 /* $NetBSD: fs.c,v 1.3 2014/12/10 04:38:03 christos Exp $ */
3 /*
4 * Automated Testing Framework (atf)
6 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
19 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
29 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #if defined(HAVE_CONFIG_H)
33 #include "bconfig.h"
34 #endif
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <libgen.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
51 #include "atf-c/defs.h"
52 #include "atf-c/error.h"
54 #include "fs.h"
55 #include "sanity.h"
56 #include "text.h"
57 #include "user.h"
59 /* ---------------------------------------------------------------------
60 * Prototypes for auxiliary functions.
61 * --------------------------------------------------------------------- */
63 static bool check_umask(const mode_t, const mode_t);
64 static atf_error_t copy_contents(const atf_fs_path_t *, char **);
65 static mode_t current_umask(void);
66 static atf_error_t do_mkdtemp(char *);
67 static atf_error_t normalize(atf_dynstr_t *, char *);
68 static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
69 static void replace_contents(atf_fs_path_t *, const char *);
70 static const char *stat_type_to_string(const int);
72 /* ---------------------------------------------------------------------
73 * The "invalid_umask" error type.
74 * --------------------------------------------------------------------- */
76 struct invalid_umask_error_data {
77 /* One of atf_fs_stat_*_type. */
78 int m_type;
80 /* The original path causing the error. */
81 /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
82 * from the error constructor, we cannot delete the path later on.
83 * Can't remember why atf_error_new does not take a hook for
84 * deletion. */
85 char m_path[1024];
87 /* The umask that caused the error. */
88 mode_t m_umask;
90 typedef struct invalid_umask_error_data invalid_umask_error_data_t;
92 static
93 void
94 invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
96 const invalid_umask_error_data_t *data;
98 PRE(atf_error_is(err, "invalid_umask"));
100 data = atf_error_data(err);
101 snprintf(buf, buflen, "Could not create the temporary %s %s because "
102 "it will not have enough access rights due to the current "
103 "umask %05o", stat_type_to_string(data->m_type),
104 data->m_path, (unsigned int)data->m_umask);
107 static
108 atf_error_t
109 invalid_umask_error(const atf_fs_path_t *path, const int type,
110 const mode_t failing_mask)
112 atf_error_t err;
113 invalid_umask_error_data_t data;
115 data.m_type = type;
117 strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
118 data.m_path[sizeof(data.m_path) - 1] = '\0';
120 data.m_umask = failing_mask;
122 err = atf_error_new("invalid_umask", &data, sizeof(data),
123 invalid_umask_format);
125 return err;
128 /* ---------------------------------------------------------------------
129 * The "unknown_file_type" error type.
130 * --------------------------------------------------------------------- */
132 struct unknown_type_error_data {
133 const char *m_path;
134 int m_type;
136 typedef struct unknown_type_error_data unknown_type_error_data_t;
138 static
139 void
140 unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
142 const unknown_type_error_data_t *data;
144 PRE(atf_error_is(err, "unknown_type"));
146 data = atf_error_data(err);
147 snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
148 data->m_path);
151 static
152 atf_error_t
153 unknown_type_error(const char *path, int type)
155 atf_error_t err;
156 unknown_type_error_data_t data;
158 data.m_path = path;
159 data.m_type = type;
161 err = atf_error_new("unknown_type", &data, sizeof(data),
162 unknown_type_format);
164 return err;
167 /* ---------------------------------------------------------------------
168 * Auxiliary functions.
169 * --------------------------------------------------------------------- */
171 static
172 bool
173 check_umask(const mode_t exp_mode, const mode_t min_mode)
175 const mode_t actual_mode = (~current_umask() & exp_mode);
176 return (actual_mode & min_mode) == min_mode;
179 static
180 atf_error_t
181 copy_contents(const atf_fs_path_t *p, char **buf)
183 atf_error_t err;
184 char *str;
186 str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
187 if (str == NULL)
188 err = atf_no_memory_error();
189 else {
190 strcpy(str, atf_dynstr_cstring(&p->m_data));
191 *buf = str;
192 err = atf_no_error();
195 return err;
198 static
199 mode_t
200 current_umask(void)
202 const mode_t current = umask(0);
203 (void)umask(current);
204 return current;
207 static
208 atf_error_t
209 do_mkdtemp(char *tmpl)
211 atf_error_t err;
213 PRE(strstr(tmpl, "XXXXXX") != NULL);
215 if (mkdtemp(tmpl) == NULL)
216 err = atf_libc_error(errno, "Cannot create temporary directory "
217 "with template '%s'", tmpl);
218 else
219 err = atf_no_error();
221 return err;
224 static
225 atf_error_t
226 do_mkstemp(char *tmpl, int *fdout)
228 atf_error_t err;
230 PRE(strstr(tmpl, "XXXXXX") != NULL);
232 *fdout = mkstemp(tmpl);
233 if (*fdout == -1)
234 err = atf_libc_error(errno, "Cannot create temporary file "
235 "with template '%s'", tmpl);
237 else
238 err = atf_no_error();
240 return err;
243 static
244 atf_error_t
245 normalize(atf_dynstr_t *d, char *p)
247 const char *ptr;
248 char *last;
249 atf_error_t err;
250 bool first;
252 PRE(strlen(p) > 0);
253 PRE(atf_dynstr_length(d) == 0);
255 if (p[0] == '/')
256 err = atf_dynstr_append_fmt(d, "/");
257 else
258 err = atf_no_error();
260 first = true;
261 last = NULL; /* Silence GCC warning. */
262 ptr = strtok_r(p, "/", &last);
263 while (!atf_is_error(err) && ptr != NULL) {
264 if (strlen(ptr) > 0) {
265 err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
266 first = false;
269 ptr = strtok_r(NULL, "/", &last);
272 return err;
275 static
276 atf_error_t
277 normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
279 char *str;
280 atf_error_t err;
281 va_list ap2;
283 err = atf_dynstr_init(d);
284 if (atf_is_error(err))
285 goto out;
287 va_copy(ap2, ap);
288 err = atf_text_format_ap(&str, p, ap2);
289 va_end(ap2);
290 if (atf_is_error(err))
291 atf_dynstr_fini(d);
292 else {
293 err = normalize(d, str);
294 free(str);
297 out:
298 return err;
301 static
302 void
303 replace_contents(atf_fs_path_t *p, const char *buf)
305 atf_error_t err;
307 PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
309 atf_dynstr_clear(&p->m_data);
310 err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
312 INV(!atf_is_error(err));
315 static
316 const char *
317 stat_type_to_string(const int type)
319 const char *str;
321 if (type == atf_fs_stat_blk_type)
322 str = "block device";
323 else if (type == atf_fs_stat_chr_type)
324 str = "character device";
325 else if (type == atf_fs_stat_dir_type)
326 str = "directory";
327 else if (type == atf_fs_stat_fifo_type)
328 str = "named pipe";
329 else if (type == atf_fs_stat_lnk_type)
330 str = "symbolic link";
331 else if (type == atf_fs_stat_reg_type)
332 str = "regular file";
333 else if (type == atf_fs_stat_sock_type)
334 str = "socket";
335 else if (type == atf_fs_stat_wht_type)
336 str = "whiteout";
337 else {
338 UNREACHABLE;
339 str = NULL;
342 return str;
345 /* ---------------------------------------------------------------------
346 * The "atf_fs_path" type.
347 * --------------------------------------------------------------------- */
350 * Constructors/destructors.
353 atf_error_t
354 atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
356 atf_error_t err;
357 va_list ap2;
359 va_copy(ap2, ap);
360 err = normalize_ap(&p->m_data, fmt, ap2);
361 va_end(ap2);
363 return err;
366 atf_error_t
367 atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
369 va_list ap;
370 atf_error_t err;
372 va_start(ap, fmt);
373 err = atf_fs_path_init_ap(p, fmt, ap);
374 va_end(ap);
376 return err;
379 atf_error_t
380 atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
382 return atf_dynstr_copy(&dest->m_data, &src->m_data);
385 void
386 atf_fs_path_fini(atf_fs_path_t *p)
388 atf_dynstr_fini(&p->m_data);
392 * Getters.
395 atf_error_t
396 atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
398 const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
399 atf_error_t err;
401 if (endpos == atf_dynstr_npos)
402 err = atf_fs_path_init_fmt(bp, ".");
403 else if (endpos == 0)
404 err = atf_fs_path_init_fmt(bp, "/");
405 else
406 err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
408 #if defined(HAVE_CONST_DIRNAME)
409 INV(atf_equal_dynstr_cstring(&bp->m_data,
410 dirname(atf_dynstr_cstring(&p->m_data))));
411 #endif /* defined(HAVE_CONST_DIRNAME) */
413 return err;
416 const char *
417 atf_fs_path_cstring(const atf_fs_path_t *p)
419 return atf_dynstr_cstring(&p->m_data);
422 atf_error_t
423 atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
425 size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
426 atf_error_t err;
428 if (begpos == atf_dynstr_npos)
429 begpos = 0;
430 else
431 begpos++;
433 err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
435 #if defined(HAVE_CONST_BASENAME)
436 INV(atf_equal_dynstr_cstring(ln,
437 basename(atf_dynstr_cstring(&p->m_data))));
438 #endif /* defined(HAVE_CONST_BASENAME) */
440 return err;
443 bool
444 atf_fs_path_is_absolute(const atf_fs_path_t *p)
446 return atf_dynstr_cstring(&p->m_data)[0] == '/';
449 bool
450 atf_fs_path_is_root(const atf_fs_path_t *p)
452 return atf_equal_dynstr_cstring(&p->m_data, "/");
456 * Modifiers.
459 atf_error_t
460 atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
462 atf_dynstr_t aux;
463 atf_error_t err;
464 va_list ap2;
466 va_copy(ap2, ap);
467 err = normalize_ap(&aux, fmt, ap2);
468 va_end(ap2);
469 if (!atf_is_error(err)) {
470 const char *auxstr = atf_dynstr_cstring(&aux);
471 const bool needslash = auxstr[0] != '/';
473 err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
474 needslash ? "/" : "", auxstr);
476 atf_dynstr_fini(&aux);
479 return err;
482 atf_error_t
483 atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
485 va_list ap;
486 atf_error_t err;
488 va_start(ap, fmt);
489 err = atf_fs_path_append_ap(p, fmt, ap);
490 va_end(ap);
492 return err;
495 atf_error_t
496 atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
498 return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
501 atf_error_t
502 atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
504 atf_error_t err;
506 PRE(!atf_fs_path_is_absolute(p));
508 err = atf_fs_getcwd(pa);
509 if (atf_is_error(err))
510 goto out;
512 err = atf_fs_path_append_path(pa, p);
513 if (atf_is_error(err))
514 atf_fs_path_fini(pa);
516 out:
517 return err;
521 * Operators.
524 bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
525 const atf_fs_path_t *p2)
527 return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
530 /* ---------------------------------------------------------------------
531 * The "atf_fs_path" type.
532 * --------------------------------------------------------------------- */
535 * Constants.
538 const int atf_fs_stat_blk_type = 1;
539 const int atf_fs_stat_chr_type = 2;
540 const int atf_fs_stat_dir_type = 3;
541 const int atf_fs_stat_fifo_type = 4;
542 const int atf_fs_stat_lnk_type = 5;
543 const int atf_fs_stat_reg_type = 6;
544 const int atf_fs_stat_sock_type = 7;
545 const int atf_fs_stat_wht_type = 8;
548 * Constructors/destructors.
551 atf_error_t
552 atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
554 atf_error_t err;
555 const char *pstr = atf_fs_path_cstring(p);
557 if (lstat(pstr, &st->m_sb) == -1) {
558 err = atf_libc_error(errno, "Cannot get information of %s; "
559 "lstat(2) failed", pstr);
560 } else {
561 int type = st->m_sb.st_mode & S_IFMT;
562 err = atf_no_error();
563 switch (type) {
564 case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break;
565 case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break;
566 case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break;
567 case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break;
568 case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break;
569 case S_IFREG: st->m_type = atf_fs_stat_reg_type; break;
570 case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
571 #if defined(S_IFWHT)
572 case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
573 #endif
574 default:
575 err = unknown_type_error(pstr, type);
579 return err;
582 void
583 atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
585 dest->m_type = src->m_type;
586 dest->m_sb = src->m_sb;
589 void
590 atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
595 * Getters.
598 dev_t
599 atf_fs_stat_get_device(const atf_fs_stat_t *st)
601 return st->m_sb.st_dev;
604 ino_t
605 atf_fs_stat_get_inode(const atf_fs_stat_t *st)
607 return st->m_sb.st_ino;
610 mode_t
611 atf_fs_stat_get_mode(const atf_fs_stat_t *st)
613 return st->m_sb.st_mode & ~S_IFMT;
616 off_t
617 atf_fs_stat_get_size(const atf_fs_stat_t *st)
619 return st->m_sb.st_size;
623 atf_fs_stat_get_type(const atf_fs_stat_t *st)
625 return st->m_type;
628 bool
629 atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
631 return st->m_sb.st_mode & S_IRUSR;
634 bool
635 atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
637 return st->m_sb.st_mode & S_IWUSR;
640 bool
641 atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
643 return st->m_sb.st_mode & S_IXUSR;
646 bool
647 atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
649 return st->m_sb.st_mode & S_IRGRP;
652 bool
653 atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
655 return st->m_sb.st_mode & S_IWGRP;
658 bool
659 atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
661 return st->m_sb.st_mode & S_IXGRP;
664 bool
665 atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
667 return st->m_sb.st_mode & S_IROTH;
670 bool
671 atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
673 return st->m_sb.st_mode & S_IWOTH;
676 bool
677 atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
679 return st->m_sb.st_mode & S_IXOTH;
682 /* ---------------------------------------------------------------------
683 * Free functions.
684 * --------------------------------------------------------------------- */
686 const int atf_fs_access_f = 1 << 0;
687 const int atf_fs_access_r = 1 << 1;
688 const int atf_fs_access_w = 1 << 2;
689 const int atf_fs_access_x = 1 << 3;
692 * An implementation of access(2) but using the effective user value
693 * instead of the real one. Also avoids false positives for root when
694 * asking for execute permissions, which appear in SunOS.
696 atf_error_t
697 atf_fs_eaccess(const atf_fs_path_t *p, int mode)
699 atf_error_t err;
700 struct stat st;
701 bool ok;
703 PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
704 mode & atf_fs_access_w || mode & atf_fs_access_x);
706 if (lstat(atf_fs_path_cstring(p), &st) == -1) {
707 err = atf_libc_error(errno, "Cannot get information from file %s",
708 atf_fs_path_cstring(p));
709 goto out;
712 err = atf_no_error();
714 /* Early return if we are only checking for existence and the file
715 * exists (stat call returned). */
716 if (mode & atf_fs_access_f)
717 goto out;
719 ok = false;
720 if (atf_user_is_root()) {
721 if (!ok && !(mode & atf_fs_access_x)) {
722 /* Allow root to read/write any file. */
723 ok = true;
726 if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
727 /* Allow root to execute the file if any of its execution bits
728 * are set. */
729 ok = true;
731 } else {
732 if (!ok && (atf_user_euid() == st.st_uid)) {
733 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
734 ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
735 ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
737 if (!ok && atf_user_is_member_of_group(st.st_gid)) {
738 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
739 ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
740 ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
742 if (!ok && ((atf_user_euid() != st.st_uid) &&
743 !atf_user_is_member_of_group(st.st_gid))) {
744 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
745 ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
746 ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
750 if (!ok)
751 err = atf_libc_error(EACCES, "Access check failed");
753 out:
754 return err;
757 atf_error_t
758 atf_fs_exists(const atf_fs_path_t *p, bool *b)
760 atf_error_t err;
762 err = atf_fs_eaccess(p, atf_fs_access_f);
763 if (atf_is_error(err)) {
764 if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
765 atf_error_free(err);
766 err = atf_no_error();
767 *b = false;
769 } else
770 *b = true;
772 return err;
775 atf_error_t
776 atf_fs_getcwd(atf_fs_path_t *p)
778 atf_error_t err;
779 char *cwd;
781 #if defined(HAVE_GETCWD_DYN)
782 cwd = getcwd(NULL, 0);
783 #else
784 cwd = getcwd(NULL, MAXPATHLEN);
785 #endif
786 if (cwd == NULL) {
787 err = atf_libc_error(errno, "Cannot determine current directory");
788 goto out;
791 err = atf_fs_path_init_fmt(p, "%s", cwd);
792 free(cwd);
794 out:
795 return err;
798 atf_error_t
799 atf_fs_mkdtemp(atf_fs_path_t *p)
801 atf_error_t err;
802 char *buf;
804 if (!check_umask(S_IRWXU, S_IRWXU)) {
805 err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
806 goto out;
809 err = copy_contents(p, &buf);
810 if (atf_is_error(err))
811 goto out;
813 err = do_mkdtemp(buf);
814 if (atf_is_error(err))
815 goto out_buf;
817 replace_contents(p, buf);
819 INV(!atf_is_error(err));
820 out_buf:
821 free(buf);
822 out:
823 return err;
826 atf_error_t
827 atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
829 atf_error_t err;
830 char *buf;
831 int fd;
833 if (!check_umask(S_IRWXU, S_IRWXU)) {
834 err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
835 goto out;
838 err = copy_contents(p, &buf);
839 if (atf_is_error(err))
840 goto out;
842 err = do_mkstemp(buf, &fd);
843 if (atf_is_error(err))
844 goto out_buf;
846 replace_contents(p, buf);
847 *fdout = fd;
849 INV(!atf_is_error(err));
850 out_buf:
851 free(buf);
852 out:
853 return err;
856 atf_error_t
857 atf_fs_rmdir(const atf_fs_path_t *p)
859 atf_error_t err;
861 if (rmdir(atf_fs_path_cstring(p))) {
862 if (errno == EEXIST) {
863 /* Some operating systems (e.g. OpenSolaris 200906) return
864 * EEXIST instead of ENOTEMPTY for non-empty directories.
865 * Homogenize the return value so that callers don't need
866 * to bother about differences in operating systems. */
867 errno = ENOTEMPTY;
869 err = atf_libc_error(errno, "Cannot remove directory");
870 } else
871 err = atf_no_error();
873 return err;
876 atf_error_t
877 atf_fs_unlink(const atf_fs_path_t *p)
879 atf_error_t err;
880 const char *path;
882 path = atf_fs_path_cstring(p);
884 if (unlink(path) != 0)
885 err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
886 else
887 err = atf_no_error();
889 return err;