1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 47    Store Directory Routines */
10 
11 #define CLEAN_BUF_SZ 16384
12 
13 #include "squid.h"
14 #include "cache_cf.h"
15 #include "ConfigOption.h"
16 #include "DiskIO/DiskIOModule.h"
17 #include "DiskIO/DiskIOStrategy.h"
18 #include "fde.h"
19 #include "FileMap.h"
20 #include "fs_io.h"
21 #include "globals.h"
22 #include "Parsing.h"
23 #include "RebuildState.h"
24 #include "SquidConfig.h"
25 #include "SquidMath.h"
26 #include "SquidTime.h"
27 #include "StatCounters.h"
28 #include "store_key_md5.h"
29 #include "StoreSearchUFS.h"
30 #include "StoreSwapLogData.h"
31 #include "tools.h"
32 #include "UFSSwapDir.h"
33 
34 #include <cerrno>
35 #include <cmath>
36 #include <random>
37 #if HAVE_SYS_STAT_H
38 #include <sys/stat.h>
39 #endif
40 
41 int Fs::Ufs::UFSSwapDir::NumberOfUFSDirs = 0;
42 int *Fs::Ufs::UFSSwapDir::UFSDirToGlobalDirMapping = NULL;
43 
44 class UFSCleanLog : public SwapDir::CleanLog
45 {
46 
47 public:
UFSCleanLog(SwapDir * aSwapDir)48     UFSCleanLog(SwapDir *aSwapDir) : sd(aSwapDir) {}
49 
50     /// Get the next entry that is a candidate for clean log writing
51     virtual const StoreEntry *nextEntry();
52 
53     /// "write" an entry to the clean log file.
54     virtual void write(StoreEntry const &);
55 
56     SBuf cur;
57     SBuf newLog;
58     SBuf cln;
59     char *outbuf = nullptr;
60     off_t outbuf_offset = 0;
61     int fd = -1;
62     RemovalPolicyWalker *walker = nullptr;
63     SwapDir *sd = nullptr;
64 };
65 
66 const StoreEntry *
nextEntry()67 UFSCleanLog::nextEntry()
68 {
69     const StoreEntry *entry = NULL;
70 
71     if (walker)
72         entry = walker->Next(walker);
73 
74     return entry;
75 }
76 
77 void
write(StoreEntry const & e)78 UFSCleanLog::write(StoreEntry const &e)
79 {
80     StoreSwapLogData s;
81     static size_t ss = sizeof(StoreSwapLogData);
82     s.op = (char) SWAP_LOG_ADD;
83     s.swap_filen = e.swap_filen;
84     s.timestamp = e.timestamp;
85     s.lastref = e.lastref;
86     s.expires = e.expires;
87     s.lastmod = e.lastModified();
88     s.swap_file_sz = e.swap_file_sz;
89     s.refcount = e.refcount;
90     s.flags = e.flags;
91     memcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH);
92     s.finalize();
93     memcpy(outbuf + outbuf_offset, &s, ss);
94     outbuf_offset += ss;
95     /* buffered write */
96 
97     if (outbuf_offset + ss >= CLEAN_BUF_SZ) {
98         if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) {
99             int xerrno = errno;
100             /* XXX This error handling should probably move up to the caller */
101             debugs(50, DBG_CRITICAL, MYNAME << newLog << ": write: " << xstrerr(xerrno));
102             debugs(50, DBG_CRITICAL, MYNAME << "Current swap logfile not replaced.");
103             file_close(fd);
104             fd = -1;
105             unlink(newLog.c_str());
106             sd->cleanLog = NULL;
107             delete this;
108             return;
109         }
110 
111         outbuf_offset = 0;
112     }
113 }
114 
115 bool
canStore(const StoreEntry & e,int64_t diskSpaceNeeded,int & load) const116 Fs::Ufs::UFSSwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const
117 {
118     if (!SwapDir::canStore(e, diskSpaceNeeded, load))
119         return false;
120 
121     if (IO->shedLoad())
122         return false;
123 
124     load = IO->load();
125     return true;
126 }
127 
128 static void
FreeObject(void * address)129 FreeObject(void *address)
130 {
131     StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address);
132     delete anObject;
133 }
134 
135 static int
rev_int_sort(const void * A,const void * B)136 rev_int_sort(const void *A, const void *B)
137 {
138     const int *i1 = (const int *)A;
139     const int *i2 = (const int *)B;
140     return *i2 - *i1;
141 }
142 
143 void
parseSizeL1L2()144 Fs::Ufs::UFSSwapDir::parseSizeL1L2()
145 {
146     int i = GetInteger();
147     if (i <= 0)
148         fatal("UFSSwapDir::parseSizeL1L2: invalid size value");
149 
150     const uint64_t size = static_cast<uint64_t>(i) << 20; // MBytes to Bytes
151 
152     /* just reconfigure it */
153     if (reconfiguring) {
154         if (size == maxSize())
155             debugs(3, 2, "Cache dir '" << path << "' size remains unchanged at " << i << " MB");
156         else
157             debugs(3, DBG_IMPORTANT, "Cache dir '" << path << "' size changed to " << i << " MB");
158     }
159 
160     max_size = size;
161 
162     l1 = GetInteger();
163 
164     if (l1 <= 0)
165         fatal("UFSSwapDir::parseSizeL1L2: invalid level 1 directories value");
166 
167     l2 = GetInteger();
168 
169     if (l2 <= 0)
170         fatal("UFSSwapDir::parseSizeL1L2: invalid level 2 directories value");
171 }
172 
173 void
reconfigure()174 Fs::Ufs::UFSSwapDir::reconfigure()
175 {
176     parseSizeL1L2();
177     parseOptions(1);
178 }
179 
180 void
parse(int anIndex,char * aPath)181 Fs::Ufs::UFSSwapDir::parse (int anIndex, char *aPath)
182 {
183     index = anIndex;
184     path = xstrdup(aPath);
185 
186     parseSizeL1L2();
187 
188     /* Initialise replacement policy stuff */
189     repl = createRemovalPolicy(Config.replPolicy);
190 
191     parseOptions(0);
192 }
193 
194 void
changeIO(DiskIOModule * module)195 Fs::Ufs::UFSSwapDir::changeIO(DiskIOModule *module)
196 {
197     DiskIOStrategy *anIO = module->createStrategy();
198     safe_free(ioType);
199     ioType = xstrdup(module->type());
200 
201     delete IO->io;
202     IO->io = anIO;
203     /* Change the IO Options */
204 
205     if (currentIOOptions && currentIOOptions->options.size() > 2) {
206         delete currentIOOptions->options.back();
207         currentIOOptions->options.pop_back();
208     }
209 
210     /* TODO: factor out these 4 lines */
211     ConfigOption *ioOptions = IO->io->getOptionTree();
212 
213     if (currentIOOptions && ioOptions)
214         currentIOOptions->options.push_back(ioOptions);
215 }
216 
217 bool
optionIOParse(char const * option,const char * value,int isaReconfig)218 Fs::Ufs::UFSSwapDir::optionIOParse(char const *option, const char *value, int isaReconfig)
219 {
220     if (strcmp(option, "IOEngine") != 0)
221         return false;
222 
223     if (isaReconfig)
224         /* silently ignore this */
225         return true;
226 
227     if (!value)
228         self_destruct();
229 
230     DiskIOModule *module = DiskIOModule::Find(value);
231 
232     if (!module)
233         self_destruct();
234 
235     changeIO(module);
236 
237     return true;
238 }
239 
240 void
optionIODump(StoreEntry * e) const241 Fs::Ufs::UFSSwapDir::optionIODump(StoreEntry * e) const
242 {
243     storeAppendPrintf(e, " IOEngine=%s", ioType);
244 }
245 
246 ConfigOption *
getOptionTree() const247 Fs::Ufs::UFSSwapDir::getOptionTree() const
248 {
249     ConfigOption *parentResult = SwapDir::getOptionTree();
250 
251     if (currentIOOptions == NULL)
252         currentIOOptions = new ConfigOptionVector();
253 
254     currentIOOptions->options.push_back(parentResult);
255 
256     currentIOOptions->options.push_back(new ConfigOptionAdapter<UFSSwapDir>(*const_cast<UFSSwapDir *>(this), &UFSSwapDir::optionIOParse, &UFSSwapDir::optionIODump));
257 
258     if (ConfigOption *ioOptions = IO->io->getOptionTree())
259         currentIOOptions->options.push_back(ioOptions);
260 
261     ConfigOption* result = currentIOOptions;
262 
263     currentIOOptions = NULL;
264 
265     return result;
266 }
267 
268 void
init()269 Fs::Ufs::UFSSwapDir::init()
270 {
271     debugs(47, 3, HERE << "Initialising UFS SwapDir engine.");
272     /* Parsing must be finished by now - force to NULL, don't delete */
273     currentIOOptions = NULL;
274     static int started_clean_event = 0;
275     static const char *errmsg =
276         "\tFailed to verify one of the swap directories, Check cache.log\n"
277         "\tfor details.  Run 'squid -z' to create swap directories\n"
278         "\tif needed, or if running Squid for the first time.";
279     IO->init();
280 
281     if (verifyCacheDirs())
282         fatal(errmsg);
283 
284     openLog();
285 
286     rebuild();
287 
288     if (!started_clean_event) {
289         eventAdd("UFS storeDirClean", CleanEvent, NULL, 15.0, 1);
290         started_clean_event = 1;
291     }
292 
293     (void) fsBlockSize(path, &fs.blksize);
294 }
295 
296 void
create()297 Fs::Ufs::UFSSwapDir::create()
298 {
299     debugs(47, 3, "Creating swap space in " << path);
300     createDirectory(path, 0);
301     createSwapSubDirs();
302 }
303 
UFSSwapDir(char const * aType,const char * anIOType)304 Fs::Ufs::UFSSwapDir::UFSSwapDir(char const *aType, const char *anIOType) :
305     SwapDir(aType),
306     IO(NULL),
307     fsdata(NULL),
308     map(new FileMap()),
309     suggest(0),
310     l1(16),
311     l2(256),
312     swaplog_fd(-1),
313     currentIOOptions(new ConfigOptionVector()),
314     ioType(xstrdup(anIOType)),
315     cur_size(0),
316     n_disk_objects(0),
317     rebuilding_(false)
318 {
319     /* modulename is only set to disk modules that are built, by configure,
320      * so the Find call should never return NULL here.
321      */
322     IO = new Fs::Ufs::UFSStrategy(DiskIOModule::Find(anIOType)->createStrategy());
323 }
324 
~UFSSwapDir()325 Fs::Ufs::UFSSwapDir::~UFSSwapDir()
326 {
327     if (swaplog_fd > -1) {
328         file_close(swaplog_fd);
329         swaplog_fd = -1;
330     }
331     xfree(ioType);
332     delete map;
333     delete IO;
334     delete currentIOOptions;
335 }
336 
337 void
dumpEntry(StoreEntry & e) const338 Fs::Ufs::UFSSwapDir::dumpEntry(StoreEntry &e) const
339 {
340     debugs(47, DBG_CRITICAL, HERE << "FILENO "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen);
341     debugs(47, DBG_CRITICAL, HERE << "PATH " << fullPath(e.swap_filen, NULL)   );
342     e.dump(0);
343 }
344 
345 bool
doubleCheck(StoreEntry & e)346 Fs::Ufs::UFSSwapDir::doubleCheck(StoreEntry & e)
347 {
348 
349     struct stat sb;
350 
351     if (::stat(fullPath(e.swap_filen, NULL), &sb) < 0) {
352         debugs(47, DBG_CRITICAL, HERE << "WARNING: Missing swap file");
353         dumpEntry(e);
354         return true;
355     }
356 
357     if ((off_t)e.swap_file_sz != sb.st_size) {
358         debugs(47, DBG_CRITICAL, HERE << "WARNING: Size Mismatch. Entry size: "
359                << e.swap_file_sz << ", file size: " << sb.st_size);
360         dumpEntry(e);
361         return true;
362     }
363 
364     return false;
365 }
366 
367 void
statfs(StoreEntry & sentry) const368 Fs::Ufs::UFSSwapDir::statfs(StoreEntry & sentry) const
369 {
370     int totl_kb = 0;
371     int free_kb = 0;
372     int totl_in = 0;
373     int free_in = 0;
374     int x;
375     storeAppendPrintf(&sentry, "First level subdirectories: %d\n", l1);
376     storeAppendPrintf(&sentry, "Second level subdirectories: %d\n", l2);
377     storeAppendPrintf(&sentry, "Maximum Size: %" PRIu64 " KB\n", maxSize() >> 10);
378     storeAppendPrintf(&sentry, "Current Size: %.2f KB\n", currentSize() / 1024.0);
379     storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n",
380                       Math::doublePercent(currentSize(), maxSize()));
381     storeAppendPrintf(&sentry, "Filemap bits in use: %d of %d (%d%%)\n",
382                       map->numFilesInMap(), map->capacity(),
383                       Math::intPercent(map->numFilesInMap(), map->capacity()));
384     x = fsStats(path, &totl_kb, &free_kb, &totl_in, &free_in);
385 
386     if (0 == x) {
387         storeAppendPrintf(&sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
388                           totl_kb - free_kb,
389                           totl_kb,
390                           Math::intPercent(totl_kb - free_kb, totl_kb));
391         storeAppendPrintf(&sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
392                           totl_in - free_in,
393                           totl_in,
394                           Math::intPercent(totl_in - free_in, totl_in));
395     }
396 
397     storeAppendPrintf(&sentry, "Flags:");
398 
399     if (flags.selected)
400         storeAppendPrintf(&sentry, " SELECTED");
401 
402     if (flags.read_only)
403         storeAppendPrintf(&sentry, " READ-ONLY");
404 
405     storeAppendPrintf(&sentry, "\n");
406 
407     IO->statfs(sentry);
408 }
409 
410 void
maintain()411 Fs::Ufs::UFSSwapDir::maintain()
412 {
413     /* TODO: possible options for improvement;
414      *
415      * Note that too much aggression here is not good. It means that disk
416      * controller is getting a long queue of removals to act on, along
417      * with its regular I/O queue, and that client traffic is 'paused'
418      * and growing the network I/O queue as well while the scan happens.
419      * Possibly bad knock-on effects as Squid catches up on all that.
420      *
421      * Bug 2448 may have been a sign of what can wrong. At the least it
422      * provides a test case for aggression effects in overflow conditions.
423      *
424      * - base removal limit on space saved, instead of count ?
425      *
426      * - base removal rate on a traffic speed counter ?
427      *   as the purge took up more time out of the second it would grow to
428      *   a graceful full pause
429      *
430      * - pass out a value to cause another event to be scheduled immediately
431      *   instead of waiting a whole second more ?
432      *   knock on; schedule less if all caches are under low-water
433      *
434      * - admin configurable removal rate or count ?
435      *   the current numbers are arbitrary, config helps with experimental
436      *   trials and future-proofing the install base.
437      *   we also have this indirectly by shifting the relative positions
438      *   of low-, high- water and the total capacity limit.
439      */
440 
441     // minSize() is swap_low_watermark in bytes
442     const uint64_t lowWaterSz = minSize();
443 
444     if (currentSize() < lowWaterSz) {
445         debugs(47, 5, "space still available in " << path);
446         return;
447     }
448 
449     /* We can't delete objects while rebuilding swap */
450     /* XXX each store should start maintaining as it comes online. */
451     if (StoreController::store_dirs_rebuilding) {
452         // suppress the warnings, except once each minute
453         static int64_t lastWarn = 0;
454         int warnLevel = 3;
455         if (lastWarn+60 < squid_curtime) {
456             lastWarn = squid_curtime;
457             warnLevel = DBG_IMPORTANT;
458         }
459         debugs(47, warnLevel, StoreController::store_dirs_rebuilding << " cache_dir still rebuilding. Skip GC for " << path);
460         return;
461     }
462 
463     // maxSize() is cache_dir total size in bytes
464     const uint64_t highWaterSz = ((maxSize() * Config.Swap.highWaterMark) / 100);
465 
466     // f is percentage of 'gap' filled between low- and high-water.
467     // Used to reduced purge rate when between water markers, and
468     // to multiply it more agressively the further above high-water
469     // it reaches. But in a graceful linear growth curve.
470     double f = 1.0;
471     if (highWaterSz > lowWaterSz) {
472         // might be equal. n/0 is bad.
473         f = (double) (currentSize() - lowWaterSz) / (highWaterSz - lowWaterSz);
474     }
475 
476     // how deep to look for a single object that can be removed
477     int max_scan = (int) (f * 400.0 + 100.0);
478 
479     // try to purge only this many objects this cycle.
480     int max_remove = (int) (f * 300.0 + 20.0);
481 
482     /*
483      * This is kinda cheap, but so we need this priority hack?
484      */
485     debugs(47, 3, "f=" << f << ", max_scan=" << max_scan << ", max_remove=" << max_remove);
486 
487     RemovalPurgeWalker *walker = repl->PurgeInit(repl, max_scan);
488 
489     int removed = 0;
490     // only purge while above low-water
491     while (currentSize() >= lowWaterSz) {
492 
493         // stop if we reached max removals for this cycle,
494         // Bug 2448 may be from this not clearing enough,
495         // but it predates the current algorithm so not sure
496         if (removed >= max_remove)
497             break;
498 
499         StoreEntry *e = walker->Next(walker);
500 
501         // stop if all objects are locked / in-use,
502         // or the cache is empty
503         if (!e)
504             break;      /* no more objects */
505 
506         ++removed;
507 
508         e->release(true);
509     }
510 
511     walker->Done(walker);
512     debugs(47, (removed ? 2 : 3), path <<
513            " removed " << removed << "/" << max_remove << " f=" <<
514            std::setprecision(4) << f << " max_scan=" << max_scan);
515 
516     // what if cache is still over the high watermark ?
517     // Store::Maintain() schedules another purge in 1 second.
518 }
519 
520 void
reference(StoreEntry & e)521 Fs::Ufs::UFSSwapDir::reference(StoreEntry &e)
522 {
523     debugs(47, 3, HERE << "referencing " << &e << " " <<
524            e.swap_dirn << "/" << e.swap_filen);
525 
526     if (repl->Referenced)
527         repl->Referenced(repl, &e, &e.repl);
528 }
529 
530 bool
dereference(StoreEntry & e)531 Fs::Ufs::UFSSwapDir::dereference(StoreEntry & e)
532 {
533     debugs(47, 3, HERE << "dereferencing " << &e << " " <<
534            e.swap_dirn << "/" << e.swap_filen);
535 
536     if (repl->Dereferenced)
537         repl->Dereferenced(repl, &e, &e.repl);
538 
539     return true; // keep e in the global store_table
540 }
541 
542 StoreIOState::Pointer
createStoreIO(StoreEntry & e,StoreIOState::STFNCB * file_callback,StoreIOState::STIOCB * aCallback,void * callback_data)543 Fs::Ufs::UFSSwapDir::createStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data)
544 {
545     return IO->create (this, &e, file_callback, aCallback, callback_data);
546 }
547 
548 StoreIOState::Pointer
openStoreIO(StoreEntry & e,StoreIOState::STFNCB * file_callback,StoreIOState::STIOCB * aCallback,void * callback_data)549 Fs::Ufs::UFSSwapDir::openStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data)
550 {
551     return IO->open (this, &e, file_callback, aCallback, callback_data);
552 }
553 
554 int
mapBitTest(sfileno filn)555 Fs::Ufs::UFSSwapDir::mapBitTest(sfileno filn)
556 {
557     return map->testBit(filn);
558 }
559 
560 void
mapBitSet(sfileno filn)561 Fs::Ufs::UFSSwapDir::mapBitSet(sfileno filn)
562 {
563     map->setBit(filn);
564 }
565 
566 void
mapBitReset(sfileno filn)567 Fs::Ufs::UFSSwapDir::mapBitReset(sfileno filn)
568 {
569     /*
570      * We have to test the bit before calling clearBit as
571      * it doesn't do bounds checking and blindly assumes
572      * filn is a valid file number, but it might not be because
573      * the map is dynamic in size.  Also clearing an already clear
574      * bit puts the map counter of-of-whack.
575      */
576 
577     if (map->testBit(filn))
578         map->clearBit(filn);
579 }
580 
581 int
mapBitAllocate()582 Fs::Ufs::UFSSwapDir::mapBitAllocate()
583 {
584     int fn;
585     fn = map->allocate(suggest);
586     map->setBit(fn);
587     suggest = fn + 1;
588     return fn;
589 }
590 
591 char *
swapSubDir(int subdirn) const592 Fs::Ufs::UFSSwapDir::swapSubDir(int subdirn)const
593 {
594     LOCAL_ARRAY(char, fullfilename, MAXPATHLEN);
595     assert(0 <= subdirn && subdirn < l1);
596     snprintf(fullfilename, MAXPATHLEN, "%s/%02X", path, subdirn);
597     return fullfilename;
598 }
599 
600 int
createDirectory(const char * aPath,int should_exist)601 Fs::Ufs::UFSSwapDir::createDirectory(const char *aPath, int should_exist)
602 {
603     int created = 0;
604 
605     struct stat st;
606     getCurrentTime();
607 
608     if (0 == ::stat(aPath, &st)) {
609         if (S_ISDIR(st.st_mode)) {
610             debugs(47, (should_exist ? 3 : DBG_IMPORTANT), aPath << " exists");
611         } else {
612             fatalf("Swap directory %s is not a directory.", aPath);
613         }
614     } else if (0 == mkdir(aPath, 0755)) {
615         debugs(47, (should_exist ? DBG_IMPORTANT : 3), aPath << " created");
616         created = 1;
617     } else {
618         int xerrno = errno;
619         fatalf("Failed to make swap directory %s: %s", aPath, xstrerr(xerrno));
620     }
621 
622     return created;
623 }
624 
625 bool
pathIsDirectory(const char * aPath) const626 Fs::Ufs::UFSSwapDir::pathIsDirectory(const char *aPath)const
627 {
628 
629     struct stat sb;
630 
631     if (::stat(aPath, &sb) < 0) {
632         int xerrno = errno;
633         debugs(47, DBG_CRITICAL, "ERROR: " << aPath << ": " << xstrerr(xerrno));
634         return false;
635     }
636 
637     if (S_ISDIR(sb.st_mode) == 0) {
638         debugs(47, DBG_CRITICAL, "WARNING: " << aPath << " is not a directory");
639         return false;
640     }
641 
642     return true;
643 }
644 
645 bool
verifyCacheDirs()646 Fs::Ufs::UFSSwapDir::verifyCacheDirs()
647 {
648     if (!pathIsDirectory(path))
649         return true;
650 
651     for (int j = 0; j < l1; ++j) {
652         char const *aPath = swapSubDir(j);
653 
654         if (!pathIsDirectory(aPath))
655             return true;
656     }
657 
658     return false;
659 }
660 
661 void
createSwapSubDirs()662 Fs::Ufs::UFSSwapDir::createSwapSubDirs()
663 {
664     LOCAL_ARRAY(char, name, MAXPATHLEN);
665 
666     for (int i = 0; i < l1; ++i) {
667         snprintf(name, MAXPATHLEN, "%s/%02X", path, i);
668 
669         int should_exist;
670 
671         if (createDirectory(name, 0))
672             should_exist = 0;
673         else
674             should_exist = 1;
675 
676         debugs(47, DBG_IMPORTANT, "Making directories in " << name);
677 
678         for (int k = 0; k < l2; ++k) {
679             snprintf(name, MAXPATHLEN, "%s/%02X/%02X", path, i, k);
680             createDirectory(name, should_exist);
681         }
682     }
683 }
684 
685 SBuf
logFile(char const * ext) const686 Fs::Ufs::UFSSwapDir::logFile(char const *ext) const
687 {
688     SBuf lpath;
689 
690     if (Config.Log.swap) {
691         static char pathtmp[MAXPATHLEN];
692         char *pathtmp2 = xstrncpy(pathtmp, path, MAXPATHLEN - 64);
693 
694         // replace all '/' with '.'
695         while ((pathtmp2 = strchr(pathtmp2, '/')))
696             *pathtmp2 = '.';
697 
698         // remove any trailing '.' characters
699         int pos = strlen(pathtmp);
700         while (pos && pathtmp[pos-1] == '.')
701             pathtmp[--pos] = '\0';
702 
703         // remove any prefix '.' characters
704         for (pathtmp2 = pathtmp; *pathtmp2 == '.'; ++pathtmp2);
705         // replace a '%s' (if any) in the config string
706         // with the resulting pathtmp2 string
707         lpath.appendf(Config.Log.swap, pathtmp2);
708 
709         // is pathtmp2 was NOT injected, append numeric file extension
710         if (lpath.cmp(Config.Log.swap) == 0) {
711             lpath.append(".", 1);
712             lpath.appendf("%02d", index);
713         }
714     } else {
715         lpath.append(path);
716         lpath.append("/swap.state", 11);
717     }
718 
719     lpath.append(ext); // may be nil, that is okay.
720 
721     return lpath;
722 }
723 
724 void
openLog()725 Fs::Ufs::UFSSwapDir::openLog()
726 {
727     if (!IamWorkerProcess())
728         return;
729 
730     assert(NumberOfUFSDirs || !UFSDirToGlobalDirMapping);
731     ++NumberOfUFSDirs;
732     assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured);
733 
734     if (rebuilding_) { // we did not close the temporary log used for rebuilding
735         assert(swaplog_fd >= 0);
736         return;
737     }
738 
739     SBuf logPath(logFile());
740     swaplog_fd = file_open(logPath.c_str(), O_WRONLY | O_CREAT | O_BINARY);
741 
742     if (swaplog_fd < 0) {
743         int xerrno = errno;
744         debugs(50, DBG_IMPORTANT, "ERROR opening swap log " << logPath << ": " << xstrerr(xerrno));
745         fatal("UFSSwapDir::openLog: Failed to open swap log.");
746     }
747 
748     debugs(50, 3, HERE << "Cache Dir #" << index << " log opened on FD " << swaplog_fd);
749 }
750 
751 void
closeLog()752 Fs::Ufs::UFSSwapDir::closeLog()
753 {
754     if (swaplog_fd < 0) /* not open */
755         return;
756 
757     --NumberOfUFSDirs;
758     assert(NumberOfUFSDirs >= 0);
759     if (!NumberOfUFSDirs)
760         safe_free(UFSDirToGlobalDirMapping);
761 
762     if (rebuilding_) // we cannot close the temporary log used for rebuilding
763         return;
764 
765     file_close(swaplog_fd);
766 
767     debugs(47, 3, "Cache Dir #" << index << " log closed on FD " << swaplog_fd);
768 
769     swaplog_fd = -1;
770 }
771 
772 bool
validL1(int anInt) const773 Fs::Ufs::UFSSwapDir::validL1(int anInt) const
774 {
775     return anInt < l1;
776 }
777 
778 bool
validL2(int anInt) const779 Fs::Ufs::UFSSwapDir::validL2(int anInt) const
780 {
781     return anInt < l2;
782 }
783 
784 StoreEntry *
addDiskRestore(const cache_key * key,sfileno file_number,uint64_t swap_file_sz,time_t expires,time_t timestamp,time_t lastref,time_t lastmod,uint32_t refcount,uint16_t newFlags,int)785 Fs::Ufs::UFSSwapDir::addDiskRestore(const cache_key * key,
786                                     sfileno file_number,
787                                     uint64_t swap_file_sz,
788                                     time_t expires,
789                                     time_t timestamp,
790                                     time_t lastref,
791                                     time_t lastmod,
792                                     uint32_t refcount,
793                                     uint16_t newFlags,
794                                     int)
795 {
796     StoreEntry *e = NULL;
797     debugs(47, 5, HERE << storeKeyText(key)  <<
798            ", fileno="<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << file_number);
799     /* if you call this you'd better be sure file_number is not
800      * already in use! */
801     e = new StoreEntry();
802     e->store_status = STORE_OK;
803     e->setMemStatus(NOT_IN_MEMORY);
804     e->attachToDisk(index, file_number, SWAPOUT_DONE);
805     e->swap_file_sz = swap_file_sz;
806     e->lastref = lastref;
807     e->timestamp = timestamp;
808     e->expires = expires;
809     e->lastModified(lastmod);
810     e->refcount = refcount;
811     e->flags = newFlags;
812     e->ping_status = PING_NONE;
813     EBIT_CLR(e->flags, ENTRY_VALIDATED);
814     mapBitSet(e->swap_filen);
815     cur_size += fs.blksize * sizeInBlocks(e->swap_file_sz);
816     ++n_disk_objects;
817     e->hashInsert(key);
818     replacementAdd (e);
819     return e;
820 }
821 
822 void
rebuild()823 Fs::Ufs::UFSSwapDir::rebuild()
824 {
825     ++StoreController::store_dirs_rebuilding;
826     eventAdd("storeRebuild", Fs::Ufs::RebuildState::RebuildStep, new Fs::Ufs::RebuildState(this), 0.0, 1);
827 }
828 
829 void
closeTmpSwapLog()830 Fs::Ufs::UFSSwapDir::closeTmpSwapLog()
831 {
832     assert(rebuilding_);
833     rebuilding_ = false;
834 
835     SBuf swaplog_path(logFile()); // where the swaplog should be
836     SBuf tmp_path(logFile(".new"));
837 
838     file_close(swaplog_fd);
839 
840     if (!FileRename(tmp_path, swaplog_path)) {
841         fatalf("Failed to rename log file " SQUIDSBUFPH " to " SQUIDSBUFPH, SQUIDSBUFPRINT(tmp_path), SQUIDSBUFPRINT(swaplog_path));
842     }
843 
844     int fd = file_open(swaplog_path.c_str(), O_WRONLY | O_CREAT | O_BINARY);
845 
846     if (fd < 0) {
847         int xerrno = errno;
848         debugs(50, DBG_IMPORTANT, "ERROR: " << swaplog_path << ": " << xstrerr(xerrno));
849         fatalf("Failed to open swap log " SQUIDSBUFPH, SQUIDSBUFPRINT(swaplog_path));
850     }
851 
852     swaplog_fd = fd;
853     debugs(47, 3, "Cache Dir #" << index << " log opened on FD " << fd);
854 }
855 
856 FILE *
openTmpSwapLog(int * clean_flag,int * zero_flag)857 Fs::Ufs::UFSSwapDir::openTmpSwapLog(int *clean_flag, int *zero_flag)
858 {
859     assert(!rebuilding_);
860 
861     SBuf swaplog_path(logFile());
862     SBuf clean_path(logFile(".last-clean"));
863     SBuf new_path(logFile(".new"));
864 
865     struct stat log_sb;
866 
867     struct stat clean_sb;
868 
869     if (::stat(swaplog_path.c_str(), &log_sb) < 0) {
870         debugs(47, DBG_IMPORTANT, "Cache Dir #" << index << ": No log file");
871         return NULL;
872     }
873 
874     *zero_flag = log_sb.st_size == 0 ? 1 : 0;
875     /* close the existing write-only FD */
876 
877     if (swaplog_fd >= 0)
878         file_close(swaplog_fd);
879 
880     /* open a write-only FD for the new log */
881     int fd = file_open(new_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
882     if (fd < 0) {
883         int xerrno = errno;
884         debugs(50, DBG_IMPORTANT, "ERROR: while opening swap log" << new_path << ": " << xstrerr(xerrno));
885         fatalf("Failed to open swap log " SQUIDSBUFPH, SQUIDSBUFPRINT(new_path));
886     }
887 
888     swaplog_fd = fd;
889     rebuilding_ = true;
890 
891     {
892         const StoreSwapLogHeader header;
893         MemBuf buf;
894         buf.init(header.record_size, header.record_size);
895         buf.append(reinterpret_cast<const char*>(&header), sizeof(header));
896         // Pad to keep in sync with UFSSwapDir::writeCleanStart().
897         memset(buf.space(), 0, header.gapSize());
898         buf.appended(header.gapSize());
899         file_write(swaplog_fd, -1, buf.content(), buf.contentSize(),
900                    NULL, NULL, buf.freeFunc());
901     }
902 
903     /* open a read-only stream of the old log */
904     FILE *fp = fopen(swaplog_path.c_str(), "rb");
905     if (!fp) {
906         int xerrno = errno;
907         debugs(50, DBG_CRITICAL, "ERROR: while opening " << swaplog_path << ": " << xstrerr(xerrno));
908         fatalf("Failed to open swap log for reading " SQUIDSBUFPH, SQUIDSBUFPRINT(swaplog_path));
909     }
910 
911     memset(&clean_sb, '\0', sizeof(struct stat));
912 
913     if (::stat(clean_path.c_str(), &clean_sb) < 0)
914         *clean_flag = 0;
915     else if (clean_sb.st_mtime < log_sb.st_mtime)
916         *clean_flag = 0;
917     else
918         *clean_flag = 1;
919 
920     safeunlink(clean_path.c_str(), 1);
921 
922     return fp;
923 }
924 
925 /*
926  * Begin the process to write clean cache state.  For AUFS this means
927  * opening some log files and allocating write buffers.  Return 0 if
928  * we succeed, and assign the 'func' and 'data' return pointers.
929  */
930 int
writeCleanStart()931 Fs::Ufs::UFSSwapDir::writeCleanStart()
932 {
933     UFSCleanLog *state = new UFSCleanLog(this);
934     StoreSwapLogHeader header;
935 #if HAVE_FCHMOD
936 
937     struct stat sb;
938 #endif
939 
940     cleanLog = NULL;
941     state->cur = logFile();
942     state->newLog = logFile(".clean");
943     state->fd = file_open(state->newLog.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
944 
945     if (state->fd < 0) {
946         delete state;
947         return -1;
948     }
949 
950     state->cln = state->cur;
951     state->cln.append(".last-clean");
952     state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
953     state->outbuf_offset = 0;
954     /*copy the header */
955     memcpy(state->outbuf, &header, sizeof(StoreSwapLogHeader));
956     // Leave a gap to keep in sync with UFSSwapDir::openTmpSwapLog().
957     memset(state->outbuf + sizeof(StoreSwapLogHeader), 0, header.gapSize());
958     state->outbuf_offset += header.record_size;
959 
960     state->walker = repl->WalkInit(repl);
961     ::unlink(state->cln.c_str());
962     debugs(47, 3, HERE << "opened " << state->newLog << ", FD " << state->fd);
963 #if HAVE_FCHMOD
964 
965     if (::stat(state->cur.c_str(), &sb) == 0)
966         fchmod(state->fd, sb.st_mode);
967 
968 #endif
969 
970     cleanLog = state;
971     return 0;
972 }
973 
974 void
writeCleanDone()975 Fs::Ufs::UFSSwapDir::writeCleanDone()
976 {
977     UFSCleanLog *state = (UFSCleanLog *)cleanLog;
978     int fd;
979 
980     if (NULL == state)
981         return;
982 
983     if (state->fd < 0)
984         return;
985 
986     state->walker->Done(state->walker);
987 
988     if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
989         int xerrno = errno;
990         debugs(50, DBG_CRITICAL, MYNAME << state->newLog << ": write: " << xstrerr(xerrno));
991         debugs(50, DBG_CRITICAL, MYNAME << "Current swap logfile not replaced.");
992         file_close(state->fd);
993         state->fd = -1;
994         ::unlink(state->newLog.c_str());
995     }
996 
997     safe_free(state->outbuf);
998     /*
999      * You can't rename open files on Microsoft "operating systems"
1000      * so we have to close before renaming.
1001      */
1002     closeLog();
1003     /* save the fd value for a later test */
1004     fd = state->fd;
1005     /* rename */
1006 
1007     if (state->fd >= 0) {
1008 #if _SQUID_OS2_ || _SQUID_WINDOWS_
1009         file_close(state->fd);
1010         state->fd = -1;
1011 #endif
1012 
1013         FileRename(state->newLog, state->cur);
1014         // TODO handle rename errors
1015     }
1016 
1017     /* touch a timestamp file if we're not still validating */
1018     if (StoreController::store_dirs_rebuilding)
1019         (void) 0;
1020     else if (fd < 0)
1021         (void) 0;
1022     else
1023         file_close(file_open(state->cln.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
1024 
1025     /* close */
1026     if (state->fd >= 0)
1027         file_close(state->fd);
1028 
1029     state->fd = -1;
1030 
1031     delete state;
1032 
1033     cleanLog = NULL;
1034 }
1035 
1036 /// safely cleans a few unused files if possible
1037 int
HandleCleanEvent()1038 Fs::Ufs::UFSSwapDir::HandleCleanEvent()
1039 {
1040     static int swap_index = 0;
1041     int i;
1042     int j = 0;
1043     int n = 0;
1044 
1045     if (!NumberOfUFSDirs)
1046         return 0; // probably in the middle of reconfiguration
1047 
1048     if (NULL == UFSDirToGlobalDirMapping) {
1049         SwapDir *sd;
1050         /*
1051          * Initialize the little array that translates UFS cache_dir
1052          * number into the Config.cacheSwap.swapDirs array index.
1053          */
1054         UFSDirToGlobalDirMapping = (int *)xcalloc(NumberOfUFSDirs, sizeof(*UFSDirToGlobalDirMapping));
1055 
1056         for (i = 0, n = 0; i < Config.cacheSwap.n_configured; ++i) {
1057             /* This is bogus, the controller should just clean each instance once */
1058             sd = dynamic_cast <SwapDir *>(INDEXSD(i));
1059 
1060             if (!UFSSwapDir::IsUFSDir(sd))
1061                 continue;
1062 
1063             UFSSwapDir *usd = dynamic_cast<UFSSwapDir *>(sd);
1064 
1065             assert (usd);
1066 
1067             UFSDirToGlobalDirMapping[n] = i;
1068             ++n;
1069 
1070             j += (usd->l1 * usd->l2);
1071         }
1072 
1073         assert(n == NumberOfUFSDirs);
1074         /*
1075          * Start the commonUfsDirClean() swap_index with a random
1076          * value.  j equals the total number of UFS level 2
1077          * swap directories
1078          */
1079         std::mt19937 mt(static_cast<uint32_t>(getCurrentTime() & 0xFFFFFFFF));
1080         xuniform_int_distribution<> dist(0, j);
1081         swap_index = dist(mt);
1082     }
1083 
1084     /* if the rebuild is finished, start cleaning directories. */
1085     if (0 == StoreController::store_dirs_rebuilding) {
1086         n = DirClean(swap_index);
1087         ++swap_index;
1088     }
1089 
1090     return n;
1091 }
1092 
1093 void
CleanEvent(void *)1094 Fs::Ufs::UFSSwapDir::CleanEvent(void *)
1095 {
1096     const int n = HandleCleanEvent();
1097     eventAdd("storeDirClean", CleanEvent, NULL,
1098              15.0 * exp(-0.25 * n), 1);
1099 }
1100 
1101 bool
IsUFSDir(SwapDir * sd)1102 Fs::Ufs::UFSSwapDir::IsUFSDir(SwapDir * sd)
1103 {
1104     UFSSwapDir *mySD = dynamic_cast<UFSSwapDir *>(sd);
1105     return (mySD != 0) ;
1106 }
1107 
1108 /*
1109  * XXX: this is broken - it assumes all cache dirs use the same
1110  * l1 and l2 scheme. -RBC 20021215. Partial fix is in place -
1111  * if not UFSSwapDir return 0;
1112  */
1113 bool
FilenoBelongsHere(int fn,int F0,int F1,int F2)1114 Fs::Ufs::UFSSwapDir::FilenoBelongsHere(int fn, int F0, int F1, int F2)
1115 {
1116     int D1, D2;
1117     int L1, L2;
1118     int filn = fn;
1119     assert(F0 < Config.cacheSwap.n_configured);
1120     assert (UFSSwapDir::IsUFSDir (dynamic_cast<SwapDir *>(INDEXSD(F0))));
1121     UFSSwapDir *sd = dynamic_cast<UFSSwapDir *>(INDEXSD(F0));
1122 
1123     if (!sd)
1124         return 0;
1125 
1126     L1 = sd->l1;
1127 
1128     L2 = sd->l2;
1129 
1130     D1 = ((filn / L2) / L2) % L1;
1131 
1132     if (F1 != D1)
1133         return 0;
1134 
1135     D2 = (filn / L2) % L2;
1136 
1137     if (F2 != D2)
1138         return 0;
1139 
1140     return 1;
1141 }
1142 
1143 int
validFileno(sfileno filn,int flag) const1144 Fs::Ufs::UFSSwapDir::validFileno(sfileno filn, int flag) const
1145 {
1146     if (filn < 0)
1147         return 0;
1148 
1149     /*
1150      * If flag is set it means out-of-range file number should
1151      * be considered invalid.
1152      */
1153     if (flag)
1154         if (filn > map->capacity())
1155             return 0;
1156 
1157     return 1;
1158 }
1159 
1160 void
unlinkFile(sfileno f)1161 Fs::Ufs::UFSSwapDir::unlinkFile(sfileno f)
1162 {
1163     debugs(79, 3, HERE << "unlinking fileno " <<  std::setfill('0') <<
1164            std::hex << std::uppercase << std::setw(8) << f << " '" <<
1165            fullPath(f,NULL) << "'");
1166     /* commonUfsDirMapBitReset(this, f); */
1167     IO->unlinkFile(fullPath(f,NULL));
1168 }
1169 
1170 bool
unlinkdUseful() const1171 Fs::Ufs::UFSSwapDir::unlinkdUseful() const
1172 {
1173     // unlinkd may be useful only in workers
1174     return IamWorkerProcess() && IO->io->unlinkdUseful();
1175 }
1176 
1177 void
evictCached(StoreEntry & e)1178 Fs::Ufs::UFSSwapDir::evictCached(StoreEntry & e)
1179 {
1180     debugs(79, 3, e);
1181     if (e.locked()) // somebody else may still be using this file
1182         return; // nothing to do: our get() always returns nil
1183 
1184     if (!e.hasDisk())
1185         return; // see evictIfFound()
1186 
1187     // Since these fields grow only after swap out ends successfully,
1188     // do not decrement them for e.swappingOut() and e.swapoutFailed().
1189     if (e.swappedOut()) {
1190         cur_size -= fs.blksize * sizeInBlocks(e.swap_file_sz);
1191         --n_disk_objects;
1192     }
1193     replacementRemove(&e);
1194     mapBitReset(e.swap_filen);
1195     UFSSwapDir::unlinkFile(e.swap_filen);
1196     e.detachFromDisk();
1197 }
1198 
1199 void
evictIfFound(const cache_key *)1200 Fs::Ufs::UFSSwapDir::evictIfFound(const cache_key *)
1201 {
1202     // UFS disk entries always have (attached) StoreEntries so if we got here,
1203     // the entry is not cached on disk and there is nothing for us to do.
1204 }
1205 
1206 void
replacementAdd(StoreEntry * e)1207 Fs::Ufs::UFSSwapDir::replacementAdd(StoreEntry * e)
1208 {
1209     debugs(47, 4, HERE << "added node " << e << " to dir " << index);
1210     repl->Add(repl, e, &e->repl);
1211 }
1212 
1213 void
replacementRemove(StoreEntry * e)1214 Fs::Ufs::UFSSwapDir::replacementRemove(StoreEntry * e)
1215 {
1216     assert(e->hasDisk());
1217 
1218     SwapDirPointer SD = INDEXSD(e->swap_dirn);
1219 
1220     assert (dynamic_cast<UFSSwapDir *>(SD.getRaw()) == this);
1221 
1222     debugs(47, 4, HERE << "remove node " << e << " from dir " << index);
1223 
1224     repl->Remove(repl, e, &e->repl);
1225 }
1226 
1227 void
dump(StoreEntry & entry) const1228 Fs::Ufs::UFSSwapDir::dump(StoreEntry & entry) const
1229 {
1230     storeAppendPrintf(&entry, " %" PRIu64 " %d %d", maxSize() >> 20, l1, l2);
1231     dumpOptions(&entry);
1232 }
1233 
1234 char *
fullPath(sfileno filn,char * fullpath) const1235 Fs::Ufs::UFSSwapDir::fullPath(sfileno filn, char *fullpath) const
1236 {
1237     LOCAL_ARRAY(char, fullfilename, MAXPATHLEN);
1238     int L1 = l1;
1239     int L2 = l2;
1240 
1241     if (!fullpath)
1242         fullpath = fullfilename;
1243 
1244     fullpath[0] = '\0';
1245 
1246     snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X/%08X",
1247              path,
1248              ((filn / L2) / L2) % L1,
1249              (filn / L2) % L2,
1250              filn);
1251 
1252     return fullpath;
1253 }
1254 
1255 int
callback()1256 Fs::Ufs::UFSSwapDir::callback()
1257 {
1258     return IO->callback();
1259 }
1260 
1261 void
sync()1262 Fs::Ufs::UFSSwapDir::sync()
1263 {
1264     IO->sync();
1265 }
1266 
1267 void
finalizeSwapoutSuccess(const StoreEntry & e)1268 Fs::Ufs::UFSSwapDir::finalizeSwapoutSuccess(const StoreEntry &e)
1269 {
1270     cur_size += fs.blksize * sizeInBlocks(e.swap_file_sz);
1271     ++n_disk_objects;
1272 }
1273 
1274 void
finalizeSwapoutFailure(StoreEntry & entry)1275 Fs::Ufs::UFSSwapDir::finalizeSwapoutFailure(StoreEntry &entry)
1276 {
1277     debugs(47, 5, entry);
1278     // rely on the expected eventual StoreEntry::release(), evictCached(), or
1279     // a similar call to call unlink(), detachFromDisk(), etc. for the entry.
1280 }
1281 
1282 void
logEntry(const StoreEntry & e,int op) const1283 Fs::Ufs::UFSSwapDir::logEntry(const StoreEntry & e, int op) const
1284 {
1285     if (swaplog_fd < 0) {
1286         debugs(36, 5, "cannot log " << e << " in the middle of reconfiguration");
1287         return;
1288     }
1289 
1290     StoreSwapLogData *s = new StoreSwapLogData;
1291     s->op = (char) op;
1292     s->swap_filen = e.swap_filen;
1293     s->timestamp = e.timestamp;
1294     s->lastref = e.lastref;
1295     s->expires = e.expires;
1296     s->lastmod = e.lastModified();
1297     s->swap_file_sz = e.swap_file_sz;
1298     s->refcount = e.refcount;
1299     s->flags = e.flags;
1300     memcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH);
1301     s->finalize();
1302     file_write(swaplog_fd,
1303                -1,
1304                s,
1305                sizeof(StoreSwapLogData),
1306                NULL,
1307                NULL,
1308                FreeObject);
1309 }
1310 
1311 int
DirClean(int swap_index)1312 Fs::Ufs::UFSSwapDir::DirClean(int swap_index)
1313 {
1314     DIR *dir_pointer = NULL;
1315     int files[20];
1316     int swapfileno;
1317     int fn;         /* same as swapfileno, but with dirn bits set */
1318     int n = 0;
1319     int k = 0;
1320     int N0, N1, N2;
1321     int D0, D1, D2;
1322     UFSSwapDir *SD;
1323     N0 = NumberOfUFSDirs;
1324     D0 = UFSDirToGlobalDirMapping[swap_index % N0];
1325     SD = dynamic_cast<UFSSwapDir *>(INDEXSD(D0));
1326     assert (SD);
1327     N1 = SD->l1;
1328     D1 = (swap_index / N0) % N1;
1329     N2 = SD->l2;
1330     D2 = ((swap_index / N0) / N1) % N2;
1331 
1332     SBuf p1;
1333     p1.appendf("%s/%02X/%02X", SD->path, D1, D2);
1334     debugs(36, 3, HERE << "Cleaning directory " << p1);
1335     dir_pointer = opendir(p1.c_str());
1336 
1337     if (!dir_pointer) {
1338         int xerrno = errno;
1339         if (xerrno == ENOENT) {
1340             debugs(36, DBG_CRITICAL, MYNAME << "WARNING: Creating " << p1);
1341             if (mkdir(p1.c_str(), 0777) == 0)
1342                 return 0;
1343         }
1344 
1345         debugs(50, DBG_CRITICAL, MYNAME << p1 << ": " << xstrerr(xerrno));
1346         safeunlink(p1.c_str(), 1);
1347         return 0;
1348     }
1349 
1350     dirent_t *de;
1351     while ((de = readdir(dir_pointer)) != NULL && k < 20) {
1352         if (sscanf(de->d_name, "%X", &swapfileno) != 1)
1353             continue;
1354 
1355         fn = swapfileno;    /* XXX should remove this cruft ! */
1356 
1357         if (SD->validFileno(fn, 1))
1358             if (SD->mapBitTest(fn))
1359                 if (UFSSwapDir::FilenoBelongsHere(fn, D0, D1, D2))
1360                     continue;
1361 
1362         files[k] = swapfileno;
1363         ++k;
1364     }
1365 
1366     closedir(dir_pointer);
1367 
1368     if (k == 0)
1369         return 0;
1370 
1371     qsort(files, k, sizeof(int), rev_int_sort);
1372 
1373     if (k > 10)
1374         k = 10;
1375 
1376     for (n = 0; n < k; ++n) {
1377         debugs(36, 3, HERE << "Cleaning file "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << files[n]);
1378         SBuf p2(p1);
1379         p2.appendf("/%08X", files[n]);
1380         safeunlink(p2.c_str(), 0);
1381         ++statCounter.swap.files_cleaned;
1382     }
1383 
1384     debugs(36, 3, HERE << "Cleaned " << k << " unused files from " << p1);
1385     return k;
1386 }
1387 
1388