From 3facbd92cb93fd584da5ca91083a9c30e5ee0396 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 7 Jun 2017 11:35:57 +0300 Subject: scanner: add files in include dirs in alphabetical order. This means that if you have a directory structure like this /foo /foo/02_rules.nft /foo/01_rules.nft where *.nft files in directory /foo are nft scripts, then an include statement in another nft script like this include "/foo/" guarantees that "01_rules.nft" is loaded before "02_rules.nft". Signed-off-by: Ismo Puustinen Signed-off-by: Pablo Neira Ayuso --- src/scanner.l | 97 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/scanner.l b/src/scanner.l index fe65e0c6..f220e593 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -13,10 +13,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -668,14 +670,37 @@ int scanner_read_file(void *scanner, const char *filename, return include_file(scanner, filename, loc); } +static int directoryfilter(const struct dirent *de) +{ + if (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0) + return 0; + + /* Accept other filenames. If we want to enable filtering based on + * filename suffix (*.nft), this would be the place to do it. + */ + return 1; +} + +static void free_scandir_des(struct dirent **des, int n_des) +{ + int i; + + for (i = 0; i < n_des; i++) + free(des[i]); + + free(des); +} + static int include_directory(void *scanner, const char *dirname, - DIR *directory, const struct location *loc) + const struct location *loc) { struct parser_state *state = yyget_extra(scanner); + struct dirent **des = NULL; struct error_record *erec; - struct dirent *de; + int ret, n_des = 0, i; + char dirbuf[PATH_MAX]; FILE *f; - int ret; if (!dirname[0] || dirname[strlen(dirname)-1] != '/') { erec = error(loc, "Include directory name \"%s\" does not end in '/'", @@ -684,23 +709,31 @@ static int include_directory(void *scanner, const char *dirname, } /* If the path is a directory, assume that all files there need - * to be included. + * to be included. Sort the file list in alphabetical order. */ - while ((de = readdir(directory))) { - char dirbuf[PATH_MAX]; + n_des = scandir(dirname, &des, directoryfilter, alphasort); + if (n_des < 0) { + erec = error(loc, "Failed to scan directory contents for \"%s\"", + dirname); + goto err; + } else if (n_des == 0) { + /* nothing to do */ + free(des); + return 0; + } + /* We need to push the files in reverse order, so that they will be + * popped in the correct order. + */ + for (i = n_des - 1; i >= 0; i--) { ret = snprintf(dirbuf, sizeof(dirbuf), "%s/%s", dirname, - de->d_name); + des[i]->d_name); if (ret < 0 || ret >= PATH_MAX) { erec = error(loc, "Too long file path \"%s/%s\"\n", - dirname, de->d_name); + dirname, des[i]->d_name); goto err; } - if (strcmp(de->d_name, ".") == 0 || - strcmp(de->d_name, "..") == 0) - continue; - f = fopen(dirbuf, "r"); if (f == NULL) { erec = error(loc, "Could not open file \"%s\": %s\n", @@ -708,12 +741,14 @@ static int include_directory(void *scanner, const char *dirname, goto err; } - erec = scanner_push_file(scanner, de->d_name, f, loc); + erec = scanner_push_file(scanner, des[i]->d_name, f, loc); if (erec != NULL) goto err; } + free_scandir_des(des, n_des); return 0; err: + free_scandir_des(des, n_des); erec_queue(erec, state->msgs); return -1; } @@ -721,27 +756,35 @@ err: static int include_dentry(void *scanner, const char *filename, const struct location *loc) { - DIR *directory; + struct parser_state *state = yyget_extra(scanner); + struct error_record *erec; + struct stat st; int ret; - /* The file can be either a simple file or a directory which - * contains files. - */ - directory = opendir(filename); - if (directory == NULL && - errno != ENOTDIR) { - /* Could not access the directory or file, keep on searching. + ret = stat(filename, &st); + if (ret == -1 && errno == ENOENT) { + /* Could not find the directory or file, keep on searching. * Return value '1' indicates to the caller that we should still * search in the next include directory. */ - ret = 1; - } else if (directory != NULL) { - ret = include_directory(scanner, filename, directory, loc); - closedir(directory); - } else { - ret = include_file(scanner, filename, loc); + return 1; + } else if (ret == 0) { + if (S_ISDIR(st.st_mode)) + return include_directory(scanner, filename, loc); + else if (S_ISREG(st.st_mode)) + return include_file(scanner, filename, loc); + else { + errno = EINVAL; + ret = -1; + } } + /* Process error for failed stat and cases where the file is not a + * directory or (a link to) a regular file. + */ + erec = error(loc, "Failed to access file \"%s\": %s\n", + filename, strerror(errno)); + erec_queue(erec, state->msgs); return ret; } -- cgit v1.2.3