diff options
| author | Nils Maier <maierman@web.de> | 2016-12-16 20:16:37 +0100 |
|---|---|---|
| committer | Nils Maier <maierman@web.de> | 2016-12-17 01:59:44 +0100 |
| commit | e98f3a558747a9cab208c7e6ce2900e5de5938d0 (patch) | |
| tree | 7c041aeed2e6959416da49ca9b1ee9c37255821a | |
| parent | f542f84b836b1fd89d89537155ea7e25e07f49be (diff) | |
Implement a somewhat compatible std::experimental::filesystem::space
Also implement space_downwards which will looks downwards in the tree of
the provided path for the first existing parent and return the disk
space for that.
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/util.h | 19 | ||||
| -rw-r--r-- | src/util_fs.cc | 119 | ||||
| -rw-r--r-- | test/Makefile.am | 1 | ||||
| -rw-r--r-- | test/UtilFsTest.cc | 116 |
5 files changed, 256 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9322112c..18ebe10a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -255,6 +255,7 @@ SRCS = \ uri_split.c uri_split.h\ usage_text.h\ util.cc util.h\ + util_fs.cc\ util_security.cc util_security.h\ ValueBase.cc ValueBase.h\ ValueBaseDiskWriter.h\ @@ -53,6 +53,7 @@ #include <algorithm> #include <vector> #include <memory> +#include <system_error> #include "a2time.h" #include "a2netcompat.h" @@ -875,6 +876,24 @@ void make_fd_cloexec(int fd); bool gainPrivilege(LPCTSTR privName); #endif // __MINGW32__ +// Basically std::experimental::filesystem, to be replaced later when the +// filesystem stdlib becomes stable and gains wide compiler support + +namespace filesystem { +struct space_info { + uintmax_t capacity; + uintmax_t free; + uintmax_t available; +}; + +space_info space(const char* path, std::error_code& code); + +// Progress downwards in the provided path, yielding a result for the first +// directory that exists. +space_info space_downwards(const char* path, std::error_code& code); + +} // namespace filesystem + } // namespace util } // namespace aria2 diff --git a/src/util_fs.cc b/src/util_fs.cc new file mode 100644 index 00000000..612e75fe --- /dev/null +++ b/src/util_fs.cc @@ -0,0 +1,119 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2016 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ + +#include "a2io.h" +#include "util.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#else // _WIN32 +#include <sys/statvfs.h> +#endif + +namespace aria2 { +namespace util { +namespace filesystem { + +space_info space(const char* path, std::error_code& ec) +{ + space_info rv{static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), + static_cast<uintmax_t>(-1)}; + if (!path || !*path) { + path = "."; + } +#ifdef _WIN32 + ULARGE_INTEGER sp_avail, sp_free, sp_cap; + auto wpath = utf8ToWChar(path); + if (GetDiskFreeSpaceExW(wpath.c_str(), &sp_avail, &sp_cap, &sp_free)) { + rv.capacity = static_cast<uintmax_t>(sp_cap.QuadPart); + rv.available = static_cast<uintmax_t>(sp_avail.QuadPart); + rv.free = static_cast<uintmax_t>(sp_free.QuadPart); + ec.clear(); + } + else { + ec.assign(GetLastError(), std::system_category()); + } +#else // _WIN32 + struct statvfs st; + if (!statvfs(path, &st)) { + rv.capacity = static_cast<uintmax_t>(st.f_blocks) * st.f_frsize; + rv.free = static_cast<uintmax_t>(st.f_bfree) * st.f_frsize; + rv.available = static_cast<uintmax_t>(st.f_bavail) * st.f_frsize; + ec.clear(); + } + else { + ec.assign(errno, std::system_category()); + } +#endif // _WIN32 + + return rv; +} + +space_info space_downwards(const char* path, std::error_code& ec) +{ + auto rv = space(path, ec); + if (!ec) { + return rv; + } + std::string spath(path); + for (;;) { + if (spath.empty()) { + break; + } +#if _WIN32 + if (spath == "\\\\") { + // raw UNC prefix + break; + } +#endif + auto pos = spath.find_last_of("/\\"); + if (pos == std::string::npos) { + spath = ""; + } + else { + spath = spath.substr(0, pos); + } + rv = space(spath.c_str(), ec); + if (!ec) { + break; + } + } + return rv; +} + +} // namespace filesystem +} // namespace util +} // namespace aria2 diff --git a/test/Makefile.am b/test/Makefile.am index c7114449..d39dcde3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,7 @@ aria2c_SOURCES = AllTest.cc\ RequestGroupTest.cc\ UtilTest1.cc\ UtilTest2.cc\ + UtilFsTest.cc\ UtilSecurityTest.cc\ UriListParserTest.cc\ HttpHeaderProcessorTest.cc\ diff --git a/test/UtilFsTest.cc b/test/UtilFsTest.cc new file mode 100644 index 00000000..e7e886d6 --- /dev/null +++ b/test/UtilFsTest.cc @@ -0,0 +1,116 @@ +#include "util.h" + +#include <fstream> +#include <cppunit/extensions/HelperMacros.h> + +#ifdef _WIN32 +static char* mkdtemp(char* tpl) +{ + char* dn = mktemp(tpl); + if (!dn) { + return dn; + } + if (mkdir(dn)) { + return nullptr; + } + return dn; +} +#endif // _WIN32 + +namespace aria2 { + +class UtilFsTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(UtilFsTest); + CPPUNIT_TEST(testSpace); + CPPUNIT_TEST(testSpacePwd); + CPPUNIT_TEST(testSpaceDownwardsFile); + CPPUNIT_TEST(testSpaceDownwardsDir); + CPPUNIT_TEST_SUITE_END(); + +private: +public: + void setUp() {} + + void testSpace(); + void testSpacePwd(); + void testSpaceDownwardsFile(); + void testSpaceDownwardsDir(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(UtilFsTest); + +void UtilFsTest::testSpace() +{ + const char* tmpl = "aria2.test.tmp.XXXXXX"; + char* tpl = strdup(tmpl); // lets just leak this + CPPUNIT_ASSERT(tpl); + char* tmp = mkdtemp(tpl); + CPPUNIT_ASSERT(tmp); + std::error_code ec; + util::filesystem::space(tmp, ec); + CPPUNIT_ASSERT(!ec); + rmdir(tmp); + auto rv = util::filesystem::space(tmp, ec); + CPPUNIT_ASSERT(ec); + CPPUNIT_ASSERT_EQUAL(rv.available, static_cast<uintmax_t>(-1)); + CPPUNIT_ASSERT_EQUAL(rv.capacity, static_cast<uintmax_t>(-1)); + CPPUNIT_ASSERT_EQUAL(rv.free, static_cast<uintmax_t>(-1)); +} + +void UtilFsTest::testSpacePwd() +{ + std::error_code ec; + util::filesystem::space(nullptr, ec); + CPPUNIT_ASSERT(!ec); + util::filesystem::space("", ec); + CPPUNIT_ASSERT(!ec); + util::filesystem::space(".", ec); + CPPUNIT_ASSERT(!ec); + util::filesystem::space("doesnotexit", ec); + CPPUNIT_ASSERT(ec); + util::filesystem::space_downwards("doesnotexit", ec); + CPPUNIT_ASSERT(!ec); +} + +void UtilFsTest::testSpaceDownwardsFile() +{ + const char* tmpl = "aria2.test.tmp.XXXXXX"; + char* tpl = strdup(tmpl); // lets just leak this + CPPUNIT_ASSERT(tpl); + char* tmp = mkdtemp(tpl); + CPPUNIT_ASSERT(tmp); + std::string tn(tmp); + tn += "/aria2.tmp"; + { + std::ofstream s(tn); + std::error_code ec; + std::string tn2(tn); + tn2 += "/something.else.entirely"; + util::filesystem::space(tn2.c_str(), ec); + CPPUNIT_ASSERT_MESSAGE(tn2, ec); + util::filesystem::space_downwards(tn2.c_str(), ec); + CPPUNIT_ASSERT_MESSAGE(tn2, !ec); + } + unlink(tn.c_str()); + rmdir(tmp); +} + +void UtilFsTest::testSpaceDownwardsDir() +{ + const char* tmpl = "aria2.test.tmp.XXXXXX"; + char* tpl = strdup(tmpl); // lets just leak this + CPPUNIT_ASSERT(tpl); + char* tmp = mkdtemp(tpl); + CPPUNIT_ASSERT(tmp); + std::string tn(tmp); + tn += "/something.else.entirely"; + std::error_code ec; + auto rv = util::filesystem::space(tn.c_str(), ec); + CPPUNIT_ASSERT_MESSAGE(tn, ec); + rv = util::filesystem::space_downwards(tn.c_str(), ec); + rmdir(tmp); + CPPUNIT_ASSERT_MESSAGE(tn, !ec); +} + +} // namespace aria2 |
