diff --git a/libraries/libapparmor/include/sys/apparmor_private.h b/libraries/libapparmor/include/sys/apparmor_private.h index 14055dfd7..32b15bab5 100644 --- a/libraries/libapparmor/include/sys/apparmor_private.h +++ b/libraries/libapparmor/include/sys/apparmor_private.h @@ -17,7 +17,6 @@ #ifndef _SYS_APPARMOR_PRIVATE_H #define _SYS_APPARMOR_PRIVATE_H 1 -#include #include #include @@ -31,8 +30,8 @@ void _aa_autofclose(FILE **f); int _aa_asprintf(char **strp, const char *fmt, ...); -int _aa_dirat_for_each(DIR *dir, const char *name, void *data, - int (* cb)(DIR *, const char *, struct stat *, void *)); +int _aa_dirat_for_each(int dirfd, const char *name, void *data, + int (* cb)(int, const char *, struct stat *, void *)); __END_DECLS diff --git a/libraries/libapparmor/src/features.c b/libraries/libapparmor/src/features.c index 88ded730e..e7bf74539 100644 --- a/libraries/libapparmor/src/features.c +++ b/libraries/libapparmor/src/features.c @@ -80,7 +80,7 @@ static int features_snprintf(struct features_struct *fst, const char *fmt, ...) return 0; } -static int features_dir_cb(DIR *dir, const char *name, struct stat *st, +static int features_dir_cb(int dirfd, const char *name, struct stat *st, void *data) { struct features_struct *fst = (struct features_struct *) data; @@ -97,7 +97,7 @@ static int features_dir_cb(DIR *dir, const char *name, struct stat *st, int len; int remaining = fst->size - (fst->pos - fst->buffer); - file = openat(dirfd(dir), name, O_RDONLY); + file = openat(dirfd, name, O_RDONLY); if (file == -1) { PDEBUG("Could not open '%s'", name); return -1; @@ -122,7 +122,7 @@ static int features_dir_cb(DIR *dir, const char *name, struct stat *st, return -1; } } else if (S_ISDIR(st->st_mode)) { - if (_aa_dirat_for_each(dir, name, fst, features_dir_cb)) + if (_aa_dirat_for_each(dirfd, name, fst, features_dir_cb)) return -1; } @@ -137,7 +137,7 @@ static int handle_features_dir(const char *filename, char *buffer, int size, { struct features_struct fst = { buffer, size, pos }; - if (_aa_dirat_for_each(NULL, filename, &fst, features_dir_cb)) { + if (_aa_dirat_for_each(AT_FDCWD, filename, &fst, features_dir_cb)) { PDEBUG("Failed evaluating %s\n", filename); return -1; } diff --git a/libraries/libapparmor/src/policy_cache.c b/libraries/libapparmor/src/policy_cache.c index 557fa5544..5ddc64036 100644 --- a/libraries/libapparmor/src/policy_cache.c +++ b/libraries/libapparmor/src/policy_cache.c @@ -16,8 +16,8 @@ * Ltd. */ -#include #include +#include #include #include #include @@ -36,12 +36,12 @@ struct aa_policy_cache { char *features_path; }; -static int clear_cache_cb(DIR *dir, const char *path, struct stat *st, +static int clear_cache_cb(int dirfd, const char *path, struct stat *st, void *data unused) { /* remove regular files */ if (S_ISREG(st->st_mode)) - return unlinkat(dirfd(dir), path, 0); + return unlinkat(dirfd, path, 0); /* do nothing with other file types */ return 0; @@ -115,7 +115,7 @@ struct replace_all_cb_data { aa_kernel_interface *kernel_interface; }; -static int replace_all_cb(DIR *dir unused, const char *name, struct stat *st, +static int replace_all_cb(int dirfd unused, const char *name, struct stat *st, void *cb_data) { int retval = 0; @@ -253,7 +253,7 @@ void aa_policy_cache_unref(aa_policy_cache *policy_cache) */ int aa_policy_cache_remove(const char *path) { - return _aa_dirat_for_each(NULL, path, NULL, clear_cache_cb); + return _aa_dirat_for_each(AT_FDCWD, path, NULL, clear_cache_cb); } /** @@ -283,7 +283,7 @@ int aa_policy_cache_replace_all(aa_policy_cache *policy_cache, cb_data.policy_cache = policy_cache; cb_data.kernel_interface = kernel_interface; - retval = _aa_dirat_for_each(NULL, policy_cache->path, &cb_data, + retval = _aa_dirat_for_each(AT_FDCWD, policy_cache->path, &cb_data, replace_all_cb); aa_kernel_interface_unref(kernel_interface); diff --git a/libraries/libapparmor/src/private.c b/libraries/libapparmor/src/private.c index f078f2887..4769f348b 100644 --- a/libraries/libapparmor/src/private.c +++ b/libraries/libapparmor/src/private.c @@ -170,25 +170,31 @@ int _aa_asprintf(char **strp, const char *fmt, ...) return rc; } +static int dot_or_dot_dot_filter(const struct dirent *ent) +{ + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + return 0; + + return 1; +} + /** * _aa_dirat_for_each: iterate over a directory calling cb for each entry - * @dir: already opened directory (MAY BE NULL) - * @name: name of the directory (MAY BE NULL) + * @dirfd: already opened directory + * @name: name of the directory (NOT NULL) * @data: data pointer to pass to the callback fn (MAY BE NULL) * @cb: the callback to pass entry too (NOT NULL) * * Iterate over the entries in a directory calling cb for each entry. - * The directory to iterate is determined by a combination of @dir and + * The directory to iterate is determined by a combination of @dirfd and * @name. * - * IF @name is a relative path it is determine relative to at @dir if it - * is specified, else it the lookup is done relative to the current - * working directory. + * See the openat section of the open(2) man page for details on valid @dirfd + * and @name combinations. This function does accept AT_FDCWD as @dirfd if + * @name should be considered relative to the current working directory. * - * If @name is not specified then @dir is used as the directory to iterate - * over. - * - * It is an error if both @name and @dir are null + * Pass "." for @name if @dirfd is the directory to iterate over. * * The cb function is called with the DIR in use and the name of the * file in that directory. If the file is to be opened it should @@ -196,88 +202,52 @@ int _aa_asprintf(char **strp, const char *fmt, ...) * * Returns: 0 on success, else -1 and errno is set to the error code */ -int _aa_dirat_for_each(DIR *dir, const char *name, void *data, - int (* cb)(DIR *, const char *, struct stat *, void *)) +int _aa_dirat_for_each(int dirfd, const char *name, void *data, + int (* cb)(int, const char *, struct stat *, void *)) { - autofree struct dirent *dirent = NULL; - DIR *d = NULL; - int error; + autofree struct dirent **namelist = NULL; + autoclose int cb_dirfd = -1; + int i, num_dirs, rc; - if (!cb || (!dir && !name)) { + if (!cb || !name) { errno = EINVAL; return -1; } - if (dir && (!name || *name != '/')) { - dirent = (struct dirent *) - malloc(offsetof(struct dirent, d_name) + - fpathconf(dirfd(dir), _PC_NAME_MAX) + 1); - } else { - dirent = (struct dirent *) - malloc(offsetof(struct dirent, d_name) + - pathconf(name, _PC_NAME_MAX) + 1); - } - if (!dirent) { - errno = ENOMEM; - PDEBUG("could not alloc dirent: %m\n"); + cb_dirfd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (cb_dirfd == -1) { + PDEBUG("could not open directory '%s': %m\n", name); return -1; } - if (name) { - if (dir && *name != '/') { - int fd = openat(dirfd(dir), name, O_RDONLY); - if (fd == -1) - goto fail; - d = fdopendir(fd); - } else { - d = opendir(name); - } - PDEBUG("Open dir '%s': %s\n", name, d ? "succeeded" : "failed"); - if (!(d)) - goto fail; - } else { /* dir && !name */ - PDEBUG("Recieved cache directory\n"); - d = dir; + num_dirs = scandirat(cb_dirfd, ".", &namelist, + dot_or_dot_dot_filter, NULL); + if (num_dirs == -1) { + PDEBUG("scandirat of directory '%s' failed: %m\n", name); + return -1; } - for (;;) { - struct dirent *ent; + for (rc = 0, i = 0; i < num_dirs; i++) { + /* Must cycle through all dirs so that each one is autofreed */ + autofree struct dirent *dir = namelist[i]; struct stat my_stat; - error = readdir_r(d, dirent, &ent); - if (error) { - errno = error; /* readdir_r directly returns an errno */ - PDEBUG("readdir_r failed: %m\n"); - goto fail; - } else if (!ent) { - break; - } - - if (strcmp(ent->d_name, ".") == 0 || - strcmp(ent->d_name, "..") == 0) + if (rc) continue; - if (fstatat(dirfd(d), ent->d_name, &my_stat, 0)) { - PDEBUG("stat failed for '%s': %m\n", name); - goto fail; + if (fstatat(cb_dirfd, dir->d_name, &my_stat, 0)) { + PDEBUG("stat failed for '%s': %m\n", dir->d_name); + rc = -1; + continue; } - if (cb(d, ent->d_name, &my_stat, data)) { - PDEBUG("dir_for_each callback failed\n"); - goto fail; + if (cb(cb_dirfd, dir->d_name, &my_stat, data)) { + PDEBUG("dir_for_each callback failed for '%s'\n", + dir->d_name); + rc = -1; + continue; } } - if (d != dir) - closedir(d); - - return 0; - -fail: - error = errno; - if (d && d != dir) - closedir(d); - errno = error; - - return -1; + return rc; } diff --git a/parser/lib.c b/parser/lib.c index 6aae67004..11c221075 100644 --- a/parser/lib.c +++ b/parser/lib.c @@ -16,7 +16,6 @@ * Ltd. */ -#include #include #include @@ -29,10 +28,10 @@ #include "lib.h" #include "parser.h" -int dirat_for_each(DIR *dir, const char *name, void *data, - int (* cb)(DIR *, const char *, struct stat *, void *)) +int dirat_for_each(int dirfd, const char *name, void *data, + int (* cb)(int, const char *, struct stat *, void *)) { - int retval = _aa_dirat_for_each(dir, name, data, cb); + int retval = _aa_dirat_for_each(dirfd, name, data, cb); if (retval) PDEBUG("dirat_for_each failed: %m\n"); diff --git a/parser/lib.h b/parser/lib.h index a980a5a84..e9a856771 100644 --- a/parser/lib.h +++ b/parser/lib.h @@ -9,8 +9,8 @@ #define asprintf _aa_asprintf -int dirat_for_each(DIR *dir, const char *name, void *data, - int (* cb)(DIR *, const char *, struct stat *, void *)); +int dirat_for_each(int dirfd, const char *name, void *data, + int (* cb)(int, const char *, struct stat *, void *)); int isodigit(char c); long strntol(const char *str, const char **endptr, int base, long maxval, diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 5af788a68..2832a1cc3 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -32,7 +32,6 @@ #include #include #include -#include #include #include @@ -120,7 +119,7 @@ struct cb_struct { const char *filename; }; -static int include_dir_cb(DIR *dir unused, const char *name, struct stat *st, +static int include_dir_cb(int dirfd unused, const char *name, struct stat *st, void *data) { struct cb_struct *d = (struct cb_struct *) data; @@ -179,7 +178,7 @@ void include_filename(char *filename, int search) struct cb_struct data = { fullpath, filename }; fclose(include_file); include_file = NULL; - if (dirat_for_each(NULL, fullpath, &data, include_dir_cb)) { + if (dirat_for_each(AT_FDCWD, fullpath, &data, include_dir_cb)) { yyerror(_("Could not process include directory" " '%s' in '%s'"), fullpath, filename);; } diff --git a/parser/parser_main.c b/parser/parser_main.c index 8d740f79a..2f78e7bd3 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -28,7 +28,6 @@ #include #include #include -#include /* enable the following line to get voluminous debug info */ /* #define DEBUG */ @@ -812,7 +811,7 @@ struct dir_cb_data { }; /* data - pointer to a dir_cb_data */ -static int profile_dir_cb(DIR *dir unused, const char *name, struct stat *st, +static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st, void *data) { int rc = 0; @@ -829,7 +828,7 @@ static int profile_dir_cb(DIR *dir unused, const char *name, struct stat *st, } /* data - pointer to a dir_cb_data */ -static int binary_dir_cb(DIR *dir unused, const char *name, struct stat *st, +static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st, void *data) { int rc = 0; @@ -962,14 +961,14 @@ int main(int argc, char *argv[]) } if (profilename && S_ISDIR(stat_file.st_mode)) { - int (*cb)(DIR *dir, const char *name, struct stat *st, + int (*cb)(int dirfd, const char *name, struct stat *st, void *data); struct dir_cb_data cb_data; cb_data.dirname = profilename; cb_data.cachedir = cacheloc; cb = binary_input ? binary_dir_cb : profile_dir_cb; - if ((retval = dirat_for_each(NULL, profilename, + if ((retval = dirat_for_each(AT_FDCWD, profilename, &cb_data, cb))) { PDEBUG("Failed loading profiles from %s\n", profilename);