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