X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=fs%2Fopen.c;h=70e0230d8e77b79a5becb1cecece91e95a062a2e;hb=36177ba655c238e33400cc2837a28720b62784bd;hp=2fac58c51910cf250607a8f7161edc608d8c18cc;hpb=d7f6884ae0ae6e406ec3500fcde16e8f51642460;p=powerpc.git diff --git a/fs/open.c b/fs/open.c index 2fac58c519..70e0230d8e 100644 --- a/fs/open.c +++ b/fs/open.c @@ -16,9 +16,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -194,7 +196,8 @@ out: return error; } -int do_truncate(struct dentry *dentry, loff_t length) +int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + struct file *filp) { int err; struct iattr newattrs; @@ -204,15 +207,19 @@ int do_truncate(struct dentry *dentry, loff_t length) return -EINVAL; newattrs.ia_size = length; - newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + newattrs.ia_valid = ATTR_SIZE | time_attrs; + if (filp) { + newattrs.ia_file = filp; + newattrs.ia_valid |= ATTR_FILE; + } - down(&dentry->d_inode->i_sem); + mutex_lock(&dentry->d_inode->i_mutex); err = notify_change(dentry, &newattrs); - up(&dentry->d_inode->i_sem); + mutex_unlock(&dentry->d_inode->i_mutex); return err; } -static inline long do_sys_truncate(const char __user * path, loff_t length) +static long do_sys_truncate(const char __user * path, loff_t length) { struct nameidata nd; struct inode * inode; @@ -236,7 +243,7 @@ static inline long do_sys_truncate(const char __user * path, loff_t length) if (!S_ISREG(inode->i_mode)) goto dput_and_out; - error = permission(inode,MAY_WRITE,&nd); + error = vfs_permission(&nd, MAY_WRITE); if (error) goto dput_and_out; @@ -262,7 +269,7 @@ static inline long do_sys_truncate(const char __user * path, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); - error = do_truncate(nd.dentry, length); + error = do_truncate(nd.dentry, length, 0, NULL); } put_write_access(inode); @@ -278,7 +285,7 @@ asmlinkage long sys_truncate(const char __user * path, unsigned long length) return do_sys_truncate(path, (long)length); } -static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small) +static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) { struct inode * inode; struct dentry *dentry; @@ -314,7 +321,7 @@ static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small) error = locks_verify_truncate(inode, file, length); if (!error) - error = do_truncate(dentry, length); + error = do_truncate(dentry, length, 0, file); out_putf: fput(file); out: @@ -377,7 +384,7 @@ asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times) error = get_user(newattrs.ia_atime.tv_sec, ×->actime); newattrs.ia_atime.tv_nsec = 0; - if (!error) + if (!error) error = get_user(newattrs.ia_mtime.tv_sec, ×->modtime); newattrs.ia_mtime.tv_nsec = 0; if (error) @@ -390,12 +397,12 @@ asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times) goto dput_and_out; if (current->fsuid != inode->i_uid && - (error = permission(inode,MAY_WRITE,&nd)) != 0) + (error = vfs_permission(&nd, MAY_WRITE)) != 0) goto dput_and_out; } - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); error = notify_change(nd.dentry, &newattrs); - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); dput_and_out: path_release(&nd); out: @@ -408,14 +415,14 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -long do_utimes(char __user * filename, struct timeval * times) +long do_utimes(int dfd, char __user *filename, struct timeval *times) { int error; struct nameidata nd; struct inode * inode; struct iattr newattrs; - error = user_path_walk(filename, &nd); + error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (error) goto out; @@ -443,25 +450,30 @@ long do_utimes(char __user * filename, struct timeval * times) goto dput_and_out; if (current->fsuid != inode->i_uid && - (error = permission(inode,MAY_WRITE,&nd)) != 0) + (error = vfs_permission(&nd, MAY_WRITE)) != 0) goto dput_and_out; } - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); error = notify_change(nd.dentry, &newattrs); - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); dput_and_out: path_release(&nd); out: return error; } -asmlinkage long sys_utimes(char __user * filename, struct timeval __user * utimes) +asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes) { struct timeval times[2]; if (utimes && copy_from_user(×, utimes, sizeof(times))) return -EFAULT; - return do_utimes(filename, utimes ? times : NULL); + return do_utimes(dfd, filename, utimes ? times : NULL); +} + +asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes) +{ + return sys_futimesat(AT_FDCWD, filename, utimes); } @@ -470,7 +482,7 @@ asmlinkage long sys_utimes(char __user * filename, struct timeval __user * utime * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */ -asmlinkage long sys_access(const char __user * filename, int mode) +asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { struct nameidata nd; int old_fsuid, old_fsgid; @@ -500,9 +512,9 @@ asmlinkage long sys_access(const char __user * filename, int mode) else current->cap_effective = current->cap_permitted; - res = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); + res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); if (!res) { - res = permission(nd.dentry->d_inode, mode, &nd); + res = vfs_permission(&nd, mode); /* SuS v2 requires we report a read only fs too */ if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode) && !special_file(nd.dentry->d_inode->i_mode)) @@ -517,6 +529,11 @@ asmlinkage long sys_access(const char __user * filename, int mode) return res; } +asmlinkage long sys_access(const char __user *filename, int mode) +{ + return sys_faccessat(AT_FDCWD, filename, mode); +} + asmlinkage long sys_chdir(const char __user * filename) { struct nameidata nd; @@ -526,7 +543,7 @@ asmlinkage long sys_chdir(const char __user * filename) if (error) goto out; - error = permission(nd.dentry->d_inode,MAY_EXEC,&nd); + error = vfs_permission(&nd, MAY_EXEC); if (error) goto dput_and_out; @@ -559,7 +576,7 @@ asmlinkage long sys_fchdir(unsigned int fd) if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = permission(inode, MAY_EXEC, NULL); + error = file_permission(file, MAY_EXEC); if (!error) set_fs_pwd(current->fs, mnt, dentry); out_putf: @@ -577,7 +594,7 @@ asmlinkage long sys_chroot(const char __user * filename) if (error) goto out; - error = permission(nd.dentry->d_inode,MAY_EXEC,&nd); + error = vfs_permission(&nd, MAY_EXEC); if (error) goto dput_and_out; @@ -615,13 +632,13 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) err = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out_putf; - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; err = notify_change(dentry, &newattrs); - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); out_putf: fput(file); @@ -629,14 +646,15 @@ out: return err; } -asmlinkage long sys_chmod(const char __user * filename, mode_t mode) +asmlinkage long sys_fchmodat(int dfd, const char __user *filename, + mode_t mode) { struct nameidata nd; struct inode * inode; int error; struct iattr newattrs; - error = user_path_walk(filename, &nd); + error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (error) goto out; inode = nd.dentry->d_inode; @@ -649,13 +667,13 @@ asmlinkage long sys_chmod(const char __user * filename, mode_t mode) if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; error = notify_change(nd.dentry, &newattrs); - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); dput_and_out: path_release(&nd); @@ -663,6 +681,11 @@ out: return error; } +asmlinkage long sys_chmod(const char __user *filename, mode_t mode) +{ + return sys_fchmodat(AT_FDCWD, filename, mode); +} + static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { struct inode * inode; @@ -691,9 +714,9 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group) } if (!S_ISDIR(inode->i_mode)) newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID; - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); error = notify_change(dentry, &newattrs); - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); out: return error; } @@ -711,6 +734,26 @@ asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group) return error; } +asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, + gid_t group, int flag) +{ + struct nameidata nd; + int error = -EINVAL; + int follow; + + if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) + goto out; + + follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; + error = __user_walk_fd(dfd, filename, follow, &nd); + if (!error) { + error = chown_common(nd.dentry, user, group); + path_release(&nd); + } +out: + return error; +} + asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group) { struct nameidata nd; @@ -738,52 +781,16 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) return error; } -/* - * Note that while the flag value (low two bits) for sys_open means: - * 00 - read-only - * 01 - write-only - * 10 - read-write - * 11 - special - * it is changed into - * 00 - no permissions needed - * 01 - read-permission - * 10 - write-permission - * 11 - read-write - * for the internal routines (ie open_namei()/follow_link() etc). 00 is - * used by symlinks. - */ -struct file *filp_open(const char * filename, int flags, int mode) -{ - int namei_flags, error; - struct nameidata nd; - - namei_flags = flags; - if ((namei_flags+1) & O_ACCMODE) - namei_flags++; - if (namei_flags & O_TRUNC) - namei_flags |= 2; - - error = open_namei(filename, namei_flags, mode, &nd); - if (!error) - return dentry_open(nd.dentry, nd.mnt, flags); - - return ERR_PTR(error); -} - -EXPORT_SYMBOL(filp_open); - -struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) +static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, + int flags, struct file *f, + int (*open)(struct inode *, struct file *)) { - struct file * f; struct inode *inode; int error; - error = -ENFILE; - f = get_empty_filp(); - if (!f) - goto cleanup_dentry; f->f_flags = flags; - f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; + f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | + FMODE_PREAD | FMODE_PWRITE; inode = dentry->d_inode; if (f->f_mode & FMODE_WRITE) { error = get_write_access(inode); @@ -798,11 +805,14 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) f->f_op = fops_get(inode->i_fop); file_move(f, &inode->i_sb->s_files); - if (f->f_op && f->f_op->open) { - error = f->f_op->open(inode,f); + if (!open && f->f_op) + open = f->f_op->open; + if (open) { + error = open(inode, f); if (error) goto cleanup_all; } + f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); @@ -828,12 +838,123 @@ cleanup_all: f->f_vfsmnt = NULL; cleanup_file: put_filp(f); -cleanup_dentry: dput(dentry); mntput(mnt); return ERR_PTR(error); } +/* + * Note that while the flag value (low two bits) for sys_open means: + * 00 - read-only + * 01 - write-only + * 10 - read-write + * 11 - special + * it is changed into + * 00 - no permissions needed + * 01 - read-permission + * 10 - write-permission + * 11 - read-write + * for the internal routines (ie open_namei()/follow_link() etc). 00 is + * used by symlinks. + */ +static struct file *do_filp_open(int dfd, const char *filename, int flags, + int mode) +{ + int namei_flags, error; + struct nameidata nd; + + namei_flags = flags; + if ((namei_flags+1) & O_ACCMODE) + namei_flags++; + + error = open_namei(dfd, filename, namei_flags, mode, &nd); + if (!error) + return nameidata_to_filp(&nd, flags); + + return ERR_PTR(error); +} + +struct file *filp_open(const char *filename, int flags, int mode) +{ + return do_filp_open(AT_FDCWD, filename, flags, mode); +} +EXPORT_SYMBOL(filp_open); + +/** + * lookup_instantiate_filp - instantiates the open intent filp + * @nd: pointer to nameidata + * @dentry: pointer to dentry + * @open: open callback + * + * Helper for filesystems that want to use lookup open intents and pass back + * a fully instantiated struct file to the caller. + * This function is meant to be called from within a filesystem's + * lookup method. + * Note that in case of error, nd->intent.open.file is destroyed, but the + * path information remains valid. + * If the open callback is set to NULL, then the standard f_op->open() + * filesystem callback is substituted. + */ +struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, + int (*open)(struct inode *, struct file *)) +{ + if (IS_ERR(nd->intent.open.file)) + goto out; + if (IS_ERR(dentry)) + goto out_err; + nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt), + nd->intent.open.flags - 1, + nd->intent.open.file, + open); +out: + return nd->intent.open.file; +out_err: + release_open_intent(nd); + nd->intent.open.file = (struct file *)dentry; + goto out; +} +EXPORT_SYMBOL_GPL(lookup_instantiate_filp); + +/** + * nameidata_to_filp - convert a nameidata to an open filp. + * @nd: pointer to nameidata + * @flags: open flags + * + * Note that this function destroys the original nameidata + */ +struct file *nameidata_to_filp(struct nameidata *nd, int flags) +{ + struct file *filp; + + /* Pick up the filp from the open intent */ + filp = nd->intent.open.file; + /* Has the filesystem initialised the file for us? */ + if (filp->f_dentry == NULL) + filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL); + else + path_release(nd); + return filp; +} + +/* + * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an + * error. + */ +struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) +{ + int error; + struct file *f; + + error = -ENFILE; + f = get_empty_filp(); + if (f == NULL) { + dput(dentry); + mntput(mnt); + return ERR_PTR(error); + } + + return __dentry_open(dentry, mnt, flags, f, NULL); +} EXPORT_SYMBOL(dentry_open); /* @@ -894,7 +1015,7 @@ out: EXPORT_SYMBOL(get_unused_fd); -static inline void __put_unused_fd(struct files_struct *files, unsigned int fd) +static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); __FD_CLR(fd, fdt->open_fds); @@ -913,7 +1034,7 @@ void fastcall put_unused_fd(unsigned int fd) EXPORT_SYMBOL(put_unused_fd); /* - * Install a file pointer in the fd array. + * Install a file pointer in the fd array. * * The VFS is full of places where we drop the files lock between * setting the open_fds bitmap and installing the file in the file @@ -938,7 +1059,7 @@ void fastcall fd_install(unsigned int fd, struct file * file) EXPORT_SYMBOL(fd_install); -long do_sys_open(const char __user *filename, int flags, int mode) +long do_sys_open(int dfd, const char __user *filename, int flags, int mode) { char *tmp = getname(filename); int fd = PTR_ERR(tmp); @@ -946,7 +1067,7 @@ long do_sys_open(const char __user *filename, int flags, int mode) if (!IS_ERR(tmp)) { fd = get_unused_fd(); if (fd >= 0) { - struct file *f = filp_open(tmp, flags, mode); + struct file *f = do_filp_open(dfd, tmp, flags, mode); if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); @@ -965,10 +1086,20 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode) if (force_o_largefile()) flags |= O_LARGEFILE; - return do_sys_open(filename, flags, mode); + return do_sys_open(AT_FDCWD, filename, flags, mode); } EXPORT_SYMBOL_GPL(sys_open); +asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, + int mode) +{ + if (force_o_largefile()) + flags |= O_LARGEFILE; + + return do_sys_open(dfd, filename, flags, mode); +} +EXPORT_SYMBOL_GPL(sys_openat); + #ifndef __alpha__ /*