/* 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_ */