View | Details | Raw Unified | Return to bug 39380
Collapse All | Expand All

(-)../dist/modules/cache/mod_cache.c (-9 / +63 lines)
Lines 323-329 static int cache_save_filter(ap_filter_t Link Here
323
    cache_server_conf *conf;
323
    cache_server_conf *conf;
324
    const char *cc_out, *cl;
324
    const char *cc_out, *cl;
325
    const char *exps, *lastmods, *dates, *etag;
325
    const char *exps, *lastmods, *dates, *etag;
326
    apr_time_t exp, date, lastmod, now;
326
    apr_time_t exp, date, lastmod, now, staleexp=APR_DATE_BAD;
327
    apr_off_t size;
327
    apr_off_t size;
328
    cache_info *info = NULL;
328
    cache_info *info = NULL;
329
    char *reason;
329
    char *reason;
Lines 606-611 static int cache_save_filter(ap_filter_t Link Here
606
            /* Oh, hey.  It isn't that stale!  Yay! */
606
            /* Oh, hey.  It isn't that stale!  Yay! */
607
            cache->handle = cache->stale_handle;
607
            cache->handle = cache->stale_handle;
608
            info = &cache->handle->cache_obj->info;
608
            info = &cache->handle->cache_obj->info;
609
            /* Save stale expiry timestamp for later perusal */
610
            staleexp = info->expire;
609
            rv = OK;
611
            rv = OK;
610
        }
612
        }
611
        else if (!r->header_only) {
613
        else if (!r->header_only) {
Lines 763-776 static int cache_save_filter(ap_filter_t Link Here
763
        ap_cache_accept_headers(cache->handle, r, 1);
765
        ap_cache_accept_headers(cache->handle, r, 1);
764
    }
766
    }
765
767
766
    /* Write away header information to cache. It is possible that we are
768
    rv = APR_EGENERAL;
767
     * trying to update headers for an entity which has already been cached.
769
    if(conf->relaxupdates && cache->stale_handle && 
768
     *
770
            staleexp != APR_DATE_BAD && now < staleexp) 
769
     * This may fail, due to an unwritable cache area. E.g. filesystem full,
771
    {
770
     * permissions problems or a read-only (re)mount. This must be handled
772
        /* Avoid storing on-disk headers that are never used. When the
771
     * later.
773
         * following conditions are fulfilled:
772
     */
774
         * - The body is NOT stale (ie. HTTP_NOT_MODIFIED when revalidating)
773
    rv = cache->provider->store_headers(cache->handle, r, info);
775
         * - The on-disk header hasn't expired.
776
         * - The request has max-age=0
777
         * Then there is no use to update the on-disk header since it won't be
778
         * used by other max-age=0 requests since they are always revalidated,
779
         * and we know it's likely there will be more max-age=0 requests since
780
         * objects tend to have the same access pattern.
781
         */
782
        const char *cc_req;
783
        char *val;
784
785
        cc_req = apr_table_get(r->headers_in, "Cache-Control");
786
        if(cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val) &&
787
                val != NULL && apr_atoi64(val) == 0) 
788
        {
789
            /* Yay, we can skip storing the on-disk header */
790
            rv = APR_SUCCESS;
791
        }
792
    }
793
    if(rv != APR_SUCCESS) {
794
        /* Write away header information to cache. It is possible that we are
795
         * trying to update headers for an entity which has already been cached.
796
         *
797
         * This may fail, due to an unwritable cache area. E.g. filesystem full,
798
         * permissions problems or a read-only (re)mount. This must be handled
799
         * later.
800
         */
801
        rv = cache->provider->store_headers(cache->handle, r, info);
802
    }
774
803
775
    /* Did we just update the cached headers on a revalidated response?
804
    /* Did we just update the cached headers on a revalidated response?
776
     *
805
     *
Lines 926-931 static void * create_cache_config(apr_po Link Here
926
    /* flag indicating that query-string should be ignored when caching */
955
    /* flag indicating that query-string should be ignored when caching */
927
    ps->ignorequerystring = 0;
956
    ps->ignorequerystring = 0;
928
    ps->ignorequerystring_set = 0;
957
    ps->ignorequerystring_set = 0;
958
    /* Whether to relax header updates on max-age=0 accesses */
959
    ps->relaxupdates = 0;
960
    ps->relaxupdates_set = 0;
929
    return ps;
961
    return ps;
930
}
962
}
931
963
Lines 975-980 static void * merge_cache_config(apr_poo Link Here
975
        (overrides->ignorequerystring_set == 0)
1007
        (overrides->ignorequerystring_set == 0)
976
        ? base->ignorequerystring
1008
        ? base->ignorequerystring
977
        : overrides->ignorequerystring;
1009
        : overrides->ignorequerystring;
1010
    ps->relaxupdates  =
1011
        (overrides->relaxupdates_set == 0)
1012
        ? base->relaxupdates
1013
        : overrides->relaxupdates;
978
    return ps;
1014
    return ps;
979
}
1015
}
980
static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
1016
static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
Lines 1166-1171 static const char *set_cache_ignore_quer Link Here
1166
    return NULL;
1202
    return NULL;
1167
}
1203
}
1168
1204
1205
static const char *set_cache_relaxupdates(cmd_parms *parms, void *dummy,
1206
                                          int flag)
1207
{
1208
    cache_server_conf *conf;
1209
1210
    conf =
1211
        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1212
                                                  &cache_module);
1213
    conf->relaxupdates = flag;
1214
    conf->relaxupdates_set = 1;
1215
    return NULL;
1216
}
1217
1169
static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
1218
static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
1170
                             apr_pool_t *ptemp, server_rec *s)
1219
                             apr_pool_t *ptemp, server_rec *s)
1171
{
1220
{
Lines 1221-1226 static const command_rec cache_cmds[] = Link Here
1221
    AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF,
1270
    AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF,
1222
                  "The factor used to estimate Expires date from "
1271
                  "The factor used to estimate Expires date from "
1223
                  "LastModified date"),
1272
                  "LastModified date"),
1273
    AP_INIT_FLAG("CacheRelaxUpdates", set_cache_relaxupdates,
1274
                 NULL, RSRC_CONF,
1275
                 "Optimize for non-transparent caches by relaxing the "
1276
                 "requirement that max-age=0 forces an update of headers "
1277
                 "stored in cache"),
1224
    {NULL}
1278
    {NULL}
1225
};
1279
};
1226
1280
(-)../dist/modules/cache/mod_cache.h (+3 lines)
Lines 153-158 typedef struct { Link Here
153
    /** ignore query-string when caching */
153
    /** ignore query-string when caching */
154
    int ignorequerystring;
154
    int ignorequerystring;
155
    int ignorequerystring_set;
155
    int ignorequerystring_set;
156
    /* Relax header updates on max-age=0 accesses */
157
    int relaxupdates;
158
    int relaxupdates_set;
156
} cache_server_conf;
159
} cache_server_conf;
157
160
158
/* cache info information */
161
/* cache info information */
(-)../dist/modules/cache/mod_disk_cache.c (-518 / +2330 lines)
Lines 22-54 Link Here
22
#include "util_filter.h"
22
#include "util_filter.h"
23
#include "util_script.h"
23
#include "util_script.h"
24
#include "util_charset.h"
24
#include "util_charset.h"
25
#include "ap_mpm.h"
26
#include "mpm_common.h"
25
27
26
/*
28
/*
27
 * mod_disk_cache: Disk Based HTTP 1.1 Cache.
29
 * mod_disk_cache: Disk Based HTTP 1.1 Cache.
28
 *
30
 *
29
 * Flow to Find the .data file:
31
 * Flow to Find the right cache file:
30
 *   Incoming client requests URI /foo/bar/baz
32
 *   Incoming client requests an URL
31
 *   Generate <hash> off of /foo/bar/baz
33
 *   Generate <hash>.header from URL
32
 *   Open <hash>.header
34
 *   Open <hash>.header
33
 *   Read in <hash>.header file (may contain Format #1 or Format #2)
35
 *   Read in <hash>.header file format identifier, which might be:
34
 *   If format #1 (Contains a list of Vary Headers):
36
 *      VARY_FORMAT_VERSION - Vary headers
35
 *      Use each header name (from .header) with our request values (headers_in) to
37
 *      DISK_FORMAT_VERSION - Metadata and headers for a cached file
36
 *      regenerate <hash> using HeaderName+HeaderValue+.../foo/bar/baz
38
 *      Anything else       - Unknown header format, remove and return.
37
 *      re-read in <hash>.header (must be format #2)
38
 *   read in <hash>.data
39
 *
39
 *
40
 * Format #1:
40
 *   If VARY_FORMAT_VERSION (Contains a list of Vary Headers):
41
 *      Use each header name with our request values (headers_in) to
42
 *      regenerate <hash>.header using HeaderName+HeaderValue+URL,
43
 *      open it, read format (must be DISK_FORMAT_VERSION).
44
 *
45
 * VARY_FORMAT_VERSION:
41
 *   apr_uint32_t format;
46
 *   apr_uint32_t format;
42
 *   apr_time_t expire;
47
 *   apr_time_t expire;
43
 *   apr_array_t vary_headers (delimited by CRLF)
48
 *   apr_array_t vary_headers (delimited by CRLF)
44
 *
49
 *
45
 * Format #2:
50
 * DISK_FORMAT_VERSION:
46
 *   disk_cache_info_t (first sizeof(apr_uint32_t) bytes is the format)
51
 *   disk_cache_info_t
47
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
52
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
48
 *   r->headers_out (delimited by CRLF)
53
 *   bodyfile (dobj->bodyfile) [length is in disk_cache_info_t->bodyname_len]
49
 *   CRLF
54
 *   optional filename (r->filename)
50
 *   r->headers_in (delimited by CRLF)
55
 *                      [length is in disk_cache_info_t->filename_len]
51
 *   CRLF
56
 *   r->headers_out (see on disk header format below)
57
 *   r->headers_in
58
 *
59
 * On disk headers are stored in the following format:
60
 *   apr_uint32_t totsize; - size of headers to follow
61
 *   totsize amount of headers, HeaderA\0ValueA\0...HeaderN\0ValueN\0
52
 */
62
 */
53
63
54
module AP_MODULE_DECLARE_DATA disk_cache_module;
64
module AP_MODULE_DECLARE_DATA disk_cache_module;
Lines 62-246 static apr_status_t recall_body(cache_ha Link Here
62
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
72
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
63
                               apr_file_t *file);
73
                               apr_file_t *file);
64
74
65
/*
66
 * Local static functions
67
 */
68
75
69
static char *header_file(apr_pool_t *p, disk_cache_conf *conf,
76
#define CACHE_LOOP_INCTIME(x) x <<= 1
70
                         disk_cache_object_t *dobj, const char *name)
77
#define CACHE_LOOP_DECTIME(x) x >>= 1
71
{
78
72
    if (!dobj->hashfile) {
79
static void cache_loop_sleep(apr_interval_time_t *t) {
73
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
80
74
                                                conf->dirlength, name);
81
    if(*t < CACHE_LOOP_MINSLEEP) {
82
        *t = CACHE_LOOP_MINSLEEP;
83
    }
84
    else if(*t > CACHE_LOOP_MAXSLEEP) {
85
        *t = CACHE_LOOP_MAXSLEEP;
75
    }
86
    }
76
87
77
    if (dobj->prefix) {
88
    apr_sleep(*t);
78
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
79
                           dobj->hashfile, CACHE_HEADER_SUFFIX, NULL);
80
     }
81
     else {
82
        return apr_pstrcat(p, conf->cache_root, "/", dobj->hashfile,
83
                           CACHE_HEADER_SUFFIX, NULL);
84
     }
85
}
89
}
86
90
87
static char *data_file(apr_pool_t *p, disk_cache_conf *conf,
91
88
                       disk_cache_object_t *dobj, const char *name)
92
/*
93
 * Modified file bucket implementation to be able to deliver files
94
 * while caching.
95
 */
96
97
/* Derived from apr_buckets_file.c */
98
99
#define BUCKET_IS_DISKCACHE(e)        ((e)->type == &bucket_type_diskcache)
100
static const apr_bucket_type_t bucket_type_diskcache;
101
102
static void diskcache_bucket_destroy(void *data)
89
{
103
{
90
    if (!dobj->hashfile) {
104
    diskcache_bucket_data *f = data;
91
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
92
                                                conf->dirlength, name);
93
    }
94
105
95
    if (dobj->prefix) {
106
    if (apr_bucket_shared_destroy(f)) {
96
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
107
        /* no need to close files here; it will get
97
                           dobj->hashfile, CACHE_DATA_SUFFIX, NULL);
108
         * done automatically when the pool gets cleaned up */
98
     }
109
        apr_bucket_free(f);
99
     else {
110
    }
100
        return apr_pstrcat(p, conf->cache_root, "/", dobj->hashfile,
101
                           CACHE_DATA_SUFFIX, NULL);
102
     }
103
}
111
}
104
112
105
static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
113
114
/* The idea here is to convert diskcache buckets to regular file buckets
115
   as data becomes available */
116
static apr_status_t diskcache_bucket_read(apr_bucket *e, const char **str,
117
                                          apr_size_t *len, 
118
                                          apr_read_type_e block)
106
{
119
{
120
    diskcache_bucket_data *a = e->data;
121
    apr_file_t *f = a->fd;
122
    apr_bucket *b = NULL;
123
    char *buf;
107
    apr_status_t rv;
124
    apr_status_t rv;
108
    char *p;
125
    apr_finfo_t finfo;
126
    apr_size_t filelength = e->length; /* bytes remaining in file past offset */
127
    apr_off_t fileoffset = e->start;
128
    apr_size_t available;
129
    apr_time_t start = apr_time_now();
130
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
131
    apr_int32_t flags;
132
#endif
133
134
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
135
    if ((flags = apr_file_flags_get(f)) & APR_XTHREAD) {
136
        /* this file descriptor is shared across multiple threads and
137
         * this OS doesn't support that natively, so as a workaround
138
         * we must reopen the file into a->readpool */
139
        const char *fname;
140
        apr_file_name_get(&fname, f);
141
142
        rv = apr_file_open(&f, fname, (flags & ~APR_XTHREAD), 0, a->readpool);
143
        if (rv != APR_SUCCESS)
144
            return rv;
145
146
        a->fd = f;
147
    }
148
#endif
149
150
    /* in case we die prematurely */
151
    *str = NULL;
152
    *len = 0;
153
154
    /* DEBUG
155
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
156
            "disk_cache: Called diskcache_bucket_read");
157
     */
158
159
    while(1) {
160
        /* Figure out how big the file is right now, sit here until
161
           it's grown enough or we get bored */
162
        rv = apr_file_info_get(&finfo, 
163
                        APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_NLINK, f);
164
        if(rv != APR_SUCCESS) {
165
            return rv;
166
        }
109
167
110
    for (p = (char*)file + conf->cache_root_len + 1;;) {
168
        if(finfo.size >= fileoffset + MIN(filelength, CACHE_BUCKET_MINCHUNK)) {
111
        p = strchr(p, '/');
112
        if (!p)
113
            break;
169
            break;
114
        *p = '\0';
170
        }
171
172
        /* No use to even wait for a deleted file */
173
        if(finfo.nlink == 0) {
174
            return APR_EGENERAL;
175
        }
176
177
        if(block == APR_NONBLOCK_READ) {
178
            return APR_EAGAIN;
179
        }
115
180
116
        rv = apr_dir_make(file,
181
        /* Check for timeout */
117
                          APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
182
        if(finfo.mtime < (apr_time_now() - a->updtimeout) ) {
118
        if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
183
            return APR_EGENERAL;
119
            /* XXX */
184
        }
185
        /* If we have progress within half the timeout period, return what
186
           we have so far */
187
        if(finfo.size > fileoffset &&
188
                start < (apr_time_now() - a->updtimeout/2) ) 
189
        {
190
            break;
120
        }
191
        }
121
        *p = '/';
192
122
        ++p;
193
        /* Increase loop delay on each pass */
194
        cache_loop_sleep(&(a->polldelay));
195
        CACHE_LOOP_INCTIME(a->polldelay);
196
    }
197
    /* Decrease the loop delay a notch so the stored value is the actual
198
       delay needed */
199
    CACHE_LOOP_DECTIME(a->polldelay);
200
201
    /* Convert this bucket to a zero-length heap bucket so we won't be called
202
       again */
203
    buf = apr_bucket_alloc(0, e->list);
204
    apr_bucket_heap_make(e, buf, 0, apr_bucket_free);
205
206
    /* Wrap as much as possible into a regular file bucket */
207
    available = MIN(filelength, finfo.size-fileoffset);
208
    b = apr_bucket_file_create(f, fileoffset, available, a->readpool, e->list);
209
    APR_BUCKET_INSERT_AFTER(e, b);
210
211
    /* DEBUG
212
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
213
            "disk_cache: diskcache_bucket_read: Converted to regular file"
214
            " off %" APR_OFF_T_FMT " len %" APR_SIZE_T_FMT,
215
            fileoffset, available);
216
     */
217
218
219
    /* Put any remains in yet another bucket */
220
    if(available < filelength) {
221
        e=b;
222
        /* for efficiency, we can just build a new apr_bucket struct
223
         * to wrap around the existing bucket */
224
        b = apr_bucket_alloc(sizeof(*b), e->list);
225
        b->start  = fileoffset + available;
226
        b->length = filelength - available;
227
        b->data   = a;
228
        b->type   = &bucket_type_diskcache;
229
        b->free   = apr_bucket_free;
230
        b->list   = e->list;
231
        APR_BUCKET_INSERT_AFTER(e, b);
232
    }
233
    else {
234
        diskcache_bucket_destroy(a);
123
    }
235
    }
236
237
    *str = buf;
238
    return APR_SUCCESS;
124
}
239
}
125
240
126
/* htcacheclean may remove directories underneath us.
241
static apr_bucket * diskcache_bucket_make(apr_bucket *b,
127
 * So, we'll try renaming three times at a cost of 0.002 seconds.
242
                                                apr_file_t *fd,
128
 */
243
                                                apr_off_t offset,
129
static apr_status_t safe_file_rename(disk_cache_conf *conf,
244
                                                apr_size_t len, 
130
                                     const char *src, const char *dest,
245
                                                apr_interval_time_t timeout,
131
                                     apr_pool_t *pool)
246
                                                apr_pool_t *p)
132
{
247
{
133
    apr_status_t rv;
248
    diskcache_bucket_data *f;
134
249
135
    rv = apr_file_rename(src, dest, pool);
250
    f = apr_bucket_alloc(sizeof(*f), b->list);
251
    f->fd = fd;
252
    f->readpool = p;
253
    f->updtimeout = timeout;
254
    f->polldelay = 0;
136
255
137
    if (rv != APR_SUCCESS) {
256
    b = apr_bucket_shared_make(b, f, offset, len);
138
        int i;
257
    b->type = &bucket_type_diskcache;
139
258
140
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
259
    return b;
141
            /* 1000 micro-seconds aka 0.001 seconds. */
260
}
142
            apr_sleep(1000);
143
261
144
            mkdir_structure(conf, dest, pool);
262
static apr_bucket * diskcache_bucket_create(apr_file_t *fd,
263
                                                  apr_off_t offset,
264
                                                  apr_size_t len, 
265
                                                  apr_interval_time_t timeout,
266
                                                  apr_pool_t *p,
267
                                                  apr_bucket_alloc_t *list)
268
{
269
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
145
270
146
            rv = apr_file_rename(src, dest, pool);
271
    APR_BUCKET_INIT(b);
272
    b->free = apr_bucket_free;
273
    b->list = list;
274
    return diskcache_bucket_make(b, fd, offset, len, timeout, p);
275
}
276
277
278
/* FIXME: This is probably only correct for the first case, that seems
279
   to be the one that occurs all the time... */
280
static apr_status_t diskcache_bucket_setaside(apr_bucket *data, 
281
                                              apr_pool_t *reqpool)
282
{
283
    diskcache_bucket_data *a = data->data;
284
    apr_file_t *fd = NULL;
285
    apr_file_t *f = a->fd;
286
    apr_pool_t *curpool = apr_file_pool_get(f);
287
288
    if (apr_pool_is_ancestor(curpool, reqpool)) {
289
        return APR_SUCCESS;
290
    }
291
292
    if (!apr_pool_is_ancestor(a->readpool, reqpool)) {
293
        /* FIXME: Figure out what needs to be done here */
294
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
295
                "disk_cache: diskcache_bucket_setaside: FIXME1");
296
        a->readpool = reqpool;
297
    }
298
299
    /* FIXME: Figure out what needs to be done here */
300
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
301
            "disk_cache: diskcache_bucket_setaside: FIXME2");
302
303
    apr_file_setaside(&fd, f, reqpool);
304
    a->fd = fd;
305
    return APR_SUCCESS;
306
}
307
308
static const apr_bucket_type_t bucket_type_diskcache = {
309
    "DISKCACHE", 5, APR_BUCKET_DATA,
310
    diskcache_bucket_destroy,
311
    diskcache_bucket_read,
312
    diskcache_bucket_setaside,
313
    apr_bucket_shared_split,
314
    apr_bucket_shared_copy
315
};
316
317
/* From apr_brigade.c */
318
319
/* A "safe" maximum bucket size, 1Gb */
320
#define MAX_BUCKET_SIZE (0x40000000)
321
322
static apr_bucket * diskcache_brigade_insert(apr_bucket_brigade *bb,
323
                                                   apr_file_t *f, apr_off_t
324
                                                   start, apr_off_t length,
325
                                                   apr_interval_time_t timeout,
326
                                                   apr_pool_t *p)
