blob: 65965b222d5c60cc51a9c853ef46df49c4d22a7a [file] [log] [blame]
// Copyright 2021 The Chromium 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 "quiche_platform_impl/quiche_file_utils_impl.h"
#if defined(_WIN32)
#include <windows.h>
#else
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif // defined(_WIN32)
#include <fstream>
#include <ios>
#include <iostream>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/types/optional.h"
namespace quiche {
#if defined(_WIN32)
std::string JoinPathImpl(absl::string_view a, absl::string_view b) {
if (a.empty()) {
return std::string(b);
}
if (b.empty()) {
return std::string(a);
}
// Win32 actually provides two different APIs for combining paths; one of them
// has issues that could potentially lead to buffer overflow, and another is
// not supported in Windows 7, which is why we're doing it manually.
a = absl::StripSuffix(a, "/");
a = absl::StripSuffix(a, "\\");
return absl::StrCat(a, "\\", b);
}
#else
std::string JoinPathImpl(absl::string_view a, absl::string_view b) {
if (a.empty()) {
return std::string(b);
}
if (b.empty()) {
return std::string(a);
}
return absl::StrCat(absl::StripSuffix(a, "/"), "/", b);
}
#endif // defined(_WIN32)
absl::optional<std::string> ReadFileContentsImpl(absl::string_view file) {
std::ifstream input_file(std::string{file}, std::ios::binary);
if (!input_file || !input_file.is_open()) {
return absl::nullopt;
}
input_file.seekg(0, std::ios_base::end);
auto file_size = input_file.tellg();
if (!input_file) {
return absl::nullopt;
}
input_file.seekg(0, std::ios_base::beg);
std::string output;
output.resize(file_size);
input_file.read(&output[0], file_size);
if (!input_file) {
return absl::nullopt;
}
return output;
}
#if defined(_WIN32)
class ScopedDir {
public:
ScopedDir(HANDLE dir) : dir_(dir) {}
~ScopedDir() {
if (dir_ != INVALID_HANDLE_VALUE) {
// The API documentation explicitly says that CloseHandle() should not be
// used on directory search handles.
FindClose(dir_);
dir_ = INVALID_HANDLE_VALUE;
}
}
HANDLE get() { return dir_; }
private:
HANDLE dir_;
};
bool EnumerateDirectoryImpl(absl::string_view path,
std::vector<std::string>& directories,
std::vector<std::string>& files) {
std::string path_owned(path);
// Explicitly check that the directory we are trying to search is in fact a
// directory.
DWORD attributes = GetFileAttributesA(path_owned.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
return false;
}
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
return false;
}
std::string search_path = JoinPathImpl(path, "*");
WIN32_FIND_DATAA file_data;
ScopedDir dir(FindFirstFileA(search_path.c_str(), &file_data));
if (dir.get() == INVALID_HANDLE_VALUE) {
return GetLastError() == ERROR_FILE_NOT_FOUND;
}
do {
std::string filename(file_data.cFileName);
if (filename == "." || filename == "..") {
continue;
}
if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
directories.push_back(std::move(filename));
} else {
files.push_back(std::move(filename));
}
} while (FindNextFileA(dir.get(), &file_data));
return GetLastError() == ERROR_NO_MORE_FILES;
}
#else // defined(_WIN32)
class ScopedDir {
public:
ScopedDir(DIR* dir) : dir_(dir) {}
~ScopedDir() {
if (dir_ != nullptr) {
closedir(dir_);
dir_ = nullptr;
}
}
DIR* get() { return dir_; }
private:
DIR* dir_;
};
bool EnumerateDirectoryImpl(absl::string_view path,
std::vector<std::string>& directories,
std::vector<std::string>& files) {
std::string path_owned(path);
ScopedDir dir(opendir(path_owned.c_str()));
if (dir.get() == nullptr) {
return false;
}
dirent* entry;
while ((entry = readdir(dir.get()))) {
const std::string filename(entry->d_name);
if (filename == "." || filename == "..") {
continue;
}
const std::string entry_path = JoinPathImpl(path, filename);
struct stat stat_entry;
if (stat(entry_path.c_str(), &stat_entry) != 0) {
return false;
}
if (S_ISREG(stat_entry.st_mode)) {
files.push_back(std::move(filename));
} else if (S_ISDIR(stat_entry.st_mode)) {
directories.push_back(std::move(filename));
}
}
return true;
}
#endif // defined(_WIN32)
} // namespace quiche