/* Unix SMB/CIFS implementation. Wrap GlusterFS GFAPI calls in vfs functions. Copyright (c) 2013 Anand Avati This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * @file vfs_glusterfs.c * @author Anand Avati * @date May 2013 * @brief Samba VFS module for glusterfs * * @todo * - sendfile/recvfile support * * A Samba VFS module for GlusterFS, based on Gluster's libgfapi. * This is a "bottom" vfs module (not something to be stacked on top of * another module), and translates (most) calls to the closest actions * available in libgfapi. * */ #include "includes.h" #include "smbd/smbd.h" #include #include #include "lib/util/dlinklist.h" #include "lib/util/tevent_unix.h" #include "smbd/globals.h" #include "lib/util/sys_rw.h" #include "smbprofile.h" #include "modules/posixacl_xattr.h" #include "lib/pthreadpool/pthreadpool_tevent.h" #define DEFAULT_VOLFILE_SERVER "localhost" #define GLUSTER_NAME_MAX 255 /** * Helper to convert struct stat to struct stat_ex. */ static void smb_stat_ex_from_stat(struct stat_ex *dst, const struct stat *src) { ZERO_STRUCTP(dst); dst->st_ex_dev = src->st_dev; dst->st_ex_ino = src->st_ino; dst->st_ex_mode = src->st_mode; dst->st_ex_nlink = src->st_nlink; dst->st_ex_uid = src->st_uid; dst->st_ex_gid = src->st_gid; dst->st_ex_rdev = src->st_rdev; dst->st_ex_size = src->st_size; dst->st_ex_atime.tv_sec = src->st_atime; dst->st_ex_mtime.tv_sec = src->st_mtime; dst->st_ex_ctime.tv_sec = src->st_ctime; dst->st_ex_btime.tv_sec = src->st_mtime; dst->st_ex_blksize = src->st_blksize; dst->st_ex_blocks = src->st_blocks; #ifdef STAT_HAVE_NSEC dst->st_ex_atime.tv_nsec = src->st_atime_nsec; dst->st_ex_mtime.tv_nsec = src->st_mtime_nsec; dst->st_ex_ctime.tv_nsec = src->st_ctime_nsec; dst->st_ex_btime.tv_nsec = src->st_mtime_nsec; #endif } /* pre-opened glfs_t */ static struct glfs_preopened { char *volume; char *connectpath; glfs_t *fs; int ref; struct glfs_preopened *next, *prev; } *glfs_preopened; static int glfs_set_preopened(const char *volume, const char *connectpath, glfs_t *fs) { struct glfs_preopened *entry = NULL; entry = talloc_zero(NULL, struct glfs_preopened); if (!entry) { errno = ENOMEM; return -1; } entry->volume = talloc_strdup(entry, volume); if (!entry->volume) { talloc_free(entry); errno = ENOMEM; return -1; } entry->connectpath = talloc_strdup(entry, connectpath); if (entry->connectpath == NULL) { talloc_free(entry); errno = ENOMEM; return -1; } entry->fs = fs; entry->ref = 1; DLIST_ADD(glfs_preopened, entry); return 0; } static glfs_t *glfs_find_preopened(const char *volume, const char *connectpath) { struct glfs_preopened *entry = NULL; for (entry = glfs_preopened; entry; entry = entry->next) { if (strcmp(entry->volume, volume) == 0 && strcmp(entry->connectpath, connectpath) == 0) { entry->ref++; return entry->fs; } } return NULL; } static void glfs_clear_preopened(glfs_t *fs) { struct glfs_preopened *entry = NULL; for (entry = glfs_preopened; entry; entry = entry->next) { if (entry->fs == fs) { if (--entry->ref) return; DLIST_REMOVE(glfs_preopened, entry); glfs_fini(entry->fs); talloc_free(entry); break; } } } static int vfs_gluster_set_volfile_servers(glfs_t *fs, const char *volfile_servers) { char *server = NULL; size_t server_count = 0; size_t server_success = 0; int ret = -1; TALLOC_CTX *frame = talloc_stackframe(); DBG_INFO("servers list %s\n", volfile_servers); while (next_token_talloc(frame, &volfile_servers, &server, " \t")) { char *transport = NULL; char *host = NULL; int port = 0; server_count++; DBG_INFO("server %zu %s\n", server_count, server); /* Determine the transport type */ if (strncmp(server, "unix+", 5) == 0) { port = 0; transport = talloc_strdup(frame, "unix"); if (!transport) { errno = ENOMEM; goto out; } host = talloc_strdup(frame, server + 5); if (!host) { errno = ENOMEM; goto out; } } else { char *p = NULL; char *port_index = NULL; if (strncmp(server, "tcp+", 4) == 0) { server += 4; } /* IPv6 is enclosed in [] * ':' before ']' is part of IPv6 * ':' after ']' indicates port */ p = server; if (server[0] == '[') { server++; p = index(server, ']'); if (p == NULL) { /* Malformed IPv6 */ continue; } p[0] = '\0'; p++; } port_index = index(p, ':'); if (port_index == NULL) { port = 0; } else { port = atoi(port_index + 1); port_index[0] = '\0'; } transport = talloc_strdup(frame, "tcp"); if (!transport) { errno = ENOMEM; goto out; } host = talloc_strdup(frame, server); if (!host) { errno = ENOMEM; goto out; } } DBG_INFO("Calling set volfile server with params " "transport=%s, host=%s, port=%d\n", transport, host, port); ret = glfs_set_volfile_server(fs, transport, host, port); if (ret < 0) { DBG_WARNING("Failed to set volfile_server " "transport=%s, host=%s, port=%d (%s)\n", transport, host, port, strerror(errno)); } else { server_success++; } } out: if (server_count == 0) { ret = -1; } else if (server_success < server_count) { DBG_WARNING("Failed to set %zu out of %zu servers parsed\n", server_count - server_success, server_count); ret = 0; } TALLOC_FREE(frame); return ret; } /* Disk Operations */ static int check_for_write_behind_translator(TALLOC_CTX *mem_ctx, glfs_t *fs, const char *volume) { char *buf = NULL; char **lines = NULL; int numlines = 0; int i; char *option; bool write_behind_present = false; size_t newlen; int ret; ret = glfs_get_volfile(fs, NULL, 0); if (ret == 0) { DBG_ERR("%s: Failed to get volfile for " "volume (%s): No volfile\n", volume, strerror(errno)); return -1; } if (ret > 0) { DBG_ERR("%s: Invalid return %d for glfs_get_volfile for " "volume (%s): No volfile\n", volume, ret, strerror(errno)); return -1; } newlen = 0 - ret; buf = talloc_zero_array(mem_ctx, char, newlen); if (buf == NULL) { return -1; } ret = glfs_get_volfile(fs, buf, newlen); if (ret != newlen) { TALLOC_FREE(buf); DBG_ERR("%s: Failed to get volfile for volume (%s)\n", volume, strerror(errno)); return -1; } option = talloc_asprintf(mem_ctx, "volume %s-write-behind", volume); if (option == NULL) { TALLOC_FREE(buf); return -1; } /* * file_lines_parse() plays horrible tricks with * the passed-in talloc pointers and the hierarchy * which makes freeing hard to get right. * * As we know mem_ctx is freed by the caller, after * this point don't free on exit and let the caller * handle it. This violates good Samba coding practice * but we know we're not leaking here. */ lines = file_lines_parse(buf, newlen, &numlines, mem_ctx); if (lines == NULL || numlines <= 0) { return -1; } /* On success, buf is now a talloc child of lines !! */ for (i=0; i < numlines; i++) { if (strequal(lines[i], option)) { write_behind_present = true; break; } } if (write_behind_present) { DBG_ERR("Write behind translator is enabled for " "volume (%s), refusing to connect! " "Please turn off the write behind translator by calling " "'gluster volume set %s performance.write-behind off' " "on the commandline. " "Check the vfs_glusterfs(8) manpage for " "further details.\n", volume, volume); return -1; } return 0; } static int vfs_gluster_connect(struct vfs_handle_struct *handle, const char *service, const char *user) { const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); const char *volfile_servers; const char *volume; char *logfile; int loglevel; glfs_t *fs = NULL; TALLOC_CTX *tmp_ctx; int ret = 0; bool write_behind_pass_through_set = false; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { ret = -1; goto done; } logfile = lp_parm_substituted_string(tmp_ctx, lp_sub, SNUM(handle->conn), "glusterfs", "logfile", NULL); loglevel = lp_parm_int(SNUM(handle->conn), "glusterfs", "loglevel", -1); volfile_servers = lp_parm_substituted_string(tmp_ctx, lp_sub, SNUM(handle->conn), "glusterfs", "volfile_server", NULL); if (volfile_servers == NULL) { volfile_servers = DEFAULT_VOLFILE_SERVER; } volume = lp_parm_const_string(SNUM(handle->conn), "glusterfs", "volume", NULL); if (volume == NULL) { volume = service; } fs = glfs_find_preopened(volume, handle->conn->connectpath); if (fs) { goto done; } fs = glfs_new(volume); if (fs == NULL) { ret = -1; goto done; } ret = vfs_gluster_set_volfile_servers(fs, volfile_servers); if (ret < 0) { DBG_ERR("Failed to set volfile_servers from list %s\n", volfile_servers); goto done; } ret = glfs_set_xlator_option(fs, "*-md-cache", "cache-posix-acl", "true"); if (ret < 0) { DEBUG(0, ("%s: Failed to set xlator options\n", volume)); goto done; } ret = glfs_set_xlator_option(fs, "*-md-cache", "cache-selinux", "true"); if (ret < 0) { DEBUG(0, ("%s: Failed to set xlator options\n", volume)); goto done; } ret = glfs_set_xlator_option(fs, "*-snapview-client", "snapdir-entry-path", handle->conn->connectpath); if (ret < 0) { DEBUG(0, ("%s: Failed to set xlator option:" " snapdir-entry-path\n", volume)); goto done; } #ifdef HAVE_GFAPI_VER_7_9 ret = glfs_set_xlator_option(fs, "*-write-behind", "pass-through", "true"); if (ret < 0) { DBG_ERR("%s: Failed to set xlator option: pass-through\n", volume); goto done; } write_behind_pass_through_set = true; #endif ret = glfs_set_logging(fs, logfile, loglevel); if (ret < 0) { DEBUG(0, ("%s: Failed to set logfile %s loglevel %d\n", volume, logfile, loglevel)); goto done; } ret = glfs_init(fs); if (ret < 0) { DEBUG(0, ("%s: Failed to initialize volume (%s)\n", volume, strerror(errno))); goto done; } if (!write_behind_pass_through_set) { ret = check_for_write_behind_translator(tmp_ctx, fs, volume); if (ret < 0) { goto done; } } ret = glfs_set_preopened(volume, handle->conn->connectpath, fs); if (ret < 0) { DEBUG(0, ("%s: Failed to register volume (%s)\n", volume, strerror(errno))); goto done; } /* * The shadow_copy2 module will fail to export subdirectories * of a gluster volume unless we specify the mount point, * because the detection fails if the file system is not * locally mounted: * https://bugzilla.samba.org/show_bug.cgi?id=13091 */ lp_do_parameter(SNUM(handle->conn), "shadow:mountpoint", "/"); /* * Unless we have an async implementation of getxattrat turn this off. */ lp_do_parameter(SNUM(handle->conn), "smbd async dosmode", "false"); done: if (ret < 0) { if (fs) glfs_fini(fs); } else { DBG_ERR("%s: Initialized volume from servers %s\n", volume, volfile_servers); handle->data = fs; } talloc_free(tmp_ctx); return ret; } static void vfs_gluster_disconnect(struct vfs_handle_struct *handle) { glfs_t *fs = NULL; fs = handle->data; glfs_clear_preopened(fs); } static uint64_t vfs_gluster_disk_free(struct vfs_handle_struct *handle, const struct smb_filename *smb_fname, uint64_t *bsize_p, uint64_t *dfree_p, uint64_t *dsize_p) { struct statvfs statvfs = { 0, }; int ret; ret = glfs_statvfs(handle->data, smb_fname->base_name, &statvfs); if (ret < 0) { return -1; } if (bsize_p != NULL) { *bsize_p = (uint64_t)statvfs.f_bsize; /* Block size */ } if (dfree_p != NULL) { *dfree_p = (uint64_t)statvfs.f_bavail; /* Available Block units */ } if (dsize_p != NULL) { *dsize_p = (uint64_t)statvfs.f_blocks; /* Total Block units */ } return (uint64_t)statvfs.f_bavail; } static int vfs_gluster_get_quota(struct vfs_handle_struct *handle, const struct smb_filename *smb_fname, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt) { errno = ENOSYS; return -1; } static int vfs_gluster_set_quota(struct vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt) { errno = ENOSYS; return -1; } static int vfs_gluster_statvfs(struct vfs_handle_struct *handle, const struct smb_filename *smb_fname, struct vfs_statvfs_struct *vfs_statvfs) { struct statvfs statvfs = { 0, }; int ret; ret = glfs_statvfs(handle->data, smb_fname->base_name, &statvfs); if (ret < 0) { DEBUG(0, ("glfs_statvfs(%s) failed: %s\n", smb_fname->base_name, strerror(errno))); return -1; } ZERO_STRUCTP(vfs_statvfs); vfs_statvfs->OptimalTransferSize = statvfs.f_frsize; vfs_statvfs->BlockSize = statvfs.f_bsize; vfs_statvfs->TotalBlocks = statvfs.f_blocks; vfs_statvfs->BlocksAvail = statvfs.f_bfree; vfs_statvfs->UserBlocksAvail = statvfs.f_bavail; vfs_statvfs->TotalFileNodes = statvfs.f_files; vfs_statvfs->FreeFileNodes = statvfs.f_ffree; vfs_statvfs->FsIdentifier = statvfs.f_fsid; vfs_statvfs->FsCapabilities = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; return ret; } static uint32_t vfs_gluster_fs_capabilities(struct vfs_handle_struct *handle, enum timestamp_set_resolution *p_ts_res) { uint32_t caps = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; #ifdef HAVE_GFAPI_VER_6 caps |= FILE_SUPPORTS_SPARSE_FILES; #endif #ifdef STAT_HAVE_NSEC *p_ts_res = TIMESTAMP_SET_NT_OR_BETTER; #endif return caps; } static glfs_fd_t *vfs_gluster_fetch_glfd(struct vfs_handle_struct *handle, const files_struct *fsp) { glfs_fd_t **glfd = (glfs_fd_t **)VFS_FETCH_FSP_EXTENSION(handle, fsp); if (glfd == NULL) { DBG_INFO("Failed to fetch fsp extension\n"); return NULL; } if (*glfd == NULL) { DBG_INFO("Empty glfs_fd_t pointer\n"); return NULL; } return *glfd; } static DIR *vfs_gluster_fdopendir(struct vfs_handle_struct *handle, files_struct *fsp, const char *mask, uint32_t attributes) { glfs_fd_t *glfd = NULL; glfd = glfs_opendir(handle->data, fsp->fsp_name->base_name); if (glfd == NULL) { return NULL; } return (DIR *)glfd; } static int vfs_gluster_closedir(struct vfs_handle_struct *handle, DIR *dirp) { int ret; START_PROFILE(syscall_closedir); ret = glfs_closedir((void *)dirp); END_PROFILE(syscall_closedir); return ret; } static struct dirent *vfs_gluster_readdir(struct vfs_handle_struct *handle, struct files_struct *dirfsp, DIR *dirp) { static char direntbuf[512]; int ret; struct dirent *dirent = 0; START_PROFILE(syscall_readdir); ret = glfs_readdir_r((void *)dirp, (void *)direntbuf, &dirent); if ((ret < 0) || (dirent == NULL)) { END_PROFILE(syscall_readdir); return NULL; } END_PROFILE(syscall_readdir); return dirent; } static void vfs_gluster_rewinddir(struct vfs_handle_struct *handle, DIR *dirp) { START_PROFILE(syscall_rewinddir); glfs_seekdir((void *)dirp, 0); END_PROFILE(syscall_rewinddir); } static int vfs_gluster_mkdirat(struct vfs_handle_struct *handle, struct files_struct *dirfsp, const struct smb_filename *smb_fname, mode_t mode) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; START_PROFILE(syscall_mkdirat); pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_mkdirat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_mkdirat(pglfd, smb_fname->base_name, mode); #else struct smb_filename *full_fname = NULL; START_PROFILE(syscall_mkdirat); full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_mkdirat); return -1; } ret = glfs_mkdir(handle->data, full_fname->base_name, mode); TALLOC_FREE(full_fname); #endif END_PROFILE(syscall_mkdirat); return ret; } static int vfs_gluster_openat(struct vfs_handle_struct *handle, const struct files_struct *dirfsp, const struct smb_filename *smb_fname, files_struct *fsp, const struct vfs_open_how *how) { int flags = how->flags; struct smb_filename *full_fname = NULL; bool have_opath = false; bool became_root = false; glfs_fd_t *glfd = NULL; glfs_fd_t *pglfd = NULL; glfs_fd_t **p_tmp; START_PROFILE(syscall_openat); if (how->resolve != 0) { END_PROFILE(syscall_openat); errno = ENOSYS; return -1; } p_tmp = VFS_ADD_FSP_EXTENSION(handle, fsp, glfs_fd_t *, NULL); if (p_tmp == NULL) { END_PROFILE(syscall_openat); errno = ENOMEM; return -1; } #ifdef O_PATH have_opath = true; if (fsp->fsp_flags.is_pathref) { flags |= O_PATH; } #endif full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_openat); return -1; } if (fsp->fsp_flags.is_pathref && !have_opath) { become_root(); became_root = true; } if (fsp_get_pathref_fd(dirfsp) != AT_FDCWD) { #ifdef HAVE_GFAPI_VER_7_11 /* * Fetch Gluster fd for parent directory using dirfsp * before calling glfs_openat(); */ pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_openat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } glfd = glfs_openat(pglfd, smb_fname->base_name, flags, how->mode); #else /* * Replace smb_fname with full_path constructed above. */ smb_fname = full_fname; #endif } if (pglfd == NULL) { /* * smb_fname can either be a full_path or the same one * as received from the caller. In the latter case we * are operating at current working directory. */ if (flags & O_CREAT) { glfd = glfs_creat(handle->data, smb_fname->base_name, flags, how->mode); } else { glfd = glfs_open(handle->data, smb_fname->base_name, flags); } } if (became_root) { unbecome_root(); } TALLOC_FREE(full_fname); fsp->fsp_flags.have_proc_fds = false; if (glfd == NULL) { END_PROFILE(syscall_openat); /* no extension destroy_fn, so no need to save errno */ VFS_REMOVE_FSP_EXTENSION(handle, fsp); return -1; } *p_tmp = glfd; END_PROFILE(syscall_openat); /* An arbitrary value for error reporting, so you know its us. */ return 13371337; } static int vfs_gluster_close(struct vfs_handle_struct *handle, files_struct *fsp) { int ret; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_close); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_close); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } VFS_REMOVE_FSP_EXTENSION(handle, fsp); ret = glfs_close(glfd); END_PROFILE(syscall_close); return ret; } static ssize_t vfs_gluster_pread(struct vfs_handle_struct *handle, files_struct *fsp, void *data, size_t n, off_t offset) { ssize_t ret; glfs_fd_t *glfd = NULL; START_PROFILE_BYTES(syscall_pread, n); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE_BYTES(syscall_pread); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } #ifdef HAVE_GFAPI_VER_7_6 ret = glfs_pread(glfd, data, n, offset, 0, NULL); #else ret = glfs_pread(glfd, data, n, offset, 0); #endif END_PROFILE_BYTES(syscall_pread); return ret; } struct vfs_gluster_pread_state { ssize_t ret; glfs_fd_t *fd; void *buf; size_t count; off_t offset; struct vfs_aio_state vfs_aio_state; SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes); }; static void vfs_gluster_pread_do(void *private_data); static void vfs_gluster_pread_done(struct tevent_req *subreq); static int vfs_gluster_pread_state_destructor(struct vfs_gluster_pread_state *state); static struct tevent_req *vfs_gluster_pread_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, files_struct *fsp, void *data, size_t n, off_t offset) { struct vfs_gluster_pread_state *state; struct tevent_req *req, *subreq; glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return NULL; } req = tevent_req_create(mem_ctx, &state, struct vfs_gluster_pread_state); if (req == NULL) { return NULL; } state->ret = -1; state->fd = glfd; state->buf = data; state->count = n; state->offset = offset; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_pread, profile_p, state->profile_bytes, n); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); subreq = pthreadpool_tevent_job_send( state, ev, handle->conn->sconn->pool, vfs_gluster_pread_do, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, vfs_gluster_pread_done, req); talloc_set_destructor(state, vfs_gluster_pread_state_destructor); return req; } static void vfs_gluster_pread_do(void *private_data) { struct vfs_gluster_pread_state *state = talloc_get_type_abort( private_data, struct vfs_gluster_pread_state); struct timespec start_time; struct timespec end_time; SMBPROFILE_BYTES_ASYNC_SET_BUSY(state->profile_bytes); PROFILE_TIMESTAMP(&start_time); do { #ifdef HAVE_GFAPI_VER_7_6 state->ret = glfs_pread(state->fd, state->buf, state->count, state->offset, 0, NULL); #else state->ret = glfs_pread(state->fd, state->buf, state->count, state->offset, 0); #endif } while ((state->ret == -1) && (errno == EINTR)); if (state->ret == -1) { state->vfs_aio_state.error = errno; } PROFILE_TIMESTAMP(&end_time); state->vfs_aio_state.duration = nsec_time_diff(&end_time, &start_time); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); } static int vfs_gluster_pread_state_destructor(struct vfs_gluster_pread_state *state) { return -1; } static void vfs_gluster_pread_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); struct vfs_gluster_pread_state *state = tevent_req_data( req, struct vfs_gluster_pread_state); int ret; ret = pthreadpool_tevent_job_recv(subreq); TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); if (ret != 0) { if (ret != EAGAIN) { tevent_req_error(req, ret); return; } /* * If we get EAGAIN from pthreadpool_tevent_job_recv() this * means the lower level pthreadpool failed to create a new * thread. Fallback to sync processing in that case to allow * some progress for the client. */ vfs_gluster_pread_do(state); } tevent_req_done(req); } static ssize_t vfs_gluster_pread_recv(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state) { struct vfs_gluster_pread_state *state = tevent_req_data( req, struct vfs_gluster_pread_state); if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { return -1; } *vfs_aio_state = state->vfs_aio_state; return state->ret; } struct vfs_gluster_pwrite_state { ssize_t ret; glfs_fd_t *fd; const void *buf; size_t count; off_t offset; struct vfs_aio_state vfs_aio_state; SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes); }; static void vfs_gluster_pwrite_do(void *private_data); static void vfs_gluster_pwrite_done(struct tevent_req *subreq); static int vfs_gluster_pwrite_state_destructor(struct vfs_gluster_pwrite_state *state); static struct tevent_req *vfs_gluster_pwrite_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, files_struct *fsp, const void *data, size_t n, off_t offset) { struct tevent_req *req, *subreq; struct vfs_gluster_pwrite_state *state; glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return NULL; } req = tevent_req_create(mem_ctx, &state, struct vfs_gluster_pwrite_state); if (req == NULL) { return NULL; } state->ret = -1; state->fd = glfd; state->buf = data; state->count = n; state->offset = offset; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_pwrite, profile_p, state->profile_bytes, n); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); subreq = pthreadpool_tevent_job_send( state, ev, handle->conn->sconn->pool, vfs_gluster_pwrite_do, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, vfs_gluster_pwrite_done, req); talloc_set_destructor(state, vfs_gluster_pwrite_state_destructor); return req; } static void vfs_gluster_pwrite_do(void *private_data) { struct vfs_gluster_pwrite_state *state = talloc_get_type_abort( private_data, struct vfs_gluster_pwrite_state); struct timespec start_time; struct timespec end_time; SMBPROFILE_BYTES_ASYNC_SET_BUSY(state->profile_bytes); PROFILE_TIMESTAMP(&start_time); do { #ifdef HAVE_GFAPI_VER_7_6 state->ret = glfs_pwrite(state->fd, state->buf, state->count, state->offset, 0, NULL, NULL); #else state->ret = glfs_pwrite(state->fd, state->buf, state->count, state->offset, 0); #endif } while ((state->ret == -1) && (errno == EINTR)); if (state->ret == -1) { state->vfs_aio_state.error = errno; } PROFILE_TIMESTAMP(&end_time); state->vfs_aio_state.duration = nsec_time_diff(&end_time, &start_time); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); } static int vfs_gluster_pwrite_state_destructor(struct vfs_gluster_pwrite_state *state) { return -1; } static void vfs_gluster_pwrite_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); struct vfs_gluster_pwrite_state *state = tevent_req_data( req, struct vfs_gluster_pwrite_state); int ret; ret = pthreadpool_tevent_job_recv(subreq); TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); if (ret != 0) { if (ret != EAGAIN) { tevent_req_error(req, ret); return; } /* * If we get EAGAIN from pthreadpool_tevent_job_recv() this * means the lower level pthreadpool failed to create a new * thread. Fallback to sync processing in that case to allow * some progress for the client. */ vfs_gluster_pwrite_do(state); } tevent_req_done(req); } static ssize_t vfs_gluster_pwrite_recv(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state) { struct vfs_gluster_pwrite_state *state = tevent_req_data( req, struct vfs_gluster_pwrite_state); if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { return -1; } *vfs_aio_state = state->vfs_aio_state; return state->ret; } static ssize_t vfs_gluster_pwrite(struct vfs_handle_struct *handle, files_struct *fsp, const void *data, size_t n, off_t offset) { ssize_t ret; glfs_fd_t *glfd = NULL; START_PROFILE_BYTES(syscall_pwrite, n); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE_BYTES(syscall_pwrite); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } #ifdef HAVE_GFAPI_VER_7_6 ret = glfs_pwrite(glfd, data, n, offset, 0, NULL, NULL); #else ret = glfs_pwrite(glfd, data, n, offset, 0); #endif END_PROFILE_BYTES(syscall_pwrite); return ret; } static off_t vfs_gluster_lseek(struct vfs_handle_struct *handle, files_struct *fsp, off_t offset, int whence) { off_t ret = 0; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_lseek); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_lseek); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_lseek(glfd, offset, whence); END_PROFILE(syscall_lseek); return ret; } static ssize_t vfs_gluster_sendfile(struct vfs_handle_struct *handle, int tofd, files_struct *fromfsp, const DATA_BLOB *hdr, off_t offset, size_t n) { errno = ENOTSUP; return -1; } static ssize_t vfs_gluster_recvfile(struct vfs_handle_struct *handle, int fromfd, files_struct *tofsp, off_t offset, size_t n) { errno = ENOTSUP; return -1; } static int vfs_gluster_renameat(struct vfs_handle_struct *handle, files_struct *srcfsp, const struct smb_filename *smb_fname_src, files_struct *dstfsp, const struct smb_filename *smb_fname_dst) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *src_pglfd = NULL; glfs_fd_t *dst_pglfd = NULL; START_PROFILE(syscall_renameat); src_pglfd = vfs_gluster_fetch_glfd(handle, srcfsp); if (src_pglfd == NULL) { END_PROFILE(syscall_renameat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } dst_pglfd = vfs_gluster_fetch_glfd(handle, dstfsp); if (dst_pglfd == NULL) { END_PROFILE(syscall_renameat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_renameat(src_pglfd, smb_fname_src->base_name, dst_pglfd, smb_fname_dst->base_name); #else struct smb_filename *full_fname_src = NULL; struct smb_filename *full_fname_dst = NULL; START_PROFILE(syscall_renameat); full_fname_src = full_path_from_dirfsp_atname(talloc_tos(), srcfsp, smb_fname_src); if (full_fname_src == NULL) { END_PROFILE(syscall_renameat); errno = ENOMEM; return -1; } full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(), dstfsp, smb_fname_dst); if (full_fname_dst == NULL) { END_PROFILE(syscall_renameat); TALLOC_FREE(full_fname_src); errno = ENOMEM; return -1; } ret = glfs_rename(handle->data, full_fname_src->base_name, full_fname_dst->base_name); TALLOC_FREE(full_fname_src); TALLOC_FREE(full_fname_dst); #endif END_PROFILE(syscall_renameat); return ret; } struct vfs_gluster_fsync_state { ssize_t ret; glfs_fd_t *fd; struct vfs_aio_state vfs_aio_state; SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes); }; static void vfs_gluster_fsync_do(void *private_data); static void vfs_gluster_fsync_done(struct tevent_req *subreq); static int vfs_gluster_fsync_state_destructor(struct vfs_gluster_fsync_state *state); static struct tevent_req *vfs_gluster_fsync_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, files_struct *fsp) { struct tevent_req *req, *subreq; struct vfs_gluster_fsync_state *state; glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return NULL; } req = tevent_req_create(mem_ctx, &state, struct vfs_gluster_fsync_state); if (req == NULL) { return NULL; } state->ret = -1; state->fd = glfd; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_fsync, profile_p, state->profile_bytes, 0); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); subreq = pthreadpool_tevent_job_send( state, ev, handle->conn->sconn->pool, vfs_gluster_fsync_do, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, vfs_gluster_fsync_done, req); talloc_set_destructor(state, vfs_gluster_fsync_state_destructor); return req; } static void vfs_gluster_fsync_do(void *private_data) { struct vfs_gluster_fsync_state *state = talloc_get_type_abort( private_data, struct vfs_gluster_fsync_state); struct timespec start_time; struct timespec end_time; SMBPROFILE_BYTES_ASYNC_SET_BUSY(state->profile_bytes); PROFILE_TIMESTAMP(&start_time); do { #ifdef HAVE_GFAPI_VER_7_6 state->ret = glfs_fsync(state->fd, NULL, NULL); #else state->ret = glfs_fsync(state->fd); #endif } while ((state->ret == -1) && (errno == EINTR)); if (state->ret == -1) { state->vfs_aio_state.error = errno; } PROFILE_TIMESTAMP(&end_time); state->vfs_aio_state.duration = nsec_time_diff(&end_time, &start_time); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); } static int vfs_gluster_fsync_state_destructor(struct vfs_gluster_fsync_state *state) { return -1; } static void vfs_gluster_fsync_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); struct vfs_gluster_fsync_state *state = tevent_req_data( req, struct vfs_gluster_fsync_state); int ret; ret = pthreadpool_tevent_job_recv(subreq); TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); if (ret != 0) { if (ret != EAGAIN) { tevent_req_error(req, ret); return; } /* * If we get EAGAIN from pthreadpool_tevent_job_recv() this * means the lower level pthreadpool failed to create a new * thread. Fallback to sync processing in that case to allow * some progress for the client. */ vfs_gluster_fsync_do(state); } tevent_req_done(req); } static int vfs_gluster_fsync_recv(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state) { struct vfs_gluster_fsync_state *state = tevent_req_data( req, struct vfs_gluster_fsync_state); if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { return -1; } *vfs_aio_state = state->vfs_aio_state; return state->ret; } static int vfs_gluster_stat(struct vfs_handle_struct *handle, struct smb_filename *smb_fname) { struct stat st; int ret; START_PROFILE(syscall_stat); ret = glfs_stat(handle->data, smb_fname->base_name, &st); if (ret == 0) { smb_stat_ex_from_stat(&smb_fname->st, &st); } if (ret < 0 && errno != ENOENT) { DEBUG(0, ("glfs_stat(%s) failed: %s\n", smb_fname->base_name, strerror(errno))); } END_PROFILE(syscall_stat); return ret; } static int vfs_gluster_fstat(struct vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) { struct stat st; int ret; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_fstat); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_fstat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_fstat(glfd, &st); if (ret == 0) { smb_stat_ex_from_stat(sbuf, &st); } if (ret < 0) { DEBUG(0, ("glfs_fstat(%d) failed: %s\n", fsp_get_io_fd(fsp), strerror(errno))); } END_PROFILE(syscall_fstat); return ret; } static int vfs_gluster_fstatat(struct vfs_handle_struct *handle, const struct files_struct *dirfsp, const struct smb_filename *smb_fname, SMB_STRUCT_STAT *sbuf, int flags) { struct stat st; int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; START_PROFILE(syscall_fstatat); pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_fstatat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_fstatat(pglfd, smb_fname->base_name, &st, flags); #else struct smb_filename *full_fname = NULL; START_PROFILE(syscall_fstatat); full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_fstatat); return -1; } ret = glfs_stat(handle->data, full_fname->base_name, &st); TALLOC_FREE(full_fname->base_name); #endif if (ret == 0) { smb_stat_ex_from_stat(sbuf, &st); } END_PROFILE(syscall_fstatat); return ret; } static int vfs_gluster_lstat(struct vfs_handle_struct *handle, struct smb_filename *smb_fname) { struct stat st; int ret; START_PROFILE(syscall_lstat); ret = glfs_lstat(handle->data, smb_fname->base_name, &st); if (ret == 0) { smb_stat_ex_from_stat(&smb_fname->st, &st); } if (ret < 0 && errno != ENOENT) { DEBUG(0, ("glfs_lstat(%s) failed: %s\n", smb_fname->base_name, strerror(errno))); } END_PROFILE(syscall_lstat); return ret; } static uint64_t vfs_gluster_get_alloc_size(struct vfs_handle_struct *handle, files_struct *fsp, const SMB_STRUCT_STAT *sbuf) { uint64_t ret; START_PROFILE(syscall_get_alloc_size); ret = sbuf->st_ex_blocks * 512; END_PROFILE(syscall_get_alloc_size); return ret; } static int vfs_gluster_unlinkat(struct vfs_handle_struct *handle, struct files_struct *dirfsp, const struct smb_filename *smb_fname, int flags) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; START_PROFILE(syscall_unlinkat); pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_unlinkat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_unlinkat(pglfd, smb_fname->base_name, flags); #else struct smb_filename *full_fname = NULL; START_PROFILE(syscall_unlinkat); full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_unlinkat); return -1; } if (flags & AT_REMOVEDIR) { ret = glfs_rmdir(handle->data, full_fname->base_name); } else { ret = glfs_unlink(handle->data, full_fname->base_name); } TALLOC_FREE(full_fname); #endif END_PROFILE(syscall_unlinkat); return ret; } static int vfs_gluster_fchmod(struct vfs_handle_struct *handle, files_struct *fsp, mode_t mode) { int ret; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_fchmod); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_fchmod); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } if (!fsp->fsp_flags.is_pathref) { /* * We can use an io_fd to remove xattrs. */ ret = glfs_fchmod(glfd, mode); } else { /* * This is no longer a handle based call. */ ret = glfs_chmod(handle->data, fsp->fsp_name->base_name, mode); } END_PROFILE(syscall_fchmod); return ret; } static int vfs_gluster_fchown(struct vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid) { int ret; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_fchown); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_fchown); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_fchown(glfd, uid, gid); END_PROFILE(syscall_fchown); return ret; } static int vfs_gluster_lchown(struct vfs_handle_struct *handle, const struct smb_filename *smb_fname, uid_t uid, gid_t gid) { int ret; START_PROFILE(syscall_lchown); ret = glfs_lchown(handle->data, smb_fname->base_name, uid, gid); END_PROFILE(syscall_lchown); return ret; } static int vfs_gluster_chdir(struct vfs_handle_struct *handle, const struct smb_filename *smb_fname) { int ret; START_PROFILE(syscall_chdir); ret = glfs_chdir(handle->data, smb_fname->base_name); END_PROFILE(syscall_chdir); return ret; } static struct smb_filename *vfs_gluster_getwd(struct vfs_handle_struct *handle, TALLOC_CTX *ctx) { char cwd[PATH_MAX] = { '\0' }; char *ret; struct smb_filename *smb_fname = NULL; START_PROFILE(syscall_getwd); ret = glfs_getcwd(handle->data, cwd, PATH_MAX - 1); END_PROFILE(syscall_getwd); if (ret == NULL) { return NULL; } smb_fname = synthetic_smb_fname(ctx, ret, NULL, NULL, 0, 0); return smb_fname; } static int vfs_gluster_fntimes(struct vfs_handle_struct *handle, files_struct *fsp, struct smb_file_time *ft) { int ret = -1; struct timespec times[2]; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_fntimes); if (is_omit_timespec(&ft->atime)) { times[0].tv_sec = fsp->fsp_name->st.st_ex_atime.tv_sec; times[0].tv_nsec = fsp->fsp_name->st.st_ex_atime.tv_nsec; } else { times[0].tv_sec = ft->atime.tv_sec; times[0].tv_nsec = ft->atime.tv_nsec; } if (is_omit_timespec(&ft->mtime)) { times[1].tv_sec = fsp->fsp_name->st.st_ex_mtime.tv_sec; times[1].tv_nsec = fsp->fsp_name->st.st_ex_mtime.tv_nsec; } else { times[1].tv_sec = ft->mtime.tv_sec; times[1].tv_nsec = ft->mtime.tv_nsec; } if ((timespec_compare(×[0], &fsp->fsp_name->st.st_ex_atime) == 0) && (timespec_compare(×[1], &fsp->fsp_name->st.st_ex_mtime) == 0)) { END_PROFILE(syscall_fntimes); return 0; } glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_fntimes); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } if (!fsp->fsp_flags.is_pathref) { ret = glfs_futimens(glfd, times); } else { ret = glfs_utimens(handle->data, fsp->fsp_name->base_name, times); } END_PROFILE(syscall_fntimes); return ret; } static int vfs_gluster_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t offset) { int ret; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_ftruncate); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_ftruncate); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } #ifdef HAVE_GFAPI_VER_7_6 ret = glfs_ftruncate(glfd, offset, NULL, NULL); #else ret = glfs_ftruncate(glfd, offset); #endif END_PROFILE(syscall_ftruncate); return ret; } static int vfs_gluster_fallocate(struct vfs_handle_struct *handle, struct files_struct *fsp, uint32_t mode, off_t offset, off_t len) { int ret; #ifdef HAVE_GFAPI_VER_6 glfs_fd_t *glfd = NULL; int keep_size, punch_hole; START_PROFILE(syscall_fallocate); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_fallocate); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } keep_size = mode & VFS_FALLOCATE_FL_KEEP_SIZE; punch_hole = mode & VFS_FALLOCATE_FL_PUNCH_HOLE; mode &= ~(VFS_FALLOCATE_FL_KEEP_SIZE|VFS_FALLOCATE_FL_PUNCH_HOLE); if (mode != 0) { END_PROFILE(syscall_fallocate); errno = ENOTSUP; return -1; } if (punch_hole) { ret = glfs_discard(glfd, offset, len); if (ret != 0) { DBG_DEBUG("glfs_discard failed: %s\n", strerror(errno)); } } ret = glfs_fallocate(glfd, keep_size, offset, len); END_PROFILE(syscall_fallocate); #else errno = ENOTSUP; ret = -1; #endif return ret; } static struct smb_filename *vfs_gluster_realpath(struct vfs_handle_struct *handle, TALLOC_CTX *ctx, const struct smb_filename *smb_fname) { char *result = NULL; struct smb_filename *result_fname = NULL; char *resolved_path = NULL; START_PROFILE(syscall_realpath); resolved_path = SMB_MALLOC_ARRAY(char, PATH_MAX+1); if (resolved_path == NULL) { END_PROFILE(syscall_realpath); errno = ENOMEM; return NULL; } result = glfs_realpath(handle->data, smb_fname->base_name, resolved_path); if (result != NULL) { result_fname = synthetic_smb_fname(ctx, result, NULL, NULL, 0, 0); } SAFE_FREE(resolved_path); END_PROFILE(syscall_realpath); return result_fname; } static bool vfs_gluster_lock(struct vfs_handle_struct *handle, files_struct *fsp, int op, off_t offset, off_t count, int type) { struct flock flock = { 0, }; int ret; glfs_fd_t *glfd = NULL; bool ok = false; START_PROFILE(syscall_fcntl_lock); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); ok = false; goto out; } flock.l_type = type; flock.l_whence = SEEK_SET; flock.l_start = offset; flock.l_len = count; flock.l_pid = 0; ret = glfs_posix_lock(glfd, op, &flock); if (op == F_GETLK) { /* lock query, true if someone else has locked */ if ((ret != -1) && (flock.l_type != F_UNLCK) && (flock.l_pid != 0) && (flock.l_pid != getpid())) { ok = true; goto out; } /* not me */ ok = false; goto out; } if (ret == -1) { ok = false; goto out; } ok = true; out: END_PROFILE(syscall_fcntl_lock); return ok; } static int vfs_gluster_filesystem_sharemode(struct vfs_handle_struct *handle, files_struct *fsp, uint32_t share_access, uint32_t access_mask) { errno = ENOSYS; return -1; } static int vfs_gluster_fcntl(vfs_handle_struct *handle, files_struct *fsp, int cmd, va_list cmd_arg) { /* * SMB_VFS_FCNTL() is currently only called by vfs_set_blocking() to * clear O_NONBLOCK, etc for LOCK_MAND and FIFOs. Ignore it. */ if (cmd == F_GETFL) { return 0; } else if (cmd == F_SETFL) { va_list dup_cmd_arg; int opt; va_copy(dup_cmd_arg, cmd_arg); opt = va_arg(dup_cmd_arg, int); va_end(dup_cmd_arg); if (opt == 0) { return 0; } DBG_ERR("unexpected fcntl SETFL(%d)\n", opt); goto err_out; } DBG_ERR("unexpected fcntl: %d\n", cmd); err_out: errno = EINVAL; return -1; } static int vfs_gluster_linux_setlease(struct vfs_handle_struct *handle, files_struct *fsp, int leasetype) { errno = ENOSYS; return -1; } static bool vfs_gluster_getlock(struct vfs_handle_struct *handle, files_struct *fsp, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid) { struct flock flock = { 0, }; int ret; glfs_fd_t *glfd = NULL; START_PROFILE(syscall_fcntl_getlock); glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { END_PROFILE(syscall_fcntl_getlock); DBG_ERR("Failed to fetch gluster fd\n"); return false; } flock.l_type = *ptype; flock.l_whence = SEEK_SET; flock.l_start = *poffset; flock.l_len = *pcount; flock.l_pid = 0; ret = glfs_posix_lock(glfd, F_GETLK, &flock); if (ret == -1) { END_PROFILE(syscall_fcntl_getlock); return false; } *ptype = flock.l_type; *poffset = flock.l_start; *pcount = flock.l_len; *ppid = flock.l_pid; END_PROFILE(syscall_fcntl_getlock); return true; } static int vfs_gluster_symlinkat(struct vfs_handle_struct *handle, const struct smb_filename *link_target, struct files_struct *dirfsp, const struct smb_filename *new_smb_fname) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; START_PROFILE(syscall_symlinkat); pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_symlinkat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_symlinkat(link_target->base_name, pglfd, new_smb_fname->base_name); #else struct smb_filename *full_fname = NULL; START_PROFILE(syscall_symlinkat); full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, new_smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_symlinkat); return -1; } ret = glfs_symlink(handle->data, link_target->base_name, full_fname->base_name); TALLOC_FREE(full_fname); #endif END_PROFILE(syscall_symlinkat); return ret; } static int vfs_gluster_readlinkat(struct vfs_handle_struct *handle, const struct files_struct *dirfsp, const struct smb_filename *smb_fname, char *buf, size_t bufsiz) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; START_PROFILE(syscall_readlinkat); pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_readlinkat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_readlinkat(pglfd, smb_fname->base_name, buf, bufsiz); #else struct smb_filename *full_fname = NULL; START_PROFILE(syscall_readlinkat); full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_readlinkat); return -1; } ret = glfs_readlink(handle->data, full_fname->base_name, buf, bufsiz); TALLOC_FREE(full_fname); #endif END_PROFILE(syscall_readlinkat); return ret; } static int vfs_gluster_linkat(struct vfs_handle_struct *handle, files_struct *srcfsp, const struct smb_filename *old_smb_fname, files_struct *dstfsp, const struct smb_filename *new_smb_fname, int flags) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *src_pglfd = NULL; glfs_fd_t *dst_pglfd = NULL; START_PROFILE(syscall_linkat); src_pglfd = vfs_gluster_fetch_glfd(handle, srcfsp); if (src_pglfd == NULL) { END_PROFILE(syscall_linkat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } dst_pglfd = vfs_gluster_fetch_glfd(handle, dstfsp); if (dst_pglfd == NULL) { END_PROFILE(syscall_linkat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_linkat(src_pglfd, old_smb_fname->base_name, dst_pglfd, new_smb_fname->base_name, flags); #else struct smb_filename *full_fname_old = NULL; struct smb_filename *full_fname_new = NULL; START_PROFILE(syscall_linkat); full_fname_old = full_path_from_dirfsp_atname(talloc_tos(), srcfsp, old_smb_fname); if (full_fname_old == NULL) { END_PROFILE(syscall_linkat); return -1; } full_fname_new = full_path_from_dirfsp_atname(talloc_tos(), dstfsp, new_smb_fname); if (full_fname_new == NULL) { END_PROFILE(syscall_linkat); TALLOC_FREE(full_fname_old); return -1; } ret = glfs_link(handle->data, full_fname_old->base_name, full_fname_new->base_name); TALLOC_FREE(full_fname_old); TALLOC_FREE(full_fname_new); #endif END_PROFILE(syscall_linkat); return ret; } static int vfs_gluster_mknodat(struct vfs_handle_struct *handle, files_struct *dirfsp, const struct smb_filename *smb_fname, mode_t mode, SMB_DEV_T dev) { int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; START_PROFILE(syscall_mknodat); pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { END_PROFILE(syscall_mknodat); DBG_ERR("Failed to fetch gluster fd\n"); return -1; } ret = glfs_mknodat(pglfd, smb_fname->base_name, mode, dev); #else struct smb_filename *full_fname = NULL; START_PROFILE(syscall_mknodat); full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { END_PROFILE(syscall_mknodat); return -1; } ret = glfs_mknod(handle->data, full_fname->base_name, mode, dev); TALLOC_FREE(full_fname); #endif END_PROFILE(syscall_mknodat); return ret; } static int vfs_gluster_fchflags(struct vfs_handle_struct *handle, struct files_struct *fsp, unsigned int flags) { errno = ENOSYS; return -1; } static NTSTATUS vfs_gluster_get_real_filename_at( struct vfs_handle_struct *handle, struct files_struct *dirfsp, const char *name, TALLOC_CTX *mem_ctx, char **found_name) { int ret; char key_buf[GLUSTER_NAME_MAX + 64]; char val_buf[GLUSTER_NAME_MAX + 1]; if (strlen(name) >= GLUSTER_NAME_MAX) { return NT_STATUS_OBJECT_NAME_INVALID; } snprintf(key_buf, GLUSTER_NAME_MAX + 64, "glusterfs.get_real_filename:%s", name); ret = glfs_getxattr(handle->data, dirfsp->fsp_name->base_name, key_buf, val_buf, GLUSTER_NAME_MAX + 1); if (ret == -1) { if (errno == ENOATTR) { errno = ENOENT; } return map_nt_error_from_unix(errno); } *found_name = talloc_strdup(mem_ctx, val_buf); if (found_name[0] == NULL) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; } static const char *vfs_gluster_connectpath( struct vfs_handle_struct *handle, const struct files_struct *dirfsp, const struct smb_filename *smb_fname) { return handle->conn->connectpath; } /* EA Operations */ static ssize_t vfs_gluster_fgetxattr(struct vfs_handle_struct *handle, files_struct *fsp, const char *name, void *value, size_t size) { glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return -1; } if (!fsp->fsp_flags.is_pathref) { /* * We can use an io_fd to retrieve xattr value. */ return glfs_fgetxattr(glfd, name, value, size); } /* * This is no longer a handle based call. */ return glfs_getxattr(handle->data, fsp->fsp_name->base_name, name, value, size); } static ssize_t vfs_gluster_flistxattr(struct vfs_handle_struct *handle, files_struct *fsp, char *list, size_t size) { glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return -1; } if (!fsp->fsp_flags.is_pathref) { /* * We can use an io_fd to list xattrs. */ return glfs_flistxattr(glfd, list, size); } else { /* * This is no longer a handle based call. */ return glfs_listxattr(handle->data, fsp->fsp_name->base_name, list, size); } } static int vfs_gluster_fremovexattr(struct vfs_handle_struct *handle, files_struct *fsp, const char *name) { glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return -1; } if (!fsp->fsp_flags.is_pathref) { /* * We can use an io_fd to remove xattrs. */ return glfs_fremovexattr(glfd, name); } else { /* * This is no longer a handle based call. */ return glfs_removexattr(handle->data, fsp->fsp_name->base_name, name); } } static int vfs_gluster_fsetxattr(struct vfs_handle_struct *handle, files_struct *fsp, const char *name, const void *value, size_t size, int flags) { glfs_fd_t *glfd = vfs_gluster_fetch_glfd(handle, fsp); if (glfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return -1; } if (!fsp->fsp_flags.is_pathref) { /* * We can use an io_fd to set xattrs. */ return glfs_fsetxattr(glfd, name, value, size, flags); } else { /* * This is no longer a handle based call. */ return glfs_setxattr(handle->data, fsp->fsp_name->base_name, name, value, size, flags); } } /* AIO Operations */ static bool vfs_gluster_aio_force(struct vfs_handle_struct *handle, files_struct *fsp) { return false; } static NTSTATUS vfs_gluster_create_dfs_pathat(struct vfs_handle_struct *handle, struct files_struct *dirfsp, const struct smb_filename *smb_fname, const struct referral *reflist, size_t referral_count) { TALLOC_CTX *frame = talloc_stackframe(); NTSTATUS status = NT_STATUS_NO_MEMORY; int ret; char *msdfs_link = NULL; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; #else struct smb_filename *full_fname = NULL; #endif /* Form the msdfs_link contents */ msdfs_link = msdfs_link_string(frame, reflist, referral_count); if (msdfs_link == NULL) { goto out; } #ifdef HAVE_GFAPI_VER_7_11 pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); status = NT_STATUS_OBJECT_NAME_NOT_FOUND; goto out; } ret = glfs_symlinkat(msdfs_link, pglfd, smb_fname->base_name); #else full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { goto out; } ret = glfs_symlink(handle->data, msdfs_link, full_fname->base_name); TALLOC_FREE(full_fname); #endif if (ret == 0) { status = NT_STATUS_OK; } else { status = map_nt_error_from_unix(errno); } out: TALLOC_FREE(frame); return status; } /* * Read and return the contents of a DFS redirect given a * pathname. A caller can pass in NULL for ppreflist and * preferral_count but still determine if this was a * DFS redirect point by getting NT_STATUS_OK back * without incurring the overhead of reading and parsing * the referral contents. */ static NTSTATUS vfs_gluster_read_dfs_pathat(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct files_struct *dirfsp, struct smb_filename *smb_fname, struct referral **ppreflist, size_t *preferral_count) { NTSTATUS status = NT_STATUS_NO_MEMORY; size_t bufsize; char *link_target = NULL; int referral_len; bool ok; #if defined(HAVE_BROKEN_READLINK) char link_target_buf[PATH_MAX]; #else char link_target_buf[7]; #endif struct stat st; struct smb_filename *full_fname = NULL; int ret; #ifdef HAVE_GFAPI_VER_7_11 glfs_fd_t *pglfd = NULL; #endif if (is_named_stream(smb_fname)) { status = NT_STATUS_OBJECT_NAME_NOT_FOUND; goto err; } if (ppreflist == NULL && preferral_count == NULL) { /* * We're only checking if this is a DFS * redirect. We don't need to return data. */ bufsize = sizeof(link_target_buf); link_target = link_target_buf; } else { bufsize = PATH_MAX; link_target = talloc_array(mem_ctx, char, bufsize); if (!link_target) { goto err; } } full_fname = full_path_from_dirfsp_atname(talloc_tos(), dirfsp, smb_fname); if (full_fname == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } ret = glfs_lstat(handle->data, full_fname->base_name, &st); if (ret < 0) { status = map_nt_error_from_unix(errno); goto err; } #ifdef HAVE_GFAPI_VER_7_11 pglfd = vfs_gluster_fetch_glfd(handle, dirfsp); if (pglfd == NULL) { DBG_ERR("Failed to fetch gluster fd\n"); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } referral_len = glfs_readlinkat(pglfd, smb_fname->base_name, link_target, bufsize - 1); #else referral_len = glfs_readlink(handle->data, full_fname->base_name, link_target, bufsize - 1); #endif if (referral_len < 0) { if (errno == EINVAL) { DBG_INFO("%s is not a link.\n", full_fname->base_name); status = NT_STATUS_OBJECT_TYPE_MISMATCH; } else { status = map_nt_error_from_unix(errno); DBG_ERR("Error reading " "msdfs link %s: %s\n", full_fname->base_name, strerror(errno)); } goto err; } link_target[referral_len] = '\0'; DBG_INFO("%s -> %s\n", full_fname->base_name, link_target); if (!strnequal(link_target, "msdfs:", 6)) { status = NT_STATUS_OBJECT_TYPE_MISMATCH; goto err; } if (ppreflist == NULL && preferral_count == NULL) { /* Early return for checking if this is a DFS link. */ TALLOC_FREE(full_fname); smb_stat_ex_from_stat(&smb_fname->st, &st); return NT_STATUS_OK; } ok = parse_msdfs_symlink(mem_ctx, lp_msdfs_shuffle_referrals(SNUM(handle->conn)), link_target, ppreflist, preferral_count); if (ok) { smb_stat_ex_from_stat(&smb_fname->st, &st); status = NT_STATUS_OK; } else { status = NT_STATUS_NO_MEMORY; } err: if (link_target != link_target_buf) { TALLOC_FREE(link_target); } TALLOC_FREE(full_fname); return status; } static struct vfs_fn_pointers glusterfs_fns = { /* Disk Operations */ .connect_fn = vfs_gluster_connect, .disconnect_fn = vfs_gluster_disconnect, .disk_free_fn = vfs_gluster_disk_free, .get_quota_fn = vfs_gluster_get_quota, .set_quota_fn = vfs_gluster_set_quota, .statvfs_fn = vfs_gluster_statvfs, .fs_capabilities_fn = vfs_gluster_fs_capabilities, .get_dfs_referrals_fn = NULL, /* Directory Operations */ .fdopendir_fn = vfs_gluster_fdopendir, .readdir_fn = vfs_gluster_readdir, .rewind_dir_fn = vfs_gluster_rewinddir, .mkdirat_fn = vfs_gluster_mkdirat, .closedir_fn = vfs_gluster_closedir, /* File Operations */ .openat_fn = vfs_gluster_openat, .create_file_fn = NULL, .close_fn = vfs_gluster_close, .pread_fn = vfs_gluster_pread, .pread_send_fn = vfs_gluster_pread_send, .pread_recv_fn = vfs_gluster_pread_recv, .pwrite_fn = vfs_gluster_pwrite, .pwrite_send_fn = vfs_gluster_pwrite_send, .pwrite_recv_fn = vfs_gluster_pwrite_recv, .lseek_fn = vfs_gluster_lseek, .sendfile_fn = vfs_gluster_sendfile, .recvfile_fn = vfs_gluster_recvfile, .renameat_fn = vfs_gluster_renameat, .fsync_send_fn = vfs_gluster_fsync_send, .fsync_recv_fn = vfs_gluster_fsync_recv, .stat_fn = vfs_gluster_stat, .fstat_fn = vfs_gluster_fstat, .fstatat_fn = vfs_gluster_fstatat, .lstat_fn = vfs_gluster_lstat, .get_alloc_size_fn = vfs_gluster_get_alloc_size, .unlinkat_fn = vfs_gluster_unlinkat, .fchmod_fn = vfs_gluster_fchmod, .fchown_fn = vfs_gluster_fchown, .lchown_fn = vfs_gluster_lchown, .chdir_fn = vfs_gluster_chdir, .getwd_fn = vfs_gluster_getwd, .fntimes_fn = vfs_gluster_fntimes, .ftruncate_fn = vfs_gluster_ftruncate, .fallocate_fn = vfs_gluster_fallocate, .lock_fn = vfs_gluster_lock, .filesystem_sharemode_fn = vfs_gluster_filesystem_sharemode, .fcntl_fn = vfs_gluster_fcntl, .linux_setlease_fn = vfs_gluster_linux_setlease, .getlock_fn = vfs_gluster_getlock, .symlinkat_fn = vfs_gluster_symlinkat, .readlinkat_fn = vfs_gluster_readlinkat, .linkat_fn = vfs_gluster_linkat, .mknodat_fn = vfs_gluster_mknodat, .realpath_fn = vfs_gluster_realpath, .fchflags_fn = vfs_gluster_fchflags, .file_id_create_fn = NULL, .fstreaminfo_fn = NULL, .get_real_filename_at_fn = vfs_gluster_get_real_filename_at, .connectpath_fn = vfs_gluster_connectpath, .create_dfs_pathat_fn = vfs_gluster_create_dfs_pathat, .read_dfs_pathat_fn = vfs_gluster_read_dfs_pathat, .brl_lock_windows_fn = NULL, .brl_unlock_windows_fn = NULL, .strict_lock_check_fn = NULL, .translate_name_fn = NULL, .fsctl_fn = NULL, /* NT ACL Operations */ .fget_nt_acl_fn = NULL, .fset_nt_acl_fn = NULL, .audit_file_fn = NULL, /* Posix ACL Operations */ .sys_acl_get_fd_fn = posixacl_xattr_acl_get_fd, .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd, .sys_acl_set_fd_fn = posixacl_xattr_acl_set_fd, .sys_acl_delete_def_fd_fn = posixacl_xattr_acl_delete_def_fd, /* EA Operations */ .getxattrat_send_fn = vfs_not_implemented_getxattrat_send, .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv, .fgetxattr_fn = vfs_gluster_fgetxattr, .flistxattr_fn = vfs_gluster_flistxattr, .fremovexattr_fn = vfs_gluster_fremovexattr, .fsetxattr_fn = vfs_gluster_fsetxattr, /* AIO Operations */ .aio_force_fn = vfs_gluster_aio_force, /* Durable handle Operations */ .durable_cookie_fn = NULL, .durable_disconnect_fn = NULL, .durable_reconnect_fn = NULL, }; static_decl_vfs; NTSTATUS vfs_glusterfs_init(TALLOC_CTX *ctx) { return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "glusterfs", &glusterfs_fns); }