blob: e0a4656c0e0cf83d6352106805e2f312122f0621 [file] [log] [blame]
// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "mock_vfs.h"
// TODO(b/328262654): Move this to a more appropriate module.
namespace pw::digital_io {
MockVfs& GetMockVfs() {
static MockVfs vfs;
return vfs;
}
MockFile* MockVfs::GetFile(const int fd) const {
if (fd < kFakeFdBase) {
return nullptr;
}
const size_t index = fd - kFakeFdBase;
if (index >= open_files_.size()) {
return nullptr;
}
return open_files_[index].get();
}
int MockVfs::InstallFile(std::unique_ptr<MockFile>&& file) {
// Find the lowest unused index.
size_t index;
for (index = 0; index < open_files_.size(); ++index) {
if (open_files_[index] == nullptr) {
break;
}
}
const int fd = kFakeFdBase + index;
PW_LOG_DEBUG("Installing fd %d: \"%s\"", fd, file->name().c_str());
const size_t min_size = index + 1;
if (min_size > open_files_.size()) {
open_files_.resize(min_size);
}
open_files_[index] = std::move(file);
return fd;
}
bool MockVfs::AllFdsClosed() const {
for (const auto& f : open_files_) {
if (f != nullptr) {
return false;
}
}
return true;
}
int MockVfs::mock_close(int fd) {
auto* file = GetFile(fd);
if (!file) {
// TODO: https://pwbug.dev/338269682 - The return value of close(2) is
// frequently ignored, so provide a hook here to let consumers handle this
// error.
errno = EBADF;
return -1;
}
int result = file->Close();
const int index = fd - kFakeFdBase;
open_files_[index] = nullptr;
return result;
}
int MockVfs::mock_ioctl(int fd, unsigned long request, void* arg) {
auto* file = GetFile(fd);
if (!file) {
errno = EBADF;
return -1;
}
return file->Ioctl(request, arg);
}
////////////////////////////////////////////////////////////////////////////////
// Syscalls wrapped via --wrap
#include <unistd.h>
extern "C" {
// close()
decltype(close) __real_close;
decltype(close) __wrap_close;
int __wrap_close(int fd) {
auto& vfs = GetMockVfs();
if (vfs.IsMockFd(fd)) {
return vfs.mock_close(fd);
}
return __real_close(fd);
}
// ioctl()
// ioctl() is actually variadic (third arg is ...), but there's no way
// (https://c-faq.com/varargs/handoff.html) to forward the args when invoked
// that way, so we lie and use void*.
int __real_ioctl(int fd, unsigned long request, void* arg);
int __wrap_ioctl(int fd, unsigned long request, void* arg);
int __wrap_ioctl(int fd, unsigned long request, void* arg) {
auto& vfs = GetMockVfs();
if (vfs.IsMockFd(fd)) {
return vfs.mock_ioctl(fd, request, arg);
}
return __real_ioctl(fd, request, arg);
}
} // extern "C"
} // namespace pw::digital_io