fs-util.c revision ee735086f8670be1591fa9593e80dd60163a7a2f
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering/***
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering This file is part of systemd.
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering Copyright 2010 Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering systemd is free software; you can redistribute it and/or modify it
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering under the terms of the GNU Lesser General Public License as published by
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering (at your option) any later version.
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering systemd is distributed in the hope that it will be useful, but
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering Lesser General Public License for more details.
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering You should have received a copy of the GNU Lesser General Public License
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering***/
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "alloc-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "dirent-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "fd-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "fileio.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "fs-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "mkdir.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "parse-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "path-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "string-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "strv.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "user-util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering#include "util.h"
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringint unlink_noerrno(const char *path) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering PROTECT_ERRNO;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering int r;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering r = unlink(path);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (r < 0)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering}
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringint rmdir_parents(const char *path, const char *stop) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering size_t l;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering int r = 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering assert(path);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering assert(stop);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering l = strlen(path);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* Skip trailing slashes */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering while (l > 0 && path[l-1] == '/')
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering l--;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering while (l > 0) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering char *t;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* Skip last component */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering while (l > 0 && path[l-1] != '/')
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering l--;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* Skip trailing slashes */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering while (l > 0 && path[l-1] == '/')
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering l--;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (l <= 0)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering break;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering t = strndup(path, l);
7f3fdb7f19a109fa3d1be92926bfe4cea1817da5Jakub Wilk if (!t)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -ENOMEM;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (path_startswith(stop, t)) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering free(t);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering r = rmdir(t);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering free(t);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (r < 0)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (errno != ENOENT)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering}
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringint rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering struct stat buf;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering int ret;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (ret >= 0)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* renameat2() exists since Linux 3.15, btrfs added support for it later.
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * If it is not implemented, fallback to another method. */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (!IN_SET(errno, EINVAL, ENOSYS))
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* The link()/unlink() fallback does not work on directories. But
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * renameat() without RENAME_NOREPLACE gives the same semantics on
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * directories, except when newpath is an *empty* directory. This is
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * good enough. */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (ret >= 0 && S_ISDIR(buf.st_mode)) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ret = renameat(olddirfd, oldpath, newdirfd, newpath);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return ret >= 0 ? 0 : -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* If it is not a directory, use the link()/unlink() fallback. */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (ret < 0)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ret = unlinkat(olddirfd, oldpath, 0);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (ret < 0) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* backup errno before the following unlinkat() alters it */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ret = errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering (void) unlinkat(newdirfd, newpath, 0);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering errno = ret;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering}
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringint readlinkat_malloc(int fd, const char *p, char **ret) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering size_t l = 100;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering int r;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering assert(p);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering assert(ret);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering for (;;) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering char *c;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering ssize_t n;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering c = new(char, l);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (!c)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return -ENOMEM;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering n = readlinkat(fd, p, c, l-1);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (n < 0) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering r = -errno;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering free(c);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return r;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if ((size_t) n < l-1) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering c[n] = 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering *ret = c;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return 0;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering free(c);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering l *= 2;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering }
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering}
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringint readlink_malloc(const char *p, char **ret) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return readlinkat_malloc(AT_FDCWD, p, ret);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering}
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringint readlink_value(const char *p, char **ret) {
_cleanup_free_ char *link = NULL;
char *value;
int r;
r = readlink_malloc(p, &link);
if (r < 0)
return r;
value = basename(link);
if (!value)
return -ENOENT;
value = strdup(value);
if (!value)
return -ENOMEM;
*ret = value;
return 0;
}
int readlink_and_make_absolute(const char *p, char **r) {
_cleanup_free_ char *target = NULL;
char *k;
int j;
assert(p);
assert(r);
j = readlink_malloc(p, &target);
if (j < 0)
return j;
k = file_in_same_dir(p, target);
if (!k)
return -ENOMEM;
*r = k;
return 0;
}
int readlink_and_canonicalize(const char *p, char **r) {
char *t, *s;
int j;
assert(p);
assert(r);
j = readlink_and_make_absolute(p, &t);
if (j < 0)
return j;
s = canonicalize_file_name(t);
if (s) {
free(t);
*r = s;
} else
*r = t;
path_kill_slashes(*r);
return 0;
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
/* Under the assumption that we are running privileged we
* first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
if (mode != MODE_INVALID)
if (chmod(path, mode) < 0)
return -errno;
if (uid != UID_INVALID || gid != GID_INVALID)
if (chown(path, uid, gid) < 0)
return -errno;
return 0;
}
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
assert(fd >= 0);
/* Under the assumption that we are running privileged we
* first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
if (mode != MODE_INVALID)
if (fchmod(fd, mode) < 0)
return -errno;
if (uid != UID_INVALID || gid != GID_INVALID)
if (fchown(fd, uid, gid) < 0)
return -errno;
return 0;
}
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
u = umask(0777);
r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
umask(u);
return r;
}
int fd_warn_permissions(const char *path, int fd) {
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
if (st.st_mode & 0111)
log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
if (st.st_mode & 0002)
log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
if (getpid() == 1 && (st.st_mode & 0044) != 0044)
log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
return 0;
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
_cleanup_close_ int fd;
int r;
assert(path);
if (parents)
mkdir_parents(path, 0755);
fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
if (fd < 0)
return -errno;
if (mode != MODE_INVALID) {
r = fchmod(fd, mode);
if (r < 0)
return -errno;
}
if (uid != UID_INVALID || gid != GID_INVALID) {
r = fchown(fd, uid, gid);
if (r < 0)
return -errno;
}
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
r = futimens(fd, ts);
} else
r = futimens(fd, NULL);
if (r < 0)
return -errno;
return 0;
}
int touch(const char *path) {
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
}
int symlink_idempotent(const char *from, const char *to) {
_cleanup_free_ char *p = NULL;
int r;
assert(from);
assert(to);
if (symlink(from, to) < 0) {
if (errno != EEXIST)
return -errno;
r = readlink_malloc(to, &p);
if (r < 0)
return r;
if (!streq(p, from))
return -EINVAL;
}
return 0;
}
int symlink_atomic(const char *from, const char *to) {
_cleanup_free_ char *t = NULL;
int r;
assert(from);
assert(to);
r = tempfn_random(to, NULL, &t);
if (r < 0)
return r;
if (symlink(from, t) < 0)
return -errno;
if (rename(t, to) < 0) {
unlink_noerrno(t);
return -errno;
}
return 0;
}
int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
_cleanup_free_ char *t = NULL;
int r;
assert(path);
r = tempfn_random(path, NULL, &t);
if (r < 0)
return r;
if (mknod(t, mode, dev) < 0)
return -errno;
if (rename(t, path) < 0) {
unlink_noerrno(t);
return -errno;
}
return 0;
}
int mkfifo_atomic(const char *path, mode_t mode) {
_cleanup_free_ char *t = NULL;
int r;
assert(path);
r = tempfn_random(path, NULL, &t);
if (r < 0)
return r;
if (mkfifo(t, mode) < 0)
return -errno;
if (rename(t, path) < 0) {
unlink_noerrno(t);
return -errno;
}
return 0;
}
int get_files_in_directory(const char *path, char ***list) {
_cleanup_closedir_ DIR *d = NULL;
size_t bufsize = 0, n = 0;
_cleanup_strv_free_ char **l = NULL;
assert(path);
/* Returns all files in a directory in *list, and the number
* of files as return value. If list is NULL returns only the
* number. */
d = opendir(path);
if (!d)
return -errno;
for (;;) {
struct dirent *de;
errno = 0;
de = readdir(d);
if (!de && errno != 0)
return -errno;
if (!de)
break;
dirent_ensure_type(d, de);
if (!dirent_is_file(de))
continue;
if (list) {
/* one extra slot is needed for the terminating NULL */
if (!GREEDY_REALLOC(l, bufsize, n + 2))
return -ENOMEM;
l[n] = strdup(de->d_name);
if (!l[n])
return -ENOMEM;
l[++n] = NULL;
} else
n++;
}
if (list) {
*list = l;
l = NULL; /* avoid freeing */
}
return n;
}