blob: a11e3302d3173574feb4c22f0f5d7db196515772 [file] [log] [blame]
#include "aos/util/file.h"
#include <fcntl.h>
#include <fts.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string_view>
#include "aos/scoped/scoped_fd.h"
namespace aos {
namespace util {
::std::string ReadFileToStringOrDie(const std::string_view filename) {
::std::string r;
ScopedFD fd(open(::std::string(filename).c_str(), O_RDONLY));
PCHECK(fd.get() != -1) << ": opening " << filename;
while (true) {
char buffer[1024];
const ssize_t result = read(fd.get(), buffer, sizeof(buffer));
PCHECK(result >= 0) << ": reading from " << filename;
if (result == 0) {
break;
}
r.append(buffer, result);
}
return r;
}
void WriteStringToFileOrDie(const std::string_view filename,
const std::string_view contents,
mode_t permissions) {
::std::string r;
ScopedFD fd(open(::std::string(filename).c_str(),
O_CREAT | O_WRONLY | O_TRUNC, permissions));
PCHECK(fd.get() != -1) << ": opening " << filename;
size_t size_written = 0;
while (size_written != contents.size()) {
const ssize_t result = write(fd.get(), contents.data() + size_written,
contents.size() - size_written);
PCHECK(result >= 0) << ": reading from " << filename;
if (result == 0) {
break;
}
size_written += result;
}
}
bool MkdirPIfSpace(std::string_view path, mode_t mode) {
auto last_slash_pos = path.find_last_of("/");
std::string folder(last_slash_pos == std::string_view::npos
? std::string_view("")
: path.substr(0, last_slash_pos));
if (folder.empty()) {
return true;
}
if (!MkdirPIfSpace(folder, mode)) {
return false;
}
const int result = mkdir(folder.c_str(), mode);
if (result == -1 && errno == EEXIST) {
VLOG(2) << folder << " already exists";
return true;
} else if (result == -1 && errno == ENOSPC) {
VLOG(2) << "Out of space";
return false;
} else {
VLOG(1) << "Created " << folder;
}
PCHECK(result == 0) << ": Error creating " << folder;
return true;
}
bool PathExists(std::string_view path) {
struct stat buffer;
return stat(path.data(), &buffer) == 0;
}
void UnlinkRecursive(std::string_view path) {
FTS *ftsp = NULL;
FTSENT *curr;
// Cast needed (in C) because fts_open() takes a "char * const *", instead
// of a "const char * const *", which is only allowed in C++. fts_open()
// does not modify the argument.
std::string p(path);
char *files[] = {const_cast<char *>(p.c_str()), NULL};
// FTS_NOCHDIR - Avoid changing cwd, which could cause unexpected behavior
// in multithreaded programs
// FTS_PHYSICAL - Don't follow symlinks. Prevents deletion of files outside
// of the specified directory
// FTS_XDEV - Don't cross filesystem boundaries
ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL);
if (!ftsp) {
return;
}
while ((curr = fts_read(ftsp))) {
switch (curr->fts_info) {
case FTS_NS:
case FTS_DNR:
case FTS_ERR:
LOG(WARNING) << "Can't read " << curr->fts_accpath;
break;
case FTS_DC:
case FTS_DOT:
case FTS_NSOK:
// Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were
// passed to fts_open()
break;
case FTS_D:
// Do nothing. Need depth-first search, so directories are deleted
// in FTS_DP
break;
case FTS_DP:
case FTS_F:
case FTS_SL:
case FTS_SLNONE:
case FTS_DEFAULT:
VLOG(1) << "Removing " << curr->fts_path;
if (remove(curr->fts_accpath) < 0) {
LOG(WARNING) << curr->fts_path
<< ": Failed to remove: " << strerror(curr->fts_errno);
}
break;
}
}
if (ftsp) {
fts_close(ftsp);
}
}
} // namespace util
} // namespace aos