-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsmb_filesystem.h
361 lines (308 loc) · 13.6 KB
/
smb_filesystem.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// Copyright 2019 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.
#ifndef SMBFS_SMB_FILESYSTEM_H_
#define SMBFS_SMB_FILESYSTEM_H_
#include <libsmbclient.h>
#include <sys/types.h>
#include <atomic>
#include <memory>
#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <base/callback.h>
#include <base/containers/mru_cache.h>
#include <base/files/file_path.h>
#include <base/macros.h>
#include <base/memory/weak_ptr.h>
#include <base/synchronization/lock.h>
#include <base/threading/thread.h>
#include <base/time/time.h>
#include <base/threading/thread_task_runner_handle.h>
#include <gtest/gtest_prod.h>
#include "smbfs/filesystem.h"
#include "smbfs/inode_map.h"
#include "smbfs/recursive_delete_operation.h"
#include "smbfs/smb_credential.h"
namespace smbfs {
class SambaInterface;
class SmbFilesystem : public Filesystem {
public:
// Delegate functions will always be called on the SmbFilesystem's constructor
// thread. Any callback parameters must be invoked on the caller thread (read:
// constructor thread).
class Delegate {
public:
using RequestCredentialsCallback =
base::OnceCallback<void(std::unique_ptr<SmbCredential> credentials)>;
// Request username/password auth credentials for the share. Invoke
// |callback| with the requested credentials, or with nullptr if no
// credentials are provided (eg. if the user closes the request dialog).
virtual void RequestCredentials(RequestCredentialsCallback callback) = 0;
};
struct Options {
Options();
~Options();
// Allow moves.
Options(Options&&);
Options& operator=(Options&&);
std::string share_path;
uid_t uid = 0;
gid_t gid = 0;
std::unique_ptr<SmbCredential> credentials;
bool allow_ntlm = false;
bool use_kerberos = false;
};
enum class ConnectError {
kOk = 0,
kNotFound,
kAccessDenied,
kSmb1Unsupported,
kUnknownError,
};
SmbFilesystem(Delegate* delegate, Options options);
~SmbFilesystem() override;
base::WeakPtr<SmbFilesystem> GetWeakPtr();
// Ensures that the SMB share can be connected to. Must NOT be called after
// the filesystem is attached to a FUSE session.
// Virtual for testing.
virtual ConnectError EnsureConnected();
// Sets the resolved IP address of the share host. |ip_address| is an IPv4
// address in network byte order, or empty. If |ip_address| is empty, any
// existing resolved address will be reset.
// Virtual for testing.
virtual void SetResolvedAddress(const std::vector<uint8_t>& ip_address);
const std::string& resolved_share_path() const {
return resolved_share_path_;
}
// Filesystem overrides.
void StatFs(std::unique_ptr<StatFsRequest> request,
fuse_ino_t inode) override;
void Lookup(std::unique_ptr<EntryRequest> request,
fuse_ino_t parent_inode,
const std::string& name) override;
void Forget(fuse_ino_t inode, uint64_t count) override;
void GetAttr(std::unique_ptr<AttrRequest> request, fuse_ino_t inode) override;
void SetAttr(std::unique_ptr<AttrRequest> request,
fuse_ino_t inode,
base::Optional<uint64_t> file_handle,
const struct stat& attr,
int to_set) override;
void Open(std::unique_ptr<OpenRequest> request,
fuse_ino_t inode,
int flags) override;
void Create(std::unique_ptr<CreateRequest> request,
fuse_ino_t parent_inode,
const std::string& name,
mode_t mode,
int flags) override;
void Read(std::unique_ptr<BufRequest> request,
fuse_ino_t inode,
uint64_t file_handle,
size_t size,
off_t offset) override;
void Write(std::unique_ptr<WriteRequest> request,
fuse_ino_t inode,
uint64_t file_handle,
const char* buf,
size_t size,
off_t offset) override;
void Release(std::unique_ptr<SimpleRequest> request,
fuse_ino_t inode,
uint64_t file_handle) override;
void Rename(std::unique_ptr<SimpleRequest> request,
fuse_ino_t old_parent_inode,
const std::string& old_name,
fuse_ino_t new_parent_inode,
const std::string& new_name) override;
void Unlink(std::unique_ptr<SimpleRequest> request,
fuse_ino_t parent_inode,
const std::string& name) override;
void OpenDir(std::unique_ptr<OpenRequest> request,
fuse_ino_t inode,
int flags) override;
void ReadDir(std::unique_ptr<DirentryRequest> request,
fuse_ino_t inode,
uint64_t file_handle,
off_t offset) override;
void ReleaseDir(std::unique_ptr<SimpleRequest> request,
fuse_ino_t inode,
uint64_t file_handle) override;
void MkDir(std::unique_ptr<EntryRequest> request,
fuse_ino_t parent_inode,
const std::string& name,
mode_t mode) override;
void RmDir(std::unique_ptr<SimpleRequest> request,
fuse_ino_t parent_inode,
const std::string& name) override;
// mojom::SmbFs helpers.
//
// Recursively deletes |path| and all of its contents if it is a directory.
// |path| is an absolute path from the root of the share (ie. it does not
// include the smb://host portion). |callback| is called with the outcome of
// the operation.
void DeleteRecursively(const base::FilePath& path,
RecursiveDeleteOperation::CompletionCallback callback);
protected:
// Protected constructor for unit tests.
SmbFilesystem(Delegate* delegate, const std::string& share_path);
// Allow mock interface to be provided during tests.
void SetSambaInterface(std::unique_ptr<SambaInterface> samba_interface);
private:
FRIEND_TEST(SmbFilesystemTest, MakeStatModeBits);
FRIEND_TEST(SmbFilesystemTest, MaybeUpdateCredentials_NoRequest);
FRIEND_TEST(SmbFilesystemTest, MaybeUpdateCredentials_RequestOnEPERM);
FRIEND_TEST(SmbFilesystemTest, MaybeUpdateCredentials_RequestOnEACCES);
FRIEND_TEST(SmbFilesystemTest, MaybeUpdateCredentials_NoDelegate);
FRIEND_TEST(SmbFilesystemTest, MaybeUpdateCredentials_OnlyOneRequest);
FRIEND_TEST(SmbFilesystemTest, MaybeUpdateCredentials_IgnoreEmptyResponse);
// Cache stat information when listing directories to reduce unnecessary
// network requests.
struct StatCacheItem {
struct stat inode_stat;
base::Time expires_at;
};
// Filesystem implementations that execute on |samba_thread_|.
void StatFsInternal(std::unique_ptr<StatFsRequest> request, fuse_ino_t inode);
void LookupInternal(std::unique_ptr<EntryRequest> request,
fuse_ino_t parent_inode,
const std::string& name);
void ForgetInternal(fuse_ino_t inode, uint64_t count);
void GetAttrInternal(std::unique_ptr<AttrRequest> request, fuse_ino_t inode);
void SetAttrInternal(std::unique_ptr<AttrRequest> request,
fuse_ino_t inode,
base::Optional<uint64_t> file_handle,
const struct stat& attr,
int to_set);
int SetFileSizeInternal(const std::string& share_file_path,
base::Optional<uint64_t> file_handle,
off_t size,
const struct stat& current_stat,
struct stat* reply_stat);
int SetUtimesInternal(const std::string& share_file_path,
int to_set,
const struct timespec& atime,
const struct timespec& mtime,
const struct stat& current_stat,
struct stat* reply_stat);
void OpenInternal(std::unique_ptr<OpenRequest> request,
fuse_ino_t inode,
int flags);
void CreateInternal(std::unique_ptr<CreateRequest> request,
fuse_ino_t parent_inode,
const std::string& name,
mode_t mode,
int flags);
void ReadInternal(std::unique_ptr<BufRequest> request,
fuse_ino_t inode,
uint64_t file_handle,
size_t size,
off_t offset);
void WriteInternal(std::unique_ptr<WriteRequest> request,
fuse_ino_t inode,
uint64_t file_handle,
const std::vector<char>& buf,
off_t offset);
void ReleaseInternal(std::unique_ptr<SimpleRequest> request,
fuse_ino_t inode,
uint64_t file_handle);
void RenameInternal(std::unique_ptr<SimpleRequest> request,
fuse_ino_t old_parent_inode,
const std::string& old_name,
fuse_ino_t new_parent_inode,
const std::string& new_name);
void UnlinkInternal(std::unique_ptr<SimpleRequest> request,
fuse_ino_t parent_inode,
const std::string& name);
void OpenDirInternal(std::unique_ptr<OpenRequest> request,
fuse_ino_t inode,
int flags);
void ReadDirInternal(std::unique_ptr<DirentryRequest> request,
fuse_ino_t inode,
uint64_t file_handle,
off_t offset);
void ReleaseDirInternal(std::unique_ptr<SimpleRequest> request,
fuse_ino_t inode,
uint64_t file_handle);
void MkDirInternal(std::unique_ptr<EntryRequest> request,
fuse_ino_t parent_inode,
const std::string& name,
mode_t mode);
void RmDirinternal(std::unique_ptr<SimpleRequest> request,
fuse_ino_t parent_inode,
const std::string& name);
// mojom::SmbFs helpers that execute on |samba_thread_|.
void DeleteRecursivelyInternal(
const base::FilePath& path,
RecursiveDeleteOperation::CompletionCallback callback);
// Called on completion of the recursive delete.
void OnDeleteRecursivelyDone(
RecursiveDeleteOperation::CompletionCallback callback,
mojom::DeleteRecursivelyError error);
// Constructs a sanitised stat struct for sending as a response.
struct stat MakeStat(ino_t inode, const struct stat& in_stat) const;
// Clear / propagate permission bits appropriately (crbug.com/1063715).
mode_t MakeStatModeBits(mode_t in_mode) const;
// Constructs a share file path suitable for passing to libsmbclient from the
// given absolute file path.
std::string MakeShareFilePath(const base::FilePath& path) const;
// Construct a share file path from the |inode|. |inode| must be a valid inode
// number.
std::string ShareFilePathFromInode(ino_t inode) const;
// Registers an open file and returns a handle to that file. Always returns a
// non-zero handle.
uint64_t AddOpenFile(SMBCFILE* file);
// Removes |handle| from the open file table.
void RemoveOpenFile(uint64_t handle);
// Returns the open file referred to by |handle|. Returns nullptr if |handle|
// does not exist.
SMBCFILE* LookupOpenFile(uint64_t handle) const;
// Request credentials, if |error| is an auth failure, and the share has not
// previously connected successfully.
void MaybeUpdateCredentials(int error);
// Request authentication credentials. Will do nothing is a request is
// currently in progress.
void RequestCredentialUpdate();
// Callback handler for Delegate::RequestCredentials().
void OnRequestCredentialsDone(std::unique_ptr<SmbCredential> credentials);
// Cache a stat structure. |inode_stat.st_ino| is used as the key.
void AddCachedInodeStat(const struct stat& inode_stat);
// Remove the cached stat structure for |inode|.
void EraseCachedInodeStat(ino_t inode);
// Lookup the cached stat structure for |inode|. Returns true on cache hit or
// false on a miss.
bool GetCachedInodeStat(ino_t inode, struct stat* out_stat);
Delegate* const delegate_ = nullptr;
const std::string share_path_;
const uid_t uid_ = 0;
const gid_t gid_ = 0;
const bool use_kerberos_ = false;
base::Thread samba_thread_;
InodeMap inode_map_{FUSE_ROOT_ID};
// Origin/constructor thread task runner.
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_ =
base::ThreadTaskRunnerHandle::Get();
std::unordered_map<uint64_t, SMBCFILE*> open_files_;
uint64_t open_files_seq_ = 1;
mutable base::Lock lock_;
std::string resolved_share_path_ = share_path_;
// Interface to libsmbclient.
std::unique_ptr<SambaInterface> samba_impl_;
// Cache stat information during ReadDir() to speed up subsequent access.
base::HashingMRUCache<ino_t, StatCacheItem> stat_cache_;
// Whether a successful connection to the SMB server has been made. Used to
// determine whether or not to request auth credentials.
// std::atomic<> load/store by default have acquire/release memory ordering.
std::atomic<bool> connected_{false};
// Flag to ensure only one credential request is active at a time.
bool requesting_credentials_ = false;
// At most one outstanding recursive delete operation can be in flight. This
// object must live entirely on |samba_thread_|.
std::unique_ptr<RecursiveDeleteOperation> recursive_delete_operation_;
base::WeakPtrFactory<SmbFilesystem> weak_factory_{this};
DISALLOW_IMPLICIT_CONSTRUCTORS(SmbFilesystem);
};
std::ostream& operator<<(std::ostream& out, SmbFilesystem::ConnectError error);
} // namespace smbfs
#endif // SMBFS_SMB_FILESYSTEM_H_