summaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-23 02:25:38 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-23 02:25:38 +0000
commit16b5d462f73eb29d1f67fa01cc1ea66afdc72569 (patch)
tree5407bd573f4840e473ea27cbe61e5c7a07131fcd /fs/namei.c
parentce8a076e11e7e5ee36007f9a3eee5bb3744cb8f6 (diff)
Merge with Linux 2.3.99-pre2.
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c87
1 files changed, 79 insertions, 8 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 97c239929..8675e28c5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -451,6 +451,76 @@ no_inode:
}
/*
+ * Restricted form of lookup. Doesn't follow links, single-component only,
+ * needs parent already locked. Doesn't follow mounts.
+ */
+struct dentry * lookup_one(const char * name, struct dentry * base)
+{
+ struct dentry * dentry;
+ struct inode *inode;
+ int err;
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+
+ inode = base->d_inode;
+ err = permission(inode, MAY_EXEC);
+ dentry = ERR_PTR(err);
+ if (err)
+ goto out;
+
+ this.name = name;
+ c = *(const unsigned char *)name;
+ if (!c)
+ goto access;
+
+ hash = init_name_hash();
+ do {
+ name++;
+ if (c == '/')
+ goto access;
+ hash = partial_name_hash(c, hash);
+ c = *(const unsigned char *)name;
+ } while (c);
+ this.len = name - (const char *) this.name;
+ this.hash = end_name_hash(hash);
+
+ /*
+ * See if the low-level filesystem might want
+ * to use its own hash..
+ */
+ if (base->d_op && base->d_op->d_hash) {
+ err = base->d_op->d_hash(base, &this);
+ dentry = ERR_PTR(err);
+ if (err < 0)
+ goto out;
+ }
+
+ dentry = cached_lookup(base, &this, 0);
+ if (!dentry) {
+ struct dentry *new = d_alloc(base, &this);
+ dentry = ERR_PTR(-ENOMEM);
+ if (!new)
+ goto out;
+ dentry = inode->i_op->lookup(inode, new);
+ if (!dentry)
+ dentry = new;
+ else {
+ dput(new);
+ if (IS_ERR(dentry))
+ goto out;
+ }
+ }
+
+out:
+ dput(base);
+ return dentry;
+access:
+ dentry = ERR_PTR(-EACCES);
+ goto out;
+}
+
+/*
* namei()
*
* is used by most simple commands to get the inode of a specified name.
@@ -609,13 +679,13 @@ exit_lock:
* which is a lot more logical, and also allows the "no perm" needed
* for symlinks (where the permissions are checked later).
*/
-struct dentry * open_namei(const char * pathname, int flag, int mode)
+struct dentry * __open_namei(const char * pathname, int flag, int mode, struct dentry * dir)
{
int acc_mode, error;
struct inode *inode;
struct dentry *dentry;
- dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));
+ dentry = lookup_dentry(pathname, dir, lookup_flags(flag));
if (IS_ERR(dentry))
return dentry;
@@ -1012,13 +1082,13 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
return error;
}
-int do_unlink(const char * name)
+int do_unlink(const char * name, struct dentry * base)
{
int error;
struct dentry *dir;
struct dentry *dentry;
- dentry = lookup_dentry(name, NULL, 0);
+ dentry = lookup_dentry(name, base, 0);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto exit;
@@ -1043,7 +1113,7 @@ asmlinkage long sys_unlink(const char * pathname)
if(IS_ERR(tmp))
return PTR_ERR(tmp);
lock_kernel();
- error = do_unlink(tmp);
+ error = do_unlink(tmp, NULL);
unlock_kernel();
putname(tmp);
@@ -1427,16 +1497,17 @@ asmlinkage long sys_rename(const char * oldname, const char * newname)
int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, const char *link)
{
- u32 len;
+ int len;
len = PTR_ERR(link);
if (IS_ERR(link))
goto out;
len = strlen(link);
- if (len > buflen)
+ if (len > (unsigned) buflen)
len = buflen;
- copy_to_user(buffer, link, len);
+ if (copy_to_user(buffer, link, len))
+ len = -EFAULT;
out:
return len;
}