1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <unistd.h> 17 18 #include <map> 19 #include <string> 20 21 #include "pw_log/log.h" 22 23 // TODO(b/328262654): Move this to a more appropriate module. 24 namespace pw::digital_io { 25 26 class MockVfs; 27 28 // A mocked representation of an open file in the Linux kernel. 29 // Consumers are expected to inherit from this class and implement their own 30 // custom test file behaviors. 31 class MockFile { 32 public: MockFile(MockVfs & vfs,int eventfd,const std::string & name)33 MockFile(MockVfs& vfs, int eventfd, const std::string& name) 34 : vfs_(vfs), eventfd_(eventfd), name_(name) {} 35 virtual ~MockFile() = default; 36 vfs()37 MockVfs& vfs() const { return vfs_; } eventfd()38 int eventfd() const { return eventfd_; } name()39 const std::string& name() { return name_; } 40 41 // Public interface, intended for use by tests. 42 void WriteEventfd(uint64_t add = 1); 43 uint64_t ReadEventfd(); 44 45 // Public interface, intended for use by MockVfs. 46 // These methods conform closely to syscalls of the same name. 47 int Close(); Ioctl(unsigned long request,void * arg)48 int Ioctl(unsigned long request, void* arg) { return DoIoctl(request, arg); } Read(void * buf,size_t count)49 ssize_t Read(void* buf, size_t count) { return DoRead(buf, count); } 50 51 private: 52 MockVfs& vfs_; 53 // NOTE: We can't use OwnedFd here because it calls close(), which is wrapped 54 // in mock_vfs, which would lead to recursion. 55 static constexpr int kInvalidFd = -1; 56 int eventfd_ = kInvalidFd; 57 const std::string name_; 58 59 // Derived class interface 60 // These methods conform closely to syscalls of the same name. DoClose()61 virtual int DoClose() { return 0; } 62 DoIoctl(unsigned long,void *)63 virtual int DoIoctl(unsigned long /* request */, void* /* arg */) { 64 PW_LOG_ERROR("[%s] Ioctl unimplemented", name_.c_str()); 65 return -1; 66 } 67 DoRead(void *,size_t)68 virtual ssize_t DoRead(void* /* buf */, size_t /* count */) { 69 PW_LOG_ERROR("[%s] Read unimplemented", name_.c_str()); 70 return -1; 71 } 72 }; 73 74 // A mocked representation of the Linux kernel's Virtual File System (VFS). 75 // Tracks the association of (fake) file descriptors -> open MockFile objects 76 // and provides a subset of mocked system calls which are handled by invoking 77 // methods on said MockFile objects. 78 class MockVfs { 79 public: MockVfs()80 MockVfs() {} 81 MockVfs(const MockVfs& other) = delete; 82 MockVfs(const MockVfs&& other) = delete; 83 MockVfs operator=(const MockVfs& other) = delete; 84 MockVfs operator=(const MockVfs&& other) = delete; 85 86 // Returns true if the fd is in the range of mocked fds, not if it is open. 87 bool IsMockFd(const int fd); 88 89 // Creates a new MockFile object associated with this vfs. 90 // The FileType template argument must be MockFile or a derived class. 91 // Arguments are forwarded to the FileType constructor. 92 template <class FileType, typename... Args> MakeFile(Args &&...args)93 std::unique_ptr<FileType> MakeFile(Args&&... args) { 94 return std::make_unique<FileType>( 95 *this, GetEventFd(), std::forward<Args>(args)...); 96 } 97 98 // Installs a MockFile object into this vfs, and returns a newly-assigned fd. 99 int InstallFile(std::unique_ptr<MockFile>&& file); 100 101 // Creates and installs a new MockFile object into this vfs and returns a 102 // newly-assigned fd. See MakeFile() and InstallFile(). 103 template <class FileType, typename... Args> InstallNewFile(Args &&...args)104 int InstallNewFile(Args&&... args) { 105 return InstallFile(MakeFile<FileType>(std::forward<Args>(args)...)); 106 } 107 108 // Returns true if there are no open files. 109 bool AllFdsClosed() const; 110 111 // Resets the vfs to its default state, closing any open files. 112 void Reset(); 113 114 // Mocked syscalls 115 int mock_close(int fd); 116 int mock_ioctl(int fd, unsigned long request, void* arg); 117 ssize_t mock_read(int fd, void* buf, size_t count); 118 119 private: 120 // All open files. Key is (real) fd number. 121 std::map<int, std::unique_ptr<MockFile>> open_files_; 122 123 int GetEventFd(); 124 125 // Gets the MockFile object associated with the given fd. 126 // If fd refers to an open MockFile, a pointer to that file is returned (and 127 // file continues to be owned by the vfs). Otherwise, returns nullptr. 128 MockFile* GetFile(const int fd) const; 129 }; 130 131 // Get a reference to a global MockVfs singleton object. 132 // This object is the one referenced by the (necessarily) global system call 133 // wrappers enabled via the --wrap= linker option. 134 MockVfs& GetMockVfs(); 135 136 } // namespace pw::digital_io 137