327
{
328
    apr_bucket *e;
329
330
    if (length < MAX_BUCKET_SIZE) {
331
        e = diskcache_bucket_create(f, start, (apr_size_t)length, timeout, p, 
332
                bb->bucket_alloc);
333
    }
334
    else {
335
        /* Several buckets are needed. */        
336
        e = diskcache_bucket_create(f, start, MAX_BUCKET_SIZE, timeout, p, 
337
                bb->bucket_alloc);
338
339
        while (length > MAX_BUCKET_SIZE) {
340
            apr_bucket *ce;
341
            apr_bucket_copy(e, &ce);
342
            APR_BRIGADE_INSERT_TAIL(bb, ce);
343
            e->start += MAX_BUCKET_SIZE;
344
            length -= MAX_BUCKET_SIZE;
147
        }
345
        }
346
        e->length = (apr_size_t)length; /* Resize just the last bucket */
148
    }
347
    }
149
348
150
    return rv;
349
    APR_BRIGADE_INSERT_TAIL(bb, e);
350
    return e;
151
}
351
}
152
352
153
static apr_status_t file_cache_el_final(disk_cache_object_t *dobj,
353
/* --------------------------------------------------------------- */
154
                                        request_rec *r)
354
355
/*
356
 * Local static functions
357
 */
358
359
static char *cache_file(apr_pool_t *p, disk_cache_conf *conf,
360
                        const char *prefix, const char *name, 
361
                        const char *suffix)
155
{
362
{
156
    /* move the data over */
157
    if (dobj->tfd) {
158
        apr_status_t rv;
159
363
160
        apr_file_close(dobj->tfd);
364
    char *hashfile;
161
365
162
        /* This assumes that the tempfile is on the same file system
366
    hashfile = ap_cache_generate_name(p, conf->dirlevels,
163
         * as the cache_root. If not, then we need a file copy/move
367
                                                conf->dirlength, name);
164
         * rather than a rename.
165
         */
166
        rv = apr_file_rename(dobj->tempfile, dobj->datafile, r->pool);
167
        if (rv != APR_SUCCESS) {
168
            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
169
                         "disk_cache: rename tempfile to datafile failed:"
170
                         " %s -> %s", dobj->tempfile, dobj->datafile);
171
            apr_file_remove(dobj->tempfile, r->pool);
172
        }
173
368
174
        dobj->tfd = NULL;
369
    /* This assumes that we always deal with Vary-stuff if there's a prefix */
370
    if (prefix) {
371
        return apr_pstrcat(p, prefix, CACHE_VDIR_SUFFIX, "/",
372
                hashfile, suffix, NULL);
373
    }
374
    else {
375
        return apr_pstrcat(p, conf->cache_root, "/", hashfile, suffix, NULL);
175
    }
376
    }
176
177
    return APR_SUCCESS;
178
}
377
}
179
378
180
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, request_rec *r)
379
380
static apr_status_t mkdir_structure(const char *file, apr_pool_t *pool)
181
{
381
{
182
    /* Remove the header file and the body file. */
382
    apr_status_t rv;
183
    apr_file_remove(dobj->hdrsfile, r->pool);
383
    char *p;
184
    apr_file_remove(dobj->datafile, r->pool);
384
    int i;
185
385
186
    /* If we opened the temporary data file, close and remove it. */
386
    p = strrchr((char *)file, '/');
187
    if (dobj->tfd) {
387
    if(!p) {
188
        apr_file_close(dobj->tfd);
388
        return APR_EGENERAL;
189
        apr_file_remove(dobj->tempfile, r->pool);
190
        dobj->tfd = NULL;
191
    }
389
    }
192
390
193
    return APR_SUCCESS;
391
    *p = '\0';
194
}
392
393
    /* Be stubborn to overcome racyness when others deletes directories
394
       while we're trying to create them */
395
    for(i=0; i < 10; i++) {
396
        rv = apr_dir_make_recursive(file, 
397
                                    APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
398
        if(rv == APR_SUCCESS) {
399
            break;
400
        }
401
    }
402
    *p = '/';
195
403
404
    return rv;
405
}
196
406
197
/* These two functions get and put state information into the data
407
/* htcacheclean may remove directories underneath us.
198
 * file for an ap_cache_el, this state information will be read
408
 * So, we'll try renaming three times at a cost of 0.002 seconds.
199
 * and written transparent to clients of this module
200
 */
409
 */
201
static int file_cache_recall_mydata(apr_file_t *fd, cache_info *info,
410
static apr_status_t safe_file_rename(const char *src, const char *dest,
202
                                    disk_cache_object_t *dobj, request_rec *r)
411
                                     apr_pool_t *pool)
203
{
412
{
204
    apr_status_t rv;
413
    apr_status_t rv;
205
    char *urlbuff;
206
    disk_cache_info_t disk_info;
207
    apr_size_t len;
208
414
209
    /* read the data from the cache file */
415
    rv = apr_file_rename(src, dest, pool);
210
    len = sizeof(disk_cache_info_t);
416
211
    rv = apr_file_read_full(fd, &disk_info, len, &len);
212
    if (rv != APR_SUCCESS) {
417
    if (rv != APR_SUCCESS) {
213
        return rv;
418
        int i;
419
420
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
421
            mkdir_structure(dest, pool);
422
423
            rv = apr_file_rename(src, dest, pool);
424
425
            if(rv != APR_SUCCESS) {
426
                /* 1000 micro-seconds aka 0.001 seconds. */
427
                apr_sleep(1000);
428
            }
429
        }
214
    }
430
    }
215
431
216
    /* Store it away so we can get it later. */
432
    return rv;
217
    dobj->disk_info = disk_info;
433
}
218
434
219
    info->status = disk_info.status;
435
/* Close fd, remove file if it was opened for writing */
220
    info->date = disk_info.date;
436
static void close_and_rm(apr_file_t *fd, const char *file, request_rec *r)
221
    info->expire = disk_info.expire;
437
{
222
    info->request_time = disk_info.request_time;
438
    apr_int32_t flags = apr_file_flags_get(fd);
223
    info->response_time = disk_info.response_time;
439
224
440
    apr_file_close(fd);
225
    /* Note that we could optimize this by conditionally doing the palloc
441
    if(flags & APR_FOPEN_WRITE) {
226
     * depending upon the size. */
442
        apr_file_remove(file, r->pool);
227
    urlbuff = apr_palloc(r->pool, disk_info.name_len + 1);
443
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
228
    len = disk_info.name_len;
444
                     "disk_cache: close_and_rm: Removed %s",
229
    rv = apr_file_read_full(fd, urlbuff, len, &len);
445
                     file);
230
    if (rv != APR_SUCCESS) {
231
        return rv;
232
    }
446
    }
233
    urlbuff[disk_info.name_len] = '\0';
447
}
234
448
235
    /* check that we have the same URL */
449
236
    /* Would strncmp be correct? */
450
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, 
237
    if (strcmp(urlbuff, dobj->name) != 0) {
451
                                            request_rec *r)
238
        return APR_EGENERAL;
452
{
453
    /* Only remove files that are opened for write when called, files
454
       opened only for reading must be explicitly removed */
455
    if(dobj->hfd) {
456
        close_and_rm(dobj->hfd, dobj->hdrsfile, r);
457
        dobj->hfd = NULL;
458
    }
459
    if(dobj->bfd) {
460
        close_and_rm(dobj->bfd, dobj->bodyfile, r);
461
        dobj->bfd = NULL;
462
    }
463
    if (dobj->tfd) {
464
        close_and_rm(dobj->tfd, dobj->tempfile, r);
465
        dobj->tfd = NULL;
239
    }
466
    }
240
467
241
    return APR_SUCCESS;
468
    return APR_SUCCESS;
242
}
469
}
243
470
471
244
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
472
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
245
                             apr_array_header_t *varray, const char *oldkey)
473
                             apr_array_header_t *varray, const char *oldkey)
246
{
474
{
Lines 319-325 static void tokens_to_array(apr_pool_t * Link Here
319
/*
547
/*
320
 * Hook and mod_cache callback functions
548
 * Hook and mod_cache callback functions
321
 */
549
 */
322
static int create_entity(cache_handle_t *h, request_rec *r, const char *key, apr_off_t len)
550
static int create_entity(cache_handle_t *h, request_rec *r, const char *key, 
551
                         apr_off_t len)
323
{
552
{
324
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
553
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
325
                                                 &disk_cache_module);
554
                                                 &disk_cache_module);
Lines 330-335 static int create_entity(cache_handle_t Link Here
330
        return DECLINED;
559
        return DECLINED;
331
    }
560
    }
332
561
562
    /* Note, len is -1 if unknown so don't trust it too hard */
563
    if (len > conf->maxfs) {
564
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
565
                     "disk_cache: URL %s failed the size check "
566
                     "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
567
                     key, len, conf->maxfs);
568
        return DECLINED;
569
    }
570
    if (len >= 0 && len < conf->minfs) {
571
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
572
                     "disk_cache: URL %s failed the size check "
573
                     "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
574
                     key, len, conf->minfs);
575
        return DECLINED;
576
    }
577
333
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
578
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
334
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
579
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
335
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
580
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
Lines 337-410 static int create_entity(cache_handle_t Link Here
337
    obj->key = apr_pstrdup(r->pool, key);
582
    obj->key = apr_pstrdup(r->pool, key);
338
583
339
    dobj->name = obj->key;
584
    dobj->name = obj->key;
340
    dobj->prefix = NULL;
341
    /* Save the cache root */
585
    /* Save the cache root */
342
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
586
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
343
    dobj->root_len = conf->cache_root_len;
587
    dobj->root_len = conf->cache_root_len;
344
    dobj->datafile = data_file(r->pool, conf, dobj, key);
588
    dobj->hdrsfile = cache_file(r->pool, conf, NULL, key, CACHE_HEADER_SUFFIX);
345
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
346
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
589
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
590
    dobj->initial_size = len;
591
    dobj->file_size = -1;
592
    dobj->lastmod = APR_DATE_BAD;
593
    dobj->updtimeout = conf->updtimeout;
594
    dobj->removedirs = conf->removedirs;
595
    dobj->header_only = r->header_only;
596
    dobj->bytes_sent = 0;
597
598
    if(r->filename != NULL && strlen(r->filename) > 0) {
599
        char buf[34];
600
        char *str;
601
602
        /* When possible, hash the body on dev:inode to minimize file
603
           duplication. */
604
        if( (r->finfo.valid & APR_FINFO_IDENT) == APR_FINFO_IDENT) {
605
            apr_uint64_t device = r->finfo.device; /* Avoid ifdef ... */
606
            apr_uint64_t inode  = r->finfo.inode;  /* ... type-mess */
607
608
            apr_snprintf(buf, sizeof(buf), "%016" APR_UINT64_T_HEX_FMT ":%016" 
609
                         APR_UINT64_T_HEX_FMT, device, inode);
610
            str = buf;
611
        }
612
        else {
613
            str = r->filename;
614
        }
615
        dobj->bodyfile = cache_file(r->pool, conf, NULL, str, 
616
                                    CACHE_BODY_SUFFIX);
617
        dobj->filename = r->filename;
618
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
619
                     "disk_cache: File %s was hashed using %s into %s",
620
                     r->filename, str, dobj->bodyfile);
621
    }
622
    else {
623
        dobj->bodyfile = cache_file(r->pool, conf, NULL, key, 
624
                                    CACHE_BODY_SUFFIX);
625
    }
347
626
348
    return OK;
627
    return OK;
349
}
628
}
350
629
351
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
630
631
static apr_status_t file_read_timeout(apr_file_t *file, char * buf,
632
                                      apr_size_t len, apr_time_t timeout)
352
{
633
{
353
    apr_uint32_t format;
634
    apr_size_t left, done;
354
    apr_size_t len;
355
    const char *nkey;
356
    apr_status_t rc;
357
    static int error_logged = 0;
358
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
359
                                                 &disk_cache_module);
360
    apr_finfo_t finfo;
635
    apr_finfo_t finfo;
361
    cache_object_t *obj;
636
    apr_status_t rc;
362
    cache_info *info;
637
    apr_interval_time_t delay=0;
363
    disk_cache_object_t *dobj;
364
    int flags;
365
638
366
    h->cache_obj = NULL;
639
    done = 0;
640
    left = len;
367
641
368
    /* Look up entity keyed to 'url' */
642
    while(1) {
369
    if (conf->cache_root == NULL) {
643
        rc = apr_file_read_full(file, buf+done, left, &len);
370
        if (!error_logged) {
644
        if (rc == APR_SUCCESS) {
371
            error_logged = 1;
645
           break;
372
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
373
                         "disk_cache: Cannot cache files to disk without a CacheRoot specified.");
374
        }
646
        }
375
        return DECLINED;
647
        done += len;
376
    }
648
        left -= len;
377
649
378
    /* Create and init the cache object */
650
        if(!APR_STATUS_IS_EOF(rc)) {
379
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
651
            return rc;
380
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
652
        }
653
        rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, file);
654
        if(rc != APR_SUCCESS) {
655
           return rc;
656
        }
657
        if(finfo.mtime < (apr_time_now() - timeout) ) {
658
            return APR_ETIMEDOUT;
659
        }
660
        cache_loop_sleep(&delay);
661
        CACHE_LOOP_INCTIME(delay);
662
    }
381
663
382
    info = &(obj->info);
664
    return APR_SUCCESS;
665
}
383
666
384
    /* Open the headers file */
385
    dobj->prefix = NULL;
386
667
387
    /* Save the cache root */
668
static apr_status_t open_header(cache_handle_t *h, request_rec *r, 
388
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
669
                                const char *key, disk_cache_conf *conf)
389
    dobj->root_len = conf->cache_root_len;
670
{
671
    int flags = APR_READ | APR_WRITE | APR_BINARY;
672
    disk_cache_format_t format;
673
    apr_status_t rc;
674
    const char *nkey = key;
675
    disk_cache_info_t disk_info;
676
    cache_object_t *obj = h->cache_obj;
677
    disk_cache_object_t *dobj = obj->vobj;
390
678
391
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
679
    /* Open header read/write so it's easy to rewrite it when needed */
392
    flags = APR_READ|APR_BINARY|APR_BUFFERED;
393
    rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
680
    rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
394
    if (rc != APR_SUCCESS) {
681
    if (rc != APR_SUCCESS) {
395
        return DECLINED;
682
        return CACHE_EDECLINED;
396
    }
683
    }
397
684
398
    /* read the format from the cache file */
685
    /* read the format from the cache file */
399
    len = sizeof(format);
686
    rc = apr_file_read_full(dobj->hfd, &format, sizeof(format), NULL);
400
    apr_file_read_full(dobj->hfd, &format, len, &len);
687
    if(APR_STATUS_IS_EOF(rc)) {
688
        return CACHE_ENODATA;
689
    }
690
    else if(rc != APR_SUCCESS) {
691
        return rc;
692
    }
401
693
694
    /* Vary-files are being written to tmpfile and moved in place, so
695
       the should always be complete */
402
    if (format == VARY_FORMAT_VERSION) {
696
    if (format == VARY_FORMAT_VERSION) {
403
        apr_array_header_t* varray;
697
        apr_array_header_t* varray;
404
        apr_time_t expire;
698
        apr_time_t expire;
699
        char *p;
405
700
406
        len = sizeof(expire);
701
        rc = apr_file_read_full(dobj->hfd, &expire, sizeof(expire), NULL);
407
        apr_file_read_full(dobj->hfd, &expire, len, &len);
702
        if(rc != APR_SUCCESS) {
703
            return rc;
704
        }
408
705
409
        varray = apr_array_make(r->pool, 5, sizeof(char*));
706
        varray = apr_array_make(r->pool, 5, sizeof(char*));
410
        rc = read_array(r, varray, dobj->hfd);
707
        rc = read_array(r, varray, dobj->hfd);
Lines 412-504 static int open_entity(cache_handle_t *h Link Here
412
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
709
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
413
                         "disk_cache: Cannot parse vary header file: %s",
710
                         "disk_cache: Cannot parse vary header file: %s",
414
                         dobj->hdrsfile);
711
                         dobj->hdrsfile);
415
            return DECLINED;
712
            return CACHE_EDECLINED;
416
        }
713
        }
417
        apr_file_close(dobj->hfd);
714
        apr_file_close(dobj->hfd);
418
715
419
        nkey = regen_key(r->pool, r->headers_in, varray, key);
716
        nkey = regen_key(r->pool, r->headers_in, varray, key);
420
717
421
        dobj->hashfile = NULL;
422
        dobj->prefix = dobj->hdrsfile;
718
        dobj->prefix = dobj->hdrsfile;
423
        dobj->hdrsfile = header_file(r->pool, conf, dobj, nkey);
719
        p = strrchr((char *)dobj->prefix, '.');
720
        if(p) {
721
            /* Cut away the suffix */
722
            *p = '\0';
723
        }
724
        dobj->hdrsfile = cache_file(r->pool, conf, dobj->prefix, nkey,
725
                                    CACHE_HEADER_SUFFIX);
424
726
425
        flags = APR_READ|APR_BINARY|APR_BUFFERED;
426
        rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
727
        rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
427
        if (rc != APR_SUCCESS) {
728
        if (rc != APR_SUCCESS) {
428
            return DECLINED;
729
            dobj->hfd = NULL;
730
            return CACHE_EDECLINED;
731
        }
732
        rc = apr_file_read_full(dobj->hfd, &format, sizeof(format), NULL);
733
        if(APR_STATUS_IS_EOF(rc)) {
734
            return CACHE_ENODATA;
735
        }
736
        else if(rc != APR_SUCCESS) {
737
            return rc;
429
        }
738
        }
430
    }
739
    }
431
    else if (format != DISK_FORMAT_VERSION) {
740
432
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
741
    if(format != DISK_FORMAT_VERSION) {
433
                     "cache_disk: File '%s' has a version mismatch. File had version: %d.",
742
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
434
                     dobj->hdrsfile, format);
743
                     "disk_cache: File '%s' had a version mismatch. File had "
435
        return DECLINED;
744
                     "version: %d (current is %d). Deleted.", dobj->hdrsfile,
436
    }
745
                     format, DISK_FORMAT_VERSION);
437
    else {
746
        file_cache_errorcleanup(dobj, r);
438
        apr_off_t offset = 0;
747
        apr_file_remove(dobj->hdrsfile, r->pool);
439
        /* This wasn't a Vary Format file, so we must seek to the
748
        return CACHE_EDECLINED;
440
         * start of the file again, so that later reads work.
441
         */
442
        apr_file_seek(dobj->hfd, APR_SET, &offset);
443
        nkey = key;
444
    }
749
    }
445
750
446
    obj->key = nkey;
751
    obj->key = nkey;
447
    dobj->key = nkey;
448
    dobj->name = key;
752
    dobj->name = key;
449
    dobj->datafile = data_file(r->pool, conf, dobj, nkey);
450
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
451
753
452
    /* Open the data file */
754
    /* read the data from the header file */
453
    flags = APR_READ|APR_BINARY;
755
    rc = apr_file_read_full(dobj->hfd, &disk_info, sizeof(disk_info), NULL);
