diff options
Diffstat (limited to 'libdiskfs/lookup.c')
-rw-r--r-- | libdiskfs/lookup.c | 191 |
1 files changed, 141 insertions, 50 deletions
diff --git a/libdiskfs/lookup.c b/libdiskfs/lookup.c index 40ce8937..1f2a2588 100644 --- a/libdiskfs/lookup.c +++ b/libdiskfs/lookup.c @@ -1,5 +1,5 @@ /* Wrapper for diskfs_lookup_hard - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. @@ -19,11 +19,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include "priv.h" +#include <string.h> + +static struct +{ + int present; + int absent; + int errors; + int dot; + int dotdot; +} cache_misses; +static spin_lock_t cm_lock = SPIN_LOCK_INITIALIZER; + /* Lookup in directory DP (which is locked) the name NAME. TYPE will either be LOOKUP, CREATE, RENAME, or REMOVE. CRED identifies the user making the call. + NAME will have leading and trailing slashes stripped. It is an + error if there are internal slashes. NAME will be modified in + place if there are slashes in it; it is therefore an error to + specify a constant NAME which contains slashes. + If the name is found, return zero, and (if NP is nonzero) set *NP to point to the node for it, locked. If the name is not found, return ENOENT, and (if NP is nonzero) set *NP to zero. If NP is @@ -62,16 +79,15 @@ Return ENOENT if NAME isn't in the directory. Return EAGAIN if NAME refers to the `..' of this filesystem's root. Return EIO if appropriate. - - This function is a wrapper for diskfs_lookup_hard. -*/ -error_t -diskfs_lookup (struct node *dp, char *name, enum lookup_type type, - struct node **np, struct dirstat *ds, - struct protid *cred) + + This function is a wrapper for diskfs_lookup_hard. */ +error_t +diskfs_lookup (struct node *dp, const char *name, enum lookup_type type, + struct node **np, struct dirstat *ds, struct protid *cred) { error_t err; - + struct node *cached; + if (type == REMOVE || type == RENAME) assert (np); @@ -81,7 +97,37 @@ diskfs_lookup (struct node *dp, char *name, enum lookup_type type, diskfs_null_dirstat (ds); return ENOTDIR; } - err = diskfs_access (dp, S_IEXEC, cred); + + /* Strip leading and trailing slashes. */ + while (*name == '/') + name++; + + if (name[0] == '\0') + { + if (ds) + diskfs_null_dirstat (ds); + return EINVAL; + } + else + { + char *p = strchr (name, '/'); + if (p != 0) + { + *p = '\0'; + do + ++p; + while (*p == '/'); + if (*p != '\0') + { + if (ds) + diskfs_null_dirstat (ds); + return EINVAL; + } + } + } + + + err = fshelp_access (&dp->dn_stat, S_IEXEC, cred->user); if (err) { if (ds) @@ -89,55 +135,100 @@ diskfs_lookup (struct node *dp, char *name, enum lookup_type type, return err; } + if (dp == cred->po->shadow_root + && name[0] == '.' && name[1] == '.' && name[2] == '\0') + /* Ran into the root. */ + { + if (ds) + diskfs_null_dirstat (ds); + return EAGAIN; + } + if (type == LOOKUP) + /* Check the cache first */ + cached = diskfs_check_lookup_cache (dp, name); + else + cached = 0; + + if (cached == (struct node *)-1) + /* Negative lookup cached. */ + { + if (np) + *np = 0; + return ENOENT; + } + else if (cached) { - /* Check the cache first */ - struct node *cached = diskfs_check_lookup_cache (dp, name); + if (np) + *np = cached; /* Return what we found. */ + else + /* Ick, the user doesn't want the result, we have to drop our + reference. */ + if (cached == dp) + diskfs_nrele (cached); + else + diskfs_nput (cached); - if (cached == (struct node *)-1) - /* Negative lookup cached. */ - { - if (np) - *np = 0; - return ENOENT; - } - else if (cached) + if (ds) + diskfs_null_dirstat (ds); + } + else + { + err = diskfs_lookup_hard (dp, name, type, np, ds, cred); + + spin_lock (&cm_lock); + if (type == LOOKUP) { - if (np) - *np = cached; /* Return what we found. */ + if (err == ENOENT) + cache_misses.absent++; + else if (err) + cache_misses.errors++; else - /* Ick, the user doesn't want the result, we have to drop our - reference. */ - if (cached == dp) - diskfs_nrele (cached); - else - diskfs_nput (cached); - if (ds) - diskfs_null_dirstat (ds); - return 0; + cache_misses.present++; + if (name[0] == '.') + { + if (name[1] == '\0') + cache_misses.dot++; + else if (name[1] == '.' && name[2] == '\0') + cache_misses.dotdot++; + } } - } - - err = diskfs_lookup_hard (dp, name, type, np, ds, cred); - if (err && err != ENOENT) - return err; - - if (type == RENAME - || (type == CREATE && err == ENOENT) - || (type == REMOVE && err != ENOENT)) - { - error_t err2 = diskfs_checkdirmod (dp, (err || !np) ? 0 : *np, cred); - if (err2) + spin_unlock (&cm_lock); + + if (err && err != ENOENT) + return err; + + if (type == RENAME + || (type == CREATE && err == ENOENT) + || (type == REMOVE && err != ENOENT)) { - if (np && !err) - diskfs_nput (*np); - return err2; + error_t err2; + + if (diskfs_name_max > 0 && strlen (name) > diskfs_name_max) + err2 = ENAMETOOLONG; + else + err2 = fshelp_checkdirmod (&dp->dn_stat, + (err || !np) ? 0 : &(*np)->dn_stat, + cred->user); + if (err2) + { + if (np && !err) + { + if (*np == dp) + diskfs_nrele (*np); + else + diskfs_nput (*np); + *np = 0; + } + return err2; + } } + + if ((type == LOOKUP || type == CREATE) && !err && np) + diskfs_enter_lookup_cache (dp, *np, name); + else if (type == LOOKUP && err == ENOENT) + diskfs_enter_lookup_cache (dp, 0, name); } - if ((type == LOOKUP || type == CREATE) && !err && np) - diskfs_enter_lookup_cache (dp, *np, name); - return err; } - |