static int rename_directory()

in src/s3fs.cpp [1407:1546]


static int rename_directory(const char* from, const char* to)
{
    S3ObjList head;
    s3obj_list_t headlist;
    std::string strfrom  = from ? from : "";   // from is without "/".
    std::string strto    = to ? to : "";       // to is without "/" too.
    std::string basepath = strfrom + "/";
    std::string newpath;                       // should be from name(not used)
    std::string nowcache;                      // now cache path(not used)
    dirtype DirType;
    bool normdir; 
    MVNODE* mn_head = NULL;
    MVNODE* mn_tail = NULL;
    MVNODE* mn_cur;
    struct stat stbuf;
    int result;
    bool is_dir;

    S3FS_PRN_INFO1("[from=%s][to=%s]", from, to);

    //
    // Initiate and Add base directory into MVNODE struct.
    //
    strto += "/";
    if(0 == chk_dir_object_type(from, newpath, strfrom, nowcache, NULL, &DirType) && DIRTYPE_UNKNOWN != DirType){
        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;
        }
    }else{
        // Something wrong about "from" directory.
    }

    //
    // get a list of all the objects
    //
    // No delimiter is specified, the result(head) is all object keys.
    // (CommonPrefixes is empty, but all object is listed in Key.)
    if(0 != (result = list_bucket(basepath.c_str(), head, NULL))){
        S3FS_PRN_ERR("list_bucket returns error.");
        return result; 
    }
    head.GetNameList(headlist);                       // get name without "/".
    S3ObjList::MakeHierarchizedList(headlist, false); // add hierarchized dir.

    s3obj_list_t::const_iterator liter;
    for(liter = headlist.begin(); headlist.end() != liter; ++liter){
        // make "from" and "to" object name.
        std::string from_name = basepath + (*liter);
        std::string to_name   = strto + (*liter);
        std::string etag      = head.GetETag((*liter).c_str());

        // Check subdirectory.
        StatCache::getStatCacheData()->HasStat(from_name, etag.c_str()); // Check ETag
        if(0 != get_object_attribute(from_name.c_str(), &stbuf, NULL)){
            S3FS_PRN_WARN("failed to get %s object attribute.", from_name.c_str());
            continue;
        }
        if(S_ISDIR(stbuf.st_mode)){
            is_dir = true;
            if(0 != chk_dir_object_type(from_name.c_str(), newpath, from_name, nowcache, NULL, &DirType) || DIRTYPE_UNKNOWN == DirType){
                S3FS_PRN_WARN("failed to get %s%s object directory type.", basepath.c_str(), (*liter).c_str());
                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{
            is_dir  = false;
            normdir = false;
        }
        
        // push this one onto the stack
        if(NULL == add_mvnode(&mn_head, &mn_tail, from_name.c_str(), to_name.c_str(), is_dir, normdir)){
            return -ENOMEM;
        }
    }

    //
    // rename
    //
    // rename directory objects.
    for(mn_cur = mn_head; mn_cur; mn_cur = mn_cur->next){
        if(mn_cur->is_dir && mn_cur->old_path && '\0' != mn_cur->old_path[0]){
            // [NOTE]
            // The ctime is updated only for the top (from) directory.
            // Other than that, it will not be updated.
            //
            if(0 != (result = clone_directory_object(mn_cur->old_path, mn_cur->new_path, (strfrom == mn_cur->old_path)))){
                S3FS_PRN_ERR("clone_directory_object returned an error(%d)", result);
                free_mvnodes(mn_head);
                return result;
            }
        }
    }

    // iterate over the list - copy the files with rename_object
    // does a safe copy - copies first and then deletes old
    for(mn_cur = mn_head; mn_cur; mn_cur = mn_cur->next){
        if(!mn_cur->is_dir){
            if(!nocopyapi && !norenameapi){
                result = rename_object(mn_cur->old_path, mn_cur->new_path, false);          // keep ctime
            }else{
                result = rename_object_nocopy(mn_cur->old_path, mn_cur->new_path, false);   // keep ctime
            }
            if(0 != result){
                S3FS_PRN_ERR("rename_object returned an error(%d)", result);
                free_mvnodes(mn_head);
                return result;
            }
        }
    }

    // Iterate over old the directories, bottoms up and remove
    for(mn_cur = mn_tail; mn_cur; mn_cur = mn_cur->prev){
        if(mn_cur->is_dir && mn_cur->old_path && '\0' != mn_cur->old_path[0]){
            if(!(mn_cur->is_normdir)){
                if(0 != (result = s3fs_rmdir(mn_cur->old_path))){
                    S3FS_PRN_ERR("s3fs_rmdir returned an error(%d)", result);
                    free_mvnodes(mn_head);
                    return result;
                }
            }else{
                // cache clear.
                StatCache::getStatCacheData()->DelStat(mn_cur->old_path);
            }
        }
    }
    free_mvnodes(mn_head);

    return 0;
}