454
#ifdef APR_SENDFILE_ENABLED
756
    if(APR_STATUS_IS_EOF(rc)) {
455
    flags |= APR_SENDFILE_ENABLED;
757
        return CACHE_ENODATA;
456
#endif
457
    rc = apr_file_open(&dobj->fd, dobj->datafile, flags, 0, r->pool);
458
    if (rc != APR_SUCCESS) {
459
        /* XXX: Log message */
460
        return DECLINED;
461
    }
758
    }
462
759
    else if(rc != APR_SUCCESS) {
463
    rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, dobj->fd);
760
        return rc;
464
    if (rc == APR_SUCCESS) {
465
        dobj->file_size = finfo.size;
466
    }
761
    }
467
762
468
    /* Read the bytes to setup the cache_info fields */
763
    /* Store it away so we can get it later. */
469
    rc = file_cache_recall_mydata(dobj->hfd, info, dobj, r);
764
    dobj->disk_info = disk_info;
470
    if (rc != APR_SUCCESS) {
471
        /* XXX log message */
472
        return DECLINED;
473
    }
474
765
475
    /* Initialize the cache_handle callback functions */
766
    return APR_SUCCESS;
476
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
477
                 "disk_cache: Recalled cached URL info header %s",  dobj->name);
478
    return OK;
479
}
767
}
480
768
481
static int remove_entity(cache_handle_t *h)
769
770
static apr_status_t open_header_timeout(cache_handle_t *h, request_rec *r, 
771
                                const char *key, disk_cache_conf *conf)
482
{
772
{
483
    /* Null out the cache object pointer so next time we start from scratch  */
773
    apr_status_t rc;
484
    h->cache_obj = NULL;
774
    apr_finfo_t finfo;
485
    return OK;
775
    apr_interval_time_t delay = 0;
776
    cache_object_t *obj = h->cache_obj;
777
    disk_cache_object_t *dobj = obj->vobj;
778
779
    while(1) {
780
        if(dobj->hfd) {
781
            apr_file_close(dobj->hfd);
782
            dobj->hfd = NULL;
783
        }
784
        rc = open_header(h, r, key, conf);
785
        if(rc != APR_SUCCESS && rc != CACHE_ENODATA) {
786
            if(rc != CACHE_EDECLINED) {
787
                ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
788
                             "disk_cache: Cannot load header file: %s",
789
                             dobj->hdrsfile);
790
            }
791
            return rc;
792
        }
793
794
        /* Objects with unknown body size will have file_size == -1 until the
795
           entire body is written and the header updated with the actual size.
796
           And since we depend on knowing the body size we wait until the size
797
           is written */
798
        if(rc == APR_SUCCESS && dobj->disk_info.file_size >= 0) {
799
            break;
800
        }
801
        rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->hfd);
802
        if(rc != APR_SUCCESS) {
803
            return rc;
804
        }
805
        if(finfo.mtime < (apr_time_now() - dobj->updtimeout)) {
806
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
807
                         "disk_cache: Timed out waiting for header file %s for "
808
                         "URL %s - caching the body failed?", 
809
                         dobj->hdrsfile, key);
810
            return CACHE_EDECLINED;
811
        }
812
        cache_loop_sleep(&delay);
813
        CACHE_LOOP_INCTIME(delay);
814
    }
815
816
    return APR_SUCCESS;
486
}
817
}
487
818
488
static int remove_url(cache_handle_t *h, apr_pool_t *p)
819
820
static apr_status_t load_header_strings(request_rec *r,
821
                                        disk_cache_object_t *dobj)
489
{
822
{
823
    apr_size_t len;
490
    apr_status_t rc;
824
    apr_status_t rc;
491
    disk_cache_object_t *dobj;
825
    char *urlbuff;
492
826
493
    /* Get disk cache object from cache handle */
827
    if(dobj->disk_info.name_len > MAX_STRING_LEN ||
494
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
828
            dobj->disk_info.bodyname_len > MAX_STRING_LEN ||
495
    if (!dobj) {
829
            dobj->disk_info.filename_len > MAX_STRING_LEN) 
496
        return DECLINED;
830
    {
831
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
832
                "disk_cache: Corrupt cache header for URL %s, deleting: %s",
833
                dobj->name, dobj->hdrsfile);
834
        file_cache_errorcleanup(dobj, r);
835
        apr_file_remove(dobj->hdrsfile, r->pool);
836
        return CACHE_EDECLINED;
497
    }
837
    }
498
838
499
    /* Delete headers file */
839
    /* FIXME: Enforce that url and bodyname is present */
500
    if (dobj->hdrsfile) {
840
501
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
841
842
    len = dobj->disk_info.name_len;
843
    urlbuff = apr_palloc(r->pool, len+1);
844
    if(urlbuff == NULL) {
845
        return APR_ENOMEM;
846
    }
847
848
    rc = file_read_timeout(dobj->hfd, urlbuff, len, dobj->updtimeout);
849
    if (rc == APR_ETIMEDOUT) {
850
        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
851
                     "disk_cache: Timed out waiting for urlbuff for "
852
                     "URL %s - caching failed?",  dobj->name);
853
        return CACHE_EDECLINED;
854
    }
855
    else if(rc != APR_SUCCESS) {
856
        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
857
                     "disk_cache: Error reading urlbuff for URL %s",
858
                     dobj->name);
859
        return CACHE_EDECLINED;
860
    }
861
    urlbuff[len] = '\0';
862
863
    /* check that we have the same URL */
864
    if (strcmp(urlbuff, dobj->name) != 0) {
865
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
866
                     "disk_cache: Cached URL %s didn't match requested "
867
                     "URL %s", urlbuff, dobj->name);
868
        file_cache_errorcleanup(dobj, r);
869
        apr_file_remove(dobj->hdrsfile, r->pool);
870
        apr_file_remove(dobj->bodyfile, r->pool);
871
        return CACHE_EDECLINED;
872
    }
873
874
    /* Read in the file the body is stored in */
875
    len = dobj->disk_info.bodyname_len;
876
    if(len > 0) {
877
        char *bodyfile = apr_palloc(r->pool, len+1);
878
879
        if(bodyfile == NULL) {
880
            return APR_ENOMEM;
881
        }
882
883
        rc = file_read_timeout(dobj->hfd, bodyfile, len, dobj->updtimeout);
884
        if (rc == APR_ETIMEDOUT) {
885
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
886
                         "disk_cache: Timed out waiting for body cache "
887
                         "filename for URL %s - caching failed?", dobj->name);
888
            return CACHE_EDECLINED;
889
        }
890
        else if(rc != APR_SUCCESS) {
891
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
892
                         "disk_cache: Error reading body cache filename for "
893
                         "URL %s", dobj->name);
894
            return CACHE_EDECLINED;
895
        }
896
        bodyfile[len] = '\0';
897
        dobj->bodyfile = apr_pstrcat(r->pool, dobj->root, "/", bodyfile, NULL);
898
    }
899
900
    /* Read in the filename */
901
    len = dobj->disk_info.filename_len;
902
    if(len > 0) {
903
        char *fnamebuf = apr_palloc(r->pool, len+1);
904
905
        if(fnamebuf == NULL) {
906
            return APR_ENOMEM;
907
        }
908
909
        rc = file_read_timeout(dobj->hfd, fnamebuf, len, dobj->updtimeout);
910
        if (rc == APR_ETIMEDOUT) {
911
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
912
                         "disk_cache: Timed out waiting for filename for "
913
                         "URL %s - caching failed?", dobj->name);
914
            return CACHE_EDECLINED;
915
        }
916
        else if(rc != APR_SUCCESS) {
917
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
918
                         "disk_cache: Error reading filename for URL %s",
919
                         dobj->name);
920
            return CACHE_EDECLINED;
921
        }
922
        fnamebuf[len] = '\0';
923
924
        dobj->filename = fnamebuf;
925
        /* We can't set r->filename here because for example mod_rewrite
926
           will exhibit different behaviour compared to a completely
927
           uncached entity (will happen if entity is revalidated for 
928
           example). */
929
        /* Save a pointer to r->filename so we can set it later on in
930
           recall_body which doesn't get r as an argument */
931
        dobj->rfilename = &(r->filename);
932
    }
933
934
    return APR_SUCCESS;
935
}
936
937
938
static apr_status_t open_body_timeout(request_rec *r, cache_object_t *cache_obj)
939
{
940
    apr_status_t rc;
941
    apr_finfo_t finfo;
942
    int flags = APR_READ|APR_BINARY;
943
    apr_interval_time_t delay = 0;
944
    disk_cache_object_t *dobj = (disk_cache_object_t *) cache_obj->vobj;
945
    cache_info *info = &(cache_obj->info);
946
    
947
#if APR_HAS_SENDFILE
948
    core_dir_config *pdconf = ap_get_module_config(r->per_dir_config,
949
                                                   &core_module);
950
    flags |= ((pdconf->enable_sendfile == ENABLE_SENDFILE_OFF)
951
             ? 0 : APR_SENDFILE_ENABLED);
952
#endif  
953
954
    if(dobj->bodyfile == NULL || strlen(dobj->bodyfile) == 0) {
955
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
956
                     "disk_cache: open_body_timeout called with NULL "
957
                     "bodyfile for URL %s",
958
                     dobj->name);
959
        return APR_EGENERAL;
960
    }
961
962
    /* Wait here until we get a body cachefile, data in it, and do quick sanity
963
     * check */
964
965
    while(1) {
966
        if(dobj->bfd == NULL) {
967
            rc = apr_file_open(&dobj->bfd, dobj->bodyfile, flags, 0, r->pool);
968
            if(rc != APR_SUCCESS) {
969
                if(info->response_time < (apr_time_now() - dobj->updtimeout) ) {
970
                    /* This usually means that the body simply wasn't cached,
971
                       due to HEAD requests for example */
972
                    ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, r->server,
973
                                 "disk_cache: Timed out waiting for bodyfile "
974
                                 "%s for URL %s - caching failed?", 
975
                                 dobj->bodyfile, dobj->name);
976
                    return CACHE_EDECLINED;
977
                }
978
                cache_loop_sleep(&delay);
979
                CACHE_LOOP_INCTIME(delay);
980
                continue;
981
            }
982
        }
983
984
        rc = apr_file_info_get(&finfo, APR_FINFO_SIZE | APR_FINFO_CSIZE | 
985
                                       APR_FINFO_MTIME | APR_FINFO_CTIME |
986
                                       APR_FINFO_NLINK, 
987
                               dobj->bfd);
988
        if(rc != APR_SUCCESS && !APR_STATUS_IS_INCOMPLETE(rc)) {
989
            return rc;
990
        }
991
        if(finfo.valid & APR_FINFO_NLINK && finfo.nlink == 0) {
992
            /* This file has been deleted, close it and try again */
993
            apr_file_close(dobj->bfd);
994
            dobj->bfd = NULL;
995
            continue;
996
        }
997
998
        /* XFS on Linux can leave corrupted files behind after a system crash,
999
           these are usually detectable by the fact that csize is smaller than
1000
           the actual filesize. The occurances we've seen has had csize=0.
1001
1002
           Note that we can't simply check for csize<size, due to file systems
1003
           with compression and/or delayed allocation like SUN ZFS.
1004
         */
1005
1006
        /* Check for non-zero sized files with zero consumed size only
1007
           when ctime is older than our update timeout. We use ctime here
1008
           because some filesystems (ZFS) has delayed allocation which means
1009
           we can't rely on csize immediately after file close */
1010
        if(finfo.valid & APR_FINFO_CSIZE && dobj->initial_size > 0 &&
1011
                finfo.csize == 0 &&
1012
                finfo.ctime < (apr_time_now() - dobj->updtimeout))
1013
        {
1014
            dobj->file_size = 0;
1015
        }
1016
        else {
1017
            dobj->file_size = finfo.size;
1018
        }
1019
1020
        /* Note that the body might have been updated by another entity
1021
           that uses the same body, which usually means that we should
1022
           revalidate too. Don't freak out completely when this happens.
1023
           We might have:
1024
           - Body in sync with this header.
1025
           - Body being cached.
1026
           - Body that failed caching.
1027
           - Body newer than this header. 
1028
         */
1029
1030
        if(dobj->initial_size < dobj->file_size) {
1031
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1032
                         "disk_cache: Cached body for too large for URL %s"
1033
                         " - revalidating.", dobj->name);
1034
            apr_file_remove(dobj->hdrsfile, r->pool);
1035
            return CACHE_EDECLINED;
1036
        }
1037
        else if(dobj->initial_size > dobj->file_size) {
1038
            /* Still caching or failed? */
1039
            if(finfo.mtime < (apr_time_now() - dobj->updtimeout) ) {
1040
                ap_log_error(APLOG_MARK, APLOG_INFO, rc, r->server,
1041
                             "disk_cache: Cached body too small for URL %s"
1042
                             " - revalidating.", dobj->name);
1043
                apr_file_remove(dobj->hdrsfile, r->pool);
1044
                return CACHE_EDECLINED;
1045
            }
1046
        }
1047
        else {
1048
            /* If right size, file has either the correct mtime or 
1049
               mtime == ctime which means the mtime isn't set. The latter
1050
               either means there was no Last-Modified available or
1051
               that we're in the window between finished copying and setting
1052
               mtime.
1053
             */
1054
            if(dobj->lastmod != APR_DATE_BAD &&
1055
                    apr_time_sec(finfo.mtime) != apr_time_sec(dobj->lastmod) &&
1056
                    (finfo.mtime != finfo.ctime || 
1057
                     finfo.mtime < (apr_time_now() - dobj->updtimeout)) ) 
1058
            {
1059
                ap_log_error(APLOG_MARK, APLOG_INFO, rc, r->server,
1060
                             "disk_cache: Cached body Last-Modified mismatch "
1061
                             "for URL %s - revalidating.", dobj->name);
1062
                apr_file_remove(dobj->hdrsfile, r->pool);
1063
                return CACHE_EDECLINED;
1064
            }
1065
        }
1066
1067
        if(dobj->file_size > 0) {
1068
            break;
1069
        }
1070
        cache_loop_sleep(&delay);
1071
        CACHE_LOOP_INCTIME(delay);
1072
    }
1073
1074
    return APR_SUCCESS;
1075
}
1076
1077
1078
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
1079
{
1080
    apr_status_t rc;
1081
    disk_cache_object_t *dobj;
1082
    cache_info *info;
1083
    static int error_logged = 0;
1084
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1085
                                                 &disk_cache_module);
1086
1087
    h->cache_obj = NULL;
1088
1089
    /* Look up entity keyed to 'url' */
1090
    if (conf->cache_root == NULL) {
1091
        if (!error_logged) {
1092
            error_logged = 1;
1093
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1094
                         "disk_cache: Cannot cache files to disk without a "
1095
                         "CacheRoot specified.");
1096
        }
1097
        return DECLINED;
1098
    }
1099
1100
    /* Create and init the cache object */
1101
    h->cache_obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
1102
    h->cache_obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
1103
    info = &(h->cache_obj->info);
1104
1105
    /* Save the cache root */
1106
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
1107
    dobj->root_len = conf->cache_root_len;
1108
1109
    dobj->hdrsfile = cache_file(r->pool, conf, NULL, key, CACHE_HEADER_SUFFIX);
1110
1111
    dobj->updtimeout = conf->updtimeout;
1112
    dobj->removedirs = conf->removedirs;
1113
    dobj->header_only = r->header_only;
1114
1115
    /* Open header and read basic info, wait until header contains
1116
       valid size information for the body */
1117
    rc = open_header_timeout(h, r, key, conf);
1118
    if(rc != APR_SUCCESS) {
1119
        if(dobj->hfd != NULL) {
1120
            apr_file_close(dobj->hfd);
1121
            dobj->hfd = NULL;
1122
        }
1123
        return DECLINED;
1124
    }
1125
1126
    info->status = dobj->disk_info.status;
1127
    info->date = dobj->disk_info.date;
1128
    info->expire = dobj->disk_info.expire;
1129
    info->request_time = dobj->disk_info.request_time;
1130
    info->response_time = dobj->disk_info.response_time;
1131
1132
    dobj->lastmod = dobj->disk_info.lastmod;
1133
    dobj->initial_size = (apr_off_t) dobj->disk_info.file_size;
1134
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1135
1136
    /* Load and check strings (URL, bodyfile, filename) */
1137
    rc = load_header_strings(r, dobj);
1138
    if(rc != APR_SUCCESS) {
1139
        if(dobj->hfd != NULL) {
1140
            apr_file_close(dobj->hfd);
1141
            dobj->hfd = NULL;
1142
        }
1143
        return DECLINED;
1144
    }
1145
1146
    /* Only need body cachefile if we have a body and this isn't a HEAD
1147
       request */
1148
    if(dobj->initial_size > 0 && !dobj->header_only) {
1149
        rc = open_body_timeout(r, h->cache_obj);
1150
        if(rc != APR_SUCCESS) {
1151
            if(dobj->hfd != NULL) {
1152
                apr_file_close(dobj->hfd);
1153
                dobj->hfd = NULL;
1154
            }
1155
            if(dobj->bfd != NULL) {
1156
                apr_file_close(dobj->bfd);
1157
                dobj->bfd = NULL;
1158
            }
1159
            return DECLINED;
1160
        }
1161
    }
1162
    else {
1163
        dobj->file_size = 0;
1164
    }
1165
1166
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1167
                 "disk_cache: Recalled status for cached URL %s from file %s",
1168
                 dobj->name, dobj->hdrsfile);
1169
    return OK;
1170
}
1171
1172
1173
static int remove_entity(cache_handle_t *h)
1174
{
1175
    disk_cache_object_t *dobj;
1176
    apr_finfo_t finfo;
1177
    apr_pool_t *p;
1178
    apr_status_t rv;
1179
1180
    /* Get disk cache object from cache handle */
1181
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1182
1183
    /* Null out the cache object pointer so next time we start from scratch */
1184
    h->cache_obj = NULL;
1185
1186
    if(!dobj) {
1187
        return OK;
1188
    }
1189
1190
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1191
            "disk_cache: remove_entity: %s", dobj->name);
1192
1193
    /* We really want to remove the cache files  here since mod_cache has
1194
       deemed it stale, but it seems like an API miss that we don't
1195
       have a pool? And why is this function separate from remove_url?
1196
       Oh well, beware of kludge ;) */
1197
1198
    if(dobj->hfd != NULL) {
1199
        /* Only remove file if fd isn't already unlinked. Not atomic, but
1200
           the best we can do? */
1201
        rv = apr_file_info_get(&finfo, APR_FINFO_NLINK, dobj->hfd);
1202
        if(rv == APR_SUCCESS && finfo.nlink != 0) {
1203
            p = apr_file_pool_get(dobj->hfd);
1204
            apr_file_remove(dobj->hdrsfile, p);
1205
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1206
                         "disk_cache: remove_entity: Deleted %s from cache.",
1207
                         dobj->hdrsfile);
1208
        }
1209
        apr_file_close(dobj->hfd);
1210
        dobj->hfd = NULL;
1211
    }
1212
    if(dobj->bfd != NULL) {
1213
        /* Only remove file if fd isn't already unlinked. Not atomic, but
1214
           the best we can do? */
1215
        rv = apr_file_info_get(&finfo, APR_FINFO_NLINK, dobj->bfd);
1216
        if(rv == APR_SUCCESS && finfo.nlink != 0) {
1217
            p = apr_file_pool_get(dobj->bfd);
1218
            apr_file_remove(dobj->bodyfile, p);
1219
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1220
                    "disk_cache: remove_entity: Deleted %s from cache.",
1221
                    dobj->bodyfile);
1222
        }
1223
        apr_file_close(dobj->bfd);
1224
        dobj->bfd = NULL;
1225
    }
1226
1227
    return OK;
1228
}
1229
1230
1231
/* FIXME: It would make sense to have the errorcleanup and this function
1232
   to be the same */
1233
static int remove_url(cache_handle_t *h, apr_pool_t *p)
1234
{
1235
    apr_status_t rc;
1236
    disk_cache_object_t *dobj;
1237
1238
    /* Get disk cache object from cache handle */
1239
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1240
    if (!dobj) {
1241
        return DECLINED;
1242
    }
1243
1244
    /* Delete headers file */
1245
    if (dobj->hdrsfile) {
1246
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
502
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
1247
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
503
1248
504
        rc = apr_file_remove(dobj->hdrsfile, p);
1249
        rc = apr_file_remove(dobj->hdrsfile, p);
Lines 507-540 static int remove_url(cache_handle_t *h, Link Here
507
             * For reason see log_error_core for the case s == NULL.
1252
             * For reason see log_error_core for the case s == NULL.
508
             */
1253
             */
509
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
1254
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
510
                   "disk_cache: Failed to delete headers file %s from cache.",
1255
                         "disk_cache: Failed to delete headers file %s "
511
                         dobj->hdrsfile);
1256
                         "from cache.", dobj->hdrsfile);
512
            return DECLINED;
1257
            return DECLINED;
513
        }
