/* * 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, const struct cred *cred) { int error; struct file *f; validate_creds(cred); /* * We must always pass in a valid mount pointer. Historically * callers got away with not passing it, but we must enforce this at * the earliest possible point now to avoid strange problems deep in the * filesystem stack. */ if (!mnt) { printk(KERN_WARNING "%s called with NULL vfsmount\n", __func__); dump_stack(); return ERR_PTR(-EINVAL); } error = -ENFILE; f = get_empty_filp(); if (f == NULL) { dput(dentry); mntput(mnt); return ERR_PTR(error); } f->f_flags = flags; return __dentry_open(dentry, mnt, f, NULL, cred); }
struct file *dentry_open(const struct path *path, int flags, const struct cred *cred) { int error; struct file *f; validate_creds(cred); /* We must always pass in a valid mount pointer. */ BUG_ON(!path->mnt); f = get_empty_filp(); if (!IS_ERR(f)) { f->f_flags = flags; f->f_path = *path; error = do_dentry_open(f, NULL, cred); if (!error) { /* from now on we need fput() to dispose of f */ error = open_check_o_direct(f); if (error) { fput(f); f = ERR_PTR(error); } } else { put_filp(f); f = ERR_PTR(error); } } return f; }
struct file *dentry_open(const struct path *path, int flags, const struct cred *cred) { int error; struct file *f; validate_creds(cred); /* We must always pass in a valid mount pointer. */ BUG_ON(!path->mnt); error = -ENFILE; f = get_empty_filp(); if (f == NULL) return ERR_PTR(error); f->f_flags = flags; f->f_path = *path; error = do_dentry_open(f, NULL, cred); if (!error) { error = open_check_o_direct(f); if (error) { fput(f); f = ERR_PTR(error); } } else { put_filp(f); f = ERR_PTR(error); } return f; }
static int appcl_mask_perm_check(struct inode *inode, int mask) { struct inode_security_label *ilabel; const char *d_behaviour = APPCL_DEFAULT_ALLOW; const struct cred *c_cred; ilabel = inode->i_security; if (!ilabel || !mask) return 0; /* * Fetch current credential and default behaviour state */ c_cred = get_current_cred(); validate_creds(c_cred); d_behaviour = ilabel->d_behaviour; if (unlikely(IS_PRIVATE(inode))) return 0; rcu_read_lock(); mutex_lock(&ilabel->lock); /* * Check current credential path against inode 'PACL' entries */ if (check_inode_path_match(inode, c_cred)) { /* * Check requested permission mask against inode 'PACL' entries */ if (appcl_check_permission_mask_match(ilabel, c_cred, mask)) goto successout; else goto failout; } else { /* * Checks DENY default behaviour * Return -EACCES if true */ if (strncmp(d_behaviour, APPCL_DEFAULT_DENY, LOWERVALUELEN) == 0) goto failout; else goto successout; } successout: rcu_read_unlock(); mutex_unlock(&ilabel->lock); put_cred(c_cred); return 0; failout: rcu_read_unlock(); mutex_unlock(&ilabel->lock); put_cred(c_cred); return -EACCES; }
/** * call_usermodehelper_exec - start a usermode application * @sub_info: information about the subprocessa * @wait: wait for the application to finish and return status. * when -1 don't wait at all, but you get no useful error back when * the program couldn't be exec'ed. This makes it safe to call * from interrupt context. * * Runs a user-space application. The application is started * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). */ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) { DECLARE_COMPLETION_ONSTACK(done); int retval = 0; BUG_ON(atomic_read(&sub_info->cred->usage) != 1); validate_creds(sub_info->cred); if (!sub_info->path) { call_usermodehelper_freeinfo(sub_info); return -EINVAL; } helper_lock(); if (sub_info->path[0] == '\0') goto out; if (!khelper_wq || usermodehelper_disabled) { retval = -EBUSY; goto out; } sub_info->complete = &done; sub_info->wait = wait; queue_work(khelper_wq, &sub_info->work); if (wait == UMH_NO_WAIT) /* task has freed sub_info */ goto unlock; if (wait & UMH_KILLABLE) { retval = wait_for_completion_killable(&done); if (!retval) goto wait_done; /* umh_complete() will see NULL and free sub_info */ if (xchg(&sub_info->complete, NULL)) goto unlock; /* fallthrough, umh_complete() was already called */ } wait_for_completion(&done); wait_done: retval = sub_info->retval; out: call_usermodehelper_freeinfo(sub_info); unlock: helper_unlock(); return retval; }
/* * 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, const struct cred *cred) { int error; struct file *f; validate_creds(cred); /* We must always pass in a valid mount pointer. */ BUG_ON(!mnt); error = -ENFILE; f = get_empty_filp(); if (f == NULL) { dput(dentry); mntput(mnt); return ERR_PTR(error); } f->f_flags = flags; return __dentry_open(dentry, mnt, f, NULL, cred); }
/** * 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. * Beware of calling it for non-regular files! Those ->open methods might block * (e.g. in fifo_open), leaving you with parent locked (and in case of fifo, * leading to a deadlock, as nobody can open that fifo anymore, because * another process to open fifo will block on locked parent when doing lookup). * 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 *)) { struct path path = { .dentry = dentry, .mnt = nd->path.mnt }; const struct cred *cred = current_cred(); if (IS_ERR(nd->intent.open.file)) goto out; if (IS_ERR(dentry)) goto out_err; nd->intent.open.file = __dentry_open(&path, nd->intent.open.file, open, cred); out: return nd->intent.open.file; out_err: release_open_intent(nd); nd->intent.open.file = ERR_CAST(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) { const struct cred *cred = current_cred(); 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_path.dentry != NULL) { nd->intent.open.file = NULL; } else { struct file *res; struct inode *inode = nd->path.dentry->d_inode; if (inode->i_op->open) { res = inode->i_op->open(nd->path.dentry, filp, cred); if (!IS_ERR(res)) { nd->intent.open.file = NULL; } return res; } res = do_dentry_open(&nd->path, filp, NULL, cred); if (!IS_ERR(res)) { int error; nd->intent.open.file = NULL; BUG_ON(res != filp); error = open_check_o_direct(filp); if (error) { fput(filp); filp = ERR_PTR(error); } } else { /* Allow nd->intent.open.file to be recycled */ filp = res; } } 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, const struct cred *cred) { struct file *f; struct file *ret; struct path path = { .dentry = dentry, .mnt = mnt }; validate_creds(cred); /* We must always pass in a valid mount pointer. */ BUG_ON(!mnt); ret = ERR_PTR(-ENFILE); f = get_empty_filp(); if (f != NULL) { f->f_flags = flags; ret = vfs_open(&path, f, cred); } path_put(&path); return ret; } EXPORT_SYMBOL(dentry_open); /** * vfs_open - open the file at the given path * @path: path to open * @filp: newly allocated file with f_flag initialized * @cred: credentials to use * * Open the file. If successful, the returned file will have acquired * an additional reference for path. */ struct file *vfs_open(struct path *path, struct file *filp, const struct cred *cred) { struct inode *inode = path->dentry->d_inode; if (inode->i_op->open) return inode->i_op->open(path->dentry, filp, cred); else return __dentry_open(path, filp, NULL, cred); } EXPORT_SYMBOL(vfs_open); static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); __clear_open_fd(fd, fdt); if (fd < files->next_fd) files->next_fd = fd; } void put_unused_fd(unsigned int fd) { struct files_struct *files = current->files; spin_lock(&files->file_lock); __put_unused_fd(files, fd); spin_unlock(&files->file_lock); } EXPORT_SYMBOL(put_unused_fd); /* * 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 * array. At any such point, we are vulnerable to a dup2() race * installing a file in the array before us. We need to detect this and * fput() the struct file we are about to overwrite in this case. * * It should never happen - if we allow dup2() do it, _really_ bad things * will follow. */ void fd_install(unsigned int fd, struct file *file) { struct files_struct *files = current->files; struct fdtable *fdt; spin_lock(&files->file_lock); fdt = files_fdtable(files); BUG_ON(fdt->fd[fd] != NULL); rcu_assign_pointer(fdt->fd[fd], file); spin_unlock(&files->file_lock); } EXPORT_SYMBOL(fd_install); static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op) { int lookup_flags = 0; int acc_mode; if (flags & O_CREAT) op->mode = (mode & S_IALLUGO) | S_IFREG; else op->mode = 0; /* Must never be set by userspace */ flags &= ~FMODE_NONOTIFY; /* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only * check for O_DSYNC if the need any syncing at all we enforce it's * always set instead of having to deal with possibly weird behaviour * for malicious applications setting only __O_SYNC. */ if (flags & __O_SYNC) flags |= O_DSYNC; /* * If we have O_PATH in the open flag. Then we * cannot have anything other than the below set of flags */ if (flags & O_PATH) { flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; acc_mode = 0; } else { acc_mode = MAY_OPEN | ACC_MODE(flags); } op->open_flag = flags; /* O_TRUNC implies we need access checks for write permissions */ if (flags & O_TRUNC) acc_mode |= MAY_WRITE; /* Allow the LSM permission hook to distinguish append access from general write access. */ if (flags & O_APPEND) acc_mode |= MAY_APPEND; op->acc_mode = acc_mode; op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; if (flags & O_CREAT) { op->intent |= LOOKUP_CREATE; if (flags & O_EXCL) op->intent |= LOOKUP_EXCL; } if (flags & O_DIRECTORY) lookup_flags |= LOOKUP_DIRECTORY; if (!(flags & O_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; return lookup_flags; }