Skip to content

Commit

Permalink
FilesystemBase now derived from IntrusiveRefcounted to support forced…
Browse files Browse the repository at this point in the history
… umount of a filesystem
  • Loading branch information
Terraneo Federico committed Jul 7, 2013
1 parent b6c30dd commit bbca560
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 86 deletions.
2 changes: 1 addition & 1 deletion main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ using namespace miosix;
int main()
{
//iprintf("Hello world, write your application here\n");
DevFs *dfs=new DevFs;
intrusive_ref_ptr<DevFs> dfs(new DevFs);
FilesystemManager& fsm=FilesystemManager::instance();
fsm.kmount("/dev",dfs);
FileDescriptorTable fdt;
Expand Down
28 changes: 14 additions & 14 deletions miosix/filesystem/devfs/devfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,39 +37,39 @@ namespace miosix {

DevFs::DevFs()
{
files["null"]=intrusive_ref_ptr<FileBase>(new NullFile(this));
files["zero"]=intrusive_ref_ptr<FileBase>(new ZeroFile(this));
files[StringPart("null")]=intrusive_ref_ptr<FileBase>(new NullFile);
files[StringPart("zero")]=intrusive_ref_ptr<FileBase>(new ZeroFile);
}

int DevFs::open(intrusive_ref_ptr<FileBase>& file, StringPart& name, int flags,
int mode)
{
return -1; //FIXME
//TODO: mode & flags
Lock<Mutex> l(mutex);
map<StringPart,intrusive_ref_ptr<FileBase> >::iterator it=files.find(name);
if(it==files.end()) return -ENOENT;
file=it->second;
return 0;
}

int DevFs::lstat(StringPart& name, struct stat *pstat)
{
return -1; //FIXME
}

int DevFs::readlink(StringPart& name, std::string& target)
{
return -1; //FIXME
Lock<Mutex> l(mutex);
map<StringPart,intrusive_ref_ptr<FileBase> >::iterator it=files.find(name);
if(it==files.end()) return -ENOENT;
it->second->fstat(pstat);
return 0;
}

bool DevFs::supportsSymlinks() const { return false; }

bool DevFs::areAllFilesClosed()
{
//Lock the mutex just in case we'll want to support dynamically removing
//device files at runtime
Lock<Mutex> l(mutex);
//Can't use openFileCount in devFS, as one instance of each file is stored
//in the map. Rather, check the reference count value. No need to use
//atomic ops to make a copy of the file before calling use_count() as the
//existence of at least one reference in the map guarantees the file won't
//be deleted.
map<string,intrusive_ref_ptr<FileBase> >::iterator it;
map<StringPart,intrusive_ref_ptr<FileBase> >::iterator it;
for(it=files.begin();it!=files.end();++it)
if(it->second.use_count()>1) return false;
return true;
Expand Down
43 changes: 26 additions & 17 deletions miosix/filesystem/devfs/devfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@

namespace miosix {

/**
* DevFs is a special filesystem meant to access devices as they were files.
* For this reason, it is a little different from other filesystems. Normal
* filesystems create FileBase objects ondemand, to answer an open() call. Such
* files have a parent pointer that points to the filesystem. On the contrary,
* DevFs is a collection of device files. Each device file is a different
* subclass of FileBase that overrides some of its member functions to access
* the handled device. These FileBase subclasses do not have a parent pointer
* into DevFs, instead, DevFs holds a map of device names and pointers into
* these derived FileBase objects, which always exist, even if they are not
* opened anywere. Opening the same file name multiple times returns the same
* instance of the file object.
*
* The reason why device files can't have the parent pointer that points into
* DevFs is that DevFs already has a pointer into those files, and since they're
* both refcounted pointers that would be a cycle of refcounted pointer that
* would cause problem if DevFs is ever umounted.
*
* Also a note on the behaviour when umounting DevFs: since the files don't
* have a parent pointer into DevFs umounting DevFs will immediately delete
* the filesystem, while the individual files won't be deleted until the
* processes that have them opened close them. This isn't an unreasonable
* behaviour, and also it must be considered that umounting DevFs shouldn't
* happen in most practical cases.
*/
class DevFs : public FilesystemBase
{
public:
Expand Down Expand Up @@ -64,22 +89,6 @@ class DevFs : public FilesystemBase
*/
virtual int lstat(StringPart& name, struct stat *pstat);

/**
* Follows a symbolic link
* \param path path identifying a symlink, relative to the local filesystem
* \param target the link target is returned here if the call succeeds.
* Note that the returned path is not relative to this filesystem, and can
* be either relative or absolute.
* \return 0 on success, a negative number on failure
*/
virtual int readlink(StringPart& name, std::string& target);

/**
* \return true if the filesystem supports symbolic links.
* In this case, the filesystem should override readlink
*/
virtual bool supportsSymlinks() const;

/**
* \internal
* \return true if all files belonging to this filesystem are closed
Expand All @@ -88,7 +97,7 @@ class DevFs : public FilesystemBase

private:
Mutex mutex;
std::map<std::string,intrusive_ref_ptr<FileBase> > files;
std::map<StringPart,intrusive_ref_ptr<FileBase> > files;
};

} //namespace miosix
Expand Down
9 changes: 2 additions & 7 deletions miosix/filesystem/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,9 @@ int ZeroFile::sync()
// class TerminalDevice
//

//TODO FileBase(0): should we override getPartent() to return device->getParent()?
TerminalDevice::TerminalDevice(intrusive_ref_ptr<FileBase> device)
: FileBase(0), device(device), echo(false), binary(false), skipNewline(false) {}

void TerminalDevice::changeDevice(intrusive_ref_ptr<FileBase> newDevice)
{
device=newDevice;
}
: FileBase(device->getParent()), device(device), echo(false),
binary(false), skipNewline(false) {}

ssize_t TerminalDevice::write(const void *data, size_t len)
{
Expand Down
16 changes: 5 additions & 11 deletions miosix/filesystem/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class FileBase : public IntrusiveRefCounted
* Constructor
* \param parent the filesystem to which this file belongs
*/
FileBase(FilesystemBase *parent) : parent(parent) {}
FileBase(intrusive_ref_ptr<FilesystemBase> parent) : parent(parent) {}

/**
* Write data to the file, if the file supports writing.
Expand Down Expand Up @@ -104,7 +104,7 @@ class FileBase : public IntrusiveRefCounted
/**
* \return a pointer to the parent filesystem
*/
const FilesystemBase *getParent() const { return parent; }
const intrusive_ref_ptr<FilesystemBase> getParent() const { return parent; }

/**
* File destructor
Expand All @@ -115,7 +115,7 @@ class FileBase : public IntrusiveRefCounted
FileBase(const FileBase&);
FileBase& operator=(const FileBase&);

FilesystemBase *parent; ///< All files must have a parent filesystem
intrusive_ref_ptr<FilesystemBase> parent; ///< Files may have a parent fs
};

/**
Expand All @@ -128,7 +128,7 @@ class NullFile : public FileBase
* Constructor
* \param parent the filesystem to which this file belongs
*/
NullFile(FilesystemBase *parent) : FileBase(parent) {}
NullFile() : FileBase(intrusive_ref_ptr<FilesystemBase>()) {}

/**
* Write data to the file, if the file supports writing.
Expand Down Expand Up @@ -189,7 +189,7 @@ class ZeroFile : public FileBase
* Constructor
* \param parent the filesystem to which this file belongs
*/
ZeroFile(FilesystemBase *parent) : FileBase(parent) {}
ZeroFile() : FileBase(intrusive_ref_ptr<FilesystemBase>()) {}

/**
* Write data to the file, if the file supports writing.
Expand Down Expand Up @@ -253,12 +253,6 @@ class TerminalDevice : public FileBase
*/
TerminalDevice(intrusive_ref_ptr<FileBase> device);

/**
* Change the proxed object
* \param newDevice new proxed device
*/
void changeDevice(intrusive_ref_ptr<FileBase> newDevice);

/**
* Convenience function to write a text string, terminated with \0.
* \param str the string to write.
Expand Down
74 changes: 44 additions & 30 deletions miosix/filesystem/file_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ class ResolvedPath
* \param off offset into path where the subpath relative to the current
* filesystem starts
*/
ResolvedPath(FilesystemBase *fs, int offset)
ResolvedPath(intrusive_ref_ptr<FilesystemBase> fs, int offset)
: result(0), fs(fs), off(offset) {}

int result; ///< 0 on success, a negative number on failure
FilesystemBase *fs; ///< pointer to the filesystem to which the file belongs
int result; ///< 0 on success, a negative number on failure
intrusive_ref_ptr<FilesystemBase> fs; ///< pointer to the filesystem to which the file belongs
/// path.c_str()+off is a string containing the relative path into the
/// filesystem for the looked up file
int off;
Expand All @@ -76,6 +76,8 @@ int FilesystemBase::readlink(StringPart& name, string& target)
return -EINVAL; //Default implementation, for filesystems without symlinks
}

bool FilesystemBase::supportsSymlinks() const { return false; }

bool FilesystemBase::areAllFilesClosed() { return openFileCount==0; }

void FilesystemBase::fileCloseHook() { atomicAdd(&openFileCount,-1); }
Expand Down Expand Up @@ -308,7 +310,7 @@ class PathResolution
* Constructor
* \param fs map of all mounted filesystems
*/
PathResolution(const map<StringPart,FilesystemBase*>& fs)
PathResolution(const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& fs)
: filesystems(fs) {}

/**
Expand Down Expand Up @@ -362,13 +364,13 @@ class PathResolution
bool checkLastComponent(string& path);

/// Mounted filesystems
const map<StringPart,FilesystemBase*>& filesystems;
const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& filesystems;

/// Pointer to root filesystem
FilesystemBase *root;
intrusive_ref_ptr<FilesystemBase> root;

/// Current filesystem while looking up path
FilesystemBase *fs;
intrusive_ref_ptr<FilesystemBase> fs;

/// True if current filesystem supports symlinks
bool syms;
Expand All @@ -393,7 +395,7 @@ class PathResolution
ResolvedPath PathResolution::resolvePath(string& path, bool followLastSymlink)
{
char rootPath[2]; rootPath[0]='/'; rootPath[1]='\0';//A non-const "/" string
map<StringPart,FilesystemBase*>::const_iterator it;
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
it=filesystems.find(StringPart(rootPath));
if(it==filesystems.end()) return ResolvedPath(-ENOENT); //should not happen
root=fs=it->second;
Expand Down Expand Up @@ -458,7 +460,7 @@ int PathResolution::normalPathComponent(string& path, bool followIfSymlink)
//NOTE: index<path.length() is necessary as for example /dev and
// /dev/ should resolve to the directory in the root filesystem, not
//to the root directory of the /dev filesystem
map<StringPart,FilesystemBase*>::const_iterator it;
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
it=filesystems.find(StringPart(path,index-1));
if(it!=filesystems.end())
{
Expand Down Expand Up @@ -550,7 +552,7 @@ int PathResolution::recursiveFindFs(string& path)
indexIntoFs=0;
break;
}
map<StringPart,FilesystemBase*>::const_iterator it;
map<StringPart,intrusive_ref_ptr<FilesystemBase> >::const_iterator it;
it=filesystems.find(StringPart(path,backIndex));
if(it!=filesystems.end())
{
Expand Down Expand Up @@ -582,7 +584,7 @@ FilesystemManager& FilesystemManager::instance()
return instance;
}

int FilesystemManager::kmount(const char* path, FilesystemBase* fs)
int FilesystemManager::kmount(const char* path, intrusive_ref_ptr<FilesystemBase> fs)
{
if(path==0 || path[0]=='\0' || fs==0) return -EFAULT;
Lock<Mutex> l(mutex);
Expand All @@ -592,28 +594,29 @@ int FilesystemManager::kmount(const char* path, FilesystemBase* fs)
if(len>PATH_MAX) return -ENAMETOOLONG;
string temp(path);
if(filesystems.insert(make_pair(StringPart(temp),fs)).second==false)
return -EBUSY;
return -EBUSY; //Means already mounted
else
return 0;
}

int FilesystemManager::umount(const char* path)
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;
int len=strlen(path);
if(len>PATH_MAX) return -ENAMETOOLONG;
string pathStr(path);
Lock<Mutex> l(mutex); //A reader-writer lock would be better
map<StringPart,FilesystemBase*>::iterator it;
it=filesystems.find(StringPart(pathStr));
fsIt it=filesystems.find(StringPart(pathStr));
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<map<StringPart,FilesystemBase*>::iterator> fsToUmount;
map<StringPart,FilesystemBase*>::iterator it2;
for(it2=filesystems.begin();it2!=filesystems.end();++it2)
vector<fsIt> fsToUmount;
for(fsIt it2=filesystems.begin();it2!=filesystems.end();++it2)
if(it2->first.startsWith(it->first)==0) fsToUmount.push_back(it2);

//Now look into all file descriptor tables if there are open files in the
Expand All @@ -630,11 +633,12 @@ int FilesystemManager::umount(const char* path)
{
intrusive_ref_ptr<FileBase> file=(*it3)->getFile(i);
if(!file) continue;
vector<map<StringPart,FilesystemBase*>::iterator>::iterator it4;
vector<fsIt>::iterator it4;
for(it4=fsToUmount.begin();it4!=fsToUmount.end();++it4)
{
if(file->getParent()!=(*it4)->second) continue;
return -EBUSY;
if(force==false) return -EBUSY;
(*it3)->close(i); //If forced umount, close the file
}
}
}
Expand All @@ -643,20 +647,30 @@ int FilesystemManager::umount(const char* path)
//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.
//In such a case there is no entry in the descriptor table (as close was
//called) but the operation is still ongoing.
vector<map<StringPart,FilesystemBase*>::iterator>::iterator it5;
for(it5=fsToUmount.begin();it5!=fsToUmount.end();++it5)
if((*it5)->second->areAllFilesClosed()==false) return -EBUSY;
//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)
{
FilesystemBase *toUmount=(*it5)->second;
filesystems.erase(*it5);
delete toUmount;
}
return 0;
}

Expand Down
Loading

0 comments on commit bbca560

Please sign in to comment.