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