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 : 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 : int flags,
404 : mode_t mode)
405 : {
406 0 : struct smb_filename *name = NULL;
407 0 : bool have_opath = false;
408 0 : bool became_root = false;
409 0 : int result = -ENOENT;
410 :
411 : /*
412 : * ceph doesn't have openat().
413 : */
414 0 : if (fsp_get_pathref_fd(dirfsp) != AT_FDCWD) {
415 0 : name = full_path_from_dirfsp_atname(talloc_tos(),
416 : dirfsp,
417 : smb_fname);
418 0 : if (name == NULL) {
419 0 : return -1;
420 : }
421 0 : smb_fname = name;
422 : }
423 :
424 0 : DBG_DEBUG("[CEPH] openat(%p, %s, %p, %d, %d)\n", handle,
425 : smb_fname_str_dbg(smb_fname), fsp, flags, mode);
426 :
427 0 : if (smb_fname->stream_name) {
428 0 : goto out;
429 : }
430 :
431 : #ifdef O_PATH
432 0 : have_opath = true;
433 0 : if (fsp->fsp_flags.is_pathref) {
434 0 : flags |= O_PATH;
435 : }
436 : #endif
437 :
438 0 : if (fsp->fsp_flags.is_pathref && !have_opath) {
439 0 : become_root();
440 0 : became_root = true;
441 : }
442 :
443 0 : result = ceph_open(handle->data, smb_fname->base_name, flags, mode);
444 :
445 0 : if (became_root) {
446 0 : unbecome_root();
447 : }
448 :
449 0 : out:
450 0 : TALLOC_FREE(name);
451 0 : fsp->fsp_flags.have_proc_fds = false;
452 0 : DBG_DEBUG("[CEPH] open(...) = %d\n", result);
453 0 : WRAP_RETURN(result);
454 : }
455 :
456 0 : static int cephwrap_close(struct vfs_handle_struct *handle, files_struct *fsp)
457 : {
458 : int result;
459 :
460 0 : DBG_DEBUG("[CEPH] close(%p, %p)\n", handle, fsp);
461 0 : result = ceph_close(handle->data, fsp_get_io_fd(fsp));
462 0 : DBG_DEBUG("[CEPH] close(...) = %d\n", result);
463 :
464 0 : WRAP_RETURN(result);
465 : }
466 :
467 0 : static ssize_t cephwrap_pread(struct vfs_handle_struct *handle, files_struct *fsp, void *data,
468 : size_t n, off_t offset)
469 : {
470 : ssize_t result;
471 :
472 0 : DBG_DEBUG("[CEPH] pread(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
473 :
474 0 : result = ceph_read(handle->data, fsp_get_io_fd(fsp), data, n, offset);
475 0 : DBG_DEBUG("[CEPH] pread(...) = %llu\n", llu(result));
476 0 : WRAP_RETURN(result);
477 : }
478 :
479 : struct cephwrap_pread_state {
480 : ssize_t bytes_read;
481 : struct vfs_aio_state vfs_aio_state;
482 : };
483 :
484 : /*
485 : * Fake up an async ceph read by calling the synchronous API.
486 : */
487 0 : static struct tevent_req *cephwrap_pread_send(struct vfs_handle_struct *handle,
488 : TALLOC_CTX *mem_ctx,
489 : struct tevent_context *ev,
490 : struct files_struct *fsp,
491 : void *data,
492 : size_t n, off_t offset)
493 : {
494 0 : struct tevent_req *req = NULL;
495 0 : struct cephwrap_pread_state *state = NULL;
496 0 : int ret = -1;
497 :
498 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
499 0 : req = tevent_req_create(mem_ctx, &state, struct cephwrap_pread_state);
500 0 : if (req == NULL) {
501 0 : return NULL;
502 : }
503 :
504 0 : ret = ceph_read(handle->data, fsp_get_io_fd(fsp), data, n, offset);
505 0 : if (ret < 0) {
506 : /* ceph returns -errno on error. */
507 0 : tevent_req_error(req, -ret);
508 0 : return tevent_req_post(req, ev);
509 : }
510 :
511 0 : state->bytes_read = ret;
512 0 : tevent_req_done(req);
513 : /* Return and schedule the completion of the call. */
514 0 : return tevent_req_post(req, ev);
515 : }
516 :
517 0 : static ssize_t cephwrap_pread_recv(struct tevent_req *req,
518 : struct vfs_aio_state *vfs_aio_state)
519 : {
520 0 : struct cephwrap_pread_state *state =
521 0 : tevent_req_data(req, struct cephwrap_pread_state);
522 :
523 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
524 0 : if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
525 0 : return -1;
526 : }
527 0 : *vfs_aio_state = state->vfs_aio_state;
528 0 : return state->bytes_read;
529 : }
530 :
531 0 : static ssize_t cephwrap_pwrite(struct vfs_handle_struct *handle, files_struct *fsp, const void *data,
532 : size_t n, off_t offset)
533 : {
534 : ssize_t result;
535 :
536 0 : DBG_DEBUG("[CEPH] pwrite(%p, %p, %p, %llu, %llu)\n", handle, fsp, data, llu(n), llu(offset));
537 0 : result = ceph_write(handle->data, fsp_get_io_fd(fsp), data, n, offset);
538 0 : DBG_DEBUG("[CEPH] pwrite(...) = %llu\n", llu(result));
539 0 : WRAP_RETURN(result);
540 : }
541 :
542 : struct cephwrap_pwrite_state {
543 : ssize_t bytes_written;
544 : struct vfs_aio_state vfs_aio_state;
545 : };
546 :
547 : /*
548 : * Fake up an async ceph write by calling the synchronous API.
549 : */
550 0 : static struct tevent_req *cephwrap_pwrite_send(struct vfs_handle_struct *handle,
551 : TALLOC_CTX *mem_ctx,
552 : struct tevent_context *ev,
553 : struct files_struct *fsp,
554 : const void *data,
555 : size_t n, off_t offset)
556 : {
557 0 : struct tevent_req *req = NULL;
558 0 : struct cephwrap_pwrite_state *state = NULL;
559 0 : int ret = -1;
560 :
561 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
562 0 : req = tevent_req_create(mem_ctx, &state, struct cephwrap_pwrite_state);
563 0 : if (req == NULL) {
564 0 : return NULL;
565 : }
566 :
567 0 : ret = ceph_write(handle->data, fsp_get_io_fd(fsp), data, n, offset);
568 0 : if (ret < 0) {
569 : /* ceph returns -errno on error. */
570 0 : tevent_req_error(req, -ret);
571 0 : return tevent_req_post(req, ev);
572 : }
573 :
574 0 : state->bytes_written = ret;
575 0 : tevent_req_done(req);
576 : /* Return and schedule the completion of the call. */
577 0 : return tevent_req_post(req, ev);
578 : }
579 :
580 0 : static ssize_t cephwrap_pwrite_recv(struct tevent_req *req,
581 : struct vfs_aio_state *vfs_aio_state)
582 : {
583 0 : struct cephwrap_pwrite_state *state =
584 0 : tevent_req_data(req, struct cephwrap_pwrite_state);
585 :
586 0 : DBG_DEBUG("[CEPH] %s\n", __func__);
587 0 : if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
588 0 : return -1;
589 : }
590 0 : *vfs_aio_state = state->vfs_aio_state;
591 0 : return state->bytes_written;
592 : }
593 :
594 0 : static off_t cephwrap_lseek(struct vfs_handle_struct *handle, files_struct *fsp, off_t offset, int whence)
595 : {
596 0 : off_t result = 0;
597 :
598 0 : DBG_DEBUG("[CEPH] cephwrap_lseek\n");
599 0 : result = ceph_lseek(handle->data, fsp_get_io_fd(fsp), offset, whence);
600 0 : WRAP_RETURN(result);
601 : }
602 :
603 0 : static ssize_t cephwrap_sendfile(struct vfs_handle_struct *handle, int tofd, files_struct *fromfsp, const DATA_BLOB *hdr,
604 : off_t offset, size_t n)
605 : {
606 : /*
607 : * We cannot support sendfile because libceph is in user space.
608 : */
609 0 : DBG_DEBUG("[CEPH] cephwrap_sendfile\n");
610 0 : errno = ENOTSUP;
611 0 : return -1;
612 : }
613 :
614 0 : static ssize_t cephwrap_recvfile(struct vfs_handle_struct *handle,
615 : int fromfd,
616 : files_struct *tofsp,
617 : off_t offset,
618 : size_t n)
619 : {
620 : /*
621 : * We cannot support recvfile because libceph is in user space.
622 : */
623 0 : DBG_DEBUG("[CEPH] cephwrap_recvfile\n");
624 0 : errno=ENOTSUP;
625 0 : return -1;
626 : }
627 :
628 0 : static int cephwrap_renameat(struct vfs_handle_struct *handle,
629 : files_struct *srcfsp,
630 : const struct smb_filename *smb_fname_src,
631 : files_struct *dstfsp,
632 : const struct smb_filename *smb_fname_dst)
633 : {
634 0 : struct smb_filename *full_fname_src = NULL;
635 0 : struct smb_filename *full_fname_dst = NULL;
636 0 : int result = -1;
637 :
638 0 : DBG_DEBUG("[CEPH] cephwrap_renameat\n");
639 0 : if (smb_fname_src->stream_name || smb_fname_dst->stream_name) {
640 0 : errno = ENOENT;
641 0 : return result;
642 : }
643 :
644 0 : full_fname_src = full_path_from_dirfsp_atname(talloc_tos(),
645 : srcfsp,
646 : smb_fname_src);
647 0 : if (full_fname_src == NULL) {
648 0 : errno = ENOMEM;
649 0 : return -1;
650 : }
651 0 : full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(),
652 : dstfsp,
653 : smb_fname_dst);
654 0 : if (full_fname_dst == NULL) {
655 0 : TALLOC_FREE(full_fname_src);
656 0 : errno = ENOMEM;
657 0 : return -1;
658 : }
659 :
660 0 : result = ceph_rename(handle->data,
661 0 : full_fname_src->base_name,
662 0 : full_fname_dst->base_name);
663 :
664 0 : TALLOC_FREE(full_fname_src);
665 0 : TALLOC_FREE(full_fname_dst);
666 :
667 0 : WRAP_RETURN(result);
668 : }
669 :
670 : /*
671 : * Fake up an async ceph fsync by calling the synchronous API.
672 : */
673 :
674 0 : static struct tevent_req *cephwrap_fsync_send(struct vfs_handle_struct *handle,
675 : TALLOC_CTX *mem_ctx,
676 : struct tevent_context *ev,
677 : files_struct *fsp)
678 : {
679 0 : struct tevent_req *req = NULL;
680 0 : struct vfs_aio_state *state = NULL;
681 0 : int ret = -1;
682 :
683 0 : DBG_DEBUG("[CEPH] cephwrap_fsync_send\n");
684 :
685 0 : req = tevent_req_create(mem_ctx, &state, struct vfs_aio_state);
686 0 : if (req == NULL) {
687 0 : return NULL;
688 : }
689 :
690 : /* Make sync call. */
691 0 : ret = ceph_fsync(handle->data, fsp_get_io_fd(fsp), false);
692 :
693 0 : if (ret != 0) {
694 : /* ceph_fsync returns -errno on error. */
695 0 : tevent_req_error(req, -ret);
696 0 : return tevent_req_post(req, ev);
697 : }
698 :
699 : /* Mark it as done. */
700 0 : tevent_req_done(req);
701 : /* Return and schedule the completion of the call. */
702 0 : return tevent_req_post(req, ev);
703 : }
704 :
705 0 : static int cephwrap_fsync_recv(struct tevent_req *req,
706 : struct vfs_aio_state *vfs_aio_state)
707 : {
708 0 : struct vfs_aio_state *state =
709 0 : tevent_req_data(req, struct vfs_aio_state);
710 :
711 0 : DBG_DEBUG("[CEPH] cephwrap_fsync_recv\n");
712 :
713 0 : if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
714 0 : return -1;
715 : }
716 0 : *vfs_aio_state = *state;
717 0 : return 0;
718 : }
719 :
720 : #define SAMBA_STATX_ATTR_MASK (CEPH_STATX_BASIC_STATS|CEPH_STATX_BTIME)
721 :
722 0 : static void init_stat_ex_from_ceph_statx(struct stat_ex *dst, const struct ceph_statx *stx)
723 : {
724 0 : DBG_DEBUG("[CEPH]\tstx = {dev = %llx, ino = %llu, mode = 0x%x, "
725 : "nlink = %llu, uid = %d, gid = %d, rdev = %llx, size = %llu, "
726 : "blksize = %llu, blocks = %llu, atime = %llu, mtime = %llu, "
727 : "ctime = %llu, btime = %llu}\n",
728 : llu(stx->stx_dev), llu(stx->stx_ino), stx->stx_mode,
729 : llu(stx->stx_nlink), stx->stx_uid, stx->stx_gid,
730 : llu(stx->stx_rdev), llu(stx->stx_size), llu(stx->stx_blksize),
731 : llu(stx->stx_blocks), llu(stx->stx_atime.tv_sec),
732 : llu(stx->stx_mtime.tv_sec), llu(stx->stx_ctime.tv_sec),
733 : llu(stx->stx_btime.tv_sec));
734 :
735 0 : if ((stx->stx_mask & SAMBA_STATX_ATTR_MASK) != SAMBA_STATX_ATTR_MASK) {
736 0 : DBG_WARNING("%s: stx->stx_mask is incorrect (wanted %x, got %x)",
737 : __func__, SAMBA_STATX_ATTR_MASK, stx->stx_mask);
738 : }
739 :
740 0 : dst->st_ex_dev = stx->stx_dev;
741 0 : dst->st_ex_rdev = stx->stx_rdev;
742 0 : dst->st_ex_ino = stx->stx_ino;
743 0 : dst->st_ex_mode = stx->stx_mode;
744 0 : dst->st_ex_uid = stx->stx_uid;
745 0 : dst->st_ex_gid = stx->stx_gid;
746 0 : dst->st_ex_size = stx->stx_size;
747 0 : dst->st_ex_nlink = stx->stx_nlink;
748 0 : dst->st_ex_atime = stx->stx_atime;
749 0 : dst->st_ex_btime = stx->stx_btime;
750 0 : dst->st_ex_ctime = stx->stx_ctime;
751 0 : dst->st_ex_mtime = stx->stx_mtime;
752 0 : dst->st_ex_itime = dst->st_ex_btime;
753 0 : dst->st_ex_iflags = ST_EX_IFLAG_CALCULATED_ITIME;
754 0 : dst->st_ex_blksize = stx->stx_blksize;
755 0 : dst->st_ex_blocks = stx->stx_blocks;
756 0 : dst->st_ex_file_id = dst->st_ex_ino;
757 0 : dst->st_ex_iflags |= ST_EX_IFLAG_CALCULATED_FILE_ID;
758 0 : }
759 :
760 0 : static int cephwrap_stat(struct vfs_handle_struct *handle,
761 : struct smb_filename *smb_fname)
762 : {
763 0 : int result = -1;
764 : struct ceph_statx stx;
765 :
766 0 : DBG_DEBUG("[CEPH] stat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
767 :
768 0 : if (smb_fname->stream_name) {
769 0 : errno = ENOENT;
770 0 : return result;
771 : }
772 :
773 0 : result = ceph_statx(handle->data, smb_fname->base_name, &stx,
774 : SAMBA_STATX_ATTR_MASK, 0);
775 0 : DBG_DEBUG("[CEPH] statx(...) = %d\n", result);
776 0 : if (result < 0) {
777 0 : WRAP_RETURN(result);
778 : }
779 :
780 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
781 0 : DBG_DEBUG("[CEPH] mode = 0x%x\n", smb_fname->st.st_ex_mode);
782 0 : return result;
783 : }
784 :
785 0 : static int cephwrap_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
786 : {
787 0 : int result = -1;
788 : struct ceph_statx stx;
789 :
790 0 : DBG_DEBUG("[CEPH] fstat(%p, %d)\n", handle, fsp_get_io_fd(fsp));
791 0 : result = ceph_fstatx(handle->data, fsp_get_io_fd(fsp), &stx,
792 : SAMBA_STATX_ATTR_MASK, 0);
793 0 : DBG_DEBUG("[CEPH] fstat(...) = %d\n", result);
794 0 : if (result < 0) {
795 0 : WRAP_RETURN(result);
796 : }
797 :
798 0 : init_stat_ex_from_ceph_statx(sbuf, &stx);
799 0 : DBG_DEBUG("[CEPH] mode = 0x%x\n", sbuf->st_ex_mode);
800 0 : return result;
801 : }
802 :
803 0 : static int cephwrap_lstat(struct vfs_handle_struct *handle,
804 : struct smb_filename *smb_fname)
805 : {
806 0 : int result = -1;
807 : struct ceph_statx stx;
808 :
809 0 : DBG_DEBUG("[CEPH] lstat(%p, %s)\n", handle, smb_fname_str_dbg(smb_fname));
810 :
811 0 : if (smb_fname->stream_name) {
812 0 : errno = ENOENT;
813 0 : return result;
814 : }
815 :
816 0 : result = ceph_statx(handle->data, smb_fname->base_name, &stx,
817 : SAMBA_STATX_ATTR_MASK, AT_SYMLINK_NOFOLLOW);
818 0 : DBG_DEBUG("[CEPH] lstat(...) = %d\n", result);
819 0 : if (result < 0) {
820 0 : WRAP_RETURN(result);
821 : }
822 :
823 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
824 0 : return result;
825 : }
826 :
827 0 : static int cephwrap_fntimes(struct vfs_handle_struct *handle,
828 : files_struct *fsp,
829 : struct smb_file_time *ft)
830 : {
831 0 : struct ceph_statx stx = { 0 };
832 : int result;
833 0 : int mask = 0;
834 :
835 0 : if (!is_omit_timespec(&ft->atime)) {
836 0 : stx.stx_atime = ft->atime;
837 0 : mask |= CEPH_SETATTR_ATIME;
838 : }
839 0 : if (!is_omit_timespec(&ft->mtime)) {
840 0 : stx.stx_mtime = ft->mtime;
841 0 : mask |= CEPH_SETATTR_MTIME;
842 : }
843 0 : if (!is_omit_timespec(&ft->create_time)) {
844 0 : stx.stx_btime = ft->create_time;
845 0 : mask |= CEPH_SETATTR_BTIME;
846 : }
847 :
848 0 : if (!mask) {
849 0 : return 0;
850 : }
851 :
852 0 : if (!fsp->fsp_flags.is_pathref) {
853 : /*
854 : * We can use an io_fd to set xattrs.
855 : */
856 0 : result = ceph_fsetattrx(handle->data,
857 : fsp_get_io_fd(fsp),
858 : &stx,
859 : mask);
860 : } else {
861 : /*
862 : * This is no longer a handle based call.
863 : */
864 0 : result = ceph_setattrx(handle->data,
865 0 : fsp->fsp_name->base_name,
866 : &stx,
867 : mask,
868 : 0);
869 : }
870 :
871 0 : DBG_DEBUG("[CEPH] ntimes(%p, %s, {%ld, %ld, %ld, %ld}) = %d\n",
872 : handle, fsp_str_dbg(fsp), ft->mtime.tv_sec, ft->atime.tv_sec,
873 : ft->ctime.tv_sec, ft->create_time.tv_sec, result);
874 :
875 0 : return result;
876 : }
877 :
878 0 : static int cephwrap_unlinkat(struct vfs_handle_struct *handle,
879 : struct files_struct *dirfsp,
880 : const struct smb_filename *smb_fname,
881 : int flags)
882 : {
883 0 : struct smb_filename *full_fname = NULL;
884 0 : int result = -1;
885 :
886 0 : DBG_DEBUG("[CEPH] unlink(%p, %s)\n",
887 : handle,
888 : smb_fname_str_dbg(smb_fname));
889 :
890 0 : if (smb_fname->stream_name) {
891 0 : errno = ENOENT;
892 0 : return result;
893 : }
894 :
895 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
896 : dirfsp,
897 : smb_fname);
898 0 : if (full_fname == NULL) {
899 0 : return -1;
900 : }
901 :
902 0 : if (flags & AT_REMOVEDIR) {
903 0 : result = ceph_rmdir(handle->data, full_fname->base_name);
904 : } else {
905 0 : result = ceph_unlink(handle->data, full_fname->base_name);
906 : }
907 0 : TALLOC_FREE(full_fname);
908 0 : DBG_DEBUG("[CEPH] unlink(...) = %d\n", result);
909 0 : WRAP_RETURN(result);
910 : }
911 :
912 0 : static int cephwrap_fchmod(struct vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
913 : {
914 : int result;
915 :
916 0 : DBG_DEBUG("[CEPH] fchmod(%p, %p, %d)\n", handle, fsp, mode);
917 0 : if (!fsp->fsp_flags.is_pathref) {
918 : /*
919 : * We can use an io_fd to remove xattrs.
920 : */
921 0 : result = ceph_fchmod(handle->data, fsp_get_io_fd(fsp), mode);
922 : } else {
923 : /*
924 : * This is no longer a handle based call.
925 : */
926 0 : result = ceph_chmod(handle->data,
927 0 : fsp->fsp_name->base_name,
928 : mode);
929 : }
930 0 : DBG_DEBUG("[CEPH] fchmod(...) = %d\n", result);
931 0 : WRAP_RETURN(result);
932 : }
933 :
934 0 : static int cephwrap_fchown(struct vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
935 : {
936 : int result;
937 :
938 0 : DBG_DEBUG("[CEPH] fchown(%p, %p, %d, %d)\n", handle, fsp, uid, gid);
939 0 : result = ceph_fchown(handle->data, fsp_get_io_fd(fsp), uid, gid);
940 0 : DBG_DEBUG("[CEPH] fchown(...) = %d\n", result);
941 0 : WRAP_RETURN(result);
942 : }
943 :
944 0 : static int cephwrap_lchown(struct vfs_handle_struct *handle,
945 : const struct smb_filename *smb_fname,
946 : uid_t uid,
947 : gid_t gid)
948 : {
949 : int result;
950 0 : DBG_DEBUG("[CEPH] lchown(%p, %s, %d, %d)\n", handle, smb_fname->base_name, uid, gid);
951 0 : result = ceph_lchown(handle->data, smb_fname->base_name, uid, gid);
952 0 : DBG_DEBUG("[CEPH] lchown(...) = %d\n", result);
953 0 : WRAP_RETURN(result);
954 : }
955 :
956 0 : static int cephwrap_chdir(struct vfs_handle_struct *handle,
957 : const struct smb_filename *smb_fname)
958 : {
959 0 : int result = -1;
960 0 : DBG_DEBUG("[CEPH] chdir(%p, %s)\n", handle, smb_fname->base_name);
961 0 : result = ceph_chdir(handle->data, smb_fname->base_name);
962 0 : DBG_DEBUG("[CEPH] chdir(...) = %d\n", result);
963 0 : WRAP_RETURN(result);
964 : }
965 :
966 0 : static struct smb_filename *cephwrap_getwd(struct vfs_handle_struct *handle,
967 : TALLOC_CTX *ctx)
968 : {
969 0 : const char *cwd = ceph_getcwd(handle->data);
970 0 : DBG_DEBUG("[CEPH] getwd(%p) = %s\n", handle, cwd);
971 0 : return synthetic_smb_fname(ctx,
972 : cwd,
973 : NULL,
974 : NULL,
975 : 0,
976 : 0);
977 : }
978 :
979 0 : static int strict_allocate_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
980 : {
981 : off_t space_to_write;
982 : int result;
983 : NTSTATUS status;
984 : SMB_STRUCT_STAT *pst;
985 :
986 0 : status = vfs_stat_fsp(fsp);
987 0 : if (!NT_STATUS_IS_OK(status)) {
988 0 : return -1;
989 : }
990 0 : pst = &fsp->fsp_name->st;
991 :
992 : #ifdef S_ISFIFO
993 0 : if (S_ISFIFO(pst->st_ex_mode))
994 0 : return 0;
995 : #endif
996 :
997 0 : if (pst->st_ex_size == len)
998 0 : return 0;
999 :
1000 : /* Shrink - just ftruncate. */
1001 0 : if (pst->st_ex_size > len) {
1002 0 : result = ceph_ftruncate(handle->data, fsp_get_io_fd(fsp), len);
1003 0 : WRAP_RETURN(result);
1004 : }
1005 :
1006 0 : space_to_write = len - pst->st_ex_size;
1007 0 : result = ceph_fallocate(handle->data, fsp_get_io_fd(fsp), 0, pst->st_ex_size,
1008 : space_to_write);
1009 0 : WRAP_RETURN(result);
1010 : }
1011 :
1012 0 : static int cephwrap_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len)
1013 : {
1014 0 : int result = -1;
1015 :
1016 0 : DBG_DEBUG("[CEPH] ftruncate(%p, %p, %llu\n", handle, fsp, llu(len));
1017 :
1018 0 : if (lp_strict_allocate(SNUM(fsp->conn))) {
1019 0 : return strict_allocate_ftruncate(handle, fsp, len);
1020 : }
1021 :
1022 0 : result = ceph_ftruncate(handle->data, fsp_get_io_fd(fsp), len);
1023 0 : WRAP_RETURN(result);
1024 : }
1025 :
1026 0 : static int cephwrap_fallocate(struct vfs_handle_struct *handle,
1027 : struct files_struct *fsp,
1028 : uint32_t mode,
1029 : off_t offset,
1030 : off_t len)
1031 : {
1032 : int result;
1033 :
1034 0 : DBG_DEBUG("[CEPH] fallocate(%p, %p, %u, %llu, %llu\n",
1035 : handle, fsp, mode, llu(offset), llu(len));
1036 : /* unsupported mode flags are rejected by libcephfs */
1037 0 : result = ceph_fallocate(handle->data, fsp_get_io_fd(fsp), mode, offset, len);
1038 0 : DBG_DEBUG("[CEPH] fallocate(...) = %d\n", result);
1039 0 : WRAP_RETURN(result);
1040 : }
1041 :
1042 0 : static bool cephwrap_lock(struct vfs_handle_struct *handle, files_struct *fsp, int op, off_t offset, off_t count, int type)
1043 : {
1044 0 : DBG_DEBUG("[CEPH] lock\n");
1045 0 : return true;
1046 : }
1047 :
1048 0 : static int cephwrap_filesystem_sharemode(struct vfs_handle_struct *handle,
1049 : files_struct *fsp,
1050 : uint32_t share_access,
1051 : uint32_t access_mask)
1052 : {
1053 0 : DBG_ERR("[CEPH] filesystem sharemodes unsupported! Consider setting "
1054 : "\"kernel share modes = no\"\n");
1055 :
1056 0 : errno = ENOSYS;
1057 0 : return -1;
1058 : }
1059 :
1060 0 : static int cephwrap_fcntl(vfs_handle_struct *handle,
1061 : files_struct *fsp, int cmd, va_list cmd_arg)
1062 : {
1063 : /*
1064 : * SMB_VFS_FCNTL() is currently only called by vfs_set_blocking() to
1065 : * clear O_NONBLOCK, etc for LOCK_MAND and FIFOs. Ignore it.
1066 : */
1067 0 : if (cmd == F_GETFL) {
1068 0 : return 0;
1069 0 : } else if (cmd == F_SETFL) {
1070 : va_list dup_cmd_arg;
1071 : int opt;
1072 :
1073 0 : va_copy(dup_cmd_arg, cmd_arg);
1074 0 : opt = va_arg(dup_cmd_arg, int);
1075 0 : va_end(dup_cmd_arg);
1076 0 : if (opt == 0) {
1077 0 : return 0;
1078 : }
1079 0 : DBG_ERR("unexpected fcntl SETFL(%d)\n", opt);
1080 0 : goto err_out;
1081 : }
1082 0 : DBG_ERR("unexpected fcntl: %d\n", cmd);
1083 0 : err_out:
1084 0 : errno = EINVAL;
1085 0 : return -1;
1086 : }
1087 :
1088 0 : static bool cephwrap_getlock(struct vfs_handle_struct *handle, files_struct *fsp, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid)
1089 : {
1090 0 : DBG_DEBUG("[CEPH] getlock returning false and errno=0\n");
1091 :
1092 0 : errno = 0;
1093 0 : return false;
1094 : }
1095 :
1096 : /*
1097 : * We cannot let this fall through to the default, because the file might only
1098 : * be accessible from libceph (which is a user-space client) but the fd might
1099 : * be for some file the kernel knows about.
1100 : */
1101 0 : static int cephwrap_linux_setlease(struct vfs_handle_struct *handle, files_struct *fsp,
1102 : int leasetype)
1103 : {
1104 0 : int result = -1;
1105 :
1106 0 : DBG_DEBUG("[CEPH] linux_setlease\n");
1107 0 : errno = ENOSYS;
1108 0 : return result;
1109 : }
1110 :
1111 0 : static int cephwrap_symlinkat(struct vfs_handle_struct *handle,
1112 : const struct smb_filename *link_target,
1113 : struct files_struct *dirfsp,
1114 : const struct smb_filename *new_smb_fname)
1115 : {
1116 0 : struct smb_filename *full_fname = NULL;
1117 0 : int result = -1;
1118 :
1119 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1120 : dirfsp,
1121 : new_smb_fname);
1122 0 : if (full_fname == NULL) {
1123 0 : return -1;
1124 : }
1125 :
1126 0 : DBG_DEBUG("[CEPH] symlink(%p, %s, %s)\n", handle,
1127 : link_target->base_name,
1128 : full_fname->base_name);
1129 :
1130 0 : result = ceph_symlink(handle->data,
1131 0 : link_target->base_name,
1132 0 : full_fname->base_name);
1133 0 : TALLOC_FREE(full_fname);
1134 0 : DBG_DEBUG("[CEPH] symlink(...) = %d\n", result);
1135 0 : WRAP_RETURN(result);
1136 : }
1137 :
1138 0 : static int cephwrap_readlinkat(struct vfs_handle_struct *handle,
1139 : const struct files_struct *dirfsp,
1140 : const struct smb_filename *smb_fname,
1141 : char *buf,
1142 : size_t bufsiz)
1143 : {
1144 0 : struct smb_filename *full_fname = NULL;
1145 0 : int result = -1;
1146 :
1147 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1148 : dirfsp,
1149 : smb_fname);
1150 0 : if (full_fname == NULL) {
1151 0 : return -1;
1152 : }
1153 :
1154 0 : DBG_DEBUG("[CEPH] readlink(%p, %s, %p, %llu)\n", handle,
1155 : full_fname->base_name, buf, llu(bufsiz));
1156 :
1157 0 : result = ceph_readlink(handle->data, full_fname->base_name, buf, bufsiz);
1158 0 : TALLOC_FREE(full_fname);
1159 0 : DBG_DEBUG("[CEPH] readlink(...) = %d\n", result);
1160 0 : WRAP_RETURN(result);
1161 : }
1162 :
1163 0 : static int cephwrap_linkat(struct vfs_handle_struct *handle,
1164 : files_struct *srcfsp,
1165 : const struct smb_filename *old_smb_fname,
1166 : files_struct *dstfsp,
1167 : const struct smb_filename *new_smb_fname,
1168 : int flags)
1169 : {
1170 0 : struct smb_filename *full_fname_old = NULL;
1171 0 : struct smb_filename *full_fname_new = NULL;
1172 0 : int result = -1;
1173 :
1174 0 : full_fname_old = full_path_from_dirfsp_atname(talloc_tos(),
1175 : srcfsp,
1176 : old_smb_fname);
1177 0 : if (full_fname_old == NULL) {
1178 0 : return -1;
1179 : }
1180 0 : full_fname_new = full_path_from_dirfsp_atname(talloc_tos(),
1181 : dstfsp,
1182 : new_smb_fname);
1183 0 : if (full_fname_new == NULL) {
1184 0 : TALLOC_FREE(full_fname_old);
1185 0 : return -1;
1186 : }
1187 :
1188 0 : DBG_DEBUG("[CEPH] link(%p, %s, %s)\n", handle,
1189 : full_fname_old->base_name,
1190 : full_fname_new->base_name);
1191 :
1192 0 : result = ceph_link(handle->data,
1193 0 : full_fname_old->base_name,
1194 0 : full_fname_new->base_name);
1195 0 : DBG_DEBUG("[CEPH] link(...) = %d\n", result);
1196 0 : TALLOC_FREE(full_fname_old);
1197 0 : TALLOC_FREE(full_fname_new);
1198 0 : WRAP_RETURN(result);
1199 : }
1200 :
1201 0 : static int cephwrap_mknodat(struct vfs_handle_struct *handle,
1202 : files_struct *dirfsp,
1203 : const struct smb_filename *smb_fname,
1204 : mode_t mode,
1205 : SMB_DEV_T dev)
1206 : {
1207 0 : struct smb_filename *full_fname = NULL;
1208 0 : int result = -1;
1209 :
1210 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1211 : dirfsp,
1212 : smb_fname);
1213 0 : if (full_fname == NULL) {
1214 0 : return -1;
1215 : }
1216 :
1217 0 : DBG_DEBUG("[CEPH] mknodat(%p, %s)\n", handle, full_fname->base_name);
1218 0 : result = ceph_mknod(handle->data, full_fname->base_name, mode, dev);
1219 0 : DBG_DEBUG("[CEPH] mknodat(...) = %d\n", result);
1220 :
1221 0 : TALLOC_FREE(full_fname);
1222 :
1223 0 : WRAP_RETURN(result);
1224 : }
1225 :
1226 : /*
1227 : * This is a simple version of real-path ... a better version is needed to
1228 : * ask libceph about symbolic links.
1229 : */
1230 0 : static struct smb_filename *cephwrap_realpath(struct vfs_handle_struct *handle,
1231 : TALLOC_CTX *ctx,
1232 : const struct smb_filename *smb_fname)
1233 : {
1234 0 : char *result = NULL;
1235 0 : const char *path = smb_fname->base_name;
1236 0 : size_t len = strlen(path);
1237 0 : struct smb_filename *result_fname = NULL;
1238 0 : int r = -1;
1239 :
1240 0 : if (len && (path[0] == '/')) {
1241 0 : r = asprintf(&result, "%s", path);
1242 0 : } else if ((len >= 2) && (path[0] == '.') && (path[1] == '/')) {
1243 0 : if (len == 2) {
1244 0 : r = asprintf(&result, "%s",
1245 0 : handle->conn->cwd_fsp->fsp_name->base_name);
1246 : } else {
1247 0 : r = asprintf(&result, "%s/%s",
1248 0 : handle->conn->cwd_fsp->fsp_name->base_name, &path[2]);
1249 : }
1250 : } else {
1251 0 : r = asprintf(&result, "%s/%s",
1252 0 : handle->conn->cwd_fsp->fsp_name->base_name, path);
1253 : }
1254 :
1255 0 : if (r < 0) {
1256 0 : return NULL;
1257 : }
1258 :
1259 0 : DBG_DEBUG("[CEPH] realpath(%p, %s) = %s\n", handle, path, result);
1260 0 : result_fname = synthetic_smb_fname(ctx,
1261 : result,
1262 : NULL,
1263 : NULL,
1264 : 0,
1265 : 0);
1266 0 : SAFE_FREE(result);
1267 0 : return result_fname;
1268 : }
1269 :
1270 :
1271 0 : static int cephwrap_fchflags(struct vfs_handle_struct *handle,
1272 : struct files_struct *fsp,
1273 : unsigned int flags)
1274 : {
1275 0 : errno = ENOSYS;
1276 0 : return -1;
1277 : }
1278 :
1279 0 : static int cephwrap_get_real_filename(struct vfs_handle_struct *handle,
1280 : const struct smb_filename *path,
1281 : const char *name,
1282 : TALLOC_CTX *mem_ctx,
1283 : char **found_name)
1284 : {
1285 : /*
1286 : * Don't fall back to get_real_filename so callers can differentiate
1287 : * between a full directory scan and an actual case-insensitive stat.
1288 : */
1289 0 : errno = EOPNOTSUPP;
1290 0 : return -1;
1291 : }
1292 :
1293 0 : static const char *cephwrap_connectpath(struct vfs_handle_struct *handle,
1294 : const struct smb_filename *smb_fname)
1295 : {
1296 0 : return handle->conn->connectpath;
1297 : }
1298 :
1299 : /****************************************************************
1300 : Extended attribute operations.
1301 : *****************************************************************/
1302 :
1303 0 : static ssize_t cephwrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size)
1304 : {
1305 : int ret;
1306 0 : DBG_DEBUG("[CEPH] fgetxattr(%p, %p, %s, %p, %llu)\n", handle, fsp, name, value, llu(size));
1307 0 : ret = ceph_fgetxattr(handle->data, fsp_get_io_fd(fsp), name, value, size);
1308 0 : DBG_DEBUG("[CEPH] fgetxattr(...) = %d\n", ret);
1309 0 : if (ret < 0) {
1310 0 : WRAP_RETURN(ret);
1311 : }
1312 0 : return (ssize_t)ret;
1313 : }
1314 :
1315 0 : static ssize_t cephwrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, char *list, size_t size)
1316 : {
1317 : int ret;
1318 0 : DBG_DEBUG("[CEPH] flistxattr(%p, %p, %p, %llu)\n",
1319 : handle, fsp, list, llu(size));
1320 0 : if (!fsp->fsp_flags.is_pathref) {
1321 : /*
1322 : * We can use an io_fd to list xattrs.
1323 : */
1324 0 : ret = ceph_flistxattr(handle->data,
1325 : fsp_get_io_fd(fsp),
1326 : list,
1327 : size);
1328 : } else {
1329 : /*
1330 : * This is no longer a handle based call.
1331 : */
1332 0 : ret = ceph_listxattr(handle->data,
1333 0 : fsp->fsp_name->base_name,
1334 : list,
1335 : size);
1336 : }
1337 0 : DBG_DEBUG("[CEPH] flistxattr(...) = %d\n", ret);
1338 0 : if (ret < 0) {
1339 0 : WRAP_RETURN(ret);
1340 : }
1341 0 : return (ssize_t)ret;
1342 : }
1343 :
1344 0 : static int cephwrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name)
1345 : {
1346 : int ret;
1347 0 : DBG_DEBUG("[CEPH] fremovexattr(%p, %p, %s)\n", handle, fsp, name);
1348 0 : if (!fsp->fsp_flags.is_pathref) {
1349 : /*
1350 : * We can use an io_fd to remove xattrs.
1351 : */
1352 0 : ret = ceph_fremovexattr(handle->data, fsp_get_io_fd(fsp), name);
1353 : } else {
1354 : /*
1355 : * This is no longer a handle based call.
1356 : */
1357 0 : ret = ceph_removexattr(handle->data,
1358 0 : fsp->fsp_name->base_name,
1359 : name);
1360 : }
1361 0 : DBG_DEBUG("[CEPH] fremovexattr(...) = %d\n", ret);
1362 0 : WRAP_RETURN(ret);
1363 : }
1364 :
1365 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)
1366 : {
1367 : int ret;
1368 0 : DBG_DEBUG("[CEPH] fsetxattr(%p, %p, %s, %p, %llu, %d)\n", handle, fsp, name, value, llu(size), flags);
1369 0 : if (!fsp->fsp_flags.is_pathref) {
1370 : /*
1371 : * We can use an io_fd to set xattrs.
1372 : */
1373 0 : ret = ceph_fsetxattr(handle->data,
1374 : fsp_get_io_fd(fsp),
1375 : name,
1376 : value,
1377 : size,
1378 : flags);
1379 : } else {
1380 : /*
1381 : * This is no longer a handle based call.
1382 : */
1383 0 : ret = ceph_setxattr(handle->data,
1384 0 : fsp->fsp_name->base_name,
1385 : name,
1386 : value,
1387 : size,
1388 : flags);
1389 : }
1390 0 : DBG_DEBUG("[CEPH] fsetxattr(...) = %d\n", ret);
1391 0 : WRAP_RETURN(ret);
1392 : }
1393 :
1394 0 : static bool cephwrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
1395 : {
1396 :
1397 : /*
1398 : * We do not support AIO yet.
1399 : */
1400 :
1401 0 : DBG_DEBUG("[CEPH] cephwrap_aio_force(%p, %p) = false (errno = ENOTSUP)\n", handle, fsp);
1402 0 : errno = ENOTSUP;
1403 0 : return false;
1404 : }
1405 :
1406 0 : static NTSTATUS cephwrap_create_dfs_pathat(struct vfs_handle_struct *handle,
1407 : struct files_struct *dirfsp,
1408 : const struct smb_filename *smb_fname,
1409 : const struct referral *reflist,
1410 : size_t referral_count)
1411 : {
1412 0 : TALLOC_CTX *frame = talloc_stackframe();
1413 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1414 : int ret;
1415 0 : char *msdfs_link = NULL;
1416 0 : struct smb_filename *full_fname = NULL;
1417 :
1418 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1419 : dirfsp,
1420 : smb_fname);
1421 0 : if (full_fname == NULL) {
1422 0 : goto out;
1423 : }
1424 :
1425 : /* Form the msdfs_link contents */
1426 0 : msdfs_link = msdfs_link_string(frame,
1427 : reflist,
1428 : referral_count);
1429 0 : if (msdfs_link == NULL) {
1430 0 : goto out;
1431 : }
1432 :
1433 0 : ret = ceph_symlink(handle->data,
1434 : msdfs_link,
1435 0 : full_fname->base_name);
1436 0 : if (ret == 0) {
1437 0 : status = NT_STATUS_OK;
1438 : } else {
1439 0 : status = map_nt_error_from_unix(-ret);
1440 : }
1441 :
1442 0 : out:
1443 :
1444 0 : DBG_DEBUG("[CEPH] create_dfs_pathat(%s) = %s\n",
1445 : full_fname != NULL ? full_fname->base_name : "",
1446 : nt_errstr(status));
1447 :
1448 0 : TALLOC_FREE(frame);
1449 0 : return status;
1450 : }
1451 :
1452 : /*
1453 : * Read and return the contents of a DFS redirect given a
1454 : * pathname. A caller can pass in NULL for ppreflist and
1455 : * preferral_count but still determine if this was a
1456 : * DFS redirect point by getting NT_STATUS_OK back
1457 : * without incurring the overhead of reading and parsing
1458 : * the referral contents.
1459 : */
1460 :
1461 0 : static NTSTATUS cephwrap_read_dfs_pathat(struct vfs_handle_struct *handle,
1462 : TALLOC_CTX *mem_ctx,
1463 : struct files_struct *dirfsp,
1464 : struct smb_filename *smb_fname,
1465 : struct referral **ppreflist,
1466 : size_t *preferral_count)
1467 : {
1468 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1469 : size_t bufsize;
1470 0 : char *link_target = NULL;
1471 : int referral_len;
1472 : bool ok;
1473 : #if defined(HAVE_BROKEN_READLINK)
1474 : char link_target_buf[PATH_MAX];
1475 : #else
1476 : char link_target_buf[7];
1477 : #endif
1478 : struct ceph_statx stx;
1479 0 : struct smb_filename *full_fname = NULL;
1480 : int ret;
1481 :
1482 0 : if (is_named_stream(smb_fname)) {
1483 0 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1484 0 : goto err;
1485 : }
1486 :
1487 0 : if (ppreflist == NULL && preferral_count == NULL) {
1488 : /*
1489 : * We're only checking if this is a DFS
1490 : * redirect. We don't need to return data.
1491 : */
1492 0 : bufsize = sizeof(link_target_buf);
1493 0 : link_target = link_target_buf;
1494 : } else {
1495 0 : bufsize = PATH_MAX;
1496 0 : link_target = talloc_array(mem_ctx, char, bufsize);
1497 0 : if (!link_target) {
1498 0 : goto err;
1499 : }
1500 : }
1501 :
1502 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1503 : dirfsp,
1504 : smb_fname);
1505 0 : if (full_fname == NULL) {
1506 0 : status = NT_STATUS_NO_MEMORY;
1507 0 : goto err;
1508 : }
1509 :
1510 0 : ret = ceph_statx(handle->data,
1511 0 : full_fname->base_name,
1512 : &stx,
1513 : SAMBA_STATX_ATTR_MASK,
1514 : AT_SYMLINK_NOFOLLOW);
1515 0 : if (ret < 0) {
1516 0 : status = map_nt_error_from_unix(-ret);
1517 0 : goto err;
1518 : }
1519 :
1520 0 : referral_len = ceph_readlink(handle->data,
1521 0 : full_fname->base_name,
1522 : link_target,
1523 0 : bufsize - 1);
1524 0 : if (referral_len < 0) {
1525 : /* ceph errors are -errno. */
1526 0 : if (-referral_len == EINVAL) {
1527 0 : DBG_INFO("%s is not a link.\n",
1528 : full_fname->base_name);
1529 0 : status = NT_STATUS_OBJECT_TYPE_MISMATCH;
1530 : } else {
1531 0 : status = map_nt_error_from_unix(-referral_len);
1532 0 : DBG_ERR("Error reading "
1533 : "msdfs link %s: %s\n",
1534 : full_fname->base_name,
1535 : strerror(errno));
1536 : }
1537 0 : goto err;
1538 : }
1539 0 : link_target[referral_len] = '\0';
1540 :
1541 0 : DBG_INFO("%s -> %s\n",
1542 : full_fname->base_name,
1543 : link_target);
1544 :
1545 0 : if (!strnequal(link_target, "msdfs:", 6)) {
1546 0 : status = NT_STATUS_OBJECT_TYPE_MISMATCH;
1547 0 : goto err;
1548 : }
1549 :
1550 0 : if (ppreflist == NULL && preferral_count == NULL) {
1551 : /* Early return for checking if this is a DFS link. */
1552 0 : TALLOC_FREE(full_fname);
1553 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
1554 0 : return NT_STATUS_OK;
1555 : }
1556 :
1557 0 : ok = parse_msdfs_symlink(mem_ctx,
1558 0 : lp_msdfs_shuffle_referrals(SNUM(handle->conn)),
1559 : link_target,
1560 : ppreflist,
1561 : preferral_count);
1562 :
1563 0 : if (ok) {
1564 0 : init_stat_ex_from_ceph_statx(&smb_fname->st, &stx);
1565 0 : status = NT_STATUS_OK;
1566 : } else {
1567 0 : status = NT_STATUS_NO_MEMORY;
1568 : }
1569 :
1570 0 : err:
1571 :
1572 0 : if (link_target != link_target_buf) {
1573 0 : TALLOC_FREE(link_target);
1574 : }
1575 0 : TALLOC_FREE(full_fname);
1576 0 : return status;
1577 : }
1578 :
1579 : static struct vfs_fn_pointers ceph_fns = {
1580 : /* Disk operations */
1581 :
1582 : .connect_fn = cephwrap_connect,
1583 : .disconnect_fn = cephwrap_disconnect,
1584 : .disk_free_fn = cephwrap_disk_free,
1585 : .get_quota_fn = cephwrap_get_quota,
1586 : .set_quota_fn = cephwrap_set_quota,
1587 : .statvfs_fn = cephwrap_statvfs,
1588 : .fs_capabilities_fn = cephwrap_fs_capabilities,
1589 :
1590 : /* Directory operations */
1591 :
1592 : .fdopendir_fn = cephwrap_fdopendir,
1593 : .readdir_fn = cephwrap_readdir,
1594 : .seekdir_fn = cephwrap_seekdir,
1595 : .telldir_fn = cephwrap_telldir,
1596 : .rewind_dir_fn = cephwrap_rewinddir,
1597 : .mkdirat_fn = cephwrap_mkdirat,
1598 : .closedir_fn = cephwrap_closedir,
1599 :
1600 : /* File operations */
1601 :
1602 : .create_dfs_pathat_fn = cephwrap_create_dfs_pathat,
1603 : .read_dfs_pathat_fn = cephwrap_read_dfs_pathat,
1604 : .openat_fn = cephwrap_openat,
1605 : .close_fn = cephwrap_close,
1606 : .pread_fn = cephwrap_pread,
1607 : .pread_send_fn = cephwrap_pread_send,
1608 : .pread_recv_fn = cephwrap_pread_recv,
1609 : .pwrite_fn = cephwrap_pwrite,
1610 : .pwrite_send_fn = cephwrap_pwrite_send,
1611 : .pwrite_recv_fn = cephwrap_pwrite_recv,
1612 : .lseek_fn = cephwrap_lseek,
1613 : .sendfile_fn = cephwrap_sendfile,
1614 : .recvfile_fn = cephwrap_recvfile,
1615 : .renameat_fn = cephwrap_renameat,
1616 : .fsync_send_fn = cephwrap_fsync_send,
1617 : .fsync_recv_fn = cephwrap_fsync_recv,
1618 : .stat_fn = cephwrap_stat,
1619 : .fstat_fn = cephwrap_fstat,
1620 : .lstat_fn = cephwrap_lstat,
1621 : .unlinkat_fn = cephwrap_unlinkat,
1622 : .fchmod_fn = cephwrap_fchmod,
1623 : .fchown_fn = cephwrap_fchown,
1624 : .lchown_fn = cephwrap_lchown,
1625 : .chdir_fn = cephwrap_chdir,
1626 : .getwd_fn = cephwrap_getwd,
1627 : .fntimes_fn = cephwrap_fntimes,
1628 : .ftruncate_fn = cephwrap_ftruncate,
1629 : .fallocate_fn = cephwrap_fallocate,
1630 : .lock_fn = cephwrap_lock,
1631 : .filesystem_sharemode_fn = cephwrap_filesystem_sharemode,
1632 : .fcntl_fn = cephwrap_fcntl,
1633 : .linux_setlease_fn = cephwrap_linux_setlease,
1634 : .getlock_fn = cephwrap_getlock,
1635 : .symlinkat_fn = cephwrap_symlinkat,
1636 : .readlinkat_fn = cephwrap_readlinkat,
1637 : .linkat_fn = cephwrap_linkat,
1638 : .mknodat_fn = cephwrap_mknodat,
1639 : .realpath_fn = cephwrap_realpath,
1640 : .fchflags_fn = cephwrap_fchflags,
1641 : .get_real_filename_fn = cephwrap_get_real_filename,
1642 : .connectpath_fn = cephwrap_connectpath,
1643 :
1644 : /* EA operations. */
1645 : .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1646 : .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1647 : .fgetxattr_fn = cephwrap_fgetxattr,
1648 : .flistxattr_fn = cephwrap_flistxattr,
1649 : .fremovexattr_fn = cephwrap_fremovexattr,
1650 : .fsetxattr_fn = cephwrap_fsetxattr,
1651 :
1652 : /* Posix ACL Operations */
1653 : .sys_acl_get_fd_fn = posixacl_xattr_acl_get_fd,
1654 : .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
1655 : .sys_acl_set_fd_fn = posixacl_xattr_acl_set_fd,
1656 : .sys_acl_delete_def_fd_fn = posixacl_xattr_acl_delete_def_fd,
1657 :
1658 : /* aio operations */
1659 : .aio_force_fn = cephwrap_aio_force,
1660 : };
1661 :
1662 : static_decl_vfs;
1663 20 : NTSTATUS vfs_ceph_init(TALLOC_CTX *ctx)
1664 : {
1665 20 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1666 : "ceph", &ceph_fns);
1667 : }
|