master
cpp 211 lines 5.34 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #include "precomp.h"
3 #include "p9errors.h"
4 #include "p9handler.h"
5 #include "p9file.h"
6 #include "p9fs.h"
7 #include "p9lx.h"
8 #include "p9util.h"
9 #include "p9tracelogging.h"
10
11 namespace p9fs {
12
13 constexpr const char* c_NobodyGroupName = "nobody";
14
15 class ShareList final : public IShareList
16 {
17 public:
18 void Add(const std::string& name, int rootFd);
19 void Remove(const std::string& name);
20 std::shared_ptr<const Share> Get(std::string_view name);
21 size_t MaximumConnectionCount() override;
22 Expected<std::shared_ptr<const IRoot>> MakeRoot(std::string_view aname, LX_UID_T uid) override;
23
24 private:
25 std::mutex m_ShareLock;
26 std::map<std::string, std::shared_ptr<Share>, std::less<>> m_Shares;
27 };
28
29 void ShareList::Add(const std::string& name, int rootFd)
30 {
31 auto share = std::make_shared<Share>();
32 share->RootFd.reset(rootFd);
33 THROW_LAST_ERROR_IF(!share->RootFd);
34
35 std::lock_guard<std::mutex> lock{m_ShareLock};
36 const bool inserted = m_Shares.try_emplace(name, std::move(share)).second;
37 if (!inserted)
38 {
39 THROW_ERRNO(EEXIST);
40 }
41 }
42
43 void ShareList::Remove(const std::string& name)
44 {
45 std::lock_guard<std::mutex> lock{m_ShareLock};
46 const auto share = m_Shares.find(name);
47 if (share == m_Shares.end())
48 {
49 THROW_ERRNO(ENOENT);
50 }
51
52 m_Shares.erase(share);
53 }
54
55 std::shared_ptr<const Share> ShareList::Get(std::string_view name)
56 {
57 std::lock_guard<std::mutex> lock{m_ShareLock};
58 auto it = m_Shares.find(name);
59 if (it != m_Shares.end())
60 {
61 return it->second;
62 }
63
64 return {};
65 }
66
67 // Returns the maximum number of concurrent connections that should be allowed
68 // based on the number and configuration of the shares.
69 size_t ShareList::MaximumConnectionCount()
70 {
71 return 4096;
72 }
73
74 Expected<std::shared_ptr<const IRoot>> ShareList::MakeRoot(std::string_view aname, LX_UID_T uid)
75 {
76 auto share = Get(aname);
77 if (!share)
78 {
79 return LxError{LX_ENOENT};
80 }
81
82 gid_t gid;
83 uid_t currentUid = geteuid();
84 if (uid == currentUid)
85 {
86 // No need to change IDs if the requested user matches the user the server is running as.
87 uid = util::c_InvalidUid;
88 gid = util::c_InvalidGid;
89 }
90 else if (currentUid == 0)
91 {
92 gid = util::GetUserGroupId(uid);
93 if (gid == util::c_InvalidGid)
94 {
95 // The user wasn't found in /etc/passwd, so use "nobody" as the group.
96 gid = util::GetGroupIdByName(c_NobodyGroupName);
97 if (gid == util::c_InvalidGid)
98 {
99 // No group named nobody, so fail the connection.
100 return LxError{LX_EINVAL};
101 }
102 }
103 }
104 else
105 {
106 // The server is not running as root, which won't work.
107 // N.B. It's possible to make this work as long as the server has CAP_SETUID, but that
108 // is currently not needed.
109 return LxError{LX_EPERM};
110 }
111
112 std::shared_ptr<const IRoot> root = std::make_shared<const Root>(share, share->RootFd.get(), uid, gid);
113 return root;
114 }
115
116 class FileSystem final : public IPlan9FileSystem
117 {
118 public:
119 // Creates a new file system, using the specified socket to listen.
120 // N.B. The socket must already be bound to an appropriate local address.
121 // N.B. The file system class takes ownership of the socket.
122 FileSystem(int socket)
123 {
124 if (!g_Watcher)
125 {
126 g_Watcher.Run();
127 }
128
129 m_Server.Reset(socket);
130 THROW_LAST_ERROR_IF(listen(socket, 1) < 0);
131 }
132
133 // Destructs a file system instance.
134 ~FileSystem() noexcept override
135 try
136 {
137 // Make sure the task finishes.
138 Pause();
139 }
140 CATCH_LOG()
141
142 // Add a share to the file system.
143 // N.B. The root FD is duplicated so this function does not take ownership of it.
144 void AddShare(const std::string& name, int rootFd) override
145 {
146 m_ShareList.Add(name, rootFd);
147 }
148
149 // Cancels any outstanding operations and stops listening for new connections.
150 void Pause() override
151 {
152 if (m_RunTask)
153 {
154 Plan9TraceLoggingProvider::ServerStop();
155 m_CancelToken.Cancel();
156 try
157 {
158 m_RunTask.Get();
159 }
160 catch (...)
161 {
162 auto error = util::LinuxErrorFromCaughtException();
163 if (error != LX_ECANCELED)
164 {
165 LOG_CAUGHT_EXCEPTION();
166 }
167 }
168
169 m_CancelToken.Reset();
170 m_RunTask = {};
171 }
172 }
173
174 // Runs the file system.
175 void Resume() override
176 {
177 Plan9TraceLoggingProvider::ServerStart();
178 m_RunTask = Run();
179 }
180
181 // Tears down the server socket.
182 void Teardown() override
183 {
184 m_Server.Reset();
185 }
186
187 bool HasConnections() const noexcept override
188 {
189 return m_WaitGroup.HasMembers();
190 }
191
192 private:
193 // Asynchronously handles incoming connections.
194 AsyncTask Run() noexcept
195 {
196 return HandleConnections(m_Server, m_ShareList, m_CancelToken, m_WaitGroup);
197 }
198
199 Socket m_Server;
200 AsyncTask m_RunTask;
201 CancelToken m_CancelToken;
202 WaitGroup m_WaitGroup;
203 ShareList m_ShareList;
204 };
205
206 std::unique_ptr<IPlan9FileSystem> CreateFileSystem(int socket)
207 {
208 return std::make_unique<FileSystem>(socket);
209 }
210
211 } // namespace p9fs