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