Skip to content

Commit

Permalink
Fix: Direct and Par renamers (#497)
Browse files Browse the repository at this point in the history
- DirectRename and ParRename now correctly handle nested file paths found in par files
  • Loading branch information
dnzbk authored Feb 7, 2025
1 parent eae035a commit c372bd6
Show file tree
Hide file tree
Showing 13 changed files with 430 additions and 163 deletions.
112 changes: 68 additions & 44 deletions daemon/nntp/ArticleWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* This file is part of nzbget. See <https://nzbget.com>.
*
* Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024 Denis <denis@nzbget.com>
* Copyright (C) 2024-2025 Denis <denis@nzbget.com>
*
* 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
Expand All @@ -20,6 +20,8 @@


#include "nzbget.h"

#include <sstream>
#include "ArticleWriter.h"
#include "DiskState.h"
#include "Options.h"
Expand Down Expand Up @@ -55,7 +57,9 @@ void ArticleWriter::SetWriteBuffer(DiskFile& outFile, int recSize)
void ArticleWriter::Prepare()
{
BuildOutputFilename();
m_resultFilename = m_articleInfo->GetResultFilename();
m_resultFilename = m_articleInfo->GetResultFilename()
? m_articleInfo->GetResultFilename()
: "";
}

bool ArticleWriter::Start(Decoder::EFormat format, const char* filename, int64 fileSize,
Expand Down Expand Up @@ -120,14 +124,14 @@ bool ArticleWriter::Start(Decoder::EFormat format, const char* filename, int64 f

if (!m_articleData.GetData())
{
detail("Article cache is full, using disk for %s", *m_infoName);
detail("Article cache is full, using disk for %s", m_infoName.c_str());
}
}

if (!m_articleData.GetData())
{
bool directWrite = (g_Options->GetDirectWrite() || m_fileInfo->GetForceDirectWrite()) && m_format == Decoder::efYenc;
const char* outFilename = directWrite ? m_outputFilename : m_tempFilename;
const char* outFilename = directWrite ? m_outputFilename.c_str() : m_tempFilename.c_str();
if (!m_outFile.Open(outFilename, directWrite ? DiskFile::omReadWrite : DiskFile::omWrite))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
Expand Down Expand Up @@ -192,8 +196,8 @@ void ArticleWriter::Finish(bool success)

if (!success)
{
FileSystem::DeleteFile(m_tempFilename);
FileSystem::DeleteFile(m_resultFilename);
FileSystem::DeleteFile(m_tempFilename.c_str());
FileSystem::DeleteFile(m_resultFilename.c_str());
return;
}

Expand All @@ -203,13 +207,13 @@ void ArticleWriter::Finish(bool success)
{
if (!directWrite && !m_articleData.GetData())
{
if (!FileSystem::MoveFile(m_tempFilename, m_resultFilename))
if (!FileSystem::MoveFile(m_tempFilename.c_str(), m_resultFilename.c_str()))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not rename file %s to %s: %s", *m_tempFilename, m_resultFilename,
"Could not rename file %s to %s: %s", m_tempFilename.c_str(), m_resultFilename.c_str(),
*FileSystem::GetLastErrorMessage());
}
FileSystem::DeleteFile(m_tempFilename);
FileSystem::DeleteFile(m_tempFilename.c_str());
}

if (m_articleData.GetData())
Expand All @@ -231,10 +235,10 @@ void ArticleWriter::Finish(bool success)
else
{
// rawmode
if (!FileSystem::MoveFile(m_tempFilename, m_resultFilename))
if (!FileSystem::MoveFile(m_tempFilename.c_str(), m_resultFilename.c_str()))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not move file %s to %s: %s", *m_tempFilename, m_resultFilename,
"Could not move file %s to %s: %s", m_tempFilename.c_str(), m_resultFilename.c_str(),
*FileSystem::GetLastErrorMessage());
}
}
Expand All @@ -243,20 +247,20 @@ void ArticleWriter::Finish(bool success)
/* creates output file and subdirectores */
bool ArticleWriter::CreateOutputFile(int64 size)
{
if (FileSystem::FileExists(m_outputFilename))
if (FileSystem::FileExists(m_outputFilename.c_str()))
{
if (FileSystem::FileSize(m_outputFilename) == size)
if (FileSystem::FileSize(m_outputFilename.c_str()) == size)
{
// keep existing old file from previous program session
return true;
}
// delete existing old file from previous program session
FileSystem::DeleteFile(m_outputFilename);
FileSystem::DeleteFile(m_outputFilename.c_str());
}

// ensure the directory exist
BString<1024> destDir;
destDir.Set(m_outputFilename, (int)(FileSystem::BaseFileName(m_outputFilename) - m_outputFilename));
destDir.Set(m_outputFilename.c_str(), (int)(FileSystem::BaseFileName(m_outputFilename.c_str()) - m_outputFilename.c_str()));
CString errmsg;

if (!FileSystem::ForceDirectories(destDir, errmsg))
Expand All @@ -266,10 +270,10 @@ bool ArticleWriter::CreateOutputFile(int64 size)
return false;
}

