1/*
2 * Automated Testing Framework (atf)
3 *
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
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.
15 *
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.
28 */
29
30#if defined(HAVE_CONFIG_H)
31#include "bconfig.h"
32#endif
33
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>
39
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>
48
49#include "atf-c/defs.h"
50#include "atf-c/error.h"
51
52#include "fs.h"
53#include "sanity.h"
54#include "text.h"
55#include "user.h"
56
57/* ---------------------------------------------------------------------
58 * Prototypes for auxiliary functions.
59 * --------------------------------------------------------------------- */
60
61static bool check_umask(const mode_t, const mode_t);
62static atf_error_t copy_contents(const atf_fs_path_t *, char **);
63static mode_t current_umask(void);
64static atf_error_t do_mkdtemp(char *);
65static atf_error_t normalize(atf_dynstr_t *, char *);
66static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
67static void replace_contents(atf_fs_path_t *, const char *);
68static const char *stat_type_to_string(const int);
69
70/* ---------------------------------------------------------------------
71 * The "invalid_umask" error type.
72 * --------------------------------------------------------------------- */
73
74struct invalid_umask_error_data {
75    /* One of atf_fs_stat_*_type. */
76    int m_type;
77
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];
84
85    /* The umask that caused the error. */
86    mode_t m_umask;
87};
88typedef struct invalid_umask_error_data invalid_umask_error_data_t;
89
90static
91void
92invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
93{
94    const invalid_umask_error_data_t *data;
95
96    PRE(atf_error_is(err, "invalid_umask"));
97
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);
103}
104
105static
106atf_error_t
107invalid_umask_error(const atf_fs_path_t *path, const int type,
108                    const mode_t failing_mask)
109{
110    atf_error_t err;
111    invalid_umask_error_data_t data;
112
113    data.m_type = type;
114
115    strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
116    data.m_path[sizeof(data.m_path) - 1] = '\0';
117
118    data.m_umask = failing_mask;
119
120    err = atf_error_new("invalid_umask", &data, sizeof(data),
121                        invalid_umask_format);
122
123    return err;
124}
125
126/* ---------------------------------------------------------------------
127 * The "unknown_file_type" error type.
128 * --------------------------------------------------------------------- */
129
130struct unknown_type_error_data {
131    const char *m_path;
132    int m_type;
133};
134typedef struct unknown_type_error_data unknown_type_error_data_t;
135
136static
137void
138unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
139{
140    const unknown_type_error_data_t *data;
141
142    PRE(atf_error_is(err, "unknown_type"));
143
144    data = atf_error_data(err);
145    snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
146             data->m_path);
147}
148
149static
150atf_error_t
151unknown_type_error(const char *path, int type)
152{
153    atf_error_t err;
154    unknown_type_error_data_t data;
155
156    data.m_path = path;
157    data.m_type = type;
158
159    err = atf_error_new("unknown_type", &data, sizeof(data),
160                        unknown_type_format);
161
162    return err;
163}
164
165/* ---------------------------------------------------------------------
166 * Auxiliary functions.
167 * --------------------------------------------------------------------- */
168
169static
170bool
171check_umask(const mode_t exp_mode, const mode_t min_mode)
172{
173    const mode_t actual_mode = (~current_umask() & exp_mode);
174    return (actual_mode & min_mode) == min_mode;
175}
176
177static
178atf_error_t
179copy_contents(const atf_fs_path_t *p, char **buf)
180{
181    atf_error_t err;
182    char *str;
183
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();
191    }
192
193    return err;
194}
195
196static
197mode_t
198current_umask(void)
199{
200    const mode_t current = umask(0);
201    (void)umask(current);
202    return current;
203}
204
205static
206atf_error_t
207do_mkdtemp(char *tmpl)
208{
209    atf_error_t err;
210
211    PRE(strstr(tmpl, "XXXXXX") != NULL);
212
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();
218
219    return err;
220}
221
222static
223atf_error_t
224do_mkstemp(char *tmpl, int *fdout)
225{
226    atf_error_t err;
227
228    PRE(strstr(tmpl, "XXXXXX") != NULL);
229
230    *fdout = mkstemp(tmpl);
231    if (*fdout == -1)
232        err = atf_libc_error(errno, "Cannot create temporary file "
233                             "with template '%s'", tmpl);
234
235    else
236        err = atf_no_error();
237
238    return err;
239}
240
241static
242atf_error_t
243normalize(atf_dynstr_t *d, char *p)
244{
245    const char *ptr;
246    char *last;
247    atf_error_t err;
248    bool first;
249
250    PRE(strlen(p) > 0);
251    PRE(atf_dynstr_length(d) == 0);
252
253    if (p[0] == '/')
254        err = atf_dynstr_append_fmt(d, "/");
255    else
256        err = atf_no_error();
257
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;
265        }
266
267        ptr = strtok_r(NULL, "/", &last);
268    }
269
270    return err;
271}
272
273static
274atf_error_t
275normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
276{
277    char *str;
278    atf_error_t err;
279    va_list ap2;
280
281    err = atf_dynstr_init(d);
282    if (atf_is_error(err))
283        goto out;
284
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);
293    }
294
295out:
296    return err;
297}
298
299static
300void
301replace_contents(atf_fs_path_t *p, const char *buf)
302{
303    atf_error_t err;
304
305    PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
306
307    atf_dynstr_clear(&p->m_data);
308    err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
309
310    INV(!atf_is_error(err));
311}
312
313static
314const char *
315stat_type_to_string(const int type)
316{
317    const char *str;
318
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;
338    }
339
340    return str;
341}
342
343/* ---------------------------------------------------------------------
344 * The "atf_fs_path" type.
345 * --------------------------------------------------------------------- */
346
347/*
348 * Constructors/destructors.
349 */
350
351atf_error_t
352atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
353{
354    atf_error_t err;
355    va_list ap2;
356
357    va_copy(ap2, ap);
358    err = normalize_ap(&p->m_data, fmt, ap2);
359    va_end(ap2);
360
361    return err;
362}
363
364atf_error_t
365atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
366{
367    va_list ap;
368    atf_error_t err;
369
370    va_start(ap, fmt);
371    err = atf_fs_path_init_ap(p, fmt, ap);
372    va_end(ap);
373
374    return err;
375}
376
377atf_error_t
378atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
379{
380    return atf_dynstr_copy(&dest->m_data, &src->m_data);
381}
382
383void
384atf_fs_path_fini(atf_fs_path_t *p)
385{
386    atf_dynstr_fini(&p->m_data);
387}
388
389/*
390 * Getters.
391 */
392
393atf_error_t
394atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
395{
396    const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
397    atf_error_t err;
398
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);
405
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) */
410
411    return err;
412}
413
414const char *
415atf_fs_path_cstring(const atf_fs_path_t *p)
416{
417    return atf_dynstr_cstring(&p->m_data);
418}
419
420atf_error_t
421atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
422{
423    size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
424    atf_error_t err;
425
426    if (begpos == atf_dynstr_npos)
427        begpos = 0;
428    else
429        begpos++;
430
431    err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
432
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) */
437
438    return err;
439}
440
441bool
442atf_fs_path_is_absolute(const atf_fs_path_t *p)
443{
444    return atf_dynstr_cstring(&p->m_data)[0] == '/';
445}
446
447bool
448atf_fs_path_is_root(const atf_fs_path_t *p)
449{
450    return atf_equal_dynstr_cstring(&p->m_data, "/");
451}
452
453/*
454 * Modifiers.
455 */
456
457atf_error_t
458atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
459{
460    atf_dynstr_t aux;
461    atf_error_t err;
462    va_list ap2;
463
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] != '/';
470
471        err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
472                                    needslash ? "/" : "", auxstr);
473
474        atf_dynstr_fini(&aux);
475    }
476
477    return err;
478}
479
480atf_error_t
481atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
482{
483    va_list ap;
484    atf_error_t err;
485
486    va_start(ap, fmt);
487    err = atf_fs_path_append_ap(p, fmt, ap);
488    va_end(ap);
489
490    return err;
491}
492
493atf_error_t
494atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
495{
496    return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
497}
498
499atf_error_t
500atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
501{
502    atf_error_t err;
503
504    PRE(!atf_fs_path_is_absolute(p));
505
506    err = atf_fs_getcwd(pa);
507    if (atf_is_error(err))
508        goto out;
509
510    err = atf_fs_path_append_path(pa, p);
511    if (atf_is_error(err))
512        atf_fs_path_fini(pa);
513
514out:
515    return err;
516}
517
518/*
519 * Operators.
520 */
521
522bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
523                               const atf_fs_path_t *p2)
524{
525    return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
526}
527
528/* ---------------------------------------------------------------------
529 * The "atf_fs_path" type.
530 * --------------------------------------------------------------------- */
531
532/*
533 * Constants.
534 */
535
536const int atf_fs_stat_blk_type  = 1;
537const int atf_fs_stat_chr_type  = 2;
538const int atf_fs_stat_dir_type  = 3;
539const int atf_fs_stat_fifo_type = 4;
540const int atf_fs_stat_lnk_type  = 5;
541const int atf_fs_stat_reg_type  = 6;
542const int atf_fs_stat_sock_type = 7;
543const int atf_fs_stat_wht_type  = 8;
544
545/*
546 * Constructors/destructors.
547 */
548
549atf_error_t
550atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
551{
552    atf_error_t err;
553    const char *pstr = atf_fs_path_cstring(p);
554
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);
574        }
575    }
576
577    return err;
578}
579
580void
581atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
582{
583    dest->m_type = src->m_type;
584    dest->m_sb = src->m_sb;
585}
586
587void
588atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
589{
590}
591
592/*
593 * Getters.
594 */
595
596dev_t
597atf_fs_stat_get_device(const atf_fs_stat_t *st)
598{
599    return st->m_sb.st_dev;
600}
601
602ino_t
603atf_fs_stat_get_inode(const atf_fs_stat_t *st)
604{
605    return st->m_sb.st_ino;
606}
607
608mode_t
609atf_fs_stat_get_mode(const atf_fs_stat_t *st)
610{
611    return st->m_sb.st_mode & ~S_IFMT;
612}
613
614off_t
615atf_fs_stat_get_size(const atf_fs_stat_t *st)
616{
617    return st->m_sb.st_size;
618}
619
620int
621atf_fs_stat_get_type(const atf_fs_stat_t *st)
622{
623    return st->m_type;
624}
625
626bool
627atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
628{
629    return st->m_sb.st_mode & S_IRUSR;
630}
631
632bool
633atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
634{
635    return st->m_sb.st_mode & S_IWUSR;
636}
637
638bool
639atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
640{
641    return st->m_sb.st_mode & S_IXUSR;
642}
643
644bool
645atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
646{
647    return st->m_sb.st_mode & S_IRGRP;
648}
649
650bool
651atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
652{
653    return st->m_sb.st_mode & S_IWGRP;
654}
655
656bool
657atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
658{
659    return st->m_sb.st_mode & S_IXGRP;
660}
661
662bool
663atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
664{
665    return st->m_sb.st_mode & S_IROTH;
666}
667
668bool
669atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
670{
671    return st->m_sb.st_mode & S_IWOTH;
672}
673
674bool
675atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
676{
677    return st->m_sb.st_mode & S_IXOTH;
678}
679
680/* ---------------------------------------------------------------------
681 * Free functions.
682 * --------------------------------------------------------------------- */
683
684const int atf_fs_access_f = 1 << 0;
685const int atf_fs_access_r = 1 << 1;
686const int atf_fs_access_w = 1 << 2;
687const int atf_fs_access_x = 1 << 3;
688
689/*
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.
693 */
694atf_error_t
695atf_fs_eaccess(const atf_fs_path_t *p, int mode)
696{
697    atf_error_t err;
698    struct stat st;
699    bool ok;
700
701    PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
702        mode & atf_fs_access_w || mode & atf_fs_access_x);
703
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;
708    }
709
710    err = atf_no_error();
711
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;
716
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;
722        }
723
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;
728        }
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));
734        }
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));
739        }
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));
745        }
746    }
747
748    if (!ok)
749        err = atf_libc_error(EACCES, "Access check failed");
750
751out:
752    return err;
753}
754
755atf_error_t
756atf_fs_exists(const atf_fs_path_t *p, bool *b)
757{
758    atf_error_t err;
759
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;
766        }
767    } else
768        *b = true;
769
770    return err;
771}
772
773atf_error_t
774atf_fs_getcwd(atf_fs_path_t *p)
775{
776    atf_error_t err;
777    char *cwd;
778
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;
787    }
788
789    err = atf_fs_path_init_fmt(p, "%s", cwd);
790    free(cwd);
791
792out:
793    return err;
794}
795
796atf_error_t
797atf_fs_mkdtemp(atf_fs_path_t *p)
798{
799    atf_error_t err;
800    char *buf;
801
802    if (!check_umask(S_IRWXU, S_IRWXU)) {
803        err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
804        goto out;
805    }
806
807    err = copy_contents(p, &buf);
808    if (atf_is_error(err))
809        goto out;
810
811    err = do_mkdtemp(buf);
812    if (atf_is_error(err))
813        goto out_buf;
814
815    replace_contents(p, buf);
816
817    INV(!atf_is_error(err));
818out_buf:
819    free(buf);
820out:
821    return err;
822}
823
824atf_error_t
825atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
826{
827    atf_error_t err;
828    char *buf;
829    int fd;
830
831    if (!check_umask(S_IRWXU, S_IRWXU)) {
832        err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
833        goto out;
834    }
835
836    err = copy_contents(p, &buf);
837    if (atf_is_error(err))
838        goto out;
839
840    err = do_mkstemp(buf, &fd);
841    if (atf_is_error(err))
842        goto out_buf;
843
844    replace_contents(p, buf);
845    *fdout = fd;
846
847    INV(!atf_is_error(err));
848out_buf:
849    free(buf);
850out:
851    return err;
852}
853
854atf_error_t
855atf_fs_rmdir(const atf_fs_path_t *p)
856{
857    atf_error_t err;
858
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;
866        }
867        err = atf_libc_error(errno, "Cannot remove directory");
868    } else
869        err = atf_no_error();
870
871    return err;
872}
873
874atf_error_t
875atf_fs_unlink(const atf_fs_path_t *p)
876{
877    atf_error_t err;
878    const char *path;
879
880    path = atf_fs_path_cstring(p);
881
882    if (unlink(path) != 0)
883        err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
884    else
885        err = atf_no_error();
886
887    return err;
888}
889