turbonfs/inc/util.h (191 lines of code) (raw):
#ifndef __AZNFSC_UTIL_H__
#define __AZNFSC_UTIL_H__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <zlib.h>
#include <string>
#include <regex>
#include <chrono>
#include <random>
#include "log.h"
using namespace std::chrono;
namespace aznfsc {
/**
* Get total RAM size in bytes.
* Note that this is the total RAM and not available RAM (which will be lesser
* and can be much lesser).
* If it cannot find the RAM size, which is extremely unlikely, it returns 0.
*/
uint64_t get_total_ram();
/**
* Set readahead_kb for kernel readahead.
* This sets the kernel readahead value of aznfsc_cfg.readahead_kb iff kernel
* data cache is enabled and user cache is not enabled. We don't want double
* readahead.
*/
void set_kernel_readahead();
/**
* Disable OOM killing for the aznfsclient process.
* Note that we try to set oom_score_adj to the lowest possible value (-1000)
* which should signify "do not oom kill".
*/
void disable_oom_kill();
/**
* Generate a random number in the range [min, max].
*/
static inline
uint64_t random_number(uint64_t min, uint64_t max)
{
static thread_local std::mt19937 gen(
(uint64_t) std::chrono::system_clock::now().time_since_epoch().count());
return min + (gen() % (max - min + 1));
}
static inline
bool is_valid_account(const std::string& account)
{
const std::regex rexpr("^[a-z0-9]{3,24}$");
return std::regex_match(account, rexpr);
}
static inline
bool is_valid_container(const std::string& container)
{
const std::regex rexpr("^[a-z0-9](?!.*--)[a-z0-9-]{1,61}[a-z0-9]$");
return std::regex_match(container, rexpr);
}
static inline
bool is_valid_cloud_suffix(const std::string& cloud_suffix)
{
const std::regex rexpr("^(z[0-9]+.)?(privatelink.)?blob(.preprod)?.core.(windows.net|usgovcloudapi.net|chinacloudapi.cn)$");
return std::regex_match(cloud_suffix, rexpr);
}
static inline
bool is_valid_xprtsec(const std::string& xprtsec)
{
return (xprtsec == "tls" || xprtsec == "none");
}
static inline
bool is_valid_cachedir(const std::string& cachedir)
{
struct stat statbuf;
if (cachedir.empty()) {
AZLogDebug("cachedir is empty");
return false;
}
if (::stat(cachedir.c_str(), &statbuf) != 0) {
AZLogWarn("stat() failed for cachedir {}: {}",
cachedir, strerror(errno));
return false;
}
if (!S_ISDIR(statbuf.st_mode)) {
AZLogWarn("cachedir {} is not a directory", cachedir);
return false;
}
/*
* Creating a probe file with the same mode as the actual backing
* files is the best way to test.
*/
const std::string probe_file = cachedir + "/.probe";
const int fd = ::open(probe_file.c_str(), O_CREAT|O_TRUNC|O_RDWR, 0600);
if (fd < 0) {
AZLogWarn("Failed to create probe file {}, cannot use cachedir {}: {}",
probe_file, cachedir, strerror(errno));
return false;
}
return true;
}
static inline
bool is_valid_lookupcache(const std::string& lookupcache)
{
return (lookupcache == "all" || lookupcache == "none" ||
lookupcache == "pos" || lookupcache == "positive");
}
static inline
bool is_valid_consistency(const std::string& consistency)
{
return (consistency == "solowriter" || consistency == "standardnfs" ||
consistency == "azurempa");
}
static inline
bool is_valid_guid(const std::string& guid)
{
const std::regex rexpr("^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$");
return std::regex_match(guid, rexpr);
}
static inline
bool is_valid_tenantid(const std::string& tenantid)
{
return is_valid_guid(tenantid);
}
static inline
bool is_valid_subscriptionid(const std::string& subscriptionid)
{
return is_valid_guid(subscriptionid);
}
/**
* A valid percentage string is of the form "80%" where the number must be
* >= 0 and <= 100. get_percent_value() will return the integer percentage
* value or -1 if percentstr is not a valid percentage string.
*/
static inline
int get_percent_value(const std::string& percentstr)
{
int percent_value;
char percent_char;
const int ret =
::sscanf(percentstr.c_str(), "%d%c", &percent_value, &percent_char);
if (ret == 2 && percent_char == '%' &&
percent_value >= 0 && percent_value <= 100) {
return percent_value;
}
return -1;
}
static inline
bool is_valid_percentage_string(const std::string& percentstr)
{
return get_percent_value(percentstr) != -1;
}
/**
* Return milliseconds since epoch.
* Use this for timestamping.
*/
static inline
int64_t get_current_msecs()
{
return duration_cast<milliseconds>(
system_clock::now().time_since_epoch()).count();
}
/**
* Return microseconds since epoch.
* Use this for accurate stats.
*/
static inline
int64_t get_current_usecs()
{
return duration_cast<microseconds>(
system_clock::now().time_since_epoch()).count();
}
/**
* Compares a timespec time ts with nfstime3 time nt and returns
* 0 if both represent the same time
* -1 if ts < nt
* 1 if ts > nt
*/
static inline
int compare_timespec_and_nfstime(const struct timespec& ts,
const struct nfstime3& nt)
{
const uint64_t ns1 = ts.tv_sec*1000'000'000ULL + ts.tv_nsec;
const uint64_t ns2 = nt.seconds*1000'000'000ULL + nt.nseconds;
if (ns1 == ns2)
return 0;
else if (ns1 < ns2)
return -1;
else
return 1;
}
static inline
int compare_timespec(const struct timespec& ts1,
const struct timespec& ts2)
{
const uint64_t ns1 = ts1.tv_sec*1000'000'000ULL + ts1.tv_nsec;
const uint64_t ns2 = ts2.tv_sec*1000'000'000ULL + ts2.tv_nsec;
if (ns1 == ns2)
return 0;
else if (ns1 < ns2)
return -1;
else
return 1;
}
static inline
int compare_nfstime(const struct nfstime3& nt1,
const struct nfstime3& nt2)
{
const uint64_t ns1 = nt1.seconds*1000'000'000ULL + nt1.nseconds;
const uint64_t ns2 = nt2.seconds*1000'000'000ULL + nt2.nseconds;
if (ns1 == ns2)
return 0;
else if (ns1 < ns2)
return -1;
else
return 1;
}
static inline
uint32_t calculate_crc32(const unsigned char *buf, int len)
{
return ::crc32(::crc32(0L, Z_NULL, 0), buf, len);
}
static inline
uint32_t calculate_crc32(const struct nfs_fh3& fh)
{
return calculate_crc32((const unsigned char*) fh.data.data_val,
fh.data.data_len);
}
/**
* cookieverf3 -> uint64, conversion.
*/
static inline
uint64_t cv2i(const cookieverf3& cookieverf)
{
return *(uint64_t *)&cookieverf;
}
/**
* Inject error with given probability percentage.
* f.e., pct_prob=0.1 would cause inject_error() to return true for 0.1% of
* the calls, i.e., 1 in 1000.
* Environment variable AZNFSC_INJECT_ERROR_PERCENT can be used to set the
* default value of pct_prob, if caller doesn't pass an explicit value.
*
* Note: Inject errors with caution. Only inject errors which can be fixed by
* retries and do not result in application failures.
*/
bool inject_error(double pct_prob = 0);
}
#endif /* __AZNFSC_UTIL_H__ */