2 * Automated Testing Framework (atf)
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
30 #if defined(HAVE_CONFIG_H)
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mount.h>
49 #include "atf-c/defs.h"
50 #include "atf-c/error.h"
57 /* ---------------------------------------------------------------------
58 * Prototypes for auxiliary functions.
59 * --------------------------------------------------------------------- */
61 static bool check_umask(const mode_t
, const mode_t
);
62 static atf_error_t
copy_contents(const atf_fs_path_t
*, char **);
63 static mode_t
current_umask(void);
64 static atf_error_t
do_mkdtemp(char *);
65 static atf_error_t
normalize(atf_dynstr_t
*, char *);
66 static atf_error_t
normalize_ap(atf_dynstr_t
*, const char *, va_list);
67 static void replace_contents(atf_fs_path_t
*, const char *);
68 static const char *stat_type_to_string(const int);
70 /* ---------------------------------------------------------------------
71 * The "invalid_umask" error type.
72 * --------------------------------------------------------------------- */
74 struct invalid_umask_error_data
{
75 /* One of atf_fs_stat_*_type. */
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
85 /* The umask that caused the error. */
88 typedef struct invalid_umask_error_data invalid_umask_error_data_t
;
92 invalid_umask_format(const atf_error_t err
, char *buf
, size_t buflen
)
94 const invalid_umask_error_data_t
*data
;
96 PRE(atf_error_is(err
, "invalid_umask"));
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
);
107 invalid_umask_error(const atf_fs_path_t
*path
, const int type
,
108 const mode_t failing_mask
)
111 invalid_umask_error_data_t data
;
115 strncpy(data
.m_path
, atf_fs_path_cstring(path
), sizeof(data
.m_path
));
116 data
.m_path
[sizeof(data
.m_path
) - 1] = '\0';
118 data
.m_umask
= failing_mask
;
120 err
= atf_error_new("invalid_umask", &data
, sizeof(data
),
121 invalid_umask_format
);
126 /* ---------------------------------------------------------------------
127 * The "unknown_file_type" error type.
128 * --------------------------------------------------------------------- */
130 struct unknown_type_error_data
{
134 typedef struct unknown_type_error_data unknown_type_error_data_t
;
138 unknown_type_format(const atf_error_t err
, char *buf
, size_t buflen
)
140 const unknown_type_error_data_t
*data
;
142 PRE(atf_error_is(err
, "unknown_type"));
144 data
= atf_error_data(err
);
145 snprintf(buf
, buflen
, "Unknown file type %d of %s", data
->m_type
,
151 unknown_type_error(const char *path
, int type
)
154 unknown_type_error_data_t data
;
159 err
= atf_error_new("unknown_type", &data
, sizeof(data
),
160 unknown_type_format
);
165 /* ---------------------------------------------------------------------
166 * Auxiliary functions.
167 * --------------------------------------------------------------------- */
171 check_umask(const mode_t exp_mode
, const mode_t min_mode
)
173 const mode_t actual_mode
= (~current_umask() & exp_mode
);
174 return (actual_mode
& min_mode
) == min_mode
;
179 copy_contents(const atf_fs_path_t
*p
, char **buf
)
184 str
= (char *)malloc(atf_dynstr_length(&p
->m_data
) + 1);
186 err
= atf_no_memory_error();
188 strcpy(str
, atf_dynstr_cstring(&p
->m_data
));
190 err
= atf_no_error();
200 const mode_t current
= umask(0);
201 (void)umask(current
);
207 do_mkdtemp(char *tmpl
)
211 PRE(strstr(tmpl
, "XXXXXX") != NULL
);
213 if (mkdtemp(tmpl
) == NULL
)
214 err
= atf_libc_error(errno
, "Cannot create temporary directory "
215 "with template '%s'", tmpl
);
217 err
= atf_no_error();
224 do_mkstemp(char *tmpl
, int *fdout
)
228 PRE(strstr(tmpl
, "XXXXXX") != NULL
);
230 *fdout
= mkstemp(tmpl
);
232 err
= atf_libc_error(errno
, "Cannot create temporary file "
233 "with template '%s'", tmpl
);
236 err
= atf_no_error();
243 normalize(atf_dynstr_t
*d
, char *p
)
251 PRE(atf_dynstr_length(d
) == 0);
254 err
= atf_dynstr_append_fmt(d
, "/");
256 err
= atf_no_error();
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
);
267 ptr
= strtok_r(NULL
, "/", &last
);
275 normalize_ap(atf_dynstr_t
*d
, const char *p
, va_list ap
)
281 err
= atf_dynstr_init(d
);
282 if (atf_is_error(err
))
286 err
= atf_text_format_ap(&str
, p
, ap2
);
288 if (atf_is_error(err
))
291 err
= normalize(d
, str
);
301 replace_contents(atf_fs_path_t
*p
, const char *buf
)
305 PRE(atf_dynstr_length(&p
->m_data
) == strlen(buf
));
307 atf_dynstr_clear(&p
->m_data
);
308 err
= atf_dynstr_append_fmt(&p
->m_data
, "%s", buf
);
310 INV(!atf_is_error(err
));
315 stat_type_to_string(const int type
)
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
)
325 else if (type
== atf_fs_stat_fifo_type
)
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
)
333 else if (type
== atf_fs_stat_wht_type
)
343 /* ---------------------------------------------------------------------
344 * The "atf_fs_path" type.
345 * --------------------------------------------------------------------- */
348 * Constructors/destructors.
352 atf_fs_path_init_ap(atf_fs_path_t
*p
, const char *fmt
, va_list ap
)
358 err
= normalize_ap(&p
->m_data
, fmt
, ap2
);
365 atf_fs_path_init_fmt(atf_fs_path_t
*p
, const char *fmt
, ...)
371 err
= atf_fs_path_init_ap(p
, fmt
, ap
);
378 atf_fs_path_copy(atf_fs_path_t
*dest
, const atf_fs_path_t
*src
)
380 return atf_dynstr_copy(&dest
->m_data
, &src
->m_data
);
384 atf_fs_path_fini(atf_fs_path_t
*p
)
386 atf_dynstr_fini(&p
->m_data
);
394 atf_fs_path_branch_path(const atf_fs_path_t
*p
, atf_fs_path_t
*bp
)
396 const size_t endpos
= atf_dynstr_rfind_ch(&p
->m_data
, '/');
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
, "/");
404 err
= atf_dynstr_init_substr(&bp
->m_data
, &p
->m_data
, 0, endpos
);
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) */
415 atf_fs_path_cstring(const atf_fs_path_t
*p
)
417 return atf_dynstr_cstring(&p
->m_data
);
421 atf_fs_path_leaf_name(const atf_fs_path_t
*p
, atf_dynstr_t
*ln
)
423 size_t begpos
= atf_dynstr_rfind_ch(&p
->m_data
, '/');
426 if (begpos
== atf_dynstr_npos
)
431 err
= atf_dynstr_init_substr(ln
, &p
->m_data
, begpos
, atf_dynstr_npos
);
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) */
442 atf_fs_path_is_absolute(const atf_fs_path_t
*p
)
444 return atf_dynstr_cstring(&p
->m_data
)[0] == '/';
448 atf_fs_path_is_root(const atf_fs_path_t
*p
)
450 return atf_equal_dynstr_cstring(&p
->m_data
, "/");
458 atf_fs_path_append_ap(atf_fs_path_t
*p
, const char *fmt
, va_list ap
)
465 err
= normalize_ap(&aux
, fmt
, ap2
);
467 if (!atf_is_error(err
)) {
468 const char *auxstr
= atf_dynstr_cstring(&aux
);
469 const bool needslash
= auxstr
[0] != '/';
471 err
= atf_dynstr_append_fmt(&p
->m_data
, "%s%s",
472 needslash
? "/" : "", auxstr
);
474 atf_dynstr_fini(&aux
);
481 atf_fs_path_append_fmt(atf_fs_path_t
*p
, const char *fmt
, ...)
487 err
= atf_fs_path_append_ap(p
, fmt
, ap
);
494 atf_fs_path_append_path(atf_fs_path_t
*p
, const atf_fs_path_t
*p2
)
496 return atf_fs_path_append_fmt(p
, "%s", atf_dynstr_cstring(&p2
->m_data
));
500 atf_fs_path_to_absolute(const atf_fs_path_t
*p
, atf_fs_path_t
*pa
)
504 PRE(!atf_fs_path_is_absolute(p
));
506 err
= atf_fs_getcwd(pa
);
507 if (atf_is_error(err
))
510 err
= atf_fs_path_append_path(pa
, p
);
511 if (atf_is_error(err
))
512 atf_fs_path_fini(pa
);
522 bool atf_equal_fs_path_fs_path(const atf_fs_path_t
*p1
,
523 const atf_fs_path_t
*p2
)
525 return atf_equal_dynstr_dynstr(&p1
->m_data
, &p2
->m_data
);
528 /* ---------------------------------------------------------------------
529 * The "atf_fs_path" type.
530 * --------------------------------------------------------------------- */
536 const int atf_fs_stat_blk_type
= 1;
537 const int atf_fs_stat_chr_type
= 2;
538 const int atf_fs_stat_dir_type
= 3;
539 const int atf_fs_stat_fifo_type
= 4;
540 const int atf_fs_stat_lnk_type
= 5;
541 const int atf_fs_stat_reg_type
= 6;
542 const int atf_fs_stat_sock_type
= 7;
543 const int atf_fs_stat_wht_type
= 8;
546 * Constructors/destructors.
550 atf_fs_stat_init(atf_fs_stat_t
*st
, const atf_fs_path_t
*p
)
553 const char *pstr
= atf_fs_path_cstring(p
);
555 if (lstat(pstr
, &st
->m_sb
) == -1) {
556 err
= atf_libc_error(errno
, "Cannot get information of %s; "
557 "lstat(2) failed", pstr
);
559 int type
= st
->m_sb
.st_mode
& S_IFMT
;
560 err
= atf_no_error();
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;
570 case S_IFWHT
: st
->m_type
= atf_fs_stat_wht_type
; break;
573 err
= unknown_type_error(pstr
, type
);
581 atf_fs_stat_copy(atf_fs_stat_t
*dest
, const atf_fs_stat_t
*src
)
583 dest
->m_type
= src
->m_type
;
584 dest
->m_sb
= src
->m_sb
;
588 atf_fs_stat_fini(atf_fs_stat_t
*st ATF_DEFS_ATTRIBUTE_UNUSED
)
597 atf_fs_stat_get_device(const atf_fs_stat_t
*st
)
599 return st
->m_sb
.st_dev
;
603 atf_fs_stat_get_inode(const atf_fs_stat_t
*st
)
605 return st
->m_sb
.st_ino
;
609 atf_fs_stat_get_mode(const atf_fs_stat_t
*st
)
611 return st
->m_sb
.st_mode
& ~S_IFMT
;
615 atf_fs_stat_get_size(const atf_fs_stat_t
*st
)
617 return st
->m_sb
.st_size
;
621 atf_fs_stat_get_type(const atf_fs_stat_t
*st
)
627 atf_fs_stat_is_owner_readable(const atf_fs_stat_t
*st
)
629 return st
->m_sb
.st_mode
& S_IRUSR
;
633 atf_fs_stat_is_owner_writable(const atf_fs_stat_t
*st
)
635 return st
->m_sb
.st_mode
& S_IWUSR
;
639 atf_fs_stat_is_owner_executable(const atf_fs_stat_t
*st
)
641 return st
->m_sb
.st_mode
& S_IXUSR
;
645 atf_fs_stat_is_group_readable(const atf_fs_stat_t
*st
)
647 return st
->m_sb
.st_mode
& S_IRGRP
;
651 atf_fs_stat_is_group_writable(const atf_fs_stat_t
*st
)
653 return st
->m_sb
.st_mode
& S_IWGRP
;
657 atf_fs_stat_is_group_executable(const atf_fs_stat_t
*st
)
659 return st
->m_sb
.st_mode
& S_IXGRP
;
663 atf_fs_stat_is_other_readable(const atf_fs_stat_t
*st
)
665 return st
->m_sb
.st_mode
& S_IROTH
;
669 atf_fs_stat_is_other_writable(const atf_fs_stat_t
*st
)
671 return st
->m_sb
.st_mode
& S_IWOTH
;
675 atf_fs_stat_is_other_executable(const atf_fs_stat_t
*st
)
677 return st
->m_sb
.st_mode
& S_IXOTH
;
680 /* ---------------------------------------------------------------------
682 * --------------------------------------------------------------------- */
684 const int atf_fs_access_f
= 1 << 0;
685 const int atf_fs_access_r
= 1 << 1;
686 const int atf_fs_access_w
= 1 << 2;
687 const int atf_fs_access_x
= 1 << 3;
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.
695 atf_fs_eaccess(const atf_fs_path_t
*p
, int mode
)
701 PRE(mode
& atf_fs_access_f
|| mode
& atf_fs_access_r
||
702 mode
& atf_fs_access_w
|| mode
& atf_fs_access_x
);
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
));
710 err
= atf_no_error();
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
)
718 if (atf_user_is_root()) {
719 if (!ok
&& !(mode
& atf_fs_access_x
)) {
720 /* Allow root to read/write any file. */
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
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
));
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
));
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
));
749 err
= atf_libc_error(EACCES
, "Access check failed");
756 atf_fs_exists(const atf_fs_path_t
*p
, bool *b
)
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
) {
764 err
= atf_no_error();
774 atf_fs_getcwd(atf_fs_path_t
*p
)
779 #if defined(HAVE_GETCWD_DYN)
780 cwd
= getcwd(NULL
, 0);
782 cwd
= getcwd(NULL
, MAXPATHLEN
);
785 err
= atf_libc_error(errno
, "Cannot determine current directory");
789 err
= atf_fs_path_init_fmt(p
, "%s", cwd
);
797 atf_fs_mkdtemp(atf_fs_path_t
*p
)
800 char *buf
= NULL
; /* MINIX: Complain in -O3 */
802 if (!check_umask(S_IRWXU
, S_IRWXU
)) {
803 err
= invalid_umask_error(p
, atf_fs_stat_dir_type
, current_umask());
807 err
= copy_contents(p
, &buf
);
808 if (atf_is_error(err
))
811 err
= do_mkdtemp(buf
);
812 if (atf_is_error(err
))
815 replace_contents(p
, buf
);
817 INV(!atf_is_error(err
));
825 atf_fs_mkstemp(atf_fs_path_t
*p
, int *fdout
)
828 char *buf
= NULL
; /* MINIX: Complain in -O3 */
831 if (!check_umask(S_IRWXU
, S_IRWXU
)) {
832 err
= invalid_umask_error(p
, atf_fs_stat_reg_type
, current_umask());
836 err
= copy_contents(p
, &buf
);
837 if (atf_is_error(err
))
840 err
= do_mkstemp(buf
, &fd
);
841 if (atf_is_error(err
))
844 replace_contents(p
, buf
);
847 INV(!atf_is_error(err
));
855 atf_fs_rmdir(const atf_fs_path_t
*p
)
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. */
867 err
= atf_libc_error(errno
, "Cannot remove directory");
869 err
= atf_no_error();
875 atf_fs_unlink(const atf_fs_path_t
*p
)
880 path
= atf_fs_path_cstring(p
);
882 if (unlink(path
) != 0)
883 err
= atf_libc_error(errno
, "Cannot unlink file: '%s'", path
);
885 err
= atf_no_error();