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

(-)dist/modules/cache/mod_disk_cache.c (-398 / +2044 lines)
Lines 22-41 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 URI /foo/bar/baz
31
 *   Generate <hash> off of /foo/bar/baz
33
 *   Generate <hash> off of /foo/bar/baz
32
 *   Open <hash>.header
34
 *   Open <hash>
33
 *   Read in <hash>.header file (may contain Format #1 or Format #2)
35
 *   Read in <hash> file (may contain Format #1 or Format #2)
34
 *   If format #1 (Contains a list of Vary Headers):
36
 *   If format #1 (Contains a list of Vary Headers):
35
 *      Use each header name (from .header) with our request values (headers_in) to
37
 *      Use each header name with our request values (headers_in) to
36
 *      regenerate <hash> using HeaderName+HeaderValue+.../foo/bar/baz
38
 *      regenerate <hash> using HeaderName+HeaderValue+.../foo/bar/baz
37
 *      re-read in <hash>.header (must be format #2)
39
 *      re-read in <hash> (must be format #2)
38
 *   read in <hash>.data
39
 *
40
 *
40
 * Format #1:
41
 * Format #1:
41
 *   apr_uint32_t format;
42
 *   apr_uint32_t format;
Lines 45-54 Link Here
45
 * Format #2:
46
 * Format #2:
46
 *   disk_cache_info_t (first sizeof(apr_uint32_t) bytes is the format)
47
 *   disk_cache_info_t (first sizeof(apr_uint32_t) bytes is the format)
47
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
48
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
49
 *   filename (r->filename) [length is in disk_cache_info_t->filename_len]
48
 *   r->headers_out (delimited by CRLF)
50
 *   r->headers_out (delimited by CRLF)
49
 *   CRLF
51
 *   CRLF
50
 *   r->headers_in (delimited by CRLF)
52
 *   r->headers_in (delimited by CRLF)
51
 *   CRLF
53
 *   CRLF
54
 *
55
 *   Data is stored at the offset specified in disk_cache_info_t.
52
 */
56
 */
53
57
54
module AP_MODULE_DECLARE_DATA disk_cache_module;
58
module AP_MODULE_DECLARE_DATA disk_cache_module;
Lines 62-133 static apr_status_t recall_body(cache_ha Link Here
62
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
66
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
63
                               apr_file_t *file);
67
                               apr_file_t *file);
64
68
69
 
65
/*
70
/*
66
 * Local static functions
71
 * Modified file bucket implementation to be able to deliver files
72
 * while caching.
67
 */
73
 */
68
74
69
static char *header_file(apr_pool_t *p, disk_cache_conf *conf,
75
/* Derived from apr_buckets_file.c */
70
                         disk_cache_object_t *dobj, const char *name)
76
77
#define BUCKET_IS_CACHE(e)        ((e)->type == &bucket_type_diskcache)
78
APU_DECLARE_DATA const apr_bucket_type_t bucket_type_diskcache;
79
80
81
typedef struct diskcache_bucket_data diskcache_bucket_data;
82
83
struct diskcache_bucket_data {
84
    /** Number of buckets using this memory */
85
    apr_bucket_refcount  refcount;
86
    apr_file_t  *fd;
87
    /** The pool into which any needed structures should
88
     *  be created while reading from this file bucket */
89
    apr_pool_t *readpool;
90
    /* Cache update timeout */
91
    apr_interval_time_t updtimeout;
92
93
};
94
95
static void diskcache_bucket_destroy(void *data)
71
{
96
{
72
    if (!dobj->hashfile) {
97
    diskcache_bucket_data *f = data;
73
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
98
74
                                                conf->dirlength, name);
99
    if (apr_bucket_shared_destroy(f)) {
100
        /* no need to close files here; it will get
101
         * done automatically when the pool gets cleaned up */
102
        apr_bucket_free(f);
103
    }
104
}
105
106
107
/* The idea here is to convert diskcache buckets to regular file buckets
108
   as data becomes available */
109
/* FIXME: Maybe we should care about the block argument... */
110
static apr_status_t diskcache_bucket_read(apr_bucket *e, const char **str,
111
                                          apr_size_t *len, 
112
                                          apr_read_type_e block)
113
{
114
    diskcache_bucket_data *a = e->data;
115
    apr_file_t *f = a->fd;
116
    apr_bucket *b = NULL;
117
    char *buf;
118
    apr_status_t rv;
119
    apr_finfo_t finfo;
120
    apr_size_t filelength = e->length;  /* bytes remaining in file past offset */
121
    apr_off_t fileoffset = e->start;
122
    apr_off_t fileend;
123
    apr_size_t available;
124
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
125
    apr_int32_t flags;
126
#endif
127
128
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
129
    if ((flags = apr_file_flags_get(f)) & APR_XTHREAD) {
130
        /* this file descriptor is shared across multiple threads and
131
         * this OS doesn't support that natively, so as a workaround
132
         * we must reopen the file into a->readpool */
133
        const char *fname;
134
        apr_file_name_get(&fname, f);
135
136
        rv = apr_file_open(&f, fname, (flags & ~APR_XTHREAD), 0, a->readpool);
137
        if (rv != APR_SUCCESS)
138
            return rv;
139
140
        a->fd = f;
141
    }
142
#endif
143
144
    /* in case we die prematurely */
145
    *str = NULL;
146
    *len = 0;
147
148
    /* DEBUG
149
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
150
            "disk_cache: Called diskcache_bucket_read");
151
     */
152
153
    while(1) {
154
        /* Figure out how big the file is right now, sit here until
155
           it's grown enough or we get bored */
156
        fileend = 0;
157
        rv = apr_file_seek(f, APR_END, &fileend);
158
        if(rv != APR_SUCCESS) {
159
            return rv;
160
        }
161
162
        if(fileend >= fileoffset + MIN(filelength, CACHE_BUF_SIZE)) {
163
            break;
164
        }
165
166
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, f);
167
        if(rv != APR_SUCCESS ||
168
                finfo.mtime < (apr_time_now() - a->updtimeout) ) 
169
        {
170
            return APR_EGENERAL;
171
        }
172
        apr_sleep(CACHE_LOOP_SLEEP);
173
    }
174
175
    /* Convert this bucket to a zero-length heap bucket so we won't be called
176
       again */
177
    buf = apr_bucket_alloc(0, e->list);
178
    apr_bucket_heap_make(e, buf, 0, apr_bucket_free);
179
180
    /* Wrap as much as possible into a regular file bucket */
181
    available = MIN(filelength, fileend-fileoffset);
182
    b = apr_bucket_file_create(f, fileoffset, available, a->readpool, e->list);
183
    APR_BUCKET_INSERT_AFTER(e, b);
184
185
    /* DEBUG
186
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
187
            "disk_cache: diskcache_bucket_read: Converted to regular file"
188
            " off %" APR_OFF_T_FMT " len %" APR_SIZE_T_FMT,
189
            fileoffset, available);
190
     */
191
192
193
    /* Put any remains in yet another bucket */
194
    if(available < filelength) {
195
        e=b;
196
        /* for efficiency, we can just build a new apr_bucket struct
197
         * to wrap around the existing bucket */
198
        b = apr_bucket_alloc(sizeof(*b), e->list);
199
        b->start  = fileoffset + available;
200
        b->length = filelength - available;
201
        b->data   = a;
202
        b->type   = &bucket_type_diskcache;
203
        b->free   = apr_bucket_free;
204
        b->list   = e->list;
205
        APR_BUCKET_INSERT_AFTER(e, b);
206
    }
207
    else {
208
        diskcache_bucket_destroy(a);
75
    }
209
    }
76
210
77
    if (dobj->prefix) {
211
    *str = buf;
78
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
212
    return APR_SUCCESS;
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
}
213
}
86
214
87
static char *data_file(apr_pool_t *p, disk_cache_conf *conf,
215
static apr_bucket * diskcache_bucket_make(apr_bucket *b,
88
                       disk_cache_object_t *dobj, const char *name)
216
                                                apr_file_t *fd,
217
                                                apr_off_t offset,
218
                                                apr_size_t len, 
219
                                                apr_interval_time_t timeout,
220
                                                apr_pool_t *p)
89
{
221
{
90
    if (!dobj->hashfile) {
222
    diskcache_bucket_data *f;
91
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
223
92
                                                conf->dirlength, name);
224
    f = apr_bucket_alloc(sizeof(*f), b->list);
225
    f->fd = fd;
226
    f->readpool = p;
227
    f->updtimeout = timeout;
228
229
    b = apr_bucket_shared_make(b, f, offset, len);
230
    b->type = &bucket_type_diskcache;
231
232
    return b;
233
}
234
235
static apr_bucket * diskcache_bucket_create(apr_file_t *fd,
236
                                                  apr_off_t offset,
237
                                                  apr_size_t len, 
238
                                                  apr_interval_time_t timeout,
239
                                                  apr_pool_t *p,
240
                                                  apr_bucket_alloc_t *list)
241
{
242
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
243
244
    APR_BUCKET_INIT(b);
245
    b->free = apr_bucket_free;
246
    b->list = list;
247
    return diskcache_bucket_make(b, fd, offset, len, timeout, p);
248
}
249
250
251
/* FIXME: This is probably only correct for the first case, that seems
252
   to be the one that occurs all the time... */
253
static apr_status_t diskcache_bucket_setaside(apr_bucket *data, 
254
                                              apr_pool_t *reqpool)
255
{
256
    diskcache_bucket_data *a = data->data;
257
    apr_file_t *fd = NULL;
258
    apr_file_t *f = a->fd;
259
    apr_pool_t *curpool = apr_file_pool_get(f);
260
261
    if (apr_pool_is_ancestor(curpool, reqpool)) {
262
        return APR_SUCCESS;
263
    }
264
265
    if (!apr_pool_is_ancestor(a->readpool, reqpool)) {
266
        /* FIXME: Debug */
267
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
268
                "disk_cache: diskcache_bucket_setaside: apa2");
269
        a->readpool = reqpool;
270
    }
271
272
    /* FIXME: Debug */
273
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
274
            "disk_cache: diskcache_bucket_setaside: apa3");
275
276
    apr_file_setaside(&fd, f, reqpool);
277
    a->fd = fd;
278
    return APR_SUCCESS;
279
}
280
281
APU_DECLARE_DATA const apr_bucket_type_t bucket_type_diskcache = {
282
    "DISKCACHE", 5, APR_BUCKET_DATA,
283
    diskcache_bucket_destroy,
284
    diskcache_bucket_read,
285
    diskcache_bucket_setaside,
286
    apr_bucket_shared_split,
287
    apr_bucket_shared_copy
288
};
289
290
/* From apr_brigade.c */
291
292
/* A "safe" maximum bucket size, 1Gb */
293
#define MAX_BUCKET_SIZE (0x40000000)
294
295
static apr_bucket * diskcache_brigade_insert(apr_bucket_brigade *bb,
296
                                                   apr_file_t *f, apr_off_t
297
                                                   start, apr_off_t length,
298
                                                   apr_interval_time_t timeout,
299
                                                   apr_pool_t *p)
300
{
301
    apr_bucket *e;
302
303
    if (length < MAX_BUCKET_SIZE) {
304
        e = diskcache_bucket_create(f, start, (apr_size_t)length, timeout, p, 
305
                bb->bucket_alloc);
306
    }
307
    else {
308
        /* Several buckets are needed. */        
309
        e = diskcache_bucket_create(f, start, MAX_BUCKET_SIZE, timeout, p, 
310
                bb->bucket_alloc);
311
312
        while (length > MAX_BUCKET_SIZE) {
313
            apr_bucket *ce;
314
            apr_bucket_copy(e, &ce);
315
            APR_BRIGADE_INSERT_TAIL(bb, ce);
316
            e->start += MAX_BUCKET_SIZE;
317
            length -= MAX_BUCKET_SIZE;
318
        }
319
        e->length = (apr_size_t)length; /* Resize just the last bucket */
93
    }
320
    }
94
321
95
    if (dobj->prefix) {
322
    APR_BRIGADE_INSERT_TAIL(bb, e);
96
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
323
    return e;
97
                           dobj->hashfile, CACHE_DATA_SUFFIX, NULL);
324
}
98
     }
325
99
     else {
326
/* --------------------------------------------------------------- */
100
        return apr_pstrcat(p, conf->cache_root, "/", dobj->hashfile,
327
101
                           CACHE_DATA_SUFFIX, NULL);
328
/*
102
     }
329
 * Local static functions
330
 */
331
332
static char *cache_file(apr_pool_t *p, disk_cache_conf *conf,
333
                        const char *prefix, const char *name)
334
{
335
336
    char *hashfile;
337
338
    hashfile = ap_cache_generate_name(p, conf->dirlevels,
339
                                                conf->dirlength, name);
340
    if (prefix) {
341
        return apr_pstrcat(p, prefix, CACHE_VDIR_SUFFIX, "/",
342
                hashfile, NULL);
343
    }
344
    else {
345
        return apr_pstrcat(p, conf->cache_root, "/", hashfile, NULL);
346
    }
103
}
347
}
104
348
105
static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
349
350
static apr_status_t mkdir_structure(const char *file, apr_pool_t *pool)
106
{
351
{
107
    apr_status_t rv;
352
    apr_status_t rv;
108
    char *p;
353
    char *p;
354
    int i;
109
355
110
    for (p = (char*)file + conf->cache_root_len + 1;;) {
356
    p = strrchr(file, '/');
111
        p = strchr(p, '/');
357
    if(!p) {
112
        if (!p)
358
        return APR_EGENERAL;
113
            break;
359
    }
114
        *p = '\0';
115
360
116
        rv = apr_dir_make(file,
361
    *p = '\0';
117
                          APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
362
118
        if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
363
    /* Be stubborn to overcome racyness when others deletes directories
119
            /* XXX */
364
       while we're trying to create them */
365
    for(i=0; i < 10; i++) {
366
        rv = apr_dir_make_recursive(file, 
367
                                    APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
368
        if(rv == APR_SUCCESS) {
369
            break;
120
        }
370
        }
121
        *p = '/';
122
        ++p;
123
    }
371
    }
372
    *p = '/';
373
374
    return rv;
124
}
375
}
125
376
126
/* htcacheclean may remove directories underneath us.
377
/* htcacheclean may remove directories underneath us.
127
 * So, we'll try renaming three times at a cost of 0.002 seconds.
378
 * So, we'll try renaming three times at a cost of 0.002 seconds.
128
 */
379
 */
129
static apr_status_t safe_file_rename(disk_cache_conf *conf,
380
static apr_status_t safe_file_rename(const char *src, const char *dest,
130
                                     const char *src, const char *dest,
131
                                     apr_pool_t *pool)
381
                                     apr_pool_t *pool)
132
{
382
{
133
    apr_status_t rv;
383
    apr_status_t rv;
Lines 138-185 static apr_status_t safe_file_rename(dis Link Here
138
        int i;
388
        int i;
139
389
140
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
390
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
141
            /* 1000 micro-seconds aka 0.001 seconds. */
391
            mkdir_structure(dest, pool);
142
            apr_sleep(1000);
143
144
            mkdir_structure(conf, dest, pool);
145
392
146
            rv = apr_file_rename(src, dest, pool);
393
            rv = apr_file_rename(src, dest, pool);
394
395
            if(rv != APR_SUCCESS) {
396
                /* 1000 micro-seconds aka 0.001 seconds. */
397
                apr_sleep(1000);
398
            }
147
        }
399
        }
148
    }
400
    }
149
401
150
    return rv;
402
    return rv;
151
}
403
}
152
404
153
static apr_status_t file_cache_el_final(disk_cache_object_t *dobj,
154
                                        request_rec *r)
