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