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