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