// ----------------------------------------------------------------------
// File: Path.hh
// Author: Andreas-Joachim Peters - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#ifndef __EOSCOMMON_PATH__
#define __EOSCOMMON_PATH__
#include "common/Namespace.hh"
#include "XrdOuc/XrdOucString.hh"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define EOS_COMMON_PATH_VERSION_PREFIX "/.sys.v#."
#define EOS_COMMON_PATH_VERSION_FILE_PREFIX ".sys.v#."
#define EOS_COMMON_PATH_ATOMIC_FILE_PREFIX ".sys.a#."
#define EOS_COMMON_PATH_ATOMIC_FILE_VERSION_PREFIX ".sys.a#.v#"
#define EOS_COMMON_PATH_BACKUP_FILE_PREFIX ".sys.b#."
#define EOS_COMMON_PATH_SQUASH_SUFFIX ".sqsh"
EOSCOMMONNAMESPACE_BEGIN
//------------------------------------------------------------------------------
//! Class providing some comfortable functions on path names
//------------------------------------------------------------------------------
class Path
{
public:
constexpr static uint32_t MAX_LEVELS = 255;
//----------------------------------------------------------------------------
//! Constructor
//----------------------------------------------------------------------------
Path(const char* path = "")
{
Init(path);
}
Path(std::string path)
{
Init(path.c_str());
}
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
~Path() = default;
//----------------------------------------------------------------------------
//! Assignment
//----------------------------------------------------------------------------
Path& operator=(std::string other)
{
this->Init(other.c_str());
return *this;
}
//----------------------------------------------------------------------------
//! Return basename/filename
//----------------------------------------------------------------------------
const char*
GetName()
{
return lastPath.c_str();
}
//----------------------------------------------------------------------------
//! Return if file is an atomic filename
//----------------------------------------------------------------------------
bool
isAtomicFile() {
return lastPath.beginswith(EOS_COMMON_PATH_ATOMIC_FILE_PREFIX);
}
//----------------------------------------------------------------------------
//! Return if path belongs to versioning
//----------------------------------------------------------------------------
bool
isVersionPath() {
return (fullPath.find(EOS_COMMON_PATH_VERSION_FILE_PREFIX)!= STR_NPOS);
}
//----------------------------------------------------------------------------
//! Return if file is a sqaush package file
//----------------------------------------------------------------------------
bool
isSquashFile() {
return (lastPath.beginswith(".") && lastPath.endswith(EOS_COMMON_PATH_SQUASH_SUFFIX));
}
//----------------------------------------------------------------------------
//! Return full path
//----------------------------------------------------------------------------
const char*
GetPath()
{
return fullPath.c_str();
}
//----------------------------------------------------------------------------
//! Return path of the parent directory
//----------------------------------------------------------------------------
const char*
GetParentPath()
{
return parentPath.c_str();
}
//----------------------------------------------------------------------------
//! Return constracted path replacing all '/' with '::'
//----------------------------------------------------------------------------
std::string
GetContractedPath()
{
XrdOucString contractedpath = GetPath();
while (contractedpath.replace("/", "..")) {}
return contractedpath.c_str();
}
//----------------------------------------------------------------------------
//! Return version directory path
//----------------------------------------------------------------------------
const char*
GetVersionDirectory()
{
versionDir = GetParentPath();
versionDir += EOS_COMMON_PATH_VERSION_PREFIX;
versionDir += GetName();
versionDir += "/";
while (versionDir.replace("//", "/")) {
}
return versionDir.c_str();
}
//----------------------------------------------------------------------------
//! Return full path
//----------------------------------------------------------------------------
XrdOucString&
GetFullPath()
{
return fullPath;
}
//----------------------------------------------------------------------------
//! Return atomic path
//----------------------------------------------------------------------------
const char*
GetAtomicPath(bool versioning, XrdOucString externuuid = "")
{
if (atomicPath.length()) {
return atomicPath.c_str();
} else {
return MakeAtomicPath(versioning, externuuid);
}
}
//----------------------------------------------------------------------------
//! Return a unique atomic version of that path
//----------------------------------------------------------------------------
const char* MakeAtomicPath(bool versioning, XrdOucString externuuid = "")
{
// create from / => /.
char suuid[40];
uuid_t uuid;
uuid_generate_time(uuid);
uuid_unparse(uuid, suuid);
// skip modification of already atomic paths
if (!lastPath.beginswith(EOS_COMMON_PATH_ATOMIC_FILE_PREFIX)) {
atomicPath = GetParentPath();
if (!versioning) {
atomicPath += EOS_COMMON_PATH_ATOMIC_FILE_PREFIX;
} else {
atomicPath += EOS_COMMON_PATH_ATOMIC_FILE_VERSION_PREFIX;
}
atomicPath += GetName();
atomicPath += ".";
// for chunk paths we have to use the same UUID for all chunks
if (!externuuid.length()) {
atomicPath += suuid;
} else {
atomicPath += externuuid;
}
} else {
atomicPath = GetPath();
}
return atomicPath.c_str();
}
//----------------------------------------------------------------------------
//! Decode an atomic path
//----------------------------------------------------------------------------
const char* DecodeAtomicPath(bool& isVersioning)
{
// create from / .
// => /
if ((lastPath.beginswith(EOS_COMMON_PATH_ATOMIC_FILE_PREFIX)) &&
(lastPath.length() > 37) &&
(lastPath[lastPath.length() - 37] == '.')) {
atomicPath = fullPath;
lastPath.erase(lastPath.length() - 37);
if (lastPath.beginswith(EOS_COMMON_PATH_ATOMIC_FILE_VERSION_PREFIX)) {
lastPath.erase(0, strlen(EOS_COMMON_PATH_ATOMIC_FILE_VERSION_PREFIX));
isVersioning = true;
} else {
lastPath.erase(0, strlen(EOS_COMMON_PATH_ATOMIC_FILE_PREFIX));
isVersioning = false;
}
fullPath = parentPath + lastPath;
}
return fullPath.c_str();
}
//----------------------------------------------------------------------------
//! Return sub path with depth i (0 is / 1 is /eos/ aso....)
//----------------------------------------------------------------------------
const char*
GetSubPath(unsigned int i)
{
if (i < subPath.size()) {
return subPath[i].c_str();
} else {
return 0;
}
}
//----------------------------------------------------------------------------
//! Return number of sub paths stored
//----------------------------------------------------------------------------
unsigned int
GetSubPathSize()
{
return subPath.size();
}
//----------------------------------------------------------------------------
//! Initialization
//----------------------------------------------------------------------------
void
Init(const char* path)
{
fullPath = path;
while (fullPath.replace("//", "/")) {}
parentPath = "/";
lastPath = "";
if ((fullPath == "/") ||
(fullPath == "/.") ||
(fullPath == "/..") ||
(fullPath == "/./") ||
(fullPath == "/../")) {
fullPath = "/";
return;
}
if (fullPath.endswith('/')) {
fullPath.erase(fullPath.length() - 1);
}
// remove /.$
if (fullPath.endswith("/.")) {
fullPath.erase(fullPath.length() - 2);
}
// recompute /..$
if (fullPath.endswith("/..")) {
fullPath += "/";
}
if (!fullPath.beginswith("/")) {
lastPath = fullPath;
return;
}
int bppos;
// convert /./
while ((bppos = fullPath.find("/./")) != STR_NPOS) {
fullPath.erase(bppos, 2);
}
// convert /..
while ((bppos = fullPath.find("/../")) != STR_NPOS) {
// Erase beginning /../
if (bppos == 0) {
fullPath.erase(0, 3);
continue;
}
int spos = fullPath.rfind("/", bppos - 1);
if (spos != STR_NPOS) {
fullPath.erase(bppos, 4);
fullPath.erase(spos + 1, bppos - spos - 1);
} else {
// Should not reach this as there will be
// at least the starting '/'
fullPath = "/";
break;
}
}
if (!fullPath.length()) {
fullPath = "/";
}
int lastpos = 0;
int pos = 0;
do {
pos = fullPath.find("/", pos);
std::string subpath;
if (pos != STR_NPOS) {
subpath.assign(fullPath.c_str(), pos + 1);
subPath.push_back(subpath);
lastpos = pos;
pos++;
}
} while (pos != STR_NPOS);
parentPath.assign(fullPath, 0, lastpos);
lastPath.assign(fullPath, lastpos + 1);
}
//----------------------------------------------------------------------------
//! Convenience function to auto-create all needed parent paths for this
//! path object with mode
//----------------------------------------------------------------------------
bool
MakeParentPath(mode_t mode)
{
int retc = 0;
struct stat buf;
if (stat(GetParentPath(), &buf)) {
for (int i = GetSubPathSize() - 1; i >= 0; i--) {
// go backwards until the directory exists
if (!stat(GetSubPath(i), &buf)) {
// this exists
for (int j = i + 1; j < (int) GetSubPathSize(); j++) {
retc |= (mkdir(GetSubPath(j), mode) ? ((errno == EEXIST) ? 0 : -1) : 0);
}
break;
}
}
}
if (retc) {
return false;
}
return true;
}
bool Globbing() {
std::string name=lastPath.c_str();
size_t index = 0;
while ((index = name.find('*', index)) != std::string::npos) {
if ( (index==0) || ((index>0) && name[index-1] != '\\')) {
return true;
}
index++;
}
index = 0;
while ((index = name.find('?', index)) != std::string::npos) {
if ( (index==0) || ((index>0) && name[index-1] != '\\')) {
return true;
}
index++;
}
index = 0;
while ((index = name.find('[', index)) != std::string::npos) {
if ( (index==0) || ((index>0) && name[index-1] != '\\')) {
return true;
}
index++;
}
index = 0;
while ((index = name.find('{', index)) != std::string::npos) {
if ( (index==0) || ((index>0) && name[index-1] != '\\')) {
return true;
}
index++;
}
return false;
}
// check if path points to a version
static bool IsVersion(std::string& path) {
return (path.find(EOS_COMMON_PATH_VERSION_PREFIX) != std::string::npos);
}
// get the shared prefix for two paths
static std::string Overlap(const char* a, const char* b) {
eos::common::Path apath(a);
eos::common::Path bpath(b);
std::string ol="/";
for ( size_t i = 0 ; i< apath.GetSubPathSize(); ++i) {
std::string ta = apath.GetSubPath(i);
const char* pb = bpath.GetSubPath(i);
std::string tb = pb?pb:"";
if (ta == tb) {
ol = ta;
} else {
return ol;
}
}
if ( std::string(apath.GetPath()) == std::string(bpath.GetPath()) ) {
return apath.GetPath();
}
return ol;
}
protected:
XrdOucString fullPath; //< the full path stored
XrdOucString parentPath; //< path of the parent directory
XrdOucString lastPath; //< the base name/file name
//! temporary version of a path e.g. basename => .basename.
XrdOucString atomicPath;
XrdOucString versionDir; //< directory name storing versions for a file path
std::vector subPath; //< a vector with all partial sub-path
};
EOSCOMMONNAMESPACE_END
#endif