s3: VFS: Remove SMB_VFS_WRITE() function and all implementations.
[bbaumbach/samba-autobuild/.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_pread(struct vfs_handle_struct *handle, files_struct *fsp, void *data,
462                         size_t n, off_t offset)
463 {
464         ssize_t result;
465
466         DBG_DEBUG("[CEPH] pread(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
467
468         result = ceph_read(handle->data, fsp->fh->fd, data, n, offset);
469         DBG_DEBUG("[CEPH] pread(...) = %llu\n", llu(result));
470         WRAP_RETURN(result);
471 }
472
473
474 static ssize_t cephwrap_pwrite(struct vfs_handle_struct *handle, files_struct *fsp, const void *data,
475                         size_t n, off_t offset)
476 {
477         ssize_t result;
478
479         DBG_DEBUG("[CEPH] pwrite(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
480         result = ceph_write(handle->data, fsp->fh->fd, data, n, offset);
481         DBG_DEBUG("[CEPH] pwrite(...) = %llu\n", llu(result));
482         WRAP_RETURN(result);
483 }
484
485 static off_t cephwrap_lseek(struct vfs_handle_struct *handle, files_struct *fsp, off_t offset, int whence)
486 {
487         off_t result = 0;
488
489         DBG_DEBUG("[CEPH] cephwrap_lseek\n");
490         /* Cope with 'stat' file opens. */
491         if (fsp->fh->fd != -1) {
492                 result = ceph_lseek(handle->data, fsp->fh->fd, offset, whence);
493         }
494         WRAP_RETURN(result);
495 }
496
497 static ssize_t cephwrap_sendfile(struct vfs_handle_struct *handle, int tofd, files_struct *fromfsp, const DATA_BLOB *hdr,
498                         off_t offset, size_t n)
499 {
500         /*
501          * We cannot support sendfile because libceph is in user space.
502          */
503         DBG_DEBUG("[CEPH] cephwrap_sendfile\n");
504         errno = ENOTSUP;
505         return -1;
506 }
507
508 static ssize_t cephwrap_recvfile(struct vfs_handle_struct *handle,
509                         int fromfd,
510                         files_struct *tofsp,
511                         off_t offset,
512                         size_t n)
513 {
514         /*
515          * We cannot support recvfile because libceph is in user space.
516          */
517         DBG_DEBUG("[CEPH] cephwrap_recvfile\n");
518         errno=ENOTSUP;
519         return -1;
520 }
521
522 static int cephwrap_rename(struct vfs_handle_struct *handle,
523                           const struct smb_filename *smb_fname_src,
524                           const struct smb_filename *smb_fname_dst)
525 {
526         int result = -1;
527         DBG_DEBUG("[CEPH] cephwrap_rename\n");
528         if (smb_fname_src->stream_name || smb_fname_dst->stream_name) {
529                 errno = ENOENT;
530                 return result;
531         }
532
533         result = ceph_rename(handle->data, smb_fname_src->base_name, smb_fname_dst->base_name);
534         WRAP_RETURN(result);
535 }
536
537 /*
538  * Fake up an async ceph fsync by calling the sychronous API.
539  */
540
541 static struct tevent_req *cephwrap_fsync_send(struct vfs_handle_struct *handle,
542                                         TALLOC_CTX *mem_ctx,
543                                         struct tevent_context *ev,
544                                         files_struct *fsp)
545 {
546         struct tevent_req *req = NULL;
547         struct vfs_aio_state *state = NULL;
548         int ret = -1;
549
550         DBG_DEBUG("[CEPH] cephwrap_fsync_send\n");
551
552         req = tevent_req_create(mem_ctx, &state, struct vfs_aio_state);
553         if (req == NULL) {
554                 return NULL;
555         }
556
557         /* Make sync call. */
558         ret = ceph_fsync(handle->data, fsp->fh->fd, false);
559
560         if (ret != 0) {
561                 /* ceph_fsync returns -errno on error. */
562                 tevent_req_error(req, -ret);
563                 return tevent_req_post(req, ev);
564         }
565
566         /* Mark it as done. */
567         tevent_req_done(req);
568         /* Return and schedule the completion of the call. */
569         return tevent_req_post(req, ev);
570 }
571
572 static int cephwrap_fsync_recv(struct tevent_req *req,
573                                 struct vfs_aio_state *vfs_aio_state)
574 {
575         struct vfs_aio_state *state =
576                 tevent_req_data(req, struct vfs_aio_state);
577
578         DBG_DEBUG("[CEPH] cephwrap_fsync_recv\n");
579
580         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
581                 return -1;
582         }
583         *vfs_aio_state = *state;
584         return 0;
585 }
586
587 #ifdef HAVE_CEPH_STATX
588 #define SAMBA_STATX_ATTR_MASK   (CEPH_STATX_BASIC_STATS|CEPH_STATX_BTIME)
589
590 static void init_stat_ex_from_ceph_statx(struct stat_ex *dst, const struct ceph_statx *stx)
591 {
592         if ((stx->stx_mask & SAMBA_STATX_ATTR_MASK) != SAMBA_STATX_ATTR_MASK)
593                 DBG_WARNING("%s: stx->stx_mask is incorrect (wanted %x, got %x)",
594                                 __func__, SAMBA_STATX_ATTR_MASK, stx->stx_mask);
595
596         dst->st_ex_dev = stx->stx_dev;
597         dst->st_ex_rdev = stx->stx_rdev;
598         dst->st_ex_ino = stx->stx_ino;
599         dst->st_ex_mode = stx->stx_mode;
600         dst->st_ex_uid = stx->stx_uid;
601         dst->st_ex_gid = stx->stx_gid;
602         dst->st_ex_size = stx->stx_size;
603         dst->st_ex_nlink = stx->stx_nlink;
604         dst->st_ex_atime = stx->stx_atime;
605         dst->st_ex_btime = stx->stx_btime;
606         dst->st_ex_ctime = stx->stx_ctime;
607         dst->st_ex_mtime = stx->stx_mtime;
608         dst->st_ex_calculated_birthtime = false;
609         dst->st_ex_blksize = stx->stx_blksize;
610         dst->st_ex_blocks = stx->stx_blocks;
611 }
612
613 static int cephwrap_stat(struct vfs_handle_struct *handle,
614                         struct smb_filename *smb_fname)
615 {
616         int result = -1;
617         struct ceph_statx stx;
618
619         DBG_DEBUG("[CEPH] stat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
620
621         if (smb_fname->stream_name) {
622                 errno = ENOENT;
623                 return result;
624         }
625
626         result = ceph_statx(handle->data, smb_fname->base_name, &stx,
627                                 SAMBA_STATX_ATTR_MASK, 0);
628         DBG_DEBUG("[CEPH] statx(...) = %d\n", result);
629         if (result < 0) {
630                 WRAP_RETURN(result);
631         } else {
632                 DBG_DEBUG("[CEPH]\tstx = {dev = %llx, ino = %llu, mode = 0x%x, nlink = %llu, "
633                            "uid = %d, gid = %d, rdev = %llx, size = %llu, blksize = %llu, "
634                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu, btime = %llu}\n",
635                            llu(stx.stx_dev), llu(stx.stx_ino), stx.stx_mode,
636                            llu(stx.stx_nlink), stx.stx_uid, stx.stx_gid, llu(stx.stx_rdev),
637                            llu(stx.stx_size), llu(stx.stx_blksize),
638                            llu(stx.stx_blocks), llu(stx.stx_atime.tv_sec), llu(stx.stx_mtime.tv_sec),
639                            llu(stx.stx_ctime.tv_sec), llu(stx.stx_btime.tv_sec));
640         }
641         init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
642         DBG_DEBUG("[CEPH] mode = 0x%x\n", smb_fname->st.st_ex_mode);
643         return result;
644 }
645
646 static int cephwrap_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
647 {
648         int result = -1;
649         struct ceph_statx stx;
650
651         DBG_DEBUG("[CEPH] fstat(%p, %d)\n", handle, fsp->fh->fd);
652         result = ceph_fstatx(handle->data, fsp->fh->fd, &stx,
653                                 SAMBA_STATX_ATTR_MASK, 0);
654         DBG_DEBUG("[CEPH] fstat(...) = %d\n", result);
655         if (result < 0) {
656                 WRAP_RETURN(result);
657         } else {
658                 DBG_DEBUG("[CEPH]\tstx = {dev = %llx, ino = %llu, mode = 0x%x, nlink = %llu, "
659                            "uid = %d, gid = %d, rdev = %llx, size = %llu, blksize = %llu, "
660                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu, btime = %llu}\n",
661                            llu(stx.stx_dev), llu(stx.stx_ino), stx.stx_mode,
662                            llu(stx.stx_nlink), stx.stx_uid, stx.stx_gid, llu(stx.stx_rdev),
663                            llu(stx.stx_size), llu(stx.stx_blksize),
664                            llu(stx.stx_blocks), llu(stx.stx_atime.tv_sec), llu(stx.stx_mtime.tv_sec),
665                            llu(stx.stx_ctime.tv_sec), llu(stx.stx_btime.tv_sec));
666         }
667         init_stat_ex_from_ceph_statx(sbuf, &stx);
668         DBG_DEBUG("[CEPH] mode = 0x%x\n", sbuf->st_ex_mode);
669         return result;
670 }
671
672 static int cephwrap_lstat(struct vfs_handle_struct *handle,
673                          struct smb_filename *smb_fname)
674 {
675         int result = -1;
676         struct ceph_statx stx;
677
678         DBG_DEBUG("[CEPH] lstat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
679
680         if (smb_fname->stream_name) {
681                 errno = ENOENT;
682                 return result;
683         }
684
685         result = ceph_statx(handle->data, smb_fname->base_name, &stx,
686                                 SAMBA_STATX_ATTR_MASK, AT_SYMLINK_NOFOLLOW);
687         DBG_DEBUG("[CEPH] lstat(...) = %d\n", result);
688         if (result < 0) {
689                 WRAP_RETURN(result);
690         }
691         init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
692         return result;
693 }
694
695 static int cephwrap_ntimes(struct vfs_handle_struct *handle,
696                          const struct smb_filename *smb_fname,
697                          struct smb_file_time *ft)
698 {
699         struct ceph_statx stx = { 0 };
700         int result;
701         int mask = 0;
702
703         if (!null_timespec(ft->atime)) {
704                 stx.stx_atime = ft->atime;
705                 mask |= CEPH_SETATTR_ATIME;
706         }
707         if (!null_timespec(ft->mtime)) {
708                 stx.stx_mtime = ft->mtime;
709                 mask |= CEPH_SETATTR_MTIME;
710         }
711         if (!null_timespec(ft->create_time)) {
712                 stx.stx_btime = ft->create_time;
713                 mask |= CEPH_SETATTR_BTIME;
714         }
715
716         if (!mask) {
717                 return 0;
718         }
719
720         result = ceph_setattrx(handle->data, smb_fname->base_name, &stx, mask, 0);
721         DBG_DEBUG("[CEPH] ntimes(%p, %s, {%ld, %ld, %ld, %ld}) = %d\n", handle, smb_fname_str_dbg(smb_fname),
722                                 ft->mtime.tv_sec, ft->atime.tv_sec, ft->ctime.tv_sec,
723                                 ft->create_time.tv_sec, result);
724         return result;
725 }
726
727 #else /* HAVE_CEPH_STATX */
728
729 static int cephwrap_stat(struct vfs_handle_struct *handle,
730                         struct smb_filename *smb_fname)
731 {
732         int result = -1;
733         struct stat stbuf;
734
735         DBG_DEBUG("[CEPH] stat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
736
737         if (smb_fname->stream_name) {
738                 errno = ENOENT;
739                 return result;
740         }
741
742         result = ceph_stat(handle->data, smb_fname->base_name, (struct stat *) &stbuf);
743         DBG_DEBUG("[CEPH] stat(...) = %d\n", result);
744         if (result < 0) {
745                 WRAP_RETURN(result);
746         } else {
747                 DBG_DEBUG("[CEPH]\tstbuf = {dev = %llu, ino = %llu, mode = 0x%x, nlink = %llu, "
748                            "uid = %d, gid = %d, rdev = %llu, size = %llu, blksize = %llu, "
749                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu}\n",
750                            llu(stbuf.st_dev), llu(stbuf.st_ino), stbuf.st_mode, llu(stbuf.st_nlink),
751                            stbuf.st_uid, stbuf.st_gid, llu(stbuf.st_rdev), llu(stbuf.st_size), llu(stbuf.st_blksize),
752                            llu(stbuf.st_blocks), llu(stbuf.st_atime), llu(stbuf.st_mtime), llu(stbuf.st_ctime));
753         }
754         init_stat_ex_from_stat(
755                         &smb_fname->st, &stbuf,
756                         lp_fake_directory_create_times(SNUM(handle->conn)));
757         DBG_DEBUG("[CEPH] mode = 0x%x\n", smb_fname->st.st_ex_mode);
758         return result;
759 }
760
761 static int cephwrap_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
762 {
763         int result = -1;
764         struct stat stbuf;
765
766         DBG_DEBUG("[CEPH] fstat(%p, %d)\n", handle, fsp->fh->fd);
767         result = ceph_fstat(handle->data, fsp->fh->fd, (struct stat *) &stbuf);
768         DBG_DEBUG("[CEPH] fstat(...) = %d\n", result);
769         if (result < 0) {
770                 WRAP_RETURN(result);
771         } else {
772                 DBG_DEBUG("[CEPH]\tstbuf = {dev = %llu, ino = %llu, mode = 0x%x, nlink = %llu, "
773                            "uid = %d, gid = %d, rdev = %llu, size = %llu, blksize = %llu, "
774                            "blocks = %llu, atime = %llu, mtime = %llu, ctime = %llu}\n",
775                            llu(stbuf.st_dev), llu(stbuf.st_ino), stbuf.st_mode, llu(stbuf.st_nlink),
776                            stbuf.st_uid, stbuf.st_gid, llu(stbuf.st_rdev), llu(stbuf.st_size), llu(stbuf.st_blksize),
777                            llu(stbuf.st_blocks), llu(stbuf.st_atime), llu(stbuf.st_mtime), llu(stbuf.st_ctime));
778         }
779
780         init_stat_ex_from_stat(
781                         sbuf, &stbuf,
782                         lp_fake_directory_create_times(SNUM(handle->conn)));
783         DBG_DEBUG("[CEPH] mode = 0x%x\n", sbuf->st_ex_mode);
784         return result;
785 }
786
787 static int cephwrap_lstat(struct vfs_handle_struct *handle,
788                          struct smb_filename *smb_fname)
789 {
790         int result = -1;
791         struct stat stbuf;
792
793         DBG_DEBUG("[CEPH] lstat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
794
795         if (smb_fname->stream_name) {
796                 errno = ENOENT;
797                 return result;
798         }
799
800         result = ceph_lstat(handle->data, smb_fname->base_name, &stbuf);
801         DBG_DEBUG("[CEPH] lstat(...) = %d\n", result);
802         if (result < 0) {
803                 WRAP_RETURN(result);
804         }
805         init_stat_ex_from_stat(
806                         &smb_fname->st, &stbuf,
807                         lp_fake_directory_create_times(SNUM(handle->conn)));
808         return result;
809 }
810
811 static int cephwrap_ntimes(struct vfs_handle_struct *handle,
812                          const struct smb_filename *smb_fname,
813                          struct smb_file_time *ft)
814 {
815         struct utimbuf buf;
816         int result;
817
818         if (null_timespec(ft->atime)) {
819                 buf.actime = smb_fname->st.st_ex_atime.tv_sec;
820         } else {
821                 buf.actime = ft->atime.tv_sec;
822         }
823         if (null_timespec(ft->mtime)) {
824                 buf.modtime = smb_fname->st.st_ex_mtime.tv_sec;
825         } else {
826                 buf.modtime = ft->mtime.tv_sec;
827         }
828         if (!null_timespec(ft->create_time)) {
829                 set_create_timespec_ea(handle->conn, smb_fname,
830                                        ft->create_time);
831         }
832         if (buf.actime == smb_fname->st.st_ex_atime.tv_sec &&
833             buf.modtime == smb_fname->st.st_ex_mtime.tv_sec) {
834                 return 0;
835         }
836
837         result = ceph_utime(handle->data, smb_fname->base_name, &buf);
838         DBG_DEBUG("[CEPH] ntimes(%p, %s, {%ld, %ld, %ld, %ld}) = %d\n", handle, smb_fname_str_dbg(smb_fname),
839                                 ft->mtime.tv_sec, ft->atime.tv_sec, ft->ctime.tv_sec,
840                                 ft->create_time.tv_sec, result);
841         return result;
842 }
843 #endif /* HAVE_CEPH_STATX */
844
845 static int cephwrap_unlink(struct vfs_handle_struct *handle,
846                           const struct smb_filename *smb_fname)
847 {
848         int result = -1;
849
850         DBG_DEBUG("[CEPH] unlink(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
851         if (smb_fname->stream_name) {
852                 errno = ENOENT;
853                 return result;
854         }
855         result = ceph_unlink(handle->data, smb_fname->base_name);
856         DBG_DEBUG("[CEPH] unlink(...) = %d\n", result);
857         WRAP_RETURN(result);
858 }
859
860 static int cephwrap_chmod(struct vfs_handle_struct *handle,
861                         const struct smb_filename *smb_fname,
862                         mode_t mode)
863 {
864         int result;
865
866         DBG_DEBUG("[CEPH] chmod(%p, %s, %d)\n", handle, smb_fname->base_name, mode);
867
868         /*
869          * We need to do this due to the fact that the default POSIX ACL
870          * chmod modifies the ACL *mask* for the group owner, not the
871          * group owner bits directly. JRA.
872          */
873
874
875         {
876                 int saved_errno = errno; /* We might get ENOSYS */
877                 result = SMB_VFS_CHMOD_ACL(handle->conn,
878                                         smb_fname,
879                                         mode);
880                 if (result == 0) {
881                         return result;
882                 }
883                 /* Error - return the old errno. */
884                 errno = saved_errno;
885         }
886
887         result = ceph_chmod(handle->data, smb_fname->base_name, mode);
888         DBG_DEBUG("[CEPH] chmod(...) = %d\n", result);
889         WRAP_RETURN(result);
890 }
891
892 static int cephwrap_fchmod(struct vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
893 {
894         int result;
895
896         DBG_DEBUG("[CEPH] fchmod(%p, %p, %d)\n", handle, fsp, mode);
897
898         /*
899          * We need to do this due to the fact that the default POSIX ACL
900          * chmod modifies the ACL *mask* for the group owner, not the
901          * group owner bits directly. JRA.
902          */
903
904         {
905                 int saved_errno = errno; /* We might get ENOSYS */
906                 if ((result = SMB_VFS_FCHMOD_ACL(fsp, mode)) == 0) {
907                         return result;
908                 }
909                 /* Error - return the old errno. */
910                 errno = saved_errno;
911         }
912
913 #if defined(HAVE_FCHMOD)
914         result = ceph_fchmod(handle->data, fsp->fh->fd, mode);
915         DBG_DEBUG("[CEPH] fchmod(...) = %d\n", result);
916         WRAP_RETURN(result);
917 #else
918         errno = ENOSYS;
919 #endif
920         return -1;
921 }
922
923 static int cephwrap_chown(struct vfs_handle_struct *handle,
924                         const struct smb_filename *smb_fname,
925                         uid_t uid,
926                         gid_t gid)
927 {
928         int result;
929         DBG_DEBUG("[CEPH] chown(%p, %s, %d, %d)\n", handle, smb_fname->base_name, uid, gid);
930         result = ceph_chown(handle->data, smb_fname->base_name, uid, gid);
931         DBG_DEBUG("[CEPH] chown(...) = %d\n", result);
932         WRAP_RETURN(result);
933 }
934
935 static int cephwrap_fchown(struct vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
936 {
937         int result;
938 #ifdef HAVE_FCHOWN
939
940         DBG_DEBUG("[CEPH] fchown(%p, %p, %d, %d)\n", handle, fsp, uid, gid);
941         result = ceph_fchown(handle->data, fsp->fh->fd, uid, gid);
942         DBG_DEBUG("[CEPH] fchown(...) = %d\n", result);
943         WRAP_RETURN(result);
944 #else
945         errno = ENOSYS;
946         result = -1;
947 #endif
948         return result;
949 }
950
951 static int cephwrap_lchown(struct vfs_handle_struct *handle,
952                         const struct smb_filename *smb_fname,
953                         uid_t uid,
954                         gid_t gid)
955 {
956         int result;
957         DBG_DEBUG("[CEPH] lchown(%p, %s, %d, %d)\n", handle, smb_fname->base_name, uid, gid);
958         result = ceph_lchown(handle->data, smb_fname->base_name, uid, gid);
959         DBG_DEBUG("[CEPH] lchown(...) = %d\n", result);
960         WRAP_RETURN(result);
961 }
962
963 static int cephwrap_chdir(struct vfs_handle_struct *handle,
964                         const struct smb_filename *smb_fname)
965 {
966         int result = -1;
967         DBG_DEBUG("[CEPH] chdir(%p, %s)\n", handle, smb_fname->base_name);
968         result = ceph_chdir(handle->data, smb_fname->base_name);
969         DBG_DEBUG("[CEPH] chdir(...) = %d\n", result);
970         WRAP_RETURN(result);
971 }
972
973 static struct smb_filename *cephwrap_getwd(struct vfs_handle_struct *handle,
974                         TALLOC_CTX *ctx)
975 {
976         const char *cwd = ceph_getcwd(handle->data);
977         DBG_DEBUG("[CEPH] getwd(%p) = %s\n", handle, cwd);
978         return synthetic_smb_fname(ctx,
979                                 cwd,
980                                 NULL,
981                                 NULL,
982                                 0);
983 }
984
985 static int strict_allocate_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
986 {
987         off_t space_to_write;
988         uint64_t space_avail;
989         uint64_t bsize,dfree,dsize;
990         int ret;
991         NTSTATUS status;
992         SMB_STRUCT_STAT *pst;
993
994         status = vfs_stat_fsp(fsp);
995         if (!NT_STATUS_IS_OK(status)) {
996                 return -1;
997         }
998         pst = &fsp->fsp_name->st;
999
1000 #ifdef S_ISFIFO
1001         if (S_ISFIFO(pst->st_ex_mode))
1002                 return 0;
1003 #endif
1004
1005         if (pst->st_ex_size == len)
1006                 return 0;
1007
1008         /* Shrink - just ftruncate. */
1009         if (pst->st_ex_size > len)
1010                 return ftruncate(fsp->fh->fd, len);
1011
1012         space_to_write = len - pst->st_ex_size;
1013
1014         /* for allocation try fallocate first. This can fail on some
1015            platforms e.g. when the filesystem doesn't support it and no
1016            emulation is being done by the libc (like on AIX with JFS1). In that
1017            case we do our own emulation. fallocate implementations can
1018            return ENOTSUP or EINVAL in cases like that. */
1019         ret = SMB_VFS_FALLOCATE(fsp, 0, pst->st_ex_size, space_to_write);
1020         if (ret == -1 && errno == ENOSPC) {
1021                 return -1;
1022         }
1023         if (ret == 0) {
1024                 return 0;
1025         }
1026         DEBUG(10,("[CEPH] strict_allocate_ftruncate: SMB_VFS_FALLOCATE failed with "
1027                 "error %d. Falling back to slow manual allocation\n", errno));
1028
1029         /* available disk space is enough or not? */
1030         space_avail =
1031             get_dfree_info(fsp->conn, fsp->fsp_name, &bsize, &dfree, &dsize);
1032         /* space_avail is 1k blocks */
1033         if (space_avail == (uint64_t)-1 ||
1034                         ((uint64_t)space_to_write/1024 > space_avail) ) {
1035                 errno = ENOSPC;
1036                 return -1;
1037         }
1038
1039         /* Write out the real space on disk. */
1040         return vfs_slow_fallocate(fsp, pst->st_ex_size, space_to_write);
1041 }
1042
1043 static int cephwrap_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
1044 {
1045         int result = -1;
1046         SMB_STRUCT_STAT st;
1047         char c = 0;
1048         off_t currpos;
1049
1050         DBG_DEBUG("[CEPH] ftruncate(%p, %p, %llu\n", handle, fsp, llu(len));
1051
1052         if (lp_strict_allocate(SNUM(fsp->conn))) {
1053                 result = strict_allocate_ftruncate(handle, fsp, len);
1054                 return result;
1055         }
1056
1057         /* we used to just check HAVE_FTRUNCATE_EXTEND and only use
1058            sys_ftruncate if the system supports it. Then I discovered that
1059            you can have some filesystems that support ftruncate
1060            expansion and some that don't! On Linux fat can't do
1061            ftruncate extend but ext2 can. */
1062
1063         result = ceph_ftruncate(handle->data, fsp->fh->fd, len);
1064         if (result == 0)
1065                 goto done;
1066
1067         /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
1068            extend a file with ftruncate. Provide alternate implementation
1069            for this */
1070         currpos = SMB_VFS_LSEEK(fsp, 0, SEEK_CUR);
1071         if (currpos == -1) {
1072                 goto done;
1073         }
1074
1075         /* Do an fstat to see if the file is longer than the requested
1076            size in which case the ftruncate above should have
1077            succeeded or shorter, in which case seek to len - 1 and
1078            write 1 byte of zero */
1079         if (SMB_VFS_FSTAT(fsp, &st) == -1) {
1080                 goto done;
1081         }
1082
1083 #ifdef S_ISFIFO
1084         if (S_ISFIFO(st.st_ex_mode)) {
1085                 result = 0;
1086                 goto done;
1087         }
1088 #endif
1089
1090         if (st.st_ex_size == len) {
1091                 result = 0;
1092                 goto done;
1093         }
1094
1095         if (st.st_ex_size > len) {
1096                 /* the sys_ftruncate should have worked */
1097                 goto done;
1098         }
1099
1100         if (SMB_VFS_PWRITE(fsp, &c, 1, len-1)!=1) {
1101                 goto done;
1102         }
1103
1104         result = 0;
1105
1106   done:
1107
1108         return result;
1109 }
1110
1111 static bool cephwrap_lock(struct vfs_handle_struct *handle, files_struct *fsp, int op, off_t offset, off_t count, int type)
1112 {
1113         DBG_DEBUG("[CEPH] lock\n");
1114         return true;
1115 }
1116
1117 static int cephwrap_kernel_flock(struct vfs_handle_struct *handle, files_struct *fsp,
1118                                 uint32_t share_mode, uint32_t access_mask)
1119 {
1120         DBG_DEBUG("[CEPH] kernel_flock\n");
1121         /*
1122          * We must return zero here and pretend all is good.
1123          * One day we might have this in CEPH.
1124          */
1125         return 0;
1126 }
1127
1128 static bool cephwrap_getlock(struct vfs_handle_struct *handle, files_struct *fsp, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid)
1129 {
1130         DBG_DEBUG("[CEPH] getlock returning false and errno=0\n");
1131
1132         errno = 0;
1133         return false;
1134 }
1135
1136 /*
1137  * We cannot let this fall through to the default, because the file might only
1138  * be accessible from libceph (which is a user-space client) but the fd might
1139  * be for some file the kernel knows about.
1140  */
1141 static int cephwrap_linux_setlease(struct vfs_handle_struct *handle, files_struct *fsp,
1142                                 int leasetype)
1143 {
1144         int result = -1;
1145
1146         DBG_DEBUG("[CEPH] linux_setlease\n");
1147         errno = ENOSYS;
1148         return result;
1149 }
1150
1151 static int cephwrap_symlink(struct vfs_handle_struct *handle,
1152                 const char *link_target,
1153                 const struct smb_filename *new_smb_fname)
1154 {
1155         int result = -1;
1156         DBG_DEBUG("[CEPH] symlink(%p, %s, %s)\n", handle,
1157                         link_target,
1158                         new_smb_fname->base_name);
1159         result = ceph_symlink(handle->data,
1160                         link_target,
1161                         new_smb_fname->base_name);
1162         DBG_DEBUG("[CEPH] symlink(...) = %d\n", result);
1163         WRAP_RETURN(result);
1164 }
1165
1166 static int cephwrap_readlink(struct vfs_handle_struct *handle,
1167                 const struct smb_filename *smb_fname,
1168                 char *buf,
1169                 size_t bufsiz)
1170 {
1171         int result = -1;
1172         DBG_DEBUG("[CEPH] readlink(%p, %s, %p, %llu)\n", handle,
1173                         smb_fname->base_name, buf, llu(bufsiz));
1174         result = ceph_readlink(handle->data, smb_fname->base_name, buf, bufsiz);
1175         DBG_DEBUG("[CEPH] readlink(...) = %d\n", result);
1176         WRAP_RETURN(result);
1177 }
1178
1179 static int cephwrap_link(struct vfs_handle_struct *handle,
1180                 const struct smb_filename *old_smb_fname,
1181                 const struct smb_filename *new_smb_fname)
1182 {
1183         int result = -1;
1184         DBG_DEBUG("[CEPH] link(%p, %s, %s)\n", handle,
1185                         old_smb_fname->base_name,
1186                         new_smb_fname->base_name);
1187         result = ceph_link(handle->data,
1188                                 old_smb_fname->base_name,
1189                                 new_smb_fname->base_name);
1190         DBG_DEBUG("[CEPH] link(...) = %d\n", result);
1191         WRAP_RETURN(result);
1192 }
1193
1194 static int cephwrap_mknod(struct vfs_handle_struct *handle,
1195                 const struct smb_filename *smb_fname,
1196                 mode_t mode,
1197                 SMB_DEV_T dev)
1198 {
1199         int result = -1;
1200         DBG_DEBUG("[CEPH] mknod(%p, %s)\n", handle, smb_fname->base_name);
1201         result = ceph_mknod(handle->data, smb_fname->base_name, mode, dev);
1202         DBG_DEBUG("[CEPH] mknod(...) = %d\n", result);
1203         WRAP_RETURN(result);
1204 }
1205
1206 /*
1207  * This is a simple version of real-path ... a better version is needed to
1208  * ask libceph about symbolic links.
1209  */
1210 static struct smb_filename *cephwrap_realpath(struct vfs_handle_struct *handle,
1211                                 TALLOC_CTX *ctx,
1212                                 const struct smb_filename *smb_fname)
1213 {
1214         char *result;
1215         const char *path = smb_fname->base_name;
1216         size_t len = strlen(path);
1217         struct smb_filename *result_fname = NULL;
1218
1219         result = SMB_MALLOC_ARRAY(char, PATH_MAX+1);
1220         if (len && (path[0] == '/')) {
1221                 int r = asprintf(&result, "%s", path);
1222                 if (r < 0) return NULL;
1223         } else if ((len >= 2) && (path[0] == '.') && (path[1] == '/')) {
1224                 if (len == 2) {
1225                         int r = asprintf(&result, "%s",
1226                                         handle->conn->connectpath);
1227                         if (r < 0) return NULL;
1228                 } else {
1229                         int r = asprintf(&result, "%s/%s",
1230                                         handle->conn->connectpath, &path[2]);
1231                         if (r < 0) return NULL;
1232                 }
1233         } else {
1234                 int r = asprintf(&result, "%s/%s",
1235                                 handle->conn->connectpath, path);
1236                 if (r < 0) return NULL;
1237         }
1238         DBG_DEBUG("[CEPH] realpath(%p, %s) = %s\n", handle, path, result);
1239         result_fname = synthetic_smb_fname(ctx,
1240                                 result,
1241                                 NULL,
1242                                 NULL,
1243                                 0);
1244         SAFE_FREE(result);
1245         return result_fname;
1246 }
1247
1248 static int cephwrap_chflags(struct vfs_handle_struct *handle,
1249                         const struct smb_filename *smb_fname,
1250                         unsigned int flags)
1251 {
1252         errno = ENOSYS;
1253         return -1;
1254 }
1255
1256 static int cephwrap_get_real_filename(struct vfs_handle_struct *handle,
1257                                      const char *path,
1258                                      const char *name,
1259                                      TALLOC_CTX *mem_ctx,
1260                                      char **found_name)
1261 {
1262         /*
1263          * Don't fall back to get_real_filename so callers can differentiate
1264          * between a full directory scan and an actual case-insensitive stat.
1265          */
1266         errno = EOPNOTSUPP;
1267         return -1;
1268 }
1269
1270 static const char *cephwrap_connectpath(struct vfs_handle_struct *handle,
1271                                        const struct smb_filename *smb_fname)
1272 {
1273         return handle->conn->connectpath;
1274 }
1275
1276 /****************************************************************
1277  Extended attribute operations.
1278 *****************************************************************/
1279
1280 static ssize_t cephwrap_getxattr(struct vfs_handle_struct *handle,
1281                         const struct smb_filename *smb_fname,
1282                         const char *name,
1283                         void *value,
1284                         size_t size)
1285 {
1286         int ret;
1287         DBG_DEBUG("[CEPH] getxattr(%p, %s, %s, %p, %llu)\n", handle,
1288                         smb_fname->base_name, name, value, llu(size));
1289         ret = ceph_getxattr(handle->data,
1290                         smb_fname->base_name, name, value, size);
1291         DBG_DEBUG("[CEPH] getxattr(...) = %d\n", ret);
1292         if (ret < 0) {
1293                 WRAP_RETURN(ret);
1294         } else {
1295                 return (ssize_t)ret;
1296         }
1297 }
1298
1299 static ssize_t cephwrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size)
1300 {
1301         int ret;
1302         DBG_DEBUG("[CEPH] fgetxattr(%p, %p, %s, %p, %llu)\n", handle, fsp, name, value, llu(size));
1303 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1304         ret = ceph_fgetxattr(handle->data, fsp->fh->fd, name, value, size);
1305 #else
1306         ret = ceph_getxattr(handle->data, fsp->fsp_name->base_name, name, value, size);
1307 #endif
1308         DBG_DEBUG("[CEPH] fgetxattr(...) = %d\n", ret);
1309         if (ret < 0) {
1310                 WRAP_RETURN(ret);
1311         } else {
1312                 return (ssize_t)ret;
1313         }
1314 }
1315
1316 static ssize_t cephwrap_listxattr(struct vfs_handle_struct *handle,
1317                         const struct smb_filename *smb_fname,
1318                         char *list,
1319                         size_t size)
1320 {
1321         int ret;
1322         DBG_DEBUG("[CEPH] listxattr(%p, %s, %p, %llu)\n", handle,
1323                         smb_fname->base_name, list, llu(size));
1324         ret = ceph_listxattr(handle->data, smb_fname->base_name, list, size);
1325         DBG_DEBUG("[CEPH] listxattr(...) = %d\n", ret);
1326         if (ret < 0) {
1327                 WRAP_RETURN(ret);
1328         } else {
1329                 return (ssize_t)ret;
1330         }
1331 }
1332
1333 static ssize_t cephwrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, char *list, size_t size)
1334 {
1335         int ret;
1336         DBG_DEBUG("[CEPH] flistxattr(%p, %p, %s, %llu)\n", handle, fsp, list, llu(size));
1337 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1338         ret = ceph_flistxattr(handle->data, fsp->fh->fd, list, size);
1339 #else
1340         ret = ceph_listxattr(handle->data, fsp->fsp_name->base_name, list, size);
1341 #endif
1342         DBG_DEBUG("[CEPH] flistxattr(...) = %d\n", ret);
1343         if (ret < 0) {
1344                 WRAP_RETURN(ret);
1345         } else {
1346                 return (ssize_t)ret;
1347         }
1348 }
1349
1350 static int cephwrap_removexattr(struct vfs_handle_struct *handle,
1351                                 const struct smb_filename *smb_fname,
1352                                 const char *name)
1353 {
1354         int ret;
1355         DBG_DEBUG("[CEPH] removexattr(%p, %s, %s)\n", handle,
1356                         smb_fname->base_name, name);
1357         ret = ceph_removexattr(handle->data, smb_fname->base_name, name);
1358         DBG_DEBUG("[CEPH] removexattr(...) = %d\n", ret);
1359         WRAP_RETURN(ret);
1360 }
1361
1362 static int cephwrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name)
1363 {
1364         int ret;
1365         DBG_DEBUG("[CEPH] fremovexattr(%p, %p, %s)\n", handle, fsp, name);
1366 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1367         ret = ceph_fremovexattr(handle->data, fsp->fh->fd, name);
1368 #else
1369         ret = ceph_removexattr(handle->data, fsp->fsp_name->base_name, name);
1370 #endif
1371         DBG_DEBUG("[CEPH] fremovexattr(...) = %d\n", ret);
1372         WRAP_RETURN(ret);
1373 }
1374
1375 static int cephwrap_setxattr(struct vfs_handle_struct *handle,
1376                                 const struct smb_filename *smb_fname,
1377                                 const char *name,
1378                                 const void *value,
1379                                 size_t size,
1380                                 int flags)
1381 {
1382         int ret;
1383         DBG_DEBUG("[CEPH] setxattr(%p, %s, %s, %p, %llu, %d)\n", handle,
1384                         smb_fname->base_name, name, value, llu(size), flags);
1385         ret = ceph_setxattr(handle->data, smb_fname->base_name,
1386                         name, value, size, flags);
1387         DBG_DEBUG("[CEPH] setxattr(...) = %d\n", ret);
1388         WRAP_RETURN(ret);
1389 }
1390
1391 static int cephwrap_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, const void *value, size_t size, int flags)
1392 {
1393         int ret;
1394         DBG_DEBUG("[CEPH] fsetxattr(%p, %p, %s, %p, %llu, %d)\n", handle, fsp, name, value, llu(size), flags);
1395 #if LIBCEPHFS_VERSION_CODE >= LIBCEPHFS_VERSION(0, 94, 0)
1396         ret = ceph_fsetxattr(handle->data, fsp->fh->fd,
1397                              name, value, size, flags);
1398 #else
1399         ret = ceph_setxattr(handle->data, fsp->fsp_name->base_name, name, value, size, flags);
1400 #endif
1401         DBG_DEBUG("[CEPH] fsetxattr(...) = %d\n", ret);
1402         WRAP_RETURN(ret);
1403 }
1404
1405 static bool cephwrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
1406 {
1407
1408         /*
1409          * We do not support AIO yet.
1410          */
1411
1412         DBG_DEBUG("[CEPH] cephwrap_aio_force(%p, %p) = false (errno = ENOTSUP)\n", handle, fsp);
1413         errno = ENOTSUP;
1414         return false;
1415 }
1416
1417 static struct vfs_fn_pointers ceph_fns = {
1418         /* Disk operations */
1419
1420         .connect_fn = cephwrap_connect,
1421         .disconnect_fn = cephwrap_disconnect,
1422         .disk_free_fn = cephwrap_disk_free,
1423         .get_quota_fn = cephwrap_get_quota,
1424         .set_quota_fn = cephwrap_set_quota,
1425         .statvfs_fn = cephwrap_statvfs,
1426         .fs_capabilities_fn = cephwrap_fs_capabilities,
1427
1428         /* Directory operations */
1429
1430         .opendir_fn = cephwrap_opendir,
1431         .fdopendir_fn = cephwrap_fdopendir,
1432         .readdir_fn = cephwrap_readdir,
1433         .seekdir_fn = cephwrap_seekdir,
1434         .telldir_fn = cephwrap_telldir,
1435         .rewind_dir_fn = cephwrap_rewinddir,
1436         .mkdir_fn = cephwrap_mkdir,
1437         .rmdir_fn = cephwrap_rmdir,
1438         .closedir_fn = cephwrap_closedir,
1439
1440         /* File operations */
1441
1442         .open_fn = cephwrap_open,
1443         .close_fn = cephwrap_close,
1444         .pread_fn = cephwrap_pread,
1445         .pwrite_fn = cephwrap_pwrite,
1446         .lseek_fn = cephwrap_lseek,
1447         .sendfile_fn = cephwrap_sendfile,
1448         .recvfile_fn = cephwrap_recvfile,
1449         .rename_fn = cephwrap_rename,
1450         .fsync_send_fn = cephwrap_fsync_send,
1451         .fsync_recv_fn = cephwrap_fsync_recv,
1452         .stat_fn = cephwrap_stat,
1453         .fstat_fn = cephwrap_fstat,
1454         .lstat_fn = cephwrap_lstat,
1455         .unlink_fn = cephwrap_unlink,
1456         .chmod_fn = cephwrap_chmod,
1457         .fchmod_fn = cephwrap_fchmod,
1458         .chown_fn = cephwrap_chown,
1459         .fchown_fn = cephwrap_fchown,
1460         .lchown_fn = cephwrap_lchown,
1461         .chdir_fn = cephwrap_chdir,
1462         .getwd_fn = cephwrap_getwd,
1463         .ntimes_fn = cephwrap_ntimes,
1464         .ftruncate_fn = cephwrap_ftruncate,
1465         .lock_fn = cephwrap_lock,
1466         .kernel_flock_fn = cephwrap_kernel_flock,
1467         .linux_setlease_fn = cephwrap_linux_setlease,
1468         .getlock_fn = cephwrap_getlock,
1469         .symlink_fn = cephwrap_symlink,
1470         .readlink_fn = cephwrap_readlink,
1471         .link_fn = cephwrap_link,
1472         .mknod_fn = cephwrap_mknod,
1473         .realpath_fn = cephwrap_realpath,
1474         .chflags_fn = cephwrap_chflags,
1475         .get_real_filename_fn = cephwrap_get_real_filename,
1476         .connectpath_fn = cephwrap_connectpath,
1477
1478         /* EA operations. */
1479         .getxattr_fn = cephwrap_getxattr,
1480         .fgetxattr_fn = cephwrap_fgetxattr,
1481         .listxattr_fn = cephwrap_listxattr,
1482         .flistxattr_fn = cephwrap_flistxattr,
1483         .removexattr_fn = cephwrap_removexattr,
1484         .fremovexattr_fn = cephwrap_fremovexattr,
1485         .setxattr_fn = cephwrap_setxattr,
1486         .fsetxattr_fn = cephwrap_fsetxattr,
1487
1488         /* Posix ACL Operations */
1489         .sys_acl_get_file_fn = posixacl_xattr_acl_get_file,
1490         .sys_acl_get_fd_fn = posixacl_xattr_acl_get_fd,
1491         .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
1492         .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
1493         .sys_acl_set_file_fn = posixacl_xattr_acl_set_file,
1494         .sys_acl_set_fd_fn = posixacl_xattr_acl_set_fd,
1495         .sys_acl_delete_def_file_fn = posixacl_xattr_acl_delete_def_file,
1496
1497         /* aio operations */
1498         .aio_force_fn = cephwrap_aio_force,
1499 };
1500
1501 static_decl_vfs;
1502 NTSTATUS vfs_ceph_init(TALLOC_CTX *ctx)
1503 {
1504         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1505                                 "ceph", &ceph_fns);
1506 }