]> granicus.if.org Git - apache/blobdiff - modules/cache/mod_file_cache.c
*) continued header revamping
[apache] / modules / cache / mod_file_cache.c
index 9793dd9e35830fe811b5906ee20f89216dacbc0e..6667f1ce7fb078ccdf97181a7cb077ec9a8fcda2 100644 (file)
     an extra stat() that's a waste.
 */
 
-#ifdef HAVE_STDIOP_H
-#include <stdio.h>
-#endif
-#ifdef HAVE_SYS_TYPES_H
+#include "apr.h"
+#include "apr_mmap.h"
+#include "apr_strings.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#if APR_HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
 
 #define CORE_PRIVATE
 
 #include "http_protocol.h"
 #include "http_request.h"
 #include "http_core.h"
-#include "apr_mmap.h"
-#include "apr_strings.h"
 
-module MODULE_VAR_EXPORT file_cache_module;
-static int once_through = 0;
+module AP_MODULE_DECLARE_DATA file_cache_module;
 
 typedef struct {
 #if APR_HAS_SENDFILE
     apr_file_t *file;
 #endif
-    char *filename;
+    const char *filename;
     apr_finfo_t finfo;
     int is_mmapped;
 #if APR_HAS_MMAP
@@ -154,40 +152,10 @@ static void *create_server_config(apr_pool_t *p, server_rec *s)
 {
     a_server_config *sconf = apr_palloc(p, sizeof(*sconf));
 
-    sconf->files = apr_make_array(p, 20, sizeof(a_file));
+    sconf->files = apr_array_make(p, 20, sizeof(a_file));
     return sconf;
 }
 
-static apr_status_t open_file(apr_file_t **file, const char *filename, int flg1, int flg2, 
-                             apr_pool_t *p)
-{
-    apr_status_t rv;
-#ifdef WIN32
-    /* The Windows file needs to be opened for overlapped i/o, which APR doesn't
-     * support.
-     */
-    HANDLE hFile;
-    hFile = CreateFile(filename,          /* pointer to name of the file */
-                       GENERIC_READ,      /* access (read-write) mode */
-                       FILE_SHARE_READ,   /* share mode */
-                       NULL,              /* pointer to security attributes */
-                       OPEN_EXISTING,     /* how to create */
-                       FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, /* file attributes */
-                       NULL);            /* handle to file with attributes to copy */
-    if (hFile != INVALID_HANDLE_VALUE) {
-        rv = apr_put_os_file(file, &hFile, p);
-    }
-    else {
-        rv = GetLastError();
-        *file = NULL;
-    }
-#else
-    rv = apr_open(file, filename, flg1, flg2, p);
-#endif
-
-    return rv;
-}
-
 static apr_status_t cleanup_file_cache(void *sconfv)
 {
     a_server_config *sconf = sconfv;
@@ -204,7 +172,7 @@ static apr_status_t cleanup_file_cache(void *sconfv)
         else 
 #endif 
 #if APR_HAS_SENDFILE
-            apr_close(file->file); 
+            apr_file_close(file->file); 
 #endif
            ++file;
            --n;
@@ -215,6 +183,12 @@ static apr_status_t cleanup_file_cache(void *sconfv)
 static const char *cachefile(cmd_parms *cmd, void *dummy, const char *filename)
 
 {
+    /* ToDo:
+     * Disable the file cache on a Windows 9X box. APR_HAS_SENDFILE will be
+     * defined in an Apache for Windows build, but apr_sendfile returns
+     * APR_ENOTIMPL on Windows 9X because TransmitFile is not available.
+     */
+
 #if APR_HAS_SENDFILE
     a_server_config *sconf;
     a_file *new_file;
@@ -224,35 +198,33 @@ static const char *cachefile(cmd_parms *cmd, void *dummy, const char *filename)
 
     /* canonicalize the file name? */
     /* os_canonical... */
-    if (apr_stat(&tmp.finfo, filename, cmd->temp_pool) != APR_SUCCESS) { 
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-           "file_cache: unable to stat(%s), skipping", filename);
+    /* XXX: uh... yea, or expect them to be -very- accurate typists */
+    if ((rc = apr_stat(&tmp.finfo, filename, APR_FINFO_MIN, 
+                       cmd->temp_pool)) != APR_SUCCESS) {
+       ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
+           "mod_file_cache: unable to stat(%s), skipping", filename);
        return NULL;
     }
     if (tmp.finfo.filetype != APR_REG) {
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-           "file_cache: %s isn't a regular file, skipping", filename);
+       ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
+           "mod_file_cache: %s isn't a regular file, skipping", filename);
        return NULL;
     }
 
