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_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (error) goto out; inode = d_get_inode(nd.dentry); error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; imutex_lock(parent(inode)); 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); imutex_unlock(parent(inode)); dput_and_out: path_release(&nd); out: return error; }
asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz) { struct nameidata nd; int error; if (bufsiz <= 0) return -EINVAL; error = __user_walk_fd(dfd, path, 0, &nd); if (!error) { struct inode * inode = nd.dentry->d_inode; error = -EINVAL; if (inode->i_op && inode->i_op->readlink) { error = security_inode_readlink(nd.dentry); if (!error) { touch_atime(nd.mnt, nd.dentry); error = inode->i_op->readlink(nd.dentry, buf, bufsiz); } } path_release(&nd); } return error; }
long link_by_fd(int file_fd, int newdfd, const char __user * newname) { int error; int fput_needed; struct file *filep; struct nameidata nd; struct dentry *new_dentry; filep = fget_light(file_fd, &fput_needed); if(!filep) return -EBADF; error = __user_walk_fd(newdfd, newname, LOOKUP_PARENT, &nd); if(error) goto file_out; error = -EXDEV; if(filep->f_vfsmnt != nd.mnt) goto out_release; new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); if(!IS_ERR(new_dentry)) { error = vfs_link(filep->f_dentry, nd.dentry->d_inode, new_dentry); dput(new_dentry); } mutex_unlock(&nd.dentry->d_inode->i_mutex); out_release: path_release(&nd); file_out: fput_light(filep, fput_needed); return error; }
/* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { struct nameidata nd; int old_fsuid, old_fsgid; kernel_cap_t old_cap; int res; const struct _inode *inode; if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; old_fsuid = current->fsuid; old_fsgid = current->fsgid; old_cap = current->cap_effective; current->fsuid = current->uid; current->fsgid = current->gid; /* * Clear the capabilities if we switch to a non-root user * * FIXME: There is a race here against sys_capset. The * capabilities can change yet we will restore the old * value below. We should hold task_capabilities_lock, * but we cannot because user_path_walk can sleep. */ if (current->uid) cap_clear(current->cap_effective); else current->cap_effective = current->cap_permitted; res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); if (res) goto out; inode = d_get_inode_ro(nd.dentry); res = vfs_permission(inode, &nd, mode); /* SuS v2 requires we report a read only fs too */ if(res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; if(IS_RDONLY(inode)) res = -EROFS; out_path_release: path_release(&nd); out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; current->cap_effective = old_cap; return res; }
int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat) { struct nameidata nd; int error; error = __user_walk_fd(dfd, name, 0, &nd); if (!error) { error = vfs_getattr(nd.mnt, nd.dentry, stat); path_release(&nd); } return error; }
/* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ long do_utimes(int dfd, char __user *filename, struct timeval *times) { int error; struct nameidata nd; struct inode * inode; struct iattr newattrs; error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (error) goto out; inode = nd.dentry->d_inode; error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto dput_and_out; newattrs.ia_atime.tv_sec = times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000; newattrs.ia_mtime.tv_sec = times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_usec * 1000; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { error = -EACCES; if (IS_IMMUTABLE(inode)) goto dput_and_out; if (current->fsuid != inode->i_uid && (error = vfs_permission(&nd, MAY_WRITE)) != 0) goto dput_and_out; } mutex_lock(&inode->i_mutex); error = notify_change(nd.dentry, &newattrs); mutex_unlock(&inode->i_mutex); dput_and_out: path_release(&nd); out: return error; }
long name_to_handle_at(int dfd, const char __user * name, struct file_handle __user * handle, int flag) { int follow; long ret = -EINVAL; struct nameidata nd; struct file *file = NULL; if(!capable(CAP_DAC_OVERRIDE)) { ret = -EPERM; goto err_out; } if((flag & ~AT_SYMLINK_FOLLOW) != 0) goto err_out; if(name == NULL && dfd != AT_FDCWD) { file = fget(dfd); if(file) nd.dentry = file->f_dentry; else { ret = -EBADF; goto err_out; } } else { follow = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; ret = __user_walk_fd(dfd, name, follow, &nd); if(ret) goto err_out; } ret = do_sys_name_to_handle(&nd, handle); if(file) fput(file); else path_release(&nd); err_out: return ret; }
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) goto out; error = chown_common(nd.dentry, user, group); path_release(&nd); out: return error; }
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_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (error) goto out; inode = nd.path.dentry->d_inode; error = mnt_want_write(nd.path.mnt); if (error) goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out_drop_write; 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.path.dentry, &newattrs); mutex_unlock(&inode->i_mutex); out_drop_write: mnt_drop_write(nd.path.mnt); dput_and_out: path_put(&nd.path); out: return error; }
/* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) { int error; struct nameidata nd; struct dentry *dentry; struct inode *inode; struct iattr newattrs; struct file *f = NULL; error = -EINVAL; if (times && (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) { goto out; } if (flags & ~AT_SYMLINK_NOFOLLOW) goto out; if (filename == NULL && dfd != AT_FDCWD) { error = -EINVAL; if (flags & AT_SYMLINK_NOFOLLOW) goto out; error = -EBADF; f = fget(dfd); if (!f) goto out; dentry = f->f_path.dentry; } else { error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); if (error) goto out; dentry = nd.dentry; } inode = dentry->d_inode; error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto dput_and_out; if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { newattrs.ia_atime.tv_sec = times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_nsec; newattrs.ia_valid |= ATTR_ATIME_SET; } if (times[1].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_MTIME; else if (times[1].tv_nsec != UTIME_NOW) { newattrs.ia_mtime.tv_sec = times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } } else { error = -EACCES; if (IS_IMMUTABLE(inode)) goto dput_and_out; if (!is_owner_or_cap(inode)) { if (f) { if (!(f->f_mode & FMODE_WRITE)) goto dput_and_out; } else { error = vfs_permission(&nd, MAY_WRITE); if (error) goto dput_and_out; } } } mutex_lock(&inode->i_mutex); error = notify_change(dentry, &newattrs); mutex_unlock(&inode->i_mutex); dput_and_out: if (f) fput(f); else path_release(&nd); out: return error; }
/* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { struct nameidata nd; int old_fsuid, old_fsgid; kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */ int res; if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; old_fsuid = current->fsuid; old_fsgid = current->fsgid; current->fsuid = current->uid; current->fsgid = current->gid; if (!issecure(SECURE_NO_SETUID_FIXUP)) { /* * Clear the capabilities if we switch to a non-root user */ #ifndef CONFIG_SECURITY_FILE_CAPABILITIES /* * FIXME: There is a race here against sys_capset. The * capabilities can change yet we will restore the old * value below. We should hold task_capabilities_lock, * but we cannot because user_path_walk can sleep. */ #endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */ if (current->uid) old_cap = cap_set_effective(__cap_empty_set); else old_cap = cap_set_effective(current->cap_permitted); } res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); if (res) goto out; res = vfs_permission(&nd, mode); /* SuS v2 requires we report a read only fs too */ if(res || !(mode & S_IWOTH) || special_file(nd.path.dentry->d_inode->i_mode)) goto out_path_release; /* * This is a rare case where using __mnt_is_readonly() * is OK without a mnt_want/drop_write() pair. Since * no actual write to the fs is performed here, we do * not need to telegraph to that to anyone. * * By doing this, we accept that this access is * inherently racy and know that the fs may change * state before we even see this result. */ if (__mnt_is_readonly(nd.path.mnt)) res = -EROFS; out_path_release: path_put(&nd.path); out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; if (!issecure(SECURE_NO_SETUID_FIXUP)) cap_set_effective(old_cap); return res; }