]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs/coss/store_dir_coss.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / fs / coss / store_dir_coss.cc
CommitLineData
db263d62 1#error COSS Support is not stable yet in Squid-3. Please do not use.
cd748f27 2/*
262a0e14 3 * $Id$
26ac0430 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.
26ac0430 25 *
cd748f27 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.
26ac0430 30 *
cd748f27 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"
a553a5a3 42#include "event.h"
528b2c61 43#include "fde.h"
d3b3ab85 44#include "SwapDir.h"
51ee7c82 45#include "StoreSwapLogData.h"
b9ae18aa 46#include "DiskIO/DiskIOModule.h"
47#include "DiskIO/DiskIOStrategy.h"
48#include "DiskIO/ReadRequest.h"
49#include "ConfigOption.h"
50#include "StoreFScoss.h"
c8f4eac4 51#include "Parsing.h"
cd748f27 52
53#define STORE_META_BUFSZ 4096
54
cd748f27 55int n_coss_dirs = 0;
56/* static int last_coss_pick_index = -1; */
a3efa961 57MemAllocator *coss_index_pool = NULL;
cd748f27 58
59typedef struct _RebuildState RebuildState;
62e76326 60
26ac0430 61struct _RebuildState {
d3b3ab85 62 CossSwapDir *sd;
cd748f27 63 int n_read;
64 FILE *log;
65 int speed;
62e76326 66
26ac0430 67 struct {
3d0ac046
HN
68 unsigned int clean:1;
69 } flags;
62e76326 70
cd748f27 71 struct _store_rebuild_data counts;
72};
73
74static char *storeCossDirSwapLogFile(SwapDir *, const char *);
75static EVH storeCossRebuildFromSwapLog;
d3b3ab85 76static StoreEntry *storeCossAddDiskRestore(CossSwapDir * SD, const cache_key * key,
62e76326 77 int file_number,
47f6e231 78 uint64_t swap_file_sz,
62e76326 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);
d3b3ab85 86static void storeCossDirRebuild(CossSwapDir * sd);
87static void storeCossDirCloseTmpSwapLog(CossSwapDir * sd);
88static FILE *storeCossDirOpenTmpSwapLog(CossSwapDir *, int *, int *);
cd748f27 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;
62e76326 97
cd748f27 98 if (Config.Log.swap) {
62e76326 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
3d0ac046 108 for (pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++);
62e76326 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 }
cd748f27 116 } else {
62e76326 117 xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64);
118 strcat(path, "/swap.state");
cd748f27 119 }
62e76326 120
cd748f27 121 if (ext)
62e76326 122 strncat(path, ext, 16);
123
cd748f27 124 return path;
125}
126
d3b3ab85 127void
128CossSwapDir::openLog()
cd748f27 129{
d3b3ab85 130 char *logPath;
131 logPath = storeCossDirSwapLogFile(this, NULL);
132 swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY);
62e76326 133
d3b3ab85 134 if (swaplog_fd < 0) {
bf8fe701 135 debugs(47, 1, "" << logPath << ": " << xstrerror());
62e76326 136 fatal("storeCossDirOpenSwapLog: Failed to open swap log.");
cd748f27 137 }
62e76326 138
bf8fe701 139 debugs(47, 3, "Cache COSS Dir #" << index << " log opened on FD " << swaplog_fd);
cd748f27 140}
141
d3b3ab85 142void
143CossSwapDir::closeLog()
cd748f27 144{
d3b3ab85 145 if (swaplog_fd < 0) /* not open */
62e76326 146 return;
147
d3b3ab85 148 file_close(swaplog_fd);
62e76326 149
bf8fe701 150 debugs(47, 3, "Cache COSS Dir #" << index << " log closed on FD " << swaplog_fd);
62e76326 151
d3b3ab85 152 swaplog_fd = -1;
cd748f27 153}
154
d3b3ab85 155void
b9ae18aa 156CossSwapDir::ioCompletedNotification()
d3b3ab85 157{
b9ae18aa 158 if (theFile->error()) {
bf8fe701 159 debugs(47, 1, "" << path << ": " << xstrerror());
1a224843 160 fatal("storeCossDirInit: Failed to open a COSS file.");
697c29aa 161 }
b9ae18aa 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;
c6ee6676 177 StoreIOState::STRCB *callback = sio->read.callback;
b9ae18aa 178 char *p;
179 CossState *cstate = dynamic_cast<CossState *>(sio.getRaw());
180 ssize_t rlen;
181
bf8fe701 182 debugs(79, 3, "storeCossReadDone: fileno " << sio->swap_filen << ", len " << len);
b9ae18aa 183 cstate->flags.reading = 0;
184
185 if (errflag) {
186 StoreFScoss::GetInstance().stats.read.fail++;
187
188 if (errflag > 0) {
189 errno = errflag;
bf8fe701 190 debugs(79, 1, "storeCossReadDone: error: " << xstrerror());
b9ae18aa 191 } else {
bf8fe701 192 debugs(79, 1, "storeCossReadDone: got failure (" << errflag << ")");
b9ae18aa 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))
c6ee6676 216 callback(cbdata, cstate->requestbuf, rlen, sio);
b9ae18aa 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
4a7a3d56 225 debugs(79, 3, "storeCossWriteMemBufDone: buf " << cossWrite->membuf << ", len " << len);
b9ae18aa 226
227
228 if (errflag) {
229 StoreFScoss::GetInstance().stats.stripe_write.fail++;
bf8fe701 230 debugs(79, 1, "storeCossWriteMemBufDone: got failure (" << errflag << ")");
ddd1cefa 231 debugs(79, 1, "size=" << cossWrite->membuf->diskend - cossWrite->membuf->diskstart);
b9ae18aa 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
e2a8733e 253 if (currentIOOptions == NULL)
254 currentIOOptions = new ConfigOptionVector();
255
b9ae18aa 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
e2a8733e 303 if (currentIOOptions == NULL)
304 currentIOOptions = new ConfigOptionVector();
305
b9ae18aa 306 currentIOOptions->options.push_back(parentResult);
e2a8733e 307
b9ae18aa 308 currentIOOptions->options.push_back(new ConfigOptionAdapter<CossSwapDir>(*const_cast<CossSwapDir *>(this), &CossSwapDir::optionIOParse, &CossSwapDir::optionIODump));
e2a8733e 309
b9ae18aa 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
e2a8733e 324 ConfigOption* result = currentIOOptions;
325
326 currentIOOptions = NULL;
327
328 return result;
b9ae18aa 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);
c8f4eac4 341 theFile = io->newFile(stripePath());
b9ae18aa 342 theFile->open(O_RDWR | O_CREAT, 0644, this);
62e76326 343
b9ae18aa 344 ++n_coss_dirs;
1a224843 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;
cd748f27 352}
353
6a566b9c 354void
d3b3ab85 355storeCossRemove(CossSwapDir * sd, StoreEntry * e)
6a566b9c 356{
e6ccf245 357 CossIndexNode *coss_node = (CossIndexNode *)e->repl.data;
6a566b9c 358 e->repl.data = NULL;
d3b3ab85 359 dlinkDelete(&coss_node->node, &sd->cossindex);
b001e822 360 coss_index_pool->free(coss_node);
d3b3ab85 361 sd->count -= 1;
6a566b9c 362}
363
364void
d3b3ab85 365storeCossAdd(CossSwapDir * sd, StoreEntry * e)
6a566b9c 366{
b001e822 367 CossIndexNode *coss_node = (CossIndexNode *)coss_index_pool->alloc();
6a566b9c 368 assert(!e->repl.data);
369 e->repl.data = coss_node;
d3b3ab85 370 dlinkAdd(e, &coss_node->node, &sd->cossindex);
371 sd->count += 1;
6a566b9c 372}
373
374static void
375storeCossRebuildComplete(void *data)
376{
e6ccf245 377 RebuildState *rb = (RebuildState *)data;
d3b3ab85 378 CossSwapDir *sd = rb->sd;
b9ae18aa 379 sd->startMembuf();
bef81ea5 380 StoreController::store_dirs_rebuilding--;
6a566b9c 381 storeCossDirCloseTmpSwapLog(rb->sd);
382 storeRebuildComplete(&rb->counts);
383 cbdataFree(rb);
384}
385
cd748f27 386static void
387storeCossRebuildFromSwapLog(void *data)
388{
e6ccf245 389 RebuildState *rb = (RebuildState *)data;
cd748f27 390 StoreEntry *e = NULL;
51ee7c82 391 StoreSwapLogData s;
392 size_t ss = sizeof(StoreSwapLogData);
cd748f27 393 double x;
394 assert(rb != NULL);
395 /* load a number of objects per invocation */
62e76326 396
d3b3ab85 397 for (int aCount = 0; aCount < rb->speed; aCount++) {
62e76326 398 if (fread(&s, ss, 1, rb->log) != 1) {
bf8fe701 399 debugs(47, 1, "Done reading " << rb->sd->path << " swaplog (" << rb->n_read << " entries)");
62e76326 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
bf8fe701 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);
62e76326 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
c8f4eac4 424 if ((e = rb->sd->get
425 (s.key)) != NULL && s.lastref > e->lastref) {
62e76326 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 */
d88e3c49 433 e->expireNow();
434 e->releaseRequest();
62e76326 435
436 if (e->swap_filen > -1) {
437 e->swap_filen = -1;
438 }
439
5f33b71d 440 e->release();
62e76326 441 /* Fake an unlink here, this is a bad hack :( */
442 storeCossRemove(rb->sd, e);
443 rb->counts.objcount--;
444 rb->counts.cancelcount++;
26ac0430
AJ
445 }
446 continue;
62e76326 447 } else {
411c6ea3 448 x = log(static_cast<double>(++rb->counts.bad_log_op)) / log(10.0);
62e76326 449
411c6ea3 450 if (0.0 == x - (double)
451 (int) x)
bf8fe701 452 debugs(47, 1, "WARNING: " << rb->counts.bad_log_op << " invalid swap log entries found");
62e76326 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
c8f4eac4 473 e = rb->sd->get
474 (s.key);
62e76326 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);
cd748f27 498 }
62e76326 499
cd748f27 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 *
d3b3ab85 506storeCossAddDiskRestore(CossSwapDir * SD, const cache_key * key,
62e76326 507 int file_number,
47f6e231 508 uint64_t swap_file_sz,
62e76326 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)
cd748f27 516{
517 StoreEntry *e = NULL;
bf8fe701 518 debugs(47, 5, "storeCossAddDiskRestore: " << storeKeyText(key) <<
519 ", fileno="<< std::setfill('0') << std::hex << std::uppercase <<
520 std::setw(8) << file_number);
521
62e76326 522 /* if you call this you'd better be sure file_number is not
cd748f27 523 * already in use! */
c8f4eac4 524 e = new StoreEntry();
cd748f27 525 e->store_status = STORE_OK;
526 e->swap_dirn = SD->index;
3900307b 527 e->setMemStatus(NOT_IN_MEMORY);
cd748f27 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;
cd748f27 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);
3fba691e 543 e->hashInsert(key); /* do it after we clear KEY_PRIVATE */
6a566b9c 544 storeCossAdd(SD, e);
1a224843 545 assert(e->swap_filen >= 0);
cd748f27 546 return e;
547}
548
28c60158 549CBDATA_TYPE(RebuildState);
cd748f27 550static void
d3b3ab85 551storeCossDirRebuild(CossSwapDir * sd)
cd748f27 552{
28c60158 553 RebuildState *rb;
cd748f27 554 int clean = 0;
555 int zero = 0;
556 FILE *fp;
28c60158 557 CBDATA_INIT_TYPE(RebuildState);
72711e31 558 rb = cbdataAlloc(RebuildState);
cd748f27 559 rb->sd = sd;
560 rb->speed = opt_foreground_rebuild ? 1 << 30 : 50;
6a566b9c 561 rb->flags.clean = (unsigned int) clean;
cd748f27 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);
bf8fe701 567 debugs(47, 1, "Rebuilding COSS storage in " << sd->path << " (" << (clean ? "CLEAN" : "DIRTY") << ")");
6a566b9c 568 rb->log = fp;
bef81ea5 569 StoreController::store_dirs_rebuilding++;
62e76326 570
cd748f27 571 if (!clean || fp == NULL) {
62e76326 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;
cd748f27 590 }
62e76326 591
c8f4eac4 592 eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1);
cd748f27 593}
594
595static void
d3b3ab85 596storeCossDirCloseTmpSwapLog(CossSwapDir * sd)
cd748f27 597{
cd748f27 598 char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
599 char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
d3b3ab85 600 int anfd;
601 file_close(sd->swaplog_fd);
62e76326 602
cd748f27 603 if (xrename(new_path, swaplog_path) < 0) {
62e76326 604 fatal("storeCossDirCloseTmpSwapLog: rename failed");
cd748f27 605 }
62e76326 606
d3b3ab85 607 anfd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY);
62e76326 608
d3b3ab85 609 if (anfd < 0) {
bf8fe701 610 debugs(50, 1, "" << swaplog_path << ": " << xstrerror());
62e76326 611 fatal("storeCossDirCloseTmpSwapLog: Failed to open swap log.");
cd748f27 612 }
62e76326 613
cd748f27 614 safe_free(swaplog_path);
615 safe_free(new_path);
d3b3ab85 616 sd->swaplog_fd = anfd;
bf8fe701 617 debugs(47, 3, "Cache COSS Dir #" << sd->index << " log opened on FD " << anfd);
cd748f27 618}
619
620static FILE *
d3b3ab85 621storeCossDirOpenTmpSwapLog(CossSwapDir * sd, int *clean_flag, int *zero_flag)
cd748f27 622{
cd748f27 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"));
62e76326 626
cd748f27 627 struct stat log_sb;
62e76326 628
cd748f27 629 struct stat clean_sb;
630 FILE *fp;
d3b3ab85 631 int anfd;
62e76326 632
c8f4eac4 633 if (::stat(swaplog_path, &log_sb) < 0) {
bf8fe701 634 debugs(50, 1, "Cache COSS Dir #" << sd->index << ": No log file");
62e76326 635 safe_free(swaplog_path);
636 safe_free(clean_path);
637 safe_free(new_path);
638 return NULL;
cd748f27 639 }
62e76326 640
cd748f27 641 *zero_flag = log_sb.st_size == 0 ? 1 : 0;
642 /* close the existing write-only FD */
62e76326 643
d3b3ab85 644 if (sd->swaplog_fd >= 0)
62e76326 645 file_close(sd->swaplog_fd);
646
cd748f27 647 /* open a write-only FD for the new log */
d3b3ab85 648 anfd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
62e76326 649
d3b3ab85 650 if (anfd < 0) {
bf8fe701 651 debugs(50, 1, "" << new_path << ": " << xstrerror());
62e76326 652 fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
cd748f27 653 }
62e76326 654
d3b3ab85 655 sd->swaplog_fd = anfd;
cd748f27 656 /* open a read-only stream of the old log */
3d37fe17 657 fp = fopen(swaplog_path, "rb");
62e76326 658
cd748f27 659 if (fp == NULL) {
bf8fe701 660 debugs(50, 0, "" << swaplog_path << ": " << xstrerror());
62e76326 661 fatal("Failed to open swap log for reading");
cd748f27 662 }
62e76326 663
cd748f27 664 memset(&clean_sb, '\0', sizeof(struct stat));
62e76326 665
c8f4eac4 666 if (::stat(clean_path, &clean_sb) < 0)
62e76326 667 *clean_flag = 0;
cd748f27 668 else if (clean_sb.st_mtime < log_sb.st_mtime)
62e76326 669 *clean_flag = 0;
cd748f27 670 else
62e76326 671 *clean_flag = 1;
672
cd748f27 673 safeunlink(clean_path, 1);
62e76326 674
cd748f27 675 safe_free(swaplog_path);
62e76326 676
cd748f27 677 safe_free(clean_path);
62e76326 678
cd748f27 679 safe_free(new_path);
62e76326 680
cd748f27 681 return fp;
682}
683
62e76326 684class CossCleanLog : public SwapDir::CleanLog
685{
686
687public:
b9ae18aa 688 CossCleanLog(CossSwapDir *);
d3b3ab85 689 virtual const StoreEntry *nextEntry();
690 virtual void write(StoreEntry const &);
cd748f27 691 char *cur;
e6ccf245 692 char *newLog;
cd748f27 693 char *cln;
694 char *outbuf;
695 off_t outbuf_offset;
696 int fd;
6a566b9c 697 dlink_node *current;
b9ae18aa 698 CossSwapDir *sd;
cd748f27 699};
700
701#define CLEAN_BUF_SZ 16384
d3b3ab85 702
b9ae18aa 703CossCleanLog::CossCleanLog(CossSwapDir *aSwapDir) : cur(NULL),newLog(NULL),cln(NULL),outbuf(NULL),
62e76326 704 outbuf_offset(0), fd(-1),current(NULL), sd(aSwapDir)
705{}
d3b3ab85 706
cd748f27 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 */
d3b3ab85 712int
713CossSwapDir::writeCleanStart()
cd748f27 714{
d3b3ab85 715 CossCleanLog *state = new CossCleanLog(this);
1f56cfef 716#if HAVE_FCHMOD
62e76326 717
cd748f27 718 struct stat sb;
1f56cfef 719#endif
62e76326 720
d3b3ab85 721 state->newLog = xstrdup(storeCossDirSwapLogFile(this, ".clean"));
e6ccf245 722 state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
d3b3ab85 723 cleanLog = NULL;
62e76326 724
81928dfe 725 if (state->fd < 0) {
62e76326 726 xfree(state->newLog);
727 delete state;
728 return -1;
81928dfe 729 }
62e76326 730
d3b3ab85 731 state->cur = xstrdup(storeCossDirSwapLogFile(this, NULL));
732 state->cln = xstrdup(storeCossDirSwapLogFile(this, ".last-clean"));
e6ccf245 733 state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
cd748f27 734 state->outbuf_offset = 0;
d3b3ab85 735 ::unlink(state->cln);
736 state->current = cossindex.tail;
bf8fe701 737 debugs(50, 3, "storeCOssDirWriteCleanLogs: opened " << state->newLog << ", FD " << state->fd);
cd748f27 738#if HAVE_FCHMOD
62e76326 739
c8f4eac4 740 if (::stat(state->cur, &sb) == 0)
62e76326 741 fchmod(state->fd, sb.st_mode);
742
cd748f27 743#endif
62e76326 744
d3b3ab85 745 cleanLog = state;
6a566b9c 746
cd748f27 747 return 0;
748}
749
c8f4eac4 750/* RBC 20050101 - I think there is a race condition here,
751 * *current can be freed as its not ref counted, if/when
26ac0430 752 * the store overruns the log writer
c8f4eac4 753 */
d3b3ab85 754const StoreEntry *
755CossCleanLog::nextEntry()
6a566b9c 756{
6a566b9c 757 const StoreEntry *entry;
62e76326 758
d3b3ab85 759 if (!current)
62e76326 760 return NULL;
761
d3b3ab85 762 entry = (const StoreEntry *) current->data;
62e76326 763
d3b3ab85 764 current = current->prev;
62e76326 765
6a566b9c 766 return entry;
767}
768
cd748f27 769/*
770 * "write" an entry to the clean log file.
771 */
d3b3ab85 772void
773CossCleanLog::write(StoreEntry const &e)
cd748f27 774{
d3b3ab85 775 CossCleanLog *state = this;
51ee7c82 776 StoreSwapLogData s;
777 static size_t ss = sizeof(StoreSwapLogData);
cd748f27 778 s.op = (char) SWAP_LOG_ADD;
d3b3ab85 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;
c3031d67 787 xmemcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH);
b9ae18aa 788 xmemcpy(outbuf + outbuf_offset, &s, ss);
789 outbuf_offset += ss;
cd748f27 790 /* buffered write */
62e76326 791
b9ae18aa 792 if (outbuf_offset + ss > CLEAN_BUF_SZ) {
793 if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) {
bf8fe701 794 debugs(50, 0, "storeCossDirWriteCleanLogs: " << newLog << ": write: " << xstrerror());
795 debugs(50, 0, "storeCossDirWriteCleanLogs: Current swap logfile not replaced.");
b9ae18aa 796 file_close(fd);
797 fd = -1;
798 unlink(newLog);
62e76326 799 sd->cleanLog = NULL;
b9ae18aa 800 delete state;
62e76326 801 return;
802 }
803
b9ae18aa 804 outbuf_offset = 0;
cd748f27 805 }
806}
807
d3b3ab85 808void
809CossSwapDir::writeCleanDone()
cd748f27 810{
d3b3ab85 811 CossCleanLog *state = (CossCleanLog *)cleanLog;
62e76326 812
a7d542ee 813 if (NULL == state)
62e76326 814 return;
815
cd748f27 816 if (state->fd < 0)
62e76326 817 return;
818
d0ef8ea8 819 if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
bf8fe701 820 debugs(50, 0, "storeCossDirWriteCleanLogs: " << state->newLog << ": write: " << xstrerror());
821 debugs(50, 0, "storeCossDirWriteCleanLogs: Current swap logfile not replaced.");
62e76326 822 file_close(state->fd);
823 state->fd = -1;
824 ::unlink(state->newLog);
cd748f27 825 }
62e76326 826
cd748f27 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 */
d3b3ab85 832 closeLog();
3d37fe17 833 /* save the fd value for a later test */
d3b3ab85 834 int anfd = state->fd;
cd748f27 835 /* rename */
62e76326 836
cd748f27 837 if (state->fd >= 0) {
966f432f 838#if defined(_SQUID_OS2_) || defined(_SQUID_WIN32_)
62e76326 839 file_close(state->fd);
840 state->fd = -1;
841
cd748f27 842#endif
62e76326 843
844 xrename(state->newLog, state->cur);
cd748f27 845 }
62e76326 846
cd748f27 847 /* touch a timestamp file if we're not still validating */
bef81ea5 848 if (StoreController::store_dirs_rebuilding)
62e76326 849 (void) 0;
d3b3ab85 850 else if (anfd < 0)
62e76326 851 (void) 0;
cd748f27 852 else
62e76326 853 file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
854
cd748f27 855 /* close */
856 safe_free(state->cur);
62e76326 857
e6ccf245 858 safe_free(state->newLog);
62e76326 859
cd748f27 860 safe_free(state->cln);
62e76326 861
cd748f27 862 if (state->fd >= 0)
62e76326 863 file_close(state->fd);
864
cd748f27 865 state->fd = -1;
62e76326 866
d3b3ab85 867 delete state;
62e76326 868
d3b3ab85 869 cleanLog = NULL;
cd748f27 870}
871
061c7171 872static void
873FreeObject(void *address)
874{
875 StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address);
876 delete anObject;
877}
878
d3b3ab85 879void
880CossSwapDir::logEntry(const StoreEntry & e, int op) const
cd748f27 881{
51ee7c82 882 StoreSwapLogData *s = new StoreSwapLogData;
cd748f27 883 s->op = (char) op;
d3b3ab85 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;
c3031d67 892 xmemcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH);
d3b3ab85 893 file_write(swaplog_fd,
62e76326 894 -1,
895 s,
51ee7c82 896 sizeof(StoreSwapLogData),
62e76326 897 NULL,
898 NULL,
061c7171 899 &FreeObject);
cd748f27 900}
901
d3b3ab85 902void
c8f4eac4 903CossSwapDir::create()
cd748f27 904{
c8f4eac4 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.");
7be3caf3 912#ifdef _SQUID_MSWIN_
913
914 mkdir(path);
915#else
916
c8f4eac4 917 mkdir(path, 0700);
7be3caf3 918#endif
919
c8f4eac4 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
cd748f27 941}
942
a4b8110e 943/* we are shutting down, flush all membufs to disk */
d3b3ab85 944CossSwapDir::~CossSwapDir()
cd748f27 945{
b9ae18aa 946 io->sync();
c8f4eac4 947
948 if (theFile != NULL)
949 theFile->close();
950
b9ae18aa 951 delete io;
cd748f27 952
d3b3ab85 953 closeLog();
c8f4eac4 954
cd748f27 955 n_coss_dirs--;
c8f4eac4 956
b9ae18aa 957 safe_free(ioModule);
c8f4eac4 958
959 safe_free(stripe_path);
cd748f27 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
d3b3ab85 971CossSwapDir::canStore(StoreEntry const &e)const
cd748f27 972{
0dbabb56 973
a4b8110e 974 /* Check if the object is a special object, we can't cache these */
62e76326 975
d3b3ab85 976 if (EBIT_TEST(e.flags, ENTRY_SPECIAL))
62e76326 977 return -1;
cd748f27 978
a4b8110e 979 /* Otherwise, we're ok */
0dbabb56 980 /* Return load, cs->aq.aq_numpending out of MAX_ASYNCOP */
b9ae18aa 981 return io->load();
cd748f27 982}
a4b8110e 983
0e23343f 984/*
985 * storeCossDirCallback - do the IO completions
986 */
d3b3ab85 987int
988CossSwapDir::callback()
0e23343f 989{
b9ae18aa 990 return io->callback();
0e23343f 991}
992
cd748f27 993/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
994
d3b3ab85 995void
996CossSwapDir::statfs(StoreEntry & sentry) const
cd748f27 997{
d3b3ab85 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",
62e76326 1002 100.0 * cur_size / max_size);
d3b3ab85 1003 storeAppendPrintf(&sentry, "Number of object collisions: %d\n", (int) numcollisions);
cd748f27 1004#if 0
1005 /* is this applicable? I Hope not .. */
1006 storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
62e76326 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));
cd748f27 1009#endif
62e76326 1010
b9ae18aa 1011 // storeAppendPrintf(&sentry, "Pending operations: %d out of %d\n", io->aq.aq_numpending, MAX_ASYNCOP);
d3b3ab85 1012 storeAppendPrintf(&sentry, "Flags:");
62e76326 1013
d3b3ab85 1014 if (flags.selected)
62e76326 1015 storeAppendPrintf(&sentry, " SELECTED");
1016
d3b3ab85 1017 if (flags.read_only)
62e76326 1018 storeAppendPrintf(&sentry, " READ-ONLY");
1019
d3b3ab85 1020 storeAppendPrintf(&sentry, "\n");
cd748f27 1021}
1022
d3b3ab85 1023void
1024CossSwapDir::parse(int anIndex, char *aPath)
cd748f27 1025{
cd748f27 1026 unsigned int i;
1027 unsigned int size;
47f6e231 1028 off_t max_offset;
cd748f27 1029
1030 i = GetInteger();
1031 size = i << 10; /* Mbytes to Kbytes */
62e76326 1032
cd748f27 1033 if (size <= 0)
62e76326 1034 fatal("storeCossDirParse: invalid size value");
cd748f27 1035
d3b3ab85 1036 index = anIndex;
62e76326 1037
d3b3ab85 1038 path = xstrdup(aPath);
62e76326 1039
d3b3ab85 1040 max_size = size;
1041
450e0c10 1042 parseOptions(0);
1043
26ac0430 1044 if (NULL == io)
b7717b61 1045 changeIO(DiskIOModule::FindDefault());
1046
c94af0b9 1047 /* Enforce maxobjsize being set to something */
d3b3ab85 1048 if (max_objsize == -1)
62e76326 1049 fatal("COSS requires max-size to be set to something other than -1!\n");
1a224843 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 */
47f6e231 1060 max_offset = (off_t) 0xFFFFFF << blksz_bits;
1a224843 1061
47f6e231 1062 if ((off_t)max_size > (max_offset>>10)) {
bf8fe701 1063 debugs(47, 0, "COSS block-size = " << (1<<blksz_bits) << " bytes");
4989878c 1064 debugs(47,0, "COSS largest file offset = " << (max_offset >> 10) << " KB");
bf8fe701 1065 debugs(47, 0, "COSS cache_dir size = " << max_size << " KB");
1a224843 1066 fatal("COSS cache_dir size exceeds largest offset\n");
1067 }
cd748f27 1068}
1069
1070
d3b3ab85 1071void
1072CossSwapDir::reconfigure(int index, char *path)
cd748f27 1073{
cd748f27 1074 unsigned int i;
1075 unsigned int size;
cd748f27 1076
1077 i = GetInteger();
1078 size = i << 10; /* Mbytes to Kbytes */
62e76326 1079
cd748f27 1080 if (size <= 0)
62e76326 1081 fatal("storeCossDirParse: invalid size value");
cd748f27 1082
d3b3ab85 1083 if (size == (size_t)max_size)
bf8fe701 1084 debugs(3, 1, "Cache COSS dir '" << path << "' size remains unchanged at " << size << " KB");
cd748f27 1085 else {
bf8fe701 1086 debugs(3, 1, "Cache COSS dir '" << path << "' size changed to " << size << " KB");
62e76326 1087 max_size = size;
cd748f27 1088 }
62e76326 1089
c94af0b9 1090 /* Enforce maxobjsize being set to something */
d3b3ab85 1091 if (max_objsize == -1)
62e76326 1092 fatal("COSS requires max-size to be set to something other than -1!\n");
cd748f27 1093}
1094
1095void
d3b3ab85 1096CossSwapDir::dump(StoreEntry &entry)const
cd748f27 1097{
5c2d7edb 1098 storeAppendPrintf(&entry, " %d", max_size >> 10);
59b2d47f 1099 dumpOptions(&entry);
cd748f27 1100}
1101
c8f4eac4 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)
cd748f27 1103{
1a224843 1104 membufs.head = NULL;
1105 membufs.tail = NULL;
1106 cossindex.head = NULL;
1107 cossindex.tail = NULL;
1108 blksz_mask = (1 << blksz_bits) - 1;
c8f4eac4 1109 repl = NULL;
1a224843 1110}
a4b8110e 1111
1a224843 1112bool
1113CossSwapDir::optionBlockSizeParse(const char *option, const char *value, int reconfiguring)
1114{
d9e04dc7 1115 assert(option);
1116
1117 if (strcmp(option, "block-size") != 0)
1118 return false;
1119
1120 if (!value)
1121 self_destruct();
1122
1a224843 1123 int blksz = atoi(value);
62e76326 1124
1a224843 1125 if (blksz == (1 << blksz_bits))
1126 /* no change */
1127 return true;
1128
1129 if (reconfiguring) {
bf8fe701 1130 debugs(47, 0, "WARNING: cannot change COSS block-size while Squid is running");
1a224843 1131 return false;
cd748f27 1132 }
62e76326 1133
1a224843 1134 int nbits = 0;
1135 int check = blksz;
1136
1137 while (check > 1) {
1138 nbits++;
1139 check >>= 1;
cd748f27 1140 }
62e76326 1141
1a224843 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;
cd748f27 1155}
62e76326 1156
1a224843 1157void
1158CossSwapDir::optionBlockSizeDump(StoreEntry * e) const
d3b3ab85 1159{
1a224843 1160 storeAppendPrintf(e, " block-size=%d", 1 << blksz_bits);
d3b3ab85 1161}
c8f4eac4 1162
1163StoreSearch *
30abd221 1164CossSwapDir::search(String const url, HttpRequest *)
c8f4eac4 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) {
30abd221 1176 String result = path;
c8f4eac4 1177 result.append("/stripe");
30abd221 1178 const_cast<CossSwapDir *>(this)->stripe_path = xstrdup(result.buf());
c8f4eac4 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}