int PathResolution::recursiveFindFs(string& path)
{
    depthIntoFs=1;
    size_t backIndex=index;
    for(;;)
    {
        backIndex=path.find_last_of('/',backIndex-1);
        if(backIndex==string::npos) return -ENOENT; //should not happpen
        if(backIndex==0)
        {
            fs=root;
            indexIntoFs=1;
            break;
        }
        map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
        it=filesystems.find(StringPart(path,backIndex));
        if(it!=filesystems.end())
        {
            fs=it->second;
            indexIntoFs=backIndex+1;
            break;
        }
        depthIntoFs++;
    }
    syms=fs->supportsSymlinks();
    return 0;
}
int PathResolution::normalPathComponent(string& path, bool followIfSymlink)
{
    map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
    it=filesystems.find(StringPart(path,index-1));
    if(it!=filesystems.end())
    {
        //Jumped to a new filesystem. Not stat-ing the path as we're
        //relying on mount not allowing to mount a filesystem on anything
        //but a directory.
        fs=it->second;
        syms=fs->supportsSymlinks();
        indexIntoFs=index>path.length() ? index-1 : index;
        depthIntoFs=1;
        return 0;
    }
    depthIntoFs++;
    if(syms && followIfSymlink)
    {
        struct stat st;
        {
            StringPart sp(path,index-1,indexIntoFs);
            if(int res=fs->lstat(sp,&st)<0) return res;
        }
        if(S_ISLNK(st.st_mode)) return followSymlink(path);
        else if(index<=path.length() && !S_ISDIR(st.st_mode)) return -ENOTDIR;
    }
    return 0;
}
int MountpointFsDirectory::getdents(void *dp, int len)
{
    if(len<minimumBufferSize) return -EINVAL;
    if(last) return 0;
    
    Lock<FastMutex> l(mutex);
    char *begin=reinterpret_cast<char*>(dp);
    char *buffer=begin;
    char *end=buffer+len;
    if(first)
    {
        first=false;
        addDefaultEntries(&buffer,currentInode,parentInode);
    }
    if(currentItem.empty()==false)
    {
        map<StringPart,int>::iterator it=dirs.find(StringPart(currentItem));
        //Someone deleted the exact directory entry we had saved (unlikely)
        if(it==dirs.end()) return -EBADF;
        for(;it!=dirs.end();++it)
        {
            if(addEntry(&buffer,end,it->second,DT_DIR,it->first)>0) continue;
            //Buffer finished
            currentItem=it->first.c_str();
            return buffer-begin;
        }
    }
    addTerminatingEntry(&buffer,end);
    last=true;
    return buffer-begin;
}
int FilesystemManager::unlinkHelper(string& path)
{
    //Do everything while keeping the mutex locked to prevent someone to
    //concurrently mount a filesystem on the directory we're unlinking
    Lock<FastMutex> l(mutex);
    ResolvedPath openData=resolvePath(path,true);
    if(openData.result<0) return openData.result;
    //After resolvePath() so path is in canonical form and symlinks are followed
    if(filesystems.find(StringPart(path))!=filesystems.end()) return -EBUSY;
    StringPart sp(path,string::npos,openData.off);
    return openData.fs->unlink(sp);
}
ResolvedPath PathResolution::resolvePath(string& path, bool followLastSymlink)
{
    map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
    it=filesystems.find(StringPart("/"));
    if(it==filesystems.end()) return ResolvedPath(-ENOENT); //should not happen
    root=fs=it->second;
    syms=fs->supportsSymlinks();
    index=1;       //Skip leading /
    indexIntoFs=1; //NOTE: caller must ensure path[0]=='/'
    depthIntoFs=1;
    linksFollowed=0;
    for(;;)
    {
        size_t slash=path.find_first_of('/',index);
        //cout<<path.substr(0,slash)<<endl;
        //Last component (no trailing /)
        if(slash==string::npos) slash=path.length(); //NOTE: one past the last

        if(slash==index)
        {
            //Path component is empty, caused by double slash, remove it
            path.erase(index,1);
        } else if(slash-index==1 && path[index]=='.')
        {
            path.erase(index,2); //Path component is ".", ignore
        } else if(slash-index==2 && path[index]=='.' && path[index+1]=='.')
        {
            int result=upPathComponent(path,slash);
            if(result<0) return ResolvedPath(result);
        } else {
            index=slash+1; //NOTE: if(slash==string::npos) two past the last
            // follow=followLastSymlink for "/link", but is true for "/link/"
            bool follow=index>path.length() ? followLastSymlink : true;
            int result=normalPathComponent(path,follow);
            if(result<0) return ResolvedPath(result);
        }
        //Last component
        if(index>=path.length())
        {
            //Remove trailing /
            size_t last=path.length()-1;
            if(path[last]=='/')
            {
                path.erase(last,1);
                //This may happen if the last path component is a fs
                if(indexIntoFs>path.length()) indexIntoFs=path.length();
            }
            return ResolvedPath(fs,indexIntoFs);
        }
    }
}
int FilesystemManager::renameHelper(string& oldPath, string& newPath)
{
    //Do everything while keeping the mutex locked to prevent someone to
    //concurrently mount a filesystem on the directory we're renaming
    Lock<FastMutex> l(mutex);
    ResolvedPath oldOpenData=resolvePath(oldPath,true);
    if(oldOpenData.result<0) return oldOpenData.result;
    ResolvedPath newOpenData=resolvePath(newPath,true);
    if(newOpenData.result<0) return newOpenData.result;
    
    if(oldOpenData.fs!=newOpenData.fs) return -EXDEV; //Can't rename across fs
    
    //After resolvePath() so path is in canonical form and symlinks are followed
    if(filesystems.find(StringPart(oldPath))!=filesystems.end()) return -EBUSY;
    if(filesystems.find(StringPart(newPath))!=filesystems.end()) return -EBUSY;
    
    StringPart oldSp(oldPath,string::npos,oldOpenData.off);
    StringPart newSp(newPath,string::npos,newOpenData.off);
    
    //Can't rename a directory into a subdirectory of itself
    if(newSp.startsWith(oldSp)) return -EINVAL;
    return oldOpenData.fs->rename(oldSp,newSp);
}
int FilesystemManager::kmount(const char* path, intrusive_ref_ptr<FilesystemBase> fs)
{
    if(path==0 || path[0]=='\0' || fs==0) return -EFAULT;
    Lock<FastMutex> l(mutex);
    size_t len=strlen(path);
    if(len>PATH_MAX) return -ENAMETOOLONG;
    string temp(path);
    if(!(temp=="/" && filesystems.empty())) //Skip check when mounting /
    {
        struct stat st;
        if(int result=statHelper(temp,&st,false)) return result;
        if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
        string parent=temp+"/..";
        if(int result=statHelper(parent,&st,false)) return result;
        fs->setParentFsMountpointInode(st.st_ino);
    }
    if(filesystems.insert(make_pair(StringPart(temp),fs)).second==false)
        return -EBUSY; //Means already mounted
    else
        return 0;
}
Ejemplo n.º 8
0
StringList
StringListOperations::Apply(const StringList& inputList, size_t maxSize,
                            const behavior::Behavior& behavior) const
{
    if (!HasOperations())
        return inputList.SubList(0, maxSize);

    uint32_t operations = fOperations;
    bool hasSelectorOperation = (operations & PATH_PART_SELECTOR_MASK) != 0;
    bool hasPathPartOperation = hasSelectorOperation
                                || (operations & (PATH_PART_REPLACER_MASK | TO_PARENT_DIRECTORY)) != 0;
    if (hasPathPartOperation && !hasSelectorOperation) {
        // Only replacer operations. Continue as if all path parts are selected.
        operations |= PATH_PART_SELECTOR_MASK;
    }

    // If a join shall be performed before to-upper/to-lower, we simply convert
    // the join parameter first and join as usual afterwards.
    String joinParameterBuffer;
    StringPart joinParameter = fJoinParameter;
    if (!joinParameter.IsEmpty()
            && (operations & (TO_UPPER | TO_LOWER)) != 0
            && behavior.GetJoinCaseOperator()
            == behavior::Behavior::JOIN_BEFORE_CASE_OPERATOR) {
        joinParameterBuffer = joinParameter;
        if ((operations & TO_UPPER) != 0)
            joinParameterBuffer.ToUpper();
        else
            joinParameterBuffer.ToLower();
        joinParameter = joinParameterBuffer;
    }

    StringList resultList;
    StringBuffer buffer;

    const StringList& list
        = inputList.IsEmpty() && (operations & REPLACE_EMPTY) != 0
          ? StringList(String(fEmptyParameter)) : inputList;

    size_t count = std::min(list.Size(), maxSize);
    for (size_t i = 0; i < count; i++) {
        String string = list.ElementAt(i);

        size_t bufferSizeBeforeElement = buffer.Length();

        // If any of the file name/path part selectors/replacers need to be
        // applied, we disassemble the string, make the modifications, and
        // reassemble it.
        if (hasPathPartOperation) {
            Path::Parts parts(string);

            if ((operations & REPLACE_GRIST) != 0)
                parts.SetGrist(fGristParameter);
            else if ((operations & SELECT_GRIST) == 0)
                parts.UnsetGrist();

            if ((operations & REPLACE_ROOT) != 0)
                parts.SetRoot(fRootParameter);
            else if ((operations & SELECT_ROOT) == 0)
                parts.UnsetRoot();

            if ((operations & REPLACE_DIRECTORY) != 0)
                parts.SetDirectory(fDirectoryParameter);
            else if ((operations & SELECT_DIRECTORY) == 0)
                parts.UnsetDirectory();

            if ((operations & REPLACE_BASE_NAME) != 0)
                parts.SetBaseName(fBaseNameParameter);
            else if ((operations & SELECT_BASE_NAME) == 0)
                parts.UnsetBaseName();

            if ((operations & REPLACE_SUFFIX) != 0)
                parts.SetSuffix(fSuffixParameter);
            else if ((operations & SELECT_SUFFIX) == 0)
                parts.UnsetSuffix();

            if ((operations & REPLACE_ARCHIVE_MEMBER) != 0)
                parts.SetArchiveMember(fArchiveMemberParameter);
            else if ((operations & SELECT_ARCHIVE_MEMBER) == 0)
                parts.UnsetArchiveMember();

            if ((operations & TO_PARENT_DIRECTORY) != 0) {
                parts.UnsetBaseName();
                parts.UnsetSuffix();
                parts.UnsetArchiveMember();
            }

            parts.GetPath(buffer, behavior);
        } else
            buffer += string;

        if ((operations & TO_UPPER) != 0) {
            char* toConvert = buffer.Data() + bufferSizeBeforeElement;
            char* end = buffer.Data() + buffer.Length();
            std::transform(toConvert, end, toConvert, ::toupper);
        } else if ((operations & TO_LOWER) != 0) {
            char* toConvert = buffer.Data() + bufferSizeBeforeElement;
            char* end = buffer.Data() + buffer.Length();
            std::transform(toConvert, end, toConvert, ::tolower);
        }

        if ((operations & JOIN) != 0) {
            if (i + 1 < count)
                buffer += joinParameter;
        } else {
            resultList.Append(buffer);
            buffer = StringPart();
        }
    }

    if ((operations & JOIN) != 0 && count > 0) {
        // Append the joined value, if we're not emulating the broken behavior
        // of jam.
        if (behavior.GetBrokenSubscriptJoin()
                == behavior::Behavior::NO_BROKEN_SUBSCRIPT_JOIN
                || count == list.Size()) {
            resultList.Append(buffer);
        }
    }

    return resultList;
}
int FilesystemManager::umount(const char* path, bool force)
{
    typedef
    typename map<StringPart,intrusive_ref_ptr<FilesystemBase> >::iterator fsIt;
    
    if(path==0 || path[0]=='\0') return -ENOENT;
    size_t len=strlen(path);
    if(len>PATH_MAX) return -ENAMETOOLONG;
    Lock<FastMutex> l(mutex); //A reader-writer lock would be better
    fsIt it=filesystems.find(StringPart(path));
    if(it==filesystems.end()) return -EINVAL;
    
    //This finds all the filesystems that have to be recursively umounted
    //to umount the required filesystem. For example, if /path and /path/path2
    //are filesystems, umounting /path must umount also /path/path2
    vector<fsIt> fsToUmount;
    for(fsIt it2=filesystems.begin();it2!=filesystems.end();++it2)
        if(it2->first.startsWith(it->first)) fsToUmount.push_back(it2);
    
    //Now look into all file descriptor tables if there are open files in the
    //filesystems to umount. If there are, return busy. This is an heavy
    //operation given the way the filesystem data structure is organized, but
    //it has been done like this to minimize the size of an entry in the file
    //descriptor table (4 bytes), and because umount happens infrequently.
    //Note that since we are locking the same mutex used by resolvePath(),
    //other threads can't open new files concurrently while we check
    #ifdef WITH_PROCESSES
    list<FileDescriptorTable*>::iterator it3;
    for(it3=fileTables.begin();it3!=fileTables.end();++it3)
    {
        for(int i=0;i<MAX_OPEN_FILES;i++)
        {
            intrusive_ref_ptr<FileBase> file=(*it3)->getFile(i);
            if(!file) continue;
            vector<fsIt>::iterator it4;
            for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
            {
                if(file->getParent()!=(*it4)->second) continue;
                if(force==false) return -EBUSY;
                (*it3)->close(i); //If forced umount, close the file
            }
        }
    }
    #else //WITH_PROCESSES
    for(int i=0;i<MAX_OPEN_FILES;i++)
    {
        intrusive_ref_ptr<FileBase> file=getFileDescriptorTable().getFile(i);
        if(!file) continue;
        vector<fsIt>::iterator it4;
        for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
        {
            if(file->getParent()!=(*it4)->second) continue;
            if(force==false) return -EBUSY;
            getFileDescriptorTable().close(i);//If forced umount, close the file
        }
    }
    #endif //WITH_PROCESSES
    
    //Now there should be no more files belonging to the filesystems to umount,
    //but check if it is really so, as there is a possible race condition
    //which is the read/close,umount where one thread performs a read (or write)
    //operation on a file descriptor, it gets preempted and another thread does
    //a close on that descriptor and an umount of the filesystem. Also, this may
    //happen in case of a forced umount. In such a case there is no entry in
    //the descriptor table (as close was called) but the operation is still
    //ongoing.
    vector<fsIt>::iterator it5;
    const int maxRetry=3; //Retry up to three times
    for(int i=0;i<maxRetry;i++)
    { 
        bool failed=false;
        for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
        {
            if((*it5)->second->areAllFilesClosed()) continue;
            if(force==false) return -EBUSY;
            failed=true;
            break;
        }
        if(!failed) break;
        if(i==maxRetry-1) return -EBUSY; //Failed to umount even if forced
        Thread::sleep(1000); //Wait to see if the filesystem operation completes
    }
    
    //It is now safe to umount all filesystems
    for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
        filesystems.erase(*it5);
    return 0;
}