1
0
Fork 0
arangodb/lib/Basics/files.cpp

2646 lines
72 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
////////////////////////////////////////////////////////////////////////////////
#include "files.h"
#ifdef _WIN32
#include <Shlwapi.h>
#include <tchar.h>
#include <chrono>
#include <thread>
#endif
#include "zlib.h"
#include <algorithm>
#include <limits.h>
#include "Basics/Exceptions.h"
#include "Basics/FileUtils.h"
#include "Basics/ReadLocker.h"
#include "Basics/ReadWriteLock.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringUtils.h"
#include "Basics/Thread.h"
#include "Basics/WriteLocker.h"
#include "Basics/conversions.h"
#include "Basics/directories.h"
#include "Basics/hashes.h"
#include "Basics/tri-strings.h"
#include "Basics/Utf8Helper.h"
#include "Basics/ScopeGuard.h"
#include "Logger/Logger.h"
#include "Random/RandomGenerator.h"
#ifdef TRI_HAVE_DIRENT_H
#include <dirent.h>
#endif
#ifdef TRI_HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef TRI_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef TRI_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef USE_ENTERPRISE
#include "Enterprise/Encryption/EncryptionFeature.h"
#endif
using namespace arangodb::basics;
using namespace arangodb;
namespace {
/// @brief names of blocking files
#ifdef TRI_HAVE_WIN32_FILE_LOCKING
std::vector<std::pair<std::string, HANDLE>> OpenedFiles;
#else
std::vector<std::pair<std::string, int>> OpenedFiles;
#endif
/// @brief lock for protected access to vector OpenedFiles
static basics::ReadWriteLock OpenedFilesLock;
/// @brief struct to remove all opened lockfiles on shutdown
struct LockfileRemover {
LockfileRemover() {}
~LockfileRemover() {
WRITE_LOCKER(locker, OpenedFilesLock);
for (auto const& it : OpenedFiles) {
#ifdef TRI_HAVE_WIN32_FILE_LOCKING
HANDLE fd = it.second;
CloseHandle(fd);
#else
int fd = it.second;
TRI_CLOSE(fd);
#endif
TRI_UnlinkFile(it.first.c_str());
}
OpenedFiles.clear();
}
};
/// @brief this instance will remove all lockfiles in its dtor
static LockfileRemover remover;
} // namespace
/// @brief read buffer size (used for bulk file reading)
#define READBUFFER_SIZE 8192
/// @brief a static buffer of zeros, used to initialize files
static char NullBuffer[4096];
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the character is a directory separator
////////////////////////////////////////////////////////////////////////////////
static constexpr bool IsDirSeparatorChar(char c) {
// the check for c != TRI_DIR_SEPARATOR_CHAR is required
// for g++6. otherwise it will warn about equal expressions
// in the two branches
return (c == TRI_DIR_SEPARATOR_CHAR || (TRI_DIR_SEPARATOR_CHAR != '/' && c == '/'));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief normalizes path
///
/// @note path will be modified in-place
////////////////////////////////////////////////////////////////////////////////
static void NormalizePath(std::string& path) {
for (auto& it : path) {
if (IsDirSeparatorChar(it)) {
it = TRI_DIR_SEPARATOR_CHAR;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief lists the directory tree
////////////////////////////////////////////////////////////////////////////////
static void ListTreeRecursively(char const* full, char const* path,
std::vector<std::string>& result) {
std::vector<std::string> dirs = TRI_FilesDirectory(full);
for (size_t j = 0; j < 2; ++j) {
for (auto const& filename : dirs) {
std::string const newFull =
arangodb::basics::FileUtils::buildFilename(full, filename);
std::string newPath;
if (*path) {
newPath = arangodb::basics::FileUtils::buildFilename(path, filename);
} else {
newPath = filename;
}
if (j == 0) {
if (TRI_IsDirectory(newFull.c_str())) {
result.push_back(newPath);
if (!TRI_IsSymbolicLink(newFull.c_str())) {
ListTreeRecursively(newFull.c_str(), newPath.c_str(), result);
}
}
} else {
if (!TRI_IsDirectory(newFull.c_str())) {
result.push_back(newPath);
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief locates a environment given configuration directory
////////////////////////////////////////////////////////////////////////////////
static std::string LocateConfigDirectoryEnv() {
std::string r;
if (!TRI_GETENV("ARANGODB_CONFIG_PATH", r)) {
return std::string();
}
NormalizePath(r);
while (!r.empty() && IsDirSeparatorChar(r[r.size() - 1])) {
r.pop_back();
}
r.push_back(TRI_DIR_SEPARATOR_CHAR);
return r;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the size of a file
///
/// Will return a negative error number on error, typically -1
////////////////////////////////////////////////////////////////////////////////
int64_t TRI_SizeFile(char const* path) {
TRI_stat_t stbuf;
int res = TRI_STAT(path, &stbuf);
if (res != 0) {
// an error occurred
return (int64_t)res;
}
return (int64_t)stbuf.st_size;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if file or directory is writable
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
bool TRI_IsWritable(char const* path) {
// ..........................................................................
// will attempt the following:
// if path is a directory, then attempt to create temporary file
// if path is a file, then attempt to open it in read/write mode
// ..........................................................................
// #error "TRI_IsWritable needs to be implemented for Windows"
// TODO: implementation for seems to be non-trivial
return true;
}
#else
bool TRI_IsWritable(char const* path) {
// we can use POSIX access() from unistd.h to check for write permissions
return (access(path, W_OK) == 0);
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if path is a directory
////////////////////////////////////////////////////////////////////////////////
bool TRI_IsDirectory(char const* path) {
TRI_stat_t stbuf;
int res;
res = TRI_STAT(path, &stbuf);
return (res == 0) && ((stbuf.st_mode & S_IFMT) == S_IFDIR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if path is a regular file
////////////////////////////////////////////////////////////////////////////////
bool TRI_IsRegularFile(char const* path) {
TRI_stat_t stbuf;
int res;
res = TRI_STAT(path, &stbuf);
return (res == 0) && ((stbuf.st_mode & S_IFMT) == S_IFREG);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if path is a symbolic link
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
bool TRI_IsSymbolicLink(char const* path) {
// TODO : check if a file is a symbolic link - without opening the file
return false;
}
#else
bool TRI_IsSymbolicLink(char const* path) {
struct stat stbuf;
int res;
res = lstat(path, &stbuf);
return (res == 0) && ((stbuf.st_mode & S_IFMT) == S_IFLNK);
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a symbolic link
////////////////////////////////////////////////////////////////////////////////
bool TRI_CreateSymbolicLink(std::string const& target,
std::string const& linkpath, std::string& error) {
#ifdef _WIN32
bool created =
::CreateSymbolicLinkW(toWString(linkpath).data(), toWString(target).data(), 0x0);
if (!created) {
auto rv = translateWindowsError(::GetLastError());
error = "failed to create a symlink " + target + " -> " + linkpath + " - " + rv.errorMessage();
}
return created;
#else
int res = symlink(target.c_str(), linkpath.c_str());
if (res < 0) {
error = "failed to create a symlink " + target + " -> " + linkpath + " - " +
strerror(errno);
}
return res == 0;
#endif
}
////////////////////////////////////////////////////////////////////////////////
/// @brief resolves a symbolic link
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
std::string TRI_ResolveSymbolicLink(std::string path, bool& hadError, bool recursive) {
return path;
}
std::string TRI_ResolveSymbolicLink(std::string path, bool recursive) {
return path;
}
#else
namespace {
static bool IsSymbolicLink(char const* path, struct stat* stbuf) {
int res = lstat(path, stbuf);
return (res == 0) && ((stbuf->st_mode & S_IFMT) == S_IFLNK);
}
} // namespace
std::string TRI_ResolveSymbolicLink(std::string path, bool& hadError, bool recursive) {
struct stat sb;
while (IsSymbolicLink(path.data(), &sb)) {
// if file is a symlink this contains the targets file name length
// instead of the file size
ssize_t buffsize = sb.st_size + 1;
// resolve symlinks
std::vector<char> buff;
buff.resize(buffsize);
auto written = ::readlink(path.c_str(), buff.data(), buff.size());
if (written) {
path = std::string(buff.data(), buff.size());
} else {
// error occured while resolving
hadError = true;
break;
}
if (!recursive) {
break;
}
}
return path;
}
std::string TRI_ResolveSymbolicLink(std::string path, bool recursive) {
bool ignore;
return TRI_ResolveSymbolicLink(std::move(path), ignore, recursive);
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if file or directory exists
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
bool TRI_ExistsFile(char const* path) {
if (path == nullptr) {
return false;
}
TRI_stat_t stbuf;
int res;
size_t len = strlen(path);
// path must not end with a \ on Windows, other stat() will return -1
if (len > 0 && path[len - 1] == TRI_DIR_SEPARATOR_CHAR) {
std::string copy(path);
// remove trailing slash
while (!copy.empty() && IsDirSeparatorChar(copy[copy.size() - 1])) {
copy.pop_back();
}
res = TRI_STAT(copy.c_str(), &stbuf);
} else {
res = TRI_STAT(path, &stbuf);
}
return res == 0;
}
#else
bool TRI_ExistsFile(char const* path) {
if (path == nullptr) {
return false;
}
struct stat stbuf;
int res = TRI_STAT(path, &stbuf);
return res == 0;
}
#endif
int TRI_ChMod(char const* path, long mode, std::string& err) {
int res;
#ifdef _WIN32
res = _wchmod(toWString(path).data(), static_cast<int>(mode));
#else
res = chmod(path, mode);
#endif
if (res != 0) {
err = "error setting desired mode " + std::to_string(mode) + " for file " +
path + ": " + strerror(errno);
return errno;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the last modification date of a file
////////////////////////////////////////////////////////////////////////////////
int TRI_MTimeFile(char const* path, int64_t* mtime) {
TRI_stat_t stbuf;
int res = TRI_STAT(path, &stbuf);
if (res == 0) {
*mtime = static_cast<int64_t>(stbuf.st_mtime);
return TRI_ERROR_NO_ERROR;
}
res = errno;
if (res == ENOENT) {
return TRI_ERROR_FILE_NOT_FOUND;
}
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return TRI_errno();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a directory, recursively
////////////////////////////////////////////////////////////////////////////////
int TRI_CreateRecursiveDirectory(char const* path, long& systemError,
std::string& systemErrorStr) {
char const* p;
char const* s;
int res = TRI_ERROR_NO_ERROR;
std::string copy = std::string(path);
p = s = copy.data();
while (*p != '\0') {
if (*p == TRI_DIR_SEPARATOR_CHAR) {
if (p - s > 0) {
#ifdef _WIN32
// Don't try to create the drive letter as directory:
if ((p - copy.data() == 2) && (s[1] == ':')) {
s = p + 1;
continue;
}
#endif
// *p = '\0';
copy[p - copy.data()] = '\0';
res = TRI_CreateDirectory(copy.c_str(), systemError, systemErrorStr);
if (res == TRI_ERROR_FILE_EXISTS || res == TRI_ERROR_NO_ERROR) {
systemErrorStr.clear();
res = TRI_ERROR_NO_ERROR;
// *p = TRI_DIR_SEPARATOR_CHAR;
copy[p - copy.data()] = TRI_DIR_SEPARATOR_CHAR;
s = p + 1;
} else {
break;
}
}
}
p++;
}
if ((res == TRI_ERROR_FILE_EXISTS || res == TRI_ERROR_NO_ERROR) && (p - s > 0)) {
res = TRI_CreateDirectory(copy.c_str(), systemError, systemErrorStr);
if (res == TRI_ERROR_FILE_EXISTS) {
systemErrorStr.clear();
res = TRI_ERROR_NO_ERROR;
}
}
TRI_ASSERT(res != TRI_ERROR_FILE_EXISTS);
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a directory
////////////////////////////////////////////////////////////////////////////////
int TRI_CreateDirectory(char const* path, long& systemError, std::string& systemErrorStr) {
TRI_ERRORBUF;
// reset error flag
TRI_set_errno(TRI_ERROR_NO_ERROR);
int res = TRI_MKDIR(path, 0777);
if (res == TRI_ERROR_NO_ERROR) {
return res;
}
// check errno
TRI_SYSTEM_ERROR();
res = errno;
if (res != TRI_ERROR_NO_ERROR) {
systemErrorStr = std::string("failed to create directory '") + path + "': " + TRI_GET_ERRORBUF;
systemError = res;
if (res == ENOENT) {
return TRI_ERROR_FILE_NOT_FOUND;
}
if (res == EEXIST) {
return TRI_ERROR_FILE_EXISTS;
}
if (res == EPERM) {
return TRI_ERROR_FORBIDDEN;
}
}
return TRI_ERROR_SYS_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes an empty directory
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveEmptyDirectory(char const* filename) {
int res = TRI_RMDIR(filename);
if (res != 0) {
LOG_TOPIC("a6c6a", TRACE, arangodb::Logger::FIXME)
<< "cannot remove directory '" << filename << "': " << TRI_LAST_ERROR_STR;
return TRI_set_errno(TRI_ERROR_SYS_ERROR);
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes a directory recursively
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveDirectory(char const* filename) {
if (TRI_IsSymbolicLink(filename)) {
LOG_TOPIC("abdb4", TRACE, arangodb::Logger::FIXME)
<< "removing symbolic link '" << filename << "'";
return TRI_UnlinkFile(filename);
} else if (TRI_IsDirectory(filename)) {
LOG_TOPIC("0207a", TRACE, arangodb::Logger::FIXME)
<< "removing directory '" << filename << "'";
int res = TRI_ERROR_NO_ERROR;
std::vector<std::string> files = TRI_FilesDirectory(filename);
for (auto const& dir : files) {
std::string full = arangodb::basics::FileUtils::buildFilename(filename, dir);
int subres = TRI_RemoveDirectory(full.c_str());
if (subres != TRI_ERROR_NO_ERROR) {
res = subres;
}
}
if (res == TRI_ERROR_NO_ERROR) {
res = TRI_RemoveEmptyDirectory(filename);
}
return res;
} else if (TRI_ExistsFile(filename)) {
LOG_TOPIC("f103f", TRACE, arangodb::Logger::FIXME)
<< "removing file '" << filename << "'";
return TRI_UnlinkFile(filename);
} else {
LOG_TOPIC("08df8", TRACE, arangodb::Logger::FIXME)
<< "attempt to remove non-existing file/directory '" << filename << "'";
// TODO: why do we actually return "no error" here?
return TRI_ERROR_NO_ERROR;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes a directory recursively
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveDirectoryDeterministic(char const* filename) {
std::vector<std::string> files = TRI_FullTreeDirectory(filename);
// start removing files from long names to short names
std::sort(files.begin(), files.end(),
[](std::string const& lhs, std::string const& rhs) -> bool {
if (lhs.size() == rhs.size()) {
// equal lengths. now compare the file/directory names
return lhs < rhs;
}
return lhs.size() > rhs.size();
});
// LOG_TOPIC("0d9b9", TRACE, arangodb::Logger::FIXME) << "removing files in directory
// '" << filename << "' in this order: " << files;
int res = TRI_ERROR_NO_ERROR;
for (auto const& it : files) {
if (it.empty()) {
// TRI_FullTreeDirectory returns "" as its first member
continue;
}
std::string full = arangodb::basics::FileUtils::buildFilename(filename, it);
int subres = TRI_RemoveDirectory(full.c_str());
if (subres != TRI_ERROR_NO_ERROR) {
res = subres;
}
}
int subres = TRI_RemoveDirectory(filename);
if (subres != TRI_ERROR_NO_ERROR) {
res = subres;
}
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief extracts the dirname
////////////////////////////////////////////////////////////////////////////////
std::string TRI_Dirname(std::string const& path) {
size_t n = path.size();
if (n == 0) {
// "" => "."
return std::string(".");
}
if (n > 1 && path[n - 1] == TRI_DIR_SEPARATOR_CHAR) {
// .../ => ...
return path.substr(0, n - 1);
}
if (n == 1 && path[0] == TRI_DIR_SEPARATOR_CHAR) {
// "/" => "/"
return std::string(TRI_DIR_SEPARATOR_STR);
} else if (n == 1 && path[0] == '.') {
return std::string(".");
} else if (n == 2 && path[0] == '.' && path[1] == '.') {
return std::string("..");
}
char const* p;
for (p = path.data() + (n - 1); path.data() < p; --p) {
if (*p == TRI_DIR_SEPARATOR_CHAR) {
break;
}
}
if (path.data() == p) {
if (*p == TRI_DIR_SEPARATOR_CHAR) {
return std::string(TRI_DIR_SEPARATOR_STR);
} else {
return std::string(".");
}
}
n = p - path.data();
return path.substr(0, n);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief extracts the basename
////////////////////////////////////////////////////////////////////////////////
std::string TRI_Basename(char const* path) {
size_t n = strlen(path);
if (1 < n) {
if (IsDirSeparatorChar(path[n - 1])) {
n -= 1;
}
}
if (n == 0) {
return std::string();
} else if (n == 1) {
if (IsDirSeparatorChar(*path)) {
return std::string(TRI_DIR_SEPARATOR_STR);
}
return std::string(path, n);
} else {
char const* p;
for (p = path + (n - 2); path < p; --p) {
if (IsDirSeparatorChar(*p)) {
break;
}
}
if (path == p) {
if (IsDirSeparatorChar(*p)) {
return std::string(path + 1, n - 1);
}
return std::string(path, n);
} else {
n -= p - path;
return std::string(p + 1, n - 1);
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a list of files in path
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_HAVE_WIN32_LIST_FILES
std::vector<std::string> TRI_FilesDirectory(char const* path) {
std::vector<std::string> result;
std::string filter(path);
filter.append("\\*");
struct _wfinddata_t fd;
intptr_t handle = _wfindfirst(toWString(filter).data(), &fd);
if (handle == -1) {
return result;
}
do {
if (wcscmp(fd.name, L".") != 0 && wcscmp(fd.name, L"..") != 0) {
result.emplace_back(fromWString(fd.name));
}
} while (_wfindnext(handle, &fd) != -1);
_findclose(handle);
return result;
}
#else
std::vector<std::string> TRI_FilesDirectory(char const* path) {
std::vector<std::string> result;
DIR* d = opendir(path);
if (d == nullptr) {
return result;
}
auto guard = scopeGuard([&d]() { closedir(d); });
struct dirent* de = readdir(d);
while (de != nullptr) {
if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) {
// may throw
result.emplace_back(std::string(de->d_name));
}
de = readdir(d);
}
return result;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief lists the directory tree including files and directories
////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> TRI_FullTreeDirectory(char const* path) {
std::vector<std::string> result;
result.push_back("");
ListTreeRecursively(path, "", result);
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief renames a file
////////////////////////////////////////////////////////////////////////////////
int TRI_RenameFile(char const* old, char const* filename, long* systemError,
std::string* systemErrorStr) {
int res;
TRI_ERRORBUF;
#ifdef _WIN32
BOOL moveResult = 0;
moveResult = MoveFileExW(toWString(old).data(), toWString(filename).data(),
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
if (!moveResult) {
TRI_SYSTEM_ERROR();
if (systemError != nullptr) {
*systemError = errno;
}
if (systemErrorStr != nullptr) {
*systemErrorStr = windowsErrorBuf;
}
LOG_TOPIC("1f6ac", TRACE, arangodb::Logger::FIXME)
<< "cannot rename file from '" << old << "' to '" << filename
<< "': " << errno << " - " << windowsErrorBuf;
res = -1;
} else {
res = 0;
}
#else
res = rename(old, filename);
#endif
if (res != 0) {
if (systemError != nullptr) {
*systemError = errno;
}
if (systemErrorStr != nullptr) {
*systemErrorStr = TRI_LAST_ERROR_STR;
}
LOG_TOPIC("d8b28", TRACE, arangodb::Logger::FIXME)
<< "cannot rename file from '" << old << "' to '" << filename
<< "': " << TRI_LAST_ERROR_STR;
return TRI_set_errno(TRI_ERROR_SYS_ERROR);
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief unlinks a file
////////////////////////////////////////////////////////////////////////////////
int TRI_UnlinkFile(char const* filename) {
int res = TRI_UNLINK(filename);
if (res != 0) {
int e = errno;
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("80e57", TRACE, arangodb::Logger::FIXME)
<< "cannot unlink file '" << filename << "': " << TRI_LAST_ERROR_STR;
if (e == ENOENT) {
return TRI_ERROR_FILE_NOT_FOUND;
}
if (e == EPERM) {
return TRI_ERROR_FORBIDDEN;
}
return TRI_ERROR_SYS_ERROR;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief reads into a buffer from a file
////////////////////////////////////////////////////////////////////////////////
bool TRI_ReadPointer(int fd, void* buffer, size_t length) {
char* ptr = static_cast<char*>(buffer);
while (0 < length) {
ssize_t n = TRI_READ(fd, ptr, static_cast<TRI_read_t>(length));
if (n < 0) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("c9c0c", ERR, arangodb::Logger::FIXME) << "cannot read: " << TRI_LAST_ERROR_STR;
return false;
} else if (n == 0) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("87f52", ERR, arangodb::Logger::FIXME)
<< "cannot read, end-of-file";
return false;
}
ptr += n;
length -= n;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief writes buffer to a file
////////////////////////////////////////////////////////////////////////////////
bool TRI_WritePointer(int fd, void const* buffer, size_t length) {
char const* ptr = static_cast<char const*>(buffer);
while (0 < length) {
ssize_t n = TRI_WRITE(fd, ptr, static_cast<TRI_write_t>(length));
if (n < 0) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("420b1", ERR, arangodb::Logger::FIXME) << "cannot write: " << TRI_LAST_ERROR_STR;
return false;
}
ptr += n;
length -= n;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief saves data to a file
////////////////////////////////////////////////////////////////////////////////
int TRI_WriteFile(char const* filename, char const* data, size_t length) {
int fd;
bool result;
fd = TRI_CREATE(filename, O_CREAT | O_EXCL | O_RDWR | TRI_O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
return TRI_set_errno(TRI_ERROR_SYS_ERROR);
}
result = TRI_WritePointer(fd, data, length);
TRI_CLOSE(fd);
if (!result) {
return TRI_errno();
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief fsyncs a file
////////////////////////////////////////////////////////////////////////////////
bool TRI_fsync(int fd) {
int res = fsync(fd);
#ifdef __APPLE__
if (res == 0) {
res = fcntl(fd, F_FULLFSYNC, 0);
}
#endif
if (res == 0) {
return true;
} else {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief slurps in a file
////////////////////////////////////////////////////////////////////////////////
char* TRI_SlurpFile(char const* filename, size_t* length) {
TRI_set_errno(TRI_ERROR_NO_ERROR);
int fd = TRI_OPEN(filename, O_RDONLY | TRI_O_CLOEXEC);
if (fd == -1) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
TRI_string_buffer_t result;
TRI_InitStringBuffer(&result, false);
while (true) {
int res = TRI_ReserveStringBuffer(&result, READBUFFER_SIZE);
if (res != TRI_ERROR_NO_ERROR) {
TRI_CLOSE(fd);
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
return nullptr;
}
ssize_t n = TRI_READ(fd, (void*)TRI_EndStringBuffer(&result), READBUFFER_SIZE);
if (n == 0) {
break;
}
if (n < 0) {
TRI_CLOSE(fd);
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
TRI_IncreaseLengthStringBuffer(&result, (size_t)n);
}
if (length != nullptr) {
*length = TRI_LengthStringBuffer(&result);
}
TRI_CLOSE(fd);
return result._buffer;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Read a file and pass contents to user function: true if entire file
/// processed
////////////////////////////////////////////////////////////////////////////////
bool TRI_ProcessFile(char const* filename,
std::function<bool(char const* block, size_t size)> const& reader) {
TRI_set_errno(TRI_ERROR_NO_ERROR);
int fd = TRI_OPEN(filename, O_RDONLY | TRI_O_CLOEXEC);
if (fd == -1) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return false;
}
TRI_string_buffer_t result;
TRI_InitStringBuffer(&result, false);
bool good = true;
while (good) {
int res = TRI_ReserveStringBuffer(&result, READBUFFER_SIZE);
if (res != TRI_ERROR_NO_ERROR) {
TRI_CLOSE(fd);
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
return false;
}
ssize_t n = TRI_READ(fd, (void*)TRI_EndStringBuffer(&result), READBUFFER_SIZE);
if (n == 0) {
break;
}
if (n < 0) {
TRI_CLOSE(fd);
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return false;
}
good = reader(result._buffer, n);
}
TRI_DestroyStringBuffer(&result);
TRI_CLOSE(fd);
return good;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief slurps in a file that is compressed and return uncompressed contents
////////////////////////////////////////////////////////////////////////////////
char* TRI_SlurpGzipFile(char const* filename, size_t* length) {
TRI_set_errno(TRI_ERROR_NO_ERROR);
gzFile gzFd(gzopen(filename,"rb"));
auto fdGuard = arangodb::scopeGuard([&gzFd](){ if (nullptr != gzFd) gzclose(gzFd); });
char * retPtr = nullptr;
if (nullptr != gzFd) {
TRI_string_buffer_t result;
TRI_InitStringBuffer(&result, false);
while (true) {
int res = TRI_ReserveStringBuffer(&result, READBUFFER_SIZE);
if (res != TRI_ERROR_NO_ERROR) {
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
return nullptr;
}
ssize_t n = gzread(gzFd, (void*)TRI_EndStringBuffer(&result), READBUFFER_SIZE);
if (n == 0) {
break;
}
if (n < 0) {
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
TRI_IncreaseLengthStringBuffer(&result, (size_t)n);
} // while
if (length != nullptr) {
*length = TRI_LengthStringBuffer(&result);
}
retPtr = result._buffer;
} // if
return retPtr;
} // TRI_SlurpGzipFile
#ifdef USE_ENTERPRISE
////////////////////////////////////////////////////////////////////////////////
/// @brief slurps in a file that is encrypted and return unencrypted contents
////////////////////////////////////////////////////////////////////////////////
char* TRI_SlurpDecryptFile(char const* filename, char const * keyfile, size_t* length) {
TRI_set_errno(TRI_ERROR_NO_ERROR);
EncryptionFeature* encryptionFeature;
encryptionFeature = application_features::ApplicationServer::getFeature<EncryptionFeature>("Encryption");
if (nullptr == encryptionFeature) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
encryptionFeature->setKeyFile(keyfile);
auto keyGuard = arangodb::scopeGuard([encryptionFeature](){ encryptionFeature->clearKey(); });
int fd = TRI_OPEN(filename, O_RDONLY | TRI_O_CLOEXEC);
if (fd == -1) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
std::unique_ptr<EncryptionFeature::Context> context;
context = encryptionFeature->beginDecryption(fd);
if (nullptr == context.get() || !context->status().ok()) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
TRI_string_buffer_t result;
TRI_InitStringBuffer(&result, false);
while (true) {
int res = TRI_ReserveStringBuffer(&result, READBUFFER_SIZE);
if (res != TRI_ERROR_NO_ERROR) {
TRI_CLOSE(fd);
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
return nullptr;
}
ssize_t n = encryptionFeature->readData(*context, (void*)TRI_EndStringBuffer(&result), READBUFFER_SIZE);
if (n == 0) {
break;
}
if (n < 0) {
TRI_CLOSE(fd);
TRI_AnnihilateStringBuffer(&result);
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
TRI_IncreaseLengthStringBuffer(&result, (size_t)n);
}
if (length != nullptr) {
*length = TRI_LengthStringBuffer(&result);
}
TRI_CLOSE(fd);
return result._buffer;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a lock file based on the PID
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_HAVE_WIN32_FILE_LOCKING
int TRI_CreateLockFile(char const* filename) {
TRI_ERRORBUF;
OVERLAPPED ol;
WRITE_LOCKER(locker, OpenedFilesLock);
for (size_t i = 0; i < OpenedFiles.size(); ++i) {
if (OpenedFiles[i].first == filename) {
// file already exists
return TRI_ERROR_NO_ERROR;
}
}
HANDLE fd = CreateFileW(toWString(filename).data(), GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (fd == INVALID_HANDLE_VALUE) {
TRI_SYSTEM_ERROR();
LOG_TOPIC("64d0d", ERR, arangodb::Logger::FIXME)
<< "cannot create lockfile '" << filename << "': " << TRI_GET_ERRORBUF;
return TRI_set_errno(TRI_ERROR_SYS_ERROR);
}
TRI_pid_t pid = Thread::currentProcessId();
std::string buf = std::to_string(pid);
DWORD len;
BOOL r = WriteFile(fd, buf.c_str(), static_cast<unsigned int>(buf.size()), &len, NULL);
if (!r || len != buf.size()) {
TRI_SYSTEM_ERROR();
LOG_TOPIC("c2286", ERR, arangodb::Logger::FIXME)
<< "cannot write lockfile '" << filename << "': " << TRI_GET_ERRORBUF;
int res = TRI_set_errno(TRI_ERROR_SYS_ERROR);
if (r) {
CloseHandle(fd);
}
TRI_UNLINK(filename);
return res;
}
memset(&ol, 0, sizeof(ol));
r = LockFileEx(fd, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 0, 128, &ol);
if (!r) {
TRI_SYSTEM_ERROR();
LOG_TOPIC("f0d61", ERR, arangodb::Logger::FIXME)
<< "cannot set lockfile status '" << filename << "': " << TRI_GET_ERRORBUF;
int res = TRI_set_errno(TRI_ERROR_SYS_ERROR);
CloseHandle(fd);
TRI_UNLINK(filename);
return res;
}
OpenedFiles.push_back(std::make_pair(filename, fd));
return TRI_ERROR_NO_ERROR;
}
#else
int TRI_CreateLockFile(char const* filename) {
WRITE_LOCKER(locker, OpenedFilesLock);
for (size_t i = 0; i < OpenedFiles.size(); ++i) {
if (OpenedFiles[i].first == filename) {
// file already exists
return TRI_ERROR_NO_ERROR;
}
}
int fd = TRI_CREATE(filename, O_CREAT | O_EXCL | O_RDWR | TRI_O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
return TRI_set_errno(TRI_ERROR_SYS_ERROR);
}
TRI_pid_t pid = Thread::currentProcessId();
std::string buf = std::to_string(pid);
int rv = TRI_WRITE(fd, buf.c_str(), static_cast<TRI_write_t>(buf.size()));
if (rv == -1) {
int res = TRI_set_errno(TRI_ERROR_SYS_ERROR);
TRI_CLOSE(fd);
TRI_UNLINK(filename);
return res;
}
struct flock lock;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
// try to lock pid file
rv = fcntl(fd, F_SETLK, &lock);
if (rv == -1) {
int res = TRI_set_errno(TRI_ERROR_SYS_ERROR);
TRI_CLOSE(fd);
TRI_UNLINK(filename);
return res;
}
OpenedFiles.push_back(std::make_pair(filename, fd));
return TRI_ERROR_NO_ERROR;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief verifies a lock file based on the PID
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_HAVE_WIN32_FILE_LOCKING
int TRI_VerifyLockFile(char const* filename) {
HANDLE fd;
if (!TRI_ExistsFile(filename)) {
return TRI_ERROR_NO_ERROR;
}
fd = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (fd == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_SHARING_VIOLATION) {
return TRI_ERROR_ARANGO_DATADIR_LOCKED;
}
return TRI_ERROR_NO_ERROR;
}
CloseHandle(fd);
TRI_UnlinkFile(filename);
return TRI_ERROR_NO_ERROR;
}
#else
int TRI_VerifyLockFile(char const* filename) {
if (!TRI_ExistsFile(filename)) {
return TRI_ERROR_NO_ERROR;
}
int fd = TRI_OPEN(filename, O_RDWR | TRI_O_CLOEXEC);
if (fd < 0) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("51db5", WARN, arangodb::Logger::FIXME)
<< "cannot open lockfile '" << filename
<< "' in write mode: " << TRI_last_error();
if (errno == EACCES) {
return TRI_ERROR_CANNOT_WRITE_FILE;
}
return TRI_ERROR_INTERNAL;
}
char buffer[128];
memset(buffer, 0,
sizeof(buffer)); // not really necessary, but this shuts up valgrind
ssize_t n = TRI_READ(fd, buffer, static_cast<TRI_read_t>(sizeof(buffer)));
TRI_DEFER(TRI_CLOSE(fd));
if (n <= 0 || n == sizeof(buffer)) {
return TRI_ERROR_NO_ERROR;
}
uint32_t fc = TRI_UInt32String(buffer);
int res = TRI_errno();
if (res != TRI_ERROR_NO_ERROR) {
// invalid pid value
return TRI_ERROR_NO_ERROR;
}
TRI_pid_t pid = fc;
// check for the existence of previous process via kill command
// from man 2 kill:
// If sig is 0, then no signal is sent, but existence and permission checks
// are still performed; this can be used to check for the existence of a
// process ID or process group ID that the caller is permitted to signal.
if (kill(pid, 0) == -1) {
LOG_TOPIC("b387d", WARN, arangodb::Logger::FIXME)
<< "found existing lockfile '" << filename << "' of previous process with pid "
<< pid << ", but that process seems to be dead already";
} else {
LOG_TOPIC("ad4b2", WARN, arangodb::Logger::FIXME)
<< "found existing lockfile '" << filename << "' of previous process with pid "
<< pid << ", and that process seems to be still running";
}
struct flock lock;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
// try to lock pid file
int canLock = fcntl(fd, F_SETLK, &lock); // Exclusive (write) lock
// file was not yet locked; could be locked
if (canLock == 0) {
// lock.l_type = F_UNLCK;
res = fcntl(fd, F_GETLK, &lock);
if (res != TRI_ERROR_NO_ERROR) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("c960a", WARN, arangodb::Logger::FIXME)
<< "fcntl on lockfile '" << filename << "' failed: " << TRI_last_error();
}
return TRI_ERROR_NO_ERROR;
}
// error!
canLock = errno;
TRI_set_errno(TRI_ERROR_SYS_ERROR);
// from man 2 fcntl: "If a conflicting lock is held by another process,
// this call returns -1 and sets errno to EACCES or EAGAIN."
if (canLock != EACCES && canLock != EAGAIN) {
LOG_TOPIC("94b6d", WARN, arangodb::Logger::FIXME)
<< "fcntl on lockfile '" << filename << "' failed: " << TRI_last_error()
<< ". a possible reason is that the filesystem does not support "
"file-locking";
}
return TRI_ERROR_ARANGO_DATADIR_LOCKED;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief releases a lock file based on the PID
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_HAVE_WIN32_FILE_LOCKING
int TRI_DestroyLockFile(char const* filename) {
WRITE_LOCKER(locker, OpenedFilesLock);
for (size_t i = 0; i < OpenedFiles.size(); ++i) {
if (OpenedFiles[i].first == filename) {
HANDLE fd = OpenedFiles[i].second;
CloseHandle(fd);
TRI_UnlinkFile(filename);
OpenedFiles.erase(OpenedFiles.begin() + i);
break;
}
}
return TRI_ERROR_NO_ERROR;
}
#else
int TRI_DestroyLockFile(char const* filename) {
WRITE_LOCKER(locker, OpenedFilesLock);
for (size_t i = 0; i < OpenedFiles.size(); ++i) {
if (OpenedFiles[i].first == filename) {
int fd = TRI_OPEN(filename, O_RDWR | TRI_O_CLOEXEC);
if (fd < 0) {
return TRI_ERROR_NO_ERROR;
}
struct flock lock;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
// release the lock
int res = fcntl(fd, F_SETLK, &lock);
TRI_CLOSE(fd);
if (res == 0) {
TRI_UnlinkFile(filename);
}
// close lock file descriptor
fd = OpenedFiles[i].second;
TRI_CLOSE(fd);
OpenedFiles.erase(OpenedFiles.begin() + i);
return res;
}
}
return TRI_ERROR_NO_ERROR;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief return the filename component of a file (without path)
////////////////////////////////////////////////////////////////////////////////
std::string TRI_GetFilename(std::string const& filename) {
size_t pos = filename.find_last_of("\\/:");
if (pos == std::string::npos) {
return filename;
}
return filename.substr(pos + 1);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the absolute path of a file
/// in contrast to realpath() this function can also be used to determine the
/// full path for files & directories that do not exist. realpath() would fail
/// for those cases.
/// It is the caller's responsibility to free the string created by this
/// function
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
std::string TRI_GetAbsolutePath(std::string const& fileName,
std::string const& currentWorkingDirectory) {
// Check that fileName actually makes some sense
if (fileName.empty()) {
return std::string();
}
// ...........................................................................
// Under windows we can assume that fileName is absolute if fileName starts
// with a letter A-Z followed by a colon followed by either a forward or
// backslash.
// ...........................................................................
if (fileName.size() >= 3 &&
((fileName[0] > 64 && fileName[0] < 91) || (fileName[0] > 96 && fileName[0] < 123)) &&
fileName[1] == ':' && (fileName[2] == '/' || fileName[2] == '\\')) {
return fileName;
}
// ...........................................................................
// The fileName itself was not absolute, so we attempt to amalgamate the
// currentWorkingDirectory with the fileName
// ...........................................................................
// Check that the currentWorkingDirectory makes sense
if (currentWorkingDirectory.empty()) {
return std::string();
}
// ...........................................................................
// Under windows the currentWorkingDirectory should start
// with a letter A-Z followed by a colon followed by either a forward or
// backslash.
// ...........................................................................
if (currentWorkingDirectory.size() >= 3 &&
((currentWorkingDirectory[0] > 64 && currentWorkingDirectory[0] < 91) ||
(currentWorkingDirectory[0] > 96 && currentWorkingDirectory[0] < 123)) &&
currentWorkingDirectory[1] == ':' &&
(currentWorkingDirectory[2] == '/' || currentWorkingDirectory[2] == '\\')) {
// e.g. C:/ or Z:\ drive letter paths
} else if (currentWorkingDirectory[0] == '/' || currentWorkingDirectory[0] == '\\') {
// directory name can also start with a backslash
// /... or \...
} else {
return std::string();
}
// Determine the total length of the new string
std::string result;
if (currentWorkingDirectory.back() == '\\' || currentWorkingDirectory.back() == '/' ||
fileName.front() == '\\' || fileName.front() == '/') {
// we do not require a backslash
result.reserve(currentWorkingDirectory.size() + fileName.size());
result.append(currentWorkingDirectory);
result.append(fileName);
} else {
// we do require a backslash
result.reserve(currentWorkingDirectory.size() + fileName.size() + 1);
result.append(currentWorkingDirectory);
result.push_back('\\');
result.append(fileName);
}
return result;
}
#else
std::string TRI_GetAbsolutePath(std::string const& fileName,
std::string const& currentWorkingDirectory) {
if (fileName.empty()) {
return std::string();
}
// name is absolute if starts with either forward or backslash
// file is also absolute if contains a colon
bool isAbsolute = (fileName[0] == '/' || fileName[0] == '\\' ||
fileName.find(':') != std::string::npos);
if (isAbsolute) {
return fileName;
}
std::string result;
if (!currentWorkingDirectory.empty()) {
result.reserve(currentWorkingDirectory.size() + fileName.size() + 1);
result.append(currentWorkingDirectory);
if (currentWorkingDirectory.back() != '/') {
result.push_back('/');
}
result.append(fileName);
}
return result;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the binary name without any path or suffix
////////////////////////////////////////////////////////////////////////////////
std::string TRI_BinaryName(char const* argv0) {
std::string result = TRI_Basename(argv0);
if (result.size() > 4 && result.substr(result.size() - 4, 4) == ".exe") {
result = result.substr(0, result.size() - 4);
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief locates the directory containing the program
////////////////////////////////////////////////////////////////////////////////
std::string TRI_LocateBinaryPath(char const* argv0) {
#if _WIN32
wchar_t buff[4096];
int res = GetModuleFileNameW(NULL, buff, sizeof(buff));
if (res != 0) {
buff[4095] = '\0';
wchar_t* q = buff + res;
while (buff < q) {
if (*q == '\\' || *q == '/') {
*q = '\0';
break;
}
--q;
}
size_t len = q - buff;
return fromWString(buff, len);
}
return std::string();
#else
std::string binaryPath;
// check if name contains a '/' ( or '\' for windows)
char const* p = argv0;
for (; *p && *p != TRI_DIR_SEPARATOR_CHAR; ++p) {
}
// contains a path
if (*p) {
binaryPath = TRI_Dirname(argv0);
}
// check PATH variable
else {
std::string pv;
if (TRI_GETENV("PATH", pv)) {
std::vector<std::string> files = basics::StringUtils::split(pv, ':', '\0');
for (auto const& prefix : files) {
std::string full;
if (!prefix.empty()) {
full = arangodb::basics::FileUtils::buildFilename(prefix, argv0);
} else {
full = arangodb::basics::FileUtils::buildFilename(".", argv0);
}
if (TRI_ExistsFile(full.c_str())) {
binaryPath = prefix;
break;
}
}
}
}
return binaryPath;
#endif
}
std::string TRI_GetInstallRoot(std::string const& binaryPath, char const* installBinaryPath) {
// First lets remove trailing (back) slashes from the bill:
size_t installPathLength = strlen(installBinaryPath);
if (installBinaryPath[installPathLength - 1] == TRI_DIR_SEPARATOR_CHAR) {
--installPathLength;
}
size_t binaryPathLength = binaryPath.size();
char const* p = binaryPath.c_str();
if (p[binaryPathLength - 1] == TRI_DIR_SEPARATOR_CHAR) {
--binaryPathLength;
}
if (installPathLength > binaryPathLength) {
return TRI_DIR_SEPARATOR_STR;
}
for (size_t i = 1; i < installPathLength; ++i) {
if (p[binaryPathLength - i] != installBinaryPath[installPathLength - i]) {
return TRI_DIR_SEPARATOR_STR;
}
}
return std::string(p, binaryPathLength - installPathLength);
}
static bool CopyFileContents(int srcFD, int dstFD, ssize_t fileSize, std::string& error) {
bool rc = true;
#if TRI_LINUX_SPLICE
int splicePipe[2];
ssize_t pipeSize = 0;
long chunkSendRemain = fileSize;
loff_t totalSentAlready = 0;
if (pipe(splicePipe) != 0) {
error = std::string("splice failed to create pipes: ") + strerror(errno);
return false;
}
while (chunkSendRemain > 0) {
if (pipeSize == 0) {
pipeSize = splice(srcFD, &totalSentAlready, splicePipe[1], nullptr,
chunkSendRemain, SPLICE_F_MOVE);
if (pipeSize == -1) {
error = std::string("splice read failed: ") + strerror(errno);
rc = false;
break;
}
}
ssize_t sent = splice(splicePipe[0], nullptr, dstFD, nullptr, pipeSize,
SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
if (sent == -1) {
error = std::string("splice read failed: ") + strerror(errno);
rc = false;
break;
}
pipeSize -= sent;
chunkSendRemain -= sent;
}
close(splicePipe[0]);
close(splicePipe[1]);
#else
// 128k:
constexpr size_t C128 = 128 * 1024;
char* buf = static_cast<char*>(TRI_Allocate(C128));
if (buf == nullptr) {
error = "failed to allocate temporary buffer";
rc = false;
}
size_t chunkRemain = fileSize;
while (rc && (chunkRemain > 0)) {
size_t readChunk = (std::min)(C128, chunkRemain);
ssize_t nRead = TRI_READ(srcFD, buf, static_cast<TRI_read_t>(readChunk));
if (nRead < 0) {
error = std::string("failed to read a chunk: ") + strerror(errno);
rc = false;
break;
}
if (nRead == 0) {
// EOF. done
break;
}
size_t writeOffset = 0;
size_t writeRemaining = static_cast<size_t>(nRead);
while (writeRemaining > 0) {
// write can write less data than requested. so we must go on writing
// until we have written out all data
ssize_t nWritten = TRI_WRITE(dstFD, buf + writeOffset,
static_cast<TRI_write_t>(writeRemaining));
if (nWritten < 0) {
// error during write
error = std::string("failed to read a chunk: ") + strerror(errno);
rc = false;
break;
}
writeOffset += static_cast<size_t>(nWritten);
writeRemaining -= static_cast<size_t>(nWritten);
}
chunkRemain -= nRead;
}
TRI_Free(buf);
#endif
return rc;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copies the contents of a file
////////////////////////////////////////////////////////////////////////////////
bool TRI_CopyFile(std::string const& src, std::string const& dst, std::string& error) {
#ifdef _WIN32
TRI_ERRORBUF;
bool rc = CopyFileW(toWString(src).data(), toWString(dst).data(), true) != 0;
if (!rc) {
TRI_SYSTEM_ERROR();
error = "failed to copy " + src + " to " + dst + ": " + TRI_GET_ERRORBUF;
}
return rc;
#else
size_t dsize;
int srcFD, dstFD;
struct stat statbuf;
srcFD = open(src.c_str(), O_RDONLY);
if (srcFD < 0) {
error = "failed to open source file " + src + ": " + strerror(errno);
return false;
}
dstFD = open(dst.c_str(), O_EXCL | O_CREAT | O_NONBLOCK | O_WRONLY, S_IRUSR | S_IWUSR);
if (dstFD < 0) {
close(srcFD);
error = "failed to open destination file " + dst + ": " + strerror(errno);
return false;
}
TRI_FSTAT(srcFD, &statbuf);
dsize = statbuf.st_size;
bool rc = CopyFileContents(srcFD, dstFD, dsize, error);
timeval times[2];
memset(times, 0, sizeof(times));
times[0].tv_sec = TRI_STAT_ATIME_SEC(statbuf);
times[1].tv_sec = TRI_STAT_MTIME_SEC(statbuf);
if (fchown(dstFD, -1 /*statbuf.st_uid*/, statbuf.st_gid) != 0) {
error = std::string("failed to chown ") + dst + ": " + strerror(errno);
// rc = false; no, this is not fatal...
}
if (fchmod(dstFD, statbuf.st_mode) != 0) {
error = std::string("failed to chmod ") + dst + ": " + strerror(errno);
rc = false;
}
#ifdef HAVE_FUTIMES
if (futimes(dstFD, times) != 0) {
error = std::string("failed to adjust age: ") + dst + ": " + strerror(errno);
rc = false;
}
#else
if (utimes(dst.c_str(), times) != 0) {
error = std::string("failed to adjust age: ") + dst + ": " + strerror(errno);
rc = false;
}
#endif
close(srcFD);
close(dstFD);
return rc;
#endif
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copies the filesystem attributes of a file
////////////////////////////////////////////////////////////////////////////////
bool TRI_CopyAttributes(std::string const& srcItem, std::string const& dstItem,
std::string& error) {
#ifndef _WIN32
struct stat statbuf;
TRI_STAT(srcItem.c_str(), &statbuf);
if (chown(dstItem.c_str(), -1 /*statbuf.st_uid*/, statbuf.st_gid) != 0) {
error = std::string("failed to chown ") + dstItem + ": " + strerror(errno);
// return false;
}
if (chmod(dstItem.c_str(), statbuf.st_mode) != 0) {
error = std::string("failed to chmod ") + dstItem + ": " + strerror(errno);
return false;
}
timeval times[2];
memset(times, 0, sizeof(times));
times[0].tv_sec = TRI_STAT_ATIME_SEC(statbuf);
times[1].tv_sec = TRI_STAT_MTIME_SEC(statbuf);
if (utimes(dstItem.c_str(), times) != 0) {
error = std::string("failed to adjust age: ") + dstItem + ": " + strerror(errno);
return false;
}
#endif
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copies a symlink; the link target is not altered.
////////////////////////////////////////////////////////////////////////////////
bool TRI_CopySymlink(std::string const& srcItem, std::string const& dstItem,
std::string& error) {
#ifndef _WIN32
char buffer[PATH_MAX];
ssize_t rc = readlink(srcItem.c_str(), buffer, sizeof(buffer) - 1);
if (rc == -1) {
error = std::string("failed to read symlink ") + srcItem + ": " + strerror(errno);
return false;
}
buffer[rc] = '\0';
if (symlink(buffer, dstItem.c_str()) != 0) {
error = std::string("failed to create symlink ") + dstItem + " -> " +
buffer + ": " + strerror(errno);
return false;
}
#endif
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a hard link; the link target is not altered.
////////////////////////////////////////////////////////////////////////////////
bool TRI_CreateHardlink(std::string const& existingFile, std::string const& newFile,
std::string& error) {
#ifndef _WIN32
int rc = link(existingFile.c_str(), newFile.c_str());
if (rc == -1) {
error = std::string("failed to create hard link ") + newFile + ": " + strerror(errno);
} // if
return 0 == rc;
#else
error = "Windows TRI_CreateHardlink not written, yet.";
return false;
#endif
}
////////////////////////////////////////////////////////////////////////////////
/// @brief locates the home directory
///
/// Under windows there is no 'HOME' directory as such so getenv("HOME") may
/// return NULL -- which it does under windows. A safer approach below
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
std::string TRI_HomeDirectory() {
std::string drive;
std::string path;
if (!TRI_GETENV("HOMEDRIVE", drive) || !TRI_GETENV("HOMEPATH", path)) {
return std::string();
}
return drive + path;
}
#else
std::string TRI_HomeDirectory() {
char const* result = getenv("HOME");
if (result == nullptr) {
return std::string(".");
}
return std::string(result);
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief calculate the crc32 checksum of a file
////////////////////////////////////////////////////////////////////////////////
int TRI_Crc32File(char const* path, uint32_t* crc) {
FILE* fin;
void* buffer;
int bufferSize;
int res;
int res2;
*crc = TRI_InitialCrc32();
bufferSize = 4096;
buffer = TRI_Allocate((size_t)bufferSize);
if (buffer == nullptr) {
return TRI_ERROR_OUT_OF_MEMORY;
}
fin = TRI_FOPEN(path, "rb");
if (fin == nullptr) {
TRI_Free(buffer);
return TRI_ERROR_FILE_NOT_FOUND;
}
res = TRI_ERROR_NO_ERROR;
while (true) {
int sizeRead = (int)fread(buffer, 1, bufferSize, fin);
if (sizeRead < bufferSize) {
if (feof(fin) == 0) {
res = errno;
break;
}
}
if (sizeRead > 0) {
*crc = TRI_BlockCrc32(*crc, static_cast<char const*>(buffer), sizeRead);
} else /* if (sizeRead <= 0) */ {
break;
}
}
TRI_Free(buffer);
res2 = fclose(fin);
if (res2 != TRI_ERROR_NO_ERROR && res2 != EOF) {
if (res == TRI_ERROR_NO_ERROR) {
res = res2;
}
// otherwise keep original error
}
*crc = TRI_FinalCrc32(*crc);
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the application's name, should be called before the first
/// call to TRI_GetTempPath
////////////////////////////////////////////////////////////////////////////////
static std::string TRI_ApplicationName = "arangodb";
void TRI_SetApplicationName(std::string const& name) {
TRI_ApplicationName = name;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the system's temporary path
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
static std::string getTempPath() {
// ..........................................................................
// Unfortunately we generally have little control on whether or not the
// application will be compiled with UNICODE defined. In some cases such as
// this one, we attempt to cater for both. MS provides some methods which are
// 'defined' for both, for example, GetTempPath (below) actually converts to
// GetTempPathA (ascii) or GetTempPathW (wide characters or what MS call
// unicode).
// ..........................................................................
#define LOCAL_MAX_PATH_BUFFER 2049
wchar_t tempPathName[LOCAL_MAX_PATH_BUFFER];
DWORD dwReturnValue = 0;
// ..........................................................................
// Attempt to locate the path where the users temporary files are stored
// Note we are imposing a limit of 2048+1 characters for the maximum size of a
// possible path
// ..........................................................................
/* from MSDN:
The GetTempPath function checks for the existence of environment variables
in the following order and uses the first path found:
The path specified by the TMP environment variable.
The path specified by the TEMP environment variable.
The path specified by the USERPROFILE environment variable.
The Windows directory.
*/
dwReturnValue = GetTempPathW(LOCAL_MAX_PATH_BUFFER, tempPathName);
if ((dwReturnValue > LOCAL_MAX_PATH_BUFFER) || (dwReturnValue == 0)) {
// something wrong
LOG_TOPIC("79ec5", TRACE, arangodb::Logger::FIXME)
<< "GetTempPathW failed: LOCAL_MAX_PATH_BUFFER=" << LOCAL_MAX_PATH_BUFFER
<< ":dwReturnValue=" << dwReturnValue;
}
std::string result = fromWString(tempPathName, dwReturnValue);
if (result.empty() || (result.back() != TRI_DIR_SEPARATOR_CHAR)) {
result += TRI_DIR_SEPARATOR_STR;
}
return result;
}
static int mkDTemp(char* s, size_t bufferSize) {
std::string tmp(s, bufferSize);
std::wstring ws = toWString(tmp);
// get writeable copy of wstring buffer and replace the _XXX part in the buffer
std::vector<wchar_t> writeBuffer;
writeBuffer.resize(ws.size());
memcpy(writeBuffer.data(), ws.data(), sizeof(wchar_t) * ws.size());
auto rc = _wmktemp_s(writeBuffer.data(), writeBuffer.size()); // requires writeable buffer -- returns errno_t
if (rc == 0) { // error of 0 is ok
// if it worked out, we need to return the utf8 version:
ws = std::wstring(writeBuffer.data(), writeBuffer.size()); // write back to wstring
tmp = fromWString(ws);
memcpy(s, tmp.data(), bufferSize); // copy back into parameter
rc = TRI_MKDIR(s, 0700);
if (rc != 0) {
rc = errno;
if (rc == ENOENT) {
// for some reason we should create the upper directory too?
std::string error;
long systemError;
rc = TRI_CreateRecursiveDirectory(s, systemError, error);
if (rc != 0) {
LOG_TOPIC("6656f", ERR, arangodb::Logger::FIXME)
<< "Unable to create temporary directory " << error;
}
}
}
}
// should error be translated to arango error code?
return rc;
}
#else
static std::string getTempPath() {
std::string system = "";
char const* v = getenv("TMPDIR");
if (v == nullptr || *v == '\0') {
system = "/tmp/";
} else if (v[strlen(v) - 1] == '/') {
system = v;
} else {
system = std::string(v) + "/";
}
return system;
}
static int mkDTemp(char* s, size_t /*bufferSize*/) {
if (mkdtemp(s) != nullptr) {
return TRI_ERROR_NO_ERROR;
}
return errno;
}
#endif
/// @brief the actual temp path used
static std::unique_ptr<char[]> SystemTempPath;
/// @brief user-defined temp path
static std::string UserTempPath;
class SystemTempPathSweeper {
std::string _systemTempPath;
public:
SystemTempPathSweeper() : _systemTempPath(){};
~SystemTempPathSweeper(void) {
if (!_systemTempPath.empty()) {
// delete directory iff directory is empty
TRI_RMDIR(_systemTempPath.c_str());
}
}
void init(const char* path) { _systemTempPath = path; }
};
SystemTempPathSweeper SystemTempPathSweeperInstance;
// The TempPath is set but not created
void TRI_SetTempPath(std::string const& temp) { UserTempPath = temp; }
std::string TRI_GetTempPath() {
char* path = SystemTempPath.get();
if (path == nullptr) {
std::string system;
if (UserTempPath.empty()) {
system = getTempPath();
} else {
system = UserTempPath;
}
// Strip any double back/slashes from the string.
while (system.find(TRI_DIR_SEPARATOR_STR TRI_DIR_SEPARATOR_STR) != std::string::npos) {
system = StringUtils::replace(system, std::string(TRI_DIR_SEPARATOR_STR TRI_DIR_SEPARATOR_STR),
std::string(TRI_DIR_SEPARATOR_STR));
}
// remove trailing DIR_SEPARATOR
while (!system.empty() && IsDirSeparatorChar(system[system.size() - 1])) {
system.pop_back();
}
// and re-append it
system.push_back(TRI_DIR_SEPARATOR_CHAR);
if (UserTempPath.empty()) {
system += TRI_ApplicationName + "_XXXXXX";
}
int tries = 0;
while (true) {
// copy to a character array
SystemTempPath.reset(new char[system.size() + 1]);
path = SystemTempPath.get();
TRI_CopyString(path, system.c_str(), system.size());
int res;
if (!UserTempPath.empty()) {
// --temp.path was specified
if (TRI_IsDirectory(system.c_str())) {
// temp directory already exists. now simply use it
break;
}
res = TRI_MKDIR(UserTempPath.c_str(), 0700);
} else {
// no --temp.path was specified
// fill template and create directory
tries = 9;
// create base directories of the new directory (but ignore any failures
// if they already exist. if this fails, the following mkDTemp will either
// succeed or fail and return an error
try {
long systemError;
std::string systemErrorStr;
std::string baseDirectory = TRI_Dirname(SystemTempPath.get());
if (baseDirectory.size() <= 1) {
baseDirectory = SystemTempPath.get();
}
// create base directory if it does not yet exist
TRI_CreateRecursiveDirectory(baseDirectory.data(), systemError, systemErrorStr);
} catch (...) {}
// fill template string (XXXXXX) with some pseudo-random value and create
// the directory
res = mkDTemp(SystemTempPath.get(), system.size() + 1);
}
if (res == TRI_ERROR_NO_ERROR) {
break;
}
// directory could not be created
// this may be a race, a permissions problem or something else
if (++tries >= 10) {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
LOG_TOPIC("6656e", ERR, arangodb::Logger::FIXME)
<< "UserTempPath: " << UserTempPath << ", system: " << system
<< ", user temp path exists: " << TRI_IsDirectory(UserTempPath.c_str())
<< ", res: " << res << ", SystemTempPath: " << SystemTempPath.get();
#endif
LOG_TOPIC("88da0", FATAL, arangodb::Logger::FIXME)
<< "failed to create a temporary directory - giving up!";
FATAL_ERROR_ABORT();
}
// sleep for a random amout of time and try again soon
// with this, we try to avoid races between multiple processes
// that try to create temp directories at the same time
std::this_thread::sleep_for(
std::chrono::milliseconds(5 + RandomGenerator::interval(uint64_t(20))));
}
SystemTempPathSweeperInstance.init(SystemTempPath.get());
}
return std::string(path);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get a temporary file name
////////////////////////////////////////////////////////////////////////////////
int TRI_GetTempName(char const* directory, std::string& result, bool createFile,
long& systemError, std::string& errorMessage) {
std::string temp = TRI_GetTempPath();
std::string dir;
if (directory != nullptr) {
dir = arangodb::basics::FileUtils::buildFilename(temp, directory);
} else {
dir = temp;
}
// remove trailing DIR_SEPARATOR
while (!dir.empty() && IsDirSeparatorChar(dir[dir.size() - 1])) {
dir.pop_back();
}
int res = TRI_CreateRecursiveDirectory(dir.c_str(), systemError, errorMessage);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
if (!TRI_IsDirectory(dir.c_str())) {
errorMessage = dir + " exists and is not a directory!";
return TRI_ERROR_CANNOT_CREATE_DIRECTORY;
}
int tries = 0;
while (tries++ < 10) {
TRI_pid_t pid = Thread::currentProcessId();
std::string tempName = "tmp-" + std::to_string(pid) + '-' +
std::to_string(RandomGenerator::interval(UINT32_MAX));
std::string filename = arangodb::basics::FileUtils::buildFilename(dir, tempName);
if (TRI_ExistsFile(filename.c_str())) {
errorMessage = std::string("Tempfile already exists! ") + filename;
} else {
if (createFile) {
FILE* fd = TRI_FOPEN(filename.c_str(), "wb");
if (fd != nullptr) {
fclose(fd);
result = filename;
return TRI_ERROR_NO_ERROR;
}
} else {
result = filename;
return TRI_ERROR_NO_ERROR;
}
}
// next try
}
return TRI_ERROR_CANNOT_CREATE_TEMP_FILE;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief locate the installation directory
//
/// Will always end in a directory separator.
////////////////////////////////////////////////////////////////////////////////
std::string TRI_LocateInstallDirectory(char const* argv0, char const* binaryPath) {
std::string thisPath = TRI_LocateBinaryPath(argv0);
return TRI_GetInstallRoot(thisPath, binaryPath) + TRI_DIR_SEPARATOR_CHAR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief locates the configuration directory
///
/// Will always end in a directory separator.
////////////////////////////////////////////////////////////////////////////////
#if _WIN32
std::string TRI_LocateConfigDirectory(char const* binaryPath) {
std::string v = LocateConfigDirectoryEnv();
if (!v.empty()) {
return v;
}
std::string r = TRI_LocateInstallDirectory(nullptr, binaryPath);
r += _SYSCONFDIR_;
r += std::string(1, TRI_DIR_SEPARATOR_CHAR);
return r;
}
#elif defined(_SYSCONFDIR_)
std::string TRI_LocateConfigDirectory(char const* binaryPath) {
std::string v = LocateConfigDirectoryEnv();
if (!v.empty()) {
return v;
}
char const* dir = _SYSCONFDIR_;
if (*dir == '\0') {
return std::string();
}
size_t len = strlen(dir);
if (dir[len - 1] != TRI_DIR_SEPARATOR_CHAR) {
return std::string(dir) + "/";
} else {
return std::string(dir);
}
}
#else
std::string TRI_LocateConfigDirectory(char const*) {
return LocateConfigDirectoryEnv();
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief get the address of the null buffer
////////////////////////////////////////////////////////////////////////////////
char* TRI_GetNullBufferFiles() { return &NullBuffer[0]; }
////////////////////////////////////////////////////////////////////////////////
/// @brief get the size of the null buffer
////////////////////////////////////////////////////////////////////////////////
size_t TRI_GetNullBufferSizeFiles() { return sizeof(NullBuffer); }
/// @brief creates a new datafile
/// returns the file descriptor or -1 if the file cannot be created
int TRI_CreateDatafile(std::string const& filename, size_t maximalSize) {
TRI_ERRORBUF;
// open the file
int fd = TRI_CREATE(filename.c_str(), O_CREAT | O_EXCL | O_RDWR | TRI_O_CLOEXEC | TRI_NOATIME,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
TRI_IF_FAILURE("CreateDatafile1") {
// intentionally fail
TRI_CLOSE(fd);
fd = -1;
errno = ENOSPC;
}
if (fd < 0) {
if (errno == ENOSPC) {
TRI_set_errno(TRI_ERROR_ARANGO_FILESYSTEM_FULL);
LOG_TOPIC("f7530", ERR, arangodb::Logger::FIXME)
<< "cannot create datafile '" << filename << "': " << TRI_last_error();
} else {
TRI_SYSTEM_ERROR();
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("53a75", ERR, arangodb::Logger::FIXME)
<< "cannot create datafile '" << filename << "': " << TRI_GET_ERRORBUF;
}
return -1;
}
// no fallocate present, or at least pretend it's not there...
int res = TRI_ERROR_NOT_IMPLEMENTED;
#ifdef __linux__
#ifdef FALLOC_FL_ZERO_RANGE
// try fallocate
res = fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, maximalSize);
#endif
#endif
if (res != TRI_ERROR_NO_ERROR) {
// either fallocate failed or it is not there...
// fill file with zeros from FileNullBuffer
size_t writeSize = TRI_GetNullBufferSizeFiles();
size_t written = 0;
while (written < maximalSize) {
if (writeSize + written > maximalSize) {
writeSize = maximalSize - written;
}
ssize_t writeResult = TRI_WRITE(fd, TRI_GetNullBufferFiles(),
static_cast<TRI_write_t>(writeSize));
TRI_IF_FAILURE("CreateDatafile2") {
// intentionally fail
writeResult = -1;
errno = ENOSPC;
}
if (writeResult < 0) {
if (errno == ENOSPC) {
TRI_set_errno(TRI_ERROR_ARANGO_FILESYSTEM_FULL);
LOG_TOPIC("449cf", ERR, arangodb::Logger::FIXME)
<< "cannot create datafile '" << filename << "': " << TRI_last_error();
} else {
TRI_SYSTEM_ERROR();
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG_TOPIC("2c4a6", ERR, arangodb::Logger::FIXME)
<< "cannot create datafile '" << filename << "': " << TRI_GET_ERRORBUF;
}
TRI_CLOSE(fd);
TRI_UnlinkFile(filename.c_str());
return -1;
}
written += static_cast<size_t>(writeResult);
}
}
// go back to offset 0
TRI_lseek_t offset = TRI_LSEEK(fd, (TRI_lseek_t)0, SEEK_SET);
if (offset == (TRI_lseek_t)-1) {
TRI_SYSTEM_ERROR();
TRI_set_errno(TRI_ERROR_SYS_ERROR);
TRI_CLOSE(fd);
// remove empty file
TRI_UnlinkFile(filename.c_str());
LOG_TOPIC("dfc52", ERR, arangodb::Logger::FIXME)
<< "cannot seek in datafile '" << filename << "': '" << TRI_GET_ERRORBUF << "'";
return -1;
}
return fd;
}
bool TRI_PathIsAbsolute(std::string const& path) {
#if _WIN32
return !PathIsRelativeW(toWString(path).data());
#else
return (!path.empty()) && path.c_str()[0] == '/';
#endif
}
////////////////////////////////////////////////////////////////////////////////
/// @brief initialize the files subsystem
////////////////////////////////////////////////////////////////////////////////
void TRI_InitializeFiles() {
// fill buffer with 0 bytes
memset(TRI_GetNullBufferFiles(), 0, TRI_GetNullBufferSizeFiles());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief shutdown the files subsystem
////////////////////////////////////////////////////////////////////////////////
void TRI_ShutdownFiles() {}
bool TRI_GETENV(char const* which, std::string& value) {
#ifdef _WIN32
wchar_t const* wideBuffer = _wgetenv(toWString(which).data());
if (wideBuffer == nullptr) {
return false;
}
value = fromWString(wideBuffer);
return true;
#else
char const* v = getenv(which);
if (v == nullptr) {
return false;
}
value.clear();
value = v;
return true;
#endif
}