blob: e69becaaf79c24c333d2a27d7e25d5e3701db095 [file] [log] [blame]
/** @file
A brief file description
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <netinet/in.h>
#include <iostream>
#include <list>
#include "tscore/I_Version.h"
#include "swoc/Scalar.h"
#include "tscore/Regex.h"
#include "tscore/Errata.h"
#include "tscpp/util/TextView.h"
#include "tscore/ink_file.h"
#include "tscore/CryptoHash.h"
#include "tscore/ts_file.h"
namespace ts::tag
{
struct bytes {
static constexpr char const *const label = " bytes";
};
} // namespace ts::tag
using swoc::round_down;
using swoc::round_up;
namespace ts
{
/* INK_ALIGN() is only to be used to align on a power of 2 boundary */
#define INK_ALIGN(size, boundary) (((size) + ((boundary)-1)) & ~((boundary)-1))
#define ROUND_TO_STORE_BLOCK(_x) INK_ALIGN((_x), 8192)
#define dir_clear(_e) \
do { \
(_e)->w[0] = 0; \
(_e)->w[1] = 0; \
(_e)->w[2] = 0; \
(_e)->w[3] = 0; \
(_e)->w[4] = 0; \
} while (0)
#define dir_assign(_e, _x) \
do { \
(_e)->w[0] = (_x)->w[0]; \
(_e)->w[1] = (_x)->w[1]; \
(_e)->w[2] = (_x)->w[2]; \
(_e)->w[3] = (_x)->w[3]; \
(_e)->w[4] = (_x)->w[4]; \
} while (0)
constexpr static uint8_t CACHE_DB_MAJOR_VERSION = 24;
constexpr static uint8_t CACHE_DB_MINOR_VERSION = 1;
/// Maximum allowed volume index.
constexpr static int MAX_VOLUME_IDX = 255;
constexpr static int ENTRIES_PER_BUCKET = 4;
constexpr static int MAX_BUCKETS_PER_SEGMENT = (1 << 16) / ENTRIES_PER_BUCKET;
using Bytes = swoc::Scalar<1, off_t, ts::tag::bytes>;
using Kilobytes = swoc::Scalar<1024, off_t, ts::tag::bytes>;
using Megabytes = swoc::Scalar<1024 * Kilobytes::SCALE, off_t, ts::tag::bytes>;
using Gigabytes = swoc::Scalar<1024 * Megabytes::SCALE, off_t, ts::tag::bytes>;
using Terabytes = swoc::Scalar<1024 * Gigabytes::SCALE, off_t, ts::tag::bytes>;
// Units of allocation for stripes.
using CacheStripeBlocks = swoc::Scalar<128 * Megabytes::SCALE, int64_t, ts::tag::bytes>;
// Size measurement of cache storage.
// Also size of meta data storage units.
using CacheStoreBlocks = swoc::Scalar<8 * Kilobytes::SCALE, int64_t, ts::tag::bytes>;
// Size unit for content stored in cache.
using CacheDataBlocks = swoc::Scalar<512, int64_t, ts::tag::bytes>;
/** A cache span is a representation of raw storage.
It corresponds to a raw disk, disk partition, file, or directory.
*/
class CacheSpan
{
public:
/// Default offset of start of data in a span.
/// @internal I think this is done to avoid collisions with partition tracking mechanisms.
static const Bytes OFFSET;
};
/** A section of storage in a span, used to contain a stripe.
This is stored in the span header to describe the stripes in the span.
@note Serializable.
@internal nee @c DiskVolBlock
*/
struct CacheStripeDescriptor {
Bytes offset; // offset of start of stripe from start of span.
CacheStoreBlocks len; // length of block.
uint32_t vol_idx; ///< If in use, the volume index.
unsigned int type : 3;
unsigned int free : 1;
};
/** Header data for a span.
This is the serializable descriptor stored in a span.
@internal nee DiskHeader
*/
struct SpanHeader {
static constexpr uint32_t MAGIC = 0xABCD1237;
uint32_t magic;
uint32_t num_volumes; /* number of discrete volumes (DiskVol) */
uint32_t num_free; /* number of disk volume blocks free */
uint32_t num_used; /* number of disk volume blocks in use */
uint32_t num_diskvol_blks; /* number of disk volume blocks */
CacheStoreBlocks num_blocks;
/// Serialized stripe descriptors. This is treated as a variable sized array.
CacheStripeDescriptor stripes[1];
};
/** Stripe data, serialized format.
@internal nee VolHeadFooter
*/
// the counterpart of this structure in ATS is called VolHeaderFooter
class StripeMeta
{
public:
static constexpr uint32_t MAGIC = 0xF1D0F00D;
uint32_t magic;
VersionNumber version;
time_t create_time;
off_t write_pos;
off_t last_write_pos;
off_t agg_pos;
uint32_t generation; // token generation (vary), this cannot be 0
uint32_t phase;
uint32_t cycle;
uint32_t sync_serial;
uint32_t write_serial;
uint32_t dirty;
uint32_t sector_size;
uint32_t unused; // pad out to 8 byte boundary
uint16_t freelist[1];
};
struct Doc {
uint32_t magic; // DOC_MAGIC
uint32_t len; // length of this fragment (including hlen & sizeof(Doc), unrounded)
uint64_t total_len; // total length of document
#if TS_ENABLE_FIPS == 1
// For FIPS CryptoHash is 256 bits vs. 128, and the 'first_key' must be checked first, so
// ensure that the new 'first_key' overlaps the old 'first_key' and that the rest of the data layout
// is the same by putting 'key' at the ned.
CryptoHash first_key; ///< first key in object.
#else
CryptoHash first_key; ///< first key in object.
CryptoHash key; ///< Key for this doc.
#endif
uint32_t hlen; ///< Length of this header.
uint32_t doc_type : 8; ///< Doc type - indicates the format of this structure and its content.
uint32_t v_major : 8; ///< Major version number.
uint32_t v_minor : 8; ///< Minor version number.
uint32_t unused : 8; ///< Unused, forced to zero.
uint32_t sync_serial;
uint32_t write_serial;
uint32_t pinned; // pinned until
uint32_t checksum;
#if TS_ENABLE_FIPS == 1
CryptoHash key; ///< Key for this doc.
#endif
uint32_t data_len();
uint32_t prefix_len();
int single_fragment();
char *hdr();
char *data();
};
/*
@internal struct Dir in P_CacheDir.h
* size: 10bytes
*/
class CacheDirEntry
{
public:
#if 0
unsigned int offset : 24;
unsigned int big : 2;
unsigned int size : 6;
unsigned int tag : 12;
unsigned int phase : 1;
unsigned int head : 1;
unsigned int pinnned : 1;
unsigned int token : 1;
unsigned int next : 16;
uint16_t offset_high;
#else
uint16_t w[5];
#endif
};
class CacheVolume
{
};
class URLparser
{
public:
bool verifyURL(std::string &url1);
Errata parseURL(TextView URI);
int getPort(std::string &fullURL, int &port_ptr, int &port_len);
private:
// DFA regex;
};
class CacheURL
{
public:
in_port_t port;
std::string scheme;
std::string url;
std::string hostname;
std::string path;
std::string query;
std::string params;
std::string fragments;
std::string user;
std::string password;
CacheURL(int port_, ts::TextView b_hostname, ts::TextView b_path, ts::TextView b_params, ts::TextView b_query,
ts::TextView b_fragments)
{
hostname.assign(b_hostname.data(), b_hostname.size());
port = port_;
path.assign(b_path.data(), b_path.size());
params.assign(b_params.data(), b_params.size());
query.assign(b_query.data(), b_query.size());
fragments.assign(b_fragments.data(), b_fragments.size());
}
CacheURL(ts::TextView blob, int port_)
{
url.assign(blob.data(), blob.size());
port = port_;
}
void
setCredential(char *p_user, int user_len, char *p_pass, int pass_len)
{
user.assign(p_user, user_len);
password.assign(p_pass, pass_len);
}
};
} // namespace ts
class DFA;
// this class matches url of the format : scheme://hostname:port/path;params?query
struct url_matcher {
url_matcher(ts::file::path const &path) // file contains a list of regex
{
std::error_code ec;
std::string load_content = ts::file::load(path, ec);
ts::TextView fileContent(load_content);
if (ec.value() == 0) {
const char **patterns;
std::vector<std::string> str_vec;
int count = 0;
while (fileContent) {
ts::TextView line = fileContent.take_prefix_at('\n');
std::string reg_str(line.data(), line.size());
str_vec.push_back(reg_str);
count++;
}
patterns = (const char **)ats_malloc(count * sizeof(char *));
int i = 0;
for (const auto &str : str_vec) {
patterns[i++] = ats_strdup(str.data());
std::cout << "regex input\n" << patterns[i - 1] << std::endl;
}
for (i = 0; i < count; i++) {
std::cout << "regex " << patterns[i] << std::endl;
}
if (regex.compile(patterns, count) != count) {
std::cout << "Check your regular expression" << std::endl;
}
if (!port.compile(R"([0-9]+$)")) {
std::cout << "Check your regular expression" << std::endl;
return;
}
}
}
url_matcher()
{
if (!regex.compile(R"(^(https?\:\/\/)")) {
std::cout << "Check your regular expression" << std::endl;
return;
}
if (!port.compile(R"([0-9]+$)")) {
std::cout << "Check your regular expression" << std::endl;
return;
}
}
~url_matcher() {}
uint8_t
match(const char *hostname) const
{
return regex.match(hostname) ? 1 : 0;
}
uint8_t
portmatch(const char *hostname, int length) const
{
return port.match({hostname, size_t(length)}) ? 1 : 0;
}
private:
DFA port;
DFA regex;
};
using ts::Bytes;
using ts::Megabytes;
using ts::CacheStoreBlocks;
using ts::CacheStripeBlocks;
using ts::StripeMeta;
using ts::CacheStripeDescriptor;
using ts::Errata;
using ts::CacheDirEntry;
using swoc::MemSpan;
using ts::Doc;
constexpr int ESTIMATED_OBJECT_SIZE = 8000;
constexpr int DEFAULT_HW_SECTOR_SIZE = 512;
constexpr int VOL_HASH_TABLE_SIZE = 32707;
constexpr unsigned short VOL_HASH_EMPTY = 65535;
constexpr int DIR_TAG_WIDTH = 12;
constexpr int DIR_DEPTH = 4;
constexpr int SIZEOF_DIR = 10;
constexpr int MAX_ENTRIES_PER_SEGMENT = (1 << 16);
constexpr int DIR_SIZE_WIDTH = 6;
constexpr int DIR_BLOCK_SIZES = 4;
constexpr int CACHE_BLOCK_SHIFT = 9;
constexpr int CACHE_BLOCK_SIZE = (1 << CACHE_BLOCK_SHIFT); // 512, smallest sector size
namespace ct
{
#define dir_big(_e) ((uint32_t)((((_e)->w[1]) >> 8) & 0x3))
#define dir_bit(_e, _w, _b) ((uint32_t)(((_e)->w[_w] >> (_b)) & 1))
#define dir_size(_e) ((uint32_t)(((_e)->w[1]) >> 10))
#define dir_approx_size(_e) ((dir_size(_e) + 1) * DIR_BLOCK_SIZE(dir_big(_e)))
#define dir_head(_e) dir_bit(_e, 2, 13)
#define DIR_MASK_TAG(_t) ((_t) & ((1 << DIR_TAG_WIDTH) - 1))
#define dir_tag(_e) ((uint32_t)((_e)->w[2] & ((1 << DIR_TAG_WIDTH) - 1)))
#define dir_offset(_e) \
((int64_t)(((uint64_t)(_e)->w[0]) | (((uint64_t)((_e)->w[1] & 0xFF)) << 16) | (((uint64_t)(_e)->w[4]) << 24)))
#define dir_set_offset(_e, _o) \
do { \
(_e)->w[0] = (uint16_t)_o; \
(_e)->w[1] = (uint16_t)((((_o) >> 16) & 0xFF) | ((_e)->w[1] & 0xFF00)); \
(_e)->w[4] = (uint16_t)((_o) >> 24); \
} while (0)
#define dir_next(_e) (_e)->w[3]
#define dir_phase(_e) dir_bit(_e, 2, 12)
#define DIR_BLOCK_SHIFT(_i) (3 * (_i))
#define DIR_BLOCK_SIZE(_i) (CACHE_BLOCK_SIZE << DIR_BLOCK_SHIFT(_i))
#define dir_set_prev(_e, _o) (_e)->w[2] = (uint16_t)(_o)
#define dir_set_next(_e, _o) (_e)->w[3] = (uint16_t)(_o)
#define dir_in_seg(_s, _i) ((CacheDirEntry *)(((char *)(_s)) + (SIZEOF_DIR * (_i))))
inline CacheDirEntry *
dir_from_offset(int64_t i, CacheDirEntry *seg)
{
#if DIR_DEPTH < 5
if (!i)
return nullptr;
return dir_in_seg(seg, i);
#else
i = i + ((i - 1) / (DIR_DEPTH - 1));
return dir_in_seg(seg, i);
#endif
}
inline CacheDirEntry *
dir_bucket(int64_t b, CacheDirEntry *seg)
{
return dir_in_seg(seg, b * DIR_DEPTH);
}
inline CacheDirEntry *
next_dir(CacheDirEntry *d, CacheDirEntry *seg)
{
int i = dir_next(d);
return dir_from_offset(i, seg);
}
inline CacheDirEntry *
dir_bucket_row(CacheDirEntry *b, int64_t i)
{
return dir_in_seg(b, i);
}
inline int64_t
dir_to_offset(const CacheDirEntry *d, const CacheDirEntry *seg)
{
#if DIR_DEPTH < 5
return (((char *)d) - ((char *)seg)) / SIZEOF_DIR;
#else
int64_t i = (int64_t)((((char *)d) - ((char *)seg)) / SIZEOF_DIR);
i = i - (i / DIR_DEPTH);
return i;
#endif
}
struct Stripe;
struct Span {
Span(ts::file::path const &path) : _path(path) {}
Errata load();
Errata loadDevice();
bool isEmpty() const;
int header_len = 0;
/// Replace all existing stripes with a single unallocated stripe covering the span.
Errata clear();
/// This is broken and needs to be cleaned up.
void clearPermanently();
ts::Rv<Stripe *> allocStripe(int vol_idx, const CacheStripeBlocks &len);
Errata updateHeader(); ///< Update serialized header and write to disk.
ts::file::path _path; ///< File system location of span.
ats_scoped_fd _fd; ///< Open file descriptor for span.
int _vol_idx = 0; ///< Forced volume.
CacheStoreBlocks _base; ///< Offset to first usable byte.
CacheStoreBlocks _offset; ///< Offset to first content byte.
// The space between _base and _offset is where the span information is stored.
CacheStoreBlocks _len; ///< Total length of span.
CacheStoreBlocks _free_space; ///< Total size of free stripes.
ink_device_geometry _geometry = ink_device_geometry(); ///< Geometry of span.
uint64_t num_usable_blocks = 0; // number of usable blocks for stripes i.e., after subtracting the skip and the disk header.
/// Local copy of serialized header data stored on in the span.
std::unique_ptr<ts::SpanHeader> _header;
/// Live information about stripes.
/// Seeded from @a _header and potentially augmented with direct probing.
std::list<Stripe *> _stripes;
};
/* --------------------------------------------------------------------------------------- */
struct Stripe {
/// Meta data is stored in 4 copies A/B and Header/Footer.
enum Copy { A = 0, B = 1 };
enum { HEAD = 0, FOOT = 1 };
/// Piece wise memory storage for the directory.
struct Chunk {
Bytes _start; ///< Starting offset relative to physical device of span.
Bytes _skip; ///< # of bytes not valid at the start of the first block.
Bytes _clip; ///< # of bytes not valid at the end of the last block.
typedef std::vector<MemSpan<void>> Chain;
Chain _chain; ///< Chain of blocks.
~Chunk();
void append(MemSpan<void> m);
void clear();
};
/// Construct from span header data.
Stripe(Span *span, const Bytes &start, const CacheStoreBlocks &len);
/// Is stripe unallocated?
bool isFree() const;
/** Probe a chunk of memory @a mem for stripe metadata.
@a mem is updated to remove memory that has been probed. If @a
meta is not @c nullptr then it is used for additional cross
checking.
@return @c true if @a mem has valid data, @c false otherwise.
*/
bool probeMeta(MemSpan<void> &mem, StripeMeta const *meta = nullptr);
/// Check a buffer for being valid stripe metadata.
/// @return @c true if valid, @c false otherwise.
static bool validateMeta(StripeMeta const *meta);
/// Load metadata for this stripe.
Errata loadMeta();
Errata loadDir();
int check_loop(int s);
void dir_check();
bool walk_bucket_chain(int s); // returns true if there is a loop
void walk_all_buckets();
/// Initialize the live data from the loaded serialized data.
void updateLiveData(enum Copy c);
Span *_span; ///< Hosting span.
CryptoHash hash_id; /// hash_id
Bytes _start; ///< Offset of first byte of stripe metadata.
Bytes _content; ///< Start of content.
CacheStoreBlocks _len; ///< Length of stripe.
uint8_t _vol_idx = 0; ///< Volume index.
uint8_t _type = 0; ///< Stripe type.
int8_t _idx = -1; ///< Stripe index in span.
int agg_buf_pos = 0;
int64_t _buckets = 0; ///< Number of buckets per segment.
int64_t _segments = 0; ///< Number of segments.
std::string hashText;
/// Meta copies, indexed by A/B then HEAD/FOOT.
StripeMeta _meta[2][2];
/// Locations for the meta data.
CacheStoreBlocks _meta_pos[2][2];
/// Directory.
Chunk _directory;
CacheDirEntry const *dir = nullptr; // the big buffer that will hold the whole directory of stripe header.
uint16_t *freelist = nullptr; // using this freelist instead of the one in StripeMeta.
// This is because the freelist is not being copied to _metap[2][2] correctly.
// need to do something about it .. hmmm :-?
int dir_freelist_length(int s);
inline CacheDirEntry *
vol_dir_segment(int s)
{
return (CacheDirEntry *)(((char *)this->dir) + (s * this->_buckets) * DIR_DEPTH * SIZEOF_DIR);
}
inline CacheDirEntry *
dir_segment(int s)
{
return vol_dir_segment(s);
}
Bytes stripe_offset(CacheDirEntry *e); // offset w.r.t the stripe content
size_t vol_dirlen();
inline int
vol_headerlen()
{
return ROUND_TO_STORE_BLOCK(sizeof(StripeMeta) + sizeof(uint16_t) * (this->_segments - 1));
}
void vol_init_data_internal();
void vol_init_data();
void dir_init_segment(int s);
void dir_free_entry(CacheDirEntry *e, int s);
CacheDirEntry *dir_delete_entry(CacheDirEntry *e, CacheDirEntry *p, int s);
// int dir_bucket_length(CacheDirEntry *b, int s);
int dir_probe(CryptoHash *key, CacheDirEntry *result, CacheDirEntry **last_collision);
bool dir_valid(CacheDirEntry *e);
bool validate_sync_serial();
Errata updateHeaderFooter();
Errata InitializeMeta();
void init_dir();
Errata clear(); // clears striped headers and footers
};
} // namespace ct