tools/llvm: Do not build with symbols
[minix3.git] / external / bsd / atf / dist / atf-c / detail / fs.c
blob7a19c47b267c22ff58d114ef4fe47265c2d5db69
1 /*
2 * Automated Testing Framework (atf)
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
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.
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.
30 #if defined(HAVE_CONFIG_H)
31 #include "bconfig.h"
32 #endif
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
40 #include <dirent.h>
41 #include <errno.h>
42 #include <libgen.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
49 #include "atf-c/defs.h"
50 #include "atf-c/error.h"
52 #include "fs.h"
53 #include "sanity.h"
54 #include "text.h"
55 #include "user.h"
57 /* ---------------------------------------------------------------------
58 * Prototypes for auxiliary functions.
59 * --------------------------------------------------------------------- */
61 static bool check_umask(const mode_t, const mode_t);
62 static atf_error_t copy_contents(const atf_fs_path_t *, char **);
63 static mode_t current_umask(void);
64 static atf_error_t do_mkdtemp(char *);
65 static atf_error_t normalize(atf_dynstr_t *, char *);
66 static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
67 static void replace_contents(atf_fs_path_t *, const char *);
68 static const char *stat_type_to_string(const int);
70 /* ---------------------------------------------------------------------
71 * The "invalid_umask" error type.
72 * --------------------------------------------------------------------- */
74 struct invalid_umask_error_data {
75 /* One of atf_fs_stat_*_type. */
76 int m_type;
78 /* The original path causing the error. */
79 /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
80 * from the error constructor, we cannot delete the path later on.
81 * Can't remember why atf_error_new does not take a hook for
82 * deletion. */
83 char m_path[1024];
85 /* The umask that caused the error. */
86 mode_t m_umask;
88 typedef struct invalid_umask_error_data invalid_umask_error_data_t;
90 static
91 void
92 invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
94 const invalid_umask_error_data_t *data;
96 PRE(atf_error_is(err, "invalid_umask"));
98 data = atf_error_data(err);
99 snprintf(buf, buflen, "Could not create the temporary %s %s because "
100 "it will not have enough access rights due to the current "
101 "umask %05o", stat_type_to_string(data->m_type),
102 data->m_path, (unsigned int)data->m_umask);
105 static
106 atf_error_t
107 invalid_umask_error(const atf_fs_path_t *path, const int type,
108 const mode_t failing_mask)
110 atf_error_t err;
111 invalid_umask_error_data_t data;
113 data.m_type = type;
115 strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
116 data.m_path[sizeof(data.m_path) - 1] = '\0';
118 data.m_umask = failing_mask;
120 err = atf_error_new("invalid_umask", &data, sizeof(data),
121 invalid_umask_format);
123 return err;
126 /* ---------------------------------------------------------------------
127 * The "unknown_file_type" error type.
128 * --------------------------------------------------------------------- */
130 struct unknown_type_error_data {
131 const char *m_path;
132 int m_type;
134 typedef struct unknown_type_error_data unknown_type_error_data_t;
136 static
137 void
138 unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
140 const unknown_type_error_data_t *data;
142 PRE(atf_error_is(err, "unknown_type"));
144 data = atf_error_data(err);
145 snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
146 data->m_path);
149 static
150 atf_error_t
151 unknown_type_error(const char *path, int type)
153 atf_error_t err;
154 unknown_type_error_data_t data;
156 data.m_path = path;
157 data.m_type = type;
159 err = atf_error_new("unknown_type", &data, sizeof(data),
160 unknown_type_format);
162 return err;
165 /* ---------------------------------------------------------------------
166 * Auxiliary functions.
167 * --------------------------------------------------------------------- */
169 static
170 bool
171 check_umask(const mode_t exp_mode, const mode_t min_mode)
173 const mode_t actual_mode = (~current_umask() & exp_mode);
174 return (actual_mode & min_mode) == min_mode;
177 static
178 atf_error_t
179 copy_contents(const atf_fs_path_t *p, char **buf)
181 atf_error_t err;
182 char *str;
184 str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
185 if (str == NULL)
186 err = atf_no_memory_error();
187 else {
188 strcpy(str, atf_dynstr_cstring(&p->m_data));
189 *buf = str;
190 err = atf_no_error();
193 return err;
196 static
197 mode_t
198 current_umask(void)
200 const mode_t current = umask(0);
201 (void)umask(current);
202 return current;
205 static
206 atf_error_t
207 do_mkdtemp(char *tmpl)
209 atf_error_t err;
211 PRE(strstr(tmpl, "XXXXXX") != NULL);
213 if (mkdtemp(tmpl) == NULL)
214 err = atf_libc_error(errno, "Cannot create temporary directory "
215 "with template '%s'", tmpl);
216 else
217 err = atf_no_error();
219 return err;
222 static
223 atf_error_t
224 do_mkstemp(char *tmpl, int *fdout)
226 atf_error_t err;
228 PRE(strstr(tmpl, "XXXXXX") != NULL);
230 *fdout = mkstemp(tmpl);
231 if (*fdout == -1)
232 err = atf_libc_error(errno, "Cannot create temporary file "
233 "with template '%s'", tmpl);
235 else
236 err = atf_no_error();
238 return err;
241 static
242 atf_error_t
243 normalize(atf_dynstr_t *d, char *p)
245 const char *ptr;
246 char *last;
247 atf_error_t err;
248 bool first;
250 PRE(strlen(p) > 0);
251 PRE(atf_dynstr_length(d) == 0);
253 if (p[0] == '/')
254 err = atf_dynstr_append_fmt(d, "/");
255 else
256 err = atf_no_error();
258 first = true;
259 last = NULL; /* Silence GCC warning. */
260 ptr = strtok_r(p, "/", &last);
261 while (!atf_is_error(err) && ptr != NULL) {
262 if (strlen(ptr) > 0) {
263 err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
264 first = false;
267 ptr = strtok_r(NULL, "/", &last);
270 return err;
273 static
274 atf_error_t
275 normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
277 char *str;
278 atf_error_t err;
279 va_list ap2;
281 err = atf_dynstr_init(d);
282 if (atf_is_error(err))
283 goto out;
285 va_copy(ap2, ap);
286 err = atf_text_format_ap(&str, p, ap2);
287 va_end(ap2);
288 if (atf_is_error(err))
289 atf_dynstr_fini(d);
290 else {
291 err = normalize(d, str);
292 free(str);
295 out:
296 return err;
299 static
300 void
301 replace_contents(atf_fs_path_t *p, const char *buf)
303 atf_error_t err;
305 PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
307 atf_dynstr_clear(&p->m_data);
308 err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
310 INV(!atf_is_error(err));
313 static
314 const char *
315 stat_type_to_string(const int type)
317 const char *str;
319 if (type == atf_fs_stat_blk_type)
320 str = "block device";
321 else if (type == atf_fs_stat_chr_type)
322 str = "character device";
323 else if (type == atf_fs_stat_dir_type)
324 str = "directory";
325 else if (type == atf_fs_stat_fifo_type)
326 str = "named pipe";
327 else if (type == atf_fs_stat_lnk_type)
328 str = "symbolic link";
329 else if (type == atf_fs_stat_reg_type)
330 str = "regular file";
331 else if (type == atf_fs_stat_sock_type)
332 str = "socket";
333 else if (type == atf_fs_stat_wht_type)
334 str = "whiteout";
335 else {
336 UNREACHABLE;
337 str = NULL;
340 return str;
343 /* ---------------------------------------------------------------------
344 * The "atf_fs_path" type.
345 * --------------------------------------------------------------------- */
348 * Constructors/destructors.
351 atf_error_t
352 atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
354 atf_error_t err;
355 va_list ap2;
357 va_copy(ap2, ap);
358 err = normalize_ap(&p->m_data, fmt, ap2);
359 va_end(ap2);
361 return err;
364 atf_error_t
365 atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
367 va_list ap;
368 atf_error_t err;
370 va_start(ap, fmt);
371 err = atf_fs_path_init_ap(p, fmt, ap);
372 va_end(ap);
374 return err;
377 atf_error_t
378 atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
380 return atf_dynstr_copy(&dest->m_data, &src->m_data);
383 void
384 atf_fs_path_fini(atf_fs_path_t *p)
386 atf_dynstr_fini(&p->m_data);
390 * Getters.
393 atf_error_t
394 atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
396 const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
397 atf_error_t err;
399 if (endpos == atf_dynstr_npos)
400 err = atf_fs_path_init_fmt(bp, ".");
401 else if (endpos == 0)
402 err = atf_fs_path_init_fmt(bp, "/");
403 else
404 err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
406 #if defined(HAVE_CONST_DIRNAME)
407 INV(atf_equal_dynstr_cstring(&bp->m_data,
408 dirname(atf_dynstr_cstring(&p->m_data))));
409 #endif /* defined(HAVE_CONST_DIRNAME) */
411 return err;
414 const char *
415 atf_fs_path_cstring(const atf_fs_path_t *p)
417 return atf_dynstr_cstring(&p->m_data);
420 atf_error_t
421 atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
423 size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
424 atf_error_t err;
426 if (begpos == atf_dynstr_npos)
427 begpos = 0;
428 else
429 begpos++;
431 err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
433 #if defined(HAVE_CONST_BASENAME)
434 INV(atf_equal_dynstr_cstring(ln,
435 basename(atf_dynstr_cstring(&p->m_data))));
436 #endif /* defined(HAVE_CONST_BASENAME) */
438 return err;
441 bool
442 atf_fs_path_is_absolute(const atf_fs_path_t *p)
444 return atf_dynstr_cstring(&p->m_data)[0] == '/';
447 bool
448 atf_fs_path_is_root(const atf_fs_path_t *p)
450 return atf_equal_dynstr_cstring(&p->m_data, "/");
454 * Modifiers.
457 atf_error_t
458 atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
460 atf_dynstr_t aux;
461 atf_error_t err;
462 va_list ap2;
464 va_copy(ap2, ap);
465 err = normalize_ap(&aux, fmt, ap2);
466 va_end(ap2);
467 if (!atf_is_error(err)) {
468 const char *auxstr = atf_dynstr_cstring(&aux);
469 const bool needslash = auxstr[0] != '/';
471 err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
472 needslash ? "/" : "", auxstr);
474 atf_dynstr_fini(&aux);
477 return err;
480 atf_error_t
481 atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
483 va_list ap;
484 atf_error_t err;
486 va_start(ap, fmt);
487 err = atf_fs_path_append_ap(p, fmt, ap);
488 va_end(ap);
490 return err;
493 atf_error_t
494 atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
496 return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
499 atf_error_t
500 atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
502 atf_error_t err;
504 PRE(!atf_fs_path_is_absolute(p));
506 err = atf_fs_getcwd(pa);
507 if (atf_is_error(err))
508 goto out;
510 err = atf_fs_path_append_path(pa, p);
511 if (atf_is_error(err))
512 atf_fs_path_fini(pa);
514 out:
515 return err;
519 * Operators.
522 bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
523 const atf_fs_path_t *p2)
525 return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
528 /* ---------------------------------------------------------------------
529 * The "atf_fs_path" type.
530 * --------------------------------------------------------------------- */
533 * Constants.
536 const int atf_fs_stat_blk_type = 1;
537 const int atf_fs_stat_chr_type = 2;
538 const int atf_fs_stat_dir_type = 3;
539 const int atf_fs_stat_fifo_type = 4;
540 const int atf_fs_stat_lnk_type = 5;
541 const int atf_fs_stat_reg_type = 6;
542 const int atf_fs_stat_sock_type = 7;
543 const int atf_fs_stat_wht_type = 8;
546 * Constructors/destructors.
549 atf_error_t
550 atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
552 atf_error_t err;
553 const char *pstr = atf_fs_path_cstring(p);
555 if (lstat(pstr, &st->m_sb) == -1) {
556 err = atf_libc_error(errno, "Cannot get information of %s; "
557 "lstat(2) failed", pstr);
558 } else {
559 int type = st->m_sb.st_mode & S_IFMT;
560 err = atf_no_error();
561 switch (type) {
562 case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break;
563 case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break;
564 case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break;
565 case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break;
566 case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break;
567 case S_IFREG: st->m_type = atf_fs_stat_reg_type; break;
568 case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
569 #if defined(S_IFWHT)
570 case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
571 #endif
572 default:
573 err = unknown_type_error(pstr, type);
577 return err;
580 void
581 atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
583 dest->m_type = src->m_type;
584 dest->m_sb = src->m_sb;
587 void
588 atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
593 * Getters.
596 dev_t
597 atf_fs_stat_get_device(const atf_fs_stat_t *st)
599 return st->m_sb.st_dev;
602 ino_t
603 atf_fs_stat_get_inode(const atf_fs_stat_t *st)
605 return st->m_sb.st_ino;
608 mode_t
609 atf_fs_stat_get_mode(const atf_fs_stat_t *st)
611 return st->m_sb.st_mode & ~S_IFMT;
614 off_t
615 atf_fs_stat_get_size(const atf_fs_stat_t *st)
617 return st->m_sb.st_size;
621 atf_fs_stat_get_type(const atf_fs_stat_t *st)
623 return st->m_type;
626 bool
627 atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
629 return st->m_sb.st_mode & S_IRUSR;
632 bool
633 atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
635 return st->m_sb.st_mode & S_IWUSR;
638 bool
639 atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
641 return st->m_sb.st_mode & S_IXUSR;
644 bool
645 atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
647 return st->m_sb.st_mode & S_IRGRP;
650 bool
651 atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
653 return st->m_sb.st_mode & S_IWGRP;
656 bool
657 atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
659 return st->m_sb.st_mode & S_IXGRP;
662 bool
663 atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
665 return st->m_sb.st_mode & S_IROTH;
668 bool
669 atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
671 return st->m_sb.st_mode & S_IWOTH;
674 bool
675 atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
677 return st->m_sb.st_mode & S_IXOTH;
680 /* ---------------------------------------------------------------------
681 * Free functions.
682 * --------------------------------------------------------------------- */
684 const int atf_fs_access_f = 1 << 0;
685 const int atf_fs_access_r = 1 << 1;
686 const int atf_fs_access_w = 1 << 2;
687 const int atf_fs_access_x = 1 << 3;
690 * An implementation of access(2) but using the effective user value
691 * instead of the real one. Also avoids false positives for root when
692 * asking for execute permissions, which appear in SunOS.
694 atf_error_t
695 atf_fs_eaccess(const atf_fs_path_t *p, int mode)
697 atf_error_t err;
698 struct stat st;
699 bool ok;
701 PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
702 mode & atf_fs_access_w || mode & atf_fs_access_x);
704 if (lstat(atf_fs_path_cstring(p), &st) == -1) {
705 err = atf_libc_error(errno, "Cannot get information from file %s",
706 atf_fs_path_cstring(p));
707 goto out;
710 err = atf_no_error();
712 /* Early return if we are only checking for existence and the file
713 * exists (stat call returned). */
714 if (mode & atf_fs_access_f)
715 goto out;
717 ok = false;
718 if (atf_user_is_root()) {
719 if (!ok && !(mode & atf_fs_access_x)) {
720 /* Allow root to read/write any file. */
721 ok = true;
724 if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
725 /* Allow root to execute the file if any of its execution bits
726 * are set. */
727 ok = true;
729 } else {
730 if (!ok && (atf_user_euid() == st.st_uid)) {
731 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
732 ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
733 ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
735 if (!ok && atf_user_is_member_of_group(st.st_gid)) {
736 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
737 ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
738 ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
740 if (!ok && ((atf_user_euid() != st.st_uid) &&
741 !atf_user_is_member_of_group(st.st_gid))) {
742 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
743 ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
744 ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
748 if (!ok)
749 err = atf_libc_error(EACCES, "Access check failed");
751 out:
752 return err;
755 atf_error_t
756 atf_fs_exists(const atf_fs_path_t *p, bool *b)
758 atf_error_t err;
760 err = atf_fs_eaccess(p, atf_fs_access_f);
761 if (atf_is_error(err)) {
762 if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
763 atf_error_free(err);
764 err = atf_no_error();
765 *b = false;
767 } else
768 *b = true;
770 return err;
773 atf_error_t
774 atf_fs_getcwd(atf_fs_path_t *p)
776 atf_error_t err;
777 char *cwd;
779 #if defined(HAVE_GETCWD_DYN)
780 cwd = getcwd(NULL, 0);
781 #else
782 cwd = getcwd(NULL, MAXPATHLEN);
783 #endif
784 if (cwd == NULL) {
785 err = atf_libc_error(errno, "Cannot determine current directory");
786 goto out;
789 err = atf_fs_path_init_fmt(p, "%s", cwd);
790 free(cwd);
792 out:
793 return err;
796 atf_error_t
797 atf_fs_mkdtemp(atf_fs_path_t *p)
799 atf_error_t err;
800 char *buf = NULL; /* MINIX: Complain in -O3 */
802 if (!check_umask(S_IRWXU, S_IRWXU)) {
803 err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
804 goto out;
807 err = copy_contents(p, &buf);
808 if (atf_is_error(err))
809 goto out;
811 err = do_mkdtemp(buf);
812 if (atf_is_error(err))
813 goto out_buf;
815 replace_contents(p, buf);
817 INV(!atf_is_error(err));
818 out_buf:
819 free(buf);
820 out:
821 return err;
824 atf_error_t
825 atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
827 atf_error_t err;
828 char *buf = NULL; /* MINIX: Complain in -O3 */
829 int fd;
831 if (!check_umask(S_IRWXU, S_IRWXU)) {
832 err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
833 goto out;
836 err = copy_contents(p, &buf);
837 if (atf_is_error(err))
838 goto out;
840 err = do_mkstemp(buf, &fd);
841 if (atf_is_error(err))
842 goto out_buf;
844 replace_contents(p, buf);
845 *fdout = fd;
847 INV(!atf_is_error(err));
848 out_buf:
849 free(buf);
850 out:
851 return err;
854 atf_error_t
855 atf_fs_rmdir(const atf_fs_path_t *p)
857 atf_error_t err;
859 if (rmdir(atf_fs_path_cstring(p))) {
860 if (errno == EEXIST) {
861 /* Some operating systems (e.g. OpenSolaris 200906) return
862 * EEXIST instead of ENOTEMPTY for non-empty directories.
863 * Homogenize the return value so that callers don't need
864 * to bother about differences in operating systems. */
865 errno = ENOTEMPTY;
867 err = atf_libc_error(errno, "Cannot remove directory");
868 } else
869 err = atf_no_error();
871 return err;
874 atf_error_t
875 atf_fs_unlink(const atf_fs_path_t *p)
877 atf_error_t err;
878 const char *path;
880 path = atf_fs_path_cstring(p);
882 if (unlink(path) != 0)
883 err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
884 else
885 err = atf_no_error();
887 return err;