155
{
156
    /* move the data over */
157
    if (dobj->tfd) {
158
        apr_status_t rv;
159
160
        apr_file_close(dobj->tfd);
161
405
162
        /* This assumes that the tempfile is on the same file system
406
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, 
163
         * as the cache_root. If not, then we need a file copy/move
407
                                            request_rec *r)
164
         * rather than a rename.
408
{
165
         */
409
    /* Remove the cache file */
166
        rv = apr_file_rename(dobj->tempfile, dobj->datafile, r->pool);
410
    apr_file_remove(dobj->cachefile, r->pool);
167
        if (rv != APR_SUCCESS) {
168
            /* XXX log */
169
        }
170
411
171
        dobj->tfd = NULL;
412
    /* If there is an alias file, remove it */
413
    if(dobj->aliasfile != NULL) {
414
        apr_file_remove(dobj->aliasfile, r->pool);
172
    }
415
    }
173
416
174
    return APR_SUCCESS;
175
}
176
177
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, request_rec *r)
178
{
179
    /* Remove the header file and the body file. */
180
    apr_file_remove(dobj->hdrsfile, r->pool);
181
    apr_file_remove(dobj->datafile, r->pool);
182
183
    /* If we opened the temporary data file, close and remove it. */
417
    /* If we opened the temporary data file, close and remove it. */
184
    if (dobj->tfd) {
418
    if (dobj->tfd) {
185
        apr_file_close(dobj->tfd);
419
        apr_file_close(dobj->tfd);
Lines 191-243 static apr_status_t file_cache_errorclea Link Here
191
}
425
}
192
426
193
427
194
/* These two functions get and put state information into the data
195
 * file for an ap_cache_el, this state information will be read
196
 * and written transparent to clients of this module
197
 */
198
static int file_cache_recall_mydata(apr_file_t *fd, cache_info *info,
199
                                    disk_cache_object_t *dobj, request_rec *r)
200
{
201
    apr_status_t rv;
202
    char *urlbuff;
203
    disk_cache_info_t disk_info;
204
    apr_size_t len;
205
206
    /* read the data from the cache file */
207
    len = sizeof(disk_cache_info_t);
208
    rv = apr_file_read_full(fd, &disk_info, len, &len);
209
    if (rv != APR_SUCCESS) {
210
        return rv;
211
    }
212
213
    /* Store it away so we can get it later. */
214
    dobj->disk_info = disk_info;
215
216
    info->status = disk_info.status;
217
    info->date = disk_info.date;
218
    info->expire = disk_info.expire;
219
    info->request_time = disk_info.request_time;
220
    info->response_time = disk_info.response_time;
221
222
    /* Note that we could optimize this by conditionally doing the palloc
223
     * depending upon the size. */
224
    urlbuff = apr_palloc(r->pool, disk_info.name_len + 1);
225
    len = disk_info.name_len;
226
    rv = apr_file_read_full(fd, urlbuff, len, &len);
227
    if (rv != APR_SUCCESS) {
228
        return rv;
229
    }
230
    urlbuff[disk_info.name_len] = '\0';
231
232
    /* check that we have the same URL */
233
    /* Would strncmp be correct? */
234
    if (strcmp(urlbuff, dobj->name) != 0) {
235
        return APR_EGENERAL;
236
    }
237
238
    return APR_SUCCESS;
239
}
240
241
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
428
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
242
                             apr_array_header_t *varray, const char *oldkey)
429
                             apr_array_header_t *varray, const char *oldkey)
243
{
430
{
Lines 327-332 static int create_entity(cache_handle_t Link Here
327
        return DECLINED;
514
        return DECLINED;
328
    }
515
    }
329
516
517
    /* Note, len is -1 if unknown so don't trust it too hard */
518
    if (len > conf->maxfs) {
519
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
520
                     "disk_cache: URL %s failed the size check "
521
                     "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
522
                     key, len, conf->maxfs);
523
        return DECLINED;
524
    }
525
    if (len >= 0 && len < conf->minfs) {
526
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
527
                     "disk_cache: URL %s failed the size check "
528
                     "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
529
                     key, len, conf->minfs);
530
        return DECLINED;
531
    }
532
330
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
533
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
331
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
534
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
332
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
535
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
Lines 334-545 static int create_entity(cache_handle_t Link Here
334
    obj->key = apr_pstrdup(r->pool, key);
537
    obj->key = apr_pstrdup(r->pool, key);
335
538
336
    dobj->name = obj->key;
539
    dobj->name = obj->key;
337
    dobj->prefix = NULL;
338
    /* Save the cache root */
540
    /* Save the cache root */
339
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
541
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
340
    dobj->root_len = conf->cache_root_len;
542
    dobj->root_len = conf->cache_root_len;
341
    dobj->datafile = data_file(r->pool, conf, dobj, key);
543
    dobj->cachefile = cache_file(r->pool, conf, dobj->prefix, key);
342
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
343
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
544
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
545
    dobj->bodysize_in = len;
546
    dobj->bodysize = -1;
547
    dobj->bodyoff = CACHE_DATA_OFFSET;
548
    dobj->updtimeout = conf->updtimeout;
549
    dobj->removedirs = conf->removedirs;
550
551
    /* FIXME: Make the minsize to cache files separately runtime config */
552
    if(len > CACHE_BUF_SIZE &&  r->filename != NULL && 
553
            strlen(r->filename) > 0)
554
    {
555
        dobj->filename = r->filename;
556
        dobj->aliasfile = dobj->cachefile;
557
        dobj->cachefile = cache_file(r->pool, conf, NULL, r->filename);
558
    }
344
559
345
    return OK;
560
    return OK;
346
}
561
}
347
562
348
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
563
564
static apr_status_t file_read_timeout(apr_file_t *file, char * buf,
565
                                      apr_size_t len, apr_time_t timeout)
349
{
566
{
350
    apr_uint32_t format;
567
    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;
568
    apr_finfo_t finfo;
358
    cache_object_t *obj;
569
    apr_status_t rc;
359
    cache_info *info;
360
    disk_cache_object_t *dobj;
361
    int flags;
362
570
363
    h->cache_obj = NULL;
571
    done = 0;
572
    left = len;
364
573
365
    /* Look up entity keyed to 'url' */
574
    while(1) {
366
    if (conf->cache_root == NULL) {
575
        rc = apr_file_read_full(file, buf+done, left, &len);
367
        if (!error_logged) {
576
        if (rc == APR_SUCCESS) {
368
            error_logged = 1;
577
           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
        }
578
        }
372
        return DECLINED;
579
        done += len;
373
    }
580
        left -= len;
374
581
375
    /* Create and init the cache object */
582
        if(rc != APR_EOF) {
376
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
583
            return rc;
377
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
584
        }
585
        rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, file);
586
        if(rc != APR_SUCCESS) {
587
           return rc;
588
        }
589
        if(finfo.mtime < (apr_time_now() - timeout) ) {
590
            return APR_ETIMEDOUT;
591
        }
592
        apr_sleep(CACHE_LOOP_SLEEP);
593
    }
378
594
379
    info = &(obj->info);
595
    return APR_SUCCESS;
596
}
380
597
381
    /* Open the headers file */
382
    dobj->prefix = NULL;
383
598
384
    /* Save the cache root */
599
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);
600
                                const char *key, disk_cache_conf *conf)
386
    dobj->root_len = conf->cache_root_len;
601
{
602
    int flags;
603
    disk_cache_format_t format;
604
    apr_status_t rc;
605
    const char *nkey = key;
606
    disk_cache_info_t disk_info;
607
    cache_object_t *obj = h->cache_obj;
608
    disk_cache_object_t *dobj = obj->vobj;
609
    core_dir_config *pdconf = ap_get_module_config(r->per_dir_config,
610
                                                   &core_module);
387
611
388
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
389
    flags = APR_READ|APR_BINARY|APR_BUFFERED;
612
    flags = APR_READ|APR_BINARY|APR_BUFFERED;
390
    rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
613
#if APR_HAS_SENDFILE
614
    flags |= ((pdconf->enable_sendfile == ENABLE_SENDFILE_OFF)
615
             ? 0 : APR_SENDFILE_ENABLED);
616
#endif  
617
618
    rc = apr_file_open(&dobj->fd, dobj->cachefile, flags, 0, r->pool);
391
    if (rc != APR_SUCCESS) {
619
    if (rc != APR_SUCCESS) {
392
        return DECLINED;
620
        return CACHE_EDECLINED;
393
    }
621
    }
394
622
395
    /* read the format from the cache file */
623
    /* read the format from the cache file */
396
    len = sizeof(format);
624
    rc = apr_file_read_full(dobj->fd, &format, sizeof(format), NULL);
397
    apr_file_read_full(dobj->hfd, &format, len, &len);
625
    if(rc != APR_SUCCESS) {
626
        return CACHE_ENODATA;
627
    }
398
628
399
    if (format == VARY_FORMAT_VERSION) {
629
    if (format == VARY_FORMAT_VERSION) {
400
        apr_array_header_t* varray;
630
        apr_array_header_t* varray;
401
        apr_time_t expire;
631
        apr_time_t expire;
402
632
403
        len = sizeof(expire);
633
        rc = apr_file_read_full(dobj->fd, &expire, sizeof(expire), NULL);
404
        apr_file_read_full(dobj->hfd, &expire, len, &len);
634
        if(rc != APR_SUCCESS) {
635
            return CACHE_ENODATA;
636
        }
405
637
406
        if (expire < r->request_time) {
638
        if (expire < r->request_time) {
407
            return DECLINED;
639
            return CACHE_EDECLINED;
408
        }
640
        }
409
641
410
        varray = apr_array_make(r->pool, 5, sizeof(char*));
642
        varray = apr_array_make(r->pool, 5, sizeof(char*));
411
        rc = read_array(r, varray, dobj->hfd);
643
        rc = read_array(r, varray, dobj->fd);
412
        if (rc != APR_SUCCESS) {
644
        if (rc != APR_SUCCESS) {
413
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
645
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
414
                         "disk_cache: Cannot parse vary header file: %s",
646
                         "disk_cache: Cannot parse vary header file: %s",
415
                         dobj->hdrsfile);
647
                         dobj->cachefile);
416
            return DECLINED;
648
            return CACHE_EDECLINED;
417
        }
649
        }
418
        apr_file_close(dobj->hfd);
650
        apr_file_close(dobj->fd);
419
651
420
        nkey = regen_key(r->pool, r->headers_in, varray, key);
652
        nkey = regen_key(r->pool, r->headers_in, varray, key);
421
653
422
        dobj->hashfile = NULL;
654
        dobj->prefix = dobj->cachefile;
423
        dobj->prefix = dobj->hdrsfile;
655
        dobj->cachefile = cache_file(r->pool, conf, dobj->prefix, nkey);
424
        dobj->hdrsfile = header_file(r->pool, conf, dobj, nkey);
425
656
426
        flags = APR_READ|APR_BINARY|APR_BUFFERED;
657
        rc = apr_file_open(&dobj->fd, dobj->cachefile, flags, 0, r->pool);
427
        rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
428
        if (rc != APR_SUCCESS) {
658
        if (rc != APR_SUCCESS) {
429
            return DECLINED;
659
            return CACHE_EDECLINED;
660
        }
661
        rc = apr_file_read_full(dobj->fd, &format, sizeof(format), NULL);
662
        if(rc != APR_SUCCESS) {
663
            return CACHE_ENODATA;
430
        }
664
        }
431
    }
432
    else if (format != DISK_FORMAT_VERSION) {
433
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
434
                     "cache_disk: File '%s' has a version mismatch. File had version: %d.",
435
                     dobj->hdrsfile, format);
436
        return DECLINED;
437
    }
438
    else {
439
        apr_off_t offset = 0;
440
        /* This wasn't a Vary Format file, so we must seek to the
441
         * start of the file again, so that later reads work.
442
         */
443
        apr_file_seek(dobj->hfd, APR_SET, &offset);
444
        nkey = key;
445
    }
665
    }
446
666
447
    obj->key = nkey;
667
    if (format == ALIAS_FORMAT_VERSION) {
448
    dobj->key = nkey;
668
        disk_cache_alias_t disk_alias;
449
    dobj->name = key;
669
        char name[MAX_STRING_LEN+1];
450
    dobj->datafile = data_file(r->pool, conf, dobj, nkey);
670
        char *filename;
451
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
671
672
        rc = apr_file_read_full(dobj->fd, &disk_alias, sizeof(disk_alias), NULL);
673
        if (rc != APR_SUCCESS) {
674
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
675
                    "disk_cache: Cannot load file header: %s",
676
                    dobj->cachefile);
677
            return CACHE_ENODATA;
678
        }
679
680
        /* We must return DECLINED here, since we can't tell whether the
681
           URL->file mapping has changed without letting the request pass
682
           through the filters that determines an eventual r->filename */
683
        if (disk_alias.expire < r->request_time) {
684
            return CACHE_EDECLINED;
685
        }
686
687
        if(disk_alias.name_len > MAX_STRING_LEN || 
688
                disk_alias.filename_len > MAX_STRING_LEN)
689
        {
690
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
691
                    "disk_cache: Corrupt file header, deleting: %s",
692
                    dobj->cachefile);
693
            file_cache_errorcleanup(dobj, r);
694
            return CACHE_EDECLINED;
695
        }
696
697
        rc = apr_file_read_full(dobj->fd, &name, disk_alias.name_len, NULL);
698
        if(rc != APR_SUCCESS) {
699
            return CACHE_ENODATA;
700
        }
701
        name[disk_alias.name_len] = '\0';
702
        /* check that we have the same URL */
703
        if (strcmp(name, key) != 0) {
704
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
705
                    "disk_cache: Cached URL %s didn't match requested URL %s",
706
                    name, key);
707
            return CACHE_EDECLINED;
708
        }
709
710
        filename = apr_palloc(r->pool, disk_alias.filename_len+1);
711
        rc = apr_file_read_full(dobj->fd, filename, disk_alias.filename_len,
712
                                NULL);
713
        if(rc != APR_SUCCESS) {
714
            return CACHE_ENODATA;
715
        }
716
        filename[disk_alias.filename_len] = '\0';
717
718
        dobj->filename = filename;
719
720
        apr_file_close(dobj->fd);
721
722
        dobj->aliasfile = dobj->cachefile;
723
        dobj->prefix = NULL;
724
        dobj->cachefile = cache_file(r->pool, conf, dobj->prefix, filename);
725
726
        rc = apr_file_open(&dobj->fd, dobj->cachefile, flags, 0, r->pool);
727
        if (rc != APR_SUCCESS) {
728
            return CACHE_EDECLINED;
729
        }
730
        rc = apr_file_read_full(dobj->fd, &format, sizeof(format), NULL);
731
        if(rc != APR_SUCCESS) {
732
            return CACHE_ENODATA;
733
        }
734
735
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
736
                     "disk_cache: URL %s is a file cached in %s",
737
                     key, dobj->cachefile);
452
738
453
    /* Open the data file */
454
    flags = APR_READ|APR_BINARY;
455
#ifdef APR_SENDFILE_ENABLED
456
    flags |= APR_SENDFILE_ENABLED;
457
#endif
458
    rc = apr_file_open(&dobj->fd, dobj->datafile, flags, 0, r->pool);
459
    if (rc != APR_SUCCESS) {
460
        /* XXX: Log message */
461
        return DECLINED;
462
    }
739
    }
463
740
464
    rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, dobj->fd);
741
    if(format != DISK_FORMAT_VERSION) {
465
    if (rc == APR_SUCCESS) {
742
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
466
        dobj->file_size = finfo.size;
743
                     "disk_cache: File '%s' had a version mismatch. File had "
744
                     "version: %d (current is %d). Deleted.", dobj->cachefile,
745
                     format, DISK_FORMAT_VERSION);
746
        file_cache_errorcleanup(dobj, r);
747
        return CACHE_EDECLINED;
467
    }
748
    }
468
749
469
    /* Read the bytes to setup the cache_info fields */
750
    obj->key = nkey;
470
    rc = file_cache_recall_mydata(dobj->hfd, info, dobj, r);
751
    dobj->name = key;
752
753
    /* read the data from the cache file */
754
    rc = apr_file_read_full(dobj->fd, &disk_info, sizeof(disk_info), NULL);
471
    if (rc != APR_SUCCESS) {
755
    if (rc != APR_SUCCESS) {
472
        /* XXX log message */
756
        ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
473
        return DECLINED;
757
                     "disk_cache: Cannot load header file: %s",
758
                     dobj->cachefile);
759
        return CACHE_ENODATA;
474
    }
760
    }
475
761
476
    /* Initialize the cache_handle callback functions */
762
    /* Store it away so we can get it later. */
477
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
763
    dobj->disk_info = disk_info;
478
                 "disk_cache: Recalled cached URL info header %s",  dobj->name);
479
    return OK;
480
}
481
764
482
static int remove_entity(cache_handle_t *h)
765
    return APR_SUCCESS;
483
{
484
    /* Null out the cache object pointer so next time we start from scratch  */
485
    h->cache_obj = NULL;
486
    return OK;
487
}
766
}
488
767
489
static int remove_url(cache_handle_t *h, apr_pool_t *p)
768
static apr_status_t open_header_timeout(cache_handle_t *h, request_rec *r, 
769
                                const char *key, disk_cache_conf *conf,
770
                                disk_cache_object_t *dobj)
490
{
771
{
491
    apr_status_t rc;
772
    apr_status_t rc;
492
    disk_cache_object_t *dobj;
773
    apr_finfo_t finfo;
493
494
    /* Get disk cache object from cache handle */
495
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
496
    if (!dobj) {
497
        return DECLINED;
498
    }
499
774
500
    /* Delete headers file */
775
    while(1) {
501
    if (dobj->hdrsfile) {
776
        if(dobj->fd) {
502
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
777
            apr_file_close(dobj->fd);
503
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
778
        }
779
        rc = open_header(h, r, key, conf);
780
        if(rc != APR_SUCCESS && rc != CACHE_ENODATA) {
781
            return rc;
782
        }
504
783
505
        rc = apr_file_remove(dobj->hdrsfile, p);
784
        if(rc == APR_SUCCESS && dobj->disk_info.bodysize >= 0) {
506
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
785
            break;
507
            /* Will only result in an output if httpd is started with -e debug.
786
        }
508
             * For reason see log_error_core for the case s == NULL.
787
        rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->fd);
509
             */
788
        if(rc != APR_SUCCESS) {
510
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
789
            return rc;
511
                   "disk_cache: Failed to delete headers file %s from cache.",
790
        }
512
                         dobj->hdrsfile);
791
        if(finfo.mtime < (apr_time_now() - dobj->updtimeout)) {
792
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
793
                         "disk_cache: Timed out waiting for header for URL %s"
794
                         " - caching the body failed?", key);
795
            return CACHE_EDECLINED;
796
        }
797
        apr_sleep(CACHE_LOOP_SLEEP);
798
    }
799
800
    return APR_SUCCESS;
801
}
802
803
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
804
{
805
    apr_status_t rc;
806
    disk_cache_object_t *dobj;
807
    cache_info *info;
808
    apr_size_t len;
809
    apr_off_t off, hdroff;
810
    static int error_logged = 0;
811
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
812
                                                 &disk_cache_module);
813
    apr_finfo_t finfo;
814
815
    h->cache_obj = NULL;
816
817
    /* Look up entity keyed to 'url' */
818
    if (conf->cache_root == NULL) {
819
        if (!error_logged) {
820
            error_logged = 1;
821
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
822
                         "disk_cache: Cannot cache files to disk without a "
823
                         "CacheRoot specified.");
824
        }
825
        return DECLINED;
826
    }
827
828
    /* Create and init the cache object */
829
    h->cache_obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
830
    h->cache_obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
831
    info = &(h->cache_obj->info);
832
833
    /* Save the cache root */
834
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
835
    dobj->root_len = conf->cache_root_len;
836
837
    dobj->cachefile = cache_file(r->pool, conf, dobj->prefix, key);
838
839
    dobj->updtimeout = conf->updtimeout;
840
    dobj->removedirs = conf->removedirs;
841
842
    /* Open header and read basic info, wait until header contains
843
       valid size information for the body */
844
    rc = open_header_timeout(h, r, key, conf, dobj);
845
    if(rc != APR_SUCCESS) {
846
        return DECLINED;
847
    }
848
849
    info->status = dobj->disk_info.status;
850
    info->date = dobj->disk_info.date;
851
    info->expire = dobj->disk_info.expire;
852
    info->request_time = dobj->disk_info.request_time;
853
    info->response_time = dobj->disk_info.response_time;
854
855
    dobj->bodyoff = (apr_off_t) dobj->disk_info.bodyoff;
856
    dobj->bodysize_in = (apr_off_t) dobj->disk_info.bodysize;
857
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
858
859
    if(dobj->disk_info.name_len > MAX_STRING_LEN ||
860
            dobj->disk_info.filename_len > MAX_STRING_LEN) 
861
    {
862
        ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
863
                "disk_cache: Corrupt cache header, deleting: %s",
864
                dobj->cachefile);
865
        file_cache_errorcleanup(dobj, r);
866
        return DECLINED;
867
    }
868
869
    if(dobj->aliasfile == NULL) {
870
        char urlbuff[MAX_STRING_LEN];
871
872
        len = dobj->disk_info.name_len;
873
874
        if(len > 0) {
875
            rc = file_read_timeout(dobj->fd, urlbuff, len, dobj->updtimeout);
876
            if (rc == APR_ETIMEDOUT) {
877
                ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
878
                             "disk_cache: Timed out waiting for urlbuff for "
879
                             "URL %s - caching failed?",  key);
880
                return DECLINED;
881
            }
882
            else if(rc != APR_SUCCESS) {
883
                ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
884
                             "disk_cache: Error reading urlbuff for URL %s",
885
                             key);
886
                return DECLINED;
887
            }
888
        }
889
        urlbuff[len] = '\0';
890
891
        /* check that we have the same URL */
892
        if (strcmp(urlbuff, dobj->name) != 0) {
893
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
894
                         "disk_cache: Cached URL %s didn't match requested "
895
                         "URL %s", urlbuff, dobj->name);
896
            return DECLINED;
897
        }
898
    }
899
    else {
900
        /* The URL has already been checked when we loaded the alias header */
901
        if(dobj->disk_info.name_len > 0) {
902
            /* Seek past the URL */
903
            off = dobj->disk_info.name_len;
904
            rc = apr_file_seek(dobj->fd, APR_CUR, &off);
905
            if(rc != APR_SUCCESS) {
906
                return rc;
907
            }
908
        }
909
    }
910
911
    /* Read in the filename */
912
    if(dobj->disk_info.filename_len > 0) {
913
        char fnamebuf[MAX_STRING_LEN];
914
915
        len = dobj->disk_info.filename_len;
916
        rc = file_read_timeout(dobj->fd, fnamebuf, len, dobj->updtimeout);
917
        if (rc == APR_ETIMEDOUT) {
918
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
919
                         "disk_cache: Timed out waiting for filename for "
920
                         "URL %s - caching failed?", key);
921
            return DECLINED;
922
        }
923
        else if(rc != APR_SUCCESS) {
924
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
925
                         "disk_cache: Error reading filename for URL %s",
926
                         key);
927
            return DECLINED;
928
        }
929
        fnamebuf[len] = '\0';
930
931
        if(dobj->aliasfile == NULL) {
932
            dobj->filename = apr_pstrdup(r->pool, fnamebuf);
933
        }
934
        else {
935
            /* Sanity check so we are delivering the right file */
936
            if(strcmp(fnamebuf, dobj->filename) != 0) {
937
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
938
                             "disk_cache: Cached file %s didn't match wanted "
939
                             "file %s", fnamebuf, dobj->filename);
940
                return DECLINED;
941
            }
942
        }
943
        r->filename = dobj->filename;
944
    }
945
946
    /* Remember where we were */
947
    hdroff = 0;
948
    rc = apr_file_seek(dobj->fd, APR_CUR, &hdroff);
949
    if(rc != APR_SUCCESS) {
950
        return rc;
951
    }
952
953
    /* Wait here until we get data in the data segment, do quick sanity check 
954
       if it's there already */
955
    while(1) {
956
        off = 0;
957
        rc = apr_file_seek(dobj->fd, APR_END, &off);
958
        if(rc != APR_SUCCESS) {
959
            return rc;
960
        }
961
        if(off > dobj->bodyoff) {
962
            dobj->bodysize = off - dobj->bodyoff;
963
        }
964
        else {
965
            dobj->bodysize = 0;
966
        }
967
968
        if(dobj->bodysize_in < dobj->bodysize) {
969
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
970
                         "disk_cache: Bad cached body for URL %s, size %"
971
                         APR_OFF_T_FMT " != %" APR_OFF_T_FMT,  dobj->name,
972
                         dobj->bodysize, off - dobj->bodyoff);
973
            file_cache_errorcleanup(dobj, r);
513
            return DECLINED;
974
            return DECLINED;
514
        }
975
        }
976
        else if(dobj->bodysize_in > dobj->bodysize) {
977
            /* Still caching or failed? */
978
            rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->fd);
979
            if(rc != APR_SUCCESS ||
980
                    finfo.mtime < (apr_time_now() - dobj->updtimeout) ) 
981
            {
982
                ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
983
                             "disk_cache: Body for URL %s is too small - "
984
                             "caching the body failed?", dobj->name);
985
                return DECLINED;
986
            }
987
        }
988
        if(dobj->bodysize > 0) {
989
            break;
990
        }
991
        apr_sleep(CACHE_LOOP_SLEEP);
992
    }
993
994
    /* Go back to where we were */
995
    rc = apr_file_seek(dobj->fd, APR_SET, &hdroff);
996
    if(rc != APR_SUCCESS) {
997
        return rc;
515
    }
998
    }
516
999
517
     /* Delete data file */
1000
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
518
    if (dobj->datafile) {
1001
                 "disk_cache: Recalled status for cached URL %s",  dobj->name);
1002
    return OK;
1003
}
1004
1005
1006
static int remove_entity(cache_handle_t *h)
1007
{
1008
    /* Null out the cache object pointer so next time we start from scratch  */
1009
    h->cache_obj = NULL;
1010
    return OK;
1011
}
1012
1013
1014
/* FIXME: It would make sense to have the errorcleanup and this function
1015
   to be the same */
1016
static int remove_url(cache_handle_t *h, apr_pool_t *p)
1017
{
1018
    apr_status_t rc;
1019
    disk_cache_object_t *dobj;
1020
1021
    /* Get disk cache object from cache handle */
1022
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1023
    if (!dobj) {
1024
        return DECLINED;
1025
    }
1026
1027
    /* Delete cache file */
1028
    if (dobj->cachefile) {
519
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1029
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
520
                     "disk_cache: Deleting %s from cache.", dobj->datafile);
1030
                     "disk_cache: Deleting %s from cache.", dobj->cachefile);
521
1031
522
        rc = apr_file_remove(dobj->datafile, p);
1032
        rc = apr_file_remove(dobj->cachefile, p);
523
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
1033
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
524
            /* Will only result in an output if httpd is started with -e debug.
1034
            /* Will only result in an output if httpd is started with -e debug.
525
             * For reason see log_error_core for the case s == NULL.
1035
             * For reason see log_error_core for the case s == NULL.
526
             */
1036
             */
527
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
1037
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
528
                      "disk_cache: Failed to delete data file %s from cache.",
1038
                         "disk_cache: Failed to delete headers file %s "
529
                         dobj->datafile);
1039
                         "from cache.", dobj->cachefile);
530
            return DECLINED;
1040
            return DECLINED;
531
        }
1041
        }
532
    }
1042
    }
533
1043
1044
    if(!dobj->removedirs) {
1045
        return OK;
1046
    }
1047
534
    /* now delete directories as far as possible up to our cache root */
1048
    /* now delete directories as far as possible up to our cache root */
535
    if (dobj->root) {
1049
    if (dobj->root) {
536
        const char *str_to_copy;
1050
        if (dobj->cachefile) {
537
538
        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->datafile;
539
        if (str_to_copy) {
540
            char *dir, *slash, *q;
1051
            char *dir, *slash, *q;
541
1052
542
            dir = apr_pstrdup(p, str_to_copy);
1053
            dir = apr_pstrdup(p, dobj->cachefile);
543
1054
544
            /* remove filename */
1055
            /* remove filename */
545
            slash = strrchr(dir, '/');
1056
            slash = strrchr(dir, '/');
Lines 550-559 static int remove_url(cache_handle_t *h, Link Here
550
             * in the way as far as possible
1061
             * in the way as far as possible
551
             *
1062
             *
552
             * Note: due to the way we constructed the file names in
1063
             * Note: due to the way we constructed the file names in
553
             * header_file and data_file, we are guaranteed that the
1064
             * cache_file, we are guaranteed that the cache_root is suffixed by
554
             * cache_root is suffixed by at least one '/' which will be
1065
             * at least one '/' which will be turned into a terminating null by
555
             * turned into a terminating null by this loop.  Therefore,
1066
             * this loop.  Therefore, we won't either delete or go above our
556
             * we won't either delete or go above our cache root.
1067
             * cache root.
557
             */
1068
             */
558
            for (q = dir + dobj->root_len; *q ; ) {
1069
            for (q = dir + dobj->root_len; *q ; ) {
559
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1070
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
Lines 584-590 static apr_status_t read_array(request_r Link Here
584
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
1095
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
585
        if (rv != APR_SUCCESS) {
1096
        if (rv != APR_SUCCESS) {
586
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1097
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
587
                          "Premature end of vary array.");
1098
                          "disk_cache: Premature end of vary array.");
588
            return rv;
1099
            return rv;
589
        }
1100
        }
590
1101
Lines 639-645 static apr_status_t store_array(apr_file Link Here
639
                         &amt);
1150
                         &amt);
640
}
1151
}
641
1152
642
static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
1153
static apr_status_t read_table(request_rec *r,
643
                               apr_table_t *table, apr_file_t *file)
1154
                               apr_table_t *table, apr_file_t *file)
644
{
1155
{
645
    char w[MAX_STRING_LEN];
1156
    char w[MAX_STRING_LEN];
Lines 652-659 static apr_status_t read_table(cache_han Link Here
652
        /* ### What about APR_EOF? */
1163
        /* ### What about APR_EOF? */
653
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
1164
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
654
        if (rv != APR_SUCCESS) {
1165
        if (rv != APR_SUCCESS) {
655
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
656
                          "Premature end of cache headers.");
657
            return rv;
1166
            return rv;
658
        }
1167
        }
659
1168
Lines 696-702 static apr_status_t read_table(cache_han Link Here
696
            }
1205
            }
697
            if (maybeASCII > maybeEBCDIC) {
1206
            if (maybeASCII > maybeEBCDIC) {
698
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1207
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
699
                             "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
1208
                             "disk_cache: CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
700
                             r->filename);
1209
                             r->filename);
701
                inbytes_left = outbytes_left = cp - w;
1210
                inbytes_left = outbytes_left = cp - w;
702
                apr_xlate_conv_buffer(ap_hdrs_from_ascii,
1211
                apr_xlate_conv_buffer(ap_hdrs_from_ascii,
Lines 721-726 static apr_status_t read_table(cache_han Link Here
721
    return APR_SUCCESS;
1230
    return APR_SUCCESS;
722
}
1231
}
723
1232
1233
1234
static apr_status_t read_table_full(cache_handle_t *handle, request_rec *r,
1235
                               apr_table_t **table, apr_file_t *file,
1236
                               apr_time_t timeout)
1237
{
1238
    apr_off_t off;
1239
    apr_finfo_t finfo;
1240
    apr_status_t rv;
1241
1242
    off = 0;
1243
    rv = apr_file_seek(file, APR_CUR, &off);
1244
    if(rv != APR_SUCCESS) {
1245
        return rv;
1246
    }
1247
1248
    while(1) {
1249
        *table = apr_table_make(r->pool, 20);
1250
        rv = read_table(r, *table, file);
1251
        if(rv == APR_SUCCESS) {
1252
            break;
1253
        }
1254
        apr_table_clear(*table);
1255
1256
        rv = apr_file_seek(file, APR_SET, &off);
1257
        if(rv != APR_SUCCESS) {
1258
            return rv;
1259
        }
1260
1261
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, file);
1262
        if(rv != APR_SUCCESS ||
1263
                finfo.mtime < (apr_time_now() - timeout) ) 
1264
        {
1265
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1266
                          "disk_cache: Timed out waiting for cache headers "
1267
                          "URL %s", handle->cache_obj->key);
1268
            return APR_EGENERAL;
1269
        }
1270
        apr_sleep(CACHE_LOOP_SLEEP);
1271
    }
1272
1273
    return APR_SUCCESS;
1274
}
1275
1276
1277
724
/*
1278
/*
725
 * Reads headers from a buffer and returns an array of headers.
1279
 * Reads headers from a buffer and returns an array of headers.
726
 * Returns NULL on file error
1280
 * Returns NULL on file error
Lines 731-751 static apr_status_t read_table(cache_han Link Here
731
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
1285
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
732
{
1286
{
733
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1287
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1288
    apr_status_t rv;
1289
    apr_off_t off;
734
1290
735
    /* This case should not happen... */
1291
    /* This case should not happen... */
736
    if (!dobj->hfd) {
1292
    if (!dobj->fd) {
737
        /* XXX log message */
1293
        /* XXX log message */
738
        return APR_NOTFOUND;
1294
        return APR_NOTFOUND;
739
    }
1295
    }
740
1296
741
    h->req_hdrs = apr_table_make(r->pool, 20);
1297
    rv = read_table_full(h, r, &(h->resp_hdrs), dobj->fd, dobj->updtimeout);
742
    h->resp_hdrs = apr_table_make(r->pool, 20);
1298
    if(rv != APR_SUCCESS) {
1299
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1300
                     "disk_cache: Timed out waiting for response headers "
1301
                     "for URL %s - caching failed?",  dobj->name);
1302
        return rv;
1303
    }
743
1304
744
    /* Call routine to read the header lines/status line */
1305
    rv = read_table_full(h, r, &(h->req_hdrs), dobj->fd, dobj->updtimeout);
745
    read_table(h, r, h->resp_hdrs, dobj->hfd);
1306
    if(rv != APR_SUCCESS) {
746
    read_table(h, r, h->req_hdrs, dobj->hfd);
1307
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1308
                     "disk_cache: Timed out waiting for request headers "
1309
                     "for URL %s - caching failed?",  dobj->name);
1310
        return rv;
1311
    }
747
1312
748
    apr_file_close(dobj->hfd);
1313
    off = 0;
1314
    rv = apr_file_seek(dobj->fd, APR_CUR, &off);
1315
    if(rv != APR_SUCCESS || off > dobj->bodyoff) {
1316
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1317
                     "disk_cache: Too much headers (%" APR_OFF_T_FMT " > %"
1318
                     APR_OFF_T_FMT ") for URL %s, skipping",
1319
                     off, dobj->bodyoff, dobj->name);
1320
        return APR_EGENERAL;
1321
    }
749
1322
750
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1323
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
751
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
1324
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
Lines 757-765 static apr_status_t recall_body(cache_ha Link Here
757
    apr_bucket *e;
1330
    apr_bucket *e;
758
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1331
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
759
1332
760
    e = apr_bucket_file_create(dobj->fd, 0, (apr_size_t) dobj->file_size, p,
1333
761
                               bb->bucket_alloc);
1334
    /* Insert as much as possible as regular file (ie. sendfile():able) */
762
    APR_BRIGADE_INSERT_HEAD(bb, e);
1335
    if(dobj->bodysize > 0) {
1336
        if(apr_brigade_insert_file(bb, dobj->fd, dobj->bodyoff, 
1337
                    dobj->bodysize, p) == NULL) 
1338
        {
1339
            return APR_ENOMEM;
1340
        }
1341
    }
1342
1343
    /* Insert any remainder as read-while-caching bucket */
1344
    if(dobj->bodysize < dobj->bodysize_in) {
1345
        if(diskcache_brigade_insert(bb, dobj->fd, 
1346
                                    dobj->bodyoff + dobj->bodysize, 
1347
                                    dobj->bodysize_in - dobj->bodysize,
1348
                                    dobj->updtimeout, p
1349
                    ) == NULL) 
1350
        {
1351
            return APR_ENOMEM;
1352
        }
1353
    }
1354
1355
763
    e = apr_bucket_eos_create(bb->bucket_alloc);
1356
    e = apr_bucket_eos_create(bb->bucket_alloc);
764
    APR_BRIGADE_INSERT_TAIL(bb, e);
1357
    APR_BRIGADE_INSERT_TAIL(bb, e);
765
1358
Lines 800-899 static apr_status_t store_table(apr_file Link Here
800
    return rv;
1393
    return rv;
801
}
1394
}
802
1395
803
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
1396
1397
static apr_status_t store_vary_header(cache_handle_t *h, disk_cache_conf *conf,
1398
                                       request_rec *r, cache_info *info,
1399
                                       const char *varyhdr)
804
{
1400
{
805
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1401
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
806
                                                 &disk_cache_module);
1402
    apr_array_header_t* varray;
1403
    const char *vfile;
807
    apr_status_t rv;
1404
    apr_status_t rv;
1405
    int flags;
1406
    disk_cache_format_t format = VARY_FORMAT_VERSION;
1407
    struct iovec iov[2];
808
    apr_size_t amt;
1408
    apr_size_t amt;
809
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
810
1409
811
    disk_cache_info_t disk_info;
1410
    if(dobj->prefix != NULL) {
812
    struct iovec iov[2];
1411
        vfile = dobj->prefix;
1412
    }
1413
    else if(dobj->aliasfile != NULL) {
1414
        vfile = dobj->aliasfile;
1415
    }
1416
    else {
1417
        vfile = dobj->cachefile;
1418
    }
813
1419
814
    /* This is flaky... we need to manage the cache_info differently */
1420
    flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL | APR_BUFFERED;
815
    h->cache_obj->info = *info;
1421
    rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile, flags, r->pool);
1422
    if (rv != APR_SUCCESS) {
1423
        return rv;
1424
    }
816
1425
817
    if (r->headers_out) {
1426
    iov[0].iov_base = (void*)&format;
818
        const char *tmp;
1427
    iov[0].iov_len = sizeof(format);
819
1428
820
        tmp = apr_table_get(r->headers_out, "Vary");
1429
    iov[1].iov_base = (void*)&info->expire;
1430
    iov[1].iov_len = sizeof(info->expire);
821
1431
822
        if (tmp) {
1432
    rv = apr_file_writev(dobj->fd, (const struct iovec *) &iov, 2, &amt);
823
            apr_array_header_t* varray;
1433
    if (rv != APR_SUCCESS) {
824
            apr_uint32_t format = VARY_FORMAT_VERSION;
1434
        file_cache_errorcleanup(dobj, r);
1435
        return rv;
1436
    }
1437
1438
    varray = apr_array_make(r->pool, 6, sizeof(char*));
1439
    tokens_to_array(r->pool, varyhdr, varray);
825
1440
826
            mkdir_structure(conf, dobj->hdrsfile, r->pool);
1441
    rv = store_array(dobj->tfd, varray);
1442
    if (rv != APR_SUCCESS) {
1443
        file_cache_errorcleanup(dobj, r);
1444
        return rv;
1445
    }
827
1446
828
            rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
1447
    rv = apr_file_close(dobj->tfd);
829
                                 APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL,
1448
    dobj->tfd = NULL;
830
                                 r->pool);
1449
    if (rv != APR_SUCCESS) {
1450
        file_cache_errorcleanup(dobj, r);
1451
        return rv;
1452
    }
831
1453
832
            if (rv != APR_SUCCESS) {
1454
    rv = safe_file_rename(dobj->tempfile, vfile, r->pool);
833
                return rv;
1455
    if (rv != APR_SUCCESS) {
834
            }
1456
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1457
                     "disk_cache: rename tempfile to varyfile failed: "
1458
                     "%s -> %s", dobj->tempfile, vfile);
1459
        file_cache_errorcleanup(dobj, r);
1460
        return rv;
1461
    }
1462
1463
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1464
1465
    if(dobj->prefix == NULL) {
1466
        const char *tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
835
1467
836
            amt = sizeof(format);
1468
        if(dobj->aliasfile != NULL) {
837
            apr_file_write(dobj->tfd, &format, &amt);
1469
            dobj->prefix = dobj->aliasfile;
1470
            dobj->aliasfile = cache_file(r->pool, conf, dobj->prefix, tmp);
1471
        }
1472
        else {
1473
            dobj->prefix = dobj->cachefile;
1474
            dobj->cachefile = cache_file(r->pool, conf, dobj->prefix, tmp);
1475
        }
1476
    }
838
1477
839
            amt = sizeof(info->expire);
1478
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
840
            apr_file_write(dobj->tfd, &info->expire, &amt);
1479
                 "disk_cache: Stored vary header for URL %s", dobj->name);
841
1480
842
            varray = apr_array_make(r->pool, 6, sizeof(char*));
1481
    return APR_SUCCESS;
843
            tokens_to_array(r->pool, tmp, varray);
1482
}
844
1483
845
            store_array(dobj->tfd, varray);
846
1484
847
            apr_file_close(dobj->tfd);
1485
static apr_status_t store_alias_header(disk_cache_object_t *dobj,
1486
                                       disk_cache_conf *conf,
1487
                                       request_rec *r, cache_info *info)
1488
{
1489
    disk_cache_format_t format = ALIAS_FORMAT_VERSION;
1490
    disk_cache_alias_t disk_alias;
1491
    apr_status_t rv;
1492
    struct iovec iov[4];
1493
    apr_size_t amt;
1494
    int flags;
848
1495
849
            dobj->tfd = NULL;
1496
    disk_alias.name_len = strlen(dobj->name);
1497
    disk_alias.filename_len = strlen(dobj->filename);
1498
    disk_alias.expire = info->expire;
1499
1500
    iov[0].iov_base = (void*)&format;
1501
    iov[0].iov_len = sizeof(format);
1502
    iov[1].iov_base = (void*)&disk_alias;
1503
    iov[1].iov_len = sizeof(disk_alias);
1504
    iov[2].iov_base = (void*)dobj->name;
1505
    iov[2].iov_len = disk_alias.name_len;
1506
    iov[3].iov_base = (void*)dobj->filename;
1507
    iov[3].iov_len = disk_alias.filename_len;
850
1508
851
            rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile,
1509
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
852
                                  r->pool);
1510
            "disk_cache: store_alias_header %s -> %s in %s",
853
            if (rv != APR_SUCCESS) {
1511
            dobj->name, dobj->filename, dobj->aliasfile);
854
                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
855
                    "disk_cache: rename tempfile to varyfile failed: %s -> %s",
856
                    dobj->tempfile, dobj->hdrsfile);
857
                return rv;
858
            }
859
1512
860
            dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1513
    flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL | APR_BUFFERED;
861
            tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
1514
    rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile, flags, r->pool);
862
            dobj->prefix = dobj->hdrsfile;
1515
    if (rv != APR_SUCCESS) {
863
            dobj->hashfile = NULL;
1516
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
864
            dobj->datafile = data_file(r->pool, conf, dobj, tmp);
1517
                "disk_cache: store_alias_header mktemp failed URL %s",
865
            dobj->hdrsfile = header_file(r->pool, conf, dobj, tmp);
1518
                dobj->name);
866
        }
1519
        return rv;
867
    }
1520
    }
868
1521
1522
    rv = apr_file_writev(dobj->tfd, (const struct iovec *) &iov, 4, &amt);
1523
1524
    if (rv != APR_SUCCESS) {
1525
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1526
                "disk_cache: store_alias_header writev failed URL %s",
1527
                dobj->name);
1528
        file_cache_errorcleanup(dobj, r);
1529
        return rv;
1530
    }
869
1531
870
    rv = apr_file_mktemp(&dobj->hfd, dobj->tempfile,
1532
    rv = apr_file_close(dobj->tfd);
871
                         APR_CREATE | APR_WRITE | APR_BINARY |
1533
    dobj->tfd = NULL;
872
                         APR_BUFFERED | APR_EXCL, r->pool);
1534
    if (rv != APR_SUCCESS) {
1535
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1536
                "disk_cache: store_alias_header close failed URL %s",
1537
                dobj->name);
1538
        file_cache_errorcleanup(dobj, r);
1539
        return rv;
1540
    }
873
1541
1542
    rv = safe_file_rename(dobj->tempfile, dobj->aliasfile, r->pool);
874
    if (rv != APR_SUCCESS) {
1543
    if (rv != APR_SUCCESS) {
1544
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1545
                     "disk_cache: rename tempfile to alias header failed: "
1546
                     "%s -> %s", dobj->tempfile, dobj->aliasfile);
1547
        file_cache_errorcleanup(dobj, r);
875
        return rv;
1548
        return rv;
876
    }
1549
    }
877
1550
878
    dobj->name = h->cache_obj->key;
1551
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1552
1553
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1554
                 "disk_cache: Stored alias header for URL %s -> file %s",
1555
                  dobj->name, dobj->filename);
1556
1557
    return APR_SUCCESS;
1558
}
1559
1560
1561
static apr_status_t store_disk_header(disk_cache_object_t *dobj,
1562
                                       request_rec *r, cache_info *info,
1563
                                       apr_time_t lastmod)
1564
{
1565
    disk_cache_format_t format = DISK_FORMAT_VERSION;
1566
    struct iovec iov[4];
1567
    int niov;
1568
    disk_cache_info_t disk_info;
1569
    apr_size_t amt;
1570
    apr_status_t rv;
879
1571
880
    disk_info.format = DISK_FORMAT_VERSION;
881
    disk_info.date = info->date;
1572
    disk_info.date = info->date;
882
    disk_info.expire = info->expire;
1573
    disk_info.expire = info->expire;
883
    disk_info.entity_version = dobj->disk_info.entity_version++;
1574
    disk_info.entity_version = dobj->disk_info.entity_version++;
884
    disk_info.request_time = info->request_time;
1575
    disk_info.request_time = info->request_time;
885
    disk_info.response_time = info->response_time;
1576
    disk_info.response_time = info->response_time;
886
    disk_info.status = info->status;
1577
    disk_info.status = info->status;
1578
    disk_info.bodyoff = dobj->bodyoff;
1579
    disk_info.bodysize = dobj->bodysize_in;
1580
    disk_info.lastmod = lastmod;
1581
1582
    niov = 0;
1583
    iov[niov].iov_base = (void*)&format;
1584
    iov[niov++].iov_len = sizeof(format);
1585
    iov[niov].iov_base = (void*)&disk_info;
1586
    iov[niov++].iov_len = sizeof(disk_cache_info_t);
1587
1588
    /* Only record the URL if we're the primary record for that URL */
1589
    if (dobj->aliasfile == NULL) {
1590
        disk_info.name_len = strlen(dobj->name);
1591
        iov[niov].iov_base = (void*)dobj->name;
1592
        iov[niov++].iov_len = disk_info.name_len;
1593
    }
1594
    else {
1595
        disk_info.name_len = 0;
1596
    }
887
1597
888
    disk_info.name_len = strlen(dobj->name);
1598
    if(r->filename != NULL && strlen(r->filename) > 0) {
889
1599
        disk_info.filename_len = strlen(r->filename);
890
    iov[0].iov_base = (void*)&disk_info;
1600
        iov[niov].iov_base = (void*)r->filename;
891
    iov[0].iov_len = sizeof(disk_cache_info_t);
1601
        iov[niov++].iov_len = disk_info.filename_len;
892
    iov[1].iov_base = (void*)dobj->name;
1602
    }
893
    iov[1].iov_len = disk_info.name_len;
1603
    else {
1604
        disk_info.filename_len = 0;
1605
    }
894
1606
895
    rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, 2, &amt);
1607
    rv = apr_file_writev(dobj->fd, (const struct iovec *) &iov, niov, &amt);
896
    if (rv != APR_SUCCESS) {
1608
    if (rv != APR_SUCCESS) {
1609
        file_cache_errorcleanup(dobj, r);
897
        return rv;
1610
        return rv;
898
    }
1611
    }
899
1612
Lines 911-918 static apr_status_t store_headers(cache_ Link Here
911
1624
912
        headers_out = apr_table_overlay(r->pool, headers_out,
1625
        headers_out = apr_table_overlay(r->pool, headers_out,
913
                                        r->err_headers_out);
1626
                                        r->err_headers_out);
914
        rv = store_table(dobj->hfd, headers_out);
1627
        rv = store_table(dobj->fd, headers_out);
915
        if (rv != APR_SUCCESS) {
1628
        if (rv != APR_SUCCESS) {
1629
            file_cache_errorcleanup(dobj, r);
916
            return rv;
1630
            return rv;
917
        }
1631
        }
918
    }
1632
    }
Lines 924-958 static apr_status_t store_headers(cache_ Link Here
924
1638
925
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
1639
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
926
                                                 r->server);
1640
                                                 r->server);
927
        rv = store_table(dobj->hfd, headers_in);
1641
        rv = store_table(dobj->fd, headers_in);
928
        if (rv != APR_SUCCESS) {
1642
        if (rv != APR_SUCCESS) {
1643
            file_cache_errorcleanup(dobj, r);
929
            return rv;
1644
            return rv;
930
        }
1645
        }
931
    }
1646
    }
932
1647
933
    apr_file_close(dobj->hfd); /* flush and close */
1648
    return APR_SUCCESS;
934
1649
}
935
    /* Remove old file with the same name. If remove fails, then
1650
936
     * perhaps we need to create the directory tree where we are
1651
/* Assumes file positioned just after the initial header, ie. as left by
937
     * about to write the new headers file.
1652
   open_header() */
1653
static int is_file_current(request_rec *r, disk_cache_object_t *dobj,
1654
                           apr_time_t lastmod)
1655
{
1656
    char filename[MAX_STRING_LEN+1];
1657
1658
    if(dobj->disk_info.name_len > 0 ||
1659
            dobj->disk_info.filename_len == 0 ||
1660
            dobj->disk_info.filename_len > MAX_STRING_LEN ||
1661
            dobj->bodysize_in != dobj->disk_info.bodysize ||
1662
            lastmod == APR_DATE_BAD ||
1663
            dobj->disk_info.lastmod == APR_DATE_BAD ||
1664
            lastmod != dobj->disk_info.lastmod)
1665
    {
1666
        return FALSE;
1667
    }
1668
    else if (apr_file_read_full(dobj->fd, filename, 
1669
                dobj->disk_info.filename_len, NULL)
1670
            == APR_SUCCESS)
1671
    {
1672
        filename[dobj->disk_info.filename_len] = '\0';
1673
        if(strcmp(r->filename, filename) != 0) {
1674
            return FALSE;
1675
        }
1676
1677
        /* This is where a file that's OK ends up */
1678
        return TRUE;
1679
    }
1680
1681
    return FALSE;
1682
}
1683
1684
1685
static apr_status_t open_new_cachefile(request_rec *r,
1686
                                       disk_cache_object_t *dobj)
1687
{
1688
    int flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_BUFFERED | APR_EXCL;
1689
    apr_status_t rv;
1690
1691
    while(1) {
1692
        rv = apr_file_open(&dobj->fd, dobj->cachefile, flags, 
1693
                APR_FPROT_UREAD | APR_FPROT_UWRITE, r->pool);
1694
1695
        /* FIXME: Debug */
1696
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1697
                     "disk_cache: open_new_cachefile: Opening %s",
1698
                     dobj->cachefile);
1699
1700
        if(APR_STATUS_IS_EEXIST(rv)) {
1701
            apr_finfo_t finfo;
1702
1703
            rv = apr_stat(&finfo, dobj->cachefile, APR_FINFO_MTIME, 
1704
                          r->pool);
1705
            if(APR_STATUS_IS_ENOENT(rv)) {
1706
                /* Someone else has already removed it, try again */
1707
                continue;
1708
            }
1709
            else if(rv != APR_SUCCESS) {
1710
                return rv;
1711
            }
1712
1713
            if(finfo.mtime < (apr_time_now() - dobj->updtimeout) ) {
1714
                /* Something stale that's left around */
1715
1716
                rv = apr_file_remove(dobj->cachefile, r->pool);
1717
                if(rv != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rv)) {
1718
                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1719
                                 "disk_cache: open_new_cachefile: Failed to "
1720
                                 "remove old %s", dobj->cachefile);
1721
                    return rv;
1722
                }
1723
                continue;
1724
            }
1725
            else {
1726
                /* Someone else has just created the file, assume
1727
                   they're caching the same thing we are and wait
1728
                   for them to finish */
1729
1730
                dobj->skipstore = TRUE;
1731
                return APR_SUCCESS;
1732
            }
1733
        }
1734
        else if(APR_STATUS_IS_ENOENT(rv)) {
1735
            /* The directory for the file didn't exist */
1736
1737
            rv = mkdir_structure(dobj->cachefile, r->pool);
1738
            if(rv != APR_SUCCESS) {
1739
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1740
                             "disk_cache: open_new_cachefile: Failed to make "
1741
                             "directory for %s", dobj->cachefile);
1742
                return rv;
1743
            }
1744
            continue;
1745
        }
1746
        else if(rv == APR_SUCCESS) {
1747
            return APR_SUCCESS;
1748
        }
1749
        else {
1750
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1751
                         "disk_cache: open_new_cachefile: Failed to open %s",
1752
                         dobj->cachefile);
1753
            return rv;
1754
        }
1755
    }
1756
1757
    /* We should never get here, so */
1758
    return APR_EGENERAL;
1759
}
1760
1761
1762
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, 
1763
                                  cache_info *info)
1764
{
1765
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1766
                                                 &disk_cache_module);
1767
    apr_status_t rv;
1768
    apr_off_t off;
1769
    int flags=0, rewriting;
1770
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1771
    apr_time_t lastmod = APR_DATE_BAD;
1772
    const char *lastmods;
1773
1774
1775
    /* This is flaky... we need to manage the cache_info differently */
1776
    h->cache_obj->info = *info;
1777
1778
    /* Get last-modified timestamp */
1779
    lastmods = apr_table_get(r->err_headers_out, "Last-Modified");
1780
    if (lastmods == NULL) {
1781
        lastmods = apr_table_get(r->headers_out, "Last-Modified");
1782
    }
1783
    if (lastmods != NULL) {
1784
        lastmod = apr_date_parse_http(lastmods);
1785
    }
1786
1787
    if(dobj->fd) {
1788
        rewriting = TRUE;
1789
1790
        /* FIXME: Configurable threshold for expire-check? */
1791
        if(lastmod != APR_DATE_BAD && lastmod == dobj->disk_info.lastmod &&
1792
                dobj->disk_info.expire > r->request_time) 
1793
        {
1794
            /* Don't store header if it hasn't changed. That is simply stupid */
1795
            dobj->skipstore = TRUE;
1796
1797
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1798
                         "disk_cache: store_headers: Headers current for URL "
1799
                         "%s", dobj->name);
1800
        }
1801
        else {
1802
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1803
                         "disk_cache: Rewriting headers for URL %s", 
1804
                         dobj->name);
1805
        }
1806
    }
1807
    else {
1808
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1809
                     "disk_cache: Storing new headers for URL %s", dobj->name);
1810
1811
        rewriting = FALSE;
1812
    }
1813
1814
    if (r->headers_out) {
1815
        const char *tmp;
1816
1817
        tmp = apr_table_get(r->headers_out, "Vary");
1818
1819
        if (tmp) {
1820
            rv = store_vary_header(h, conf, r, info, tmp);
1821
            if(rv != APR_SUCCESS) {
1822
                return rv;
1823
            }
1824
        }
1825
    } 
1826
1827
    /* If we're rewriting, this automatically means that the alias header
1828
       was up to date */
1829
    if(dobj->aliasfile && !rewriting) {
1830
        rv = store_alias_header(dobj, conf, r, info);
1831
        if(rv != APR_SUCCESS) {
1832
            return rv;
1833
        }
1834
1835
        /* We might end up here due to the alias header being expired, which
1836
           causes the request to tumble through the filter chain as if it was
1837
           totally uncached. We might already have cached the file the URL
1838
           refers to though, so try to open the header and check */
1839
1840
        rv = open_header_timeout(h, r, h->cache_obj->key, conf, dobj);
1841
        if(rv == APR_SUCCESS) {
1842
            /* OK, there was something there already. But is it the
1843
               right thing? */
1844
            if(is_file_current(r, dobj, lastmod)) {
1845
                ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1846
                             "disk_cache: Found already cached file for "
1847
                             "URL %s", dobj->name);
1848
                dobj->skipstore = TRUE;
1849
            }
1850
            else {
1851
                /* Nopes. Let's get rid of it */
1852
                apr_file_close(dobj->fd);
1853
                dobj->fd = NULL;
1854
                rv = apr_file_remove(dobj->cachefile, r->pool);
1855
                if(rv != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rv)) {
1856
                    return rv;
1857
                }
1858
            }
1859
        }
1860
    }
1861
1862
    if(dobj->skipstore) {
1863
        return APR_SUCCESS;
1864
    }
1865
1866
    if(rewriting) {
1867
        /* Assume we are just rewriting the header if we have an fd. The
1868
           fd might be readonly though, in that case reopen it for writes.
1869
           Something equivalent to fdopen would have been handy. */
1870
1871
        flags = apr_file_flags_get(dobj->fd);
1872
1873
        if(!(flags & APR_WRITE)) {
1874
            apr_file_close(dobj->fd);
1875
            rv = apr_file_open(&dobj->fd, dobj->cachefile, 
1876
                    APR_WRITE | APR_BINARY | APR_BUFFERED, 0, r->pool);
1877
            if (rv != APR_SUCCESS) {
1878
                return rv;
1879
            }
1880
        }
1881
        else {
1882
            /* We can write here, so let's just move to the right place */
1883
            apr_off_t off=0;
1884
            rv = apr_file_seek(dobj->fd, APR_SET, &off);
1885
            if (rv != APR_SUCCESS) {
1886
                return rv;
1887
            }
1888
        }
1889
    }
1890
    else {
1891
        rv = open_new_cachefile(r, dobj);
1892
        if(rv != APR_SUCCESS) {
1893
            return rv;
1894
        }
1895
    }
1896
1897
    if(dobj->skipstore) {
1898
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1899
                     "disk_cache: Skipping store for URL %s: Someone else "
1900
                     "beat us to it",  dobj->name);
1901
        return APR_SUCCESS;
1902
    }
1903
1904
    rv = store_disk_header(dobj, r, info, lastmod);
1905
    if(rv != APR_SUCCESS) {
1906
        return rv;
1907
    }
1908
1909
    /* FIXME: It's probably better/safer to just set a maximum header size
1910
       and not try to be flexible. There are races if writing too much headers
1911
       since other threads might think it's a body */
1912
1913
    /* Find out how much we wrote */
1914
    off = 0;
1915
    rv = apr_file_seek(dobj->fd, APR_CUR, &off);
1916
    if (rv != APR_SUCCESS) {
1917
        return rv;
1918
    }
1919
1920
    if(rewriting) {
1921
1922
        /* Reopen if read/write-mode differs, flush otherwise */
1923
        if(!(flags & APR_WRITE)) {
1924
            rv = apr_file_close(dobj->fd);
1925
            if (rv != APR_SUCCESS) {
1926
                file_cache_errorcleanup(dobj, r);
1927
                return rv;
1928
            }
1929
            rv = apr_file_open(&dobj->fd, dobj->cachefile, flags, 0, r->pool);
1930
            if (rv != APR_SUCCESS) {
1931
                return rv;
1932
            }
1933
        }
1934
        else {
1935
            rv = apr_file_flush(dobj->fd);
1936
            if (rv != APR_SUCCESS) {
1937
                file_cache_errorcleanup(dobj, r);
1938
                return rv;
1939
            }
1940
        }
1941
1942
        if(off > dobj->bodyoff) {
1943
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1944
                         "disk_cache: Header size %" APR_OFF_T_FMT " > %"
1945
                         APR_OFF_T_FMT " for URL %s, skipping",
1946
                         off, dobj->bodyoff, dobj->name);
1947
            return APR_EGENERAL;
1948
        }
1949
    }
1950
    else {
1951
        /* Storing stuff from scratch */
1952
1953
        if(off * 1.25 > dobj->bodyoff) {
1954
            /* Whoops, we wrote too much headers. Recalculate body offset
1955
               and redo to get breathing room for future header updates */
1956
1957
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
1958
                    "disk_cache: Header size %" APR_OFF_T_FMT " > %"
1959
                    APR_OFF_T_FMT " for URL %s, will rewrite headers again",
1960
                    (apr_off_t) (off * 1.25), dobj->bodyoff, dobj->name);
1961
1962
            dobj->bodyoff = ((off*1.25)/CACHE_DATA_OFFSET)+1;
1963
            dobj->bodyoff *= CACHE_DATA_OFFSET;
1964
1965
            rv = apr_file_close(dobj->fd);
1966
            dobj->fd = NULL;
1967
            if (rv != APR_SUCCESS) {
1968
                file_cache_errorcleanup(dobj, r);
1969
                return rv;
1970
            }
1971
            rv = apr_file_remove(dobj->cachefile, r->pool);
1972
            if (rv != APR_SUCCESS) {
1973
                return rv;
1974
            }
1975
1976
            return store_headers(h, r, info);
1977
        }
1978
1979
        rv = apr_file_flush(dobj->fd);
1980
        if (rv != APR_SUCCESS) {
1981
            file_cache_errorcleanup(dobj, r);
1982
            return rv;
1983
        }
1984
    }
1985
1986
    /* store_body() assumes the fd is correctly positioned */
1987
    off = dobj->bodyoff;
1988
    rv = apr_file_seek(dobj->fd, APR_SET, &off);
1989
    if (rv != APR_SUCCESS) {
1990
        return rv;
1991
    }
1992
1993
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1994
                 "disk_cache: Stored headers for URL %s",  dobj->name);
1995
    return APR_SUCCESS;
1996
}
1997
1998
1999
/* Stuff needed by the background copy thread */
2000
typedef struct copyinfo copyinfo;
2001
struct copyinfo {
2002
    apr_off_t len;
2003
    /* Source info */
2004
    const char *srcfile;
2005
    apr_finfo_t srcinfo;
2006
    apr_off_t srcoff;
2007
    /* Destination info */
2008
    const char *destfile;
2009
    apr_off_t destoff; 
2010
2011
    /* Our private pool */
2012
    apr_pool_t *pool;
2013
2014
#if APR_HAS_THREADS
2015
    /* Background process info */
2016
    apr_thread_t *t;
2017
#endif /* APR_HAS_THREADS */
2018
#if APR_HAS_FORK
2019
    apr_proc_t *proc;
2020
#endif /* APR_HAS_FORK */
2021
2022
    /* For logging */
2023
    const server_rec *s;
2024
};
2025
2026
2027
/* FIXME: Check if source file has changed during copying? */
2028
/* Either provide srcfd, or provide srcfile and srcinfo containing
2029
   APR_FINFO_INODE|APR_FINFO_MTIME to make sure we have opened the right file
2030
   (someone might have just replaced it which messes up things).
2031
 */
2032
/* FIXME: If copy_body bails then the cache bucket should be able to revert
2033
   to delivering from the original fd.
2034
   */
2035
static apr_status_t copy_body(apr_file_t *srcfd, const char *srcfile, 
2036
                              apr_finfo_t *srcinfo, apr_off_t srcoff, 
2037
                              const char *destfile, apr_off_t destoff, 
2038
                              apr_off_t len, apr_pool_t *p)
