]> git.ipfire.org Git - thirdparty/squid.git/blob - src/fs/diskd/store_dir_diskd.cc
MODIO_1 commit. This change (including documentation) implements a more
[thirdparty/squid.git] / src / fs / diskd / store_dir_diskd.cc
1
2 /*
3 * $Id: store_dir_diskd.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $
4 *
5 * DEBUG: section 47 Store Directory Routines
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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.h"
37 #if HAVE_STATVFS
38 #if HAVE_SYS_STATVFS_H
39 #include <sys/statvfs.h>
40 #endif
41 #endif
42
43 #include <sys/ipc.h>
44 #include <sys/msg.h>
45 #include <sys/shm.h>
46
47 #include "store_diskd.h"
48
49 #define DefaultLevelOneDirs 16
50 #define DefaultLevelTwoDirs 256
51 #define STORE_META_BDISKDZ 4096
52
53 #ifndef SQUID_PREFIX
54 #error "SQUID_PREFIX needs defining!"
55 #endif
56
57 diskd_stats_t diskd_stats;
58
59 typedef struct _RebuildState RebuildState;
60 struct _RebuildState {
61 SwapDir *sd;
62 int n_read;
63 FILE *log;
64 int speed;
65 int curlvl1;
66 int curlvl2;
67 struct {
68 unsigned int need_to_validate:1;
69 unsigned int clean:1;
70 unsigned int init:1;
71 } flags;
72 int done;
73 int in_dir;
74 int fn;
75 struct dirent *entry;
76 DIR *td;
77 char fullpath[SQUID_MAXPATHLEN];
78 char fullfilename[SQUID_MAXPATHLEN];
79 struct _store_rebuild_data counts;
80 };
81
82 static int n_diskd_dirs = 0;
83 static int *diskd_dir_index = NULL;
84 MemPool * diskd_state_pool = NULL;
85 static int diskd_initialised = 0;
86
87 static char *storeDiskdDirSwapSubDir(SwapDir *, int subdirn);
88 static int storeDiskdDirCreateDirectory(const char *path, int);
89 static int storeDiskdDirVerifyCacheDirs(SwapDir *);
90 static int storeDiskdDirVerifyDirectory(const char *path);
91 static void storeDiskdDirCreateSwapSubDirs(SwapDir *);
92 static char *storeDiskdDirSwapLogFile(SwapDir *, const char *);
93 static EVH storeDiskdDirRebuildFromDirectory;
94 static EVH storeDiskdDirRebuildFromSwapLog;
95 static int storeDiskdDirGetNextFile(RebuildState *, int *sfileno, int *size);
96 static StoreEntry *storeDiskdDirAddDiskRestore(SwapDir *SD, const cache_key * key,
97 int file_number,
98 size_t swap_file_sz,
99 time_t expires,
100 time_t timestamp,
101 time_t lastref,
102 time_t lastmod,
103 u_num32 refcount,
104 u_short flags,
105 int clean);
106 static void storeDiskdDirRebuild(SwapDir * sd);
107 static void storeDiskdDirCloseTmpSwapLog(SwapDir * sd);
108 static FILE *storeDiskdDirOpenTmpSwapLog(SwapDir *, int *, int *);
109 static STLOGOPEN storeDiskdDirOpenSwapLog;
110 static STINIT storeDiskdDirInit;
111 static STFREE storeDiskdDirFree;
112 static STLOGCLEANOPEN storeDiskdDirWriteCleanOpen;
113 static void storeDiskdDirWriteCleanClose(SwapDir * sd);
114 static STLOGCLEANWRITE storeDiskdDirWriteCleanEntry;
115 static STLOGCLOSE storeDiskdDirCloseSwapLog;
116 static STLOGWRITE storeDiskdDirSwapLog;
117 static STNEWFS storeDiskdDirNewfs;
118 static STDUMP storeDiskdDirDump;
119 static STMAINTAINFS storeDiskdDirMaintain;
120 static STCHECKOBJ storeDiskdDirCheckObj;
121 static STREFOBJ storeDiskdDirRefObj;
122 static STUNREFOBJ storeDiskdDirUnrefObj;
123 static QS rev_int_sort;
124 static int storeDiskdDirClean(int swap_index);
125 static EVH storeDiskdDirCleanEvent;
126 static int storeDiskdDirIs(SwapDir * sd);
127 static int storeDiskdFilenoBelongsHere(int fn, int F0, int F1, int F2);
128 static int storeDiskdCleanupDoubleCheck(SwapDir *, StoreEntry *);
129 static void storeDiskdDirStats(SwapDir *, StoreEntry *);
130 static void storeDiskdDirInitBitmap(SwapDir *);
131 static int storeDiskdDirValidFileno(SwapDir *, sfileno);
132 static int storeDiskdDirCheckExpired(SwapDir *, StoreEntry *);
133 #if !HEAP_REPLACEMENT
134 static time_t storeDiskdDirExpiredReferenceAge(SwapDir *);
135 #endif
136 static void storeDiskdStats(StoreEntry * sentry);
137 static void storeDiskdDirSync(SwapDir *);
138 static void storeDiskdDirCallback(SwapDir *);
139
140
141 /*
142 * These functions were ripped straight out of the heart of store_dir.c.
143 * They assume that the given filenum is on a diskd partiton, which may or
144 * may not be true..
145 * XXX this evilness should be tidied up at a later date!
146 */
147
148 int
149 storeDiskdDirMapBitTest(SwapDir *SD, int fn)
150 {
151 sfileno filn = fn;
152 diskdinfo_t *diskdinfo;
153 diskdinfo = (diskdinfo_t *)SD->fsdata;
154 return file_map_bit_test(diskdinfo->map, filn);
155 }
156
157 void
158 storeDiskdDirMapBitSet(SwapDir *SD, int fn)
159 {
160 sfileno filn = fn;
161 diskdinfo_t *diskdinfo;
162 diskdinfo = (diskdinfo_t *)SD->fsdata;
163 file_map_bit_set(diskdinfo->map, filn);
164 }
165
166 void
167 storeDiskdDirMapBitReset(SwapDir *SD, int fn)
168 {
169 sfileno filn = fn;
170 diskdinfo_t *diskdinfo;
171 diskdinfo = (diskdinfo_t *)SD->fsdata;
172 file_map_bit_reset(diskdinfo->map, filn);
173 }
174
175 int
176 storeDiskdDirMapBitAllocate(SwapDir *SD)
177 {
178 diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
179 int fn;
180 fn = file_map_allocate(diskdinfo->map, diskdinfo->suggest);
181 file_map_bit_set(diskdinfo->map, fn);
182 diskdinfo->suggest = fn + 1;
183 return fn;
184 }
185
186 /*
187 * Initialise the diskd bitmap
188 *
189 * If there already is a bitmap, and the numobjects is larger than currently
190 * configured, we allocate a new bitmap and 'grow' the old one into it.
191 */
192 static void
193 storeDiskdDirInitBitmap(SwapDir *sd)
194 {
195 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
196
197 if (diskdinfo->map == NULL) {
198 /* First time */
199 diskdinfo->map = file_map_create();
200 } else if (diskdinfo->map->max_n_files) {
201 /* it grew, need to expand */
202 /* XXX We don't need it anymore .. */
203 }
204 /* else it shrunk, and we leave the old one in place */
205 }
206
207 static char *
208 storeDiskdDirSwapSubDir(SwapDir * sd, int subdirn)
209 {
210 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
211
212 LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
213 assert(0 <= subdirn && subdirn < diskdinfo->l1);
214 snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
215 return fullfilename;
216 }
217
218 static int
219 storeDiskdDirCreateDirectory(const char *path, int should_exist)
220 {
221 int created = 0;
222 struct stat st;
223 getCurrentTime();
224 if (0 == stat(path, &st)) {
225 if (S_ISDIR(st.st_mode)) {
226 debug(20, should_exist ? 3 : 1) ("%s exists\n", path);
227 } else {
228 fatalf("Swap directory %s is not a directory.", path);
229 }
230 } else if (0 == mkdir(path, 0755)) {
231 debug(20, should_exist ? 1 : 3) ("%s created\n", path);
232 created = 1;
233 } else {
234 fatalf("Failed to make swap directory %s: %s",
235 path, xstrerror());
236 }
237 return created;
238 }
239
240 static int
241 storeDiskdDirVerifyDirectory(const char *path)
242 {
243 struct stat sb;
244 if (stat(path, &sb) < 0) {
245 debug(20, 0) ("%s: %s\n", path, xstrerror());
246 return -1;
247 }
248 if (S_ISDIR(sb.st_mode) == 0) {
249 debug(20, 0) ("%s is not a directory\n", path);
250 return -1;
251 }
252 return 0;
253 }
254
255 /*
256 * This function is called by storeDiskdDirInit(). If this returns < 0,
257 * then Squid exits, complains about swap directories not
258 * existing, and instructs the admin to run 'squid -z'
259 */
260 static int
261 storeDiskdDirVerifyCacheDirs(SwapDir * sd)
262 {
263 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
264 int j;
265 const char *path = sd->path;
266
267 if (storeDiskdDirVerifyDirectory(path) < 0)
268 return -1;
269 for (j = 0; j < diskdinfo->l1; j++) {
270 path = storeDiskdDirSwapSubDir(sd, j);
271 if (storeDiskdDirVerifyDirectory(path) < 0)
272 return -1;
273 }
274 return 0;
275 }
276
277 static void
278 storeDiskdDirCreateSwapSubDirs(SwapDir * sd)
279 {
280 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
281 int i, k;
282 int should_exist;
283 LOCAL_ARRAY(char, name, MAXPATHLEN);
284 for (i = 0; i < diskdinfo->l1; i++) {
285 snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
286 if (storeDiskdDirCreateDirectory(name, 0))
287 should_exist = 0;
288 else
289 should_exist = 1;
290 debug(47, 1) ("Making directories in %s\n", name);
291 for (k = 0; k < diskdinfo->l2; k++) {
292 snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
293 storeDiskdDirCreateDirectory(name, should_exist);
294 }
295 }
296 }
297
298 static char *
299 storeDiskdDirSwapLogFile(SwapDir * sd, const char *ext)
300 {
301 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
302 LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN);
303 LOCAL_ARRAY(char, digit, 32);
304 char *pathtmp2;
305 if (Config.Log.swap) {
306 xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64);
307 while (index(pathtmp,'/'))
308 *index(pathtmp,'/')='.';
309 while (strlen(pathtmp) && pathtmp[strlen(pathtmp)-1]=='.')
310 pathtmp[strlen(pathtmp)-1]= '\0';
311 for(pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++);
312 snprintf(path, SQUID_MAXPATHLEN-64, Config.Log.swap, pathtmp2);
313 if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) {
314 strcat(path, ".");
315 snprintf(digit, 32, "%02d", sd->index);
316 strncat(path, digit, 3);
317 }
318 } else {
319 xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64);
320 strcat(path, "/swap.state");
321 }
322 if (ext)
323 strncat(path, ext, 16);
324 return path;
325 }
326
327 static void
328 storeDiskdDirOpenSwapLog(SwapDir * sd)
329 {
330 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
331 char *path;
332 int fd;
333 path = storeDiskdDirSwapLogFile(sd, NULL);
334 fd = file_open(path, O_WRONLY | O_CREAT);
335 if (fd < 0) {
336 debug(50, 1) ("%s: %s\n", path, xstrerror());
337 fatal("storeDiskdDirOpenSwapLog: Failed to open swap log.");
338 }
339 debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
340 diskdinfo->swaplog_fd = fd;
341 if (0 == n_diskd_dirs)
342 assert(NULL == diskd_dir_index);
343 n_diskd_dirs++;
344 assert(n_diskd_dirs <= Config.cacheSwap.n_configured);
345 }
346
347 static void
348 storeDiskdDirCloseSwapLog(SwapDir * sd)
349 {
350 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
351 if (diskdinfo->swaplog_fd < 0) /* not open */
352 return;
353 file_close(diskdinfo->swaplog_fd);
354 debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
355 sd->index, diskdinfo->swaplog_fd);
356 diskdinfo->swaplog_fd = -1;
357 n_diskd_dirs--;
358 assert(n_diskd_dirs >= 0);
359 if (0 == n_diskd_dirs)
360 safe_free(diskd_dir_index);
361 }
362
363 static void
364 storeDiskdDirInit(SwapDir * sd)
365 {
366 static int started_clean_event = 0;
367 int x;
368 int i;
369 int rfd;
370 int ikey = (getpid() << 16) + (sd->index << 4);
371 char *args[5];
372 char skey1[32];
373 char skey2[32];
374 char skey3[32];
375 diskdinfo_t *diskdinfo = (diskdinfo_t *) sd->fsdata;
376 static const char *errmsg =
377 "\tFailed to verify one of the swap directories, Check cache.log\n"
378 "\tfor details. Run 'squid -z' to create swap directories\n"
379 "\tif needed, or if running Squid for the first time.";
380
381 diskdinfo->smsgid = msgget((key_t) ikey, 0700 | IPC_CREAT);
382 if (diskdinfo->smsgid < 0) {
383 debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
384 fatal("msgget failed");
385 }
386 diskdinfo->rmsgid = msgget((key_t) (ikey + 1), 0700 | IPC_CREAT);
387 if (diskdinfo->rmsgid < 0) {
388 debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
389 fatal("msgget failed");
390 }
391 diskdinfo->shm.id = shmget((key_t) (ikey + 2),
392 SHMBUFS * SHMBUF_BLKSZ, 0600 | IPC_CREAT);
393 if (diskdinfo->shm.id < 0) {
394 debug(50, 0) ("storeDiskdInit: shmget: %s\n", xstrerror());
395 fatal("shmget failed");
396 }
397 diskdinfo->shm.buf = shmat(diskdinfo->shm.id, NULL, 0);
398 if (diskdinfo->shm.buf == (void *) -1) {
399 debug(50, 0) ("storeDiskdInit: shmat: %s\n", xstrerror());
400 fatal("shmat failed");
401 }
402 diskd_stats.shmbuf_count += SHMBUFS;
403 for (i = 0; i < SHMBUFS; i++)
404 storeDiskdShmPut(sd, i * SHMBUF_BLKSZ);
405 snprintf(skey1, 32, "%d", ikey);
406 snprintf(skey2, 32, "%d", ikey + 1);
407 snprintf(skey3, 32, "%d", ikey + 2);
408 args[0] = "diskd";
409 args[1] = skey1;
410 args[2] = skey2;
411 args[3] = skey3;
412 args[4] = NULL;
413 #if HAVE_POLL && defined(_SQUID_OSF_)
414 /* pipes and poll() don't get along on DUNIX -DW */
415 x = ipcCreate(IPC_TCP_SOCKET,
416 #else
417 x = ipcCreate(IPC_FIFO,
418 #endif
419 SQUID_PREFIX "/bin/diskd",
420 args,
421 "diskd",
422 &rfd,
423 &diskdinfo->wfd);
424 if (x < 0)
425 fatal("execl " SQUID_PREFIX "/bin/diskd failed");
426 if (rfd != diskdinfo->wfd)
427 comm_close(rfd);
428 fd_note(diskdinfo->wfd, "squid -> diskd");
429 commSetTimeout(diskdinfo->wfd, -1, NULL, NULL);
430 commSetNonBlocking(diskdinfo->wfd);
431 storeDiskdDirInitBitmap(sd);
432 if (storeDiskdDirVerifyCacheDirs(sd) < 0)
433 fatal(errmsg);
434 storeDiskdDirOpenSwapLog(sd);
435 storeDiskdDirRebuild(sd);
436 if (!started_clean_event) {
437 eventAdd("storeDirClean", storeDiskdDirCleanEvent, NULL, 15.0, 1);
438 started_clean_event = 1;
439 }
440 }
441
442
443 static void
444 storeDiskdStats(StoreEntry * sentry)
445 {
446 storeAppendPrintf(sentry, "sent_count: %d\n", diskd_stats.sent_count);
447 storeAppendPrintf(sentry, "recv_count: %d\n", diskd_stats.recv_count);
448 storeAppendPrintf(sentry, "max_away: %d\n", diskd_stats.max_away);
449 storeAppendPrintf(sentry, "max_shmuse: %d\n", diskd_stats.max_shmuse);
450 storeAppendPrintf(sentry, "open_fail_queue_len: %d\n", diskd_stats.open_fail_queue_len);
451 storeAppendPrintf(sentry, "block_queue_len: %d\n", diskd_stats.block_queue_len);
452 diskd_stats.max_away = diskd_stats.max_shmuse = 0;
453 }
454
455 /*
456 * storeDiskdDirSync
457 *
458 * Sync any pending data. We just sit around and read the queue
459 * until the data has finished writing.
460 */
461 static void
462 storeDiskdDirSync(SwapDir *SD)
463 {
464 /* XXX NOT DONE YET! */
465 #warning "storeDiskdSync() needs to be written"
466 }
467
468
469 /*
470 * storeDiskdDirCallback
471 *
472 * Handle callbacks. If we have more than magic2 requests away, we block
473 * until the queue is below magic2. Otherwise, we simply return when we
474 * don't get a message.
475 */
476 static void
477 storeDiskdDirCallback(SwapDir *SD)
478 {
479 diomsg M;
480 int x;
481 diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
482
483 if (diskdinfo->away >= diskdinfo->magic2)
484 diskd_stats.block_queue_len++;
485
486 if (diskd_stats.sent_count - diskd_stats.recv_count >
487 diskd_stats.max_away) {
488 diskd_stats.max_away = diskd_stats.sent_count - diskd_stats.recv_count;
489 diskd_stats.max_shmuse = diskd_stats.shmbuf_count;
490 }
491
492 /* if we are above magic2, we do not break under any reason */
493 while (1) {
494 memset(&M, '\0', sizeof(M));
495 x = msgrcv(diskdinfo->rmsgid, &M, msg_snd_rcv_sz, 0, IPC_NOWAIT);
496 if (x < 0) {
497 if (diskdinfo->away >= diskdinfo->magic2)
498 continue;
499 else
500 break;
501 } else if (x != msg_snd_rcv_sz) {
502 debug(81, 1) ("storeDiskdReadIndividualQueue: msgget returns %d\n",
503 x);
504 break;
505 }
506 diskd_stats.recv_count++;
507 diskdinfo->away--;
508 storeDiskdHandle(&M);
509 if (M.shm_offset > -1)
510 storeDiskdShmPut(SD, M.shm_offset);
511 }
512 }
513
514
515
516 static void
517 storeDiskdDirRebuildFromDirectory(void *data)
518 {
519 RebuildState *rb = data;
520 SwapDir *SD = rb->sd;
521 LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE);
522 StoreEntry *e = NULL;
523 StoreEntry tmpe;
524 cache_key key[MD5_DIGEST_CHARS];
525 int sfileno = 0;
526 int count;
527 int size;
528 struct stat sb;
529 int swap_hdr_len;
530 int fd = -1;
531 tlv *tlv_list;
532 tlv *t;
533 assert(rb != NULL);
534 debug(20, 3) ("storeDiskdDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
535 for (count = 0; count < rb->speed; count++) {
536 assert(fd == -1);
537 fd = storeDiskdDirGetNextFile(rb, &sfileno, &size);
538 if (fd == -2) {
539 debug(20, 1) ("Done scanning %s swaplog (%d entries)\n",
540 rb->sd->path, rb->n_read);
541 store_dirs_rebuilding--;
542 storeDiskdDirCloseTmpSwapLog(rb->sd);
543 storeRebuildComplete(&rb->counts);
544 cbdataFree(rb);
545 return;
546 } else if (fd < 0) {
547 continue;
548 }
549 assert(fd > -1);
550 /* lets get file stats here */
551 if (fstat(fd, &sb) < 0) {
552 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: fstat(FD %d): %s\n",
553 fd, xstrerror());
554 file_close(fd);
555 store_open_disk_fd--;
556 fd = -1;
557 continue;
558 }
559 if ((++rb->counts.scancount & 0xFFFF) == 0)
560 debug(20, 3) (" %s %7d files opened so far.\n",
561 rb->sd->path, rb->counts.scancount);
562 debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno);
563 Counter.syscalls.disk.reads++;
564 if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
565 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: read(FD %d): %s\n",
566 fd, xstrerror());
567 file_close(fd);
568 store_open_disk_fd--;
569 fd = -1;
570 continue;
571 }
572 file_close(fd);
573 store_open_disk_fd--;
574 fd = -1;
575 swap_hdr_len = 0;
576 #if USE_TRUNCATE
577 if (sb.st_size == 0)
578 continue;
579 #endif
580 tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len);
581 if (tlv_list == NULL) {
582 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: failed to get meta data\n");
583 /* XXX shouldn't this be a call to storeDiskdUnlink ? */
584 storeDiskdDirUnlinkFile(SD, sfileno);
585 continue;
586 }
587 debug(20, 3) ("storeDiskdDirRebuildFromDirectory: successful swap meta unpacking\n");
588 memset(key, '\0', MD5_DIGEST_CHARS);
589 memset(&tmpe, '\0', sizeof(StoreEntry));
590 for (t = tlv_list; t; t = t->next) {
591 switch (t->type) {
592 case STORE_META_KEY:
593 assert(t->length == MD5_DIGEST_CHARS);
594 xmemcpy(key, t->value, MD5_DIGEST_CHARS);
595 break;
596 case STORE_META_STD:
597 assert(t->length == STORE_HDR_METASIZE);
598 xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE);
599 break;
600 default:
601 break;
602 }
603 }
604 storeSwapTLVFree(tlv_list);
605 tlv_list = NULL;
606 if (storeKeyNull(key)) {
607 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: NULL key\n");
608 storeDiskdDirUnlinkFile(SD, sfileno);
609 continue;
610 }
611 tmpe.key = key;
612 /* check sizes */
613 if (tmpe.swap_file_sz == 0) {
614 tmpe.swap_file_sz = sb.st_size;
615 } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
616 tmpe.swap_file_sz = sb.st_size;
617 } else if (tmpe.swap_file_sz != sb.st_size) {
618 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
619 tmpe.swap_file_sz, (int) sb.st_size);
620 storeDiskdDirUnlinkFile(SD, sfileno);
621 continue;
622 }
623 if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
624 storeDiskdDirUnlinkFile(SD, sfileno);
625 rb->counts.badflags++;
626 continue;
627 }
628 e = storeGet(key);
629 if (e && e->lastref >= tmpe.lastref) {
630 /* key already exists, current entry is newer */
631 /* keep old, ignore new */
632 rb->counts.dupcount++;
633 continue;
634 } else if (NULL != e) {
635 /* URL already exists, this swapfile not being used */
636 /* junk old, load new */
637 storeRelease(e); /* release old entry */
638 rb->counts.dupcount++;
639 }
640 rb->counts.objcount++;
641 storeEntryDump(&tmpe, 5);
642 e = storeDiskdDirAddDiskRestore(SD, key,
643 sfileno,
644 tmpe.swap_file_sz,
645 tmpe.expires,
646 tmpe.timestamp,
647 tmpe.lastref,
648 tmpe.lastmod,
649 tmpe.refcount, /* refcount */
650 tmpe.flags, /* flags */
651 (int) rb->flags.clean);
652 storeDirSwapLog(e, SWAP_LOG_ADD);
653 }
654 eventAdd("storeRebuild", storeDiskdDirRebuildFromDirectory, rb, 0.0, 1);
655 }
656
657 static void
658 storeDiskdDirRebuildFromSwapLog(void *data)
659 {
660 RebuildState *rb = data;
661 SwapDir *SD = rb->sd;
662 StoreEntry *e = NULL;
663 storeSwapLogData s;
664 size_t ss = sizeof(storeSwapLogData);
665 int count;
666 int used; /* is swapfile already in use? */
667 int disk_entry_newer; /* is the log entry newer than current entry? */
668 double x;
669 assert(rb != NULL);
670 /* load a number of objects per invocation */
671 for (count = 0; count < rb->speed; count++) {
672 if (fread(&s, ss, 1, rb->log) != 1) {
673 debug(20, 1) ("Done reading %s swaplog (%d entries)\n",
674 rb->sd->path, rb->n_read);
675 fclose(rb->log);
676 rb->log = NULL;
677 store_dirs_rebuilding--;
678 storeDiskdDirCloseTmpSwapLog(rb->sd);
679 storeRebuildComplete(&rb->counts);
680 cbdataFree(rb);
681 return;
682 }
683 rb->n_read++;
684 if (s.op <= SWAP_LOG_NOP)
685 continue;
686 if (s.op >= SWAP_LOG_MAX)
687 continue;
688 debug(20, 3) ("storeDiskdDirRebuildFromSwapLog: %s %s %08X\n",
689 swap_log_op_str[(int) s.op],
690 storeKeyText(s.key),
691 s.swap_filen);
692 if (s.op == SWAP_LOG_ADD) {
693 (void) 0;
694 } else if (s.op == SWAP_LOG_DEL) {
695 if ((e = storeGet(s.key)) != NULL) {
696 /*
697 * Make sure we don't unlink the file, it might be
698 * in use by a subsequent entry. Also note that
699 * we don't have to subtract from store_swap_size
700 * because adding to store_swap_size happens in
701 * the cleanup procedure.
702 */
703 storeExpireNow(e);
704 storeReleaseRequest(e);
705 storeDiskdDirReplRemove(e);
706 if (e->swap_filen > -1) {
707 storeDiskdDirMapBitReset(SD, e->swap_filen);
708 e->swap_filen = -1;
709 e->swap_dirn = -1;
710 }
711 storeRelease(e);
712 rb->counts.objcount--;
713 rb->counts.cancelcount++;
714 }
715 continue;
716 } else {
717 x = log(++rb->counts.bad_log_op) / log(10.0);
718 if (0.0 == x - (double) (int) x)
719 debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
720 rb->counts.bad_log_op);
721 rb->counts.invalid++;
722 continue;
723 }
724 if ((++rb->counts.scancount & 0xFFFF) == 0)
725 debug(20, 3) (" %7d %s Entries read so far.\n",
726 rb->counts.scancount, rb->sd->path);
727 if (!storeDiskdDirValidFileno(SD, s.swap_filen)) {
728 rb->counts.invalid++;
729 continue;
730 }
731 if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
732 rb->counts.badflags++;
733 continue;
734 }
735 e = storeGet(s.key);
736 used = storeDiskdDirMapBitTest(SD, s.swap_filen);
737 /* If this URL already exists in the cache, does the swap log
738 * appear to have a newer entry? Compare 'lastref' from the
739 * swap log to e->lastref. */
740 disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0;
741 if (used && !disk_entry_newer) {
742 /* log entry is old, ignore it */
743 rb->counts.clashcount++;
744 continue;
745 } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) {
746 /* swapfile taken, same URL, newer, update meta */
747 if (e->store_status == STORE_OK) {
748 e->lastref = s.timestamp;
749 e->timestamp = s.timestamp;
750 e->expires = s.expires;
751 e->lastmod = s.lastmod;
752 e->flags = s.flags;
753 e->refcount += s.refcount;
754 #if HEAP_REPLACEMENT
755 storeHeapPositionUpdate(e, SD);
756 storeDiskdDirUnrefObj(SD, e);
757 #endif
758 } else {
759 debug_trap("storeDiskdDirRebuildFromSwapLog: bad condition");
760 debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__);
761 }
762 continue;
763 } else if (used) {
764 /* swapfile in use, not by this URL, log entry is newer */
765 /* This is sorta bad: the log entry should NOT be newer at this
766 * point. If the log is dirty, the filesize check should have
767 * caught this. If the log is clean, there should never be a
768 * newer entry. */
769 debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n",
770 SD->index, s.swap_filen);
771 /* I'm tempted to remove the swapfile here just to be safe,
772 * but there is a bad race condition in the NOVM version if
773 * the swapfile has recently been opened for writing, but
774 * not yet opened for reading. Because we can't map
775 * swapfiles back to StoreEntrys, we don't know the state
776 * of the entry using that file. */
777 /* We'll assume the existing entry is valid, probably because
778 * were in a slow rebuild and the the swap file number got taken
779 * and the validation procedure hasn't run. */
780 assert(rb->flags.need_to_validate);
781 rb->counts.clashcount++;
782 continue;
783 } else if (e && !disk_entry_newer) {
784 /* key already exists, current entry is newer */
785 /* keep old, ignore new */
786 rb->counts.dupcount++;
787 continue;
788 } else if (e) {
789 /* key already exists, this swapfile not being used */
790 /* junk old, load new */
791 storeExpireNow(e);
792 storeReleaseRequest(e);
793 storeDiskdDirReplRemove(e);
794 if (e->swap_filen > -1) {
795 /* Make sure we don't actually unlink the file */
796 storeDiskdDirMapBitReset(SD, e->swap_filen);
797 e->swap_filen = -1;
798 e->swap_dirn = -1;
799 }
800 storeRelease(e);
801 rb->counts.dupcount++;
802 } else {
803 /* URL doesnt exist, swapfile not in use */
804 /* load new */
805 (void) 0;
806 }
807 /* update store_swap_size */
808 rb->counts.objcount++;
809 e = storeDiskdDirAddDiskRestore(SD, s.key,
810 s.swap_filen,
811 s.swap_file_sz,
812 s.expires,
813 s.timestamp,
814 s.lastref,
815 s.lastmod,
816 s.refcount,
817 s.flags,
818 (int) rb->flags.clean);
819 storeDirSwapLog(e, SWAP_LOG_ADD);
820 }
821 eventAdd("storeRebuild", storeDiskdDirRebuildFromSwapLog, rb, 0.0, 1);
822 }
823
824 static int
825 storeDiskdDirGetNextFile(RebuildState * rb, int *sfileno, int *size)
826 {
827 SwapDir *SD = rb->sd;
828 diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
829 int fd = -1;
830 int used = 0;
831 int dirs_opened = 0;
832 debug(20, 3) ("storeDiskdDirGetNextFile: flag=%d, %d: /%02X/%02X\n",
833 rb->flags.init,
834 rb->sd->index,
835 rb->curlvl1,
836 rb->curlvl2);
837 if (rb->done)
838 return -2;
839 while (fd < 0 && rb->done == 0) {
840 fd = -1;
841 if (0 == rb->flags.init) { /* initialize, open first file */
842 rb->done = 0;
843 rb->curlvl1 = 0;
844 rb->curlvl2 = 0;
845 rb->in_dir = 0;
846 rb->flags.init = 1;
847 assert(Config.cacheSwap.n_configured > 0);
848 }
849 if (0 == rb->in_dir) { /* we need to read in a new directory */
850 snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X",
851 rb->sd->path,
852 rb->curlvl1, rb->curlvl2);
853 if (rb->flags.init && rb->td != NULL)
854 closedir(rb->td);
855 rb->td = NULL;
856 if (dirs_opened)
857 return -1;
858 rb->td = opendir(rb->fullpath);
859 dirs_opened++;
860 if (rb->td == NULL) {
861 debug(50, 1) ("storeDiskdDirGetNextFile: opendir: %s: %s\n",
862 rb->fullpath, xstrerror());
863 } else {
864 rb->entry = readdir(rb->td); /* skip . and .. */
865 rb->entry = readdir(rb->td);
866 if (rb->entry == NULL && errno == ENOENT)
867 debug(20, 1) ("storeDiskdDirGetNextFile: directory does not exist!.\n");
868 debug(20, 3) ("storeDiskdDirGetNextFile: Directory %s\n", rb->fullpath);
869 }
870 }
871 if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) {
872 rb->in_dir++;
873 if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) {
874 debug(20, 3) ("storeDiskdDirGetNextFile: invalid %s\n",
875 rb->entry->d_name);
876 continue;
877 }
878 if (!storeDiskdFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
879 debug(20, 3) ("storeDiskdDirGetNextFile: %08X does not belong in %d/%d/%d\n",
880 rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
881 continue;
882 }
883 used = storeDiskdDirMapBitTest(SD, rb->fn);
884 if (used) {
885 debug(20, 3) ("storeDiskdDirGetNextFile: Locked, continuing with next.\n");
886 continue;
887 }
888 snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
889 rb->fullpath, rb->entry->d_name);
890 debug(20, 3) ("storeDiskdDirGetNextFile: Opening %s\n", rb->fullfilename);
891 fd = file_open(rb->fullfilename, O_RDONLY);
892 if (fd < 0)
893 debug(50, 1) ("storeDiskdDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror());
894 else
895 store_open_disk_fd++;
896 continue;
897 }
898 rb->in_dir = 0;
899 if (++rb->curlvl2 < diskdinfo->l2)
900 continue;
901 rb->curlvl2 = 0;
902 if (++rb->curlvl1 < diskdinfo->l1)
903 continue;
904 rb->curlvl1 = 0;
905 rb->done = 1;
906 }
907 *sfileno = rb->fn;
908 return fd;
909 }
910
911 /* Add a new object to the cache with empty memory copy and pointer to disk
912 * use to rebuild store from disk. */
913 static StoreEntry *
914 storeDiskdDirAddDiskRestore(SwapDir *SD, const cache_key * key,
915 int file_number,
916 size_t swap_file_sz,
917 time_t expires,
918 time_t timestamp,
919 time_t lastref,
920 time_t lastmod,
921 u_num32 refcount,
922 u_short flags,
923 int clean)
924 {
925 StoreEntry *e = NULL;
926 debug(20, 5) ("storeDiskdAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number);
927 /* if you call this you'd better be sure file_number is not
928 * already in use! */
929 e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL);
930 e->store_status = STORE_OK;
931 storeSetMemStatus(e, NOT_IN_MEMORY);
932 e->swap_status = SWAPOUT_DONE;
933 e->swap_filen = file_number;
934 e->swap_dirn = SD->index;
935 e->swap_file_sz = swap_file_sz;
936 e->lock_count = 0;
937 #if !HEAP_REPLACEMENT
938 e->refcount = 0;
939 #endif
940 e->lastref = lastref;
941 e->timestamp = timestamp;
942 e->expires = expires;
943 e->lastmod = lastmod;
944 e->refcount = refcount;
945 e->flags = flags;
946 EBIT_SET(e->flags, ENTRY_CACHABLE);
947 EBIT_CLR(e->flags, RELEASE_REQUEST);
948 EBIT_CLR(e->flags, KEY_PRIVATE);
949 e->ping_status = PING_NONE;
950 EBIT_CLR(e->flags, ENTRY_VALIDATED);
951 storeDiskdDirMapBitSet(SD, e->swap_filen);
952 storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */
953 storeDiskdDirReplAdd(SD, e);
954 return e;
955 }
956
957 static void
958 storeDiskdDirRebuild(SwapDir * sd)
959 {
960 RebuildState *rb = xcalloc(1, sizeof(*rb));
961 int clean = 0;
962 int zero = 0;
963 FILE *fp;
964 EVH *func = NULL;
965 rb->sd = sd;
966 rb->speed = opt_foreground_rebuild ? 1 << 30 : 50;
967 /*
968 * If the swap.state file exists in the cache_dir, then
969 * we'll use storeDiskdDirRebuildFromSwapLog(), otherwise we'll
970 * use storeDiskdDirRebuildFromDirectory() to open up each file
971 * and suck in the meta data.
972 */
973 fp = storeDiskdDirOpenTmpSwapLog(sd, &clean, &zero);
974 if (fp == NULL || zero) {
975 if (fp != NULL)
976 fclose(fp);
977 func = storeDiskdDirRebuildFromDirectory;
978 } else {
979 func = storeDiskdDirRebuildFromSwapLog;
980 rb->log = fp;
981 rb->flags.clean = (unsigned int) clean;
982 }
983 if (!clean)
984 rb->flags.need_to_validate = 1;
985 debug(20, 1) ("Rebuilding storage in %s (%s)\n",
986 sd->path, clean ? "CLEAN" : "DIRTY");
987 store_dirs_rebuilding++;
988 cbdataAdd(rb, cbdataXfree, 0);
989 eventAdd("storeRebuild", func, rb, 0.0, 1);
990 }
991
992 static void
993 storeDiskdDirCloseTmpSwapLog(SwapDir * sd)
994 {
995 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
996 char *swaplog_path = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
997 char *new_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".new"));
998 int fd;
999 file_close(diskdinfo->swaplog_fd);
1000 #ifdef _SQUID_OS2_
1001 if (unlink(swaplog_path) < 0) {
1002 debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
1003 fatal("storeDiskdDirCloseTmpSwapLog: unlink failed");
1004 }
1005 #endif
1006 if (xrename(new_path, swaplog_path) < 0) {
1007 fatal("storeDiskdDirCloseTmpSwapLog: rename failed");
1008 }
1009 fd = file_open(swaplog_path, O_WRONLY | O_CREAT);
1010 if (fd < 0) {
1011 debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror());
1012 fatal("storeDiskdDirCloseTmpSwapLog: Failed to open swap log.");
1013 }
1014 safe_free(swaplog_path);
1015 safe_free(new_path);
1016 diskdinfo->swaplog_fd = fd;
1017 debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
1018 }
1019
1020 static FILE *
1021 storeDiskdDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
1022 {
1023 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
1024 char *swaplog_path = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
1025 char *clean_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean"));
1026 char *new_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".new"));
1027 struct stat log_sb;
1028 struct stat clean_sb;
1029 FILE *fp;
1030 int fd;
1031 if (stat(swaplog_path, &log_sb) < 0) {
1032 debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index);
1033 safe_free(swaplog_path);
1034 safe_free(clean_path);
1035 safe_free(new_path);
1036 return NULL;
1037 }
1038 *zero_flag = log_sb.st_size == 0 ? 1 : 0;
1039 /* close the existing write-only FD */
1040 if (diskdinfo->swaplog_fd >= 0)
1041 file_close(diskdinfo->swaplog_fd);
1042 /* open a write-only FD for the new log */
1043 fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC);
1044 if (fd < 0) {
1045 debug(50, 1) ("%s: %s\n", new_path, xstrerror());
1046 fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
1047 }
1048 diskdinfo->swaplog_fd = fd;
1049 /* open a read-only stream of the old log */
1050 fp = fopen(swaplog_path, "r");
1051 if (fp == NULL) {
1052 debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
1053 fatal("Failed to open swap log for reading");
1054 }
1055 memset(&clean_sb, '\0', sizeof(struct stat));
1056 if (stat(clean_path, &clean_sb) < 0)
1057 *clean_flag = 0;
1058 else if (clean_sb.st_mtime < log_sb.st_mtime)
1059 *clean_flag = 0;
1060 else
1061 *clean_flag = 1;
1062 safeunlink(clean_path, 1);
1063 safe_free(swaplog_path);
1064 safe_free(clean_path);
1065 safe_free(new_path);
1066 return fp;
1067 }
1068
1069 struct _clean_state {
1070 char *cur;
1071 char *new;
1072 char *cln;
1073 char *outbuf;
1074 off_t outbuf_offset;
1075 int fd;
1076 };
1077
1078 #define CLEAN_BUF_SZ 16384
1079 /*
1080 * Begin the process to write clean cache state. For DISKD this means
1081 * opening some log files and allocating write buffers. Return 0 if
1082 * we succeed, and assign the 'func' and 'data' return pointers.
1083 */
1084 static int
1085 storeDiskdDirWriteCleanOpen(SwapDir * sd)
1086 {
1087 struct _clean_state *state = xcalloc(1, sizeof(*state));
1088 struct stat sb;
1089 sd->log.clean.write = NULL;
1090 sd->log.clean.state = NULL;
1091 state->cur = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
1092 state->new = xstrdup(storeDiskdDirSwapLogFile(sd, ".clean"));
1093 state->cln = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean"));
1094 state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
1095 state->outbuf_offset = 0;
1096 unlink(state->new);
1097 unlink(state->cln);
1098 state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC);
1099 if (state->fd < 0)
1100 return -1;
1101 debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
1102 state->new, state->fd);
1103 #if HAVE_FCHMOD
1104 if (stat(state->cur, &sb) == 0)
1105 fchmod(state->fd, sb.st_mode);
1106 #endif
1107 sd->log.clean.write = storeDiskdDirWriteCleanEntry;
1108 sd->log.clean.state = state;
1109 return 0;
1110 }
1111
1112 /*
1113 * "write" an entry to the clean log file.
1114 */
1115 static void
1116 storeDiskdDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd)
1117 {
1118 storeSwapLogData s;
1119 static size_t ss = sizeof(storeSwapLogData);
1120 struct _clean_state *state = sd->log.clean.state;
1121 if (NULL == e) {
1122 storeDiskdDirWriteCleanClose(sd);
1123 return;
1124 }
1125 memset(&s, '\0', ss);
1126 s.op = (char) SWAP_LOG_ADD;
1127 s.swap_filen = e->swap_filen;
1128 s.timestamp = e->timestamp;
1129 s.lastref = e->lastref;
1130 s.expires = e->expires;
1131 s.lastmod = e->lastmod;
1132 s.swap_file_sz = e->swap_file_sz;
1133 s.refcount = e->refcount;
1134 s.flags = e->flags;
1135 xmemcpy(&s.key, e->key, MD5_DIGEST_CHARS);
1136 xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
1137 state->outbuf_offset += ss;
1138 /* buffered write */
1139 if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
1140 if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
1141 debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
1142 state->new, xstrerror());
1143 debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
1144 file_close(state->fd);
1145 state->fd = -1;
1146 unlink(state->new);
1147 safe_free(state);
1148 sd->log.clean.state = NULL;
1149 sd->log.clean.write = NULL;
1150 }
1151 state->outbuf_offset = 0;
1152 }
1153 }
1154
1155 static void
1156 storeDiskdDirWriteCleanClose(SwapDir * sd)
1157 {
1158 struct _clean_state *state = sd->log.clean.state;
1159 if (state->fd < 0)
1160 return;
1161 if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
1162 debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
1163 state->new, xstrerror());
1164 debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
1165 "not replaced.\n");
1166 file_close(state->fd);
1167 state->fd = -1;
1168 unlink(state->new);
1169 }
1170 safe_free(state->outbuf);
1171 /*
1172 * You can't rename open files on Microsoft "operating systems"
1173 * so we have to close before renaming.
1174 */
1175 storeDiskdDirCloseSwapLog(sd);
1176 /* rename */
1177 if (state->fd >= 0) {
1178 #ifdef _SQUID_OS2_
1179 file_close(state->fd);
1180 state->fd = -1;
1181 if (unlink(cur) < 0)
1182 debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n",
1183 xstrerror(), cur);
1184 #endif
1185 xrename(state->new, state->cur);
1186 }
1187 /* touch a timestamp file if we're not still validating */
1188 if (store_dirs_rebuilding)
1189 (void) 0;
1190 else if (state->fd < 0)
1191 (void) 0;
1192 else
1193 file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC));
1194 /* close */
1195 safe_free(state->cur);
1196 safe_free(state->new);
1197 safe_free(state->cln);
1198 if (state->fd >= 0)
1199 file_close(state->fd);
1200 state->fd = -1;
1201 safe_free(state);
1202 sd->log.clean.state = NULL;
1203 sd->log.clean.write = NULL;
1204 }
1205
1206 static void
1207 storeDiskdDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
1208 {
1209 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
1210 storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData));
1211 s->op = (char) op;
1212 s->swap_filen = e->swap_filen;
1213 s->timestamp = e->timestamp;
1214 s->lastref = e->lastref;
1215 s->expires = e->expires;
1216 s->lastmod = e->lastmod;
1217 s->swap_file_sz = e->swap_file_sz;
1218 s->refcount = e->refcount;
1219 s->flags = e->flags;
1220 xmemcpy(s->key, e->key, MD5_DIGEST_CHARS);
1221 file_write(diskdinfo->swaplog_fd,
1222 -1,
1223 s,
1224 sizeof(storeSwapLogData),
1225 NULL,
1226 NULL,
1227 xfree);
1228 }
1229
1230 static void
1231 storeDiskdDirNewfs(SwapDir * sd)
1232 {
1233 debug(47, 3) ("Creating swap space in %s\n", sd->path);
1234 storeDiskdDirCreateDirectory(sd->path, 0);
1235 storeDiskdDirCreateSwapSubDirs(sd);
1236 }
1237
1238 static int
1239 rev_int_sort(const void *A, const void *B)
1240 {
1241 const int *i1 = A;
1242 const int *i2 = B;
1243 return *i2 - *i1;
1244 }
1245
1246 static int
1247 storeDiskdDirClean(int swap_index)
1248 {
1249 DIR *dp = NULL;
1250 struct dirent *de = NULL;
1251 LOCAL_ARRAY(char, p1, MAXPATHLEN + 1);
1252 LOCAL_ARRAY(char, p2, MAXPATHLEN + 1);
1253 #if USE_TRUNCATE
1254 struct stat sb;
1255 #endif
1256 int files[20];
1257 int swapfileno;
1258 int fn; /* same as swapfileno, but with dirn bits set */
1259 int n = 0;
1260 int k = 0;
1261 int N0, N1, N2;
1262 int D0, D1, D2;
1263 SwapDir *SD;
1264 diskdinfo_t *diskdinfo;
1265 N0 = n_diskd_dirs;
1266 D0 = diskd_dir_index[swap_index % N0];
1267 SD = &Config.cacheSwap.swapDirs[D0];
1268 diskdinfo = (diskdinfo_t *)SD->fsdata;
1269 N1 = diskdinfo->l1;
1270 D1 = (swap_index / N0) % N1;
1271 N2 = diskdinfo->l2;
1272 D2 = ((swap_index / N0) / N1) % N2;
1273 snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X",
1274 Config.cacheSwap.swapDirs[D0].path, D1, D2);
1275 debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1);
1276 dp = opendir(p1);
1277 if (dp == NULL) {
1278 if (errno == ENOENT) {
1279 debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1);
1280 if (mkdir(p1, 0777) == 0)
1281 return 0;
1282 }
1283 debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror());
1284 safeunlink(p1, 1);
1285 return 0;
1286 }
1287 while ((de = readdir(dp)) != NULL && k < 20) {
1288 if (sscanf(de->d_name, "%X", &swapfileno) != 1)
1289 continue;
1290 fn = swapfileno; /* XXX should remove this cruft ! */
1291 if (storeDiskdDirValidFileno(SD, fn))
1292 if (storeDiskdDirMapBitTest(SD, fn))
1293 if (storeDiskdFilenoBelongsHere(fn, D0, D1, D2))
1294 continue;
1295 #if USE_TRUNCATE
1296 if (!stat(de->d_name, &sb))
1297 if (sb.st_size == 0)
1298 continue;
1299 #endif
1300 files[k++] = swapfileno;
1301 }
1302 closedir(dp);
1303 if (k == 0)
1304 return 0;
1305 qsort(files, k, sizeof(int), rev_int_sort);
1306 if (k > 10)
1307 k = 10;
1308 for (n = 0; n < k; n++) {
1309 debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]);
1310 snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]);
1311 #if USE_TRUNCATE
1312 truncate(p2, 0);
1313 #else
1314 safeunlink(p2, 0);
1315 #endif
1316 Counter.swap_files_cleaned++;
1317 }
1318 debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1);
1319 return k;
1320 }
1321
1322 static void
1323 storeDiskdDirCleanEvent(void *unused)
1324 {
1325 static int swap_index = 0;
1326 int i;
1327 int j = 0;
1328 int n = 0;
1329 /*
1330 * Assert that there are DISKD cache_dirs configured, otherwise
1331 * we should never be called.
1332 */
1333 assert(n_diskd_dirs);
1334 if (NULL == diskd_dir_index) {
1335 SwapDir *sd;
1336 diskdinfo_t *diskdinfo;
1337 /*
1338 * Initialize the little array that translates DISKD cache_dir
1339 * number into the Config.cacheSwap.swapDirs array index.
1340 */
1341 diskd_dir_index = xcalloc(n_diskd_dirs, sizeof(*diskd_dir_index));
1342 for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
1343 sd = &Config.cacheSwap.swapDirs[i];
1344 if (!storeDiskdDirIs(sd))
1345 continue;
1346 diskd_dir_index[n++] = i;
1347 diskdinfo = (diskdinfo_t *)sd->fsdata;
1348 j += (diskdinfo->l1 * diskdinfo->l2);
1349 }
1350 assert(n == n_diskd_dirs);
1351 /*
1352 * Start the storeDiskdDirClean() swap_index with a random
1353 * value. j equals the total number of DISKD level 2
1354 * swap directories
1355 */
1356 swap_index = (int) (squid_random() % j);
1357 }
1358 if (0 == store_dirs_rebuilding) {
1359 n = storeDiskdDirClean(swap_index);
1360 swap_index++;
1361 }
1362 eventAdd("storeDirClean", storeDiskdDirCleanEvent, NULL,
1363 15.0 * exp(-0.25 * n), 1);
1364 }
1365
1366 static int
1367 storeDiskdDirIs(SwapDir * sd)
1368 {
1369 if (strncmp(sd->type, "diskd", 3) == 0)
1370 return 1;
1371 return 0;
1372 }
1373
1374 /*
1375 * Does swapfile number 'fn' belong in cachedir #F0,
1376 * level1 dir #F1, level2 dir #F2?
1377 */
1378 static int
1379 storeDiskdFilenoBelongsHere(int fn, int F0, int F1, int F2)
1380 {
1381 int D1, D2;
1382 int L1, L2;
1383 int filn = fn;
1384 diskdinfo_t *diskdinfo;
1385 assert(F0 < Config.cacheSwap.n_configured);
1386 diskdinfo = (diskdinfo_t *)Config.cacheSwap.swapDirs[F0].fsdata;
1387 L1 = diskdinfo->l1;
1388 L2 = diskdinfo->l2;
1389 D1 = ((filn / L2) / L2) % L1;
1390 if (F1 != D1)
1391 return 0;
1392 D2 = (filn / L2) % L2;
1393 if (F2 != D2)
1394 return 0;
1395 return 1;
1396 }
1397
1398 int
1399 storeDiskdDirValidFileno(SwapDir *SD, sfileno filn)
1400 {
1401 diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
1402 if (filn < 0)
1403 return 0;
1404 if (filn > diskdinfo->map->max_n_files)
1405 return 0;
1406 return 1;
1407 }
1408
1409 void
1410 storeDiskdDirMaintain(SwapDir *SD)
1411 {
1412 StoreEntry *e = NULL;
1413 int scanned = 0;
1414 int locked = 0;
1415 int expired = 0;
1416 int max_scan;
1417 int max_remove;
1418 double f;
1419 static time_t last_warn_time = 0;
1420 #if !HEAP_REPLACEMENT
1421 dlink_node *m;
1422 dlink_node *prev = NULL;
1423 #else
1424 heap_key age;
1425 heap_key min_age = 0.0;
1426 link_list *locked_entries = NULL;
1427 #if HEAP_REPLACEMENT_DEBUG
1428 if (!verify_heap_property(SD->repl.heap.heap)) {
1429 debug(20, 1) ("Heap property violated!\n");
1430 }
1431 #endif
1432 #endif
1433 /* We can't delete objects while rebuilding swap */
1434 if (store_dirs_rebuilding) {
1435 return;
1436 } else {
1437 f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
1438 f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
1439 max_scan = (int) (f * 400.0 + 100.0);
1440 max_remove = (int) (f * 70.0 + 10.0);
1441 /*
1442 * This is kinda cheap, but so we need this priority hack?
1443 */
1444 #if 0
1445 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
1446 #endif
1447 }
1448 debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove);
1449 #if HEAP_REPLACEMENT
1450 while (heap_nodes(SD->repl.heap.heap) > 0) {
1451 if (store_swap_size < store_swap_low)
1452 break;
1453 if (expired >= max_remove)
1454 break;
1455 if (scanned >= max_scan)
1456 break;
1457 age = heap_peepminkey(SD->repl.heap.heap);
1458 e = heap_extractmin(SD->repl.heap.heap);
1459 e->repl.node = NULL; /* no longer in the heap */
1460 scanned++;
1461 if (storeEntryLocked(e)) {
1462 /*
1463 * Entry is in use ... put it in a linked list to ignore it.
1464 */
1465 if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1466 /*
1467 * If this was a "SPECIAL" do not add it back into the heap.
1468 * It will always be "SPECIAL" and therefore never removed.
1469 */
1470 debug(20, 4) ("storeDiskdDirMaintain: locked url %s\n",
1471 (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->
1472 key));
1473 linklistPush(&locked_entries, e);
1474 }
1475 locked++;
1476 continue;
1477 } else if (storeDiskdDirCheckExpired(SD, e)) {
1478 /*
1479 * Note: This will not check the reference age ifdef
1480 * HEAP_REPLACEMENT, but it does some other useful
1481 * checks...
1482 */
1483 expired++;
1484 debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
1485 age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
1486 min_age = age;
1487 storeRelease(e);
1488 } else {
1489 /*
1490 * Did not expire the object so we need to add it back
1491 * into the heap!
1492 */
1493 debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
1494 storeKeyText(e->key));
1495 linklistPush(&locked_entries, e);
1496 continue;
1497 }
1498 if (store_swap_size < store_swap_low)
1499 break;
1500 else if (expired >= max_remove)
1501 break;
1502 else if (scanned >= max_scan)
1503 break;
1504 }
1505 /*
1506 * Bump the heap age factor.
1507 */
1508 if (min_age > 0.0)
1509 SD->repl.heap.heap->age = min_age;
1510 /*
1511 * Reinsert all bumped locked entries back into heap...
1512 */
1513 while ((e = linklistShift(&locked_entries)))
1514 e->repl.node = heap_insert(SD->repl.heap.heap, e);
1515 #else
1516 for (m = SD->repl.lru.list.tail; m; m = prev) {
1517 prev = m->prev;
1518 e = m->data;
1519 scanned++;
1520 if (storeEntryLocked(e)) {
1521 /*
1522 * If there is a locked entry at the tail of the LRU list,
1523 * move it to the beginning to get it out of the way.
1524 * Theoretically, we might have all locked objects at the
1525 * tail, and then we'll never remove anything here and the
1526 * LRU age will go to zero.
1527 */
1528 if (memInUse(MEM_STOREENTRY) > max_scan) {
1529 dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
1530 dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
1531 }
1532 locked++;
1533
1534 } else if (storeDiskdDirCheckExpired(SD, e)) {
1535 expired++;
1536 storeRelease(e);
1537 }
1538 if (expired >= max_remove)
1539 break;
1540 if (scanned >= max_scan)
1541 break;
1542 }
1543 #endif
1544 debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l
1545 ocked %d f=%.03f\n",
1546 scanned, max_scan, expired, max_remove, locked, f);
1547 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
1548 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY));
1549 debug(20, 3) (" %6d were scanned\n", scanned);
1550 debug(20, 3) (" %6d were locked\n", locked);
1551 debug(20, 3) (" %6d were expired\n", expired);
1552 if (store_swap_size < Config.Swap.maxSize)
1553 return;
1554 if (squid_curtime - last_warn_time < 10)
1555 return;
1556 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
1557 store_swap_size, Config.Swap.maxSize);
1558 last_warn_time = squid_curtime;
1559 }
1560
1561 /*
1562 * storeDiskdDirCheckObj
1563 *
1564 * This routine is called by storeDirSelectSwapDir to see if the given
1565 * object is able to be stored on this filesystem. DISKD filesystems will
1566 * happily store anything as long as the LRU time isn't too small.
1567 */
1568 int
1569 storeDiskdDirCheckObj(SwapDir *SD, const StoreEntry *e)
1570 {
1571 int loadav;
1572
1573 diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
1574 #if !HEAP_REPLACEMENT
1575 if (storeDiskdDirExpiredReferenceAge(SD) < 300) {
1576 debug(20, 3) ("storeDiskdDirCheckObj: NO: LRU Age = %d\n",
1577 storeDiskdDirExpiredReferenceAge(SD));
1578 /* store_check_cachable_hist.no.lru_age_too_low++; */
1579 return -1;
1580 }
1581 #endif
1582
1583 /* Check the queue length */
1584 if (diskdinfo->away >= diskdinfo->magic1)
1585 return -1;
1586
1587 /* Calculate the storedir load relative to magic2 on a scale of 0 .. 1000 */
1588 if (diskdinfo->away == 0)
1589 loadav = 0;
1590 else
1591 loadav = diskdinfo->magic2 * 1000 / diskdinfo->away;
1592 return loadav;
1593 }
1594
1595 /*
1596 * storeDiskdDirRefObj
1597 *
1598 * This routine is called whenever an object is referenced, so we can
1599 * maintain replacement information within the storage fs.
1600 */
1601 void
1602 storeDiskdDirRefObj(SwapDir *SD, StoreEntry *e)
1603 {
1604 debug(1, 3) ("storeDiskdDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
1605 e->swap_filen);
1606 #if HEAP_REPLACEMENT
1607 /* Nothing to do here */
1608 #else
1609 /* Reference the object */
1610 if (!EBIT_TEST(e->flags, RELEASE_REQUEST) &&
1611 !EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1612 dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
1613 dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
1614 }
1615 #endif
1616 }
1617
1618 /*
1619 * storeDiskdDirUnrefObj
1620 * This routine is called whenever the last reference to an object is
1621 * removed, to maintain replacement information within the storage fs.
1622 */
1623 void
1624 storeDiskdDirUnrefObj(SwapDir *SD, StoreEntry *e)
1625 {
1626 debug(1, 3) ("storeDiskdDirUnrefObj: referencing %p %d/%d\n", e,
1627 e->swap_dirn, e->swap_filen);
1628 #if HEAP_REPLACEMENT
1629 if (e->repl.node)
1630 heap_update(SD->repl.heap.heap, e->repl.node, e);
1631 #endif
1632 }
1633
1634 /*
1635 * storeDiskdDirUnlinkFile
1636 *
1637 * This is a *synchronous* unlink which is currently used in the rebuild
1638 * process. This is bad, but it'll have to stay until the dir rebuild
1639 * uses storeDiskdUnlink() ..
1640 */
1641 void
1642 storeDiskdDirUnlinkFile(SwapDir *SD, sfileno f)
1643 {
1644 debug(79, 3) ("storeDiskdDirUnlinkFile: unlinking fileno %08X\n", f);
1645 storeDiskdDirMapBitReset(SD, f);
1646 unlinkdUnlink(storeDiskdDirFullPath(SD, f, NULL));
1647 }
1648
1649 #if !HEAP_REPLACEMENT
1650 /*
1651 * storeDiskdDirExpiredReferenceAge
1652 *
1653 * The LRU age is scaled exponentially between 1 minute and
1654 * Config.referenceAge , when store_swap_low < store_swap_size <
1655 * store_swap_high. This keeps store_swap_size within the low and high
1656 * water marks. If the cache is very busy then store_swap_size stays
1657 * closer to the low water mark, if it is not busy, then it will stay
1658 * near the high water mark. The LRU age value can be examined on the
1659 * cachemgr 'info' page.
1660 */
1661 static time_t
1662 storeDiskdDirExpiredReferenceAge(SwapDir *SD)
1663 {
1664 double x;
1665 double z;
1666 time_t age;
1667 long store_high, store_low;
1668
1669 store_high = (long) (((float) SD->max_size *
1670 (float) Config.Swap.highWaterMark) / (float) 100);
1671 store_low = (long) (((float) SD->max_size *
1672 (float) Config.Swap.lowWaterMark) / (float) 100);
1673 debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size);
1674
1675 x = (double) (store_high - SD->cur_size) /
1676 (store_high - store_low);
1677 x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
1678 z = pow((double) (Config.referenceAge / 60), x);
1679 age = (time_t) (z * 60.0);
1680 if (age < 60)
1681 age = 60;
1682 else if (age > Config.referenceAge)
1683 age = Config.referenceAge;
1684 return age;
1685 }
1686 #endif
1687
1688 /*
1689 * storeDiskdDirCheckExpired
1690 *
1691 * Check whether the given object is expired or not
1692 * It breaks layering a little by calling the upper layers to find
1693 * out whether the object is locked or not, but we can't help this
1694 * right now.
1695 */
1696 static int
1697 storeDiskdDirCheckExpired(SwapDir *SD, StoreEntry *e)
1698 {
1699 if (storeEntryLocked(e))
1700 return 0;
1701 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
1702 return 1;
1703 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
1704 return 1;
1705
1706 #if HEAP_REPLACEMENT
1707 /*
1708 * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap
1709 * controls the replacement of objects.
1710 */
1711 return 1;
1712 #else
1713 if (squid_curtime - e->lastref > storeDiskdDirExpiredReferenceAge(SD))
1714 return 1;
1715 return 0;
1716 #endif
1717 }
1718
1719 /*
1720 * Add and remove the given StoreEntry from the replacement policy in
1721 * use.
1722 */
1723
1724 void
1725 storeDiskdDirReplAdd(SwapDir *SD, StoreEntry *e)
1726 {
1727 debug(20, 4) ("storeDiskdDirReplAdd: added node %p to dir %d\n", e,
1728 SD->index);
1729 #if HEAP_REPLACEMENT
1730 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1731 (void) 0;
1732 } else {
1733 e->repl.node = heap_insert(SD->repl.heap.heap, e);
1734 debug(20, 4) ("storeDiskdDirReplAdd: inserted node 0x%x\n", e->repl.node);
1735 }
1736 #else
1737 /* Shouldn't we not throw special objects into the lru ? */
1738 dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
1739 #endif
1740 }
1741
1742
1743 void
1744 storeDiskdDirReplRemove(StoreEntry *e)
1745 {
1746 SwapDir *SD = INDEXSD(e->swap_dirn);
1747 debug(20, 4) ("storeDiskdDirReplRemove: remove node %p from dir %d\n", e,
1748 SD->index);
1749 #if HEAP_REPLACEMENT
1750 /* And now, release the object from the replacement policy */
1751 if (e->repl.node) {
1752 debug(20, 4) ("storeDiskdDirReplRemove: deleting node 0x%x\n",
1753 e->repl.node);
1754 heap_delete(SD->repl.heap.heap, e->repl.node);
1755 e->repl.node = NULL;
1756 }
1757 #else
1758 dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
1759 #endif
1760 }
1761
1762
1763
1764 /*
1765 * SHM manipulation routines
1766 */
1767
1768 void *
1769 storeDiskdShmGet(SwapDir * sd, int *shm_offset)
1770 {
1771 char *buf;
1772 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
1773 buf = linklistShift(&diskdinfo->shm.stack);
1774 assert(buf);
1775 *shm_offset = buf - diskdinfo->shm.buf;
1776 assert(0 <= *shm_offset && *shm_offset < SHMBUFS * SHMBUF_BLKSZ);
1777 diskd_stats.shmbuf_count++;
1778 return buf;
1779 }
1780
1781 void
1782 storeDiskdShmPut(SwapDir * sd, int offset)
1783 {
1784 char *buf;
1785 diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
1786 assert(offset >= 0);
1787 assert(offset < SHMBUFS * SHMBUF_BLKSZ);
1788 buf = diskdinfo->shm.buf + offset;
1789 linklistPush(&diskdinfo->shm.stack, buf);
1790 diskd_stats.shmbuf_count--;
1791 }
1792
1793
1794
1795
1796 /* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
1797
1798 void
1799 storeDiskdDirStats(SwapDir *SD, StoreEntry * sentry)
1800 {
1801 diskdinfo_t *diskdinfo;
1802 #if HAVE_STATVFS
1803 struct statvfs sfs;
1804 #endif
1805 diskdinfo = (diskdinfo_t *)SD->fsdata;
1806 storeAppendPrintf(sentry, "First level subdirectories: %d\n", diskdinfo->l1);
1807 storeAppendPrintf(sentry, "Second level subdirectories: %d\n", diskdinfo->l2);
1808 storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size);
1809 storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size);
1810 storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n",
1811 100.0 * SD->cur_size / SD->max_size);
1812 storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
1813 diskdinfo->map->n_files_in_map, diskdinfo->map->max_n_files,
1814 percent(diskdinfo->map->n_files_in_map, diskdinfo->map->max_n_files));
1815 #if HAVE_STATVFS
1816 #define fsbtoblk(num, fsbs, bs) \
1817 (((fsbs) != 0 && (fsbs) < (bs)) ? \
1818 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
1819 if (!statvfs(SD->path, &sfs)) {
1820 storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
1821 fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024),
1822 fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024),
1823 percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks));
1824 storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
1825 sfs.f_files - sfs.f_ffree, sfs.f_files,
1826 percent(sfs.f_files - sfs.f_ffree, sfs.f_files));
1827 }
1828 #endif
1829 storeAppendPrintf(sentry, "Flags:");
1830 if (SD->flags.selected)
1831 storeAppendPrintf(sentry, " SELECTED");
1832 if (SD->flags.read_only)
1833 storeAppendPrintf(sentry, " READ-ONLY");
1834 storeAppendPrintf(sentry, "\n");
1835 #if !HEAP_REPLACEMENT
1836 storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n",
1837 (double) storeDiskdDirExpiredReferenceAge(SD) / 86400.0);
1838 #else
1839 #if 0
1840 storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n",
1841 heap_peepminkey(sd.repl.heap.heap));
1842 #endif
1843 #endif
1844 storeAppendPrintf(sentry, "Pending operations: %d\n", diskdinfo->away);
1845 }
1846
1847 /*
1848 * storeDiskdDirReconfigure
1849 *
1850 * This routine is called when the given swapdir needs reconfiguring
1851 */
1852 void
1853 storeDiskdDirReconfigure(SwapDir *sd, int index, char *path)
1854 {
1855 char *token;
1856 int i;
1857 int size;
1858 int l1;
1859 int l2;
1860 int magic1, magic2;
1861 unsigned int read_only = 0;
1862 diskdinfo_t *diskdinfo;
1863
1864 i = GetInteger();
1865 size = i << 10; /* Mbytes to kbytes */
1866 if (size <= 0)
1867 fatal("storeDiskdDirReconfigure: invalid size value");
1868 i = GetInteger();
1869 l1 = i;
1870 if (l1 <= 0)
1871 fatal("storeDiskdDirReconfigure: invalid level 1 directories value");
1872 i = GetInteger();
1873 l2 = i;
1874 if (l2 <= 0)
1875 fatal("storeDiskdDirReconfigure: invalid level 2 directories value");
1876 i = GetInteger();
1877 magic1 = i;
1878 if (magic1 <= 0)
1879 fatal("storeDiskdDirParse: invalid magic1 value");
1880 i = GetInteger();
1881 magic2 = i;
1882 if (magic2 <= 0)
1883 fatal("storeDiskdDirParse: invalid magic2 value");
1884 if ((token = strtok(NULL, w_space)))
1885 if (!strcasecmp(token, "read-only"))
1886 read_only = 1;
1887
1888 /* just reconfigure it */
1889 if (size == sd->max_size)
1890 debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n",
1891 path, size);
1892 else
1893 debug(3, 1) ("Cache dir '%s' size changed to %d KB\n",
1894 path, size);
1895 sd->max_size = size;
1896 if (sd->flags.read_only != read_only)
1897 debug(3, 1) ("Cache dir '%s' now %s\n",
1898 path, read_only ? "Read-Only" : "Read-Write");
1899 diskdinfo = (diskdinfo_t *)sd->fsdata;
1900 diskdinfo->magic1 = magic1;
1901 diskdinfo->magic2 = magic2;
1902 sd->flags.read_only = read_only;
1903 return;
1904 }
1905
1906 void
1907 storeDiskdDirDump(StoreEntry * entry, const char *name, SwapDir * s)
1908 {
1909 diskdinfo_t *diskdinfo = (diskdinfo_t *)s->fsdata;
1910 storeAppendPrintf(entry, "%s %s %s %d %d %d\n",
1911 name,
1912 "diskd",
1913 s->path,
1914 s->max_size >> 10,
1915 diskdinfo->l1,
1916 diskdinfo->l2);
1917 }
1918
1919 /*
1920 * Only "free" the filesystem specific stuff here
1921 */
1922 static void
1923 storeDiskdDirFree(SwapDir * s)
1924 {
1925 diskdinfo_t *diskdinfo = (diskdinfo_t *)s->fsdata;
1926 if (diskdinfo->swaplog_fd > -1) {
1927 file_close(diskdinfo->swaplog_fd);
1928 diskdinfo->swaplog_fd = -1;
1929 }
1930 filemapFreeMemory(diskdinfo->map);
1931 xfree(diskdinfo);
1932 s->fsdata = NULL; /* Will aid debugging... */
1933
1934 }
1935
1936 char *
1937 storeDiskdDirFullPath(SwapDir *SD, sfileno filn, char *fullpath)
1938 {
1939 LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
1940 diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
1941 int L1 = diskdinfo->l1;
1942 int L2 = diskdinfo->l2;
1943 if (!fullpath)
1944 fullpath = fullfilename;
1945 fullpath[0] = '\0';
1946 snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X",
1947 SD->path,
1948 ((filn / L2) / L2) % L1,
1949 (filn / L2) % L2,
1950 filn);
1951 return fullpath;
1952 }
1953
1954 /*
1955 * storeDiskdCleanupDoubleCheck
1956 *
1957 * This is called by storeCleanup() if -S was given on the command line.
1958 */
1959 static int
1960 storeDiskdCleanupDoubleCheck(SwapDir *sd, StoreEntry *e)
1961 {
1962 struct stat sb;
1963
1964 if (stat(storeDiskdDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
1965 debug(20, 0) ("storeDiskdCleanupDoubleCheck: MISSING SWAP FILE\n");
1966 debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
1967 debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
1968 storeDiskdDirFullPath(sd, e->swap_filen, NULL));
1969 storeEntryDump(e, 0);
1970 return -1;
1971 }
1972 if (e->swap_file_sz != sb.st_size) {
1973 debug(20, 0) ("storeDiskdCleanupDoubleCheck: SIZE MISMATCH\n");
1974 debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
1975 debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
1976 storeDiskdDirFullPath(sd, e->swap_filen, NULL));
1977 debug(20, 0) ("storeDiskdCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n",
1978 e->swap_file_sz, (int) sb.st_size);
1979 storeEntryDump(e, 0);
1980 return -1;
1981 }
1982 return 0;
1983 }
1984
1985 /*
1986 * storeDiskdDirParse
1987 *
1988 * Called when a *new* fs is being setup.
1989 */
1990 void
1991 storeDiskdDirParse(SwapDir *sd, int index, char *path)
1992 {
1993 char *token;
1994 int i;
1995 int size;
1996 int l1;
1997 int l2;
1998 int magic1, magic2;
1999 unsigned int read_only = 0;
2000 diskdinfo_t *diskdinfo;
2001
2002 i = GetInteger();
2003 size = i << 10; /* Mbytes to kbytes */
2004 if (size <= 0)
2005 fatal("storeDiskdDirParse: invalid size value");
2006 i = GetInteger();
2007 l1 = i;
2008 if (l1 <= 0)
2009 fatal("storeDiskdDirParse: invalid level 1 directories value");
2010 i = GetInteger();
2011 l2 = i;
2012 if (l2 <= 0)
2013 fatal("storeDiskdDirParse: invalid level 2 directories value");
2014 i = GetInteger();
2015 magic1 = i;
2016 if (magic1 <= 0)
2017 fatal("storeDiskdDirParse: invalid magic1 value");
2018 i = GetInteger();
2019 magic2 = i;
2020 if (magic2 <= 0)
2021 fatal("storeDiskdDirParse: invalid magic2 value");
2022
2023
2024 if ((token = strtok(NULL, w_space)))
2025 if (!strcasecmp(token, "read-only"))
2026 read_only = 1;
2027
2028 diskdinfo = xmalloc(sizeof(diskdinfo_t));
2029 if (diskdinfo == NULL)
2030 fatal("storeDiskdDirParse: couldn't xmalloc() diskdinfo_t!\n");
2031
2032 sd->index = index;
2033 sd->path = xstrdup(path);
2034 sd->max_size = size;
2035 sd->fsdata = diskdinfo;
2036 diskdinfo->l1 = l1;
2037 diskdinfo->l2 = l2;
2038 diskdinfo->swaplog_fd = -1;
2039 diskdinfo->map = NULL; /* Debugging purposes */
2040 diskdinfo->suggest = 0;
2041 diskdinfo->magic1 = magic1;
2042 diskdinfo->magic2 = magic2;
2043 sd->flags.read_only = read_only;
2044 sd->init = storeDiskdDirInit;
2045 sd->newfs = storeDiskdDirNewfs;
2046 sd->dump = storeDiskdDirDump;
2047 sd->freefs = storeDiskdDirFree;
2048 sd->dblcheck = storeDiskdCleanupDoubleCheck;
2049 sd->statfs = storeDiskdDirStats;
2050 sd->maintainfs = storeDiskdDirMaintain;
2051 sd->checkobj = storeDiskdDirCheckObj;
2052 sd->refobj = storeDiskdDirRefObj;
2053 sd->unrefobj = storeDiskdDirUnrefObj;
2054 sd->callback = storeDiskdDirCallback;
2055 sd->sync = storeDiskdDirSync;
2056 sd->obj.create = storeDiskdCreate;
2057 sd->obj.open = storeDiskdOpen;
2058 sd->obj.close = storeDiskdClose;
2059 sd->obj.read = storeDiskdRead;
2060 sd->obj.write = storeDiskdWrite;
2061 sd->obj.unlink = storeDiskdUnlink;
2062 sd->log.open = storeDiskdDirOpenSwapLog;
2063 sd->log.close = storeDiskdDirCloseSwapLog;
2064 sd->log.write = storeDiskdDirSwapLog;
2065 sd->log.clean.open = storeDiskdDirWriteCleanOpen;
2066
2067 /* Initialise replacement policy stuff */
2068 #if HEAP_REPLACEMENT
2069 /*
2070 * Create new heaps with cache replacement policies attached to them.
2071 * The cache replacement policy is specified as either GDSF or LFUDA in
2072 * the squid.conf configuration file. Note that the replacement policy
2073 * applies only to the disk replacement algorithm. Memory replacement
2074 * always uses GDSF since we want to maximize object hit rate.
2075 */
2076 if (Config.replPolicy) {
2077 if (tolower(Config.replPolicy[0]) == 'g') {
2078 debug(20, 1) ("Using GDSF disk replacement policy\n");
2079 sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
2080 } else if (tolower(Config.replPolicy[0]) == 'l') {
2081 if (tolower(Config.replPolicy[1]) == 'f') {
2082 debug(20, 1) ("Using LFUDA disk replacement policy\n");
2083 sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
2084 } else if (tolower(Config.replPolicy[1]) == 'r') {
2085 debug(20, 1) ("Using LRU heap disk replacement policy\n");
2086 sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
2087 }
2088 } else {
2089 debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
2090 sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
2091 }
2092 } else {
2093 debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
2094 sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
2095 }
2096 #else
2097 sd->repl.lru.list.head = NULL;
2098 sd->repl.lru.list.tail = NULL;
2099 #endif
2100 }
2101
2102 /*
2103 * Initial setup / end destruction
2104 */
2105 void
2106 storeDiskdDirDone(void)
2107 {
2108 memPoolDestroy(diskd_state_pool);
2109 diskd_initialised = 0;
2110 }
2111
2112 void
2113 storeFsSetup_diskd(storefs_entry_t *storefs)
2114 {
2115 assert(!diskd_initialised);
2116 storefs->parsefunc = storeDiskdDirParse;
2117 storefs->reconfigurefunc = storeDiskdDirReconfigure;
2118 storefs->donefunc = storeDiskdDirDone;
2119 diskd_state_pool = memPoolCreate("DISKD IO State data", sizeof(diskdstate_t));
2120 memset(&diskd_stats, '\0', sizeof(diskd_stats));
2121 cachemgrRegister("diskd", "DISKD Stats", storeDiskdStats, 0, 1);
2122 debug(81, 1) ("diskd started\n");
2123 diskd_initialised = 1;
2124 }
2125