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