]> git.proxmox.com Git - ceph.git/blob - ceph/src/client/fuse_ll.cc
update sources to 12.2.7
[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 <signal.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25
26 // ceph
27 #include "common/errno.h"
28 #include "common/safe_io.h"
29 #include "include/types.h"
30 #include "Client.h"
31 #include "Fh.h"
32 #include "ioctl.h"
33 #include "common/config.h"
34 #include "include/assert.h"
35 #include "include/cephfs/ceph_statx.h"
36
37 #include "fuse_ll.h"
38 #include <fuse.h>
39 #include <fuse_lowlevel.h>
40
41 #define dout_context g_ceph_context
42
43 #define FINO_INO(x) ((x) & ((1ull<<48)-1ull))
44 #define FINO_STAG(x) ((x) >> 48)
45 #define MAKE_FINO(i,s) ((i) | ((s) << 48))
46
47 #define MINORBITS 20
48 #define MINORMASK ((1U << MINORBITS) - 1)
49
50 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
51 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
52 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
53
54 static uint32_t new_encode_dev(dev_t dev)
55 {
56 unsigned major = MAJOR(dev);
57 unsigned minor = MINOR(dev);
58 return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
59 }
60
61 static dev_t new_decode_dev(uint32_t dev)
62 {
63 unsigned major = (dev & 0xfff00) >> 8;
64 unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
65 return MKDEV(major, minor);
66 }
67
68 class CephFuse::Handle {
69 public:
70 Handle(Client *c, int fd);
71 ~Handle();
72
73 int init(int argc, const char *argv[]);
74 int start();
75 int loop();
76 void finalize();
77
78 uint64_t fino_snap(uint64_t fino);
79 uint64_t make_fake_ino(inodeno_t ino, snapid_t snapid);
80 Inode * iget(fuse_ino_t fino);
81 void iput(Inode *in);
82
83 int fd_on_success;
84 Client *client;
85
86 struct fuse_chan *ch;
87 struct fuse_session *se;
88 char *mountpoint;
89
90 Mutex stag_lock;
91 int last_stag;
92
93 ceph::unordered_map<uint64_t,int> snap_stag_map;
94 ceph::unordered_map<int,uint64_t> stag_snap_map;
95
96 pthread_key_t fuse_req_key;
97 void set_fuse_req(fuse_req_t);
98 fuse_req_t get_fuse_req();
99
100 struct fuse_args args;
101 };
102
103 static int getgroups(fuse_req_t req, gid_t **sgids)
104 {
105 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
106 assert(sgids);
107 int c = fuse_req_getgroups(req, 0, NULL);
108 if (c < 0) {
109 return c;
110 }
111 if (c == 0) {
112 return 0;
113 }
114
115 gid_t *gids = new (std::nothrow) gid_t[c];
116 if (!gids) {
117 return -ENOMEM;
118 }
119 c = fuse_req_getgroups(req, c, gids);
120 if (c < 0) {
121 delete gids;
122 } else {
123 *sgids = gids;
124 }
125 return c;
126 #endif
127 return -ENOSYS;
128 }
129
130 static void get_fuse_groups(UserPerm& perms, fuse_req_t req)
131 {
132 if (g_conf->get_val<bool>("fuse_set_user_groups")) {
133 gid_t *gids = NULL;
134 int count = getgroups(req, &gids);
135
136 if (count > 0) {
137 perms.init_gids(gids, count);
138 } else if (count < 0) {
139 derr << __func__ << ": getgroups failed: " << cpp_strerror(-count)
140 << dendl;
141 }
142 }
143 }
144
145
146 static CephFuse::Handle *fuse_ll_req_prepare(fuse_req_t req)
147 {
148 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req);
149 cfuse->set_fuse_req(req);
150 return cfuse;
151 }
152
153 static void fuse_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
154 {
155 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
156 const struct fuse_ctx *ctx = fuse_req_ctx(req);
157 struct fuse_entry_param fe;
158 Inode *i2, *i1 = cfuse->iget(parent); // see below
159 int r;
160 UserPerm perms(ctx->uid, ctx->gid);
161 get_fuse_groups(perms, req);
162
163 memset(&fe, 0, sizeof(fe));
164 r = cfuse->client->ll_lookup(i1, name, &fe.attr, &i2, perms);
165 if (r >= 0) {
166 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
167 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
168 fuse_reply_entry(req, &fe);
169 } else {
170 fuse_reply_err(req, -r);
171 }
172
173 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
174 // fuse_ll_forget()
175 cfuse->iput(i1);
176 }
177
178 static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino,
179 long unsigned nlookup)
180 {
181 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
182 cfuse->client->ll_forget(cfuse->iget(ino), nlookup+1);
183 fuse_reply_none(req);
184 }
185
186 static void fuse_ll_getattr(fuse_req_t req, fuse_ino_t ino,
187 struct fuse_file_info *fi)
188 {
189 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
190 const struct fuse_ctx *ctx = fuse_req_ctx(req);
191 Inode *in = cfuse->iget(ino);
192 struct stat stbuf;
193 UserPerm perms(ctx->uid, ctx->gid);
194 get_fuse_groups(perms, req);
195
196 (void) fi; // XXX
197
198 if (cfuse->client->ll_getattr(in, &stbuf, perms)
199 == 0) {
200 stbuf.st_ino = cfuse->make_fake_ino(stbuf.st_ino, stbuf.st_dev);
201 stbuf.st_rdev = new_encode_dev(stbuf.st_rdev);
202 fuse_reply_attr(req, &stbuf, 0);
203 } else
204 fuse_reply_err(req, ENOENT);
205
206 cfuse->iput(in); // iput required
207 }
208
209 static void fuse_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
210 int to_set, struct fuse_file_info *fi)
211 {
212 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
213 const struct fuse_ctx *ctx = fuse_req_ctx(req);
214 Inode *in = cfuse->iget(ino);
215 UserPerm perms(ctx->uid, ctx->gid);
216 get_fuse_groups(perms, req);
217
218 int mask = 0;
219 if (to_set & FUSE_SET_ATTR_MODE) mask |= CEPH_SETATTR_MODE;
220 if (to_set & FUSE_SET_ATTR_UID) mask |= CEPH_SETATTR_UID;
221 if (to_set & FUSE_SET_ATTR_GID) mask |= CEPH_SETATTR_GID;
222 if (to_set & FUSE_SET_ATTR_MTIME) mask |= CEPH_SETATTR_MTIME;
223 if (to_set & FUSE_SET_ATTR_ATIME) mask |= CEPH_SETATTR_ATIME;
224 if (to_set & FUSE_SET_ATTR_SIZE) mask |= CEPH_SETATTR_SIZE;
225 #if !defined(DARWIN)
226 if (to_set & FUSE_SET_ATTR_MTIME_NOW) mask |= CEPH_SETATTR_MTIME_NOW;
227 if (to_set & FUSE_SET_ATTR_ATIME_NOW) mask |= CEPH_SETATTR_ATIME_NOW;
228 #endif
229
230 int r = cfuse->client->ll_setattr(in, attr, mask, perms);
231 if (r == 0)
232 fuse_reply_attr(req, attr, 0);
233 else
234 fuse_reply_err(req, -r);
235
236 cfuse->iput(in); // iput required
237 }
238
239 // XATTRS
240
241 static void fuse_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
242 const char *value, size_t size,
243 int flags
244 #if defined(DARWIN)
245 ,uint32_t pos
246 #endif
247 )
248 {
249 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
250 const struct fuse_ctx *ctx = fuse_req_ctx(req);
251 Inode *in = cfuse->iget(ino);
252 UserPerm perms(ctx->uid, ctx->gid);
253 get_fuse_groups(perms, req);
254
255 int r = cfuse->client->ll_setxattr(in, name, value, size, flags, perms);
256 fuse_reply_err(req, -r);
257
258 cfuse->iput(in); // iput required
259 }
260
261 static void fuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
262 {
263 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
264 const struct fuse_ctx *ctx = fuse_req_ctx(req);
265 Inode *in = cfuse->iget(ino);
266 char buf[size];
267 UserPerm perms(ctx->uid, ctx->gid);
268 get_fuse_groups(perms, req);
269
270 int r = cfuse->client->ll_listxattr(in, buf, size, perms);
271 if (size == 0 && r >= 0)
272 fuse_reply_xattr(req, r);
273 else if (r >= 0)
274 fuse_reply_buf(req, buf, r);
275 else
276 fuse_reply_err(req, -r);
277
278 cfuse->iput(in); // iput required
279 }
280
281 static void fuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
282 size_t size
283 #if defined(DARWIN)
284 ,uint32_t position
285 #endif
286 )
287 {
288 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
289 const struct fuse_ctx *ctx = fuse_req_ctx(req);
290 Inode *in = cfuse->iget(ino);
291 char buf[size];
292 UserPerm perms(ctx->uid, ctx->gid);
293 get_fuse_groups(perms, req);
294
295 int r = cfuse->client->ll_getxattr(in, name, buf, size, perms);
296 if (size == 0 && r >= 0)
297 fuse_reply_xattr(req, r);
298 else if (r >= 0)
299 fuse_reply_buf(req, buf, r);
300 else
301 fuse_reply_err(req, -r);
302
303 cfuse->iput(in); // iput required
304 }
305
306 static void fuse_ll_removexattr(fuse_req_t req, fuse_ino_t ino,
307 const char *name)
308 {
309 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
310 const struct fuse_ctx *ctx = fuse_req_ctx(req);
311 Inode *in = cfuse->iget(ino);
312 UserPerm perms(ctx->uid, ctx->gid);
313 get_fuse_groups(perms, req);
314
315 int r = cfuse->client->ll_removexattr(in, name, perms);
316 fuse_reply_err(req, -r);
317
318 cfuse->iput(in); // iput required
319 }
320
321 static void fuse_ll_opendir(fuse_req_t req, fuse_ino_t ino,
322 struct fuse_file_info *fi)
323 {
324 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
325 const struct fuse_ctx *ctx = fuse_req_ctx(req);
326 Inode *in = cfuse->iget(ino);
327 void *dirp;
328
329 UserPerm perms(ctx->uid, ctx->gid);
330 get_fuse_groups(perms, req);
331
332 int r = cfuse->client->ll_opendir(in, fi->flags, (dir_result_t **)&dirp,
333 perms);
334 if (r >= 0) {
335 fi->fh = (uint64_t)dirp;
336 fuse_reply_open(req, fi);
337 } else {
338 fuse_reply_err(req, -r);
339 }
340
341 cfuse->iput(in); // iput required
342 }
343
344 static void fuse_ll_readlink(fuse_req_t req, fuse_ino_t ino)
345 {
346 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
347 const struct fuse_ctx *ctx = fuse_req_ctx(req);
348 Inode *in = cfuse->iget(ino);
349 char buf[PATH_MAX + 1]; // leave room for a null terminator
350 UserPerm perms(ctx->uid, ctx->gid);
351 get_fuse_groups(perms, req);
352
353 int r = cfuse->client->ll_readlink(in, buf, sizeof(buf) - 1, perms);
354 if (r >= 0) {
355 buf[r] = '\0';
356 fuse_reply_readlink(req, buf);
357 } else {
358 fuse_reply_err(req, -r);
359 }
360
361 cfuse->iput(in); // iput required
362 }
363
364 static void fuse_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
365 mode_t mode, dev_t rdev)
366 {
367 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
368 const struct fuse_ctx *ctx = fuse_req_ctx(req);
369 Inode *i2, *i1 = cfuse->iget(parent);
370 struct fuse_entry_param fe;
371 UserPerm perms(ctx->uid, ctx->gid);
372 get_fuse_groups(perms, req);
373
374 memset(&fe, 0, sizeof(fe));
375
376 int r = cfuse->client->ll_mknod(i1, name, mode, new_decode_dev(rdev),
377 &fe.attr, &i2, perms);
378 if (r == 0) {
379 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
380 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
381 fuse_reply_entry(req, &fe);
382 } else {
383 fuse_reply_err(req, -r);
384 }
385
386 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
387 // fuse_ll_forget()
388 cfuse->iput(i1); // iput required
389 }
390
391 static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
392 mode_t mode)
393 {
394 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
395 const struct fuse_ctx *ctx = fuse_req_ctx(req);
396 Inode *i2, *i1;
397 struct fuse_entry_param fe;
398
399 memset(&fe, 0, sizeof(fe));
400 UserPerm perm(ctx->uid, ctx->gid);
401 get_fuse_groups(perm, req);
402 #ifdef HAVE_SYS_SYNCFS
403 if (cfuse->fino_snap(parent) == CEPH_SNAPDIR &&
404 cfuse->client->cct->_conf->fuse_multithreaded &&
405 cfuse->client->cct->_conf->fuse_syncfs_on_mksnap) {
406 int err = 0;
407 int fd = ::open(cfuse->mountpoint, O_RDONLY | O_DIRECTORY);
408 if (fd < 0) {
409 err = errno;
410 } else {
411 int r = ::syncfs(fd);
412 if (r < 0)
413 err = errno;
414 ::close(fd);
415 }
416 if (err) {
417 fuse_reply_err(req, err);
418 return;
419 }
420 }
421 #endif
422
423 i1 = cfuse->iget(parent);
424 int r = cfuse->client->ll_mkdir(i1, name, mode, &fe.attr, &i2, perm);
425 if (r == 0) {
426 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
427 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
428 fuse_reply_entry(req, &fe);
429 } else {
430 fuse_reply_err(req, -r);
431 }
432
433 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
434 // fuse_ll_forget()
435 cfuse->iput(i1); // iput required
436 }
437
438 static void fuse_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
439 {
440 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
441 const struct fuse_ctx *ctx = fuse_req_ctx(req);
442 Inode *in = cfuse->iget(parent);
443 UserPerm perm(ctx->uid, ctx->gid);
444 get_fuse_groups(perm, req);
445
446 int r = cfuse->client->ll_unlink(in, name, perm);
447 fuse_reply_err(req, -r);
448
449 cfuse->iput(in); // iput required
450 }
451
452 static void fuse_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
453 {
454 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
455 const struct fuse_ctx *ctx = fuse_req_ctx(req);
456 Inode *in = cfuse->iget(parent);
457 UserPerm perms(ctx->uid, ctx->gid);
458 get_fuse_groups(perms, req);
459
460 int r = cfuse->client->ll_rmdir(in, name, perms);
461 fuse_reply_err(req, -r);
462
463 cfuse->iput(in); // iput required
464 }
465
466 static void fuse_ll_symlink(fuse_req_t req, const char *existing,
467 fuse_ino_t parent, const char *name)
468 {
469 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
470 const struct fuse_ctx *ctx = fuse_req_ctx(req);
471 Inode *i2, *i1 = cfuse->iget(parent);
472 struct fuse_entry_param fe;
473 UserPerm perms(ctx->uid, ctx->gid);
474 get_fuse_groups(perms, req);
475
476 memset(&fe, 0, sizeof(fe));
477
478 int r = cfuse->client->ll_symlink(i1, name, existing, &fe.attr, &i2, perms);
479 if (r == 0) {
480 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
481 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
482 fuse_reply_entry(req, &fe);
483 } else {
484 fuse_reply_err(req, -r);
485 }
486
487 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
488 // fuse_ll_forget()
489 cfuse->iput(i1); // iput required
490 }
491
492 static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
493 fuse_ino_t newparent, const char *newname)
494 {
495 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
496 const struct fuse_ctx *ctx = fuse_req_ctx(req);
497 Inode *in = cfuse->iget(parent);
498 Inode *nin = cfuse->iget(newparent);
499 UserPerm perm(ctx->uid, ctx->gid);
500 get_fuse_groups(perm, req);
501
502 int r = cfuse->client->ll_rename(in, name, nin, newname, perm);
503 fuse_reply_err(req, -r);
504
505 cfuse->iput(in); // iputs required
506 cfuse->iput(nin);
507 }
508
509 static void fuse_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
510 const char *newname)
511 {
512 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
513 const struct fuse_ctx *ctx = fuse_req_ctx(req);
514 Inode *in = cfuse->iget(ino);
515 Inode *nin = cfuse->iget(newparent);
516 struct fuse_entry_param fe;
517
518 memset(&fe, 0, sizeof(fe));
519 UserPerm perm(ctx->uid, ctx->gid);
520 get_fuse_groups(perm, req);
521
522 /*
523 * Note that we could successfully link, but then fail the subsequent
524 * getattr and return an error. Perhaps we should ignore getattr errors,
525 * but then how do we tell FUSE that the attrs are bogus?
526 */
527 int r = cfuse->client->ll_link(in, nin, newname, perm);
528 if (r == 0) {
529 r = cfuse->client->ll_getattr(in, &fe.attr, perm);
530 if (r == 0) {
531 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
532 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
533 fuse_reply_entry(req, &fe);
534 }
535 }
536
537 if (r != 0) {
538 /*
539 * Many ll operations in libcephfs return an extra inode reference, but
540 * ll_link currently does not. Still, FUSE needs one for the new dentry,
541 * so we commandeer the reference taken earlier when ll_link is successful.
542 * On error however, we must put that reference.
543 */
544 cfuse->iput(in);
545 fuse_reply_err(req, -r);
546 }
547
548 cfuse->iput(nin);
549 }
550
551 static void fuse_ll_open(fuse_req_t req, fuse_ino_t ino,
552 struct fuse_file_info *fi)
553 {
554 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
555 const struct fuse_ctx *ctx = fuse_req_ctx(req);
556 Inode *in = cfuse->iget(ino);
557 Fh *fh = NULL;
558 UserPerm perms(ctx->uid, ctx->gid);
559 get_fuse_groups(perms, req);
560
561 int r = cfuse->client->ll_open(in, fi->flags, &fh, perms);
562 if (r == 0) {
563 fi->fh = (uint64_t)fh;
564 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
565 if (cfuse->client->cct->_conf->fuse_disable_pagecache)
566 fi->direct_io = 1;
567 else if (cfuse->client->cct->_conf->fuse_use_invalidate_cb)
568 fi->keep_cache = 1;
569 #endif
570 fuse_reply_open(req, fi);
571 } else {
572 fuse_reply_err(req, -r);
573 }
574
575 cfuse->iput(in); // iput required
576 }
577
578 static void fuse_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
579 struct fuse_file_info *fi)
580 {
581 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
582 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
583 bufferlist bl;
584 int r = cfuse->client->ll_read(fh, off, size, &bl);
585 if (r >= 0)
586 fuse_reply_buf(req, bl.c_str(), bl.length());
587 else
588 fuse_reply_err(req, -r);
589 }
590
591 static void fuse_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
592 size_t size, off_t off, struct fuse_file_info *fi)
593 {
594 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
595 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
596 int r = cfuse->client->ll_write(fh, off, size, buf);
597 if (r >= 0)
598 fuse_reply_write(req, r);
599 else
600 fuse_reply_err(req, -r);
601 }
602
603 static void fuse_ll_flush(fuse_req_t req, fuse_ino_t ino,
604 struct fuse_file_info *fi)
605 {
606 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
607 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
608 int r = cfuse->client->ll_flush(fh);
609 fuse_reply_err(req, -r);
610 }
611
612 #ifdef FUSE_IOCTL_COMPAT
613 static void fuse_ll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi,
614 unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
615 {
616 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
617
618 if (flags & FUSE_IOCTL_COMPAT) {
619 fuse_reply_err(req, ENOSYS);
620 return;
621 }
622
623 switch (static_cast<unsigned>(cmd)) {
624 case CEPH_IOC_GET_LAYOUT: {
625 file_layout_t layout;
626 struct ceph_ioctl_layout l;
627 Fh *fh = (Fh*)fi->fh;
628 cfuse->client->ll_file_layout(fh, &layout);
629 l.stripe_unit = layout.stripe_unit;
630 l.stripe_count = layout.stripe_count;
631 l.object_size = layout.object_size;
632 l.data_pool = layout.pool_id;
633 fuse_reply_ioctl(req, 0, &l, sizeof(struct ceph_ioctl_layout));
634 }
635 break;
636 default:
637 fuse_reply_err(req, EINVAL);
638 }
639 }
640 #endif
641
642 #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9)
643
644 static void fuse_ll_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
645 off_t offset, off_t length,
646 struct fuse_file_info *fi)
647 {
648 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
649 Fh *fh = (Fh*)fi->fh;
650 int r = cfuse->client->ll_fallocate(fh, mode, offset, length);
651 fuse_reply_err(req, -r);
652 }
653
654 #endif
655
656 static void fuse_ll_release(fuse_req_t req, fuse_ino_t ino,
657 struct fuse_file_info *fi)
658 {
659 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
660 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
661 int r = cfuse->client->ll_release(fh);
662 fuse_reply_err(req, -r);
663 }
664
665 static void fuse_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
666 struct fuse_file_info *fi)
667 {
668 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
669 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
670 int r = cfuse->client->ll_fsync(fh, datasync);
671 fuse_reply_err(req, -r);
672 }
673
674 struct readdir_context {
675 fuse_req_t req;
676 char *buf;
677 size_t size;
678 size_t pos; /* in buf */
679 uint64_t snap;
680 };
681
682 /*
683 * return 0 on success, -1 if out of space
684 */
685 static int fuse_ll_add_dirent(void *p, struct dirent *de,
686 struct ceph_statx *stx, off_t next_off,
687 Inode *in)
688 {
689 struct readdir_context *c = (struct readdir_context *)p;
690 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(c->req);
691
692 struct stat st;
693 st.st_ino = cfuse->make_fake_ino(stx->stx_ino, c->snap);
694 st.st_mode = stx->stx_mode;
695 st.st_rdev = new_encode_dev(stx->stx_rdev);
696
697 size_t room = c->size - c->pos;
698 size_t entrysize = fuse_add_direntry(c->req, c->buf + c->pos, room,
699 de->d_name, &st, next_off);
700 if (entrysize > room)
701 return -ENOSPC;
702
703 /* success */
704 c->pos += entrysize;
705 return 0;
706 }
707
708 static void fuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
709 off_t off, struct fuse_file_info *fi)
710 {
711 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
712
713 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
714 cfuse->client->seekdir(dirp, off);
715
716 struct readdir_context rc;
717 rc.req = req;
718 rc.buf = new char[size];
719 rc.size = size;
720 rc.pos = 0;
721 rc.snap = cfuse->fino_snap(ino);
722
723 int r = cfuse->client->readdir_r_cb(dirp, fuse_ll_add_dirent, &rc);
724 if (r == 0 || r == -ENOSPC) /* ignore ENOSPC from our callback */
725 fuse_reply_buf(req, rc.buf, rc.pos);
726 else
727 fuse_reply_err(req, -r);
728 delete[] rc.buf;
729 }
730
731 static void fuse_ll_releasedir(fuse_req_t req, fuse_ino_t ino,
732 struct fuse_file_info *fi)
733 {
734 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
735 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
736 cfuse->client->ll_releasedir(dirp);
737 fuse_reply_err(req, 0);
738 }
739
740 static void fuse_ll_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
741 struct fuse_file_info *fi)
742 {
743 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
744 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
745 int r = cfuse->client->ll_fsyncdir(dirp);
746 fuse_reply_err(req, -r);
747 }
748
749 static void fuse_ll_access(fuse_req_t req, fuse_ino_t ino, int mask)
750 {
751 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
752 const struct fuse_ctx *ctx = fuse_req_ctx(req);
753 Inode *in = cfuse->iget(ino);
754 UserPerm perms(ctx->uid, ctx->gid);
755 get_fuse_groups(perms, req);
756
757 int r = cfuse->client->inode_permission(in, perms, mask);
758 fuse_reply_err(req, -r);
759 cfuse->iput(in);
760 }
761
762 static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name,
763 mode_t mode, struct fuse_file_info *fi)
764 {
765 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
766 const struct fuse_ctx *ctx = fuse_req_ctx(req);
767 Inode *i1 = cfuse->iget(parent), *i2;
768 struct fuse_entry_param fe;
769 Fh *fh = NULL;
770 UserPerm perms(ctx->uid, ctx->gid);
771 get_fuse_groups(perms, req);
772
773 memset(&fe, 0, sizeof(fe));
774
775 // pass &i2 for the created inode so that ll_create takes an initial ll_ref
776 int r = cfuse->client->ll_create(i1, name, mode, fi->flags, &fe.attr, &i2,
777 &fh, perms);
778 if (r == 0) {
779 fi->fh = (uint64_t)fh;
780 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
781 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
782 if (cfuse->client->cct->_conf->fuse_disable_pagecache)
783 fi->direct_io = 1;
784 else if (cfuse->client->cct->_conf->fuse_use_invalidate_cb)
785 fi->keep_cache = 1;
786 #endif
787 fuse_reply_create(req, &fe, fi);
788 } else
789 fuse_reply_err(req, -r);
790 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
791 // fuse_ll_forget()
792 cfuse->iput(i1); // iput required
793 }
794
795 static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino)
796 {
797 struct statvfs stbuf;
798 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
799 Inode *in = cfuse->iget(ino);
800 const struct fuse_ctx *ctx = fuse_req_ctx(req);
801 UserPerm perms(ctx->uid, ctx->gid);
802 get_fuse_groups(perms, req);
803
804 int r = cfuse->client->ll_statfs(in, &stbuf, perms);
805 if (r == 0)
806 fuse_reply_statfs(req, &stbuf);
807 else
808 fuse_reply_err(req, -r);
809
810 cfuse->iput(in); // iput required
811 }
812
813 static void fuse_ll_getlk(fuse_req_t req, fuse_ino_t ino,
814 struct fuse_file_info *fi, struct flock *lock)
815 {
816 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
817 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
818
819 int r = cfuse->client->ll_getlk(fh, lock, fi->lock_owner);
820 if (r == 0)
821 fuse_reply_lock(req, lock);
822 else
823 fuse_reply_err(req, -r);
824 }
825
826 static void fuse_ll_setlk(fuse_req_t req, fuse_ino_t ino,
827 struct fuse_file_info *fi, struct flock *lock, int sleep)
828 {
829 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
830 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
831
832 // must use multithread if operation may block
833 if (!cfuse->client->cct->_conf->fuse_multithreaded &&
834 sleep && lock->l_type != F_UNLCK) {
835 fuse_reply_err(req, EDEADLK);
836 return;
837 }
838
839 int r = cfuse->client->ll_setlk(fh, lock, fi->lock_owner, sleep);
840 fuse_reply_err(req, -r);
841 }
842
843 static void fuse_ll_interrupt(fuse_req_t req, void* data)
844 {
845 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
846 cfuse->client->ll_interrupt(data);
847 }
848
849 static void switch_interrupt_cb(void *handle, void* data)
850 {
851 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
852 fuse_req_t req = cfuse->get_fuse_req();
853
854 if (data)
855 fuse_req_interrupt_func(req, fuse_ll_interrupt, data);
856 else
857 fuse_req_interrupt_func(req, NULL, NULL);
858 }
859
860 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
861 static void fuse_ll_flock(fuse_req_t req, fuse_ino_t ino,
862 struct fuse_file_info *fi, int cmd)
863 {
864 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
865 Fh *fh = (Fh*)fi->fh;
866
867 // must use multithread if operation may block
868 if (!cfuse->client->cct->_conf->fuse_multithreaded &&
869 !(cmd & (LOCK_NB | LOCK_UN))) {
870 fuse_reply_err(req, EDEADLK);
871 return;
872 }
873
874 int r = cfuse->client->ll_flock(fh, cmd, fi->lock_owner);
875 fuse_reply_err(req, -r);
876 }
877 #endif
878
879 #if !defined(DARWIN)
880 static mode_t umask_cb(void *handle)
881 {
882 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
883 fuse_req_t req = cfuse->get_fuse_req();
884 const struct fuse_ctx *ctx = fuse_req_ctx(req);
885 return ctx->umask;
886 }
887 #endif
888
889 static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off,
890 int64_t len)
891 {
892 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
893 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
894 fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid);
895 fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len);
896 #endif
897 }
898
899 static void dentry_invalidate_cb(void *handle, vinodeno_t dirino,
900 vinodeno_t ino, string& name)
901 {
902 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
903 fuse_ino_t fdirino = cfuse->make_fake_ino(dirino.ino, dirino.snapid);
904 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
905 fuse_ino_t fino = 0;
906 if (ino.ino != inodeno_t())
907 fino = cfuse->make_fake_ino(ino.ino, ino.snapid);
908 fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name.c_str(), name.length());
909 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
910 fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name.c_str(), name.length());
911 #endif
912 }
913
914 static int remount_cb(void *handle)
915 {
916 // used for trimming kernel dcache. when remounting a file system, linux kernel
917 // trims all unused dentries in the file system
918 char cmd[1024];
919 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
920 snprintf(cmd, sizeof(cmd), "mount -i -o remount %s", cfuse->mountpoint);
921 int r = system(cmd);
922 if (r != 0 && r != -1) {
923 r = WEXITSTATUS(r);
924 }
925
926 return r;
927 }
928
929 static void do_init(void *data, fuse_conn_info *conn)
930 {
931 CephFuse::Handle *cfuse = (CephFuse::Handle *)data;
932 Client *client = cfuse->client;
933
934 #if !defined(DARWIN)
935 if (!client->cct->_conf->fuse_default_permissions &&
936 client->ll_handle_umask()) {
937 // apply umask in userspace if posix acl is enabled
938 if(conn->capable & FUSE_CAP_DONT_MASK)
939 conn->want |= FUSE_CAP_DONT_MASK;
940 }
941 #endif
942
943 if (cfuse->fd_on_success) {
944 //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
945 // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
946 // from a daemonized child process.
947 uint32_t r = -1;
948 int err = safe_write(cfuse->fd_on_success, &r, sizeof(r));
949 if (err) {
950 derr << "fuse_ll: do_init: safe_write failed with error "
951 << cpp_strerror(err) << dendl;
952 ceph_abort();
953 }
954 //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
955
956 // close stdout, etc.
957 ::close(0);
958 ::close(1);
959 ::close(2);
960 }
961 }
962
963 const static struct fuse_lowlevel_ops fuse_ll_oper = {
964 init: do_init,
965 destroy: 0,
966 lookup: fuse_ll_lookup,
967 forget: fuse_ll_forget,
968 getattr: fuse_ll_getattr,
969 setattr: fuse_ll_setattr,
970 readlink: fuse_ll_readlink,
971 mknod: fuse_ll_mknod,
972 mkdir: fuse_ll_mkdir,
973 unlink: fuse_ll_unlink,
974 rmdir: fuse_ll_rmdir,
975 symlink: fuse_ll_symlink,
976 rename: fuse_ll_rename,
977 link: fuse_ll_link,
978 open: fuse_ll_open,
979 read: fuse_ll_read,
980 write: fuse_ll_write,
981 flush: fuse_ll_flush,
982 release: fuse_ll_release,
983 fsync: fuse_ll_fsync,
984 opendir: fuse_ll_opendir,
985 readdir: fuse_ll_readdir,
986 releasedir: fuse_ll_releasedir,
987 fsyncdir: fuse_ll_fsyncdir,
988 statfs: fuse_ll_statfs,
989 setxattr: fuse_ll_setxattr,
990 getxattr: fuse_ll_getxattr,
991 listxattr: fuse_ll_listxattr,
992 removexattr: fuse_ll_removexattr,
993 access: fuse_ll_access,
994 create: fuse_ll_create,
995 getlk: fuse_ll_getlk,
996 setlk: fuse_ll_setlk,
997 bmap: 0,
998 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
999 #ifdef FUSE_IOCTL_COMPAT
1000 ioctl: fuse_ll_ioctl,
1001 #else
1002 ioctl: 0,
1003 #endif
1004 poll: 0,
1005 #endif
1006 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1007 write_buf: 0,
1008 retrieve_reply: 0,
1009 forget_multi: 0,
1010 flock: fuse_ll_flock,
1011 #endif
1012 #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9)
1013 fallocate: fuse_ll_fallocate
1014 #endif
1015 };
1016
1017
1018 CephFuse::Handle::Handle(Client *c, int fd) :
1019 fd_on_success(fd),
1020 client(c),
1021 ch(NULL),
1022 se(NULL),
1023 mountpoint(NULL),
1024 stag_lock("fuse_ll.cc stag_lock"),
1025 last_stag(0)
1026 {
1027 snap_stag_map[CEPH_NOSNAP] = 0;
1028 stag_snap_map[0] = CEPH_NOSNAP;
1029 memset(&args, 0, sizeof(args));
1030 }
1031
1032 CephFuse::Handle::~Handle()
1033 {
1034 fuse_opt_free_args(&args);
1035 }
1036
1037 void CephFuse::Handle::finalize()
1038 {
1039 if (se)
1040 fuse_remove_signal_handlers(se);
1041 if (ch)
1042 fuse_session_remove_chan(ch);
1043 if (se)
1044 fuse_session_destroy(se);
1045 if (ch)
1046 fuse_unmount(mountpoint, ch);
1047
1048 pthread_key_delete(fuse_req_key);
1049 }
1050
1051 int CephFuse::Handle::init(int argc, const char *argv[])
1052 {
1053
1054 int r = pthread_key_create(&fuse_req_key, NULL);
1055 if (r) {
1056 derr << "pthread_key_create failed." << dendl;
1057 return r;
1058 }
1059
1060 // set up fuse argc/argv
1061 int newargc = 0;
1062 const char **newargv = (const char **) malloc((argc + 10) * sizeof(char *));
1063 if(!newargv)
1064 return ENOMEM;
1065
1066 newargv[newargc++] = argv[0];
1067 newargv[newargc++] = "-f"; // stay in foreground
1068
1069 if (client->cct->_conf->fuse_allow_other) {
1070 newargv[newargc++] = "-o";
1071 newargv[newargc++] = "allow_other";
1072 }
1073 if (client->cct->_conf->fuse_default_permissions) {
1074 newargv[newargc++] = "-o";
1075 newargv[newargc++] = "default_permissions";
1076 }
1077 #if defined(__linux__)
1078 if (client->cct->_conf->fuse_big_writes) {
1079 newargv[newargc++] = "-o";
1080 newargv[newargc++] = "big_writes";
1081 }
1082 if (client->cct->_conf->fuse_atomic_o_trunc) {
1083 newargv[newargc++] = "-o";
1084 newargv[newargc++] = "atomic_o_trunc";
1085 }
1086 #endif
1087 if (client->cct->_conf->fuse_debug)
1088 newargv[newargc++] = "-d";
1089
1090 for (int argctr = 1; argctr < argc; argctr++)
1091 newargv[newargc++] = argv[argctr];
1092
1093 derr << "init, newargv = " << newargv << " newargc=" << newargc << dendl;
1094 struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv);
1095 args = a; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1096
1097 if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) {
1098 derr << "fuse_parse_cmdline failed." << dendl;
1099 fuse_opt_free_args(&args);
1100 free(newargv);
1101 return EINVAL;
1102 }
1103
1104 assert(args.allocated); // Checking fuse has realloc'd args so we can free newargv
1105 free(newargv);
1106 return 0;
1107 }
1108
1109 int CephFuse::Handle::start()
1110 {
1111 ch = fuse_mount(mountpoint, &args);
1112 if (!ch) {
1113 derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl;
1114 return EIO;
1115 }
1116
1117 se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
1118 if (!se) {
1119 derr << "fuse_lowlevel_new failed" << dendl;
1120 return EDOM;
1121 }
1122
1123 signal(SIGTERM, SIG_DFL);
1124 signal(SIGINT, SIG_DFL);
1125 if (fuse_set_signal_handlers(se) == -1) {
1126 derr << "fuse_set_signal_handlers failed" << dendl;
1127 return ENOSYS;
1128 }
1129
1130 fuse_session_add_chan(se, ch);
1131
1132
1133 struct client_callback_args args = {
1134 handle: this,
1135 ino_cb: client->cct->_conf->fuse_use_invalidate_cb ? ino_invalidate_cb : NULL,
1136 dentry_cb: dentry_invalidate_cb,
1137 switch_intr_cb: switch_interrupt_cb,
1138 #if defined(__linux__)
1139 remount_cb: remount_cb,
1140 #endif
1141 #if !defined(DARWIN)
1142 umask_cb: umask_cb,
1143 #endif
1144 };
1145 client->ll_register_callbacks(&args);
1146
1147 return 0;
1148 }
1149
1150 int CephFuse::Handle::loop()
1151 {
1152 if (client->cct->_conf->fuse_multithreaded) {
1153 return fuse_session_loop_mt(se);
1154 } else {
1155 return fuse_session_loop(se);
1156 }
1157 }
1158
1159 uint64_t CephFuse::Handle::fino_snap(uint64_t fino)
1160 {
1161 if (fino == FUSE_ROOT_ID)
1162 return CEPH_NOSNAP;
1163
1164 if (client->use_faked_inos()) {
1165 vinodeno_t vino = client->map_faked_ino(fino);
1166 return vino.snapid;
1167 } else {
1168 Mutex::Locker l(stag_lock);
1169 uint64_t stag = FINO_STAG(fino);
1170 assert(stag_snap_map.count(stag));
1171 return stag_snap_map[stag];
1172 }
1173 }
1174
1175 Inode * CephFuse::Handle::iget(fuse_ino_t fino)
1176 {
1177 if (fino == FUSE_ROOT_ID)
1178 return client->get_root();
1179
1180 if (client->use_faked_inos()) {
1181 return client->ll_get_inode((ino_t)fino);
1182 } else {
1183 vinodeno_t vino(FINO_INO(fino), fino_snap(fino));
1184 return client->ll_get_inode(vino);
1185 }
1186 }
1187
1188 void CephFuse::Handle::iput(Inode *in)
1189 {
1190 client->ll_put(in);
1191 }
1192
1193 uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino, snapid_t snapid)
1194 {
1195 if (client->use_faked_inos()) {
1196 // already faked by libcephfs
1197 if (ino == client->get_root_ino())
1198 return FUSE_ROOT_ID;
1199
1200 return ino;
1201 } else {
1202 if (snapid == CEPH_NOSNAP && ino == client->get_root_ino())
1203 return FUSE_ROOT_ID;
1204
1205 Mutex::Locker l(stag_lock);
1206 uint64_t stag;
1207 if (snap_stag_map.count(snapid) == 0) {
1208 stag = ++last_stag;
1209 snap_stag_map[snapid] = stag;
1210 stag_snap_map[stag] = snapid;
1211 } else
1212 stag = snap_stag_map[snapid];
1213 inodeno_t fino = MAKE_FINO(ino, stag);
1214 //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1215 return fino;
1216 }
1217 }
1218
1219 void CephFuse::Handle::set_fuse_req(fuse_req_t req)
1220 {
1221 pthread_setspecific(fuse_req_key, (void*)req);
1222 }
1223
1224 fuse_req_t CephFuse::Handle::get_fuse_req()
1225 {
1226 return (fuse_req_t) pthread_getspecific(fuse_req_key);
1227 }
1228
1229
1230 CephFuse::CephFuse(Client *c, int fd) : _handle(new CephFuse::Handle(c, fd))
1231 {
1232 }
1233
1234 CephFuse::~CephFuse()
1235 {
1236 delete _handle;
1237 }
1238
1239 int CephFuse::init(int argc, const char *argv[])
1240 {
1241 return _handle->init(argc, argv);
1242 }
1243
1244 int CephFuse::start()
1245 {
1246 return _handle->start();
1247 }
1248
1249 int CephFuse::loop()
1250 {
1251 return _handle->loop();
1252 }
1253
1254 void CephFuse::finalize()
1255 {
1256 return _handle->finalize();
1257 }
1258
1259 std::string CephFuse::get_mount_point() const
1260 {
1261 if (_handle->mountpoint) {
1262 return _handle->mountpoint;
1263 } else {
1264 return "";
1265 }
1266 }