Return bool from reset_alarm_* apis.
[akaros.git] / kern / src / vfs.c
index 3193b45..cd330bc 100644 (file)
@@ -112,16 +112,26 @@ void vfs_init(void)
        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 */
@@ -575,7 +585,7 @@ void init_sb(struct super_block *sb, struct vfsmount *vmnt,
 {
        /* 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!");
@@ -631,17 +641,11 @@ static void dentry_set_name(struct dentry *dentry, char *name)
        }
 }
 
-/* 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);
@@ -662,6 +666,8 @@ struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
        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;
@@ -672,6 +678,21 @@ struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
        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().
  *
@@ -1234,15 +1255,22 @@ ssize_t generic_file_write(struct file *file, const char *buf, size_t count,
        /* 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;
@@ -1363,6 +1391,10 @@ struct file *do_file_open(char *path, int flags, int mode)
                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);       
@@ -1378,6 +1410,7 @@ struct file *do_file_open(char *path, int flags, int mode)
        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;
                }
@@ -2290,7 +2323,8 @@ struct file *get_file_from_fd(struct files_struct *open_files, int file_desc)
 }
 
 /* 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;
 
@@ -2306,10 +2340,10 @@ static int grow_fd_set(struct files_struct *open_files) {
        /* 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;
@@ -2327,11 +2361,24 @@ static int grow_fd_set(struct files_struct *open_files) {
 }
 
 /* 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);
        }
 }
 
@@ -2383,6 +2430,7 @@ struct file *put_file_from_fd(struct files_struct *open_files, int file_desc)
 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)
@@ -2403,27 +2451,29 @@ static int __get_fd(struct files_struct *open_files, int low_fd)
                        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)
@@ -2431,7 +2481,8 @@ static int __claim_fd(struct files_struct *open_files, int file_desc)
 
        /* 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();
        }
 
@@ -2447,7 +2498,8 @@ static int __claim_fd(struct files_struct *open_files, int file_desc)
        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;
@@ -2458,9 +2510,12 @@ int claim_fd(struct files_struct *open_files, int file_desc)
 }
 
 /* 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);
@@ -2485,12 +2540,14 @@ int insert_file(struct files_struct *open_files, struct file *file, int low_fd,
        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;
@@ -2508,7 +2565,7 @@ void close_all_files(struct files_struct *open_files, bool cloexec)
                        /* 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;
@@ -2575,18 +2632,21 @@ static void __chpwd(struct fs_struct *fs_env, struct dentry *new_pwd)
 }
 
 /* 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)