Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / atf / dist / atf-c / fs.c
blob5d5365e43eb4232b6c3b530d840a007e8ba0a41f
1 /*
2 * Automated Testing Framework (atf)
4 * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
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.
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)
31 #include "bconfig.h"
32 #endif
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>
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>
49 #include "atf-c/error.h"
50 #include "atf-c/fs.h"
51 #include "atf-c/process.h"
52 #include "atf-c/sanity.h"
53 #include "atf-c/text.h"
54 #include "atf-c/user.h"
56 /* ---------------------------------------------------------------------
57 * Prototypes for auxiliary functions.
58 * --------------------------------------------------------------------- */
60 static bool check_umask(const mode_t, const mode_t);
61 static atf_error_t cleanup_aux(const atf_fs_path_t *, dev_t, bool);
62 static atf_error_t cleanup_aux_dir(const char *, const atf_fs_stat_t *, bool);
63 static atf_error_t copy_contents(const atf_fs_path_t *, char **);
64 static mode_t current_umask(void);
65 static atf_error_t do_mkdtemp(char *);
66 static atf_error_t do_unmount(const atf_fs_path_t *);
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. */
78 int m_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
84 * deletion. */
85 char m_path[1024];
87 /* The umask that caused the error. */
88 mode_t m_umask;
90 typedef struct invalid_umask_error_data invalid_umask_error_data_t;
92 static
93 void
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);
107 static
108 atf_error_t
109 invalid_umask_error(const atf_fs_path_t *path, const int type,
110 const mode_t failing_mask)
112 atf_error_t err;
113 invalid_umask_error_data_t data;
115 data.m_type = type;
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);
125 return err;
128 /* ---------------------------------------------------------------------
129 * The "unknown_file_type" error type.
130 * --------------------------------------------------------------------- */
132 struct unknown_type_error_data {
133 const char *m_path;
134 int m_type;
136 typedef struct unknown_type_error_data unknown_type_error_data_t;
138 static
139 void
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,
148 data->m_path);
151 static
152 atf_error_t
153 unknown_type_error(const char *path, int type)
155 atf_error_t err;
156 unknown_type_error_data_t data;
158 data.m_path = path;
159 data.m_type = type;
161 err = atf_error_new("unknown_type", &data, sizeof(data),
162 unknown_type_format);
164 return err;
167 /* ---------------------------------------------------------------------
168 * Auxiliary functions.
169 * --------------------------------------------------------------------- */
171 static
172 bool
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;
179 /* The erase parameter in this routine is to control nested mount points.
180 * We want to descend into a mount point to unmount anything that is
181 * mounted under it, but we do not want to delete any files while doing
182 * this traversal. In other words, we erase files until we cross the
183 * first mount point, and after that point we only scan and unmount. */
184 static
185 atf_error_t
186 cleanup_aux(const atf_fs_path_t *p, dev_t parent_device, bool erase)
188 const char *pstr = atf_fs_path_cstring(p);
189 atf_error_t err;
190 atf_fs_stat_t st;
192 err = atf_fs_stat_init(&st, p);
193 if (atf_is_error(err))
194 goto out;
196 if (atf_fs_stat_get_type(&st) == atf_fs_stat_dir_type) {
197 err = cleanup_aux_dir(pstr, &st,
198 atf_fs_stat_get_device(&st) == parent_device);
199 if (atf_is_error(err))
200 goto out_st;
203 if (atf_fs_stat_get_device(&st) != parent_device) {
204 err = do_unmount(p);
205 if (atf_is_error(err))
206 goto out_st;
209 if (erase) {
210 if (atf_fs_stat_get_type(&st) == atf_fs_stat_dir_type) {
211 err = atf_fs_rmdir(p);
212 } else {
213 if (unlink(pstr) == -1)
214 err = atf_libc_error(errno, "Cannot remove file %s", pstr);
215 else
216 INV(!atf_is_error(err));
220 out_st:
221 atf_fs_stat_fini(&st);
222 out:
223 return err;
226 static
227 atf_error_t
228 cleanup_aux_dir(const char *pstr, const atf_fs_stat_t *st, bool erase)
230 DIR *d;
231 atf_error_t err;
232 struct dirent *de;
234 if (erase && !(atf_fs_stat_get_mode(st) & S_IWUSR)) {
235 if (chmod(pstr, atf_fs_stat_get_mode(st) | S_IWUSR) == -1) {
236 err = atf_libc_error(errno, "Cannot grant write permissions "
237 "to %s", pstr);
238 goto out;
242 d = opendir(pstr);
243 if (d == NULL) {
244 err = atf_libc_error(errno, "Cannot open directory %s", pstr);
245 goto out;
248 err = atf_no_error();
249 while (!atf_is_error(err) && (de = readdir(d)) != NULL) {
250 atf_fs_path_t p;
252 err = atf_fs_path_init_fmt(&p, "%s/%s", pstr, de->d_name);
253 if (!atf_is_error(err)) {
254 if (strcmp(de->d_name, ".") != 0 &&
255 strcmp(de->d_name, "..") != 0)
256 err = cleanup_aux(&p, atf_fs_stat_get_device(st), erase);
258 atf_fs_path_fini(&p);
262 closedir(d);
264 out:
265 return err;
268 static
269 atf_error_t
270 copy_contents(const atf_fs_path_t *p, char **buf)
272 atf_error_t err;
273 char *str;
275 str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
276 if (str == NULL)
277 err = atf_no_memory_error();
278 else {
279 strcpy(str, atf_dynstr_cstring(&p->m_data));
280 *buf = str;
281 err = atf_no_error();
284 return err;
287 static
288 mode_t
289 current_umask(void)
291 const mode_t current = umask(0);
292 (void)umask(current);
293 return current;
296 static
297 atf_error_t
298 do_mkdtemp(char *tmpl)
300 atf_error_t err;
302 PRE(strstr(tmpl, "XXXXXX") != NULL);
304 if (mkdtemp(tmpl) == NULL)
305 err = atf_libc_error(errno, "Cannot create temporary directory "
306 "with template '%s'", tmpl);
307 else
308 err = atf_no_error();
310 return err;
313 static
314 atf_error_t
315 do_mkstemp(char *tmpl, int *fdout)
317 atf_error_t err;
319 PRE(strstr(tmpl, "XXXXXX") != NULL);
321 *fdout = mkstemp(tmpl);
322 if (*fdout == -1)
323 err = atf_libc_error(errno, "Cannot create temporary file "
324 "with template '%s'", tmpl);
326 else
327 err = atf_no_error();
329 return err;
332 static
333 atf_error_t
334 do_unmount(const atf_fs_path_t *p)
336 atf_error_t err;
337 atf_fs_path_t pa;
338 const char *pastr;
340 /* At least, FreeBSD's unmount(2) requires the path to be absolute.
341 * Let's make it absolute in all cases just to be safe that this does
342 * not affect other systems. */
344 if (!atf_fs_path_is_absolute(p))
345 err = atf_fs_path_to_absolute(p, &pa);
346 else
347 err = atf_fs_path_copy(&pa, p);
348 if (atf_is_error(err))
349 goto out;
350 pastr = atf_fs_path_cstring(&pa);
352 #if defined(HAVE_UNMOUNT)
353 if (unmount(pastr, 0) == -1)
354 err = atf_libc_error(errno, "Cannot unmount %s", pastr);
355 else
356 err = atf_no_error();
357 #else
359 /* We could use umount(2) instead if it was available... but
360 * trying to do so under, e.g. Linux, is a nightmare because we
361 * also have to update /etc/mtab to match what we did. It is
362 * simpler to just leave the system-specific umount(8) tool deal
363 * with it, at least for now. */
364 atf_fs_path_t prog;
366 /* TODO: Should have an atf_fs_find_in_path function or similar to
367 * avoid relying on the automatic path lookup of exec, which I'd
368 * like to get rid of. */
369 err = atf_fs_path_init_fmt(&prog, "umount");
370 if (!atf_is_error(err)) {
371 atf_process_status_t status;
372 const char *argv[3] = { "unmount", pastr, NULL };
374 err = atf_process_exec_array(&status, &prog, argv, NULL, NULL);
375 if (!atf_is_error(err)) {
376 if (!atf_process_status_exited(&status) ||
377 atf_process_status_exitstatus(&status) != EXIT_SUCCESS) {
378 /* XXX: This is the wrong error type. */
379 err = atf_libc_error(EINVAL, "Failed to exec unmount");
382 atf_process_status_fini(&status);
385 atf_fs_path_fini(&prog);
388 #endif
390 atf_fs_path_fini(&pa);
391 out:
392 return err;
395 static
396 atf_error_t
397 normalize(atf_dynstr_t *d, char *p)
399 const char *ptr;
400 char *last;
401 atf_error_t err;
402 bool first;
404 PRE(strlen(p) > 0);
405 PRE(atf_dynstr_length(d) == 0);
407 if (p[0] == '/')
408 err = atf_dynstr_append_fmt(d, "/");
409 else
410 err = atf_no_error();
412 first = true;
413 last = NULL; /* Silence GCC warning. */
414 ptr = strtok_r(p, "/", &last);
415 while (!atf_is_error(err) && ptr != NULL) {
416 if (strlen(ptr) > 0) {
417 err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
418 first = false;
421 ptr = strtok_r(NULL, "/", &last);
424 return err;
427 static
428 atf_error_t
429 normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
431 char *str;
432 atf_error_t err;
433 va_list ap2;
435 err = atf_dynstr_init(d);
436 if (atf_is_error(err))
437 goto out;
439 va_copy(ap2, ap);
440 err = atf_text_format_ap(&str, p, ap2);
441 va_end(ap2);
442 if (atf_is_error(err))
443 atf_dynstr_fini(d);
444 else {
445 err = normalize(d, str);
446 free(str);
449 out:
450 return err;
453 static
454 void
455 replace_contents(atf_fs_path_t *p, const char *buf)
457 atf_error_t err;
459 PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
461 atf_dynstr_clear(&p->m_data);
462 err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
464 INV(!atf_is_error(err));
467 static
468 const char *
469 stat_type_to_string(const int type)
471 const char *str;
473 if (type == atf_fs_stat_blk_type)
474 str = "block device";
475 else if (type == atf_fs_stat_chr_type)
476 str = "character device";
477 else if (type == atf_fs_stat_dir_type)
478 str = "directory";
479 else if (type == atf_fs_stat_fifo_type)
480 str = "named pipe";
481 else if (type == atf_fs_stat_lnk_type)
482 str = "symbolic link";
483 else if (type == atf_fs_stat_reg_type)
484 str = "regular file";
485 else if (type == atf_fs_stat_sock_type)
486 str = "socket";
487 else if (type == atf_fs_stat_wht_type)
488 str = "whiteout";
489 else {
490 UNREACHABLE;
491 str = NULL;
494 return str;
497 /* ---------------------------------------------------------------------
498 * The "atf_fs_path" type.
499 * --------------------------------------------------------------------- */
502 * Constructors/destructors.
505 atf_error_t
506 atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
508 atf_error_t err;
509 va_list ap2;
511 atf_object_init(&p->m_object);
513 va_copy(ap2, ap);
514 err = normalize_ap(&p->m_data, fmt, ap2);
515 va_end(ap2);
517 return err;
520 atf_error_t
521 atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
523 va_list ap;
524 atf_error_t err;
526 va_start(ap, fmt);
527 err = atf_fs_path_init_ap(p, fmt, ap);
528 va_end(ap);
530 return err;
533 atf_error_t
534 atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
536 atf_object_copy(&dest->m_object, &src->m_object);
538 return atf_dynstr_copy(&dest->m_data, &src->m_data);
541 void
542 atf_fs_path_fini(atf_fs_path_t *p)
544 atf_dynstr_fini(&p->m_data);
546 atf_object_fini(&p->m_object);
550 * Getters.
553 atf_error_t
554 atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
556 const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
557 atf_error_t err;
559 if (endpos == atf_dynstr_npos)
560 err = atf_fs_path_init_fmt(bp, ".");
561 else if (endpos == 0)
562 err = atf_fs_path_init_fmt(bp, "/");
563 else {
564 atf_object_init(&bp->m_object);
565 err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
568 #if defined(HAVE_CONST_DIRNAME)
569 INV(atf_equal_dynstr_cstring(&bp->m_data,
570 dirname(atf_dynstr_cstring(&p->m_data))));
571 #endif /* defined(HAVE_CONST_DIRNAME) */
573 return err;
576 const char *
577 atf_fs_path_cstring(const atf_fs_path_t *p)
579 return atf_dynstr_cstring(&p->m_data);
582 atf_error_t
583 atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
585 size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
586 atf_error_t err;
588 if (begpos == atf_dynstr_npos)
589 begpos = 0;
590 else
591 begpos++;
593 err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
595 #if defined(HAVE_CONST_BASENAME)
596 INV(atf_equal_dynstr_cstring(ln,
597 basename(atf_dynstr_cstring(&p->m_data))));
598 #endif /* defined(HAVE_CONST_BASENAME) */
600 return err;
603 bool
604 atf_fs_path_is_absolute(const atf_fs_path_t *p)
606 return atf_dynstr_cstring(&p->m_data)[0] == '/';
609 bool
610 atf_fs_path_is_root(const atf_fs_path_t *p)
612 return atf_equal_dynstr_cstring(&p->m_data, "/");
616 * Modifiers.
619 atf_error_t
620 atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
622 atf_dynstr_t aux;
623 atf_error_t err;
624 va_list ap2;
626 va_copy(ap2, ap);
627 err = normalize_ap(&aux, fmt, ap2);
628 va_end(ap2);
629 if (!atf_is_error(err)) {
630 const char *auxstr = atf_dynstr_cstring(&aux);
631 const bool needslash = auxstr[0] != '/';
633 err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
634 needslash ? "/" : "", auxstr);
636 atf_dynstr_fini(&aux);
639 return err;
642 atf_error_t
643 atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
645 va_list ap;
646 atf_error_t err;
648 va_start(ap, fmt);
649 err = atf_fs_path_append_ap(p, fmt, ap);
650 va_end(ap);
652 return err;
655 atf_error_t
656 atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
658 return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
661 atf_error_t
662 atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
664 atf_error_t err;
666 PRE(!atf_fs_path_is_absolute(p));
668 err = atf_fs_getcwd(pa);
669 if (atf_is_error(err))
670 goto out;
672 err = atf_fs_path_append_path(pa, p);
673 if (atf_is_error(err))
674 atf_fs_path_fini(pa);
676 out:
677 return err;
681 * Operators.
684 bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
685 const atf_fs_path_t *p2)
687 return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
690 /* ---------------------------------------------------------------------
691 * The "atf_fs_path" type.
692 * --------------------------------------------------------------------- */
695 * Constants.
698 const int atf_fs_stat_blk_type = 1;
699 const int atf_fs_stat_chr_type = 2;
700 const int atf_fs_stat_dir_type = 3;
701 const int atf_fs_stat_fifo_type = 4;
702 const int atf_fs_stat_lnk_type = 5;
703 const int atf_fs_stat_reg_type = 6;
704 const int atf_fs_stat_sock_type = 7;
705 const int atf_fs_stat_wht_type = 8;
708 * Constructors/destructors.
711 atf_error_t
712 atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
714 atf_error_t err;
715 const char *pstr = atf_fs_path_cstring(p);
717 if (lstat(pstr, &st->m_sb) == -1) {
718 err = atf_libc_error(errno, "Cannot get information of %s; "
719 "lstat(2) failed", pstr);
720 } else {
721 int type = st->m_sb.st_mode & S_IFMT;
722 err = atf_no_error();
723 switch (type) {
724 case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break;
725 case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break;
726 case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break;
727 case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break;
728 case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break;
729 case S_IFREG: st->m_type = atf_fs_stat_reg_type; break;
730 case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
731 #if defined(S_IFWHT)
732 case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
733 #endif
734 default:
735 err = unknown_type_error(pstr, type);
739 if (!atf_is_error(err))
740 atf_object_init(&st->m_object);
742 return err;
745 void
746 atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
748 atf_object_copy(&dest->m_object, &src->m_object);
750 dest->m_type = src->m_type;
751 dest->m_sb = src->m_sb;
754 void
755 atf_fs_stat_fini(atf_fs_stat_t *st)
757 atf_object_fini(&st->m_object);
761 * Getters.
764 dev_t
765 atf_fs_stat_get_device(const atf_fs_stat_t *st)
767 return st->m_sb.st_dev;
770 ino_t
771 atf_fs_stat_get_inode(const atf_fs_stat_t *st)
773 return st->m_sb.st_ino;
776 mode_t
777 atf_fs_stat_get_mode(const atf_fs_stat_t *st)
779 return st->m_sb.st_mode & ~S_IFMT;
782 off_t
783 atf_fs_stat_get_size(const atf_fs_stat_t *st)
785 return st->m_sb.st_size;
789 atf_fs_stat_get_type(const atf_fs_stat_t *st)
791 return st->m_type;
794 bool
795 atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
797 return st->m_sb.st_mode & S_IRUSR;
800 bool
801 atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
803 return st->m_sb.st_mode & S_IWUSR;
806 bool
807 atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
809 return st->m_sb.st_mode & S_IXUSR;
812 bool
813 atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
815 return st->m_sb.st_mode & S_IRGRP;
818 bool
819 atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
821 return st->m_sb.st_mode & S_IWGRP;
824 bool
825 atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
827 return st->m_sb.st_mode & S_IXGRP;
830 bool
831 atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
833 return st->m_sb.st_mode & S_IROTH;
836 bool
837 atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
839 return st->m_sb.st_mode & S_IWOTH;
842 bool
843 atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
845 return st->m_sb.st_mode & S_IXOTH;
848 /* ---------------------------------------------------------------------
849 * Free functions.
850 * --------------------------------------------------------------------- */
852 const int atf_fs_access_f = 1 << 0;
853 const int atf_fs_access_r = 1 << 1;
854 const int atf_fs_access_w = 1 << 2;
855 const int atf_fs_access_x = 1 << 3;
857 atf_error_t
858 atf_fs_cleanup(const atf_fs_path_t *p)
860 atf_error_t err;
861 atf_fs_stat_t info;
863 err = atf_fs_stat_init(&info, p);
864 if (atf_is_error(err))
865 return err;
867 err = cleanup_aux(p, atf_fs_stat_get_device(&info), true);
869 atf_fs_stat_fini(&info);
871 return err;
875 * An implementation of access(2) but using the effective user value
876 * instead of the real one. Also avoids false positives for root when
877 * asking for execute permissions, which appear in SunOS.
879 atf_error_t
880 atf_fs_eaccess(const atf_fs_path_t *p, int mode)
882 atf_error_t err;
883 struct stat st;
884 bool ok;
886 PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
887 mode & atf_fs_access_w || mode & atf_fs_access_x);
889 if (lstat(atf_fs_path_cstring(p), &st) == -1) {
890 err = atf_libc_error(errno, "Cannot get information from file %s",
891 atf_fs_path_cstring(p));
892 goto out;
895 err = atf_no_error();
897 /* Early return if we are only checking for existence and the file
898 * exists (stat call returned). */
899 if (mode & atf_fs_access_f)
900 goto out;
902 ok = false;
903 if (atf_user_is_root()) {
904 if (!ok && !(mode & atf_fs_access_x)) {
905 /* Allow root to read/write any file. */
906 ok = true;
909 if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
910 /* Allow root to execute the file if any of its execution bits
911 * are set. */
912 ok = true;
914 } else {
915 if (!ok && (atf_user_euid() == st.st_uid)) {
916 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
917 ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
918 ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
920 if (!ok && atf_user_is_member_of_group(st.st_gid)) {
921 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
922 ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
923 ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
925 if (!ok && ((atf_user_euid() != st.st_uid) &&
926 !atf_user_is_member_of_group(st.st_gid))) {
927 ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
928 ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
929 ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
933 if (!ok)
934 err = atf_libc_error(EACCES, "Access check failed");
936 out:
937 return err;
940 atf_error_t
941 atf_fs_exists(const atf_fs_path_t *p, bool *b)
943 atf_error_t err;
945 err = atf_fs_eaccess(p, atf_fs_access_f);
946 if (atf_is_error(err)) {
947 if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
948 atf_error_free(err);
949 err = atf_no_error();
950 *b = false;
952 } else
953 *b = true;
955 return err;
958 atf_error_t
959 atf_fs_getcwd(atf_fs_path_t *p)
961 atf_error_t err;
962 char *cwd;
964 #if defined(HAVE_GETCWD_DYN)
965 cwd = getcwd(NULL, 0);
966 #else
967 cwd = getcwd(NULL, MAXPATHLEN);
968 #endif
969 if (cwd == NULL) {
970 err = atf_libc_error(errno, "Cannot determine current directory");
971 goto out;
974 err = atf_fs_path_init_fmt(p, "%s", cwd);
975 free(cwd);
977 out:
978 return err;
981 atf_error_t
982 atf_fs_mkdtemp(atf_fs_path_t *p)
984 atf_error_t err;
985 char *buf;
987 if (!check_umask(S_IRWXU, S_IRWXU)) {
988 err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
989 goto out;
992 err = copy_contents(p, &buf);
993 if (atf_is_error(err))
994 goto out;
996 err = do_mkdtemp(buf);
997 if (atf_is_error(err))
998 goto out_buf;
1000 replace_contents(p, buf);
1002 INV(!atf_is_error(err));
1003 out_buf:
1004 free(buf);
1005 out:
1006 return err;
1009 atf_error_t
1010 atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
1012 atf_error_t err;
1013 char *buf;
1014 int fd;
1016 if (!check_umask(S_IRWXU, S_IRWXU)) {
1017 err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
1018 goto out;
1021 err = copy_contents(p, &buf);
1022 if (atf_is_error(err))
1023 goto out;
1025 err = do_mkstemp(buf, &fd);
1026 if (atf_is_error(err))
1027 goto out_buf;
1029 replace_contents(p, buf);
1030 *fdout = fd;
1032 INV(!atf_is_error(err));
1033 out_buf:
1034 free(buf);
1035 out:
1036 return err;
1039 atf_error_t
1040 atf_fs_rmdir(const atf_fs_path_t *p)
1042 atf_error_t err;
1044 if (rmdir(atf_fs_path_cstring(p))) {
1045 if (errno == EEXIST) {
1046 /* Some operating systems (e.g. OpenSolaris 200906) return
1047 * EEXIST instead of ENOTEMPTY for non-empty directories.
1048 * Homogenize the return value so that callers don't need
1049 * to bother about differences in operating systems. */
1050 errno = ENOTEMPTY;
1052 err = atf_libc_error(errno, "Cannot remove directory");
1053 } else
1054 err = atf_no_error();
1056 return err;
1059 atf_error_t
1060 atf_fs_unlink(const atf_fs_path_t *p)
1062 atf_error_t err;
1063 const char *path;
1065 path = atf_fs_path_cstring(p);
1067 if (unlink(path) != 0)
1068 err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
1069 else
1070 err = atf_no_error();
1072 return err;