fs.c revision 275988
1/* Copyright (c) 2007 The NetBSD Foundation, Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25
26#include "atf-c/detail/fs.h"
27
28#if defined(HAVE_CONFIG_H)
29#include "config.h"
30#endif
31
32#include <sys/types.h>
33#include <sys/param.h>
34#include <sys/mount.h>
35#include <sys/stat.h>
36#include <sys/wait.h>
37
38#include <dirent.h>
39#include <errno.h>
40#include <libgen.h>
41#include <stdarg.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "atf-c/defs.h"
48#include "atf-c/detail/sanity.h"
49#include "atf-c/detail/text.h"
50#include "atf-c/detail/user.h"
51#include "atf-c/error.h"
52
53/* ---------------------------------------------------------------------
54 * Prototypes for auxiliary functions.
55 * --------------------------------------------------------------------- */
56
57static bool check_umask(const mode_t, const mode_t);
58static atf_error_t copy_contents(const atf_fs_path_t *, char **);
59static mode_t current_umask(void);
60static atf_error_t do_mkdtemp(char *);
61static atf_error_t normalize(atf_dynstr_t *, char *);
62static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
63static void replace_contents(atf_fs_path_t *, const char *);
64static const char *stat_type_to_string(const int);
65
66/* ---------------------------------------------------------------------
67 * The "invalid_umask" error type.
68 * --------------------------------------------------------------------- */
69
70struct invalid_umask_error_data {
71    /* One of atf_fs_stat_*_type. */
72    int m_type;
73
74    /* The original path causing the error. */
75    /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
76     * from the error constructor, we cannot delete the path later on.
77     * Can't remember why atf_error_new does not take a hook for
78     * deletion. */
79    char m_path[1024];
80
81    /* The umask that caused the error. */
82    mode_t m_umask;
83};
84typedef struct invalid_umask_error_data invalid_umask_error_data_t;
85
86static
87void
88invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
89{
90    const invalid_umask_error_data_t *data;
91
92    PRE(atf_error_is(err, "invalid_umask"));
93
94    data = atf_error_data(err);
95    snprintf(buf, buflen, "Could not create the temporary %s %s because "
96             "it will not have enough access rights due to the current "
97             "umask %05o", stat_type_to_string(data->m_type),
98             data->m_path, (unsigned int)data->m_umask);
99}
100
101static
102atf_error_t
103invalid_umask_error(const atf_fs_path_t *path, const int type,
104                    const mode_t failing_mask)
105{
106    atf_error_t err;
107    invalid_umask_error_data_t data;
108
109    data.m_type = type;
110
111    strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
112    data.m_path[sizeof(data.m_path) - 1] = '\0';
113
114    data.m_umask = failing_mask;
115
116    err = atf_error_new("invalid_umask", &data, sizeof(data),
117                        invalid_umask_format);
118
119    return err;
120}
121
122/* ---------------------------------------------------------------------
123 * The "unknown_file_type" error type.
124 * --------------------------------------------------------------------- */
125
126struct unknown_type_error_data {
127    const char *m_path;
128    int m_type;
129};
130typedef struct unknown_type_error_data unknown_type_error_data_t;
131
132static
133void
134unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
135{
136    const unknown_type_error_data_t *data;
137
138    PRE(atf_error_is(err, "unknown_type"));
139
140    data = atf_error_data(err);
141    snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
142             data->m_path);
143}
144
145static
146atf_error_t
147unknown_type_error(const char *path, int type)
148{
149    atf_error_t err;
150    unknown_type_error_data_t data;
151
152    data.m_path = path;
153    data.m_type = type;
154
155    err = atf_error_new("unknown_type", &data, sizeof(data),
156                        unknown_type_format);
157
158    return err;
159}
160
161/* ---------------------------------------------------------------------
162 * Auxiliary functions.
163 * --------------------------------------------------------------------- */
164
165static
166bool
167check_umask(const mode_t exp_mode, const mode_t min_mode)
168{
169    const mode_t actual_mode = (~current_umask() & exp_mode);
170    return (actual_mode & min_mode) == min_mode;
171}
172
173static
174atf_error_t
175copy_contents(const atf_fs_path_t *p, char **buf)
176{
177    atf_error_t err;
178    char *str;
179
180    str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
181    if (str == NULL)
182        err = atf_no_memory_error();
183    else {
184        strcpy(str, atf_dynstr_cstring(&p->m_data));
185        *buf = str;
186        err = atf_no_error();
187    }
188
189    return err;
190}
191
192static
193mode_t
194current_umask(void)
195{
196    const mode_t current = umask(0);
197    (void)umask(current);
198    return current;
199}
200
201static
202atf_error_t
203do_mkdtemp(char *tmpl)
204{
205    atf_error_t err;
206
207    PRE(strstr(tmpl, "XXXXXX") != NULL);
208
209    if (mkdtemp(tmpl) == NULL)
210        err = atf_libc_error(errno, "Cannot create temporary directory "
211                             "with template '%s'", tmpl);
212    else
213        err = atf_no_error();
214
215    return err;
216}
217
218static
219atf_error_t
220do_mkstemp(char *tmpl, int *fdout)
221{
222    atf_error_t err;
223
224    PRE(strstr(tmpl, "XXXXXX") != NULL);
225
226    *fdout = mkstemp(tmpl);
227    if (*fdout == -1)
228        err = atf_libc_error(errno, "Cannot create temporary file "
229                             "with template '%s'", tmpl);
230
231    else
232        err = atf_no_error();
233
234    return err;
235}
236
237static
238atf_error_t
239normalize(atf_dynstr_t *d, char *p)
240{
241    const char *ptr;
242    char *last;
243    atf_error_t err;
244    bool first;
245
246    PRE(strlen(p) > 0);
247    PRE(atf_dynstr_length(d) == 0);
248
249    if (p[0] == '/')
250        err = atf_dynstr_append_fmt(d, "/");
251    else
252        err = atf_no_error();
253
254    first = true;
255    last = NULL; /* Silence GCC warning. */
256    ptr = strtok_r(p, "/", &last);
257    while (!atf_is_error(err) && ptr != NULL) {
258        if (strlen(ptr) > 0) {
259            err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
260            first = false;
261        }
262
263        ptr = strtok_r(NULL, "/", &last);
264    }
265
266    return err;
267}
268
269static
270atf_error_t
271normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
272{
273    char *str;
274    atf_error_t err;
275    va_list ap2;
276
277    err = atf_dynstr_init(d);
278    if (atf_is_error(err))
279        goto out;
280
281    va_copy(ap2, ap);
282    err = atf_text_format_ap(&str, p, ap2);
283    va_end(ap2);
284    if (atf_is_error(err))
285        atf_dynstr_fini(d);
286    else {
287        err = normalize(d, str);
288        free(str);
289    }
290
291out:
292    return err;
293}
294
295static
296void
297replace_contents(atf_fs_path_t *p, const char *buf)
298{
299    atf_error_t err;
300
301    PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
302
303    atf_dynstr_clear(&p->m_data);
304    err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
305
306    INV(!atf_is_error(err));
307}
308
309static
310const char *
311stat_type_to_string(const int type)
312{
313    const char *str;
314
315    if (type == atf_fs_stat_blk_type)
316        str = "block device";
317    else if (type == atf_fs_stat_chr_type)
318        str = "character device";
319    else if (type == atf_fs_stat_dir_type)
320        str = "directory";
321    else if (type == atf_fs_stat_fifo_type)
322        str = "named pipe";
323    else if (type == atf_fs_stat_lnk_type)
324        str = "symbolic link";
325    else if (type == atf_fs_stat_reg_type)
326        str = "regular file";
327    else if (type == atf_fs_stat_sock_type)
328        str = "socket";
329    else if (type == atf_fs_stat_wht_type)
330        str = "whiteout";
331    else {
332        UNREACHABLE;
333        str = NULL;
334    }
335
336    return str;
337}
338
339/* ---------------------------------------------------------------------
340 * The "atf_fs_path" type.
341 * --------------------------------------------------------------------- */
342
343/*
344 * Constructors/destructors.
345 */
346
347atf_error_t
348atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
349{
350    atf_error_t err;
351    va_list ap2;
352
353    va_copy(ap2, ap);
354    err = normalize_ap(&p->m_data, fmt, ap2);
355    va_end(ap2);
356
357    return err;
358}
359
360atf_error_t
361atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
362{
363    va_list ap;
364    atf_error_t err;
365
366    va_start(ap, fmt);
367    err = atf_fs_path_init_ap(p, fmt, ap);
368    va_end(ap);
369
370    return err;
371}
372
373atf_error_t
374atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
375{
376    return atf_dynstr_copy(&dest->m_data, &src->m_data);
377}
378
379void
380atf_fs_path_fini(atf_fs_path_t *p)
381{
382    atf_dynstr_fini(&p->m_data);
383}
384
385/*
386 * Getters.
387 */
388
389atf_error_t
390atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
391{
392    const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
393    atf_error_t err;
394
395    if (endpos == atf_dynstr_npos)
396        err = atf_fs_path_init_fmt(bp, ".");
397    else if (endpos == 0)
398        err = atf_fs_path_init_fmt(bp, "/");
399    else
400        err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
401
402#if defined(HAVE_CONST_DIRNAME)
403    INV(atf_equal_dynstr_cstring(&bp->m_data,
404                                 dirname(atf_dynstr_cstring(&p->m_data))));
405#endif /* defined(HAVE_CONST_DIRNAME) */
406
407    return err;
408}
409
410const char *
411atf_fs_path_cstring(const atf_fs_path_t *p)
412{
413    return atf_dynstr_cstring(&p->m_data);
414}
415
416atf_error_t
417atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
418{
419    size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
420    atf_error_t err;
421
422    if (begpos == atf_dynstr_npos)
423        begpos = 0;
424    else
425        begpos++;
426
427    err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
428
429#if defined(HAVE_CONST_BASENAME)
430    INV(atf_equal_dynstr_cstring(ln,
431                                 basename(atf_dynstr_cstring(&p->m_data))));
432#endif /* defined(HAVE_CONST_BASENAME) */
433
434    return err;
435}
436
437bool
438atf_fs_path_is_absolute(const atf_fs_path_t *p)
439{
440    return atf_dynstr_cstring(&p->m_data)[0] == '/';
441}
442
443bool
444atf_fs_path_is_root(const atf_fs_path_t *p)
445{
446    return atf_equal_dynstr_cstring(&p->m_data, "/");
447}
448
449/*
450 * Modifiers.
451 */
452
453atf_error_t
454atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
455{
456    atf_dynstr_t aux;
457    atf_error_t err;
458    va_list ap2;
459
460    va_copy(ap2, ap);
461    err = normalize_ap(&aux, fmt, ap2);
462    va_end(ap2);
463    if (!atf_is_error(err)) {
464        const char *auxstr = atf_dynstr_cstring(&aux);
465        const bool needslash = auxstr[0] != '/';
466
467        err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
468                                    needslash ? "/" : "", auxstr);
469
470        atf_dynstr_fini(&aux);
471    }
472
473    return err;
474}
475
476atf_error_t
477atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
478{
479    va_list ap;
480    atf_error_t err;
481
482    va_start(ap, fmt);
483    err = atf_fs_path_append_ap(p, fmt, ap);
484    va_end(ap);
485
486    return err;
487}
488
489atf_error_t
490atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
491{
492    return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
493}
494
495atf_error_t
496atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
497{
498    atf_error_t err;
499
500    PRE(!atf_fs_path_is_absolute(p));
501
502    err = atf_fs_getcwd(pa);
503    if (atf_is_error(err))
504        goto out;
505
506    err = atf_fs_path_append_path(pa, p);
507    if (atf_is_error(err))
508        atf_fs_path_fini(pa);
509
510out:
511    return err;
512}
513
514/*
515 * Operators.
516 */
517
518bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
519                               const atf_fs_path_t *p2)
520{
521    return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
522}
523
524/* ---------------------------------------------------------------------
525 * The "atf_fs_path" type.
526 * --------------------------------------------------------------------- */
527
528/*
529 * Constants.
530 */
531
532const int atf_fs_stat_blk_type  = 1;
533const int atf_fs_stat_chr_type  = 2;
534const int atf_fs_stat_dir_type  = 3;
535const int atf_fs_stat_fifo_type = 4;
536const int atf_fs_stat_lnk_type  = 5;
537const int atf_fs_stat_reg_type  = 6;
538const int atf_fs_stat_sock_type = 7;
539const int atf_fs_stat_wht_type  = 8;
540
541/*
542 * Constructors/destructors.
543 */
544
545atf_error_t
546atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
547{
548    atf_error_t err;
549    const char *pstr = atf_fs_path_cstring(p);
550
551    if (lstat(pstr, &st->m_sb) == -1) {
552        err = atf_libc_error(errno, "Cannot get information of %s; "
553                             "lstat(2) failed", pstr);
554    } else {
555        int type = st->m_sb.st_mode & S_IFMT;
556        err = atf_no_error();
557        switch (type) {
558            case S_IFBLK:  st->m_type = atf_fs_stat_blk_type;  break;
559            case S_IFCHR:  st->m_type = atf_fs_stat_chr_type;  break;
560            case S_IFDIR:  st->m_type = atf_fs_stat_dir_type;  break;
561            case S_IFIFO:  st->m_type = atf_fs_stat_fifo_type; break;
562            case S_IFLNK:  st->m_type = atf_fs_stat_lnk_type;  break;
563            case S_IFREG:  st->m_type = atf_fs_stat_reg_type;  break;
564            case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
565#if defined(S_IFWHT)
566            case S_IFWHT:  st->m_type = atf_fs_stat_wht_type;  break;
567#endif
568            default:
569                err = unknown_type_error(pstr, type);
570        }
571    }
572
573    return err;
574}
575
576void
577atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
578{
579    dest->m_type = src->m_type;
580    dest->m_sb = src->m_sb;
581}
582
583void
584atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
585{
586}
587
588/*
589 * Getters.
590 */
591
592dev_t
593atf_fs_stat_get_device(const atf_fs_stat_t *st)
594{
595    return st->m_sb.st_dev;
596}
597
598ino_t
599atf_fs_stat_get_inode(const atf_fs_stat_t *st)
600{
601    return st->m_sb.st_ino;
602}
603
604mode_t
605atf_fs_stat_get_mode(const atf_fs_stat_t *st)
606{
607    return st->m_sb.st_mode & ~S_IFMT;
608}
609
610off_t
611atf_fs_stat_get_size(const atf_fs_stat_t *st)
612{
613    return st->m_sb.st_size;
614}
615
616int
617atf_fs_stat_get_type(const atf_fs_stat_t *st)
618{
619    return st->m_type;
620}
621
622bool
623atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
624{
625    return st->m_sb.st_mode & S_IRUSR;
626}
627
628bool
629atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
630{
631    return st->m_sb.st_mode & S_IWUSR;
632}
633
634bool
635atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
636{
637    return st->m_sb.st_mode & S_IXUSR;
638}
639
640bool
641atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
642{
643    return st->m_sb.st_mode & S_IRGRP;
644}
645
646bool
647atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
648{
649    return st->m_sb.st_mode & S_IWGRP;
650}
651
652bool
653atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
654{
655    return st->m_sb.st_mode & S_IXGRP;
656}
657
658bool
659atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
660{
661    return st->m_sb.st_mode & S_IROTH;
662}
663
664bool
665atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
666{
667    return st->m_sb.st_mode & S_IWOTH;
668}
669
670bool
671atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
672{
673    return st->m_sb.st_mode & S_IXOTH;
674}
675
676/* ---------------------------------------------------------------------
677 * Free functions.
678 * --------------------------------------------------------------------- */
679
680const int atf_fs_access_f = 1 << 0;
681const int atf_fs_access_r = 1 << 1;
682const int atf_fs_access_w = 1 << 2;
683const int atf_fs_access_x = 1 << 3;
684
685/*
686 * An implementation of access(2) but using the effective user value
687 * instead of the real one.  Also avoids false positives for root when
688 * asking for execute permissions, which appear in SunOS.
689 */
690atf_error_t
691atf_fs_eaccess(const atf_fs_path_t *p, int mode)
692{
693    atf_error_t err;
694    struct stat st;
695    bool ok;
696
697    PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
698        mode & atf_fs_access_w || mode & atf_fs_access_x);
699
700    if (lstat(atf_fs_path_cstring(p), &st) == -1) {
701        err = atf_libc_error(errno, "Cannot get information from file %s",
702                             atf_fs_path_cstring(p));
703        goto out;
704    }
705
706    err = atf_no_error();
707
708    /* Early return if we are only checking for existence and the file
709     * exists (stat call returned). */
710    if (mode & atf_fs_access_f)
711        goto out;
712
713    ok = false;
714    if (atf_user_is_root()) {
715        if (!ok && !(mode & atf_fs_access_x)) {
716            /* Allow root to read/write any file. */
717            ok = true;
718        }
719
720        if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
721            /* Allow root to execute the file if any of its execution bits
722             * are set. */
723            ok = true;
724        }
725    } else {
726        if (!ok && (atf_user_euid() == st.st_uid)) {
727            ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
728                 ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
729                 ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
730        }
731        if (!ok && atf_user_is_member_of_group(st.st_gid)) {
732            ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
733                 ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
734                 ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
735        }
736        if (!ok && ((atf_user_euid() != st.st_uid) &&
737                    !atf_user_is_member_of_group(st.st_gid))) {
738            ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
739                 ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
740                 ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
741        }
742    }
743
744    if (!ok)
745        err = atf_libc_error(EACCES, "Access check failed");
746
747out:
748    return err;
749}
750
751atf_error_t
752atf_fs_exists(const atf_fs_path_t *p, bool *b)
753{
754    atf_error_t err;
755
756    err = atf_fs_eaccess(p, atf_fs_access_f);
757    if (atf_is_error(err)) {
758        if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
759            atf_error_free(err);
760            err = atf_no_error();
761            *b = false;
762        }
763    } else
764        *b = true;
765
766    return err;
767}
768
769atf_error_t
770atf_fs_getcwd(atf_fs_path_t *p)
771{
772    atf_error_t err;
773    char *cwd;
774
775#if defined(HAVE_GETCWD_DYN)
776    cwd = getcwd(NULL, 0);
777#else
778    cwd = getcwd(NULL, MAXPATHLEN);
779#endif
780    if (cwd == NULL) {
781        err = atf_libc_error(errno, "Cannot determine current directory");
782        goto out;
783    }
784
785    err = atf_fs_path_init_fmt(p, "%s", cwd);
786    free(cwd);
787
788out:
789    return err;
790}
791
792atf_error_t
793atf_fs_mkdtemp(atf_fs_path_t *p)
794{
795    atf_error_t err;
796    char *buf;
797
798    if (!check_umask(S_IRWXU, S_IRWXU)) {
799        err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
800        goto out;
801    }
802
803    err = copy_contents(p, &buf);
804    if (atf_is_error(err))
805        goto out;
806
807    err = do_mkdtemp(buf);
808    if (atf_is_error(err))
809        goto out_buf;
810
811    replace_contents(p, buf);
812
813    INV(!atf_is_error(err));
814out_buf:
815    free(buf);
816out:
817    return err;
818}
819
820atf_error_t
821atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
822{
823    atf_error_t err;
824    char *buf;
825    int fd;
826
827    if (!check_umask(S_IRWXU, S_IRWXU)) {
828        err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
829        goto out;
830    }
831
832    err = copy_contents(p, &buf);
833    if (atf_is_error(err))
834        goto out;
835
836    err = do_mkstemp(buf, &fd);
837    if (atf_is_error(err))
838        goto out_buf;
839
840    replace_contents(p, buf);
841    *fdout = fd;
842
843    INV(!atf_is_error(err));
844out_buf:
845    free(buf);
846out:
847    return err;
848}
849
850atf_error_t
851atf_fs_rmdir(const atf_fs_path_t *p)
852{
853    atf_error_t err;
854
855    if (rmdir(atf_fs_path_cstring(p))) {
856        if (errno == EEXIST) {
857            /* Some operating systems (e.g. OpenSolaris 200906) return
858             * EEXIST instead of ENOTEMPTY for non-empty directories.
859             * Homogenize the return value so that callers don't need
860             * to bother about differences in operating systems. */
861            errno = ENOTEMPTY;
862        }
863        err = atf_libc_error(errno, "Cannot remove directory");
864    } else
865        err = atf_no_error();
866
867    return err;
868}
869
870atf_error_t
871atf_fs_unlink(const atf_fs_path_t *p)
872{
873    atf_error_t err;
874    const char *path;
875
876    path = atf_fs_path_cstring(p);
877
878    if (unlink(path) != 0)
879        err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
880    else
881        err = atf_no_error();
882
883    return err;
884}
885