libreoffice-online/common/FileUtil.cpp
Pranav Kant f63858433b loplugin:includeform
Change-Id: Ib62a7aa61062f00698aa3e8a144438de5c57e53d
2017-12-20 21:21:05 +05:30

259 lines
7.3 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* 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 <config.h>
#include "FileUtil.hpp"
#include <ftw.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <mutex>
#include <string>
#include <Poco/TemporaryFile.h>
#include "Log.hpp"
#include "Util.hpp"
#include "Unit.hpp"
namespace
{
void alertAllUsersAndLog(const std::string& message, const std::string& cmd, const std::string& kind)
{
LOG_ERR(message);
Util::alertAllUsers(cmd, kind);
}
}
namespace FileUtil
{
std::string createRandomDir(const std::string& path)
{
const auto name = Util::rng::getFilename(64);
Poco::File(Poco::Path(path, name)).createDirectories();
return name;
}
std::string getTempFilePath(const std::string& srcDir, const std::string& srcFilename, const std::string& dstFilenamePrefix)
{
const std::string srcPath = srcDir + '/' + srcFilename;
const std::string dstFilename = dstFilenamePrefix + Util::encodeId(Util::rng::getNext()) + '_' + srcFilename;
const std::string dstPath = Poco::Path(Poco::Path::temp(), dstFilename).toString();
Poco::File(srcPath).copyTo(dstPath);
Poco::TemporaryFile::registerForDeletion(dstPath);
return dstPath;
}
std::string getTempFilePath(const std::string& srcDir, const std::string& srcFilename)
{
return getTempFilePath(srcDir, srcFilename, "");
}
bool saveDataToFileSafely(const std::string& fileName, const char *data, size_t size)
{
const auto tempFileName = fileName + ".temp";
std::fstream outStream(tempFileName, std::ios::out);
// If we can't create the file properly, just remove it
if (!outStream.good())
{
alertAllUsersAndLog("Creating " + tempFileName + " failed, disk full?", "internal", "diskfull");
// Try removing both just in case
std::remove(tempFileName.c_str());
std::remove(fileName.c_str());
return false;
}
else
{
outStream.write(data, size);
if (!outStream.good())
{
alertAllUsersAndLog("Writing to " + tempFileName + " failed, disk full?", "internal", "diskfull");
outStream.close();
std::remove(tempFileName.c_str());
std::remove(fileName.c_str());
return false;
}
else
{
outStream.close();
if (!outStream.good())
{
alertAllUsersAndLog("Closing " + tempFileName + " failed, disk full?", "internal", "diskfull");
std::remove(tempFileName.c_str());
std::remove(fileName.c_str());
return false;
}
else
{
// Everything OK, rename the file to its proper name
if (std::rename(tempFileName.c_str(), fileName.c_str()) == 0)
{
LOG_DBG("Renaming " << tempFileName << " to " << fileName << " OK.");
return true;
}
else
{
alertAllUsersAndLog("Renaming " + tempFileName + " to " + fileName + " failed, disk full?", "internal", "diskfull");
std::remove(tempFileName.c_str());
std::remove(fileName.c_str());
return false;
}
}
}
}
}
static int nftw_cb(const char *fpath, const struct stat*, int type, struct FTW*)
{
if (type == FTW_DP)
{
rmdir(fpath);
}
else if (type == FTW_F || type == FTW_SL)
{
unlink(fpath);
}
// Always continue even when things go wrong.
return 0;
}
void removeFile(const std::string& path, const bool recursive)
{
try
{
struct stat sb;
if (!recursive || stat(path.c_str(), &sb) == -1 || S_ISREG(sb.st_mode))
{
// Non-recursive directories, and files.
Poco::File(path).remove(recursive);
}
else
{
// Directories only.
nftw(path.c_str(), nftw_cb, 128, FTW_DEPTH | FTW_PHYS);
}
}
catch (const std::exception&)
{
// Already removed or we don't care about failures.
}
}
} // namespace FileUtil
namespace
{
struct fs
{
fs(const std::string& p, dev_t d)
: path(p), dev(d)
{
}
std::string path;
dev_t dev;
};
struct fsComparator
{
bool operator() (const fs& lhs, const fs& rhs) const
{
return (lhs.dev < rhs.dev);
}
};
static std::mutex fsmutex;
static std::set<fs, fsComparator> filesystems;
} // anonymous namespace
namespace FileUtil
{
void registerFileSystemForDiskSpaceChecks(const std::string& path)
{
std::lock_guard<std::mutex> lock(fsmutex);
if (!path.empty())
{
std::string dirPath = path;
std::string::size_type lastSlash = dirPath.rfind('/');
assert(lastSlash != std::string::npos);
dirPath = dirPath.substr(0, lastSlash + 1) + '.';
struct stat s;
if (stat(dirPath.c_str(), &s) == 0)
{
filesystems.insert(fs(dirPath, s.st_dev));
}
}
}
std::string checkDiskSpaceOnRegisteredFileSystems(const bool cacheLastCheck)
{
std::lock_guard<std::mutex> lock(fsmutex);
static std::chrono::steady_clock::time_point lastCheck;
std::chrono::steady_clock::time_point now(std::chrono::steady_clock::now());
// Don't check more often that once a minute
if (std::chrono::duration_cast<std::chrono::seconds>(now - lastCheck).count() < 60)
return std::string();
if (cacheLastCheck)
lastCheck = now;
for (auto& i: filesystems)
{
if (!checkDiskSpace(i.path))
{
return i.path;
}
}
return std::string();
}
bool checkDiskSpace(const std::string& path)
{
assert(!path.empty());
bool hookResult;
if (UnitBase::get().filterCheckDiskSpace(path, hookResult))
return hookResult;
struct statfs sfs;
if (statfs(path.c_str(), &sfs) == -1)
return true;
// we should be able to run just OK with 5GB
constexpr int64_t ENOUGH_SPACE = int64_t(5)*1024*1024*1024;
if (static_cast<int64_t>(sfs.f_bavail) * sfs.f_bsize > ENOUGH_SPACE)
return true;
if (static_cast<double>(sfs.f_bavail) / sfs.f_blocks <= 0.05)
return false;
return true;
}
} // namespace FileUtil
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */