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

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