/* Hierarchial filesystem support Copyright (C) 1995, 2002 Free Software Foundation, Inc. Written by Miles Bader 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 #include #include #include #include #include #include #include /* Include the hook calling macros and non-rpc hook definitions (to get those, include "trees-s-hooks.h"). */ #include "treefs-hooks.h" #ifdef TREEFS_DEFINE_EI #define TREEFS_EI #else #define TREEFS_EI __extern_inline #endif /* ---------------------------------------------------------------- */ 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 { io_statbuf_t stat; struct treefs_fsys *fsys; struct trans_link active_trans; char *passive_trans; struct lock_box user_lock; pthread_mutex_t 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; pthread_mutex_t 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. */ io_statbuf_t 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 pthread_spinlock_t treefs_node_refcnt_lock; extern void treefs_node_ref (struct treefs_node *node); extern void treefs_node_release (struct treefs_node *node); extern void treefs_node_unref (struct treefs_node *node); extern void treefs_node_ref_weak (struct treefs_node *node); extern void treefs_node_release_weak (struct treefs_node *node); extern void treefs_node_unref_weak (struct treefs_node *node); #if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI) /* 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). */ TREEFS_EI void treefs_node_ref (struct treefs_node *node) { int new_ref; pthread_spin_lock (&treefs_node_refcnt_lock); node->refs++; new_ref = (node->refs == 1); pthread_spin_unlock (&treefs_node_refcnt_lock); if (new_ref) { pthread_mutex_lock (&node->lock); treefs_node_new_refs (node); pthread_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. */ TREEFS_EI void treefs_node_release (struct treefs_node *node) { int tried_drop_weak_refs = 0; loop: pthread_spin_lock (&treefs_node_refcnt_lock); assert_backtrace (node->refs); node->refs--; if (node->refs + node->weak_refs == 0) treefs_node_drop (node); else if (node->refs == 0 && !tried_drop_weak_refs) { pthread_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. */ pthread_spin_unlock (&treefs_node_refcnt_lock); node->refs++; pthread_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 pthread_spin_unlock (&treefs_node_refcnt_lock); pthread_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. */ TREEFS_EI void treefs_node_unref (struct treefs_node *node) { int tried_drop_weak_refs = 0; loop: pthread_spin_lock (&treefs_node_refcnt_lock); assert_backtrace (node->refs); node->refs--; if (node->refs + node->weak_refs == 0) { pthread_mutex_lock (&node->lock); treefs_node_drop (node); } else if (node->refs == 0) { pthread_mutex_lock (&node->lock); pthread_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 */ pthread_spin_unlock (&treefs_node_refcnt_lock); node->refs++; pthread_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... */ pthread_mutex_unlock (&node->lock); goto loop; } pthread_mutex_unlock (&node->lock); } else pthread_spin_unlock (&treefs_node_refcnt_lock); } /* Add a weak reference to a node. */ TREEFS_EI void treefs_node_ref_weak (struct treefs_node *node) { pthread_spin_lock (&treefs_node_refcnt_lock); node->weak_refs++; pthread_spin_unlock (&treefs_node_refcnt_lock); } /* Unlock node NODE and release a weak reference */ TREEFS_EI void treefs_node_release_weak (struct treefs_node *node) { pthread_spin_lock (&treefs_node_refcnt_lock); assert_backtrace (node->weak_refs); node->weak_refs--; if (node->refs + node->weak_refs == 0) treefs_node_drop (node); else { pthread_spin_unlock (&treefs_node_refcnt_lock); pthread_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). */ TREEFS_EI void treefs_node_unref_weak (struct treefs_node *node) { pthread_spin_lock (&treefs_node_refcnt_lock); assert_backtrace (node->weak_refs); node->weak_refs--; if (node->refs + node->weak_refs == 0) { pthread_mutex_lock (&node->lock); treefs_node_drop (node); } else pthread_spin_unlock (&treefs_node_refcnt_lock); } #endif /* Use extern inlines. */ /* ---------------------------------------------------------------- */ /* 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. */ extern int treefs_auth_has_uid (struct treefs_auth *auth, uid_t uid); extern int treefs_auth_in_group (struct treefs_auth *auth, gid_t gid); #if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI) /* Return nonzero iff the user identified by AUTH has uid UID. */ TREEFS_EI 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. */ TREEFS_EI 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; } #endif /* Use extern inlines. */ /* ---------------------------------------------------------------- */ /* 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__ */