Skip to content

Commit

Permalink
Fixed empty-file-issue on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianReimold committed Mar 1, 2024
1 parent a858f5e commit 9bd25ef
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 46 deletions.
6 changes: 6 additions & 0 deletions fineftp-server/src/ftp_session.cpp
Expand Up @@ -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;
Expand Down
105 changes: 64 additions & 41 deletions fineftp-server/src/win32/file_man.cpp
Expand Up @@ -21,24 +21,26 @@ std::map<ReadableFile::Str, std::weak_ptr<ReadableFile>> 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<std::mutex> lock{guard};
if (!pth_.empty())
if (!path_.empty())
{
(void)files.erase(pth_);
(void)files.erase(path_);
}
}

std::shared_ptr<ReadableFile> ReadableFile::get(const Str& pth)
std::shared_ptr<ReadableFile> ReadableFile::get(const Str& file_path)
{
std::basic_ostringstream<Str::value_type> os;
for (auto c : pth)
for (auto c : file_path)
{
if (c == '/')
{
Expand All @@ -50,62 +52,83 @@ std::shared_ptr<ReadableFile> 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<std::mutex> 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<ReadableFile> 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<uint8_t*>(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<uint8_t*>(map_start);
readable_file_ptr->handle_ = file_handle;
readable_file_ptr->map_handle_ = map_handle;
}

std::shared_ptr<ReadableFile> p{new ReadableFile{}};
p->pth_ = std::move(s);
p->size_ = sz.QuadPart;
p->data_ = static_cast<uint8_t*>(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)
Expand Down
8 changes: 4 additions & 4 deletions fineftp-server/src/win32/file_man.h
Expand Up @@ -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<ReadableFile> get(const Str& pth);
static std::shared_ptr<ReadableFile> get(const Str& file_path);

/// Returns the size of the file.
///
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion 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)

0 comments on commit 9bd25ef

Please sign in to comment.