Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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 int getgroups_cb(void *handle, gid_t **sgids)
131 {
132   CephFuse::Handle *cfuse = (CephFuse::Handle *) handle;
133   fuse_req_t req = cfuse->get_fuse_req();
134   return getgroups(req, sgids);
135 }
136
137 #define GET_GROUPS(perms, req)  {                               \
138   if (g_conf->get_val<bool>("fuse_set_user_groups")) {  \
139     gid_t *gids = NULL;                                         \
140     int count = getgroups(req, &gids);                          \
141     perms.init_gids(gids, count);                               \
142     perms.take_gids();                                          \
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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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   fuse_reply_err(req, 0);
752 }
753
754 static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name,
755                            mode_t mode, struct fuse_file_info *fi)
756 {
757   CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
758   const struct fuse_ctx *ctx = fuse_req_ctx(req);
759   Inode *i1 = cfuse->iget(parent), *i2;
760   struct fuse_entry_param fe;
761   Fh *fh = NULL;
762   UserPerm perms(ctx->uid, ctx->gid);
763   GET_GROUPS(perms, req);
764
765   memset(&fe, 0, sizeof(fe));
766
767   // pass &i2 for the created inode so that ll_create takes an initial ll_ref
768   int r = cfuse->client->ll_create(i1, name, mode, fi->flags, &fe.attr, &i2,
769                                    &fh, perms);
770   if (r == 0) {
771     fi->fh = (uint64_t)fh;
772     fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
773 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
774     if (cfuse->client->cct->_conf->fuse_disable_pagecache)
775       fi->direct_io = 1;
776     else if (cfuse->client->cct->_conf->fuse_use_invalidate_cb)
777       fi->keep_cache = 1;
778 #endif
779     fuse_reply_create(req, &fe, fi);
780   } else
781     fuse_reply_err(req, -r);
782   // XXX NB, we dont iput(i2) because FUSE will do so in a matching
783   // fuse_ll_forget()
784   cfuse->iput(i1); // iput required
785 }
786
787 static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino)
788 {
789   struct statvfs stbuf;
790   CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
791   Inode *in = cfuse->iget(ino);
792   const struct fuse_ctx *ctx = fuse_req_ctx(req);
793   UserPerm perms(ctx->uid, ctx->gid);
794   GET_GROUPS(perms, req);
795
796   int r = cfuse->client->ll_statfs(in, &stbuf, perms);
797   if (r == 0)
798     fuse_reply_statfs(req, &stbuf);
799   else
800     fuse_reply_err(req, -r);
801
802   cfuse->iput(in); // iput required
803 }
804
805 static void fuse_ll_getlk(fuse_req_t req, fuse_ino_t ino,
806                           struct fuse_file_info *fi, struct flock *lock)
807 {
808   CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
809   Fh *fh = reinterpret_cast<Fh*>(fi->fh);
810
811   int r = cfuse->client->ll_getlk(fh, lock, fi->lock_owner);
812   if (r == 0)
813     fuse_reply_lock(req, lock);
814   else
815     fuse_reply_err(req, -r);
816 }
817
818 static void fuse_ll_setlk(fuse_req_t req, fuse_ino_t ino,
819                           struct fuse_file_info *fi, struct flock *lock, int sleep)
820 {
821   CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
822   Fh *fh = reinterpret_cast<Fh*>(fi->fh);
823
824   // must use multithread if operation may block
825   if (!cfuse->client->cct->_conf->fuse_multithreaded &&
826       sleep && lock->l_type != F_UNLCK) {
827     fuse_reply_err(req, EDEADLK);
828     return;
829   }
830
831   int r = cfuse->client->ll_setlk(fh, lock, fi->lock_owner, sleep);
832   fuse_reply_err(req, -r);
833 }
834
835 static void fuse_ll_interrupt(fuse_req_t req, void* data)
836 {
837   CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
838   cfuse->client->ll_interrupt(data);
839 }
840
841 static void switch_interrupt_cb(void *handle, void* data)
842 {
843   CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
844   fuse_req_t req = cfuse->get_fuse_req();
845
846   if (data)
847     fuse_req_interrupt_func(req, fuse_ll_interrupt, data);
848   else
849     fuse_req_interrupt_func(req, NULL, NULL);
850 }
851
852 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
853 static void fuse_ll_flock(fuse_req_t req, fuse_ino_t ino,
854                           struct fuse_file_info *fi, int cmd)
855 {
856   CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
857   Fh *fh = (Fh*)fi->fh;
858
859   // must use multithread if operation may block
860   if (!cfuse->client->cct->_conf->fuse_multithreaded &&
861       !(cmd & (LOCK_NB | LOCK_UN))) {
862     fuse_reply_err(req, EDEADLK);
863     return;
864   }
865
866   int r = cfuse->client->ll_flock(fh, cmd, fi->lock_owner);
867   fuse_reply_err(req, -r);
868 }
869 #endif
870
871 #if !defined(DARWIN)
872 static mode_t umask_cb(void *handle)
873 {
874   CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
875   fuse_req_t req = cfuse->get_fuse_req();
876   const struct fuse_ctx *ctx = fuse_req_ctx(req);
877   return ctx->umask;
878 }
879 #endif
880
881 static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off,
882                               int64_t len)
883 {
884 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
885   CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
886   fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid);
887   fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len);
888 #endif
889 }
890
891 static void dentry_invalidate_cb(void *handle, vinodeno_t dirino,
892                                  vinodeno_t ino, string& name)
893 {
894   CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
895   fuse_ino_t fdirino = cfuse->make_fake_ino(dirino.ino, dirino.snapid);
896 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
897   fuse_ino_t fino = 0;
898   if (ino.ino != inodeno_t())
899     fino = cfuse->make_fake_ino(ino.ino, ino.snapid);
900   fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name.c_str(), name.length());
901 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
902   fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name.c_str(), name.length());
903 #endif
904 }
905
906 static int remount_cb(void *handle)
907 {
908   // used for trimming kernel dcache. when remounting a file system, linux kernel
909   // trims all unused dentries in the file system
910   char cmd[1024];
911   CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
912   snprintf(cmd, sizeof(cmd), "mount -i -o remount %s", cfuse->mountpoint);
913   int r = system(cmd);
914   if (r != 0 && r != -1) {
915     r = WEXITSTATUS(r);
916   }
917
918   return r;
919 }
920
921 static void do_init(void *data, fuse_conn_info *conn)
922 {
923   CephFuse::Handle *cfuse = (CephFuse::Handle *)data;
924   Client *client = cfuse->client;
925
926 #if !defined(DARWIN)
927   if (!client->cct->_conf->fuse_default_permissions &&
928       client->ll_handle_umask()) {
929     // apply umask in userspace if posix acl is enabled
930     if(conn->capable & FUSE_CAP_DONT_MASK)
931       conn->want |= FUSE_CAP_DONT_MASK;
932   }
933 #endif
934
935   if (cfuse->fd_on_success) {
936     //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
937     // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
938     // from a daemonized child process.
939     uint32_t r = -1;
940     int err = safe_write(cfuse->fd_on_success, &r, sizeof(r));
941     if (err) {
942       derr << "fuse_ll: do_init: safe_write failed with error "
943            << cpp_strerror(err) << dendl;
944       ceph_abort();
945     }
946     //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
947
948     // close stdout, etc.
949     ::close(0);
950     ::close(1);
951     ::close(2);
952   }
953 }
954
955 const static struct fuse_lowlevel_ops fuse_ll_oper = {
956  init: do_init,
957  destroy: 0,
958  lookup: fuse_ll_lookup,
959  forget: fuse_ll_forget,
960  getattr: fuse_ll_getattr,
961  setattr: fuse_ll_setattr,
962  readlink: fuse_ll_readlink,
963  mknod: fuse_ll_mknod,
964  mkdir: fuse_ll_mkdir,
965  unlink: fuse_ll_unlink,
966  rmdir: fuse_ll_rmdir,
967  symlink: fuse_ll_symlink,
968  rename: fuse_ll_rename,
969  link: fuse_ll_link,
970  open: fuse_ll_open,
971  read: fuse_ll_read,
972  write: fuse_ll_write,
973  flush: fuse_ll_flush,
974  release: fuse_ll_release,
975  fsync: fuse_ll_fsync,
976  opendir: fuse_ll_opendir,
977  readdir: fuse_ll_readdir,
978  releasedir: fuse_ll_releasedir,
979  fsyncdir: fuse_ll_fsyncdir,
980  statfs: fuse_ll_statfs,
981  setxattr: fuse_ll_setxattr,
982  getxattr: fuse_ll_getxattr,
983  listxattr: fuse_ll_listxattr,
984  removexattr: fuse_ll_removexattr,
985  access: fuse_ll_access,
986  create: fuse_ll_create,
987  getlk: fuse_ll_getlk,
988  setlk: fuse_ll_setlk,
989  bmap: 0,
990 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
991 #ifdef FUSE_IOCTL_COMPAT
992  ioctl: fuse_ll_ioctl,
993 #else
994  ioctl: 0,
995 #endif
996  poll: 0,
997 #endif
998 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
999  write_buf: 0,
1000  retrieve_reply: 0,
1001  forget_multi: 0,
1002  flock: fuse_ll_flock,
1003 #endif
1004 #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9)
1005  fallocate: fuse_ll_fallocate
1006 #endif
1007 };
1008
1009
1010 CephFuse::Handle::Handle(Client *c, int fd) :
1011   fd_on_success(fd),
1012   client(c),
1013   ch(NULL),
1014   se(NULL),
1015   mountpoint(NULL),
1016   stag_lock("fuse_ll.cc stag_lock"),
1017   last_stag(0)
1018 {
1019   snap_stag_map[CEPH_NOSNAP] = 0;
1020   stag_snap_map[0] = CEPH_NOSNAP;
1021   memset(&args, 0, sizeof(args));
1022 }
1023
1024 CephFuse::Handle::~Handle()
1025 {
1026   fuse_opt_free_args(&args);
1027 }
1028
1029 void CephFuse::Handle::finalize()
1030 {
1031   if (se)
1032     fuse_remove_signal_handlers(se);
1033   if (ch)
1034     fuse_session_remove_chan(ch);
1035   if (se)
1036     fuse_session_destroy(se);
1037   if (ch)
1038     fuse_unmount(mountpoint, ch);
1039
1040   pthread_key_delete(fuse_req_key);
1041 }
1042
1043 int CephFuse::Handle::init(int argc, const char *argv[])
1044 {
1045
1046   int r = pthread_key_create(&fuse_req_key, NULL);
1047   if (r) {
1048     derr << "pthread_key_create failed." << dendl;
1049     return r;
1050   }
1051
1052   // set up fuse argc/argv
1053   int newargc = 0;
1054   const char **newargv = (const char **) malloc((argc + 10) * sizeof(char *));
1055   if(!newargv)
1056     return ENOMEM;
1057
1058   newargv[newargc++] = argv[0];
1059   newargv[newargc++] = "-f";  // stay in foreground
1060
1061   if (client->cct->_conf->fuse_allow_other) {
1062     newargv[newargc++] = "-o";
1063     newargv[newargc++] = "allow_other";
1064   }
1065   if (client->cct->_conf->fuse_default_permissions) {
1066     newargv[newargc++] = "-o";
1067     newargv[newargc++] = "default_permissions";
1068   }
1069 #if defined(__linux__)
1070   if (client->cct->_conf->fuse_big_writes) {
1071     newargv[newargc++] = "-o";
1072     newargv[newargc++] = "big_writes";
1073   }
1074   if (client->cct->_conf->fuse_atomic_o_trunc) {
1075     newargv[newargc++] = "-o";
1076     newargv[newargc++] = "atomic_o_trunc";
1077   }
1078 #endif
1079   if (client->cct->_conf->fuse_debug)
1080     newargv[newargc++] = "-d";
1081
1082   for (int argctr = 1; argctr < argc; argctr++)
1083     newargv[newargc++] = argv[argctr];
1084
1085   derr << "init, newargv = " << newargv << " newargc=" << newargc << dendl;
1086   struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv);
1087   args = a;  // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1088
1089   if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) {
1090     derr << "fuse_parse_cmdline failed." << dendl;
1091     fuse_opt_free_args(&args);
1092     free(newargv);
1093     return EINVAL;
1094   }
1095
1096   assert(args.allocated);  // Checking fuse has realloc'd args so we can free newargv
1097   free(newargv);
1098   return 0;
1099 }
1100
1101 int CephFuse::Handle::start()
1102 {
1103   ch = fuse_mount(mountpoint, &args);
1104   if (!ch) {
1105     derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl;
1106     return EIO;
1107   }
1108
1109   se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
1110   if (!se) {
1111     derr << "fuse_lowlevel_new failed" << dendl;
1112     return EDOM;
1113   }
1114
1115   signal(SIGTERM, SIG_DFL);
1116   signal(SIGINT, SIG_DFL);
1117   if (fuse_set_signal_handlers(se) == -1) {
1118     derr << "fuse_set_signal_handlers failed" << dendl;
1119     return ENOSYS;
1120   }
1121
1122   fuse_session_add_chan(se, ch);
1123
1124
1125   struct client_callback_args args = {
1126     handle: this,
1127     ino_cb: client->cct->_conf->fuse_use_invalidate_cb ? ino_invalidate_cb : NULL,
1128     dentry_cb: dentry_invalidate_cb,
1129     switch_intr_cb: switch_interrupt_cb,
1130 #if defined(__linux__)
1131     remount_cb: remount_cb,
1132 #endif
1133     getgroups_cb: getgroups_cb,
1134 #if !defined(DARWIN)
1135     umask_cb: umask_cb,
1136 #endif
1137   };
1138   client->ll_register_callbacks(&args);
1139
1140   return 0;
1141 }
1142
1143 int CephFuse::Handle::loop()
1144 {
1145   if (client->cct->_conf->fuse_multithreaded) {
1146     return fuse_session_loop_mt(se);
1147   } else {
1148     return fuse_session_loop(se);
1149   }
1150 }
1151
1152 uint64_t CephFuse::Handle::fino_snap(uint64_t fino)
1153 {
1154   if (fino == FUSE_ROOT_ID)
1155     return CEPH_NOSNAP;
1156
1157   if (client->use_faked_inos()) {
1158     vinodeno_t vino  = client->map_faked_ino(fino);
1159     return vino.snapid;
1160   } else {
1161     Mutex::Locker l(stag_lock);
1162     uint64_t stag = FINO_STAG(fino);
1163     assert(stag_snap_map.count(stag));
1164     return stag_snap_map[stag];
1165   }
1166 }
1167
1168 Inode * CephFuse::Handle::iget(fuse_ino_t fino)
1169 {
1170   if (fino == FUSE_ROOT_ID)
1171     return client->get_root();
1172
1173   if (client->use_faked_inos()) {
1174     return client->ll_get_inode((ino_t)fino);
1175   } else {
1176     vinodeno_t vino(FINO_INO(fino), fino_snap(fino));
1177     return client->ll_get_inode(vino);
1178   }
1179 }
1180
1181 void CephFuse::Handle::iput(Inode *in)
1182 {
1183     client->ll_put(in);
1184 }
1185
1186 uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino, snapid_t snapid)
1187 {
1188   if (client->use_faked_inos()) {
1189     // already faked by libcephfs
1190     if (ino == client->get_root_ino())
1191       return FUSE_ROOT_ID;
1192
1193     return ino;
1194   } else {
1195     if (snapid == CEPH_NOSNAP && ino == client->get_root_ino())
1196       return FUSE_ROOT_ID;
1197
1198     Mutex::Locker l(stag_lock);
1199     uint64_t stag;
1200     if (snap_stag_map.count(snapid) == 0) {
1201       stag = ++last_stag;
1202       snap_stag_map[snapid] = stag;
1203       stag_snap_map[stag] = snapid;
1204     } else
1205       stag = snap_stag_map[snapid];
1206     inodeno_t fino = MAKE_FINO(ino, stag);
1207     //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1208     return fino;
1209   }
1210 }
1211
1212 void CephFuse::Handle::set_fuse_req(fuse_req_t req)
1213 {
1214   pthread_setspecific(fuse_req_key, (void*)req);
1215 }
1216
1217 fuse_req_t CephFuse::Handle::get_fuse_req()
1218 {
1219   return (fuse_req_t) pthread_getspecific(fuse_req_key);
1220 }
1221
1222
1223 CephFuse::CephFuse(Client *c, int fd) : _handle(new CephFuse::Handle(c, fd))
1224 {
1225 }
1226
1227 CephFuse::~CephFuse()
1228 {
1229   delete _handle;
1230 }
1231
1232 int CephFuse::init(int argc, const char *argv[])
1233 {
1234   return _handle->init(argc, argv);
1235 }
1236
1237 int CephFuse::start()
1238 {
1239   return _handle->start();
1240 }
1241
1242 int CephFuse::loop()
1243 {
1244   return _handle->loop();
1245 }
1246
1247 void CephFuse::finalize()
1248 {
1249   return _handle->finalize();
1250 }
1251
1252 std::string CephFuse::get_mount_point() const
1253 {
1254   if (_handle->mountpoint) {
1255     return _handle->mountpoint;
1256   } else {
1257     return "";
1258   }
1259 }