blob: 31ae65ebb6f2895de60bb50fdcecb908b2a3bd7e [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "src/ledger/lib/files/path.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <lib/fit/function.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <functional>
#include <list>
#include <memory>
#include "src/ledger/lib/files/directory.h"
namespace ledger {
namespace {
void SafeCloseDir(DIR* dir) {
if (dir)
closedir(dir);
}
bool ForEachEntry(int root_fd, const std::string& path,
fit::function<bool(const std::string& path)> callback) {
int dir_fd = openat(root_fd, path.c_str(), O_RDONLY);
if (dir_fd == -1) {
return false;
}
std::unique_ptr<DIR, decltype(&SafeCloseDir)> dir(fdopendir(dir_fd), SafeCloseDir);
if (!dir.get())
return false;
for (struct dirent* entry = readdir(dir.get()); entry != nullptr; entry = readdir(dir.get())) {
char* name = entry->d_name;
if (name[0]) {
if (name[0] == '.') {
if (!name[1] || (name[1] == '.' && !name[2])) {
// . or ..
continue;
}
}
if (!callback(path + "/" + name))
return false;
}
}
return true;
}
} // namespace
std::string GetDirectoryName(const std::string& path) {
size_t separator = path.rfind('/');
if (separator == 0u)
return "/";
if (separator == std::string::npos)
return std::string();
return path.substr(0, separator);
}
std::string GetBaseName(const std::string& path) {
size_t separator = path.rfind('/');
if (separator == std::string::npos)
return path;
return path.substr(separator + 1);
}
bool DeletePathAt(int root_fd, const std::string& path, bool recursive) {
struct stat stat_buffer;
if (fstatat(root_fd, path.c_str(), &stat_buffer, AT_SYMLINK_NOFOLLOW) != 0)
return (errno == ENOENT || errno == ENOTDIR);
if (!S_ISDIR(stat_buffer.st_mode))
return (unlinkat(root_fd, path.c_str(), 0) == 0);
if (!recursive)
return (unlinkat(root_fd, path.c_str(), AT_REMOVEDIR) == 0);
// Use std::list, as ForEachEntry callback will modify the container. If the
// container is a vector, this will invalidate the reference to the content.
std::list<std::string> directories;
directories.push_back(path);
for (auto it = directories.begin(); it != directories.end(); ++it) {
if (!ForEachEntry(root_fd, *it, [root_fd, &directories](const std::string& child) {
if (IsDirectoryAt(root_fd, child)) {
directories.push_back(child);
} else {
if (unlinkat(root_fd, child.c_str(), 0) != 0)
return false;
}
return true;
})) {
return false;
}
}
for (auto it = directories.rbegin(); it != directories.rend(); ++it) {
if (unlinkat(root_fd, it->c_str(), AT_REMOVEDIR) != 0)
return false;
}
return true;
}
} // namespace ledger