/** * 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; path_get(&nd->path); res = do_dentry_open(nd->path.dentry, nd->path.mnt, 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; }
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; error = vfs_open(path, f, 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; }
struct file *dentry_open(const struct path *path, int flags, const struct cred *cred) { int error; struct file *f; validate_creds(cred); 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) { 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 struct file *__dentry_open(struct path *path, struct file *f, int (*open)(struct inode *, struct file *), const struct cred *cred) { struct file *res = do_dentry_open(path, f, open, cred); if (!IS_ERR(res)) { int error = open_check_o_direct(f); if (error) { fput(res); res = ERR_PTR(error); } } else { put_filp(f); } return res; }
/* * Ideally this function should call VFS:do_last() in order to keep all its * checkings. But it is very hard for aufs to regenerate several VFS internal * structure such as nameidata. This is a second (or third) best approach. * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). */ int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, struct vfsub_aopen_args *args, struct au_branch *br) { int err; struct file *file = args->file; /* copied from linux/fs/namei.c:atomic_open() */ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; IMustLock(dir); AuDebugOn(!dir->i_op->atomic_open); err = au_br_test_oflag(args->open_flag, br); if (unlikely(err)) goto out; args->file->f_path.dentry = DENTRY_NOT_SET; args->file->f_path.mnt = au_br_mnt(br); err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, args->create_mode, args->opened); if (err >= 0) { /* some filesystems don't set FILE_CREATED while succeeded? */ if (*args->opened & FILE_CREATED) fsnotify_create(dir, dentry); } else goto out; if (!err) { /* todo: call VFS:may_open() here */ err = open_check_o_direct(file); /* todo: ima_file_check() too? */ if (!err && (args->open_flag & __FMODE_EXEC)) err = deny_write_access(file); if (unlikely(err)) /* note that the file is created and still opened */ goto out; } atomic_inc(&br->br_count); fsnotify_open(file); out: return err; }
/** * 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; }