-    /* Note: open_file should call apr_open for Unix and CreateFile for Windows.
-     * The Windows file needs to be opened for async I/O to allow multiple threads
-     * to serve it up at once.
-     */
-    rc = open_file(&fd, filename, APR_READ, APR_OS_DEFAULT, cmd->pool);
+    rc = apr_file_open(&fd, filename, APR_READ | APR_XTHREAD, APR_OS_DEFAULT, cmd->pool);
     if (rc != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
-                     "file_cache: unable to open(%s, O_RDONLY), skipping", filename);
+                     "mod_file_cache: unable to open(%s, O_RDONLY), skipping", filename);
        return NULL;
     }
     tmp.file = fd;
     tmp.filename = apr_pstrdup(cmd->pool, filename);
     sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
-    new_file = apr_push_array(sconf->files);
+    new_file = apr_array_push(sconf->files);
     *new_file = tmp;
     if (sconf->files->nelts == 1) {
        /* first one, register the cleanup */
-       apr_register_cleanup(cmd->pool, sconf, cleanup_file_cache, apr_null_cleanup);
+       apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null);
     }
 
     new_file->is_mmapped = FALSE;
@@ -260,7 +232,7 @@ static const char *cachefile(cmd_parms *cmd, void *dummy, const char *filename)
     return NULL;
 #else
     /* Sendfile not supported on this platform */
-    ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+    ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
                  "mod_file_cache: unable to cache file: %s. Sendfile is not supported on this OS", filename);
     return NULL;
 #endif
@@ -273,39 +245,42 @@ static const char *mmapfile(cmd_parms *cmd, void *dummy, const char *filename)
     a_file *new_file;
     a_file tmp;
     apr_file_t *fd = NULL;
+    apr_status_t rc;
+    const char *fspec;
 
-    if (apr_stat(&tmp.finfo, filename, cmd->temp_pool) != APR_SUCCESS) { 
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+    fspec = ap_os_case_canonical_filename(cmd->pool, filename);
+    if ((rc = apr_stat(&tmp.finfo, fspec, APR_FINFO_MIN, 
+                       cmd->temp_pool)) != APR_SUCCESS) {
+       ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
            "mod_file_cache: unable to stat(%s), skipping", filename);
        return NULL;
     }
     if ((tmp.finfo.filetype) != APR_REG) {
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+       ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
            "mod_file_cache: %s isn't a regular file, skipping", filename);
        return NULL;
     }
-    if (apr_open(&fd, filename, APR_READ, APR_OS_DEFAULT, cmd->temp_pool) 
-                != APR_SUCCESS) { 
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-           "mod_file_cache: unable to open(%s, O_RDONLY), skipping", filename);
+    if ((rc = apr_file_open(&fd, fspec, APR_READ, APR_OS_DEFAULT, 
+                       cmd->temp_pool)) != APR_SUCCESS) { 
+       ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
+                     "mod_file_cache: unable to open %s, skipping", 
+                     filename);
        return NULL;
     }
