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