]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store.cc
Put storeDeleteBehind() call before storeCopy callback. Apparently
[thirdparty/squid.git] / src / store.cc
CommitLineData
ff5731b1 1
30a4f2a8 2/*
38f903c4 3 * $Id: store.cc,v 1.259 1997/06/18 16:15:53 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 20 Storeage Manager
6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
c943f331 31
32/*
30a4f2a8 33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
c943f331 105 */
090089c4 106
107/*
108 * Here is a summary of the routines which change mem_status and swap_status:
109 * Added 11/18/95
110 *
111 * Routine mem_status swap_status status
112 * ---------------------------------------------------------------------------
6eb42cae 113 * storeCreateEntry NOT_IN_MEMORY NO_SWAP
090089c4 114 * storeComplete IN_MEMORY NO_SWAP
115 * storeSwapOutStart SWAPPING_OUT
116 * storeSwapOutHandle(fail) NO_SWAP
117 * storeSwapOutHandle(ok) SWAP_OK
118 * ---------------------------------------------------------------------------
119 * storeAddDiskRestore NOT_IN_MEMORY SWAP_OK
120 * storeSwapInStart SWAPPING_IN
121 * storeSwapInHandle(fail) NOT_IN_MEMORY
122 * storeSwapInHandle(ok) IN_MEMORY
123 * ---------------------------------------------------------------------------
124 * storeAbort IN_MEMORY NO_SWAP
125 * storePurgeMem NOT_IN_MEMORY
126 * ---------------------------------------------------------------------------
127 * You can reclaim an object's space if it's:
128 * storeGetSwapSpace !SWAPPING_IN !SWAPPING_OUT !STORE_PENDING
129 *
130 */
131
44a47c6e 132#include "squid.h" /* goes first */
641941c0 133#include "filemap.h"
134#include "store_dir.h"
090089c4 135
136#define REBUILD_TIMESTAMP_DELTA_MAX 2
090089c4 137#define SWAP_BUF DISK_PAGE_SIZE
090089c4 138
227fbb74 139#define WITH_MEMOBJ 1
140#define WITHOUT_MEMOBJ 0
141
fcf5283d 142#define STORE_IN_MEM_BUCKETS (229)
090089c4 143
2285407f 144#define STORE_LOG_CREATE 0
145#define STORE_LOG_SWAPIN 1
146#define STORE_LOG_SWAPOUT 2
147#define STORE_LOG_RELEASE 3
148
38792624 149#define ENTRY_INMEM_SIZE(X) ((X)->e_current_len - (X)->e_lowest_offset)
150
147d3115 151static char *storeLogTags[] =
152{
153 "CREATE",
154 "SWAPIN",
155 "SWAPOUT",
156 "RELEASE"
2285407f 157};
158
0ee4272b 159const char *memStatusStr[] =
e62d2dea 160{
9dfb6c1c 161 "NOT_IN_MEMORY",
162 "SWAPPING_IN",
163 "IN_MEMORY"
164};
165
0ee4272b 166const char *pingStatusStr[] =
e62d2dea 167{
f17936ab 168 "PING_NONE",
9dfb6c1c 169 "PING_WAITING",
170 "PING_TIMEOUT",
f17936ab 171 "PING_DONE"
9dfb6c1c 172};
173
0ee4272b 174const char *storeStatusStr[] =
e62d2dea 175{
9dfb6c1c 176 "STORE_OK",
177 "STORE_PENDING",
178 "STORE_ABORTED"
179};
180
0ee4272b 181const char *swapStatusStr[] =
e62d2dea 182{
9dfb6c1c 183 "NO_SWAP",
184 "SWAPPING_OUT",
185 "SWAP_OK"
186};
187
5608850b 188struct storeRebuildState {
189 struct _rebuild_dir {
190 int dirn;
3e347513 191 FILE *log;
5608850b 192 int speed;
193 int clean;
194 struct _rebuild_dir *next;
3e347513 195 } *rebuild_dir;
3facc31e 196 int objcount; /* # objects successfully reloaded */
197 int expcount; /* # objects expired */
198 int linecount; /* # lines parsed from cache logfile */
199 int clashcount; /* # swapfile clashes avoided */
200 int dupcount; /* # duplicates purged */
f182d1c5 201 int need_to_validate;
5608850b 202 time_t start;
203 time_t stop;
204 char *line_in;
205 size_t line_in_sz;
3facc31e 206};
207
d3c12084 208struct _bucketOrder {
0c77d853 209 unsigned int bucket;
210 int index;
d3c12084 211};
212
0a0bf5db 213typedef struct storeCleanList {
214 char *key;
215 struct storeCleanList *next;
216} storeCleanList;
217
35ac11a3 218typedef void (VCB) _PARAMS((void *));
0a0bf5db 219
220typedef struct valid_ctrl_t {
221 struct stat *sb;
222 StoreEntry *e;
d7b78fbb 223 VCB *callback;
0a0bf5db 224 void *callback_data;
225} valid_ctrl_t;
226
227typedef struct swapin_ctrl_t {
228 StoreEntry *e;
229 char *path;
582b6456 230 SIH *callback;
0a0bf5db 231 void *callback_data;
232} swapin_ctrl_t;
233
234typedef struct lock_ctrl_t {
582b6456 235 SIH *callback;
0a0bf5db 236 void *callback_data;
237 StoreEntry *e;
238} lock_ctrl_t;
239
240typedef struct swapout_ctrl_t {
241 char *swapfilename;
242 int oldswapstatus;
243 StoreEntry *e;
0a0bf5db 244} swapout_ctrl_t;
245
30a4f2a8 246/* initializtion flag */
311ea387 247int store_rebuilding = 1;
30a4f2a8 248
249/* Static Functions */
76dd1215 250static void storeCreateHashTable _PARAMS((int (*)_PARAMS((const char *, const char *))));
24382924 251static int compareLastRef _PARAMS((StoreEntry **, StoreEntry **));
252static int compareSize _PARAMS((StoreEntry **, StoreEntry **));
429fdbec 253static int compareBucketOrder _PARAMS((struct _bucketOrder *, struct _bucketOrder *));
254static int storeCheckExpired _PARAMS((const StoreEntry *, int flag));
0ee4272b 255static int storeCheckPurgeMem _PARAMS((const StoreEntry *));
382d851a 256static int storeClientListSearch _PARAMS((const MemObject *, void *));
0ee4272b 257static int storeCopy _PARAMS((const StoreEntry *, int, int, char *, int *));
258static int storeEntryLocked _PARAMS((const StoreEntry *));
259static int storeEntryValidLength _PARAMS((const StoreEntry *));
38792624 260static void storeGetMemSpace _PARAMS((int));
24382924 261static int storeHashDelete _PARAMS((StoreEntry *));
0ee4272b 262static int storeShouldPurgeMem _PARAMS((const StoreEntry *));
d89d1fb6 263static DRCB storeSwapInHandle;
d7b78fbb 264static VCB storeSwapInValidateComplete;
0a0bf5db 265static void storeSwapInStartComplete _PARAMS((void *, int));
24382924 266static int swapInError _PARAMS((int, StoreEntry *));
267static mem_ptr new_MemObjectData _PARAMS((void));
268static MemObject *new_MemObject _PARAMS((void));
269static StoreEntry *new_StoreEntry _PARAMS((int));
0a0bf5db 270static StoreEntry *storeAddDiskRestore _PARAMS((const char *,
271 int,
272 int,
273 time_t,
274 time_t,
311ea387 275 time_t,
276 int));
24382924 277static StoreEntry *storeGetInMemFirst _PARAMS((void));
278static StoreEntry *storeGetInMemNext _PARAMS((void));
279static unsigned int storeGetBucketNum _PARAMS((void));
67508012 280static void destroy_MemObject _PARAMS((MemObject *));
281static void destroy_MemObjectData _PARAMS((MemObject *));
282static void destroy_StoreEntry _PARAMS((StoreEntry *));
24382924 283static void storeDeleteBehind _PARAMS((StoreEntry *));
284static void storePurgeMem _PARAMS((StoreEntry *));
24382924 285static void storeSetMemStatus _PARAMS((StoreEntry *, mem_status_t));
286static void storeStartRebuildFromDisk _PARAMS((void));
0a0bf5db 287static void storeSwapOutStart _PARAMS((StoreEntry * e));
288static void storeSwapOutStartComplete _PARAMS((void *, int));
d89d1fb6 289static DWCB storeSwapOutHandle;
67508012 290static void storeHashMemInsert _PARAMS((StoreEntry *));
291static void storeHashMemDelete _PARAMS((StoreEntry *));
fe54d06d 292static void storeSetPrivateKey _PARAMS((StoreEntry *));
d7b78fbb 293static EVH storeDoRebuildFromDisk;
294static EVH storeCleanup;
295static VCB storeCleanupComplete;
296static void storeValidate _PARAMS((StoreEntry *, VCB *, void *));
297static AIOCB storeValidateComplete;
5608850b 298static void storeRebuiltFromDisk _PARAMS((struct storeRebuildState * data));
2cc3f720 299static unsigned int getKeyCounter _PARAMS((void));
429fdbec 300static void storePutUnusedFileno _PARAMS((int fileno));
301static int storeGetUnusedFileno _PARAMS((void));
30a4f2a8 302
090089c4 303/* Now, this table is inaccessible to outsider. They have to use a method
304 * to access a value in internal storage data structure. */
35ac11a3 305static hash_table *store_table = NULL;
090089c4 306/* hash table for in-memory-only objects */
35ac11a3 307static hash_table *in_mem_table = NULL;
090089c4 308
090089c4 309/* current memory storage size */
e954773d 310unsigned long store_mem_size = 0;
311
13c72789 312static int store_pages_max = 0;
e954773d 313static int store_pages_high = 0;
314static int store_pages_low = 0;
090089c4 315
090089c4 316/* current file name, swap file, use number as a filename */
76fefb77 317int store_swap_size = 0; /* kilobytes !! */
58104eab 318static int store_swap_high = 0;
319static int store_swap_low = 0;
2285407f 320static int storelog_fd = -1;
090089c4 321
090089c4 322/* key temp buffer */
ec74cf5f 323static char key_temp_buffer[MAX_URL + 100];
090089c4 324
66cedb85 325/* expiration parameters and stats */
326static int store_buckets;
e924600d 327static int store_maintain_rate;
66cedb85 328static int store_maintain_buckets;
e924600d 329static int scan_revolutions;
d3c12084 330static struct _bucketOrder *MaintBucketsOrder = NULL;
66cedb85 331
b8d8561b 332static MemObject *
333new_MemObject(void)
227fbb74 334{
30a4f2a8 335 MemObject *mem = get_free_mem_obj();
336 mem->reply = xcalloc(1, sizeof(struct _http_reply));
33b589ff 337 mem->reply->date = -2;
338 mem->reply->expires = -2;
339 mem->reply->last_modified = -2;
0a0bf5db 340 mem->request = NULL;
579a45cd 341 meta_data.mem_obj_count++;
30a4f2a8 342 meta_data.misc += sizeof(struct _http_reply);
a3d5953d 343 debug(20, 3) ("new_MemObject: returning %p\n", mem);
30a4f2a8 344 return mem;
227fbb74 345}
346
b8d8561b 347static StoreEntry *
348new_StoreEntry(int mem_obj_flag)
090089c4 349{
350 StoreEntry *e = NULL;
351
30a4f2a8 352 e = xcalloc(1, sizeof(StoreEntry));
227fbb74 353 meta_data.store_entries++;
354 if (mem_obj_flag)
355 e->mem_obj = new_MemObject();
a3d5953d 356 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
227fbb74 357 return e;
090089c4 358}
359
b8d8561b 360static void
361destroy_MemObject(MemObject * mem)
090089c4 362{
a3d5953d 363 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
30a4f2a8 364 destroy_MemObjectData(mem);
52d4522b 365 safe_free(mem->clients);
30a4f2a8 366 safe_free(mem->reply);
367 safe_free(mem->e_abort_msg);
368 requestUnlink(mem->request);
369 mem->request = NULL;
370 put_free_mem_obj(mem);
579a45cd 371 meta_data.mem_obj_count--;
30a4f2a8 372 meta_data.misc -= sizeof(struct _http_reply);
090089c4 373}
374
b8d8561b 375static void
376destroy_StoreEntry(StoreEntry * e)
090089c4 377{
a3d5953d 378 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
85b701ed 379 if (!e) {
380 debug_trap("destroy_StoreEntry: NULL Entry");
381 return;
382 }
227fbb74 383 if (e->mem_obj)
384 destroy_MemObject(e->mem_obj);
30a4f2a8 385 if (e->url) {
386 meta_data.url_strings -= strlen(e->url);
387 safe_free(e->url);
388 } else {
a3d5953d 389 debug(20, 3) ("destroy_StoreEntry: WARNING: Entry without URL string!\n");
30a4f2a8 390 }
391 if (BIT_TEST(e->flag, KEY_URL))
392 e->key = NULL;
393 else
394 safe_free(e->key);
227fbb74 395 xfree(e);
396 meta_data.store_entries--;
090089c4 397}
398
b8d8561b 399static mem_ptr
400new_MemObjectData(void)
227fbb74 401{
a3d5953d 402 debug(20, 3) ("new_MemObjectData: calling memInit()\n");
579a45cd 403 meta_data.mem_data_count++;
227fbb74 404 return memInit();
405}
090089c4 406
b8d8561b 407static void
408destroy_MemObjectData(MemObject * mem)
090089c4 409{
a3d5953d 410 debug(20, 3) ("destroy_MemObjectData: destroying %p, %d bytes\n",
e954773d 411 mem->data, mem->e_current_len);
38792624 412 store_mem_size -= ENTRY_INMEM_SIZE(mem);
30a4f2a8 413 if (mem->data) {
d7b78fbb 414 memFree(mem->data);
30a4f2a8 415 mem->data = NULL;
579a45cd 416 meta_data.mem_data_count--;
090089c4 417 }
30a4f2a8 418 mem->e_current_len = 0;
090089c4 419}
420
090089c4 421/* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
422
423/*
424 * Create 2 hash tables, "table" has all objects, "in_mem_table" has only
425 * objects in the memory.
426 */
427
76dd1215 428static void
35ac11a3 429storeCreateHashTable(HASHCMP * cmp_func)
090089c4 430{
66cedb85 431 store_table = hash_create(cmp_func, store_buckets, hash4);
432 in_mem_table = hash_create(cmp_func, STORE_IN_MEM_BUCKETS, hash4);
090089c4 433}
434
b8d8561b 435static void
436storeHashMemInsert(StoreEntry * e)
b32508fb 437{
438 hash_insert(in_mem_table, e->key, e);
439 meta_data.hot_vm++;
440}
441
b8d8561b 442static void
443storeHashMemDelete(StoreEntry * e)
b32508fb 444{
445 hash_link *hptr = hash_lookup(in_mem_table, e->key);
85b701ed 446 if (hptr == NULL) {
447 debug_trap("storeHashMemDelete: key not found");
448 return;
449 }
b32508fb 450 hash_delete_link(in_mem_table, hptr);
451 meta_data.hot_vm--;
452}
090089c4 453
b8d8561b 454static int
455storeHashInsert(StoreEntry * e)
090089c4 456{
a3d5953d 457 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
227fbb74 458 e, e->key);
090089c4 459 if (e->mem_status == IN_MEMORY)
b32508fb 460 storeHashMemInsert(e);
461 return hash_join(store_table, (hash_link *) e);
090089c4 462}
463
b8d8561b 464static int
465storeHashDelete(StoreEntry * e)
090089c4 466{
b32508fb 467 if (e->mem_status == IN_MEMORY)
468 storeHashMemDelete(e);
469 return hash_remove_link(store_table, (hash_link *) e);
090089c4 470}
471
472/*
473 * maintain the in-mem hash table according to the changes of mem_status
234967c9 474 * This routine replaces the instruction "e->store_status = status;"
090089c4 475 */
24382924 476static void
b8d8561b 477storeSetMemStatus(StoreEntry * e, mem_status_t status)
090089c4 478{
85b701ed 479 if (e->key == NULL) {
480 debug_trap("storeSetMemStatus: NULL key");
481 return;
482 } else if (status != IN_MEMORY && e->mem_status == IN_MEMORY)
b32508fb 483 storeHashMemDelete(e);
484 else if (status == IN_MEMORY && e->mem_status != IN_MEMORY)
485 storeHashMemInsert(e);
090089c4 486 e->mem_status = status;
487}
488
489/* -------------------------------------------------------------------------- */
490
b8d8561b 491static void
fe4e214f 492storeLog(int tag, const StoreEntry * e)
2285407f 493{
0be3ec60 494 LOCAL_ARRAY(char, logmsg, MAX_URL << 1);
ca98227c 495 MemObject *mem = e->mem_obj;
496 struct _http_reply *reply;
2285407f 497 if (storelog_fd < 0)
498 return;
ca98227c 499 if (mem == NULL)
500 return;
501 reply = mem->reply;
2abe4822 502 sprintf(logmsg, "%9d.%03d %-7s %08X %4d %9d %9d %9d %s %d/%d %s %s\n",
2285407f 503 (int) current_time.tv_sec,
504 (int) current_time.tv_usec / 1000,
505 storeLogTags[tag],
2abe4822 506 e->swap_file_number,
ca98227c 507 reply->code,
508 (int) reply->date,
509 (int) reply->last_modified,
510 (int) reply->expires,
511 reply->content_type[0] ? reply->content_type : "unknown",
512 reply->content_length,
513 mem->e_current_len - mem->reply->hdr_sz,
514 RequestMethodStr[e->method],
2285407f 515 e->key);
516 file_write(storelog_fd,
517 xstrdup(logmsg),
518 strlen(logmsg),
2285407f 519 NULL,
e47afcdb 520 NULL,
521 xfree);
2285407f 522}
523
090089c4 524
525/* get rid of memory copy of the object */
620da955 526/* Only call this if storeCheckPurgeMem(e) returns 1 */
24382924 527static void
b8d8561b 528storePurgeMem(StoreEntry * e)
090089c4 529{
a3d5953d 530 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n", e->key);
2aca8433 531 if (e->mem_obj == NULL)
090089c4 532 return;
090089c4 533 storeSetMemStatus(e, NOT_IN_MEMORY);
227fbb74 534 destroy_MemObject(e->mem_obj);
535 e->mem_obj = NULL;
090089c4 536}
537
0a0bf5db 538void
95c4b18f 539storeLockObject(StoreEntry * e)
090089c4 540{
090089c4 541 e->lock_count++;
a3d5953d 542 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
148a2921 543 e->key, (int) e->lock_count);
b8de7ebe 544 e->lastref = squid_curtime;
090089c4 545}
546
b8d8561b 547void
548storeReleaseRequest(StoreEntry * e)
2285407f 549{
aadcfbad 550 if (BIT_TEST(e->flag, RELEASE_REQUEST))
58bcd31b 551 return;
9e4ad609 552 if (!storeEntryLocked(e))
553 fatal_dump("storeReleaseRequest: unlocked entry");
a3d5953d 554 debug(20, 3) ("storeReleaseRequest: '%s'\n", e->key);
9e4ad609 555 BIT_SET(e->flag, RELEASE_REQUEST);
fe54d06d 556 storeSetPrivateKey(e);
2285407f 557}
558
090089c4 559/* unlock object, return -1 if object get released after unlock
560 * otherwise lock_count */
b8d8561b 561int
562storeUnlockObject(StoreEntry * e)
090089c4 563{
6c895381 564 MemObject *mem = e->mem_obj;
565 e->lock_count--;
a3d5953d 566 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
6c895381 567 e->key, e->lock_count);
30a4f2a8 568 if (e->lock_count)
a1e47288 569 return (int) e->lock_count;
5b00be7a 570 if (e->store_status == STORE_PENDING) {
f182d1c5 571 assert(!BIT_TEST(e->flag, ENTRY_DISPATCHED));
572 BIT_SET(e->flag, RELEASE_REQUEST);
5b00be7a 573 }
429fdbec 574 if (storePendingNClients(e) > 0)
575 debug_trap("storeUnlockObject: unlocked entry with pending clients\n");
aadcfbad 576 if (BIT_TEST(e->flag, RELEASE_REQUEST)) {
30a4f2a8 577 storeRelease(e);
aadcfbad 578 } else if (BIT_TEST(e->flag, ABORT_MSG_PENDING)) {
30a4f2a8 579 /* This is where the negative cache gets storeAppended */
580 /* Briefly lock to replace content with abort message */
581 e->lock_count++;
6c895381 582 destroy_MemObjectData(mem);
30a4f2a8 583 e->object_len = 0;
6c895381 584 mem->data = new_MemObjectData();
585 storeAppend(e, mem->e_abort_msg, strlen(mem->e_abort_msg));
586 e->object_len = mem->e_current_len = strlen(mem->e_abort_msg);
30a4f2a8 587 BIT_RESET(e->flag, ABORT_MSG_PENDING);
588 e->lock_count--;
56f29785 589 } else if (storeShouldPurgeMem(e)) {
30a4f2a8 590 storePurgeMem(e);
090089c4 591 }
6c895381 592 return 0;
090089c4 593}
594
595/* Lookup an object in the cache.
596 * return just a reference to object, don't start swapping in yet. */
b8d8561b 597StoreEntry *
0ee4272b 598storeGet(const char *url)
090089c4 599{
a3d5953d 600 debug(20, 3) ("storeGet: looking up %s\n", url);
b15fe823 601 return (StoreEntry *) hash_lookup(store_table, url);
090089c4 602}
603
b8d8561b 604unsigned int
605getKeyCounter(void)
04e8dbaa 606{
2d25dc1a 607 static unsigned int key_counter = 0;
7cb386d0 608 if (++key_counter == (1 << 24))
2cc3f720 609 key_counter = 1;
2d25dc1a 610 return key_counter;
04e8dbaa 611}
612
2cc3f720 613unsigned int
614storeReqnum(StoreEntry * entry, method_t method)
615{
69951dbc 616 unsigned int k;
2cc3f720 617 if (BIT_TEST(entry->flag, KEY_PRIVATE))
69951dbc 618 k = atoi(entry->key);
619 else
620 k = getKeyCounter();
2cc3f720 621 if (method == METHOD_GET)
69951dbc 622 return k;
623 return (method << 24) | k;
2cc3f720 624}
625
0ee4272b 626const char *
627storeGeneratePrivateKey(const char *url, method_t method, int num)
6eb42cae 628{
6eb42cae 629 if (num == 0)
04e8dbaa 630 num = getKeyCounter();
2cc3f720 631 else if (num & 0xFF000000) {
632 method = (method_t) (num >> 24);
633 num &= 0x00FFFFFF;
634 }
a3d5953d 635 debug(20, 3) ("storeGeneratePrivateKey: '%s'\n", url);
6eb42cae 636 key_temp_buffer[0] = '\0';
637 sprintf(key_temp_buffer, "%d/%s/%s",
638 num,
73ddce89 639 RequestMethodStr[method],
6eb42cae 640 url);
641 return key_temp_buffer;
642}
643
0ee4272b 644const char *
645storeGeneratePublicKey(const char *url, method_t method)
090089c4 646{
a3d5953d 647 debug(20, 3) ("storeGeneratePublicKey: type=%d %s\n", method, url);
73ddce89 648 switch (method) {
6eb42cae 649 case METHOD_GET:
227fbb74 650 return url;
983061ed 651 /* NOTREACHED */
227fbb74 652 break;
6eb42cae 653 case METHOD_POST:
30a4f2a8 654 case METHOD_PUT:
6eb42cae 655 case METHOD_HEAD:
3facc31e 656 case METHOD_CONNECT:
b3b64e58 657 case METHOD_TRACE:
2cc3f720 658 sprintf(key_temp_buffer, "/%s/%s", RequestMethodStr[method], url);
b3b64e58 659 return key_temp_buffer;
660 /* NOTREACHED */
661 break;
227fbb74 662 default:
85b701ed 663 debug_trap("storeGeneratePublicKey: Unsupported request method");
227fbb74 664 break;
665 }
666 return NULL;
667}
668
fe54d06d 669static void
b8d8561b 670storeSetPrivateKey(StoreEntry * e)
227fbb74 671{
6eb42cae 672 hash_link *table_entry = NULL;
0ee4272b 673 const char *newkey = NULL;
6eb42cae 674 if (e->key && BIT_TEST(e->flag, KEY_PRIVATE))
675 return; /* is already private */
73ddce89 676 newkey = storeGeneratePrivateKey(e->url, e->method, 0);
ec74cf5f 677 if ((table_entry = hash_lookup(store_table, newkey))) {
be0d70ce 678 debug_trap("storeSetPrivateKey: duplicate private key");
85b701ed 679 return;
227fbb74 680 }
07622ccd 681 if (e->key)
edae7bc0 682 storeHashDelete(e);
07622ccd 683 if (e->key && !BIT_TEST(e->flag, KEY_URL))
684 safe_free(e->key);
685 e->key = xstrdup(newkey);
6eb42cae 686 storeHashInsert(e);
227fbb74 687 BIT_RESET(e->flag, KEY_URL);
6eb42cae 688 BIT_SET(e->flag, KEY_CHANGE);
689 BIT_SET(e->flag, KEY_PRIVATE);
227fbb74 690}
691
b8d8561b 692void
693storeSetPublicKey(StoreEntry * e)
227fbb74 694{
6eb42cae 695 StoreEntry *e2 = NULL;
696 hash_link *table_entry = NULL;
0ee4272b 697 const char *newkey = NULL;
234967c9 698 int loop_detect = 0;
6eb42cae 699
700 if (e->key && !BIT_TEST(e->flag, KEY_PRIVATE))
701 return; /* is already public */
702
73ddce89 703 newkey = storeGeneratePublicKey(e->url, e->method);
ec74cf5f 704 while ((table_entry = hash_lookup(store_table, newkey))) {
a3d5953d 705 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", newkey);
07622ccd 706 e2 = (StoreEntry *) table_entry;
707 storeSetPrivateKey(e2);
30a4f2a8 708 storeRelease(e2);
234967c9 709 if (loop_detect++ == 10)
710 fatal_dump("storeSetPublicKey() is looping!!");
711 newkey = storeGeneratePublicKey(e->url, e->method);
6eb42cae 712 }
07622ccd 713 if (e->key)
edae7bc0 714 storeHashDelete(e);
07622ccd 715 if (e->key && !BIT_TEST(e->flag, KEY_URL))
716 safe_free(e->key);
73ddce89 717 if (e->method == METHOD_GET) {
6eb42cae 718 e->key = e->url;
719 BIT_SET(e->flag, KEY_URL);
720 BIT_RESET(e->flag, KEY_CHANGE);
721 } else {
07622ccd 722 e->key = xstrdup(newkey);
6eb42cae 723 BIT_RESET(e->flag, KEY_URL);
724 BIT_SET(e->flag, KEY_CHANGE);
227fbb74 725 }
07622ccd 726 BIT_RESET(e->flag, KEY_PRIVATE);
227fbb74 727 storeHashInsert(e);
728}
729
b8d8561b 730StoreEntry *
62dec5a7 731storeCreateEntry(const char *url, int flags, method_t method)
090089c4 732{
090089c4 733 StoreEntry *e = NULL;
30a4f2a8 734 MemObject *mem = NULL;
a3d5953d 735 debug(20, 3) ("storeCreateEntry: '%s' icp flags=%x\n", url, flags);
090089c4 736
6eb42cae 737 e = new_StoreEntry(WITH_MEMOBJ);
30a4f2a8 738 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
739 mem = e->mem_obj;
090089c4 740 e->url = xstrdup(url);
6eb42cae 741 meta_data.url_strings += strlen(url);
73ddce89 742 e->method = method;
234967c9 743 if (BIT_TEST(flags, REQ_CACHABLE)) {
1c481e00 744 BIT_SET(e->flag, ENTRY_CACHABLE);
090089c4 745 BIT_RESET(e->flag, RELEASE_REQUEST);
746 } else {
1c481e00 747 BIT_RESET(e->flag, ENTRY_CACHABLE);
2daae136 748 storeReleaseRequest(e);
090089c4 749 }
234967c9 750 if (BIT_TEST(flags, REQ_HIERARCHICAL))
751 BIT_SET(e->flag, HIERARCHICAL);
752 else
753 BIT_RESET(e->flag, HIERARCHICAL);
754 if (neighbors_do_private_keys || !BIT_TEST(flags, REQ_HIERARCHICAL))
6eb42cae 755 storeSetPrivateKey(e);
090089c4 756 else
6eb42cae 757 storeSetPublicKey(e);
caebbe00 758 BIT_SET(e->flag, ENTRY_HTML);
090089c4 759
234967c9 760 e->store_status = STORE_PENDING;
090089c4 761 storeSetMemStatus(e, NOT_IN_MEMORY);
762 e->swap_status = NO_SWAP;
763 e->swap_file_number = -1;
30a4f2a8 764 mem->data = new_MemObjectData();
090089c4 765 e->refcount = 0;
b8de7ebe 766 e->lastref = squid_curtime;
ca98227c 767 e->timestamp = 0; /* set in storeTimestampsSet() */
30a4f2a8 768 e->ping_status = PING_NONE;
0a0bf5db 769 BIT_SET(e->flag, ENTRY_VALIDATED);
227fbb74 770
090089c4 771 /* allocate client list */
52d4522b 772 mem->nclients = MIN_CLIENT;
d3a743b1 773 mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client));
2285407f 774 /* storeLog(STORE_LOG_CREATE, e); */
090089c4 775 return e;
776}
777
778/* Add a new object to the cache with empty memory copy and pointer to disk
779 * use to rebuild store from disk. */
24382924 780static StoreEntry *
311ea387 781storeAddDiskRestore(const char *url, int file_number, int size, time_t expires, time_t timestamp, time_t lastmod, int clean)
090089c4 782{
783 StoreEntry *e = NULL;
784
a3d5953d 785 debug(20, 5) ("StoreAddDiskRestore: '%s': size %d: expires %d: fileno=%08X\n",
090089c4 786 url, size, expires, file_number);
787
3facc31e 788 /* if you call this you'd better be sure file_number is not
789 * already in use! */
66a91d2d 790
090089c4 791 meta_data.url_strings += strlen(url);
792
227fbb74 793 e = new_StoreEntry(WITHOUT_MEMOBJ);
090089c4 794 e->url = xstrdup(url);
73ddce89 795 e->method = METHOD_GET;
6eb42cae 796 storeSetPublicKey(e);
1c481e00 797 BIT_SET(e->flag, ENTRY_CACHABLE);
090089c4 798 BIT_RESET(e->flag, RELEASE_REQUEST);
6eb42cae 799 BIT_SET(e->flag, ENTRY_HTML);
234967c9 800 e->store_status = STORE_OK;
090089c4 801 storeSetMemStatus(e, NOT_IN_MEMORY);
802 e->swap_status = SWAP_OK;
803 e->swap_file_number = file_number;
090089c4 804 e->object_len = size;
805 e->lock_count = 0;
090089c4 806 e->refcount = 0;
3c0117c9 807 e->lastref = timestamp;
f5469bce 808 e->timestamp = timestamp;
809 e->expires = expires;
810 e->lastmod = lastmod;
30a4f2a8 811 e->ping_status = PING_NONE;
311ea387 812 if (clean) {
3e347513 813 BIT_SET(e->flag, ENTRY_VALIDATED);
9e4ad609 814 /* Only set the file bit if we know its a valid entry */
815 /* otherwise, set it in the validation procedure */
816 storeDirMapBitSet(file_number);
817 } else {
3e347513 818 BIT_RESET(e->flag, ENTRY_VALIDATED);
9e4ad609 819 }
090089c4 820 return e;
821}
822
b8d8561b 823int
382d851a 824storeUnregister(StoreEntry * e, void *data)
090089c4 825{
826 int i;
6e40f263 827 MemObject *mem = e->mem_obj;
f990cccc 828 struct _store_client *sc;
38792624 829 if (mem == NULL)
830 return 0;
a3d5953d 831 debug(20, 3) ("storeUnregister: called for '%s'\n", e->key);
382d851a 832 if ((i = storeClientListSearch(mem, data)) < 0)
52d4522b 833 return 0;
f990cccc 834 sc = &mem->clients[i];
fe96bbe6 835 sc->seen_offset = 0;
836 sc->copy_offset = 0;
f990cccc 837 sc->callback = NULL;
838 sc->callback_data = NULL;
a3d5953d 839 debug(20, 9) ("storeUnregister: returning 1\n");
52d4522b 840 return 1;
090089c4 841}
842
b8d8561b 843int
fe4e214f 844storeGetLowestReaderOffset(const StoreEntry * entry)
234967c9 845{
0ee4272b 846 const MemObject *mem = entry->mem_obj;
30a4f2a8 847 int lowest = mem->e_current_len;
234967c9 848 int i;
52d4522b 849 for (i = 0; i < mem->nclients; i++) {
382d851a 850 if (mem->clients[i].callback_data == NULL)
234967c9 851 continue;
fe96bbe6 852 if (mem->clients[i].copy_offset < lowest)
853 lowest = mem->clients[i].copy_offset;
234967c9 854 }
855 return lowest;
856}
857
090089c4 858/* Call to delete behind upto "target lowest offset"
234967c9 859 * also, update e_lowest_offset */
24382924 860static void
b8d8561b 861storeDeleteBehind(StoreEntry * e)
090089c4 862{
30a4f2a8 863 MemObject *mem = e->mem_obj;
38792624 864 int old_lowest_offset = mem->e_lowest_offset;
865 int new_lowest_offset;
866 int target_offset = storeGetLowestReaderOffset(e);
867 if (target_offset == 0)
868 return;
38f903c4 869 new_lowest_offset = (int) memFreeDataUpto(mem->data, target_offset);
38792624 870 store_mem_size -= new_lowest_offset - old_lowest_offset;
871 mem->e_lowest_offset = new_lowest_offset;
090089c4 872}
873
874/* Call handlers waiting for data to be appended to E. */
498d87ae 875void
b8d8561b 876InvokeHandlers(StoreEntry * e)
090089c4 877{
878 int i;
9dfb6c1c 879 MemObject *mem = e->mem_obj;
d89d1fb6 880 STCB *callback = NULL;
52d4522b 881 struct _store_client *sc;
d89d1fb6 882 size_t size;
1bab7e0b 883 if (mem->clients == NULL && mem->nclients) {
28070024 884 debug_trap("InvokeHandlers: NULL mem->clients");
885 return;
886 }
d89d1fb6 887 /* walk the entire list looking for valid callbacks */
52d4522b 888 for (i = 0; i < mem->nclients; i++) {
889 sc = &mem->clients[i];
382d851a 890 if (sc->callback_data == NULL)
63cc9284 891 continue;
d89d1fb6 892 if ((callback = sc->callback) == NULL)
63cc9284 893 continue;
52d4522b 894 sc->callback = NULL;
382d851a 895 /* Don't NULL the callback_data, its used to identify the client */
d89d1fb6 896 size = memCopy(mem->data,
fe96bbe6 897 sc->copy_offset,
d89d1fb6 898 sc->copy_buf,
899 sc->copy_size);
900 callback(sc->callback_data, sc->copy_buf, size);
090089c4 901 }
090089c4 902}
903
6eb42cae 904/* Mark object as expired */
b8d8561b 905void
906storeExpireNow(StoreEntry * e)
9174e204 907{
a3d5953d 908 debug(20, 3) ("storeExpireNow: '%s'\n", e->key);
b8de7ebe 909 e->expires = squid_curtime;
9174e204 910}
911
6eb42cae 912/* switch object to deleting behind mode call by
913 * retrieval module when object gets too big. */
b8d8561b 914void
915storeStartDeleteBehind(StoreEntry * e)
090089c4 916{
aadcfbad 917 if (BIT_TEST(e->flag, DELETE_BEHIND))
090089c4 918 return;
a3d5953d 919 debug(20, e->mem_obj->e_current_len ? 1 : 3)
920 ("storeStartDeleteBehind: '%s' at %d bytes\n",
48f44632 921 e->url, e->mem_obj->e_current_len);
6eb42cae 922 storeSetPrivateKey(e);
090089c4 923 BIT_SET(e->flag, DELETE_BEHIND);
2daae136 924 storeReleaseRequest(e);
1c481e00 925 BIT_RESET(e->flag, ENTRY_CACHABLE);
9174e204 926 storeExpireNow(e);
090089c4 927}
928
929/* Append incoming data from a primary server to an entry. */
b8d8561b 930void
3a1c3e2f 931storeAppend(StoreEntry * e, const char *buf, int len)
090089c4 932{
3a1c3e2f 933 MemObject *mem = e->mem_obj;
934 assert(mem != NULL);
935 assert(len >= 0);
090089c4 936 if (len) {
a3d5953d 937 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", len, e->key);
38792624 938 storeGetMemSpace(len);
450e6053 939 if (sm_stats.n_pages_in_use > store_pages_low) {
38792624 940 if (mem->e_current_len > Config.Store.maxObjectSize)
941 storeStartDeleteBehind(e);
942 }
090089c4 943 store_mem_size += len;
3a1c3e2f 944 memAppend(mem->data, buf, len);
38792624 945 mem->e_current_len += len;
090089c4 946 }
aadcfbad 947 if (e->store_status != STORE_ABORTED && !BIT_TEST(e->flag, DELAY_SENDING))
090089c4 948 InvokeHandlers(e);
090089c4 949}
950
24382924 951#ifdef __STDC__
b8d8561b 952void
fe4e214f 953storeAppendPrintf(StoreEntry * e, const char *fmt,...)
c30c5a73 954{
15c05bb0 955 va_list args;
95d659f0 956 LOCAL_ARRAY(char, buf, 4096);
15c05bb0 957 va_start(args, fmt);
c30c5a73 958#else
b8d8561b 959void
960storeAppendPrintf(va_alist)
15c05bb0 961 va_dcl
962{
963 va_list args;
964 StoreEntry *e = NULL;
0ee4272b 965 const char *fmt = NULL;
95d659f0 966 LOCAL_ARRAY(char, buf, 4096);
15c05bb0 967 va_start(args);
968 e = va_arg(args, StoreEntry *);
969 fmt = va_arg(args, char *);
c30c5a73 970#endif
15c05bb0 971 buf[0] = '\0';
972 vsprintf(buf, fmt, args);
973 storeAppend(e, buf, strlen(buf));
974 va_end(args);
c30c5a73 975}
976
090089c4 977/* swapping in handle */
582b6456 978static void
979storeSwapInHandle(int u1, const char *buf, int len, int flag, void *data)
090089c4 980{
582b6456 981 StoreEntry *e = data;
30a4f2a8 982 MemObject *mem = e->mem_obj;
311ea387 983 assert(mem);
a3d5953d 984 debug(20, 2) ("storeSwapInHandle: '%s'\n", e->key);
090089c4 985 if ((flag < 0) && (flag != DISK_EOF)) {
a3d5953d 986 debug(20, 0) ("storeSwapInHandle: SwapIn failure (err code = %d).\n", flag);
30a4f2a8 987 put_free_8k_page(mem->e_swap_buf);
090089c4 988 storeSetMemStatus(e, NOT_IN_MEMORY);
e47afcdb 989 file_close(mem->swapin_fd);
090089c4 990 swapInError(-1, e); /* Invokes storeAbort() and completes the I/O */
582b6456 991 return;
090089c4 992 }
a3d5953d 993 debug(20, 5) ("storeSwapInHandle: e->swap_offset = %d\n", mem->swap_offset);
994 debug(20, 5) ("storeSwapInHandle: e->e_current_len = %d\n", mem->e_current_len);
995 debug(20, 5) ("storeSwapInHandle: e->object_len = %d\n", e->object_len);
ca98227c 996 if (len && mem->swap_offset == 0)
997 httpParseReplyHeaders(buf, mem->reply);
998 /* Assumes we got all the headers in one read() */
090089c4 999 /* always call these, even if len == 0 */
30a4f2a8 1000 mem->swap_offset += len;
090089c4 1001 storeAppend(e, buf, len);
30a4f2a8 1002 if (mem->e_current_len < e->object_len && flag != DISK_EOF) {
090089c4 1003 /* some more data to swap in, reschedule */
e47afcdb 1004 file_read(mem->swapin_fd,
30a4f2a8 1005 mem->e_swap_buf,
090089c4 1006 SWAP_BUF,
30a4f2a8 1007 mem->swap_offset,
cd1fb0eb 1008 storeSwapInHandle,
1009 e);
582b6456 1010 return;
977a52ae 1011 }
9e4ad609 1012 if (mem->e_current_len > e->object_len)
1013 debug_trap("storeSwapInHandle: Too much data read!");
977a52ae 1014 /* complete swapping in */
1015 storeSetMemStatus(e, IN_MEMORY);
1016 put_free_8k_page(mem->e_swap_buf);
1017 file_close(mem->swapin_fd);
1018 storeLog(STORE_LOG_SWAPIN, e);
a3d5953d 1019 debug(20, 5) ("storeSwapInHandle: SwapIn complete: '%s' from %s.\n",
977a52ae 1020 e->url, storeSwapFullPath(e->swap_file_number, NULL));
1021 if (mem->e_current_len != e->object_len) {
9e4ad609 1022 debug_trap("storeSwapInHandle: Object size mismatch");
a3d5953d 1023 debug(20, 0) (" --> '%s'\n", e->url);
1024 debug(20, 0) (" --> Expecting %d bytes from file: %s\n", e->object_len,
977a52ae 1025 storeSwapFullPath(e->swap_file_number, NULL));
a3d5953d 1026 debug(20, 0) (" --> Only read %d bytes\n",
977a52ae 1027 mem->e_current_len);
1028 }
1029 e->lock_count++; /* lock while calling handler */
1030 InvokeHandlers(e); /* once more after mem_status state change */
977a52ae 1031 e->lock_count--;
1032 if (BIT_TEST(e->flag, RELEASE_REQUEST)) {
1033 storeRelease(e);
1034 } else if ((mem = e->mem_obj)) {
1035 requestUnlink(mem->request);
1036 mem->request = NULL;
090089c4 1037 }
090089c4 1038}
1039
1040/* start swapping in */
95c4b18f 1041void
582b6456 1042storeSwapInStart(StoreEntry * e, SIH * callback, void *callback_data)
090089c4 1043{
0a0bf5db 1044 swapin_ctrl_t *ctrlp;
311ea387 1045 if (e->mem_status != NOT_IN_MEMORY) {
1046 callback(callback_data, 0);
1047 return;
1048 }
1049 if (e->store_status == STORE_PENDING) {
2abe4822 1050 callback(callback_data, 0);
1051 return;
1052 }
311ea387 1053 if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) {
97a88399 1054 if (storeDirMapBitTest(e->swap_file_number)) {
311ea387 1055 /* someone took our file while we weren't looking */
1056 callback(callback_data, -1);
1057 return;
1058 }
1059 }
95c4b18f 1060 assert(e->swap_status == SWAP_OK);
1061 assert(e->swap_file_number >= 0);
1062 assert(e->mem_obj == NULL);
0379374a 1063 e->mem_obj = new_MemObject();
0a0bf5db 1064 ctrlp = xmalloc(sizeof(swapin_ctrl_t));
1065 ctrlp->e = e;
1066 ctrlp->callback = callback;
1067 ctrlp->callback_data = callback_data;
95c4b18f 1068 if (BIT_TEST(e->flag, ENTRY_VALIDATED))
35ac11a3 1069 storeSwapInValidateComplete(ctrlp);
95c4b18f 1070 else
1071 storeValidate(e, storeSwapInValidateComplete, ctrlp);
0a0bf5db 1072}
090089c4 1073
0a0bf5db 1074
1075static void
35ac11a3 1076storeSwapInValidateComplete(void *data)
0a0bf5db 1077{
1078 swapin_ctrl_t *ctrlp = (swapin_ctrl_t *) data;
0a0bf5db 1079 StoreEntry *e;
1080 e = ctrlp->e;
95c4b18f 1081 assert(e->mem_status == NOT_IN_MEMORY);
0a0bf5db 1082 if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) {
fc07a0e4 1083 /* Invoke a store abort that should free the memory object */
0a0bf5db 1084 (ctrlp->callback) (ctrlp->callback_data, -1);
1085 xfree(ctrlp);
1086 return;
090089c4 1087 }
95c4b18f 1088 ctrlp->path = xstrdup(storeSwapFullPath(e->swap_file_number, NULL));
1089 file_open(ctrlp->path, O_RDONLY, storeSwapInStartComplete, ctrlp);
0a0bf5db 1090}
1091
1092static void
1093storeSwapInStartComplete(void *data, int fd)
1094{
0a0bf5db 1095 swapin_ctrl_t *ctrlp = (swapin_ctrl_t *) data;
1096 StoreEntry *e = ctrlp->e;
0379374a 1097 MemObject *mem = e->mem_obj;
1098 assert(e->mem_obj != NULL);
95c4b18f 1099 assert(e->mem_status == NOT_IN_MEMORY);
1100 assert(e->swap_status == SWAP_OK);
0a0bf5db 1101 if (fd < 0) {
a3d5953d 1102 debug(20, 0) ("storeSwapInStartComplete: Failed for '%s'\n", e->url);
0a0bf5db 1103 /* Invoke a store abort that should free the memory object */
1104 (ctrlp->callback) (ctrlp->callback_data, -1);
1105 xfree(ctrlp->path);
1106 xfree(ctrlp);
1107 return;
1108 }
0a0bf5db 1109 storeSetMemStatus(e, SWAPPING_IN);
e47afcdb 1110 mem->swapin_fd = (short) fd;
a3d5953d 1111 debug(20, 5) ("storeSwapInStart: initialized swap file '%s' for '%s'\n",
0a0bf5db 1112 ctrlp->path, e->url);
e47afcdb 1113 mem->data = new_MemObjectData();
1114 mem->swap_offset = 0;
1115 mem->e_swap_buf = get_free_8k_page();
090089c4 1116 /* start swapping daemon */
fc07a0e4 1117 file_read(fd,
e47afcdb 1118 mem->e_swap_buf,
090089c4 1119 SWAP_BUF,
e47afcdb 1120 mem->swap_offset,
cd1fb0eb 1121 storeSwapInHandle,
1122 e);
0a0bf5db 1123 (ctrlp->callback) (ctrlp->callback_data, 0);
1124 xfree(ctrlp->path);
1125 xfree(ctrlp);
090089c4 1126}
1127
24382924 1128static void
d89d1fb6 1129storeSwapOutHandle(int fd, int flag, size_t len, void *data)
090089c4 1130{
582b6456 1131 StoreEntry *e = data;
e47afcdb 1132 MemObject *mem = e->mem_obj;
a3d5953d 1133 debug(20, 3) ("storeSwapOutHandle: '%s'\n", e->key);
be0d70ce 1134 if (mem == NULL)
1135 fatal_dump("storeSwapOutHandle: NULL mem_obj");
090089c4 1136 if (flag < 0) {
a3d5953d 1137 debug(20, 1) ("storeSwapOutHandle: SwapOut failure (err code = %d).\n",
090089c4 1138 flag);
1139 e->swap_status = NO_SWAP;
e47afcdb 1140 put_free_8k_page(mem->e_swap_buf);
090089c4 1141 file_close(fd);
090089c4 1142 if (e->swap_file_number != -1) {
429fdbec 1143 storePutUnusedFileno(e->swap_file_number);
090089c4 1144 e->swap_file_number = -1;
1145 }
b6a63185 1146 storeRelease(e);
090089c4 1147 if (flag == DISK_NO_SPACE_LEFT) {
1148 /* reduce the swap_size limit to the current size. */
b1c0cc67 1149 Config.Swap.maxSize = store_swap_size;
1150 storeConfigure();
090089c4 1151 }
1152 return;
1153 }
a3d5953d 1154 debug(20, 6) ("storeSwapOutHandle: e->swap_offset = %d\n", mem->swap_offset);
1155 debug(20, 6) ("storeSwapOutHandle: e->e_swap_buf_len = %d\n", mem->e_swap_buf_len);
1156 debug(20, 6) ("storeSwapOutHandle: e->object_len = %d\n", e->object_len);
1157 debug(20, 6) ("storeSwapOutHandle: store_swap_size = %dk\n", store_swap_size);
e47afcdb 1158 mem->swap_offset += mem->e_swap_buf_len;
090089c4 1159 /* round up */
c932b107 1160 storeDirUpdateSwapSize(e->swap_file_number, mem->e_swap_buf_len, 1);
e47afcdb 1161 if (mem->swap_offset >= e->object_len) {
090089c4 1162 /* swapping complete */
1163 e->swap_status = SWAP_OK;
e47afcdb 1164 file_close(mem->swapout_fd);
2285407f 1165 storeLog(STORE_LOG_SWAPOUT, e);
a3d5953d 1166 debug(20, 5) ("storeSwapOutHandle: SwapOut complete: '%s' to %s.\n",
090089c4 1167 e->url, storeSwapFullPath(e->swap_file_number, NULL));
e47afcdb 1168 put_free_8k_page(mem->e_swap_buf);
5608850b 1169 storeDirSwapLog(e);
a528eb87 1170 HTTPCacheInfo->proto_newobject(HTTPCacheInfo,
e47afcdb 1171 mem->request->protocol,
30a4f2a8 1172 e->object_len,
1173 FALSE);
090089c4 1174 /* check if it's request to be released. */
aadcfbad 1175 if (BIT_TEST(e->flag, RELEASE_REQUEST))
090089c4 1176 storeRelease(e);
56f29785 1177 else if (storeShouldPurgeMem(e))
30a4f2a8 1178 storePurgeMem(e);
12f6386d 1179 else {
1180 requestUnlink(mem->request);
1181 mem->request = NULL;
1182 }
090089c4 1183 return;
1184 }
1185 /* write some more data, reschedule itself. */
0a0bf5db 1186 if (storeCopy(e, mem->swap_offset, SWAP_BUF, mem->e_swap_buf, &(mem->e_swap_buf_len)) < 0) {
a3d5953d 1187 debug(20, 1) ("storeSwapOutHandle: SwapOut failure (err code = %d).\n",
0a0bf5db 1188 flag);
1189 e->swap_status = NO_SWAP;
1190 put_free_8k_page(mem->e_swap_buf);
1191 file_close(fd);
1192 if (e->swap_file_number != -1) {
641941c0 1193 storeDirMapBitReset(e->swap_file_number);
0a0bf5db 1194 safeunlink(storeSwapFullPath(e->swap_file_number, NULL), 0);
1195 e->swap_file_number = -1;
1196 }
1197 storeRelease(e);
1198 return;
1199 }
e47afcdb 1200 file_write(mem->swapout_fd,
1201 mem->e_swap_buf,
1202 mem->e_swap_buf_len,
3facc31e 1203 storeSwapOutHandle,
e47afcdb 1204 e,
1205 NULL);
090089c4 1206 return;
090089c4 1207}
1208
090089c4 1209/* start swapping object to disk */
0a0bf5db 1210static void
b8d8561b 1211storeSwapOutStart(StoreEntry * e)
090089c4 1212{
0a0bf5db 1213 swapout_ctrl_t *ctrlp;
f2052513 1214 LOCAL_ARRAY(char, swapfilename, SQUID_MAXPATHLEN);
95c4b18f 1215 if ((e->swap_file_number = storeGetUnusedFileno()) < 0)
1216 e->swap_file_number = storeDirMapAllocate();
1217 storeSwapFullPath(e->swap_file_number, swapfilename);
0a0bf5db 1218 ctrlp = xmalloc(sizeof(swapout_ctrl_t));
1219 ctrlp->swapfilename = xstrdup(swapfilename);
1220 ctrlp->e = e;
1221 ctrlp->oldswapstatus = e->swap_status;
0a0bf5db 1222 e->swap_status = SWAPPING_OUT;
1223 file_open(swapfilename,
0a0bf5db 1224 O_WRONLY | O_CREAT | O_TRUNC,
1225 storeSwapOutStartComplete,
1226 ctrlp);
1227}
1228
1229static void
1230storeSwapOutStartComplete(void *data, int fd)
1231{
641941c0 1232 swapout_ctrl_t *ctrlp = data;
1233 int oldswapstatus = ctrlp->oldswapstatus;
1234 char *swapfilename = ctrlp->swapfilename;
1235 StoreEntry *e = ctrlp->e;
0a0bf5db 1236 int x;
0a0bf5db 1237 MemObject *mem;
0a0bf5db 1238 xfree(ctrlp);
090089c4 1239 if (fd < 0) {
a3d5953d 1240 debug(20, 0) ("storeSwapOutStart: Unable to open swapfile: %s\n",
090089c4 1241 swapfilename);
95c4b18f 1242 storeDirMapBitReset(e->swap_file_number);
090089c4 1243 e->swap_file_number = -1;
0a0bf5db 1244 if (e->swap_status == SWAPPING_OUT)
1245 e->swap_status = oldswapstatus;
1246 xfree(swapfilename);
1247 return;
090089c4 1248 }
0a0bf5db 1249 mem = e->mem_obj;
e47afcdb 1250 mem->swapout_fd = (short) fd;
a3d5953d 1251 debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to FD %d FILE %s.\n",
090089c4 1252 e->url, fd, swapfilename);
a3d5953d 1253 debug(20, 5) ("swap_file_number=%08X\n", e->swap_file_number);
090089c4 1254 e->swap_status = SWAPPING_OUT;
e47afcdb 1255 mem->swap_offset = 0;
1256 mem->e_swap_buf = get_free_8k_page();
1257 mem->e_swap_buf_len = 0;
0a0bf5db 1258 x = storeCopy(e,
e47afcdb 1259 0,
1260 SWAP_BUF,
1261 mem->e_swap_buf,
a1e47288 1262 &mem->e_swap_buf_len);
0a0bf5db 1263 if (x < 0) {
a3d5953d 1264 debug(20, 1) ("storeCopy returned %d for '%s'\n", x, e->key);
0a0bf5db 1265 e->swap_file_number = -1;
1266 file_close(fd);
641941c0 1267 storeDirMapBitReset(e->swap_file_number);
0a0bf5db 1268 e->swap_file_number = -1;
1269 safeunlink(swapfilename, 1);
1270 if (e->swap_status == SWAPPING_OUT)
1271 e->swap_status = oldswapstatus;
1272 xfree(swapfilename);
1273 return;
1274 }
090089c4 1275 /* start swapping daemon */
e47afcdb 1276 x = file_write(mem->swapout_fd,
1277 mem->e_swap_buf,
1278 mem->e_swap_buf_len,
e47afcdb 1279 storeSwapOutHandle,
1280 e,
1281 NULL);
1282 if (x != DISK_OK)
1283 fatal_dump(NULL); /* This shouldn't happen */
0a0bf5db 1284 xfree(swapfilename);
090089c4 1285}
1286
1287/* recreate meta data from disk image in swap directory */
3facc31e 1288/* Add one swap file at a time from disk storage */
48f44632 1289static void
1290storeDoRebuildFromDisk(void *data)
3facc31e 1291{
5608850b 1292 struct storeRebuildState *RB = data;
429fdbec 1293 LOCAL_ARRAY(char, swapfile, MAXPATHLEN);
f2052513 1294 LOCAL_ARRAY(char, url, MAX_URL);
090089c4 1295 StoreEntry *e = NULL;
090089c4 1296 time_t expires;
1297 time_t timestamp;
f5469bce 1298 time_t lastmod;
1299 int scan1;
1300 int scan2;
1301 int scan3;
1302 int scan4;
3facc31e 1303 off_t size;
090089c4 1304 int sfileno = 0;
30a4f2a8 1305 int count;
f5469bce 1306 int x;
5608850b 1307 struct _rebuild_dir *d;
1308 struct _rebuild_dir **D;
429fdbec 1309 int used; /* is swapfile already in use? */
1310 int newer; /* is the log entry newer than current entry? */
1311
30a4f2a8 1312 /* load a number of objects per invocation */
5608850b 1313 if ((d = RB->rebuild_dir) == NULL) {
3e347513 1314 storeRebuiltFromDisk(RB);
1315 return;
5608850b 1316 }
1317 for (count = 0; count < d->speed; count++) {
1318 if (fgets(RB->line_in, RB->line_in_sz, d->log) == NULL) {
a3d5953d 1319 debug(20, 1) ("Done reading Cache Dir #%d swap log\n", d->dirn);
3e347513 1320 fclose(d->log);
1321 d->log = NULL;
1322 storeDirCloseTmpSwapLog(d->dirn);
1323 RB->rebuild_dir = d->next;
1324 safe_free(d);
1325 eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0);
1326 return;
48f44632 1327 }
5608850b 1328 if ((++RB->linecount & 0x3FFF) == 0)
a3d5953d 1329 debug(20, 1) (" %7d Lines read so far.\n", RB->linecount);
1330 debug(20, 9) ("line_in: %s", RB->line_in);
5608850b 1331 if (RB->line_in[0] == '\0')
1332 continue;
1333 if (RB->line_in[0] == '\n')
1334 continue;
1335 if (RB->line_in[0] == '#')
1336 continue;
f5469bce 1337 url[0] = '\0';
f5469bce 1338 sfileno = 0;
1339 scan1 = 0;
1340 scan2 = 0;
30a4f2a8 1341 scan3 = 0;
f5469bce 1342 scan4 = 0;
5608850b 1343 x = sscanf(RB->line_in, "%x %x %x %x %d %s",
0be3ec60 1344 &sfileno, /* swap_file_number */
1345 &scan1, /* timestamp */
1346 &scan2, /* expires */
1347 &scan3, /* last modified */
1348 &scan4, /* size */
1349 url); /* url */
429fdbec 1350 if (x < 1)
30a4f2a8 1351 continue;
429fdbec 1352 storeSwapFullPath(sfileno, swapfile);
9e4ad609 1353 if (x != 6)
429fdbec 1354 continue;
0a0bf5db 1355 if (sfileno < 0)
1356 continue;
c932b107 1357 sfileno = storeDirProperFileno(d->dirn, sfileno);
f5469bce 1358 timestamp = (time_t) scan1;
1359 expires = (time_t) scan2;
1360 lastmod = (time_t) scan3;
1361 size = (off_t) scan4;
30a4f2a8 1362
429fdbec 1363 e = storeGet(url);
1364 used = storeDirMapBitTest(sfileno);
1365 /* If this URL already exists in the cache, does the swap log
1366 * appear to have a newer entry? Compare 'timestamp' from the
1367 * swap log to e->lastref. Note, we can't compare e->timestamp
1368 * because it is the Date: header from the HTTP reply and
1369 * doesn't really tell us when the object was added to the
1370 * cache. */
1371 newer = e ? (timestamp > e->lastref ? 1 : 0) : 0;
1372 if (used && !newer) {
1373 /* log entry is old, ignore it */
1374 RB->clashcount++;
1375 continue;
1376 } else if (used && e && e->swap_file_number == sfileno) {
1377 /* swapfile taken, same URL, newer, update meta */
1378 e->lastref = timestamp;
1379 e->timestamp = timestamp;
1380 e->expires = expires;
1381 e->lastmod = lastmod;
1382 continue;
1383 } else if (used) {
1384 /* swapfile in use, not by this URL, log entry is newer */
1385 /* This is sorta bad: the log entry should NOT be newer at this
1386 * point. If the log is dirty, the filesize check should have
1387 * caught this. If the log is clean, there should never be a
1388 * newer entry. */
a3d5953d 1389 debug(20, 1) ("WARNING: newer swaplog entry for fileno %08X\n",
30a4f2a8 1390 sfileno);
429fdbec 1391 /* I'm tempted to remove the swapfile here just to be safe,
1392 * but there is a bad race condition in the NOVM version if
1393 * the swapfile has recently been opened for writing, but
1394 * not yet opened for reading. Because we can't map
1395 * swapfiles back to StoreEntrys, we don't know the state
1396 * of the entry using that file. */
ccbc48f5 1397 /* We'll assume the existing entry is valid, probably because
1398 were in a slow rebuild and the the swap file number got taken
1399 and the validation procedure hasn't run. */
1400 assert(RB->need_to_validate);
5608850b 1401 RB->clashcount++;
30a4f2a8 1402 continue;
429fdbec 1403 } else if (e) {
1404 /* URL already exists, this swapfile not being used */
1405 /* junk old, load new */
1406 storeRelease(e); /* release old entry */
1407 RB->dupcount++;
1408 } else {
1409 /* URL doesnt exist, swapfile not in use */
1410 /* load new */
1411 (void) 0;
30a4f2a8 1412 }
1413 /* update store_swap_size */
c932b107 1414 storeDirUpdateSwapSize(sfileno, size, 1);
5608850b 1415 RB->objcount++;
f5469bce 1416 e = storeAddDiskRestore(url,
30a4f2a8 1417 sfileno,
1418 (int) size,
1419 expires,
f5469bce 1420 timestamp,
311ea387 1421 lastmod,
1422 d->clean);
5608850b 1423 storeDirSwapLog(e);
a528eb87 1424 HTTPCacheInfo->proto_newobject(HTTPCacheInfo,
1ac9bc38 1425 urlParseProtocol(url),
30a4f2a8 1426 (int) size,
1427 TRUE);
c943f331 1428 }
5608850b 1429 RB->rebuild_dir = d->next;
76fefb77 1430 for (D = &RB->rebuild_dir; *D; D = &(*D)->next);
5608850b 1431 *D = d;
1432 d->next = NULL;
1433 eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0);
3facc31e 1434}
1435
0a0bf5db 1436static void
1437storeCleanup(void *data)
1438{
1439 static storeCleanList *list = NULL;
1440 storeCleanList *curr;
1441 static int bucketnum = -1;
1442 static int validnum = 0;
1443 StoreEntry *e;
1444 hash_link *link_ptr = NULL;
1445 if (list == NULL) {
1446 if (++bucketnum >= store_buckets) {
a3d5953d 1447 debug(20, 1) (" Completed Validation Procedure\n");
1448 debug(20, 1) (" Validated %d Entries\n", validnum);
f182d1c5 1449 store_rebuilding = 0;
0a0bf5db 1450 return;
1451 }
1452 link_ptr = hash_get_bucket(store_table, bucketnum);
1453 for (; link_ptr; link_ptr = link_ptr->next) {
1454 e = (StoreEntry *) link_ptr;
35ac11a3 1455 if (BIT_TEST(e->flag, ENTRY_VALIDATED))
1456 continue;
1457 if (BIT_TEST(e->flag, RELEASE_REQUEST))
1458 continue;
311ea387 1459 curr = xcalloc(1, sizeof(storeCleanList));
0a0bf5db 1460 curr->key = xstrdup(e->key);
1461 curr->next = list;
1462 list = curr;
1463 }
1464 }
1465 if (list == NULL) {
1466 eventAdd("storeCleanup", storeCleanup, NULL, 0);
1467 return;
1468 }
1469 curr = list;
1470 list = list->next;
1471 e = (StoreEntry *) hash_lookup(store_table, curr->key);
35ac11a3 1472 if (e != NULL) {
1473 assert(!BIT_TEST(e->flag, ENTRY_VALIDATED));
127d93d9 1474 storeLockObject(e);
3e347513 1475 storeValidate(e, storeCleanupComplete, e);
a3d5953d 1476 if ((++validnum & 0xFFF) == 0)
1477 debug(20, 1) (" %7d Entries Validated so far.\n", validnum);
35ac11a3 1478 assert(validnum <= meta_data.store_entries);
95c4b18f 1479 }
0a0bf5db 1480 xfree(curr->key);
1481 xfree(curr);
1482 eventAdd("storeCleanup", storeCleanup, NULL, 0);
1483}
1484
0a0bf5db 1485static void
35ac11a3 1486storeCleanupComplete(void *data)
0a0bf5db 1487{
1488 StoreEntry *e = data;
35ac11a3 1489 storeUnlockObject(e);
0a0bf5db 1490 if (!BIT_TEST(e->flag, ENTRY_VALIDATED))
1491 storeRelease(e);
1492}
1493
1494static void
1495storeValidate(StoreEntry * e, VCB callback, void *callback_data)
1496{
1497 valid_ctrl_t *ctrlp;
1498 char *path;
1499 struct stat *sb;
606bd3b5 1500#if !USE_ASYNC_IO
95c4b18f 1501 int x;
606bd3b5 1502#endif
d7b78fbb 1503 assert(!BIT_TEST(e->flag, ENTRY_VALIDATED));
0a0bf5db 1504 if (e->swap_file_number < 0) {
1505 BIT_RESET(e->flag, ENTRY_VALIDATED);
35ac11a3 1506 callback(callback_data);
0a0bf5db 1507 return;
1508 }
1509 path = storeSwapFullPath(e->swap_file_number, NULL);
1510 sb = xmalloc(sizeof(struct stat));
1511 ctrlp = xmalloc(sizeof(valid_ctrl_t));
1512 ctrlp->sb = sb;
1513 ctrlp->e = e;
1514 ctrlp->callback = callback;
1515 ctrlp->callback_data = callback_data;
1516#if USE_ASYNC_IO
1517 aioStat(path, sb, storeValidateComplete, ctrlp);
1518#else
95c4b18f 1519 /* When evaluating the actual arguments in a function call, the order
1520 * in which the arguments and the function expression are evaluated is
1521 * not specified; */
1522 x = stat(path, sb);
1523 storeValidateComplete(ctrlp, x, errno);
0a0bf5db 1524#endif
1525 return;
1526}
1527
1528static void
1529storeValidateComplete(void *data, int retcode, int errcode)
1530{
1531 valid_ctrl_t *ctrlp = data;
1532 struct stat *sb = ctrlp->sb;
1533 StoreEntry *e = ctrlp->e;
1534 char *path;
1535 if (retcode < 0 && errcode == EWOULDBLOCK) {
1536 path = storeSwapFullPath(e->swap_file_number, NULL);
1537 retcode = stat(path, sb);
1538 }
9e4ad609 1539 if (retcode < 0 || sb->st_size == 0 || sb->st_size != e->object_len) {
0a0bf5db 1540 BIT_RESET(e->flag, ENTRY_VALIDATED);
9e4ad609 1541 } else {
0a0bf5db 1542 BIT_SET(e->flag, ENTRY_VALIDATED);
9e4ad609 1543 storeDirMapBitSet(e->swap_file_number);
1544 }
0a0bf5db 1545 errno = errcode;
35ac11a3 1546 ctrlp->callback(ctrlp->callback_data);
0a0bf5db 1547 xfree(sb);
1548 xfree(ctrlp);
1549}
1550
3facc31e 1551/* meta data recreated from disk image in swap directory */
b8d8561b 1552static void
5608850b 1553storeRebuiltFromDisk(struct storeRebuildState *data)
3facc31e 1554{
1555 time_t r;
1556 time_t stop;
97c03d3c 1557 stop = squid_curtime;
3facc31e 1558 r = stop - data->start;
a3d5953d 1559 debug(20, 1) ("Finished rebuilding storage from disk image.\n");
1560 debug(20, 1) (" %7d Lines read from previous logfile.\n", data->linecount);
1561 debug(20, 1) (" %7d Objects loaded.\n", data->objcount);
1562 debug(20, 1) (" %7d Objects expired.\n", data->expcount);
1563 debug(20, 1) (" %7d Duplicate URLs purged.\n", data->dupcount);
1564 debug(20, 1) (" %7d Swapfile clashes avoided.\n", data->clashcount);
1565 debug(20, 1) (" Took %d seconds (%6.1lf objects/sec).\n",
3facc31e 1566 r > 0 ? r : 0, (double) data->objcount / (r > 0 ? r : 1));
a3d5953d 1567 debug(20, 1) (" store_swap_size = %dk\n", store_swap_size);
f182d1c5 1568 if (data->need_to_validate) {
a3d5953d 1569 debug(20, 1) ("Beginning Validation Procedure\n");
0a0bf5db 1570 eventAdd("storeCleanup", storeCleanup, NULL, 0);
f182d1c5 1571 } else {
a3d5953d 1572 store_rebuilding = 0;
0a0bf5db 1573 }
f182d1c5 1574 safe_free(data->line_in);
1575 safe_free(data);
090089c4 1576}
1577
24382924 1578static void
b8d8561b 1579storeStartRebuildFromDisk(void)
3facc31e 1580{
3facc31e 1581 int i;
5608850b 1582 struct storeRebuildState *RB;
1583 struct _rebuild_dir *d;
1584 FILE *fp;
1585 int clean;
1586 RB = xcalloc(1, sizeof(struct storeRebuildState));
1587 RB->start = squid_curtime;
1588 for (i = 0; i < ncache_dirs; i++) {
1589 fp = storeDirOpenTmpSwapLog(i, &clean);
1590 if (fp == NULL)
1591 continue;
1592 d = xcalloc(1, sizeof(struct _rebuild_dir));
1593 d->dirn = i;
1594 d->log = fp;
1595 d->clean = clean;
1596 d->speed = opt_foreground_rebuild ? 1 << 30 : 50;
1597 d->next = RB->rebuild_dir;
1598 RB->rebuild_dir = d;
1599 if (!clean)
f182d1c5 1600 RB->need_to_validate = 1;
a3d5953d 1601 debug(20, 1) ("Rebuilding storage in Cache Dir #%d (%s)\n",
5608850b 1602 i, clean ? "CLEAN" : "DIRTY");
1603 }
1604 RB->line_in_sz = 4096;
1605 RB->line_in = xcalloc(1, RB->line_in_sz);
30a4f2a8 1606 if (opt_foreground_rebuild) {
5608850b 1607 storeDoRebuildFromDisk(RB);
30a4f2a8 1608 } else {
5608850b 1609 eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0);
30a4f2a8 1610 }
3facc31e 1611}
090089c4 1612
b8d8561b 1613static int
1614storeCheckSwapable(StoreEntry * e)
6602e70e 1615{
a7e59001 1616 if (e->method != METHOD_GET) {
a3d5953d 1617 debug(20, 2) ("storeCheckSwapable: NO: non-GET method\n");
1c481e00 1618 } else if (!BIT_TEST(e->flag, ENTRY_CACHABLE)) {
a3d5953d 1619 debug(20, 2) ("storeCheckSwapable: NO: not cachable\n");
6602e70e 1620 } else if (BIT_TEST(e->flag, RELEASE_REQUEST)) {
a3d5953d 1621 debug(20, 2) ("storeCheckSwapable: NO: release requested\n");
6602e70e 1622 } else if (!storeEntryValidLength(e)) {
a3d5953d 1623 debug(20, 2) ("storeCheckSwapable: NO: wrong content-length\n");
79b5cc5f 1624 } else if (BIT_TEST(e->flag, ENTRY_NEGCACHED)) {
a3d5953d 1625 debug(20, 2) ("storeCheckSwapable: NO: negative cached\n");
3e98df20 1626 return 0; /* avoid release call below */
38792624 1627 } else if (e->mem_obj->e_current_len > Config.Store.maxObjectSize) {
a3d5953d 1628 debug(20, 2) ("storeCheckSwapable: NO: too big\n");
d4432957 1629 } else {
6602e70e 1630 return 1;
d4432957 1631 }
6602e70e 1632
2daae136 1633 storeReleaseRequest(e);
1c481e00 1634 BIT_RESET(e->flag, ENTRY_CACHABLE);
6602e70e 1635 return 0;
1636}
1637
1638
090089c4 1639
1640/* Complete transfer into the local cache. */
b8d8561b 1641void
1642storeComplete(StoreEntry * e)
090089c4 1643{
a3d5953d 1644 debug(20, 3) ("storeComplete: '%s'\n", e->key);
22e4fa85 1645 e->object_len = e->mem_obj->e_current_len;
b8de7ebe 1646 e->lastref = squid_curtime;
234967c9 1647 e->store_status = STORE_OK;
090089c4 1648 storeSetMemStatus(e, IN_MEMORY);
1649 e->swap_status = NO_SWAP;
3a1c3e2f 1650 InvokeHandlers(e);
aadcfbad 1651 if (BIT_TEST(e->flag, RELEASE_REQUEST))
090089c4 1652 storeRelease(e);
12f6386d 1653 else if (storeCheckSwapable(e))
1654 storeSwapOutStart(e);
1655 else {
1656 requestUnlink(e->mem_obj->request);
1657 e->mem_obj->request = NULL;
1658 }
090089c4 1659}
1660
1661/*
1662 * Fetch aborted. Tell all clients to go home. Negatively cache
1663 * abort message, freeing the data for this object
1664 */
b8d8561b 1665void
0ee4272b 1666storeAbort(StoreEntry * e, const char *msg)
090089c4 1667{
95d659f0 1668 LOCAL_ARRAY(char, mime_hdr, 300);
caebbe00 1669 char *abort_msg;
3e98df20 1670 MemObject *mem = e->mem_obj;
8f39b81d 1671 assert(e->store_status == STORE_PENDING);
1672 assert(mem != NULL);
a3d5953d 1673 /*assert(ping_status != PING_WAITING); */
1674 debug(20, 6) ("storeAbort: '%s'\n", e->key);
79b5cc5f 1675 storeNegativeCache(e);
234967c9 1676 e->store_status = STORE_ABORTED;
090089c4 1677 storeSetMemStatus(e, IN_MEMORY);
1678 /* No DISK swap for negative cached object */
1679 e->swap_status = NO_SWAP;
b8de7ebe 1680 e->lastref = squid_curtime;
95c4b18f 1681 storeLockObject(e);
090089c4 1682 /* Count bytes faulted through cache but not moved to disk */
a528eb87 1683 HTTPCacheInfo->proto_touchobject(HTTPCacheInfo,
3e98df20 1684 mem->request ? mem->request->protocol : PROTO_NONE,
1685 mem->e_current_len);
090089c4 1686 if (msg) {
caebbe00 1687 abort_msg = get_free_8k_page();
1688 strcpy(abort_msg, "HTTP/1.0 400 Cache Detected Error\r\n");
1689 mk_mime_hdr(mime_hdr,
1690 "text/html",
1691 strlen(msg),
1692 (time_t) Config.negativeTtl,
1693 squid_curtime);
1694 strcat(abort_msg, mime_hdr);
aadcfbad 1695 strcat(abort_msg, "\r\n");
caebbe00 1696 strncat(abort_msg, msg, 8191 - strlen(abort_msg));
090089c4 1697 storeAppend(e, abort_msg, strlen(abort_msg));
a1e47288 1698 safe_free(mem->e_abort_msg);
3e98df20 1699 mem->e_abort_msg = xstrdup(abort_msg);
090089c4 1700 /* Set up object for negative caching */
1701 BIT_SET(e->flag, ABORT_MSG_PENDING);
caebbe00 1702 put_free_8k_page(abort_msg);
090089c4 1703 }
1704 /* We assign an object length here--The only other place we assign the
1705 * object length is in storeComplete() */
3e98df20 1706 e->object_len = mem->e_current_len;
bfcaf585 1707 if (mem->abort.callback) {
1708 mem->abort.callback(mem->abort.data);
1709 mem->abort.callback = NULL;
1710 }
090089c4 1711 InvokeHandlers(e);
090089c4 1712 storeUnlockObject(e);
85b701ed 1713 return;
090089c4 1714}
1715
090089c4 1716/* get the first in memory object entry in the storage */
24382924 1717static StoreEntry *
b8d8561b 1718storeGetInMemFirst(void)
090089c4 1719{
1720 hash_link *first = NULL;
c3c5b744 1721 first = hash_first(in_mem_table);
090089c4 1722 return (first ? ((StoreEntry *) first->item) : NULL);
1723}
1724
1725
1726/* get the next in memory object entry in the storage for a given
1727 * search pointer */
24382924 1728static StoreEntry *
b8d8561b 1729storeGetInMemNext(void)
090089c4 1730{
1731 hash_link *next = NULL;
c3c5b744 1732 next = hash_next(in_mem_table);
090089c4 1733 return (next ? ((StoreEntry *) next->item) : NULL);
1734}
1735
1736/* get the first entry in the storage */
b8d8561b 1737StoreEntry *
1738storeGetFirst(void)
090089c4 1739{
c3c5b744 1740 return ((StoreEntry *) hash_first(store_table));
090089c4 1741}
1742
1743
1744/* get the next entry in the storage for a given search pointer */
b8d8561b 1745StoreEntry *
1746storeGetNext(void)
090089c4 1747{
c3c5b744 1748 return ((StoreEntry *) hash_next(store_table));
090089c4 1749}
1750
620da955 1751/* free up all ttl-expired objects */
48f44632 1752void
1753storePurgeOld(void *unused)
090089c4 1754{
1755 StoreEntry *e = NULL;
090089c4 1756 int n = 0;
620da955 1757 int count = 0;
48f44632 1758 /* reschedule */
1759 eventAdd("storePurgeOld", storePurgeOld, NULL, Config.cleanRate);
090089c4 1760 for (e = storeGetFirst(); e; e = storeGetNext()) {
0a5b9b32 1761 if ((++n & 0xFF) == 0) {
2abe4822 1762 if (shutdown_pending || reconfigure_pending)
0a5b9b32 1763 break;
1764 }
090089c4 1765 if ((n & 0xFFF) == 0)
a3d5953d 1766 debug(20, 2) ("storeWalkThrough: %7d objects so far.\n", n);
429fdbec 1767 if (storeCheckExpired(e, 1))
cc61958c 1768 count += storeRelease(e);
090089c4 1769 }
a3d5953d 1770 debug(20, 0) ("storePurgeOld: Removed %d objects\n", count);
090089c4 1771}
1772
1773
090089c4 1774/* Clear Memory storage to accommodate the given object len */
38792624 1775static void
d4432957 1776storeGetMemSpace(int size)
090089c4 1777{
b32508fb 1778 StoreEntry *e = NULL;
1779 StoreEntry **list = NULL;
1780 int list_count = 0;
090089c4 1781 int n_expired = 0;
66c2848f 1782 int n_purged = 0;
579a45cd 1783 int n_released = 0;
3bbba102 1784 int n_locked = 0;
090089c4 1785 int i;
b32508fb 1786 static time_t last_warning = 0;
1787 static time_t last_check = 0;
e954773d 1788 int pages_needed;
090089c4 1789
b32508fb 1790 if (squid_curtime == last_check)
38792624 1791 return;
b32508fb 1792 last_check = squid_curtime;
e954773d 1793 pages_needed = (size / SM_PAGE_SIZE) + 1;
61cd203b 1794 if (sm_stats.n_pages_in_use + pages_needed < store_pages_high)
38792624 1795 return;
311ea387 1796 if (store_rebuilding)
38792624 1797 return;
a3d5953d 1798 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
090089c4 1799
579a45cd 1800 list = xcalloc(meta_data.mem_obj_count, sizeof(ipcache_entry *));
090089c4 1801 for (e = storeGetInMemFirst(); e; e = storeGetInMemNext()) {
579a45cd 1802 if (list_count == meta_data.mem_obj_count)
b32508fb 1803 break;
0a0bf5db 1804 if (storeEntryLocked(e))
1805 continue;
429fdbec 1806 if (storeCheckExpired(e, 0)) {
a3d5953d 1807 debug(20, 2) ("storeGetMemSpace: Expired: %s\n", e->url);
090089c4 1808 n_expired++;
090089c4 1809 storeRelease(e);
579a45cd 1810 } else if (storeCheckPurgeMem(e)) {
a3d5953d 1811 debug(20, 3) ("storeGetMemSpace: Adding '%s'\n", e->url);
579a45cd 1812 *(list + list_count) = e;
1813 list_count++;
5e79098a 1814 } else if (!storeEntryLocked(e)) {
a3d5953d 1815 debug(20, 3) ("storeGetMemSpace: Adding '%s'\n", e->url);
579a45cd 1816 *(list + list_count) = e;
1817 list_count++;
1818 } else {
1819 n_locked++;
090089c4 1820 }
090089c4 1821 }
a3d5953d 1822 debug(20, 5) ("storeGetMemSpace: Sorting LRU_list: %7d items\n", list_count);
b32508fb 1823 qsort((char *) list,
1824 list_count,
6e40f263 1825 sizeof(StoreEntry *),
582b6456 1826 (QS *) compareSize);
090089c4 1827
1828 /* Kick LRU out until we have enough memory space */
b32508fb 1829 for (i = 0; i < list_count; i++) {
e954773d 1830 if (sm_stats.n_pages_in_use + pages_needed < store_pages_low)
9d90e665 1831 break;
579a45cd 1832 e = *(list + i);
1833 if (storeCheckPurgeMem(e)) {
5e79098a 1834 storePurgeMem(e);
1835 n_purged++;
579a45cd 1836 } else if (!storeEntryLocked(e)) {
e954773d 1837 /* These will be neg-cached objects */
cc61958c 1838 n_released += storeRelease(e);
579a45cd 1839 } else {
85b701ed 1840 debug_trap("storeGetMemSpace: Bad Entry in LRU list");
579a45cd 1841 }
090089c4 1842 }
1843
579a45cd 1844 i = 3;
13c72789 1845 if (sm_stats.n_pages_in_use > store_pages_max) {
3bbba102 1846 if (squid_curtime - last_warning > 600) {
a3d5953d 1847 debug(20, 0) ("WARNING: Exceeded 'cache_mem' size (%dK > %dK)\n",
13c72789 1848 sm_stats.n_pages_in_use * 4, store_pages_max * 4);
b32508fb 1849 last_warning = squid_curtime;
a3d5953d 1850 debug(20, 0) ("Perhaps you should increase cache_mem?\n");
579a45cd 1851 i = 0;
090089c4 1852 }
090089c4 1853 }
a3d5953d 1854 debug(20, i) ("storeGetMemSpace stats:\n");
1855 debug(20, i) (" %6d objects locked in memory\n", n_locked);
1856 debug(20, i) (" %6d LRU candidates\n", list_count);
1857 debug(20, i) (" %6d were purged\n", n_purged);
1858 debug(20, i) (" %6d were released\n", n_released);
3d700ed6 1859 xfree(list);
090089c4 1860}
1861
b8d8561b 1862static int
1863compareSize(StoreEntry ** e1, StoreEntry ** e2)
090089c4 1864{
6e40f263 1865 if (!e1 || !e2)
1866 fatal_dump(NULL);
22e4fa85 1867 if ((*e1)->mem_obj->e_current_len > (*e2)->mem_obj->e_current_len)
090089c4 1868 return (1);
22e4fa85 1869 if ((*e1)->mem_obj->e_current_len < (*e2)->mem_obj->e_current_len)
090089c4 1870 return (-1);
090089c4 1871 return (0);
1872}
1873
b8d8561b 1874static int
1875compareLastRef(StoreEntry ** e1, StoreEntry ** e2)
090089c4 1876{
1877 if (!e1 || !e2)
1878 fatal_dump(NULL);
090089c4 1879 if ((*e1)->lastref > (*e2)->lastref)
1880 return (1);
090089c4 1881 if ((*e1)->lastref < (*e2)->lastref)
1882 return (-1);
090089c4 1883 return (0);
1884}
1885
cb6ae8c3 1886static int
d3c12084 1887compareBucketOrder(struct _bucketOrder *a, struct _bucketOrder *b)
cb6ae8c3 1888{
d3c12084 1889 return a->index - b->index;
cb6ae8c3 1890}
1891
090089c4 1892/* returns the bucket number to work on,
1893 * pointer to next bucket after each calling
1894 */
24382924 1895static unsigned int
b8d8561b 1896storeGetBucketNum(void)
090089c4 1897{
1898 static unsigned int bucket = 0;
66cedb85 1899 if (bucket >= store_buckets)
090089c4 1900 bucket = 0;
1901 return (bucket++);
1902}
1903
66cedb85 1904#define SWAP_MAX_HELP (store_buckets/2)
090089c4 1905
1906/* The maximum objects to scan for maintain storage space */
0a0bf5db 1907#define SWAP_LRUSCAN_COUNT 1024
1908#define SWAP_LRU_REMOVE_COUNT 64
090089c4 1909
1910/* Clear Swap storage to accommodate the given object len */
b8d8561b 1911int
1912storeGetSwapSpace(int size)
090089c4 1913{
1914 static int fReduceSwap = 0;
1915 static int swap_help = 0;
a1b0d7cf 1916 StoreEntry *e = NULL;
090089c4 1917 int scanned = 0;
1918 int removed = 0;
090089c4 1919 int locked = 0;
1920 int locked_size = 0;
a1b0d7cf 1921 int list_count = 0;
1922 int scan_count = 0;
1923 int max_list_count = SWAP_LRUSCAN_COUNT << 1;
090089c4 1924 int i;
a1b0d7cf 1925 StoreEntry **LRU_list;
090089c4 1926 hash_link *link_ptr = NULL, *next = NULL;
63017fae 1927 int kb_size = ((size + 1023) >> 10);
090089c4 1928
1929 if (store_swap_size + kb_size <= store_swap_low)
1930 fReduceSwap = 0;
1931 if (!fReduceSwap && (store_swap_size + kb_size <= store_swap_high)) {
1932 return 0;
1933 }
a3d5953d 1934 debug(20, 2) ("storeGetSwapSpace: Starting...\n");
090089c4 1935
1936 /* Set flag if swap size over high-water-mark */
1937 if (store_swap_size + kb_size > store_swap_high)
1938 fReduceSwap = 1;
1939
a3d5953d 1940 debug(20, 2) ("storeGetSwapSpace: Need %d bytes...\n", size);
090089c4 1941
a1b0d7cf 1942 LRU_list = xcalloc(max_list_count, sizeof(StoreEntry *));
1943 /* remove expired objects until recover enough or no expired objects */
66cedb85 1944 for (i = 0; i < store_buckets; i++) {
ec74cf5f 1945 link_ptr = hash_get_bucket(store_table, storeGetBucketNum());
090089c4 1946 if (link_ptr == NULL)
1947 continue;
429fdbec 1948 /* this for loop handles one bucket of hash table */
79b5cc5f 1949 for (; link_ptr; link_ptr = next) {
a1b0d7cf 1950 if (list_count == max_list_count)
1951 break;
090089c4 1952 scanned++;
1953 next = link_ptr->next;
1954 e = (StoreEntry *) link_ptr;
0a0bf5db 1955 if (!BIT_TEST(e->flag, ENTRY_VALIDATED))
1956 continue;
429fdbec 1957 if (storeCheckExpired(e, 0)) {
a3d5953d 1958 debug(20, 3) ("storeGetSwapSpace: Expired '%s'\n", e->url);
429fdbec 1959 storeRelease(e);
79b5cc5f 1960 } else if (!storeEntryLocked(e)) {
fedac7e5 1961 *(LRU_list + list_count) = e;
a1b0d7cf 1962 list_count++;
1963 scan_count++;
090089c4 1964 } else {
ff5731b1 1965 locked++;
1966 locked_size += e->mem_obj->e_current_len;
090089c4 1967 }
429fdbec 1968 } /* for, end of one bucket of hash table */
a1b0d7cf 1969 qsort((char *) LRU_list,
1970 list_count,
6e40f263 1971 sizeof(StoreEntry *),
582b6456 1972 (QS *) compareLastRef);
85b701ed 1973 if (list_count > SWAP_LRU_REMOVE_COUNT)
fedac7e5 1974 list_count = SWAP_LRU_REMOVE_COUNT; /* chop list */
a1b0d7cf 1975 if (scan_count > SWAP_LRUSCAN_COUNT)
090089c4 1976 break;
1977 } /* for */
1978
30a4f2a8 1979#ifdef LOTSA_DEBUGGING
090089c4 1980 /* end of candidate selection */
a3d5953d 1981 debug(20, 2) ("storeGetSwapSpace: Current Size: %7d kbytes\n",
30a4f2a8 1982 store_swap_size);
a3d5953d 1983 debug(20, 2) ("storeGetSwapSpace: High W Mark: %7d kbytes\n",
30a4f2a8 1984 store_swap_high);
a3d5953d 1985 debug(20, 2) ("storeGetSwapSpace: Low W Mark: %7d kbytes\n",
30a4f2a8 1986 store_swap_low);
a3d5953d 1987 debug(20, 2) ("storeGetSwapSpace: Entry count: %7d items\n",
30a4f2a8 1988 meta_data.store_entries);
a3d5953d 1989 debug(20, 2) ("storeGetSwapSpace: Visited: %7d buckets\n",
30a4f2a8 1990 i + 1);
a3d5953d 1991 debug(20, 2) ("storeGetSwapSpace: Scanned: %7d items\n",
30a4f2a8 1992 scanned);
a3d5953d 1993 debug(20, 2) ("storeGetSwapSpace: Expired: %7d items\n",
30a4f2a8 1994 expired);
a3d5953d 1995 debug(20, 2) ("storeGetSwapSpace: Locked: %7d items\n",
30a4f2a8 1996 locked);
a3d5953d 1997 debug(20, 2) ("storeGetSwapSpace: Locked Space: %7d bytes\n",
30a4f2a8 1998 locked_size);
a3d5953d 1999 debug(20, 2) ("storeGetSwapSpace: Scan in array: %7d bytes\n",
30a4f2a8 2000 scan_in_objs);
a3d5953d 2001 debug(20, 2) ("storeGetSwapSpace: LRU candidate: %7d items\n",
30a4f2a8 2002 LRU_list->index);
2003#endif /* LOTSA_DEBUGGING */
090089c4 2004
cc61958c 2005 for (i = 0; i < list_count; i++)
2006 removed += storeRelease(*(LRU_list + i));
a1b0d7cf 2007 if (store_swap_size + kb_size <= store_swap_low)
fedac7e5 2008 fReduceSwap = 0;
a3d5953d 2009 debug(20, 2) ("storeGetSwapSpace: After Freeing Size: %7d kbytes\n",
a1b0d7cf 2010 store_swap_size);
090089c4 2011 /* free the list */
a1b0d7cf 2012 safe_free(LRU_list);
090089c4 2013
2014 if ((store_swap_size + kb_size > store_swap_high)) {
63017fae 2015 i = 2;
090089c4 2016 if (++swap_help > SWAP_MAX_HELP) {
a3d5953d 2017 debug(20, 0) ("WARNING: Repeated failures to free up disk space!\n");
63017fae 2018 i = 0;
090089c4 2019 }
a3d5953d 2020 debug(20, i) ("storeGetSwapSpace: Disk usage is over high water mark\n");
2021 debug(20, i) ("--> store_swap_high = %d KB\n", store_swap_high);
2022 debug(20, i) ("--> store_swap_size = %d KB\n", store_swap_size);
2023 debug(20, i) ("--> asking for %d KB\n", kb_size);
090089c4 2024 } else {
2025 swap_help = 0;
2026 }
a3d5953d 2027 debug(20, 2) ("Removed %d objects\n", removed);
090089c4 2028 return 0;
2029}
2030
2031
2032/* release an object from a cache */
cc61958c 2033/* return number of objects released. */
b8d8561b 2034int
2035storeRelease(StoreEntry * e)
090089c4 2036{
cc61958c 2037 StoreEntry *hentry = NULL;
0ee4272b 2038 const char *hkey;
a3d5953d 2039 debug(20, 3) ("storeRelease: Releasing: '%s'\n", e->key);
090089c4 2040 /* If, for any reason we can't discard this object because of an
2041 * outstanding request, mark it for pending release */
2042 if (storeEntryLocked(e)) {
9174e204 2043 storeExpireNow(e);
a3d5953d 2044 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
2daae136 2045 storeReleaseRequest(e);
cc61958c 2046 return 0;
090089c4 2047 }
cc61958c 2048 /* check if coresponding HEAD object exists. */
73ddce89 2049 if (e->method == METHOD_GET) {
cc61958c 2050 hkey = storeGeneratePublicKey(e->url, METHOD_HEAD);
2051 if ((hentry = (StoreEntry *) hash_lookup(store_table, hkey)))
38792624 2052 storeExpireNow(hentry);
090089c4 2053 }
311ea387 2054 if (store_rebuilding) {
a3d5953d 2055 debug(20, 2) ("storeRelease: Delaying release until store is rebuilt: '%s'\n",
30a4f2a8 2056 e->key ? e->key : e->url ? e->url : "NO URL");
2057 storeExpireNow(e);
2058 storeSetPrivateKey(e);
35ac11a3 2059 BIT_SET(e->flag, RELEASE_REQUEST);
cc61958c 2060 return 0;
30a4f2a8 2061 }
090089c4 2062 if (e->swap_status == SWAP_OK && (e->swap_file_number > -1)) {
9e4ad609 2063 if (BIT_TEST(e->flag, ENTRY_VALIDATED))
2064 storePutUnusedFileno(e->swap_file_number);
c932b107 2065 storeDirUpdateSwapSize(e->swap_file_number, e->object_len, -1);
090089c4 2066 e->swap_file_number = -1;
a528eb87 2067 HTTPCacheInfo->proto_purgeobject(HTTPCacheInfo,
1ac9bc38 2068 urlParseProtocol(e->url),
30a4f2a8 2069 e->object_len);
090089c4 2070 }
f7a5493b 2071 storeHashDelete(e);
147d3115 2072 storeLog(STORE_LOG_RELEASE, e);
30a4f2a8 2073 destroy_StoreEntry(e);
cc61958c 2074 return 1;
090089c4 2075}
2076
090089c4 2077/* return 1 if a store entry is locked */
24382924 2078static int
fe4e214f 2079storeEntryLocked(const StoreEntry * e)
090089c4 2080{
30a4f2a8 2081 if (e->lock_count)
2082 return 1;
2083 if (e->swap_status == SWAPPING_OUT)
2084 return 1;
2085 if (e->mem_status == SWAPPING_IN)
2086 return 1;
a1e47288 2087 if (e->store_status == STORE_PENDING)
2088 return 1;
30a4f2a8 2089 return 0;
090089c4 2090}
2091
b8d8561b 2092static int
fe4e214f 2093storeCopy(const StoreEntry * e, int stateoffset, int maxSize, char *buf, int *size)
090089c4 2094{
429fdbec 2095 MemObject *mem = e->mem_obj;
d89d1fb6 2096 size_t s;
2097 assert(stateoffset >= mem->e_lowest_offset);
2098 s = memCopy(mem->data, stateoffset, buf, maxSize);
429fdbec 2099 return *size = s;
090089c4 2100}
2101
2102/* check if there is any client waiting for this object at all */
2103/* return 1 if there is at least one client */
b8d8561b 2104int
fe4e214f 2105storeClientWaiting(const StoreEntry * e)
090089c4 2106{
2107 int i;
6e40f263 2108 MemObject *mem = e->mem_obj;
52d4522b 2109 if (mem->clients) {
2110 for (i = 0; i < mem->nclients; i++) {
382d851a 2111 if (mem->clients[i].callback_data != NULL)
090089c4 2112 return 1;
2113 }
2114 }
2115 return 0;
2116}
2117
b8d8561b 2118static int
382d851a 2119storeClientListSearch(const MemObject * mem, void *data)
090089c4 2120{
2121 int i;
52d4522b 2122 if (mem->clients) {
2123 for (i = 0; i < mem->nclients; i++) {
382d851a 2124 if (mem->clients[i].callback_data != data)
6e40f263 2125 continue;
090089c4 2126 return i;
6e40f263 2127 }
090089c4 2128 }
2129 return -1;
2130}
2131
2132/* add client with fd to client list */
52d4522b 2133int
fe96bbe6 2134storeClientListAdd(StoreEntry * e, void *data)
090089c4 2135{
2136 int i;
6e40f263 2137 MemObject *mem = e->mem_obj;
52d4522b 2138 struct _store_client *oldlist = NULL;
6e40f263 2139 int oldsize;
f2ea1762 2140 assert(mem != NULL);
090089c4 2141 /* look for empty slot */
52d4522b 2142 if (mem->clients == NULL) {
2143 mem->nclients = MIN_CLIENT;
d3a743b1 2144 mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client));
090089c4 2145 }
52d4522b 2146 for (i = 0; i < mem->nclients; i++) {
382d851a 2147 if (mem->clients[i].callback_data == data)
429fdbec 2148 return i; /* its already here */
382d851a 2149 if (mem->clients[i].callback_data == NULL)
6e40f263 2150 break;
2151 }
52d4522b 2152 if (i == mem->nclients) {
a3d5953d 2153 debug(20, 3) ("storeClientListAdd: Growing clients for '%s'\n", e->url);
52d4522b 2154 oldlist = mem->clients;
2155 oldsize = mem->nclients;
2156 mem->nclients <<= 1;
d3a743b1 2157 mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client));
6e40f263 2158 for (i = 0; i < oldsize; i++)
52d4522b 2159 mem->clients[i] = oldlist[i];
6e40f263 2160 safe_free(oldlist);
52d4522b 2161 i = oldsize;
6e40f263 2162 }
382d851a 2163 mem->clients[i].callback_data = data;
fe96bbe6 2164 mem->clients[i].seen_offset = 0;
2165 mem->clients[i].copy_offset = 0;
52d4522b 2166 return i;
090089c4 2167}
2168
2169/* same to storeCopy but also register client fd and last requested offset
2170 * for each client */
d89d1fb6 2171void
38792624 2172storeClientCopy(StoreEntry * e,
fe96bbe6 2173 off_t seen_offset,
2174 off_t copy_offset,
d89d1fb6 2175 size_t size,
38792624 2176 char *buf,
d89d1fb6 2177 STCB * callback,
382d851a 2178 void *data)
38792624 2179{
2180 int ci;
d89d1fb6 2181 size_t sz;
6e40f263 2182 MemObject *mem = e->mem_obj;
fe96bbe6 2183 struct _store_client *sc;
d4b1b930 2184 static int recurse_detect = 0;
fe96bbe6 2185 assert(seen_offset <= mem->e_current_len);
cb36fc2f 2186 assert(copy_offset >= mem->e_lowest_offset);
f182d1c5 2187 assert(recurse_detect < 3); /* could == 1 for IMS not modified's */
d89d1fb6 2188 if ((ci = storeClientListSearch(mem, data)) < 0)
2189 fatal_dump("storeClientCopy: Unregistered client");
fe96bbe6 2190 sc = &mem->clients[ci];
2191 sc->copy_offset = copy_offset;
2192 sc->seen_offset = seen_offset;
2193 if (seen_offset == mem->e_current_len) {
2194 /* client has already seen this, wait for more */
2195 sc->callback = callback;
2196 sc->copy_buf = buf;
2197 sc->copy_size = size;
2198 sc->copy_offset = copy_offset;
d89d1fb6 2199 return;
090089c4 2200 }
38f903c4 2201 if (BIT_TEST(e->flag, DELETE_BEHIND))
2202 storeDeleteBehind(e);
fe96bbe6 2203 sz = memCopy(mem->data, copy_offset, buf, size);
f182d1c5 2204 recurse_detect++;
d89d1fb6 2205 callback(data, buf, sz);
f182d1c5 2206 recurse_detect--;
090089c4 2207}
2208
24382924 2209static int
fe4e214f 2210storeEntryValidLength(const StoreEntry * e)
6602e70e 2211{
ffe4a367 2212 int diff;
2285407f 2213 int hdr_sz;
2214 int content_length;
ffe4a367 2215
2285407f 2216 if (e->mem_obj == NULL)
ffe4a367 2217 fatal_dump("storeEntryValidLength: NULL mem_obj");
2218
2285407f 2219 hdr_sz = e->mem_obj->reply->hdr_sz;
147d3115 2220 content_length = e->mem_obj->reply->content_length;
2285407f 2221
a3d5953d 2222 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", e->key);
2223 debug(20, 5) ("storeEntryValidLength: object_len = %d\n", e->object_len);
2224 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n", hdr_sz);
2225 debug(20, 5) ("storeEntryValidLength: content_length = %d\n", content_length);
ffe4a367 2226
2285407f 2227 if (content_length == 0) {
a3d5953d 2228 debug(20, 5) ("storeEntryValidLength: Zero content length; assume valid; '%s'\n",
ffe4a367 2229 e->key);
2230 return 1;
2231 }
2285407f 2232 if (hdr_sz == 0) {
a3d5953d 2233 debug(20, 5) ("storeEntryValidLength: Zero header size; assume valid; '%s'\n",
ffe4a367 2234 e->key);
2235 return 1;
2236 }
2285407f 2237 diff = hdr_sz + content_length - e->object_len;
ffe4a367 2238 if (diff != 0) {
a3d5953d 2239 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
ffe4a367 2240 diff < 0 ? -diff : diff,
2241 diff < 0 ? "small" : "big",
2242 e->key);
2243 return 0;
2244 }
2245 return 1;
2246}
6602e70e 2247
d3c12084 2248#if HAVE_RANDOM
2249#define squid_random random
2250#elif HAVE_LRAND48
2251#define squid_random lrand48
2252#else
2253#define squid_random rand
2254#endif
2255
63017fae 2256static void
2257storeRandomizeBuckets(void)
2258{
2259 int i;
d3c12084 2260 struct _bucketOrder *b;
63017fae 2261 if (MaintBucketsOrder == NULL)
d3c12084 2262 MaintBucketsOrder = xcalloc(store_buckets, sizeof(struct _bucketOrder));
2263 for (i = 0; i < store_buckets; i++) {
2264 b = MaintBucketsOrder + i;
2265 b->bucket = (unsigned int) i;
2266 b->index = (int) squid_random();
2267 }
63017fae 2268 qsort((char *) MaintBucketsOrder,
2269 store_buckets,
d3c12084 2270 sizeof(struct _bucketOrder),
582b6456 2271 (QS *) compareBucketOrder);
63017fae 2272}
2273
66cedb85 2274static void
2275storeInitHashValues(void)
2276{
2277 int i;
a7e59001 2278 /* Calculate size of hash table (maximum currently 64k buckets). */
38792624 2279 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
a3d5953d 2280 debug(20, 1) ("Swap maxSize %d kB, estimated %d objects\n",
66cedb85 2281 Config.Swap.maxSize, i);
38792624 2282 i /= Config.Store.objectsPerBucket;
a3d5953d 2283 debug(20, 1) ("Target number of buckets: %d\n", i);
66cedb85 2284 /* ideally the full scan period should be configurable, for the
2285 * moment it remains at approximately 24 hours. */
2286 if (i < 8192)
2287 store_buckets = 7951, store_maintain_rate = 10;
2288 else if (i < 12288)
2289 store_buckets = 12149, store_maintain_rate = 7;
2290 else if (i < 16384)
2291 store_buckets = 16231, store_maintain_rate = 5;
2292 else if (i < 32768)
2293 store_buckets = 33493, store_maintain_rate = 2;
2294 else
2295 store_buckets = 65357, store_maintain_rate = 1;
2296 store_maintain_buckets = 1;
63017fae 2297 storeRandomizeBuckets();
a3d5953d 2298 debug(20, 1) ("Using %d Store buckets, maintain %d bucket%s every %d second%s\n",
66cedb85 2299 store_buckets,
2300 store_maintain_buckets,
2301 store_maintain_buckets == 1 ? null_string : "s",
2302 store_maintain_rate,
2303 store_maintain_rate == 1 ? null_string : "s");
2304}
2305
b8d8561b 2306void
2307storeInit(void)
c943f331 2308{
234967c9 2309 char *fname = NULL;
66cedb85 2310 storeInitHashValues();
aadcfbad 2311 storeCreateHashTable(urlcmp);
b6f794d6 2312 if (strcmp((fname = Config.Log.store), "none") == 0)
234967c9 2313 storelog_fd = -1;
2314 else
f17936ab 2315 storelog_fd = file_open(fname, O_WRONLY | O_CREAT, NULL, NULL);
234967c9 2316 if (storelog_fd < 0)
a3d5953d 2317 debug(20, 1) ("Store logging disabled\n");
641941c0 2318 if (ncache_dirs < 1)
2319 fatal("No cache_dir's specified in config file");
e7a22b88 2320 storeVerifySwapDirs();
2321 storeDirOpenSwapLogs();
b6f794d6 2322 if (!opt_zap_disk_store)
3facc31e 2323 storeStartRebuildFromDisk();
30a4f2a8 2324 else
311ea387 2325 store_rebuilding = 0;
b1c0cc67 2326}
c943f331 2327
b8d8561b 2328void
2329storeConfigure(void)
b1c0cc67 2330{
e954773d 2331 int store_mem_high = 0;
2332 int store_mem_low = 0;
b6f794d6 2333 store_mem_high = (long) (Config.Mem.maxSize / 100) *
2334 Config.Mem.highWaterMark;
2335 store_mem_low = (long) (Config.Mem.maxSize / 100) *
2336 Config.Mem.lowWaterMark;
090089c4 2337
b1c0cc67 2338 store_swap_high = (long) (((float) Config.Swap.maxSize *
2339 (float) Config.Swap.highWaterMark) / (float) 100);
2340 store_swap_low = (long) (((float) Config.Swap.maxSize *
2341 (float) Config.Swap.lowWaterMark) / (float) 100);
e954773d 2342
13c72789 2343 store_pages_max = Config.Mem.maxSize / SM_PAGE_SIZE;
e954773d 2344 store_pages_high = store_mem_high / SM_PAGE_SIZE;
2345 store_pages_low = store_mem_low / SM_PAGE_SIZE;
090089c4 2346}
2347
b8d8561b 2348int
0ee4272b 2349urlcmp(const char *url1, const char *url2)
090089c4 2350{
2351 if (!url1 || !url2)
6eb42cae 2352 fatal_dump("urlcmp: Got a NULL url pointer.");
090089c4 2353 return (strcmp(url1, url2));
2354}
2355
090089c4 2356/*
2357 * This routine is to be called by main loop in main.c.
2358 * It removes expired objects on only one bucket for each time called.
2359 * returns the number of objects removed
2360 *
2361 * This should get called 1/s from main().
2362 */
48f44632 2363void
2364storeMaintainSwapSpace(void *unused)
090089c4 2365{
30a4f2a8 2366 static time_t last_time = 0;
cb6ae8c3 2367 static int bucket_index = 0;
090089c4 2368 hash_link *link_ptr = NULL, *next = NULL;
2369 StoreEntry *e = NULL;
2370 int rm_obj = 0;
58104eab 2371 int scan_buckets = 0;
66cedb85 2372 int scan_obj = 0;
d3c12084 2373 static struct _bucketOrder *b;
090089c4 2374
48f44632 2375 eventAdd("storeMaintain", storeMaintainSwapSpace, NULL, 1);
30a4f2a8 2376 /* We can't delete objects while rebuilding swap */
311ea387 2377 if (store_rebuilding)
48f44632 2378 return;
30a4f2a8 2379
090089c4 2380 /* Purges expired objects, check one bucket on each calling */
66cedb85 2381 if (squid_curtime - last_time >= store_maintain_rate) {
58104eab 2382 for (;;) {
2383 if (scan_obj && scan_buckets >= store_maintain_buckets)
2384 break;
2385 if (++scan_buckets > 100)
2386 break;
66cedb85 2387 last_time = squid_curtime;
cb6ae8c3 2388 if (bucket_index >= store_buckets) {
2389 bucket_index = 0;
66cedb85 2390 scan_revolutions++;
a3d5953d 2391 debug(51, 1) ("Completed %d full expiration scans of store table\n",
66cedb85 2392 scan_revolutions);
d25ad3e1 2393 storeRandomizeBuckets();
66cedb85 2394 }
d3c12084 2395 b = MaintBucketsOrder + bucket_index++;
2396 next = hash_get_bucket(store_table, b->bucket);
cc61958c 2397 while ((link_ptr = next)) {
66cedb85 2398 scan_obj++;
2399 next = link_ptr->next;
2400 e = (StoreEntry *) link_ptr;
429fdbec 2401 if (!storeCheckExpired(e, 1))
66cedb85 2402 continue;
cc61958c 2403 rm_obj += storeRelease(e);
66cedb85 2404 }
090089c4 2405 }
2406 }
a3d5953d 2407 debug(51, rm_obj ? 2 : 9) ("Removed %d of %d objects from bucket %d\n",
d3c12084 2408 rm_obj, scan_obj, (int) b->bucket);
79b5cc5f 2409 /* Scan row of hash table each second and free storage if we're
2410 * over the high-water mark */
2411 storeGetSwapSpace(0);
090089c4 2412}
2413
090089c4 2414
2415/*
5608850b 2416 * storeWriteCleanLogs
090089c4 2417 *
2418 * Writes a "clean" swap log file from in-memory metadata.
2419 */
b8d8561b 2420int
5608850b 2421storeWriteCleanLogs(void)
090089c4 2422{
2423 StoreEntry *e = NULL;
9e6c462c 2424 int *fd;
5608850b 2425 char *line;
090089c4 2426 int n = 0;
2427 time_t start, stop, r;
10389b74 2428 struct stat sb;
9e6c462c 2429 char **cur;
2430 char **new;
2431 char **cln;
5608850b 2432 int dirn;
30a4f2a8 2433 if (store_rebuilding) {
a3d5953d 2434 debug(20, 1) ("Not currently OK to rewrite swap log.\n");
2435 debug(20, 1) ("storeWriteCleanLogs: Operation aborted.\n");
090089c4 2436 return 0;
2437 }
a3d5953d 2438 debug(20, 1) ("storeWriteCleanLogs: Starting...\n");
97c03d3c 2439 start = squid_curtime;
9e6c462c 2440 fd = xcalloc(ncache_dirs, sizeof(int));
2441 cur = xcalloc(ncache_dirs, sizeof(char *));
2442 new = xcalloc(ncache_dirs, sizeof(char *));
2443 cln = xcalloc(ncache_dirs, sizeof(char *));
3e347513 2444 for (dirn = 0; dirn < ncache_dirs; dirn++) {
2445 fd[dirn] = -1;
2446 cur[dirn] = xstrdup(storeDirSwapLogFile(dirn, NULL));
2447 new[dirn] = xstrdup(storeDirSwapLogFile(dirn, ".clean"));
2448 cln[dirn] = xstrdup(storeDirSwapLogFile(dirn, ".last-clean"));
2449 safeunlink(new[dirn], 1);
2450 safeunlink(cln[dirn], 1);
f17936ab 2451 fd[dirn] = file_open(new[dirn],
9e4ad609 2452 O_WRONLY | O_CREAT | O_TRUNC,
2453 NULL,
2454 NULL);
3e347513 2455 if (fd[dirn] < 0) {
a3d5953d 2456 debug(50, 0) ("storeWriteCleanLogs: %s: %s\n", new[dirn], xstrerror());
5608850b 2457 continue;
2458 }
4ac29a5b 2459#if HAVE_FCHMOD
3e347513 2460 if (stat(cur[dirn], &sb) == 0)
5608850b 2461 fchmod(fd[dirn], sb.st_mode);
4ac29a5b 2462#endif
5608850b 2463 }
2464 line = xcalloc(1, 16384);
090089c4 2465 for (e = storeGetFirst(); e; e = storeGetNext()) {
090089c4 2466 if (e->swap_file_number < 0)
2467 continue;
2468 if (e->swap_status != SWAP_OK)
2469 continue;
2470 if (e->object_len <= 0)
2471 continue;
3ba50d75 2472 if (BIT_TEST(e->flag, RELEASE_REQUEST))
0a21bd84 2473 continue;
2474 if (BIT_TEST(e->flag, KEY_PRIVATE))
2475 continue;
5608850b 2476 if ((dirn = storeDirNumber(e->swap_file_number)) >= ncache_dirs) {
2477 debug_trap("storeWriteCleanLogss: dirn out of range");
2478 continue;
2479 }
2480 if (fd[dirn] < 0)
2481 continue;
0a0bf5db 2482 sprintf(line, "%08x %08x %08x %08x %9d %s\n",
f5469bce 2483 (int) e->swap_file_number,
2484 (int) e->timestamp,
2485 (int) e->expires,
2486 (int) e->lastmod,
2487 e->object_len,
2488 e->url);
5608850b 2489 if (write(fd[dirn], line, strlen(line)) < 0) {
a3d5953d 2490 debug(50, 0) ("storeWriteCleanLogs: %s: %s\n", new[dirn], xstrerror());
2491 debug(20, 0) ("storeWriteCleanLogs: Current swap logfile not replaced.\n");
5608850b 2492 file_close(fd[dirn]);
2493 fd[dirn] = -1;
2494 safeunlink(cln[dirn], 0);
2495 continue;
30a4f2a8 2496 }
5608850b 2497 if ((++n & 0x3FFF) == 0) {
a3d5953d 2498 debug(20, 1) (" %7d lines written so far.\n", n);
090089c4 2499 }
2500 }
5608850b 2501 safe_free(line);
3e347513 2502 for (dirn = 0; dirn < ncache_dirs; dirn++) {
2503 file_close(fd[dirn]);
2504 fd[dirn] = -1;
2505 if (rename(new[dirn], cur[dirn]) < 0) {
a3d5953d 2506 debug(50, 0) ("storeWriteCleanLogs: rename failed: %s\n",
3e347513 2507 xstrerror());
2508 }
5608850b 2509 }
2510 storeDirCloseSwapLogs();
2511 storeDirOpenSwapLogs();
97c03d3c 2512 stop = squid_curtime;
090089c4 2513 r = stop - start;
a3d5953d 2514 debug(20, 1) (" Finished. Wrote %d lines.\n", n);
2515 debug(20, 1) (" Took %d seconds (%6.1lf lines/sec).\n",
090089c4 2516 r > 0 ? r : 0, (double) n / (r > 0 ? r : 1));
0a0bf5db 2517 /* touch a timestamp file if we're not still validating */
3e347513 2518 for (dirn = 0; dirn < ncache_dirs; dirn++) {
f182d1c5 2519 if (!store_rebuilding)
f17936ab 2520 file_close(file_open(cln[dirn],
3e347513 2521 O_WRONLY | O_CREAT | O_TRUNC, NULL, NULL));
5608850b 2522 safe_free(cur[dirn]);
2523 safe_free(new[dirn]);
2524 safe_free(cln[dirn]);
0a0bf5db 2525 }
9e6c462c 2526 safe_free(cur);
2527 safe_free(new);
2528 safe_free(cln);
2529 safe_free(fd);
090089c4 2530 return n;
2531}
2532
24382924 2533static int
b8d8561b 2534swapInError(int fd_unused, StoreEntry * entry)
090089c4 2535{
b8de7ebe 2536 squid_error_entry(entry, ERR_DISK_IO, xstrerror());
090089c4 2537 return 0;
2538}
2539
b8d8561b 2540int
fe4e214f 2541storePendingNClients(const StoreEntry * e)
090089c4 2542{
2543 int npend = 0;
52d4522b 2544 MemObject *mem = e->mem_obj;
090089c4 2545 int i;
52d4522b 2546 if (mem == NULL)
090089c4 2547 return 0;
b012353a 2548 for (i = 0; i < mem->nclients; i++) {
382d851a 2549 if (mem->clients[i].callback_data == NULL)
52d4522b 2550 continue;
b012353a 2551 npend++;
090089c4 2552 }
2553 return npend;
2554}
d8b45066 2555
b8d8561b 2556void
2557storeRotateLog(void)
d8b45066 2558{
2559 char *fname = NULL;
2560 int i;
95d659f0 2561 LOCAL_ARRAY(char, from, MAXPATHLEN);
2562 LOCAL_ARRAY(char, to, MAXPATHLEN);
2edc2504 2563 struct stat sb;
d8b45066 2564
234967c9 2565 if (storelog_fd > -1) {
2566 file_close(storelog_fd);
2567 storelog_fd = -1;
2568 }
b6f794d6 2569 if ((fname = Config.Log.store) == NULL)
2d25dc1a 2570 return;
234967c9 2571 if (strcmp(fname, "none") == 0)
2572 return;
71d95578 2573#ifdef S_ISREG
2edc2504 2574 if (stat(fname, &sb) == 0)
2575 if (S_ISREG(sb.st_mode) == 0)
2576 return;
71d95578 2577#endif
234967c9 2578
a3d5953d 2579 debug(20, 1) ("storeRotateLog: Rotating.\n");
d8b45066 2580
2581 /* Rotate numbers 0 through N up one */
b6f794d6 2582 for (i = Config.Log.rotateNumber; i > 1;) {
2d25dc1a 2583 i--;
2584 sprintf(from, "%s.%d", fname, i - 1);
2585 sprintf(to, "%s.%d", fname, i);
2586 rename(from, to);
d8b45066 2587 }
2588 /* Rotate the current log to .0 */
b6f794d6 2589 if (Config.Log.rotateNumber > 0) {
2d25dc1a 2590 sprintf(to, "%s.%d", fname, 0);
2591 rename(fname, to);
d8b45066 2592 }
f17936ab 2593 storelog_fd = file_open(fname, O_WRONLY | O_CREAT, NULL, NULL);
234967c9 2594 if (storelog_fd < 0) {
a3d5953d 2595 debug(50, 0) ("storeRotateLog: %s: %s\n", fname, xstrerror());
2596 debug(20, 1) ("Store logging disabled\n");
234967c9 2597 }
d8b45066 2598}
30a4f2a8 2599
56f29785 2600static int
fe4e214f 2601storeShouldPurgeMem(const StoreEntry * e)
56f29785 2602{
2603 if (storeCheckPurgeMem(e) == 0)
2604 return 0;
e954773d 2605 if (sm_stats.n_pages_in_use > store_pages_low)
56f29785 2606 return 1;
2607 return 0;
2608}
2609
2610
30a4f2a8 2611/*
2612 * Check if its okay to remove the memory data for this object, but
2613 * leave the StoreEntry around. Designed to be called from
2614 * storeUnlockObject() and storeSwapOutHandle().
2615 */
b8d8561b 2616static int
fe4e214f 2617storeCheckPurgeMem(const StoreEntry * e)
30a4f2a8 2618{
2619 if (storeEntryLocked(e))
2620 return 0;
2621 if (e->store_status != STORE_OK)
2622 return 0;
3e98df20 2623 if (e->swap_status != SWAP_OK)
2624 return 0;
30a4f2a8 2625 return 1;
2626}
a1e47288 2627
b8d8561b 2628static int
429fdbec 2629storeCheckExpired(const StoreEntry * e, int check_lru_age)
620da955 2630{
429fdbec 2631 time_t max_age;
620da955 2632 if (storeEntryLocked(e))
2633 return 0;
7cc1830f 2634 if (BIT_TEST(e->flag, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
2635 return 1;
429fdbec 2636 if (!check_lru_age)
2637 return 0;
2638 if ((max_age = storeExpiredReferenceAge()) <= 0)
db9ccd5a 2639 return 0;
2640 if (squid_curtime - e->lastref > max_age)
66cedb85 2641 return 1;
66cedb85 2642 return 0;
620da955 2643}
2644
db9ccd5a 2645/*
2646 * storeExpiredReferenceAge
2647 *
429fdbec 2648 * The LRU age is scaled exponentially between 1 minute and
2649 * Config.referenceAge , when store_swap_low < store_swap_size <
2650 * store_swap_high. This keeps store_swap_size within the low and high
2651 * water marks. If the cache is very busy then store_swap_size stays
2652 * closer to the low water mark, if it is not busy, then it will stay
2653 * near the high water mark. The LRU age value can be examined on the
2654 * cachemgr 'info' page.
fbdfb3cf 2655 */
35feb4aa 2656time_t
58104eab 2657storeExpiredReferenceAge(void)
2658{
58104eab 2659 double x;
2660 double z;
2661 time_t age;
429fdbec 2662 if (Config.referenceAge == 0)
2663 return 0;
2664 x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
2665 x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
2666 z = pow((double) Config.referenceAge, x);
58104eab 2667 age = (time_t) (z * 60.0);
2668 if (age < 60)
2669 age = 60;
2670 else if (age > 31536000)
2671 age = 31536000;
2672 return age;
0a362c34 2673}
2edc2504 2674
b8d8561b 2675void
2676storeCloseLog(void)
457bedbd 2677{
15be2259 2678 if (storelog_fd >= 0)
40fcf5d4 2679 file_close(storelog_fd);
457bedbd 2680}
79b5cc5f 2681
b8d8561b 2682void
2683storeNegativeCache(StoreEntry * e)
79b5cc5f 2684{
ff5731b1 2685 e->expires = squid_curtime + Config.negativeTtl;
2686 BIT_SET(e->flag, ENTRY_NEGCACHED);
79b5cc5f 2687}
0a21bd84 2688
2689void
2690storeFreeMemory(void)
2691{
2692 StoreEntry *e;
df92bd2a 2693 StoreEntry **list;
2694 int i = 0;
ea6b05e3 2695 int j;
56e15c50 2696 list = xcalloc(meta_data.store_entries, sizeof(StoreEntry *));
df92bd2a 2697 e = (StoreEntry *) hash_first(store_table);
2698 while (e && i < meta_data.store_entries) {
2699 *(list + i) = e;
2700 i++;
2701 e = (StoreEntry *) hash_next(store_table);
2702 }
ea6b05e3 2703 for (j = 0; j < i; j++)
2704 destroy_StoreEntry(*(list + j));
df92bd2a 2705 xfree(list);
0a21bd84 2706 hashFreeMemory(store_table);
e161be2e 2707 safe_free(MaintBucketsOrder);
9e4ad609 2708 storeDirCloseSwapLogs();
0a21bd84 2709}
a7e59001 2710
2711int
2712expiresMoreThan(time_t expires, time_t when)
2713{
48f44632 2714 if (expires < 0) /* No Expires given */
2715 return 1;
2716 return (expires > (squid_curtime + when));
a7e59001 2717}
fe54d06d 2718
2719int
2720storeEntryValidToSend(StoreEntry * e)
2721{
2722 if (BIT_TEST(e->flag, RELEASE_REQUEST))
2723 return 0;
2724 if (BIT_TEST(e->flag, ENTRY_NEGCACHED))
44f78c24 2725 if (e->expires <= squid_curtime)
fe54d06d 2726 return 0;
2727 if (e->store_status == STORE_ABORTED)
2728 return 0;
2729 return 1;
2730}
62663274 2731
ca98227c 2732void
2733storeTimestampsSet(StoreEntry * entry)
2734{
2735 time_t served_date = -1;
2736 struct _http_reply *reply = entry->mem_obj->reply;
2737 served_date = reply->date > -1 ? reply->date : squid_curtime;
2738 entry->expires = reply->expires;
2739 if (reply->last_modified > -1)
2740 entry->lastmod = reply->last_modified;
2741 else
2742 entry->lastmod = served_date;
2743 entry->timestamp = served_date;
2744}
429fdbec 2745
2746#define FILENO_STACK_SIZE 128
2747static int fileno_stack[FILENO_STACK_SIZE];
2748int fileno_stack_count = 0;
2749
2750static int
2751storeGetUnusedFileno(void)
2752{
9e4ad609 2753 int fn;
429fdbec 2754 if (fileno_stack_count < 1)
2755 return -1;
9e4ad609 2756 fn = fileno_stack[--fileno_stack_count];
2757 storeDirMapBitSet(fn);
2758 return fn;
429fdbec 2759}
2760
2761static void
2762storePutUnusedFileno(int fileno)
2763{
bf33177b 2764 assert(storeDirMapBitTest(fileno));
9e4ad609 2765 storeDirMapBitReset(fileno);
429fdbec 2766 if (fileno_stack_count < FILENO_STACK_SIZE)
2767 fileno_stack[fileno_stack_count++] = fileno;
2768 else
2769 unlinkdUnlink(storeSwapFullPath(fileno, NULL));
2770}
bfcaf585 2771
2772void
2773storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
2774{
2775 MemObject *mem = e->mem_obj;
2776 assert(mem);
2777 assert(mem->abort.callback == NULL);
2778 mem->abort.callback = cb;
2779 mem->abort.data = data;
2780}
2781
2782void
2783storeUnregisterAbort(StoreEntry * e)
2784{
2785 MemObject *mem = e->mem_obj;
2786 assert(mem);
2787 mem->abort.callback = NULL;
2788}