/* Copyright (c) 2008-2009 Z RESEARCH, Inc. This file is part of GlusterFS. GlusterFS 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. GlusterFS 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 . */ #ifndef CORE_PRIVATE #define CORE_PRIVATE #endif #include #include #include #include #include #include #include #include #include #include #include #define GLUSTERFS_INVALID_LOGLEVEL "mod_glusterfs: Unrecognized log-level "\ "\"%s\", possible values are \"DEBUG|"\ "WARNING|ERROR|CRITICAL|NONE\"\n" #define GLUSTERFS_HANDLER "glusterfs-handler" #define GLUSTERFS_CHUNK_SIZE 131072 module MODULE_VAR_EXPORT glusterfs_module; /*TODO: verify error returns to server core */ typedef struct glusterfs_dir_config { char *logfile; char *loglevel; char *specfile; char *mount_dir; char *buf; size_t xattr_file_size; uint32_t cache_timeout; } glusterfs_dir_config_t; typedef struct glusterfs_async_local { int op_ret; int op_errno; char async_read_complete; off_t length; off_t read_bytes; glusterfs_iobuf_t *buf; request_rec *request; pthread_mutex_t lock; pthread_cond_t cond; }glusterfs_async_local_t; #define GLUSTERFS_CMD_PERMS ACCESS_CONF static glusterfs_dir_config_t * mod_glusterfs_dconfig(request_rec *r) { glusterfs_dir_config_t *dir_config = NULL; if (r->per_dir_config != NULL) { dir_config = ap_get_module_config (r->per_dir_config, &glusterfs_module); } return dir_config; } static const char *add_xattr_file_size(cmd_parms *cmd, void *dummy, char *arg) { glusterfs_dir_config_t *dir_config = dummy; dir_config->xattr_file_size = atoi (arg); return NULL; } static const char *set_cache_timeout(cmd_parms *cmd, void *dummy, char *arg) { glusterfs_dir_config_t *dir_config = dummy; dir_config->cache_timeout = atoi (arg); return NULL; } static const char *set_loglevel(cmd_parms *cmd, void *dummy, char *arg) { glusterfs_dir_config_t *dir_config = dummy; char *error = NULL; if (strncasecmp (arg, "DEBUG", strlen ("DEBUG")) && strncasecmp (arg, "WARNING", strlen ("WARNING")) && strncasecmp (arg, "CRITICAL", strlen ("CRITICAL")) && strncasecmp (arg, "NONE", strlen ("NONE")) && strncasecmp (arg, "ERROR", strlen ("ERROR"))) error = GLUSTERFS_INVALID_LOGLEVEL; else dir_config->loglevel = arg; return error; } static const char *add_logfile(cmd_parms *cmd, void *dummy, char *arg) { glusterfs_dir_config_t *dir_config = dummy; dir_config->logfile = arg; return NULL; } static const char *add_specfile(cmd_parms *cmd, void *dummy, char *arg) { glusterfs_dir_config_t *dir_config = dummy; dir_config->specfile = arg; return NULL; } static void * mod_glusterfs_create_dir_config(pool *p, char *dirspec) { glusterfs_dir_config_t *dir_config = NULL; dir_config = (glusterfs_dir_config_t *) ap_pcalloc(p, sizeof(*dir_config)); dir_config->mount_dir = dirspec; dir_config->logfile = dir_config->specfile = (char *)0; dir_config->loglevel = "warning"; dir_config->cache_timeout = 0; dir_config->buf = NULL; return (void *) dir_config; } static void mod_glusterfs_child_init(server_rec *s, pool *p) { void **urls = NULL; int n, i; core_server_config *mod_core_config = ap_get_module_config (s->module_config, &core_module); glusterfs_dir_config_t *dir_config = NULL; glusterfs_init_params_t params = {0, }; n = mod_core_config->sec_url->nelts; urls = (void **)mod_core_config->sec_url->elts; for (i = 0; i < n; i++) { dir_config = ap_get_module_config (urls[i], &glusterfs_module); if (dir_config) { memset (¶ms, 0, sizeof (params)); params.logfile = dir_config->logfile; params.loglevel = dir_config->loglevel; params.lookup_timeout = dir_config->cache_timeout; params.stat_timeout = dir_config->cache_timeout; params.specfile = dir_config->specfile; glusterfs_mount (dir_config->mount_dir, ¶ms); } dir_config = NULL; } } static void mod_glusterfs_child_exit(server_rec *s, pool *p) { void **urls = NULL; int n, i; core_server_config *mod_core_config = NULL; glusterfs_dir_config_t *dir_config = NULL; mod_core_config = ap_get_module_config (s->module_config, &core_module); n = mod_core_config->sec_url->nelts; urls = (void **)mod_core_config->sec_url->elts; for (i = 0; i < n; i++) { dir_config = ap_get_module_config (urls[i], &glusterfs_module); if (dir_config) { glusterfs_umount (dir_config->mount_dir); } } } static int mod_glusterfs_fixup(request_rec *r) { glusterfs_dir_config_t *dir_config = NULL; int access_status; int ret; char *path = NULL; dir_config = mod_glusterfs_dconfig(r); if (dir_config && dir_config->mount_dir && !(strncmp (ap_pstrcat (r->pool, dir_config->mount_dir, "/", NULL), r->uri, strlen (dir_config->mount_dir) + 1) && !r->handler)) r->handler = ap_pstrdup (r->pool, GLUSTERFS_HANDLER); if (!r->handler || (r->handler && strcmp (r->handler, GLUSTERFS_HANDLER))) return DECLINED; path = r->uri; memset (&r->finfo, 0, sizeof (r->finfo)); dir_config->buf = calloc (1, dir_config->xattr_file_size); if (!dir_config->buf) { return HTTP_INTERNAL_SERVER_ERROR; } ret = glusterfs_get (path, dir_config->buf, dir_config->xattr_file_size, &r->finfo); if (ret == -1 || r->finfo.st_size > dir_config->xattr_file_size || S_ISDIR (r->finfo.st_mode)) { free (dir_config->buf); dir_config->buf = NULL; if (ret == -1) { int error = HTTP_NOT_FOUND; char *emsg = NULL; if (r->path_info == NULL) { emsg = ap_pstrcat(r->pool, strerror (errno), r->filename, NULL); } else { emsg = ap_pstrcat(r->pool, strerror (errno), r->filename, r->path_info, NULL); } ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, "%s", emsg); if (errno != ENOENT) { error = HTTP_INTERNAL_SERVER_ERROR; } return error; } } if (r->uri && strlen (r->uri) && r->uri[strlen(r->uri) - 1] == '/') r->handler = NULL; r->filename = ap_pstrcat (r->pool, r->filename, r->path_info, NULL); if ((access_status = ap_find_types(r)) != 0) { return DECLINED; } return OK; } int mod_glusterfs_readv_async_cbk (int32_t op_ret, int32_t op_errno, glusterfs_iobuf_t *buf, void *cbk_data) { glusterfs_async_local_t *local = cbk_data; pthread_mutex_lock (&local->lock); { local->async_read_complete = 1; local->buf = buf; local->op_ret = op_ret; local->op_errno = op_errno; pthread_cond_signal (&local->cond); } pthread_mutex_unlock (&local->lock); return 0; } /* use read_async just to avoid memcpy of read buffer in libglusterfsclient */ static int mod_glusterfs_read_async (request_rec *r, glusterfs_file_t fd, off_t offset, off_t length) { glusterfs_async_local_t local; off_t end; int nbytes; int complete; pthread_cond_init (&local.cond, NULL); pthread_mutex_init (&local.lock, NULL); memset (&local, 0, sizeof (local)); local.request = r; if (length > 0) end = offset + length; do { glusterfs_iobuf_t *buf; int i; if (length > 0) { nbytes = end - offset; if (nbytes > GLUSTERFS_CHUNK_SIZE) nbytes = GLUSTERFS_CHUNK_SIZE; } else nbytes = GLUSTERFS_CHUNK_SIZE; glusterfs_read_async(fd, nbytes, offset, mod_glusterfs_readv_async_cbk, (void *)&local); pthread_mutex_lock (&local.lock); { while (!local.async_read_complete) { pthread_cond_wait (&local.cond, &local.lock); } local.async_read_complete = 0; buf = local.buf; if (length < 0) complete = (local.op_ret <= 0); else { local.read_bytes += local.op_ret; complete = ((local.read_bytes == length) || (local.op_ret < 0)); } } pthread_mutex_unlock (&local.lock); for (i = 0; i < buf->count; i++) { if (ap_rwrite (buf->vector[i].iov_base, buf->vector[i].iov_len, r) < 0) { local.op_ret = -1; complete = 1; break; } } glusterfs_free (buf); offset += nbytes; } while (!complete); return (local.op_ret < 0 ? SERVER_ERROR : OK); } static int mod_glusterfs_handler(request_rec *r) { glusterfs_dir_config_t *dir_config; char *path = NULL; int error = OK; int rangestatus = 0; int errstatus = OK; glusterfs_file_t fd; if (!r->handler || (r->handler && strcmp (r->handler, GLUSTERFS_HANDLER))) return DECLINED; if (r->uri[0] == '\0' || r->uri[strlen(r->uri) - 1] == '/') { return DECLINED; } dir_config = mod_glusterfs_dconfig (r); if (r->method_number != M_GET) { return METHOD_NOT_ALLOWED; } ap_update_mtime(r, r->finfo.st_mtime); ap_set_last_modified(r); ap_set_etag(r); ap_table_setn(r->headers_out, "Accept-Ranges", "bytes"); if (((errstatus = ap_meets_conditions(r)) != OK) || (errstatus = ap_set_content_length(r, r->finfo.st_size))) { return errstatus; } rangestatus = ap_set_byterange(r); ap_send_http_header(r); if (r->finfo.st_size <= dir_config->xattr_file_size && dir_config->buf) { if (!r->header_only) { error = OK; ap_log_rerror (APLOG_MARK, APLOG_NOTICE, r, "fetching data from glusterfs through " "xattr interface\n"); if (!rangestatus) { if (ap_rwrite (dir_config->buf, r->finfo.st_size, r) < 0) { error = HTTP_INTERNAL_SERVER_ERROR; } } else { long offset, length; while (ap_each_byterange (r, &offset, &length)) { if (ap_rwrite (dir_config->buf + offset, length, r) < 0) { error = HTTP_INTERNAL_SERVER_ERROR; break; } } } } free (dir_config->buf); dir_config->buf = NULL; return error; } path = r->uri; fd = glusterfs_open (path , O_RDONLY, 0); if (fd == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "file permissions deny server access: %s", r->filename); return FORBIDDEN; } if (!r->header_only) { if (!rangestatus) { mod_glusterfs_read_async (r, fd, 0, -1); } else { long offset, length; while (ap_each_byterange(r, &offset, &length)) { mod_glusterfs_read_async (r, fd, offset, length); } } } glusterfs_close (fd); return error; } static const command_rec mod_glusterfs_cmds[] = { {"GlusterfsLogfile", add_logfile, NULL, GLUSTERFS_CMD_PERMS, TAKE1, "Glusterfs Logfile"}, {"GlusterfsLoglevel", set_loglevel, NULL, GLUSTERFS_CMD_PERMS, TAKE1, "Glusterfs Loglevel:anyone of none, critical, error, warning, debug"}, {"GlusterfsCacheTimeout", set_cache_timeout, NULL, GLUSTERFS_CMD_PERMS, TAKE1, "Timeout value in seconds for caching lookups and stats"}, {"GlusterfsVolumeSpecfile", add_specfile, NULL, GLUSTERFS_CMD_PERMS, TAKE1, "Glusterfs Specfile required to access contents of this directory"}, {"GlusterfsXattrFileSize", add_xattr_file_size, NULL, GLUSTERFS_CMD_PERMS, TAKE1, "Maximum size of the file to be fetched using xattr interface of " "glusterfs"}, {NULL} }; static const handler_rec mod_glusterfs_handlers[] = { {GLUSTERFS_HANDLER, mod_glusterfs_handler}, {NULL} }; module glusterfs_module = { STANDARD_MODULE_STUFF, NULL, mod_glusterfs_create_dir_config, /* per-directory config creator */ NULL, NULL, /* server config creator */ NULL, /* server config merger */ mod_glusterfs_cmds, /* command table */ mod_glusterfs_handlers, /* [7] list of handlers */ NULL, /* [2] filename-to-URI translation */ NULL, /* [5] check/validate user_id */ NULL, /* [6] check user_id is valid *here* */ NULL, /* [4] check access by host address */ NULL, /* [7] MIME type checker/setter */ mod_glusterfs_fixup, /* [8] fixups */ NULL, /* [10] logger */ #if MODULE_MAGIC_NUMBER >= 19970103 NULL, /* [3] header parser */ #endif #if MODULE_MAGIC_NUMBER >= 19970719 mod_glusterfs_child_init, /* process initializer */ #endif #if MODULE_MAGIC_NUMBER >= 19970728 mod_glusterfs_child_exit, /* process exit/cleanup */ #endif #if MODULE_MAGIC_NUMBER >= 19970902 NULL /* [1] post read_request handling */ #endif };