/***************************************************************************
 *   Copyright (C) 2013 by Terraneo Federico                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   As a special exception, if other files instantiate templates or use   *
 *   macros or inline functions from this file, or you compile this file   *
 *   and link it with other works to produce a work based on this file,    *
 *   this file does not by itself cause the resulting work to be covered   *
 *   by the GNU General Public License. However the source code for this   *
 *   file must still be made available in accordance with the GNU General  *
 *   Public License. This exception does not invalidate any other reasons  *
 *   why a work based on this file might be covered by the GNU General     *
 *   Public License.                                                       *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
 ***************************************************************************/

#include "file_access.h"
#include <vector>
#include <climits>
#include <fcntl.h>
#include "console/console_device.h"
#include "mountpointfs/mountpointfs.h"
#include "fat32/fat32.h"
#include "kernel/logging.h"
#ifdef WITH_PROCESSES
#include "kernel/process.h"
#endif //WITH_PROCESSES

using namespace std;

#ifdef WITH_FILESYSTEM

namespace miosix {

/*
 * A note on the use of strings in this file. This file uses three string
 * types: C string, C++ std::string and StringPart which is an efficent
 * in-place substring of either a C or C++ string.
 * 
 * The functions which are meant to be used by clients of the filesystem
 * API take file names as C strings. This is becase that's the signature
 * of the POSIX calls, such as fopen(), stat(), umount(), ... It is
 * worth noticing that these C strings are const. However, resolving paths
 * requires a writable scratchpad string to be able to remove
 * useless path components, such as "/./", go backwards when a "/../" is
 * found, and follow symbolic links. To this end, all these functions
 * make a copy of the passed string into a temporary C++ string that
 * will be deallocated as soon as the function returns.
 * Resolving a path, however, requires to scan all its path components
 * one by one, checking if the path up to that point is a symbolic link
 * or a mountpoint of a filesystem.
 * For example, resolving "/home/test/file" requires to make three
 * substrings, "/home", "/home/test" and "/home/test/file". Of course,
 * one can simply use the substr() member function of the std::string
 * class. However, this means making lots of tiny memory allocations on
 * the heap, increasing the RAM footprint to resolve a path. To this end,
 * the StringPart class was introduced, to create fast in-place substring
 * of a string.
 */

//
// class FileDescriptorTable
//

FileDescriptorTable::FileDescriptorTable()
    : mutex(FastMutex::RECURSIVE), cwd("/")
{
    FilesystemManager::instance().addFileDescriptorTable(this);
    files[0]=files[1]=files[2]=intrusive_ref_ptr<FileBase>(
        new TerminalDevice(DefaultConsole::instance().get()));
}

FileDescriptorTable::FileDescriptorTable(const FileDescriptorTable& rhs)
    : mutex(FastMutex::RECURSIVE), cwd(rhs.cwd)
{
    //No need to lock the mutex since we are in a constructor and there can't
    //be pointers to this in other threads yet
    for(int i=0;i<MAX_OPEN_FILES;i++) this->files[i]=atomic_load(&rhs.files[i]);
    FilesystemManager::instance().addFileDescriptorTable(this);
}

FileDescriptorTable& FileDescriptorTable::operator=(
        const FileDescriptorTable& rhs)
{
    Lock<FastMutex> l(mutex);
    for(int i=0;i<MAX_OPEN_FILES;i++)
        atomic_store(&this->files[i],atomic_load(&rhs.files[i]));
    return *this;
}

int FileDescriptorTable::open(const char* name, int flags, int mode)
{
    if(name==0 || name[0]=='\0') return -EFAULT;
    Lock<FastMutex> l(mutex);
    for(int i=3;i<MAX_OPEN_FILES;i++)
    {
        if(files[i]) continue;
        //Found an empty file descriptor
        string path=absolutePath(name);
        if(path.empty()) return -ENAMETOOLONG;
        ResolvedPath openData=FilesystemManager::instance().resolvePath(path);
        if(openData.result<0) return openData.result;
        StringPart sp(path,string::npos,openData.off);
        int result=openData.fs->open(files[i],sp,flags,mode);
        if(result==0) return i; //The file descriptor
        else return result; //The error code
    }
    return -ENFILE;
}

int FileDescriptorTable::close(int fd)
{
    //No need to lock the mutex when deleting
    if(fd<0 || fd>=MAX_OPEN_FILES) return -EBADF;
    intrusive_ref_ptr<FileBase> toClose;
    toClose=atomic_exchange(files+fd,intrusive_ref_ptr<FileBase>());
    if(!toClose) return -EBADF; //File entry was not open
    return 0;
}

void FileDescriptorTable::closeAll()
{
    for(int i=0;i<MAX_OPEN_FILES;i++)
        atomic_exchange(files+i,intrusive_ref_ptr<FileBase>());
}

int FileDescriptorTable::getcwd(char *buf, size_t len)
{
    if(buf==0 || len<2) return -EINVAL; //We don't support the buf==0 extension
    Lock<FastMutex> l(mutex);
    struct stat st;
    if(stat(".",&st) || !S_ISDIR(st.st_mode)) return -ENOENT;
    if(cwd.length()>len) return -ERANGE;
    strncpy(buf,cwd.c_str(),len);
    if(cwd.length()>1) buf[cwd.length()-1]='\0'; //Erase last '/' in cwd
    return 0;
}

int FileDescriptorTable::chdir(const char* name)
{
    if(name==0 || name[0]=='\0') return -EFAULT;
    size_t len=strlen(name);
    if(name[len-1]!='/') len++; //Reserve room for trailing slash
    Lock<FastMutex> l(mutex);
    if(name[0]!='/') len+=cwd.length();
    if(len>PATH_MAX) return -ENAMETOOLONG;
    
    string newCwd;
    newCwd.reserve(len);
    if(name[0]=='/') newCwd=name;
    else {
        newCwd=cwd;
        newCwd+=name;
    }
    ResolvedPath openData=FilesystemManager::instance().resolvePath(newCwd);
    if(openData.result<0) return openData.result;
    struct stat st;
    StringPart sp(newCwd,string::npos,openData.off);
    if(int result=openData.fs->lstat(sp,&st)) return result;
    if(!S_ISDIR(st.st_mode)) return -ENOTDIR;
    //NOTE: put after resolvePath() as it strips trailing /
    //Also put after lstat() as it fails if path has a trailing slash
    newCwd+='/';
    cwd=newCwd;
    return 0;
}

int FileDescriptorTable::mkdir(const char *name, int mode)
{
    if(name==0 || name[0]=='\0') return -EFAULT;
    string path=absolutePath(name);
    if(path.empty()) return -ENAMETOOLONG;
    ResolvedPath openData=FilesystemManager::instance().resolvePath(path,true);
    if(openData.result<0) return openData.result;
    StringPart sp(path,string::npos,openData.off);
    return openData.fs->mkdir(sp,mode);
}

int FileDescriptorTable::rmdir(const char *name)
{
    if(name==0 || name[0]=='\0') return -EFAULT;
    string path=absolutePath(name);
    if(path.empty()) return -ENAMETOOLONG;
    ResolvedPath openData=FilesystemManager::instance().resolvePath(path,true);
    if(openData.result<0) return openData.result;
    StringPart sp(path,string::npos,openData.off);
    return openData.fs->rmdir(sp);
}

int FileDescriptorTable::unlink(const char *name)
{
    if(name==0 || name[0]=='\0') return -EFAULT;
    string path=absolutePath(name);
    if(path.empty()) return -ENAMETOOLONG;
    return FilesystemManager::instance().unlinkHelper(path);
}

int FileDescriptorTable::rename(const char *oldName, const char *newName)
{
    if(oldName==0 || oldName[0]=='\0') return -EFAULT;
    if(newName==0 || newName[0]=='\0') return -EFAULT;
    string oldPath=absolutePath(oldName);
    string newPath=absolutePath(newName);
    if(oldPath.empty() || newPath.empty()) return -ENAMETOOLONG;
    return FilesystemManager::instance().renameHelper(oldPath,newPath);
}

int FileDescriptorTable::statImpl(const char* name, struct stat* pstat, bool f)
{
    if(name==0 || name[0]=='\0' || pstat==0) return -EFAULT;
    string path=absolutePath(name);
    if(path.empty()) return -ENAMETOOLONG;
    return FilesystemManager::instance().statHelper(path,pstat,f);
}

FileDescriptorTable::~FileDescriptorTable()
{
    FilesystemManager::instance().removeFileDescriptorTable(this);
    //There's no need to lock the mutex and explicitly close files eventually
    //left open, because if there are other threads accessing this while we are
    //being deleted we have bigger problems anyway
}

string FileDescriptorTable::absolutePath(const char* path)
{
    size_t len=strlen(path);
    if(len>PATH_MAX) return "";
    if(path[0]=='/') return path;
    Lock<FastMutex> l(mutex);
    if(len+cwd.length()>PATH_MAX) return "";
    return cwd+path;
}

/**
 * This class implements the path resolution logic
 */
class PathResolution
{
public:
    /**
     * Constructor
     * \param fs map of all mounted filesystems
     */
    PathResolution(const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& fs)
            : filesystems(fs) {}
    
