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