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