diff options
Diffstat (limited to 'libtreefs/dir-lookup.c')
-rw-r--r-- | libtreefs/dir-lookup.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/libtreefs/dir-lookup.c b/libtreefs/dir-lookup.c new file mode 100644 index 00000000..51c2cbd2 --- /dev/null +++ b/libtreefs/dir-lookup.c @@ -0,0 +1,305 @@ +/* Default treefs_s_dir_lookup hook + + Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + + 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. */ + +#include <fcntl.h> +#include <string.h> + +#include <hurd/fsys.h> + +#include "treefs.h" +#include "treefs-s-hooks.h" + +/* Default dir_lookup hook. This code was originally copied from diskfs. */ +error_t +_treefs_s_dir_lookup (struct treefs_handle *h, + char *path, int flags, mode_t mode, + enum retry_type *retry, char *retry_name, + file_t *result, mach_msg_type_name_t *result_type) +{ + struct treefs_node *dir; + struct treefs_node *node; + unsigned symlink_expansions = 0; + error_t err = 0; + char *path_buf = 0; + int path_buf_len = 0; + int lastcomp = 0; + int mustbedir = 0; + + flags &= O_HURD; + mode &= ~S_IFMT; + + /* Skip leading slashes */ + while (path[0] == '/') + path++; + + *result_type = MACH_MSG_TYPE_MAKE_SEND; + *retry = FS_RETRY_NORMAL; + retry_name[0] = '\0'; + + if (path[0] == '\0') + { + mustbedir = 1; + + /* Set things up in the state expected by the code from gotit: on. */ + dir = 0; + node = h->po->node; + mutex_lock (&node->lock); + treefs_node_ref (node); + goto gotit; + } + + dir = h->po->node; + mutex_lock (&dir->lock); + node = 0; + + treefs_node_ref (dir); /* acquire a ref for later node_release */ + + do + { + char *nextname; + + assert (!lastcomp); + + /* Find the name of the next pathname component */ + nextname = index (path, '/'); + + if (nextname) + { + *nextname++ = '\0'; + while (*nextname == '/') + nextname++; + if (*nextname == '\0') + { + /* These are the rules for filenames ending in /. */ + nextname = 0; + lastcomp = 1; + mustbedir = 1; + + } + else + lastcomp = 0; + } + else + lastcomp = 1; + + node = 0; + + /* Lookup the next pathname component. */ + if (!lastcomp) + err = treefs_dir_lookup (dir, path, h->auth, 0, 0, &node); + else + /* ... and in this case, the last. Note that the S_IFREG only + applies in the case of O_CREAT, which is turned off for + directories anyway. */ + err = + treefs_dir_lookup (dir, path, h->auth, flags, mode | S_IFREG, &node); + + /* If we get an error we're done */ + if (err == EAGAIN) + { + if (h->po->parent_port != MACH_PORT_NULL) + { + *retry = FS_RETRY_REAUTH; + *result = h->po->parent_port; + *result_type = MACH_MSG_TYPE_COPY_SEND; + if (!lastcomp) + strcpy (retry_name, nextname); + err = 0; + goto out; + } + else + /* The global filesystem root... .. == . */ + { + err = 0; + node = dir; + treefs_node_ref (node); + } + } + + if (err) + goto out; + + /* If this is translated, start the translator (if necessary) + and return. */ + /* The check for `node != dir' simplifies this code a great + deal. Such a translator should already have been started, + so there's no lossage in doing it this way. */ + if ((!lastcomp || !(flags & O_NOTRANS)) + && node != dir) + { + file_t dir_port = MACH_PORT_NULL, child_fsys; + + /* Be very careful not to hold an inode lock while fetching + a translator lock and vice versa. */ + + mutex_unlock (&node->lock); + mutex_unlock (&dir->lock); + + do + { + err = + treefs_node_get_active_trans (node, dir, h->po->parent_port, + &dir_port, &child_fsys); + if (err == 0 && child_fsys != MACH_PORT_NULL) + { + err = + fsys_getroot (child_fsys, dir_port, + MACH_MSG_TYPE_COPY_SEND, + h->auth->uids, h->auth->nuids, + h->auth->gids, h->auth->ngids, + lastcomp ? flags : 0, + retry, retry_name, result); + /* If we got MACH_SEND_INVALID_DEST or MIG_SERVER_DIED, then + the server is dead. Zero out the old control port and try + everything again. */ + if (err == MACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED) + treefs_node_drop_active_trans (node, child_fsys); + } + } + while (err == MACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED); + + if (err || child_fsys) + { + /* We're done; return to the user. If there are more + components after this name, be sure to append them to the + user's retry path. */ + if (!err && !lastcomp) + { + strcat (retry_name, "/"); + strcat (retry_name, nextname); + } + + *result_type = MACH_MSG_TYPE_MOVE_SEND; + + treefs_node_unref (dir); + treefs_node_unref (node); + if (dir_port) + mach_port_deallocate (mach_task_self (), dir_port); + + return err; + } + + /* We're here if we tried the translator check, and it + failed. Lock everything back, and make sure we do it + in the right order. */ + if (strcmp (path, "..") != 0) + { + mutex_unlock (&node->lock); + mutex_lock (&dir->lock); + mutex_lock (&node->lock); + } + else + mutex_lock (&dir->lock); + } + + if (treefs_node_type (node) == S_IFLNK + && !(lastcomp && (flags & (O_NOLINK|O_NOTRANS)))) + /* Handle symlink interpretation */ + { + unsigned nextname_len = nextname ? strlen (nextname) + 1 : 0; + /* max space we currently have for the sym link */ + unsigned sym_len = path_buf_len - nextname_len - 1; + + if (symlink_expansions++ > node->fsys->max_symlinks) + { + err = ELOOP; + goto out; + } + + err = treefs_node_get_symlink (node, path_buf, &sym_len); + if (err == E2BIG) + /* Symlink contents + extra path won't fit in our buffer, so + reallocate it and try again. */ + { + path_buf_len = sym_len + nextname_len + 1; + path_buf = alloca (path_buf_len); + err = treefs_node_get_symlink (node, path_buf, &sym_len); + } + if (err) + goto out; + + if (nextname) + { + path_buf[sym_len] = '/'; + bcopy (nextname, path_buf + sym_len + 1, nextname_len - 1); + } + path_buf[nextname_len + sym_len] = '\0'; + + if (path_buf[0] == '/') + { + /* Punt to the caller. */ + *retry = FS_RETRY_MAGICAL; + *result = MACH_PORT_NULL; + strcpy (retry_name, path_buf); + goto out; + } + + path = path_buf; + if (lastcomp) + { + lastcomp = 0; + /* Symlinks to nonexistent files aren't allowed to cause + creation, so clear the flag here. */ + flags &= ~O_CREAT; + } + treefs_node_release (node); + node = 0; + } + else + { + /* Handle normal nodes */ + path = nextname; + if (node == dir) + treefs_node_unref (dir); + else + treefs_node_release (dir); + if (!lastcomp) + { + dir = node; + node = 0; + } + else + dir = 0; + } + } while (path && *path); + + gotit: + /* At this point, node is the node to return. */ + + if (mustbedir && !treefs_node_isdir (node)) + err = ENOTDIR; + if (err) + goto out; + + err = treefs_node_create_right (node, flags, h->po->parent_port, h->auth, + result); + + out: + if (node) + { + if (dir == node) + treefs_node_unref (node); + else + treefs_node_release (node); + } + if (dir) + treefs_node_release (dir); + return err; +} |