diff --git a/ChangeLog b/ChangeLog index 6573216cc..1c97adc00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -45,6 +45,7 @@ * fix uTP edge case where udp socket buffer fills up * fix nagle implementation in uTP + * fix resume file issue related to daylight savings time on windows * improve error checking in lazy_bdecode 0.16.16 release diff --git a/src/file.cpp b/src/file.cpp index 06797e7f5..da4dd4c38 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -173,6 +173,17 @@ namespace libtorrent if (p[i] == '/') p[i] = '\\'; return p; } + + time_t file_time_to_posix(FILETIME f) + { + const boost::uint64_t posix_time_offset = 11644473600LL; + boost::uint64_t ft = (boost::uint64_t(f.dwHighDateTime) << 32) + | f.dwLowDateTime; + + // windows filetime is specified in 100 nanoseconds resolution. + // convert to seconds + return time_t(ft / 10000000 - posix_time_offset); + } #endif void stat_file(std::string inf, file_status* s @@ -185,26 +196,39 @@ namespace libtorrent if (!inf.empty() && (inf[inf.size() - 1] == '\\' || inf[inf.size() - 1] == '/')) inf.resize(inf.size() - 1); -#endif #if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS +#define GetFileAttributesEx_ GetFileAttributesExW std::wstring f = convert_to_wstring(inf); #else +#define GetFileAttributesEx_ GetFileAttributesExA std::string f = convert_to_native(inf); #endif - -#if defined TORRENT_WINDOWS - struct _stati64 ret; -#if TORRENT_USE_WSTRING - if (_wstati64(f.c_str(), &ret) < 0) -#else - if (_stati64(f.c_str(), &ret) < 0) -#endif + WIN32_FILE_ATTRIBUTE_DATA data; + if (!GetFileAttributesEx(f.c_str(), GetFileExInfoStandard, &data)) { - ec.assign(errno, generic_category()); + ec.assign(GetLastError(), boost::system::get_system_category()); return; } + + s->file_size = (boost::uint64_t(data.nFileSizeHigh) << 32) | data.nFileSizeLow; + s->ctime = file_time_to_posix(data.ftCreationTime); + s->atime = file_time_to_posix(data.ftLastAccessTime); + s->mtime = file_time_to_posix(data.ftLastWriteTime); + + s->mode = ((data.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) + ? file_status::regular_file : 0) + | ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? file_status::directory : 0) + | ((data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) + ? file_status::character_special : 0); + #else + + // posix version + + std::string f = convert_to_native(inf); + struct stat ret; int retval; if (flags & dont_follow_links) @@ -216,26 +240,21 @@ namespace libtorrent ec.assign(errno, generic_category()); return; } -#endif // TORRENT_WINDOWS s->file_size = ret.st_size; s->atime = ret.st_atime; s->mtime = ret.st_mtime; s->ctime = ret.st_ctime; -#if defined TORRENT_WINDOWS - s->mode = ((ret.st_mode & _S_IFREG) ? file_status::regular_file : 0) - | ((ret.st_mode & _S_IFDIR) ? file_status::directory : 0) - | ((ret.st_mode & _S_IFCHR) ? file_status::character_special : 0) - | ((ret.st_mode & _S_IFIFO) ? file_status::fifo : 0); -#else - s->mode = (S_ISREG(ret.st_mode) ? file_status::regular_file : 0) - | (S_ISDIR(ret.st_mode) ? file_status::directory : 0) - | (S_ISLNK(ret.st_mode) ? file_status::link : 0) - | (S_ISFIFO(ret.st_mode) ? file_status::fifo : 0) - | (S_ISCHR(ret.st_mode) ? file_status::character_special : 0) - | (S_ISBLK(ret.st_mode) ? file_status::block_special : 0) - | (S_ISSOCK(ret.st_mode) ? file_status::socket : 0); -#endif + + s->mode = (S_ISREG(ret.st_mode) ? file_status::regular_file : 0) + | (S_ISDIR(ret.st_mode) ? file_status::directory : 0) + | (S_ISLNK(ret.st_mode) ? file_status::link : 0) + | (S_ISFIFO(ret.st_mode) ? file_status::fifo : 0) + | (S_ISCHR(ret.st_mode) ? file_status::character_special : 0) + | (S_ISBLK(ret.st_mode) ? file_status::block_special : 0) + | (S_ISSOCK(ret.st_mode) ? file_status::socket : 0); + +#endif // TORRENT_WINDOWS } void rename(std::string const& inf, std::string const& newf, error_code& ec) diff --git a/test/test_file.cpp b/test/test_file.cpp index f04b0d286..c21c4ce59 100644 --- a/test/test_file.cpp +++ b/test/test_file.cpp @@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/file.hpp" #include "test.hpp" +#include "setup_transfer.hpp" // for test_sleep #include // for strcmp #include #include @@ -58,8 +59,55 @@ int touch_file(std::string const& filename, int size) return 0; } +void test_create_directory() +{ + error_code ec; + create_directory("__foobar__", ec); + TEST_CHECK(!ec); + + file_status st; + stat_file("__foobar__", &st, ec); + TEST_CHECK(!ec); + + TEST_CHECK(st.mode & file_status::directory); + + remove("__foobar__", ec); + TEST_CHECK(!ec); +} + +void test_stat() +{ + error_code ec; + + // test that the modification timestamps + touch_file("__test_timestamp__", 10); + + file_status st1; + stat_file("__test_timestamp__", &st1, ec); + TEST_CHECK(!ec); + + // sleep for 3 seconds and then make sure the difference in timestamp is + // between 2-4 seconds after touching it again + test_sleep(3000); + + touch_file("__test_timestamp__", 10); + + file_status st2; + stat_file("__test_timestamp__", &st2, ec); + TEST_CHECK(!ec); + + int diff = int(st2.mtime - st1.mtime); + fprintf(stderr, "timestamp difference: %d seconds. expected approx. 3 seconds\n" + , diff); + + TEST_CHECK(diff >= 2 && diff <= 4); +} + int test_main() { + test_create_directory(); + test_stat(); + error_code ec; create_directory("file_test_dir", ec);