1 /* Copyright (c) 2007 The NetBSD Foundation, Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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. */
26 #include "atf-c/detail/fs.h"
28 #if defined(HAVE_CONFIG_H)
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/mount.h>
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"
53 /* ---------------------------------------------------------------------
54 * Prototypes for auxiliary functions.
55 * --------------------------------------------------------------------- */
57 static bool check_umask(const mode_t, const mode_t);
58 static atf_error_t copy_contents(const atf_fs_path_t *, char **);
59 static mode_t current_umask(void);
60 static atf_error_t do_mkdtemp(char *);
61 static atf_error_t normalize(atf_dynstr_t *, char *);
62 static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
63 static void replace_contents(atf_fs_path_t *, const char *);
64 static const char *stat_type_to_string(const int);
66 /* ---------------------------------------------------------------------
67 * The "invalid_umask" error type.
68 * --------------------------------------------------------------------- */
70 struct invalid_umask_error_data {
71 /* One of atf_fs_stat_*_type. */
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
81 /* The umask that caused the error. */
84 typedef struct invalid_umask_error_data invalid_umask_error_data_t;
88 invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
90 const invalid_umask_error_data_t *data;
92 PRE(atf_error_is(err, "invalid_umask"));
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);
103 invalid_umask_error(const atf_fs_path_t *path, const int type,
104 const mode_t failing_mask)
107 invalid_umask_error_data_t data;
111 strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
112 data.m_path[sizeof(data.m_path) - 1] = '\0';
114 data.m_umask = failing_mask;
116 err = atf_error_new("invalid_umask", &data, sizeof(data),
117 invalid_umask_format);
122 /* ---------------------------------------------------------------------
123 * The "unknown_file_type" error type.
124 * --------------------------------------------------------------------- */
126 struct unknown_type_error_data {
130 typedef struct unknown_type_error_data unknown_type_error_data_t;
134 unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
136 const unknown_type_error_data_t *data;
138 PRE(atf_error_is(err, "unknown_type"));
140 data = atf_error_data(err);
141 snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
147 unknown_type_error(const char *path, int type)
150 unknown_type_error_data_t data;
155 err = atf_error_new("unknown_type", &data, sizeof(data),
156 unknown_type_format);
161 /* ---------------------------------------------------------------------
162 * Auxiliary functions.
163 * --------------------------------------------------------------------- */
167 check_umask(const mode_t exp_mode, const mode_t min_mode)
169 const mode_t actual_mode = (~current_umask() & exp_mode);
170 return (actual_mode & min_mode) == min_mode;
175 copy_contents(const atf_fs_path_t *p, char **buf)
180 str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
182 err = atf_no_memory_error();
184 strcpy(str, atf_dynstr_cstring(&p->m_data));
186 err = atf_no_error();
196 const mode_t current = umask(0);
197 (void)umask(current);
203 do_mkdtemp(char *tmpl)
207 PRE(strstr(tmpl, "XXXXXX") != NULL);
209 if (mkdtemp(tmpl) == NULL)
210 err = atf_libc_error(errno, "Cannot create temporary directory "
211 "with template '%s'", tmpl);
213 err = atf_no_error();
220 do_mkstemp(char *tmpl, int *fdout)
224 PRE(strstr(tmpl, "XXXXXX") != NULL);
226 *fdout = mkstemp(tmpl);
228 err = atf_libc_error(errno, "Cannot create temporary file "
229 "with template '%s'", tmpl);
232 err = atf_no_error();
239 normalize(atf_dynstr_t *d, char *p)
247 PRE(atf_dynstr_length(d) == 0);
250 err = atf_dynstr_append_fmt(d, "/");
252 err = atf_no_error();
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);
263 ptr = strtok_r(NULL, "/", &last);
271 normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
277 err = atf_dynstr_init(d);
278 if (atf_is_error(err))
282 err = atf_text_format_ap(&str, p, ap2);
284 if (atf_is_error(err))
287 err = normalize(d, str);
297 replace_contents(atf_fs_path_t *p, const char *buf)
301 PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
303 atf_dynstr_clear(&p->m_data);
304 err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
306 INV(!atf_is_error(err));
311 stat_type_to_string(const int type)
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)
321 else if (type == atf_fs_stat_fifo_type)
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)
329 else if (type == atf_fs_stat_wht_type)
339 /* ---------------------------------------------------------------------
340 * The "atf_fs_path" type.
341 * --------------------------------------------------------------------- */
344 * Constructors/destructors.
348 atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
354 err = normalize_ap(&p->m_data, fmt, ap2);
361 atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
367 err = atf_fs_path_init_ap(p, fmt, ap);
374 atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
376 return atf_dynstr_copy(&dest->m_data, &src->m_data);
380 atf_fs_path_fini(atf_fs_path_t *p)
382 atf_dynstr_fini(&p->m_data);
390 atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
392 const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
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, "/");
400 err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
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) */
411 atf_fs_path_cstring(const atf_fs_path_t *p)
413 return atf_dynstr_cstring(&p->m_data);
417 atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
419 size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
422 if (begpos == atf_dynstr_npos)
427 err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
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) */
438 atf_fs_path_is_absolute(const atf_fs_path_t *p)
440 return atf_dynstr_cstring(&p->m_data)[0] == '/';
444 atf_fs_path_is_root(const atf_fs_path_t *p)
446 return atf_equal_dynstr_cstring(&p->m_data, "/");
454 atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
461 err = normalize_ap(&aux, fmt, ap2);
463 if (!atf_is_error(err)) {
464 const char *auxstr = atf_dynstr_cstring(&aux);
465 const bool needslash = auxstr[0] != '/';
467 err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
468 needslash ? "/" : "", auxstr);
470 atf_dynstr_fini(&aux);
477 atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
483 err = atf_fs_path_append_ap(p, fmt, ap);
490 atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
492 return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
496 atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
500 PRE(!atf_fs_path_is_absolute(p));
502 err = atf_fs_getcwd(pa);
503 if (atf_is_error(err))
506 err = atf_fs_path_append_path(pa, p);
507 if (atf_is_error(err))
508 atf_fs_path_fini(pa);
518 bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
519 const atf_fs_path_t *p2)
521 return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
524 /* ---------------------------------------------------------------------
525 * The "atf_fs_path" type.
526 * --------------------------------------------------------------------- */
532 const int atf_fs_stat_blk_type = 1;
533 const int atf_fs_stat_chr_type = 2;
534 const int atf_fs_stat_dir_type = 3;
535 const int atf_fs_stat_fifo_type = 4;
536 const int atf_fs_stat_lnk_type = 5;
537 const int atf_fs_stat_reg_type = 6;
538 const int atf_fs_stat_sock_type = 7;
539 const int atf_fs_stat_wht_type = 8;
542 * Constructors/destructors.
546 atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
549 const char *pstr = atf_fs_path_cstring(p);
551 if (lstat(pstr, &st->m_sb) == -1) {
552 err = atf_libc_error(errno, "Cannot get information of %s; "
553 "lstat(2) failed", pstr);
555 int type = st->m_sb.st_mode & S_IFMT;
556 err = atf_no_error();
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;
566 case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
569 err = unknown_type_error(pstr, type);
577 atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
579 dest->m_type = src->m_type;
580 dest->m_sb = src->m_sb;
584 atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
593 atf_fs_stat_get_device(const atf_fs_stat_t *st)
595 return st->m_sb.st_dev;
599 atf_fs_stat_get_inode(const atf_fs_stat_t *st)
601 return st->m_sb.st_ino;
605 atf_fs_stat_get_mode(const atf_fs_stat_t *st)
607 return st->m_sb.st_mode & ~S_IFMT;
611 atf_fs_stat_get_size(const atf_fs_stat_t *st)
613 return st->m_sb.st_size;
617 atf_fs_stat_get_type(const atf_fs_stat_t *st)
623 atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
625 return st->m_sb.st_mode & S_IRUSR;
629 atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
631 return st->m_sb.st_mode & S_IWUSR;
635 atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
637 return st->m_sb.st_mode & S_IXUSR;
641 atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
643 return st->m_sb.st_mode & S_IRGRP;
647 atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
649 return st->m_sb.st_mode & S_IWGRP;
653 atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
655 return st->m_sb.st_mode & S_IXGRP;
659 atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
661 return st->m_sb.st_mode & S_IROTH;
665 atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
667 return st->m_sb.st_mode & S_IWOTH;
671 atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
673 return st->m_sb.st_mode & S_IXOTH;
676 /* ---------------------------------------------------------------------
678 * --------------------------------------------------------------------- */
680 const int atf_fs_access_f = 1 << 0;
681 const int atf_fs_access_r = 1 << 1;
682 const int atf_fs_access_w = 1 << 2;
683 const int atf_fs_access_x = 1 << 3;
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.
691 atf_fs_eaccess(const atf_fs_path_t *p, int mode)
697 PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
698 mode & atf_fs_access_w || mode & atf_fs_access_x);
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));
706 err = atf_no_error();
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)
714 if (atf_user_is_root()) {
715 if (!ok && !(mode & atf_fs_access_x)) {
716 /* Allow root to read/write any file. */
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
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));
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));
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));
745 err = atf_libc_error(EACCES, "Access check failed");
752 atf_fs_exists(const atf_fs_path_t *p, bool *b)
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) {
760 err = atf_no_error();
770 atf_fs_getcwd(atf_fs_path_t *p)
775 #if defined(HAVE_GETCWD_DYN)
776 cwd = getcwd(NULL, 0);
778 cwd = getcwd(NULL, MAXPATHLEN);
781 err = atf_libc_error(errno, "Cannot determine current directory");
785 err = atf_fs_path_init_fmt(p, "%s", cwd);
793 atf_fs_mkdtemp(atf_fs_path_t *p)
798 if (!check_umask(S_IRWXU, S_IRWXU)) {
799 err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
803 err = copy_contents(p, &buf);
804 if (atf_is_error(err))
807 err = do_mkdtemp(buf);
808 if (atf_is_error(err))
811 replace_contents(p, buf);
813 INV(!atf_is_error(err));
821 atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
827 if (!check_umask(S_IRWXU, S_IRWXU)) {
828 err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
832 err = copy_contents(p, &buf);
833 if (atf_is_error(err))
836 err = do_mkstemp(buf, &fd);
837 if (atf_is_error(err))
840 replace_contents(p, buf);
843 INV(!atf_is_error(err));
851 atf_fs_rmdir(const atf_fs_path_t *p)
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. */
863 err = atf_libc_error(errno, "Cannot remove directory");
865 err = atf_no_error();
871 atf_fs_unlink(const atf_fs_path_t *p)
876 path = atf_fs_path_cstring(p);
878 if (unlink(path) != 0)
879 err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
881 err = atf_no_error();