2039
{
2040
    apr_status_t rc;
2041
    apr_file_t *destfd;
2042
    apr_size_t size;
2043
    apr_finfo_t finfo;
2044
    apr_time_t starttime = apr_time_now();
2045
    char buf[CACHE_BUF_SIZE];
2046
2047
    /* Sanity-check arguments */
2048
    if((srcfd == NULL && srcfile == NULL) || 
2049
            (srcfd != NULL && srcfile != NULL) ||
2050
            (srcfile != NULL && srcinfo == NULL)) 
2051
    {
2052
        return APR_EGENERAL;
2053
    }
2054
2055
    if(srcfile != NULL) {
2056
        rc = apr_file_open(&srcfd, srcfile, APR_READ | APR_BINARY, 0, p);
2057
        if(rc != APR_SUCCESS) {
2058
            return rc;
2059
        }
2060
        rc = apr_file_info_get(&finfo, APR_FINFO_INODE|APR_FINFO_MTIME, srcfd);
2061
        if(rc != APR_SUCCESS) {
2062
            return rc;
2063
        }
2064
        if(srcinfo->inode != finfo.inode || srcinfo->mtime < finfo.mtime) {
2065
            return APR_EGENERAL;
2066
        }
2067
    }
2068
    if(srcoff != 0) {
2069
        rc = apr_file_seek(srcfd, APR_SET, &srcoff);
2070
        if(rc != APR_SUCCESS) {
2071
            return rc;
2072
        }
2073
    }
2074
2075
    rc = apr_file_open(&destfd, destfile, APR_WRITE | APR_BINARY, 0, p);
2076
    if(rc != APR_SUCCESS) {
2077
        return rc;
2078
    }
2079
    if(destoff != 0) {
2080
        rc = apr_file_seek(destfd, APR_SET, &destoff);
2081
        if(rc != APR_SUCCESS) {
2082
            return rc;
2083
        }
2084
    }
2085
2086
    /* Tried doing this with mmap, but sendfile on Linux got confused when
2087
       sending a file while it was being written to from an mmapped area.
2088
       The traditional way seems to be good enough, and less complex.
938
     */
2089
     */
939
    rv = apr_file_remove(dobj->hdrsfile, r->pool);
2090
    while(len > 0) {
2091
        size=MIN(len, CACHE_BUF_SIZE);
2092
2093
        rc = apr_file_read_full (srcfd, buf, size, NULL);
2094
        if(rc != APR_SUCCESS) {
2095
            return rc;
2096
        }
2097
2098
        rc = apr_file_write_full(destfd, buf, size, NULL);
2099
        if(rc != APR_SUCCESS) {
2100
            return rc;
2101
        }
2102
        len -= size;
2103
    }
2104
2105
    /* Check if file has changed during copying. This is not 100% foolproof
2106
       due to NFS attribute caching when on NFS etc. */
2107
    /* FIXME: Can we assume that we're always copying an entire file? In that
2108
              case we can check if the current filesize matches the length
2109
              we think it is */
2110
    rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, srcfd);
2111
    if(rc != APR_SUCCESS) {
2112
        apr_file_close(destfd);
2113
        return rc;
2114
    }
2115
    if(starttime < finfo.mtime) {
2116
        apr_file_close(destfd);
2117
        return APR_EGENERAL;
2118
    }
2119
2120
    if(srcfile != NULL) {
2121
        apr_file_close(srcfd);
2122
    }
2123
    return apr_file_close(destfd);
2124
}
2125
2126
2127
#if APR_HAS_THREADS
2128
static apr_status_t bgcopy_thread_cleanup(void *data) {
2129
    copyinfo *ci = data;
2130
    apr_status_t rc, ret;
2131
    apr_pool_t *p;
2132
2133
    /* FIXME: Debug */
2134
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2135
                 "disk_cache: bgcopy_thread_cleanup: %s -> %s",
2136
                 ci->srcfile, ci->destfile);
2137
2138
    rc = apr_thread_join(&ret, ci->t);
2139
    if(rc != APR_SUCCESS) {
2140
        ap_log_error(APLOG_MARK, APLOG_ERR, rc, ci->s,
2141
                     "disk_cache: bgcopy_thread_cleanup: apr_thread_join "
2142
                     "failed %s -> %s", ci->srcfile, ci->destfile);
2143
        return rc;
2144
    }
2145
    if(ret != APR_SUCCESS) {
2146
        ap_log_error(APLOG_MARK, APLOG_ERR, ret, ci->s,
2147
                     "disk_cache: Background caching body %s -> %s failed",
2148
                     ci->srcfile, ci->destfile);
2149
    }
2150
2151
    /* FIXME: Debug */
2152
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2153
                 "disk_cache: bgcopy_thread_cleanup: SUCCESS %s -> %s",
2154
                 ci->srcfile, ci->destfile);
2155
2156
    /* Destroy our private pool */
2157
    p = ci->pool;
2158
    apr_pool_destroy(p);
2159
2160
    return APR_SUCCESS;
2161
}
2162
2163
2164
static void *bgcopy_thread(apr_thread_t *t, void *data)
2165
{
2166
    copyinfo *ci = data;
2167
    apr_pool_t *p;
2168
    apr_status_t rc;
2169
2170
    p = apr_thread_pool_get(t);
2171
2172
    /* FIXME: Debug */
2173
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2174
                 "disk_cache: bgcopy_thread: start %s -> %s",
2175
                 ci->srcfile, ci->destfile);
2176
2177
    rc = copy_body(NULL, ci->srcfile, &(ci->srcinfo), ci->srcoff, ci->destfile, 
2178
                   ci->destoff, ci->len, p);
2179
2180
    if(rc != APR_SUCCESS) {
2181
        apr_file_remove(ci->destfile, p);
2182
    }
2183
2184
    /* FIXME: Debug */
2185
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2186
                 "disk_cache: bgcopy_thread: done %s -> %s",
2187
                 ci->srcfile, ci->destfile);
2188
2189
    apr_thread_exit(t, rc);
2190
    return NULL;
2191
}
2192
#endif /* APR_HAS_THREADS */
2193
2194
2195
#if APR_HAS_FORK
2196
static apr_status_t bgcopy_child_cleanup(void *data) {
2197
    copyinfo *ci = data;
2198
    int status;
2199
    apr_exit_why_e why;
2200
    apr_pool_t *p;
2201
2202
    apr_proc_wait(ci->proc, &status, &why, APR_WAIT);
2203
    if(why == APR_PROC_EXIT) {
2204
        if(status != APR_SUCCESS) {
2205
            ap_log_error(APLOG_MARK, APLOG_ERR, status, ci->s,
2206
                         "disk_cache: Background caching body %s -> %s failed",
2207
                         ci->srcfile, ci->destfile);
2208
            return APR_SUCCESS;
2209
        }
2210
    }
2211
    else if(status & (APR_PROC_SIGNAL | APR_PROC_SIGNAL_CORE) ) {
2212
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, ci->s,
2213
                     "disk_cache: Background caching body %s -> %s failed, "
2214
                     "caught signal %d", ci->srcfile, ci->destfile, status);
2215
        return APR_SUCCESS;
2216
    }
2217
2218
    /* Destroy our private pool */
2219
    p = ci->pool;
2220
    apr_pool_destroy(p);
2221
2222
    return APR_SUCCESS;
2223
}
2224
#endif /* APR_HAS_FORK */
2225
2226
2227
static apr_status_t do_bgcopy(apr_file_t *srcfd, apr_off_t off, apr_off_t len, 
2228
                              const char *cachefile, apr_off_t bodyoff, 
2229
                              request_rec *r)
2230
{
2231
    copyinfo *ci;
2232
    apr_status_t rv;
2233
    apr_pool_t *newpool;
2234
    const char *srcfile;
2235
    int mpm_query_info;
2236
2237
    /* It seems pool memory gets destroyed before the cleanup functions
2238
       are called when an error occur (a dropped connection, for example),
2239
       so we need a pool of our own
2240
     */
2241
    rv = apr_pool_create(&newpool, NULL);
940
    if (rv != APR_SUCCESS) {
2242
    if (rv != APR_SUCCESS) {
941
        mkdir_structure(conf, dobj->hdrsfile, r->pool);
2243
        return rv;
2244
    }
2245
2246
    ci = apr_palloc(newpool, sizeof(*ci));
2247
    if(ci == NULL) {
2248
        apr_pool_destroy(newpool);
2249
        return APR_ENOMEM;
942
    }
2250
    }
943
2251
944
    rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile, r->pool);
2252
    rv = apr_file_name_get(&srcfile, srcfd);
2253
    if(rv != APR_SUCCESS) {
2254
        return rv;
2255
    }
2256
    rv = apr_file_info_get(&(ci->srcinfo), APR_FINFO_INODE|APR_FINFO_MTIME,
2257
                           srcfd);
2258
    if(rv != APR_SUCCESS) {
2259
        return rv;
2260
    }
2261
2262
    ci->pool = newpool;
2263
    ci->srcoff = off;
2264
    ci->len = len;
2265
    ci->srcfile = apr_pstrdup(newpool, srcfile);
2266
    ci->destfile = apr_pstrdup(newpool, cachefile);
2267
    ci->destoff = bodyoff;
2268
    ci->s = r->server;
2269
2270
#if APR_HAS_THREADS
2271
    if(ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_query_info) == APR_SUCCESS) {
2272
        apr_threadattr_t *ta;
2273
        apr_thread_t *t;
2274
        rv = apr_threadattr_create(&ta, newpool);
2275
        if(rv != APR_SUCCESS) {
2276
            apr_pool_destroy(newpool);
2277
            return rv;
2278
        }
2279
2280
        apr_threadattr_detach_set(ta, FALSE);
2281
2282
        /* FIXME: This makes module unloadable on AIX */
2283
#if 0
2284
#ifdef AP_MPM_WANT_SET_STACKSIZE
2285
        if (ap_thread_stacksize != 0) {
2286
            apr_threadattr_stacksize_set(ta, ap_thread_stacksize);
2287
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2288
                    "disk_cache: BG thread stacksize set to %"
2289
                    APR_SIZE_T_FMT, ap_thread_stacksize);
2290
        }
2291
#endif /* AP_MPM_WANT_SET_STACKSIZE */
2292
#endif /* 0 */
2293
2294
        rv = apr_thread_create (&t, ta, bgcopy_thread, ci, newpool);
2295
        if (rv != APR_SUCCESS) {
2296
            apr_pool_destroy(newpool);
2297
            return rv;
2298
        }
2299
        ci->t = t;
2300
2301
        apr_pool_cleanup_register(r->pool, ci, bgcopy_thread_cleanup,
2302
                apr_pool_cleanup_null);
2303
    }
2304
    else
2305
#endif /* APR_HAS_THREADS */
2306
#if APR_HAS_FORK
2307
    if(ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_query_info) == APR_SUCCESS) {
2308
        ci->proc = apr_palloc(newpool, sizeof(apr_proc_t));
2309
        if(ci->proc == NULL) {
2310
            apr_pool_destroy(newpool);
2311
            return APR_ENOMEM;
2312
        }
2313
        rv = apr_proc_fork(ci->proc, newpool);
2314
        if(rv == APR_INCHILD) {
2315
            /* Child */
2316
            rv = copy_body(NULL, ci->srcfile, &(ci->srcinfo), ci->srcoff, 
2317
                           ci->destfile, ci->destoff, ci->len, ci->pool);
2318
            if(rv != APR_SUCCESS) {
2319
                apr_file_remove(ci->destfile, ci->pool);
2320
            }
2321
            exit(rv);
2322
        }
2323
        else if(rv == APR_INPARENT) {
2324
            apr_pool_cleanup_register(r->pool, ci, bgcopy_child_cleanup,
2325
                    apr_pool_cleanup_null);
2326
        }
2327
        else {
2328
            return rv;
2329
        }
2330
    }
2331
    else 
2332
#endif /* APR_HAS_FORK */
2333
    if(1)
2334
    {
2335
        rv = copy_body(srcfd, NULL, NULL, ci->srcoff, ci->destfile, ci->destoff,
2336
                       ci->len, newpool);
2337
        apr_pool_destroy(newpool);
2338
    }
2339
2340
    return rv;
2341
}
2342
2343
static apr_status_t replace_brigade_with_cache(cache_handle_t *h,
2344
                                               request_rec *r,
2345
                                               apr_bucket_brigade *bb)
2346
{
2347
    apr_status_t rv;
2348
    int flags;
2349
    apr_bucket *e;
2350
    core_dir_config *pdcfg = ap_get_module_config(r->per_dir_config,
2351
            &core_module);
2352
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
2353
2354
    flags = APR_READ|APR_BINARY;
2355
#if APR_HAS_SENDFILE
2356
    flags |= ((pdcfg->enable_sendfile == ENABLE_SENDFILE_OFF)
2357
            ? 0 : APR_SENDFILE_ENABLED);
2358
#endif
2359
2360
    rv = apr_file_open(&dobj->fd, dobj->cachefile, flags, 0, r->pool);
945
    if (rv != APR_SUCCESS) {
2361
    if (rv != APR_SUCCESS) {
946
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2362
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
947
                     "disk_cache: rename tempfile to hdrsfile failed: %s -> %s",
2363
                     "disk_cache: Error opening cachefile %s for URL %s",
948
                     dobj->tempfile, dobj->hdrsfile);
2364
                     dobj->cachefile, dobj->name);
949
        return rv;
2365
        return rv;
950
    }
2366
    }
951
2367
952
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
2368
    /* First, empty the brigade */
2369
    e  = APR_BRIGADE_FIRST(bb);
2370
    while (e != APR_BRIGADE_SENTINEL(bb)) {
2371
        apr_bucket *d;
2372
        d = e;
2373
        e = APR_BUCKET_NEXT(e);
2374
        apr_bucket_delete(d);
2375
    }
953
2376
2377
    /* Then, populate it with our cached instance */
2378
    rv = recall_body(h, r->pool, bb);
2379
    if (rv != APR_SUCCESS) {
2380
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2381
                     "disk_cache: Error serving URL %s from cache", dobj->name);
2382
        return rv;
2383
    }
954
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2384
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
955
                 "disk_cache: Stored headers for URL %s",  dobj->name);
2385
                 "disk_cache: Serving cached body for URL %s", dobj->name);
2386
956
    return APR_SUCCESS;
2387
    return APR_SUCCESS;
957
}
2388
}
958
2389
Lines 961-1047 static apr_status_t store_body(cache_han Link Here
961
{
2392
{
962
    apr_bucket *e;
2393
    apr_bucket *e;
963
    apr_status_t rv;
2394
    apr_status_t rv;
2395
    int copy_file = FALSE;
964
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
2396
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
965
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
2397
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
966
                                                 &disk_cache_module);
2398
                                                 &disk_cache_module);
967
2399
968
    /* We write to a temp file and then atomically rename the file over
2400
    if(r->no_cache) {
969
     * in file_cache_el_final().
2401
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
970
     */
2402
                     "disk_cache: store_body called for URL %s even though"
971
    if (!dobj->tfd) {
2403
                     "no_cache is set", dobj->name);
972
        rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
2404
        file_cache_errorcleanup(dobj, r);
973
                             APR_CREATE | APR_WRITE | APR_BINARY |
2405
        return APR_EGENERAL;
974
                             APR_BUFFERED | APR_EXCL, r->pool);
2406
    }
975
        if (rv != APR_SUCCESS) {
2407
976
            return rv;
2408
    if(dobj->bodysize < 0) {
2409
        dobj->bodysize = 0;
2410
    }
2411
2412
    dobj->store_body_called++;
2413
2414
    /* Check if we should skip the whole storing-business */
2415
    if(dobj->skipstore) {
2416
        if(dobj->store_body_called == 1 &&
2417
                dobj->bodysize_in >= 0 &&
2418
                APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
2419
        {
2420
            /* Yay, we can replace the body with the cached instance */
2421
            return replace_brigade_with_cache(h, r, bb);
977
        }
2422
        }
978
        dobj->file_size = 0;
2423
        return APR_SUCCESS;
979
    }
2424
    }
