blob: 94ca7abdfcaa0ca3deb52f62956e682d54b0a640 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "smbprovider/iterator/directory_iterator.h"
#include <utility>
#include <base/check.h>
#include <base/check_op.h>
#include <base/containers/contains.h>
#include <base/logging.h>
#include "smbprovider/constants.h"
#include "smbprovider/smbprovider_helper.h"
namespace smbprovider {
namespace {
constexpr int32_t kNoMoreEntriesError = -1;
} // namespace
BaseDirectoryIterator::BaseDirectoryIterator(const std::string& dir_path,
SambaInterface* samba_interface)
: BaseDirectoryIterator(dir_path,
samba_interface,
kDefaultMetadataBatchSize,
false /* include_metadata */) {}
BaseDirectoryIterator::BaseDirectoryIterator(const std::string& dir_path,
SambaInterface* samba_interface,
size_t batch_size)
: BaseDirectoryIterator(
dir_path, samba_interface, batch_size, false /* include_metadata */) {
}
BaseDirectoryIterator::BaseDirectoryIterator(const std::string& dir_path,
SambaInterface* samba_interface,
size_t batch_size,
bool include_metadata)
: dir_path_(dir_path),
batch_size_(batch_size),
include_metadata_(include_metadata),
samba_interface_(samba_interface->AsWeakPtr()) {}
BaseDirectoryIterator::BaseDirectoryIterator(BaseDirectoryIterator&& other)
: dir_path_(std::move(other.dir_path_)),
entries_(std::move(other.entries_)),
current_entry_index_(other.current_entry_index_),
batch_size_(other.batch_size_),
dir_id_(other.dir_id_),
is_done_(other.is_done_),
is_initialized_(other.is_initialized_),
include_metadata_(other.include_metadata_),
samba_interface_(std::move(other.samba_interface_)) {
other.dir_id_ = -1;
other.is_initialized_ = false;
other.samba_interface_ = nullptr;
}
BaseDirectoryIterator::~BaseDirectoryIterator() {
if (dir_id_ != -1) {
CloseDirectory();
}
}
int32_t BaseDirectoryIterator::Init() {
DCHECK(!is_initialized_);
int32_t open_dir_error = OpenDirectory();
if (open_dir_error != 0) {
return open_dir_error;
}
is_initialized_ = true;
return Next();
}
int32_t BaseDirectoryIterator::Next() {
DCHECK(is_initialized_);
DCHECK(!is_done_);
++current_entry_index_;
if (current_entry_index_ >= entries_.size()) {
int32_t result = FillBuffer();
if (result != 0 && result != kNoMoreEntriesError) {
return result;
}
}
return 0;
}
const DirectoryEntry& BaseDirectoryIterator::Get() {
DCHECK(is_initialized_);
DCHECK(!is_done_);
DCHECK_LT(current_entry_index_, entries_.size());
return entries_[current_entry_index_];
}
bool BaseDirectoryIterator::IsDone() {
DCHECK(is_initialized_);
return is_done_;
}
int32_t BaseDirectoryIterator::OpenDirectory() {
DCHECK_EQ(-1, dir_id_);
return samba_interface_->OpenDirectory(dir_path_, &dir_id_);
}
void BaseDirectoryIterator::CloseDirectory() {
const int32_t dir_id = std::exchange(dir_id_, -1);
DCHECK_NE(-1, dir_id);
if (!samba_interface_) {
LOG(ERROR) << "Cannot close directory [" << dir_id
<< "]: Samba implementation already deleted";
return;
}
const int32_t error = samba_interface_->CloseDirectory(dir_id);
if (error != 0) {
LOG(ERROR) << "Cannot close directory [" << dir_id
<< "]: " << GetErrorFromErrno(error);
}
}
int32_t BaseDirectoryIterator::FillBuffer() {
int32_t fetch_error = include_metadata_ ? ReadEntriesWithMetadataToVector()
: ReadEntriesToVector();
if (fetch_error != 0) {
return fetch_error;
}
if (entries_.empty()) {
// Succeeded but nothing valid left to read.
is_done_ = true;
return kNoMoreEntriesError;
}
return 0;
}
int32_t BaseDirectoryIterator::ReadEntriesToVector() {
DCHECK(!include_metadata_);
DCHECK_GT(batch_size_, 0);
ClearVector();
for (size_t i = 0; i < batch_size_; i++) {
const struct smbc_dirent* dirent = nullptr;
int fetch_error = samba_interface_->GetDirectoryEntry(dir_id_, &dirent);
if (fetch_error) {
return fetch_error;
}
if (!dirent) {
// There are no more files, but this is not an error. The next call to
// refill the buffer will hit this case on the first iteration of the
// loop and will return an empty vector which will cause FillBuffer()
// to set |done_| and return |kNoMoreEntriesError|.
return 0;
}
AddEntryIfValid(*dirent);
}
// Completed the batch successfully.
return 0;
}
int32_t BaseDirectoryIterator::ReadEntriesWithMetadataToVector() {
DCHECK(include_metadata_);
DCHECK_GT(batch_size_, 0);
ClearVector();
for (size_t i = 0; i < batch_size_; i++) {
const struct libsmb_file_info* file_info = nullptr;
int fetch_error =
samba_interface_->GetDirectoryEntryWithMetadata(dir_id_, &file_info);
if (fetch_error) {
return fetch_error;
}
if (!file_info) {
// There are no more files, but this is not an error. The next call to
// refill the buffer will hit this case on the first iteration of the
// loop and will return an empty vector which will cause FillBuffer()
// to set |done_| and return |kNoMoreEntriesError|.
return 0;
}
AddEntryIfValid(*file_info);
}
// Completed the batch successfully.
return 0;
}
void BaseDirectoryIterator::ClearVector() {
entries_.clear();
current_entry_index_ = 0;
}
void BaseDirectoryIterator::AddEntryIfValid(const smbc_dirent& dirent) {
const std::string name(dirent.name);
// Ignore "." and ".." entries.
// TODO(allenvic): Handle SMBC_LINK
if (IsSelfOrParentDir(name) || !ShouldIncludeEntryType(dirent.smbc_type) ||
base::Contains(name, '/') || base::Contains(name, '\\')) {
return;
}
bool is_directory =
dirent.smbc_type == SMBC_DIR || dirent.smbc_type == SMBC_FILE_SHARE;
entries_.emplace_back(is_directory, name, AppendPath(dir_path_, name));
}
void BaseDirectoryIterator::AddEntryIfValid(
const struct libsmb_file_info& file_info) {
const std::string name(file_info.name);
const uint16_t attrs(file_info.attrs);
// Ignore "." and ".." entries as well as symlinks.
// TODO(zentaro): Investigate how this API deals with directories that are
// file shares.
if (IsSelfOrParentDir(name) || IsSymlink(file_info.attrs) ||
base::Contains(name, '/') || base::Contains(name, '\\')) {
return;
}
bool is_directory = attrs & kFileAttributeDirectory;
entries_.emplace_back(is_directory, name, AppendPath(dir_path_, name),
file_info.size, file_info.mtime_ts.tv_sec);
}
} // namespace smbprovider