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