980
2425
981
    for (e = APR_BRIGADE_FIRST(bb);
2426
    /* Check if this is a complete single sequential file, eligable for
982
         e != APR_BRIGADE_SENTINEL(bb);
2427
     * file copy.
983
         e = APR_BUCKET_NEXT(e))
2428
     */
2429
    /* FIXME: Make the min size to do file copy run-time config? */
2430
    if(dobj->store_body_called == 1 &&
2431
            dobj->bodysize_in > APR_BUCKET_BUFF_SIZE &&
2432
            APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
984
    {
2433
    {
985
        const char *str;
2434
        apr_off_t begin = -1;
986
        apr_size_t length, written;
2435
        apr_off_t pos = -1;
987
        rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
2436
        apr_file_t *fd = NULL;
988
        if (rv != APR_SUCCESS) {
2437
        apr_bucket_file *a;
989
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2438
990
                         "cache_disk: Error when reading bucket for URL %s",
2439
        copy_file = TRUE;
991
                         h->cache_obj->key);
2440
992
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2441
        for (e = APR_BRIGADE_FIRST(bb);
993
            file_cache_errorcleanup(dobj, r);
2442
                e != APR_BRIGADE_SENTINEL(bb);
994
            return rv;
2443
                e = APR_BUCKET_NEXT(e))
2444
        {
2445
            if(APR_BUCKET_IS_EOS(e)) {
2446
                break;
2447
            }
2448
            if(!APR_BUCKET_IS_FILE(e)) {
2449
                copy_file = FALSE;
2450
                break;
2451
            }
2452
2453
            a = e->data;
2454
2455
            if(begin < 0) {
2456
                begin = pos = e->start;
2457
                fd = a->fd;
2458
            }
2459
2460
            if(fd != a->fd || pos != e->start) {
2461
                copy_file = FALSE;
2462
                break;
2463
            }
2464
2465
            pos += e->length;
995
        }
2466
        }
996
        rv = apr_file_write_full(dobj->tfd, str, length, &written);
2467
    }
997
        if (rv != APR_SUCCESS) {
2468
998
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2469
    if(copy_file) {
999
                         "cache_disk: Error when writing cache file for URL %s",
2470
        apr_bucket_file *a;
1000
                         h->cache_obj->key);
2471
1001
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2472
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1002
            file_cache_errorcleanup(dobj, r);
2473
                     "disk_cache: Copying body for URL %s, len %"
1003
            return rv;
2474
                     APR_OFF_T_FMT, dobj->name, dobj->bodysize_in);
2475
2476
        e = APR_BRIGADE_FIRST(bb);
2477
        a = e->data;
2478
2479
        if(dobj->bodysize_in > conf->minbgsize) {
2480
            rv = do_bgcopy(a->fd, e->start, dobj->bodysize_in, dobj->cachefile,
2481
                           dobj->bodyoff, r);
2482
            if(rv != APR_SUCCESS) {
2483
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2484
                             "disk_cache: Initiating background copy failed, "
2485
                             "URL %s", dobj->name);
2486
                file_cache_errorcleanup(dobj, r);
2487
                return rv;
2488
            }
1004
        }
2489
        }
1005
        dobj->file_size += written;
2490
        else {
1006
        if (dobj->file_size > conf->maxfs) {
2491
            rv = copy_body(a->fd, NULL, NULL, e->start, dobj->cachefile, 
1007
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2492
                           dobj->bodyoff, dobj->bodysize_in, r->pool);
1008
                         "cache_disk: URL %s failed the size check "
2493
            if(rv != APR_SUCCESS) {
1009
                         "(%" APR_OFF_T_FMT ">%" APR_SIZE_T_FMT ")",
2494
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1010
                         h->cache_obj->key, dobj->file_size, conf->maxfs);
2495
                             "disk_cache: Copying body failed, "
1011
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2496
                             "URL %s", dobj->name);
1012
            file_cache_errorcleanup(dobj, r);
2497
                file_cache_errorcleanup(dobj, r);
1013
            return APR_EGENERAL;
2498
                return rv;
2499
            }
1014
        }
2500
        }
2501
1015
    }
2502
    }
2503
    else {
2504
        if(dobj->store_body_called == 1) {
2505
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
2506
                         "disk_cache: Caching body for URL %s, len %"
2507
                         APR_OFF_T_FMT, dobj->name, dobj->bodysize_in);
2508
        }
1016
2509
1017
    /* Was this the final bucket? If yes, close the temp file and perform
2510
        for (e = APR_BRIGADE_FIRST(bb);
1018
     * sanity checks.
2511
                e != APR_BRIGADE_SENTINEL(bb);
1019
     */
2512
                e = APR_BUCKET_NEXT(e))
1020
    if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
2513
        {   
1021
        if (r->connection->aborted || r->no_cache) {
2514
            const char *str;
2515
            apr_size_t length, written;
2516
2517
            /* Ignore the non-data-buckets */
2518
            if(APR_BUCKET_IS_METADATA(e)) {
2519
                continue;
2520
            }
2521
2522
            rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
2523
            if (rv != APR_SUCCESS) {
2524
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2525
                             "disk_cache: Error when reading bucket for URL %s",
2526
                             dobj->name);
2527
                file_cache_errorcleanup(dobj, r);
2528
                return rv;
2529
            }
2530
            rv = apr_file_write_full(dobj->fd, str, length, &written);
2531
            if (rv != APR_SUCCESS) {
2532
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2533
                             "disk_cache: Error when writing cache file for "
2534
                             "URL %s", dobj->name);
2535
                file_cache_errorcleanup(dobj, r);
2536
                return rv;
2537
            }
2538
            dobj->bodysize += written;
2539
            if (dobj->bodysize > conf->maxfs) {
2540
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2541
                             "disk_cache: URL %s failed the size check "
2542
                             "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
2543
                             dobj->name, dobj->bodysize, conf->maxfs);
2544
                file_cache_errorcleanup(dobj, r);
2545
                return APR_EGENERAL;
2546
            }
2547
        }
2548
    }
2549
2550
2551
    /* Drop out here if this wasn't the end */
2552
    if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
2553
        return APR_SUCCESS;
2554
    }
2555
2556
    if(!copy_file) {
2557
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2558
                     "disk_cache: Done caching URL %s, len %" APR_OFF_T_FMT,
2559
                     dobj->name, dobj->bodysize);
2560
2561
        /* FIXME: Do we really need to check r->no_cache here since we checked
2562
           it in the beginning? */
2563
        /* Assume that if we've got an initial size then bucket brigade
2564
           was complete and there's no danger in keeping it even if the
2565
           connection was aborted */
2566
        if (r->no_cache || (r->connection->aborted && dobj->bodysize_in < 0)) {
1022
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
2567
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1023
                         "disk_cache: Discarding body for URL %s "
2568
                         "disk_cache: Discarding body for URL %s "
1024
                         "because connection has been aborted.",
2569
                         "because connection has been aborted.",
1025
                         h->cache_obj->key);
2570
                         dobj->name);
1026
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2571
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
1027
            file_cache_errorcleanup(dobj, r);
2572
            file_cache_errorcleanup(dobj, r);
1028
            return APR_EGENERAL;
2573
            return APR_EGENERAL;
1029
        }
2574
        }
1030
        if (dobj->file_size < conf->minfs) {
2575
2576
        if (dobj->bodysize < conf->minfs) {
1031
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2577
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1032
                         "cache_disk: URL %s failed the size check "
2578
                         "disk_cache: URL %s failed the size check "
1033
                         "(%" APR_OFF_T_FMT "<%" APR_SIZE_T_FMT ")",
2579
                         "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
1034
                         h->cache_obj->key, dobj->file_size, conf->minfs);
2580
                         dobj->name, dobj->bodysize, conf->minfs);
1035
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2581
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
1036
            file_cache_errorcleanup(dobj, r);
2582
            file_cache_errorcleanup(dobj, r);
1037
            return APR_EGENERAL;
2583
            return APR_EGENERAL;
1038
        }
2584
        }
1039
2585
1040
        /* All checks were fine. Move tempfile to final destination */
2586
        if(dobj->bodysize_in < 0) {
1041
        /* Link to the perm file, and close the descriptor */
2587
            /* Update header information now that we know the size */
1042
        file_cache_el_final(dobj, r);
2588
            dobj->bodysize_in = dobj->bodysize;
1043
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2589
            rv = store_headers(h, r, &(h->cache_obj->info));
1044
                     "disk_cache: Body for URL %s cached.",  dobj->name);
2590
            if(rv != APR_SUCCESS) {
2591
                file_cache_errorcleanup(dobj, r);
2592
                return rv;
2593
            }
2594
        }
2595
        else if(dobj->bodysize_in != dobj->bodysize) {
2596
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2597
                         "disk_cache: URL %s - body size mismatch: suggested %"
2598
                         APR_OFF_T_FMT "  bodysize %" APR_OFF_T_FMT ")",
2599
                         dobj->name, dobj->bodysize_in, dobj->bodysize);
2600
            file_cache_errorcleanup(dobj, r);
2601
            return APR_EGENERAL;
2602
        }
2603
    }
2604
2605
    rv = apr_file_close(dobj->fd);
2606
    if(rv != APR_SUCCESS) {
2607
        file_cache_errorcleanup(dobj, r);
2608
        return rv;
2609
    }
2610
2611
    /* Redirect to cachefile if we copied a plain file */
2612
    if(copy_file) {
2613
        rv = replace_brigade_with_cache(h, r, bb);
2614
        if(rv != APR_SUCCESS) {
2615
            return rv;
2616
        }
1045
    }
2617
    }
1046
2618
1047
    return APR_SUCCESS;
2619
    return APR_SUCCESS;
Lines 1056-1061 static void *create_config(apr_pool_t *p Link Here
1056
    conf->dirlength = DEFAULT_DIRLENGTH;
2628
    conf->dirlength = DEFAULT_DIRLENGTH;
1057
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
2629
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
1058
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
2630
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
2631
    conf->minbgsize = DEFAULT_MIN_BACKGROUND_SIZE;
2632
    conf->updtimeout = DEFAULT_UPDATE_TIMEOUT;
1059
2633
1060
    conf->cache_root = NULL;
2634
    conf->cache_root = NULL;
1061
    conf->cache_root_len = 0;
2635
    conf->cache_root_len = 0;
Lines 1117-1123 static const char Link Here
1117
{
2691
{
1118
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2692
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
1119
                                                 &disk_cache_module);
2693
                                                 &disk_cache_module);
1120
    conf->minfs = atoi(arg);
2694
2695
    if (apr_strtoff(&conf->minfs, arg, NULL, 0) != APR_SUCCESS ||
2696
            conf->minfs < 0) 
2697
    {
2698
        return "CacheMinFileSize argument must be a non-negative integer representing the min size of a file to cache in bytes.";
2699
    }
1121
    return NULL;
2700
    return NULL;
1122
}
2701
}
1123
static const char
2702
static const char
Lines 1125-1134 static const char Link Here
1125
{
2704
{
1126
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2705
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
1127
                                                 &disk_cache_module);
2706
                                                 &disk_cache_module);
1128
    conf->maxfs = atoi(arg);
2707
2708
    if (apr_strtoff(&conf->maxfs, arg, NULL, 0) != APR_SUCCESS ||
2709
            conf->maxfs < 0) 
2710
    {
2711
        return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
2712
    }
2713
1129
    return NULL;
2714
    return NULL;
1130
}
2715
}
1131
2716
2717
2718
static const char
2719
*set_cache_minbgsize(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2720
{
2721
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2722
                                                 &disk_cache_module);
2723
2724
    if (apr_strtoff(&conf->minbgsize, arg, NULL, 0) != APR_SUCCESS ||
2725
            conf->minbgsize < 0) 
2726
    {
2727
        return "CacheMinBGSize argument must be a non-negative integer representing the min size in bytes for a file to be eligable for background caching";
2728
    }
2729
2730
    return NULL;
2731
}
2732
2733
2734
static const char
2735
*set_cache_updtimeout(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2736
{
2737
    apr_int64_t val;
2738
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2739
                                                 &disk_cache_module);
2740
2741
    if (apr_strtoff(&val, arg, NULL, 0) != APR_SUCCESS || val < 0) 
2742
    {
2743
        return "CacheUpdateTimeout argument must be a non-negative integer representing the timeout in milliseconds for cache update operations";
2744
    }
2745
2746
    conf->updtimeout = val * 1000;
2747
2748
    return NULL;
2749
}
2750
2751
2752
static const char
2753
*set_cache_removedirs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2754
{
2755
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2756
                                                 &disk_cache_module);
2757
2758
    if (strcasecmp(arg, "on") == 0 || strcasecmp(arg, "true")) {
2759
        conf->removedirs = TRUE;
2760
    }
2761
    else if (strcasecmp(arg, "off") == 0 || strcasecmp(arg, "false")) {
2762
        conf->removedirs = FALSE;
2763
    }
2764
    else {
2765
        return "CacheRemoveDirectories argument must be either on, true, off or false";
2766
    }
2767
2768
    return NULL;
2769
}
2770
2771
1132
static const command_rec disk_cache_cmds[] =
2772
static const command_rec disk_cache_cmds[] =
1133
{
2773
{
1134
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
2774
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
Lines 1141-1146 static const command_rec disk_cache_cmds Link Here
1141
                  "The minimum file size to cache a document"),
2781
                  "The minimum file size to cache a document"),
1142
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
2782
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
1143
                  "The maximum file size to cache a document"),
2783
                  "The maximum file size to cache a document"),
2784
    AP_INIT_TAKE1("CacheMinBGSize", set_cache_minbgsize, NULL, RSRC_CONF,
2785
                  "The minimum file size for background caching"),
2786
    AP_INIT_TAKE1("CacheUpdateTimeout", set_cache_updtimeout, NULL, RSRC_CONF,
2787
                  "Timeout in ms for cache updates"),
2788
    AP_INIT_TAKE1("CacheRemoveDirectories", set_cache_removedirs, NULL, RSRC_CONF,
2789
                  "Should we try to remove directories when we remove expired cache files."),
1144
    {NULL}
2790
    {NULL}
1145
};
2791
};
1146
2792
(-)dist/modules/cache/mod_disk_cache.h (-23 / +86 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 ALIAS_FORMAT_VERSION 6
28
#define DISK_FORMAT_VERSION 7
26
29
27
#define CACHE_HEADER_SUFFIX ".header"
28
#define CACHE_DATA_SUFFIX   ".data"
29
#define CACHE_VDIR_SUFFIX   ".vary"
30
#define CACHE_VDIR_SUFFIX   ".vary"
30
31
32
#define CACHE_BUF_SIZE 65536
33
#define CACHE_DATA_OFFSET 16384
34
35
/* How long to sleep before retrying while looping */
36
#define CACHE_LOOP_SLEEP 200000
37
31
#define AP_TEMPFILE_PREFIX "/"
38
#define AP_TEMPFILE_PREFIX "/"
32
#define AP_TEMPFILE_BASE   "aptmp"
39
#define AP_TEMPFILE_BASE   "aptmp"
33
#define AP_TEMPFILE_SUFFIX "XXXXXX"
40
#define AP_TEMPFILE_SUFFIX "XXXXXX"
Lines 35-56 Link Here
35
#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
42
#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
36
#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
43
#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
37
44
45
typedef apr_uint32_t disk_cache_format_t;
46
38
typedef struct {
47
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.  */
48
    /* The HTTP status code returned for this response.  */
42
    int status;
49
    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. */
50
    /* The number of times we've cached this entity. */
46
    apr_size_t entity_version;
51
    apr_uint32_t entity_version;
47
    /* Miscellaneous time values. */
52
    /* Miscellaneous time values. */
48
    apr_time_t date;
53
    apr_time_t date;
49
    apr_time_t expire;
54
    apr_time_t expire;
50
    apr_time_t request_time;
55
    apr_time_t request_time;
51
    apr_time_t response_time;
56
    apr_time_t response_time;
57
    apr_time_t lastmod; /* Last-Modified (if present) */
58
59
    /* The body size forced to 64bit to not break when people go from non-LFS
60
     * to LFS builds */
61
    apr_int64_t bodysize;
62
    /* Where the data is located in the file */
63
    apr_int64_t bodyoff;
64
65
    /* The size of the entity name that follows. */
66
    apr_uint32_t name_len;
67
    /* The size of the filename that follows, to fill in r->filename */
68
    apr_uint32_t filename_len;
69
70
    /* On disk:
71
       * name_len long string of entity name.
72
       * filename_len long string of filename
73
     */
52
} disk_cache_info_t;
74
} disk_cache_info_t;
53
75
76
typedef struct {
77
    apr_uint32_t name_len;
78
    apr_uint32_t filename_len;
79
    apr_time_t expire;
80
81
    /* On disk:
82
       * name_len long string of entity name.
83
       * filename_len long string of filename the URL refers to.
84
     */
85
} disk_cache_alias_t;
86
87
88
/* Don't expose module-related stuff unless needed */
89
#ifdef AP_FILTER_H
54
/*
90
/*
55
 * disk_cache_object_t
91
 * disk_cache_object_t
56
 * Pointed to by cache_object_t::vobj
92
 * Pointed to by cache_object_t::vobj
Lines 58-75 typedef struct { Link Here
58
typedef struct disk_cache_object {
94
typedef struct disk_cache_object {
59
    const char *root;        /* the location of the cache directory */