if (!FileSystem::AllocateFile(m_outputFilename, size, g_Options->GetArticleCache() == 0, errmsg))
if (!FileSystem::AllocateFile(m_outputFilename.c_str(), size, g_Options->GetArticleCache() == 0, errmsg))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not create file %s: %s", *m_outputFilename, *errmsg);
"Could not create file %s: %s", m_outputFilename.c_str(), *errmsg);
return false;
}

Expand All @@ -278,28 +282,45 @@ bool ArticleWriter::CreateOutputFile(int64 size)

void ArticleWriter::BuildOutputFilename()
{
BString<1024> filename("%s%c%i.%03i", g_Options->GetTempDir(), PATH_SEPARATOR,
m_fileInfo->GetId(), m_articleInfo->GetPartNumber());
if (!m_fileInfo || !m_articleInfo || Util::EmptyStr(g_Options->GetTempDir()))
return;

std::stringstream ss;
ss << g_Options->GetTempDir();
ss << PATH_SEPARATOR;
ss << m_fileInfo->GetId();
ss << ".";
ss << std::setfill('0') << std::setw(3) << m_articleInfo->GetPartNumber();

std::string filename = ss.str();

m_articleInfo->SetResultFilename(filename);
m_tempFilename.Format("%s.tmp", *filename);
m_articleInfo->SetResultFilename(filename.c_str());
m_tempFilename = filename + ".tmp";

const std::string& outputFilename = m_fileInfo->GetOutputFilename();
if (g_Options->GetDirectWrite() || m_fileInfo->GetForceDirectWrite())
{
Guard guard = m_fileInfo->GuardOutputFile();

if (m_fileInfo->GetOutputFilename())
if (!outputFilename.empty())
{
filename = m_fileInfo->GetOutputFilename();
filename = outputFilename;
}
else
else if (!Util::EmptyStr(m_fileInfo->GetNzbInfo()->GetDestDir()))
{
filename.Format("%s%c%i.out.tmp", m_fileInfo->GetNzbInfo()->GetDestDir(),
PATH_SEPARATOR, m_fileInfo->GetId());
ss.str("");
ss.clear();

ss << m_fileInfo->GetNzbInfo()->GetDestDir();
ss << PATH_SEPARATOR;
ss << m_fileInfo->GetId();
ss << ".out.tmp";

filename = ss.str();
m_fileInfo->SetOutputFilename(filename);
}

m_outputFilename = *filename;
m_outputFilename = std::move(filename);
}
}

