1 /* $NetBSD: fs.c,v 1.3 2014/12/10 04:38:03 christos Exp $ */
4 * Automated Testing Framework (atf)
6 * Copyright (c) 2007 The NetBSD Foundation, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
32 #if defined(HAVE_CONFIG_H)
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
51 #include "atf-c/defs.h"
52 #include "atf-c/error.h"
59 /* ---------------------------------------------------------------------
60 * Prototypes for auxiliary functions.
61 * --------------------------------------------------------------------- */
63 static bool check_umask(const mode_t
, const mode_t
);
64 static atf_error_t
copy_contents(const atf_fs_path_t
*, char **);
65 static mode_t
current_umask(void);
66 static atf_error_t
do_mkdtemp(char *);
67 static atf_error_t
normalize(atf_dynstr_t
*, char *);
68 static atf_error_t
normalize_ap(atf_dynstr_t
*, const char *, va_list);
69 static void replace_contents(atf_fs_path_t
*, const char *);
70 static const char *stat_type_to_string(const int);
72 /* ---------------------------------------------------------------------
73 * The "invalid_umask" error type.
74 * --------------------------------------------------------------------- */
76 struct invalid_umask_error_data
{
77 /* One of atf_fs_stat_*_type. */
80 /* The original path causing the error. */
81 /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
82 * from the error constructor, we cannot delete the path later on.
83 * Can't remember why atf_error_new does not take a hook for
87 /* The umask that caused the error. */
90 typedef struct invalid_umask_error_data invalid_umask_error_data_t
;
94 invalid_umask_format(const atf_error_t err
, char *buf
, size_t buflen
)
96 const invalid_umask_error_data_t
*data
;
98 PRE(atf_error_is(err
, "invalid_umask"));
100 data
= atf_error_data(err
);
101 snprintf(buf
, buflen
, "Could not create the temporary %s %s because "
102 "it will not have enough access rights due to the current "
103 "umask %05o", stat_type_to_string(data
->m_type
),
104 data
->m_path
, (unsigned int)data
->m_umask
);
109 invalid_umask_error(const atf_fs_path_t
*path
, const int type
,
110 const mode_t failing_mask
)
113 invalid_umask_error_data_t data
;
117 strncpy(data
.m_path
, atf_fs_path_cstring(path
), sizeof(data
.m_path
));
118 data
.m_path
[sizeof(data
.m_path
) - 1] = '\0';
120 data
.m_umask
= failing_mask
;
122 err
= atf_error_new("invalid_umask", &data
, sizeof(data
),
123 invalid_umask_format
);
128 /* ---------------------------------------------------------------------
129 * The "unknown_file_type" error type.
130 * --------------------------------------------------------------------- */
132 struct unknown_type_error_data
{
136 typedef struct unknown_type_error_data unknown_type_error_data_t
;
140 unknown_type_format(const atf_error_t err
, char *buf
, size_t buflen
)
142 const unknown_type_error_data_t
*data
;
144 PRE(atf_error_is(err
, "unknown_type"));
146 data
= atf_error_data(err
);
147 snprintf(buf
, buflen
, "Unknown file type %d of %s", data
->m_type
,
153 unknown_type_error(const char *path
, int type
)
156 unknown_type_error_data_t data
;
161 err
= atf_error_new("unknown_type", &data
, sizeof(data
),
162 unknown_type_format
);
167 /* ---------------------------------------------------------------------
168 * Auxiliary functions.
169 * --------------------------------------------------------------------- */
173 check_umask(const mode_t exp_mode
, const mode_t min_mode
)
175 const mode_t actual_mode
= (~current_umask() & exp_mode
);
176 return (actual_mode
& min_mode
) == min_mode
;
181 copy_contents(const atf_fs_path_t
*p
, char **buf
)
186 str
= (char *)malloc(atf_dynstr_length(&p
->m_data
) + 1);
188 err
= atf_no_memory_error();
190 strcpy(str
, atf_dynstr_cstring(&p
->m_data
));
192 err
= atf_no_error();
202 const mode_t current
= umask(0);
203 (void)umask(current
);
209 do_mkdtemp(char *tmpl
)
213 PRE(strstr(tmpl
, "XXXXXX") != NULL
);
215 if (mkdtemp(tmpl
) == NULL
)
216 err
= atf_libc_error(errno
, "Cannot create temporary directory "
217 "with template '%s'", tmpl
);
219 err
= atf_no_error();
226 do_mkstemp(char *tmpl
, int *fdout
)
230 PRE(strstr(tmpl
, "XXXXXX") != NULL
);
232 *fdout
= mkstemp(tmpl
);
234 err
= atf_libc_error(errno
, "Cannot create temporary file "
235 "with template '%s'", tmpl
);
238 err
= atf_no_error();
245 normalize(atf_dynstr_t
*d
, char *p
)
253 PRE(atf_dynstr_length(d
) == 0);
256 err
= atf_dynstr_append_fmt(d
, "/");
258 err
= atf_no_error();
261 last
= NULL
; /* Silence GCC warning. */
262 ptr
= strtok_r(p
, "/", &last
);
263 while (!atf_is_error(err
) && ptr
!= NULL
) {
264 if (strlen(ptr
) > 0) {
265 err
= atf_dynstr_append_fmt(d
, "%s%s", first
? "" : "/", ptr
);
269 ptr
= strtok_r(NULL
, "/", &last
);
277 normalize_ap(atf_dynstr_t
*d
, const char *p
, va_list ap
)
283 err
= atf_dynstr_init(d
);
284 if (atf_is_error(err
))
288 err
= atf_text_format_ap(&str
, p
, ap2
);
290 if (atf_is_error(err
))
293 err
= normalize(d
, str
);
303 replace_contents(atf_fs_path_t
*p
, const char *buf
)
307 PRE(atf_dynstr_length(&p
->m_data
) == strlen(buf
));
309 atf_dynstr_clear(&p
->m_data
);
310 err
= atf_dynstr_append_fmt(&p
->m_data
, "%s", buf
);
312 INV(!atf_is_error(err
));
317 stat_type_to_string(const int type
)
321 if (type
== atf_fs_stat_blk_type
)
322 str
= "block device";
323 else if (type
== atf_fs_stat_chr_type
)
324 str
= "character device";
325 else if (type
== atf_fs_stat_dir_type
)
327 else if (type
== atf_fs_stat_fifo_type
)
329 else if (type
== atf_fs_stat_lnk_type
)
330 str
= "symbolic link";
331 else if (type
== atf_fs_stat_reg_type
)
332 str
= "regular file";
333 else if (type
== atf_fs_stat_sock_type
)
335 else if (type
== atf_fs_stat_wht_type
)
345 /* ---------------------------------------------------------------------
346 * The "atf_fs_path" type.
347 * --------------------------------------------------------------------- */
350 * Constructors/destructors.
354 atf_fs_path_init_ap(atf_fs_path_t
*p
, const char *fmt
, va_list ap
)
360 err
= normalize_ap(&p
->m_data
, fmt
, ap2
);
367 atf_fs_path_init_fmt(atf_fs_path_t
*p
, const char *fmt
, ...)
373 err
= atf_fs_path_init_ap(p
, fmt
, ap
);
380 atf_fs_path_copy(atf_fs_path_t
*dest
, const atf_fs_path_t
*src
)
382 return atf_dynstr_copy(&dest
->m_data
, &src
->m_data
);
386 atf_fs_path_fini(atf_fs_path_t
*p
)
388 atf_dynstr_fini(&p
->m_data
);
396 atf_fs_path_branch_path(const atf_fs_path_t
*p
, atf_fs_path_t
*bp
)
398 const size_t endpos
= atf_dynstr_rfind_ch(&p
->m_data
, '/');
401 if (endpos
== atf_dynstr_npos
)
402 err
= atf_fs_path_init_fmt(bp
, ".");
403 else if (endpos
== 0)
404 err
= atf_fs_path_init_fmt(bp
, "/");
406 err
= atf_dynstr_init_substr(&bp
->m_data
, &p
->m_data
, 0, endpos
);
408 #if defined(HAVE_CONST_DIRNAME)
409 INV(atf_equal_dynstr_cstring(&bp
->m_data
,
410 dirname(atf_dynstr_cstring(&p
->m_data
))));
411 #endif /* defined(HAVE_CONST_DIRNAME) */
417 atf_fs_path_cstring(const atf_fs_path_t
*p
)
419 return atf_dynstr_cstring(&p
->m_data
);
423 atf_fs_path_leaf_name(const atf_fs_path_t
*p
, atf_dynstr_t
*ln
)
425 size_t begpos
= atf_dynstr_rfind_ch(&p
->m_data
, '/');
428 if (begpos
== atf_dynstr_npos
)
433 err
= atf_dynstr_init_substr(ln
, &p
->m_data
, begpos
, atf_dynstr_npos
);
435 #if defined(HAVE_CONST_BASENAME)
436 INV(atf_equal_dynstr_cstring(ln
,
437 basename(atf_dynstr_cstring(&p
->m_data
))));
438 #endif /* defined(HAVE_CONST_BASENAME) */
444 atf_fs_path_is_absolute(const atf_fs_path_t
*p
)
446 return atf_dynstr_cstring(&p
->m_data
)[0] == '/';
450 atf_fs_path_is_root(const atf_fs_path_t
*p
)
452 return atf_equal_dynstr_cstring(&p
->m_data
, "/");
460 atf_fs_path_append_ap(atf_fs_path_t
*p
, const char *fmt
, va_list ap
)
467 err
= normalize_ap(&aux
, fmt
, ap2
);
469 if (!atf_is_error(err
)) {
470 const char *auxstr
= atf_dynstr_cstring(&aux
);
471 const bool needslash
= auxstr
[0] != '/';
473 err
= atf_dynstr_append_fmt(&p
->m_data
, "%s%s",
474 needslash
? "/" : "", auxstr
);
476 atf_dynstr_fini(&aux
);
483 atf_fs_path_append_fmt(atf_fs_path_t
*p
, const char *fmt
, ...)
489 err
= atf_fs_path_append_ap(p
, fmt
, ap
);
496 atf_fs_path_append_path(atf_fs_path_t
*p
, const atf_fs_path_t
*p2
)
498 return atf_fs_path_append_fmt(p
, "%s", atf_dynstr_cstring(&p2
->m_data
));
502 atf_fs_path_to_absolute(const atf_fs_path_t
*p
, atf_fs_path_t
*pa
)
506 PRE(!atf_fs_path_is_absolute(p
));
508 err
= atf_fs_getcwd(pa
);
509 if (atf_is_error(err
))
512 err
= atf_fs_path_append_path(pa
, p
);
513 if (atf_is_error(err
))
514 atf_fs_path_fini(pa
);
524 bool atf_equal_fs_path_fs_path(const atf_fs_path_t
*p1
,
525 const atf_fs_path_t
*p2
)
527 return atf_equal_dynstr_dynstr(&p1
->m_data
, &p2
->m_data
);
530 /* ---------------------------------------------------------------------
531 * The "atf_fs_path" type.
532 * --------------------------------------------------------------------- */
538 const int atf_fs_stat_blk_type
= 1;
539 const int atf_fs_stat_chr_type
= 2;
540 const int atf_fs_stat_dir_type
= 3;
541 const int atf_fs_stat_fifo_type
= 4;
542 const int atf_fs_stat_lnk_type
= 5;
543 const int atf_fs_stat_reg_type
= 6;
544 const int atf_fs_stat_sock_type
= 7;
545 const int atf_fs_stat_wht_type
= 8;
548 * Constructors/destructors.
552 atf_fs_stat_init(atf_fs_stat_t
*st
, const atf_fs_path_t
*p
)
555 const char *pstr
= atf_fs_path_cstring(p
);
557 if (lstat(pstr
, &st
->m_sb
) == -1) {
558 err
= atf_libc_error(errno
, "Cannot get information of %s; "
559 "lstat(2) failed", pstr
);
561 int type
= st
->m_sb
.st_mode
& S_IFMT
;
562 err
= atf_no_error();
564 case S_IFBLK
: st
->m_type
= atf_fs_stat_blk_type
; break;
565 case S_IFCHR
: st
->m_type
= atf_fs_stat_chr_type
; break;
566 case S_IFDIR
: st
->m_type
= atf_fs_stat_dir_type
; break;
567 case S_IFIFO
: st
->m_type
= atf_fs_stat_fifo_type
; break;
568 case S_IFLNK
: st
->m_type
= atf_fs_stat_lnk_type
; break;
569 case S_IFREG
: st
->m_type
= atf_fs_stat_reg_type
; break;
570 case S_IFSOCK
: st
->m_type
= atf_fs_stat_sock_type
; break;
572 case S_IFWHT
: st
->m_type
= atf_fs_stat_wht_type
; break;
575 err
= unknown_type_error(pstr
, type
);
583 atf_fs_stat_copy(atf_fs_stat_t
*dest
, const atf_fs_stat_t
*src
)
585 dest
->m_type
= src
->m_type
;
586 dest
->m_sb
= src
->m_sb
;
590 atf_fs_stat_fini(atf_fs_stat_t
*st ATF_DEFS_ATTRIBUTE_UNUSED
)
599 atf_fs_stat_get_device(const atf_fs_stat_t
*st
)
601 return st
->m_sb
.st_dev
;
605 atf_fs_stat_get_inode(const atf_fs_stat_t
*st
)
607 return st
->m_sb
.st_ino
;
611 atf_fs_stat_get_mode(const atf_fs_stat_t
*st
)
613 return st
->m_sb
.st_mode
& ~S_IFMT
;
617 atf_fs_stat_get_size(const atf_fs_stat_t
*st
)
619 return st
->m_sb
.st_size
;
623 atf_fs_stat_get_type(const atf_fs_stat_t
*st
)
629 atf_fs_stat_is_owner_readable(const atf_fs_stat_t
*st
)
631 return st
->m_sb
.st_mode
& S_IRUSR
;
635 atf_fs_stat_is_owner_writable(const atf_fs_stat_t
*st
)
637 return st
->m_sb
.st_mode
& S_IWUSR
;
641 atf_fs_stat_is_owner_executable(const atf_fs_stat_t
*st
)
643 return st
->m_sb
.st_mode
& S_IXUSR
;
647 atf_fs_stat_is_group_readable(const atf_fs_stat_t
*st
)
649 return st
->m_sb
.st_mode
& S_IRGRP
;
653 atf_fs_stat_is_group_writable(const atf_fs_stat_t
*st
)
655 return st
->m_sb
.st_mode
& S_IWGRP
;
659 atf_fs_stat_is_group_executable(const atf_fs_stat_t
*st
)
661 return st
->m_sb
.st_mode
& S_IXGRP
;
665 atf_fs_stat_is_other_readable(const atf_fs_stat_t
*st
)
667 return st
->m_sb
.st_mode
& S_IROTH
;
671 atf_fs_stat_is_other_writable(const atf_fs_stat_t
*st
)
673 return st
->m_sb
.st_mode
& S_IWOTH
;
677 atf_fs_stat_is_other_executable(const atf_fs_stat_t
*st
)
679 return st
->m_sb
.st_mode
& S_IXOTH
;
682 /* ---------------------------------------------------------------------
684 * --------------------------------------------------------------------- */
686 const int atf_fs_access_f
= 1 << 0;
687 const int atf_fs_access_r
= 1 << 1;
688 const int atf_fs_access_w
= 1 << 2;
689 const int atf_fs_access_x
= 1 << 3;
692 * An implementation of access(2) but using the effective user value
693 * instead of the real one. Also avoids false positives for root when
694 * asking for execute permissions, which appear in SunOS.
697 atf_fs_eaccess(const atf_fs_path_t
*p
, int mode
)
703 PRE(mode
& atf_fs_access_f
|| mode
& atf_fs_access_r
||
704 mode
& atf_fs_access_w
|| mode
& atf_fs_access_x
);
706 if (lstat(atf_fs_path_cstring(p
), &st
) == -1) {
707 err
= atf_libc_error(errno
, "Cannot get information from file %s",
708 atf_fs_path_cstring(p
));
712 err
= atf_no_error();
714 /* Early return if we are only checking for existence and the file
715 * exists (stat call returned). */
716 if (mode
& atf_fs_access_f
)
720 if (atf_user_is_root()) {
721 if (!ok
&& !(mode
& atf_fs_access_x
)) {
722 /* Allow root to read/write any file. */
726 if (!ok
&& (st
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))) {
727 /* Allow root to execute the file if any of its execution bits
732 if (!ok
&& (atf_user_euid() == st
.st_uid
)) {
733 ok
= ((mode
& atf_fs_access_r
) && (st
.st_mode
& S_IRUSR
)) ||
734 ((mode
& atf_fs_access_w
) && (st
.st_mode
& S_IWUSR
)) ||
735 ((mode
& atf_fs_access_x
) && (st
.st_mode
& S_IXUSR
));
737 if (!ok
&& atf_user_is_member_of_group(st
.st_gid
)) {
738 ok
= ((mode
& atf_fs_access_r
) && (st
.st_mode
& S_IRGRP
)) ||
739 ((mode
& atf_fs_access_w
) && (st
.st_mode
& S_IWGRP
)) ||
740 ((mode
& atf_fs_access_x
) && (st
.st_mode
& S_IXGRP
));
742 if (!ok
&& ((atf_user_euid() != st
.st_uid
) &&
743 !atf_user_is_member_of_group(st
.st_gid
))) {
744 ok
= ((mode
& atf_fs_access_r
) && (st
.st_mode
& S_IROTH
)) ||
745 ((mode
& atf_fs_access_w
) && (st
.st_mode
& S_IWOTH
)) ||
746 ((mode
& atf_fs_access_x
) && (st
.st_mode
& S_IXOTH
));
751 err
= atf_libc_error(EACCES
, "Access check failed");
758 atf_fs_exists(const atf_fs_path_t
*p
, bool *b
)
762 err
= atf_fs_eaccess(p
, atf_fs_access_f
);
763 if (atf_is_error(err
)) {
764 if (atf_error_is(err
, "libc") && atf_libc_error_code(err
) == ENOENT
) {
766 err
= atf_no_error();
776 atf_fs_getcwd(atf_fs_path_t
*p
)
781 #if defined(HAVE_GETCWD_DYN)
782 cwd
= getcwd(NULL
, 0);
784 cwd
= getcwd(NULL
, MAXPATHLEN
);
787 err
= atf_libc_error(errno
, "Cannot determine current directory");
791 err
= atf_fs_path_init_fmt(p
, "%s", cwd
);
799 atf_fs_mkdtemp(atf_fs_path_t
*p
)
804 if (!check_umask(S_IRWXU
, S_IRWXU
)) {
805 err
= invalid_umask_error(p
, atf_fs_stat_dir_type
, current_umask());
809 err
= copy_contents(p
, &buf
);
810 if (atf_is_error(err
))
813 err
= do_mkdtemp(buf
);
814 if (atf_is_error(err
))
817 replace_contents(p
, buf
);
819 INV(!atf_is_error(err
));
827 atf_fs_mkstemp(atf_fs_path_t
*p
, int *fdout
)
833 if (!check_umask(S_IRWXU
, S_IRWXU
)) {
834 err
= invalid_umask_error(p
, atf_fs_stat_reg_type
, current_umask());
838 err
= copy_contents(p
, &buf
);
839 if (atf_is_error(err
))
842 err
= do_mkstemp(buf
, &fd
);
843 if (atf_is_error(err
))
846 replace_contents(p
, buf
);
849 INV(!atf_is_error(err
));
857 atf_fs_rmdir(const atf_fs_path_t
*p
)
861 if (rmdir(atf_fs_path_cstring(p
))) {
862 if (errno
== EEXIST
) {
863 /* Some operating systems (e.g. OpenSolaris 200906) return
864 * EEXIST instead of ENOTEMPTY for non-empty directories.
865 * Homogenize the return value so that callers don't need
866 * to bother about differences in operating systems. */
869 err
= atf_libc_error(errno
, "Cannot remove directory");
871 err
= atf_no_error();
877 atf_fs_unlink(const atf_fs_path_t
*p
)
882 path
= atf_fs_path_cstring(p
);
884 if (unlink(path
) != 0)
885 err
= atf_libc_error(errno
, "Cannot unlink file: '%s'", path
);
887 err
= atf_no_error();