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; }
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; }