From fc20af17e8f587c9f69082905cbeec0e964362c2 Mon Sep 17 00:00:00 2001 From: jorton Date: Tue, 25 Jun 2019 14:21:56 +0000 Subject: Add apr_dir_pread(), a variant of apr_dir_read() which allows callers to read a directory with constant memory consumption: * include/apr_file_info.h: Add warning on memory consumption for apr_dir_read; declare apr_dir_pread. * file_io/unix/dir.c (apr_dir_pread): Rename from apr_dir_read and take pool argument. (apr_dir_read): Reimplement using it. * file_io/win32/dir.c, file_io/os2/dir.c: Likewise, but untested. * test/testdir.c (test_pread) [APR_POOL_DEBUG]: Add test case. git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@1862071 13f79535-47bb-0310-9956-ffa450edef68 --- file_io/os2/dir.c | 12 ++++++++---- file_io/unix/dir.c | 12 +++++++++--- file_io/win32/dir.c | 8 +++++++- include/apr_file_info.h | 22 ++++++++++++++++++++++ test/testdir.c | 35 ++++++++++++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/file_io/os2/dir.c b/file_io/os2/dir.c index f1554b6f3..d1a0072cc 100644 --- a/file_io/os2/dir.c +++ b/file_io/os2/dir.c @@ -79,24 +79,28 @@ APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *thedir) return APR_FROM_OS_ERROR(rv); } - - APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir) +{ + return apr_dir_pread(finfo, wanted, thedir, thedir->pool); +} + +APR_DECLARE(apr_status_t) apr_dir_pread(apr_finfo_t *finfo, apr_int32_t wanted, + apr_dir_t *thedir, apr_pool_t *pool) { int rv; ULONG entries = 1; if (thedir->handle == 0) { thedir->handle = HDIR_CREATE; - rv = DosFindFirst(apr_pstrcat(thedir->pool, thedir->dirname, "/*", NULL), &thedir->handle, + rv = DosFindFirst(apr_pstrcat(pool, thedir->dirname, "/*", NULL), &thedir->handle, FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY, &thedir->entry, sizeof(thedir->entry), &entries, FIL_STANDARD); } else { rv = DosFindNext(thedir->handle, &thedir->entry, sizeof(thedir->entry), &entries); } - finfo->pool = thedir->pool; + finfo->pool = pool; finfo->fname = NULL; finfo->valid = 0; diff --git a/file_io/unix/dir.c b/file_io/unix/dir.c index d9b344f30..5a17b8601 100644 --- a/file_io/unix/dir.c +++ b/file_io/unix/dir.c @@ -141,6 +141,12 @@ static apr_filetype_e filetype_from_dirent_type(int type) apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir) +{ + return apr_dir_pread(finfo, wanted, thedir, thedir->pool); +} + +apr_status_t apr_dir_pread(apr_finfo_t *finfo, apr_int32_t wanted, + apr_dir_t *thedir, apr_pool_t *pool) { apr_status_t ret = 0; #ifdef DIRENT_TYPE @@ -251,7 +257,7 @@ apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_cpystrn(end, thedir->entry->d_name, sizeof fspec - (end - fspec)); - ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool); + ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, pool); /* We passed a stack name that will disappear */ finfo->fname = NULL; } @@ -263,7 +269,7 @@ apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, /* We don't bail because we fail to stat, when we are only -required- * to readdir... but the result will be APR_INCOMPLETE */ - finfo->pool = thedir->pool; + finfo->pool = pool; finfo->valid = 0; #ifdef DIRENT_TYPE if (type != APR_UNKFILE) { @@ -279,7 +285,7 @@ apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, #endif } - finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name); + finfo->name = apr_pstrdup(pool, thedir->entry->d_name); finfo->valid |= APR_FINFO_NAME; if (wanted) diff --git a/file_io/win32/dir.c b/file_io/win32/dir.c index f44bceb97..1703ac4b3 100644 --- a/file_io/win32/dir.c +++ b/file_io/win32/dir.c @@ -90,6 +90,12 @@ APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *dir) APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir) +{ + return apr_dir_pread(finfo, wanted, thedir, thedir->pool); +} + +APR_DECLARE(apr_status_t) apr_dir_pread(apr_finfo_t *finfo, apr_int32_t wanted, + apr_dir_t *thedir, apr_pool_t *pool) { apr_status_t rv; char *fname; @@ -150,7 +156,7 @@ APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) thedir->entry, 0, 1, fname, wanted); - finfo->pool = thedir->pool; + finfo->pool = pool; finfo->valid |= APR_FINFO_NAME; finfo->name = fname; diff --git a/include/apr_file_info.h b/include/apr_file_info.h index cfddc68da..f08762cea 100644 --- a/include/apr_file_info.h +++ b/include/apr_file_info.h @@ -263,10 +263,32 @@ APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *thedir); * not be filled in, and you need to check the @c finfo->valid bitmask * to verify that what you're looking for is there. When no more * entries are available, APR_ENOENT is returned. + * + * @warning Memory will be allocated in the pool passed to apr_dir_open; + * use apr_dir_pread() and a temporary pool to restrict memory + * consumption for a large directory. */ APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir); +/** + * Read the next entry from the specified directory. + * @param finfo the file info structure and filled in by apr_dir_read + * @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ + values + * @param thedir the directory descriptor returned from apr_dir_open + * @param pool the pool to use for allocations + * @remark No ordering is guaranteed for the entries read. + * + * @note If @c APR_INCOMPLETE is returned all the fields in @a finfo may + * not be filled in, and you need to check the @c finfo->valid bitmask + * to verify that what you're looking for is there. When no more + * entries are available, APR_ENOENT is returned. + */ +APR_DECLARE(apr_status_t) apr_dir_pread(apr_finfo_t *finfo, apr_int32_t wanted, + apr_dir_t *thedir, apr_pool_t *pool); + + /** * Rewind the directory to the first entry. * @param thedir the directory descriptor to rewind. diff --git a/test/testdir.c b/test/testdir.c index 21876be5d..bb3399cbf 100644 --- a/test/testdir.c +++ b/test/testdir.c @@ -430,6 +430,36 @@ static void test_readmore_info(abts_case* tc, void* data) ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); } +#if APR_POOL_DEBUG +static void test_pread(abts_case *tc, void *data) +{ + apr_dir_t *dir; + apr_finfo_t finfo; + apr_size_t before, after; + apr_pool_t *subp; + + APR_ASSERT_SUCCESS(tc, "apr_dir_open failed", apr_dir_open(&dir, "data", p)); + + apr_pool_create(&subp, p); + + before = apr_pool_num_bytes(p, 0); + + APR_ASSERT_SUCCESS(tc, "apr_dir_read failed", + apr_dir_pread(&finfo, APR_FINFO_DIRENT, dir, subp)); + + after = apr_pool_num_bytes(p, 0); + + ABTS_PTR_EQUAL(tc, finfo.pool, subp); + + apr_pool_destroy(subp); + + APR_ASSERT_SUCCESS(tc, "apr_dir_close failed", apr_dir_close(dir)); + + ABTS_INT_EQUAL(tc, before, after); + +} +#endif + abts_suite *testdir(abts_suite *suite) { suite = ADD_SUITE(suite) @@ -451,7 +481,10 @@ abts_suite *testdir(abts_suite *suite) abts_run_test(suite, test_closedir, NULL); abts_run_test(suite, test_uncleared_errno, NULL); abts_run_test(suite, test_readmore_info, NULL); - +#if APR_POOL_DEBUG + abts_run_test(suite, test_pread, NULL); +#endif + return suite; } -- cgit v1.2.1