1258
        }
514
    }
1259
    }
515
1260
516
     /* Delete data file */
1261
    /* Only delete body cache file if it isn't backed by a real file */
517
    if (dobj->datafile) {
1262
    if(!dobj->filename && dobj->bodyfile) {
518
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1263
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
519
                     "disk_cache: Deleting %s from cache.", dobj->datafile);
1264
                     "disk_cache: Deleting %s from cache.", dobj->bodyfile);
520
1265
521
        rc = apr_file_remove(dobj->datafile, p);
1266
        rc = apr_file_remove(dobj->bodyfile, p);
522
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
1267
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
523
            /* Will only result in an output if httpd is started with -e debug.
524
             * For reason see log_error_core for the case s == NULL.
525
             */
526
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
1268
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
527
                      "disk_cache: Failed to delete data file %s from cache.",
1269
                         "disk_cache: Failed to delete body file %s "
528
                         dobj->datafile);
1270
                         "from cache.", dobj->bodyfile);
529
            return DECLINED;
1271
            return DECLINED;
530
        }
1272
        }
531
    }
1273
    }
532
1274
1275
    if(!dobj->removedirs) {
1276
        return OK;
1277
    }
1278
533
    /* now delete directories as far as possible up to our cache root */
1279
    /* now delete directories as far as possible up to our cache root */
534
    if (dobj->root) {
1280
    if (dobj->root) {
535
        const char *str_to_copy;
1281
        const char *str_to_copy;
536
1282
537
        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->datafile;
1283
        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->bodyfile;
538
        if (str_to_copy) {
1284
        if (str_to_copy) {
539
            char *dir, *slash, *q;
1285
            char *dir, *slash, *q;
540
1286
Lines 549-558 static int remove_url(cache_handle_t *h, Link Here
549
             * in the way as far as possible
1295
             * in the way as far as possible
550
             *
1296
             *
551
             * Note: due to the way we constructed the file names in
1297
             * Note: due to the way we constructed the file names in
552
             * header_file and data_file, we are guaranteed that the
1298
             * cache_file, we are guaranteed that the cache_root is suffixed by
553
             * cache_root is suffixed by at least one '/' which will be
1299
             * at least one '/' which will be turned into a terminating null by
554
             * turned into a terminating null by this loop.  Therefore,
1300
             * this loop.  Therefore, we won't either delete or go above our
555
             * we won't either delete or go above our cache root.
1301
             * cache root.
556
             */
1302
             */
557
            for (q = dir + dobj->root_len; *q ; ) {
1303
            for (q = dir + dobj->root_len; *q ; ) {
558
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1304
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
Lines 583-589 static apr_status_t read_array(request_r Link Here
583
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
1329
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
584
        if (rv != APR_SUCCESS) {
1330
        if (rv != APR_SUCCESS) {
585
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1331
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
586
                          "Premature end of vary array.");
1332
                          "disk_cache: Premature end of vary array.");
587
            return rv;
1333
            return rv;
588
        }
1334
        }
589
1335
Lines 624-631 static apr_status_t store_array(apr_file Link Here
624
        iov[1].iov_base = CRLF;
1370
        iov[1].iov_base = CRLF;
625
        iov[1].iov_len = sizeof(CRLF) - 1;
1371
        iov[1].iov_len = sizeof(CRLF) - 1;
626
1372
627
        rv = apr_file_writev(fd, (const struct iovec *) &iov, 2,
1373
        rv = apr_file_writev_full(fd, (const struct iovec *) &iov, 2, &amt);
628
                             &amt);
629
        if (rv != APR_SUCCESS) {
1374
        if (rv != APR_SUCCESS) {
630
            return rv;
1375
            return rv;
631
        }
1376
        }
Lines 634-725 static apr_status_t store_array(apr_file Link Here
634
    iov[0].iov_base = CRLF;
1379
    iov[0].iov_base = CRLF;
635
    iov[0].iov_len = sizeof(CRLF) - 1;
1380
    iov[0].iov_len = sizeof(CRLF) - 1;
636
1381
637
    return apr_file_writev(fd, (const struct iovec *) &iov, 1,
1382
    return apr_file_writev_full(fd, (const struct iovec *) &iov, 1,
638
                         &amt);
1383
                         &amt);
639
}
1384
}
640
1385
641
static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
1386
/* Load table stored by store_table */
1387
static apr_status_t read_table(request_rec *r,
642
                               apr_table_t *table, apr_file_t *file)
1388
                               apr_table_t *table, apr_file_t *file)
643
{
1389
{
644
    char w[MAX_STRING_LEN];
1390
    char *s, *k, *v;
645
    char *l;
1391
    apr_uint32_t totsize = 0;
646
    int p;
647
    apr_status_t rv;
1392
    apr_status_t rv;
648
1393
649
    while (1) {
1394
    rv = apr_file_read_full(file, &totsize, sizeof(totsize), NULL);
650
1395
    if(rv != APR_SUCCESS) {
651
        /* ### What about APR_EOF? */
1396
        return rv;
652
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
1397
    }
653
        if (rv != APR_SUCCESS) {
654
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
655
                          "Premature end of cache headers.");
656
            return rv;
657
        }
658
659
        /* Delete terminal (CR?)LF */
660
1398
661
        p = strlen(w);
1399
    s = apr_palloc(r->pool, totsize);
662
        /* Indeed, the host's '\n':
1400
    if(s == NULL) {
663
           '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
1401
        return APR_ENOMEM;
664
           -- whatever the script generates.
1402
    }
665
        */
666
        if (p > 0 && w[p - 1] == '\n') {
667
            if (p > 1 && w[p - 2] == CR) {
668
                w[p - 2] = '\0';
669
            }
670
            else {
671
                w[p - 1] = '\0';
672
            }
673
        }
674
1403
675
        /* If we've finished reading the headers, break out of the loop. */
1404
    rv = apr_file_read_full(file, s, totsize, NULL);
676
        if (w[0] == '\0') {
1405
    if(rv != APR_SUCCESS) {
677
            break;
1406
        return rv;
678
        }
1407
    }
679
1408
680
#if APR_CHARSET_EBCDIC
1409
    k=s;
681
        /* Chances are that we received an ASCII header text instead of
1410
    while(k < s + totsize) {
682
         * the expected EBCDIC header lines. Try to auto-detect:
1411
        /* FIXME: Do a pointer-loop instead of strlen to make sure we don't
1412
                  walk outside of allocated memory if on-disk data has been
1413
                  corrupted
683
         */
1414
         */
684
        if (!(l = strchr(w, ':'))) {
1415
        v = k + strlen(k) + 1;
685
            int maybeASCII = 0, maybeEBCDIC = 0;
1416
        apr_table_addn(table, k, v);
686
            unsigned char *cp, native;
1417
        k = v + strlen(v) + 1;
687
            apr_size_t inbytes_left, outbytes_left;
688
689
            for (cp = w; *cp != '\0'; ++cp) {
690
                native = apr_xlate_conv_byte(ap_hdrs_from_ascii, *cp);
691
                if (apr_isprint(*cp) && !apr_isprint(native))
692
                    ++maybeEBCDIC;
693
                if (!apr_isprint(*cp) && apr_isprint(native))
694
                    ++maybeASCII;
695
            }
696
            if (maybeASCII > maybeEBCDIC) {
697
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
698
                             "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
699
                             r->filename);
700
                inbytes_left = outbytes_left = cp - w;
701
                apr_xlate_conv_buffer(ap_hdrs_from_ascii,
702
                                      w, &inbytes_left, w, &outbytes_left);
703
            }
704
        }
705
#endif /*APR_CHARSET_EBCDIC*/
706
707
        /* if we see a bogus header don't ignore it. Shout and scream */
708
        if (!(l = strchr(w, ':'))) {
709
            return APR_EGENERAL;
710
        }
711
712
        *l++ = '\0';
713
        while (*l && apr_isspace(*l)) {
714
            ++l;
715
        }
716
717
        apr_table_add(table, w, l);
718
    }
1418
    }
719
1419
720
    return APR_SUCCESS;
1420
    return APR_SUCCESS;
721
}
1421
}
722
1422
1423
723
/*
1424
/*
724
 * Reads headers from a buffer and returns an array of headers.
1425
 * Reads headers from a buffer and returns an array of headers.
725
 * Returns NULL on file error
1426
 * Returns NULL on file error
Lines 730-750 static apr_status_t read_table(cache_han Link Here
730
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
1431
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
731
{
1432
{
732
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1433
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1434
    apr_status_t rv;
1435
    apr_off_t off;
1436
    apr_finfo_t finfo;
1437
    apr_interval_time_t delay = 0;
733
1438
734
    /* This case should not happen... */
1439
    /* This case should not happen... */
735
    if (!dobj->hfd) {
1440
    if (!dobj->hfd) {
736
        /* XXX log message */
1441
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1442
                     "disk_cache: recall_headers called without fd for URL %s",
1443
                     dobj->name);
737
        return APR_NOTFOUND;
1444
        return APR_NOTFOUND;
738
    }
1445
    }
739
1446
740
    h->req_hdrs = apr_table_make(r->pool, 20);
1447
    off = 0;
1448
    rv = apr_file_seek(dobj->hfd, APR_CUR, &off);
1449
    if(rv != APR_SUCCESS) {
1450
        return rv;
1451
    }
1452
741
    h->resp_hdrs = apr_table_make(r->pool, 20);
1453
    h->resp_hdrs = apr_table_make(r->pool, 20);
1454
    h->req_hdrs = apr_table_make(r->pool, 20);
742
1455
743
    /* Call routine to read the header lines/status line */
1456
    while(1) {
744
    read_table(h, r, h->resp_hdrs, dobj->hfd);
1457
        rv = read_table(r, h->resp_hdrs, dobj->hfd);
745
    read_table(h, r, h->req_hdrs, dobj->hfd);
1458
        if(rv != APR_SUCCESS) {
1459
            apr_table_clear(h->resp_hdrs);
1460
        }
1461
        else {
1462
            rv = read_table(r, h->req_hdrs, dobj->hfd);
1463
            if(rv != APR_SUCCESS) {
1464
                apr_table_clear(h->req_hdrs);
1465
            }
1466
        }
1467
        if(rv == APR_SUCCESS) {
1468
            break;
1469
        }
1470
        if(!APR_STATUS_IS_EOF(rv)) {
1471
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
1472
                          "disk_cache: Error reading cache headers "
1473
                          "URL %s", dobj->name);
1474
            if(dobj->hfd != NULL) {
1475
                apr_file_close(dobj->hfd);
1476
                dobj->hfd = NULL;
1477
            }
1478
            if(dobj->bfd != NULL) {
1479
                apr_file_close(dobj->bfd);
1480
                dobj->bfd = NULL;
1481
            }
1482
            return rv;
1483
        }
746
1484
747
    apr_file_close(dobj->hfd);
1485
        /* FIXME: Check if header file deleted (nlinks==0) and reopen it if
1486
         * that's the case */
1487
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->hfd);
1488
        if(rv != APR_SUCCESS ||
1489
                finfo.mtime < (apr_time_now() - dobj->updtimeout) ) 
1490
        {
1491
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1492
                          "disk_cache: Timed out waiting for cache headers "
1493
                          "URL %s", dobj->name);
1494
            if(dobj->hfd != NULL) {
1495
                apr_file_close(dobj->hfd);
1496
                dobj->hfd = NULL;
1497
            }
1498
            if(dobj->bfd != NULL) {
1499
                apr_file_close(dobj->bfd);
1500
                dobj->bfd = NULL;
1501
            }
1502
            return APR_EGENERAL;
1503
        }
1504
        rv = apr_file_seek(dobj->hfd, APR_SET, &off);
1505
        if(rv != APR_SUCCESS) {
1506
            return rv;
1507
        }
1508
        cache_loop_sleep(&delay);
1509
        CACHE_LOOP_INCTIME(delay);
1510
    }
748
1511
749
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1512
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
750
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
1513
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
Lines 755-1055 static apr_status_t recall_body(cache_ha Link Here
755
{
1518
{
756
    apr_bucket *e;
1519
    apr_bucket *e;
757
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1520
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1521
    apr_off_t bytes_already_done;
1522
1523
    if(dobj->hfd != NULL) {
1524
        /* Close header cache file, it won't be needed anymore */
1525
        apr_file_close(dobj->hfd);
1526
        dobj->hfd = NULL;
1527
    }
1528
1529
    if(dobj->initial_size > 0 && !dobj->header_only && dobj->bfd == NULL) {
1530
        /* This should never happen, really... */
1531
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
1532
                     "disk_cache: recall_body: Called but no fd open, URL %s "
1533
                     "from file %s", dobj->name, dobj->bodyfile);
1534
        return APR_EGENERAL;
1535
    }
1536
1537
    /* Restore r->filename if not present */
1538
    if(dobj->filename != NULL && dobj->rfilename != NULL && 
1539
            *(dobj->rfilename) == NULL) 
1540
    {
1541
        *(dobj->rfilename) = dobj->filename;
1542
    }
1543
1544
    /* Insert as much as possible as regular file (ie. sendfile():able) */
1545
    /* We need to make sure to skip the beginning of the file if we've
1546
       already sent some bytes, e.g., due to mod_proxy */
1547
    if(dobj->file_size > dobj->bytes_sent) {
1548
        if(apr_brigade_insert_file(bb, dobj->bfd, dobj->bytes_sent, 
1549
                                   dobj->file_size - dobj->bytes_sent, p) == NULL) 
1550
        {
1551
            return APR_ENOMEM;
1552
        }
1553
        bytes_already_done = dobj->file_size;
1554
    } else {
1555
        bytes_already_done = dobj->bytes_sent;
1556
    }
1557
1558
    /* Insert any remainder as read-while-caching bucket */
1559
    if(bytes_already_done < dobj->initial_size) {
1560
        if(diskcache_brigade_insert(bb, dobj->bfd, bytes_already_done, 
1561
                                    dobj->initial_size - bytes_already_done,
1562
                                    dobj->updtimeout, p
1563
                    ) == NULL) 
1564
        {
1565
            return APR_ENOMEM;
1566
        }
1567
    }
758
1568
759
    e = apr_bucket_file_create(dobj->fd, 0, (apr_size_t) dobj->file_size, p,
760
                               bb->bucket_alloc);
761
    APR_BRIGADE_INSERT_HEAD(bb, e);
762
    e = apr_bucket_eos_create(bb->bucket_alloc);
1569
    e = apr_bucket_eos_create(bb->bucket_alloc);
763
    APR_BRIGADE_INSERT_TAIL(bb, e);
1570
    APR_BRIGADE_INSERT_TAIL(bb, e);
764
1571
1572
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1573
                 "disk_cache: recall_body: Succeeded for URL %s from file %s",
1574
                 dobj->name, dobj->bodyfile);
1575
765
    return APR_SUCCESS;
1576
    return APR_SUCCESS;
766
}
1577
}
767
1578
768
static apr_status_t store_table(apr_file_t *fd, apr_table_t *table)
1579
/* Store table on disk.
1580
 * Format on disk: apr_uint32_t totsize - total size of data following totsize
1581
 *                 totsize of data, consisting of key\0value\0...key\0value\0
1582
 */
1583
static apr_status_t store_table(apr_file_t *fd, apr_table_t *table,
1584
                                request_rec *r)
769
{
1585
{
770
    int i;
1586
    int i, nelts, niov;
771
    apr_status_t rv;
1587
    apr_status_t rv = APR_SUCCESS;
772
    struct iovec iov[4];
1588
    apr_uint32_t totsize = 0;
773
    apr_size_t amt;
774
    apr_table_entry_t *elts;
1589
    apr_table_entry_t *elts;
1590
    struct iovec *iov;
1591
1592
    nelts = apr_table_elts(table)->nelts;
1593
1594
    /* Allocate space for the size-header plus two elements per table entry */
1595
1596
    iov = apr_palloc(r->pool, (1+nelts*2) * sizeof(struct iovec));
1597
    if(iov == NULL) {
1598
        return APR_ENOMEM;
1599
    }
775
1600
776
    elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
1601
    elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
777
    for (i = 0; i < apr_table_elts(table)->nelts; ++i) {
1602
    niov = 1;
1603
    for (i = 0; i < nelts; ++i) {
778
        if (elts[i].key != NULL) {
1604
        if (elts[i].key != NULL) {
779
            iov[0].iov_base = elts[i].key;
1605
            iov[niov].iov_base = elts[i].key;
780
            iov[0].iov_len = strlen(elts[i].key);
1606
            iov[niov].iov_len = strlen(elts[i].key)+1;
781
            iov[1].iov_base = ": ";
1607
            totsize += iov[niov++].iov_len;
782
            iov[1].iov_len = sizeof(": ") - 1;
1608
            iov[niov].iov_base = elts[i].val;
783
            iov[2].iov_base = elts[i].val;
1609
            iov[niov].iov_len = strlen(elts[i].val)+1;
784
            iov[2].iov_len = strlen(elts[i].val);
1610
            totsize += iov[niov++].iov_len;
785
            iov[3].iov_base = CRLF;
1611
        }
786
            iov[3].iov_len = sizeof(CRLF) - 1;
1612
    }
1613
    iov[0].iov_base = (void *) &totsize;
1614
    iov[0].iov_len = sizeof(totsize);
1615
    i=0;
1616
    while(niov > 0) {
1617
        /* Need to write this in chunks, APR_MAX_IOVEC_SIZE is really small
1618
           on some OS's */
1619
        int chunk = MIN(niov, APR_MAX_IOVEC_SIZE);
1620
        apr_size_t amt;
1621
1622
        rv = apr_file_writev_full(fd, (const struct iovec *) &iov[i], chunk,
1623
                                  &amt);
1624
        if(rv != APR_SUCCESS) {
1625
            return rv;
1626
        }
1627
        niov -= chunk;
1628
        i += chunk;
1629
    }
1630
    return rv;
1631
}
1632
1633
1634
static apr_status_t open_new_file(request_rec *r, const char *filename,
1635
                                  apr_file_t **fd, disk_cache_conf *conf)
1636
{
1637
    int flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL;
1638
    apr_status_t rv;
1639
1640
    while(1) {
1641
        rv = apr_file_open(fd, filename, flags, 
1642
                           APR_FPROT_UREAD | APR_FPROT_UWRITE, r->pool);
1643
1644
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1645
                     "disk_cache: open_new_file: Opening %s", filename);
1646
1647
        if(APR_STATUS_IS_EEXIST(rv)) {
1648
            apr_finfo_t finfo;
1649
1650
            rv = apr_stat(&finfo, filename, APR_FINFO_MTIME, r->pool);
1651
            if(APR_STATUS_IS_ENOENT(rv)) {
1652
                /* Someone else has already removed it, try again */
1653
                continue;
1654
            }
1655
            else if(rv != APR_SUCCESS) {
1656
                return rv;
1657
            }
1658
1659
            /* FIXME: We should really check for size and mtime that matches
1660
               the source file too if available */
1661
            if(finfo.mtime < (apr_time_now() - conf->updtimeout) ) {
1662
                /* Something stale that's left around */
1663
1664
                rv = apr_file_remove(filename, r->pool);
1665
                if(rv != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rv)) {
1666
                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1667
                                 "disk_cache: open_new_file: Failed to "
1668
                                 "remove old %s", filename);
1669
                    return rv;
1670
                }
1671
                continue;
1672
            }
1673
            else {
1674
                /* Someone else has just created the file, return identifiable
1675
                   status so calling function can do the right thing */
1676
1677
                return CACHE_EEXIST;
1678
            }
1679
        }
1680
        else if(APR_STATUS_IS_ENOENT(rv)) {
1681
            /* The directory for the file didn't exist */
1682
1683
            rv = mkdir_structure(filename, r->pool);
1684
            if(rv != APR_SUCCESS) {
1685
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1686
                             "disk_cache: open_new_file: Failed to make "
1687
                             "directory for %s", filename);
1688
                return rv;
1689
            }
1690
            continue;
1691
        }
1692
        else if(rv == APR_SUCCESS) {
1693
            return APR_SUCCESS;
1694
        }
1695
        else {
1696
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1697
                         "disk_cache: open_new_file: Failed to open %s",
1698
                         filename);
1699
            return rv;
1700
        }
