/*
Unix SMB/CIFS implementation.
Wrap GlusterFS GFAPI calls in vfs functions.
Copyright (c) 2013 Anand Avati <avati@redhat.com>
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 <http://www.gnu.org/licenses/>.
*/
/**
* @file vfs_glusterfs.c
* @author Anand Avati <avati@redhat.com>
* @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 <stdio.h>
#include "api/glfs.h"
#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"
#define DEFAULT_VOLFILE_SERVER "localhost"
static int read_fd = -1;
static int write_fd = -1;
static struct tevent_fd *aio_read_event = NULL;
/**
* 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);
}
}
}
static int vfs_gluster_set_volfile_servers(glfs_t *fs,
const char *volfile_servers)
{
char *server = NULL;
int server_count = 0;
int 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 %d %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 %d out of %d servers parsed\n",
server_count - server_success, server_count);
re
|