printk("vfs_init() completed\n");
}
+/* FS's can provide another, if they want */
+int generic_dentry_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash = 5381;
+
+ for (int i = 0; i < qstr->len; i++) {
+ /* hash * 33 + c, djb2's technique */
+ hash = ((hash << 5) + hash) + qstr->name[i];
+ }
+ return hash;
+}
+
/* Builds / populates the qstr of a dentry based on its d_iname. If there is an
* l_name, (long), it will use that instead of the inline name. This will
* probably change a bit. */
void qstr_builder(struct dentry *dentry, char *l_name)
{
dentry->d_name.name = l_name ? l_name : dentry->d_iname;
- // TODO: pending what we actually do in d_hash
- //dentry->d_name.hash = dentry->d_op->d_hash(dentry, &dentry->d_name);
- dentry->d_name.hash = 0xcafebabe;
dentry->d_name.len = strnlen(dentry->d_name.name, MAX_FILENAME_SZ);
+ dentry->d_name.hash = dentry->d_op->d_hash(dentry, &dentry->d_name);
}
/* Useful little helper - return the string ptr for a given file */
{
/* Build and init the first dentry / inode. The dentry ref is stored later
* by vfsmount's mnt_root. The parent is dealt with later. */
- struct dentry *d_root = get_dentry(sb, 0, "/"); /* probably right */
+ struct dentry *d_root = get_dentry_with_ops(sb, 0, "/", d_op);
if (!d_root)
panic("OOM! init_sb() can't fail yet!");
}
}
-/* Helper to alloc and initialize a generic dentry. The following needs to be
- * set still: d_op (if no parent), d_fs_info (opt), d_inode, connect the inode
- * to the dentry (and up the d_kref again), maybe dcache_put(). The inode
- * stitching is done in get_inode() or lookup (depending on the FS).
- * The setting of the d_op might be problematic when dealing with mounts. Just
- * overwrite it.
- *
- * If the name is longer than the inline name, it will kmalloc a buffer, so
- * don't worry about the storage for *name after calling this. */
-struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
- char *name)
+/* Gets a dentry. If there is no parent, use d_op. Only called directly by
+ * superblock init code. */
+struct dentry *get_dentry_with_ops(struct super_block *sb,
+ struct dentry *parent, char *name,
+ struct dentry_operations *d_op)
{
assert(name);
struct dentry *dentry = kmem_cache_alloc(dentry_kcache, 0);
if (parent) { /* no parent for rootfs mount */
kref_get(&parent->d_kref, 1);
dentry->d_op = parent->d_op; /* d_op set in init_sb for parentless */
+ } else {
+ dentry->d_op = d_op;
}
dentry->d_parent = parent;
dentry->d_flags = DENTRY_USED;
return dentry;
}
+/* Helper to alloc and initialize a generic dentry. The following needs to be
+ * set still: d_op (if no parent), d_fs_info (opt), d_inode, connect the inode
+ * to the dentry (and up the d_kref again), maybe dcache_put(). The inode
+ * stitching is done in get_inode() or lookup (depending on the FS).
+ * The setting of the d_op might be problematic when dealing with mounts. Just
+ * overwrite it.
+ *
+ * If the name is longer than the inline name, it will kmalloc a buffer, so
+ * don't worry about the storage for *name after calling this. */
+struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
+ char *name)
+{
+ return get_dentry_with_ops(sb, parent, name, 0);
+}
+
/* Called when the dentry is unreferenced (after kref == 0). This works closely
* with the resurrection in dcache_get().
*
/* Consider pushing some error checking higher in the VFS */
if (!count)
return 0;
- /* Extend the file. Should put more checks in here, and maybe do this per
- * page in the for loop below. */
- if (orig_off + count > file->f_dentry->d_inode->i_size) {
- /* lock for writes to i_size. we allow lockless reads. checking the
- * i_size again in case of concurrent writers since our orig check. */
+ if (file->f_flags & O_APPEND) {
spin_lock(&file->f_dentry->d_inode->i_lock);
- if (orig_off + count > file->f_dentry->d_inode->i_size)
- file->f_dentry->d_inode->i_size = orig_off + count;
+ orig_off = file->f_dentry->d_inode->i_size;
+ /* setting the filesize here, instead of during the extend-check, since
+ * we need to atomically reserve space and set our write position. */
+ file->f_dentry->d_inode->i_size += count;
spin_unlock(&file->f_dentry->d_inode->i_lock);
+ } else {
+ if (orig_off + count > file->f_dentry->d_inode->i_size) {
+ /* lock for writes to i_size. we allow lockless reads. recheck
+ * i_size in case of concurrent writers since our orig check. */
+ spin_lock(&file->f_dentry->d_inode->i_lock);
+ if (orig_off + count > file->f_dentry->d_inode->i_size)
+ file->f_dentry->d_inode->i_size = orig_off + count;
+ spin_unlock(&file->f_dentry->d_inode->i_lock);
+ }
}
page_off = orig_off & (PGSIZE - 1);
first_idx = orig_off >> PGSHIFT;
kref_get(&file_d->d_kref, 1);
goto open_the_file;
}
+ if (!(flags & O_CREAT)) {
+ set_errno(-error);
+ goto out_path_only;
+ }
/* So it didn't already exist, release the path from the previous lookup,
* and then we try to create it. */
path_release(nd);
file_d = do_lookup(nd->dentry, nd->last.name);
if (!file_d) {
if (!(flags & O_CREAT)) {
+ warn("Extremely unlikely race, probably a bug");
set_errno(ENOENT);
goto out_path_only;
}
}
/* Grow the vfs fd set */
-static int grow_fd_set(struct files_struct *open_files) {
+static int grow_fd_set(struct files_struct *open_files)
+{
int n;
struct file_desc *nfd, *ofd;
/* Grow the open_files->fd array in increments of NR_OPEN_FILES_DEFAULT */
n = open_files->max_files + NR_OPEN_FILES_DEFAULT;
if (n > NR_FILE_DESC_MAX)
- n = NR_FILE_DESC_MAX;
+ return -EMFILE;
nfd = kzmalloc(n * sizeof(struct file_desc), 0);
if (nfd == NULL)
- return -1;
+ return -ENOMEM;
/* Move the old array on top of the new one */
ofd = open_files->fd;
}
/* Free the vfs fd set if necessary */
-static void free_fd_set(struct files_struct *open_files) {
+static void free_fd_set(struct files_struct *open_files)
+{
+ void *free_me;
if (open_files->open_fds != (struct fd_set*)&open_files->open_fds_init) {
- kfree(open_files->open_fds);
assert(open_files->fd != open_files->fd_array);
- kfree(open_files->fd);
+ /* need to reset the pointers to the internal addrs, in case we take a
+ * look while debugging. 0 them out, since they have old data. our
+ * current versions should all be closed. */
+ memset(&open_files->open_fds_init, 0, sizeof(struct small_fd_set));
+ memset(&open_files->fd_array, 0, sizeof(open_files->fd_array));
+
+ free_me = open_files->open_fds;
+ open_files->open_fds = (struct fd_set*)&open_files->open_fds_init;
+ kfree(free_me);
+
+ free_me = open_files->fd;
+ open_files->fd = open_files->fd_array;
+ kfree(free_me);
}
}
static int __get_fd(struct files_struct *open_files, int low_fd)
{
int slot = -1;
+ int error;
if ((low_fd < 0) || (low_fd > NR_FILE_DESC_MAX))
return -EINVAL;
if (open_files->closed)
break;
}
if (slot == -1) {
- /* Expand the FD array and fd_set */
- if (grow_fd_set(open_files) == -1)
- return -ENOMEM;
- /* loop after growing */
+ if ((error = grow_fd_set(open_files)))
+ return error;
}
}
return slot;
}
-/* Gets and claims a free FD, used by 9ns. < 0 == error. */
-int get_fd(struct files_struct *open_files, int low_fd)
+/* Gets and claims a free FD, used by 9ns. < 0 == error. cloexec is tracked on
+ * the VFS FD. It's value will be O_CLOEXEC (not 1) or 0. */
+int get_fd(struct files_struct *open_files, int low_fd, int cloexec)
{
int slot;
spin_lock(&open_files->lock);
slot = __get_fd(open_files, low_fd);
+ if (cloexec && (slot >= 0))
+ open_files->fd[slot].fd_flags |= FD_CLOEXEC;
spin_unlock(&open_files->lock);
return slot;
}
static int __claim_fd(struct files_struct *open_files, int file_desc)
{
+ int error;
if ((file_desc < 0) || (file_desc > NR_FILE_DESC_MAX))
return -EINVAL;
if (open_files->closed)
/* Grow the open_files->fd_set until the file_desc can fit inside it */
while(file_desc >= open_files->max_files) {
- grow_fd_set(open_files);
+ if ((error = grow_fd_set(open_files)))
+ return error;
cpu_relax();
}
return 0;
}
-/* Claims a specific FD when duping FDs. used by 9ns. < 0 == error. */
+/* Claims a specific FD when duping FDs. used by 9ns. < 0 == error. No need
+ * for cloexec here, since it's not used during dup. */
int claim_fd(struct files_struct *open_files, int file_desc)
{
int ret;
}
/* Inserts the file in the files_struct, returning the corresponding new file
- * descriptor, or an error code. We start looking for open fds from low_fd. */
+ * descriptor, or an error code. We start looking for open fds from low_fd.
+ *
+ * Passing cloexec is a bit cheap, since we might want to expand it to support
+ * more FD options in the future. */
int insert_file(struct files_struct *open_files, struct file *file, int low_fd,
- bool must)
+ bool must, bool cloexec)
{
int slot, ret;
spin_lock(&open_files->lock);
kref_get(&file->f_kref, 1);
open_files->fd[slot].fd_file = file;
open_files->fd[slot].fd_flags = 0;
+ if (cloexec)
+ open_files->fd[slot].fd_flags |= FD_CLOEXEC;
spin_unlock(&open_files->lock);
return slot;
}
/* Closes all open files. Mostly just a "put" for all files. If cloexec, it
- * will only close files that are opened with O_CLOEXEC. */
+ * will only close the FDs with FD_CLOEXEC (opened with O_CLOEXEC or fcntld). */
void close_all_files(struct files_struct *open_files, bool cloexec)
{
struct file *file;
/* no file == 9ns uses the FD. they will deal with it */
if (!file)
continue;
- if (cloexec && !(open_files->fd[i].fd_flags & O_CLOEXEC))
+ if (cloexec && !(open_files->fd[i].fd_flags & FD_CLOEXEC))
continue;
/* Actually close the file */
open_files->fd[i].fd_file = 0;
}
/* Change the working directory of the given fs env (one per process, at this
- * point). Returns 0 for success, -ERROR for whatever error. */
+ * point). Returns 0 for success, sets errno and returns -1 otherwise. */
int do_chdir(struct fs_struct *fs_env, char *path)
{
struct nameidata nd_r = {0}, *nd = &nd_r;
- int retval;
- retval = path_lookup(path, LOOKUP_DIRECTORY, nd);
- if (!retval) {
- /* nd->dentry is the place we want our PWD to be */
- __chpwd(fs_env, nd->dentry);
+ int error;
+ error = path_lookup(path, LOOKUP_DIRECTORY, nd);
+ if (error) {
+ set_errno(-error);
+ path_release(nd);
+ return -1;
}
+ /* nd->dentry is the place we want our PWD to be */
+ __chpwd(fs_env, nd->dentry);
path_release(nd);
- return retval;
+ return 0;
}
int do_fchdir(struct fs_struct *fs_env, struct file *file)