1701
    }
1702
1703
    /* We should never get here, so */
1704
    return APR_EGENERAL;
1705
}
1706
1707
1708
static apr_status_t store_vary_header(cache_handle_t *h, disk_cache_conf *conf,
1709
                                       request_rec *r, cache_info *info,
1710
                                       const char *varyhdr)
1711
{
1712
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1713
    apr_array_header_t* varray;
1714
    const char *vfile;
1715
    apr_status_t rv;
1716
    int flags;
1717
    disk_cache_format_t format = VARY_FORMAT_VERSION;
1718
    struct iovec iov[2];
1719
    apr_size_t amt;
1720
1721
    /* We should always write the vary format hints to the original header
1722
     * path, otherwise they will never be refreshed.  */
1723
1724
    vfile = dobj->hdrsfile;
1725
1726
    flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL | APR_BUFFERED;
1727
    rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile, flags, r->pool);
1728
    if (rv != APR_SUCCESS) {
1729
        return rv;
1730
    }
1731
1732
    iov[0].iov_base = (void*)&format;
1733
    iov[0].iov_len = sizeof(format);
1734
1735
    iov[1].iov_base = (void*)&info->expire;
1736
    iov[1].iov_len = sizeof(info->expire);
1737
1738
    rv = apr_file_writev_full(dobj->tfd, (const struct iovec *) &iov, 2, &amt);
1739
    if (rv != APR_SUCCESS) {
1740
        file_cache_errorcleanup(dobj, r);
1741
        return rv;
1742
    }
1743
1744
    varray = apr_array_make(r->pool, 6, sizeof(char*));
1745
    tokens_to_array(r->pool, varyhdr, varray);
1746
1747
    rv = store_array(dobj->tfd, varray);
1748
    if (rv != APR_SUCCESS) {
1749
        file_cache_errorcleanup(dobj, r);
1750
        return rv;
1751
    }
1752
1753
    rv = apr_file_close(dobj->tfd);
1754
    dobj->tfd = NULL;
1755
    if (rv != APR_SUCCESS) {
1756
        file_cache_errorcleanup(dobj, r);
1757
        apr_file_remove(dobj->tempfile, r->pool);
1758
        return rv;
1759
    }
1760
1761
    rv = safe_file_rename(dobj->tempfile, vfile, r->pool);
1762
    if (rv != APR_SUCCESS) {
1763
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1764
                     "disk_cache: rename tempfile to varyfile failed: "
1765
                     "%s -> %s", dobj->tempfile, vfile);
1766
        file_cache_errorcleanup(dobj, r);
1767
        apr_file_remove(dobj->tempfile, r->pool);
1768
        return rv;
1769
    }
1770
1771
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1772
1773
    if(dobj->prefix == NULL) {
1774
        const char *tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
1775
        char *p;
1776
1777
        dobj->prefix = dobj->hdrsfile;
1778
        p = strrchr((char *)dobj->prefix, '.');
1779
        if(p) {
1780
            /* Cut away the suffix */
1781
            *p = '\0';
1782
        }
1783
        dobj->hdrsfile = cache_file(r->pool, conf, dobj->prefix, tmp, 
1784
                                    CACHE_HEADER_SUFFIX);
1785
        dobj->bodyfile = cache_file(r->pool, conf, dobj->prefix, tmp, 
1786
                                    CACHE_BODY_SUFFIX);
1787
    }
1788
1789
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1790
                 "disk_cache: Stored vary header for URL %s", dobj->name);
1791
1792
    return APR_SUCCESS;
1793
}
1794
1795
1796
static apr_status_t store_disk_header(disk_cache_object_t *dobj,
1797
                                       request_rec *r, cache_info *info)
1798
{
1799
    disk_cache_format_t format = DISK_FORMAT_VERSION;
1800
    struct iovec iov[5];
1801
    int niov;
1802
    disk_cache_info_t disk_info;
1803
    apr_size_t amt;
1804
    apr_status_t rv;
1805
1806
    disk_info.date = info->date;
1807
    disk_info.expire = info->expire;
1808
    disk_info.entity_version = dobj->disk_info.entity_version++;
1809
    disk_info.request_time = info->request_time;
1810
    disk_info.response_time = info->response_time;
1811
    disk_info.status = info->status;
1812
    disk_info.file_size = dobj->initial_size;
1813
    disk_info.lastmod = dobj->lastmod;
1814
1815
    niov = 0;
1816
    iov[niov].iov_base = (void*)&format;
1817
    iov[niov++].iov_len = sizeof(format);
1818
    iov[niov].iov_base = (void*)&disk_info;
1819
    iov[niov++].iov_len = sizeof(disk_cache_info_t);
1820
1821
    disk_info.name_len = strlen(dobj->name);
1822
    iov[niov].iov_base = (void*)dobj->name;
1823
    iov[niov++].iov_len = disk_info.name_len;
1824
1825
    if(dobj->initial_size > 0) {
1826
        /* We know the bodyfile is root/bodyname ... */
1827
        char *bodyname = (char *) dobj->bodyfile + dobj->root_len + 1;
1828
        disk_info.bodyname_len = strlen(bodyname);
1829
        iov[niov].iov_base = (void*)bodyname;
1830
        iov[niov++].iov_len = disk_info.bodyname_len;
1831
    }
1832
    else {
1833
        disk_info.bodyname_len = 0;
1834
    }
1835
1836
    if(r->filename != NULL && strlen(r->filename) > 0) {
1837
        disk_info.filename_len = strlen(r->filename);
1838
        iov[niov].iov_base = (void*)r->filename;
1839
        iov[niov++].iov_len = disk_info.filename_len;
1840
    }
1841
    else {
1842
        disk_info.filename_len = 0;
1843
    }
1844
1845
    rv = apr_file_writev_full(dobj->hfd, (const struct iovec *) &iov, niov, 
1846
                              &amt);
1847
    if (rv != APR_SUCCESS) {
1848
        file_cache_errorcleanup(dobj, r);
1849
        return rv;
1850
    }
1851
1852
    if (r->headers_out) {
1853
        apr_table_t *headers_out;
1854
1855
        headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
1856
                                                  r->server);
1857
1858
        if (!apr_table_get(headers_out, "Content-Type")
1859
            && r->content_type) {
1860
            apr_table_setn(headers_out, "Content-Type",
1861
                           ap_make_content_type(r, r->content_type));
1862
        }
1863
1864
        headers_out = apr_table_overlay(r->pool, headers_out,
1865
                                        r->err_headers_out);
1866
        rv = store_table(dobj->hfd, headers_out, r);
1867
        if (rv != APR_SUCCESS) {
1868
            file_cache_errorcleanup(dobj, r);
1869
            return rv;
1870
        }
1871
    }
1872
1873
    /* Parse the vary header and dump those fields from the headers_in. */
1874
    /* FIXME: Make call to the same thing cache_select calls to crack Vary. */
1875
    if (r->headers_in) {
1876
        apr_table_t *headers_in;
1877
1878
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
1879
                                                 r->server);
1880
        rv = store_table(dobj->hfd, headers_in, r);
1881
        if (rv != APR_SUCCESS) {
1882
            file_cache_errorcleanup(dobj, r);
1883
            return rv;
1884
        }
1885
    }
1886
1887
    /* Store it away so we can get it later. */
1888
    dobj->disk_info = disk_info;
1889
1890
    return APR_SUCCESS;
1891
}
1892
1893
1894
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, 
1895
                                  cache_info *info)
1896
{
1897
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1898
                                                 &disk_cache_module);
1899
    apr_status_t rv;
1900
    int rewriting;
1901
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1902
    const char *lastmods;
1903
1904
1905
    /* This is flaky... we need to manage the cache_info differently */
1906
    h->cache_obj->info = *info;
1907
1908
    /* Get last-modified timestamp */
1909
    lastmods = apr_table_get(r->err_headers_out, "Last-Modified");
1910
    if (lastmods == NULL) {
1911
        lastmods = apr_table_get(r->headers_out, "Last-Modified");
1912
    }
1913
    if (lastmods != NULL) {
1914
        dobj->lastmod = apr_date_parse_http(lastmods);
1915
    }
1916
1917
    if(dobj->hfd) {
1918
        rewriting = TRUE;
1919
1920
        /* Don't update header on disk if the following is met:
1921
           - The body size is known.
1922
           - If Last-Modified is known, it has to be identical.
1923
           - It's not expired.
1924
           - Date in cached header isn't older than updtimeout.
1925
         */
1926
        if( dobj->disk_info.file_size >= 0 && (dobj->lastmod == APR_DATE_BAD || 
1927
                dobj->lastmod == dobj->disk_info.lastmod) &&
1928
                dobj->disk_info.expire > r->request_time &&
1929
                dobj->disk_info.date > info->date - dobj->updtimeout) 
1930
        {
1931
            dobj->skipstore = TRUE;
1932
1933
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1934
                         "disk_cache: store_headers: Headers current for URL "
1935
                         "%s", dobj->name);
1936
        }
1937
        else {
1938
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1939
                         "disk_cache: Rewriting headers for URL %s", 
1940
                         dobj->name);
1941
        }
1942
    }
1943
    else {
1944
        rewriting = FALSE;
1945
1946
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1947
                     "disk_cache: Storing new headers for URL %s", dobj->name);
1948
    }
1949
1950
    if (r->headers_out) {
1951
        const char *tmp;
1952
1953
        tmp = apr_table_get(r->headers_out, "Vary");
1954
1955
        if (tmp) {
1956
            rv = store_vary_header(h, conf, r, info, tmp);
1957
            if(rv != APR_SUCCESS) {
1958
                return rv;
1959
            }
1960
        }
1961
    } 
1962
1963
    if(dobj->skipstore) {
1964
        apr_file_close(dobj->hfd);
1965
        dobj->hfd = NULL;
1966
        return APR_SUCCESS;
1967
    }
1968
1969
    if(rewriting) {
1970
        apr_finfo_t     finfo;
1971
1972
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->hfd);
1973
        if(rv != APR_SUCCESS) {
1974
            return rv;
1975
        }
787
1976
788
            rv = apr_file_writev(fd, (const struct iovec *) &iov, 4,
1977
        /* FIXME: Isn't this a bit redundant? It probably causes more
789
                                 &amt);
1978
           trouble than it's fixing, especially since we handle it above
1979
           except for looking at mtime */
1980
        /* Don't store disk headers more often than updtimeout */
1981
        if(dobj->disk_info.file_size >= 0 &&
1982
                dobj->disk_info.expire > r->request_time &&
1983
                r->request_time < finfo.mtime + dobj->updtimeout) 
1984
        {
1985
            dobj->skipstore = TRUE;
1986
        }
1987
        else {
1988
            /* This triggers bugs in APR when using APR_BUFFERED */
1989
            apr_off_t off=0;
1990
            rv = apr_file_seek(dobj->hfd, APR_SET, &off);
790
            if (rv != APR_SUCCESS) {
1991
            if (rv != APR_SUCCESS) {
791
                return rv;
1992
                return rv;
792
            }
1993
            }
1994
            rv = apr_file_trunc(dobj->hfd, 0);
1995
            if(rv != APR_SUCCESS) {
1996
                return rv;
1997
            }
793
        }
1998
        }
1999
2000
    }
2001
    else {
2002
        rv = open_new_file(r, dobj->hdrsfile, &(dobj->hfd), conf);
2003
        if(rv == CACHE_EEXIST) {
2004
            dobj->skipstore = TRUE;
2005
        }
2006
        else if(rv != APR_SUCCESS) {
2007
            return rv;
2008
        }
2009
    }
2010
2011
    if(dobj->skipstore) {
2012
        if(dobj->hfd) {
2013
            apr_file_close(dobj->hfd);
2014
            dobj->hfd = NULL;
2015
        }
2016
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2017
                     "disk_cache: Skipping store for URL %s: Someone else "
2018
                     "beat us to it",  dobj->name);
2019
        return APR_SUCCESS;
2020
    }
2021
2022
    rv = store_disk_header(dobj, r, info);
2023
    if(rv != APR_SUCCESS) {
2024
        return rv;
2025
    }
2026
2027
    /* If the body size is unknown, the header file will be rewritten later
2028
       so we can't close it */
2029
    if(dobj->initial_size >= 0) {
2030
        rv = apr_file_close(dobj->hfd);
2031
        dobj->hfd = NULL;
2032
        if(rv != APR_SUCCESS) {
2033
            apr_file_remove(dobj->hdrsfile, r->pool);
2034
            return rv;
2035
        }
2036
    }
2037
2038
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2039
                 "disk_cache: Stored headers for URL %s",  dobj->name);
2040
    return APR_SUCCESS;
2041
}
2042
2043
2044
static apr_status_t check_destfd_timeout(apr_file_t *fd, apr_time_t now,
2045
                                         apr_time_t updtimeout) 
2046
{
2047
    apr_status_t rc;
2048
    apr_finfo_t finfo;
2049
2050
    /* Get mtime and nlink for our opened destfile */
2051
    rc = apr_file_info_get(&finfo, APR_FINFO_MTIME | APR_FINFO_NLINK, fd);
2052
    if(rc != APR_SUCCESS) {
2053
        return rc;
2054
    }
2055
2056
    /* If link count is zero, file is deleted */
2057
    if(finfo.nlink == 0) {
2058
        return APR_ETIMEDOUT;
2059
    }
2060
2061
    /* Check if mtime on destfile shows us having timed out */
2062
    if(now - finfo.mtime > updtimeout) {
2063
        return APR_ETIMEDOUT;
2064
    }
2065
2066
    return APR_SUCCESS;
2067
}
2068
2069
2070
static apr_status_t copy_body(apr_pool_t *p,
2071
                              apr_file_t *srcfd, apr_off_t srcoff, 
2072
                              apr_file_t *destfd, apr_off_t destoff, 
2073
                              apr_off_t len, apr_interval_time_t updtimeout)
2074
{
2075
    apr_status_t rc;
2076
    apr_size_t size;
2077
    apr_finfo_t finfo;
2078
    apr_time_t starttime = apr_time_now();
2079
    apr_time_t last = starttime;
2080
    apr_time_t lastcheck = 0;
2081
    unsigned int i=0, freq=1;
2082
    apr_interval_time_t minintvl = updtimeout/10;
2083
    apr_interval_time_t maxintvl = minintvl*3;
2084
2085
    char *buf = apr_palloc(p, MIN(len, CACHE_BUF_SIZE));
2086
    if (!buf) {
2087
        return APR_ENOMEM;
2088
    }
2089
2090
    if(srcoff != 0) {
2091
        rc = apr_file_seek(srcfd, APR_SET, &srcoff);
2092
        if(rc != APR_SUCCESS) {
2093
            return rc;
2094
        }
2095
    }
2096
2097
    if(destoff != 0) {
2098
        rc = apr_file_seek(destfd, APR_SET, &destoff);
2099
        if(rc != APR_SUCCESS) {
2100
            return rc;
2101
        }
2102
    }
2103
2104
    /* Tried doing this with mmap, but sendfile on Linux got confused when
2105
       sending a file while it was being written to from an mmapped area.
2106
       The traditional way seems to be good enough, and less complex.
2107
     */
2108
    while(len > 0) {
2109
        size=MIN(len, CACHE_BUF_SIZE);
2110
2111
        rc = apr_file_read_full (srcfd, buf, size, NULL);
2112
        if(rc != APR_SUCCESS) {
2113
            return rc;
2114
        }
2115
2116
        /* Do timeout checks before we do the write, this is what other clients
2117
           will see. Don't waste resources by calling apr_time_now() on each
2118
           iteration. */
2119
        if(i++ % freq == 0) {
2120
            apr_time_t now = apr_time_now();
2121
            apr_time_t elapsed = now-last;
2122
2123
            /* Do closer inspection at updtimeout intervals */
2124
            if(now-lastcheck > updtimeout) {
2125
                rc = check_destfd_timeout(destfd, now, updtimeout);
2126
                if(rc != APR_SUCCESS) {
2127
                    return rc;
2128
                }
2129
                lastcheck = now;
2130
            }
2131
2132
            if(elapsed > updtimeout) {
2133
                if(freq > 1) {
2134
                    /* The close inspection above will catch a timeout. 
2135
                       If we get here, make sure we recalibrate at which
2136
                       frequency we should check stuff */
2137
                    freq = 1;
2138
                }
2139
            }
2140
            else if(elapsed < minintvl) {
2141
                freq <<= 1;
2142
                freq |= 1;
2143
            }
2144
            else if(elapsed > maxintvl && freq > 1) {
2145
                freq >>= 1;
2146
            }
2147
            last = now;
2148
        }
2149
2150
        rc = apr_file_write_full(destfd, buf, size, NULL);
2151
        if(rc != APR_SUCCESS) {
2152
            return rc;
2153
        }
2154
        len -= size;
2155
    }
2156
2157
    /* Make sure we are the one having cached the destfile */
2158
    rc = check_destfd_timeout(destfd, apr_time_now(), updtimeout);
2159
    if(rc != APR_SUCCESS) {
2160
        return rc;
2161
    }
2162
2163
    /* Check if file has changed during copying. This is not 100% foolproof
2164
       due to NFS attribute caching when on NFS etc. */
2165
    /* FIXME: Can we assume that we're always copying an entire file? In that
2166
              case we can check if the current filesize matches the length
2167
              we think it is */
2168
    rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, srcfd);
2169
    if(rc != APR_SUCCESS) {
2170
        return rc;
2171
    }
2172
    if(starttime < finfo.mtime) {
2173
        return APR_EGENERAL;
794
    }
2174
    }
795
    iov[0].iov_base = CRLF;
2175
796
    iov[0].iov_len = sizeof(CRLF) - 1;
2176
    return APR_SUCCESS;
797
    rv = apr_file_writev(fd, (const struct iovec *) &iov, 1,
798
                         &amt);
799
    return rv;
800
}
2177
}
801
2178
802
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
2179
2180
/* Provide srcfile and srcinfo containing
2181
   APR_FINFO_INODE|APR_FINFO_MTIME to make sure we have opened the right file
2182
   (someone might have just replaced it which messes up things).
2183
*/
2184
static apr_status_t copy_body_nofd(apr_pool_t *p, const char *srcfile, 
2185
                                   apr_off_t srcoff, apr_finfo_t *srcinfo,
2186
                                   const char *destfile, apr_off_t destoff, 
2187
                                   apr_off_t len, 
2188
                                   apr_interval_time_t updtimeout)
