]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <sys/file.h> | |
16 | #include <sys/types.h> | |
17 | #include <sys/wait.h> | |
11fdf7f2 | 18 | #include <limits.h> |
7c673cae FG |
19 | #include <signal.h> |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
23 | #include <errno.h> | |
24 | #include <fcntl.h> | |
25 | #include <unistd.h> | |
26 | ||
33c7a0ef TL |
27 | #if defined(__linux__) |
28 | #include <libgen.h> | |
29 | #include <sys/vfs.h> | |
30 | #include <sys/xattr.h> | |
31 | #include <linux/magic.h> | |
32 | #endif | |
33 | ||
7c673cae FG |
34 | // ceph |
35 | #include "common/errno.h" | |
36 | #include "common/safe_io.h" | |
37 | #include "include/types.h" | |
38 | #include "Client.h" | |
39 | #include "Fh.h" | |
40 | #include "ioctl.h" | |
41 | #include "common/config.h" | |
11fdf7f2 | 42 | #include "include/ceph_assert.h" |
e306af50 TL |
43 | #include "include/cephfs/ceph_ll_client.h" |
44 | #include "include/ceph_fuse.h" | |
7c673cae FG |
45 | |
46 | #include "fuse_ll.h" | |
7c673cae FG |
47 | #include <fuse_lowlevel.h> |
48 | ||
49 | #define dout_context g_ceph_context | |
50 | ||
51 | #define FINO_INO(x) ((x) & ((1ull<<48)-1ull)) | |
52 | #define FINO_STAG(x) ((x) >> 48) | |
11fdf7f2 TL |
53 | #define MAKE_FINO(i,s) ((i) | ((int64_t)(s) << 48)) |
54 | #define STAG_MASK 0xffff | |
2a845540 TL |
55 | #define G_NOSNAP_STAG 0 // for all CEPH_NOSNAP |
56 | #define G_SNAPDIR_STAG 1 // for all CEPH_SNAPDIR | |
7c673cae FG |
57 | |
58 | #define MINORBITS 20 | |
59 | #define MINORMASK ((1U << MINORBITS) - 1) | |
60 | ||
61 | #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) | |
62 | #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) | |
63 | #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) | |
64 | ||
33c7a0ef TL |
65 | #if defined(__linux__) |
66 | #ifndef FUSE_SUPER_MAGIC | |
67 | #define FUSE_SUPER_MAGIC 0x65735546 | |
68 | #endif | |
69 | ||
70 | #define _CEPH_CLIENT_ID "ceph.client_id" | |
71 | #endif | |
72 | ||
2a845540 TL |
73 | /* |
74 | * The dedicated struct for snapid <-> stag map for each ceph | |
75 | * inode, and the stag is a number in range [2, 0xffff], and | |
76 | * the stag number 0 is reserved for CEPH_NOSNAP and 1 is | |
77 | * reserved for CEPH_SNAPDIR. | |
78 | */ | |
79 | struct ceph_fuse_fake_inode_stag { | |
80 | ceph::unordered_map<uint64_t,int> snap_stag_map; // <snapid, stagid> | |
81 | ceph::unordered_map<int, uint64_t> stag_snap_map; // <stagid, snapid> | |
82 | int last_stag = 1; | |
83 | }; | |
84 | ||
20effc67 TL |
85 | using namespace std; |
86 | ||
f67539c2 TL |
87 | static const ceph::unordered_map<int,int> cephfs_errno_to_system_errno = { |
88 | {CEPHFS_EBLOCKLISTED, ESHUTDOWN}, | |
89 | {CEPHFS_EPERM, EPERM}, | |
90 | {CEPHFS_ESTALE, ESTALE}, | |
91 | {CEPHFS_ENOSPC, ENOSPC}, | |
92 | {CEPHFS_ETIMEDOUT, ETIMEDOUT}, | |
93 | {CEPHFS_EIO, EIO}, | |
94 | {CEPHFS_ENOTCONN, ENOTCONN}, | |
95 | {CEPHFS_EEXIST, EEXIST}, | |
96 | {CEPHFS_EINTR, EINTR}, | |
97 | {CEPHFS_EINVAL, EINVAL}, | |
98 | {CEPHFS_EBADF, EBADF}, | |
99 | {CEPHFS_EROFS, EROFS}, | |
100 | {CEPHFS_EAGAIN, EAGAIN}, | |
101 | {CEPHFS_EACCES, EACCES}, | |
102 | {CEPHFS_ELOOP, ELOOP}, | |
103 | {CEPHFS_EISDIR, EISDIR}, | |
104 | {CEPHFS_ENOENT, ENOENT}, | |
105 | {CEPHFS_ENOTDIR, ENOTDIR}, | |
106 | {CEPHFS_ENAMETOOLONG, ENAMETOOLONG}, | |
107 | {CEPHFS_EBUSY, EBUSY}, | |
108 | {CEPHFS_EDQUOT, EDQUOT}, | |
109 | {CEPHFS_EFBIG, EFBIG}, | |
110 | {CEPHFS_ERANGE, ERANGE}, | |
111 | {CEPHFS_ENXIO, ENXIO}, | |
112 | {CEPHFS_ECANCELED, ECANCELED}, | |
113 | {CEPHFS_ENODATA, ENODATA}, | |
114 | {CEPHFS_EOPNOTSUPP, EOPNOTSUPP}, | |
115 | {CEPHFS_EXDEV, EXDEV}, | |
116 | {CEPHFS_ENOMEM, ENOMEM}, | |
117 | {CEPHFS_ENOTRECOVERABLE, ENOTRECOVERABLE}, | |
118 | {CEPHFS_ENOSYS, ENOSYS}, | |
119 | {CEPHFS_ENOTEMPTY, ENOTEMPTY}, | |
120 | {CEPHFS_EDEADLK, EDEADLK}, | |
121 | {CEPHFS_EDOM, EDOM}, | |
122 | {CEPHFS_EMLINK, EMLINK}, | |
123 | {CEPHFS_ETIME, ETIME}, | |
124 | {CEPHFS_EOLDSNAPC, EIO} // forcing to EIO for now | |
125 | }; | |
126 | ||
127 | /* Requirements: | |
128 | * cephfs_errno >= 0 | |
129 | */ | |
130 | static int get_sys_errno(int cephfs_errno) | |
131 | { | |
132 | if (cephfs_errno == 0) | |
133 | return 0; | |
134 | ||
135 | auto it = cephfs_errno_to_system_errno.find(cephfs_errno); | |
136 | if (it != cephfs_errno_to_system_errno.end()) | |
137 | return it->second; | |
138 | return EIO; | |
139 | } | |
140 | ||
7c673cae FG |
141 | static uint32_t new_encode_dev(dev_t dev) |
142 | { | |
143 | unsigned major = MAJOR(dev); | |
144 | unsigned minor = MINOR(dev); | |
145 | return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); | |
146 | } | |
147 | ||
148 | static dev_t new_decode_dev(uint32_t dev) | |
149 | { | |
150 | unsigned major = (dev & 0xfff00) >> 8; | |
151 | unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); | |
152 | return MKDEV(major, minor); | |
153 | } | |
154 | ||
155 | class CephFuse::Handle { | |
156 | public: | |
157 | Handle(Client *c, int fd); | |
158 | ~Handle(); | |
159 | ||
160 | int init(int argc, const char *argv[]); | |
161 | int start(); | |
162 | int loop(); | |
163 | void finalize(); | |
164 | ||
165 | uint64_t fino_snap(uint64_t fino); | |
166 | uint64_t make_fake_ino(inodeno_t ino, snapid_t snapid); | |
167 | Inode * iget(fuse_ino_t fino); | |
168 | void iput(Inode *in); | |
169 | ||
170 | int fd_on_success; | |
171 | Client *client; | |
172 | ||
2a845540 | 173 | struct fuse_session *se = nullptr; |
1911f103 TL |
174 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
175 | struct fuse_cmdline_opts opts; | |
f67539c2 | 176 | struct fuse_conn_info_opts *conn_opts; |
1911f103 | 177 | #else |
2a845540 TL |
178 | struct fuse_chan *ch = nullptr; |
179 | char *mountpoint = nullptr; | |
1911f103 | 180 | #endif |
7c673cae | 181 | |
9f95a23c | 182 | ceph::mutex stag_lock = ceph::make_mutex("fuse_ll.cc stag_lock"); |
7c673cae | 183 | |
2a845540 TL |
184 | // a map of <ceph ino, fino stag/snapid map> |
185 | ceph::unordered_map<uint64_t, struct ceph_fuse_fake_inode_stag> g_fino_maps; | |
7c673cae | 186 | |
11fdf7f2 | 187 | pthread_key_t fuse_req_key = 0; |
7c673cae FG |
188 | void set_fuse_req(fuse_req_t); |
189 | fuse_req_t get_fuse_req(); | |
190 | ||
191 | struct fuse_args args; | |
192 | }; | |
193 | ||
33c7a0ef TL |
194 | #if defined(__linux__) |
195 | static int already_fuse_mounted(const char *path, bool &already_mounted) | |
196 | { | |
197 | struct statx path_statx; | |
198 | struct statx parent_statx; | |
199 | char path_copy[PATH_MAX] = {0}; | |
200 | char *parent_path = NULL; | |
201 | int err = 0; | |
202 | ||
203 | already_mounted = false; | |
204 | ||
205 | strncpy(path_copy, path, sizeof(path_copy)-1); | |
206 | parent_path = dirname(path_copy); | |
207 | ||
208 | // get stat information for original path | |
209 | if (-1 == statx(AT_FDCWD, path, AT_STATX_DONT_SYNC, STATX_INO, &path_statx)) { | |
210 | err = errno; | |
211 | derr << "fuse_ll: already_fuse_mounted: statx(" << path << ") failed with error " | |
212 | << cpp_strerror(err) << dendl; | |
213 | return err; | |
214 | } | |
215 | ||
216 | // if path isn't directory, then it can't be a mountpoint. | |
217 | if (!(path_statx.stx_mode & S_IFDIR)) { | |
218 | err = EINVAL; | |
219 | derr << "fuse_ll: already_fuse_mounted: " | |
220 | << path << " is not a directory" << dendl; | |
221 | return err; | |
222 | } | |
223 | ||
224 | // get stat information for parent path | |
225 | if (-1 == statx(AT_FDCWD, parent_path, AT_STATX_DONT_SYNC, STATX_INO, &parent_statx)) { | |
226 | err = errno; | |
227 | derr << "fuse_ll: already_fuse_mounted: statx(" << parent_path << ") failed with error " | |
228 | << cpp_strerror(err) << dendl; | |
229 | return err; | |
230 | } | |
231 | ||
232 | // if original path and parent have different device ids, | |
233 | // then the path is a mount point | |
234 | // or, if they refer to the same path, then it's probably | |
235 | // the root directory '/' and therefore path is a mountpoint | |
236 | if( path_statx.stx_dev_major != parent_statx.stx_dev_major || | |
237 | path_statx.stx_dev_minor != parent_statx.stx_dev_minor || | |
238 | ( path_statx.stx_dev_major == parent_statx.stx_dev_major && | |
239 | path_statx.stx_dev_minor == parent_statx.stx_dev_minor && | |
240 | path_statx.stx_ino == parent_statx.stx_ino | |
241 | ) | |
242 | ) { | |
243 | struct statfs path_statfs; | |
244 | if (-1 == statfs(path, &path_statfs)) { | |
245 | err = errno; | |
246 | derr << "fuse_ll: already_fuse_mounted: statfs(" << path << ") failed with error " | |
247 | << cpp_strerror(err) << dendl; | |
248 | return err; | |
249 | } | |
250 | ||
251 | if(FUSE_SUPER_MAGIC == path_statfs.f_type) { | |
252 | // if getxattr returns positive length means value exist for ceph.client_id | |
253 | // then ceph fuse is already mounted on path | |
254 | char client_id[128] = {0}; | |
255 | if (getxattr(path, _CEPH_CLIENT_ID, &client_id, sizeof(client_id)) > 0) { | |
256 | already_mounted = true; | |
257 | derr << path << " already mounted by " << client_id << dendl; | |
258 | } | |
259 | } | |
260 | } | |
261 | ||
262 | return err; | |
263 | } | |
264 | #else // non-linux platforms | |
265 | static int already_fuse_mounted(const char *path, bool &already_mounted) | |
266 | { | |
267 | already_mounted = false; | |
268 | return 0; | |
269 | } | |
270 | #endif | |
271 | ||
7c673cae FG |
272 | static int getgroups(fuse_req_t req, gid_t **sgids) |
273 | { | |
274 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
11fdf7f2 | 275 | ceph_assert(sgids); |
7c673cae FG |
276 | int c = fuse_req_getgroups(req, 0, NULL); |
277 | if (c < 0) { | |
278 | return c; | |
279 | } | |
280 | if (c == 0) { | |
281 | return 0; | |
282 | } | |
283 | ||
b5b8bbf5 FG |
284 | gid_t *gids = new (std::nothrow) gid_t[c]; |
285 | if (!gids) { | |
f67539c2 | 286 | return -get_sys_errno(CEPHFS_ENOMEM); |
7c673cae | 287 | } |
b5b8bbf5 | 288 | c = fuse_req_getgroups(req, c, gids); |
7c673cae | 289 | if (c < 0) { |
11fdf7f2 | 290 | delete[] gids; |
b5b8bbf5 FG |
291 | } else { |
292 | *sgids = gids; | |
7c673cae FG |
293 | } |
294 | return c; | |
295 | #endif | |
f67539c2 | 296 | return -get_sys_errno(CEPHFS_ENOSYS); |
7c673cae FG |
297 | } |
298 | ||
28e407b8 | 299 | static void get_fuse_groups(UserPerm& perms, fuse_req_t req) |
7c673cae | 300 | { |
adb31ebb TL |
301 | CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req); |
302 | if (cfuse->client->cct->_conf.get_val<bool>("fuse_set_user_groups")) { | |
28e407b8 AA |
303 | gid_t *gids = NULL; |
304 | int count = getgroups(req, &gids); | |
305 | ||
306 | if (count > 0) { | |
307 | perms.init_gids(gids, count); | |
308 | } else if (count < 0) { | |
309 | derr << __func__ << ": getgroups failed: " << cpp_strerror(-count) | |
310 | << dendl; | |
311 | } | |
312 | } | |
7c673cae FG |
313 | } |
314 | ||
7c673cae FG |
315 | |
316 | static CephFuse::Handle *fuse_ll_req_prepare(fuse_req_t req) | |
317 | { | |
318 | CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req); | |
319 | cfuse->set_fuse_req(req); | |
320 | return cfuse; | |
321 | } | |
322 | ||
323 | static void fuse_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) | |
324 | { | |
325 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
326 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
327 | struct fuse_entry_param fe; | |
328 | Inode *i2, *i1 = cfuse->iget(parent); // see below | |
329 | int r; | |
330 | UserPerm perms(ctx->uid, ctx->gid); | |
28e407b8 | 331 | get_fuse_groups(perms, req); |
7c673cae | 332 | |
11fdf7f2 TL |
333 | if (!i1) |
334 | { | |
335 | r = cfuse->client->lookup_ino(parent, perms, &i1); | |
336 | if (r < 0) { | |
f67539c2 | 337 | fuse_reply_err(req, get_sys_errno(-r)); |
11fdf7f2 TL |
338 | return; |
339 | } | |
340 | } | |
341 | ||
7c673cae FG |
342 | memset(&fe, 0, sizeof(fe)); |
343 | r = cfuse->client->ll_lookup(i1, name, &fe.attr, &i2, perms); | |
344 | if (r >= 0) { | |
345 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
346 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
347 | fuse_reply_entry(req, &fe); | |
348 | } else { | |
f67539c2 | 349 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
350 | } |
351 | ||
352 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
353 | // fuse_ll_forget() | |
354 | cfuse->iput(i1); | |
355 | } | |
356 | ||
20effc67 TL |
357 | // fuse3 has changed forget function signature |
358 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) | |
359 | static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino, | |
360 | uint64_t nlookup) | |
361 | #else | |
7c673cae FG |
362 | static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino, |
363 | long unsigned nlookup) | |
20effc67 | 364 | #endif |
7c673cae FG |
365 | { |
366 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
2a845540 TL |
367 | Inode *in = cfuse->iget(ino); |
368 | if (in) | |
369 | cfuse->client->ll_forget(in, nlookup+1); | |
7c673cae FG |
370 | fuse_reply_none(req); |
371 | } | |
372 | ||
373 | static void fuse_ll_getattr(fuse_req_t req, fuse_ino_t ino, | |
374 | struct fuse_file_info *fi) | |
375 | { | |
376 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
377 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
378 | struct stat stbuf; |
379 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
380 | Inode *in = cfuse->iget(ino); |
381 | if (!in) { | |
382 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
383 | return; | |
384 | } | |
385 | ||
28e407b8 | 386 | get_fuse_groups(perms, req); |
2a845540 | 387 | |
7c673cae FG |
388 | (void) fi; // XXX |
389 | ||
390 | if (cfuse->client->ll_getattr(in, &stbuf, perms) | |
391 | == 0) { | |
392 | stbuf.st_ino = cfuse->make_fake_ino(stbuf.st_ino, stbuf.st_dev); | |
393 | stbuf.st_rdev = new_encode_dev(stbuf.st_rdev); | |
394 | fuse_reply_attr(req, &stbuf, 0); | |
395 | } else | |
396 | fuse_reply_err(req, ENOENT); | |
397 | ||
398 | cfuse->iput(in); // iput required | |
399 | } | |
400 | ||
401 | static void fuse_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, | |
402 | int to_set, struct fuse_file_info *fi) | |
403 | { | |
404 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
405 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae | 406 | UserPerm perms(ctx->uid, ctx->gid); |
2a845540 TL |
407 | Inode *in = cfuse->iget(ino); |
408 | if (!in) { | |
409 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
410 | return; | |
411 | } | |
412 | ||
28e407b8 | 413 | get_fuse_groups(perms, req); |
7c673cae FG |
414 | |
415 | int mask = 0; | |
416 | if (to_set & FUSE_SET_ATTR_MODE) mask |= CEPH_SETATTR_MODE; | |
417 | if (to_set & FUSE_SET_ATTR_UID) mask |= CEPH_SETATTR_UID; | |
418 | if (to_set & FUSE_SET_ATTR_GID) mask |= CEPH_SETATTR_GID; | |
419 | if (to_set & FUSE_SET_ATTR_MTIME) mask |= CEPH_SETATTR_MTIME; | |
420 | if (to_set & FUSE_SET_ATTR_ATIME) mask |= CEPH_SETATTR_ATIME; | |
421 | if (to_set & FUSE_SET_ATTR_SIZE) mask |= CEPH_SETATTR_SIZE; | |
11fdf7f2 | 422 | #if !defined(__APPLE__) |
7c673cae FG |
423 | if (to_set & FUSE_SET_ATTR_MTIME_NOW) mask |= CEPH_SETATTR_MTIME_NOW; |
424 | if (to_set & FUSE_SET_ATTR_ATIME_NOW) mask |= CEPH_SETATTR_ATIME_NOW; | |
425 | #endif | |
426 | ||
427 | int r = cfuse->client->ll_setattr(in, attr, mask, perms); | |
428 | if (r == 0) | |
429 | fuse_reply_attr(req, attr, 0); | |
430 | else | |
f67539c2 | 431 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
432 | |
433 | cfuse->iput(in); // iput required | |
434 | } | |
435 | ||
436 | // XATTRS | |
437 | ||
438 | static void fuse_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, | |
439 | const char *value, size_t size, | |
440 | int flags | |
11fdf7f2 | 441 | #if defined(__APPLE__) |
7c673cae FG |
442 | ,uint32_t pos |
443 | #endif | |
444 | ) | |
445 | { | |
446 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
447 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae | 448 | UserPerm perms(ctx->uid, ctx->gid); |
2a845540 TL |
449 | Inode *in = cfuse->iget(ino); |
450 | if (!in) { | |
451 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
452 | return; | |
453 | } | |
454 | ||
28e407b8 | 455 | get_fuse_groups(perms, req); |
7c673cae FG |
456 | |
457 | int r = cfuse->client->ll_setxattr(in, name, value, size, flags, perms); | |
f67539c2 | 458 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
459 | |
460 | cfuse->iput(in); // iput required | |
461 | } | |
462 | ||
463 | static void fuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) | |
464 | { | |
465 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
466 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
467 | char buf[size]; |
468 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
469 | Inode *in = cfuse->iget(ino); |
470 | if (!in) { | |
471 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
472 | return; | |
473 | } | |
474 | ||
28e407b8 | 475 | get_fuse_groups(perms, req); |
7c673cae FG |
476 | |
477 | int r = cfuse->client->ll_listxattr(in, buf, size, perms); | |
478 | if (size == 0 && r >= 0) | |
479 | fuse_reply_xattr(req, r); | |
480 | else if (r >= 0) | |
481 | fuse_reply_buf(req, buf, r); | |
482 | else | |
f67539c2 | 483 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
484 | |
485 | cfuse->iput(in); // iput required | |
486 | } | |
487 | ||
488 | static void fuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, | |
489 | size_t size | |
11fdf7f2 | 490 | #if defined(__APPLE__) |
7c673cae FG |
491 | ,uint32_t position |
492 | #endif | |
493 | ) | |
494 | { | |
495 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
496 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
497 | char buf[size]; |
498 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
499 | Inode *in = cfuse->iget(ino); |
500 | if (!in) { | |
501 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
502 | return; | |
503 | } | |
504 | ||
28e407b8 | 505 | get_fuse_groups(perms, req); |
7c673cae FG |
506 | |
507 | int r = cfuse->client->ll_getxattr(in, name, buf, size, perms); | |
508 | if (size == 0 && r >= 0) | |
509 | fuse_reply_xattr(req, r); | |
510 | else if (r >= 0) | |
511 | fuse_reply_buf(req, buf, r); | |
512 | else | |
f67539c2 | 513 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
514 | |
515 | cfuse->iput(in); // iput required | |
516 | } | |
517 | ||
518 | static void fuse_ll_removexattr(fuse_req_t req, fuse_ino_t ino, | |
519 | const char *name) | |
520 | { | |
521 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
522 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae | 523 | UserPerm perms(ctx->uid, ctx->gid); |
2a845540 TL |
524 | Inode *in = cfuse->iget(ino); |
525 | if (!in) { | |
526 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
527 | return; | |
528 | } | |
529 | ||
28e407b8 | 530 | get_fuse_groups(perms, req); |
7c673cae FG |
531 | |
532 | int r = cfuse->client->ll_removexattr(in, name, perms); | |
f67539c2 | 533 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
534 | |
535 | cfuse->iput(in); // iput required | |
536 | } | |
537 | ||
538 | static void fuse_ll_opendir(fuse_req_t req, fuse_ino_t ino, | |
539 | struct fuse_file_info *fi) | |
540 | { | |
541 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
542 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
2a845540 | 543 | UserPerm perms(ctx->uid, ctx->gid); |
7c673cae | 544 | void *dirp; |
2a845540 TL |
545 | Inode *in = cfuse->iget(ino); |
546 | if (!in) { | |
547 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
548 | return; | |
549 | } | |
7c673cae | 550 | |
28e407b8 | 551 | get_fuse_groups(perms, req); |
7c673cae FG |
552 | |
553 | int r = cfuse->client->ll_opendir(in, fi->flags, (dir_result_t **)&dirp, | |
554 | perms); | |
555 | if (r >= 0) { | |
556 | fi->fh = (uint64_t)dirp; | |
557 | fuse_reply_open(req, fi); | |
558 | } else { | |
f67539c2 | 559 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
560 | } |
561 | ||
562 | cfuse->iput(in); // iput required | |
563 | } | |
564 | ||
565 | static void fuse_ll_readlink(fuse_req_t req, fuse_ino_t ino) | |
566 | { | |
567 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
568 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
569 | char buf[PATH_MAX + 1]; // leave room for a null terminator |
570 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
571 | Inode *in = cfuse->iget(ino); |
572 | if (!in) { | |
573 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
574 | return; | |
575 | } | |
7c673cae | 576 | |
2a845540 | 577 | get_fuse_groups(perms, req); |
7c673cae FG |
578 | int r = cfuse->client->ll_readlink(in, buf, sizeof(buf) - 1, perms); |
579 | if (r >= 0) { | |
580 | buf[r] = '\0'; | |
581 | fuse_reply_readlink(req, buf); | |
582 | } else { | |
f67539c2 | 583 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
584 | } |
585 | ||
586 | cfuse->iput(in); // iput required | |
587 | } | |
588 | ||
589 | static void fuse_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, | |
590 | mode_t mode, dev_t rdev) | |
591 | { | |
592 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
593 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
594 | struct fuse_entry_param fe; |
595 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
596 | Inode *i2, *i1 = cfuse->iget(parent); |
597 | if (!i1) { | |
598 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
599 | return; | |
600 | } | |
601 | ||
28e407b8 | 602 | get_fuse_groups(perms, req); |
7c673cae FG |
603 | |
604 | memset(&fe, 0, sizeof(fe)); | |
605 | ||
606 | int r = cfuse->client->ll_mknod(i1, name, mode, new_decode_dev(rdev), | |
607 | &fe.attr, &i2, perms); | |
608 | if (r == 0) { | |
609 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
610 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
611 | fuse_reply_entry(req, &fe); | |
612 | } else { | |
f67539c2 | 613 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
614 | } |
615 | ||
616 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
617 | // fuse_ll_forget() | |
618 | cfuse->iput(i1); // iput required | |
619 | } | |
620 | ||
621 | static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, | |
622 | mode_t mode) | |
623 | { | |
624 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
625 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
626 | Inode *i2, *i1; | |
627 | struct fuse_entry_param fe; | |
628 | ||
629 | memset(&fe, 0, sizeof(fe)); | |
630 | UserPerm perm(ctx->uid, ctx->gid); | |
28e407b8 | 631 | get_fuse_groups(perm, req); |
7c673cae | 632 | #ifdef HAVE_SYS_SYNCFS |
11fdf7f2 TL |
633 | auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>( |
634 | "fuse_multithreaded"); | |
635 | auto fuse_syncfs_on_mksnap = cfuse->client->cct->_conf.get_val<bool>( | |
636 | "fuse_syncfs_on_mksnap"); | |
7c673cae | 637 | if (cfuse->fino_snap(parent) == CEPH_SNAPDIR && |
11fdf7f2 | 638 | fuse_multithreaded && fuse_syncfs_on_mksnap) { |
7c673cae | 639 | int err = 0; |
1911f103 TL |
640 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
641 | int fd = ::open(cfuse->opts.mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC); | |
642 | #else | |
91327a77 | 643 | int fd = ::open(cfuse->mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC); |
1911f103 | 644 | #endif |
7c673cae FG |
645 | if (fd < 0) { |
646 | err = errno; | |
647 | } else { | |
648 | int r = ::syncfs(fd); | |
649 | if (r < 0) | |
650 | err = errno; | |
651 | ::close(fd); | |
652 | } | |
653 | if (err) { | |
654 | fuse_reply_err(req, err); | |
655 | return; | |
656 | } | |
657 | } | |
658 | #endif | |
659 | ||
660 | i1 = cfuse->iget(parent); | |
2a845540 TL |
661 | if (!i1) { |
662 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
663 | return; | |
664 | } | |
665 | ||
7c673cae FG |
666 | int r = cfuse->client->ll_mkdir(i1, name, mode, &fe.attr, &i2, perm); |
667 | if (r == 0) { | |
668 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
669 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
670 | fuse_reply_entry(req, &fe); | |
671 | } else { | |
f67539c2 | 672 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
673 | } |
674 | ||
675 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
676 | // fuse_ll_forget() | |
677 | cfuse->iput(i1); // iput required | |
678 | } | |
679 | ||
680 | static void fuse_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) | |
681 | { | |
682 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
683 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae | 684 | UserPerm perm(ctx->uid, ctx->gid); |
2a845540 TL |
685 | Inode *in = cfuse->iget(parent); |
686 | if (!in) { | |
687 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
688 | return; | |
689 | } | |
690 | ||
28e407b8 | 691 | get_fuse_groups(perm, req); |
7c673cae FG |
692 | |
693 | int r = cfuse->client->ll_unlink(in, name, perm); | |
f67539c2 | 694 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
695 | |
696 | cfuse->iput(in); // iput required | |
697 | } | |
698 | ||
699 | static void fuse_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) | |
700 | { | |
701 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
702 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae | 703 | UserPerm perms(ctx->uid, ctx->gid); |
2a845540 TL |
704 | Inode *in = cfuse->iget(parent); |
705 | if (!in) { | |
706 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
707 | return; | |
708 | } | |
709 | ||
28e407b8 | 710 | get_fuse_groups(perms, req); |
7c673cae FG |
711 | |
712 | int r = cfuse->client->ll_rmdir(in, name, perms); | |
f67539c2 | 713 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
714 | |
715 | cfuse->iput(in); // iput required | |
716 | } | |
717 | ||
718 | static void fuse_ll_symlink(fuse_req_t req, const char *existing, | |
719 | fuse_ino_t parent, const char *name) | |
720 | { | |
721 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
722 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
723 | struct fuse_entry_param fe; |
724 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
725 | Inode *i2, *i1 = cfuse->iget(parent); |
726 | if (!i1) { | |
727 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
728 | return; | |
729 | } | |
730 | ||
28e407b8 | 731 | get_fuse_groups(perms, req); |
7c673cae FG |
732 | |
733 | memset(&fe, 0, sizeof(fe)); | |
734 | ||
735 | int r = cfuse->client->ll_symlink(i1, name, existing, &fe.attr, &i2, perms); | |
736 | if (r == 0) { | |
737 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
738 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
739 | fuse_reply_entry(req, &fe); | |
740 | } else { | |
f67539c2 | 741 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
742 | } |
743 | ||
744 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching | |
745 | // fuse_ll_forget() | |
746 | cfuse->iput(i1); // iput required | |
747 | } | |
748 | ||
749 | static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name, | |
1911f103 TL |
750 | fuse_ino_t newparent, const char *newname |
751 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) | |
752 | , unsigned int flags | |
753 | #endif | |
754 | ) | |
7c673cae FG |
755 | { |
756 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
757 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
2a845540 | 758 | UserPerm perm(ctx->uid, ctx->gid); |
7c673cae FG |
759 | Inode *in = cfuse->iget(parent); |
760 | Inode *nin = cfuse->iget(newparent); | |
2a845540 TL |
761 | if (!in || !nin) { |
762 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
763 | return; | |
764 | } | |
765 | ||
28e407b8 | 766 | get_fuse_groups(perm, req); |
7c673cae FG |
767 | |
768 | int r = cfuse->client->ll_rename(in, name, nin, newname, perm); | |
f67539c2 | 769 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
770 | |
771 | cfuse->iput(in); // iputs required | |
772 | cfuse->iput(nin); | |
773 | } | |
774 | ||
775 | static void fuse_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, | |
776 | const char *newname) | |
777 | { | |
778 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
779 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
2a845540 | 780 | struct fuse_entry_param fe; |
7c673cae FG |
781 | Inode *in = cfuse->iget(ino); |
782 | Inode *nin = cfuse->iget(newparent); | |
2a845540 TL |
783 | if (!in || !nin) { |
784 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
785 | return; | |
786 | } | |
7c673cae FG |
787 | |
788 | memset(&fe, 0, sizeof(fe)); | |
789 | UserPerm perm(ctx->uid, ctx->gid); | |
28e407b8 | 790 | get_fuse_groups(perm, req); |
2a845540 | 791 | |
7c673cae FG |
792 | /* |
793 | * Note that we could successfully link, but then fail the subsequent | |
794 | * getattr and return an error. Perhaps we should ignore getattr errors, | |
795 | * but then how do we tell FUSE that the attrs are bogus? | |
796 | */ | |
797 | int r = cfuse->client->ll_link(in, nin, newname, perm); | |
798 | if (r == 0) { | |
799 | r = cfuse->client->ll_getattr(in, &fe.attr, perm); | |
800 | if (r == 0) { | |
801 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
802 | fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev); | |
803 | fuse_reply_entry(req, &fe); | |
804 | } | |
805 | } | |
806 | ||
807 | if (r != 0) { | |
808 | /* | |
809 | * Many ll operations in libcephfs return an extra inode reference, but | |
810 | * ll_link currently does not. Still, FUSE needs one for the new dentry, | |
811 | * so we commandeer the reference taken earlier when ll_link is successful. | |
812 | * On error however, we must put that reference. | |
813 | */ | |
814 | cfuse->iput(in); | |
f67539c2 | 815 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
816 | } |
817 | ||
818 | cfuse->iput(nin); | |
819 | } | |
820 | ||
821 | static void fuse_ll_open(fuse_req_t req, fuse_ino_t ino, | |
822 | struct fuse_file_info *fi) | |
823 | { | |
824 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
825 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
826 | Fh *fh = NULL; |
827 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
828 | Inode *in = cfuse->iget(ino); |
829 | if (!in) { | |
830 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
831 | return; | |
832 | } | |
833 | ||
28e407b8 | 834 | get_fuse_groups(perms, req); |
7c673cae FG |
835 | |
836 | int r = cfuse->client->ll_open(in, fi->flags, &fh, perms); | |
837 | if (r == 0) { | |
838 | fi->fh = (uint64_t)fh; | |
839 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
11fdf7f2 TL |
840 | auto fuse_disable_pagecache = cfuse->client->cct->_conf.get_val<bool>( |
841 | "fuse_disable_pagecache"); | |
842 | auto fuse_use_invalidate_cb = cfuse->client->cct->_conf.get_val<bool>( | |
843 | "fuse_use_invalidate_cb"); | |
844 | if (fuse_disable_pagecache) | |
7c673cae | 845 | fi->direct_io = 1; |
11fdf7f2 | 846 | else if (fuse_use_invalidate_cb) |
7c673cae FG |
847 | fi->keep_cache = 1; |
848 | #endif | |
849 | fuse_reply_open(req, fi); | |
850 | } else { | |
f67539c2 | 851 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
852 | } |
853 | ||
854 | cfuse->iput(in); // iput required | |
855 | } | |
856 | ||
857 | static void fuse_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, | |
858 | struct fuse_file_info *fi) | |
859 | { | |
860 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
861 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
862 | bufferlist bl; | |
863 | int r = cfuse->client->ll_read(fh, off, size, &bl); | |
f67539c2 TL |
864 | if (r >= 0) { |
865 | vector<iovec> iov; | |
866 | size_t len; | |
867 | struct fuse_bufvec *bufv; | |
868 | ||
869 | if (bl.get_num_buffers() > IOV_MAX) | |
870 | bl.rebuild(); | |
871 | ||
872 | bl.prepare_iov(&iov); | |
873 | len = sizeof(struct fuse_bufvec) + sizeof(struct fuse_buf) * (iov.size() - 1); | |
874 | bufv = (struct fuse_bufvec *)calloc(1, len); | |
875 | if (bufv) { | |
876 | int i = 0; | |
877 | bufv->count = iov.size(); | |
878 | for (auto &v: iov) { | |
879 | bufv->buf[i].mem = v.iov_base; | |
880 | bufv->buf[i++].size = v.iov_len; | |
881 | } | |
882 | fuse_reply_data(req, bufv, FUSE_BUF_SPLICE_MOVE); | |
883 | free(bufv); | |
884 | return; | |
885 | } | |
886 | iov.insert(iov.begin(), {0}); // the first one is reserved for fuse_out_header | |
887 | fuse_reply_iov(req, &iov[0], iov.size()); | |
888 | } else | |
889 | fuse_reply_err(req, get_sys_errno(-r)); | |
7c673cae FG |
890 | } |
891 | ||
892 | static void fuse_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, | |
893 | size_t size, off_t off, struct fuse_file_info *fi) | |
894 | { | |
895 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
896 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
897 | int r = cfuse->client->ll_write(fh, off, size, buf); | |
898 | if (r >= 0) | |
899 | fuse_reply_write(req, r); | |
900 | else | |
f67539c2 | 901 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
902 | } |
903 | ||
904 | static void fuse_ll_flush(fuse_req_t req, fuse_ino_t ino, | |
905 | struct fuse_file_info *fi) | |
906 | { | |
907 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
908 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
909 | int r = cfuse->client->ll_flush(fh); | |
f67539c2 | 910 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
911 | } |
912 | ||
913 | #ifdef FUSE_IOCTL_COMPAT | |
e306af50 TL |
914 | static void fuse_ll_ioctl(fuse_req_t req, fuse_ino_t ino, |
915 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 5) | |
916 | unsigned int cmd, | |
917 | #else | |
918 | int cmd, | |
919 | #endif | |
920 | void *arg, struct fuse_file_info *fi, | |
7c673cae FG |
921 | unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) |
922 | { | |
923 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
924 | ||
925 | if (flags & FUSE_IOCTL_COMPAT) { | |
926 | fuse_reply_err(req, ENOSYS); | |
927 | return; | |
928 | } | |
929 | ||
930 | switch (static_cast<unsigned>(cmd)) { | |
931 | case CEPH_IOC_GET_LAYOUT: { | |
932 | file_layout_t layout; | |
933 | struct ceph_ioctl_layout l; | |
934 | Fh *fh = (Fh*)fi->fh; | |
935 | cfuse->client->ll_file_layout(fh, &layout); | |
936 | l.stripe_unit = layout.stripe_unit; | |
937 | l.stripe_count = layout.stripe_count; | |
938 | l.object_size = layout.object_size; | |
939 | l.data_pool = layout.pool_id; | |
940 | fuse_reply_ioctl(req, 0, &l, sizeof(struct ceph_ioctl_layout)); | |
941 | } | |
942 | break; | |
943 | default: | |
944 | fuse_reply_err(req, EINVAL); | |
945 | } | |
946 | } | |
947 | #endif | |
948 | ||
494da23a | 949 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) |
7c673cae FG |
950 | |
951 | static void fuse_ll_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, | |
952 | off_t offset, off_t length, | |
953 | struct fuse_file_info *fi) | |
954 | { | |
955 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
956 | Fh *fh = (Fh*)fi->fh; | |
957 | int r = cfuse->client->ll_fallocate(fh, mode, offset, length); | |
f67539c2 | 958 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
959 | } |
960 | ||
961 | #endif | |
962 | ||
963 | static void fuse_ll_release(fuse_req_t req, fuse_ino_t ino, | |
964 | struct fuse_file_info *fi) | |
965 | { | |
966 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
967 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
968 | int r = cfuse->client->ll_release(fh); | |
f67539c2 | 969 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
970 | } |
971 | ||
972 | static void fuse_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, | |
973 | struct fuse_file_info *fi) | |
974 | { | |
975 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
976 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
977 | int r = cfuse->client->ll_fsync(fh, datasync); | |
f67539c2 | 978 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
979 | } |
980 | ||
981 | struct readdir_context { | |
982 | fuse_req_t req; | |
983 | char *buf; | |
984 | size_t size; | |
985 | size_t pos; /* in buf */ | |
986 | uint64_t snap; | |
987 | }; | |
988 | ||
989 | /* | |
990 | * return 0 on success, -1 if out of space | |
991 | */ | |
992 | static int fuse_ll_add_dirent(void *p, struct dirent *de, | |
993 | struct ceph_statx *stx, off_t next_off, | |
994 | Inode *in) | |
995 | { | |
996 | struct readdir_context *c = (struct readdir_context *)p; | |
997 | CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(c->req); | |
998 | ||
999 | struct stat st; | |
1000 | st.st_ino = cfuse->make_fake_ino(stx->stx_ino, c->snap); | |
1001 | st.st_mode = stx->stx_mode; | |
1002 | st.st_rdev = new_encode_dev(stx->stx_rdev); | |
1003 | ||
1004 | size_t room = c->size - c->pos; | |
1005 | size_t entrysize = fuse_add_direntry(c->req, c->buf + c->pos, room, | |
1006 | de->d_name, &st, next_off); | |
1007 | if (entrysize > room) | |
1008 | return -ENOSPC; | |
1009 | ||
1010 | /* success */ | |
1011 | c->pos += entrysize; | |
1012 | return 0; | |
1013 | } | |
1014 | ||
1015 | static void fuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, | |
1016 | off_t off, struct fuse_file_info *fi) | |
1017 | { | |
1018 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1019 | ||
1020 | dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh); | |
1021 | cfuse->client->seekdir(dirp, off); | |
1022 | ||
1023 | struct readdir_context rc; | |
1024 | rc.req = req; | |
2a845540 TL |
1025 | rc.snap = cfuse->fino_snap(ino); |
1026 | if (rc.snap == CEPH_MAXSNAP) { | |
1027 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
1028 | return; | |
1029 | } | |
7c673cae FG |
1030 | rc.buf = new char[size]; |
1031 | rc.size = size; | |
1032 | rc.pos = 0; | |
7c673cae FG |
1033 | |
1034 | int r = cfuse->client->readdir_r_cb(dirp, fuse_ll_add_dirent, &rc); | |
f67539c2 | 1035 | if (r == 0 || r == -CEPHFS_ENOSPC) /* ignore ENOSPC from our callback */ |
7c673cae FG |
1036 | fuse_reply_buf(req, rc.buf, rc.pos); |
1037 | else | |
f67539c2 | 1038 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1039 | delete[] rc.buf; |
1040 | } | |
1041 | ||
1042 | static void fuse_ll_releasedir(fuse_req_t req, fuse_ino_t ino, | |
1043 | struct fuse_file_info *fi) | |
1044 | { | |
1045 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1046 | dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh); | |
1047 | cfuse->client->ll_releasedir(dirp); | |
1048 | fuse_reply_err(req, 0); | |
1049 | } | |
1050 | ||
1051 | static void fuse_ll_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, | |
1052 | struct fuse_file_info *fi) | |
1053 | { | |
1054 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1055 | dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh); | |
1056 | int r = cfuse->client->ll_fsyncdir(dirp); | |
f67539c2 | 1057 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1058 | } |
1059 | ||
1060 | static void fuse_ll_access(fuse_req_t req, fuse_ino_t ino, int mask) | |
1061 | { | |
28e407b8 AA |
1062 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); |
1063 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
28e407b8 | 1064 | UserPerm perms(ctx->uid, ctx->gid); |
2a845540 TL |
1065 | Inode *in = cfuse->iget(ino); |
1066 | if (!in) { | |
1067 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
1068 | return; | |
1069 | } | |
1070 | ||
28e407b8 AA |
1071 | get_fuse_groups(perms, req); |
1072 | ||
1073 | int r = cfuse->client->inode_permission(in, perms, mask); | |
f67539c2 | 1074 | fuse_reply_err(req, get_sys_errno(-r)); |
28e407b8 | 1075 | cfuse->iput(in); |
7c673cae FG |
1076 | } |
1077 | ||
1078 | static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name, | |
1079 | mode_t mode, struct fuse_file_info *fi) | |
1080 | { | |
1081 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1082 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
7c673cae FG |
1083 | struct fuse_entry_param fe; |
1084 | Fh *fh = NULL; | |
1085 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
1086 | Inode *i1 = cfuse->iget(parent), *i2; |
1087 | if (!i1) { | |
1088 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
1089 | return; | |
1090 | } | |
1091 | ||
28e407b8 | 1092 | get_fuse_groups(perms, req); |
7c673cae FG |
1093 | |
1094 | memset(&fe, 0, sizeof(fe)); | |
1095 | ||
1096 | // pass &i2 for the created inode so that ll_create takes an initial ll_ref | |
1097 | int r = cfuse->client->ll_create(i1, name, mode, fi->flags, &fe.attr, &i2, | |
1098 | &fh, perms); | |
1099 | if (r == 0) { | |
1100 | fi->fh = (uint64_t)fh; | |
1101 | fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev); | |
1102 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
11fdf7f2 TL |
1103 | auto fuse_disable_pagecache = cfuse->client->cct->_conf.get_val<bool>( |
1104 | "fuse_disable_pagecache"); | |
1105 | auto fuse_use_invalidate_cb = cfuse->client->cct->_conf.get_val<bool>( | |
1106 | "fuse_use_invalidate_cb"); | |
1107 | if (fuse_disable_pagecache) | |
7c673cae | 1108 | fi->direct_io = 1; |
11fdf7f2 | 1109 | else if (fuse_use_invalidate_cb) |
7c673cae FG |
1110 | fi->keep_cache = 1; |
1111 | #endif | |
1112 | fuse_reply_create(req, &fe, fi); | |
1113 | } else | |
f67539c2 | 1114 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1115 | // XXX NB, we dont iput(i2) because FUSE will do so in a matching |
1116 | // fuse_ll_forget() | |
1117 | cfuse->iput(i1); // iput required | |
1118 | } | |
1119 | ||
1120 | static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino) | |
1121 | { | |
1122 | struct statvfs stbuf; | |
1123 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
7c673cae FG |
1124 | const struct fuse_ctx *ctx = fuse_req_ctx(req); |
1125 | UserPerm perms(ctx->uid, ctx->gid); | |
2a845540 TL |
1126 | Inode *in = cfuse->iget(ino); |
1127 | if (!in) { | |
1128 | fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); | |
1129 | return; | |
1130 | } | |
1131 | ||
28e407b8 | 1132 | get_fuse_groups(perms, req); |
7c673cae FG |
1133 | |
1134 | int r = cfuse->client->ll_statfs(in, &stbuf, perms); | |
1135 | if (r == 0) | |
1136 | fuse_reply_statfs(req, &stbuf); | |
1137 | else | |
f67539c2 | 1138 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1139 | |
1140 | cfuse->iput(in); // iput required | |
1141 | } | |
1142 | ||
1143 | static void fuse_ll_getlk(fuse_req_t req, fuse_ino_t ino, | |
1144 | struct fuse_file_info *fi, struct flock *lock) | |
1145 | { | |
1146 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1147 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
1148 | ||
1149 | int r = cfuse->client->ll_getlk(fh, lock, fi->lock_owner); | |
1150 | if (r == 0) | |
1151 | fuse_reply_lock(req, lock); | |
1152 | else | |
f67539c2 | 1153 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1154 | } |
1155 | ||
1156 | static void fuse_ll_setlk(fuse_req_t req, fuse_ino_t ino, | |
1157 | struct fuse_file_info *fi, struct flock *lock, int sleep) | |
1158 | { | |
1159 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1160 | Fh *fh = reinterpret_cast<Fh*>(fi->fh); | |
1161 | ||
1162 | // must use multithread if operation may block | |
11fdf7f2 TL |
1163 | auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>( |
1164 | "fuse_multithreaded"); | |
1165 | if (!fuse_multithreaded && sleep && lock->l_type != F_UNLCK) { | |
7c673cae FG |
1166 | fuse_reply_err(req, EDEADLK); |
1167 | return; | |
1168 | } | |
1169 | ||
1170 | int r = cfuse->client->ll_setlk(fh, lock, fi->lock_owner, sleep); | |
f67539c2 | 1171 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1172 | } |
1173 | ||
1174 | static void fuse_ll_interrupt(fuse_req_t req, void* data) | |
1175 | { | |
1176 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1177 | cfuse->client->ll_interrupt(data); | |
1178 | } | |
1179 | ||
1180 | static void switch_interrupt_cb(void *handle, void* data) | |
1181 | { | |
1182 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
1183 | fuse_req_t req = cfuse->get_fuse_req(); | |
1184 | ||
1185 | if (data) | |
1186 | fuse_req_interrupt_func(req, fuse_ll_interrupt, data); | |
1187 | else | |
1188 | fuse_req_interrupt_func(req, NULL, NULL); | |
1189 | } | |
1190 | ||
1191 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) | |
1192 | static void fuse_ll_flock(fuse_req_t req, fuse_ino_t ino, | |
1193 | struct fuse_file_info *fi, int cmd) | |
1194 | { | |
1195 | CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); | |
1196 | Fh *fh = (Fh*)fi->fh; | |
1197 | ||
1198 | // must use multithread if operation may block | |
11fdf7f2 TL |
1199 | auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>( |
1200 | "fuse_multithreaded"); | |
1201 | if (!fuse_multithreaded && !(cmd & (LOCK_NB | LOCK_UN))) { | |
7c673cae FG |
1202 | fuse_reply_err(req, EDEADLK); |
1203 | return; | |
1204 | } | |
1205 | ||
1206 | int r = cfuse->client->ll_flock(fh, cmd, fi->lock_owner); | |
f67539c2 | 1207 | fuse_reply_err(req, get_sys_errno(-r)); |
7c673cae FG |
1208 | } |
1209 | #endif | |
1210 | ||
11fdf7f2 | 1211 | #if !defined(__APPLE__) |
7c673cae FG |
1212 | static mode_t umask_cb(void *handle) |
1213 | { | |
1214 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
1215 | fuse_req_t req = cfuse->get_fuse_req(); | |
1216 | const struct fuse_ctx *ctx = fuse_req_ctx(req); | |
1217 | return ctx->umask; | |
1218 | } | |
1219 | #endif | |
1220 | ||
1221 | static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off, | |
1222 | int64_t len) | |
1223 | { | |
1224 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
1225 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
1226 | fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid); | |
1911f103 TL |
1227 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1228 | fuse_lowlevel_notify_inval_inode(cfuse->se, fino, off, len); | |
1229 | #else | |
7c673cae FG |
1230 | fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len); |
1231 | #endif | |
1911f103 | 1232 | #endif |
7c673cae FG |
1233 | } |
1234 | ||
1235 | static void dentry_invalidate_cb(void *handle, vinodeno_t dirino, | |
e306af50 | 1236 | vinodeno_t ino, const char *name, size_t len) |
7c673cae FG |
1237 | { |
1238 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; | |
1239 | fuse_ino_t fdirino = cfuse->make_fake_ino(dirino.ino, dirino.snapid); | |
1240 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) | |
1241 | fuse_ino_t fino = 0; | |
1242 | if (ino.ino != inodeno_t()) | |
1243 | fino = cfuse->make_fake_ino(ino.ino, ino.snapid); | |
1911f103 | 1244 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
e306af50 | 1245 | fuse_lowlevel_notify_delete(cfuse->se, fdirino, fino, name, len); |
1911f103 | 1246 | #else |
e306af50 | 1247 | fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name, len); |
1911f103 | 1248 | #endif |
7c673cae | 1249 | #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) |
e306af50 | 1250 | fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name, len); |
7c673cae FG |
1251 | #endif |
1252 | } | |
1253 | ||
1254 | static int remount_cb(void *handle) | |
1255 | { | |
1256 | // used for trimming kernel dcache. when remounting a file system, linux kernel | |
1257 | // trims all unused dentries in the file system | |
11fdf7f2 | 1258 | char cmd[128+PATH_MAX]; |
7c673cae | 1259 | CephFuse::Handle *cfuse = (CephFuse::Handle *)handle; |
1911f103 TL |
1260 | snprintf(cmd, sizeof(cmd), "LIBMOUNT_FSTAB=/dev/null mount -i -o remount %s", |
1261 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) | |
1262 | cfuse->opts.mountpoint); | |
1263 | #else | |
1264 | cfuse->mountpoint); | |
1265 | #endif | |
7c673cae FG |
1266 | int r = system(cmd); |
1267 | if (r != 0 && r != -1) { | |
1268 | r = WEXITSTATUS(r); | |
1269 | } | |
1270 | ||
1271 | return r; | |
1272 | } | |
1273 | ||
1274 | static void do_init(void *data, fuse_conn_info *conn) | |
1275 | { | |
1276 | CephFuse::Handle *cfuse = (CephFuse::Handle *)data; | |
1277 | Client *client = cfuse->client; | |
1278 | ||
f67539c2 TL |
1279 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1280 | fuse_apply_conn_info_opts(cfuse->conn_opts, conn); | |
1281 | #endif | |
1282 | ||
1283 | if(conn->capable & FUSE_CAP_SPLICE_MOVE) | |
1284 | conn->want |= FUSE_CAP_SPLICE_MOVE; | |
1285 | ||
11fdf7f2 | 1286 | #if !defined(__APPLE__) |
92f5a8d4 | 1287 | if (!client->fuse_default_permissions && client->ll_handle_umask()) { |
7c673cae FG |
1288 | // apply umask in userspace if posix acl is enabled |
1289 | if(conn->capable & FUSE_CAP_DONT_MASK) | |
1290 | conn->want |= FUSE_CAP_DONT_MASK; | |
1291 | } | |
11fdf7f2 TL |
1292 | if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) |
1293 | conn->want |= FUSE_CAP_EXPORT_SUPPORT; | |
7c673cae FG |
1294 | #endif |
1295 | ||
1296 | if (cfuse->fd_on_success) { | |
1297 | //cout << "fuse init signaling on fd " << fd_on_success << std::endl; | |
1298 | // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1` | |
1299 | // from a daemonized child process. | |
1300 | uint32_t r = -1; | |
1301 | int err = safe_write(cfuse->fd_on_success, &r, sizeof(r)); | |
1302 | if (err) { | |
1303 | derr << "fuse_ll: do_init: safe_write failed with error " | |
1304 | << cpp_strerror(err) << dendl; | |
1305 | ceph_abort(); | |
1306 | } | |
1307 | //cout << "fuse init done signaling on fd " << fd_on_success << std::endl; | |
1308 | ||
1309 | // close stdout, etc. | |
1310 | ::close(0); | |
1311 | ::close(1); | |
1312 | ::close(2); | |
1313 | } | |
1314 | } | |
1315 | ||
1316 | const static struct fuse_lowlevel_ops fuse_ll_oper = { | |
1317 | init: do_init, | |
1318 | destroy: 0, | |
1319 | lookup: fuse_ll_lookup, | |
1320 | forget: fuse_ll_forget, | |
1321 | getattr: fuse_ll_getattr, | |
1322 | setattr: fuse_ll_setattr, | |
1323 | readlink: fuse_ll_readlink, | |
1324 | mknod: fuse_ll_mknod, | |
1325 | mkdir: fuse_ll_mkdir, | |
1326 | unlink: fuse_ll_unlink, | |
1327 | rmdir: fuse_ll_rmdir, | |
1328 | symlink: fuse_ll_symlink, | |
1329 | rename: fuse_ll_rename, | |
1330 | link: fuse_ll_link, | |
1331 | open: fuse_ll_open, | |
1332 | read: fuse_ll_read, | |
1333 | write: fuse_ll_write, | |
1334 | flush: fuse_ll_flush, | |
1335 | release: fuse_ll_release, | |
1336 | fsync: fuse_ll_fsync, | |
1337 | opendir: fuse_ll_opendir, | |
1338 | readdir: fuse_ll_readdir, | |
1339 | releasedir: fuse_ll_releasedir, | |
1340 | fsyncdir: fuse_ll_fsyncdir, | |
1341 | statfs: fuse_ll_statfs, | |
1342 | setxattr: fuse_ll_setxattr, | |
1343 | getxattr: fuse_ll_getxattr, | |
1344 | listxattr: fuse_ll_listxattr, | |
1345 | removexattr: fuse_ll_removexattr, | |
1346 | access: fuse_ll_access, | |
1347 | create: fuse_ll_create, | |
1348 | getlk: fuse_ll_getlk, | |
1349 | setlk: fuse_ll_setlk, | |
1350 | bmap: 0, | |
1351 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
1352 | #ifdef FUSE_IOCTL_COMPAT | |
1353 | ioctl: fuse_ll_ioctl, | |
1354 | #else | |
1355 | ioctl: 0, | |
1356 | #endif | |
1357 | poll: 0, | |
1358 | #endif | |
1359 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) | |
1360 | write_buf: 0, | |
1361 | retrieve_reply: 0, | |
1362 | forget_multi: 0, | |
1363 | flock: fuse_ll_flock, | |
1364 | #endif | |
494da23a | 1365 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9) |
7c673cae FG |
1366 | fallocate: fuse_ll_fallocate |
1367 | #endif | |
1368 | }; | |
1369 | ||
1370 | ||
1371 | CephFuse::Handle::Handle(Client *c, int fd) : | |
1372 | fd_on_success(fd), | |
2a845540 | 1373 | client(c) |
7c673cae | 1374 | { |
7c673cae | 1375 | memset(&args, 0, sizeof(args)); |
1911f103 TL |
1376 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1377 | memset(&opts, 0, sizeof(opts)); | |
1378 | #endif | |
7c673cae FG |
1379 | } |
1380 | ||
1381 | CephFuse::Handle::~Handle() | |
1382 | { | |
1383 | fuse_opt_free_args(&args); | |
1384 | } | |
1385 | ||
1386 | void CephFuse::Handle::finalize() | |
1387 | { | |
1911f103 TL |
1388 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1389 | if (se) { | |
1390 | fuse_remove_signal_handlers(se); | |
1391 | fuse_session_unmount(se); | |
1392 | fuse_session_destroy(se); | |
1393 | } | |
f67539c2 TL |
1394 | if (conn_opts) |
1395 | free(conn_opts); | |
1911f103 TL |
1396 | if (opts.mountpoint) |
1397 | free(opts.mountpoint); | |
1398 | #else | |
7c673cae FG |
1399 | if (se) |
1400 | fuse_remove_signal_handlers(se); | |
1401 | if (ch) | |
1402 | fuse_session_remove_chan(ch); | |
1403 | if (se) | |
1404 | fuse_session_destroy(se); | |
1405 | if (ch) | |
1406 | fuse_unmount(mountpoint, ch); | |
1911f103 | 1407 | #endif |
7c673cae FG |
1408 | |
1409 | pthread_key_delete(fuse_req_key); | |
1410 | } | |
1411 | ||
1412 | int CephFuse::Handle::init(int argc, const char *argv[]) | |
1413 | { | |
1414 | ||
1415 | int r = pthread_key_create(&fuse_req_key, NULL); | |
1416 | if (r) { | |
1417 | derr << "pthread_key_create failed." << dendl; | |
1418 | return r; | |
1419 | } | |
1420 | ||
1421 | // set up fuse argc/argv | |
1422 | int newargc = 0; | |
f67539c2 | 1423 | const char **newargv = (const char **) malloc((argc + 17) * sizeof(char *)); |
7c673cae FG |
1424 | if(!newargv) |
1425 | return ENOMEM; | |
1426 | ||
1427 | newargv[newargc++] = argv[0]; | |
1428 | newargv[newargc++] = "-f"; // stay in foreground | |
1429 | ||
11fdf7f2 TL |
1430 | auto fuse_allow_other = client->cct->_conf.get_val<bool>( |
1431 | "fuse_allow_other"); | |
1432 | auto fuse_default_permissions = client->cct->_conf.get_val<bool>( | |
1433 | "fuse_default_permissions"); | |
1911f103 | 1434 | #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) |
11fdf7f2 TL |
1435 | auto fuse_big_writes = client->cct->_conf.get_val<bool>( |
1436 | "fuse_big_writes"); | |
f67539c2 | 1437 | #endif |
1911f103 TL |
1438 | auto fuse_max_write = client->cct->_conf.get_val<Option::size_t>( |
1439 | "fuse_max_write"); | |
11fdf7f2 TL |
1440 | auto fuse_atomic_o_trunc = client->cct->_conf.get_val<bool>( |
1441 | "fuse_atomic_o_trunc"); | |
f67539c2 TL |
1442 | auto fuse_splice_read = client->cct->_conf.get_val<bool>( |
1443 | "fuse_splice_read"); | |
1444 | auto fuse_splice_write = client->cct->_conf.get_val<bool>( | |
1445 | "fuse_splice_write"); | |
1446 | auto fuse_splice_move = client->cct->_conf.get_val<bool>( | |
1447 | "fuse_splice_move"); | |
11fdf7f2 TL |
1448 | auto fuse_debug = client->cct->_conf.get_val<bool>( |
1449 | "fuse_debug"); | |
11fdf7f2 TL |
1450 | |
1451 | if (fuse_allow_other) { | |
7c673cae FG |
1452 | newargv[newargc++] = "-o"; |
1453 | newargv[newargc++] = "allow_other"; | |
1454 | } | |
11fdf7f2 | 1455 | if (fuse_default_permissions) { |
7c673cae FG |
1456 | newargv[newargc++] = "-o"; |
1457 | newargv[newargc++] = "default_permissions"; | |
1458 | } | |
1459 | #if defined(__linux__) | |
1911f103 | 1460 | #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) |
11fdf7f2 | 1461 | if (fuse_big_writes) { |
7c673cae FG |
1462 | newargv[newargc++] = "-o"; |
1463 | newargv[newargc++] = "big_writes"; | |
1464 | } | |
f67539c2 | 1465 | #endif |
11fdf7f2 TL |
1466 | if (fuse_max_write > 0) { |
1467 | char strsplice[65]; | |
1468 | newargv[newargc++] = "-o"; | |
11fdf7f2 TL |
1469 | sprintf(strsplice, "max_write=%zu", (size_t)fuse_max_write); |
1470 | newargv[newargc++] = strsplice; | |
1471 | } | |
1472 | if (fuse_atomic_o_trunc) { | |
7c673cae FG |
1473 | newargv[newargc++] = "-o"; |
1474 | newargv[newargc++] = "atomic_o_trunc"; | |
1475 | } | |
f67539c2 TL |
1476 | if (fuse_splice_read) { |
1477 | newargv[newargc++] = "-o"; | |
1478 | newargv[newargc++] = "splice_read"; | |
1479 | } | |
1480 | if (fuse_splice_write) { | |
1481 | newargv[newargc++] = "-o"; | |
1482 | newargv[newargc++] = "splice_write"; | |
1483 | } | |
1484 | if (fuse_splice_move) { | |
1485 | newargv[newargc++] = "-o"; | |
1486 | newargv[newargc++] = "splice_move"; | |
1487 | } | |
7c673cae | 1488 | #endif |
11fdf7f2 | 1489 | if (fuse_debug) |
7c673cae FG |
1490 | newargv[newargc++] = "-d"; |
1491 | ||
1492 | for (int argctr = 1; argctr < argc; argctr++) | |
1493 | newargv[newargc++] = argv[argctr]; | |
1494 | ||
1495 | derr << "init, newargv = " << newargv << " newargc=" << newargc << dendl; | |
1496 | struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv); | |
1497 | args = a; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment | |
1498 | ||
1911f103 TL |
1499 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1500 | if (fuse_parse_cmdline(&args, &opts) == -1) { | |
1501 | #else | |
7c673cae | 1502 | if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) { |
1911f103 | 1503 | #endif |
7c673cae FG |
1504 | derr << "fuse_parse_cmdline failed." << dendl; |
1505 | fuse_opt_free_args(&args); | |
1506 | free(newargv); | |
1507 | return EINVAL; | |
1508 | } | |
1509 | ||
f67539c2 TL |
1510 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1511 | derr << "init, args.argv = " << args.argv << " args.argc=" << args.argc << dendl; | |
1512 | conn_opts = fuse_parse_conn_info_opts(&args); | |
1513 | if (!conn_opts) { | |
1514 | derr << "fuse_parse_conn_info_opts failed" << dendl; | |
1515 | fuse_opt_free_args(&args); | |
1516 | free(newargv); | |
1517 | return EINVAL; | |
1518 | } | |
1519 | #endif | |
1520 | ||
11fdf7f2 | 1521 | ceph_assert(args.allocated); // Checking fuse has realloc'd args so we can free newargv |
7c673cae | 1522 | free(newargv); |
20effc67 TL |
1523 | |
1524 | struct ceph_client_callback_args cb_args = { | |
1525 | handle: this, | |
1526 | ino_cb: client->cct->_conf.get_val<bool>("fuse_use_invalidate_cb") ? | |
1527 | ino_invalidate_cb : NULL, | |
1528 | dentry_cb: dentry_invalidate_cb, | |
1529 | switch_intr_cb: switch_interrupt_cb, | |
1530 | #if defined(__linux__) | |
1531 | remount_cb: remount_cb, | |
1532 | #endif | |
1533 | #if !defined(__APPLE__) | |
1534 | umask_cb: umask_cb, | |
1535 | #endif | |
1536 | }; | |
1537 | r = client->ll_register_callbacks2(&cb_args); | |
1538 | if (r) { | |
1539 | derr << "registering callbacks failed: " << r << dendl; | |
1540 | return r; | |
1541 | } | |
1542 | ||
7c673cae FG |
1543 | return 0; |
1544 | } | |
1545 | ||
1546 | int CephFuse::Handle::start() | |
1547 | { | |
33c7a0ef TL |
1548 | bool is_mounted = false; |
1549 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) | |
1550 | int err = already_fuse_mounted(opts.mountpoint, is_mounted); | |
1551 | #else | |
1552 | int err = already_fuse_mounted(mountpoint, is_mounted); | |
1553 | #endif | |
1554 | if (err) { | |
1555 | return err; | |
1556 | } | |
1557 | ||
1558 | if (is_mounted) { | |
1559 | return EBUSY; | |
1560 | } | |
1561 | ||
1911f103 TL |
1562 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1563 | se = fuse_session_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this); | |
f67539c2 TL |
1564 | if (!se) { |
1565 | derr << "fuse_session_new failed" << dendl; | |
1566 | return EDOM; | |
1567 | } | |
1911f103 | 1568 | #else |
7c673cae FG |
1569 | ch = fuse_mount(mountpoint, &args); |
1570 | if (!ch) { | |
1571 | derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl; | |
1572 | return EIO; | |
1573 | } | |
1574 | ||
1575 | se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this); | |
1576 | if (!se) { | |
1577 | derr << "fuse_lowlevel_new failed" << dendl; | |
1578 | return EDOM; | |
1579 | } | |
f67539c2 | 1580 | #endif |
7c673cae FG |
1581 | |
1582 | signal(SIGTERM, SIG_DFL); | |
1583 | signal(SIGINT, SIG_DFL); | |
1584 | if (fuse_set_signal_handlers(se) == -1) { | |
1585 | derr << "fuse_set_signal_handlers failed" << dendl; | |
1586 | return ENOSYS; | |
1587 | } | |
1588 | ||
1911f103 TL |
1589 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1590 | if (fuse_session_mount(se, opts.mountpoint) != 0) { | |
1591 | derr << "fuse_session_mount failed" << dendl; | |
1592 | return ENOSYS; | |
1593 | } | |
1594 | #else | |
7c673cae | 1595 | fuse_session_add_chan(se, ch); |
1911f103 | 1596 | #endif |
7c673cae | 1597 | |
7c673cae FG |
1598 | return 0; |
1599 | } | |
1600 | ||
1601 | int CephFuse::Handle::loop() | |
1602 | { | |
11fdf7f2 TL |
1603 | auto fuse_multithreaded = client->cct->_conf.get_val<bool>( |
1604 | "fuse_multithreaded"); | |
1605 | if (fuse_multithreaded) { | |
1e59de90 | 1606 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 12) |
e306af50 | 1607 | { |
1e59de90 TL |
1608 | struct fuse_loop_config *conf = fuse_loop_cfg_create(); |
1609 | ceph_assert(conf != nullptr); | |
e306af50 | 1610 | |
1e59de90 TL |
1611 | fuse_loop_cfg_set_clone_fd(conf, opts.clone_fd); |
1612 | fuse_loop_cfg_set_idle_threads(conf, opts.max_idle_threads); | |
1613 | fuse_loop_cfg_set_max_threads(conf, opts.max_threads); | |
1614 | ||
1615 | int r = fuse_session_loop_mt(se, conf); | |
1616 | ||
1617 | fuse_loop_cfg_destroy(conf); | |
1618 | return r; | |
1619 | } | |
1620 | #elif FUSE_VERSION >= FUSE_MAKE_VERSION(3, 1) | |
1621 | { | |
1622 | struct fuse_loop_config conf = { | |
1623 | clone_fd: opts.clone_fd, | |
1624 | max_idle_threads: opts.max_idle_threads | |
1625 | }; | |
e306af50 TL |
1626 | return fuse_session_loop_mt(se, &conf); |
1627 | } | |
1628 | #elif FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) | |
1911f103 TL |
1629 | return fuse_session_loop_mt(se, opts.clone_fd); |
1630 | #else | |
7c673cae | 1631 | return fuse_session_loop_mt(se); |
1911f103 | 1632 | #endif |
7c673cae FG |
1633 | } else { |
1634 | return fuse_session_loop(se); | |
1635 | } | |
1636 | } | |
1637 | ||
1638 | uint64_t CephFuse::Handle::fino_snap(uint64_t fino) | |
1639 | { | |
1640 | if (fino == FUSE_ROOT_ID) | |
1641 | return CEPH_NOSNAP; | |
1642 | ||
1643 | if (client->use_faked_inos()) { | |
1644 | vinodeno_t vino = client->map_faked_ino(fino); | |
1645 | return vino.snapid; | |
1646 | } else { | |
11fdf7f2 | 1647 | std::lock_guard l(stag_lock); |
7c673cae | 1648 | uint64_t stag = FINO_STAG(fino); |
2a845540 TL |
1649 | if (stag == 0) |
1650 | return CEPH_NOSNAP; | |
1651 | else if (stag == 1) | |
1652 | return CEPH_SNAPDIR; | |
1653 | ||
1654 | inodeno_t ino = FINO_INO(fino); | |
1655 | ||
1656 | // does the fino_maps for the ino exist ? | |
1657 | if (!g_fino_maps.count(ino)) | |
1658 | return CEPH_MAXSNAP; | |
1659 | ||
1660 | auto &fino_maps = g_fino_maps[ino]; | |
1661 | ||
1662 | // does the stagid <--> snapid map exist ? | |
1663 | if (!fino_maps.stag_snap_map.count(stag)) | |
1664 | return CEPH_MAXSNAP; | |
1665 | ||
1666 | // get the snapid | |
1667 | return fino_maps.stag_snap_map[stag]; | |
7c673cae FG |
1668 | } |
1669 | } | |
1670 | ||
1671 | Inode * CephFuse::Handle::iget(fuse_ino_t fino) | |
1672 | { | |
1673 | if (fino == FUSE_ROOT_ID) | |
1674 | return client->get_root(); | |
1675 | ||
1676 | if (client->use_faked_inos()) { | |
1677 | return client->ll_get_inode((ino_t)fino); | |
1678 | } else { | |
2a845540 TL |
1679 | uint64_t snap = fino_snap(fino); |
1680 | if (snap == CEPH_MAXSNAP) | |
1681 | return NULL; | |
1682 | vinodeno_t vino(FINO_INO(fino), snap); | |
7c673cae FG |
1683 | return client->ll_get_inode(vino); |
1684 | } | |
1685 | } | |
1686 | ||
1687 | void CephFuse::Handle::iput(Inode *in) | |
1688 | { | |
2a845540 | 1689 | client->ll_put(in); |
7c673cae FG |
1690 | } |
1691 | ||
1692 | uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino, snapid_t snapid) | |
1693 | { | |
1694 | if (client->use_faked_inos()) { | |
1695 | // already faked by libcephfs | |
1696 | if (ino == client->get_root_ino()) | |
1697 | return FUSE_ROOT_ID; | |
1698 | ||
1699 | return ino; | |
1700 | } else { | |
1701 | if (snapid == CEPH_NOSNAP && ino == client->get_root_ino()) | |
1702 | return FUSE_ROOT_ID; | |
1703 | ||
2a845540 TL |
1704 | int stag; |
1705 | if (snapid == CEPH_NOSNAP) { | |
1706 | stag = G_NOSNAP_STAG; | |
1707 | } else if (snapid == CEPH_SNAPDIR) { | |
1708 | stag = G_SNAPDIR_STAG; | |
1709 | } else { | |
1710 | std::lock_guard l(stag_lock); | |
1711 | auto &fino_maps = g_fino_maps[ino]; // will insert it anyway if not exists | |
11fdf7f2 | 1712 | |
2a845540 TL |
1713 | // already exist ? |
1714 | if (fino_maps.snap_stag_map.count(snapid)) { | |
1715 | inodeno_t fino = MAKE_FINO(ino, fino_maps.snap_stag_map[snapid]); | |
1716 | return fino; | |
11fdf7f2 TL |
1717 | } |
1718 | ||
2a845540 TL |
1719 | // create a new snapid <--> stagid map |
1720 | int first = fino_maps.last_stag & STAG_MASK; | |
1721 | stag = (++fino_maps.last_stag) & STAG_MASK; | |
1722 | for (; stag != first; stag = (++fino_maps.last_stag) & STAG_MASK) { | |
1723 | // stag 0 is reserved for CEPH_NOSNAP and 1 for CEPH_SNAPDIR | |
1724 | if (stag == 0 || stag == 1) | |
1725 | continue; | |
1726 | ||
1727 | // the new stag is not used ? | |
1728 | if (!fino_maps.stag_snap_map.count(stag)) { | |
1729 | fino_maps.snap_stag_map[snapid] = stag; | |
1730 | fino_maps.stag_snap_map[stag] = snapid; | |
1731 | break; | |
1732 | } | |
1733 | ||
1734 | // the stag is already used by a snpaid, | |
1735 | // try to free it | |
1736 | auto _snapid = fino_maps.stag_snap_map[stag]; | |
1737 | if (!client->ll_get_snap_ref(_snapid)) { | |
1738 | fino_maps.snap_stag_map.erase(_snapid); | |
1739 | fino_maps.snap_stag_map[snapid] = stag; | |
1740 | fino_maps.stag_snap_map[stag] = snapid; | |
1741 | break; | |
1742 | } | |
1743 | } | |
1744 | if (stag == first) { | |
1745 | /* | |
1746 | * It shouldn't be here because the max snapshots for each | |
1747 | * directory is 4_K, and here we have around 64_K, which is | |
1748 | * from 0xffff - 2, stags could be used for each directory. | |
1749 | * | |
1750 | * More detail please see mds 'mds_max_snaps_per_dir' option. | |
1751 | */ | |
1752 | ceph_abort_msg("run out of stag"); | |
11fdf7f2 TL |
1753 | } |
1754 | } | |
11fdf7f2 | 1755 | |
7c673cae FG |
1756 | inodeno_t fino = MAKE_FINO(ino, stag); |
1757 | //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl; | |
1758 | return fino; | |
1759 | } | |
1760 | } | |
1761 | ||
1762 | void CephFuse::Handle::set_fuse_req(fuse_req_t req) | |
1763 | { | |
1764 | pthread_setspecific(fuse_req_key, (void*)req); | |
1765 | } | |
1766 | ||
1767 | fuse_req_t CephFuse::Handle::get_fuse_req() | |
1768 | { | |
1769 | return (fuse_req_t) pthread_getspecific(fuse_req_key); | |
1770 | } | |
1771 | ||
1772 | ||
1773 | CephFuse::CephFuse(Client *c, int fd) : _handle(new CephFuse::Handle(c, fd)) | |
1774 | { | |
1775 | } | |
1776 | ||
1777 | CephFuse::~CephFuse() | |
1778 | { | |
1779 | delete _handle; | |
1780 | } | |
1781 | ||
1782 | int CephFuse::init(int argc, const char *argv[]) | |
1783 | { | |
1784 | return _handle->init(argc, argv); | |
1785 | } | |
1786 | ||
1787 | int CephFuse::start() | |
1788 | { | |
1789 | return _handle->start(); | |
1790 | } | |
1791 | ||
1792 | int CephFuse::loop() | |
1793 | { | |
1794 | return _handle->loop(); | |
1795 | } | |
1796 | ||
1797 | void CephFuse::finalize() | |
1798 | { | |
1799 | return _handle->finalize(); | |
1800 | } | |
1801 | ||
1802 | std::string CephFuse::get_mount_point() const | |
1803 | { | |
1911f103 TL |
1804 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
1805 | if (_handle->opts.mountpoint) { | |
1806 | return _handle->opts.mountpoint; | |
1807 | #else | |
7c673cae FG |
1808 | if (_handle->mountpoint) { |
1809 | return _handle->mountpoint; | |
1911f103 | 1810 | #endif |
7c673cae FG |
1811 | } else { |
1812 | return ""; | |
1813 | } | |
1814 | } |