Line data Source code
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 libcephfs.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 = [any others you need go here] ceph
31 : */
32 :
33 : #include "includes.h"
34 : #include "smbd/smbd.h"
35 : #include "system/filesys.h"
36 : #include <dirent.h>
37 : #include <sys/statvfs.h>
38 : #include "cephfs/libcephfs.h"
39 : #include "smbprofile.h"
40 : #include "modules/posixacl_xattr.h"
41 : #include "lib/util/tevent_unix.h"
42 :
43 : #undef DBGC_CLASS
44 : #define DBGC_CLASS DBGC_VFS
45 :
46 : #ifndef LIBCEPHFS_VERSION
47 : #define LIBCEPHFS_VERSION(maj, min, extra) ((maj << 16) + (min << 8) + extra)
48 : #define LIBCEPHFS_VERSION_CODE LIBCEPHFS_VERSION(0, 0, 0)
49 : #endif
50 :
51 : /*
52 : * Use %llu whenever we have a 64bit unsigned int, and cast to (long long unsigned)
53 : */
54 : #define llu(_var) ((long long unsigned)_var)
55 :
56 : /*
57 : * Note, libcephfs's return code model is to return -errno! So we have to
58 : * convert to what Samba expects, with is set errno to -return and return -1
59 : */
60 : #define WRAP_RETURN(_res) \
61 : errno = 0; \
62 : if (_res < 0) { \
63 : errno = -_res; \
64 : return -1; \
65 : } \
66 : return _res \
67 :
68 : /*
69 : * Track unique connections, as virtual mounts, to cephfs file systems.
70 : * Individual mounts will be set on the handle->data attribute, but
71 : * the mounts themselves will be shared so as not to spawn extra mounts
72 : * to the same cephfs.
73 : *
74 : * Individual mounts are IDed by a 'cookie' value that is a string built
75 : * from identifying parameters found in smb.conf.
76 : */
77 :
78 : static struct cephmount_cached {
79 : char *cookie;
80 : uint32_t count;
81 : struct ceph_mount_info *mount;
82 : struct cephmount_cached *next, *prev;
83 : } *cephmount_cached;
84 :
85 0 : static int cephmount_cache_add(const char *cookie,
86 : struct ceph_mount_info *mount)
87 : {
88 0 : struct cephmount_cached *entry = NULL;
89 :
90 0 : entry = talloc_zero(NULL, struct cephmount_cached);
91 0 : if (entry == NULL) {
92 0 : errno = ENOMEM;
93 0 : return -1;
94 : }
95 :
96 0 : entry->cookie = talloc_strdup(entry, cookie);
97 0 : if (entry->cookie == NULL) {
98 0 : talloc_free(entry);
99 0 : errno = ENOMEM;
100 0 : return -1;
101 : }
102 :
103 0 : entry->mount = mount;
104 0 : entry->count = 1;
105 :
106 0 : DBG_DEBUG("adding mount cache entry for %s\n", entry->cookie);
107 0 : DLIST_ADD(cephmount_cached, entry);
108 0 : return 0;
109 : }
110 :
111 0 : static struct ceph_mount_info *cephmount_cache_update(const char *cookie)
112 : {
113 0 : struct cephmount_cached *entry = NULL;
114 :
115 0 : for (entry = cephmount_cached; entry; entry = entry->next) {
116 0 : if (strcmp(entry->cookie, cookie) == 0) {
117 0 : entry->count++;
118 0 : DBG_DEBUG("updated mount cache: count is [%"
119 : PRIu32 "]\n", entry->count);
120 0 : return entry->mount;
121 : }
122 : }
123 :
124 0 : errno = ENOENT;
125 0 : return NULL;
126 : }
127 :
128 0 : static int cephmount_cache_remove(struct ceph_mount_info *mount)
129 : {
130 0 : struct cephmount_cached *entry = NULL;
131 :
132 0 : for (entry = cephmount_cached; entry; entry = entry->next) {
133 0 : if (entry->mount == mount) {
134 0 : if (--entry->count) {
135 0 : DBG_DEBUG("updated mount cache: count is [%"
136 : PRIu32 "]\n", entry->count);
137 0 : return entry->count;
138 : }
139 :
140 0 : DBG_DEBUG("removing mount cache entry for %s\n",
141 : entry->cookie);
142 0 : DLIST_REMOVE(cephmount_cached, entry);
143 0 : talloc_free(entry);
144 0 : return 0;
145 : }
146 : }
147 0 : errno = ENOENT;
148 0 : return -1;
149 : }
150 :
151 0 : static char *cephmount_get_cookie(TALLOC_CTX * mem_ctx, const int snum)
152 : {
153 : const char *conf_file =
154 0 : lp_parm_const_string(snum, "ceph", "config_file", ".");
155 0 : const char *user_id = lp_parm_const_string(snum, "ceph", "user_id", "");
156 : const char *fsname =
157 0 : lp_parm_const_string(snum, "ceph", "filesystem", "");
158 0 : return talloc_asprintf(mem_ctx, "(%s/%s/%s)", conf_file, user_id,
159 : fsname);
160 : }
161 :
162 0 : static int cephmount_select_fs(struct ceph_mount_info *mnt, const char *fsname)
163 : {
164 : /*
165 : * ceph_select_filesystem was added in ceph 'nautilus' (v14).
166 : * Earlier versions of libcephfs will lack that API function.
167 : * At the time of this writing (Feb 2023) all versions of ceph
168 : * supported by ceph upstream have this function.
169 : */
170 : #if defined(HAVE_CEPH_SELECT_FILESYSTEM)
171 0 : DBG_DEBUG("[CEPH] calling: ceph_select_filesystem with %s\n", fsname);
172 0 : return ceph_select_filesystem(mnt, fsname);
173 : #else
174 : DBG_ERR("[CEPH] ceph_select_filesystem not available\n");
175 : return -ENOTSUP;
176 : #endif
177 : }
178 :
179 0 : static struct ceph_mount_info *cephmount_mount_fs(const int snum)
180 : {
181 : int ret;
182 : char buf[256];
183 0 : struct ceph_mount_info *mnt = NULL;
184 : /* if config_file and/or user_id are NULL, ceph will use defaults */
185 : const char *conf_file =
186 0 : lp_parm_const_string(snum, "ceph", "config_file", NULL);
187 : const char *user_id =
188 0 : lp_parm_const_string(snum, "ceph", "user_id", NULL);
189 : const char *fsname =
190 0 : lp_parm_const_string(snum, "ceph", "filesystem", NULL);
191 :
192 0 : DBG_DEBUG("[CEPH] calling: ceph_create\n");
193 0 : ret = ceph_create(&mnt, user_id);
194 0 : if (ret) {
195 0 : errno = -ret;
196 0 : return NULL;
197 : }
198 :
199 0 : DBG_DEBUG("[CEPH] calling: ceph_conf_read_file with %s\n",
200 : (conf_file == NULL ? "default path" : conf_file));
201 0 : ret = ceph_conf_read_file(mnt, conf_file);
202 0 : if (ret) {
203 0 : goto err_cm_release;
204 : }
205 :
206 0 : DBG_DEBUG("[CEPH] calling: ceph_conf_get\n");
207 0 : ret = ceph_conf_get(mnt, "log file", buf, sizeof(buf));
208 0 : if (ret < 0) {
209 0 : goto err_cm_release;
210 : }
211 :
212 : /* libcephfs disables POSIX ACL support by default, enable it... */
213 0 : ret = ceph_conf_set(mnt, "client_acl_type", "posix_acl");
214 0 : if (ret < 0) {
215 0 : goto err_cm_release;
216 : }
217 : /* tell libcephfs to perform local permission checks */
218 0 : ret = ceph_conf_set(mnt, "fuse_default_permissions", "false");
219 0 : if (ret < 0) {
220 0 : goto err_cm_release;
221 : }
222 : /*
223 : * select a cephfs file system to use:
224 : * In ceph, multiple file system support has been stable since 'pacific'.
225 : * Permit different shares to access different file systems.
226 : */
227 0 : if (fsname != NULL) {
228 0 : ret = cephmount_select_fs(mnt, fsname);
229 0 : if (ret < 0) {
230 0 : goto err_cm_release;
231 : }
232 : }
233 :
234 0 : DBG_DEBUG("[CEPH] calling: ceph_mount\n");
235 0 : ret = ceph_mount(mnt, NULL);
236 0 : if (ret >= 0) {
237 0 : goto cm_done;
238 : }
239 :
240 0 : err_cm_release:
241 0 : ceph_release(mnt);
242 0 : mnt = NULL;
243 0 : DBG_DEBUG("[CEPH] Error mounting fs: %s\n", strerror(-ret));
244 0 : cm_done:
245 : /*
246 : * Handle the error correctly. Ceph returns -errno.
247 : */
248 0 : if (ret) {
249 0 : errno = -ret;
250 : }
251 0 : return mnt;
252 : }
253 :
254 : /* Check for NULL pointer parameters in cephwrap_* functions */
255 :
256 : /* We don't want to have NULL function pointers lying around. Someone
257 : is sure to try and execute them. These stubs are used to prevent
258 : this possibility. */
259 :
260 0 : static int cephwrap_connect(struct vfs_handle_struct *handle,
261 : const char *service, const char *user)
262 : {
263 0 : int ret = 0;
264 0 : struct ceph_mount_info *cmount = NULL;
265 0 : int snum = SNUM(handle->conn);
266 0 : char *cookie = cephmount_get_cookie(handle, snum);
267 0 : if (cookie == NULL) {
268 0 : return -1;
269 : }
270 :
271 0 : cmount = cephmount_cache_update(cookie);
272 0 : if (cmount != NULL) {
273 0 : goto connect_ok;
274 : }
275 :
276 0 : cmount = cephmount_mount_fs(snum);
277 0 : if (cmount == NULL) {
278 0 : ret = -1;
279 0 : goto connect_fail;
280 : }
281 0 : ret = cephmount_cache_add(cookie, cmount);
282 0 : if (ret) {
283 0 : goto connect_fail;
284 : }
285 :
286 0 : connect_ok:
287 0 : handle->data = cmount;
288 : /*
289 : * Unless we have an async implementation of getxattrat turn this off.
290 : */
291 0 : lp_do_parameter(SNUM(handle->conn), "smbd async dosmode", "false");
292 0 : connect_fail:
293 0 : talloc_free(cookie);
294 0 : return ret;
295 : }
296 :
297 0 : static void cephwrap_disconnect(struct vfs_handle_struct *handle)
298 : {
299 0 : int ret = cephmount_cache_remove(handle->data);
300 0 : if (ret < 0) {
301 0 : DBG_ERR("failed to remove ceph mount from cache: %s\n",
302 : strerror(errno));
303 0 : return;
304 : }
305 0 : if (ret > 0) {
306 0 : DBG_DEBUG("mount cache entry still in use\n");
307 0 : return;
308 : }
309 :
310 0 : ret = ceph_unmount(handle->data);
311 0 : if (ret < 0) {
312 0 : DBG_ERR("[CEPH] failed to unmount: %s\n", strerror(-ret));
313 : }
314 :
315 0 : ret = ceph_release(handle->data);
316 0 : if (ret < 0) {
317 0 : DBG_ERR("[CEPH] failed to release: %s\n", strerror(-ret));
318 : }
319 0 : handle->data = NULL;
320 : }
321 :
322 : /* Disk operations */
323 :
324 0 : static uint64_t cephwrap_disk_free(struct vfs_handle_struct *handle,
325 : const struct smb_filename *smb_fname,
326 : uint64_t *bsize,
327 : uint64_t *dfree,
328 : uint64_t *dsize)
329 : {
330 0 : struct statvfs statvfs_buf = { 0 };
331 : int ret;
332 :
333 0 : if (!(ret = ceph_statfs(handle->data, smb_fname->base_name,
334 : &statvfs_buf))) {
335 : /*
336 : * Provide all the correct values.
337 : */
338 0 : *bsize = statvfs_buf.f_bsize;
339 0 : *dfree = statvfs_buf.f_bavail;
340 0 : *dsize = statvfs_buf.f_blocks;
341 0 : DBG_DEBUG("[CEPH] bsize: %llu, dfree: %llu, dsize: %llu\n",
342 : llu(*bsize), llu(*dfree), llu(*dsize));
343 0 : return *dfree;
344 : } else {
345 0 : DBG_DEBUG("[CEPH] ceph_statfs returned %d\n", ret);
346 0 : WRAP_RETURN(ret);
347 : }
348 : }
349 :
350 0 : static int cephwrap_get_quota(struct vfs_handle_struct *handle,
351 : const struct smb_filename *smb_fname,
352 : enum SMB_QUOTA_TYPE qtype,
353 : unid_t id,
354 : SMB_DISK_QUOTA *qt)
355 : {
356 : /* libcephfs: Ceph does not implement this */
357 : #if 0
358 : /* was ifdef HAVE_SYS_QUOTAS */
359 : int ret;
360 :
361 : ret = ceph_get_quota(handle->conn->connectpath, qtype, id, qt);
362 :
363 : if (ret) {
364 : errno = -ret;
365 : ret = -1;
366 : }
367 :
368 : return ret;
369 : #else
370 0 : errno = ENOSYS;
371 0 : return -1;
372 : #endif
373 : }
374 :
375 0 : static int cephwrap_set_quota(struct vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
376 : {
377 : /* libcephfs: Ceph does not implement this */
378 : #if 0
379 : /* was ifdef HAVE_SYS_QUOTAS */
380 : int ret;
381 :
382 : ret = ceph_set_quota(handle->conn->connectpath, qtype, id, qt);
383 : if (ret) {
384 : errno = -ret;
385 : ret = -1;
386 : }
387 :
388 : return ret;
389 : #else
390 0 : WRAP_RETURN(-ENOSYS);
391 : #endif
392 : }
393 :
394 0 : static int cephwrap_statvfs(struct vfs_handle_struct *handle,
395 : const struct smb_filename *smb_fname,
396 : struct vfs_statvfs_struct *statbuf)
397 : {
398 0 : struct statvfs statvfs_buf = { 0 };
399 : int ret;
400 :
401 0 : ret = ceph_statfs(handle->data, smb_fname->base_name, &statvfs_buf);
402 0 : if (ret < 0) {
403 0 : WRAP_RETURN(ret);
404 : }
405 :
406 0 : statbuf->OptimalTransferSize = statvfs_buf.f_frsize;
407 0 : statbuf->BlockSize = statvfs_buf.f_bsize;
408 0 : statbuf->TotalBlocks = statvfs_buf.f_blocks;
409 0 : statbuf->BlocksAvail = statvfs_buf.f_bfree;
410 0 : statbuf->UserBlocksAvail = statvfs_buf.f_bavail;
411 0 : statbuf->TotalFileNodes = statvfs_buf.f_files;
412 0 : statbuf->FreeFileNodes = statvfs_buf.f_ffree;
413 0 : statbuf->FsIdentifier = statvfs_buf.f_fsid;
414 0 : DBG_DEBUG("[CEPH] f_bsize: %ld, f_blocks: %ld, f_bfree: %ld, f_bavail: %ld\n",
415 : (long int)statvfs_buf.f_bsize, (long int)statvfs_buf.f_blocks,
416 : (long int)statvfs_buf.f_bfree, (long int)statvfs_buf.f_bavail);
417 :
418 0 : return ret;
419 : }
420 :
421 0 : static uint32_t cephwrap_fs_capabilities(struct vfs_handle_struct *handle,
422 : enum timestamp_set_resolution *p_ts_res)
423 : {
424 0 : uint32_t caps = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES;
425 :
426 0 : *p_ts_res = TIMESTAMP_SET_NT_OR_BETTER;
427 :
428 0 : return caps;
429 : }
430 :
431 : /* Directory operations */
432 :
433 0 : static DIR *cephwrap_fdopendir(struct vfs_handle_struct *handle,
434 : struct files_struct *fsp,
435 : const char *mask,
436 : uint32_t attributes)
437 : {
438 0 : int ret = 0;
439 0 : struct ceph_dir_result *result = NULL;
440 0 : DBG_DEBUG("[CEPH] fdopendir(%p, %p)\n", handle, fsp);
441 :
442 0 : ret = ceph_opendir(handle->data, fsp->fsp_name->base_name, &result);
443 0 : if (ret < 0) {
444 0 : result = NULL;
445 0 : errno = -ret; /* We return result which is NULL in this case */
446 : }
447 :
448 0 : DBG_DEBUG("[CEPH] fdopendir(...) = %d\n", ret);
449 0 : return (DIR *) result;
450 : }
451 :
452 0 : static struct dirent *cephwrap_readdir(struct vfs_handle_struct *handle,
453 : struct files_struct *dirfsp,
454 : DIR *dirp)
455 : {
456 0 : struct dirent *result = NULL;
457 :
458 0 : DBG_DEBUG("[CEPH] readdir(%p, %p)\n", handle, dirp);
459 0 : result = ceph_readdir(handle->data, (struct ceph_dir_result *) dirp);
460 0 : DBG_DEBUG("[CEPH] readdir(...) = %p\n", result);
461 :
462 0 : return result;
463 : }
464 :
465 0 : static void cephwrap_rewinddir(struct vfs_handle_struct *handle, DIR *dirp)
466 : {
467 0 : DBG_DEBUG("[CEPH] rewinddir(%p, %p)\n", handle, dirp);
468 0 : ceph_rewinddir(handle->data, (struct ceph_dir_result *) dirp);
469 0 : }
470 :
471 0 : static int cephwrap_mkdirat(struct vfs_handle_struct *handle,
472 : files_struct *dirfsp,
473 : const struct smb_filename *smb_fname,
474 : mode_t mode)
475 : {
476 0 : struct smb_filename *full_fname = NULL;
477 : int result;
478 :
479 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
480 : dirfsp,
481 : smb_fname);
482 0 : if (full_fname == NULL) {
483 0 : return -1;
484 : }
485 :
486 0 : DBG_DEBUG("[CEPH] mkdir(%p, %s)\n",
487 : handle, smb_fname_str_dbg(full_fname));
488 :
489 0 : result = ceph_mkdir(handle->data, full_fname->base_name, mode);
490 :
491 0 : TALLOC_FREE(full_fname);
492 :
493 0 : return WRAP_RETURN(result);
494 : }
495 :
496 0 : static int cephwrap_closedir(struct vfs_handle_struct *handle, DIR *dirp)
497 : {
498 : int result;
499 :
500 0 : DBG_DEBUG("[CEPH] closedir(%p, %p)\n", handle, dirp);
501 0 : result = ceph_closedir(handle->data, (struct ceph_dir_result *) dirp);
502 0 : DBG_DEBUG("[CEPH] closedir(...) = %d\n", result);
503 0 : WRAP_RETURN(result);
504 : }
505 :
506 : /* File operations */
507 :
508 0 : static int cephwrap_openat(struct vfs_handle_struct *handle,
509 : const struct files_struct *dirfsp,
510 : const struct smb_filename *smb_fname,
511 : files_struct *fsp,
512 : const struct vfs_open_how *how)
513 : {
514 0 : int flags = how->flags;
515 0 : mode_t mode = how->mode;
516 0 : struct smb_filename *name = NULL;
517 0 : bool have_opath = false;
518 0 : bool became_root = false;
519 0 : int result = -ENOENT;
520 :
521 0 : if (how->resolve != 0) {
522 0 : errno = ENOSYS;
523 0 : return -1;
524 : }
525 :
526 : /*
527 : * ceph doesn't have openat().
528 : */
529 0 : if (fsp_get_pathref_fd(dirfsp) != AT_FDCWD) {
530 0 : name = full_path_from_dirfsp_atname(talloc_tos(),
531 : dirfsp,
532 : smb_fname);
533 0 : if (name == NULL) {
534 0 : return -1;
535 : }
536 0 : smb_fname = name;
537 : }
538 :
539 0 : DBG_DEBUG("[CEPH] openat(%p, %s, %p, %d, %d)\n", handle,
540 : smb_fname_str_dbg(smb_fname), fsp, flags, mode);
541 :
542 0 : if (smb_fname->stream_name) {
543 0 : goto out;
544 : }
545 :
546 : #ifdef O_PATH
547 0 : have_opath = true;
548 0 : if (fsp->fsp_flags.is_pathref) {
549 0 : flags |= O_PATH;
550 : }
551 : #endif
552 :
553 0 : if (fsp->fsp_flags.is_pathref && !have_opath) {
554 0 : become_root();
555 0 : became_root = true;
556 : }
557 :
558 0 : result = ceph_open(handle->data, smb_fname->base_name, flags, mode);
559 :
560 0 : if (became_root) {
561 0 : unbecome_root();
562 : }
563 :
564 0 : out:
565 0 : TALLOC_FREE(name);
566 0 : fsp->fsp_flags.have_proc_fds = false;
567 0 : DBG_DEBUG("[CEPH] open(...) = %d\n", result);
568 0 : WRAP_RETURN(result);
569 : }
570 :
571 0 : static int cephwrap_close(struct vfs_handle_struct *handle, files_struct *fsp)
572 : {
573 : int result;
574 :
575 0 : DBG_DEBUG("[CEPH] close(%p, %p)\n", handle, fsp);
576 0 : result = ceph_close(handle->data, fsp_get_pathref_fd(fsp));
577 0 : DBG_DEBUG("[CEPH] close(...) = %d\n", result);
578 :
579 0 : WRAP_RETURN(result);
580 : }
581 :
582 0 : static ssize_t cephwrap_pread(struct vfs_handle_struct *handle, files_struct *fsp, void *data,
583 : size_t n, off_t offset)
584 : {
585 : ssize_t result;
586 :
587 0 : DBG_DEBUG("[CEPH] pread(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
588 :
589 0 : result = ceph_read(handle->data, fsp_get_io_fd(fsp), data, n, offset);
590 0 : DBG_DEBUG("[CEPH] pread(...) = %llu\n", llu(result));
591 0 : WRAP_RETURN(result);
592 : }
593 :
594 : struct cephwrap_pread_state {
595 : ssize_t bytes_read;
596 : struct vfs_aio_state vfs_aio_state;
597 : };
598 :
599 : /*
600 : * Fake up an async ceph read by calling the synchronous API.
601 : */
602 0 : static struct tevent_req *cephwrap_pread_send(struct vfs_handle_struct *handle,
603 : TALLOC_CTX *mem_ctx,
604 : struct tevent_context *ev,
605 : struct files_struct *fsp,
606 : void *data,
607 : size_t n, off_t offset)
608 : {
609 0 : struct tevent_req *req = NULL;
610 0 : struct cephwrap_pread_state *state = NULL;
611 0 : int ret = -1;
612 :
613 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
614 0 : req = tevent_req_create(mem_ctx, &state, struct cephwrap_pread_state);
615 0 : if (req == NULL) {
616 0 : return NULL;
617 : }
618 :
619 0 : ret = ceph_read(handle->data, fsp_get_io_fd(fsp), data, n, offset);
620 0 : if (ret < 0) {
621 : /* ceph returns -errno on error. */
622 0 : tevent_req_error(req, -ret);
623 0 : return tevent_req_post(req, ev);
624 : }
625 :
626 0 : state->bytes_read = ret;
627 0 : tevent_req_done(req);
628 : /* Return and schedule the completion of the call. */
629 0 : return tevent_req_post(req, ev);
630 : }
631 :
632 0 : static ssize_t cephwrap_pread_recv(struct tevent_req *req,
633 : struct vfs_aio_state *vfs_aio_state)
634 : {
635 : struct cephwrap_pread_state *state =
636 0 : tevent_req_data(req, struct cephwrap_pread_state);
637 :
638 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
639 0 : if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
640 0 : return -1;
641 : }
642 0 : *vfs_aio_state = state->vfs_aio_state;
643 0 : return state->bytes_read;
644 : }
645 :
646 0 : static ssize_t cephwrap_pwrite(struct vfs_handle_struct *handle, files_struct *fsp, const void *data,
647 : size_t n, off_t offset)
648 : {
649 : ssize_t result;
650 :
651 0 : DBG_DEBUG("[CEPH] pwrite(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
652 0 : result = ceph_write(handle->data, fsp_get_io_fd(fsp), data, n, offset);
653 0 : DBG_DEBUG("[CEPH] pwrite(...) = %llu\n", llu(result));
654 0 : WRAP_RETURN(result);
655 : }
656 :
657 : struct cephwrap_pwrite_state {
658 : ssize_t bytes_written;
659 : struct vfs_aio_state vfs_aio_state;
660 : };
661 :
662 : /*
663 : * Fake up an async ceph write by calling the synchronous API.
664 : */
665 0 : static struct tevent_req *cephwrap_pwrite_send(struct vfs_handle_struct *handle,
666 : TALLOC_CTX *mem_ctx,
667 : struct tevent_context *ev,
668 : struct files_struct *fsp,
669 : const void *data,
670 : size_t n, off_t offset)
671 : {
672 0 : struct tevent_req *req = NULL;
673 0 : struct cephwrap_pwrite_state *state = NULL;
674 0 : int ret = -1;
675 :
676 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
677 0 : req = tevent_req_create(mem_ctx, &state, struct cephwrap_pwrite_state);
678 0 : if (req == NULL) {
679 0 : return NULL;
680 : }
681 :
682 0 : ret = ceph_write(handle->data, fsp_get_io_fd(fsp), data, n, offset);
683 0 : if (ret < 0) {
684 : /* ceph returns -errno on error. */
685 0 : tevent_req_error(req, -ret);
686 0 : return tevent_req_post(req, ev);
687 : }
688 :
689 0 : state->bytes_written = ret;
690 0 : tevent_req_done(req);
691 : /* Return and schedule the completion of the call. */
692 0 : return tevent_req_post(req, ev);
693 : }
694 :
695 0 : static ssize_t cephwrap_pwrite_recv(struct tevent_req *req,
696 : struct vfs_aio_state *vfs_aio_state)
697 : {
698 : struct cephwrap_pwrite_state *state =
699 0 : tevent_req_data(req, struct cephwrap_pwrite_state);
700 :
701 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
702 0 : if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
703 0 : return -1;
704 : }
705 0 : *vfs_aio_state = state->vfs_aio_state;
706 0 : return state->bytes_written;
707 : }
708 :
709 0 : static off_t cephwrap_lseek(struct vfs_handle_struct *handle, files_struct *fsp, off_t offset, int whence)
710 : {
711 0 : off_t result = 0;
712 :
713 0 : DBG_DEBUG("[CEPH] cephwrap_lseek\n");
714 0 : result = ceph_lseek(handle->data, fsp_get_io_fd(fsp), offset, whence);
715 0 : WRAP_RETURN(result);
716 : }
717 :
718 0 : static ssize_t cephwrap_sendfile(struct vfs_handle_struct *handle, int tofd, files_struct *fromfsp, const DATA_BLOB *hdr,
719 : off_t offset, size_t n)
720 : {
721 : /*
722 : * We cannot support sendfile because libcephfs is in user space.
723 : */
724 0 : DBG_DEBUG("[CEPH] cephwrap_sendfile\n");
725 0 : errno = ENOTSUP;
726 0 : return -1;
727 : }
728 :
729 0 : static ssize_t cephwrap_recvfile(struct vfs_handle_struct *handle,
730 : int fromfd,
731 : files_struct *tofsp,
732 : off_t offset,
733 : size_t n)
734 : {
735 : /*
736 : * We cannot support recvfile because libcephfs is in user space.
737 : */
738 0 : DBG_DEBUG("[CEPH] cephwrap_recvfile\n");
739 0 : errno=ENOTSUP;
740 0 : return -1;
741 : }
742 :
743 0 : static int cephwrap_renameat(struct vfs_handle_struct *handle,
744 : files_struct *srcfsp,
745 : const struct smb_filename *smb_fname_src,
746 : files_struct *dstfsp,
747 : const struct smb_filename *smb_fname_dst)
748 : {
749 0 : struct smb_filename *full_fname_src = NULL;
750 0 : struct smb_filename *full_fname_dst = NULL;
751 0 : int result = -1;
752 :
753 0 : DBG_DEBUG("[CEPH] cephwrap_renameat\n");
754 0 : if (smb_fname_src->stream_name || smb_fname_dst->stream_name) {
755 0 : errno = ENOENT;
756 0 : return result;
757 : }
758 :
759 0 : full_fname_src = full_path_from_dirfsp_atname(talloc_tos(),
760 : srcfsp,
761 : smb_fname_src);
762 0 : if (full_fname_src == NULL) {
763 0 : errno = ENOMEM;
764 0 : return -1;
765 : }
766 0 : full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(),
767 : dstfsp,
768 : smb_fname_dst);
769 0 : if (full_fname_dst == NULL) {
770 0 : TALLOC_FREE(full_fname_src);
771 0 : errno = ENOMEM;
772 0 : return -1;
773 : }
774 :
775 0 : result = ceph_rename(handle->data,
776 0 : full_fname_src->base_name,
777 0 : full_fname_dst->base_name);
778 :
779 0 : TALLOC_FREE(full_fname_src);
780 0 : TALLOC_FREE(full_fname_dst);
781 :
782 0 : WRAP_RETURN(result);
783 : }
784 :
785 : /*
786 : * Fake up an async ceph fsync by calling the synchronous API.
787 : */
788 :
789 0 : static struct tevent_req *cephwrap_fsync_send(struct vfs_handle_struct *handle,
790 : TALLOC_CTX *mem_ctx,
791 : struct tevent_context *ev,
792 : files_struct *fsp)
793 : {
794 0 : struct tevent_req *req = NULL;
795 0 : struct vfs_aio_state *state = NULL;
796 0 : int ret = -1;
797 :
798 0 : DBG_DEBUG("[CEPH] cephwrap_fsync_send\n");
799 :
800 0 : req = tevent_req_create(mem_ctx, &state, struct vfs_aio_state);
801 0 : if (req == NULL) {
802 0 : return NULL;
803 : }
804 :
805 : /* Make sync call. */
806 0 : ret = ceph_fsync(handle->data, fsp_get_io_fd(fsp), false);
807 :
808 0 : if (ret != 0) {
809 : /* ceph_fsync returns -errno on error. */
810 0 : tevent_req_error(req, -ret);
811 0 : return tevent_req_post(req, ev);
812 : }
813 :
814 : /* Mark it as done. */
815 0 : tevent_req_done(req);
816 : /* Return and schedule the completion of the call. */
817 0 : return tevent_req_post(req, ev);
818 : }
819 :
820 0 : static int cephwrap_fsync_recv(struct tevent_req *req,
821 : struct vfs_aio_state *vfs_aio_state)
822 : {
823 : struct vfs_aio_state *state =
824 0 : tevent_req_data(req, struct vfs_aio_state);
825 :
826 0 : DBG_DEBUG("[CEPH] cephwrap_fsync_recv\n");
827 :
828 0 : if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
829 0 : return -1;
830 : }
831 0 : *vfs_aio_state = *state;
832 0 : return 0;
833 : }
834 :
835 : #define SAMBA_STATX_ATTR_MASK (CEPH_STATX_BASIC_STATS|CEPH_STATX_BTIME)
836 :
837 0 : static void init_stat_ex_from_ceph_statx(struct stat_ex *dst, const struct ceph_statx *stx)
838 : {
839 0 : DBG_DEBUG("[CEPH]\tstx = {dev = %llx, ino = %llu, mode = 0x%x, "
840 : "nlink = %llu, uid = %d, gid = %d, rdev = %llx, size = %llu, "
841 : "blksize = %llu, blocks = %llu, atime = %llu, mtime = %llu, "
842 : "ctime = %llu, btime = %llu}\n",
843 : llu(stx->stx_dev), llu(stx->stx_ino), stx->stx_mode,
844 : llu(stx->stx_nlink), stx->stx_uid, stx->stx_gid,
845 : llu(stx->stx_rdev), llu(stx->stx_size), llu(stx->stx_blksize),
846 : llu(stx->stx_blocks), llu(stx->stx_atime.tv_sec),
847 : llu(stx->stx_mtime.tv_sec), llu(stx->stx_ctime.tv_sec),
848 : llu(stx->stx_btime.tv_sec));
849 :
850 0 : if ((stx->stx_mask & SAMBA_STATX_ATTR_MASK) != SAMBA_STATX_ATTR_MASK) {
851 0 : DBG_WARNING("%s: stx->stx_mask is incorrect (wanted %x, got %x)\n",
852 : __func__, SAMBA_STATX_ATTR_MASK, stx->stx_mask);
853 : }
854 :
855 0 : dst->st_ex_dev = stx->stx_dev;
856 0 : dst->st_ex_rdev = stx->stx_rdev;
857 0 : dst->st_ex_ino = stx->stx_ino;
858 0 : dst->st_ex_mode = stx->stx_mode;
859 0 : dst->st_ex_uid = stx->stx_uid;
860 0 : dst->st_ex_gid = stx->stx_gid;
861 0 : dst->st_ex_size = stx->stx_size;
862 0 : dst->st_ex_nlink = stx->stx_nlink;
863 0 : dst->st_ex_atime = stx->stx_atime;
864 0 : dst->st_ex_btime = stx->stx_btime;
865 0 : dst->st_ex_ctime = stx->stx_ctime;
866 0 : dst->st_ex_mtime = stx->stx_mtime;
867 0 : dst->st_ex_blksize = stx->stx_blksize;
868 0 : dst->st_ex_blocks = stx->stx_blocks;
869 0 : }
870 :
871 0 : static int cephwrap_stat(struct vfs_handle_struct *handle,
872 : struct smb_filename *smb_fname)
873 : {
874 0 : int result = -1;
875 0 : struct ceph_statx stx = { 0 };
876 :
877 0 : DBG_DEBUG("[CEPH] stat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
878 :
879 0 : if (smb_fname->stream_name) {
880 0 : errno = ENOENT;
881 0 : return result;
882 : }
883 :
884 0 : result = ceph_statx(handle->data, smb_fname->base_name, &stx,
885 : SAMBA_STATX_ATTR_MASK, 0);
886 0 : DBG_DEBUG("[CEPH] statx(...) = %d\n", result);
887 0 : if (result < 0) {
888 0 : WRAP_RETURN(result);
889 : }
890 :
891 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
892 0 : DBG_DEBUG("[CEPH] mode = 0x%x\n", smb_fname->st.st_ex_mode);
893 0 : return result;
894 : }
895 :
896 0 : static int cephwrap_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
897 : {
898 0 : int result = -1;
899 0 : struct ceph_statx stx = { 0 };
900 0 : int fd = fsp_get_pathref_fd(fsp);
901 :
902 0 : DBG_DEBUG("[CEPH] fstat(%p, %d)\n", handle, fd);
903 0 : result = ceph_fstatx(handle->data, fd, &stx,
904 : SAMBA_STATX_ATTR_MASK, 0);
905 0 : DBG_DEBUG("[CEPH] fstat(...) = %d\n", result);
906 0 : if (result < 0) {
907 0 : WRAP_RETURN(result);
908 : }
909 :
910 0 : init_stat_ex_from_ceph_statx(sbuf, &stx);
911 0 : DBG_DEBUG("[CEPH] mode = 0x%x\n", sbuf->st_ex_mode);
912 0 : return result;
913 : }
914 :
915 0 : static int cephwrap_lstat(struct vfs_handle_struct *handle,
916 : struct smb_filename *smb_fname)
917 : {
918 0 : int result = -1;
919 0 : struct ceph_statx stx = { 0 };
920 :
921 0 : DBG_DEBUG("[CEPH] lstat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
922 :
923 0 : if (smb_fname->stream_name) {
924 0 : errno = ENOENT;
925 0 : return result;
926 : }
927 :
928 0 : result = ceph_statx(handle->data, smb_fname->base_name, &stx,
929 : SAMBA_STATX_ATTR_MASK, AT_SYMLINK_NOFOLLOW);
930 0 : DBG_DEBUG("[CEPH] lstat(...) = %d\n", result);
931 0 : if (result < 0) {
932 0 : WRAP_RETURN(result);
933 : }
934 :
935 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
936 0 : return result;
937 : }
938 :
939 0 : static int cephwrap_fntimes(struct vfs_handle_struct *handle,
940 : files_struct *fsp,
941 : struct smb_file_time *ft)
942 : {
943 0 : struct ceph_statx stx = { 0 };
944 : int result;
945 0 : int mask = 0;
946 :
947 0 : if (!is_omit_timespec(&ft->atime)) {
948 0 : stx.stx_atime = ft->atime;
949 0 : mask |= CEPH_SETATTR_ATIME;
950 : }
951 0 : if (!is_omit_timespec(&ft->mtime)) {
952 0 : stx.stx_mtime = ft->mtime;
953 0 : mask |= CEPH_SETATTR_MTIME;
954 : }
955 0 : if (!is_omit_timespec(&ft->create_time)) {
956 0 : stx.stx_btime = ft->create_time;
957 0 : mask |= CEPH_SETATTR_BTIME;
958 : }
959 :
960 0 : if (!mask) {
961 0 : return 0;
962 : }
963 :
964 0 : if (!fsp->fsp_flags.is_pathref) {
965 : /*
966 : * We can use an io_fd to set xattrs.
967 : */
968 0 : result = ceph_fsetattrx(handle->data,
969 : fsp_get_io_fd(fsp),
970 : &stx,
971 : mask);
972 : } else {
973 : /*
974 : * This is no longer a handle based call.
975 : */
976 0 : result = ceph_setattrx(handle->data,
977 0 : fsp->fsp_name->base_name,
978 : &stx,
979 : mask,
980 : 0);
981 : }
982 :
983 0 : DBG_DEBUG("[CEPH] ntimes(%p, %s, {%ld, %ld, %ld, %ld}) = %d\n",
984 : handle, fsp_str_dbg(fsp), ft->mtime.tv_sec, ft->atime.tv_sec,
985 : ft->ctime.tv_sec, ft->create_time.tv_sec, result);
986 :
987 0 : return result;
988 : }
989 :
990 0 : static int cephwrap_unlinkat(struct vfs_handle_struct *handle,
991 : struct files_struct *dirfsp,
992 : const struct smb_filename *smb_fname,
993 : int flags)
994 : {
995 0 : struct smb_filename *full_fname = NULL;
996 0 : int result = -1;
997 :
998 0 : DBG_DEBUG("[CEPH] unlink(%p, %s)\n",
999 : handle,
1000 : smb_fname_str_dbg(smb_fname));
1001 :
1002 0 : if (smb_fname->stream_name) {
1003 0 : errno = ENOENT;
1004 0 : return result;
1005 : }
1006 :
1007 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1008 : dirfsp,
1009 : smb_fname);
1010 0 : if (full_fname == NULL) {
1011 0 : return -1;
1012 : }
1013 :
1014 0 : if (flags & AT_REMOVEDIR) {
1015 0 : result = ceph_rmdir(handle->data, full_fname->base_name);
1016 : } else {
1017 0 : result = ceph_unlink(handle->data, full_fname->base_name);
1018 : }
1019 0 : TALLOC_FREE(full_fname);
1020 0 : DBG_DEBUG("[CEPH] unlink(...) = %d\n", result);
1021 0 : WRAP_RETURN(result);
1022 : }
1023 :
1024 0 : static int cephwrap_fchmod(struct vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
1025 : {
1026 : int result;
1027 :
1028 0 : DBG_DEBUG("[CEPH] fchmod(%p, %p, %d)\n", handle, fsp, mode);
1029 0 : if (!fsp->fsp_flags.is_pathref) {
1030 : /*
1031 : * We can use an io_fd to change permissions.
1032 : */
1033 0 : result = ceph_fchmod(handle->data, fsp_get_io_fd(fsp), mode);
1034 : } else {
1035 : /*
1036 : * This is no longer a handle based call.
1037 : */
1038 0 : result = ceph_chmod(handle->data,
1039 0 : fsp->fsp_name->base_name,
1040 : mode);
1041 : }
1042 0 : DBG_DEBUG("[CEPH] fchmod(...) = %d\n", result);
1043 0 : WRAP_RETURN(result);
1044 : }
1045 :
1046 0 : static int cephwrap_fchown(struct vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
1047 : {
1048 : int result;
1049 :
1050 0 : DBG_DEBUG("[CEPH] fchown(%p, %p, %d, %d)\n", handle, fsp, uid, gid);
1051 0 : if (!fsp->fsp_flags.is_pathref) {
1052 : /*
1053 : * We can use an io_fd to change ownership.
1054 : */
1055 0 : result = ceph_fchown(handle->data,
1056 : fsp_get_io_fd(fsp),
1057 : uid,
1058 : gid);
1059 : } else {
1060 : /*
1061 : * This is no longer a handle based call.
1062 : */
1063 0 : result = ceph_chown(handle->data,
1064 0 : fsp->fsp_name->base_name,
1065 : uid,
1066 : gid);
1067 : }
1068 :
1069 0 : DBG_DEBUG("[CEPH] fchown(...) = %d\n", result);
1070 0 : WRAP_RETURN(result);
1071 : }
1072 :
1073 0 : static int cephwrap_lchown(struct vfs_handle_struct *handle,
1074 : const struct smb_filename *smb_fname,
1075 : uid_t uid,
1076 : gid_t gid)
1077 : {
1078 : int result;
1079 0 : DBG_DEBUG("[CEPH] lchown(%p, %s, %d, %d)\n", handle, smb_fname->base_name, uid, gid);
1080 0 : result = ceph_lchown(handle->data, smb_fname->base_name, uid, gid);
1081 0 : DBG_DEBUG("[CEPH] lchown(...) = %d\n", result);
1082 0 : WRAP_RETURN(result);
1083 : }
1084 :
1085 0 : static int cephwrap_chdir(struct vfs_handle_struct *handle,
1086 : const struct smb_filename *smb_fname)
1087 : {
1088 0 : int result = -1;
1089 0 : DBG_DEBUG("[CEPH] chdir(%p, %s)\n", handle, smb_fname->base_name);
1090 0 : result = ceph_chdir(handle->data, smb_fname->base_name);
1091 0 : DBG_DEBUG("[CEPH] chdir(...) = %d\n", result);
1092 0 : WRAP_RETURN(result);
1093 : }
1094 :
1095 0 : static struct smb_filename *cephwrap_getwd(struct vfs_handle_struct *handle,
1096 : TALLOC_CTX *ctx)
1097 : {
1098 0 : const char *cwd = ceph_getcwd(handle->data);
1099 0 : DBG_DEBUG("[CEPH] getwd(%p) = %s\n", handle, cwd);
1100 0 : return synthetic_smb_fname(ctx,
1101 : cwd,
1102 : NULL,
1103 : NULL,
1104 : 0,
1105 : 0);
1106 : }
1107 :
1108 0 : static int strict_allocate_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
1109 : {
1110 : off_t space_to_write;
1111 : int result;
1112 : NTSTATUS status;
1113 : SMB_STRUCT_STAT *pst;
1114 :
1115 0 : status = vfs_stat_fsp(fsp);
1116 0 : if (!NT_STATUS_IS_OK(status)) {
1117 0 : return -1;
1118 : }
1119 0 : pst = &fsp->fsp_name->st;
1120 :
1121 : #ifdef S_ISFIFO
1122 0 : if (S_ISFIFO(pst->st_ex_mode))
1123 0 : return 0;
1124 : #endif
1125 :
1126 0 : if (pst->st_ex_size == len)
1127 0 : return 0;
1128 :
1129 : /* Shrink - just ftruncate. */
1130 0 : if (pst->st_ex_size > len) {
1131 0 : result = ceph_ftruncate(handle->data, fsp_get_io_fd(fsp), len);
1132 0 : WRAP_RETURN(result);
1133 : }
1134 :
1135 0 : space_to_write = len - pst->st_ex_size;
1136 0 : result = ceph_fallocate(handle->data, fsp_get_io_fd(fsp), 0, pst->st_ex_size,
1137 : space_to_write);
1138 0 : WRAP_RETURN(result);
1139 : }
1140 :
1141 0 : static int cephwrap_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
1142 : {
1143 0 : int result = -1;
1144 :
1145 0 : DBG_DEBUG("[CEPH] ftruncate(%p, %p, %llu\n", handle, fsp, llu(len));
1146 :
1147 0 : if (lp_strict_allocate(SNUM(fsp->conn))) {
1148 0 : return strict_allocate_ftruncate(handle, fsp, len);
1149 : }
1150 :
1151 0 : result = ceph_ftruncate(handle->data, fsp_get_io_fd(fsp), len);
1152 0 : WRAP_RETURN(result);
1153 : }
1154 :
1155 0 : static int cephwrap_fallocate(struct vfs_handle_struct *handle,
1156 : struct files_struct *fsp,
1157 : uint32_t mode,
1158 : off_t offset,
1159 : off_t len)
1160 : {
1161 : int result;
1162 :
1163 0 : DBG_DEBUG("[CEPH] fallocate(%p, %p, %u, %llu, %llu\n",
1164 : handle, fsp, mode, llu(offset), llu(len));
1165 : /* unsupported mode flags are rejected by libcephfs */
1166 0 : result = ceph_fallocate(handle->data, fsp_get_io_fd(fsp), mode, offset, len);
1167 0 : DBG_DEBUG("[CEPH] fallocate(...) = %d\n", result);
1168 0 : WRAP_RETURN(result);
1169 : }
1170 :
1171 0 : static bool cephwrap_lock(struct vfs_handle_struct *handle, files_struct *fsp, int op, off_t offset, off_t count, int type)
1172 : {
1173 0 : DBG_DEBUG("[CEPH] lock\n");
1174 0 : return true;
1175 : }
1176 :
1177 0 : static int cephwrap_filesystem_sharemode(struct vfs_handle_struct *handle,
1178 : files_struct *fsp,
1179 : uint32_t share_access,
1180 : uint32_t access_mask)
1181 : {
1182 0 : DBG_ERR("[CEPH] filesystem sharemodes unsupported! Consider setting "
1183 : "\"kernel share modes = no\"\n");
1184 :
1185 0 : errno = ENOSYS;
1186 0 : return -1;
1187 : }
1188 :
1189 0 : static int cephwrap_fcntl(vfs_handle_struct *handle,
1190 : files_struct *fsp, int cmd, va_list cmd_arg)
1191 : {
1192 : /*
1193 : * SMB_VFS_FCNTL() is currently only called by vfs_set_blocking() to
1194 : * clear O_NONBLOCK, etc for LOCK_MAND and FIFOs. Ignore it.
1195 : */
1196 0 : if (cmd == F_GETFL) {
1197 0 : return 0;
1198 0 : } else if (cmd == F_SETFL) {
1199 : va_list dup_cmd_arg;
1200 : int opt;
1201 :
1202 0 : va_copy(dup_cmd_arg, cmd_arg);
1203 0 : opt = va_arg(dup_cmd_arg, int);
1204 0 : va_end(dup_cmd_arg);
1205 0 : if (opt == 0) {
1206 0 : return 0;
1207 : }
1208 0 : DBG_ERR("unexpected fcntl SETFL(%d)\n", opt);
1209 0 : goto err_out;
1210 : }
1211 0 : DBG_ERR("unexpected fcntl: %d\n", cmd);
1212 0 : err_out:
1213 0 : errno = EINVAL;
1214 0 : return -1;
1215 : }
1216 :
1217 0 : static bool cephwrap_getlock(struct vfs_handle_struct *handle, files_struct *fsp, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid)
1218 : {
1219 0 : DBG_DEBUG("[CEPH] getlock returning false and errno=0\n");
1220 :
1221 0 : errno = 0;
1222 0 : return false;
1223 : }
1224 :
1225 : /*
1226 : * We cannot let this fall through to the default, because the file might only
1227 : * be accessible from libcephfs (which is a user-space client) but the fd might
1228 : * be for some file the kernel knows about.
1229 : */
1230 0 : static int cephwrap_linux_setlease(struct vfs_handle_struct *handle, files_struct *fsp,
1231 : int leasetype)
1232 : {
1233 0 : int result = -1;
1234 :
1235 0 : DBG_DEBUG("[CEPH] linux_setlease\n");
1236 0 : errno = ENOSYS;
1237 0 : return result;
1238 : }
1239 :
1240 0 : static int cephwrap_symlinkat(struct vfs_handle_struct *handle,
1241 : const struct smb_filename *link_target,
1242 : struct files_struct *dirfsp,
1243 : const struct smb_filename *new_smb_fname)
1244 : {
1245 0 : struct smb_filename *full_fname = NULL;
1246 0 : int result = -1;
1247 :
1248 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1249 : dirfsp,
1250 : new_smb_fname);
1251 0 : if (full_fname == NULL) {
1252 0 : return -1;
1253 : }
1254 :
1255 0 : DBG_DEBUG("[CEPH] symlink(%p, %s, %s)\n", handle,
1256 : link_target->base_name,
1257 : full_fname->base_name);
1258 :
1259 0 : result = ceph_symlink(handle->data,
1260 0 : link_target->base_name,
1261 0 : full_fname->base_name);
1262 0 : TALLOC_FREE(full_fname);
1263 0 : DBG_DEBUG("[CEPH] symlink(...) = %d\n", result);
1264 0 : WRAP_RETURN(result);
1265 : }
1266 :
1267 0 : static int cephwrap_readlinkat(struct vfs_handle_struct *handle,
1268 : const struct files_struct *dirfsp,
1269 : const struct smb_filename *smb_fname,
1270 : char *buf,
1271 : size_t bufsiz)
1272 : {
1273 0 : struct smb_filename *full_fname = NULL;
1274 0 : int result = -1;
1275 :
1276 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1277 : dirfsp,
1278 : smb_fname);
1279 0 : if (full_fname == NULL) {
1280 0 : return -1;
1281 : }
1282 :
1283 0 : DBG_DEBUG("[CEPH] readlink(%p, %s, %p, %llu)\n", handle,
1284 : full_fname->base_name, buf, llu(bufsiz));
1285 :
1286 0 : result = ceph_readlink(handle->data, full_fname->base_name, buf, bufsiz);
1287 0 : TALLOC_FREE(full_fname);
1288 0 : DBG_DEBUG("[CEPH] readlink(...) = %d\n", result);
1289 0 : WRAP_RETURN(result);
1290 : }
1291 :
1292 0 : static int cephwrap_linkat(struct vfs_handle_struct *handle,
1293 : files_struct *srcfsp,
1294 : const struct smb_filename *old_smb_fname,
1295 : files_struct *dstfsp,
1296 : const struct smb_filename *new_smb_fname,
1297 : int flags)
1298 : {
1299 0 : struct smb_filename *full_fname_old = NULL;
1300 0 : struct smb_filename *full_fname_new = NULL;
1301 0 : int result = -1;
1302 :
1303 0 : full_fname_old = full_path_from_dirfsp_atname(talloc_tos(),
1304 : srcfsp,
1305 : old_smb_fname);
1306 0 : if (full_fname_old == NULL) {
1307 0 : return -1;
1308 : }
1309 0 : full_fname_new = full_path_from_dirfsp_atname(talloc_tos(),
1310 : dstfsp,
1311 : new_smb_fname);
1312 0 : if (full_fname_new == NULL) {
1313 0 : TALLOC_FREE(full_fname_old);
1314 0 : return -1;
1315 : }
1316 :
1317 0 : DBG_DEBUG("[CEPH] link(%p, %s, %s)\n", handle,
1318 : full_fname_old->base_name,
1319 : full_fname_new->base_name);
1320 :
1321 0 : result = ceph_link(handle->data,
1322 0 : full_fname_old->base_name,
1323 0 : full_fname_new->base_name);
1324 0 : DBG_DEBUG("[CEPH] link(...) = %d\n", result);
1325 0 : TALLOC_FREE(full_fname_old);
1326 0 : TALLOC_FREE(full_fname_new);
1327 0 : WRAP_RETURN(result);
1328 : }
1329 :
1330 0 : static int cephwrap_mknodat(struct vfs_handle_struct *handle,
1331 : files_struct *dirfsp,
1332 : const struct smb_filename *smb_fname,
1333 : mode_t mode,
1334 : SMB_DEV_T dev)
1335 : {
1336 0 : struct smb_filename *full_fname = NULL;
1337 0 : int result = -1;
1338 :
1339 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1340 : dirfsp,
1341 : smb_fname);
1342 0 : if (full_fname == NULL) {
1343 0 : return -1;
1344 : }
1345 :
1346 0 : DBG_DEBUG("[CEPH] mknodat(%p, %s)\n", handle, full_fname->base_name);
1347 0 : result = ceph_mknod(handle->data, full_fname->base_name, mode, dev);
1348 0 : DBG_DEBUG("[CEPH] mknodat(...) = %d\n", result);
1349 :
1350 0 : TALLOC_FREE(full_fname);
1351 :
1352 0 : WRAP_RETURN(result);
1353 : }
1354 :
1355 : /*
1356 : * This is a simple version of real-path ... a better version is needed to
1357 : * ask libcephfs about symbolic links.
1358 : */
1359 0 : static struct smb_filename *cephwrap_realpath(struct vfs_handle_struct *handle,
1360 : TALLOC_CTX *ctx,
1361 : const struct smb_filename *smb_fname)
1362 : {
1363 0 : char *result = NULL;
1364 0 : const char *path = smb_fname->base_name;
1365 0 : size_t len = strlen(path);
1366 0 : struct smb_filename *result_fname = NULL;
1367 0 : int r = -1;
1368 :
1369 0 : if (len && (path[0] == '/')) {
1370 0 : r = asprintf(&result, "%s", path);
1371 0 : } else if ((len >= 2) && (path[0] == '.') && (path[1] == '/')) {
1372 0 : if (len == 2) {
1373 0 : r = asprintf(&result, "%s",
1374 0 : handle->conn->cwd_fsp->fsp_name->base_name);
1375 : } else {
1376 0 : r = asprintf(&result, "%s/%s",
1377 0 : handle->conn->cwd_fsp->fsp_name->base_name, &path[2]);
1378 : }
1379 : } else {
1380 0 : r = asprintf(&result, "%s/%s",
1381 0 : handle->conn->cwd_fsp->fsp_name->base_name, path);
1382 : }
1383 :
1384 0 : if (r < 0) {
1385 0 : return NULL;
1386 : }
1387 :
1388 0 : DBG_DEBUG("[CEPH] realpath(%p, %s) = %s\n", handle, path, result);
1389 0 : result_fname = synthetic_smb_fname(ctx,
1390 : result,
1391 : NULL,
1392 : NULL,
1393 : 0,
1394 : 0);
1395 0 : SAFE_FREE(result);
1396 0 : return result_fname;
1397 : }
1398 :
1399 :
1400 0 : static int cephwrap_fchflags(struct vfs_handle_struct *handle,
1401 : struct files_struct *fsp,
1402 : unsigned int flags)
1403 : {
1404 0 : errno = ENOSYS;
1405 0 : return -1;
1406 : }
1407 :
1408 0 : static NTSTATUS cephwrap_get_real_filename_at(
1409 : struct vfs_handle_struct *handle,
1410 : struct files_struct *dirfsp,
1411 : const char *name,
1412 : TALLOC_CTX *mem_ctx,
1413 : char **found_name)
1414 : {
1415 : /*
1416 : * Don't fall back to get_real_filename so callers can differentiate
1417 : * between a full directory scan and an actual case-insensitive stat.
1418 : */
1419 0 : return NT_STATUS_NOT_SUPPORTED;
1420 : }
1421 :
1422 0 : static const char *cephwrap_connectpath(
1423 : struct vfs_handle_struct *handle,
1424 : const struct files_struct *dirfsp,
1425 : const struct smb_filename *smb_fname)
1426 : {
1427 0 : return handle->conn->connectpath;
1428 : }
1429 :
1430 : /****************************************************************
1431 : Extended attribute operations.
1432 : *****************************************************************/
1433 :
1434 0 : static ssize_t cephwrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size)
1435 : {
1436 : int ret;
1437 0 : DBG_DEBUG("[CEPH] fgetxattr(%p, %p, %s, %p, %llu)\n", handle, fsp, name, value, llu(size));
1438 0 : ret = ceph_fgetxattr(handle->data, fsp_get_io_fd(fsp), name, value, size);
1439 0 : DBG_DEBUG("[CEPH] fgetxattr(...) = %d\n", ret);
1440 0 : if (ret < 0) {
1441 0 : WRAP_RETURN(ret);
1442 : }
1443 0 : return (ssize_t)ret;
1444 : }
1445 :
1446 0 : static ssize_t cephwrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, char *list, size_t size)
1447 : {
1448 : int ret;
1449 0 : DBG_DEBUG("[CEPH] flistxattr(%p, %p, %p, %llu)\n",
1450 : handle, fsp, list, llu(size));
1451 0 : if (!fsp->fsp_flags.is_pathref) {
1452 : /*
1453 : * We can use an io_fd to list xattrs.
1454 : */
1455 0 : ret = ceph_flistxattr(handle->data,
1456 : fsp_get_io_fd(fsp),
1457 : list,
1458 : size);
1459 : } else {
1460 : /*
1461 : * This is no longer a handle based call.
1462 : */
1463 0 : ret = ceph_listxattr(handle->data,
1464 0 : fsp->fsp_name->base_name,
1465 : list,
1466 : size);
1467 : }
1468 0 : DBG_DEBUG("[CEPH] flistxattr(...) = %d\n", ret);
1469 0 : if (ret < 0) {
1470 0 : WRAP_RETURN(ret);
1471 : }
1472 0 : return (ssize_t)ret;
1473 : }
1474 :
1475 0 : static int cephwrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name)
1476 : {
1477 : int ret;
1478 0 : DBG_DEBUG("[CEPH] fremovexattr(%p, %p, %s)\n", handle, fsp, name);
1479 0 : if (!fsp->fsp_flags.is_pathref) {
1480 : /*
1481 : * We can use an io_fd to remove xattrs.
1482 : */
1483 0 : ret = ceph_fremovexattr(handle->data, fsp_get_io_fd(fsp), name);
1484 : } else {
1485 : /*
1486 : * This is no longer a handle based call.
1487 : */
1488 0 : ret = ceph_removexattr(handle->data,
1489 0 : fsp->fsp_name->base_name,
1490 : name);
1491 : }
1492 0 : DBG_DEBUG("[CEPH] fremovexattr(...) = %d\n", ret);
1493 0 : WRAP_RETURN(ret);
1494 : }
1495 :
1496 0 : static int cephwrap_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, const void *value, size_t size, int flags)
1497 : {
1498 : int ret;
1499 0 : DBG_DEBUG("[CEPH] fsetxattr(%p, %p, %s, %p, %llu, %d)\n", handle, fsp, name, value, llu(size), flags);
1500 0 : if (!fsp->fsp_flags.is_pathref) {
1501 : /*
1502 : * We can use an io_fd to set xattrs.
1503 : */
1504 0 : ret = ceph_fsetxattr(handle->data,
1505 : fsp_get_io_fd(fsp),
1506 : name,
1507 : value,
1508 : size,
1509 : flags);
1510 : } else {
1511 : /*
1512 : * This is no longer a handle based call.
1513 : */
1514 0 : ret = ceph_setxattr(handle->data,
1515 0 : fsp->fsp_name->base_name,
1516 : name,
1517 : value,
1518 : size,
1519 : flags);
1520 : }
1521 0 : DBG_DEBUG("[CEPH] fsetxattr(...) = %d\n", ret);
1522 0 : WRAP_RETURN(ret);
1523 : }
1524 :
1525 0 : static bool cephwrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
1526 : {
1527 :
1528 : /*
1529 : * We do not support AIO yet.
1530 : */
1531 :
1532 0 : DBG_DEBUG("[CEPH] cephwrap_aio_force(%p, %p) = false (errno = ENOTSUP)\n", handle, fsp);
1533 0 : errno = ENOTSUP;
1534 0 : return false;
1535 : }
1536 :
1537 0 : static NTSTATUS cephwrap_create_dfs_pathat(struct vfs_handle_struct *handle,
1538 : struct files_struct *dirfsp,
1539 : const struct smb_filename *smb_fname,
1540 : const struct referral *reflist,
1541 : size_t referral_count)
1542 : {
1543 0 : TALLOC_CTX *frame = talloc_stackframe();
1544 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1545 : int ret;
1546 0 : char *msdfs_link = NULL;
1547 0 : struct smb_filename *full_fname = NULL;
1548 :
1549 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1550 : dirfsp,
1551 : smb_fname);
1552 0 : if (full_fname == NULL) {
1553 0 : goto out;
1554 : }
1555 :
1556 : /* Form the msdfs_link contents */
1557 0 : msdfs_link = msdfs_link_string(frame,
1558 : reflist,
1559 : referral_count);
1560 0 : if (msdfs_link == NULL) {
1561 0 : goto out;
1562 : }
1563 :
1564 0 : ret = ceph_symlink(handle->data,
1565 : msdfs_link,
1566 0 : full_fname->base_name);
1567 0 : if (ret == 0) {
1568 0 : status = NT_STATUS_OK;
1569 : } else {
1570 0 : status = map_nt_error_from_unix(-ret);
1571 : }
1572 :
1573 0 : out:
1574 :
1575 0 : DBG_DEBUG("[CEPH] create_dfs_pathat(%s) = %s\n",
1576 : full_fname != NULL ? full_fname->base_name : "",
1577 : nt_errstr(status));
1578 :
1579 0 : TALLOC_FREE(frame);
1580 0 : return status;
1581 : }
1582 :
1583 : /*
1584 : * Read and return the contents of a DFS redirect given a
1585 : * pathname. A caller can pass in NULL for ppreflist and
1586 : * preferral_count but still determine if this was a
1587 : * DFS redirect point by getting NT_STATUS_OK back
1588 : * without incurring the overhead of reading and parsing
1589 : * the referral contents.
1590 : */
1591 :
1592 0 : static NTSTATUS cephwrap_read_dfs_pathat(struct vfs_handle_struct *handle,
1593 : TALLOC_CTX *mem_ctx,
1594 : struct files_struct *dirfsp,
1595 : struct smb_filename *smb_fname,
1596 : struct referral **ppreflist,
1597 : size_t *preferral_count)
1598 : {
1599 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1600 : size_t bufsize;
1601 0 : char *link_target = NULL;
1602 : int referral_len;
1603 : bool ok;
1604 : #if defined(HAVE_BROKEN_READLINK)
1605 : char link_target_buf[PATH_MAX];
1606 : #else
1607 : char link_target_buf[7];
1608 : #endif
1609 0 : struct ceph_statx stx = { 0 };
1610 0 : struct smb_filename *full_fname = NULL;
1611 : int ret;
1612 :
1613 0 : if (is_named_stream(smb_fname)) {
1614 0 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1615 0 : goto err;
1616 : }
1617 :
1618 0 : if (ppreflist == NULL && preferral_count == NULL) {
1619 : /*
1620 : * We're only checking if this is a DFS
1621 : * redirect. We don't need to return data.
1622 : */
1623 0 : bufsize = sizeof(link_target_buf);
1624 0 : link_target = link_target_buf;
1625 : } else {
1626 0 : bufsize = PATH_MAX;
1627 0 : link_target = talloc_array(mem_ctx, char, bufsize);
1628 0 : if (!link_target) {
1629 0 : goto err;
1630 : }
1631 : }
1632 :
1633 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1634 : dirfsp,
1635 : smb_fname);
1636 0 : if (full_fname == NULL) {
1637 0 : status = NT_STATUS_NO_MEMORY;
1638 0 : goto err;
1639 : }
1640 :
1641 0 : ret = ceph_statx(handle->data,
1642 0 : full_fname->base_name,
1643 : &stx,
1644 : SAMBA_STATX_ATTR_MASK,
1645 : AT_SYMLINK_NOFOLLOW);
1646 0 : if (ret < 0) {
1647 0 : status = map_nt_error_from_unix(-ret);
1648 0 : goto err;
1649 : }
1650 :
1651 0 : referral_len = ceph_readlink(handle->data,
1652 0 : full_fname->base_name,
1653 : link_target,
1654 0 : bufsize - 1);
1655 0 : if (referral_len < 0) {
1656 : /* ceph errors are -errno. */
1657 0 : if (-referral_len == EINVAL) {
1658 0 : DBG_INFO("%s is not a link.\n",
1659 : full_fname->base_name);
1660 0 : status = NT_STATUS_OBJECT_TYPE_MISMATCH;
1661 : } else {
1662 0 : status = map_nt_error_from_unix(-referral_len);
1663 0 : DBG_ERR("Error reading "
1664 : "msdfs link %s: %s\n",
1665 : full_fname->base_name,
1666 : strerror(errno));
1667 : }
1668 0 : goto err;
1669 : }
1670 0 : link_target[referral_len] = '\0';
1671 :
1672 0 : DBG_INFO("%s -> %s\n",
1673 : full_fname->base_name,
1674 : link_target);
1675 :
1676 0 : if (!strnequal(link_target, "msdfs:", 6)) {
1677 0 : status = NT_STATUS_OBJECT_TYPE_MISMATCH;
1678 0 : goto err;
1679 : }
1680 :
1681 0 : if (ppreflist == NULL && preferral_count == NULL) {
1682 : /* Early return for checking if this is a DFS link. */
1683 0 : TALLOC_FREE(full_fname);
1684 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
1685 0 : return NT_STATUS_OK;
1686 : }
1687 :
1688 0 : ok = parse_msdfs_symlink(mem_ctx,
1689 0 : lp_msdfs_shuffle_referrals(SNUM(handle->conn)),
1690 : link_target,
1691 : ppreflist,
1692 : preferral_count);
1693 :
1694 0 : if (ok) {
1695 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
1696 0 : status = NT_STATUS_OK;
1697 : } else {
1698 0 : status = NT_STATUS_NO_MEMORY;
1699 : }
1700 :
1701 0 : err:
1702 :
1703 0 : if (link_target != link_target_buf) {
1704 0 : TALLOC_FREE(link_target);
1705 : }
1706 0 : TALLOC_FREE(full_fname);
1707 0 : return status;
1708 : }
1709 :
1710 : static struct vfs_fn_pointers ceph_fns = {
1711 : /* Disk operations */
1712 :
1713 : .connect_fn = cephwrap_connect,
1714 : .disconnect_fn = cephwrap_disconnect,
1715 : .disk_free_fn = cephwrap_disk_free,
1716 : .get_quota_fn = cephwrap_get_quota,
1717 : .set_quota_fn = cephwrap_set_quota,
1718 : .statvfs_fn = cephwrap_statvfs,
1719 : .fs_capabilities_fn = cephwrap_fs_capabilities,
1720 :
1721 : /* Directory operations */
1722 :
1723 : .fdopendir_fn = cephwrap_fdopendir,
1724 : .readdir_fn = cephwrap_readdir,
1725 : .rewind_dir_fn = cephwrap_rewinddir,
1726 : .mkdirat_fn = cephwrap_mkdirat,
1727 : .closedir_fn = cephwrap_closedir,
1728 :
1729 : /* File operations */
1730 :
1731 : .create_dfs_pathat_fn = cephwrap_create_dfs_pathat,
1732 : .read_dfs_pathat_fn = cephwrap_read_dfs_pathat,
1733 : .openat_fn = cephwrap_openat,
1734 : .close_fn = cephwrap_close,
1735 : .pread_fn = cephwrap_pread,
1736 : .pread_send_fn = cephwrap_pread_send,
1737 : .pread_recv_fn = cephwrap_pread_recv,
1738 : .pwrite_fn = cephwrap_pwrite,
1739 : .pwrite_send_fn = cephwrap_pwrite_send,
1740 : .pwrite_recv_fn = cephwrap_pwrite_recv,
1741 : .lseek_fn = cephwrap_lseek,
1742 : .sendfile_fn = cephwrap_sendfile,
1743 : .recvfile_fn = cephwrap_recvfile,
1744 : .renameat_fn = cephwrap_renameat,
1745 : .fsync_send_fn = cephwrap_fsync_send,
1746 : .fsync_recv_fn = cephwrap_fsync_recv,
1747 : .stat_fn = cephwrap_stat,
1748 : .fstat_fn = cephwrap_fstat,
1749 : .lstat_fn = cephwrap_lstat,
1750 : .unlinkat_fn = cephwrap_unlinkat,
1751 : .fchmod_fn = cephwrap_fchmod,
1752 : .fchown_fn = cephwrap_fchown,
1753 : .lchown_fn = cephwrap_lchown,
1754 : .chdir_fn = cephwrap_chdir,
1755 : .getwd_fn = cephwrap_getwd,
1756 : .fntimes_fn = cephwrap_fntimes,
1757 : .ftruncate_fn = cephwrap_ftruncate,
1758 : .fallocate_fn = cephwrap_fallocate,
1759 : .lock_fn = cephwrap_lock,
1760 : .filesystem_sharemode_fn = cephwrap_filesystem_sharemode,
1761 : .fcntl_fn = cephwrap_fcntl,
1762 : .linux_setlease_fn = cephwrap_linux_setlease,
1763 : .getlock_fn = cephwrap_getlock,
1764 : .symlinkat_fn = cephwrap_symlinkat,
1765 : .readlinkat_fn = cephwrap_readlinkat,
1766 : .linkat_fn = cephwrap_linkat,
1767 : .mknodat_fn = cephwrap_mknodat,
1768 : .realpath_fn = cephwrap_realpath,
1769 : .fchflags_fn = cephwrap_fchflags,
1770 : .get_real_filename_at_fn = cephwrap_get_real_filename_at,
1771 : .connectpath_fn = cephwrap_connectpath,
1772 :
1773 : /* EA operations. */
1774 : .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1775 : .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1776 : .fgetxattr_fn = cephwrap_fgetxattr,
1777 : .flistxattr_fn = cephwrap_flistxattr,
1778 : .fremovexattr_fn = cephwrap_fremovexattr,
1779 : .fsetxattr_fn = cephwrap_fsetxattr,
1780 :
1781 : /* Posix ACL Operations */
1782 : .sys_acl_get_fd_fn = posixacl_xattr_acl_get_fd,
1783 : .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
1784 : .sys_acl_set_fd_fn = posixacl_xattr_acl_set_fd,
1785 : .sys_acl_delete_def_fd_fn = posixacl_xattr_acl_delete_def_fd,
1786 :
1787 : /* aio operations */
1788 : .aio_force_fn = cephwrap_aio_force,
1789 : };
1790 :
1791 : static_decl_vfs;
1792 27 : NTSTATUS vfs_ceph_init(TALLOC_CTX *ctx)
1793 : {
1794 27 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1795 : "ceph", &ceph_fns);
1796 : }
|