    /**
     * The main purpose of this class, resolve a path
     * \param path inout parameter with the path to resolve. The resolved path
     * will be modified in-place in this string. The path must be absolute and
     * start with a "/". The caller is responsible for that.
     * \param followLastSymlink if true, follow last symlink
     * \return a resolved path
     */
    ResolvedPath resolvePath(string& path, bool followLastSymlink);
    
private:
    /**
     * Handle a /../ in a path
     * \param path path string
     * \param slash path[slash] is the / character after the ..
     * \return 0 on success, a negative number on error
     */
    int upPathComponent(string& path, size_t slash);
    
    /**
     * Handle a normal path component in a path, i.e, a path component
     * that is neither //, /./ or /../
     * \param path path string
     * \param followIfSymlink if true, follow symbolic links
     * \return 0 on success, or a negative number on error
     */
    int normalPathComponent(string& path, bool followIfSymlink);
    
    /**
     * Follow a symbolic link
     * \param path path string. The relative path into the current filesystem 
     * must be a symbolic link (verified by the caller).
     * \return 0 on success, a negative number on failure
     */
    int followSymlink(string& path);
    
    /**
     * Find to which filesystem this path belongs
     * \param path path string.
     * \return 0 on success, a negative number on failure
     */
    int recursiveFindFs(string& path);

