mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-20 14:16:02 +00:00
![Fabio M. De Francesco](/assets/img/avatar_default.png)
With commit 849ad04cf5
("new helper: put_and_unmap_page()"), Al Viro
introduced the put_and_unmap_page() to use in those many places where we
have a common pattern consisting of calls to kunmap_local() +
put_page().
Obviously, first we unmap and then we put pages. Instead, the original
name of this helper seems to imply that we first put and then unmap.
Therefore, rename the helper and change the only known upstreamed user
(i.e., fs/sysv) before this helper enters common use and might become
difficult to find all call sites and instead easy to break the builds.
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
Reviewed-by: Eric Biggers <ebiggers@google.com>
Message-Id: <20230602103307.5637-1-fmdefrancesco@gmail.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
280 lines
5.8 KiB
C
280 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/fs/sysv/namei.c
|
|
*
|
|
* minix/namei.c
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
*
|
|
* coh/namei.c
|
|
* Copyright (C) 1993 Pascal Haible, Bruno Haible
|
|
*
|
|
* sysv/namei.c
|
|
* Copyright (C) 1993 Bruno Haible
|
|
* Copyright (C) 1997, 1998 Krzysztof G. Baranowski
|
|
*/
|
|
|
|
#include <linux/pagemap.h>
|
|
#include "sysv.h"
|
|
|
|
static int add_nondir(struct dentry *dentry, struct inode *inode)
|
|
{
|
|
int err = sysv_add_link(dentry, inode);
|
|
if (!err) {
|
|
d_instantiate(dentry, inode);
|
|
return 0;
|
|
}
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
return err;
|
|
}
|
|
|
|
static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry, unsigned int flags)
|
|
{
|
|
struct inode * inode = NULL;
|
|
ino_t ino;
|
|
|
|
if (dentry->d_name.len > SYSV_NAMELEN)
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
ino = sysv_inode_by_name(dentry);
|
|
if (ino)
|
|
inode = sysv_iget(dir->i_sb, ino);
|
|
return d_splice_alias(inode, dentry);
|
|
}
|
|
|
|
static int sysv_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode, dev_t rdev)
|
|
{
|
|
struct inode * inode;
|
|
int err;
|
|
|
|
if (!old_valid_dev(rdev))
|
|
return -EINVAL;
|
|
|
|
inode = sysv_new_inode(dir, mode);
|
|
err = PTR_ERR(inode);
|
|
|
|
if (!IS_ERR(inode)) {
|
|
sysv_set_inode(inode, rdev);
|
|
mark_inode_dirty(inode);
|
|
err = add_nondir(dentry, inode);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int sysv_create(struct mnt_idmap *idmap, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode, bool excl)
|
|
{
|
|
return sysv_mknod(&nop_mnt_idmap, dir, dentry, mode, 0);
|
|
}
|
|
|
|
static int sysv_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
|
struct dentry *dentry, const char *symname)
|
|
{
|
|
int err = -ENAMETOOLONG;
|
|
int l = strlen(symname)+1;
|
|
struct inode * inode;
|
|
|
|
if (l > dir->i_sb->s_blocksize)
|
|
goto out;
|
|
|
|
inode = sysv_new_inode(dir, S_IFLNK|0777);
|
|
err = PTR_ERR(inode);
|
|
if (IS_ERR(inode))
|
|
goto out;
|
|
|
|
sysv_set_inode(inode, 0);
|
|
err = page_symlink(inode, symname, l);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
mark_inode_dirty(inode);
|
|
err = add_nondir(dentry, inode);
|
|
out:
|
|
return err;
|
|
|
|
out_fail:
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
goto out;
|
|
}
|
|
|
|
static int sysv_link(struct dentry * old_dentry, struct inode * dir,
|
|
struct dentry * dentry)
|
|
{
|
|
struct inode *inode = d_inode(old_dentry);
|
|
|
|
inode->i_ctime = current_time(inode);
|
|
inode_inc_link_count(inode);
|
|
ihold(inode);
|
|
|
|
return add_nondir(dentry, inode);
|
|
}
|
|
|
|
static int sysv_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode)
|
|
{
|
|
struct inode * inode;
|
|
int err;
|
|
|
|
inode_inc_link_count(dir);
|
|
|
|
inode = sysv_new_inode(dir, S_IFDIR|mode);
|
|
err = PTR_ERR(inode);
|
|
if (IS_ERR(inode))
|
|
goto out_dir;
|
|
|
|
sysv_set_inode(inode, 0);
|
|
|
|
inode_inc_link_count(inode);
|
|
|
|
err = sysv_make_empty(inode, dir);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
err = sysv_add_link(dentry, inode);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
d_instantiate(dentry, inode);
|
|
out:
|
|
return err;
|
|
|
|
out_fail:
|
|
inode_dec_link_count(inode);
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
out_dir:
|
|
inode_dec_link_count(dir);
|
|
goto out;
|
|
}
|
|
|
|
static int sysv_unlink(struct inode * dir, struct dentry * dentry)
|
|
{
|
|
struct inode * inode = d_inode(dentry);
|
|
struct page * page;
|
|
struct sysv_dir_entry * de;
|
|
int err;
|
|
|
|
de = sysv_find_entry(dentry, &page);
|
|
if (!de)
|
|
return -ENOENT;
|
|
|
|
err = sysv_delete_entry(de, page);
|
|
if (!err) {
|
|
inode->i_ctime = dir->i_ctime;
|
|
inode_dec_link_count(inode);
|
|
}
|
|
unmap_and_put_page(page, de);
|
|
return err;
|
|
}
|
|
|
|
static int sysv_rmdir(struct inode * dir, struct dentry * dentry)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
int err = -ENOTEMPTY;
|
|
|
|
if (sysv_empty_dir(inode)) {
|
|
err = sysv_unlink(dir, dentry);
|
|
if (!err) {
|
|
inode->i_size = 0;
|
|
inode_dec_link_count(inode);
|
|
inode_dec_link_count(dir);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Anybody can rename anything with this: the permission checks are left to the
|
|
* higher-level routines.
|
|
*/
|
|
static int sysv_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
|
struct dentry *old_dentry, struct inode *new_dir,
|
|
struct dentry *new_dentry, unsigned int flags)
|
|
{
|
|
struct inode * old_inode = d_inode(old_dentry);
|
|
struct inode * new_inode = d_inode(new_dentry);
|
|
struct page * dir_page = NULL;
|
|
struct sysv_dir_entry * dir_de = NULL;
|
|
struct page * old_page;
|
|
struct sysv_dir_entry * old_de;
|
|
int err = -ENOENT;
|
|
|
|
if (flags & ~RENAME_NOREPLACE)
|
|
return -EINVAL;
|
|
|
|
old_de = sysv_find_entry(old_dentry, &old_page);
|
|
if (!old_de)
|
|
goto out;
|
|
|
|
if (S_ISDIR(old_inode->i_mode)) {
|
|
err = -EIO;
|
|
dir_de = sysv_dotdot(old_inode, &dir_page);
|
|
if (!dir_de)
|
|
goto out_old;
|
|
}
|
|
|
|
if (new_inode) {
|
|
struct page * new_page;
|
|
struct sysv_dir_entry * new_de;
|
|
|
|
err = -ENOTEMPTY;
|
|
if (dir_de && !sysv_empty_dir(new_inode))
|
|
goto out_dir;
|
|
|
|
err = -ENOENT;
|
|
new_de = sysv_find_entry(new_dentry, &new_page);
|
|
if (!new_de)
|
|
goto out_dir;
|
|
err = sysv_set_link(new_de, new_page, old_inode);
|
|
unmap_and_put_page(new_page, new_de);
|
|
if (err)
|
|
goto out_dir;
|
|
new_inode->i_ctime = current_time(new_inode);
|
|
if (dir_de)
|
|
drop_nlink(new_inode);
|
|
inode_dec_link_count(new_inode);
|
|
} else {
|
|
err = sysv_add_link(new_dentry, old_inode);
|
|
if (err)
|
|
goto out_dir;
|
|
if (dir_de)
|
|
inode_inc_link_count(new_dir);
|
|
}
|
|
|
|
err = sysv_delete_entry(old_de, old_page);
|
|
if (err)
|
|
goto out_dir;
|
|
|
|
mark_inode_dirty(old_inode);
|
|
|
|
if (dir_de) {
|
|
err = sysv_set_link(dir_de, dir_page, new_dir);
|
|
if (!err)
|
|
inode_dec_link_count(old_dir);
|
|
}
|
|
|
|
out_dir:
|
|
if (dir_de)
|
|
unmap_and_put_page(dir_page, dir_de);
|
|
out_old:
|
|
unmap_and_put_page(old_page, old_de);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* directories can handle most operations...
|
|
*/
|
|
const struct inode_operations sysv_dir_inode_operations = {
|
|
.create = sysv_create,
|
|
.lookup = sysv_lookup,
|
|
.link = sysv_link,
|
|
.unlink = sysv_unlink,
|
|
.symlink = sysv_symlink,
|
|
.mkdir = sysv_mkdir,
|
|
.rmdir = sysv_rmdir,
|
|
.mknod = sysv_mknod,
|
|
.rename = sysv_rename,
|
|
.getattr = sysv_getattr,
|
|
};
|