summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdiskfs/lookup.c155
1 files changed, 92 insertions, 63 deletions
diff --git a/libdiskfs/lookup.c b/libdiskfs/lookup.c
index 96cce94e..7c1c7133 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 Free Software Foundation, Inc.
Written by Michael I. Bushnell, p/BSG.
This file is part of the GNU Hurd.
@@ -66,6 +66,10 @@ static spin_lock_t cm_lock = SPIN_LOCK_INITIALIZER;
locked, so don't lock it or add a reference to it.
(SPEC_DOTDOT will not be given with CREATE.)
+ DEPTH is the number of nodes between DP and the filesystem root.
+ If NEW_DEPTH is non-zero, then for a non-error return, the depth of the
+ resulting node NP is returned in it.
+
Return ENOTDIR if DP is not a directory.
Return EACCES if CRED isn't allowed to search DP.
Return EACCES if completing the operation will require writing
@@ -79,9 +83,10 @@ static spin_lock_t cm_lock = SPIN_LOCK_INITIALIZER;
error_t
diskfs_lookup (struct node *dp, char *name, enum lookup_type type,
struct node **np, struct dirstat *ds,
- struct protid *cred)
+ struct protid *cred, unsigned depth, unsigned *new_depth)
{
error_t err;
+ struct node *cached;
if (type == REMOVE || type == RENAME)
assert (np);
@@ -92,6 +97,7 @@ diskfs_lookup (struct node *dp, char *name, enum lookup_type type,
diskfs_null_dirstat (ds);
return ENOTDIR;
}
+
err = fshelp_access (&dp->dn_stat, S_IEXEC, cred->user);
if (err)
{
@@ -100,79 +106,102 @@ diskfs_lookup (struct node *dp, char *name, enum lookup_type type,
return err;
}
- if (type == LOOKUP)
+ if (depth == 0 && name[0] == '.' && name[1] == '.' && name[2] == '\0')
+ /* Ran into the root. */
{
- /* Check the cache first */
- struct node *cached = diskfs_check_lookup_cache (dp, name);
-
- if (cached == (struct node *)-1)
- /* Negative lookup cached. */
- {
- if (np)
- *np = 0;
- return ENOENT;
- }
- else if (cached)
- {
- 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 (ds)
- diskfs_null_dirstat (ds);
- return 0;
- }
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return EAGAIN;
}
-
- err = diskfs_lookup_hard (dp, name, type, np, ds, cred);
- spin_lock (&cm_lock);
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)
{
- if (err == ENOENT)
- cache_misses.absent++;
- else if (err)
- cache_misses.errors++;
- else
- cache_misses.present++;
- if (name[0] == '.')
+ 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 (ds)
+ diskfs_null_dirstat (ds);
+ }
+ else
+ {
+ err = diskfs_lookup_hard (dp, name, type, np, ds, cred);
+ assert (err != EAGAIN); /* We should never get EAGAIN now that we're
+ detecting the root ourselves. */
+
+ spin_lock (&cm_lock);
+ if (type == LOOKUP)
{
- if (name[1] == '\0')
- cache_misses.dot++;
- else if (name[1] == '.' && name[2] == '\0')
- cache_misses.dotdot++;
+ if (err == ENOENT)
+ cache_misses.absent++;
+ else if (err)
+ cache_misses.errors++;
+ else
+ cache_misses.present++;
+ if (name[0] == '.')
+ {
+ if (name[1] == '\0')
+ cache_misses.dot++;
+ else if (name[1] == '.' && name[2] == '\0')
+ cache_misses.dotdot++;
+ }
}
- }
- spin_unlock (&cm_lock);
+ spin_unlock (&cm_lock);
- if (err && err != ENOENT)
- return err;
+ if (err && err != ENOENT)
+ return err;
- if (type == RENAME
- || (type == CREATE && err == ENOENT)
- || (type == REMOVE && err != ENOENT))
- {
- error_t err2 = fshelp_checkdirmod (&dp->dn_stat,
- (err || !np) ? 0 : &(*np)->dn_stat,
- cred->user);
- if (err2)
+ if (type == RENAME
+ || (type == CREATE && err == ENOENT)
+ || (type == REMOVE && err != ENOENT))
{
- if (np && !err)
- diskfs_nput (*np);
- return err2;
+ error_t err2 = fshelp_checkdirmod (&dp->dn_stat,
+ (err || !np) ? 0 : &(*np)->dn_stat,
+ cred->user);
+ if (err2)
+ {
+ if (np && !err)
+ diskfs_nput (*np);
+ 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);
+ else if (type == LOOKUP && err == ENOENT)
+ diskfs_enter_lookup_cache (dp, 0, name);
+ }
+
+ if (!err && new_depth)
+ if (name[0] == '.')
+ if (name[1] == '\0')
+ *new_depth = depth;
+ else if (name[1] == '.' && name[2] == '\0')
+ *new_depth = depth - 1;
+ else
+ *new_depth = depth + 1;
+ else
+ *new_depth = depth + 1;
+
return err;
}