Expand Down Expand Up @@ -376,13 +397,13 @@ void ArticleWriter::CompleteFileParts()
}
else if (directWrite && cached)
{
if (!outfile.Open(m_outputFilename, DiskFile::omReadWrite))
if (!outfile.Open(m_outputFilename.c_str(), DiskFile::omReadWrite))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not open file %s: %s", *m_outputFilename, *FileSystem::GetLastErrorMessage());
"Could not open file %s: %s", m_outputFilename.c_str(), *FileSystem::GetLastErrorMessage());
return;
}
tmpdestfile = *m_outputFilename;
tmpdestfile = m_outputFilename.c_str();
}
else if (g_Options->GetRawArticle())
{
Expand Down Expand Up @@ -501,22 +522,22 @@ void ArticleWriter::CompleteFileParts()

if (directWrite)
{
if (!FileSystem::SameFilename(m_outputFilename, ofn) &&
!FileSystem::MoveFile(m_outputFilename, ofn))
if (!FileSystem::SameFilename(m_outputFilename.c_str(), ofn) &&
!FileSystem::MoveFile(m_outputFilename.c_str(), ofn))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not move file %s to %s: %s", *m_outputFilename, *ofn,
"Could not move file %s to %s: %s", m_outputFilename.c_str(), *ofn,
*FileSystem::GetLastErrorMessage());
}

// if destination directory was changed delete the old directory (if empty)
int len = strlen(nzbDestDir);
if (!(!strncmp(nzbDestDir, m_outputFilename, len) &&
if (!(!strncmp(nzbDestDir, m_outputFilename.c_str(), len) &&
(m_outputFilename[len] == PATH_SEPARATOR || m_outputFilename[len] == ALT_PATH_SEPARATOR)))
{
debug("Checking old dir for: %s", *m_outputFilename);
debug("Checking old dir for: %s", m_outputFilename.c_str());
BString<1024> oldDestDir;
oldDestDir.Set(m_outputFilename, (int)(FileSystem::BaseFileName(m_outputFilename) - m_outputFilename));
oldDestDir.Set(m_outputFilename.c_str(), (int)(FileSystem::BaseFileName(m_outputFilename.c_str()) - m_outputFilename.c_str()));
if (FileSystem::DirEmpty(oldDestDir))
{
debug("Deleting old dir: %s", *oldDestDir);
Expand Down Expand Up @@ -562,10 +583,11 @@ void ArticleWriter::CompleteFileParts()
{
// file was renamed during completion, need to move the file
ofn = FileSystem::MakeUniqueFilename(nzbDestDir, m_fileInfo->GetFilename());
if (!FileSystem::MoveFile(m_fileInfo->GetOutputFilename(), ofn))
const std::string& outputFilename = m_fileInfo->GetOutputFilename();
if (!FileSystem::MoveFile(outputFilename.c_str(), ofn))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not rename file %s to %s: %s", m_fileInfo->GetOutputFilename(),
"Could not rename file %s to %s: %s", outputFilename.c_str(),
*ofn, *FileSystem::GetLastErrorMessage());
}
m_fileInfo->SetOutputFilename(ofn);
Expand All @@ -581,7 +603,7 @@ void ArticleWriter::CompleteFileParts()

void ArticleWriter::FlushCache()
{
detail("Flushing cache for %s", *m_infoName);
detail("Flushing cache for %s", m_infoName.c_str());

bool directWrite = g_Options->GetDirectWrite() && m_fileInfo->GetOutputInitialized();
DiskFile outfile;
Expand Down Expand Up @@ -624,10 +646,11 @@ void ArticleWriter::FlushCache()

if (directWrite && !outfile.Active())
{
if (!outfile.Open(m_fileInfo->GetOutputFilename(), DiskFile::omReadWrite))
const std::string& outputFilename = m_fileInfo->GetOutputFilename();
if (!outfile.Open(outputFilename.c_str(), DiskFile::omReadWrite))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not open file %s: %s", m_fileInfo->GetOutputFilename(),
"Could not open file %s: %s", outputFilename.c_str(),
*FileSystem::GetLastErrorMessage());
// prevent multiple error messages
pa->DiscardSegment();
Expand Down Expand Up @@ -699,7 +722,7 @@ void ArticleWriter::FlushCache()
}

detail("Saved %i articles (%.2f MB) from cache into disk for %s", flushedArticles,
(float)(flushedSize / 1024.0 / 1024.0), *m_infoName);
(float)(flushedSize / 1024.0 / 1024.0), m_infoName.c_str());
}

bool ArticleWriter::MoveCompletedFiles(NzbInfo* nzbInfo, const char* oldDestDir)
Expand Down Expand Up @@ -750,14 +773,15 @@ bool ArticleWriter::MoveCompletedFiles(NzbInfo* nzbInfo, const char* oldDestDir)
break;
}

