#include "fs.h"
#include "defaults.h"
#if defined(_win_)
#include "fs_win.h"
#else
#include <unistd.h>
#include <errno.h>
#endif
#include <util/generic/yexception.h>
#include <util/memory/tempbuf.h>
#include <util/stream/file.h>
#include <util/charset/wide.h>
#include <util/folder/iterator.h>
#include <util/system/fstat.h>
#include <util/folder/path.h>
bool NFs::Remove(const TString& path) {
#if defined(_win_)
return NFsPrivate::WinRemove(path);
#else
return ::remove(path.data()) == 0;
#endif
}
void NFs::RemoveRecursive(const TString& path) {
static const TStringBuf errStr = "error while removing ";
if (!NFs::Exists(path)) {
return;
}
if (!TFileStat(path).IsDir()) {
if (!NFs::Remove(path)) {
ythrow TSystemError() << errStr << path << " with cwd (" << NFs::CurrentWorkingDirectory() << ")";
}
}
TDirIterator dir(path);
for (auto it = dir.begin(); it != dir.end(); ++it) {
switch (it->fts_info) {
case FTS_DOT:
case FTS_D:
break;
default:
if (!NFs::Remove(it->fts_path)) {
ythrow TSystemError() << errStr << it->fts_path << " with cwd (" << NFs::CurrentWorkingDirectory() << ")";
}
break;
}
}
}
bool NFs::MakeDirectory(const TString& path, EFilePermissions mode) {
#if defined(_win_)
Y_UNUSED(mode);
return NFsPrivate::WinMakeDirectory(path);
#else
return mkdir(path.data(), mode) == 0;
#endif
}
bool NFs::MakeDirectoryRecursive(const TString& path, EFilePermissions mode, bool alwaysCreate) {
if (NFs::Exists(path) && TFileStat(path).IsDir()) {
if (alwaysCreate) {
ythrow TIoException() << "path " << path << " already exists"
<< " with cwd (" << NFs::CurrentWorkingDirectory() << ")";
}
return true;
} else {
//NOTE: recursion is finite due to existence of "." and "/"
if (!NFs::MakeDirectoryRecursive(TFsPath(path).Parent(), mode, false)) {
return false;
}
bool isDirMade = NFs::MakeDirectory(path, mode);
if (!isDirMade && alwaysCreate) {
ythrow TIoException() << "failed to create " << path << " with cwd (" << NFs::CurrentWorkingDirectory() << ")";
}
return TFileStat(path).IsDir();
}
}
bool NFs::Rename(const TString& oldPath, const TString& newPath) {
#if defined(_win_)
return NFsPrivate::WinRename(oldPath, newPath);
#else
return ::rename(oldPath.data(), newPath.data()) == 0;
#endif
}
void NFs::HardLinkOrCopy(const TString& existingPath, const TString& newPath) {
if (!NFs::HardLink(existingPath, newPath)) {
Copy(existingPath, newPath);
}
}
bool NFs::HardLink(const TString& existingPath, const TString& newPath) {
#if defined(_win_)
return NFsPrivate::WinHardLink(existingPath, newPath);
#elif defined(_unix_)
return (0 == link(existingPath.data(), newPath.data()));
#endif
}
bool NFs::SymLink(const TString& targetPath, const TString& linkPath) {
#if defined(_win_)
return NFsPrivate::WinSymLink(targetPath, linkPath);
#elif defined(_unix_)
return 0 == symlink(targetPath.data(), linkPath.data());
#endif
}
TString NFs::ReadLink(const TString& path) {
#if defined(_win_)
return NFsPrivate::WinReadLink(path);
#elif defined(_unix_)
TTempBuf buf;
while (true) {
ssize_t r = readlink(path.data(), buf.Data(), buf.Size());
if (r < 0) {
ythrow yexception() << "can't read link " << path << ", errno = " << errno;
}
if (r < (ssize_t)buf.Size()) {
return TString(buf.Data(), r);
}
buf = TTempBuf(buf.Size() * 2);
}
#endif
}
void NFs::Cat(const TString& dstPath, const TString& srcPath) {
TUnbufferedFileInput src(srcPath);
TUnbufferedFileOutput dst(TFile(dstPath, ForAppend | WrOnly | Seq));
TransferData(&src, &dst);
}
void NFs::Copy(const TString& existingPath, const TString& newPath) {
TUnbufferedFileInput src(existingPath);
TUnbufferedFileOutput dst(TFile(newPath, CreateAlways | WrOnly | Seq));
TransferData(&src, &dst);
}
bool NFs::Exists(const TString& path) {
#if defined(_win_)
return NFsPrivate::WinExists(path);
#elif defined(_unix_)
return access(path.data(), F_OK) == 0;
#endif
}
TString NFs::CurrentWorkingDirectory() {
#if defined(_win_)
return NFsPrivate::WinCurrentWorkingDirectory();
#elif defined(_unix_)
TTempBuf result;
char* r = getcwd(result.Data(), result.Size());
if (r == nullptr) {
throw TIoSystemError() << "failed to getcwd";
}
return result.Data();
#endif
}
void NFs::SetCurrentWorkingDirectory(TString path) {
#ifdef _win_
bool ok = NFsPrivate::WinSetCurrentWorkingDirectory(path);
#else
bool ok = !chdir(path.data());
#endif
if (!ok) {
ythrow TSystemError() << "failed to change directory to " << path.Quote();
}
}