blob: 4d17ba0944e8c2878cfaf17e9f9bb59b550f0f7d [file] [log] [blame]
// Copyright 2018 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "filesystem.h"
#ifndef _WIN32
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#else
#include "config_win.h"
#endif
#include <fstream>
#include "file_dir.h"
#include "glog/logging.h"
#include "path.h"
using std::string;
namespace file {
#ifdef _WIN32
// unlink is deprecated on Win. Use _unlink.
#define unlink _unlink
#endif // _WIN32
::util::Status RecursivelyDelete(absl::string_view path,
const file::Options& options) {
string name(path);
// TODO: rewrite non recursive like devtools/goma/server/dirutil.cc?
std::vector<devtools_goma::DirEntry> entries;
if (!devtools_goma::ListDirectory(name, &entries)) {
return ::util::Status(false);
}
if (entries.empty()) {
if (unlink(name.c_str()) != 0) {
return ::util::Status(false);
}
}
for (const auto& ent : entries) {
if (ent.name == "." || ent.name == "..") {
continue;
}
const string& filename = file::JoinPath(name, ent.name);
if (ent.is_dir) {
::util::Status status = RecursivelyDelete(filename, options);
if (!status.ok()) {
return status;
}
} else {
if (unlink(filename.c_str()) != 0) {
return ::util::Status(false);
}
}
}
if (!devtools_goma::DeleteDirectory(name)) {
return ::util::Status(false);
}
return ::util::Status(true);
}
::util::Status IsDirectory(absl::string_view path, const file::Options&) {
string str = string(path);
#ifndef _WIN32
struct stat st;
if (stat(str.c_str(), &st) == 0) {
return ::util::Status(S_ISDIR(st.st_mode));
}
return ::util::Status(false);
#else
DWORD attr = GetFileAttributesA(str.c_str());
if (attr == INVALID_FILE_ATTRIBUTES)
return ::util::Status(false);
return ::util::Status((attr & FILE_ATTRIBUTE_DIRECTORY) ==
FILE_ATTRIBUTE_DIRECTORY);
#endif
}
::util::Status CreateDir(absl::string_view path, const file::Options& options) {
string cpath(path);
#ifndef _WIN32
int r = mkdir(cpath.c_str(), options.creation_mode());
if (r < 0) {
PLOG(ERROR) << "CreateDir failed: " << path;
return ::util::Status(false);
}
#else
UNREFERENCED_PARAMETER(options);
if (!CreateDirectoryA(cpath.c_str(), nullptr)) {
DWORD err = GetLastError();
LOG(ERROR) << "CreateDir failed: " << cpath << ": " << err;
LOG_SYSRESULT(err);
return ::util::Status(false);
}
#endif
return ::util::Status(true);
}
::util::Status Copy(absl::string_view from,
absl::string_view to,
const Options& options) {
string cfrom(from);
string cto(to);
#ifdef _WIN32
if (!CopyFileA(cfrom.c_str(), cto.c_str(), !options.overwrite())) {
DWORD err = GetLastError();
LOG_SYSRESULT(err);
LOG(WARNING) << "failed to copy file:"
<< " from=" << from << " to=" << to;
return ::util::Status(false);
}
return ::util::Status(true);
#else
std::ifstream ifs(cfrom, std::ifstream::binary);
if (!ifs) {
LOG(WARNING) << "Input file not found: " << from;
return ::util::Status(false);
}
struct stat stat_buf;
if (!options.overwrite() && (0 == stat(cto.c_str(), &stat_buf))) {
LOG(ERROR) << "File " << to << " exists and overwrite is disabled";
return ::util::Status(false);
}
std::ofstream ofs(cto, std::ofstream::binary);
if (!ofs) {
LOG(WARNING) << "Cannot open output file: " << to;
return ::util::Status(false);
}
::util::Status status(true);
while (!ifs.eof()) {
char buf[4096];
ifs.read(buf, sizeof(buf));
if (ifs.fail() && !ifs.eof()) {
LOG(WARNING) << "Failed to read file from: " << from;
return ::util::Status(false);
}
ofs.write(buf, ifs.gcount());
}
return ::util::Status(true);
#endif
}
} // namespace file