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

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

Return to bug 39380