// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2019 Junjiro R. Okajima */ /* * sub-routines for VFS */ #include #include #include #include "aufs.h" struct file *vfsub_dentry_open(struct path *path, int flags) { struct file *file; file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, current_cred()); if (!IS_ERR_OR_NULL(file) && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) i_readcount_inc(d_inode(path->dentry)); return file; } struct file *vfsub_filp_open(const char *path, int oflags, int mode) { struct file *file; lockdep_off(); file = filp_open(path, oflags /* | __FMODE_NONOTIFY */, mode); lockdep_on(); return file; } int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) { int err; err = kern_path(name, flags, path); /* add more later */ return err; } struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, int len) { struct path path = { .mnt = NULL }; /* VFS checks it too, but by WARN_ON_ONCE() */ IMustLock(d_inode(parent)); path.dentry = lookup_one_len(name, parent, len); if (IS_ERR(path.dentry)) goto out; out: AuTraceErrPtr(path.dentry); return path.dentry; } /* ---------------------------------------------------------------------- */ int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) { int err; struct dentry *d; IMustLock(dir); d = path->dentry; path->dentry = d->d_parent; err = security_path_mknod(path, d, mode, 0); path->dentry = d; if (unlikely(err)) goto out; lockdep_off(); err = vfs_create(dir, path->dentry, mode, want_excl); lockdep_on(); out: return err; } static int au_test_nlink(struct inode *inode) { const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ if (!au_test_fs_no_limit_nlink(inode->i_sb) || inode->i_nlink < link_max) return 0; return -EMLINK; } int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, struct inode **delegated_inode) { int err; struct dentry *d; IMustLock(dir); err = au_test_nlink(d_inode(src_dentry)); if (unlikely(err)) return err; /* we don't call may_linkat() */ d = path->dentry; path->dentry = d->d_parent; err = security_path_link(src_dentry, path, d); path->dentry = d; if (unlikely(err)) goto out; lockdep_off(); err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); lockdep_on(); out: return err; } int vfsub_mkdir(struct inode *dir, struct path *path, int mode) { int err; struct dentry *d; IMustLock(dir); d = path->dentry; path->dentry = d->d_parent; err = security_path_mkdir(path, d, mode); path->dentry = d; if (unlikely(err)) goto out; lockdep_off(); err = vfs_mkdir(dir, path->dentry, mode); lockdep_on(); out: return err; } int vfsub_rmdir(struct inode *dir, struct path *path) { int err; struct dentry *d; IMustLock(dir); d = path->dentry; path->dentry = d->d_parent; err = security_path_rmdir(path, d); path->dentry = d; if (unlikely(err)) goto out; lockdep_off(); err = vfs_rmdir(dir, path->dentry); lockdep_on(); out: return err; } /* ---------------------------------------------------------------------- */ struct unlink_args { int *errp; struct inode *dir; struct path *path; struct inode **delegated_inode; }; static void call_unlink(void *args) { struct unlink_args *a = args; struct dentry *d = a->path->dentry; struct inode *h_inode; const int stop_sillyrename = (au_test_nfs(d->d_sb) && au_dcount(d) == 1); IMustLock(a->dir); a->path->dentry = d->d_parent; *a->errp = security_path_unlink(a->path, d); a->path->dentry = d; if (unlikely(*a->errp)) return; if (!stop_sillyrename) dget(d); h_inode = NULL; if (d_is_positive(d)) { h_inode = d_inode(d); ihold(h_inode); } lockdep_off(); *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); lockdep_on(); if (!stop_sillyrename) dput(d); if (h_inode) iput(h_inode); AuTraceErr(*a->errp); } /* * @dir: must be locked. * @dentry: target dentry. */ int vfsub_unlink(struct inode *dir, struct path *path, struct inode **delegated_inode, int force) { int err; struct unlink_args args = { .errp = &err, .dir = dir, .path = path, .delegated_inode = delegated_inode }; if (!force) call_unlink(&args); else { int wkq_err; wkq_err = au_wkq_wait(call_unlink, &args); if (unlikely(wkq_err)) err = wkq_err; } return err; }