-    if (apr_mmap_create(&tmp.mm, fd, 0, tmp.finfo.size, cmd->pool) != APR_SUCCESS) { 
-       int save_errno = errno;
-       apr_close(fd);
-       errno = save_errno;
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+    if ((rc = apr_mmap_create(&tmp.mm, fd, 0, tmp.finfo.size, APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) { 
+       apr_file_close(fd);
+       ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
            "mod_file_cache: unable to mmap %s, skipping", filename);
        return NULL;
     }
-    apr_close(fd);
-    tmp.filename = apr_pstrdup(cmd->pool, filename);
+    apr_file_close(fd);
+    tmp.filename = fspec;
     sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
-    new_file = apr_push_array(sconf->files);
+    new_file = apr_array_push(sconf->files);
     *new_file = tmp;
     if (sconf->files->nelts == 1) {
        /* first one, register the cleanup */
-       apr_register_cleanup(cmd->pool, sconf, cleanup_file_cache, apr_null_cleanup); 
+       apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null); 
     }
 
     new_file->is_mmapped = TRUE;
@@ -333,8 +308,6 @@ static void file_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
     a_file *elts;
     int nelts;
 
-    once_through++;
-
     /* sort the elements of the main_server, by filename */
     sconf = ap_get_module_config(s->module_config, &file_cache_module);
     elts = (a_file *)sconf->files->elts;
@@ -384,42 +357,45 @@ static int file_cache_xlat(request_rec *r)
 }
 
 
-static int mmap_handler(request_rec *r, a_file *file, int rangestatus)
+static int mmap_handler(request_rec *r, a_file *file)
 {
 #if APR_HAS_MMAP
-    if (!rangestatus) {
-        ap_send_mmap (file->mm, r, 0, file->finfo.size);
-    }
-    else {
-        apr_size_t length;
-        apr_off_t offset;
-        while (ap_each_byterange(r, &offset, &length)) {
-            ap_send_mmap(file->mm, r, offset, length);
-        }
-    }
+    ap_send_mmap (file->mm, r, 0, file->finfo.size);
 #endif
     return OK;
 }
 
