xref: /aosp_15_r20/external/pigweed/pw_digital_io_linux/mock_vfs.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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