diff options
Diffstat (limited to 'libtreefs/treefs.h')
-rw-r--r-- | libtreefs/treefs.h | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/libtreefs/treefs.h b/libtreefs/treefs.h new file mode 100644 index 00000000..b0c26849 --- /dev/null +++ b/libtreefs/treefs.h @@ -0,0 +1,472 @@ +/* Hierarchial filesystem support + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __TREEFS_H__ +#define __TREEFS_H__ + +#include <errno.h> +#include <cthreads.h> +#include <assert.h> + +#include <sys/stat.h> + +#include <hurd/hurd_types.h> +#include <hurd/ports.h> +#include <hurd/fshelp.h> + +/* Include the hook calling macros and non-rpc hook definitions (to get + those, include "trees-s-hooks.h"). */ +#include "treefs-hooks.h" + +/* ---------------------------------------------------------------- */ + +typedef void (**treefs_hook_vector_t)(); + +/* A list of nodes. */ +struct treefs_node_list; + +/* Each user port referring to a file points to one of these. */ +struct treefs_handle +{ + struct port_info pi; + struct treefs_auth *auth; /* User identification */ + struct treefs_peropen *po; /* The io object itself */ + void *u; /* for user use */ +}; + +/* An authentication cookie. */ +struct treefs_auth +{ + int refs; + uid_t *uids, *gids; + int nuids, ngids; + int isroot; + void *u; /* for user use */ +}; + +/* Bits the user is permitted to set with io_*_openmodes */ +#define TREEFS_SETTABLE_FLAGS (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME) + +/* Bits that are turned off after open */ +#define TREEFS_OPENONLY_FLAGS (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK) + +struct treefs_peropen +{ + int refs; + int open_flags; + int user_lock_state; + + /* A port to the directory through which this open file was reached. */ + mach_port_t parent_port; + + void *u; /* for user use */ + + struct treefs_node *node; +}; + +/* A filesystem node in the tree. */ +struct treefs_node +{ + struct stat stat; + struct treefs_fsys *fsys; + + struct trans_link active_trans; + char *passive_trans; + struct lock_box user_lock; + + struct mutex lock; + unsigned refs, weak_refs; + + /* Node ops */ + treefs_hook_vector_t hooks; + + /* If this node is a directory, then this is the directory state. */ + struct treefs_node_list *children; + + void *u; /* for user use */ +}; + +struct treefs_node_list +{ + struct treefs_node **nodes; + unsigned short num_nodes, nodes_alloced; + char *names; + unsigned short names_len, names_alloced; +}; + +struct treefs_fsys +{ + struct port_info pi; + struct mutex lock; + + /* The root node in this filesystem. */ + struct treefs_node *root; + + /* The port for the node which this filesystem is translating. */ + mach_port_t underlying_port; + /* And stat info for it. */ + struct stat underlying_stat; + + /* Flags from the TREEFS_FSYS_ set. */ + int flags; + /* Max number of symlink expansions allowed. */ + unsigned max_symlinks; + /* Sync interval (in seconds). 0 means all operations should be + synchronous, any negative value means never sync. */ + int sync_interval; + + /* Values to return from a statfs. */ + int fs_type; + int fs_id; + + /* This is the hook vector that each new node in this filesystem starts out + with. */ + treefs_hook_vector_t hooks; + + /* The port bucket to which all of our ports belongs. */ + struct ports_bucket *port_bucket; + + /* Various classes of ports we know about. */ + struct port_class *handle_port_class; + + void *u; /* for user use */ +}; + +/* Filesystem flags. */ +#define TREEFS_FSYS_READONLY 0x1 + +/* ---------------------------------------------------------------- */ +/* In-core directory management routines (`mdir' == `memory dir'). These are + intended for keeping non-permanent directory state. If called on a + non-dir, ENOTDIR is returned. */ + +/* Add CHILD to DIR as NAME, replacing any existing entry. If OLD_CHILD is + NULL, and NAME already exists in dir, EEXIST is returned, otherwise, any + previous child is replaced and returned in OLD_CHILD. DIR should be + locked. */ +error_t treefs_mdir_add (struct treefs_node *dir, char *name, + struct treefs_node *child, + struct treefs_node **old_child); + +/* Remove any entry in DIR called NAME. If there is no such entry, ENOENT is + returned. If OLD_CHILD is non-NULL, any removed entry is returned in it. + DIR should be locked. */ +error_t treefs_mdir_remove (struct treefs_node *dir, char *name, + struct treefs_node **old_child); + +/* Returns in NODE any entry called NAME in DIR, or NULL (and ENOENT) if + there isn't such. DIR should be locked. */ +error_t treefs_mdir_get (struct treefs_node *dir, char *name, + struct treefs_node **node); + +/* Call FUN on each child of DIR; if FUN returns a non-zero value at any + point, stop iterating and return that value immediately. */ +error_t treefs_mdir_for_each (struct treefs_node *dir, + error_t (*fun)(struct treefs_node *node)); + +/* ---------------------------------------------------------------- */ +/* Functions for dealing with node lists. */ + +/* Return a new node list, or NULL if a memory allocation error occurs. */ +struct treefs_node_list *treefs_make_node_list (); + +/* Add NODE to LIST as NAME, replacing any existing entry. If OLD_NODE is + NULL, and an entry NAME already exists, EEXIST is returned, otherwise, any + previous child is replaced and returned in OLD_NODE. */ +error_t treefs_node_list_add (struct treefs_node_list *list, char *name, + struct treefs_node *node, + struct treefs_node **old_node); + +/* Remove any entry in LIST called NAME. If there is no such entry, ENOENT is + returned. If OLD_NODE is non-NULL, any removed entry is returned in it. */ +error_t treefs_node_list_remove (struct treefs_node_list *list, char *name, + struct treefs_node **old_node); + +/* Returns in NODE any entry called NAME in LIST, or NULL (and ENOENT) if + there isn't such. */ +error_t treefs_node_list_get (struct treefs_node_list *list, char *name, + struct treefs_node **node); + +/* Call FUN on each node in LIST; if FUN returns a non-zero value at any + point, stop iterating and return that value immediately. */ +error_t treefs_node_list_for_each (struct treefs_node_list *list, + error_t (*fun)(char *name, + struct treefs_node *node)); + +/* ---------------------------------------------------------------- */ +/* Functions for manipulating hook vectors. */ + +typedef void (*treefs_hook_vector_init_t[TREEFS_NUM_HOOKS])(); + +extern treefs_hook_vector_init_t treefs_default_hooks; + +/* Returns a copy of the treefs hook vector HOOKS, or a zero'd vector if HOOKS + is NULL. If HOOKS is NULL, treefs_default_hooks is used. If a memory + allocation error occurs, NULL is returned. */ +treefs_hook_vector_t treefs_hooks_clone (treefs_hook_vector_t hooks); + +/* Copies each non-NULL entry in OVERRIDES into HOOKS. */ +void treefs_hooks_override (treefs_hook_vector_t hooks, + treefs_hook_vector_t overrides); + +/* Sets the hook NUM in HOOKS to HOOK. */ +void treefs_hooks_set (treefs_hook_vector_t hooks, + unsigned num, void (*hook)()); + +/* ---------------------------------------------------------------- */ +/* Reference counting function (largely stolen from diskfs). */ + +extern spin_lock_t treefs_node_refcnt_lock; + +/* Add a hard reference to a node. If there were no hard + references previously, then the node cannot be locked + (because you must hold a hard reference to hold the lock). */ +extern inline void +treefs_node_ref (struct treefs_node *node) +{ + int new_ref; + spin_lock (&treefs_node_refcnt_lock); + node->refs++; + new_ref = (node->refs == 1); + spin_unlock (&treefs_node_refcnt_lock); + if (new_ref) + { + mutex_lock (&node->lock); + treefs_node_new_refs (node); + mutex_unlock (&node->lock); + } +} + +/* Unlock node NODE and release a hard reference; if this is the last + hard reference and there are no links to the file then request + weak references to be dropped. */ +extern inline void +treefs_node_release (struct treefs_node *node) +{ + int tried_drop_weak_refs = 0; + + loop: + spin_lock (&treefs_node_refcnt_lock); + assert (node->refs); + node->refs--; + if (node->refs + node->weak_refs == 0) + treefs_node_drop (node); + else if (node->refs == 0 && !tried_drop_weak_refs) + { + spin_unlock (&treefs_node_refcnt_lock); + treefs_node_lost_refs (node); + if (treefs_node_unlinked (node)) + { + /* There are no links. If there are weak references that + can be dropped, we can't let them postpone deallocation. + So attempt to drop them. But that's a user-supplied + routine, which might result in further recursive calls to + the ref-counting system. So we have to reacquire our + reference around the call to forestall disaster. */ + spin_unlock (&treefs_node_refcnt_lock); + node->refs++; + spin_unlock (&treefs_node_refcnt_lock); + + treefs_node_try_dropping_weak_refs (node); + + /* But there's no value in looping forever in this + routine; only try to drop weak references once. */ + tried_drop_weak_refs = 1; + + /* Now we can drop the reference back... */ + goto loop; + } + } + else + spin_unlock (&treefs_node_refcnt_lock); + mutex_unlock (&node->lock); +} + +/* Release a hard reference on NODE. If NODE is locked by anyone, then + this cannot be the last hard reference (because you must hold a + hard reference in order to hold the lock). If this is the last + hard reference and there are no links, then request weak references + to be dropped. */ +extern inline void +treefs_node_unref (struct treefs_node *node) +{ + int tried_drop_weak_refs = 0; + + loop: + spin_lock (&treefs_node_refcnt_lock); + assert (node->refs); + node->refs--; + if (node->refs + node->weak_refs == 0) + { + mutex_lock (&node->lock); + treefs_node_drop (node); + } + else if (node->refs == 0) + { + mutex_lock (&node->lock); + spin_unlock (&treefs_node_refcnt_lock); + treefs_node_lost_refs (node); + if (treefs_node_unlinked(node) && !tried_drop_weak_refs) + { + /* Same issue here as in nodeut; see that for explanation */ + spin_unlock (&treefs_node_refcnt_lock); + node->refs++; + spin_unlock (&treefs_node_refcnt_lock); + + treefs_node_try_dropping_weak_refs (node); + tried_drop_weak_refs = 1; + + /* Now we can drop the reference back... */ + mutex_unlock (&node->lock); + goto loop; + } + mutex_unlock (&node->lock); + } + else + spin_unlock (&treefs_node_refcnt_lock); +} + +/* Add a weak reference to a node. */ +extern inline void +treefs_node_ref_weak (struct treefs_node *node) +{ + spin_lock (&treefs_node_refcnt_lock); + node->weak_refs++; + spin_unlock (&treefs_node_refcnt_lock); +} + +/* Unlock node NODE and release a weak reference */ +extern inline void +treefs_node_release_weak (struct treefs_node *node) +{ + spin_lock (&treefs_node_refcnt_lock); + assert (node->weak_refs); + node->weak_refs--; + if (node->refs + node->weak_refs == 0) + treefs_node_drop (node); + else + { + spin_unlock (&treefs_node_refcnt_lock); + mutex_unlock (&node->lock); + } +} + +/* Release a weak reference on NODE. If NODE is locked by anyone, then + this cannot be the last reference (because you must hold a + hard reference in order to hold the lock). */ +extern inline void +treefs_node_unref_weak (struct treefs_node *node) +{ + spin_lock (&treefs_node_refcnt_lock); + assert (node->weak_refs); + node->weak_refs--; + if (node->refs + node->weak_refs == 0) + { + mutex_lock (&node->lock); + treefs_node_drop (node); + } + else + spin_unlock (&treefs_node_refcnt_lock); +} + +/* ---------------------------------------------------------------- */ + +/* Return in PORT a send right for a new handle, pointing at the peropen PO, + with rights initialized from AUTH. */ +error_t +treefs_peropen_create_right (struct treefs_peropen *po, + struct treefs_auth *auth, + mach_port_t *port); + +/* Return a send right for a new handle and a new peropen, pointing at NODE, + with rights initialized from AUTH. MODE and PARENT_PORT are used to + initialize the corresponding fields in the new peropen. */ +error_t +treefs_node_create_right (struct treefs_node *node, int flags, + mach_port_t parent_port, struct treefs_auth *auth, + mach_port_t *port); + +/* ---------------------------------------------------------------- */ +/* Auth functions; copied from diskfs. */ + +/* Return nonzero iff the user identified by AUTH has uid UID. */ +extern inline int +treefs_auth_has_uid (struct treefs_auth *auth, uid_t uid) +{ + int i; + for (i = 0; i < auth->nuids; i++) + if (auth->uids[i] == uid) + return 1; + return 0; +} + +/* Return nonzero iff the user identified by AUTH has group GID. */ +extern inline int +treefs_auth_in_group (struct treefs_auth *auth, gid_t gid) +{ + int i; + for (i = 0; i < auth->ngids; i++) + if (auth->gids[i] == gid) + return 1; + return 0; +} + +/* ---------------------------------------------------------------- */ +/* Helper routines for dealing with translators. */ + +/* Return the active translator control port for NODE. If there is no + translator, active or passive, MACH_PORT_NULL is returned in CONTROL_PORT. + If there is a translator, it is started if necessary, and returned in + CONTROL_PORT. *DIR_PORT should be a port right to use as the new + translators parent directory. If it is MACH_PORT_NULL, a port is created + from DIR and PARENT_PORT and stored in *DIR_PORT; otherwise DIR and + PARENT_PORT are not used. Neither NODE or DIR should be locked when + calling this function. */ +error_t treefs_node_get_active_trans (struct treefs_node *node, + struct treefs_node *dir, + mach_port_t parent_port, + mach_port_t *control_port, + mach_port_t *dir_port); + +/* Drop the active translator CONTROL_PORT on NODE, unless it's no longer the + current active translator, in which case just drop a reference to it. */ +void treefs_node_drop_active_trans (struct treefs_node *node, + mach_port_t control_port); + +/* ---------------------------------------------------------------- */ +/* Basic node creation. */ + +/* Create a basic node, with one reference and no user-specific fields + initialized, and return it in NODE */ +error_t +treefs_create_node (struct treefs_fsys *fsys, struct treefs_node **node); + +/* Immediately destroy NODE, with no user-finalization. */ +error_t treefs_free_node (struct treefs_node *node); + +/* ---------------------------------------------------------------- */ +/* Some global variables. */ + +/* The port class used by treefs to make filesystem control ports. */ +struct port_class *treefs_fsys_port_class; + +#endif /* __TREEFS_H__ */ |