summaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /fs/namei.c
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c280
1 files changed, 169 insertions, 111 deletions
diff --git a/fs/namei.c b/fs/namei.c
index e047cde28..a50881bc8 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -8,8 +8,6 @@
* Some corrections by tytso.
*/
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -18,31 +16,9 @@
#include <linux/stat.h>
#include <linux/mm.h>
-#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+#include <asm/uaccess.h>
-/*
- * How long a filename can we get from user space?
- * -EFAULT if invalid area
- * 0 if ok (ENAMETOOLONG before EFAULT)
- * >0 EFAULT after xx bytes
- */
-static inline int get_max_filename(unsigned long address)
-{
- struct vm_area_struct * vma;
-
- if (get_fs() == KERNEL_DS)
- return 0;
- vma = find_vma(current, address);
- if (!vma || vma->vm_start > address || !(vma->vm_flags & VM_READ))
- return -EFAULT;
- address = vma->vm_end - address;
- if (address > PAGE_SIZE)
- return 0;
- if (vma->vm_next && vma->vm_next->vm_start == vma->vm_end &&
- (vma->vm_next->vm_flags & VM_READ))
- return 0;
- return address;
-}
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
/*
* In order to reduce some races, while at the same time doing additional
@@ -51,36 +27,41 @@ static inline int get_max_filename(unsigned long address)
*
* POSIX.1 2.4: an empty pathname is invalid (ENOENT).
*/
+static inline int do_getname(const char *filename, char *page)
+{
+ int retval;
+ unsigned long len = PAGE_SIZE;
+
+ if ((unsigned long) filename >= TASK_SIZE) {
+ if (get_fs() != KERNEL_DS)
+ return -EFAULT;
+ } else if (TASK_SIZE - (unsigned long) filename < PAGE_SIZE)
+ len = TASK_SIZE - (unsigned long) filename;
+
+ retval = strncpy_from_user((char *)page, filename, len);
+ if (retval > 0) {
+ if (retval < len)
+ return 0;
+ return -ENAMETOOLONG;
+ } else if (!retval)
+ retval = -ENOENT;
+ return retval;
+}
+
int getname(const char * filename, char **result)
{
- int i, error;
unsigned long page;
- char * tmp, c;
-
- i = get_max_filename((unsigned long) filename);
- if (i < 0)
- return i;
- error = -EFAULT;
- if (!i) {
- error = -ENAMETOOLONG;
- i = PAGE_SIZE;
- }
- c = get_fs_byte(filename++);
- if (!c)
- return -ENOENT;
- if(!(page = __get_free_page(GFP_KERNEL)))
- return -ENOMEM;
- *result = tmp = (char *) page;
- while (--i) {
- *(tmp++) = c;
- c = get_fs_byte(filename++);
- if (!c) {
- *tmp = '\0';
- return 0;
- }
+ int retval;
+
+ page = __get_free_page(GFP_KERNEL);
+ retval = -ENOMEM;
+ if (page) {
+ *result = (char *)page;
+ retval = do_getname(filename, (char *) page);
+ if (retval < 0)
+ free_page(page);
}
- free_page(page);
- return error;
+ return retval;
}
void putname(char * name)
@@ -102,6 +83,9 @@ int permission(struct inode * inode,int mask)
if (inode->i_op && inode->i_op->permission)
return inode->i_op->permission(inode, mask);
+ else if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
+ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+ return -EROFS; /* Nobody gets write access to a read-only fs */
else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
return -EACCES; /* Nobody gets write access to an immutable file */
else if (current->fsuid == inode->i_uid)
@@ -122,27 +106,27 @@ int permission(struct inode * inode,int mask)
*/
int get_write_access(struct inode * inode)
{
- struct task_struct ** p;
+ struct task_struct * p;
if ((inode->i_count > 1) && S_ISREG(inode->i_mode)) /* shortcut */
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ for_each_task(p) {
struct vm_area_struct * mpnt;
- if (!*p)
+ if (!p->mm)
continue;
- for(mpnt = (*p)->mm->mmap; mpnt; mpnt = mpnt->vm_next) {
+ for(mpnt = p->mm->mmap; mpnt; mpnt = mpnt->vm_next) {
if (inode != mpnt->vm_inode)
continue;
if (mpnt->vm_flags & VM_DENYWRITE)
return -ETXTBSY;
}
}
- inode->i_wcount++;
+ inode->i_writecount++;
return 0;
}
void put_write_access(struct inode * inode)
{
- inode->i_wcount--;
+ inode->i_writecount--;
}
/*
@@ -151,7 +135,7 @@ void put_write_access(struct inode * inode)
* fathers (pseudo-roots, mount-points)
*/
int lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+ struct inode ** result)
{
struct super_block * sb;
int perm;
@@ -185,7 +169,7 @@ int lookup(struct inode * dir,const char * name, int len,
*result = dir;
return 0;
}
- return dir->i_op->lookup(dir,name,len,result);
+ return dir->i_op->lookup(dir, name, len, result);
}
int follow_link(struct inode * dir, struct inode * inode,
@@ -211,8 +195,8 @@ int follow_link(struct inode * dir, struct inode * inode,
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
-static int dir_namei(const char * pathname, int * namelen, const char ** name,
- struct inode * base, struct inode ** res_inode)
+static int dir_namei(const char *pathname, int *namelen, const char **name,
+ struct inode * base, struct inode **res_inode)
{
char c;
const char * thisname;
@@ -237,7 +221,7 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
if (!c)
break;
base->i_count++;
- error = lookup(base,thisname,len,&inode);
+ error = lookup(base, thisname, len, &inode);
if (error) {
iput(base);
return error;
@@ -256,25 +240,25 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
return 0;
}
-static int _namei(const char * pathname, struct inode * base,
- int follow_links, struct inode ** res_inode)
+int _namei(const char * pathname, struct inode * base,
+ int follow_links, struct inode ** res_inode)
{
- const char * basename;
+ const char *basename;
int namelen,error;
struct inode * inode;
*res_inode = NULL;
- error = dir_namei(pathname,&namelen,&basename,base,&base);
+ error = dir_namei(pathname, &namelen, &basename, base, &base);
if (error)
return error;
base->i_count++; /* lookup uses up base */
- error = lookup(base,basename,namelen,&inode);
+ error = lookup(base, basename, namelen, &inode);
if (error) {
iput(base);
return error;
}
if (follow_links) {
- error = follow_link(base,inode,0,0,&inode);
+ error = follow_link(base, inode, 0, 0, &inode);
if (error)
return error;
} else
@@ -283,14 +267,14 @@ static int _namei(const char * pathname, struct inode * base,
return 0;
}
-int lnamei(const char * pathname, struct inode ** res_inode)
+int lnamei(const char *pathname, struct inode **res_inode)
{
int error;
char * tmp;
- error = getname(pathname,&tmp);
+ error = getname(pathname, &tmp);
if (!error) {
- error = _namei(tmp,NULL,0,res_inode);
+ error = _namei(tmp, NULL, 0, res_inode);
putname(tmp);
}
return error;
@@ -303,14 +287,14 @@ int lnamei(const char * pathname, struct inode ** res_inode)
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
*/
-int namei(const char * pathname, struct inode ** res_inode)
+int namei(const char *pathname, struct inode **res_inode)
{
int error;
char * tmp;
- error = getname(pathname,&tmp);
+ error = getname(pathname, &tmp);
if (!error) {
- error = _namei(tmp,NULL,1,res_inode);
+ error = _namei(tmp, NULL, 1, res_inode);
putname(tmp);
}
return error;
@@ -330,7 +314,7 @@ int namei(const char * pathname, struct inode ** res_inode)
* for symlinks (where the permissions are checked later).
*/
int open_namei(const char * pathname, int flag, int mode,
- struct inode ** res_inode, struct inode * base)
+ struct inode ** res_inode, struct inode * base)
{
const char * basename;
int namelen,error;
@@ -338,7 +322,7 @@ int open_namei(const char * pathname, int flag, int mode,
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;
- error = dir_namei(pathname,&namelen,&basename,base,&dir);
+ error = dir_namei(pathname, &namelen, &basename, base, &dir);
if (error)
return error;
if (!namelen) { /* special case: '/usr/' etc */
@@ -357,28 +341,30 @@ int open_namei(const char * pathname, int flag, int mode,
dir->i_count++; /* lookup eats the dir */
if (flag & O_CREAT) {
down(&dir->i_sem);
- error = lookup(dir,basename,namelen,&inode);
+ error = lookup(dir, basename, namelen, &inode);
if (!error) {
if (flag & O_EXCL) {
iput(inode);
error = -EEXIST;
}
- } else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- ; /* error is already set! */
+ } else if (IS_RDONLY(dir))
+ error = -EROFS;
else if (!dir->i_op || !dir->i_op->create)
error = -EACCES;
- else if (IS_RDONLY(dir))
- error = -EROFS;
+ else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
+ ; /* error is already set! */
else {
dir->i_count++; /* create eats the dir */
- error = dir->i_op->create(dir,basename,namelen,mode,res_inode);
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
+ error = dir->i_op->create(dir, basename, namelen, mode, res_inode);
up(&dir->i_sem);
iput(dir);
return error;
}
up(&dir->i_sem);
} else
- error = lookup(dir,basename,namelen,&inode);
+ error = lookup(dir, basename, namelen, &inode);
if (error) {
iput(dir);
return error;
@@ -394,7 +380,18 @@ int open_namei(const char * pathname, int flag, int mode,
iput(inode);
return error;
}
- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+ /*
+ * 2-Feb-1995 Bruce Perens <Bruce@Pixar.com>
+ * Allow opens of Unix domain sockets and FIFOs for write on
+ * read-only filesystems. Their data does not live on the disk.
+ *
+ * If there was something like IS_NODEV(inode) for
+ * pipes and/or sockets I'd check it here.
+ */
+ flag &= ~O_TRUNC;
+ }
+ else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
if (IS_NODEV(inode)) {
iput(inode);
return -EACCES;
@@ -409,30 +406,36 @@ int open_namei(const char * pathname, int flag, int mode,
/*
* An append-only file must be opened in append mode for writing
*/
- if (IS_APPEND(inode) && ((flag & 2) && !(flag & O_APPEND))) {
+ if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) {
iput(inode);
return -EPERM;
}
if (flag & O_TRUNC) {
- struct iattr newattrs;
-
if ((error = get_write_access(inode))) {
iput(inode);
return error;
}
- newattrs.ia_size = 0;
- newattrs.ia_valid = ATTR_SIZE;
- if ((error = notify_change(inode, &newattrs))) {
- put_write_access(inode);
+ /*
+ * Refuse to truncate files with mandatory locks held on them
+ */
+ error = locks_verify_locked(inode);
+ if (error) {
iput(inode);
return error;
}
- inode->i_size = 0;
- if (inode->i_op && inode->i_op->truncate)
- inode->i_op->truncate(inode);
- inode->i_dirt = 1;
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
+
+ error = do_truncate(inode, 0);
put_write_access(inode);
- }
+ if (error) {
+ iput(inode);
+ return error;
+ }
+ } else
+ if (flag & FMODE_WRITE)
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
*res_inode = inode;
return 0;
}
@@ -444,7 +447,7 @@ int do_mknod(const char * filename, int mode, dev_t dev)
struct inode * dir;
mode &= ~current->fs->umask;
- error = dir_namei(filename,&namelen,&basename, NULL, &dir);
+ error = dir_namei(filename, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -464,6 +467,8 @@ int do_mknod(const char * filename, int mode, dev_t dev)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
error = dir->i_op->mknod(dir,basename,namelen,mode,dev);
up(&dir->i_sem);
@@ -482,7 +487,7 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
case 0:
mode |= S_IFREG;
break;
- case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO:
+ case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
break;
default:
return -EINVAL;
@@ -495,13 +500,50 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
return error;
}
+/*
+ * Some operations need to remove trailing slashes for POSIX.1
+ * conformance. For rename we also need to change the behaviour
+ * depending on whether we had a trailing slash or not.. (we
+ * cannot rename normal files with trailing slashes, only dirs)
+ *
+ * "dummy" is used to make sure we don't do "/" -> "".
+ */
+static int remove_trailing_slashes(char * name)
+{
+ int result;
+ char dummy[1];
+ char *remove = dummy+1;
+
+ for (;;) {
+ char c = *name;
+ name++;
+ if (!c)
+ break;
+ if (c != '/') {
+ remove = NULL;
+ continue;
+ }
+ if (remove)
+ continue;
+ remove = name;
+ }
+
+ result = 0;
+ if (remove) {
+ remove[-1] = 0;
+ result = 1;
+ }
+
+ return result;
+}
+
static int do_mkdir(const char * pathname, int mode)
{
const char * basename;
int namelen, error;
struct inode * dir;
- error = dir_namei(pathname,&namelen,&basename,NULL,&dir);
+ error = dir_namei(pathname, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -521,8 +563,10 @@ static int do_mkdir(const char * pathname, int mode)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
- error = dir->i_op->mkdir(dir, basename, namelen, mode & 0777 & ~current->fs->umask);
+ error = dir->i_op->mkdir(dir, basename, namelen, mode & 01777 & ~current->fs->umask);
up(&dir->i_sem);
iput(dir);
return error;
@@ -535,6 +579,7 @@ asmlinkage int sys_mkdir(const char * pathname, int mode)
error = getname(pathname,&tmp);
if (!error) {
+ remove_trailing_slashes(tmp);
error = do_mkdir(tmp,mode);
putname(tmp);
}
@@ -547,7 +592,7 @@ static int do_rmdir(const char * name)
int namelen, error;
struct inode * dir;
- error = dir_namei(name,&namelen,&basename,NULL,&dir);
+ error = dir_namei(name, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -573,6 +618,8 @@ static int do_rmdir(const char * name)
iput(dir);
return -EPERM;
}
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
return dir->i_op->rmdir(dir,basename,namelen);
}
@@ -583,6 +630,7 @@ asmlinkage int sys_rmdir(const char * pathname)
error = getname(pathname,&tmp);
if (!error) {
+ remove_trailing_slashes(tmp);
error = do_rmdir(tmp);
putname(tmp);
}
@@ -595,7 +643,7 @@ static int do_unlink(const char * name)
int namelen, error;
struct inode * dir;
- error = dir_namei(name,&namelen,&basename,NULL,&dir);
+ error = dir_namei(name, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -621,6 +669,8 @@ static int do_unlink(const char * name)
iput(dir);
return -EPERM;
}
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
return dir->i_op->unlink(dir,basename,namelen);
}
@@ -643,7 +693,7 @@ static int do_symlink(const char * oldname, const char * newname)
const char * basename;
int namelen, error;
- error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+ error = dir_namei(newname, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -663,6 +713,8 @@ static int do_symlink(const char * oldname, const char * newname)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
error = dir->i_op->symlink(dir,basename,namelen,oldname);
up(&dir->i_sem);
@@ -693,7 +745,7 @@ static int do_link(struct inode * oldinode, const char * newname)
const char * basename;
int namelen, error;
- error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+ error = dir_namei(newname, &namelen, &basename, NULL, &dir);
if (error) {
iput(oldinode);
return error;
@@ -732,6 +784,8 @@ static int do_link(struct inode * oldinode, const char * newname)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
error = dir->i_op->link(oldinode, dir, basename, namelen);
up(&dir->i_sem);
@@ -745,7 +799,7 @@ asmlinkage int sys_link(const char * oldname, const char * newname)
char * to;
struct inode * oldinode;
- error = namei(oldname, &oldinode);
+ error = lnamei(oldname, &oldinode);
if (error)
return error;
error = getname(newname,&to);
@@ -758,13 +812,13 @@ asmlinkage int sys_link(const char * oldname, const char * newname)
return error;
}
-static int do_rename(const char * oldname, const char * newname)
+static int do_rename(const char * oldname, const char * newname, int must_be_dir)
{
struct inode * old_dir, * new_dir;
const char * old_base, * new_base;
int old_len, new_len, error;
- error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir);
+ error = dir_namei(oldname, &old_len, &old_base, NULL, &old_dir);
if (error)
return error;
if ((error = permission(old_dir,MAY_WRITE | MAY_EXEC)) != 0) {
@@ -777,7 +831,7 @@ static int do_rename(const char * oldname, const char * newname)
iput(old_dir);
return -EPERM;
}
- error = dir_namei(newname,&new_len,&new_base,NULL,&new_dir);
+ error = dir_namei(newname, &new_len, &new_base, NULL, &new_dir);
if (error) {
iput(old_dir);
return error;
@@ -818,9 +872,11 @@ static int do_rename(const char * oldname, const char * newname)
return -EPERM;
}
new_dir->i_count++;
+ if (new_dir->i_sb && new_dir->i_sb->dq_op)
+ new_dir->i_sb->dq_op->initialize(new_dir, -1);
down(&new_dir->i_sem);
error = old_dir->i_op->rename(old_dir, old_base, old_len,
- new_dir, new_base, new_len);
+ new_dir, new_base, new_len, must_be_dir);
up(&new_dir->i_sem);
iput(new_dir);
return error;
@@ -835,7 +891,9 @@ asmlinkage int sys_rename(const char * oldname, const char * newname)
if (!error) {
error = getname(newname,&to);
if (!error) {
- error = do_rename(from,to);
+ error = do_rename(from,to,
+ remove_trailing_slashes(from) |
+ remove_trailing_slashes(to));
putname(to);
}
putname(from);