ceph: VFS: Add asynchronous fsync to ceph module, fake using synchronous call.
[samba.git] / source3 / modules / vfs_ceph.c
1 /*
2    Unix SMB/CIFS implementation.
3    Wrap disk only vfs functions to sidestep dodgy compilers.
4    Copyright (C) Tim Potter 1998
5    Copyright (C) Jeremy Allison 2007
6    Copyright (C) Brian Chrisman 2011 <bchrisman@gmail.com>
7    Copyright (C) Richard Sharpe 2011 <realrichardsharpe@gmail.com>
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24  * This VFS only works with the libceph.so user-space client. It is not needed
25  * if you are using the kernel client or the FUSE client.
26  *
27  * Add the following smb.conf parameter to each share that will be hosted on
28  * Ceph:
29  *
30  *   vfs objects = ceph [any others you need go here]
31  */
32
33 #include "includes.h"
34 #include "smbd/smbd.h"
35 #include <dirent.h>
36 #include <sys/statvfs.h>
37 #include "cephfs/libcephfs.h"
38 #include "smbprofile.h"
39 #include "modules/posixacl_xattr.h"
40 #include "lib/util/tevent_unix.h"
41
42 #undef DBGC_CLASS
43 #define DBGC_CLASS DBGC_VFS
44
45 #ifndef LIBCEPHFS_VERSION
46 #define LIBCEPHFS_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
47 #define LIBCEPHFS_VERSION_CODE LIBCEPHFS_VERSION(0, 0, 0)
48 #endif
49
50 /*
51  * Use %llu whenever we have a 64bit unsigned int, and cast to (long long unsigned)
52  */
53 #define llu(_var) ((long long unsigned)_var)
54
55 /*
56  * Note, libceph's return code model is to return -errno! So we have to convert
57  * to what Samba expects, with is set errno to -return and return -1
58  */
59 #define WRAP_RETURN(_res) \
60         errno = 0; \
61         if (_res < 0) { \
62                 errno = -_res; \
63                 return -1; \
64         } \
65         return _res \
66
67 /*
68  * We mount only one file system and then all shares are assumed to be in that.
69  * FIXME: If we want to support more than one FS, then we have to deal with
70  * this differently.
71  *
72  * So, cmount tells us if we have been this way before and whether
73  * we need to mount ceph and cmount_cnt tells us how many times we have
74  * connected
75  */
76 static struct ceph_mount_info * cmount = NULL;
77 static uint32_t cmount_cnt = 0;
78
79 /* Check for NULL pointer parameters in cephwrap_* functions */
80
81 /* We don't want to have NULL function pointers lying around.  Someone
82    is sure to try and execute them.  These stubs are used to prevent
83    this possibility. */
84
85 static int cephwrap_connect(struct vfs_handle_struct *handle,  const char *service, const char *user)
86 {
87         int ret;
88         char buf[256];
89         int snum = SNUM(handle->conn);
90         const char *conf_file;
91         const char *user_id;
92
93         if (cmount) {
94                 handle->data = cmount; /* We have been here before */
95                 cmount_cnt++;
96                 return 0;
97         }
98
99         /* if config_file and/or user_id are NULL, ceph will use defaults */
100         conf_file = lp_parm_const_string(snum, "ceph", "config_file", NULL);
101         user_id = lp_parm_const_string(snum, "ceph", "user_id", NULL);
102
103         DBG_DEBUG("[CEPH] calling: ceph_create\n");
104         ret = ceph_create(&cmount, user_id);
105         if (ret) {
106                 goto err_out;
107         }
108
109         DBG_DEBUG("[CEPH] calling: ceph_conf_read_file with %s\n",
110                   (conf_file == NULL ? "default path" : conf_file));
111         ret = ceph_conf_read_file(cmount, conf_file);
112         if (ret) {
113                 goto err_cm_release;
114         }
115
116         DBG_DEBUG("[CEPH] calling: ceph_conf_get\n");
117         ret = ceph_conf_get(cmount, "log file", buf, sizeof(buf));
118         if (ret < 0) {
119                 goto err_cm_release;
120         }
121
122         DBG_DEBUG("[CEPH] calling: ceph_mount\n");
123         ret = ceph_mount(cmount, NULL);
124         if (ret < 0) {
125                 goto err_cm_release;
126         }
127
128         /*
129          * encode mount context/state into our vfs/connection holding structure
130          * cmount is a ceph_mount_t*
131          */
132         handle->data = cmount;
133         cmount_cnt++;
134
135         return 0;
136
137 err_cm_release:
138         ceph_release(cmount);
139         cmount = NULL;
140 err_out:
141         /*
142          * Handle the error correctly. Ceph returns -errno.
143          */
144         DBG_DEBUG("[CEPH] Error return: %s\n", strerror(-ret));
145         WRAP_RETURN(ret);
146 }
147
148 static void cephwrap_disconnect(struct vfs_handle_struct *handle)
149 {
150         int ret;
151
152         if (!cmount) {
153                 DBG_ERR("[CEPH] Error, ceph not mounted\n");
154                 return;
155         }
156
157         /* Should we unmount/shutdown? Only if the last disconnect? */
158         if (--cmount_cnt) {
159                 DBG_DEBUG("[CEPH] Not shuting down CEPH because still more connections\n");
160                 return;
161         }
162
163         ret = ceph_unmount(cmount);
164         if (ret < 0) {
165                 DBG_ERR("[CEPH] failed to unmount: %s\n", strerror(-ret));
166         }
167
168         ret = ceph_release(cmount);
169         if (ret < 0) {
170                 DBG_ERR("[CEPH] failed to release: %s\n", strerror(-ret));
171         }
172
173         cmount = NULL;  /* Make it safe */
174 }
175
176 /* Disk operations */
177
178 static uint64_t cephwrap_disk_free(struct vfs_handle_struct *handle,
179                                 const struct smb_filename *smb_fname,
180                                 uint64_t *bsize,
181                                 uint64_t *dfree,
182                                 uint64_t *dsize)
183 {
184         struct statvfs statvfs_buf;
185         int ret;
186
187         if (!(ret = ceph_statfs(handle->data, smb_fname->base_name,
188                         &statvfs_buf))) {
189                 /*
190                  * Provide all the correct values.
191                  */
192                 *bsize = statvfs_buf.f_bsize;
193                 *dfree = statvfs_buf.f_bavail;
194                 *dsize = statvfs_buf.f_blocks;
195                 DBG_DEBUG("[CEPH] bsize: %llu, dfree: %llu, dsize: %llu\n",
196                         llu(*bsize), llu(*dfree), llu(*dsize));
197                 return *dfree;
198         } else {
199                 DBG_DEBUG("[CEPH] ceph_statfs returned %d\n", ret);
200                 WRAP_RETURN(ret);
201         }
202 }
203
204 static int cephwrap_get_quota(struct vfs_handle_struct *handle,
205                                 const struct smb_filename *smb_fname,
206                                 enum SMB_QUOTA_TYPE qtype,
207                                 unid_t id,
208                                 SMB_DISK_QUOTA *qt)
209 {
210         /* libceph: Ceph does not implement this */
211 #if 0
212 /* was ifdef HAVE_SYS_QUOTAS */
213         int ret;
214
215         ret = ceph_get_quota(handle->conn->connectpath, qtype, id, qt);
216
217         if (ret) {
218                 errno = -ret;
219                 ret = -1;
220         }
221
222         return ret;
223 #else
224         errno = ENOSYS;
225         return -1;
226 #endif
227 }
228
229 static int cephwrap_set_quota(struct vfs_handle_struct *handle,  enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
230 {
231         /* libceph: Ceph does not implement this */
232 #if 0
233 /* was ifdef HAVE_SYS_QUOTAS */
234         int ret;
235
236         ret = ceph_set_quota(handle->conn->connectpath, qtype, id, qt);
237         if (ret) {
238                 errno = -ret;
239                 ret = -1;
240         }
241
242         return ret;
243 #else
244         WRAP_RETURN(-ENOSYS);
245 #endif
246 }
247
248 static int cephwrap_statvfs(struct vfs_handle_struct *handle,
249                                 const struct smb_filename *smb_fname,
250                                 vfs_statvfs_struct *statbuf)
251 {
252         struct statvfs statvfs_buf;
253         int ret;
254
255         ret = ceph_statfs(handle->data, smb_fname->base_name, &statvfs_buf);
256         if (ret < 0) {
257                 WRAP_RETURN(ret);
258         } else {
259                 statbuf->OptimalTransferSize = statvfs_buf.f_frsize;
260                 statbuf->BlockSize = statvfs_buf.f_bsize;
261                 statbuf->TotalBlocks = statvfs_buf.f_blocks;
262                 statbuf->BlocksAvail = statvfs_buf.f_bfree;
263                 statbuf->UserBlocksAvail = statvfs_buf.f_bavail;
264                 statbuf->TotalFileNodes = statvfs_buf.f_files;
265                 statbuf->FreeFileNodes = statvfs_buf.f_ffree;
266                 statbuf->FsIdentifier = statvfs_buf.f_fsid;
267                 DBG_DEBUG("[CEPH] f_bsize: %ld, f_blocks: %ld, f_bfree: %ld, f_bavail: %ld\n",
268                         (long int)statvfs_buf.f_bsize, (long int)statvfs_buf.f_blocks,
269                         (long int)statvfs_buf.f_bfree, (long int)statvfs_buf.f_bavail);
270         }
271         return ret;
272 }
273
274 static uint32_t cephwrap_fs_capabilities(struct vfs_handle_struct *handle,
275                                          enum timestamp_set_resolution *p_ts_res)
276 {
277         uint32_t caps = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES;
278
279 #ifdef HAVE_CEPH_STATX
280         *p_ts_res = TIMESTAMP_SET_NT_OR_BETTER;
281 #else
282         *p_ts_res = TIMESTAMP_SET_MSEC;
283 #endif
284
285         return caps;
286 }
287
288 /* Directory operations */
289
290 static DIR *cephwrap_opendir(struct vfs_handle_struct *handle,
291                              const struct smb_filename *smb_fname,
292                              const char *mask, uint32_t attr)
293 {
294         int ret = 0;
295         struct ceph_dir_result *result;
296         DBG_DEBUG("[CEPH] opendir(%p, %s)\n", handle, smb_fname->base_name);
297
298         /* Returns NULL if it does not exist or there are problems ? */
299         ret = ceph_opendir(handle->data, smb_fname->base_name, &result);
300         if (ret < 0) {
301                 result = NULL;
302                 errno = -ret; /* We return result which is NULL in this case */
303         }
304
305         DBG_DEBUG("[CEPH] opendir(...) = %d\n", ret);
306         return (DIR *) result;
307 }
308
309 static DIR *cephwrap_fdopendir(struct vfs_handle_struct *handle,
310                                struct files_struct *fsp,
311                                const char *mask,
312                                uint32_t attributes)
313 {
314         int ret = 0;
315         struct ceph_dir_result *result;
316         DBG_DEBUG("[CEPH] fdopendir(%p, %p)\n", handle, fsp);
317
318         ret = ceph_opendir(handle->data, fsp->fsp_name->base_name, &result);
319         if (ret < 0) {
320                 result = NULL;
321                 errno = -ret; /* We return result which is NULL in this case */
322         }
323
324         DBG_DEBUG("[CEPH] fdopendir(...) = %d\n", ret);
325         return (DIR *) result;
326 }
327
328 static struct dirent *cephwrap_readdir(struct vfs_handle_struct *handle,
329                                        DIR *dirp,
330                                        SMB_STRUCT_STAT *sbuf)
331 {
332         struct dirent *result;
333
334         DBG_DEBUG("[CEPH] readdir(%p, %p)\n", handle, dirp);
335         result = ceph_readdir(handle->data, (struct ceph_dir_result *) dirp);
336         DBG_DEBUG("[CEPH] readdir(...) = %p\n", result);
337
338         /* Default Posix readdir() does not give us stat info.
339          * Set to invalid to indicate we didn't return this info. */
340         if (sbuf)
341                 SET_STAT_INVALID(*sbuf);
342         return result;
343 }
344
345 static void cephwrap_seekdir(struct vfs_handle_struct *handle, DIR *dirp, long offset)
346 {
347         DBG_DEBUG("[CEPH] seekdir(%p, %p, %ld)\n", handle, dirp, offset);
348         ceph_seekdir(handle->data, (struct ceph_dir_result *) dirp, offset);
349 }
350
351 static long cephwrap_telldir(struct vfs_handle_struct *handle, DIR *dirp)
352 {
353         long ret;
354         DBG_DEBUG("[CEPH] telldir(%p, %p)\n", handle, dirp);
355         ret = ceph_telldir(handle->data, (struct ceph_dir_result *) dirp);
356         DBG_DEBUG("[CEPH] telldir(...) = %ld\n", ret);
357         WRAP_RETURN(ret);
358 }
359
360 static void cephwrap_rewinddir(struct vfs_handle_struct *handle, DIR *dirp)
361 {
362         DBG_DEBUG("[CEPH] rewinddir(%p, %p)\n", handle, dirp);
363         ceph_rewinddir(handle->data, (struct ceph_dir_result *) dirp);
364 }
365
366 static int cephwrap_mkdir(struct vfs_handle_struct *handle,
367                           const struct smb_filename *smb_fname,
368                           mode_t mode)
369 {
370         int result;
371         bool has_dacl = False;
372         char *parent = NULL;
373         const char *path = smb_fname->base_name;
374
375         DBG_DEBUG("[CEPH] mkdir(%p, %s)\n", handle, path);
376
377         if (lp_inherit_acls(SNUM(handle->conn))
378             && parent_dirname(talloc_tos(), path, &parent, NULL)
379             && (has_dacl = directory_has_default_acl(handle->conn, parent)))
380                 mode = 0777;
381
382         TALLOC_FREE(parent);
383
384         result = ceph_mkdir(handle->data, path, mode);
385
386         /*
387          * Note. This order is important
388          */
389         if (result) {
390                 WRAP_RETURN(result);
391         } else if (result == 0 && !has_dacl) {
392                 /*
393                  * We need to do this as the default behavior of POSIX ACLs
394                  * is to set the mask to be the requested group permission
395                  * bits, not the group permission bits to be the requested
396                  * group permission bits. This is not what we want, as it will
397                  * mess up any inherited ACL bits that were set. JRA.
398                  */
399                 int saved_errno = errno; /* We may get ENOSYS */
400                 if ((SMB_VFS_CHMOD_ACL(handle->conn, smb_fname, mode) == -1) &&
401                                 (errno == ENOSYS)) {
402                         errno = saved_errno;
403                 }
404         }
405
406         return result;
407 }
408
409 static int cephwrap_rmdir(struct vfs_handle_struct *handle,
410                         const struct smb_filename *smb_fname)
411 {
412         int result;
413
414         DBG_DEBUG("[CEPH] rmdir(%p, %s)\n", handle, smb_fname->base_name);
415         result = ceph_rmdir(handle->data, smb_fname->base_name);
416         DBG_DEBUG("[CEPH] rmdir(...) = %d\n", result);
417         WRAP_RETURN(result);
418 }
419
420 static int cephwrap_closedir(struct vfs_handle_struct *handle, DIR *dirp)
421 {
422         int result;
423
424         DBG_DEBUG("[CEPH] closedir(%p, %p)\n", handle, dirp);
425         result = ceph_closedir(handle->data, (struct ceph_dir_result *) dirp);
426         DBG_DEBUG("[CEPH] closedir(...) = %d\n", result);
427         WRAP_RETURN(result);
428 }
429
430 /* File operations */
431
432 static int cephwrap_open(struct vfs_handle_struct *handle,
433                         struct smb_filename *smb_fname,
434                         files_struct *fsp, int flags, mode_t mode)
435 {
436         int result = -ENOENT;
437         DBG_DEBUG("[CEPH] open(%p, %s, %p, %d, %d)\n", handle,
438                   smb_fname_str_dbg(smb_fname), fsp, flags, mode);
439
440         if (smb_fname->stream_name) {
441                 goto out;
442         }
443
444         result = ceph_open(handle->data, smb_fname->base_name, flags, mode);
445 out:
446         DBG_DEBUG("[CEPH] open(...) = %d\n", result);
447         WRAP_RETURN(result);
448 }
449
450 static int cephwrap_close(struct vfs_handle_struct *handle, files_struct *fsp)
451 {
452         int result;
453
454         DBG_DEBUG("[CEPH] close(%p, %p)\n", handle, fsp);
455         result = ceph_close(handle->data, fsp->fh->fd);
456         DBG_DEBUG("[CEPH] close(...) = %d\n", result);
457
458         WRAP_RETURN(result);
459 }
460
461 static ssize_t cephwrap_read(struct vfs_handle_struct *handle, files_struct *fsp, void *data, size_t n)
462 {
463         ssize_t result;
464
465         DBG_DEBUG("[CEPH] read(%p, %p, %p, %llu)\n", handle, fsp, data, llu(n));
466
467         /* Using -1 for the offset means read/write rather than pread/pwrite */
468         result = ceph_read(handle->data, fsp->fh->fd, data, n, -1);
469         DBG_DEBUG("[CEPH] read(...) = %llu\n", llu(result));
470         WRAP_RETURN(result);
471 }
472
473 static ssize_t cephwrap_pread(struct vfs_handle_struct *handle, files_struct *fsp, void *data,
474                         size_t n, off_t offset)
475 {
476         ssize_t result;
477
478         DBG_DEBUG("[CEPH] pread(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
479
480         result = ceph_read(handle->data, fsp->fh->fd, data, n, offset);
481         DBG_DEBUG("[CEPH] pread(...) = %llu\n", llu(result));
482         WRAP_RETURN(result);
483 }
484
485
486 static ssize_t cephwrap_write(struct vfs_handle_struct *handle, files_struct *fsp, const void *data, size_t n)
487 {
488         ssize_t result;
489
490         DBG_DEBUG("[CEPH] write(%p, %p, %p, %llu)\n", handle, fsp, data, llu(n));
491
492         result = ceph_write(handle->data, fsp->fh->fd, data, n, -1);
493
494         DBG_DEBUG("[CEPH] write(...) = %llu\n", llu(result));
495         if (result < 0) {
496                 WRAP_RETURN(result);
497         }
498         fsp->fh->pos += result;
499         return result;
500 }
501
502 static ssize_t cephwrap_pwrite(struct vfs_handle_struct *handle, files_struct *fsp, const void *data,
503                         size_t n, off_t offset)
504 {
505         ssize_t result;
506
507         DBG_DEBUG("[CEPH] pwrite(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
508         result = ceph_write(handle->data, fsp->fh->fd, data, n, offset);
509         DBG_DEBUG("[CEPH] pwrite(...) = %llu\n", llu(result));
510         WRAP_RETURN(result);
511 }
512
513 static off_t cephwrap_lseek(struct vfs_handle_struct *handle, files_struct *fsp, off_t offset, int whence)
514 {
515         off_t result = 0;
516
517         DBG_DEBUG("[CEPH] cephwrap_lseek\n");
518         /* Cope with 'stat' file opens. */
519         if (fsp->fh->fd != -1) {
520                 result = ceph_lseek(handle->data, fsp->fh->fd, offset, whence);
521         }
522         WRAP_RETURN(result);
523 }
524
525 static ssize_t cephwrap_sendfile(struct vfs_handle_struct *handle, int tofd, files_struct *fromfsp, const DATA_BLOB *hdr,
526                         off_t offset, size_t n)
527 {
528         /*
529          * We cannot support sendfile because libceph is in user space.
530          */
531         DBG_DEBUG("[CEPH] cephwrap_sendfile\n");
532         errno = ENOTSUP;
533         return -1;
534 }
535
536 static ssize_t cephwrap_recvfile(struct vfs_handle_struct *handle,
537                         int fromfd,
538                         files_struct *tofsp,
539                         off_t offset,
540                         size_t n)
541 {
542         /*
543          * We cannot support recvfile because libceph is in user space.
544          */
545         DBG_DEBUG("[CEPH] cephwrap_recvfile\n");
546         errno=ENOTSUP;
547         return -1;
548 }
549
550 static int cephwrap_rename(struct vfs_handle_struct *handle,
551                           const struct smb_filename *smb_fname_src,
552                           const struct smb_filename *smb_fname_dst)
553 {
554         int result = -1;
555         DBG_DEBUG("[CEPH] cephwrap_rename\n");
556         if (smb_fname_src->stream_name || smb_fname_dst->stream_name) {
557                 errno = ENOENT;
558                 return result;
559         }
560
561         result = ceph_rename(handle->data, smb_fname_src->base_name, smb_fname_dst->base_name);
562         WRAP_RETURN(result);
563 }
564
565 static int cephwrap_fsync(struct vfs_handle_struct *handle, files_struct *fsp)
566 {
567         int result;
568         DBG_DEBUG("[CEPH] cephwrap_fsync\n");
569         result = ceph_fsync(handle->data, fsp->fh->fd, false);
570         WRAP_RETURN(result);
571 }
572
573 /*
574  * Fake up an async ceph fsync by calling the sychronous API.
575  */
576
577 static struct tevent_req *cephwrap_fsync_send(struct vfs_handle_struct *handle,
578                                         TALLOC_CTX *mem_ctx,
579                                         struct tevent_context *ev,
580                                         files_struct *fsp)
581 {
582         struct tevent_req *req = NULL;
583         struct vfs_aio_state *state = NULL;
584         int ret = -1;
585
586         DBG_DEBUG("[CEPH] cephwrap_fsync_send\n");
587
588         req = tevent_req_create(mem_ctx, &state, struct vfs_aio_state);
589         if (req == NULL) {
590                 return NULL;
591         }
592
593         /* Make sync call. */
594         ret = ceph_fsync(handle->data, fsp->fh->fd, false);
595
596         if (ret != 0) {
597                 /* ceph_fsync returns -errno on error. */
598                 tevent_req_error(req, -ret);
599                 return tevent_req_post(req, ev);
600         }
601
602         /* Mark it as done. */
603         tevent_req_done(req);
604         /* Return and schedule the completion of the call. */
605         return tevent_req_post(req, ev);
606 }
607
608 static int cephwrap_fsync_recv(struct tevent_req *req,
609                                 struct vfs_aio_state *vfs_aio_state)
610 {
611         struct vfs_aio_state *state =
612                 tevent_req_data(req, struct vfs_aio_state);
613
614         DBG_DEBUG("[CEPH] cephwrap_fsync_recv\n");
615
616         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
617                 return -1;
618         }
619         *vfs_aio_state = *state;
620         return 0;
621 }
622
623 #ifdef HAVE_CEPH_STATX
624 #define SAMBA_STATX_ATTR_MASK   (CEPH_STATX_BASIC_STATS|CEPH_STATX_BTIME)
625
626 static void init_stat_ex_from_ceph_statx(struct stat_ex *dst, const struct ceph_statx *stx)
627 {
628         if ((stx->stx_mask & SAMBA_STATX_ATTR_MASK) != SAMBA_STATX_ATTR_MASK)
629                 DBG_WARNING("%s: stx->stx_mask is incorrect (wanted %x, got %x)",
630                                 __func__, SAMBA_STATX_ATTR_MASK, stx->stx_mask);
631
632         dst->st_ex_dev = stx->stx_dev;
633         dst->st_ex_rdev = stx->stx_rdev;
634         dst->st_ex_ino = stx->stx_ino;
635         dst->st_ex_mode = stx->stx_mode;
636         dst->st_ex_uid = stx->stx_uid;
637         dst->st_ex_gid = stx->stx_gid;
638         dst->st_ex_size = stx->stx_size;
639         dst->st_ex_nlink = stx->stx_nlink;
640         dst->st_ex_atime = stx->stx_atime;
641         dst->st_ex_btime = stx->stx_btime;
642         dst->st_ex_ctime = stx->stx_ctime;
643         dst->st_ex_mtime = stx->stx_mtime;
644         dst->st_ex_calculated_birthtime = false;
645         dst->st_ex_blksize = stx->stx_blksize;
646         dst->st_ex_blocks = stx->stx_blocks;
647 }
648
649 static int cephwrap_stat(struct vfs_handle_struct *handle,
650                         struct smb_filename *smb_fname)
651 {
652         int result = -1;
653         struct ceph_statx stx;
654
655         DBG_DEBUG("[CEPH] stat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
656
657         if (smb_fname->stream_name) {
658                 errno = ENOENT;
659                 return result;
660         }
661
662         result = ceph_statx(handle->data, smb_fname->base_name, &stx,
663                                 SAMBA_STATX_ATTR_MASK, 0);
664         DBG_DEBUG("[CEPH] statx(...) = %d\n", result);
665         if (result < 0) {
666                 WRAP_RETURN(result);
667         } else {
668                 DBG_DEBUG("[CEPH]\tstx = {dev = %llx, ino = %llu, mode = 0x%x, nlink = %llu, "
669                            "uid = %d, gid = %d, rdev = %llx, size = %llu, blksize = %llu, "
670                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu, btime = %llu}\n",
671                            llu(stx.stx_dev), llu(stx.stx_ino), stx.stx_mode,
672                            llu(stx.stx_nlink), stx.stx_uid, stx.stx_gid, llu(stx.stx_rdev),
673                            llu(stx.stx_size), llu(stx.stx_blksize),
674                            llu(stx.stx_blocks), llu(stx.stx_atime.tv_sec), llu(stx.stx_mtime.tv_sec),
675                            llu(stx.stx_ctime.tv_sec), llu(stx.stx_btime.tv_sec));
676         }
677         init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
678         DBG_DEBUG("[CEPH] mode = 0x%x\n", smb_fname->st.st_ex_mode);
679         return result;
680 }
681
682 static int cephwrap_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
683 {
684         int result = -1;
685         struct ceph_statx stx;
686
687         DBG_DEBUG("[CEPH] fstat(%p, %d)\n", handle, fsp->fh->fd);
688         result = ceph_fstatx(handle->data, fsp->fh->fd, &stx,
689                                 SAMBA_STATX_ATTR_MASK, 0);
690         DBG_DEBUG("[CEPH] fstat(...) = %d\n", result);
691         if (result < 0) {
692                 WRAP_RETURN(result);
693         } else {
694                 DBG_DEBUG("[CEPH]\tstx = {dev = %llx, ino = %llu, mode = 0x%x, nlink = %llu, "
695                            "uid = %d, gid = %d, rdev = %llx, size = %llu, blksize = %llu, "
696                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu, btime = %llu}\n",
697                            llu(stx.stx_dev), llu(stx.stx_ino), stx.stx_mode,
698                            llu(stx.stx_nlink), stx.stx_uid, stx.stx_gid, llu(stx.stx_rdev),
699                            llu(stx.stx_size), llu(stx.stx_blksize),
700                            llu(stx.stx_blocks), llu(stx.stx_atime.tv_sec), llu(stx.stx_mtime.tv_sec),
701                            llu(stx.stx_ctime.tv_sec), llu(stx.stx_btime.tv_sec));
702         }
703         init_stat_ex_from_ceph_statx(sbuf, &stx);
704         DBG_DEBUG("[CEPH] mode = 0x%x\n", sbuf->st_ex_mode);
705         return result;
706 }
707
708 static int cephwrap_lstat(struct vfs_handle_struct *handle,
709                          struct smb_filename *smb_fname)
710 {
711         int result = -1;
712         struct ceph_statx stx;
713
714         DBG_DEBUG("[CEPH] lstat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
715
716         if (smb_fname->stream_name) {
717                 errno = ENOENT;
718                 return result;
719         }
720
721         result = ceph_statx(handle->data, smb_fname->base_name, &stx,
722                                 SAMBA_STATX_ATTR_MASK, AT_SYMLINK_NOFOLLOW);
723         DBG_DEBUG("[CEPH] lstat(...) = %d\n", result);
724         if (result < 0) {
725                 WRAP_RETURN(result);
726         }
727         init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
728         return result;
729 }
730
731 static int cephwrap_ntimes(struct vfs_handle_struct *handle,
732                          const struct smb_filename *smb_fname,
733                          struct smb_file_time *ft)
734 {
735         struct ceph_statx stx = { 0 };
736         int result;
737         int mask = 0;
738
739         if (!null_timespec(ft->atime)) {
740                 stx.stx_atime = ft->atime;
741                 mask |= CEPH_SETATTR_ATIME;
742         }
743         if (!null_timespec(ft->mtime)) {
744                 stx.stx_mtime = ft->mtime;
745                 mask |= CEPH_SETATTR_MTIME;
746         }
747         if (!null_timespec(ft->create_time)) {
748                 stx.stx_btime = ft->create_time;
749                 mask |= CEPH_SETATTR_BTIME;
750         }
751
752         if (!mask) {
753                 return 0;
754         }
755
756         result = ceph_setattrx(handle->data, smb_fname->base_name, &stx, mask, 0);
757         DBG_DEBUG("[CEPH] ntimes(%p, %s, {%ld, %ld, %ld, %ld}) = %d\n", handle, smb_fname_str_dbg(smb_fname),
758                                 ft->mtime.tv_sec, ft->atime.tv_sec, ft->ctime.tv_sec,
759                                 ft->create_time.tv_sec, result);
760         return result;
761 }
762
763 #else /* HAVE_CEPH_STATX */
764
765 static int cephwrap_stat(struct vfs_handle_struct *handle,
766                         struct smb_filename *smb_fname)
767 {
768         int result = -1;
769         struct stat stbuf;
770
771         DBG_DEBUG("[CEPH] stat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
772
773         if (smb_fname->stream_name) {
774                 errno = ENOENT;
775                 return result;
776         }
777
778         result = ceph_stat(handle->data, smb_fname->base_name, (struct stat *) &stbuf);
779         DBG_DEBUG("[CEPH] stat(...) = %d\n", result);
780         if (result < 0) {
781                 WRAP_RETURN(result);
782         } else {
783                 DBG_DEBUG("[CEPH]\tstbuf = {dev = %llu, ino = %llu, mode = 0x%x, nlink = %llu, "
784                            "uid = %d, gid = %d, rdev = %llu, size = %llu, blksize = %llu, "
785                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu}\n",
786                            llu(stbuf.st_dev), llu(stbuf.st_ino), stbuf.st_mode, llu(stbuf.st_nlink),
787                            stbuf.st_uid, stbuf.st_gid, llu(stbuf.st_rdev), llu(stbuf.st_size), llu(stbuf.st_blksize),
788                            llu(stbuf.st_blocks), llu(stbuf.st_atime), llu(stbuf.st_mtime), llu(stbuf.st_ctime));
789         }
790         init_stat_ex_from_stat(
791                         &smb_fname->st, &stbuf,
792                         lp_fake_directory_create_times(SNUM(handle->conn)));
793         DBG_DEBUG("[CEPH] mode = 0x%x\n", smb_fname->st.st_ex_mode);
794         return result;
795 }
796
797 static int cephwrap_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
798 {
799         int result = -1;
800         struct stat stbuf;
801
802         DBG_DEBUG("[CEPH] fstat(%p, %d)\n", handle, fsp->fh->fd);
803         result = ceph_fstat(handle->data, fsp->fh->fd, (struct stat *) &stbuf);
804         DBG_DEBUG("[CEPH] fstat(...) = %d\n", result);
805         if (result < 0) {
806                 WRAP_RETURN(result);
807         } else {
808                 DBG_DEBUG("[CEPH]\tstbuf = {dev = %llu, ino = %llu, mode = 0x%x, nlink = %llu, "
809                            "uid = %d, gid = %d, rdev = %llu, size = %llu, blksize = %llu, "
810                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu}\n",
811                            llu(stbuf.st_dev), llu(stbuf.st_ino), stbuf.st_mode, llu(stbuf.st_nlink),
812                            stbuf.st_uid, stbuf.st_gid, llu(stbuf.st_rdev), llu(stbuf.st_size), llu(stbuf.st_blksize),
813                            llu(stbuf.st_blocks), llu(stbuf.st_atime), llu(stbuf.st_mtime), llu(stbuf.st_ctime));
814         }
815
816         init_stat_ex_from_stat(
817                         sbuf, &stbuf,
818                         lp_fake_directory_create_times(SNUM(handle->conn)));
819         DBG_DEBUG("[CEPH] mode = 0x%x\n", sbuf->st_ex_mode);
820         return result;
821 }
822
823 static int cephwrap_lstat(struct vfs_handle_struct *handle,
824                          struct smb_filename *smb_fname)
825 {
826         int result = -1;
827         struct stat stbuf;
828
829         DBG_DEBUG("[CEPH] lstat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
830
831         if (smb_fname->stream_name) {
832                 errno = ENOENT;
833                 return result;
834         }
835
836         result = ceph_lstat(handle->data, smb_fname->base_name, &stbuf);
837         DBG_DEBUG("[CEPH] lstat(...) = %d\n", result);
838         if (result < 0) {
839                 WRAP_RETURN(result);
840         }
841         init_stat_ex_from_stat(
842                         &smb_fname->st, &stbuf,
843                         lp_fake_directory_create_times(SNUM(handle->conn)));
844         return result;
845 }
846
847 static int cephwrap_ntimes(struct vfs_handle_struct *handle,
848                          const struct smb_filename *smb_fname,
849                          struct smb_file_time *ft)
850 {
851         struct utimbuf buf;
852         int result;
853
854         if (null_timespec(ft->atime)) {
855                 buf.actime = smb_fname->st.st_ex_atime.tv_sec;
856         } else {
857                 buf.actime = ft->atime.tv_sec;
858         }
859         if (null_timespec(ft->mtime)) {
860                 buf.modtime = smb_fname->st.st_ex_mtime.tv_sec;
861         } else {
862                 buf.modtime = ft->mtime.tv_sec;
863         }
864         if (!null_timespec(ft->create_time)) {
865                 set_create_timespec_ea(handle->conn, smb_fname,
866                                        ft->create_time);
867         }
868         if (buf.actime == smb_fname->st.st_ex_atime.tv_sec &&
869             buf.modtime == smb_fname->st.st_ex_mtime.tv_sec) {
870                 return 0;
871         }
872
873         result = ceph_utime(handle->data, smb_fname->base_name, &buf);
874         DBG_DEBUG("[CEPH] ntimes(%p, %s, {%ld, %ld, %ld, %ld}) = %d\n", handle, smb_fname_str_dbg(smb_fname),
875                                 ft->mtime.tv_sec, ft->atime.tv_sec, ft->ctime.tv_sec,
876                                 ft->create_time.tv_sec, result);
877         return result;
878 }
879 #endif /* HAVE_CEPH_STATX */
880
881 static int cephwrap_unlink(struct vfs_handle_struct *handle,
882                           const struct smb_filename *smb_fname)
883 {
884         int result = -1;
885
886         DBG_DEBUG("[CEPH] unlink(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
887         if (smb_fname->stream_name) {
888                 errno = ENOENT;
889                 return result;
890         }
891         result = ceph_unlink(handle->data, smb_fname->base_name);
892         DBG_DEBUG("[CEPH] unlink(...) = %d\n", result);
893         WRAP_RETURN(result);
894 }
895
896 static int cephwrap_chmod(struct vfs_handle_struct *handle,
897                         const struct smb_filename *smb_fname,
898                         mode_t mode)
899 {
900         int result;
901
902         DBG_DEBUG("[CEPH] chmod(%p, %s, %d)\n", handle, smb_fname->base_name, mode);
903
904         /*
905          * We need to do this due to the fact that the default POSIX ACL
906          * chmod modifies the ACL *mask* for the group owner, not the
907          * group owner bits directly. JRA.
908          */
909
910
911         {
912                 int saved_errno = errno; /* We might get ENOSYS */
913                 result = SMB_VFS_CHMOD_ACL(handle->conn,
914                                         smb_fname,
915                                         mode);
916                 if (result == 0) {
917                         return result;
918                 }
919                 /* Error - return the old errno. */
920                 errno = saved_errno;
921         }
922
923         result = ceph_chmod(handle->data, smb_fname->base_name, mode);
924         DBG_DEBUG("[CEPH] chmod(...) = %d\n", result);
925         WRAP_RETURN(result);
926 }
927
928 static int cephwrap_fchmod(struct vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
929 {
930         int result;
931
932         DBG_DEBUG("[CEPH] fchmod(%p, %p, %d)\n", handle, fsp, mode);
933
934         /*
935          * We need to do this due to the fact that the default POSIX ACL
936          * chmod modifies the ACL *mask* for the group owner, not the
937          * group owner bits directly. JRA.
938          */
939
940         {
941                 int saved_errno = errno; /* We might get ENOSYS */
942                 if ((result = SMB_VFS_FCHMOD_ACL(fsp, mode)) == 0) {
943                         return result;
944                 }
945                 /* Error - return the old errno. */
946                 errno = saved_errno;
947         }
948
949 #if defined(HAVE_FCHMOD)
950         result = ceph_fchmod(handle->data, fsp->fh->fd, mode);
951         DBG_DEBUG("[CEPH] fchmod(...) = %d\n", result);
952         WRAP_RETURN(result);
953 #else
954         errno = ENOSYS;
955 #endif
956         return -1;
957 }
958
959 static int cephwrap_chown(struct vfs_handle_struct *handle,
960                         const struct smb_filename *smb_fname,
961                         uid_t uid,
962                         gid_t gid)
963 {
964         int result;
965         DBG_DEBUG("[CEPH] chown(%p, %s, %d, %d)\n", handle, smb_fname->base_name, uid, gid);
966         result = ceph_chown(handle->data, smb_fname->base_name, uid, gid);
967         DBG_DEBUG("[CEPH] chown(...) = %d\n", result);
968         WRAP_RETURN(result);
969 }
970
971 static int cephwrap_fchown(struct vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
972 {
973         int result;
974 #ifdef HAVE_FCHOWN
975
976         DBG_DEBUG("[CEPH] fchown(%p, %p, %d, %d)\n", handle, fsp, uid, gid);
977         result = ceph_fchown(handle->data, fsp->fh->fd, uid, gid);
978         DBG_DEBUG("[CEPH] fchown(...) = %d\n", result);
979         WRAP_RETURN(result);
980 #else
981         errno = ENOSYS;
982         result = -1;
983 #endif
984         return result;
985 }
986
987 static int cephwrap_lchown(struct vfs_handle_struct *handle,
988                         const struct smb_filename *smb_fname,
989                         uid_t uid,
990                         gid_t gid)
991 {
992         int result;
993         DBG_DEBUG("[CEPH] lchown(%p, %s, %d, %d)\n", handle, smb_fname->base_name, uid, gid);
994         result = ceph_lchown(handle->data, smb_fname->base_name, uid, gid);
995         DBG_DEBUG("[CEPH] lchown(...) = %d\n", result);
996         WRAP_RETURN(result);
997 }
998
999 static int cephwrap_chdir(struct vfs_handle_struct *handle,
1000                         const struct smb_filename *smb_fname)
1001 {
1002         int result = -1;
1003         DBG_DEBUG("[CEPH] chdir(%p, %s)\n", handle, smb_fname->base_name);
1004         result = ceph_chdir(handle->data, smb_fname->base_name);
1005         DBG_DEBUG("[CEPH] chdir(...) = %d\n", result);
1006         WRAP_RETURN(result);
1007 }
1008
1009 static struct smb_filename *cephwrap_getwd(struct vfs_handle_struct *handle,
1010                         TALLOC_CTX *ctx)
1011 {
1012         const char *cwd = ceph_getcwd(handle->data);
1013         DBG_DEBUG("[CEPH] getwd(%p) = %s\n", handle, cwd);
1014         return synthetic_smb_fname(ctx,
1015                                 cwd,
1016                                 NULL,
1017                                 NULL,
1018                                 0);
1019 }
1020
1021 static int strict_allocate_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
1022 {
1023         off_t space_to_write;
1024         uint64_t space_avail;
1025         uint64_t bsize,dfree,dsize;
1026         int ret;
1027         NTSTATUS status;
1028         SMB_STRUCT_STAT *pst;
1029
1030         status = vfs_stat_fsp(fsp);
1031         if (!NT_STATUS_IS_OK(status)) {
1032                 return -1;
1033         }
1034         pst = &fsp->fsp_name->st;
1035
1036 #ifdef S_ISFIFO
1037         if (S_ISFIFO(pst->st_ex_mode))
1038                 return 0;
1039 #endif
1040
1041         if (pst->st_ex_size == len)
1042                 return 0;
1043
1044         /* Shrink - just ftruncate. */
1045         if (pst->st_ex_size > len)
1046                 return ftruncate(fsp->fh->fd, len);
1047
1048         space_to_write = len - pst->st_ex_size;
1049
1050         /* for allocation try fallocate first. This can fail on some
1051            platforms e.g. when the filesystem doesn't support it and no
1052            emulation is being done by the libc (like on AIX with JFS1). In that
1053            case we do our own emulation. fallocate implementations can
1054            return ENOTSUP or EINVAL in cases like that. */
1055         ret = SMB_VFS_FALLOCATE(fsp, 0, pst->st_ex_size, space_to_write);
1056         if (ret == -1 && errno == ENOSPC) {
1057                 return -1;
1058         }
1059         if (ret == 0) {
1060                 return 0;
1061         }
1062         DEBUG(10,("[CEPH] strict_allocate_ftruncate: SMB_VFS_FALLOCATE failed with "
1063                 "error %d. Falling back to slow manual allocation\n", errno));
1064
1065         /* available disk space is enough or not? */
1066         space_avail =
1067             get_dfree_info(fsp->conn, fsp->fsp_name, &bsize, &dfree, &dsize);
1068         /* space_avail is 1k blocks */
1069         if (space_avail == (uint64_t)-1 ||
1070                         ((uint64_t)space_to_write/1024 > space_avail) ) {
1071                 errno = ENOSPC;
1072                 return -1;
1073         }
1074
1075         /* Write out the real space on disk. */
1076         return vfs_slow_fallocate(fsp, pst->st_ex_size, space_to_write);
1077 }
1078
1079 static int cephwrap_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
1080 {
1081         int result = -1;
1082         SMB_STRUCT_STAT st;
1083         char c = 0;
1084         off_t currpos;
1085
1086         DBG_DEBUG("[CEPH] ftruncate(%p, %p, %llu\n", handle, fsp, llu(len));
1087
1088         if (lp_strict_allocate(SNUM(fsp->conn))) {
1089                 result = strict_allocate_ftruncate(handle, fsp, len);
1090                 return result;
1091         }
1092
1093         /* we used to just check HAVE_FTRUNCATE_EXTEND and only use
1094            sys_ftruncate if the system supports it. Then I discovered that
1095            you can have some filesystems that support ftruncate
1096            expansion and some that don't! On Linux fat can't do
1097            ftruncate extend but ext2 can. */
1098
1099         result = ceph_ftruncate(handle->data, fsp->fh->fd, len);
1100         if (result == 0)
1101                 goto done;
1102
1103         /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
1104            extend a file with ftruncate. Provide alternate implementation
1105            for this */
1106         currpos = SMB_VFS_LSEEK(fsp, 0, SEEK_CUR);
1107         if (currpos == -1) {
1108                 goto done;
1109         }
1110
1111         /* Do an fstat to see if the file is longer than the requested
1112            size in which case the ftruncate above should have
1113            succeeded or shorter, in which case seek to len - 1 and
1114            write 1 byte of zero */
1115         if (SMB_VFS_FSTAT(fsp, &st) == -1) {
1116                 goto done;
1117         }
1118
1119 #ifdef S_ISFIFO
1120         if (S_ISFIFO(st.st_ex_mode)) {
1121                 result = 0;
1122                 goto done;
1123         }
1124 #endif
1125
1126         if (st.st_ex_size == len) {
1127                 result = 0;
1128                 goto done;
1129         }
1130
1131         if (st.st_ex_size > len) {
1132                 /* the sys_ftruncate should have worked */
1133                 goto done;
1134         }
1135
1136         if (SMB_VFS_LSEEK(fsp, len-1, SEEK_SET) != len -1)
1137                 goto done;
1138
1139         if (SMB_VFS_WRITE(fsp, &c, 1)!=1)
1140                 goto done;
1141
1142         /* Seek to where we were */
1143         if (SMB_VFS_LSEEK(fsp, currpos, SEEK_SET) != currpos)
1144                 goto done;
1145         result = 0;
1146
1147   done:
1148
1149         return result;
1150 }
1151
1152 static bool cephwrap_lock(struct vfs_handle_struct *handle, files_struct *fsp, int op, off_t offset, off_t count, int type)
1153 {
1154         DBG_DEBUG("[CEPH] lock\n");
1155         return true;
1156 }
1157
1158 static int cephwrap_kernel_flock(struct vfs_handle_struct *handle, files_struct *fsp,
1159                                 uint32_t share_mode, uint32_t access_mask)
1160 {
1161         DBG_DEBUG("[CEPH] kernel_flock\n");
1162         /*
1163          * We must return zero here and pretend all is good.
1164          * One day we might have this in CEPH.
1165          */
1166         return 0;
1167 }
1168
1169 static bool cephwrap_getlock(struct vfs_handle_struct *handle, files_struct *fsp, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid)
1170 {
1171         DBG_DEBUG("[CEPH] getlock returning false and errno=0\n");
1172
1173         errno = 0;
1174         return false;
1175 }
1176
1177 /*
1178  * We cannot let this fall through to the default, because the file might only
1179  * be accessible from libceph (which is a user-space client) but the fd might
1180  * be for some file the kernel knows about.
1181  */
1182 static int cephwrap_linux_setlease(struct vfs_handle_struct *handle, files_struct *fsp,
1183                                 int leasetype)
1184 {
1185         int result = -1;
1186
1187         DBG_DEBUG("[CEPH] linux_setlease\n");
1188         errno = ENOSYS;
1189         return result;
1190 }
1191
1192 static int cephwrap_symlink(struct vfs_handle_struct *handle,
1193                 const char *link_target,
1194                 const struct smb_filename *new_smb_fname)
1195 {
1196         int result = -1;
1197         DBG_DEBUG("[CEPH] symlink(%p, %s, %s)\n", handle,
1198                         link_target,
1199                         new_smb_fname->base_name);
1200         result = ceph_symlink(handle->data,
1201                         link_target,
1202                         new_smb_fname->base_name);
1203         DBG_DEBUG("[CEPH] symlink(...) = %d\n", result);
1204         WRAP_RETURN(result);
1205 }
1206
1207 static int cephwrap_readlink(struct vfs_handle_struct *handle,
1208                 const struct smb_filename *smb_fname,
1209                 char *buf,
1210                 size_t bufsiz)
1211 {
1212         int result = -1;
1213         DBG_DEBUG("[CEPH] readlink(%p, %s, %p, %llu)\n", handle,
1214                         smb_fname->base_name, buf, llu(bufsiz));
1215         result = ceph_readlink(handle->data, smb_fname->base_name, buf, bufsiz);
1216         DBG_DEBUG("[CEPH] readlink(...) = %d\n", result);
1217         WRAP_RETURN(result);
1218 }
1219
1220 static int cephwrap_link(struct vfs_handle_struct *handle,
1221                 const struct smb_filename *old_smb_fname,
1222                 const struct smb_filename *new_smb_fname)
1223 {
1224         int result = -1;
1225         DBG_DEBUG("[CEPH] link(%p, %s, %s)\n", handle,
1226                         old_smb_fname->base_name,
1227                         new_smb_fname->base_name);
1228         result = ceph_link(handle->data,
1229                                 old_smb_fname->base_name,
1230                                 new_smb_fname->base_name);
1231         DBG_DEBUG("[CEPH] link(...) = %d\n", result);
1232         WRAP_RETURN(result);
1233 }
1234
1235 static int cephwrap_mknod(struct vfs_handle_struct *handle,
1236                 const struct smb_filename *smb_fname,
1237                 mode_t mode,
1238                 SMB_DEV_T dev)
1239 {
1240         int result = -1;
1241         DBG_DEBUG("[CEPH] mknod(%p, %s)\n", handle, smb_fname->base_name);
1242         result = ceph_mknod(handle->data, smb_fname->base_name, mode, dev);
1243         DBG_DEBUG("[CEPH] mknod(...) = %d\n", result);
1244         WRAP_RETURN(result);
1245 }
1246
1247 /*
1248  * This is a simple version of real-path ... a better version is needed to
1249  * ask libceph about symbolic links.
1250  */
1251 static struct smb_filename *cephwrap_realpath(struct vfs_handle_struct *handle,
1252                                 TALLOC_CTX *ctx,
1253                                 const struct smb_filename *smb_fname)
1254 {
1255         char *result;
1256         const char *path = smb_fname->base_name;
1257         size_t len = strlen(path);
1258         struct smb_filename *result_fname = NULL;
1259
1260         result = SMB_MALLOC_ARRAY(char, PATH_MAX+1);
1261         if (len && (path[0] == '/')) {
1262                 int r = asprintf(&result, "%s", path);
1263                 if (r < 0) return NULL;
1264         } else if ((len >= 2) && (path[0] == '.') && (path[1] == '/')) {
1265                 if (len == 2) {
1266                         int r = asprintf(&result, "%s",
1267                                         handle->conn->connectpath);
1268                         if (r < 0) return NULL;
1269                 } else {
1270                         int r = asprintf(&result, "%s/%s",
1271                                         handle->conn->connectpath, &path[2]);
1272                         if (r < 0) return NULL;
1273                 }
1274         } else {
1275                 int r = asprintf(&result, "%s/%s",
1276                                 handle->conn->connectpath, path);
1277                 if (r < 0) return NULL;
1278         }
1279         DBG_DEBUG("[CEPH] realpath(%p, %s) = %s\n", handle, path, result);
1280         result_fname = synthetic_smb_fname(ctx,
1281                                 result,
1282                                 NULL,
1283                                 NULL,
1284                                 0);
1285         SAFE_FREE(result);
1286         return result_fname;
1287 }
1288
1289 static int cephwrap_chflags(struct vfs_handle_struct *handle,
1290                         const struct smb_filename *smb_fname,
1291                         unsigned int flags)
1292 {
1293         errno = ENOSYS;
1294         return -1;
1295 }
1296
1297 static int cephwrap_get_real_filename(struct vfs_handle_struct *handle,
1298                                      const char *path,
1299                                      const char *name,
1300                                      TALLOC_CTX *mem_ctx,
1301                                      char **found_name)
1302 {
1303         /*
1304          * Don't fall back to get_real_filename so callers can differentiate
1305          * between a full directory scan and an actual case-insensitive stat.
1306          */
1307         errno = EOPNOTSUPP;
1308         return -1;
1309 }
1310
1311 static const char *cephwrap_connectpath(struct vfs_handle_struct *handle,
1312                                        const struct smb_filename *smb_fname)
1313 {
1314         return handle->conn->connectpath;
1315 }
1316
1317 /****************************************************************
1318  Extended attribute operations.
1319 *****************************************************************/
1320
1321 static ssize_t cephwrap_getxattr(struct vfs_handle_struct *handle,
1322                         const struct smb_filename *smb_fname,
1323                         const char *name,
1324                         void *value,
1325                         size_t size)
1326 {
1327         int ret;
1328         DBG_DEBUG("[CEPH] getxattr(%p, %s, %s, %p, %llu)\n", handle,
1329                         smb_fname->base_name, name, value, llu(size));
1330         ret = ceph_getxattr(handle->data,
1331                         smb_fname->base_name, name, value, size);
1332         DBG_DEBUG("[CEPH] getxattr(...) = %d\n", ret);
1333         if (ret < 0) {
1334                 WRAP_RETURN(ret);
1335         } else {
1336                 return (ssize_t)ret;
1337         }
1338 }
1339
1340 static ssize_t cephwrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size)
1341 {
1342         int ret;
1343         DBG_DEBUG("[CEPH] fgetxattr(%p, %p, %s, %p, %llu)\n", handle, fsp, name, value, llu(size));
1344 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1345         ret = ceph_fgetxattr(handle->data, fsp->fh->fd, name, value, size);
1346 #else
1347         ret = ceph_getxattr(handle->data, fsp->fsp_name->base_name, name, value, size);
1348 #endif
1349         DBG_DEBUG("[CEPH] fgetxattr(...) = %d\n", ret);
1350         if (ret < 0) {
1351                 WRAP_RETURN(ret);
1352         } else {
1353                 return (ssize_t)ret;
1354         }
1355 }
1356
1357 static ssize_t cephwrap_listxattr(struct vfs_handle_struct *handle,
1358                         const struct smb_filename *smb_fname,
1359                         char *list,
1360                         size_t size)
1361 {
1362         int ret;
1363         DBG_DEBUG("[CEPH] listxattr(%p, %s, %p, %llu)\n", handle,
1364                         smb_fname->base_name, list, llu(size));
1365         ret = ceph_listxattr(handle->data, smb_fname->base_name, list, size);
1366         DBG_DEBUG("[CEPH] listxattr(...) = %d\n", ret);
1367         if (ret < 0) {
1368                 WRAP_RETURN(ret);
1369         } else {
1370                 return (ssize_t)ret;
1371         }
1372 }
1373
1374 static ssize_t cephwrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, char *list, size_t size)
1375 {
1376         int ret;
1377         DBG_DEBUG("[CEPH] flistxattr(%p, %p, %s, %llu)\n", handle, fsp, list, llu(size));
1378 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1379         ret = ceph_flistxattr(handle->data, fsp->fh->fd, list, size);
1380 #else
1381         ret = ceph_listxattr(handle->data, fsp->fsp_name->base_name, list, size);
1382 #endif
1383         DBG_DEBUG("[CEPH] flistxattr(...) = %d\n", ret);
1384         if (ret < 0) {
1385                 WRAP_RETURN(ret);
1386         } else {
1387                 return (ssize_t)ret;
1388         }
1389 }
1390
1391 static int cephwrap_removexattr(struct vfs_handle_struct *handle,
1392                                 const struct smb_filename *smb_fname,
1393                                 const char *name)
1394 {
1395         int ret;
1396         DBG_DEBUG("[CEPH] removexattr(%p, %s, %s)\n", handle,
1397                         smb_fname->base_name, name);
1398         ret = ceph_removexattr(handle->data, smb_fname->base_name, name);
1399         DBG_DEBUG("[CEPH] removexattr(...) = %d\n", ret);
1400         WRAP_RETURN(ret);
1401 }
1402
1403 static int cephwrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name)
1404 {
1405         int ret;
1406         DBG_DEBUG("[CEPH] fremovexattr(%p, %p, %s)\n", handle, fsp, name);
1407 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1408         ret = ceph_fremovexattr(handle->data, fsp->fh->fd, name);
1409 #else
1410         ret = ceph_removexattr(handle->data, fsp->fsp_name->base_name, name);
1411 #endif
1412         DBG_DEBUG("[CEPH] fremovexattr(...) = %d\n", ret);
1413         WRAP_RETURN(ret);
1414 }
1415
1416 static int cephwrap_setxattr(struct vfs_handle_struct *handle,
1417                                 const struct smb_filename *smb_fname,
1418                                 const char *name,
1419                                 const void *value,
1420                                 size_t size,
1421                                 int flags)
1422 {
1423         int ret;
1424         DBG_DEBUG("[CEPH] setxattr(%p, %s, %s, %p, %llu, %d)\n", handle,
1425                         smb_fname->base_name, name, value, llu(size), flags);
1426         ret = ceph_setxattr(handle->data, smb_fname->base_name,
1427                         name, value, size, flags);
1428         DBG_DEBUG("[CEPH] setxattr(...) = %d\n", ret);
1429         WRAP_RETURN(ret);
1430 }
1431
1432 static int cephwrap_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, const void *value, size_t size, int flags)
1433 {
1434         int ret;
1435         DBG_DEBUG("[CEPH] fsetxattr(%p, %p, %s, %p, %llu, %d)\n", handle, fsp, name, value, llu(size), flags);
1436 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1437         ret = ceph_fsetxattr(handle->data, fsp->fh->fd,
1438                              name, value, size, flags);
1439 #else
1440         ret = ceph_setxattr(handle->data, fsp->fsp_name->base_name, name, value, size, flags);
1441 #endif
1442         DBG_DEBUG("[CEPH] fsetxattr(...) = %d\n", ret);
1443         WRAP_RETURN(ret);
1444 }
1445
1446 static bool cephwrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
1447 {
1448
1449         /*
1450          * We do not support AIO yet.
1451          */
1452
1453         DBG_DEBUG("[CEPH] cephwrap_aio_force(%p, %p) = false (errno = ENOTSUP)\n", handle, fsp);
1454         errno = ENOTSUP;
1455         return false;
1456 }
1457
1458 static struct vfs_fn_pointers ceph_fns = {
1459         /* Disk operations */
1460
1461         .connect_fn = cephwrap_connect,
1462         .disconnect_fn = cephwrap_disconnect,
1463         .disk_free_fn = cephwrap_disk_free,
1464         .get_quota_fn = cephwrap_get_quota,
1465         .set_quota_fn = cephwrap_set_quota,
1466         .statvfs_fn = cephwrap_statvfs,
1467         .fs_capabilities_fn = cephwrap_fs_capabilities,
1468
1469         /* Directory operations */
1470
1471         .opendir_fn = cephwrap_opendir,
1472         .fdopendir_fn = cephwrap_fdopendir,
1473         .readdir_fn = cephwrap_readdir,
1474         .seekdir_fn = cephwrap_seekdir,
1475         .telldir_fn = cephwrap_telldir,
1476         .rewind_dir_fn = cephwrap_rewinddir,
1477         .mkdir_fn = cephwrap_mkdir,
1478         .rmdir_fn = cephwrap_rmdir,
1479         .closedir_fn = cephwrap_closedir,
1480
1481         /* File operations */
1482
1483         .open_fn = cephwrap_open,
1484         .close_fn = cephwrap_close,
1485         .read_fn = cephwrap_read,
1486         .pread_fn = cephwrap_pread,
1487         .write_fn = cephwrap_write,
1488         .pwrite_fn = cephwrap_pwrite,
1489         .lseek_fn = cephwrap_lseek,
1490         .sendfile_fn = cephwrap_sendfile,
1491         .recvfile_fn = cephwrap_recvfile,
1492         .rename_fn = cephwrap_rename,
1493         .fsync_fn = cephwrap_fsync,
1494         .fsync_send_fn = cephwrap_fsync_send,
1495         .fsync_recv_fn = cephwrap_fsync_recv,
1496         .stat_fn = cephwrap_stat,
1497         .fstat_fn = cephwrap_fstat,
1498         .lstat_fn = cephwrap_lstat,
1499         .unlink_fn = cephwrap_unlink,
1500         .chmod_fn = cephwrap_chmod,
1501         .fchmod_fn = cephwrap_fchmod,
1502         .chown_fn = cephwrap_chown,
1503         .fchown_fn = cephwrap_fchown,
1504         .lchown_fn = cephwrap_lchown,
1505         .chdir_fn = cephwrap_chdir,
1506         .getwd_fn = cephwrap_getwd,
1507         .ntimes_fn = cephwrap_ntimes,
1508         .ftruncate_fn = cephwrap_ftruncate,
1509         .lock_fn = cephwrap_lock,
1510         .kernel_flock_fn = cephwrap_kernel_flock,
1511         .linux_setlease_fn = cephwrap_linux_setlease,
1512         .getlock_fn = cephwrap_getlock,
1513         .symlink_fn = cephwrap_symlink,
1514         .readlink_fn = cephwrap_readlink,
1515         .link_fn = cephwrap_link,
1516         .mknod_fn = cephwrap_mknod,
1517         .realpath_fn = cephwrap_realpath,
1518         .chflags_fn = cephwrap_chflags,
1519         .get_real_filename_fn = cephwrap_get_real_filename,
1520         .connectpath_fn = cephwrap_connectpath,
1521
1522         /* EA operations. */
1523         .getxattr_fn = cephwrap_getxattr,
1524         .fgetxattr_fn = cephwrap_fgetxattr,
1525         .listxattr_fn = cephwrap_listxattr,
1526         .flistxattr_fn = cephwrap_flistxattr,
1527         .removexattr_fn = cephwrap_removexattr,
1528         .fremovexattr_fn = cephwrap_fremovexattr,
1529         .setxattr_fn = cephwrap_setxattr,
1530         .fsetxattr_fn = cephwrap_fsetxattr,
1531
1532         /* Posix ACL Operations */
1533         .sys_acl_get_file_fn = posixacl_xattr_acl_get_file,
1534         .sys_acl_get_fd_fn = posixacl_xattr_acl_get_fd,
1535         .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
1536         .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
1537         .sys_acl_set_file_fn = posixacl_xattr_acl_set_file,
1538         .sys_acl_set_fd_fn = posixacl_xattr_acl_set_fd,
1539         .sys_acl_delete_def_file_fn = posixacl_xattr_acl_delete_def_file,
1540
1541         /* aio operations */
1542         .aio_force_fn = cephwrap_aio_force,
1543 };
1544
1545 static_decl_vfs;
1546 NTSTATUS vfs_ceph_init(TALLOC_CTX *ctx)
1547 {
1548         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1549                                 "ceph", &ceph_fns);
1550 }