diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /fs/namei.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 280 |
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); |