]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs/coss/store_dir_coss.cc
Fully deprecated storeLockObject(). Everything now directly calls the
[thirdparty/squid.git] / src / fs / coss / store_dir_coss.cc
CommitLineData
a4b8110e 1
cd748f27 2/*
ddd1cefa 3 * $Id: store_dir_coss.cc,v 1.61 2005/07/03 20:41:46 serassio Exp $
c8f4eac4 4 * vim: set et :
cd748f27 5 *
17bb3486 6 * DEBUG: section 47 Store COSS Directory Routines
cd748f27 7 * AUTHOR: Eric Stern
8 *
2b6662ba 9 * SQUID Web Proxy Cache http://www.squid-cache.org/
cd748f27 10 * ----------------------------------------------------------
11 *
2b6662ba 12 * Squid is the result of efforts by numerous individuals from
13 * the Internet community; see the CONTRIBUTORS file for full
14 * details. Many organizations have provided support for Squid's
15 * development; see the SPONSORS file for full details. Squid is
16 * Copyrighted (C) 2001 by the Regents of the University of
17 * California; see the COPYRIGHT file for full details. Squid
18 * incorporates software developed and/or copyrighted by other
19 * sources; see the CREDITS file for full details.
cd748f27 20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 *
35 */
36
37#include "squid.h"
b9ae18aa 38#include "CossSwapDir.h"
e6ccf245 39#include "Store.h"
cd748f27 40
41#include "store_coss.h"
528b2c61 42#include "fde.h"
d3b3ab85 43#include "SwapDir.h"
51ee7c82 44#include "StoreSwapLogData.h"
b9ae18aa 45#include "DiskIO/DiskIOModule.h"
46#include "DiskIO/DiskIOStrategy.h"
47#include "DiskIO/ReadRequest.h"
48#include "ConfigOption.h"
49#include "StoreFScoss.h"
c8f4eac4 50#include "Parsing.h"
cd748f27 51
52#define STORE_META_BUFSZ 4096
53
cd748f27 54int n_coss_dirs = 0;
55/* static int last_coss_pick_index = -1; */
b001e822 56MemAllocatorProxy *coss_index_pool = NULL;
cd748f27 57
58typedef struct _RebuildState RebuildState;
62e76326 59
60struct _RebuildState
61{
d3b3ab85 62 CossSwapDir *sd;
cd748f27 63 int n_read;
64 FILE *log;
65 int speed;
62e76326 66
67 struct
68 {
69
70unsigned int clean:
71 1;
72 }
73
74 flags;
75
cd748f27 76 struct _store_rebuild_data counts;
77};
78
79static char *storeCossDirSwapLogFile(SwapDir *, const char *);
80static EVH storeCossRebuildFromSwapLog;
d3b3ab85 81static StoreEntry *storeCossAddDiskRestore(CossSwapDir * SD, const cache_key * key,
62e76326 82 int file_number,
83 size_t swap_file_sz,
84 time_t expires,
85 time_t timestamp,
86 time_t lastref,
87 time_t lastmod,
88 u_int32_t refcount,
89 u_int16_t flags,
90 int clean);
d3b3ab85 91static void storeCossDirRebuild(CossSwapDir * sd);
92static void storeCossDirCloseTmpSwapLog(CossSwapDir * sd);
93static FILE *storeCossDirOpenTmpSwapLog(CossSwapDir *, int *, int *);
cd748f27 94
95static char *
96storeCossDirSwapLogFile(SwapDir * sd, const char *ext)
97{
98 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
99 LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN);
100 LOCAL_ARRAY(char, digit, 32);
101 char *pathtmp2;
62e76326 102
cd748f27 103 if (Config.Log.swap) {
62e76326 104 xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64);
105 pathtmp2 = pathtmp;
106
107 while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL)
108 *pathtmp2 = '.';
109
110 while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.')
111 pathtmp[strlen(pathtmp) - 1] = '\0';
112
113 for (pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++)
114
115 ;
116 snprintf(path, SQUID_MAXPATHLEN - 64, Config.Log.swap, pathtmp2);
117
118 if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) {
119 strcat(path, ".");
120 snprintf(digit, 32, "%02d", sd->index);
121 strncat(path, digit, 3);
122 }
cd748f27 123 } else {
62e76326 124 xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64);
125 strcat(path, "/swap.state");
cd748f27 126 }
62e76326 127
cd748f27 128 if (ext)
62e76326 129 strncat(path, ext, 16);
130
cd748f27 131 return path;
132}
133
d3b3ab85 134void
135CossSwapDir::openLog()
cd748f27 136{
d3b3ab85 137 char *logPath;
138 logPath = storeCossDirSwapLogFile(this, NULL);
139 swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY);
62e76326 140
d3b3ab85 141 if (swaplog_fd < 0) {
62e76326 142 debug(47, 1) ("%s: %s\n", logPath, xstrerror());
143 fatal("storeCossDirOpenSwapLog: Failed to open swap log.");
cd748f27 144 }
62e76326 145
d3b3ab85 146 debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", index, swaplog_fd);
cd748f27 147}
148
d3b3ab85 149void
150CossSwapDir::closeLog()
cd748f27 151{
d3b3ab85 152 if (swaplog_fd < 0) /* not open */
62e76326 153 return;
154
d3b3ab85 155 file_close(swaplog_fd);
62e76326 156
99cce4cb 157 debug(47, 3) ("Cache COSS Dir #%d log closed on FD %d\n",
62e76326 158 index, swaplog_fd);
159
d3b3ab85 160 swaplog_fd = -1;
cd748f27 161}
162
d3b3ab85 163void
b9ae18aa 164CossSwapDir::ioCompletedNotification()
d3b3ab85 165{
b9ae18aa 166 if (theFile->error()) {
62e76326 167 debug(47, 1) ("%s: %s\n", path, xstrerror());
1a224843 168 fatal("storeCossDirInit: Failed to open a COSS file.");
697c29aa 169 }
b9ae18aa 170}
171
172void
173CossSwapDir::closeCompleted()
174{
175 theFile = NULL;
176}
177
178void
179CossSwapDir::readCompleted(const char *buf, int len, int errflag, RefCount<ReadRequest> aRequest)
180{
181 CossRead* cossRead= dynamic_cast<CossRead *>(aRequest.getRaw());
182 assert (cossRead);
183 StoreIOState::Pointer sio = cossRead->sio;
184 void *cbdata;
185 STRCB *callback = sio->read.callback;
186 char *p;
187 CossState *cstate = dynamic_cast<CossState *>(sio.getRaw());
188 ssize_t rlen;
189
190 debug(79, 3) ("storeCossReadDone: fileno %d, len %d\n",
191 sio->swap_filen, len);
192 cstate->flags.reading = 0;
193
194 if (errflag) {
195 StoreFScoss::GetInstance().stats.read.fail++;
196
197 if (errflag > 0) {
198 errno = errflag;
199 debug(79, 1) ("storeCossReadDone: error: %s\n", xstrerror());
200 } else {
201 debug(79, 1) ("storeCossReadDone: got failure (%d)\n", errflag);
202 }
203
204 rlen = -1;
205 } else {
206 StoreFScoss::GetInstance().stats.read.success++;
207
208 if (cstate->readbuffer == NULL) {
209 cstate->readbuffer = (char *)xmalloc(cstate->st_size);
210 p = storeCossMemPointerFromDiskOffset(storeCossFilenoToDiskOffset(sio->swap_filen),
211 NULL);
212 xmemcpy(cstate->readbuffer, p, cstate->st_size);
213 }
214
215 sio->offset_ += len;
216 xmemcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset],
217 cstate->requestlen);
218 rlen = (size_t) cstate->requestlen;
219 }
220
221 assert(callback);
222 sio->read.callback = NULL;
223
224 if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
225 callback(cbdata, cstate->requestbuf, rlen);
226}
227
228void
229CossSwapDir::writeCompleted(int errflag, size_t len, RefCount<WriteRequest> writeRequest)
230{
231 CossWrite* cossWrite= dynamic_cast<CossWrite *>(writeRequest.getRaw());
232 assert (cossWrite);
233
234 debug(79, 3) ("storeCossWriteMemBufDone: buf %p, len %ld\n", cossWrite->membuf, (long int) len);
235
236
237 if (errflag) {
238 StoreFScoss::GetInstance().stats.stripe_write.fail++;
239 debug(79, 1) ("storeCossWriteMemBufDone: got failure (%d)\n", errflag);
ddd1cefa 240 debugs(79, 1, "size=" << cossWrite->membuf->diskend - cossWrite->membuf->diskstart);
b9ae18aa 241 } else {
242 StoreFScoss::GetInstance().stats.stripe_write.success++;
243 }
244
245
246 dlinkDelete(&cossWrite->membuf->node, &membufs);
247 cbdataFree(cossWrite->membuf);
248 StoreFScoss::GetInstance().stats.stripes--;
249}
250
251void
252CossSwapDir::changeIO(DiskIOModule *module)
253{
254 DiskIOStrategy *anIO = module->createStrategy();
255 safe_free(ioModule);
256 ioModule = xstrdup(module->type());
257
258 delete io;
259 io = anIO;
260 /* Change the IO Options */
261
e2a8733e 262 if (currentIOOptions == NULL)
263 currentIOOptions = new ConfigOptionVector();
264
b9ae18aa 265 if (currentIOOptions->options.size() > 3)
266 delete currentIOOptions->options.pop_back();
267
268 /* TODO: factor out these 4 lines */
269 ConfigOption *ioOptions = NULL;
270
271 if (io)
272 ioOptions = io->getOptionTree();
273
274 if (ioOptions)
275 currentIOOptions->options.push_back(ioOptions);
276}
277
278bool
279CossSwapDir::optionIOParse(char const *option, const char *value, int reconfiguring)
280{
281 if (strcmp(option, "IOEngine") != 0)
282 return false;
283
284 if (reconfiguring)
285 /* silently ignore this */
286 return true;
287
288 if (!value)
289 self_destruct();
290
291 DiskIOModule *module = DiskIOModule::Find(value);
292
293 if (!module)
294 self_destruct();
295
296 changeIO(module);
297
298 return true;
299}
300
301void
302CossSwapDir::optionIODump(StoreEntry * e) const
303{
304 storeAppendPrintf(e, " IOEngine=%s", ioModule);
305}
306
307ConfigOption *
308CossSwapDir::getOptionTree() const
309{
310 ConfigOption *parentResult = SwapDir::getOptionTree();
311
e2a8733e 312 if (currentIOOptions == NULL)
313 currentIOOptions = new ConfigOptionVector();
314
b9ae18aa 315 currentIOOptions->options.push_back(parentResult);
e2a8733e 316
b9ae18aa 317 currentIOOptions->options.push_back(new ConfigOptionAdapter<CossSwapDir>(*const_cast<CossSwapDir *>(this), &CossSwapDir::optionIOParse, &CossSwapDir::optionIODump));
e2a8733e 318
b9ae18aa 319 currentIOOptions->options.push_back(
320 new ConfigOptionAdapter<CossSwapDir>(*const_cast<CossSwapDir *>(this),
321 &CossSwapDir::optionBlockSizeParse,
322 &CossSwapDir::optionBlockSizeDump));
323
324
325 ConfigOption *ioOptions = NULL;
326
327 if (io)
328 ioOptions = io->getOptionTree();
329
330 if (ioOptions)
331 currentIOOptions->options.push_back(ioOptions);
332
e2a8733e 333 ConfigOption* result = currentIOOptions;
334
335 currentIOOptions = NULL;
336
337 return result;
b9ae18aa 338}
339
340void
341CossSwapDir::init()
342{
343 /* FIXME: SwapDirs aren't refcounted. We call IORequestor calls, which
344 * are refcounted. SO, we up our count once to avoid implicit delete's.
345 */
346 RefCountReference();
347 io->init();
348 openLog();
349 storeCossDirRebuild(this);
c8f4eac4 350 theFile = io->newFile(stripePath());
b9ae18aa 351 theFile->open(O_RDWR | O_CREAT, 0644, this);
62e76326 352
b9ae18aa 353 ++n_coss_dirs;
1a224843 354 /*
355 * fs.blksize is normally determined by calling statvfs() etc,
356 * but we just set it here. It is used in accounting the
357 * total store size, and is reported in cachemgr 'storedir'
358 * page.
359 */
360 fs.blksize = 1 << blksz_bits;
cd748f27 361}
362
6a566b9c 363void
d3b3ab85 364storeCossRemove(CossSwapDir * sd, StoreEntry * e)
6a566b9c 365{
e6ccf245 366 CossIndexNode *coss_node = (CossIndexNode *)e->repl.data;
6a566b9c 367 e->repl.data = NULL;
d3b3ab85 368 dlinkDelete(&coss_node->node, &sd->cossindex);
b001e822 369 coss_index_pool->free(coss_node);
d3b3ab85 370 sd->count -= 1;
6a566b9c 371}
372
373void
d3b3ab85 374storeCossAdd(CossSwapDir * sd, StoreEntry * e)
6a566b9c 375{
b001e822 376 CossIndexNode *coss_node = (CossIndexNode *)coss_index_pool->alloc();
6a566b9c 377 assert(!e->repl.data);
378 e->repl.data = coss_node;
d3b3ab85 379 dlinkAdd(e, &coss_node->node, &sd->cossindex);
380 sd->count += 1;
6a566b9c 381}
382
383static void
384storeCossRebuildComplete(void *data)
385{
e6ccf245 386 RebuildState *rb = (RebuildState *)data;
d3b3ab85 387 CossSwapDir *sd = rb->sd;
b9ae18aa 388 sd->startMembuf();
6a566b9c 389 store_dirs_rebuilding--;
390 storeCossDirCloseTmpSwapLog(rb->sd);
391 storeRebuildComplete(&rb->counts);
392 cbdataFree(rb);
393}
394
cd748f27 395static void
396storeCossRebuildFromSwapLog(void *data)
397{
e6ccf245 398 RebuildState *rb = (RebuildState *)data;
cd748f27 399 StoreEntry *e = NULL;
51ee7c82 400 StoreSwapLogData s;
401 size_t ss = sizeof(StoreSwapLogData);
cd748f27 402 double x;
403 assert(rb != NULL);
404 /* load a number of objects per invocation */
62e76326 405
d3b3ab85 406 for (int aCount = 0; aCount < rb->speed; aCount++) {
62e76326 407 if (fread(&s, ss, 1, rb->log) != 1) {
408 debug(47, 1) ("Done reading %s swaplog (%d entries)\n",
409 rb->sd->path, rb->n_read);
410 fclose(rb->log);
411 rb->log = NULL;
412 storeCossRebuildComplete(rb);
413 return;
414 }
415
416 rb->n_read++;
417
418 if (s.op <= SWAP_LOG_NOP)
419 continue;
420
421 if (s.op >= SWAP_LOG_MAX)
422 continue;
423
424 debug(47, 3) ("storeCossRebuildFromSwapLog: %s %s %08X\n",
425 swap_log_op_str[(int) s.op],
426 storeKeyText(s.key),
427 s.swap_filen);
428
429 if (s.op == SWAP_LOG_ADD) {
430 (void) 0;
431 } else if (s.op == SWAP_LOG_DEL) {
432 /* Delete unless we already have a newer copy */
433
c8f4eac4 434 if ((e = rb->sd->get
435 (s.key)) != NULL && s.lastref > e->lastref) {
62e76326 436 /*
437 * Make sure we don't unlink the file, it might be
438 * in use by a subsequent entry. Also note that
439 * we don't have to subtract from store_swap_size
440 * because adding to store_swap_size happens in
441 * the cleanup procedure.
442 */
443 storeExpireNow(e);
444 storeReleaseRequest(e);
445
446 if (e->swap_filen > -1) {
447 e->swap_filen = -1;
448 }
449
450 storeRelease(e);
451 /* Fake an unlink here, this is a bad hack :( */
452 storeCossRemove(rb->sd, e);
453 rb->counts.objcount--;
454 rb->counts.cancelcount++;
455 }
456
457 continue;
458 } else {
411c6ea3 459 x = log(static_cast<double>(++rb->counts.bad_log_op)) / log(10.0);
62e76326 460
411c6ea3 461 if (0.0 == x - (double)
462 (int) x)
62e76326 463 debug(47, 1) ("WARNING: %d invalid swap log entries found\n",
464 rb->counts.bad_log_op);
465
466 rb->counts.invalid++;
467
468 continue;
469 }
470
471 if ((++rb->counts.scancount & 0xFFF) == 0) {
472
473 struct stat sb;
474
475 if (0 == fstat(fileno(rb->log), &sb))
476 storeRebuildProgress(rb->sd->index,
477 (int) sb.st_size / ss, rb->n_read);
478 }
479
480 if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
481 rb->counts.badflags++;
482 continue;
483 }
484
c8f4eac4 485 e = rb->sd->get
486 (s.key);
62e76326 487
488 if (e) {
489 /* key already exists, current entry is newer */
490 /* keep old, ignore new */
491 rb->counts.dupcount++;
492 continue;
493 }
494
495 /* update store_swap_size */
496 rb->counts.objcount++;
497
498 e = storeCossAddDiskRestore(rb->sd, s.key,
499 s.swap_filen,
500 s.swap_file_sz,
501 s.expires,
502 s.timestamp,
503 s.lastref,
504 s.lastmod,
505 s.refcount,
506 s.flags,
507 (int) rb->flags.clean);
508
509 storeDirSwapLog(e, SWAP_LOG_ADD);
cd748f27 510 }
62e76326 511
cd748f27 512 eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1);
513}
514
515/* Add a new object to the cache with empty memory copy and pointer to disk
516 * use to rebuild store from disk. */
517static StoreEntry *
d3b3ab85 518storeCossAddDiskRestore(CossSwapDir * SD, const cache_key * key,
62e76326 519 int file_number,
520 size_t swap_file_sz,
521 time_t expires,
522 time_t timestamp,
523 time_t lastref,
524 time_t lastmod,
525 u_int32_t refcount,
526 u_int16_t flags,
527 int clean)
cd748f27 528{
529 StoreEntry *e = NULL;
99cce4cb 530 debug(47, 5) ("storeCossAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number);
62e76326 531 /* if you call this you'd better be sure file_number is not
cd748f27 532 * already in use! */
c8f4eac4 533 e = new StoreEntry();
cd748f27 534 e->store_status = STORE_OK;
535 e->swap_dirn = SD->index;
536 storeSetMemStatus(e, NOT_IN_MEMORY);
537 e->swap_status = SWAPOUT_DONE;
538 e->swap_filen = file_number;
539 e->swap_file_sz = swap_file_sz;
540 e->lock_count = 0;
cd748f27 541 e->lastref = lastref;
542 e->timestamp = timestamp;
543 e->expires = expires;
544 e->lastmod = lastmod;
545 e->refcount = refcount;
546 e->flags = flags;
547 EBIT_SET(e->flags, ENTRY_CACHABLE);
548 EBIT_CLR(e->flags, RELEASE_REQUEST);
549 EBIT_CLR(e->flags, KEY_PRIVATE);
550 e->ping_status = PING_NONE;
551 EBIT_CLR(e->flags, ENTRY_VALIDATED);
552 storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */
6a566b9c 553 storeCossAdd(SD, e);
1a224843 554 assert(e->swap_filen >= 0);
cd748f27 555 return e;
556}
557
28c60158 558CBDATA_TYPE(RebuildState);
cd748f27 559static void
d3b3ab85 560storeCossDirRebuild(CossSwapDir * sd)
cd748f27 561{
28c60158 562 RebuildState *rb;
cd748f27 563 int clean = 0;
564 int zero = 0;
565 FILE *fp;
28c60158 566 CBDATA_INIT_TYPE(RebuildState);
72711e31 567 rb = cbdataAlloc(RebuildState);
cd748f27 568 rb->sd = sd;
569 rb->speed = opt_foreground_rebuild ? 1 << 30 : 50;
6a566b9c 570 rb->flags.clean = (unsigned int) clean;
cd748f27 571 /*
572 * If the swap.state file exists in the cache_dir, then
573 * we'll use storeCossRebuildFromSwapLog().
574 */
575 fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero);
99cce4cb 576 debug(47, 1) ("Rebuilding COSS storage in %s (%s)\n",
62e76326 577 sd->path, clean ? "CLEAN" : "DIRTY");
6a566b9c 578 rb->log = fp;
579 store_dirs_rebuilding++;
62e76326 580
cd748f27 581 if (!clean || fp == NULL) {
62e76326 582 /* COSS cannot yet rebuild from a dirty state. If the log
583 * is dirty then the COSS contents is thrown away.
584 * Why? I guess it is because some contents will be lost,
585 * and COSS cannot verify this..
586 */
587
588 if (fp != NULL)
589 fclose(fp);
590
591 /*
592 * XXX Make sure we don't trigger an assertion if this is the first
593 * storedir, since if we are, this call will cause storeRebuildComplete
594 * to prematurely complete the rebuild process, and then some other
595 * storedir will try to rebuild and eventually die.
596 */
597 eventAdd("storeCossRebuildComplete", storeCossRebuildComplete, rb, 0.0, 0);
598
599 return;
cd748f27 600 }
62e76326 601
c8f4eac4 602 eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1);
cd748f27 603}
604
605static void
d3b3ab85 606storeCossDirCloseTmpSwapLog(CossSwapDir * sd)
cd748f27 607{
cd748f27 608 char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
609 char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
d3b3ab85 610 int anfd;
611 file_close(sd->swaplog_fd);
62e76326 612
cd748f27 613 if (xrename(new_path, swaplog_path) < 0) {
62e76326 614 fatal("storeCossDirCloseTmpSwapLog: rename failed");
cd748f27 615 }
62e76326 616
d3b3ab85 617 anfd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY);
62e76326 618
d3b3ab85 619 if (anfd < 0) {
62e76326 620 debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror());
621 fatal("storeCossDirCloseTmpSwapLog: Failed to open swap log.");
cd748f27 622 }
62e76326 623
cd748f27 624 safe_free(swaplog_path);
625 safe_free(new_path);
d3b3ab85 626 sd->swaplog_fd = anfd;
627 debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, anfd);
cd748f27 628}
629
630static FILE *
d3b3ab85 631storeCossDirOpenTmpSwapLog(CossSwapDir * sd, int *clean_flag, int *zero_flag)
cd748f27 632{
cd748f27 633 char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
634 char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
635 char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
62e76326 636
cd748f27 637 struct stat log_sb;
62e76326 638
cd748f27 639 struct stat clean_sb;
640 FILE *fp;
d3b3ab85 641 int anfd;
62e76326 642
c8f4eac4 643 if (::stat(swaplog_path, &log_sb) < 0) {
62e76326 644 debug(50, 1) ("Cache COSS Dir #%d: No log file\n", sd->index);
645 safe_free(swaplog_path);
646 safe_free(clean_path);
647 safe_free(new_path);
648 return NULL;
cd748f27 649 }
62e76326 650
cd748f27 651 *zero_flag = log_sb.st_size == 0 ? 1 : 0;
652 /* close the existing write-only FD */
62e76326 653
d3b3ab85 654 if (sd->swaplog_fd >= 0)
62e76326 655 file_close(sd->swaplog_fd);
656
cd748f27 657 /* open a write-only FD for the new log */
d3b3ab85 658 anfd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
62e76326 659
d3b3ab85 660 if (anfd < 0) {
62e76326 661 debug(50, 1) ("%s: %s\n", new_path, xstrerror());
662 fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
cd748f27 663 }
62e76326 664
d3b3ab85 665 sd->swaplog_fd = anfd;
cd748f27 666 /* open a read-only stream of the old log */
3d37fe17 667 fp = fopen(swaplog_path, "rb");
62e76326 668
cd748f27 669 if (fp == NULL) {
62e76326 670 debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
671 fatal("Failed to open swap log for reading");
cd748f27 672 }
62e76326 673
cd748f27 674 memset(&clean_sb, '\0', sizeof(struct stat));
62e76326 675
c8f4eac4 676 if (::stat(clean_path, &clean_sb) < 0)
62e76326 677 *clean_flag = 0;
cd748f27 678 else if (clean_sb.st_mtime < log_sb.st_mtime)
62e76326 679 *clean_flag = 0;
cd748f27 680 else
62e76326 681 *clean_flag = 1;
682
cd748f27 683 safeunlink(clean_path, 1);
62e76326 684
cd748f27 685 safe_free(swaplog_path);
62e76326 686
cd748f27 687 safe_free(clean_path);
62e76326 688
cd748f27 689 safe_free(new_path);
62e76326 690
cd748f27 691 return fp;
692}
693
62e76326 694class CossCleanLog : public SwapDir::CleanLog
695{
696
697public:
b9ae18aa 698 CossCleanLog(CossSwapDir *);
d3b3ab85 699 virtual const StoreEntry *nextEntry();
700 virtual void write(StoreEntry const &);
cd748f27 701 char *cur;
e6ccf245 702 char *newLog;
cd748f27 703 char *cln;
704 char *outbuf;
705 off_t outbuf_offset;
706 int fd;
6a566b9c 707 dlink_node *current;
b9ae18aa 708 CossSwapDir *sd;
cd748f27 709};
710
711#define CLEAN_BUF_SZ 16384
d3b3ab85 712
b9ae18aa 713CossCleanLog::CossCleanLog(CossSwapDir *aSwapDir) : cur(NULL),newLog(NULL),cln(NULL),outbuf(NULL),
62e76326 714 outbuf_offset(0), fd(-1),current(NULL), sd(aSwapDir)
715{}
d3b3ab85 716
cd748f27 717/*
718 * Begin the process to write clean cache state. For COSS this means
719 * opening some log files and allocating write buffers. Return 0 if
720 * we succeed, and assign the 'func' and 'data' return pointers.
721 */
d3b3ab85 722int
723CossSwapDir::writeCleanStart()
cd748f27 724{
d3b3ab85 725 CossCleanLog *state = new CossCleanLog(this);
1f56cfef 726#if HAVE_FCHMOD
62e76326 727
cd748f27 728 struct stat sb;
1f56cfef 729#endif
62e76326 730
d3b3ab85 731 state->newLog = xstrdup(storeCossDirSwapLogFile(this, ".clean"));
e6ccf245 732 state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
d3b3ab85 733 cleanLog = NULL;
62e76326 734
81928dfe 735 if (state->fd < 0) {
62e76326 736 xfree(state->newLog);
737 delete state;
738 return -1;
81928dfe 739 }
62e76326 740
d3b3ab85 741 state->cur = xstrdup(storeCossDirSwapLogFile(this, NULL));
742 state->cln = xstrdup(storeCossDirSwapLogFile(this, ".last-clean"));
e6ccf245 743 state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
cd748f27 744 state->outbuf_offset = 0;
d3b3ab85 745 ::unlink(state->cln);
746 state->current = cossindex.tail;
99cce4cb 747 debug(50, 3) ("storeCOssDirWriteCleanLogs: opened %s, FD %d\n",
62e76326 748 state->newLog, state->fd);
cd748f27 749#if HAVE_FCHMOD
62e76326 750
c8f4eac4 751 if (::stat(state->cur, &sb) == 0)
62e76326 752 fchmod(state->fd, sb.st_mode);
753
cd748f27 754#endif
62e76326 755
d3b3ab85 756 cleanLog = state;
6a566b9c 757
cd748f27 758 return 0;
759}
760
c8f4eac4 761/* RBC 20050101 - I think there is a race condition here,
762 * *current can be freed as its not ref counted, if/when
763 * the store overruns the log writer
764 */
d3b3ab85 765const StoreEntry *
766CossCleanLog::nextEntry()
6a566b9c 767{
6a566b9c 768 const StoreEntry *entry;
62e76326 769
d3b3ab85 770 if (!current)
62e76326 771 return NULL;
772
d3b3ab85 773 entry = (const StoreEntry *) current->data;
62e76326 774
d3b3ab85 775 current = current->prev;
62e76326 776
6a566b9c 777 return entry;
778}
779
cd748f27 780/*
781 * "write" an entry to the clean log file.
782 */
d3b3ab85 783void
784CossCleanLog::write(StoreEntry const &e)
cd748f27 785{
d3b3ab85 786 CossCleanLog *state = this;
51ee7c82 787 StoreSwapLogData s;
788 static size_t ss = sizeof(StoreSwapLogData);
cd748f27 789 s.op = (char) SWAP_LOG_ADD;
d3b3ab85 790 s.swap_filen = e.swap_filen;
791 s.timestamp = e.timestamp;
792 s.lastref = e.lastref;
793 s.expires = e.expires;
794 s.lastmod = e.lastmod;
795 s.swap_file_sz = e.swap_file_sz;
796 s.refcount = e.refcount;
797 s.flags = e.flags;
798 xmemcpy(&s.key, e.key, MD5_DIGEST_CHARS);
b9ae18aa 799 xmemcpy(outbuf + outbuf_offset, &s, ss);
800 outbuf_offset += ss;
cd748f27 801 /* buffered write */
62e76326 802
b9ae18aa 803 if (outbuf_offset + ss > CLEAN_BUF_SZ) {
804 if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) {
62e76326 805 debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
b9ae18aa 806 newLog, xstrerror());
62e76326 807 debug(50, 0) ("storeCossDirWriteCleanLogs: Current swap logfile not replaced.\n");
b9ae18aa 808 file_close(fd);
809 fd = -1;
810 unlink(newLog);
62e76326 811 sd->cleanLog = NULL;
b9ae18aa 812 delete state;
62e76326 813 return;
814 }
815
b9ae18aa 816 outbuf_offset = 0;
cd748f27 817 }
818}
819
d3b3ab85 820void
821CossSwapDir::writeCleanDone()
cd748f27 822{
d3b3ab85 823 CossCleanLog *state = (CossCleanLog *)cleanLog;
62e76326 824
a7d542ee 825 if (NULL == state)
62e76326 826 return;
827
cd748f27 828 if (state->fd < 0)
62e76326 829 return;
830
d0ef8ea8 831 if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
62e76326 832 debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
833 state->newLog, xstrerror());
834 debug(50, 0) ("storeCossDirWriteCleanLogs: Current swap logfile "
835 "not replaced.\n");
836 file_close(state->fd);
837 state->fd = -1;
838 ::unlink(state->newLog);
cd748f27 839 }
62e76326 840
cd748f27 841 safe_free(state->outbuf);
842 /*
843 * You can't rename open files on Microsoft "operating systems"
844 * so we have to close before renaming.
845 */
d3b3ab85 846 closeLog();
3d37fe17 847 /* save the fd value for a later test */
d3b3ab85 848 int anfd = state->fd;
cd748f27 849 /* rename */
62e76326 850
cd748f27 851 if (state->fd >= 0) {
966f432f 852#if defined(_SQUID_OS2_) || defined(_SQUID_WIN32_)
62e76326 853 file_close(state->fd);
854 state->fd = -1;
855
cd748f27 856#endif
62e76326 857
858 xrename(state->newLog, state->cur);
cd748f27 859 }
62e76326 860
cd748f27 861 /* touch a timestamp file if we're not still validating */
862 if (store_dirs_rebuilding)
62e76326 863 (void) 0;
d3b3ab85 864 else if (anfd < 0)
62e76326 865 (void) 0;
cd748f27 866 else
62e76326 867 file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
868
cd748f27 869 /* close */
870 safe_free(state->cur);
62e76326 871
e6ccf245 872 safe_free(state->newLog);
62e76326 873
cd748f27 874 safe_free(state->cln);
62e76326 875
cd748f27 876 if (state->fd >= 0)
62e76326 877 file_close(state->fd);
878
cd748f27 879 state->fd = -1;
62e76326 880
d3b3ab85 881 delete state;
62e76326 882
d3b3ab85 883 cleanLog = NULL;
cd748f27 884}
885
061c7171 886static void
887FreeObject(void *address)
888{
889 StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address);
890 delete anObject;
891}
892
d3b3ab85 893void
894CossSwapDir::logEntry(const StoreEntry & e, int op) const
cd748f27 895{
51ee7c82 896 StoreSwapLogData *s = new StoreSwapLogData;
cd748f27 897 s->op = (char) op;
d3b3ab85 898 s->swap_filen = e.swap_filen;
899 s->timestamp = e.timestamp;
900 s->lastref = e.lastref;
901 s->expires = e.expires;
902 s->lastmod = e.lastmod;
903 s->swap_file_sz = e.swap_file_sz;
904 s->refcount = e.refcount;
905 s->flags = e.flags;
906 xmemcpy(s->key, e.key, MD5_DIGEST_CHARS);
907 file_write(swaplog_fd,
62e76326 908 -1,
909 s,
51ee7c82 910 sizeof(StoreSwapLogData),
62e76326 911 NULL,
912 NULL,
061c7171 913 &FreeObject);
cd748f27 914}
915
d3b3ab85 916void
c8f4eac4 917CossSwapDir::create()
cd748f27 918{
c8f4eac4 919 debugs (47, 3, "Creating swap space in " << path);
920
921 struct stat swap_sb;
922 int swap;
923
924 if (::stat(path, &swap_sb) < 0) {
925 debugs (47, 2, "COSS swap space space being allocated.");
7be3caf3 926#ifdef _SQUID_MSWIN_
927
928 mkdir(path);
929#else
930
c8f4eac4 931 mkdir(path, 0700);
7be3caf3 932#endif
933
c8f4eac4 934 }
935
936 /* should check here for directories instead of files, and for file size
937 * TODO - if nothing changes, there is nothing to do
938 */
939 swap = open(stripePath(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
940
941 /* TODO just set the file size */
942 /* swap size is in K */
943 char *block[1024];
944
945 memset(&block, '\0', 1024);
946
947 for (off_t offset = 0; offset < max_size; ++offset) {
948 if (write (swap, block, 1024) < 1024) {
949 debugs (47, 0, "Failed to create COSS swap space in " << path);
950 }
951 }
952
953 close (swap);
954
cd748f27 955}
956
a4b8110e 957/* we are shutting down, flush all membufs to disk */
d3b3ab85 958CossSwapDir::~CossSwapDir()
cd748f27 959{
b9ae18aa 960 io->sync();
c8f4eac4 961
962 if (theFile != NULL)
963 theFile->close();
964
b9ae18aa 965 delete io;
cd748f27 966
d3b3ab85 967 closeLog();
c8f4eac4 968
cd748f27 969 n_coss_dirs--;
c8f4eac4 970
b9ae18aa 971 safe_free(ioModule);
c8f4eac4 972
973 safe_free(stripe_path);
cd748f27 974}
975
976/*
977 * storeCossDirCheckObj
978 *
979 * This routine is called by storeDirSelectSwapDir to see if the given
980 * object is able to be stored on this filesystem. COSS filesystems will
981 * not store everything. We don't check for maxobjsize here since its
982 * done by the upper layers.
983 */
984int
d3b3ab85 985CossSwapDir::canStore(StoreEntry const &e)const
cd748f27 986{
0dbabb56 987
a4b8110e 988 /* Check if the object is a special object, we can't cache these */
62e76326 989
d3b3ab85 990 if (EBIT_TEST(e.flags, ENTRY_SPECIAL))
62e76326 991 return -1;
cd748f27 992
a4b8110e 993 /* Otherwise, we're ok */
0dbabb56 994 /* Return load, cs->aq.aq_numpending out of MAX_ASYNCOP */
b9ae18aa 995 return io->load();
cd748f27 996}
a4b8110e 997
0e23343f 998/*
999 * storeCossDirCallback - do the IO completions
1000 */
d3b3ab85 1001int
1002CossSwapDir::callback()
0e23343f 1003{
b9ae18aa 1004 return io->callback();
0e23343f 1005}
1006
cd748f27 1007/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
1008
d3b3ab85 1009void
1010CossSwapDir::statfs(StoreEntry & sentry) const
cd748f27 1011{
d3b3ab85 1012 storeAppendPrintf(&sentry, "\n");
1013 storeAppendPrintf(&sentry, "Maximum Size: %d KB\n", max_size);
1014 storeAppendPrintf(&sentry, "Current Size: %d KB\n", cur_size);
1015 storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n",
62e76326 1016 100.0 * cur_size / max_size);
d3b3ab85 1017 storeAppendPrintf(&sentry, "Number of object collisions: %d\n", (int) numcollisions);
cd748f27 1018#if 0
1019 /* is this applicable? I Hope not .. */
1020 storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
62e76326 1021 SD->map->n_files_in_map, SD->map->max_n_files,
1022 percent(SD->map->n_files_in_map, SD->map->max_n_files));
cd748f27 1023#endif
62e76326 1024
b9ae18aa 1025 // storeAppendPrintf(&sentry, "Pending operations: %d out of %d\n", io->aq.aq_numpending, MAX_ASYNCOP);
d3b3ab85 1026 storeAppendPrintf(&sentry, "Flags:");
62e76326 1027
d3b3ab85 1028 if (flags.selected)
62e76326 1029 storeAppendPrintf(&sentry, " SELECTED");
1030
d3b3ab85 1031 if (flags.read_only)
62e76326 1032 storeAppendPrintf(&sentry, " READ-ONLY");
1033
d3b3ab85 1034 storeAppendPrintf(&sentry, "\n");
cd748f27 1035}
1036
d3b3ab85 1037void
1038CossSwapDir::parse(int anIndex, char *aPath)
cd748f27 1039{
cd748f27 1040 unsigned int i;
1041 unsigned int size;
81ec98e4 1042 unsigned long max_offset;
cd748f27 1043
1044 i = GetInteger();
1045 size = i << 10; /* Mbytes to Kbytes */
62e76326 1046
cd748f27 1047 if (size <= 0)
62e76326 1048 fatal("storeCossDirParse: invalid size value");
cd748f27 1049
d3b3ab85 1050 index = anIndex;
62e76326 1051
d3b3ab85 1052 path = xstrdup(aPath);
62e76326 1053
d3b3ab85 1054 max_size = size;
1055
450e0c10 1056 parseOptions(0);
1057
c94af0b9 1058 /* Enforce maxobjsize being set to something */
d3b3ab85 1059 if (max_objsize == -1)
62e76326 1060 fatal("COSS requires max-size to be set to something other than -1!\n");
1a224843 1061
1062 if (max_objsize > COSS_MEMBUF_SZ)
1063 fatalf("COSS max-size option must be less than COSS_MEMBUF_SZ (%d)\n",
1064 COSS_MEMBUF_SZ);
1065
1066 /*
1067 * check that we won't overflow sfileno later. 0xFFFFFF is the
1068 * largest possible sfileno, assuming sfileno is a 25-bit
1069 * signed integer, as defined in structs.h.
1070 */
81ec98e4 1071 max_offset = (unsigned long) 0xFFFFFF << blksz_bits;
1a224843 1072
81ec98e4 1073 if ((unsigned long)max_size > (unsigned long)(max_offset>>10)) {
1a224843 1074 debug(47,0)("COSS block-size = %d bytes\n", 1<<blksz_bits);
4989878c 1075 debugs(47,0, "COSS largest file offset = " << (max_offset >> 10) << " KB");
1a224843 1076 debug(47,0)("COSS cache_dir size = %d KB\n", max_size);
1077 fatal("COSS cache_dir size exceeds largest offset\n");
1078 }
cd748f27 1079}
1080
1081
d3b3ab85 1082void
1083CossSwapDir::reconfigure(int index, char *path)
cd748f27 1084{
cd748f27 1085 unsigned int i;
1086 unsigned int size;
cd748f27 1087
1088 i = GetInteger();
1089 size = i << 10; /* Mbytes to Kbytes */
62e76326 1090
cd748f27 1091 if (size <= 0)
62e76326 1092 fatal("storeCossDirParse: invalid size value");
cd748f27 1093
d3b3ab85 1094 if (size == (size_t)max_size)
62e76326 1095 debug(3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size);
cd748f27 1096 else {
62e76326 1097 debug(3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size);
1098 max_size = size;
cd748f27 1099 }
62e76326 1100
c94af0b9 1101 /* Enforce maxobjsize being set to something */
d3b3ab85 1102 if (max_objsize == -1)
62e76326 1103 fatal("COSS requires max-size to be set to something other than -1!\n");
cd748f27 1104}
1105
1106void
d3b3ab85 1107CossSwapDir::dump(StoreEntry &entry)const
cd748f27 1108{
59b2d47f 1109 storeAppendPrintf(&entry, " %d", max_size >> 20);
1110 dumpOptions(&entry);
cd748f27 1111}
1112
c8f4eac4 1113CossSwapDir::CossSwapDir() : SwapDir ("coss"), swaplog_fd(-1), count(0), current_membuf (NULL), current_offset(0), numcollisions(0), blksz_bits(0), io (NULL), ioModule(NULL), currentIOOptions(new ConfigOptionVector()), stripe_path(NULL)
cd748f27 1114{
1a224843 1115 membufs.head = NULL;
1116 membufs.tail = NULL;
1117 cossindex.head = NULL;
1118 cossindex.tail = NULL;
1119 blksz_mask = (1 << blksz_bits) - 1;
c8f4eac4 1120 repl = NULL;
1a224843 1121}
a4b8110e 1122
1a224843 1123bool
1124CossSwapDir::optionBlockSizeParse(const char *option, const char *value, int reconfiguring)
1125{
d9e04dc7 1126 assert(option);
1127
1128 if (strcmp(option, "block-size") != 0)
1129 return false;
1130
1131 if (!value)
1132 self_destruct();
1133
1a224843 1134 int blksz = atoi(value);
62e76326 1135
1a224843 1136 if (blksz == (1 << blksz_bits))
1137 /* no change */
1138 return true;
1139
1140 if (reconfiguring) {
1141 debug(47, 0) ("WARNING: cannot change COSS block-size while"
1142 " Squid is running\n");
1143 return false;
cd748f27 1144 }
62e76326 1145
1a224843 1146 int nbits = 0;
1147 int check = blksz;
1148
1149 while (check > 1) {
1150 nbits++;
1151 check >>= 1;
cd748f27 1152 }
62e76326 1153
1a224843 1154 check = 1 << nbits;
1155
1156 if (check != blksz)
1157 fatal("COSS block-size must be a power of 2\n");
1158
1159 if (nbits > 13)
1160 fatal("COSS block-size must be 8192 or smaller\n");
1161
1162 blksz_bits = nbits;
1163
1164 blksz_mask = (1 << blksz_bits) - 1;
1165
1166 return true;
cd748f27 1167}
62e76326 1168
1a224843 1169void
1170CossSwapDir::optionBlockSizeDump(StoreEntry * e) const
d3b3ab85 1171{
1a224843 1172 storeAppendPrintf(e, " block-size=%d", 1 << blksz_bits);
d3b3ab85 1173}
c8f4eac4 1174
1175StoreSearch *
1176CossSwapDir::search(String const url, HttpRequest *)
1177{
1178 if (url.size())
1179 fatal ("Cannot search by url yet\n");
1180
1181 return new StoreSearchCoss (this);
1182}
1183
1184char const *
1185CossSwapDir::stripePath() const
1186{
1187 if (!stripe_path) {
1188 String result = path;
1189 result.append("/stripe");
1190 const_cast<CossSwapDir *>(this)->stripe_path = xstrdup(result.buf());
1191 }
1192
1193 return stripe_path;
1194}
1195
1196CBDATA_CLASS_INIT(StoreSearchCoss);
1197StoreSearchCoss::StoreSearchCoss(RefCount<CossSwapDir> aSwapDir) : sd(aSwapDir), callback (NULL), cbdata(NULL), _done (false), current(NULL), next_(sd->cossindex.tail)
1198{
1199 /* TODO: this races with the store as does the cleanlog stuff.
1200 * FIXME by making coss_nodes ref counted */
1201}
1202
1203/* do not link
1204StoreSearchCoss::StoreSearchCoss(StoreSearchCoss const &);
1205*/
1206
1207StoreSearchCoss::~StoreSearchCoss()
1208{}
1209
1210void
1211StoreSearchCoss::next(void (callback)(void *cbdata), void *cbdata)
1212{
1213 next();
1214 callback (cbdata);
1215}
1216
1217bool
1218StoreSearchCoss::next()
1219{
1220 current = next_;
1221
1222 if (next_)
1223 next_ = next_->prev;
1224
1225 if (!current)
1226 _done = true;
1227
1228 return current != NULL;
1229}
1230
1231bool
1232StoreSearchCoss::error() const
1233{
1234 return false;
1235}
1236
1237bool
1238StoreSearchCoss::isDone() const
1239{
1240 return _done;
1241}
1242
1243StoreEntry *
1244StoreSearchCoss::currentItem()
1245{
1246 if (!current)
1247 return NULL;
1248
1249 return static_cast<StoreEntry *>( current->data );
1250}