const std::string& outputFilename = fileInfo->GetOutputFilename();
if (fileInfo->GetActiveDownloads() > 0)
{
Guard guard = fileInfo->GuardOutputFile();
pendingWrites = fileInfo->GetOutputInitialized() && !Util::EmptyStr(fileInfo->GetOutputFilename());
pendingWrites = fileInfo->GetOutputInitialized() && !outputFilename.empty();
}
else
{
pendingWrites = fileInfo->GetOutputInitialized() && !Util::EmptyStr(fileInfo->GetOutputFilename());
pendingWrites = fileInfo->GetOutputInitialized() && !outputFilename.empty();
}
}

Expand Down
13 changes: 7 additions & 6 deletions daemon/nntp/ArticleWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* This file is part of nzbget. See <https://nzbget.com>.
*
* Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024 Denis <denis@nzbget.com>
* Copyright (C) 2024-2025 Denis <denis@nzbget.com>
*
* 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
Expand Down Expand Up @@ -50,7 +50,8 @@ class CachedSegmentData : public SegmentData
class ArticleWriter
{
public:
void SetInfoName(const char* infoName) { m_infoName = infoName; }
void SetInfoName(const char* infoName) { m_infoName = infoName ? infoName : ""; }
void SetInfoName(std::string infoName) { m_infoName = std::move(infoName); }
void SetFileInfo(FileInfo* fileInfo) { m_fileInfo = fileInfo; }
void SetArticleInfo(ArticleInfo* articleInfo) { m_articleInfo = articleInfo; }
void Prepare();
Expand All @@ -67,16 +68,16 @@ class ArticleWriter
FileInfo* m_fileInfo;
ArticleInfo* m_articleInfo;
DiskFile m_outFile;
CString m_tempFilename;
CString m_outputFilename;
const char* m_resultFilename = nullptr;
std::string m_tempFilename;
std::string m_outputFilename;
std::string m_resultFilename;
std::string m_infoName;
Decoder::EFormat m_format = Decoder::efUnknown;
CachedSegmentData m_articleData;
int64 m_articleOffset;
int m_articleSize;
int m_articlePtr;
bool m_duplicate = false;
CString m_infoName;

bool CreateOutputFile(int64 size);
void BuildOutputFilename();
Expand Down
30 changes: 25 additions & 5 deletions daemon/postprocess/ParRenamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,18 +427,38 @@ void ParRenamer::RenameParFile(const char* destDir, const char* filename, const

void ParRenamer::RenameFile(const char* srcFilename, const char* destFileName)
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", FileSystem::BaseFileName(srcFilename), FileSystem::BaseFileName(destFileName));
const auto [oldPath, oldName] = FileSystem::SplitPathAndFilename(srcFilename);
const auto [newPath, newName] = FileSystem::SplitPathAndFilename(destFileName);

PrintMessage(Message::mkInfo, "Renaming %s to %s", oldName.c_str(), newName.c_str());

if (oldPath != newPath)
{
CString errmsg;
if (!FileSystem::ForceDirectories(newPath.c_str(), errmsg))
{
PrintMessage(Message::mkError,
"Failed to rename file %s to %s. Could not create the target directory \"%s\"",
oldName.c_str(), newName.c_str(), newPath.c_str()
);

return;
}
}

if (!FileSystem::MoveFile(srcFilename, destFileName))
{
PrintMessage(Message::mkError, "Could not rename %s to %s: %s", srcFilename, destFileName,
*FileSystem::GetLastErrorMessage());
PrintMessage(Message::mkError,
"Failed to rename file %s to %s: %s",
srcFilename, destFileName, *FileSystem::GetLastErrorMessage()
);
return;
}

m_renamedCount++;
++m_renamedCount;

// notify about new file name
RegisterRenamedFile(FileSystem::BaseFileName(srcFilename), FileSystem::BaseFileName(destFileName));
RegisterRenamedFile(oldName.c_str(), newName.c_str());
}

void ParRenamer::RenameBadParFiles()
Expand Down
Loading

0 comments on commit c372bd6

Please sign in to comment.