1 /** @file
2 
3     Main program file for Cache Tool.
4 
5     @section license License
6 
7     Licensed to the Apache Software Foundation (ASF) under one
8     or more contributor license agreements.  See the NOTICE file
9     distributed with this work for additional information
10     regarding copyright ownership.  The ASF licenses this file
11     to you under the Apache License, Version 2.0 (the
12     "License"); you may not use this file except in compliance
13     with the License.  You may obtain a copy of the License at
14 
15     http://www.apache.org/licenses/LICENSE-2.0
16 
17     Unless required by applicable law or agreed to in writing, software
18     distributed under the License is distributed on an "AS IS" BASIS,
19     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20     See the License for the specific language governing permissions and
21     limitations under the License.
22  */
23 
24 #include <iostream>
25 #include <list>
26 #include <memory>
27 #include <vector>
28 #include <map>
29 #include <system_error>
30 #include <fcntl.h>
31 #include <cctype>
32 #include <cstring>
33 #include <vector>
34 #include <unordered_set>
35 #include <ctime>
36 #include <bitset>
37 #include <cinttypes>
38 
39 #include "tscore/ink_memory.h"
40 #include "tscore/ink_file.h"
41 #include "tscore/BufferWriter.h"
42 #include "tscore/CryptoHash.h"
43 #include "tscore/ArgParser.h"
44 #include <thread>
45 
46 #include "CacheDefs.h"
47 #include "CacheScan.h"
48 
49 using ts::Bytes;
50 using ts::Megabytes;
51 using ts::CacheStoreBlocks;
52 using ts::CacheStripeBlocks;
53 using ts::StripeMeta;
54 using ts::CacheStripeDescriptor;
55 using ts::Errata;
56 using ts::CacheDirEntry;
57 using ts::MemSpan;
58 using ts::Doc;
59 
60 enum { SILENT = 0, NORMAL, VERBOSE } Verbosity = NORMAL;
61 extern int cache_config_min_average_object_size;
62 extern CacheStoreBlocks Vol_hash_alloc_size;
63 extern int OPEN_RW_FLAG;
64 const Bytes ts::CacheSpan::OFFSET{CacheStoreBlocks{1}};
65 ts::file::path SpanFile;
66 ts::file::path VolumeFile;
67 ts::ArgParser parser;
68 
69 Errata err;
70 
71 namespace ct
72 {
73 /* --------------------------------------------------------------------------------------- */
74 /// A live volume.
75 /// Volume data based on data from loaded spans.
76 struct Volume {
77   int _idx;               ///< Volume index.
78   CacheStoreBlocks _size; ///< Amount of storage allocated.
79   std::vector<Stripe *> _stripes;
80 
81   /// Remove all data related to @a span.
82   //  void clearSpan(Span *span);
83   /// Remove all allocated space and stripes.
84   void clear();
85 };
86 
87 #if 0
88 void
89 Volume::clearSpan(Span* span)
90 {
91   auto spot = std::remove_if(_stripes.begin(), _stripes.end(), [span,this](Stripe* stripe) { return stripe->_span == span ? ( this->_size -= stripe->_len , true ) : false; });
92   _stripes.erase(spot, _stripes.end());
93 }
94 #endif
95 
96 void
clear()97 Volume::clear()
98 {
99   _size.assign(0);
100   _stripes.clear();
101 }
102 /* --------------------------------------------------------------------------------------- */
103 /// Data parsed from the volume config file.
104 struct VolumeConfig {
105   Errata load(ts::file::path const &path);
106 
107   /// Data direct from the config file.
108   struct Data {
109     int _idx     = 0;         ///< Volume index.
110     int _percent = 0;         ///< Size if specified as a percent.
111     Megabytes _size{0};       ///< Size if specified as an absolute.
112     CacheStripeBlocks _alloc; ///< Allocation size.
113 
114     // Methods handy for parsing
115     bool
hasSizect::VolumeConfig::Data116     hasSize() const
117     {
118       return _percent > 0 || _size > 0;
119     }
120     bool
hasIndexct::VolumeConfig::Data121     hasIndex() const
122     {
123       return _idx > 0;
124     }
125   };
126 
127   std::vector<Data> _volumes;
128   using iterator       = std::vector<Data>::iterator;
129   using const_iterator = std::vector<Data>::const_iterator;
130 
131   iterator
beginct::VolumeConfig132   begin()
133   {
134     return _volumes.begin();
135   }
136   iterator
endct::VolumeConfig137   end()
138   {
139     return _volumes.end();
140   }
141 #if 0
142   const_iterator
143   begin() const
144   {
145     return _volumes.begin();
146   }
147   const_iterator
148   end() const
149   {
150     return _volumes.end();
151   }
152 #endif
153   //  Errata validatePercentAllocation();
154   void convertToAbsolute(const ts::CacheStripeBlocks &total_span_size);
155 };
156 
157 #if 0
158 Errata
159 VolumeConfig::validatePercentAllocation()
160 {
161   Errata zret;
162   int n = 0;
163   for (auto &vol : _volumes)
164     n += vol._percent;
165   if (n > 100)
166     zret.push(0, 10, "Volume percent allocation ", n, " is more than 100%");
167   return zret;
168 }
169 #endif
170 
171 void
convertToAbsolute(const ts::CacheStripeBlocks & n)172 VolumeConfig::convertToAbsolute(const ts::CacheStripeBlocks &n)
173 {
174   for (auto &vol : _volumes) {
175     if (vol._percent) {
176       vol._alloc.assign((n.count() * vol._percent + 99) / 100);
177     } else {
178       vol._alloc = round_up(vol._size);
179     }
180   }
181 }
182 /* --------------------------------------------------------------------------------------- */
183 struct Cache {
184   ~Cache();
185 
186   Errata loadSpan(ts::file::path const &path);
187   Errata loadSpanConfig(ts::file::path const &path);
188   Errata loadSpanDirect(ts::file::path const &path, int vol_idx = -1, const Bytes &size = Bytes(-1));
189   Errata loadURLs(ts::file::path const &path);
190 
191   Errata allocStripe(Span *span, int vol_idx, const CacheStripeBlocks &len);
192 
193   /// Change the @a span to have a single, unused stripe occupying the entire @a span.
194   //  void clearSpan(Span *span);
195   /// Clear all allocated space.
196   void clearAllocation();
197 
198   enum class SpanDumpDepth { SPAN, STRIPE, DIRECTORY };
199   void dumpSpans(SpanDumpDepth depth);
200   void dumpVolumes();
201   void build_stripe_hash_table();
202   Stripe *key_to_stripe(CryptoHash *key, const char *hostname, int host_len);
203   //  ts::CacheStripeBlocks calcTotalSpanPhysicalSize();
204   ts::CacheStripeBlocks calcTotalSpanConfiguredSize();
205 
206   std::list<Span *> _spans;
207   std::map<int, Volume> _volumes;
208   std::vector<Stripe *> globalVec_stripe;
209   std::unordered_set<ts::CacheURL *> URLset;
210   unsigned short *stripes_hash_table;
211 };
212 
213 Errata
allocStripe(Span * span,int vol_idx,const CacheStripeBlocks & len)214 Cache::allocStripe(Span *span, int vol_idx, const CacheStripeBlocks &len)
215 {
216   auto rv = span->allocStripe(vol_idx, len);
217   std::cout << span->_path.string() << ":" << vol_idx << std::endl;
218   if (rv.isOK()) {
219     _volumes[vol_idx]._stripes.push_back(rv);
220   }
221   return rv.errata();
222 }
223 
224 #if 0
225 void
226 Cache::clearSpan(Span* span)
227 {
228   for ( auto& item : _volumes ) item.second.clearSpan(span);
229   span->clear();
230 }
231 #endif
232 
233 void
clearAllocation()234 Cache::clearAllocation()
235 {
236   for (auto span : _spans) {
237     span->clear();
238   }
239   for (auto &item : _volumes) {
240     item.second.clear();
241   }
242 }
243 /* --------------------------------------------------------------------------------------- */
244 /// Temporary structure used for doing allocation computations.
245 class VolumeAllocator
246 {
247   /// Working struct that tracks allocation information.
248   struct V {
249     VolumeConfig::Data const &_config; ///< Configuration instance.
250     CacheStripeBlocks _size;           ///< Current actual size.
251     int64_t _deficit;                  ///< fractional deficit
252     int64_t _shares;                   ///< relative amount of free space to allocate
253 
Vct::VolumeAllocator::V254     V(VolumeConfig::Data const &config, const CacheStripeBlocks &size, int64_t deficit = 0, int64_t shares = 0)
255       : _config(config), _size(size), _deficit(deficit), _shares(shares)
256     {
257     }
258     V &
operator =ct::VolumeAllocator::V259     operator=(V const &that)
260     {
261       new (this) V(that._config, that._size, that._deficit, that._shares);
262       return *this;
263     }
264   };
265 
266   using AV = std::vector<V>;
267   AV _av; ///< Working vector of volume data.
268 
269   Cache _cache;       ///< Current state.
270   VolumeConfig _vols; ///< Configuration state.
271 
272 public:
273   VolumeAllocator();
274 
275   Errata load(ts::file::path const &spanFile, ts::file::path const &volumeFile);
276   Errata fillEmptySpans();
277   Errata fillAllSpans();
278   Errata allocateSpan(ts::file::path const &spanFile);
279   void dumpVolumes();
280 
281 protected:
282   /// Update the allocation for a span.
283   Errata allocateFor(Span &span);
284 };
285 
286 VolumeAllocator::VolumeAllocator() = default;
287 
288 Errata
load(ts::file::path const & spanFile,ts::file::path const & volumeFile)289 VolumeAllocator::load(ts::file::path const &spanFile, ts::file::path const &volumeFile)
290 {
291   Errata zret;
292 
293   if (volumeFile.empty()) {
294     zret.push(0, 9, "Volume config file not set");
295   }
296   if (spanFile.empty()) {
297     zret.push(0, 9, "Span file not set");
298   }
299 
300   if (zret) {
301     zret = _vols.load(volumeFile);
302     if (zret) {
303       zret = _cache.loadSpan(spanFile);
304       if (zret) {
305         CacheStripeBlocks total = _cache.calcTotalSpanConfiguredSize();
306         _vols.convertToAbsolute(total);
307         for (auto &vol : _vols) {
308           CacheStripeBlocks size(0);
309           auto spot = _cache._volumes.find(vol._idx);
310           if (spot != _cache._volumes.end()) {
311             size = round_down(spot->second._size);
312           }
313           _av.push_back({vol, size, 0, 0});
314         }
315       }
316     }
317   }
318   return zret;
319 }
320 
321 void
dumpVolumes()322 VolumeAllocator::dumpVolumes()
323 {
324   _cache.dumpVolumes();
325 }
326 
327 Errata
fillEmptySpans()328 VolumeAllocator::fillEmptySpans()
329 {
330   Errata zret;
331   // Walk the spans, skipping ones that are not empty.
332   for (auto span : _cache._spans) {
333     if (span->isEmpty()) {
334       this->allocateFor(*span);
335     }
336   }
337   return zret;
338 }
339 
340 Errata
allocateSpan(ts::file::path const & input_file_path)341 VolumeAllocator::allocateSpan(ts::file::path const &input_file_path)
342 {
343   Errata zret;
344   for (auto span : _cache._spans) {
345     if (span->_path.view() == input_file_path.view()) {
346       std::cout << "===============================" << std::endl;
347       if (span->_header) {
348         zret.push(0, 1, "Disk already initialized with valid header");
349       } else {
350         this->allocateFor(*span);
351         span->updateHeader();
352         for (auto &strp : span->_stripes) {
353           strp->updateHeaderFooter();
354         }
355       }
356     }
357   }
358   for (auto &_v : _av) {
359     std::cout << _v._size << std::endl;
360   }
361   return zret;
362 }
363 
364 Errata
fillAllSpans()365 VolumeAllocator::fillAllSpans()
366 {
367   Errata zret;
368   // clear all current volume allocations.
369   for (auto &v : _av) {
370     v._size.assign(0);
371   }
372   // Allocate for each span, clearing as it goes.
373   _cache.clearAllocation();
374   for (auto span : _cache._spans) {
375     this->allocateFor(*span);
376   }
377   return zret;
378 }
379 
380 Errata
allocateFor(Span & span)381 VolumeAllocator::allocateFor(Span &span)
382 {
383   Errata zret;
384 
385   /// Scaling factor for shares, effectively the accuracy.
386   static const int64_t SCALE = 1000;
387   int64_t total_shares       = 0;
388 
389   if (Verbosity >= NORMAL) {
390     std::cout << "Allocating " << CacheStripeBlocks(round_down(span._len)).count() << " stripe blocks from span "
391               << span._path.string() << std::endl;
392   }
393 
394   // Walk the volumes and get the relative allocations.
395   for (auto &v : _av) {
396     auto delta = v._config._alloc - v._size;
397     if (delta > 0) {
398       v._deficit = (delta.count() * SCALE) / v._config._alloc.count();
399       v._shares  = delta.count() * v._deficit;
400       total_shares += v._shares;
401     } else {
402       v._shares = 0;
403     }
404   }
405   assert(total_shares != 0);
406   // Now allocate blocks.
407   CacheStripeBlocks span_blocks(round_down(span._free_space));
408   CacheStripeBlocks span_used{0};
409 
410   // sort by deficit so least relatively full volumes go first.
411   std::sort(_av.begin(), _av.end(), [](V const &lhs, V const &rhs) { return lhs._deficit > rhs._deficit; });
412   for (auto &v : _av) {
413     if (v._shares) {
414       CacheStripeBlocks n{(((span_blocks - span_used).count() * v._shares) + total_shares - 1) / total_shares};
415       CacheStripeBlocks delta{v._config._alloc - v._size};
416       // Not sure why this is needed. But a large and empty volume can dominate the shares
417       // enough to get more than it actually needs if the other volume are relative small or full.
418       // I need to do more math to see if the weighting can be adjusted to not have this happen.
419       n = std::min(n, delta);
420       v._size += n;
421       span_used += n;
422       total_shares -= v._shares;
423       Errata z = _cache.allocStripe(&span, v._config._idx, round_up(n));
424       if (Verbosity >= NORMAL) {
425         std::cout << "           " << n << " to volume " << v._config._idx << std::endl;
426       }
427       if (!z) {
428         std::cout << z;
429       }
430     }
431   }
432   if (Verbosity >= NORMAL) {
433     std::cout << "     Total " << span_used << std::endl;
434   }
435   if (OPEN_RW_FLAG) {
436     if (Verbosity >= NORMAL) {
437       std::cout << " Updating Header ... ";
438     }
439     zret = span.updateHeader();
440   }
441   _cache.dumpVolumes(); // debug
442   if (Verbosity >= NORMAL) {
443     if (zret) {
444       std::cout << " Done" << std::endl;
445     } else {
446       std::cout << " Error" << std::endl << zret;
447     }
448   }
449 
450   return zret;
451 }
452 /* --------------------------------------------------------------------------------------- */
453 Errata
loadSpan(ts::file::path const & path)454 Cache::loadSpan(ts::file::path const &path)
455 {
456   Errata zret;
457   std::error_code ec;
458   auto fs = ts::file::status(path, ec);
459 
460   if (path.empty()) {
461     zret = Errata::Message(0, EINVAL, "A span file specified by --spans is required");
462   } else if (!ts::file::is_readable(path)) {
463     zret = Errata::Message(0, EPERM, '\'', path.string(), "' is not readable.");
464   } else if (ts::file::is_regular_file(fs)) {
465     zret = this->loadSpanConfig(path);
466   } else {
467     zret = this->loadSpanDirect(path);
468   }
469   return zret;
470 }
471 
472 Errata
loadSpanDirect(ts::file::path const & path,int vol_idx,const Bytes & size)473 Cache::loadSpanDirect(ts::file::path const &path, int vol_idx, const Bytes &size)
474 {
475   Errata zret;
476   std::unique_ptr<Span> span(new Span(path));
477   zret = span->load();
478   if (zret) {
479     if (span->_header) {
480       int nspb = span->_header->num_diskvol_blks;
481       for (auto i = 0; i < nspb; ++i) {
482         ts::CacheStripeDescriptor &raw = span->_header->stripes[i];
483         Stripe *stripe                 = new Stripe(span.get(), raw.offset, raw.len);
484         stripe->_idx                   = i;
485         if (raw.free == 0) {
486           stripe->_vol_idx = raw.vol_idx;
487           stripe->_type    = raw.type;
488           _volumes[stripe->_vol_idx]._stripes.push_back(stripe);
489           _volumes[stripe->_vol_idx]._size += stripe->_len;
490           stripe->vol_init_data();
491         } else {
492           span->_free_space += stripe->_len;
493         }
494         span->_stripes.push_back(stripe);
495         globalVec_stripe.push_back(stripe);
496       }
497       span->_vol_idx = vol_idx;
498     } else {
499       span->clear();
500     }
501     _spans.push_back(span.release());
502   }
503   return zret;
504 }
505 
506 Errata
loadSpanConfig(ts::file::path const & path)507 Cache::loadSpanConfig(ts::file::path const &path)
508 {
509   static const ts::TextView TAG_ID("id");
510   static const ts::TextView TAG_VOL("volume");
511 
512   Errata zret;
513   std::error_code ec;
514   std::string load_content = ts::file::load(path, ec);
515   if (ec.value() == 0) {
516     ts::TextView content(load_content);
517     while (content) {
518       ts::TextView line = content.take_prefix_at('\n');
519       line.ltrim_if(&isspace);
520       if (line.empty() || '#' == *line) {
521         continue;
522       }
523       ts::TextView path = line.take_prefix_if(&isspace);
524       if (path) {
525         // After this the line is [size] [id=string] [volume=#]
526         while (line) {
527           ts::TextView value(line.take_prefix_if(&isspace));
528           if (value) {
529             ts::TextView tag(value.take_prefix_at('='));
530             if (!tag) { // must be the size
531             } else if (0 == strcasecmp(tag, TAG_ID)) {
532             } else if (0 == strcasecmp(tag, TAG_VOL)) {
533               ts::TextView text;
534               auto n = ts::svtoi(value, &text);
535               if (text == value && 0 < n && n < 256) {
536               } else {
537                 zret.push(0, 0, "Invalid volume index '", value, "'");
538               }
539             }
540           }
541         }
542         zret = this->loadSpan(ts::file::path(path));
543       }
544     }
545   } else {
546     zret = Errata::Message(0, EBADF, "Unable to load ", path.string());
547   }
548   return zret;
549 }
550 
551 Errata
loadURLs(ts::file::path const & path)552 Cache::loadURLs(ts::file::path const &path)
553 {
554   static const ts::TextView TAG_VOL("url");
555   ts::URLparser parser;
556   Errata zret;
557 
558   std::error_code ec;
559   std::string load_content = ts::file::load(path, ec);
560   if (ec.value() == 0) {
561     ts::TextView content(load_content);
562     while (!content.empty()) {
563       ts::TextView blob = content.take_prefix_at('\n');
564 
565       ts::TextView tag(blob.take_prefix_at('='));
566       if (tag.empty()) {
567       } else if (0 == strcasecmp(tag, TAG_VOL)) {
568         std::string url;
569         url.assign(blob.data(), blob.size());
570         int port_ptr = -1, port_len = -1;
571         int port = parser.getPort(url, port_ptr, port_len);
572         if (port_ptr >= 0 && port_len > 0) {
573           url.erase(port_ptr, port_len + 1); // get rid of :PORT
574         }
575         std::cout << "port # " << port << ":" << port_ptr << ":" << port_len << ":" << url << std::endl;
576         ts::CacheURL *curl = new ts::CacheURL(url, port);
577         URLset.emplace(curl);
578       }
579     }
580   } else {
581     zret = Errata::Message(0, EBADF, "Unable to load ", path.string());
582   }
583   return zret;
584 }
585 
586 void
dumpSpans(SpanDumpDepth depth)587 Cache::dumpSpans(SpanDumpDepth depth)
588 {
589   if (depth >= SpanDumpDepth::SPAN) {
590     for (auto span : _spans) {
591       if (nullptr == span->_header) {
592         std::cout << "Span: " << span->_path.string() << " is uninitialized" << std::endl;
593       } else {
594         std::cout << "\n----------------------------------\n"
595                   << "Span: " << span->_path.string() << "\n----------------------------------\n"
596                   << "#Magic: " << span->_header->magic << " #Volumes: " << span->_header->num_volumes
597                   << "  #in use: " << span->_header->num_used << "  #free: " << span->_header->num_free
598                   << "  #stripes: " << span->_header->num_diskvol_blks << "  Len(bytes): " << span->_header->num_blocks.value()
599                   << std::endl;
600 
601         for (auto stripe : span->_stripes) {
602           std::cout << "\n>>>>>>>>> Stripe " << static_cast<int>(stripe->_idx) << " @ " << stripe->_start
603                     << " len=" << stripe->_len.count() << " blocks "
604                     << " vol=" << static_cast<int>(stripe->_vol_idx) << " type=" << static_cast<int>(stripe->_type) << " "
605                     << (stripe->isFree() ? "free" : "in-use") << std::endl;
606 
607           std::cout << "      " << stripe->_segments << " segments with " << stripe->_buckets << " buckets per segment for "
608                     << stripe->_buckets * stripe->_segments * ts::ENTRIES_PER_BUCKET << " total directory entries taking "
609                     << stripe->_buckets * stripe->_segments * sizeof(CacheDirEntry) * ts::ENTRIES_PER_BUCKET
610                     //                        << " out of " << (delta-header_len).units() << " bytes."
611                     << std::endl;
612           if (depth >= SpanDumpDepth::STRIPE) {
613             Errata r = stripe->loadMeta();
614             if (r) {
615               // print Meta[A][HEAD]
616               std::string MetaCopy[2] = {"A", "B"};
617               std::string MetaType[2] = {"HEAD", "FOOT"};
618               for (int i = 0; i < 2; i++) {
619                 for (int j = 0; j < 2; j++) {
620                   std::cout << "\n" << MetaCopy[i] << ":" << MetaType[j] << "\n" << std::endl;
621                   std::cout << " Magic:" << stripe->_meta[i][j].magic << "\n version: major: " << stripe->_meta[i][j].version._major
622                             << "\n version: minor: " << stripe->_meta[i][j].version._minor
623                             << "\n create_time: " << stripe->_meta[i][j].create_time
624                             << "\n write_pos: " << stripe->_meta[i][j].write_pos
625                             << "\n last_write_pos: " << stripe->_meta[i][j].last_write_pos
626                             << "\n agg_pos: " << stripe->_meta[i][j].agg_pos << "\n generation: " << stripe->_meta[i][j].generation
627                             << "\n phase: " << stripe->_meta[i][j].phase << "\n cycle: " << stripe->_meta[i][j].cycle
628                             << "\n sync_serial: " << stripe->_meta[i][j].sync_serial
629                             << "\n write_serial: " << stripe->_meta[i][j].write_serial << "\n dirty: " << stripe->_meta[i][j].dirty
630                             << "\n sector_size: " << stripe->_meta[i][j].sector_size << std::endl;
631                 }
632               }
633               if (!stripe->validate_sync_serial()) {
634                 std::cout << "WARNING:::::Validity check failed for sync_serials" << std::endl;
635               }
636               stripe->_directory.clear();
637             } else {
638               std::cout << r;
639             }
640           }
641         }
642       }
643     }
644   }
645 }
646 
647 void
dumpVolumes()648 Cache::dumpVolumes()
649 {
650   for (auto const &elt : _volumes) {
651     size_t size = 0;
652     for (auto const &r : elt.second._stripes) {
653       size += r->_len;
654     }
655 
656     std::cout << "Volume " << elt.first << " has " << elt.second._stripes.size() << " stripes and " << size << " bytes"
657               << std::endl;
658   }
659 }
660 
661 ts::CacheStripeBlocks
calcTotalSpanConfiguredSize()662 Cache::calcTotalSpanConfiguredSize()
663 {
664   ts::CacheStripeBlocks zret(0);
665 
666   for (auto span : _spans) {
667     zret += round_down(span->_len);
668   }
669   return zret;
670 }
671 
672 #if 0
673 ts::CacheStripeBlocks
674 Cache::calcTotalSpanPhysicalSize()
675 {
676   ts::CacheStripeBlocks zret(0);
677 
678   for (auto span : _spans) {
679     // This is broken, physical_size doesn't work for devices, need to fix that.
680     zret += round_down(span->_path.physical_size());
681   }
682   return zret;
683 }
684 #endif
685 
~Cache()686 Cache::~Cache()
687 {
688   for (auto *span : _spans) {
689     delete span;
690   }
691 }
692 
693 Errata
load()694 Span::load()
695 {
696   Errata zret;
697   std::error_code ec;
698   auto fs = ts::file::status(_path, ec);
699 
700   if (!ts::file::is_readable(_path)) {
701     zret = Errata::Message(0, EPERM, _path.string(), " is not readable.");
702   } else if (ts::file::is_char_device(fs) || ts::file::is_block_device(fs)) {
703     zret = this->loadDevice();
704   } else if (ts::file::is_dir(fs)) {
705     zret.push(0, 1, "Directory support not yet available");
706   } else {
707     zret.push(0, EBADF, _path.string(), " is not a valid file type");
708   }
709   return zret;
710 }
711 
712 Errata
loadDevice()713 Span::loadDevice()
714 {
715   Errata zret;
716   int flags;
717 
718   flags = OPEN_RW_FLAG
719 #if defined(O_DIRECT)
720           | O_DIRECT
721 #endif
722 #if defined(O_DSYNC)
723           | O_DSYNC
724 #endif
725     ;
726 
727   ats_scoped_fd fd(!_path.empty() ? ::open(_path.c_str(), flags) : ats_scoped_fd());
728 
729   if (fd.isValid()) {
730     if (ink_file_get_geometry(fd, _geometry)) {
731       off_t offset = ts::CacheSpan::OFFSET;
732       CacheStoreBlocks span_hdr_size(1);                        // default.
733       static const ssize_t BUFF_SIZE = CacheStoreBlocks::SCALE; // match default span_hdr_size
734       alignas(512) char buff[BUFF_SIZE];
735       ssize_t n = pread(fd, buff, BUFF_SIZE, offset);
736       if (n >= BUFF_SIZE) {
737         ts::SpanHeader &span_hdr = reinterpret_cast<ts::SpanHeader &>(buff);
738         _base                    = round_up(offset);
739         // See if it looks valid
740         if (span_hdr.magic == ts::SpanHeader::MAGIC && span_hdr.num_diskvol_blks == span_hdr.num_used + span_hdr.num_free) {
741           int nspb      = span_hdr.num_diskvol_blks;
742           span_hdr_size = round_up(sizeof(ts::SpanHeader) + (nspb - 1) * sizeof(ts::CacheStripeDescriptor));
743           _header.reset(new (malloc(span_hdr_size)) ts::SpanHeader);
744           if (span_hdr_size <= BUFF_SIZE) {
745             memcpy(static_cast<void *>(_header.get()), buff, span_hdr_size);
746           } else {
747             // TODO - check the pread return
748             ssize_t n = pread(fd, _header.get(), span_hdr_size, offset);
749             if (n < span_hdr_size) {
750               std::cout << "Failed to read the Span Header" << std::endl;
751             }
752           }
753           _len = _header->num_blocks;
754         } else {
755           zret = Errata::Message(0, 0, _path.string(), " header is uninitialized or invalid");
756           std::cout << "Span: " << _path.string() << " header is uninitialized or invalid" << std::endl;
757           _len = round_down(_geometry.totalsz) - _base;
758         }
759         // valid FD means the device is accessible and has enough storage to be configured.
760         _fd     = fd.release();
761         _offset = _base + span_hdr_size;
762       } else {
763         zret = Errata::Message(0, errno, "Failed to read from ", _path.string(), '[', errno, ':', strerror(errno), ']');
764       }
765     } else {
766       zret = Errata::Message(0, 23, "Unable to get device geometry for ", _path.string());
767     }
768   } else {
769     zret = Errata::Message(0, errno, "Unable to open ", _path.string());
770   }
771   return zret;
772 }
773 
774 ts::Rv<Stripe *>
allocStripe(int vol_idx,const CacheStripeBlocks & len)775 Span::allocStripe(int vol_idx, const CacheStripeBlocks &len)
776 {
777   for (auto spot = _stripes.begin(), limit = _stripes.end(); spot != limit; ++spot) {
778     Stripe *stripe = *spot;
779     if (stripe->isFree()) {
780       if (len < stripe->_len) {
781         // If the remains would be less than a stripe block, just take it all.
782         if (stripe->_len <= (len + CacheStripeBlocks(1))) {
783           stripe->_vol_idx = vol_idx;
784           stripe->_type    = 1;
785           return stripe;
786         } else {
787           Stripe *ns = new Stripe(this, stripe->_start, len);
788           stripe->_start += len;
789           stripe->_len -= len;
790           ns->_vol_idx = vol_idx;
791           ns->_type    = 1;
792           _stripes.insert(spot, ns);
793           return ns;
794         }
795       }
796     }
797   }
798   return ts::Rv<Stripe *>(nullptr,
799                           Errata::Message(0, 15, "Failed to allocate stripe of size ", len, " - no free block large enough"));
800 }
801 
802 bool
isEmpty() const803 Span::isEmpty() const
804 {
805   return std::all_of(_stripes.begin(), _stripes.end(), [](Stripe *s) { return s->_vol_idx == 0; });
806 }
807 
808 Errata
clear()809 Span::clear()
810 {
811   Stripe *stripe;
812   std::for_each(_stripes.begin(), _stripes.end(), [](Stripe *s) { delete s; });
813   _stripes.clear();
814 
815   // Gah, due to lack of anything better, TS depends on the number of usable blocks to be consistent
816   // with internal calculations so have to match that here. Yay.
817   CacheStoreBlocks eff = _len - _base; // starting # of usable blocks.
818   // The maximum number of volumes that can store stored, accounting for the space used to store the descriptors.
819   int n   = (eff - sizeof(ts::SpanHeader)) / (CacheStripeBlocks::SCALE + sizeof(CacheStripeDescriptor));
820   _offset = _base + round_up(sizeof(ts::SpanHeader) + (n - 1) * sizeof(CacheStripeDescriptor));
821   stripe  = new Stripe(this, _offset, _len - _offset);
822   stripe->vol_init_data();
823   stripe->InitializeMeta();
824   _stripes.push_back(stripe);
825   _free_space = stripe->_len;
826 
827   return Errata();
828 }
829 
830 Errata
updateHeader()831 Span::updateHeader()
832 {
833   Errata zret;
834   int n = _stripes.size();
835   CacheStripeDescriptor *sd;
836   CacheStoreBlocks hdr_size = round_up(sizeof(ts::SpanHeader) + (n - 1) * sizeof(ts::CacheStripeDescriptor));
837   void *raw                 = ats_memalign(512, hdr_size);
838   ts::SpanHeader *hdr       = static_cast<ts::SpanHeader *>(raw);
839   std::bitset<ts::MAX_VOLUME_IDX + 1> volume_mask;
840 
841   hdr->magic            = ts::SpanHeader::MAGIC;
842   hdr->num_free         = 0;
843   hdr->num_used         = 0;
844   hdr->num_diskvol_blks = n;
845   hdr->num_blocks       = _len;
846 
847   sd = hdr->stripes;
848   for (auto stripe : _stripes) {
849     sd->offset               = stripe->_start;
850     sd->len                  = stripe->_len;
851     sd->vol_idx              = stripe->_vol_idx;
852     sd->type                 = stripe->_type;
853     volume_mask[sd->vol_idx] = true;
854     if (sd->vol_idx == 0) {
855       sd->free = true;
856       ++(hdr->num_free);
857     } else {
858       sd->free = false;
859       ++(hdr->num_used);
860     }
861 
862     ++sd;
863   }
864   volume_mask[0]   = false; // don't include free stripes in distinct volume count.
865   hdr->num_volumes = volume_mask.count();
866   _header.reset(hdr);
867   if (OPEN_RW_FLAG) {
868     ssize_t r = pwrite(_fd, hdr, hdr_size, ts::CacheSpan::OFFSET);
869     if (r < ts::CacheSpan::OFFSET) {
870       zret.push(0, errno, "Failed to update span - ", strerror(errno));
871     }
872   } else {
873     std::cout << "Writing not enabled, no updates performed" << std::endl;
874   }
875   return zret;
876 }
877 
878 void
clearPermanently()879 Span::clearPermanently()
880 {
881   if (OPEN_RW_FLAG) {
882     alignas(512) static char zero[CacheStoreBlocks::SCALE]; // should be all zero, it's static.
883     std::cout << "Clearing " << _path.string() << " permanently on disk ";
884     ssize_t n = pwrite(_fd, zero, sizeof(zero), ts::CacheSpan::OFFSET);
885     if (n == sizeof(zero)) {
886       std::cout << "done";
887     } else {
888       const char *text = strerror(errno);
889       std::cout << "failed";
890       if (n >= 0) {
891         std::cout << " - " << n << " of " << sizeof(zero) << " bytes written";
892       }
893       std::cout << " - " << text;
894     }
895 
896     std::cout << std::endl;
897     // clear the stripes as well
898     for (auto *strp : _stripes) {
899       strp->loadMeta();
900       std::cout << "Clearing stripe @" << strp->_start << " of length: " << strp->_len << std::endl;
901       strp->clear();
902     }
903   } else {
904     std::cout << "Clearing " << _path.string() << " not performed, write not enabled" << std::endl;
905   }
906 }
907 
908 // explicit pair for random table in build_vol_hash_table
909 struct rtable_pair {
910   unsigned int rval; ///< relative value, used to sort.
911   unsigned int idx;  ///< volume mapping table index.
912 };
913 
914 // comparison operator for random table in build_vol_hash_table
915 // sorts based on the randomly assigned rval
916 static int
cmprtable(const void * aa,const void * bb)917 cmprtable(const void *aa, const void *bb)
918 {
919   rtable_pair *a = (rtable_pair *)aa;
920   rtable_pair *b = (rtable_pair *)bb;
921   if (a->rval < b->rval) {
922     return -1;
923   }
924   if (a->rval > b->rval) {
925     return 1;
926   }
927   return 0;
928 }
929 
930 unsigned int
next_rand(unsigned int * p)931 next_rand(unsigned int *p)
932 {
933   unsigned int seed = *p;
934   seed              = 1103515145 * seed + 12345;
935   *p                = seed;
936   return seed;
937 }
938 
939 void
build_stripe_hash_table()940 Cache::build_stripe_hash_table()
941 {
942   int num_stripes = globalVec_stripe.size();
943   CacheStoreBlocks total;
944   unsigned int *forvol         = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
945   unsigned int *gotvol         = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
946   unsigned int *rnd            = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
947   unsigned short *ttable       = static_cast<unsigned short *>(ats_malloc(sizeof(unsigned short) * VOL_HASH_TABLE_SIZE));
948   unsigned int *rtable_entries = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
949   unsigned int rtable_size     = 0;
950   int i                        = 0;
951   uint64_t used                = 0;
952 
953   // estimate allocation
954   for (auto &elt : globalVec_stripe) {
955     // printf("stripe length %" PRId64 "\n", elt->_len.count());
956     rtable_entries[i] = static_cast<int64_t>(elt->_len) / Vol_hash_alloc_size;
957     rtable_size += rtable_entries[i];
958     uint64_t x = elt->hash_id.fold();
959     // seed random number generator
960     rnd[i] = static_cast<unsigned int>(x);
961     total += elt->_len;
962     i++;
963   }
964   i = 0;
965   for (auto &elt : globalVec_stripe) {
966     forvol[i] = total ? static_cast<int64_t>(VOL_HASH_TABLE_SIZE * elt->_len) / total : 0;
967     used += forvol[i];
968     gotvol[i] = 0;
969     i++;
970   }
971 
972   // spread around the excess
973   int extra = VOL_HASH_TABLE_SIZE - used;
974   for (int i = 0; i < extra; i++) {
975     forvol[i % num_stripes]++;
976   }
977 
978   // initialize table to "empty"
979   for (int i = 0; i < VOL_HASH_TABLE_SIZE; i++) {
980     ttable[i] = VOL_HASH_EMPTY;
981   }
982 
983   // generate random numbers proportional to allocation
984   rtable_pair *rtable = static_cast<rtable_pair *>(ats_malloc(sizeof(rtable_pair) * rtable_size));
985   int rindex          = 0;
986   for (int i = 0; i < num_stripes; i++) {
987     for (int j = 0; j < static_cast<int>(rtable_entries[i]); j++) {
988       rtable[rindex].rval = next_rand(&rnd[i]);
989       rtable[rindex].idx  = i;
990       rindex++;
991     }
992   }
993   assert(rindex == (int)rtable_size);
994   // sort (rand #, vol $ pairs)
995   qsort(rtable, rtable_size, sizeof(rtable_pair), cmprtable);
996   unsigned int width = (1LL << 32) / VOL_HASH_TABLE_SIZE;
997   unsigned int pos; // target position to allocate
998   // select vol with closest random number for each bucket
999   i = 0; // index moving through the random numbers
1000   for (int j = 0; j < VOL_HASH_TABLE_SIZE; j++) {
1001     pos = width / 2 + j * width; // position to select closest to
1002     while (pos > rtable[i].rval && i < static_cast<int>(rtable_size) - 1) {
1003       i++;
1004     }
1005     ttable[j] = rtable[i].idx;
1006     gotvol[rtable[i].idx]++;
1007   }
1008   for (int i = 0; i < num_stripes; i++) {
1009     printf("build_vol_hash_table index %d mapped to %d requested %d got %d\n", i, i, forvol[i], gotvol[i]);
1010   }
1011   stripes_hash_table = ttable;
1012 
1013   ats_free(forvol);
1014   ats_free(gotvol);
1015   ats_free(rnd);
1016   ats_free(rtable_entries);
1017   ats_free(rtable);
1018 }
1019 
1020 Stripe *
key_to_stripe(CryptoHash * key,const char * hostname,int host_len)1021 Cache::key_to_stripe(CryptoHash *key, const char *hostname, int host_len)
1022 {
1023   uint32_t h = (key->slice32(2) >> DIR_TAG_WIDTH) % VOL_HASH_TABLE_SIZE;
1024   return globalVec_stripe[stripes_hash_table[h]];
1025 }
1026 
1027 /* --------------------------------------------------------------------------------------- */
1028 Errata
load(ts::file::path const & path)1029 VolumeConfig::load(ts::file::path const &path)
1030 {
1031   static const ts::TextView TAG_SIZE("size");
1032   static const ts::TextView TAG_VOL("volume");
1033 
1034   Errata zret;
1035 
1036   int ln = 0;
1037 
1038   std::error_code ec;
1039   std::string load_content = ts::file::load(path, ec);
1040   if (ec.value() == 0) {
1041     ts::TextView content(load_content);
1042     while (content) {
1043       Data v;
1044 
1045       ++ln;
1046       ts::TextView line = content.take_prefix_at('\n');
1047       line.ltrim_if(&isspace);
1048       if (line.empty() || '#' == *line) {
1049         continue;
1050       }
1051 
1052       while (line) {
1053         ts::TextView value(line.take_prefix_if(&isspace));
1054         ts::TextView tag(value.take_prefix_at('='));
1055         if (tag.empty()) {
1056           zret.push(0, 1, "Line ", ln, " is invalid");
1057         } else if (0 == strcasecmp(tag, TAG_SIZE)) {
1058           if (v.hasSize()) {
1059             zret.push(0, 5, "Line ", ln, " has field ", TAG_SIZE, " more than once");
1060           } else {
1061             ts::TextView text;
1062             auto n = ts::svtoi(value, &text);
1063             if (text) {
1064               ts::TextView percent(text.data_end(), value.data_end()); // clip parsed number.
1065               if (percent.empty()) {
1066                 v._size = CacheStripeBlocks(round_up(Megabytes(n)));
1067                 if (v._size.count() != n) {
1068                   zret.push(0, 0, "Line ", ln, " size ", n, " was rounded up to ", v._size);
1069                 }
1070               } else if ('%' == *percent && percent.size() == 1) {
1071                 v._percent = n;
1072               } else {
1073                 zret.push(0, 3, "Line ", ln, " has invalid value '", value, "' for ", TAG_SIZE, " field");
1074               }
1075             } else {
1076               zret.push(0, 2, "Line ", ln, " has invalid value '", value, "' for ", TAG_SIZE, " field");
1077             }
1078           }
1079         } else if (0 == strcasecmp(tag, TAG_VOL)) {
1080           if (v.hasIndex()) {
1081             zret.push(0, 6, "Line ", ln, " has field ", TAG_VOL, " more than once");
1082           } else {
1083             ts::TextView text;
1084             auto n = ts::svtoi(value, &text);
1085             if (text == value) {
1086               v._idx = n;
1087             } else {
1088               zret.push(0, 4, "Line ", ln, " has invalid value '", value, "' for ", TAG_VOL, " field");
1089             }
1090           }
1091         }
1092       }
1093       if (v.hasSize() && v.hasIndex()) {
1094         _volumes.push_back(std::move(v));
1095       } else {
1096         if (!v.hasSize()) {
1097           zret.push(0, 7, "Line ", ln, " does not have the required field ", TAG_SIZE);
1098         }
1099         if (!v.hasIndex()) {
1100           zret.push(0, 8, "Line ", ln, " does not have the required field ", TAG_VOL);
1101         }
1102       }
1103     }
1104   } else {
1105     zret = Errata::Message(0, EBADF, "Unable to load ", path.string());
1106   }
1107   return zret;
1108 }
1109 } // namespace ct
1110 
1111 using namespace ct;
1112 void
List_Stripes(Cache::SpanDumpDepth depth)1113 List_Stripes(Cache::SpanDumpDepth depth)
1114 {
1115   Cache cache;
1116 
1117   if ((err = cache.loadSpan(SpanFile))) {
1118     cache.dumpSpans(depth);
1119     cache.dumpVolumes();
1120   }
1121 }
1122 
1123 void
Cmd_Allocate_Empty_Spans()1124 Cmd_Allocate_Empty_Spans()
1125 {
1126   VolumeAllocator va;
1127 
1128   //  OPEN_RW_FLAG = O_RDWR;
1129   if ((err = va.load(SpanFile, VolumeFile))) {
1130     va.fillEmptySpans();
1131   }
1132 }
1133 
1134 void
Simulate_Span_Allocation()1135 Simulate_Span_Allocation()
1136 {
1137   VolumeAllocator va;
1138 
1139   if (VolumeFile.empty()) {
1140     err.push(0, 9, "Volume config file not set");
1141   }
1142   if (SpanFile.empty()) {
1143     err.push(0, 9, "Span file not set");
1144   }
1145 
1146   if (err) {
1147     if ((err = va.load(SpanFile, VolumeFile)).isOK()) {
1148       err = va.fillAllSpans();
1149       va.dumpVolumes();
1150     }
1151   }
1152 }
1153 
1154 void
Clear_Spans()1155 Clear_Spans()
1156 {
1157   Cache cache;
1158 
1159   if (!OPEN_RW_FLAG) {
1160     err.push(0, 1, "Writing Not Enabled.. Please use --write to enable writing to disk");
1161     return;
1162   }
1163 
1164   if ((err = cache.loadSpan(SpanFile))) {
1165     for (auto *span : cache._spans) {
1166       span->clearPermanently();
1167     }
1168   }
1169 }
1170 
1171 void
Find_Stripe(ts::file::path const & input_file_path)1172 Find_Stripe(ts::file::path const &input_file_path)
1173 {
1174   // scheme=http user=u password=p host=172.28.56.109 path=somepath query=somequery port=1234
1175   // input file format: scheme://hostname:port/somepath;params?somequery user=USER password=PASS
1176   // user, password, are optional; scheme and host are required
1177 
1178   //  char* h= http://user:pass@IPADDRESS/path_to_file;?port      <== this is the format we need
1179   //  url_matcher matcher;
1180   //  if (matcher.match(h))
1181   //    std::cout << h << " : is valid" << std::endl;
1182   //  else
1183   //    std::cout << h << " : is NOT valid" << std::endl;
1184 
1185   Cache cache;
1186   if (!input_file_path.empty()) {
1187     printf("passed argv %s\n", input_file_path.c_str());
1188   }
1189   cache.loadURLs(input_file_path);
1190   if ((err = cache.loadSpan(SpanFile))) {
1191     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1192     cache.build_stripe_hash_table();
1193     for (auto host : cache.URLset) {
1194       CryptoContext ctx;
1195       CryptoHash hashT;
1196       ts::LocalBufferWriter<33> w;
1197       ctx.update(host->url.data(), host->url.size());
1198       ctx.update(&host->port, sizeof(host->port));
1199       ctx.finalize(hashT);
1200       Stripe *stripe_ = cache.key_to_stripe(&hashT, host->url.data(), host->url.size());
1201       w.print("{}", hashT);
1202       printf("hash of %.*s is %.*s: Stripe  %s \n", static_cast<int>(host->url.size()), host->url.data(),
1203              static_cast<int>(w.size()), w.data(), stripe_->hashText.data());
1204     }
1205   }
1206 }
1207 
1208 void
dir_check()1209 dir_check()
1210 {
1211   Cache cache;
1212   if ((err = cache.loadSpan(SpanFile))) {
1213     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1214     for (auto &stripe : cache.globalVec_stripe) {
1215       stripe->dir_check();
1216     }
1217   }
1218   printf("\nCHECK succeeded\n");
1219 }
1220 
1221 void
walk_bucket_chain(const std::string & devicePath)1222 walk_bucket_chain(const std::string &devicePath)
1223 {
1224   Cache cache;
1225   if ((err = cache.loadSpan(SpanFile))) {
1226     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1227     for (auto sp : cache._spans) {
1228       if (devicePath.size() > 0 && sp->_path.view() == devicePath) {
1229         for (auto strp : sp->_stripes) {
1230           strp->loadMeta();
1231           strp->loadDir();
1232           strp->walk_all_buckets();
1233         }
1234       }
1235     }
1236   }
1237 }
1238 
1239 void
Clear_Span(const std::string & devicePath)1240 Clear_Span(const std::string &devicePath)
1241 {
1242   Cache cache;
1243   if ((err = cache.loadSpan(SpanFile))) {
1244     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1245     for (auto sp : cache._spans) {
1246       if (devicePath.size() > 0 && sp->_path.view() == devicePath) {
1247         printf("clearing %s\n", devicePath.data());
1248         sp->clearPermanently();
1249       }
1250     }
1251   }
1252   return;
1253 }
1254 
1255 void
Check_Freelist(const std::string & devicePath)1256 Check_Freelist(const std::string &devicePath)
1257 {
1258   Cache cache;
1259   if ((err = cache.loadSpan(SpanFile))) {
1260     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1261     for (auto sp : cache._spans) {
1262       if (devicePath.size() > 0 && sp->_path.view() == devicePath) {
1263         printf("Scanning %s\n", devicePath.data());
1264         for (auto strp : sp->_stripes) {
1265           strp->loadMeta();
1266           strp->loadDir();
1267           for (int s = 0; s < strp->_segments; s++) {
1268             strp->check_loop(s);
1269           }
1270         }
1271         break;
1272       }
1273     }
1274   }
1275 }
1276 
1277 void
Init_disk(ts::file::path const & input_file_path)1278 Init_disk(ts::file::path const &input_file_path)
1279 {
1280   Cache cache;
1281   VolumeAllocator va;
1282 
1283   if (!OPEN_RW_FLAG) {
1284     err.push(0, 1, "Writing Not Enabled.. Please use --write to enable writing to disk");
1285     return;
1286   }
1287 
1288   err = va.load(SpanFile, VolumeFile);
1289   va.allocateSpan(input_file_path);
1290 }
1291 
1292 void
Get_Response(ts::file::path const & input_file_path)1293 Get_Response(ts::file::path const &input_file_path)
1294 {
1295   // scheme=http user=u password=p host=172.28.56.109 path=somepath query=somequery port=1234
1296   // input file format: scheme://hostname:port/somepath;params?somequery user=USER password=PASS
1297   // user, password, are optional; scheme and host are required
1298 
1299   //  char* h= http://user:pass@IPADDRESS/path_to_file;?port      <== this is the format we need
1300 
1301   Cache cache;
1302   if (!input_file_path.empty()) {
1303     printf("passed argv %s\n", input_file_path.c_str());
1304   }
1305   cache.loadURLs(input_file_path);
1306   if ((err = cache.loadSpan(SpanFile))) {
1307     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1308     cache.build_stripe_hash_table();
1309     for (auto host : cache.URLset) {
1310       CryptoContext ctx;
1311       CryptoHash hashT;
1312       ts::LocalBufferWriter<33> w;
1313       ctx.update(host->url.data(), host->url.size());
1314       ctx.update(&host->port, sizeof(host->port));
1315       ctx.finalize(hashT);
1316       Stripe *stripe_ = cache.key_to_stripe(&hashT, host->url.data(), host->url.size());
1317       w.print("{}", hashT);
1318       printf("hash of %.*s is %.*s: Stripe  %s \n", static_cast<int>(host->url.size()), host->url.data(),
1319              static_cast<int>(w.size()), w.data(), stripe_->hashText.data());
1320       CacheDirEntry *dir_result = nullptr;
1321       stripe_->loadMeta();
1322       stripe_->loadDir();
1323       stripe_->dir_probe(&hashT, dir_result, nullptr);
1324     }
1325   }
1326 }
1327 
scan_span(Span * span,ts::file::path const & regex_path)1328 void static scan_span(Span *span, ts::file::path const &regex_path)
1329 {
1330   for (auto strp : span->_stripes) {
1331     strp->loadMeta();
1332     strp->loadDir();
1333 
1334     if (!regex_path.empty()) {
1335       CacheScan cs(strp, regex_path);
1336       cs.Scan(true);
1337     } else {
1338       CacheScan cs(strp);
1339       cs.Scan(false);
1340     }
1341   }
1342 }
1343 
1344 void
Scan_Cache(ts::file::path const & regex_path)1345 Scan_Cache(ts::file::path const &regex_path)
1346 {
1347   Cache cache;
1348   std::vector<std::thread> threadPool;
1349   if ((err = cache.loadSpan(SpanFile))) {
1350     if (err.size()) {
1351       return;
1352     }
1353     cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1354     for (auto sp : cache._spans) {
1355       threadPool.emplace_back(scan_span, sp, regex_path);
1356     }
1357     for (auto &th : threadPool) {
1358       th.join();
1359     }
1360   }
1361 }
1362 
1363 int
main(int argc,const char * argv[])1364 main(int argc, const char *argv[])
1365 {
1366   ts::file::path input_url_file;
1367   std::string inputFile;
1368 
1369   parser.add_global_usage(std::string(argv[0]) + " --spans <SPAN> --volume <FILE> <COMMAND> [<SUBCOMMAND> ...]\n");
1370   parser.require_commands()
1371     .add_option("--help", "-h", "")
1372     .add_option("--spans", "-s", "", "", 1)
1373     .add_option("--volumes", "-v", "", "", 1)
1374     .add_option("--write", "-w", "")
1375     .add_option("--input", "-i", "", "", 1)
1376     .add_option("--device", "-d", "", "", 1)
1377     .add_option("--aos", "-o", "", "", 1);
1378 
1379   parser.add_command("list", "List elements of the cache", []() { List_Stripes(Cache::SpanDumpDepth::SPAN); })
1380     .add_command("stripes", "List the stripes", []() { List_Stripes(Cache::SpanDumpDepth::STRIPE); });
1381   parser.add_command("clear", "Clear spans", []() { Clear_Spans(); }).add_command("span", "clear an specific span", [&]() {
1382     Clear_Span(inputFile);
1383   });
1384   auto &c = parser.add_command("dir_check", "cache check").require_commands();
1385   c.add_command("full", "Full report of the cache storage", &dir_check);
1386   c.add_command("freelist", "check the freelist for loop", [&]() { Check_Freelist(inputFile); });
1387   c.add_command("bucket_chain", "walk bucket chains for loops", [&]() { walk_bucket_chain(inputFile); });
1388   parser.add_command("volumes", "Volumes", &Simulate_Span_Allocation);
1389   parser.add_command("alloc", "Storage allocation")
1390     .require_commands()
1391     .add_command("free", "Allocate storage on free (empty) spans", &Cmd_Allocate_Empty_Spans);
1392   parser.add_command("find", "Find Stripe Assignment", [&]() { Find_Stripe(input_url_file); });
1393   parser.add_command("clearspan", "clear specific span").add_command("span", "device path", [&]() { Clear_Span(inputFile); });
1394   parser.add_command("retrieve", " retrieve the response of the given list of URLs", [&]() { Get_Response(input_url_file); });
1395   parser.add_command("init", " Initializes uninitialized span", [&]() { Init_disk(input_url_file); });
1396   parser.add_command("scan", " Scans the whole cache and lists the urls of the cached contents",
1397                      [&]() { Scan_Cache(input_url_file); });
1398 
1399   // parse the arguments
1400   auto arguments = parser.parse(argv);
1401   if (auto data = arguments.get("spans")) {
1402     SpanFile = data.value();
1403   }
1404   if (auto data = arguments.get("volumes")) {
1405     VolumeFile = data.value();
1406   }
1407   if (auto data = arguments.get("input")) {
1408     input_url_file = data.value();
1409   }
1410   if (auto data = arguments.get("aos")) {
1411     cache_config_min_average_object_size = std::stoi(data.value());
1412   }
1413   if (auto data = arguments.get("device")) {
1414     inputFile = data.value();
1415   }
1416   if (auto data = arguments.get("write")) {
1417     OPEN_RW_FLAG = O_RDWR;
1418     std::cout << "NOTE: Writing to physical devices enabled" << std::endl;
1419   }
1420 
1421   if (arguments.has_action()) {
1422     arguments.invoke();
1423   }
1424 
1425   if (err.size()) {
1426     std::cerr << err;
1427     exit(1);
1428   }
1429 
1430   return 0;
1431 }
1432