803
{
2189
{
804
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
2190
    apr_status_t rc;
805
                                                 &disk_cache_module);
2191
    apr_file_t *srcfd, *destfd;
806
    apr_status_t rv;
2192
    apr_finfo_t finfo;
807
    apr_size_t amt;
808
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
809
2193
810
    disk_cache_info_t disk_info;
2194
    rc = apr_file_open(&srcfd, srcfile, APR_READ | APR_BINARY, 0, p);
811
    struct iovec iov[2];
2195
    if(rc != APR_SUCCESS) {
2196
        return rc;
2197
    }
2198
    rc = apr_file_info_get(&finfo, APR_FINFO_INODE|APR_FINFO_MTIME, srcfd);
2199
    if(rc != APR_SUCCESS) {
2200
        return rc;
2201
    }
2202
    /* FIXME: Should probably check device too */
2203
    if(srcinfo->inode != finfo.inode || srcinfo->mtime < finfo.mtime) {
2204
        return APR_EGENERAL;
2205
    }
812
2206
813
    /* This is flaky... we need to manage the cache_info differently */
2207
    rc = apr_file_open(&destfd, destfile, APR_WRITE | APR_BINARY, 0, p);
814
    h->cache_obj->info = *info;
2208
    if(rc != APR_SUCCESS) {
2209
        return rc;
2210
    }
815
2211
816
    if (r->headers_out) {
2212
    rc = copy_body(p, srcfd, srcoff, destfd, destoff, len, updtimeout);
817
        const char *tmp;
2213
    apr_file_close(srcfd);
2214
    if(rc != APR_SUCCESS) {
2215
        apr_file_close(destfd);
2216
        return rc;
2217
    }
818
2218
819
        tmp = apr_table_get(r->headers_out, "Vary");
2219
    rc = apr_file_close(destfd);
820
2220
821
        if (tmp) {
2221
    /* Set mtime on file */
822
            apr_array_header_t* varray;
2222
    apr_file_mtime_set(destfile, finfo.mtime, p);
823
            apr_uint32_t format = VARY_FORMAT_VERSION;
2223
2224
    return rc;
2225
}
824
2226
825
            /* If we were initially opened as a vary format, rollback
826
             * that internal state for the moment so we can recreate the
827
             * vary format hints in the appropriate directory.
828
             */
829
            if (dobj->prefix) {
830
                dobj->hdrsfile = dobj->prefix;
831
                dobj->prefix = NULL;
832
            }
833
2227
834
            mkdir_structure(conf, dobj->hdrsfile, r->pool);
2228
#if APR_HAS_THREADS
2229
static apr_status_t bgcopy_thread_cleanup(void *data)
2230
{
2231
    copyinfo *ci = data;
2232
    apr_status_t rc, ret;
2233
    apr_pool_t *p;
835
2234
836
            rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
2235
    /* FIXME: Debug */
837
                                 APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL,
2236
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
838
                                 r->pool);
2237
                 "disk_cache: bgcopy_thread_cleanup: %s -> %s",
2238
                 ci->srcfile, ci->destfile);
839
2239
840
            if (rv != APR_SUCCESS) {
2240
    rc = apr_thread_join(&ret, ci->t);
841
                return rv;
2241
    if(rc != APR_SUCCESS) {
842
            }
2242
        ap_log_error(APLOG_MARK, APLOG_ERR, rc, ci->s,
2243
                     "disk_cache: bgcopy_thread_cleanup: apr_thread_join "
2244
                     "failed %s -> %s", ci->srcfile, ci->destfile);
2245
        return rc;
2246
    }
2247
    if(ret != APR_SUCCESS) {
2248
        ap_log_error(APLOG_MARK, APLOG_ERR, ret, ci->s,
2249
                     "disk_cache: Background caching body %s -> %s failed",
2250
                     ci->srcfile, ci->destfile);
2251
    }
843
2252
844
            amt = sizeof(format);
2253
    /* FIXME: Debug */
845
            apr_file_write(dobj->tfd, &format, &amt);
2254
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2255
                 "disk_cache: bgcopy_thread_cleanup: SUCCESS %s -> %s",
2256
                 ci->srcfile, ci->destfile);
846
2257
847
            amt = sizeof(info->expire);
2258
    /* Destroy our private pool */
848
            apr_file_write(dobj->tfd, &info->expire, &amt);
2259
    p = ci->pool;
2260
    apr_pool_destroy(p);
2261
2262
    return APR_SUCCESS;
2263
}
849
2264
850
            varray = apr_array_make(r->pool, 6, sizeof(char*));
851
            tokens_to_array(r->pool, tmp, varray);
852
2265
853
            store_array(dobj->tfd, varray);
2266
static void *bgcopy_thread(apr_thread_t *t, void *data)
2267
{
2268
    copyinfo *ci = data;
2269
    apr_pool_t *p;
2270
    apr_status_t rc;
854
2271
855
            apr_file_close(dobj->tfd);
2272
    p = apr_thread_pool_get(t);
856
2273
857
            dobj->tfd = NULL;
2274
    /* FIXME: Debug */
2275
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2276
                 "disk_cache: bgcopy_thread: start %s -> %s",
2277
                 ci->srcfile, ci->destfile);
858
2278
859
            rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile,
2279
    rc = copy_body_nofd(p, ci->srcfile, ci->srcoff, &(ci->srcinfo), 
860
                                  r->pool);
2280
                        ci->destfile, ci->destoff, ci->len, ci->updtimeout);
861
            if (rv != APR_SUCCESS) {
862
                ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
863
                    "disk_cache: rename tempfile to varyfile failed: %s -> %s",
864
                    dobj->tempfile, dobj->hdrsfile);
865
                apr_file_remove(dobj->tempfile, r->pool);
866
                return rv;
867
            }
868
2281
869
            dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
2282
    if(rc != APR_ETIMEDOUT && rc != APR_SUCCESS) {
870
            tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
2283
        apr_file_remove(ci->destfile, p);
871
            dobj->prefix = dobj->hdrsfile;
872
            dobj->hashfile = NULL;
873
            dobj->datafile = data_file(r->pool, conf, dobj, tmp);
874
            dobj->hdrsfile = header_file(r->pool, conf, dobj, tmp);
875
        }
876
    }
2284
    }
877
2285
2286
    /* FIXME: Debug */
2287
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2288
                 "disk_cache: bgcopy_thread: done %s -> %s",
2289
                 ci->srcfile, ci->destfile);
2290
2291
    apr_thread_exit(t, rc);
2292
    return NULL;
2293
}
2294
#endif /* APR_HAS_THREADS */
2295
2296
2297
#if APR_HAS_FORK
2298
static apr_status_t bgcopy_child_cleanup(void *data) {
2299
    copyinfo *ci = data;
2300
    int status;
2301
    apr_exit_why_e why;
2302
    apr_pool_t *p;
2303
2304
    apr_proc_wait(ci->proc, &status, &why, APR_WAIT);
2305
    if(why == APR_PROC_EXIT) {
2306
        if(status != APR_SUCCESS) {
2307
            ap_log_error(APLOG_MARK, APLOG_ERR, status, ci->s,
2308
                         "disk_cache: Background caching body %s -> %s failed",
2309
                         ci->srcfile, ci->destfile);
2310
            return APR_SUCCESS;
2311
        }
2312
    }
2313
    else if(status & (APR_PROC_SIGNAL | APR_PROC_SIGNAL_CORE) ) {
2314
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, ci->s,
2315
                     "disk_cache: Background caching body %s -> %s failed, "
2316
                     "caught signal %d", ci->srcfile, ci->destfile, status);
2317
        return APR_SUCCESS;
2318
    }
2319
2320
    /* Destroy our private pool */
2321
    p = ci->pool;
2322
    apr_pool_destroy(p);
878
2323
879
    rv = apr_file_mktemp(&dobj->hfd, dobj->tempfile,
2324
    return APR_SUCCESS;
880
                         APR_CREATE | APR_WRITE | APR_BINARY |
2325
}
881
                         APR_BUFFERED | APR_EXCL, r->pool);
2326
#endif /* APR_HAS_FORK */
882
2327
2328
2329
static apr_status_t do_bgcopy(apr_file_t *srcfd, apr_off_t srcoff, 
2330
                              apr_file_t *destfd, apr_off_t destoff, 
2331
                              apr_off_t len, apr_interval_time_t updtimeout,
2332
                              conn_rec *c)
2333
{
2334
    copyinfo *ci;
2335
    apr_status_t rv;
2336
    apr_pool_t *newpool;
2337
    const char *srcfile, *destfile;
2338
    int mpm_query_info;
2339
2340
    /* It seems pool gets destroyed (ie. fd's closed) before our cleanup 
2341
       function is called when an error occurs (a dropped connection, for
2342
       example), so we need a pool of our own.
2343
     */
2344
    rv = apr_pool_create(&newpool, NULL);
883
    if (rv != APR_SUCCESS) {
2345
    if (rv != APR_SUCCESS) {
884
        return rv;
2346
        return rv;
885
    }
2347
    }
886
2348
887
    disk_info.format = DISK_FORMAT_VERSION;
2349
    ci = apr_palloc(newpool, sizeof(*ci));
888
    disk_info.date = info->date;
2350
    if(ci == NULL) {
889
    disk_info.expire = info->expire;
2351
        apr_pool_destroy(newpool);
890
    disk_info.entity_version = dobj->disk_info.entity_version++;
2352
        return APR_ENOMEM;
891
    disk_info.request_time = info->request_time;
2353
    }
892
    disk_info.response_time = info->response_time;
893
    disk_info.status = info->status;
894
895
    disk_info.name_len = strlen(dobj->name);
896
2354
897
    iov[0].iov_base = (void*)&disk_info;
2355
    rv = apr_file_name_get(&srcfile, srcfd);
898
    iov[0].iov_len = sizeof(disk_cache_info_t);
2356
    if(rv != APR_SUCCESS) {
899
    iov[1].iov_base = (void*)dobj->name;
2357
        return rv;
900
    iov[1].iov_len = disk_info.name_len;
2358
    }
2359
    rv = apr_file_info_get(&(ci->srcinfo), APR_FINFO_INODE|APR_FINFO_MTIME,
2360
                           srcfd);
2361
    if(rv != APR_SUCCESS) {
2362
        return rv;
2363
    }
901
2364
902
    rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, 2, &amt);
2365
    rv = apr_file_name_get(&destfile, destfd);
903
    if (rv != APR_SUCCESS) {
2366
    if(rv != APR_SUCCESS) {
904
        return rv;
2367
        return rv;
905
    }
2368
    }
906
2369
907
    if (r->headers_out) {
2370
    ci->pool = newpool;
908
        apr_table_t *headers_out;
2371
    ci->srcfile = apr_pstrdup(newpool, srcfile);
2372
    ci->srcoff = srcoff;
2373
    ci->destfile = apr_pstrdup(newpool, destfile);
2374
    ci->destoff = destoff;
2375
    ci->len = len;
2376
    ci->updtimeout = updtimeout;
2377
    ci->s = c->base_server;
2378
2379
#if APR_HAS_THREADS
2380
    if(ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_query_info) == APR_SUCCESS) {
2381
        apr_threadattr_t *ta;
2382
        apr_thread_t *t;
2383
        rv = apr_threadattr_create(&ta, newpool);
2384
        if(rv != APR_SUCCESS) {
2385
            apr_pool_destroy(newpool);
2386
            return rv;
2387
        }
909
2388
910
        headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
2389
        apr_threadattr_detach_set(ta, FALSE);
911
                                                  r->server);
912
2390
913
        if (!apr_table_get(headers_out, "Content-Type")
2391
        /* FIXME: This makes module unloadable on AIX */
914
            && r->content_type) {
2392
#if 0
915
            apr_table_setn(headers_out, "Content-Type",
2393
#ifdef AP_MPM_WANT_SET_STACKSIZE
916
                           ap_make_content_type(r, r->content_type));
2394
        if (ap_thread_stacksize != 0) {
2395
            apr_threadattr_stacksize_set(ta, ap_thread_stacksize);
2396
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server,
2397
                    "disk_cache: BG thread stacksize set to %"
2398
                    APR_SIZE_T_FMT, ap_thread_stacksize);
917
        }
2399
        }
2400
#endif /* AP_MPM_WANT_SET_STACKSIZE */
2401
#endif /* 0 */
918
2402
919
        headers_out = apr_table_overlay(r->pool, headers_out,
2403
        rv = apr_thread_create (&t, ta, bgcopy_thread, ci, newpool);
920
                                        r->err_headers_out);
921
        rv = store_table(dobj->hfd, headers_out);
922
        if (rv != APR_SUCCESS) {
2404
        if (rv != APR_SUCCESS) {
2405
            apr_pool_destroy(newpool);
923
            return rv;
2406
            return rv;
924
        }
2407
        }
925
    }
2408
        ci->t = t;
926
927
    /* Parse the vary header and dump those fields from the headers_in. */
928
    /* FIXME: Make call to the same thing cache_select calls to crack Vary. */
929
    if (r->headers_in) {
930
        apr_table_t *headers_in;
931
2409
932
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
2410
        apr_pool_cleanup_register(c->pool, ci, 
933
                                                 r->server);
2411
                                  bgcopy_thread_cleanup, apr_pool_cleanup_null);
934
        rv = store_table(dobj->hfd, headers_in);
2412
    }
935
        if (rv != APR_SUCCESS) {
2413
    else
2414
#endif /* APR_HAS_THREADS */
2415
#if APR_HAS_FORK
2416
    if(ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_query_info) == APR_SUCCESS) {
2417
        ci->proc = apr_palloc(newpool, sizeof(apr_proc_t));
2418
        if(ci->proc == NULL) {
2419
            apr_pool_destroy(newpool);
2420
            return APR_ENOMEM;
2421
        }
2422
        rv = apr_proc_fork(ci->proc, newpool);
2423
        if(rv == APR_INCHILD) {
2424
            /* Child */
2425
            rv = copy_body_nofd(ci->pool, ci->srcfile, ci->srcoff, 
2426
                                &(ci->srcinfo), ci->destfile, ci->destoff, 
2427
                                ci->len, ci->updtimeout);
2428
            if(rv != APR_ETIMEDOUT && rv != APR_SUCCESS) {
2429
                apr_file_remove(ci->destfile, ci->pool);
2430
            }
2431
            exit(rv);
2432
        }
2433
        else if(rv == APR_INPARENT) {
2434
            apr_pool_cleanup_register(c->pool, ci, 
2435
                                      bgcopy_child_cleanup, 
2436
                                      apr_pool_cleanup_null);
2437
        }
2438
        else {
936
            return rv;
2439
            return rv;
937
        }
2440
        }
938
    }
2441
    }
2442
    else 
2443
#endif /* APR_HAS_FORK */
2444
    if(1)
2445
    {
2446
        rv = copy_body(newpool, srcfd, ci->srcoff, destfd, ci->destoff,
2447
                       ci->len, ci->updtimeout);
2448
        apr_pool_destroy(newpool);
2449
    }
2450
2451
    return rv;
2452
}
939
2453
940
    apr_file_close(dobj->hfd); /* flush and close */
941
2454
942
    /* Remove old file with the same name. If remove fails, then
2455
static apr_status_t replace_brigade_with_cache(cache_handle_t *h,
943
     * perhaps we need to create the directory tree where we are
2456
                                               request_rec *r,
944
     * about to write the new headers file.
2457
                                               apr_bucket_brigade *bb)
945
     */
2458
{
946
    rv = apr_file_remove(dobj->hdrsfile, r->pool);
2459
    apr_status_t rv;
947
    if (rv != APR_SUCCESS) {
2460
    apr_bucket *e;
948
        mkdir_structure(conf, dobj->hdrsfile, r->pool);
2461
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
2462
2463
    if(dobj->bfd) {
2464
        apr_file_close(dobj->bfd);
2465
        dobj->bfd = NULL;
949
    }
2466
    }
950
2467
951
    rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile, r->pool);
2468
    rv = open_body_timeout(r, h->cache_obj);
952
    if (rv != APR_SUCCESS) {
2469
    if(rv == CACHE_EDECLINED) {
953
        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
2470
        return APR_ETIMEDOUT;
954
                     "disk_cache: rename tempfile to hdrsfile failed: %s -> %s",
2471
    }
955
                     dobj->tempfile, dobj->hdrsfile);
2472
    else if(rv != APR_SUCCESS) {
956
        apr_file_remove(dobj->tempfile, r->pool);
2473
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2474
                     "disk_cache: Error opening bodyfile %s for URL %s",
2475
                     dobj->bodyfile, dobj->name);
957
        return rv;
2476
        return rv;
958
    }
2477
    }
959
2478
960
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
2479
    /* First, empty the brigade */
2480
    e  = APR_BRIGADE_FIRST(bb);
2481
    while (e != APR_BRIGADE_SENTINEL(bb)) {
2482
        apr_bucket *d;
2483
        d = e;
2484
        e = APR_BUCKET_NEXT(e);
2485
        apr_bucket_delete(d);
2486
    }
2487
2488
    /* Then, populate it with our cached instance */
961
2489
2490
    /* in case we've already sent part, e.g. via mod_proxy */
2491
    dobj->bytes_sent = r->bytes_sent;
2492
2493
    rv = recall_body(h, r->pool, bb);
2494
    if (rv != APR_SUCCESS) {
2495
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2496
                     "disk_cache: Error serving URL %s from cache", dobj->name);
2497
        return rv;
2498
    }
962
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2499
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
963
                 "disk_cache: Stored headers for URL %s",  dobj->name);
2500
                 "disk_cache: Serving cached body for URL %s", dobj->name);
2501
964
    return APR_SUCCESS;
2502
    return APR_SUCCESS;
965
}
2503
}
966
2504
2505
967
static apr_status_t store_body(cache_handle_t *h, request_rec *r,
2506
static apr_status_t store_body(cache_handle_t *h, request_rec *r,
968
                               apr_bucket_brigade *bb)
2507
                               apr_bucket_brigade *bb)
969
{
2508
{
970
    apr_bucket *e;
2509
    apr_bucket *e;
971
    apr_status_t rv;
2510
    apr_status_t rv;
2511
    int copy_file = FALSE, first_call = FALSE, did_bgcopy = FALSE;
972
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
2512
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
973
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
2513
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
974
                                                 &disk_cache_module);
2514
                                                 &disk_cache_module);
975
2515
976
    /* We write to a temp file and then atomically rename the file over
2516
    if(r->no_cache) {
977
     * in file_cache_el_final().
2517
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
978
     */
2518
                     "disk_cache: store_body called for URL %s even though"
979
    if (!dobj->tfd) {
2519
                     "no_cache is set", dobj->name);
980
        rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
2520
        file_cache_errorcleanup(dobj, r);
981
                             APR_CREATE | APR_WRITE | APR_BINARY |
2521
        return APR_EGENERAL;
982
                             APR_BUFFERED | APR_EXCL, r->pool);
2522
    }
983
        if (rv != APR_SUCCESS) {
2523
984
            return rv;
2524
    if(dobj->initial_size == 0) {
2525
        /* Don't waste a body cachefile on a 0 length body */
2526
        return APR_SUCCESS;
2527
    }
2528
2529
    /* Only perform these actions when called the first time */
2530
    if(dobj->bfd == NULL) {
2531
        first_call = TRUE;
2532
2533
        if(dobj->lastmod != APR_DATE_BAD) {
2534
            apr_finfo_t finfo;
2535
            rv = apr_stat(&finfo, dobj->bodyfile, 
2536
                          APR_FINFO_MTIME | APR_FINFO_SIZE | APR_FINFO_CSIZE, 
2537
                          r->pool);
2538
            if(rv == APR_SUCCESS || APR_STATUS_IS_INCOMPLETE(rv)) {
2539
                /* Dest-file will have same mtime as source if it's
2540
                   current */
2541
                /* FIXME: This code and the one used in open_body should
2542
                   probably be identical... */
2543
                if(dobj->lastmod <= finfo.mtime && 
2544
                        dobj->initial_size == finfo.size &&
2545
                        !(finfo.valid & APR_FINFO_CSIZE && finfo.csize < finfo.size))
2546
                {
2547
                    /* Assume it's a valid cached body there already */
2548
                    dobj->skipstore = TRUE;
2549
                }
2550
            }
2551
        }
2552
2553
        if(!dobj->skipstore) {
2554
            /* FIXME: We should pass the source file's size and mtime so
2555
               open_new_file() can more reliably determine if the target
2556
               file is current or stale. */
2557
            rv = open_new_file(r, dobj->bodyfile, &(dobj->bfd), conf);
2558
            if(rv == CACHE_EEXIST) {
2559
                /* Someone else beat us to storing this */
2560
                dobj->skipstore = TRUE;
2561
            }
2562
            else if(rv != APR_SUCCESS) {
2563
                file_cache_errorcleanup(dobj, r);
2564
                apr_file_remove(dobj->hdrsfile, r->pool);
2565
                return rv;
2566
            }
2567
            else {
2568
                dobj->file_size = 0;
2569
            }
2570
        }
2571
2572
        if(dobj->skipstore) {
2573
            /* Someone else beat us to storing this object */
2574
            if( dobj->initial_size > 0 &&
2575
                    APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
2576
            {
2577
                /* Yay, we can replace the body with the cached instance */
2578
                return replace_brigade_with_cache(h, r, bb);
2579
            }
2580
2581
            return APR_SUCCESS;
985
        }
2582
        }
986
        dobj->file_size = 0;
987
    }
2583
    }
