From 9bd25ef1b4f8fe6494d80082c7688c98c7c10a1f Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:56:40 +0100 Subject: [PATCH] Fixed empty-file-issue on Windows --- fineftp-server/src/ftp_session.cpp | 6 ++ fineftp-server/src/win32/file_man.cpp | 105 ++++++++++++++++---------- fineftp-server/src/win32/file_man.h | 8 +- fineftp-server/version.cmake | 2 +- 4 files changed, 75 insertions(+), 46 deletions(-) diff --git a/fineftp-server/src/ftp_session.cpp b/fineftp-server/src/ftp_session.cpp index eda76dc..3be83a9 100755 --- a/fineftp-server/src/ftp_session.cpp +++ b/fineftp-server/src/ftp_session.cpp @@ -1289,6 +1289,12 @@ namespace fineftp { me->sendFtpMessage(FtpReplyCode::CLOSING_DATA_CONNECTION, "Done"); } + else if (file->data() == nullptr) + { + // Error that should never happen. If it does, it's a bug in the server. + // Usually, if the data is null, the file size should be 0. + me->sendFtpMessage(FtpReplyCode::TRANSFER_ABORTED, "Data transfer aborted: File data is null"); + } else { me->data_socket_weakptr_ = data_socket; diff --git a/fineftp-server/src/win32/file_man.cpp b/fineftp-server/src/win32/file_man.cpp index cded3b1..ce53c29 100644 --- a/fineftp-server/src/win32/file_man.cpp +++ b/fineftp-server/src/win32/file_man.cpp @@ -21,24 +21,26 @@ std::map> files; ReadableFile::~ReadableFile() { - if (INVALID_HANDLE_VALUE != handle_) - { + if (data_ != nullptr) ::UnmapViewOfFile(data_); + + if (map_handle_ != INVALID_HANDLE_VALUE) ::CloseHandle(map_handle_); + + if (handle_ != INVALID_HANDLE_VALUE) ::CloseHandle(handle_); - } const std::lock_guard lock{guard}; - if (!pth_.empty()) + if (!path_.empty()) { - (void)files.erase(pth_); + (void)files.erase(path_); } } -std::shared_ptr ReadableFile::get(const Str& pth) +std::shared_ptr ReadableFile::get(const Str& file_path) { std::basic_ostringstream os; - for (auto c : pth) + for (auto c : file_path) { if (c == '/') { @@ -50,62 +52,83 @@ std::shared_ptr ReadableFile::get(const Str& pth) } } - auto&& s = os.str(); + auto&& file_path_fixed_separators = os.str(); // See if we already have this file mapped const std::lock_guard lock{guard}; - auto fit = files.find(s); - if (files.end() != fit) + auto existing_files_it = files.find(file_path_fixed_separators); + if (files.end() != existing_files_it) { - auto p = fit->second.lock(); - if (p) + auto readable_file_ptr = existing_files_it->second.lock(); + if (readable_file_ptr) { - return p; + return readable_file_ptr; } } #if !defined(__GNUG__) - HANDLE handle = - ::CreateFileW(s.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + HANDLE file_handle = + ::CreateFileW(file_path_fixed_separators.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); #else - auto handle = - ::CreateFileA(s.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + auto file_handle = + ::CreateFileA(file_path_fixed_separators.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); #endif - if (INVALID_HANDLE_VALUE == handle) + if (INVALID_HANDLE_VALUE == file_handle) { return {}; } - LARGE_INTEGER sz; - if (0 == ::GetFileSizeEx(handle, &sz)) + // Get the file size by Using GetFileInformationByHandle + BY_HANDLE_FILE_INFORMATION file_info; + if (0 == ::GetFileInformationByHandle(file_handle, &file_info)) { - ::CloseHandle(handle); + ::CloseHandle(file_handle); return {}; } - - auto* map_handle = ::CreateFileMapping(handle, nullptr, PAGE_READONLY, sz.HighPart, sz.LowPart, nullptr); - if ((map_handle == INVALID_HANDLE_VALUE) || (map_handle == nullptr)) - { - ::CloseHandle(handle); - return {}; + LARGE_INTEGER file_size; + file_size.LowPart = file_info.nFileSizeLow; + file_size.HighPart = file_info.nFileSizeHigh; + + // Create new ReadableFile ptr + std::shared_ptr readable_file_ptr(new ReadableFile{}); + + if (file_size.QuadPart == 0) + { + // Handle zero-size files + readable_file_ptr->path_ = std::move(file_path_fixed_separators); + readable_file_ptr->size_ = file_size.QuadPart; + readable_file_ptr->data_ = static_cast(nullptr); + readable_file_ptr->handle_ = file_handle; + readable_file_ptr->map_handle_ = INVALID_HANDLE_VALUE; } - - auto* map_start = ::MapViewOfFile(map_handle, FILE_MAP_READ, 0, 0, sz.QuadPart); - if (nullptr == map_start) + else { - ::CloseHandle(map_handle); - ::CloseHandle(handle); - return {}; + // Handle non-zero-size files + auto* map_handle = ::CreateFileMapping(file_handle, nullptr, PAGE_READONLY, file_size.HighPart, file_size.LowPart, nullptr); + if ((map_handle == INVALID_HANDLE_VALUE) || (map_handle == nullptr)) + { + ::CloseHandle(file_handle); + return {}; + } + + auto* map_start = ::MapViewOfFile(map_handle, FILE_MAP_READ, 0, 0, file_size.QuadPart); + if (nullptr == map_start) + { + ::CloseHandle(map_handle); + ::CloseHandle(file_handle); + return {}; + } + + readable_file_ptr->path_ = std::move(file_path_fixed_separators); + readable_file_ptr->size_ = file_size.QuadPart; + readable_file_ptr->data_ = static_cast(map_start); + readable_file_ptr->handle_ = file_handle; + readable_file_ptr->map_handle_ = map_handle; } - std::shared_ptr p{new ReadableFile{}}; - p->pth_ = std::move(s); - p->size_ = sz.QuadPart; - p->data_ = static_cast(map_start); - p->handle_ = handle; - p->map_handle_ = map_handle; - files[p->pth_] = p; - return p; + // Add readable_file_ptr to the map and return it to the user + files[readable_file_ptr->path_] = readable_file_ptr; + return readable_file_ptr; } WriteableFile::WriteableFile(const std::string& filename, std::ios::openmode mode) diff --git a/fineftp-server/src/win32/file_man.h b/fineftp-server/src/win32/file_man.h index 7b8ad12..8fc12f3 100644 --- a/fineftp-server/src/win32/file_man.h +++ b/fineftp-server/src/win32/file_man.h @@ -33,10 +33,10 @@ class ReadableFile /// Retrieves the file at the specified path. /// - /// @param pth The path of the file. + /// @param file_path The path of the file. /// /// @param The requested file or nullptr if the file could not be retrieved. - static std::shared_ptr get(const Str& pth); + static std::shared_ptr get(const Str& file_path); /// Returns the size of the file. /// @@ -56,7 +56,7 @@ class ReadableFile private: ReadableFile() = default; - Str pth_ = {}; + Str path_ = {}; std::size_t size_ = {}; std::uint8_t* data_ = {}; HANDLE handle_ = INVALID_HANDLE_VALUE; @@ -105,7 +105,7 @@ inline const std::uint8_t* ReadableFile::data() const inline const ReadableFile::Str& ReadableFile::path() const { - return pth_; + return path_; } inline bool WriteableFile::good() const diff --git a/fineftp-server/version.cmake b/fineftp-server/version.cmake index 7c441ad..730153e 100644 --- a/fineftp-server/version.cmake +++ b/fineftp-server/version.cmake @@ -1,3 +1,3 @@ set(FINEFTP_SERVER_VERSION_MAJOR 1) set(FINEFTP_SERVER_VERSION_MINOR 4) -set(FINEFTP_SERVER_VERSION_PATCH 1) +set(FINEFTP_SERVER_VERSION_PATCH 2)