95
    const char *root;        /* the location of the cache directory */
60
    apr_size_t root_len;
96
    apr_size_t root_len;
61
    char *tempfile;    /* temp file tohold the content */
97
62
    const char *prefix;
98
    /* Temporary file */
63
    const char *datafile;    /* name of file where the data will go */
99
    apr_file_t *tfd;
64
    const char *hdrsfile;    /* name of file where the hdrs will go */
100
    char *tempfile;
65
    const char *hashfile;    /* Computed hash key for this URI */
101
66
    const char *name;   /* Requested URI without vary bits - suitable for mortals. */
102
    /* Cache file */
67
    const char *key;    /* On-disk prefix; URI with Vary bits (if present) */
103
    apr_file_t *fd;
68
    apr_file_t *fd;          /* data file */
104
    const char *cachefile;
69
    apr_file_t *hfd;         /* headers file */
105
70
    apr_file_t *tfd;         /* temporary file for data */
106
    const char *name;           /* Requested URI without vary bits - 
71
    apr_off_t file_size;     /*  File size of the cached data file  */
107
                                   suitable for mortals. */
72
    disk_cache_info_t disk_info; /* Header information. */
108
    const char *prefix;         /* Prefix to deal with Vary headers */
109
    char *filename;       /* Filename of requested URL (if present) */
110
    const char *aliasfile;      /* File that contains the URL->file mapping */
111
112
    apr_off_t bodysize_in;      /* Size of body as reported upstreams */
113
    apr_off_t bodysize;         /* File size of the cached body */
114
    apr_off_t bodyoff;          /* At which offset to store the body */
115
116
    int store_body_called;      /* Number of times store_body() has executed */
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
    apr_interval_time_t updtimeout; /* Cache update timeout */
123
124
    disk_cache_info_t disk_info; /* Disk header information. */
73
} disk_cache_object_t;
125
} disk_cache_object_t;
74
126
75
127
Lines 82-95 typedef struct disk_cache_object { Link Here
82
#define DEFAULT_DIRLENGTH 2
134
#define DEFAULT_DIRLENGTH 2
83
#define DEFAULT_MIN_FILE_SIZE 1
135
#define DEFAULT_MIN_FILE_SIZE 1
84
#define DEFAULT_MAX_FILE_SIZE 1000000
136
#define DEFAULT_MAX_FILE_SIZE 1000000
137
/* Background caching disabled by default */
138
#define DEFAULT_MIN_BACKGROUND_SIZE DEFAULT_MAX_FILE_SIZE
139
#define DEFAULT_UPDATE_TIMEOUT apr_time_from_sec(10)
85
140
86
typedef struct {
141
typedef struct {
87
    const char* cache_root;
142
    const char* cache_root;
88
    apr_size_t cache_root_len;
143
    apr_size_t cache_root_len;
89
    int dirlevels;               /* Number of levels of subdirectories */
144
    int dirlevels;               /* Number of levels of subdirectories */
90
    int dirlength;               /* Length of subdirectory names */
145
    int dirlength;               /* Length of subdirectory names */
91
    apr_size_t minfs;            /* minumum file size for cached files */
146
    apr_off_t minfs;             /* minumum file size for cached files */
92
    apr_size_t maxfs;            /* maximum file size for cached files */
147
    apr_off_t maxfs;             /* maximum file size for cached files */
148
    apr_off_t minbgsize;         /* minimum file size to do bg caching */
149
    apr_interval_time_t updtimeout;   /* Cache update timeout */
150
    int removedirs;              /* Should we try to remove directories? */
93
} disk_cache_conf;
151
} disk_cache_conf;
94
152
153
#define CACHE_ENODATA (APR_OS_START_USERERR+1)
154
#define CACHE_EDECLINED (APR_OS_START_USERERR+2)
155
156
#endif /* AP_FILTER_H */
157
95
#endif /*MOD_DISK_CACHE_H*/
158
#endif /*MOD_DISK_CACHE_H*/
(-)dist/support/htcacheclean.c (-121 / +47 lines)
Lines 70-79 Link Here
70
typedef struct _direntry {
70
typedef struct _direntry {
71
    APR_RING_ENTRY(_direntry) link;
71
    APR_RING_ENTRY(_direntry) link;
72
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
72
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
73
    apr_time_t htime; /* headers file modification time */
73
    apr_time_t htime; /* file modification time */
74
    apr_time_t dtime; /* body file modification time */
74
    apr_off_t hsize;  /* file size */
75
    apr_off_t hsize;  /* headers file size */
76
    apr_off_t dsize;  /* body or temporary file size */
77
    char *basename;   /* file/fileset base name */
75
    char *basename;   /* file/fileset base name */
78
} DIRENTRY;
76
} DIRENTRY;
79
77
Lines 81-91 typedef struct _entry { Link Here
81
    APR_RING_ENTRY(_entry) link;
79
    APR_RING_ENTRY(_entry) link;
82
    apr_time_t expire;        /* cache entry exiration time */
80
    apr_time_t expire;        /* cache entry exiration time */
83
    apr_time_t response_time; /* cache entry time of last response to client */
81
    apr_time_t response_time; /* cache entry time of last response to client */
84
    apr_time_t htime;         /* headers file modification time */
82
    apr_time_t htime;         /* file modification time */
85
    apr_time_t dtime;         /* body file modification time */
83
    apr_off_t hsize;          /* file size */
86
    apr_off_t hsize;          /* headers file size */
87
    apr_off_t dsize;          /* body or temporary file size */
88
    char *basename;           /* fileset base name */
84
    char *basename;           /* fileset base name */
85
    char *name;               /* entity name */
89
} ENTRY;
86
} ENTRY;
90
87
91
88
Lines 255-264 static void delete_entry(char *path, cha Link Here
255
    /* temp pool, otherwise lots of memory could be allocated */
252
    /* temp pool, otherwise lots of memory could be allocated */
256
    apr_pool_create(&p, pool);
253
    apr_pool_create(&p, pool);
257
254
258
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
255
    nextpath = apr_pstrcat(p, path, "/", basename, NULL);
259
    apr_file_remove(nextpath, p);
260
261
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
262
    apr_file_remove(nextpath, p);
256
    apr_file_remove(nextpath, p);
263
257
264
    apr_pool_destroy(p);
258
    apr_pool_destroy(p);
Lines 286-292 static int process_dir(char *path, apr_p Link Here
286
    apr_finfo_t info;
280
    apr_finfo_t info;
287
    apr_size_t len;
281
    apr_size_t len;
288
    apr_time_t current, deviation;
282
    apr_time_t current, deviation;
289
    char *nextpath, *base, *ext, *orig_basename;
283
    char *nextpath, *base, *orig_basename;
290
    APR_RING_ENTRY(_direntry) anchor;
284
    APR_RING_ENTRY(_direntry) anchor;
291
    DIRENTRY *d, *t, *n;
285
    DIRENTRY *d, *t, *n;
292
    ENTRY *e;
286
    ENTRY *e;
Lines 329-340 static int process_dir(char *path, apr_p 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 static int process_dir(char *path, apr_p Link Here
386
            continue;
379
            continue;
387
        }
380
        }
388
381
389
        if (!ext) {
382
        if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
390
            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;
393
                d->type = TEMP;
394
                d->dsize = info.size;
395
                apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
396
            }
397
            continue;
398
        }
399
400
        if (!strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
401
            *ext = '\0';
402
            d->basename += skip;
384
            d->basename += skip;
403
            /* if a user manually creates a '.header' file */
385
            d->type = TEMP;
404
            if (d->basename[0] == '\0') {
405
                continue;
406
            }
407
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
408
            if (t) {
409
                d = t;
410
            }
411
            d->type |= HEADER;
412
            d->htime = info.mtime;
413
            d->hsize = info.size;
386
            d->hsize = info.size;
414
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
387
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
415
            continue;
388
            continue;
416
        }
389
        }
417
390
418
        if (!strcasecmp(ext, CACHE_DATA_SUFFIX)) {
391
        /* Assume that everything else are cachefiles */
419
            *ext = '\0';
392
        d->basename += skip;
420
            d->basename += skip;
393
        t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
421
            /* if a user manually creates a '.data' file */
394
        if (t) {
422
            if (d->basename[0] == '\0') {
395
            d = t;
423
                continue;
396
        }
424
            }
397
        d->type |= HEADERDATA;
425
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
398
        d->htime = info.mtime;
426
            if (t) {
399
        d->hsize = info.size;
427
                d = t;
400
        apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
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 static int process_dir(char *path, apr_p 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-481 static int process_dir(char *path, apr_p 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_palloc(pool,
439
                            e->basename = apr_palloc(pool,
477
                                                     strlen(d->basename) + 1);
440
                                                     strlen(d->basename) + 1);
478
                            strcpy(e->basename, d->basename);
441
                            strcpy(e->basename, d->basename);
442
                            e->name = apr_palloc(pool, disk_info.name_len+1);
443
                            if(apr_file_read_full(fd, e->name,
444
                                                  disk_info.name_len, NULL)
445
                                    == APR_SUCCESS)
446
                            {
447
                                e->name[disk_info.name_len] = '\0';
448
                            }
449
                            else {
450
                                e->name = "UNKNOWN";
451
                            }
452
                            apr_file_close(fd);
479
                            break;
453
                            break;
480
                        }
454
                        }
481
                        else {
455
                        else {
Lines 484-494 static int process_dir(char *path, apr_p Link Here
484
                    }
458
                    }
485
                    else if (format == VARY_FORMAT_VERSION) {
459
                    else if (format == VARY_FORMAT_VERSION) {
486
                        /* This must be a URL that added Vary headers later,
460
                        /* This must be a URL that added Vary headers later,
487
                         * so kill the orphaned .data file
461
                         * so kill the orphaned cachefile
488
                         */
462
                         */
489
                        apr_file_close(fd);
463
                        apr_file_close(fd);
490
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
464
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
491
                                                    CACHE_DATA_SUFFIX, NULL),
465
                                                    NULL),
492
                                        p);
466
                                        p);
493
                    }
467
                    }
494
                }
468
                }
Lines 509-564 static int process_dir(char *path, apr_p Link Here
509
                || d->htime > current + deviation) {
483
                || d->htime > current + deviation) {
510
                delete_entry(path, d->basename, p);
484
                delete_entry(path, d->basename, p);
511
                unsolicited += d->hsize;
485
                unsolicited += d->hsize;
512
                unsolicited += d->dsize;
513
            }
514
            break;
515
516
        /* single data and header files may be deleted either in realclean
517
         * mode or if their modification timestamp is not within a
518
         * specified positive or negative offset to the current time.
519
         * this handling is necessary due to possible race conditions
520
         * between apache and this process
521
         */
522
        case HEADER:
523
            current = apr_time_now();
524
            nextpath = apr_pstrcat(p, path, "/", d->basename,
525
                                   CACHE_HEADER_SUFFIX, NULL);
526
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
527
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
528
                len = sizeof(format);
529
                if (apr_file_read_full(fd, &format, len,
530
                                       &len) == APR_SUCCESS) {
531
                    if (format == VARY_FORMAT_VERSION) {
532
                        apr_time_t expires;
533
534
                        len = sizeof(expires);
535
536
                        apr_file_read_full(fd, &expires, len, &len);
537
538
                        apr_file_close(fd);
539
540
                        if (expires < current) {
541
                            delete_entry(path, d->basename, p);
542
                        }
543
                        break;
544
                    }
545
                }
546
                apr_file_close(fd);
547
            }
548
549
            if (realclean || d->htime < current - deviation
550
                || d->htime > current + deviation) {
551
                delete_entry(path, d->basename, p);
552
                unsolicited += d->hsize;
553
            }
554
            break;
555
556
        case DATA:
557
            current = apr_time_now();
558
            if (realclean || d->dtime < current - deviation
559
                || d->dtime > current + deviation) {
560
                delete_entry(path, d->basename, p);
561
                unsolicited += d->dsize;
562
            }
486
            }
563
            break;
487
            break;
564
488
Lines 567-573 static int process_dir(char *path, apr_p Link Here
567
         */
491
         */
568
        case TEMP:
492
        case TEMP:
569
            delete_file(path, d->basename, p);
493
            delete_file(path, d->basename, p);
570
            unsolicited += d->dsize;
494
            unsolicited += d->hsize;
571
            break;
495
            break;
572
        }
496
        }
573
    }
497
    }
Lines 604-610 static void purge(char *path, apr_pool_t Link Here
604
         e != APR_RING_SENTINEL(&root, _entry, link);
528
         e != APR_RING_SENTINEL(&root, _entry, link);
605
         e = APR_RING_NEXT(e, link)) {
529
         e = APR_RING_NEXT(e, link)) {
606
        sum += e->hsize;
530
        sum += e->hsize;
607
        sum += e->dsize;
608
        entries++;
531
        entries++;
609
    }
532
    }
610
533
Lines 623-632 static void purge(char *path, apr_pool_t Link Here
623
    for (e = APR_RING_FIRST(&root);
546
    for (e = APR_RING_FIRST(&root);
624
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
547
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
625
        n = APR_RING_NEXT(e, link);
548
        n = APR_RING_NEXT(e, link);
626
        if (e->response_time > now || e->htime > now || e->dtime > now) {
549
        if (e->response_time > now || e->htime > now ) {
627
            delete_entry(path, e->basename, pool);
550
            delete_entry(path, e->basename, pool);
628
            sum -= e->hsize;
551
            sum -= e->hsize;
629
            sum -= e->dsize;
630
            entries--;
552
            entries--;
631
            APR_RING_REMOVE(e, link);
553
            APR_RING_REMOVE(e, link);
632
            if (sum <= max) {
554
            if (sum <= max) {
Lines 649-656 static void purge(char *path, apr_pool_t Link Here
649
        n = APR_RING_NEXT(e, link);
571
        n = APR_RING_NEXT(e, link);
650
        if (e->expire != APR_DATE_BAD && e->expire < now) {
572
        if (e->expire != APR_DATE_BAD && e->expire < now) {
651
            delete_entry(path, e->basename, pool);
573
            delete_entry(path, e->basename, pool);
574
            if(verbose > 1) {
575
                apr_file_printf(errfile, "Expired: %s\n", e->name);
576
            }
652
            sum -= e->hsize;
577
            sum -= e->hsize;
653
            sum -= e->dsize;
654
            entries--;
578
            entries--;
655
            APR_RING_REMOVE(e, link);
579
            APR_RING_REMOVE(e, link);
656
            if (sum <= max) {
580
            if (sum <= max) {
Lines 678-691 static void purge(char *path, apr_pool_t Link Here
678
        for (e = APR_RING_NEXT(oldest, link);
602
        for (e = APR_RING_NEXT(oldest, link);
679
             e != APR_RING_SENTINEL(&root, _entry, link);
603
             e != APR_RING_SENTINEL(&root, _entry, link);
680
             e = APR_RING_NEXT(e, link)) {
604
             e = APR_RING_NEXT(e, link)) {
681
            if (e->dtime < oldest->dtime) {
605
            if (e->htime < oldest->htime) {
682
                oldest = e;
606
                oldest = e;
683
            }
607
            }
684
        }
608
        }
685
609
686
        delete_entry(path, oldest->basename, pool);
610
        delete_entry(path, oldest->basename, pool);
611
        if(verbose > 1) {
612
            apr_file_printf(errfile, "Old: (%d s) "
613
                            "(%" APR_OFF_T_FMT " b) %s\n", 
614
                            (int) apr_time_sec(apr_time_now() - oldest->htime),
615
                            oldest->hsize, oldest->name);
616
        }
687
        sum -= oldest->hsize;
617
        sum -= oldest->hsize;
688
        sum -= oldest->dsize;
689
        entries--;
618
        entries--;
690
        APR_RING_REMOVE(oldest, link);
619
        APR_RING_REMOVE(oldest, link);
691
    }
620
    }
Lines 833-842 int main(int argc, const char * const ar Link Here
833
                break;
762
                break;
834
763
835
            case 'v':
764
            case 'v':
836
                if (verbose) {
765
                verbose++;
837
                    usage();
838
                }
839
                verbose = 1;
840
                break;
766
                break;
841
767
842
            case 'r':
768
            case 'r':

Return to bug 39380