988
2584
989
    for (e = APR_BRIGADE_FIRST(bb);
2585
    /* Check if this is a complete single sequential file, eligable for
990
         e != APR_BRIGADE_SENTINEL(bb);
2586
     * file copy.
991
         e = APR_BUCKET_NEXT(e))
2587
     */
2588
    /* FIXME: Make the min size to do file copy run-time config? */
2589
    if(dobj->file_size == 0 && 
2590
            dobj->initial_size > APR_BUCKET_BUFF_SIZE &&
2591
            APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
992
    {
2592
    {
993
        const char *str;
2593
        apr_off_t begin = -1;
994
        apr_size_t length, written;
2594
        apr_off_t pos = -1;
995
        rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
2595
        apr_file_t *fd = NULL;
996
        if (rv != APR_SUCCESS) {
2596
        apr_bucket_file *a;
997
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2597
998
                         "cache_disk: Error when reading bucket for URL %s",
2598
        copy_file = TRUE;
999
                         h->cache_obj->key);
2599
1000
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2600
        for (e = APR_BRIGADE_FIRST(bb);
1001
            file_cache_errorcleanup(dobj, r);
2601
                e != APR_BRIGADE_SENTINEL(bb);
1002
            return rv;
2602
                e = APR_BUCKET_NEXT(e))
2603
        {
2604
            if(APR_BUCKET_IS_EOS(e)) {
2605
                break;
2606
            }
2607
            if(!APR_BUCKET_IS_FILE(e)) {
2608
                copy_file = FALSE;
2609
                break;
2610
            }
2611
2612
            a = e->data;
2613
2614
            if(begin < 0) {
2615
                begin = pos = e->start;
2616
                fd = a->fd;
2617
            }
2618
2619
            if(fd != a->fd || pos != e->start) {
2620
                copy_file = FALSE;
2621
                break;
2622
            }
2623
2624
            pos += e->length;
1003
        }
2625
        }
1004
        rv = apr_file_write_full(dobj->tfd, str, length, &written);
2626
1005
        if (rv != APR_SUCCESS) {
2627
        if(copy_file) {
1006
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2628
            dobj->file_size = pos;
1007
                         "cache_disk: Error when writing cache file for URL %s",
2629
        }
1008
                         h->cache_obj->key);
2630
    }
1009
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2631
1010
            file_cache_errorcleanup(dobj, r);
2632
    if(copy_file) {
2633
        apr_bucket_file *a;
2634
2635
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
2636
                     "disk_cache: Copying body for URL %s, len %"
2637
                     APR_OFF_T_FMT, dobj->name, dobj->file_size);
2638
2639
        e = APR_BRIGADE_FIRST(bb);
2640
        a = e->data;
2641
2642
        if(dobj->file_size > conf->minbgsize) {
2643
            rv = do_bgcopy(a->fd, e->start, dobj->bfd, 0, dobj->file_size,
2644
                           dobj->updtimeout, r->connection);
2645
            did_bgcopy = TRUE;
2646
        }
2647
        else {
2648
            rv = copy_body(r->pool, a->fd, e->start, dobj->bfd, 0,
2649
                           dobj->file_size, dobj->updtimeout);
2650
        }
2651
        if(rv != APR_SUCCESS) {
2652
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2653
                         "disk_cache: Copying body failed, "
2654
                         "URL %s", dobj->name);
2655
            if(rv != APR_ETIMEDOUT) {
2656
                file_cache_errorcleanup(dobj, r);
2657
                apr_file_remove(dobj->bodyfile, r->pool);
2658
            }
1011
            return rv;
2659
            return rv;
1012
        }
2660
        }
1013
        dobj->file_size += written;
2661
1014
        if (dobj->file_size > conf->maxfs) {
2662
    }
1015
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2663
    else {
1016
                         "cache_disk: URL %s failed the size check "
2664
        if(first_call) {
1017
                         "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
2665
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1018
                         h->cache_obj->key, dobj->file_size, conf->maxfs);
2666
                         "disk_cache: Caching body for URL %s, len %"
1019
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2667
                         APR_OFF_T_FMT, dobj->name, dobj->initial_size);
1020
            file_cache_errorcleanup(dobj, r);
2668
        }
1021
            return APR_EGENERAL;
2669
2670
        for (e = APR_BRIGADE_FIRST(bb);
2671
                e != APR_BRIGADE_SENTINEL(bb);
2672
                e = APR_BUCKET_NEXT(e))
2673
        {   
2674
            const char *str;
2675
            apr_size_t length, written;
2676
2677
            /* Ignore the non-data-buckets */
2678
            if(APR_BUCKET_IS_METADATA(e)) {
2679
                continue;
2680
            }
2681
2682
            rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
2683
            if (rv != APR_SUCCESS) {
2684
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2685
                             "disk_cache: Error when reading bucket for URL %s",
2686
                             dobj->name);
2687
                file_cache_errorcleanup(dobj, r);
2688
                apr_file_remove(dobj->hdrsfile, r->pool);
2689
                apr_file_remove(dobj->bodyfile, r->pool);
2690
                return rv;
2691
            }
2692
            rv = apr_file_write_full(dobj->bfd, str, length, &written);
2693
            if (rv != APR_SUCCESS) {
2694
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2695
                             "disk_cache: Error when writing cache file for "
2696
                             "URL %s", dobj->name);
2697
                file_cache_errorcleanup(dobj, r);
2698
                apr_file_remove(dobj->hdrsfile, r->pool);
2699
                apr_file_remove(dobj->bodyfile, r->pool);
2700
                return rv;
2701
            }
2702
            dobj->file_size += written;
2703
            if (dobj->file_size > conf->maxfs) {
2704
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2705
                             "disk_cache: URL %s failed the size check "
2706
                             "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
2707
                             dobj->name, dobj->file_size, conf->maxfs);
2708
                file_cache_errorcleanup(dobj, r);
2709
                apr_file_remove(dobj->hdrsfile, r->pool);
2710
                apr_file_remove(dobj->bodyfile, r->pool);
2711
                return APR_EGENERAL;
2712
            }
1022
        }
2713
        }
1023
    }
2714
    }
1024
2715
1025
    /* Was this the final bucket? If yes, close the temp file and perform
2716
1026
     * sanity checks.
2717
    /* Drop out here if this wasn't the end */
1027
     */
2718
    if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1028
    if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
2719
        return APR_SUCCESS;
1029
        if (r->connection->aborted || r->no_cache) {
2720
    }
2721
2722
    if(!copy_file) {
2723
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2724
                     "disk_cache: Done caching URL %s, len %" APR_OFF_T_FMT,
2725
                     dobj->name, dobj->file_size);
2726
2727
        /* FIXME: Do we really need to check r->no_cache here since we checked
2728
           it in the beginning? */
2729
        /* Assume that if we've got an initial size then bucket brigade
2730
           was complete and there's no danger in keeping it even if the
2731
           connection was aborted */
2732
        if (r->no_cache || (r->connection->aborted && dobj->initial_size < 0)) {
1030
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
2733
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1031
                         "disk_cache: Discarding body for URL %s "
2734
                         "disk_cache: Discarding body for URL %s "
1032
                         "because connection has been aborted.",
2735
                         "because connection has been aborted.",
1033
                         h->cache_obj->key);
2736
                         dobj->name);
1034
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2737
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
1035
            file_cache_errorcleanup(dobj, r);
2738
            file_cache_errorcleanup(dobj, r);
2739
            apr_file_remove(dobj->hdrsfile, r->pool);
2740
            apr_file_remove(dobj->bodyfile, r->pool);
1036
            return APR_EGENERAL;
2741
            return APR_EGENERAL;
1037
        }
2742
        }
2743
1038
        if (dobj->file_size < conf->minfs) {
2744
        if (dobj->file_size < conf->minfs) {
1039
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2745
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1040
                         "cache_disk: URL %s failed the size check "
2746
                         "disk_cache: URL %s failed the size check "
1041
                         "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
2747
                         "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
1042
                         h->cache_obj->key, dobj->file_size, conf->minfs);
2748
                         dobj->name, dobj->file_size, conf->minfs);
1043
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2749
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
1044
            file_cache_errorcleanup(dobj, r);
2750
            file_cache_errorcleanup(dobj, r);
2751
            apr_file_remove(dobj->hdrsfile, r->pool);
2752
            apr_file_remove(dobj->bodyfile, r->pool);
1045
            return APR_EGENERAL;
2753
            return APR_EGENERAL;
1046
        }
2754
        }
1047
2755
1048
        /* All checks were fine. Move tempfile to final destination */
2756
        if(dobj->initial_size < 0) {
1049
        /* Link to the perm file, and close the descriptor */
2757
            /* Update header information now that we know the size */
1050
        file_cache_el_final(dobj, r);
2758
            dobj->initial_size = dobj->file_size;
1051
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2759
            rv = store_headers(h, r, &(h->cache_obj->info));
1052
                     "disk_cache: Body for URL %s cached.",  dobj->name);
2760
            if(rv != APR_SUCCESS) {
2761
                file_cache_errorcleanup(dobj, r);
2762
                apr_file_remove(dobj->hdrsfile, r->pool);
2763
                apr_file_remove(dobj->bodyfile, r->pool);
2764
                return rv;
2765
            }
2766
        }
2767
        else if(dobj->initial_size != dobj->file_size) {
2768
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2769
                         "disk_cache: URL %s - body size mismatch: suggested %"
2770
                         APR_OFF_T_FMT "  file_size %" APR_OFF_T_FMT ")",
2771
                         dobj->name, dobj->initial_size, dobj->file_size);
2772
            file_cache_errorcleanup(dobj, r);
2773
            apr_file_remove(dobj->hdrsfile, r->pool);
2774
            apr_file_remove(dobj->bodyfile, r->pool);
2775
            return APR_EGENERAL;
2776
        }
2777
    }
2778
2779
    /* All checks were fine, close output file */
2780
    rv = apr_file_close(dobj->bfd);
2781
    dobj->bfd = NULL;
2782
    if(rv != APR_SUCCESS) {
2783
        apr_file_remove(dobj->bodyfile, r->pool);
2784
        file_cache_errorcleanup(dobj, r);
2785
        return rv;
2786
    }
2787
2788
    /* Set mtime on body file */
2789
    if(!did_bgcopy && dobj->lastmod != APR_DATE_BAD) {
2790
        apr_file_mtime_set(dobj->bodyfile, dobj->lastmod, r->pool);
2791
    }
2792
2793
2794
    /* Redirect to cachefile if we copied a plain file */
2795
    if(copy_file) {
2796
        rv = replace_brigade_with_cache(h, r, bb);
2797
        if(rv != APR_SUCCESS) {
2798
            return rv;
2799
        }
1053
    }
2800
    }
1054
2801
1055
    return APR_SUCCESS;
2802
    return APR_SUCCESS;
Lines 1064-1069 static void *create_config(apr_pool_t *p Link Here
1064
    conf->dirlength = DEFAULT_DIRLENGTH;
2811
    conf->dirlength = DEFAULT_DIRLENGTH;
1065
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
2812
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
1066
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
2813
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
2814
    conf->updtimeout = DEFAULT_UPDATE_TIMEOUT;
2815
    conf->minbgsize = DEFAULT_MIN_BACKGROUND_SIZE;
1067
2816
1068
    conf->cache_root = NULL;
2817
    conf->cache_root = NULL;
1069
    conf->cache_root_len = 0;
2818
    conf->cache_root_len = 0;
Lines 1105-1110 static const char Link Here
1105
    conf->dirlevels = val;
2854
    conf->dirlevels = val;
1106
    return NULL;
2855
    return NULL;
1107
}
2856
}
2857
1108
static const char
2858
static const char
1109
*set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2859
*set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1110
{
2860
{
Lines 1144-1152 static const char Link Here
1144
    {
2894
    {
1145
        return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
2895
        return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
1146
    }
2896
    }
2897
2898
    return NULL;
2899
}
2900
2901
2902
static const char
2903
*set_cache_updtimeout(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2904
{
2905
    apr_int64_t val;
2906
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2907
                                                 &disk_cache_module);
2908
2909
    if (apr_strtoff(&val, arg, NULL, 0) != APR_SUCCESS || val < 0) 
2910
    {
2911
        return "CacheUpdateTimeout argument must be a non-negative integer representing the timeout in milliseconds for cache update operations";
2912
    }
2913
2914
    conf->updtimeout = val * 1000;
2915
2916
    return NULL;
2917
}
2918
2919
2920
static const char
2921
*set_cache_minbgsize(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2922
{
2923
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2924
                                                 &disk_cache_module);
2925
2926
    if (apr_strtoff(&conf->minbgsize, arg, NULL, 0) != APR_SUCCESS ||
2927
            conf->minbgsize < 0) 
2928
    {
2929
        return "CacheMinBGSize argument must be a non-negative integer representing the min size in bytes for a file to be eligable for background caching";
2930
    }
2931
2932
    return NULL;
2933
}
2934
2935
2936
static const char
2937
*set_cache_removedirs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2938
{
2939
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2940
                                                 &disk_cache_module);
2941
2942
    if (strcasecmp(arg, "on") == 0 || strcasecmp(arg, "true")) {
2943
        conf->removedirs = TRUE;
2944
    }
2945
    else if (strcasecmp(arg, "off") == 0 || strcasecmp(arg, "false")) {
2946
        conf->removedirs = FALSE;
2947
    }
2948
    else {
2949
        return "CacheRemoveDirectories argument must be either on, true, off or false";
2950
    }
2951
1147
    return NULL;
2952
    return NULL;
1148
}
2953
}
1149
2954
2955
1150
static const command_rec disk_cache_cmds[] =
2956
static const command_rec disk_cache_cmds[] =
1151
{
2957
{
1152
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
2958
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
Lines 1159-1164 static const command_rec disk_cache_cmds Link Here
1159
                  "The minimum file size to cache a document"),
2965
                  "The minimum file size to cache a document"),
1160
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
2966
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
1161
                  "The maximum file size to cache a document"),
2967
                  "The maximum file size to cache a document"),
2968
    AP_INIT_TAKE1("CacheUpdateTimeout", set_cache_updtimeout, NULL, RSRC_CONF,
2969
                  "Timeout in ms for cache updates"),
2970
    AP_INIT_TAKE1("CacheMinBGSize", set_cache_minbgsize, NULL, RSRC_CONF,
2971
                  "The minimum file size for background caching"),
2972
    AP_INIT_TAKE1("CacheRemoveDirectories", set_cache_removedirs, NULL, RSRC_CONF,
2973
                  "Should we try to remove directories when we remove expired cache files."),
1162
    {NULL}
2974
    {NULL}
1163
};
2975
};
1164
2976
(-)../dist/modules/cache/mod_disk_cache.h (-21 / +133 lines)
Lines 22-33 Link Here
22
 */
22
 */
23
23
24
#define VARY_FORMAT_VERSION 3
24
#define VARY_FORMAT_VERSION 3
25
#define DISK_FORMAT_VERSION 4
25
#define DISK_FORMAT_VERSION_OLD 4
26
#define DISK_FORMAT_VERSION_OLD2 5
27
#define DISK_FORMAT_VERSION_OLD3 7
28
#define DISK_FORMAT_VERSION 8
26
29
27
#define CACHE_HEADER_SUFFIX ".header"
30
#define CACHE_HEADER_SUFFIX ".header"
28
#define CACHE_DATA_SUFFIX   ".data"
31
#define CACHE_BODY_SUFFIX   ".body"
29
#define CACHE_VDIR_SUFFIX   ".vary"
32
#define CACHE_VDIR_SUFFIX   ".vary"
30
33
34
/* Size of buffer used when copying files */
35
#define CACHE_BUF_SIZE 262144
36
37
/* How much the file on disk must have grown beyond the current offset
38
   before diskcache_bucket_read breaks out of the stat/sleep-loop */
39
#define CACHE_BUCKET_MINCHUNK 524288
40
41
/* How long to sleep before retrying while looping (micro-seconds) */
42
#define CACHE_LOOP_MINSLEEP 10000
43
#define CACHE_LOOP_MAXSLEEP 1000000
44
31
#define AP_TEMPFILE_PREFIX "/"
45
#define AP_TEMPFILE_PREFIX "/"
32
#define AP_TEMPFILE_BASE   "aptmp"
46
#define AP_TEMPFILE_BASE   "aptmp"
33
#define AP_TEMPFILE_SUFFIX "XXXXXX"
47
#define AP_TEMPFILE_SUFFIX "XXXXXX"
Lines 35-56 Link Here
35
#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
49
#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
36
#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
50
#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
37
51
52
typedef apr_uint32_t disk_cache_format_t;
53
38
typedef struct {
54
typedef struct {
39
    /* Indicates the format of the header struct stored on-disk. */
40
    apr_uint32_t format;
41
    /* The HTTP status code returned for this response.  */
55
    /* The HTTP status code returned for this response.  */
42
    int status;
56
    apr_int32_t status;
43
    /* The size of the entity name that follows. */
44
    apr_size_t name_len;
45
    /* The number of times we've cached this entity. */
57
    /* The number of times we've cached this entity. */
46
    apr_size_t entity_version;
58
    apr_uint32_t entity_version;
47
    /* Miscellaneous time values. */
59
    /* Miscellaneous time values. */
48
    apr_time_t date;
60
    apr_time_t date;
49
    apr_time_t expire;
61
    apr_time_t expire;
50
    apr_time_t request_time;
62
    apr_time_t request_time;
51
    apr_time_t response_time;
63
    apr_time_t response_time;
64
    apr_time_t lastmod; /* Last-Modified (if present) */
65
66
    /* The body size forced to 64bit to not break when people go from non-LFS
67
     * to LFS builds */
68
    apr_int64_t file_size;
69
70
    /* The size of the entity name that follows. */
71
    apr_uint32_t name_len;
72
    /* The size of the body cache filename */
73
    apr_uint32_t bodyname_len;
74
    /* The size of the filename that follows, to fill in r->filename */
75
    apr_uint32_t filename_len;
76
77
    /* On disk:
78
       * name_len long string of entity name.
79
       * bodyname_len long string of body cache filename (without cacheroot).
80
       * filename_len long string of filename
81
     */
52
} disk_cache_info_t;
82
} disk_cache_info_t;
53
83
84
85
/* Don't expose module-related stuff unless needed */
86
#ifdef AP_FILTER_H
54
/*
87
/*
55
 * disk_cache_object_t
88
 * disk_cache_object_t
56
 * Pointed to by cache_object_t::vobj
89
 * Pointed to by cache_object_t::vobj
Lines 58-75 typedef struct { Link Here
58
typedef struct disk_cache_object {
91
typedef struct disk_cache_object {
59
    const char *root;        /* the location of the cache directory */
92
    const char *root;        /* the location of the cache directory */
60
    apr_size_t root_len;
93
    apr_size_t root_len;
61
    char *tempfile;    /* temp file tohold the content */
94
62
    const char *prefix;
95
    /* Temporary file */
63
    const char *datafile;    /* name of file where the data will go */
96
    apr_file_t *tfd;
64
    const char *hdrsfile;    /* name of file where the hdrs will go */
97
    char *tempfile;
65
    const char *hashfile;    /* Computed hash key for this URI */
98
66
    const char *name;   /* Requested URI without vary bits - suitable for mortals. */
99
    /* Header cache file */
67
    const char *key;    /* On-disk prefix; URI with Vary bits (if present) */
100
    apr_file_t *hfd;
68
    apr_file_t *fd;          /* data file */
101
    const char *hdrsfile;
69
    apr_file_t *hfd;         /* headers file */
102
70
    apr_file_t *tfd;         /* temporary file for data */
103
    /* Body cache file */
71
    apr_off_t file_size;     /*  File size of the cached data file  */
104
    apr_file_t *bfd;
72
    disk_cache_info_t disk_info; /* Header information. */
105
    const char *bodyfile;
106
107
    const char *name;           /* Requested URI without vary bits - 
108
                                   suitable for mortals. */
109
    const char *prefix;         /* Prefix to deal with Vary headers */
110
    char *filename;             /* Filename of requested URL (if present) */
111
    char **rfilename;           /* Pointer to r->filename */
112
113
    apr_off_t initial_size;      /* Size of body as reported upstreams */
114
    apr_off_t file_size;         /* File size of the cached body */
115
116
    apr_time_t lastmod;          /* Last-Modified (if present) */
117
118
    int skipstore;              /* Set if we should skip storing stuff */
119
120
    int removedirs;             /* Set it we should rmdir when doing rm */
121
122
    int header_only;            /* Copy of r->header_only */
123
124
    apr_interval_time_t updtimeout; /* Cache update timeout */
125
126
    disk_cache_info_t disk_info; /* Disk header information. */
127
128
    apr_off_t bytes_sent; /* Copy of r->bytes_sent before calling recall_body */
