diff options
author | Anderson Toshiyuki Sasaki <ansasaki@redhat.com> | 2019-07-31 15:15:22 +0200 |
---|---|---|
committer | Jakub Jelen <jjelen@redhat.com> | 2019-08-08 09:45:06 +0200 |
commit | 80c1dbdb618882c40eb0fb13987705d1d1e6fde3 (patch) | |
tree | 5d5d089b8d33169535ebd53f0b08e47f1beb35be | |
parent | 4505c076b3331d2fa32c4cbd2969b8c0bc6e444f (diff) | |
download | libssh-80c1dbdb618882c40eb0fb13987705d1d1e6fde3.tar.gz libssh-80c1dbdb618882c40eb0fb13987705d1d1e6fde3.tar.xz libssh-80c1dbdb618882c40eb0fb13987705d1d1e6fde3.zip |
misc: Introduce internal function ssh_mkdirs()
If the given path includes missing directories, ssh_mkdirs() tries to
create them recursively.
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 742918cb1cf99b711538dc9c21e3850fd487dfd6)
-rw-r--r-- | include/libssh/misc.h | 3 | ||||
-rw-r--r-- | src/misc.c | 75 | ||||
-rw-r--r-- | tests/unittests/torture_misc.c | 93 |
3 files changed, 166 insertions, 5 deletions
diff --git a/include/libssh/misc.h b/include/libssh/misc.h index a6108fcc..543de04f 100644 --- a/include/libssh/misc.h +++ b/include/libssh/misc.h @@ -85,4 +85,7 @@ int ssh_match_group(const char *group, const char *object); void uint64_inc(unsigned char *counter); void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len); + +int ssh_mkdirs(const char *pathname, mode_t mode); + #endif /* MISC_H_ */ @@ -950,16 +950,81 @@ char *ssh_basename (const char *path) { * * @return 0 on success, < 0 on error with errno set. */ -int ssh_mkdir(const char *pathname, mode_t mode) { - int r; +int ssh_mkdir(const char *pathname, mode_t mode) +{ + int r; +#ifdef _WIN32 + r = _mkdir(pathname); +#else + r = mkdir(pathname, mode); +#endif + return r; +} + +/** + * @brief Attempts to create a directory with the given pathname. The missing + * directories in the given pathname are created recursively. + * + * @param[in] pathname The path name to create the directory. + * + * @param[in] mode The permissions to use. + * + * @return 0 on success, < 0 on error with errno set. + * + * @note mode is ignored on Windows systems. + */ +int ssh_mkdirs(const char *pathname, mode_t mode) +{ + int rc = 0; + char *parent = NULL; + + if (pathname == NULL || + pathname[0] == '\0' || + !strcmp(pathname, "/") || + !strcmp(pathname, ".")) + { + errno = EINVAL; + return -1; + } + + errno = 0; + +#ifdef _WIN32 + rc = _mkdir(pathname); +#else + rc = mkdir(pathname, mode); +#endif + + if (rc < 0) { + /* If a directory was missing, try to create the parent */ + if (errno == ENOENT) { + parent = ssh_dirname(pathname); + if (parent == NULL) { + errno = ENOMEM; + return -1; + } + + rc = ssh_mkdirs(parent, mode); + if (rc < 0) { + /* We could not create the parent */ + SAFE_FREE(parent); + return -1; + } + + SAFE_FREE(parent); + + /* Try again */ + errno = 0; #ifdef _WIN32 - r = _mkdir(pathname); + rc = _mkdir(pathname); #else - r = mkdir(pathname, mode); + rc = mkdir(pathname, mode); #endif + } + } - return r; + return rc; } /** diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c index 18cb1a42..bd7cffc6 100644 --- a/tests/unittests/torture_misc.c +++ b/tests/unittests/torture_misc.c @@ -397,6 +397,98 @@ static void torture_ssh_dir_writeable(UNUSED_PARAM(void **state)) SAFE_FREE(tmp_dir); } +static void torture_ssh_mkdirs(UNUSED_PARAM(void **state)) +{ + char *tmp_dir = NULL; + char *cwd = NULL; + char buffer[256]; + + ssize_t count = 0; + + int rc; + + /* Get current working directory */ + cwd = torture_get_current_working_dir(); + assert_non_null(cwd); + + /* Create a base disposable directory */ + tmp_dir = torture_make_temp_dir(template); + assert_non_null(tmp_dir); + + /* Create a single directory */ + count = snprintf(buffer, sizeof(buffer), "%s/a", tmp_dir); + assert_return_code(count, errno); + + rc = ssh_mkdirs(buffer, 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 1); + + /* Create directories recursively */ + count = snprintf(buffer, sizeof(buffer), "%s/b/c/d", tmp_dir); + assert_return_code(count, errno); + + rc = ssh_mkdirs(buffer, 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 1); + + /* Change directory */ + rc = torture_change_dir(tmp_dir); + assert_return_code(rc, errno); + + /* Create single local directory */ + rc = ssh_mkdirs("e", 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable("e"); + assert_int_equal(rc, 1); + + /* Create local directories recursively */ + rc = ssh_mkdirs("f/g/h", 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable("f/g/h"); + assert_int_equal(rc, 1); + + /* Negative test for creating "." directory */ + rc = ssh_mkdirs(".", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating "/" directory */ + rc = ssh_mkdirs("/", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating "" directory */ + rc = ssh_mkdirs("", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating NULL directory */ + rc = ssh_mkdirs(NULL, 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating existing directory */ + rc = ssh_mkdirs("a", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EEXIST); + + /* Return to original directory */ + rc = torture_change_dir(cwd); + assert_return_code(rc, errno); + + /* Cleanup */ + torture_rmdirs(tmp_dir); + + SAFE_FREE(tmp_dir); + SAFE_FREE(cwd); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -415,6 +507,7 @@ int torture_run_tests(void) { cmocka_unit_test(torture_timeout_update), cmocka_unit_test(torture_ssh_analyze_banner), cmocka_unit_test(torture_ssh_dir_writeable), + cmocka_unit_test(torture_ssh_mkdirs), }; ssh_init(); |