-static int sendfile_handler(request_rec *r, a_file *file, int rangestatus)
+static int sendfile_handler(request_rec *r, a_file *file)
 {
 #if APR_HAS_SENDFILE
-    apr_size_t length, nbytes;
-    apr_off_t offset = 0;
+    apr_size_t nbytes;
     apr_status_t rv = APR_EINIT;
 
-    if (!rangestatus) {
-        rv = ap_send_fd(file->file, r, 0, file->finfo.size, &nbytes);
-    }
-    else {
-        while (ap_each_byterange(r, &offset, &length)) {
-            if ((rv = ap_send_fd(file->file, r, offset, length, &nbytes)) != APR_SUCCESS)
-                break;
-        }
+    /* A cached file handle (more importantly, its file pointer) is 
+     * shared by all threads in the process. The file pointer will 
+     * be corrupted if multiple threads attempt to read from the 
+     * cached file handle. The sendfile API does not rely on the position 
+     * of the file pointer instead taking explicit file offset and 
+     * length arguments. 
+     *
+     * We should call ap_send_fd with a cached file handle IFF 
+     * we are CERTAIN the file will be served with apr_sendfile(). 
+     * The presense of an AP_FTYPE_FILTER in the filter chain nearly
+     * guarantees that apr_sendfile will NOT be used to send the file.
+     * Furthermore, AP_FTYPE_CONTENT filters will be at the beginning
+     * of the chain, so it should suffice to just check the first 
+     * filter in the chain. If the first filter is not a content filter, 
+     * assume apr_sendfile() will be used to send the content.
+     */
+    if (r->output_filters && r->output_filters->frec) {
+        if (r->output_filters->frec->ftype == AP_FTYPE_CONTENT)
+            return DECLINED;
     }
+
+
+    rv = ap_send_fd(file->file, r, 0, file->finfo.size, &nbytes);
     if (rv != APR_SUCCESS) {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
-                      "mod_file_cache: sendfile_handler error serving file: %s", r->filename);
+        /* ap_send_fd will log the error */
         return HTTP_INTERNAL_SERVER_ERROR;
     }
 #endif
@@ -429,9 +405,16 @@ static int sendfile_handler(request_rec *r, a_file *file, int rangestatus)
 static int file_cache_handler(request_rec *r) 
 {
     a_file *match;
-    int rangestatus, errstatus;
+    int errstatus;
     int rc = OK;
 
+    /* XXX: not sure if this is right yet
+     * see comment in http_core.c:default_handler
+     */
+    if (ap_strcmp_match(r->handler, "*/*")) {
+        return DECLINED;
+    }
+
     /* we don't handle anything but GET */
     if (r->method_number != M_GET) return DECLINED;
 
@@ -454,20 +437,19 @@ static int file_cache_handler(request_rec *r)
     ap_update_mtime(r, match->finfo.mtime);
     ap_set_last_modified(r);
     ap_set_etag(r);
-    if (((errstatus = ap_meets_conditions(r)) != OK)
-       || (errstatus = ap_set_content_length (r, match->finfo.size))) {
-           return errstatus;
+    if ((errstatus = ap_meets_conditions(r)) != OK) {
+       return errstatus;
     }
+    ap_set_content_length(r, match->finfo.size);
 
-    rangestatus = ap_set_byterange(r);
     ap_send_http_header(r);
 
     /* Call appropriate handler */
     if (!r->header_only) {    
         if (match->is_mmapped == TRUE)
-            rc = mmap_handler(r, match, rangestatus);
+            rc = mmap_handler(r, match);
         else
-            rc = sendfile_handler(r, match, rangestatus);
+            rc = sendfile_handler(r, match);
     }
 
     return rc;
@@ -476,31 +458,26 @@ static int file_cache_handler(request_rec *r)
 static command_rec file_cache_cmds[] =
 {
 AP_INIT_ITERATE("cachefile", cachefile, NULL, RSRC_CONF,
-     "A space seperated list of files to add to the file handle cache at config time"),
+     "A space separated list of files to add to the file handle cache at config time"),
 AP_INIT_ITERATE("mmapfile", mmapfile, NULL, RSRC_CONF,
-     "A space seperated list of files to mmap at config time"),
+     "A space separated list of files to mmap at config time"),
     {NULL}
 };
 
-static void register_hooks(void)
+static void register_hooks(apr_pool_t *p)
 {
-    ap_hook_post_config(file_cache_post_config, NULL, NULL, AP_HOOK_MIDDLE);
-    ap_hook_translate_name(file_cache_xlat, NULL, NULL, AP_HOOK_MIDDLE);
+    ap_hook_handler(file_cache_handler, NULL, NULL, APR_HOOK_LAST);
+    ap_hook_post_config(file_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_translate_name(file_cache_xlat, NULL, NULL, APR_HOOK_MIDDLE);
     /* This trick doesn't work apparently because the translate hooks
        are single shot. If the core_hook returns OK, then our hook is 
        not called.
-    ap_hook_translate_name(file_cache_xlat, aszPre, NULL, AP_HOOK_MIDDLE); 
+    ap_hook_translate_name(file_cache_xlat, aszPre, NULL, APR_HOOK_MIDDLE); 
     */
 
 }
 
-static const handler_rec file_cache_handlers[] =
-{
-    { "*/*", file_cache_handler },
-    { NULL }
-};
-
-module MODULE_VAR_EXPORT file_cache_module =
+module AP_MODULE_DECLARE_DATA file_cache_module =
 {
     STANDARD20_MODULE_STUFF,
     NULL,                     /* create per-directory config structure */
@@ -508,6 +485,5 @@ module MODULE_VAR_EXPORT file_cache_module =
     create_server_config,     /* create per-server config structure */
     NULL,                     /* merge per-server config structures */
     file_cache_cmds,          /* command handlers */
-    file_cache_handlers,      /* handlers */
     register_hooks            /* register hooks */
 };