Changed codes for issue: 27(+)

1) Feature Request: Compatability with other S3FS clients(Issue: 27)
    Rechanges source code.

2) For other S3 clients
    Supports the directory which is no objects.
    If there is a object which has "/" charactor(ex. "<bucket>/dir/file"), the directory("dir") object is no object.
    Exsample, you can upload the object which name is "s3://bucket/dir/file" by the s3cmd.
    Then the "dir" is not object in bucket("dir").
    This s3fs codes understands this case.



git-svn-id: http://s3fs.googlecode.com/svn/trunk@414 df820570-a93a-0410-bd06-b72b767a4274
This commit is contained in:
ggtakec@gmail.com 2013-04-29 14:31:10 +00:00
parent 36447a23eb
commit b973eaae44
5 changed files with 330 additions and 214 deletions

View File

@ -101,7 +101,7 @@ time_t StatCache::UnsetExpireTime(void)
return old; return old;
} }
bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag) bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag, bool* pisforce)
{ {
bool is_delete_cache = false; bool is_delete_cache = false;
string strpath = key; string strpath = key;
@ -144,7 +144,9 @@ bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool ove
meta->clear(); meta->clear();
(*meta) = (*iter).second.meta; (*meta) = (*iter).second.meta;
} }
if(pisforce != NULL){
(*pisforce) = (*iter).second.isforce;
}
(*iter).second.hit_count++; (*iter).second.hit_count++;
pthread_mutex_unlock(&StatCache::stat_cache_lock); pthread_mutex_unlock(&StatCache::stat_cache_lock);
return true; return true;
@ -163,7 +165,7 @@ bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool ove
return false; return false;
} }
bool StatCache::AddStat(std::string& key, headers_t& meta) bool StatCache::AddStat(std::string& key, headers_t& meta, bool forcedir)
{ {
if(CacheSize< 1){ if(CacheSize< 1){
return true; return true;
@ -177,7 +179,7 @@ bool StatCache::AddStat(std::string& key, headers_t& meta)
} }
struct stat st; struct stat st;
if(!convert_header_to_stat(key.c_str(), meta, &st)){ if(!convert_header_to_stat(key.c_str(), meta, &st, forcedir)){
return false; return false;
} }
@ -185,6 +187,7 @@ bool StatCache::AddStat(std::string& key, headers_t& meta)
stat_cache[key].stbuf = st; stat_cache[key].stbuf = st;
stat_cache[key].hit_count = 0; stat_cache[key].hit_count = 0;
stat_cache[key].cache_date = time(NULL); // Set time. stat_cache[key].cache_date = time(NULL); // Set time.
stat_cache[key].isforce = forcedir;
//copy only some keys //copy only some keys
for (headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter) { for (headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter) {
@ -272,7 +275,7 @@ bool StatCache::DelStat(const char* key)
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Functions // Functions
//------------------------------------------------------------------- //-------------------------------------------------------------------
bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst) bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst, bool forcedir)
{ {
headers_t::const_iterator iter; headers_t::const_iterator iter;
@ -295,16 +298,20 @@ bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst)
if(iter != meta.end()){ if(iter != meta.end()){
strConType = (*iter).second; strConType = (*iter).second;
} }
if(strConType == "application/x-directory"){ if(forcedir){
pst->st_mode |= S_IFDIR; pst->st_mode |= S_IFDIR;
}else if(0 < strlen(path) && '/' == path[strlen(path) - 1]){ }else{
if(strConType == "binary/octet-stream" || strConType == "application/octet-stream"){ if(strConType == "application/x-directory"){
pst->st_mode |= S_IFDIR; pst->st_mode |= S_IFDIR;
}else if(0 < strlen(path) && '/' == path[strlen(path) - 1]){
if(strConType == "binary/octet-stream" || strConType == "application/octet-stream"){
pst->st_mode |= S_IFDIR;
}else{
pst->st_mode |= S_IFREG;
}
}else{ }else{
pst->st_mode |= S_IFREG; pst->st_mode |= S_IFREG;
} }
}else{
pst->st_mode |= S_IFREG;
} }
// blocks // blocks

View File

@ -11,8 +11,9 @@ struct stat_cache_entry {
unsigned long hit_count; unsigned long hit_count;
time_t cache_date; time_t cache_date;
headers_t meta; headers_t meta;
bool isforce;
stat_cache_entry() : hit_count(0), cache_date(0) { stat_cache_entry() : hit_count(0), cache_date(0), isforce(false) {
memset(&stbuf, 0, sizeof(struct stat)); memset(&stbuf, 0, sizeof(struct stat));
meta.clear(); meta.clear();
} }
@ -34,7 +35,7 @@ class StatCache
unsigned long CacheSize; unsigned long CacheSize;
private: private:
bool GetStat(std::string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag); bool GetStat(std::string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag, bool* pisforce);
// Truncate stat cache // Truncate stat cache
bool TruncateCache(void); bool TruncateCache(void);
@ -55,24 +56,24 @@ class StatCache
time_t UnsetExpireTime(void); time_t UnsetExpireTime(void);
// Get stat cache // Get stat cache
bool GetStat(std::string& key, struct stat* pst, headers_t* meta, bool overcheck = true) { bool GetStat(std::string& key, struct stat* pst, headers_t* meta, bool overcheck = true, bool* pisforce = NULL) {
return GetStat(key, pst, meta, overcheck, NULL); return GetStat(key, pst, meta, overcheck, NULL, pisforce);
} }
bool GetStat(std::string& key, struct stat* pst, bool overcheck = true) { bool GetStat(std::string& key, struct stat* pst, bool overcheck = true) {
return GetStat(key, pst, NULL, overcheck, NULL); return GetStat(key, pst, NULL, overcheck, NULL, NULL);
} }
bool GetStat(std::string& key, headers_t* meta, bool overcheck = true) { bool GetStat(std::string& key, headers_t* meta, bool overcheck = true) {
return GetStat(key, NULL, meta, overcheck, NULL); return GetStat(key, NULL, meta, overcheck, NULL, NULL);
} }
bool HasStat(std::string& key, bool overcheck = true) { bool HasStat(std::string& key, bool overcheck = true) {
return GetStat(key, NULL, NULL, overcheck, NULL); return GetStat(key, NULL, NULL, overcheck, NULL, NULL);
} }
bool HasStat(std::string& key, const char* etag, bool overcheck = true) { bool HasStat(std::string& key, const char* etag, bool overcheck = true) {
return GetStat(key, NULL, NULL, overcheck, etag); return GetStat(key, NULL, NULL, overcheck, etag, NULL);
} }
// Add stat cache // Add stat cache
bool AddStat(std::string& key, headers_t& meta); bool AddStat(std::string& key, headers_t& meta, bool forcedir = false);
// Delete stat cache // Delete stat cache
bool DelStat(const char* key); bool DelStat(const char* key);
@ -84,6 +85,6 @@ class StatCache
// //
// Functions // Functions
// //
bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst); bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst, bool forcedir = false);
#endif // S3FS_CACHE_H_ #endif // S3FS_CACHE_H_

View File

@ -56,6 +56,10 @@ using namespace std;
#define DIRTYPE_NEW 0 #define DIRTYPE_NEW 0
#define DIRTYPE_OLD 1 #define DIRTYPE_OLD 1
#define DIRTYPE_FOLDER 2 #define DIRTYPE_FOLDER 2
#define DIRTYPE_NOOBJ 3
#define IS_REPLACEDIR(type) (DIRTYPE_OLD == type || DIRTYPE_FOLDER == type || DIRTYPE_NOOBJ == type)
#define IS_RMTYPEDIR(type) (DIRTYPE_OLD == type || DIRTYPE_FOLDER == type)
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Typedef // Typedef
@ -120,12 +124,13 @@ static s3fs_pathtofd_t s3fs_pathtofd; // path -> fd
// Static functions : prototype // Static functions : prototype
//------------------------------------------------------------------- //-------------------------------------------------------------------
static bool is_special_name_folder_object(const char *path); static bool is_special_name_folder_object(const char *path);
static int chk_dir_object_type(const char *path, string& strpath, string& strdelpath, headers_t* pmeta = NULL, int* pDirType = NULL); static int chk_dir_object_type(const char *path, string& newpath, string& nowpath, string& nowcache, headers_t* pmeta = NULL, int* pDirType = NULL);
static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t* pmeta = NULL, bool overcheck = true); static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t* pmeta = NULL, bool overcheck = true, bool* pisforce = NULL);
static int check_object_access(const char *path, int mask, struct stat* pstbuf); static int check_object_access(const char *path, int mask, struct stat* pstbuf);
static int check_object_owner(const char *path, struct stat* pstbuf); static int check_object_owner(const char *path, struct stat* pstbuf);
static int check_parent_object_access(const char *path, int mask); static int check_parent_object_access(const char *path, int mask);
static int list_bucket(const char *path, S3ObjList& head, const char* delimiter); static int list_bucket(const char *path, S3ObjList& head, const char* delimiter);
static int directory_empty(const char *path);
static bool is_truncated(const char *xml); static bool is_truncated(const char *xml);
static int append_objects_from_xml_ex(const char* path, xmlDocPtr doc, xmlXPathContextPtr ctx, static int append_objects_from_xml_ex(const char* path, xmlDocPtr doc, xmlXPathContextPtr ctx,
const char* ex_contents, const char* ex_key, const char* ex_etag, int isCPrefix, S3ObjList& head); const char* ex_contents, const char* ex_key, const char* ex_etag, int isCPrefix, S3ObjList& head);
@ -200,69 +205,92 @@ static bool is_special_name_folder_object(const char *path)
return true; return true;
} }
static int chk_dir_object_type(const char *path, string& strpath, string& strdelpath, headers_t* pmeta, int* pDirType) // [Detail]
// This function is complicated for checking directory object type.
// Arguments is used for deleting cache/path, and remake directory object.
// Please see the codes which calls this function.
//
// path: target path
// newpath: should be object path for making/putting/getting after checking
// nowpath: now object name for deleting after checking
// nowcache: now cache path for deleting after checking
// pmeta: headers map
// pDirType: directory object type
//
static int chk_dir_object_type(const char *path, string& newpath, string& nowpath, string& nowcache, headers_t* pmeta, int* pDirType)
{ {
int result = -1; int TypeTmp;
strpath = path; int result = -1;
if(pDirType){ bool isforce = false;
*pDirType= DIRTYPE_NEW; int* pType = pDirType ? pDirType : &TypeTmp;
// Normalize new path.
newpath = path;
if('/' != newpath[newpath.length() - 1]){
string::size_type Pos;
if(string::npos != (Pos = newpath.find("_$folder$", 0))){
newpath = newpath.substr(0, Pos);
}
newpath += "/";
} }
// At first, if not have "/", check path with "/". // Alwayes check "dir/" at first.
if('/' != strpath[strpath.length() - 1]){ if(0 == (result = get_object_attribute(newpath.c_str(), NULL, pmeta, false, &isforce))){
strpath += "/"; // Found "dir/" cache --> Check for "_$folder$", "no dir object"
strdelpath = strpath; nowcache = newpath;
result = get_object_attribute(strpath.c_str(), NULL, pmeta, false); if(is_special_name_folder_object(newpath.c_str())){
} // "_$folder$" type.
if(0 == result){ (*pType) = DIRTYPE_FOLDER;
// Found "dir/" --> Check for "_$folder$" nowpath = newpath.substr(0, newpath.length() - 1) + "_$folder$"; // cut and add
if(is_special_name_folder_object(strpath.c_str())){ }else if(isforce){
if(0 < strpath.length() && '/' == strpath[strpath.length() - 1]){ // "no dir object" type.
strpath = strpath.substr(0, strpath.length() - 1); (*pType) = DIRTYPE_NOOBJ;
} nowpath = "";
strdelpath = strpath + "_$folder$"; }else{
if(pDirType){ nowpath = path;
*pDirType = DIRTYPE_FOLDER; if(0 < nowpath.length() && '/' == nowpath[nowpath.length() - 1]){
// "dir/" type
(*pType) = DIRTYPE_NEW;
}else{
// "dir" type
(*pType) = DIRTYPE_OLD;
} }
} }
}else{ }else{
// Need to chack old type directory("dir"). // Check "dir"
strpath = path; nowpath = newpath.substr(0, newpath.length() - 1);
strdelpath = strpath; if(0 == (result = get_object_attribute(nowpath.c_str(), NULL, pmeta, false, &isforce))){
if(0 == (result = get_object_attribute(strpath.c_str(), NULL, pmeta, false))){ // Found "dir" cache --> this case is only "dir" type.
if(0 < strpath.length() && '/' != strpath[strpath.length() - 1]){ // Because, if object is "_$folder$" or "no dir object", the cache is "dir/" type.
if(pDirType){ // (But "no dir objet" is checked here.)
*pDirType = DIRTYPE_OLD; nowcache = nowpath;
} if(isforce){
(*pType) = DIRTYPE_NOOBJ;
nowpath = "";
}else{ }else{
// Found "dir/" --> Check for "_$folder$" (*pType) = DIRTYPE_OLD;
if(is_special_name_folder_object(strpath.c_str())){
strpath = strpath.substr(0, strpath.length() - 1);
strdelpath = strpath + "_$folder$";
if(pDirType){
*pDirType = DIRTYPE_FOLDER;
}
}
} }
}else{ }else{
// Check for "_$folder$" // Not found cache --> check for "_$folder$" and "no dir object".
if(is_special_name_folder_object(strpath.c_str())){ nowcache = ""; // This case is no cahce.
if(0 < strpath.length() && '/' == strpath[strpath.length() - 1]){ nowpath += "_$folder$";
strpath = strpath.substr(0, strpath.length() - 1); if(is_special_name_folder_object(nowpath.c_str())){
} // "_$folder$" type.
strdelpath = strpath + "_$folder$"; (*pType) = DIRTYPE_FOLDER;
if(pDirType){ result = 0; // result is OK.
*pDirType = DIRTYPE_FOLDER; }else if(-ENOTEMPTY == directory_empty(newpath.c_str())){
} // "no dir object" type.
result = get_object_attribute(strpath.c_str(), NULL, pmeta, false); (*pType) = DIRTYPE_NOOBJ;
nowpath = ""; // now path.
result = 0; // result is OK.
}else{
// Error: Unknown type.
(*pType) = DIRTYPE_UNKNOWN;
newpath = "";
nowpath = "";
} }
} }
} }
if(0 != result){
if(pDirType){
*pDirType = DIRTYPE_UNKNOWN;
}
}
return result; return result;
} }
@ -270,7 +298,7 @@ static int chk_dir_object_type(const char *path, string& strpath, string& strdel
// Get object attributes with stat cache. // Get object attributes with stat cache.
// This function is base for s3fs_getattr(). // This function is base for s3fs_getattr().
// //
static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t* pmeta, bool overcheck) static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t* pmeta, bool overcheck, bool* pisforce)
{ {
int result = -1; int result = -1;
struct stat tmpstbuf; struct stat tmpstbuf;
@ -280,6 +308,7 @@ static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t
string strpath; string strpath;
string s3_realpath; string s3_realpath;
string::size_type Pos; string::size_type Pos;
bool forcedir = false;
//FGPRINT(" get_object_attribute[path=%s]\n", path); //FGPRINT(" get_object_attribute[path=%s]\n", path);
@ -299,7 +328,10 @@ static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t
strpath += "/"; strpath += "/";
} }
} }
if(StatCache::getStatCacheData()->GetStat(strpath, pstat, pheader, overcheck)){ if(pisforce){
(*pisforce) = false;
}
if(StatCache::getStatCacheData()->GetStat(strpath, pstat, pheader, overcheck, pisforce)){
return 0; return 0;
} }
@ -319,15 +351,32 @@ static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t
strpath = path; strpath = path;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
if(0 != (result = curl_get_headers(s3_realpath.c_str(), (*pheader)))){ if(0 != (result = curl_get_headers(s3_realpath.c_str(), (*pheader)))){
if(overcheck && string::npos == strpath.find("_$folder$", 0)){ // Not found --> check( if overcheck )
// check for s3fox etc if(overcheck){
strpath += "_$folder$"; if(string::npos == strpath.find("_$folder$", 0)){
s3_realpath = get_realpath(strpath.c_str()); // path doesn't have "_$folder$" --> check for s3fox etc
if(0 != (result = curl_get_headers(s3_realpath.c_str(), (*pheader)))){ strpath += "_$folder$";
return result; s3_realpath = get_realpath(strpath.c_str());
result = curl_get_headers(s3_realpath.c_str(), (*pheader));
}
if(0 == result){
// found "_$folder$" object.
strpath = path; // reset original
strpath += "/";
}else{
// path does not have "_$folder$" --> check "no dir obejct".
if(-ENOTEMPTY == directory_empty(path)){
// found "no dir obejct".
forcedir = true;
strpath = path; // reset original
strpath += "/";
if(pisforce){
(*pisforce) = true;
}
}else{
return result;
}
} }
strpath = path; // reset original
strpath += "/";
}else{ }else{
return result; return result;
} }
@ -342,11 +391,11 @@ static int get_object_attribute(const char *path, struct stat *pstbuf, headers_t
} }
// add into stat cache // add into stat cache
if(!StatCache::getStatCacheData()->AddStat(strpath, (*pheader))){ if(!StatCache::getStatCacheData()->AddStat(strpath, (*pheader), forcedir)){
FGPRINT(" get_object_attribute: failed adding stat cache [path=%s]\n", strpath.c_str()); FGPRINT(" get_object_attribute: failed adding stat cache [path=%s]\n", strpath.c_str());
return -ENOENT; return -ENOENT;
} }
if(!StatCache::getStatCacheData()->GetStat(strpath, pstat, pheader, overcheck)){ if(!StatCache::getStatCacheData()->GetStat(strpath, pstat, pheader, overcheck, pisforce)){
FGPRINT(" get_object_attribute: failed getting added stat cache [path=%s]\n", strpath.c_str()); FGPRINT(" get_object_attribute: failed getting added stat cache [path=%s]\n", strpath.c_str());
return -ENOENT; return -ENOENT;
} }
@ -1821,6 +1870,8 @@ static int s3fs_rmdir(const char *path)
StatCache::getStatCacheData()->DelStat(strpath.c_str()); StatCache::getStatCacheData()->DelStat(strpath.c_str());
} }
} }
// If there is no "dir" and "dir/" object(this case is made by s3cmd/s3sync),
// the cache key is "dir/". So we get error only onece(delete "dir/").
// check for "_$folder$" object. // check for "_$folder$" object.
// This processing is necessary for other S3 clients compatibility. // This processing is necessary for other S3 clients compatibility.
@ -2065,13 +2116,17 @@ static int clone_directory_object(const char *from, const char *to)
return result; return result;
} }
static int rename_directory(const char *from, const char *to) { static int rename_directory(const char *from, const char *to)
{
S3ObjList head; S3ObjList head;
s3obj_list_t headlist; s3obj_list_t headlist;
string strfrom = from ? from : ""; // from is without "/". string strfrom = from ? from : ""; // from is without "/".
string strto = to ? to : ""; // to is without "/" too. string strto = to ? to : ""; // to is without "/" too.
string basepath = strfrom + "/"; string basepath = strfrom + "/";
string strdelpath; string newpath; // should be from name(not used)
string nowcache; // now cache path(not used)
int DirType;
bool normdir;
MVNODE* mn_head = NULL; MVNODE* mn_head = NULL;
MVNODE* mn_tail = NULL; MVNODE* mn_tail = NULL;
MVNODE* mn_cur; MVNODE* mn_cur;
@ -2086,12 +2141,18 @@ static int rename_directory(const char *from, const char *to) {
// Initiate and Add base directory into MVNODE struct. // Initiate and Add base directory into MVNODE struct.
// //
strto += "/"; strto += "/";
if(0 == chk_dir_object_type(from, strfrom, strdelpath)){ if(0 == chk_dir_object_type(from, newpath, strfrom, nowcache, NULL, &DirType) && DIRTYPE_UNKNOWN != DirType){
if(NULL == (add_mvnode(&mn_head, &mn_tail, strdelpath.c_str(), strto.c_str(), true))){ if(DIRTYPE_NOOBJ != DirType){
normdir = false;
}else{
normdir = true;
strfrom = from; // from directory is not removed, but from directory attr is needed.
}
if(NULL == (add_mvnode(&mn_head, &mn_tail, strfrom.c_str(), strto.c_str(), true, normdir))){
return -ENOMEM; return -ENOMEM;
} }
}else{ }else{
// No base directory: maybe a case of made no directory by s3cmd. // Something wrong about "from" directory.
} }
// //
@ -2103,8 +2164,9 @@ static int rename_directory(const char *from, const char *to) {
FGPRINT(" rename_directory list_bucket returns error.\n"); FGPRINT(" rename_directory list_bucket returns error.\n");
return result; return result;
} }
head.GetNameList(headlist); // get name without "/".
S3ObjList::MakeHierarchizedList(headlist, false); // add hierarchized dir.
head.GetNameList(headlist); // get name without "/".
s3obj_list_t::const_iterator liter; s3obj_list_t::const_iterator liter;
for(liter = headlist.begin(); headlist.end() != liter; liter++){ for(liter = headlist.begin(); headlist.end() != liter; liter++){
// make "from" and "to" object name. // make "from" and "to" object name.
@ -2120,17 +2182,23 @@ static int rename_directory(const char *from, const char *to) {
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
is_dir = true; is_dir = true;
if(0 != chk_dir_object_type(from_name.c_str(), from_name, strdelpath)){ if(0 != chk_dir_object_type(from_name.c_str(), newpath, from_name, nowcache, NULL, &DirType) || DIRTYPE_UNKNOWN == DirType){
FGPRINT(" rename_directory - failed to get %s object directory type.\n", from_name.c_str()); FGPRINT(" rename_directory - failed to get %s%s object directory type.\n", basepath.c_str(), (*liter).c_str());
continue; continue;
} }
if(DIRTYPE_NOOBJ != DirType){
normdir = false;
}else{
normdir = true;
from_name = basepath + (*liter); // from directory is not removed, but from directory attr is needed.
}
}else{ }else{
is_dir = false; is_dir = false;
strdelpath = from_name; normdir = false;
} }
// push this one onto the stack // push this one onto the stack
if(NULL == add_mvnode(&mn_head, &mn_tail, strdelpath.c_str(), to_name.c_str(), is_dir)){ if(NULL == add_mvnode(&mn_head, &mn_tail, from_name.c_str(), to_name.c_str(), is_dir, normdir)){
return -ENOMEM; return -ENOMEM;
} }
} }
@ -2140,7 +2208,7 @@ static int rename_directory(const char *from, const char *to) {
// //
// rename directory objects. // rename directory objects.
for(mn_cur = mn_head; mn_cur; mn_cur = mn_cur->next){ for(mn_cur = mn_head; mn_cur; mn_cur = mn_cur->next){
if(mn_cur->is_dir){ if(mn_cur->is_dir && mn_cur->old_path && '\0' != mn_cur->old_path[0]){
if(0 != (result = clone_directory_object(mn_cur->old_path, mn_cur->new_path))){ if(0 != (result = clone_directory_object(mn_cur->old_path, mn_cur->new_path))){
FGPRINT(" rename_directory - failed(%d) to rename %s directory object to %s.\n", result, mn_cur->old_path, mn_cur->new_path); FGPRINT(" rename_directory - failed(%d) to rename %s directory object to %s.\n", result, mn_cur->old_path, mn_cur->new_path);
SYSLOGERR("clone_directory_object returned an error(%d)", result); SYSLOGERR("clone_directory_object returned an error(%d)", result);
@ -2170,12 +2238,17 @@ static int rename_directory(const char *from, const char *to) {
// Iterate over old the directories, bottoms up and remove // Iterate over old the directories, bottoms up and remove
for(mn_cur = mn_tail; mn_cur; mn_cur = mn_cur->prev){ for(mn_cur = mn_tail; mn_cur; mn_cur = mn_cur->prev){
if(mn_cur->is_dir){ if(mn_cur->is_dir && mn_cur->old_path && '\0' != mn_cur->old_path[0]){
if(0 != (result = s3fs_rmdir(mn_cur->old_path))){ if(!(mn_cur->is_normdir)){
FGPRINT(" rename_directory - failed(%d) to remove %s directory object.\n", result, mn_cur->old_path); if(0 != (result = s3fs_rmdir(mn_cur->old_path))){
SYSLOGERR("s3fs_rmdir returned an error(%d)", result); FGPRINT(" rename_directory - failed(%d) to remove %s directory object.\n", result, mn_cur->old_path);
free_mvnodes(mn_head); SYSLOGERR("s3fs_rmdir returned an error(%d)", result);
return -EIO; free_mvnodes(mn_head);
return -EIO;
}
}else{
// cache clear.
StatCache::getStatCacheData()->DelStat(mn_cur->old_path);
} }
} }
} }
@ -2229,7 +2302,8 @@ static int s3fs_chmod(const char *path, mode_t mode)
int result; int result;
string s3_realpath; string s3_realpath;
string strpath; string strpath;
string strdel; string newpath;
string nowcache;
headers_t meta; headers_t meta;
struct stat stbuf; struct stat stbuf;
int nDirType = DIRTYPE_UNKNOWN; int nDirType = DIRTYPE_UNKNOWN;
@ -2244,11 +2318,11 @@ static int s3fs_chmod(const char *path, mode_t mode)
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
result = chk_dir_object_type(path, strpath, strdel, &meta, &nDirType); result = chk_dir_object_type(path, newpath, strpath, nowcache, &meta, &nDirType);
s3_realpath = get_realpath(strdel.c_str()); s3_realpath = get_realpath(strpath.c_str());
}else{ }else{
strpath = path; strpath = path;
strdel = strpath; nowcache = strpath;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
result = get_object_attribute(strpath.c_str(), NULL, &meta); result = get_object_attribute(strpath.c_str(), NULL, &meta);
} }
@ -2256,21 +2330,22 @@ static int s3fs_chmod(const char *path, mode_t mode)
return result; return result;
} }
if(S_ISDIR(stbuf.st_mode) && DIRTYPE_NEW != nDirType){ if(S_ISDIR(stbuf.st_mode) && IS_REPLACEDIR(nDirType)){
// directory object of old version // Should rebuild directory object(except new type)
// Need to remove old dir("dir") and make new dir("dir/") // Need to remove old dir("dir" etc) and make new dir("dir/")
// At first, remove directory old object // At first, remove directory old object
if(0 != (result = curl_delete(s3_realpath.c_str()))){ if(IS_RMTYPEDIR(nDirType)){
return result; if(0 != (result = curl_delete(s3_realpath.c_str()))){
return result;
}
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
// Make new directory object("dir/") // Make new directory object("dir/")
if(0 != (result = create_directory_object(strpath.c_str(), mode, stbuf.st_mtime, stbuf.st_uid, stbuf.st_gid))){ if(0 != (result = create_directory_object(newpath.c_str(), mode, stbuf.st_mtime, stbuf.st_uid, stbuf.st_gid))){
return result; return result;
} }
}else{ }else{
// normal object or directory object of newer version // normal object or directory object of newer version
meta["x-amz-meta-mode"] = str(mode); meta["x-amz-meta-mode"] = str(mode);
@ -2280,7 +2355,7 @@ static int s3fs_chmod(const char *path, mode_t mode)
if(put_headers(strpath.c_str(), meta) != 0){ if(put_headers(strpath.c_str(), meta) != 0){
return -EIO; return -EIO;
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
} }
return 0; return 0;
@ -2290,7 +2365,8 @@ static int s3fs_chmod_nocopy(const char *path, mode_t mode) {
int result; int result;
string s3_realpath; string s3_realpath;
string strpath; string strpath;
string strdel; string newpath;
string nowcache;
headers_t meta; headers_t meta;
struct stat stbuf; struct stat stbuf;
int nDirType = DIRTYPE_UNKNOWN; int nDirType = DIRTYPE_UNKNOWN;
@ -2306,11 +2382,11 @@ static int s3fs_chmod_nocopy(const char *path, mode_t mode) {
// Get attributes // Get attributes
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
result = chk_dir_object_type(path, strpath, strdel, &meta, &nDirType); result = chk_dir_object_type(path, newpath, strpath, nowcache, &meta, &nDirType);
s3_realpath = get_realpath(strdel.c_str()); s3_realpath = get_realpath(strpath.c_str());
}else{ }else{
strpath = path; strpath = path;
strdel = strpath; nowcache = strpath;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
result = get_object_attribute(strpath.c_str(), NULL, &meta); result = get_object_attribute(strpath.c_str(), NULL, &meta);
} }
@ -2319,29 +2395,21 @@ static int s3fs_chmod_nocopy(const char *path, mode_t mode) {
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
if(DIRTYPE_NEW != nDirType){ // Should rebuild all directory object
// directory object of old version // Need to remove old dir("dir" etc) and make new dir("dir/")
// Need to remove old dir("dir") and make new dir("dir/")
// At first, remove directory old object // At first, remove directory old object
if(IS_RMTYPEDIR(nDirType)){
if(0 != (result = curl_delete(s3_realpath.c_str()))){ if(0 != (result = curl_delete(s3_realpath.c_str()))){
return result; return result;
} }
StatCache::getStatCacheData()->DelStat(strdel);
// Make new directory object("dir/")
if(0 != (result = create_directory_object(strpath.c_str(), mode, stbuf.st_mtime, stbuf.st_uid, stbuf.st_gid))){
return result;
}
}else{
// directory object of new version
// Over put directory object.
if(0 != (result = create_directory_object(strpath.c_str(), mode, stbuf.st_mtime, stbuf.st_uid, stbuf.st_gid))){
return result;
}
StatCache::getStatCacheData()->DelStat(strdel);
} }
StatCache::getStatCacheData()->DelStat(nowcache);
// Make new directory object("dir/")
if(0 != (result = create_directory_object(newpath.c_str(), mode, stbuf.st_mtime, stbuf.st_uid, stbuf.st_gid))){
return result;
}
}else{ }else{
// normal object or directory object of newer version // normal object or directory object of newer version
int fd; int fd;
@ -2377,7 +2445,7 @@ static int s3fs_chmod_nocopy(const char *path, mode_t mode) {
if(isclose){ if(isclose){
close(fd); close(fd);
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
} }
return result; return result;
@ -2387,7 +2455,8 @@ static int s3fs_chown(const char *path, uid_t uid, gid_t gid) {
int result; int result;
string s3_realpath; string s3_realpath;
string strpath; string strpath;
string strdel; string newpath;
string nowcache;
headers_t meta; headers_t meta;
struct stat stbuf; struct stat stbuf;
int nDirType = DIRTYPE_UNKNOWN; int nDirType = DIRTYPE_UNKNOWN;
@ -2402,11 +2471,11 @@ static int s3fs_chown(const char *path, uid_t uid, gid_t gid) {
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
result = chk_dir_object_type(path, strpath, strdel, &meta, &nDirType); result = chk_dir_object_type(path, newpath, strpath, nowcache, &meta, &nDirType);
s3_realpath = get_realpath(strdel.c_str()); s3_realpath = get_realpath(strpath.c_str());
}else{ }else{
strpath = path; strpath = path;
strdel = strpath; nowcache = strpath;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
result = get_object_attribute(strpath.c_str(), NULL, &meta); result = get_object_attribute(strpath.c_str(), NULL, &meta);
} }
@ -2423,18 +2492,20 @@ static int s3fs_chown(const char *path, uid_t uid, gid_t gid) {
gid = grdata->gr_gid; gid = grdata->gr_gid;
} }
if(S_ISDIR(stbuf.st_mode) && DIRTYPE_NEW != nDirType){ if(S_ISDIR(stbuf.st_mode) && IS_REPLACEDIR(nDirType)){
// directory object of old version // Should rebuild directory object(except new type)
// Need to remove old dir("dir") and make new dir("dir/") // Need to remove old dir("dir" etc) and make new dir("dir/")
// At first, remove directory old object // At first, remove directory old object
if(0 != (result = curl_delete(s3_realpath.c_str()))){ if(IS_RMTYPEDIR(nDirType)){
return result; if(0 != (result = curl_delete(s3_realpath.c_str()))){
return result;
}
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
// Make new directory object("dir/") // Make new directory object("dir/")
if(0 != (result = create_directory_object(strpath.c_str(), stbuf.st_mode, stbuf.st_mtime, uid, gid))){ if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, stbuf.st_mtime, uid, gid))){
return result; return result;
} }
}else{ }else{
@ -2446,7 +2517,7 @@ static int s3fs_chown(const char *path, uid_t uid, gid_t gid) {
if(put_headers(strpath.c_str(), meta) != 0){ if(put_headers(strpath.c_str(), meta) != 0){
return -EIO; return -EIO;
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
} }
return 0; return 0;
@ -2456,7 +2527,8 @@ static int s3fs_chown_nocopy(const char *path, uid_t uid, gid_t gid) {
int result; int result;
string s3_realpath; string s3_realpath;
string strpath; string strpath;
string strdel; string newpath;
string nowcache;
headers_t meta; headers_t meta;
struct stat stbuf; struct stat stbuf;
int nDirType = DIRTYPE_UNKNOWN; int nDirType = DIRTYPE_UNKNOWN;
@ -2472,11 +2544,11 @@ static int s3fs_chown_nocopy(const char *path, uid_t uid, gid_t gid) {
// Get attributes // Get attributes
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
result = chk_dir_object_type(path, strpath, strdel, &meta, &nDirType); result = chk_dir_object_type(path, newpath, strpath, nowcache, &meta, &nDirType);
s3_realpath = get_realpath(strdel.c_str()); s3_realpath = get_realpath(strpath.c_str());
}else{ }else{
strpath = path; strpath = path;
strdel = strpath; nowcache = strpath;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
result = get_object_attribute(strpath.c_str(), NULL, &meta); result = get_object_attribute(strpath.c_str(), NULL, &meta);
} }
@ -2494,27 +2566,20 @@ static int s3fs_chown_nocopy(const char *path, uid_t uid, gid_t gid) {
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
if(DIRTYPE_NEW != nDirType){ // Should rebuild all directory object
// directory object of old version // Need to remove old dir("dir" etc) and make new dir("dir/")
// Need to remove old dir("dir") and make new dir("dir/")
// At first, remove directory old object // At first, remove directory old object
if(IS_RMTYPEDIR(nDirType)){
if(0 != (result = curl_delete(s3_realpath.c_str()))){ if(0 != (result = curl_delete(s3_realpath.c_str()))){
return result; return result;
} }
StatCache::getStatCacheData()->DelStat(strdel); }
StatCache::getStatCacheData()->DelStat(nowcache);
// Make new directory object("dir/") // Make new directory object("dir/")
if(0 != (result = create_directory_object(strpath.c_str(), stbuf.st_mode, stbuf.st_mtime, uid, gid))){ if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, stbuf.st_mtime, uid, gid))){
return result; return result;
}
}else{
// directory object of new version
// Over put directory object.
if(0 != (result = create_directory_object(strpath.c_str(), stbuf.st_mode, stbuf.st_mtime, uid, gid))){
return result;
}
StatCache::getStatCacheData()->DelStat(strdel);
} }
}else{ }else{
// normal object or directory object of newer version // normal object or directory object of newer version
@ -2553,7 +2618,7 @@ static int s3fs_chown_nocopy(const char *path, uid_t uid, gid_t gid) {
if(isclose){ if(isclose){
close(fd); close(fd);
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
} }
return result; return result;
@ -3380,7 +3445,8 @@ static int s3fs_utimens(const char *path, const struct timespec ts[2]) {
int result; int result;
string s3_realpath; string s3_realpath;
string strpath; string strpath;
string strdel; string newpath;
string nowcache;
headers_t meta; headers_t meta;
struct stat stbuf; struct stat stbuf;
int nDirType = DIRTYPE_UNKNOWN; int nDirType = DIRTYPE_UNKNOWN;
@ -3395,11 +3461,11 @@ static int s3fs_utimens(const char *path, const struct timespec ts[2]) {
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
result = chk_dir_object_type(path, strpath, strdel, &meta, &nDirType); result = chk_dir_object_type(path, newpath, strpath, nowcache, &meta, &nDirType);
s3_realpath = get_realpath(strdel.c_str()); s3_realpath = get_realpath(strpath.c_str());
}else{ }else{
strpath = path; strpath = path;
strdel = strpath; nowcache = strpath;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
result = get_object_attribute(strpath.c_str(), NULL, &meta); result = get_object_attribute(strpath.c_str(), NULL, &meta);
} }
@ -3407,18 +3473,20 @@ static int s3fs_utimens(const char *path, const struct timespec ts[2]) {
return result; return result;
} }
if(S_ISDIR(stbuf.st_mode) && DIRTYPE_NEW != nDirType){ if(S_ISDIR(stbuf.st_mode) && IS_REPLACEDIR(nDirType)){
// directory object of old version // Should rebuild directory object(except new type)
// Need to remove old dir("dir") and make new dir("dir/") // Need to remove old dir("dir" etc) and make new dir("dir/")
// At first, remove directory old object // At first, remove directory old object
if(0 != (result = curl_delete(s3_realpath.c_str()))){ if(IS_RMTYPEDIR(nDirType)){
return result; if(0 != (result = curl_delete(s3_realpath.c_str()))){
return result;
}
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
// Make new directory object("dir/") // Make new directory object("dir/")
if(0 != (result = create_directory_object(strpath.c_str(), stbuf.st_mode, ts[1].tv_sec, stbuf.st_uid, stbuf.st_gid))){ if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts[1].tv_sec, stbuf.st_uid, stbuf.st_gid))){
return result; return result;
} }
}else{ }else{
@ -3429,7 +3497,7 @@ static int s3fs_utimens(const char *path, const struct timespec ts[2]) {
if(put_headers(strpath.c_str(), meta) != 0){ if(put_headers(strpath.c_str(), meta) != 0){
return -EIO; return -EIO;
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
} }
return 0; return 0;
@ -3439,7 +3507,8 @@ static int s3fs_utimens_nocopy(const char *path, const struct timespec ts[2]) {
int result; int result;
string s3_realpath; string s3_realpath;
string strpath; string strpath;
string strdel; string newpath;
string nowcache;
headers_t meta; headers_t meta;
struct stat stbuf; struct stat stbuf;
int nDirType = DIRTYPE_UNKNOWN; int nDirType = DIRTYPE_UNKNOWN;
@ -3455,11 +3524,11 @@ static int s3fs_utimens_nocopy(const char *path, const struct timespec ts[2]) {
// Get attributes // Get attributes
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
result = chk_dir_object_type(path, strpath, strdel, &meta, &nDirType); result = chk_dir_object_type(path, newpath, strpath, nowcache, &meta, &nDirType);
s3_realpath = get_realpath(strdel.c_str()); s3_realpath = get_realpath(strpath.c_str());
}else{ }else{
strpath = path; strpath = path;
strdel = strpath; nowcache = strpath;
s3_realpath = get_realpath(strpath.c_str()); s3_realpath = get_realpath(strpath.c_str());
result = get_object_attribute(strpath.c_str(), NULL, &meta); result = get_object_attribute(strpath.c_str(), NULL, &meta);
} }
@ -3468,27 +3537,20 @@ static int s3fs_utimens_nocopy(const char *path, const struct timespec ts[2]) {
} }
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){
if(DIRTYPE_NEW != nDirType){ // Should rebuild all directory object
// directory object of old version // Need to remove old dir("dir" etc) and make new dir("dir/")
// Need to remove old dir("dir") and make new dir("dir/")
// At first, remove directory old object // At first, remove directory old object
if(IS_RMTYPEDIR(nDirType)){
if(0 != (result = curl_delete(s3_realpath.c_str()))){ if(0 != (result = curl_delete(s3_realpath.c_str()))){
return result; return result;
} }
StatCache::getStatCacheData()->DelStat(strdel); }
StatCache::getStatCacheData()->DelStat(nowcache);
// Make new directory object("dir/") // Make new directory object("dir/")
if(0 != (result = create_directory_object(strpath.c_str(), stbuf.st_mode, ts[1].tv_sec, stbuf.st_uid, stbuf.st_gid))){ if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts[1].tv_sec, stbuf.st_uid, stbuf.st_gid))){
return result; return result;
}
}else{
// directory object of new version
// Over put directory object.
if(0 != (result = create_directory_object(strpath.c_str(), stbuf.st_mode, ts[1].tv_sec, stbuf.st_uid, stbuf.st_gid))){
return result;
}
StatCache::getStatCacheData()->DelStat(strdel);
} }
}else{ }else{
// normal object or directory object of newer version // normal object or directory object of newer version
@ -3529,7 +3591,7 @@ static int s3fs_utimens_nocopy(const char *path, const struct timespec ts[2]) {
if(isclose){ if(isclose){
close(fd); close(fd);
} }
StatCache::getStatCacheData()->DelStat(strdel); StatCache::getStatCacheData()->DelStat(nowcache);
} }
return result; return result;

View File

@ -241,10 +241,52 @@ bool S3ObjList::GetNameList(s3obj_list_t& list, bool OnlyNormalized, bool CutSla
return true; return true;
} }
typedef std::map<std::string, bool> s3obj_h_t;
bool S3ObjList::MakeHierarchizedList(s3obj_list_t& list, bool haveSlash)
{
s3obj_h_t h_map;
s3obj_h_t::iterator hiter;
s3obj_list_t::const_iterator liter;
for(liter = list.begin(); list.end() != liter; liter++){
string strtmp = (*liter);
if(1 < strtmp.length() && '/' == strtmp[strtmp.length() - 1]){
strtmp = strtmp.substr(0, strtmp.length() - 1);
}
h_map[strtmp] = true;
// check hierarchized directory
for(string::size_type pos = strtmp.find_last_of("/"); string::npos != pos; pos = strtmp.find_last_of("/")){
strtmp = strtmp.substr(0, pos);
if(0 == strtmp.length() || "/" == strtmp){
break;
}
if(h_map.end() == h_map.find(strtmp)){
// not found
h_map[strtmp] = false;
}
}
}
// check map and add lost hierarchized directory.
for(hiter = h_map.begin(); hiter != h_map.end(); ++hiter){
if(false == (*hiter).second){
// add hierarchized directory.
string strtmp = (*hiter).first;
if(haveSlash){
strtmp += "/";
}
list.push_back(strtmp);
}
}
return true;
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Utility functions for moving objects // Utility functions for moving objects
//------------------------------------------------------------------- //-------------------------------------------------------------------
MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir) MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir, bool normdir)
{ {
MVNODE *p; MVNODE *p;
char *p_old_path; char *p_old_path;
@ -272,9 +314,10 @@ MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir)
return NULL; return NULL;
} }
p->old_path = p_old_path; p->old_path = p_old_path;
p->new_path = p_new_path; p->new_path = p_new_path;
p->is_dir = is_dir; p->is_dir = is_dir;
p->is_normdir = normdir;
p->prev = NULL; p->prev = NULL;
p->next = NULL; p->next = NULL;
return p; return p;
@ -283,7 +326,7 @@ MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir)
// //
// Add sorted MVNODE data(Ascending order) // Add sorted MVNODE data(Ascending order)
// //
MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const char *new_path, bool is_dir) MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const char *new_path, bool is_dir, bool normdir)
{ {
if(!head || !tail){ if(!head || !tail){
return NULL; return NULL;
@ -308,7 +351,7 @@ MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const cha
// Add into before cur-pos. // Add into before cur-pos.
// ex: cur("abc"), mvnew("ab") // ex: cur("abc"), mvnew("ab")
// ex: cur("abc"), mvnew("abb") // ex: cur("abc"), mvnew("abb")
if(NULL == (mvnew = create_mvnode(old_path, new_path, is_dir))){ if(NULL == (mvnew = create_mvnode(old_path, new_path, is_dir, normdir))){
return NULL; return NULL;
} }
if(cur->prev){ if(cur->prev){
@ -325,7 +368,7 @@ MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const cha
} }
} }
// Add into tail. // Add into tail.
if(NULL == (mvnew = create_mvnode(old_path, new_path, is_dir))){ if(NULL == (mvnew = create_mvnode(old_path, new_path, is_dir, normdir))){
return NULL; return NULL;
} }
mvnew->prev = (*tail); mvnew->prev = (*tail);

View File

@ -51,12 +51,15 @@ class S3ObjList
std::string GetETag(const char* name) const; std::string GetETag(const char* name) const;
bool IsDir(const char* name) const; bool IsDir(const char* name) const;
bool GetNameList(s3obj_list_t& list, bool OnlyNormalized = true, bool CutSlash = true) const; bool GetNameList(s3obj_list_t& list, bool OnlyNormalized = true, bool CutSlash = true) const;
static bool MakeHierarchizedList(s3obj_list_t& list, bool haveSlash);
}; };
typedef struct mvnode { typedef struct mvnode {
char *old_path; char *old_path;
char *new_path; char *new_path;
bool is_dir; bool is_dir;
bool is_normdir;
struct mvnode *prev; struct mvnode *prev;
struct mvnode *next; struct mvnode *next;
} MVNODE; } MVNODE;
@ -66,8 +69,8 @@ typedef struct mvnode {
//------------------------------------------------------------------- //-------------------------------------------------------------------
std::string get_realpath(const char *path); std::string get_realpath(const char *path);
MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir); MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir, bool normdir = false);
MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const char *new_path, bool is_dir); MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const char *new_path, bool is_dir, bool normdir = false);
void free_mvnodes(MVNODE *head); void free_mvnodes(MVNODE *head);
std::string get_username(uid_t uid); std::string get_username(uid_t uid);