Below is the file 'src/fs/aufs/store_dir_aufs.c' from this revision. You can also download the file.
/* * $Id: store_dir_aufs.c,v 1.40.2.14 2005/03/26 23:27:10 serassio Exp $ * * DEBUG: section 47 Store Directory Routines * AUTHOR: Duane Wessels * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from * the Internet community; see the CONTRIBUTORS file for full * details. Many organizations have provided support for Squid's * development; see the SPONSORS file for full details. Squid is * Copyrighted (C) 2001 by the Regents of the University of * California; see the COPYRIGHT file for full details. Squid * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" #include "store_asyncufs.h" #define DefaultLevelOneDirs 16 #define DefaultLevelTwoDirs 256 #define STORE_META_BUFSZ 4096 typedef struct _RebuildState RebuildState; struct _RebuildState { SwapDir *sd; int n_read; FILE *log; int speed; int curlvl1; int curlvl2; struct { unsigned int need_to_validate:1; unsigned int clean:1; unsigned int init:1; } flags; int done; int in_dir; int fn; struct dirent *entry; DIR *td; char fullpath[SQUID_MAXPATHLEN]; char fullfilename[SQUID_MAXPATHLEN]; struct _store_rebuild_data counts; }; int n_asyncufs_dirs = 0; static int *asyncufs_dir_index = NULL; MemPool *squidaio_state_pool = NULL; MemPool *aufs_qread_pool = NULL; MemPool *aufs_qwrite_pool = NULL; static int asyncufs_initialised = 0; static char *storeAufsDirSwapSubDir(SwapDir *, int subdirn); static int storeAufsDirCreateDirectory(const char *path, int); static int storeAufsDirVerifyCacheDirs(SwapDir *); static int storeAufsDirVerifyDirectory(const char *path); static void storeAufsDirCreateSwapSubDirs(SwapDir *); static char *storeAufsDirSwapLogFile(SwapDir *, const char *); static EVH storeAufsDirRebuildFromDirectory; static EVH storeAufsDirRebuildFromSwapLog; static int storeAufsDirGetNextFile(RebuildState *, sfileno *, int *size); static StoreEntry *storeAufsDirAddDiskRestore(SwapDir * SD, const cache_key * key, sfileno file_number, squid_file_sz swap_file_sz, time_t expires, time_t timestamp, time_t lastref, time_t lastmod, u_num32 refcount, u_short flags, int clean); static void storeAufsDirRebuild(SwapDir * sd); static void storeAufsDirCloseTmpSwapLog(SwapDir * sd); static FILE *storeAufsDirOpenTmpSwapLog(SwapDir *, int *, int *); static STLOGOPEN storeAufsDirOpenSwapLog; static STINIT storeAufsDirInit; static STFREE storeAufsDirFree; static STLOGCLEANSTART storeAufsDirWriteCleanStart; static STLOGCLEANNEXTENTRY storeAufsDirCleanLogNextEntry; static STLOGCLEANWRITE storeAufsDirWriteCleanEntry; static STLOGCLEANDONE storeAufsDirWriteCleanDone; static STLOGCLOSE storeAufsDirCloseSwapLog; static STLOGWRITE storeAufsDirSwapLog; static STNEWFS storeAufsDirNewfs; static STDUMP storeAufsDirDump; static STMAINTAINFS storeAufsDirMaintain; static STCHECKOBJ storeAufsDirCheckObj; static STCHECKLOADAV storeAufsDirCheckLoadAv; static STREFOBJ storeAufsDirRefObj; static STUNREFOBJ storeAufsDirUnrefObj; static QS rev_int_sort; static int storeAufsDirClean(int swap_index); static EVH storeAufsDirCleanEvent; static int storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2); static int storeAufsCleanupDoubleCheck(SwapDir *, StoreEntry *); static void storeAufsDirStats(SwapDir *, StoreEntry *); static void storeAufsDirInitBitmap(SwapDir *); static int storeAufsDirValidFileno(SwapDir *, sfileno, int); /* The MAIN externally visible function */ STSETUP storeFsSetup_aufs; /* * These functions were ripped straight out of the heart of store_dir.c. * They assume that the given filenum is on a asyncufs partiton, which may or * may not be true.. * XXX this evilness should be tidied up at a later date! */ static int storeAufsDirMapBitTest(SwapDir * SD, sfileno filn) { squidaioinfo_t *aioinfo; aioinfo = (squidaioinfo_t *) SD->fsdata; return file_map_bit_test(aioinfo->map, filn); } static void storeAufsDirMapBitSet(SwapDir * SD, sfileno filn) { squidaioinfo_t *aioinfo; aioinfo = (squidaioinfo_t *) SD->fsdata; file_map_bit_set(aioinfo->map, filn); } void storeAufsDirMapBitReset(SwapDir * SD, sfileno filn) { squidaioinfo_t *aioinfo; aioinfo = (squidaioinfo_t *) SD->fsdata; /* * We have to test the bit before calling file_map_bit_reset. * file_map_bit_reset doesn't do bounds checking. It assumes * filn is a valid file number, but it might not be because * the map is dynamic in size. Also clearing an already clear * bit puts the map counter of-of-whack. */ if (file_map_bit_test(aioinfo->map, filn)) file_map_bit_reset(aioinfo->map, filn); } int storeAufsDirMapBitAllocate(SwapDir * SD) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata; int fn; fn = file_map_allocate(aioinfo->map, aioinfo->suggest); file_map_bit_set(aioinfo->map, fn); aioinfo->suggest = fn + 1; return fn; } /* * Initialise the asyncufs bitmap * * If there already is a bitmap, and the numobjects is larger than currently * configured, we allocate a new bitmap and 'grow' the old one into it. */ static void storeAufsDirInitBitmap(SwapDir * sd) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata; if (aioinfo->map == NULL) { /* First time */ aioinfo->map = file_map_create(); } else if (aioinfo->map->max_n_files) { /* it grew, need to expand */ /* XXX We don't need it anymore .. */ } /* else it shrunk, and we leave the old one in place */ } static char * storeAufsDirSwapSubDir(SwapDir * sd, int subdirn) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata; LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); assert(0 <= subdirn && subdirn < aioinfo->l1); snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn); return fullfilename; } static int storeAufsDirCreateDirectory(const char *path, int should_exist) { int created = 0; struct stat st; getCurrentTime(); if (0 == stat(path, &st)) { if (S_ISDIR(st.st_mode)) { debug(47, should_exist ? 3 : 1) ("%s exists\n", path); } else { fatalf("Swap directory %s is not a directory.", path); } } else if (0 == mkdir(path, 0755)) { debug(47, should_exist ? 1 : 3) ("%s created\n", path); created = 1; } else { fatalf("Failed to make swap directory %s: %s", path, xstrerror()); } return created; } static int storeAufsDirVerifyDirectory(const char *path) { struct stat sb; if (stat(path, &sb) < 0) { debug(47, 0) ("%s: %s\n", path, xstrerror()); return -1; } if (S_ISDIR(sb.st_mode) == 0) { debug(47, 0) ("%s is not a directory\n", path); return -1; } return 0; } /* * This function is called by storeAufsDirInit(). If this returns < 0, * then Squid exits, complains about swap directories not * existing, and instructs the admin to run 'squid -z' */ static int storeAufsDirVerifyCacheDirs(SwapDir * sd) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata; int j; const char *path = sd->path; if (storeAufsDirVerifyDirectory(path) < 0) return -1; for (j = 0; j < aioinfo->l1; j++) { path = storeAufsDirSwapSubDir(sd, j); if (storeAufsDirVerifyDirectory(path) < 0) return -1; } return 0; } static void storeAufsDirCreateSwapSubDirs(SwapDir * sd) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata; int i, k; int should_exist; LOCAL_ARRAY(char, name, MAXPATHLEN); for (i = 0; i < aioinfo->l1; i++) { snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i); if (storeAufsDirCreateDirectory(name, 0)) should_exist = 0; else should_exist = 1; debug(47, 1) ("Making directories in %s\n", name); for (k = 0; k < aioinfo->l2; k++) { snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k); storeAufsDirCreateDirectory(name, should_exist); } } } static char * storeAufsDirSwapLogFile(SwapDir * sd, const char *ext) { LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN); LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN); LOCAL_ARRAY(char, digit, 32); char *pathtmp2; if (Config.Log.swap) { xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64); pathtmp2 = pathtmp; while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL) *pathtmp2 = '.'; while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.') pathtmp[strlen(pathtmp) - 1] = '\0'; for (pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++); snprintf(path, SQUID_MAXPATHLEN - 64, Config.Log.swap, pathtmp2); if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) { strcat(path, "."); snprintf(digit, 32, "%02d", sd->index); strncat(path, digit, 3); } } else { xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64); strcat(path, "/swap.state"); } if (ext) strncat(path, ext, 16); return path; } static void storeAufsDirOpenSwapLog(SwapDir * sd) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata; char *path; int fd; path = storeAufsDirSwapLogFile(sd, NULL); if (aioinfo->swaplog_fd >= 0) { debug(50, 1) ("storeAufsDirOpenSwapLog: %s already open\n", path); return; } fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY); if (fd < 0) { debug(50, 1) ("%s: %s\n", path, xstrerror()); fatal("storeAufsDirOpenSwapLog: Failed to open swap log."); } debug(50, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); aioinfo->swaplog_fd = fd; } static void storeAufsDirCloseSwapLog(SwapDir * sd) { squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata; if (aioinfo->swaplog_fd < 0) /* not open */ return; file_close(aioinfo->swaplog_fd); debug(47, 3) ("Cache Dir #%d log closed on FD %d\n", sd->index, aioinfo->swaplog_fd); aioinfo->swaplog_fd = -1; } static void storeAufsDirInit(SwapDir * sd) { static int started_clean_event = 0; static const char *errmsg = "\tFailed to verify one of the swap directories, Check cache.log\n" "\tfor details. Run 'squid -z' to create swap directories\n" "\tif needed, or if running Squid for the first time."; storeAufsDirInitBitmap(sd); if (storeAufsDirVerifyCacheDirs(sd) < 0) fatal(errmsg); squidaio_init(); storeAufsDirOpenSwapLog(sd); storeAufsDirRebuild(sd); if (!started_clean_event) { eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL, 15.0, 1); started_clean_event = 1; } (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize); } static void storeAufsDirRebuildComplete(RebuildState * rb) { if (rb->log) { debug(47, 1) ("Done reading %s swaplog (%d entries)\n", rb->sd->path, rb->n_read); fclose(rb->log); rb->log = NULL; } else { debug(47, 1) ("Done scanning %s (%d entries)\n", rb->sd->path, rb->counts.scancount); } store_dirs_rebuilding--; storeAufsDirCloseTmpSwapLog(rb->sd); storeRebuildComplete(&rb->counts); cbdataFree(rb); } static void storeAufsDirRebuildFromDirectory(void *data) { RebuildState *rb = data; SwapDir *SD = rb->sd; LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE); StoreEntry *e = NULL; StoreEntry tmpe; cache_key key[MD5_DIGEST_CHARS]; sfileno filn = 0; int count; int size; struct stat sb; int swap_hdr_len; int fd = -1; tlv *tlv_list; tlv *t; assert(rb != NULL); debug(47, 3) ("storeAufsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index); for (count = 0; count < rb->speed; count++) { assert(fd == -1); fd = storeAufsDirGetNextFile(rb, &filn, &size); if (fd == -2) { storeAufsDirRebuildComplete(rb); return; } else if (fd < 0) { continue; } assert(fd > -1); /* lets get file stats here */ if (fstat(fd, &sb) < 0) { debug(47, 1) ("storeAufsDirRebuildFromDirectory: fstat(FD %d): %s\n", fd, xstrerror()); file_close(fd); store_open_disk_fd--; fd = -1; continue; } if ((++rb->counts.scancount & 0xFFFF) == 0) debug(47, 3) (" %s %7d files opened so far.\n", rb->sd->path, rb->counts.scancount); debug(47, 9) ("file_in: fd=%d %08X\n", fd, filn); statCounter.syscalls.disk.reads++; if (FD_READ_METHOD(fd, hdr_buf, SM_PAGE_SIZE) < 0) { debug(47, 1) ("storeAufsDirRebuildFromDirectory: read(FD %d): %s\n", fd, xstrerror()); file_close(fd); store_open_disk_fd--; fd = -1; continue; } file_close(fd); store_open_disk_fd--; fd = -1; swap_hdr_len = 0; #if USE_TRUNCATE if (sb.st_size == 0) continue; #endif tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len); if (tlv_list == NULL) { debug(47, 1) ("storeAufsDirRebuildFromDirectory: failed to get meta data\n"); /* XXX shouldn't this be a call to storeAufsUnlink ? */ storeAufsDirUnlinkFile(SD, filn); continue; } debug(47, 3) ("storeAufsDirRebuildFromDirectory: successful swap meta unpacking\n"); memset(key, '\0', MD5_DIGEST_CHARS); memset(&tmpe, '\0', sizeof(StoreEntry)); for (t = tlv_list; t; t = t->next) { switch (t->type) { case STORE_META_KEY: assert(t->length == MD5_DIGEST_CHARS); xmemcpy(key, t->value, MD5_DIGEST_CHARS); break; #if SIZEOF_SQUID_FILE_SZ == SIZEOF_SIZE_T case STORE_META_STD: assert(t->length == STORE_HDR_METASIZE); xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE); break; #else case STORE_META_STD_LFS: assert(t->length == STORE_HDR_METASIZE); xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE); break; case STORE_META_STD: assert(t->length == STORE_HDR_METASIZE_OLD); { struct { time_t timestamp; time_t lastref; time_t expires; time_t lastmod; size_t swap_file_sz; u_short refcount; u_short flags; } *tmp = t->value; assert(sizeof(*tmp) == STORE_HDR_METASIZE_OLD); tmpe.timestamp = tmp->timestamp; tmpe.lastref = tmp->lastref; tmpe.expires = tmp->expires; tmpe.lastmod = tmp->lastmod; tmpe.swap_file_sz = tmp->swap_file_sz; tmpe.refcount = tmp->refcount; tmpe.flags = tmp->flags; } break; #endif default: break; } } storeSwapTLVFree(tlv_list); tlv_list = NULL; if (storeKeyNull(key)) { debug(47, 1) ("storeAufsDirRebuildFromDirectory: NULL key\n"); storeAufsDirUnlinkFile(SD, filn); continue; } tmpe.hash.key = key; /* check sizes */ if (tmpe.swap_file_sz == 0) { tmpe.swap_file_sz = sb.st_size; } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) { tmpe.swap_file_sz = sb.st_size; } else if (tmpe.swap_file_sz != sb.st_size) { debug(47, 1) ("storeAufsDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n", (long int) tmpe.swap_file_sz, (long int) sb.st_size); storeAufsDirUnlinkFile(SD, filn); continue; } if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) { storeAufsDirUnlinkFile(SD, filn); rb->counts.badflags++; continue; } e = storeGet(key); if (e && e->lastref >= tmpe.lastref) { /* key already exists, current entry is newer */ /* keep old, ignore new */ rb->counts.dupcount++; continue; } else if (NULL != e) { /* URL already exists, this swapfile not being used */ /* junk old, load new */ storeRelease(e); /* release old entry */ rb->counts.dupcount++; } rb->counts.objcount++; storeEntryDump(&tmpe, 5); e = storeAufsDirAddDiskRestore(SD, key, filn, tmpe.swap_file_sz, tmpe.expires, tmpe.timestamp, tmpe.lastref, tmpe.lastmod, tmpe.refcount, /* refcount */ tmpe.flags, /* flags */ (int) rb->flags.clean); storeDirSwapLog(e, SWAP_LOG_ADD); } eventAdd("storeRebuild", storeAufsDirRebuildFromDirectory, rb, 0.0, 1); } static void storeAufsDirRebuildFromSwapLog(void *data) { RebuildState *rb = data; SwapDir *SD = rb->sd; StoreEntry *e = NULL; storeSwapLogData s; size_t ss = sizeof(storeSwapLogData); int count; int used; /* is swapfile already in use? */ int disk_entry_newer; /* is the log entry newer than current entry? */ double x; assert(rb != NULL); /* load a number of objects per invocation */ for (count = 0; count < rb->speed; count++) { if (fread(&s, ss, 1, rb->log) != 1) { storeAufsDirRebuildComplete(rb); return; } rb->n_read++; /* * BC: during 2.4 development, we changed the way swap file * numbers are assigned and stored. The high 16 bits used * to encode the SD index number. There used to be a call * to storeDirProperFileno here that re-assigned the index * bits. Now, for backwards compatibility, we just need * to mask it off. */ s.swap_filen &= 0x00FFFFFF; debug(47, 3) ("storeAufsDirRebuildFromSwapLog: %s %s %08X\n", swap_log_op_str[(int) s.op], storeKeyText(s.key), s.swap_filen); if (s.op == SWAP_LOG_ADD) { (void) 0; } else if (s.op == SWAP_LOG_DEL) { /* Delete unless we already have a newer copy */ if ((e = storeGet(s.key)) != NULL && s.lastref > e->lastref) { /* * Make sure we don't unlink the file, it might be * in use by a subsequent entry. Also note that * we don't have to subtract from store_swap_size * because adding to store_swap_size happens in * the cleanup procedure. */ storeExpireNow(e); storeReleaseRequest(e); if (e->swap_filen > -1) { storeAufsDirReplRemove(e); storeAufsDirMapBitReset(SD, e->swap_filen); e->swap_filen = -1; e->swap_dirn = -1; } storeRelease(e); rb->counts.objcount--; rb->counts.cancelcount++; } continue; } else { x = log(++rb->counts.bad_log_op) / log(10.0); if (0.0 == x - (double) (int) x) debug(47, 1) ("WARNING: %d invalid swap log entries found\n", rb->counts.bad_log_op); rb->counts.invalid++; continue; } if ((++rb->counts.scancount & 0xFFF) == 0) { struct stat sb; if (0 == fstat(fileno(rb->log), &sb)) storeRebuildProgress(SD->index, (int) sb.st_size / ss, rb->n_read); } if (!storeAufsDirValidFileno(SD, s.swap_filen, 0)) { rb->counts.invalid++; continue; } if (EBIT_TEST(s.flags, KEY_PRIVATE)) { rb->counts.badflags++; continue; } e = storeGet(s.key); used = storeAufsDirMapBitTest(SD, s.swap_filen); /* If this URL already exists in the cache, does the swap log * appear to have a newer entry? Compare 'lastref' from the * swap log to e->lastref. */ disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0; if (used && !disk_entry_newer) { /* log entry is old, ignore it */ rb->counts.clashcount++; continue; } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) { /* swapfile taken, same URL, newer, update meta */ if (e->store_status == STORE_OK) { e->lastref = s.timestamp; e->timestamp = s.timestamp; e->expires = s.expires; e->lastmod = s.lastmod; e->flags = s.flags; e->refcount += s.refcount; storeAufsDirUnrefObj(SD, e); } else { debug_trap("storeAufsDirRebuildFromSwapLog: bad condition"); debug(47, 1) ("\tSee %s:%d\n", __FILE__, __LINE__); } continue; } else if (used) { /* swapfile in use, not by this URL, log entry is newer */ /* This is sorta bad: the log entry should NOT be newer at this * point. If the log is dirty, the filesize check should have * caught this. If the log is clean, there should never be a * newer entry. */ debug(47, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n", SD->index, s.swap_filen); /* I'm tempted to remove the swapfile here just to be safe, * but there is a bad race condition in the NOVM version if * the swapfile has recently been opened for writing, but * not yet opened for reading. Because we can't map * swapfiles back to StoreEntrys, we don't know the state * of the entry using that file. */ /* We'll assume the existing entry is valid, probably because * were in a slow rebuild and the the swap file number got taken * and the validation procedure hasn't run. */ assert(rb->flags.need_to_validate); rb->counts.clashcount++; continue; } else if (e && !disk_entry_newer) { /* key already exists, current entry is newer */ /* keep old, ignore new */ rb->counts.dupcount++; continue; } else if (e) { /* key already exists, this swapfile not being used */ /* junk old, load new */ storeExpireNow(e); storeReleaseRequest(e); if (e->swap_filen > -1) { storeAufsDirReplRemove(e); /* Make sure we don't actually unlink the file */ storeAufsDirMapBitReset(SD, e->swap_filen); e->swap_filen = -1; e->swap_dirn = -1; } storeRelease(e); rb->counts.dupcount++; } else { /* URL doesnt exist, swapfile not in use */ /* load new */ (void) 0; } /* update store_swap_size */ rb->counts.objcount++; e = storeAufsDirAddDiskRestore(SD, s.key, s.swap_filen, s.swap_file_sz, s.expires, s.timestamp, s.lastref, s.lastmod, s.refcount, s.flags, (int) rb->flags.clean); storeDirSwapLog(e, SWAP_LOG_ADD); } eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLog, rb, 0.0, 1); } #if SIZEOF_SQUID_FILE_SZ != SIZEOF_SIZE_T /* This is an exact copy of the above, but using storeSwapLogDataOld entry type */ static void storeAufsDirRebuildFromSwapLogOld(void *data) { RebuildState *rb = data; SwapDir *SD = rb->sd; StoreEntry *e = NULL; storeSwapLogDataOld s; size_t ss = sizeof(storeSwapLogDataOld); int count; int used; /* is swapfile already in use? */ int disk_entry_newer; /* is the log entry newer than current entry? */ double x; assert(rb != NULL); /* load a number of objects per invocation */ for (count = 0; count < rb->speed; count++) { if (fread(&s, ss, 1, rb->log) != 1) { storeAufsDirRebuildComplete(rb); return; } rb->n_read++; /* * BC: during 2.4 development, we changed the way swap file * numbers are assigned and stored. The high 16 bits used * to encode the SD index number. There used to be a call * to storeDirProperFileno here that re-assigned the index * bits. Now, for backwards compatibility, we just need * to mask it off. */ s.swap_filen &= 0x00FFFFFF; debug(47, 3) ("storeAufsDirRebuildFromSwapLog: %s %s %08X\n", swap_log_op_str[(int) s.op], storeKeyText(s.key), s.swap_filen); if (s.op == SWAP_LOG_ADD) { (void) 0; } else if (s.op == SWAP_LOG_DEL) { /* Delete unless we already have a newer copy */ if ((e = storeGet(s.key)) != NULL && s.lastref > e->lastref) { /* * Make sure we don't unlink the file, it might be * in use by a subsequent entry. Also note that * we don't have to subtract from store_swap_size * because adding to store_swap_size happens in * the cleanup procedure. */ storeExpireNow(e); storeReleaseRequest(e); if (e->swap_filen > -1) { storeAufsDirReplRemove(e); storeAufsDirMapBitReset(SD, e->swap_filen); e->swap_filen = -1; e->swap_dirn = -1; } storeRelease(e); rb->counts.objcount--; rb->counts.cancelcount++; } continue; } else { x = log(++rb->counts.bad_log_op) / log(10.0); if (0.0 == x - (double) (int) x) debug(47, 1) ("WARNING: %d invalid swap log entries found\n", rb->counts.bad_log_op); rb->counts.invalid++; continue; } if ((++rb->counts.scancount & 0xFFF) == 0) { struct stat sb; if (0 == fstat(fileno(rb->log), &sb)) storeRebuildProgress(SD->index, (int) sb.st_size / ss, rb->n_read); } if (!storeAufsDirValidFileno(SD, s.swap_filen, 0)) { rb->counts.invalid++; continue; } if (EBIT_TEST(s.flags, KEY_PRIVATE)) { rb->counts.badflags++; continue; } e = storeGet(s.key); used = storeAufsDirMapBitTest(SD, s.swap_filen); /* If this URL already exists in the cache, does the swap log * appear to have a newer entry? Compare 'lastref' from the * swap log to e->lastref. */ disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0; if (used && !disk_entry_newer) { /* log entry is old, ignore it */ rb->counts.clashcount++; continue; } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) { /* swapfile taken, same URL, newer, update meta */ if (e->store_status == STORE_OK) { e->lastref = s.timestamp; e->timestamp = s.timestamp; e->expires = s.expires; e->lastmod = s.lastmod; e->flags = s.flags; e->refcount += s.refcount; storeAufsDirUnrefObj(SD, e); } else { debug_trap("storeAufsDirRebuildFromSwapLog: bad condition"); debug(47, 1) ("\tSee %s:%d\n", __FILE__, __LINE__); } continue; } else if (used) { /* swapfile in use, not by this URL, log entry is newer */ /* This is sorta bad: the log entry should NOT be newer at this * point. If the log is dirty, the filesize check should have * caught this. If the log is clean, there should never be a * newer entry. */ debug(47, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n", SD->index, s.swap_filen); /* I'm tempted to remove the swapfile here just to be safe, * but there is a bad race condition in the NOVM version if * the swapfile has recently been opened for writing, but * not yet opened for reading. Because we can't map * swapfiles back to StoreEntrys, we don't know the state * of the entry using that file. */ /* We'll assume the existing entry is valid, probably because * were in a slow rebuild and the the swap file number got taken * and the validation procedure hasn't run. */ assert(rb->flags.need_to_validate); rb->counts.clashcount++; continue; } else if (e && !disk_entry_newer) { /* key already exists, current entry is newer */ /* keep old, ignore new */ rb->counts.dupcount++; continue; } else if (e) { /* key already exists, this swapfile not being used */ /* junk old, load new */ storeExpireNow(e); storeReleaseRequest(e); if (e->swap_filen > -1) { storeAufsDirReplRemove(e); /* Make sure we don't actually unlink the file */ storeAufsDirMapBitReset(SD, e->swap_filen); e->swap_filen = -1; e->swap_dirn = -1; } storeRelease(e); rb->counts.dupcount++; } else { /* URL doesnt exist, swapfile not in use */ /* load new */ (void) 0; } /* update store_swap_size */ rb->counts.objcount++; e = storeAufsDirAddDiskRestore(SD, s.key, s.swap_filen, s.swap_file_sz, s.expires, s.timestamp, s.lastref, s.lastmod, s.refcount, s.flags, (int) rb->flags.clean); storeDirSwapLog(e, SWAP_LOG_ADD); } eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLogOld, rb, 0.0, 1); } #endif static void storeAufsDirRebuildFromSwapLogCheckVersion(void *data) { RebuildState *rb = data; storeSwapLogHeader hdr; if (fread(&hdr, sizeof(hdr), 1, rb->log) != 1) { storeAufsDirRebuildComplete(rb); return; } if (hdr.op == SWAP_LOG_VERSION) { if (fseek(rb->log, hdr.record_size, SEEK_SET) != 0) { storeAufsDirRebuildComplete(rb); return; } if (hdr.version == 1 && hdr.record_size == sizeof(storeSwapLogData)) { eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLog, rb, 0.0, 1); return; } #if SIZEOF_SQUID_FILE_SZ != SIZEOF_SIZE_T if (hdr.version == 1 && hdr.record_size == sizeof(storeSwapLogDataOld)) { debug(47, 1) ("storeAufsDirRebuildFromSwapLog: Found current version but without large file support. Upgrading\n"); eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLogOld, rb, 0.0, 1); return; } #endif debug(47, 1) ("storeAufsDirRebuildFromSwapLog: Unsupported swap.state version %d size %d\n", hdr.version, hdr.record_size); storeAufsDirRebuildComplete(rb); return; } rewind(rb->log); debug(47, 1) ("storeAufsDirRebuildFromSwapLog: Old version detected. Upgrading\n"); #if SIZEOF_SQUID_FILE_SZ == SIZEOF_SIZE_T eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLog, rb, 0.0, 1); #else eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLogOld, rb, 0.0, 1); #endif } static int storeAufsDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size) { SwapDir *SD = rb->sd; squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata; int fd = -1; int used = 0; int dirs_opened = 0; debug(47, 3) ("storeAufsDirGetNextFile: flag=%d, %d: /%02X/%02X\n", rb->flags.init, rb->sd->index, rb->curlvl1, rb->curlvl2); if (rb->done) return -2; while (fd < 0 && rb->done == 0) { fd = -1; if (0 == rb->flags.init) { /* initialize, open first file */ rb->done = 0; rb->curlvl1 = 0; rb->curlvl2 = 0; rb->in_dir = 0; rb->flags.init = 1; assert(Config.cacheSwap.n_configured > 0); } if (0 == rb->in_dir) { /* we need to read in a new directory */ snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X", rb->sd->path, rb->curlvl1, rb->curlvl2); if (dirs_opened) return -1; rb->td = opendir(rb->fullpath); dirs_opened++; if (rb->td == NULL) { debug(47, 1) ("storeAufsDirGetNextFile: opendir: %s: %s\n", rb->fullpath, xstrerror()); } else { rb->entry = readdir(rb->td); /* skip . and .. */ rb->entry = readdir(rb->td); if (rb->entry == NULL && errno == ENOENT) debug(47, 1) ("storeAufsDirGetNextFile: directory does not exist!.\n"); debug(47, 3) ("storeAufsDirGetNextFile: Directory %s\n", rb->fullpath); } } if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) { rb->in_dir++; if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) { debug(47, 3) ("storeAufsDirGetNextFile: invalid %s\n", rb->entry->d_name); continue; } if (!storeAufsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) { debug(47, 3) ("storeAufsDirGetNextFile: %08X does not belong in %d/%d/%d\n", rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2); continue; } used = storeAufsDirMapBitTest(SD, rb->fn); if (used) { debug(47, 3) ("storeAufsDirGetNextFile: Locked, continuing with next.\n"); continue; } snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s", rb->fullpath, rb->entry->d_name); debug(47, 3) ("storeAufsDirGetNextFile: Opening %s\n", rb->fullfilename); fd = file_open(rb->fullfilename, O_RDONLY | O_BINARY); if (fd < 0) debug(47, 1) ("storeAufsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror()); else store_open_disk_fd++; continue; } if (rb->td != NULL) closedir(rb->td); rb->td = NULL; rb->in_dir = 0; if (++rb->curlvl2 < aioinfo->l2) continue; rb->curlvl2 = 0; if (++rb->curlvl1 < aioinfo->l1) continue; rb->curlvl1 = 0; rb->done = 1; } *filn_p = rb->fn; return fd; } /* Add a new object to the cache with empty memory copy and pointer to disk * use to rebuild store from disk. */ static StoreEntry * storeAufsDirAddDiskRestore(SwapDir * SD, const cache_key * key, sfileno file_number, squid_file_sz swap_file_sz, time_t expires, time_t timestamp, time_t lastref, time_t lastmod, u_num32 refcount, u_short flags, int clean) { StoreEntry *e = NULL; debug(47, 5) ("storeAufsAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number); /* if you call this you'd better be sure file_number is not * already in use! */ e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL); e->store_status = STORE_OK; storeSetMemStatus(e, NOT_IN_MEMORY); e->swap_status = SWAPOUT_DONE; e->swap_filen = file_number; e->swap_dirn = SD->index; e->swap_file_sz = swap_file_sz; e->lock_count = 0; e->lastref = lastref; e->timestamp = timestamp; e->expires = expires; e->lastmod = lastmod; e->refcount = refcount; e->flags = flags; EBIT_SET(e->flags, ENTRY_CACHABLE); EBIT_CLR(e->flags, RELEASE_REQUEST); EBIT_CLR(e->flags, KEY_PRIVATE); e->ping_status = PING_NONE; EBIT_CLR(e->flags, ENTRY_VALIDATED); storeAufsDirMapBitSet(SD, e->swap_filen); storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */ storeAufsDirReplAdd(SD, e); return e; } CBDATA_TYPE(RebuildState); static void storeAufsDirRebuild(SwapDir * sd) { RebuildState *rb; int clean = 0; int zero = 0; FILE *fp; EVH *func = NULL; CBDATA_INIT_TYPE(RebuildState); rb = cbdataAlloc(RebuildState); rb->sd = sd; rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; /* * If the swap.state file exists in the cache_dir, then * we'll use storeAufsDirRebuildFromSwapLog(), otherwise we'll * use storeAufsDirRebuildFromDirectory() to open up each file * and suck in the meta data. */ fp = storeAufsDirOpenTmpSwapLog(sd, &clean, &zero); if (fp == NULL || zero) { if (fp != NULL) fclose(fp); func = storeAufsDirRebuildFromDirectory; } else { func = storeAufsDirRebuildFromSwapLogCheckVersion; rb->log