OpenRTX/lib/miosix-kernel/miosix/filesystem/file_access.h

554 wiersze
18 KiB
C++

/***************************************************************************
* 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/> *
***************************************************************************/
#ifndef FILE_ACCESS_H
#define FILE_ACCESS_H
#include <map>
#include <list>
#include <string>
#include <errno.h>
#include <sys/stat.h>
#include "file.h"
#include "stringpart.h"
#include "devfs/devfs.h"
#include "kernel/sync.h"
#include "kernel/intrusive.h"
#include "config/miosix_settings.h"
#ifdef WITH_FILESYSTEM
namespace miosix {
/**
* The result of resolvePath().
*/
class ResolvedPath
{
public:
/**
* Constructor
*/
ResolvedPath() : result(-EINVAL), fs(0), off(0) {}
/**
* Constructor
* \param result error code
*/
explicit ResolvedPath(int result) : result(result), fs(0), off(0) {}
/**
* Constructor
* \param fs filesystem
* \param off offset into path where the subpath relative to the current
* filesystem starts
*/
ResolvedPath(intrusive_ref_ptr<FilesystemBase> fs, size_t offset)
: result(0), fs(fs), off(offset) {}
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
size_t off;
};
/**
* This class maps file descriptors to file objects, allowing to
* perform file operations
*/
class FileDescriptorTable
{
public:
/**
* Constructor
*/
FileDescriptorTable();
/**
* Copy constructor
* \param rhs object to copy from
*/
FileDescriptorTable(const FileDescriptorTable& rhs);
/**
* Operator=
* \param rhs object to copy from
* \return *this
*/
FileDescriptorTable& operator=(const FileDescriptorTable& rhs);
/**
* Open a file
* \param name file name
* \param flags file open mode
* \param mode allows to set file permissions
* \return a file descriptor, or a negative number on error
*/
int open(const char *name, int flags, int mode);
/**
* Close a file
* \param fd file descriptor to close
* \return 0 on success or a negative number on failure
*/
int close(int fd);
/**
* Close all files
*/
void closeAll();
/**
* Write data to the file, if the file supports writing.
* \param data the data to write
* \param len the number of bytes to write
* \return the number of written characters, or a negative number in case
* of errors
*/
ssize_t write(int fd, const void *data, size_t len)
{
if(data==0) return -EFAULT;
//Important, since len is specified by standard to be unsigned, but the
//return value has to be signed
if(static_cast<ssize_t>(len)<0) return -EINVAL;
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->write(data,len);
}
/**
* Read data from the file, if the file supports reading.
* \param data buffer to store read data
* \param len the number of bytes to read
* \return the number of read characters, or a negative number in case
* of errors
*/
ssize_t read(int fd, void *data, size_t len)
{
if(data==0) return -EFAULT;
//Important, since len is specified by standard to be unsigned, but the
//return value has to be signed
if(static_cast<ssize_t>(len)<0) return -EINVAL;
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->read(data,len);
}
/**
* Move file pointer, if the file supports random-access.
* \param pos offset to sum to the beginning of the file, current position
* or end of file, depending on whence
* \param whence SEEK_SET, SEEK_CUR or SEEK_END
* \return the offset from the beginning of the file if the operation
* completed, or a negative number in case of errors
*/
off_t lseek(int fd, off_t pos, int whence)
{
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->lseek(pos,whence);
}
/**
* Return file information.
* \param pstat pointer to stat struct
* \return 0 on success, or a negative number on failure
*/
int fstat(int fd, struct stat *pstat) const
{
if(pstat==0) return -EFAULT;
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->fstat(pstat);
}
/**
* Check whether the file refers to a terminal.
* \return 1 if it is a terminal, 0 if it is not, or a negative number in
* case of errors
*/
int isatty(int fd) const
{
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->isatty();
}
/**
* Return file information, follows last symlink
* \param path file to stat
* \param pstat pointer to stat struct
* \return 0 on success, or a negative number on failure
*/
int stat(const char *name, struct stat *pstat)
{
return statImpl(name,pstat,true);
}
/**
* Return file information, does not follow last symlink
* \param path file to stat
* \param pstat pointer to stat struct
* \return 0 on success, or a negative number on failure
*/
int lstat(const char *name, struct stat *pstat)
{
return statImpl(name,pstat,false);
}
/**
* Perform various operations on a file descriptor
* \param cmd specifies the operation to perform
* \param opt optional argument that some operation require
* \return the exact return value depends on CMD, -1 is returned on error
*/
int fcntl(int fd, int cmd, int opt)
{
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->fcntl(cmd,opt);
}
/**
* Perform various operations on a file descriptor
* \param cmd specifies the operation to perform
* \param arg optional argument that some operation require
* \return the exact return value depends on CMD, -1 is returned on error
*/
int ioctl(int fd, int cmd, void *arg)
{
//arg unchecked here, as some ioctl don't use it
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->ioctl(cmd,arg);
}
/**
* List directory content
* \param dp dp pointer to a memory buffer where one or more struct dirent
* will be placed. dp must be four words aligned.
* \param len memory buffer size.
* \return the number of bytes read on success, or a negative number on
* failure.
*/
int getdents(int fd, void *dp, int len)
{
if(dp==0) return -EFAULT;
if(reinterpret_cast<unsigned>(dp) & 0x3) return -EFAULT; //Not aligned
intrusive_ref_ptr<FileBase> file=getFile(fd);
if(!file) return -EBADF;
return file->getdents(dp,len);
}
/**
* Return current directory
* \param buf the current directory is stored here
* \param len buffer length, if it is not big enough, ERANGE is returned
* \return 0 on success, or a negative number on failure
*/
int getcwd(char *buf, size_t len);
/**
* Change current directory
* \param path new current directory
* \return 0 on success, or a negative number on failure
*/
int chdir(const char *name);
/**
* Create a directory
* \param name directory to create
* \param mode directory permissions
* \return 0 on success, or a negative number on failure
*/
int mkdir(const char *name, int mode);
/**
* Remove a directory if empty
* \param name directory to create
* \return 0 on success, or a negative number on failure
*/
int rmdir(const char *name);
/**
* Remove a file or directory
* \param name file or directory to remove
* \return 0 on success, or a negative number on failure
*/
int unlink(const char *name);
/**
* Rename a file or directory
* \param oldName old file name
* \param newName new file name
* \return 0 on success, or a negative number on failure
*/
int rename(const char *oldName, const char *newName);
/**
* Retrieves an entry in the file descriptor table
* \param fd file descriptor, index into the table
* \return a refcounted poiter to the file at the desired entry
* (which may be empty), or an empty refcounted pointer if the index is
* out of bounds
*/
intrusive_ref_ptr<FileBase> getFile(int fd) const
{
if(fd<0 || fd>=MAX_OPEN_FILES) return intrusive_ref_ptr<FileBase>();
return atomic_load(files+fd);
}
/**
* Destructor
*/
~FileDescriptorTable();
private:
/**
* Append cwd to path if it is not an absolute path
* \param path an absolute or relative path, must not be null
* \return an absolute path, or an empty string if the path would exceed
* PATH_MAX
*/
std::string absolutePath(const char *path);
/**
* Return file information (implements both stat and lstat)
* \param path file to stat
* \param pstat pointer to stat struct
* \param f true to follow last synlink (stat),
* false to not follow it (lstat)
* \return 0 on success, or a negative number on failure
*/
int statImpl(const char *name, struct stat *pstat, bool f);
FastMutex mutex; ///< Locks on writes to file object pointers, not on accesses
std::string cwd; ///< Current working directory
/// Holds the mapping between fd and file objects
intrusive_ref_ptr<FileBase> files[MAX_OPEN_FILES];
};
/**
* This class contains information on all the mounted filesystems
*/
class FilesystemManager
{
public:
/**
* \return the instance of the filesystem manager (singleton)
*/
static FilesystemManager& instance();
/**
* Low level mount operation, meant to be used only inside the kernel,
* and board support packages. It is the only mount operation that can
* mount the root filesystem.
* \param path path where to mount the filesystem
* \param fs filesystem to mount. Ownership of the pointer is transferred
* to the FilesystemManager class
* \return 0 on success, a negative number on failure
*/
int kmount(const char *path, intrusive_ref_ptr<FilesystemBase> fs);
/**
* Unmounts a filesystem
* \param path path to a filesytem
* \param force true to umount the filesystem even if busy
* \return 0 on success, or a negative number on error
*/
int umount(const char *path, bool force=false);
/**
* Umount all filesystems, to be called before system shutdown or reboot
*/
void umountAll();
#ifdef WITH_DEVFS
/**
* \return a pointer to the devfs, useful to add other device files
*/
intrusive_ref_ptr<DevFs> getDevFs() const { return atomic_load(&devFs); }
/**
* Called by basicFilesystemSetup() or directly by the BSP to set the
* pointer returned by getDevFs()
* \param dev pointer to the DevFs
*/
void setDevFs(intrusive_ref_ptr<DevFs> dev)
{
atomic_store(&devFs,dev);
}
#endif //WITH_DEVFS
/**
* Resolve a path to identify the filesystem it belongs
* \param path an absolute path name, that must start with '/'. Note that
* this is an inout parameter, the string is modified so as to return the
* full resolved path. In particular, the returned string differs from the
* passed one by not containing useless path components, such as "/./" and
* "//", by not containing back path componenets ("/../"), and may be
* entirely different from the passed one if a symlink was encountered
* during name resolution. The use of an inout parameter is to minimize
* the number of copies of the path string, optimizing for speed and size
* in the common case, but also means that a copy of the original string
* needs to be made if the original has to be used later.
* \param followLastSymlink true if the symlink in the last path component
*(the one that does not end with a /, if it exists, has to be followed)
* \return the resolved path
*/
ResolvedPath resolvePath(std::string& path, bool followLastSymlink=true);
/**
* \internal
* Helper function to unlink a file or directory. Only meant to be used by
* FileDescriptorTable::unlink()
* \param path path of file or directory to unlink
* \return 0 on success, or a neagtive number on failure
*/
int unlinkHelper(std::string& path);
/**
* \internal
* Helper function to stat a file or directory. Only meant to be used by
* FileDescriptorTable::statImpl()
* \param path path of file or directory to stat
* \param pstat pointer to stat struct
* \param f f true to follow last synlink (stat),
* false to not follow it (lstat)
* \return 0 on success, or a negative number on failure
*/
int statHelper(std::string& path, struct stat *pstat, bool f);
/**
* \internal
* Helper function to unlink a file or directory. Only meant to be used by
* FileDescriptorTable::unlink()
* \param oldPath path of file or directory to unlink
* \param newPath path of file or directory to unlink
* \return 0 on success, or a neagtive number on failure
*/
int renameHelper(std::string& oldPath, std::string& newPath);
/**
* \internal
* Called by FileDescriptorTable's constructor. Never call this function
* from user code.
*/
void addFileDescriptorTable(FileDescriptorTable *fdt)
{
#ifdef WITH_PROCESSES
if(isKernelRunning())
{
Lock<FastMutex> l(mutex);
fileTables.push_back(fdt);
} else {
//This function is also called before the kernel is started,
//and in this case it is forbidden to lock mutexes
fileTables.push_back(fdt);
}
#endif //WITH_PROCESSES
}
/**
* \internal
* Called by FileDescriptorTable's constructor. Never call this function
* from user code.
*/
void removeFileDescriptorTable(FileDescriptorTable *fdt)
{
#ifdef WITH_PROCESSES
Lock<FastMutex> l(mutex);
fileTables.remove(fdt);
#endif //WITH_PROCESSES
}
/**
* \internal
* \return an unique id used to identify a filesystem, mostly for filling
* in the st_dev field when stat is called.
*/
static short int getFilesystemId();
private:
/**
* Constructor, private as it is a singleton
*/
FilesystemManager() : mutex(FastMutex::RECURSIVE) {}
FilesystemManager(const FilesystemManager&);
FilesystemManager& operator=(const FilesystemManager&);
FastMutex mutex; ///< To protect against concurrent access
/// Mounted filesystem
std::map<StringPart,intrusive_ref_ptr<FilesystemBase> > filesystems;
#ifdef WITH_PROCESSES
std::list<FileDescriptorTable*> fileTables; ///< Process file tables
#endif //WITH_PROCESSES
#ifdef WITH_DEVFS
intrusive_ref_ptr<DevFs> devFs;
#endif //WITH_DEVFS
static int devCount; ///< For assigning filesystemId to filesystems
};
/**
* This is a simplified function to mount the root and /dev filesystems,
* meant to be called from bspInit2(). It mounts a MountpointFs as root, then
* creates a /dev directory, and mounts /dev there. It also takes the passed
* device and if it is not null it adds the device di DevFs as /dev/sda.
* Last, it attempts to mount /dev/sda at /sd as a Fat32 filesystem.
* In case the bsp needs another filesystem setup, such as having a fat32
* filesystem as /, this function can't be used, but instead the bsp needs to
* mount the filesystems manually.
* \param dev disk device that will be added as /dev/sda and mounted on /sd
* \return a pointer to the DevFs, so as to be able to add other device files,
* but only if WITH_DEVFS is defined
*/
#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);
/**
* \return a pointer to the file descriptor table associated with the
* current process.
*/
FileDescriptorTable& getFileDescriptorTable();
} //namespace miosix
#endif //WITH_FILESYSTEM
#endif //FILE_ACCESS_H