/* Copyright (C) 2014 Stony Brook University
This file is part of Graphene Library OS.
Graphene Library OS is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Graphene Library OS 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see . */
/*
* shim_fs.h
*
* Definitions of types and functions for file system bookkeeping.
*/
#ifndef _SHIM_FS_H_
#define _SHIM_FS_H_
#include
#include
#include
#include
#include
#include
#include
struct shim_handle;
#define FS_POLL_RD 0x01
#define FS_POLL_WR 0x02
#define FS_POLL_ER 0x04
#define FS_POLL_SZ 0x08
struct shim_fs_ops {
/* mount: mount an uri to the certain location */
int (*mount)(const char* uri, void** mount_data);
int (*unmount)(void* mount_data);
/* close: clean up the file state inside the handle */
int (*close)(struct shim_handle* hdl);
/* read: the content from the file opened as handle */
ssize_t (*read)(struct shim_handle* hdl, void* buf, size_t count);
/* write: the content from the file opened as handle */
ssize_t (*write)(struct shim_handle* hdl, const void* buf, size_t count);
/* mmap: mmap handle to address */
int (*mmap)(struct shim_handle* hdl, void** addr, size_t size, int prot, int flags,
off_t offset);
/* flush: flush out user buffer */
int (*flush)(struct shim_handle* hdl);
/* seek: the content from the file opened as handle */
off_t (*seek)(struct shim_handle* hdl, off_t offset, int wence);
/* move, copy: rename or duplicate the file */
int (*move)(const char* trim_old_name, const char* trim_new_name);
int (*copy)(const char* trim_old_name, const char* trim_new_name);
/* Returns 0 on success, -errno on error */
int (*truncate)(struct shim_handle* hdl, off_t len);
/* hstat: get status of the file */
int (*hstat)(struct shim_handle* hdl, struct stat* buf);
/* setflags: set flags of the file */
int (*setflags)(struct shim_handle* hdl, int flags);
/* hput: delete the handle and close the PAL handle. */
void (*hput)(struct shim_handle* hdl);
/* lock and unlock the file */
int (*lock)(const char* trim_name);
int (*unlock)(const char* trim_name);
/* lock and unlock the file system */
int (*lockfs)(void);
int (*unlockfs)(void);
/* checkout/reowned/checkin a single handle for migration */
int (*checkout)(struct shim_handle* hdl);
int (*checkin)(struct shim_handle* hdl);
/* poll a single handle */
/* POLL_RD|POLL_WR: return POLL_RD|POLL_WR for readable|writable,
POLL_ER for failure, -EAGAIN for unknown. */
/* POLL_SZ: return total size */
off_t (*poll)(struct shim_handle* hdl, int poll_type);
/* checkpoint/migrate the file system */
ssize_t (*checkpoint)(void** checkpoint, void* mount_data);
int (*migrate)(void* checkpoint, void** mount_data);
};
#define DENTRY_VALID 0x0001 /* this dentry is verified to be valid */
#define DENTRY_NEGATIVE 0x0002 /* recently deleted or inaccessible */
#define DENTRY_RECENTLY 0x0004 /* recently used */
#define DENTRY_PERSIST 0x0008 /* added as a persistent dentry */
#define DENTRY_HASHED 0x0010 /* added in the dcache */
#define DENTRY_MOUNTPOINT 0x0040 /* this dentry is a mount point */
#define DENTRY_ISLINK 0x0080 /* this dentry is a link */
#define DENTRY_ISDIRECTORY 0x0100 /* this dentry is a directory */
#define DENTRY_LOCKED 0x0200 /* locked by mountpoints at children */
/* These flags are not used */
//#define DENTRY_REACHABLE 0x0400 /* permission checked to be reachable */
//#define DENTRY_UNREACHABLE 0x0800 /* permission checked to be unreachable */
#define DENTRY_LISTED 0x1000 /* children in directory listed */
#define DENTRY_INO_UPDATED 0x2000 /* ino updated */
#define DENTRY_ANCESTOR 0x4000 /* Auto-generated dentry to connect a mount point in the \
* manifest to the root, when one or more intermediate \
* directories do not exist on the underlying FS. The semantics \
* of subsequent changes to such directories (or attempts to \
* really create them) are not currently well-defined. */
// Catch memory corruption issues by checking for invalid state values
#define DENTRY_INVALID_FLAGS (~0x7FFF)
#define DCACHE_HASH_SIZE 1024
#define DCACHE_HASH(hash) ((hash) & (DCACHE_HASH_SIZE - 1))
DEFINE_LIST(shim_dentry);
DEFINE_LISTP(shim_dentry);
struct shim_dentry {
int state; /* flags for managing state */
struct shim_mount* fs; /* this dentry's mounted fs */
struct shim_qstr rel_path; /* the path is relative to its mount point */
struct shim_qstr name; /* caching the file's name. */
/* DEP 6/16/17: For now, let's try not hashing; I suspect it is overkill for most purposes.
* I'll leave the field here for now, but propose we move to a per-directory table to accelerate
* lookups, rather than a global table, since this just supports one process.
*/
LIST_TYPE(shim_dentry) hlist; /* to resolve collisions in the hash table */
LIST_TYPE(shim_dentry) list; /* put dentry to different list according to its availability, \
* persistent or freeable */
struct shim_dentry* parent;
int nchildren;
LISTP_TYPE(shim_dentry) children; /* These children and siblings link */
LIST_TYPE(shim_dentry) siblings;
struct shim_mount* mounted;
void* data;
unsigned long ino;
mode_t type;
mode_t mode;
struct shim_lock lock;
REFTYPE ref_count;
};
struct shim_d_ops {
/* open: provide a filename relative to the mount point and flags,
modify the shim handle, file_data is "inode" equivalent */
int (*open)(struct shim_handle* hdl, struct shim_dentry* dent, int flags);
/* look up dentry and allocate internal data.
*
* On a successful lookup (non-error, can be negative),
* this function should call get_new_dentry(), populating additional fields,
* and storing the new dentry in dent.
*/
int (*lookup)(struct shim_dentry* dent);
/* this is to check file type and access, returning the stat.st_mode */
int (*mode)(struct shim_dentry* dent, mode_t* mode);
/* detach internal data from dentry */
int (*dput)(struct shim_dentry* dent);
/* create a dentry inside a directory */
int (*creat)(struct shim_handle* hdl, struct shim_dentry* dir, struct shim_dentry* dent,
int flags, mode_t mode);
/* unlink a dentry inside a directory */
int (*unlink)(struct shim_dentry* dir, struct shim_dentry* dent);
/* create a directory inside a directory */
int (*mkdir)(struct shim_dentry* dir, struct shim_dentry* dent, mode_t mode);
/* stat: get status of the file */
int (*stat)(struct shim_dentry* dent, struct stat* buf);
/* extracts the symlink name and saves in link */
int (*follow_link)(struct shim_dentry* dent, struct shim_qstr* link);
/* set up symlink name to a dentry */
int (*set_link)(struct shim_dentry* dent, const char* link);
/* change the mode or owner of a dentry */
int (*chmod)(struct shim_dentry* dent, mode_t mode);
int (*chown)(struct shim_dentry* dent, int uid, int gid);
/* change the name of a dentry */
int (*rename)(struct shim_dentry* old, struct shim_dentry* new);
/* readdir: given the path relative to the mount point, read the childs
into the the buffer. This call always returns everything under
the directory in one big buffer; you do not need to try again
or keep a cursor in the directory. You do need to free the
returned buffer. */
int (*readdir)(struct shim_dentry* dent, struct shim_dirent** dirent);
};
#define MAX_PATH 4096
#define MAX_FILENAME 255
DEFINE_LIST(shim_mount);
struct shim_mount {
char type[8]; // Null-terminated.
struct shim_dentry* mount_point;
struct shim_qstr path;
struct shim_qstr uri;
struct shim_fs_ops* fs_ops;
struct shim_d_ops* d_ops;
struct shim_dentry* root;
void* data;
void* cpdata;
size_t cpsize;
REFTYPE ref_count;
LIST_TYPE(shim_mount) hlist;
LIST_TYPE(shim_mount) list;
};
extern struct shim_dentry* dentry_root;
#define LOOKUP_FOLLOW 001
#define LOOKUP_DIRECTORY 002
#define LOOKUP_CONTINUE 004 // No longer needed
#define LOOKUP_PARENT 010 // Not sure we need this
#define F_OK 0
// XXX: Duplicate definition; should probably weed out includes of host system
// include of unistd.h in future work
//#define R_OK 001
//#define W_OK 002
//#define X_OK 004
#define MAY_EXEC 001
#define MAY_WRITE 002
#define MAY_READ 004
#if 0
#define MAY_APPEND 010
#endif
#define NO_MODE ((mode_t)-1)
#define ACC_MODE(x) \
((((x) == O_RDONLY || (x) == O_RDWR) ? MAY_READ : 0) | \
(((x) == O_WRONLY || (x) == O_RDWR) ? MAY_WRITE : 0))
#define LOOKUP_OPEN 0100 // Appears to be ignored
#define LOOKUP_CREATE 0200
#define LOOKUP_ACCESS 0400 // Appears to be ignored
#define LOOKUP_SYNC (LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_ACCESS)
enum lookup_type {
LAST_NORM,
LAST_ROOT,
LAST_DOT,
LAST_DOTDOT,
LAST_BIND
};
struct lookup {
struct shim_dentry* dentry;
struct shim_mount* mount;
const char* last;
int depth;
int flags;
enum lookup_type last_type;
};
/* initialization for fs and mounts */
int init_fs(void);
int init_mount_root(void);
int init_mount(void);
/* path utilities */
const char* get_file_name(const char* path, size_t len);
/* file system operations */
int mount_fs(const char* mount_type, const char* mount_uri, const char* mount_point,
struct shim_dentry* parent, struct shim_dentry** dentp, bool make_ancestor);
int unmount_fs(const char* mount_point);
int search_builtin_fs(const char* type, struct shim_mount** fs);
void get_mount(struct shim_mount* mount);
void put_mount(struct shim_mount* mount);
struct shim_mount* find_mount_from_uri(const char* uri);
#include
static inline void set_handle_fs(struct shim_handle* hdl, struct shim_mount* fs) {
get_mount(fs);
hdl->fs = fs;
memcpy(hdl->fs_type, fs->type, sizeof(hdl->fs_type));
}
int walk_mounts(int (*walk)(struct shim_mount* mount, void* arg), void* arg);
/* functions for dcache supports */
int init_dcache(void);
extern struct shim_lock dcache_lock;
/* Checks permission (specified by mask) of a dentry. If force is not set, permission is considered
* granted on invalid dentries.
* Assumes that caller has acquired dcache_lock. */
int permission(struct shim_dentry* dent, mode_t mask);
/* This function looks up a single dentry based on its parent dentry pointer and the name. `namelen`
* is the length of char* name. The dentry is returned in pointer *new.
*
* The caller should hold the dcache_lock.
*/
int lookup_dentry(struct shim_dentry* parent, const char* name, int namelen,
struct shim_dentry** new, struct shim_mount* fs);
/* Looks up path under start dentry. Saves in dent.
*
* Assumes dcache_lock is held; main difference from path_lookupat is that dcache_lock is not
* released on return.
*
* The refcount is dropped by one on the returned dentry.
*
* The make_ancestor flag creates pseudo-dentries for any parent paths that are not in cache and do
* not exist on the underlying file system. This is intended for use only in setting up the
* file system view specified in the manifest.
*
* If the file isnt' found, returns -ENOENT.
*
* If the LOOKUP_DIRECTORY flag is set, and the found file isn't a directory, returns -ENOTDIR.
*/
int __path_lookupat(struct shim_dentry* start, const char* path, int flags,
struct shim_dentry** dent, int link_depth, struct shim_mount* fs,
bool make_ancestor);
/* Just wraps __path_lookupat, but also acquires and releases the dcache_lock. */
int path_lookupat(struct shim_dentry* start, const char* name, int flags, struct shim_dentry** dent,
struct shim_mount* fs);
/*
* This function returns a dentry (in *dir) from a handle corresponding to dirfd.
* If dirfd == AT_FDCWD returns current working directory.
*
* Returned dentry must be a directory.
*
* Increments dentry ref count by one.
*/
int get_dirfd_dentry(int dirfd, struct shim_dentry** dir);
/* Open path with given flags, in mode, similar to Unix open.
*
* The start dentry specifies where to begin the search.
* `hdl` is an optional argument; if passed in, it is initialized to refer to the opened path.
*
* The result is stored in `dent`.
*/
int open_namei(struct shim_handle* hdl, struct shim_dentry* start, const char* path, int flags,
int mode, struct shim_dentry** dent);
/* This function calls the low-level file system to do the work of opening file indicated by dent,
* and initializing it in hdl. Flags are standard open flags.
*
* If O_TRUNC is specified, this function is responsible for calling the underlying truncate
* function.
*/
int dentry_open(struct shim_handle* hdl, struct shim_dentry* dent, int flags);
/* This function enumerates a directory and caches the results in the dentry.
*
* Input: A dentry for a directory in the DENTRY_ISDIRECTORY and not in the DENTRY_LISTED state.
* The dentry DENTRY_LISTED flag is set upon success.
*
* Return value: 0 on success, <0 on error
*/
int list_directory_dentry(struct shim_dentry* dir);
/* This function caches the contents of a directory (dent), already in the listed state, in a buffer
* associated with a handle (hdl).
*
* This function should only be called once on a handle.
*
* Returns 0 on success, <0 on failure.
*/
int list_directory_handle(struct shim_dentry* dent, struct shim_handle* hdl);
/* Increment the reference count on dent */
void get_dentry(struct shim_dentry* dent);
/* Decrement the reference count on dent */
void put_dentry(struct shim_dentry* dent);
static_always_inline void fast_pathcpy(char* dst, const char* src, size_t size, char** ptr) {
char* d = dst;
const char* s = src;
for (size_t i = 0; i < size; i++, s++, d++)
*d = *s;
*ptr = d;
}
static_always_inline char* dentry_get_path(struct shim_dentry* dent, bool on_stack,
size_t* sizeptr) {
struct shim_mount* fs = dent->fs;
char* buffer;
char* c;
size_t bufsize = dent->rel_path.len + 1;
if (fs)
bufsize += fs->path.len + 1;
if (on_stack) {
c = buffer = __alloca(bufsize);
} else {
if (!(c = buffer = malloc(bufsize)))
return NULL;
}
if (fs && !qstrempty(&fs->path))
fast_pathcpy(c, qstrgetstr(&fs->path), fs->path.len, &c);
if (dent->rel_path.len) {
const char* path = qstrgetstr(&dent->rel_path);
int len = dent->rel_path.len;
if (c > buffer && *(c - 1) == '/') {
if (*path == '/')
path++;
} else {
if (*path != '/')
*(c++) = '/';
}
fast_pathcpy(c, path, len, &c);
}
if (sizeptr)
*sizeptr = c - buffer;
*c = 0;
return buffer;
}
static_always_inline const char* dentry_get_name(struct shim_dentry* dent) {
return qstrgetstr(&dent->name);
}
/* Allocate and initialize a new dentry for path name, under parent. Return the dentry.
*
* `mount` is the mountpoint the dentry is under; this is typically the parent->fs, but is passed
* explicitly for initializing the dentry of a mountpoint.
*
* If hashptr is passed (as an optimization), this is a hash of the name.
*
* If parent is non-null, the ref count is 2; else it is 1.
*
* This function also sets up both a name and a relative path
*/
struct shim_dentry* get_new_dentry(struct shim_mount* mount, struct shim_dentry* parent,
const char* name, int namelen, HASHTYPE* hashptr);
/* This function searches for name/namelen (as the relative path).
*
* If requested, the expected hash of the dentry is returned in hashptr, primarily so that the
* hashing can be reused to add the dentry later.
*
* The reference count on the found dentry is incremented by one.
*
* Used only by shim_namei.c
*/
struct shim_dentry* __lookup_dcache(struct shim_dentry* start, const char* name, int namelen,
HASHTYPE* hashptr);
/* This function recursively deletes and frees all dentries under root
*
* XXX: Current code doesn't do a free..
*/
int __del_dentry_tree(struct shim_dentry* root);
/*
* Returns true if `anc` is an ancestor of `dent`.
*/
bool dentry_is_ancestor(struct shim_dentry* anc, struct shim_dentry* dent);
/* XXX: Future work: current dcache never shrinks. Would be nice to be able to do something like LRU
* under space pressure, although for a single app, this may be over-kill. */
/* hashing utilities */
#define MOUNT_HASH_BYTE 1
#define MOUNT_HASH_WIDTH 8
#define MOUNT_HASH_SIZE 256
#define MOUNT_HASH(hash) ((hash) & (MOUNT_HASH_SIZE - 1))
HASHTYPE hash_path(const char* path, size_t size);
HASHTYPE rehash_name(HASHTYPE parent_hbuf, const char* name, size_t size);
HASHTYPE rehash_path(HASHTYPE ancester_hbuf, const char* path, size_t size);
extern struct shim_fs_ops chroot_fs_ops;
extern struct shim_d_ops chroot_d_ops;
extern struct shim_fs_ops str_fs_ops;
extern struct shim_d_ops str_d_ops;
extern struct shim_fs_ops dev_fs_ops;
extern struct shim_d_ops dev_d_ops;
extern struct shim_fs_ops config_fs_ops;
extern struct shim_d_ops config_d_ops;
extern struct shim_fs_ops proc_fs_ops;
extern struct shim_d_ops proc_d_ops;
extern struct shim_mount chroot_builtin_fs;
extern struct shim_mount pipe_builtin_fs;
extern struct shim_mount socket_builtin_fs;
extern struct shim_mount epoll_builtin_fs;
extern struct shim_mount eventfd_builtin_fs;
/* proc file system */
struct proc_nm_ops {
int (*match_name)(const char* name);
int (*list_name)(const char* name, struct shim_dirent** buf, int count);
};
struct proc_fs_ops {
int (*open)(struct shim_handle* hdl, const char* name, int flags);
int (*mode)(const char* name, mode_t* mode);
int (*stat)(const char* name, struct stat* buf);
int (*follow_link)(const char* name, struct shim_qstr* link);
};
struct proc_dir;
struct proc_ent {
const char* name; /* A proc_callback should at least have a name or nm_ops. Otherwise, it is a
* NULL-end. */
const struct proc_nm_ops* nm_ops;
const struct proc_fs_ops* fs_ops;
const struct proc_dir* dir;
};
struct proc_dir {
int size;
const struct proc_ent ent[];
};
/* string-type file system */
int str_add_dir(const char* path, mode_t mode, struct shim_dentry** dent);
int str_add_file(const char* path, mode_t mode, struct shim_dentry** dent);
int str_open(struct shim_handle* hdl, struct shim_dentry* dent, int flags);
int str_dput(struct shim_dentry* dent);
int str_close(struct shim_handle* hdl);
ssize_t str_read(struct shim_handle* hdl, void* buf, size_t count);
ssize_t str_write(struct shim_handle* hdl, const void* buf, size_t count);
off_t str_seek(struct shim_handle* hdl, off_t offset, int whence);
int str_flush(struct shim_handle* hdl);
#endif /* _SHIM_FS_H_ */