73
} disk_cache_object_t;
129
} disk_cache_object_t;
74
130
75
131
Lines 82-95 typedef struct disk_cache_object { Link Here
82
#define DEFAULT_DIRLENGTH 2
138
#define DEFAULT_DIRLENGTH 2
83
#define DEFAULT_MIN_FILE_SIZE 1
139
#define DEFAULT_MIN_FILE_SIZE 1
84
#define DEFAULT_MAX_FILE_SIZE 1000000
140
#define DEFAULT_MAX_FILE_SIZE 1000000
141
/* Background caching disabled by default */
142
#define DEFAULT_MIN_BACKGROUND_SIZE DEFAULT_MAX_FILE_SIZE
143
#define DEFAULT_UPDATE_TIMEOUT apr_time_from_sec(10)
85
144
86
typedef struct {
145
typedef struct {
87
    const char* cache_root;
146
    const char* cache_root;
88
    apr_size_t cache_root_len;
147
    apr_size_t cache_root_len;
89
    int dirlevels;               /* Number of levels of subdirectories */
148
    int dirlevels;               /* Number of levels of subdirectories */
90
    int dirlength;               /* Length of subdirectory names */
149
    int dirlength;               /* Length of subdirectory names */
91
    apr_off_t minfs;             /* minimum file size for cached files */
150
    apr_off_t minfs;             /* minumum file size for cached files */
92
    apr_off_t maxfs;             /* maximum file size for cached files */
151
    apr_off_t maxfs;             /* maximum file size for cached files */
152
    apr_off_t minbgsize;         /* minimum file size to do bg caching */
153
    apr_interval_time_t updtimeout;   /* Cache update timeout */
154
    int removedirs;              /* Should we try to remove directories? */
93
} disk_cache_conf;
155
} disk_cache_conf;
94
156
157
typedef struct diskcache_bucket_data diskcache_bucket_data;
158
struct diskcache_bucket_data {
159
    /** Number of buckets using this memory */
160
    apr_bucket_refcount  refcount;
161
    apr_file_t  *fd;
162
    /** The pool into which any needed structures should
163
     *  be created while reading from this file bucket */
164
    apr_pool_t *readpool;
165
    /* Cache update timeout */
166
    apr_interval_time_t updtimeout;
167
    /* Adaptive loop delay timeout */
168
    apr_interval_time_t polldelay;
169
};
170
171
/* Stuff needed by the background copy thread */
172
typedef struct copyinfo copyinfo;
173
struct copyinfo {
174
    apr_off_t len;
175
    /* Source info */
176
    const char *srcfile;
177
    apr_finfo_t srcinfo;
178
    apr_off_t srcoff;
179
    /* Destination info */
180
    const char *destfile;
181
    apr_off_t destoff;
182
183
    /* Cache update timeout */
184
    apr_interval_time_t updtimeout;
185
186
    /* Our private pool */
187
    apr_pool_t *pool;
188
189
#if APR_HAS_THREADS
190
    /* Background process info */
191
    apr_thread_t *t;
192
#endif /* APR_HAS_THREADS */
193
#if APR_HAS_FORK
194
    apr_proc_t *proc;
195
#endif /* APR_HAS_FORK */
196
197
    /* For logging */
198
    const server_rec *s;
199
};
200
201
#define CACHE_ENODATA (APR_OS_START_USERERR+1)
202
#define CACHE_EDECLINED (APR_OS_START_USERERR+2)
203
#define CACHE_EEXIST (APR_OS_START_USERERR+3)
204
205
#endif /* AP_FILTER_H */
206
95
#endif /*MOD_DISK_CACHE_H*/
207
#endif /*MOD_DISK_CACHE_H*/
(-)../dist/support/htcacheclean.c (-121 / +60 lines)
Lines 70-79 Link Here
70
typedef struct _direntry {
70
typedef struct _direntry {
71
    APR_RING_ENTRY(_direntry) link;
71
    APR_RING_ENTRY(_direntry) link;
72
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
72
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
73
    apr_time_t htime; /* headers file modification time */
73
    apr_time_t htime; /* file modification time */
74
    apr_time_t dtime; /* body file modification time */
74
    apr_off_t hsize;  /* file size */
75
    apr_off_t hsize;  /* headers file size */
76
    apr_off_t dsize;  /* body or temporary file size */
77
    char *basename;   /* file/fileset base name */
75
    char *basename;   /* file/fileset base name */
78
} DIRENTRY;
76
} DIRENTRY;
79
77
Lines 81-91 typedef struct _entry { Link Here
81
    APR_RING_ENTRY(_entry) link;
79
    APR_RING_ENTRY(_entry) link;
82
    apr_time_t expire;        /* cache entry exiration time */
80
    apr_time_t expire;        /* cache entry exiration time */
83
    apr_time_t response_time; /* cache entry time of last response to client */
81
    apr_time_t response_time; /* cache entry time of last response to client */
84
    apr_time_t htime;         /* headers file modification time */
82
    apr_time_t htime;         /* file modification time */
85
    apr_time_t dtime;         /* body file modification time */
83
    apr_off_t hsize;          /* file size */
86
    apr_off_t hsize;          /* headers file size */
87
    apr_off_t dsize;          /* body or temporary file size */
88
    char *basename;           /* fileset base name */
84
    char *basename;           /* fileset base name */
85
    char *name;               /* entity name */
89
} ENTRY;
86
} ENTRY;
90
87
91
88
Lines 255-264 static void delete_entry(char *path, cha Link Here
255
    /* temp pool, otherwise lots of memory could be allocated */
252
    /* temp pool, otherwise lots of memory could be allocated */
256
    apr_pool_create(&p, pool);
253
    apr_pool_create(&p, pool);
257
254
258
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
255
    nextpath = apr_pstrcat(p, path, "/", basename, NULL);
259
    apr_file_remove(nextpath, p);
260
261
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
262
    apr_file_remove(nextpath, p);
256
    apr_file_remove(nextpath, p);
263
257
264
    apr_pool_destroy(p);
258
    apr_pool_destroy(p);
Lines 286-298 static int process_dir(char *path, apr_p Link Here
286
    apr_finfo_t info;
280
    apr_finfo_t info;
287
    apr_size_t len;
281
    apr_size_t len;
288
    apr_time_t current, deviation;
282
    apr_time_t current, deviation;
289
    char *nextpath, *base, *ext, *orig_basename;
283
    char *nextpath, *base, *orig_basename;
290
    APR_RING_ENTRY(_direntry) anchor;
284
    APR_RING_ENTRY(_direntry) anchor;
291
    DIRENTRY *d, *t, *n;
285
    DIRENTRY *d, *t, *n;
292
    ENTRY *e;
286
    ENTRY *e;
293
    int skip, retries;
287
    int skip, retries;
294
    disk_cache_info_t disk_info;
288
    disk_cache_info_t disk_info;
295
289
290
296
    APR_RING_INIT(&anchor, _direntry, link);
291
    APR_RING_INIT(&anchor, _direntry, link);
297
    apr_pool_create(&p, pool);
292
    apr_pool_create(&p, pool);
298
    h = apr_hash_make(p);
293
    h = apr_hash_make(p);
Lines 329-340 static int process_dir(char *path, apr_p Link Here
329
        if (!base++) {
324
        if (!base++) {
330
            base = d->basename;
325
            base = d->basename;
331
        }
326
        }
332
        ext = strchr(base, '.');
333
327
334
        /* there may be temporary files which may be gone before
328
        /* there may be temporary files which may be gone before
335
         * processing, always skip these if not in realclean mode
329
         * processing, always skip these if not in realclean mode
336
         */
330
         */
337
        if (!ext && !realclean) {
331
        if (!realclean) {
338
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
332
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
339
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
333
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
340
                continue;
334
                continue;
Lines 386-436 static int process_dir(char *path, apr_p Link Here
386
            continue;
380
            continue;
387
        }
381
        }
388
382
389
        if (!ext) {
383
       if(strncasecmp(base + strlen(base) - sizeof(CACHE_HEADER_SUFFIX) + 1, CACHE_HEADER_SUFFIX, sizeof(CACHE_HEADER_SUFFIX))) {
384
               continue;
385
       }
386
387
390
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
388
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
391
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
389
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
392
                d->basename += skip;
390
                d->basename += skip;
393
                d->type = TEMP;
391
                d->type = TEMP;
394
                d->dsize = info.size;
392
            d->hsize = info.size;
395
                apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
393
                apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
396
            }
397
            continue;
394
            continue;
398
        }
395
        }
399
396
400
        if (!strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
397
        /* Assume that everything else are cachefiles */
401
            *ext = '\0';
402
            d->basename += skip;
398
            d->basename += skip;
403
            /* if a user manually creates a '.header' file */
404
            if (d->basename[0] == '\0') {
405
                continue;
406
            }
407
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
399
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
408
            if (t) {
400
            if (t) {
409
                d = t;
401
                d = t;
410
            }
402
            }
411
            d->type |= HEADER;
403
        d->type |= HEADERDATA;
412
            d->htime = info.mtime;
404
            d->htime = info.mtime;
413
            d->hsize = info.size;
405
            d->hsize = info.size;
414
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
406
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
415
            continue;
416
        }
417
418
        if (!strcasecmp(ext, CACHE_DATA_SUFFIX)) {
419
            *ext = '\0';
420
            d->basename += skip;
421
            /* if a user manually creates a '.data' file */
422
            if (d->basename[0] == '\0') {
423
                continue;
424
            }
425
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
426
            if (t) {
427
                d = t;
428
            }
429
            d->type |= DATA;
430
            d->dtime = info.mtime;
431
            d->dsize = info.size;
432
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
433
        }
434
    }
407
    }
435
408
436
    if (interrupted) {
409
    if (interrupted) {
Lines 448-479 static int process_dir(char *path, apr_p Link Here
448
421
449
        switch(d->type) {
422
        switch(d->type) {
450
        case HEADERDATA:
423
        case HEADERDATA:
451
            nextpath = apr_pstrcat(p, path, "/", d->basename,
424
            nextpath = apr_pstrcat(p, path, "/", d->basename, NULL);
452
                                   CACHE_HEADER_SUFFIX, NULL);
453
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
425
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
454
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
426
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
455
                len = sizeof(format);
427
                len = sizeof(format);
456
                if (apr_file_read_full(fd, &format, len,
428
                if (apr_file_read_full(fd, &format, len,
457
                                       &len) == APR_SUCCESS) {
429
                                       &len) == APR_SUCCESS) {
458
                    if (format == DISK_FORMAT_VERSION) {
430
               if (format == DISK_FORMAT_VERSION) {
459
                        apr_off_t offset = 0;
431
                        apr_off_t offset;
460
461
                        apr_file_seek(fd, APR_SET, &offset);
462
432
463
                        len = sizeof(disk_cache_info_t);
433
                        len = sizeof(disk_cache_info_t);
464
434
465
                        if (apr_file_read_full(fd, &disk_info, len,
435
                        if (apr_file_read_full(fd, &disk_info, len,
466
                                               &len) == APR_SUCCESS) {
436
                                               &len) == APR_SUCCESS) {
467
                            apr_file_close(fd);
468
                            e = apr_palloc(pool, sizeof(ENTRY));
437
                            e = apr_palloc(pool, sizeof(ENTRY));
469
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
438
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
470
                            e->expire = disk_info.expire;
439
                           e->expire = disk_info.expire;
471
                            e->response_time = disk_info.response_time;
440
                            e->response_time = disk_info.response_time;
472
                            e->htime = d->htime;
441
                            e->htime = disk_info.date;
473
                            e->dtime = d->dtime;
442
                            e->hsize = disk_info.file_size;
474
                            e->hsize = d->hsize;
475
                            e->dsize = d->dsize;
476
                            e->basename = apr_pstrdup(pool, d->basename);
443
                            e->basename = apr_pstrdup(pool, d->basename);
444
                            e->name = apr_palloc(pool, disk_info.bodyname_len+1);
445
                            offset = disk_info.name_len;
446
                            apr_file_seek(fd, APR_CUR, &offset);
447
                            if(apr_file_read_full(fd, e->name,
448
                                        disk_info.bodyname_len, NULL)
449
                                    == APR_SUCCESS)
450
                            {
451
                               e->name[disk_info.bodyname_len] = '\0';
452
                            }
453
                            else {
454
                                e->name = "UNKNOWN";
455
                            }
456
                               apr_file_close(fd);
457
477
                            break;
458
                            break;
478
                        }
459
                        }
479
                        else {
460
                        else {
Lines 482-492 static int process_dir(char *path, apr_p Link Here
482
                    }
463
                    }
483
                    else if (format == VARY_FORMAT_VERSION) {
464
                    else if (format == VARY_FORMAT_VERSION) {
484
                        /* This must be a URL that added Vary headers later,
465
                        /* This must be a URL that added Vary headers later,
485
                         * so kill the orphaned .data file
466
                         * so kill the orphaned cachefile
486
                         */
467
                         */
487
                        apr_file_close(fd);
468
                        apr_file_close(fd);
488
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
469
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
489
                                                    CACHE_DATA_SUFFIX, NULL),
470
                                                    NULL),
490
                                        p);
471
                                        p);
491
                    }
472
                    }
492
                }
473
                }
Lines 505-562 static int process_dir(char *path, apr_p Link Here
505
            current = apr_time_now();
486
            current = apr_time_now();
506
            if (realclean || d->htime < current - deviation
487
            if (realclean || d->htime < current - deviation
507
                || d->htime > current + deviation) {
488
                || d->htime > current + deviation) {
508
                delete_entry(path, d->basename, p);
489
               apr_file_printf(errfile, "header time %lld, current time %lld, deviation time %lld"
490
                              APR_EOL_STR, d->htime, current, deviation );
491
               delete_entry(path, d->basename, p);
509
                unsolicited += d->hsize;
492
                unsolicited += d->hsize;
510
                unsolicited += d->dsize;
511
            }
512
            break;
513
514
        /* single data and header files may be deleted either in realclean
515
         * mode or if their modification timestamp is not within a
516
         * specified positive or negative offset to the current time.
517
         * this handling is necessary due to possible race conditions
518
         * between apache and this process
519
         */
520
        case HEADER:
521
            current = apr_time_now();
522
            nextpath = apr_pstrcat(p, path, "/", d->basename,
523
                                   CACHE_HEADER_SUFFIX, NULL);
524
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
525
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
526
                len = sizeof(format);
527
                if (apr_file_read_full(fd, &format, len,
528
                                       &len) == APR_SUCCESS) {
529
                    if (format == VARY_FORMAT_VERSION) {
530
                        apr_time_t expires;
531
532
                        len = sizeof(expires);
533
534
                        apr_file_read_full(fd, &expires, len, &len);
535
536
                        apr_file_close(fd);
537
538
                        if (expires < current) {
539
                            delete_entry(path, d->basename, p);
540
                        }
541
                        break;
542
                    }
543
                }
544
                apr_file_close(fd);
545
            }
546
547
            if (realclean || d->htime < current - deviation
548
                || d->htime > current + deviation) {
549
                delete_entry(path, d->basename, p);
550
                unsolicited += d->hsize;
551
            }
552
            break;
553
554
        case DATA:
555
            current = apr_time_now();
556
            if (realclean || d->dtime < current - deviation
557
                || d->dtime > current + deviation) {
558
                delete_entry(path, d->basename, p);
559
                unsolicited += d->dsize;
560
            }
493
            }
561
            break;
494
            break;
562
495
Lines 565-571 static int process_dir(char *path, apr_p Link Here
565
         */
498
         */
566
        case TEMP:
499
        case TEMP:
567
            delete_file(path, d->basename, p);
500
            delete_file(path, d->basename, p);
568
            unsolicited += d->dsize;
501
            unsolicited += d->hsize;
569
            break;
502
            break;
570
        }
503
        }
571
    }
504
    }
Lines 595-600 static void purge(char *path, apr_pool_t Link Here
595
    apr_off_t sum, total, entries, etotal;
528
    apr_off_t sum, total, entries, etotal;
596
    ENTRY *e, *n, *oldest;
529
    ENTRY *e, *n, *oldest;
597
530
531
598
    sum = 0;
532
    sum = 0;
599
    entries = 0;
533
    entries = 0;
600
534
Lines 602-608 static void purge(char *path, apr_pool_t Link Here
602
         e != APR_RING_SENTINEL(&root, _entry, link);
536
         e != APR_RING_SENTINEL(&root, _entry, link);
603
         e = APR_RING_NEXT(e, link)) {
537
         e = APR_RING_NEXT(e, link)) {
604
        sum += e->hsize;
538
        sum += e->hsize;
605
        sum += e->dsize;
606
        entries++;
539
        entries++;
607
    }
540
    }
608
541
Lines 621-630 static void purge(char *path, apr_pool_t Link Here
621
    for (e = APR_RING_FIRST(&root);
554
    for (e = APR_RING_FIRST(&root);
622
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
555
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
623
        n = APR_RING_NEXT(e, link);
556
        n = APR_RING_NEXT(e, link);
624
        if (e->response_time > now || e->htime > now || e->dtime > now) {
557
        if (e->response_time > now || e->htime > now ) {
625
            delete_entry(path, e->basename, pool);
558
            delete_entry(path, e->basename, pool);
559
            delete_entry(path, e->name, pool);
626
            sum -= e->hsize;
560
            sum -= e->hsize;
627
            sum -= e->dsize;
628
            entries--;
561
            entries--;
629
            APR_RING_REMOVE(e, link);
562
            APR_RING_REMOVE(e, link);
630
            if (sum <= max) {
563
            if (sum <= max) {
Lines 647-654 static void purge(char *path, apr_pool_t Link Here
647
        n = APR_RING_NEXT(e, link);
580
        n = APR_RING_NEXT(e, link);
648
        if (e->expire != APR_DATE_BAD && e->expire < now) {
581
        if (e->expire != APR_DATE_BAD && e->expire < now) {
649
            delete_entry(path, e->basename, pool);
582
            delete_entry(path, e->basename, pool);
583
            delete_entry(path, e->name, pool);
584
            if(verbose >= 1) {
585
                apr_file_printf(errfile, "Expired: %s\n", e->name);
586
            }
650
            sum -= e->hsize;
587
            sum -= e->hsize;
651
            sum -= e->dsize;
652
            entries--;
588
            entries--;
653
            APR_RING_REMOVE(e, link);
589
            APR_RING_REMOVE(e, link);
654
            if (sum <= max) {
590
            if (sum <= max) {
Lines 676-689 static void purge(char *path, apr_pool_t Link Here
676
        for (e = APR_RING_NEXT(oldest, link);
612
        for (e = APR_RING_NEXT(oldest, link);
677
             e != APR_RING_SENTINEL(&root, _entry, link);
613
             e != APR_RING_SENTINEL(&root, _entry, link);
678
             e = APR_RING_NEXT(e, link)) {
614
             e = APR_RING_NEXT(e, link)) {
679
            if (e->dtime < oldest->dtime) {
615
            if (e->htime < oldest->htime) {
680
                oldest = e;
616
                oldest = e;
681
            }
617
            }
682
        }
618
        }
683
619
684
        delete_entry(path, oldest->basename, pool);
620
        delete_entry(path, oldest->basename, pool);
621
        delete_entry(path, oldest->name, pool);
622
        if(verbose >= 1) {
623
            apr_file_printf(errfile, "Old: (%d s) "
624
                            "(%" APR_OFF_T_FMT " b) %s\n", 
625
                            (int) apr_time_sec(apr_time_now() - oldest->htime),
626
                            oldest->hsize, oldest->name);
627
        }
685
        sum -= oldest->hsize;
628
        sum -= oldest->hsize;
686
        sum -= oldest->dsize;
687
        entries--;
629
        entries--;
688
        APR_RING_REMOVE(oldest, link);
630
        APR_RING_REMOVE(oldest, link);
689
    }
631
    }
Lines 831-840 int main(int argc, const char * const ar Link Here
831
                break;
773
                break;
832
774
833
            case 'v':
775
            case 'v':
834
                if (verbose) {
776
                verbose++;
835
                    usage();
836
                }
837
                verbose = 1;
838
                break;
777
                break;
839
778
840
            case 'r':
779
            case 'r':
Lines 985-991 int main(int argc, const char * const ar Link Here
985
            break;
924
            break;
986
        }
925
        }
987
926
988
        if (dowork && !interrupted) {
927
       if (dowork && !interrupted) {
989
            if (!process_dir(path, instance) && !interrupted) {
928
            if (!process_dir(path, instance) && !interrupted) {
990
                purge(path, instance, max);
929
                purge(path, instance, max);
991
            }
930
            }

Return to bug 39380