    /// Mounted filesystems
    const map<StringPart,intrusive_ref_ptr<FilesystemBase> >& filesystems;
    
    /// Pointer to root filesystem
    intrusive_ref_ptr<FilesystemBase> root;
    
    /// Current filesystem while looking up path
    intrusive_ref_ptr<FilesystemBase> fs;
    
    /// True if current filesystem supports symlinks
    bool syms;
    
    /// path[index] is first unhandled char
    size_t index;
    
    /// path.substr(indexIntoFs) is the relative path to current filesystem
    size_t indexIntoFs;
    
    /// How many components does the relative path have in current fs
    int depthIntoFs;
    
    /// How many symlinks we've found so far
    int linksFollowed;
    
    /// Maximum number of symbolic links to follow (to avoid endless loops)
    static const int maxLinkToFollow=2;
};

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 PathResolution::upPathComponent(string& path, size_t slash)
{
    if(index<=1) return -ENOENT; //root dir has no parent
    size_t removeStart=path.find_last_of('/',index-2);
    if(removeStart==string::npos) return -ENOENT; //should not happen
    path.erase(removeStart,slash-removeStart);
    index=removeStart+1;
    //This may happen when merging a path like "/dir/.."
    if(path.empty()) path='/';
    //This may happen if the new last path component is a fs, e.g. "/dev/null/.."
    if(indexIntoFs>path.length()) indexIntoFs=path.length();
    if(--depthIntoFs>0) return 0;
    
    //Depth went to zero, escape current filesystem
    return recursiveFindFs(path);
}

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 PathResolution::followSymlink(string& path)
{
    if(++linksFollowed>=maxLinkToFollow) return -ELOOP;
    string target;
    {
        StringPart sp(path,index-1,indexIntoFs);
        if(int res=fs->readlink(sp,target)<0) return res;
    }
    if(target.empty()) return -ENOENT; //Should not happen
    if(target[0]=='/')
    {
        //Symlink is absolute
        size_t newPathLen=target.length()+path.length()-index+1;
        if(newPathLen>PATH_MAX) return -ENAMETOOLONG;
        string newPath;
        newPath.reserve(newPathLen);
        newPath=target;
        if(index<=path.length())
            newPath.insert(newPath.length(),path,index-1,string::npos);
        path.swap(newPath);
        fs=root;
        syms=root->supportsSymlinks();
        index=1;
        indexIntoFs=1;
        depthIntoFs=1;
    } else {
        //Symlink is relative
        size_t removeStart=path.find_last_of('/',index-2);
        size_t newPathLen=path.length()-(index-removeStart-2)+target.length();
        if(newPathLen>PATH_MAX) return -ENAMETOOLONG;
        string newPath;
        newPath.reserve(newPathLen);
        newPath.insert(0,path,0,removeStart+1);
        newPath+=target;
        if(index<=path.length())
            newPath.insert(newPath.length(),path,index-1,string::npos);
        path.swap(newPath);
        index=removeStart+1;
        depthIntoFs--;
    }
    return 0;
}

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

//
// class FilesystemManager
//

FilesystemManager& FilesystemManager::instance()
{
    static FilesystemManager instance;
    return instance;
}

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

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

void FilesystemManager::umountAll()
{
    Lock<FastMutex> l(mutex);
    #ifdef WITH_PROCESSES
    list<FileDescriptorTable*>::iterator it;
    for(it=fileTables.begin();it!=fileTables.end();++it) (*it)->closeAll();
    #else //WITH_PROCESSES
    getFileDescriptorTable().closeAll();
    #endif //WITH_PROCESSES
    filesystems.clear();
}

ResolvedPath FilesystemManager::resolvePath(string& path, bool followLastSymlink)
{
    //see man path_resolution. This code supports arbitrarily mounted
    //filesystems, symbolic links resolution, but no hardlinks to directories
    if(path.length()>PATH_MAX) return ResolvedPath(-ENAMETOOLONG);
    if(path.empty() || path[0]!='/') return ResolvedPath(-ENOENT);

    Lock<FastMutex> l(mutex);
    PathResolution pr(filesystems);
    return pr.resolvePath(path,followLastSymlink);
}

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

int FilesystemManager::statHelper(string& path, struct stat *pstat, bool f)
{
    ResolvedPath openData=resolvePath(path,f);
    if(openData.result<0) return openData.result;
    StringPart sp(path,string::npos,openData.off);
    return openData.fs->lstat(sp,pstat);
}

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

short int FilesystemManager::getFilesystemId()
{
    return atomicAddExchange(&devCount,1);
}

int FilesystemManager::devCount=1;

#ifdef WITH_DEVFS
intrusive_ref_ptr<DevFs> //return value is a pointer to DevFs
#else //WITH_DEVFS
void                     //return value is void
#endif //WITH_DEVFS
basicFilesystemSetup(intrusive_ref_ptr<Device> dev)
{
    bootlog("Mounting MountpointFs as / ... ");
    FilesystemManager& fsm=FilesystemManager::instance();
    intrusive_ref_ptr<FilesystemBase> rootFs(new MountpointFs);
    bootlog(fsm.kmount("/",rootFs)==0 ? "Ok\n" : "Failed\n");
    
    #ifdef WITH_DEVFS
    bootlog("Mounting DevFs as /dev ... ");
    StringPart sp("dev");
    int r1=rootFs->mkdir(sp,0755); 
    intrusive_ref_ptr<DevFs> devfs(new DevFs);
    int r2=fsm.kmount("/dev",devfs);
    bool devFsOk=(r1==0 && r2==0);
    bootlog(devFsOk ? "Ok\n" : "Failed\n");
    if(!devFsOk) return devfs;
    fsm.setDevFs(devfs);
    #endif //WITH_DEVFS
    
    bootlog("Mounting Fat32Fs as /sd ... ");
    bool fat32failed=false;
    intrusive_ref_ptr<FileBase> disk;
    #ifdef WITH_DEVFS
    if(dev) devfs->addDevice("sda",dev);
    StringPart sda("sda");
    if(devfs->open(disk,sda,O_RDWR,0)<0) fat32failed=true;
    #else //WITH_DEVFS
    if(dev && dev->open(disk,intrusive_ref_ptr<FilesystemBase>(0),O_RDWR,0)<0)
        fat32failed=true;
    #endif //WITH_DEVFS
    
    intrusive_ref_ptr<Fat32Fs> fat32;
    if(fat32failed==false)
    {
        fat32=new Fat32Fs(disk);
        if(fat32->mountFailed()) fat32failed=true;
    }
    if(fat32failed==false)
    {
        StringPart sd("sd");
        fat32failed=rootFs->mkdir(sd,0755)!=0;
        fat32failed=fsm.kmount("/sd",fat32)!=0;
    }
    bootlog(fat32failed==0 ? "Ok\n" : "Failed\n");
    
    #ifdef WITH_DEVFS
    return devfs;
    #endif //WITH_DEVFS
}

FileDescriptorTable& getFileDescriptorTable()
{
    #ifdef WITH_PROCESSES
    //Something like
    return Thread::getCurrentThread()->getProcess()->getFileTable();
    #else //WITH_PROCESSES
    static FileDescriptorTable fileTable; ///< The only file table
    return fileTable;
    #endif //WITH_PROCESSES
}

} //namespace miosix

#endif //WITH_FILESYSTEM
