2021-10-21 14:29:15 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "QuarantineUtil.hpp"
|
|
|
|
|
|
|
|
#include <Poco/Path.h>
|
|
|
|
#include <Poco/URI.h>
|
|
|
|
#include "ClientSession.hpp"
|
2021-11-18 06:08:14 -06:00
|
|
|
#include "COOLWSD.hpp"
|
2021-10-21 14:29:15 -05:00
|
|
|
#include "DocumentBroker.hpp"
|
|
|
|
|
|
|
|
#include <common/Common.hpp>
|
|
|
|
#include <common/StringVector.hpp>
|
|
|
|
#include <common/Log.hpp>
|
2021-12-14 16:47:44 -06:00
|
|
|
#include <common/JailUtil.hpp>
|
2021-10-21 14:29:15 -05:00
|
|
|
|
|
|
|
namespace Quarantine
|
|
|
|
{
|
2021-11-03 05:32:37 -05:00
|
|
|
bool isQuarantineEnabled()
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-18 06:08:14 -06:00
|
|
|
return COOLWSD::getConfigValue<bool>("quarantine_files[@enable]", false);
|
2021-11-03 05:32:37 -05:00
|
|
|
}
|
2021-11-03 02:08:22 -05:00
|
|
|
|
2021-11-03 05:32:37 -05:00
|
|
|
void createQuarantineMap()
|
|
|
|
{
|
|
|
|
if (!isQuarantineEnabled())
|
2021-11-03 02:08:22 -05:00
|
|
|
return;
|
|
|
|
|
2021-10-21 14:29:15 -05:00
|
|
|
std::vector<std::string> files;
|
2021-11-18 06:08:14 -06:00
|
|
|
Poco::File(COOLWSD::QuarantinePath).list(files);
|
|
|
|
COOLWSD::QuarantineMap.clear();
|
2021-10-21 14:29:15 -05:00
|
|
|
|
|
|
|
std::vector<StringToken> tokens;
|
|
|
|
std::string decoded;
|
|
|
|
|
|
|
|
std::sort(files.begin(), files.end());
|
2022-02-02 17:14:56 -06:00
|
|
|
for (const auto& file : files)
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
|
|
|
|
2022-03-29 20:37:57 -05:00
|
|
|
StringVector::tokenize(file.c_str(), file.size(), '_', tokens);
|
2021-10-21 14:29:15 -05:00
|
|
|
Poco::URI::decode(file.substr(tokens[2]._index), decoded);
|
2021-11-18 06:08:14 -06:00
|
|
|
COOLWSD::QuarantineMap[decoded].emplace_back(COOLWSD::QuarantinePath + file);
|
2021-10-21 14:29:15 -05:00
|
|
|
|
|
|
|
tokens.clear();
|
|
|
|
decoded.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeQuarantine()
|
|
|
|
{
|
2021-11-03 05:32:37 -05:00
|
|
|
if (!isQuarantineEnabled())
|
2021-11-03 02:08:22 -05:00
|
|
|
return;
|
|
|
|
|
2021-11-18 06:08:14 -06:00
|
|
|
FileUtil::removeFile(COOLWSD::QuarantinePath, true);
|
2021-10-21 14:29:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// returns quarentine directory size in bytes
|
|
|
|
// files with hardlink count of more than 1 is not counted
|
|
|
|
// because they are originally stored in jails
|
|
|
|
std::size_t quarantineSize()
|
|
|
|
{
|
2021-11-03 05:32:37 -05:00
|
|
|
if (!isQuarantineEnabled())
|
2021-11-03 02:08:22 -05:00
|
|
|
return 0;
|
2021-11-03 05:32:37 -05:00
|
|
|
|
2021-10-21 14:29:15 -05:00
|
|
|
std::vector<std::string> files;
|
2021-11-18 06:08:14 -06:00
|
|
|
Poco::File(COOLWSD::QuarantinePath).list(files);
|
2021-10-21 14:29:15 -05:00
|
|
|
std::size_t size = 0;
|
2022-02-02 17:14:56 -06:00
|
|
|
for (const auto& file : files)
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-18 06:08:14 -06:00
|
|
|
FileUtil::Stat f(COOLWSD::QuarantinePath + file);
|
2021-10-21 14:29:15 -05:00
|
|
|
|
|
|
|
if (f.hardLinkCount() == 1)
|
|
|
|
size += f.size();
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void makeQuarantineSpace()
|
|
|
|
{
|
2021-11-03 05:32:37 -05:00
|
|
|
if (!isQuarantineEnabled())
|
2021-10-21 14:29:15 -05:00
|
|
|
return;
|
|
|
|
|
2021-11-18 06:08:14 -06:00
|
|
|
std::size_t sizeLimit = COOLWSD::getConfigValue<std::size_t>("quarantine_files.limit_dir_size_mb", 0)*1024*1024;
|
2021-11-03 02:08:22 -05:00
|
|
|
|
2021-10-21 14:29:15 -05:00
|
|
|
std::vector<std::string> files;
|
2021-11-18 06:08:14 -06:00
|
|
|
Poco::File(COOLWSD::QuarantinePath).list(files);
|
2021-10-21 14:29:15 -05:00
|
|
|
|
|
|
|
std::sort(files.begin(), files.end());
|
|
|
|
|
2021-11-18 06:08:14 -06:00
|
|
|
std::size_t timeLimit = COOLWSD::getConfigValue<std::size_t>("quarantine_files.expiry_min", 30);
|
2021-11-10 14:24:01 -06:00
|
|
|
const auto timeNow = std::chrono::system_clock::now();
|
|
|
|
const auto ts = std::chrono::duration_cast<std::chrono::seconds>(timeNow.time_since_epoch()).count();
|
|
|
|
|
2021-10-21 14:29:15 -05:00
|
|
|
std::size_t currentSize = quarantineSize();
|
2021-11-10 14:24:01 -06:00
|
|
|
auto index = files.begin();
|
|
|
|
while (index != files.end() && !files.empty())
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-18 06:08:14 -06:00
|
|
|
FileUtil::Stat file(COOLWSD::QuarantinePath + *index);
|
2021-11-10 14:24:01 -06:00
|
|
|
const auto modifyTime = std::chrono::duration_cast<std::chrono::seconds>(file.modifiedTimepoint().time_since_epoch()).count();
|
|
|
|
bool isExpired = static_cast<std::size_t>(ts - modifyTime) > timeLimit * 60;
|
|
|
|
|
|
|
|
if ( (file.hardLinkCount() == 1) && (isExpired || (currentSize >= sizeLimit)) )
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-10 14:24:01 -06:00
|
|
|
currentSize -= file.size();
|
2021-11-18 06:08:14 -06:00
|
|
|
FileUtil::removeFile(COOLWSD::QuarantinePath + *index, true);
|
2021-11-10 14:24:01 -06:00
|
|
|
files.erase(index);
|
2021-10-21 14:29:15 -05:00
|
|
|
}
|
2021-11-10 14:24:01 -06:00
|
|
|
else
|
|
|
|
index++;
|
2021-10-21 14:29:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:14:56 -06:00
|
|
|
void clearOldQuarantineVersions(const std::string& Wopiscr)
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-03 05:32:37 -05:00
|
|
|
if (!isQuarantineEnabled())
|
2021-11-03 02:08:22 -05:00
|
|
|
return;
|
|
|
|
|
2021-11-18 06:08:14 -06:00
|
|
|
std::size_t maxVersionCount = COOLWSD::getConfigValue<std::size_t>("quarantine_files.max_versions_to_maintain", 2);
|
2021-10-21 14:29:15 -05:00
|
|
|
std::string decoded;
|
|
|
|
Poco::URI::decode(Wopiscr, decoded);
|
2021-11-18 06:08:14 -06:00
|
|
|
while (COOLWSD::QuarantineMap[decoded].size() > maxVersionCount)
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-18 06:08:14 -06:00
|
|
|
FileUtil::removeFile(COOLWSD::QuarantineMap[decoded][0]);
|
|
|
|
COOLWSD::QuarantineMap[decoded].erase(COOLWSD::QuarantineMap[decoded].begin());
|
2021-10-21 14:29:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:14:56 -06:00
|
|
|
bool quarantineFile(DocumentBroker* docBroker, const std::string& docName)
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-03 05:32:37 -05:00
|
|
|
if (!isQuarantineEnabled())
|
2021-10-21 14:29:15 -05:00
|
|
|
return false;
|
|
|
|
|
|
|
|
std::string docKey;
|
|
|
|
Poco::URI::encode(docBroker->getDocKey(), "?#/", docKey);
|
|
|
|
|
|
|
|
const auto timeNow = std::chrono::system_clock::now();
|
|
|
|
const std::string ts = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(timeNow.time_since_epoch()).count());
|
|
|
|
|
2021-12-14 16:47:44 -06:00
|
|
|
std::string sourcefilePath;
|
|
|
|
if(JailUtil::isBindMountingEnabled())
|
|
|
|
{
|
2022-02-02 17:14:56 -06:00
|
|
|
sourcefilePath = COOLWSD::ChildRoot + "tmp/cool-" + docBroker->getJailId() +
|
|
|
|
"/user/docs/" + docBroker->getJailId() + '/' + docName;
|
2021-12-14 16:47:44 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-02 17:14:56 -06:00
|
|
|
sourcefilePath = COOLWSD::ChildRoot + docBroker->getJailId() + "/tmp/user/docs/" +
|
|
|
|
docBroker->getJailId() + '/' + docName;
|
2021-12-14 16:47:44 -06:00
|
|
|
}
|
2021-10-21 14:29:15 -05:00
|
|
|
|
2021-11-28 10:53:45 -06:00
|
|
|
std::string linkedFileName = ts + '_' + std::to_string(docBroker->getPid()) + '_' + docKey + '_' + docName;
|
2021-11-18 06:08:14 -06:00
|
|
|
std::string linkedFilePath = COOLWSD::QuarantinePath + linkedFileName;
|
2021-10-21 14:29:15 -05:00
|
|
|
|
2021-11-23 08:13:59 -06:00
|
|
|
auto& fileList = COOLWSD::QuarantineMap[docBroker->getDocKey()];
|
|
|
|
if(!fileList.empty())
|
|
|
|
{
|
|
|
|
FileUtil::Stat sourceStat(sourcefilePath);
|
|
|
|
FileUtil::Stat lastFileStat(fileList[fileList.size()-1]);
|
|
|
|
|
|
|
|
if(lastFileStat.inodeNumber() == sourceStat.inodeNumber())
|
|
|
|
{
|
|
|
|
LOG_INF("Quarantining of file " << sourcefilePath << " to " << linkedFilePath
|
|
|
|
<< " is skipped because this file version is already quarantined.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 14:29:15 -05:00
|
|
|
|
|
|
|
makeQuarantineSpace();
|
|
|
|
|
|
|
|
int result_link = link(sourcefilePath.c_str(),linkedFilePath.c_str());
|
|
|
|
|
2021-11-08 03:09:54 -06:00
|
|
|
if (result_link == 0)
|
2021-10-21 14:29:15 -05:00
|
|
|
{
|
2021-11-23 08:13:59 -06:00
|
|
|
fileList.emplace_back(linkedFilePath);
|
2021-10-21 14:29:15 -05:00
|
|
|
clearOldQuarantineVersions(docKey);
|
|
|
|
makeQuarantineSpace();
|
|
|
|
|
2021-11-08 03:09:54 -06:00
|
|
|
LOG_INF("Quarantined " << sourcefilePath << " to " << linkedFilePath);
|
2021-10-21 14:29:15 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-08 03:09:54 -06:00
|
|
|
int saved_errno = errno;
|
|
|
|
LOG_ERR("Quarantining of file " << sourcefilePath << " to " << linkedFilePath << " failed: "
|
|
|
|
<< Util::symbolicErrno(saved_errno) << ": " << std::strerror(saved_errno));
|
2021-10-21 14:29:15 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-04 08:09:27 -05:00
|
|
|
}
|