aboutsummaryrefslogtreecommitdiff
path: root/libdiskfs/lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdiskfs/lookup.c')
-rw-r--r--libdiskfs/lookup.c191
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;
}
-