mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
![Christian Brauner](/assets/img/avatar_default.png)
Extend some inode methods with an additional user namespace argument. A filesystem that is aware of idmapped mounts will receive the user namespace the mount has been marked with. This can be used for additional permission checking and also to enable filesystems to translate between uids and gids if they need to. We have implemented all relevant helpers in earlier patches. As requested we simply extend the exisiting inode method instead of introducing new ones. This is a little more code churn but it's mostly mechanical and doesnt't leave us with additional inode methods. Link: https://lore.kernel.org/r/20210121131959.646623-25-christian.brauner@ubuntu.com Cc: Christoph Hellwig <hch@lst.de> Cc: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
275 lines
5.6 KiB
C
275 lines
5.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/fs/minix/namei.c
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
*/
|
|
|
|
#include "minix.h"
|
|
|
|
static int add_nondir(struct dentry *dentry, struct inode *inode)
|
|
{
|
|
int err = minix_add_link(dentry, inode);
|
|
if (!err) {
|
|
d_instantiate(dentry, inode);
|
|
return 0;
|
|
}
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
return err;
|
|
}
|
|
|
|
static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry, unsigned int flags)
|
|
{
|
|
struct inode * inode = NULL;
|
|
ino_t ino;
|
|
|
|
if (dentry->d_name.len > minix_sb(dir->i_sb)->s_namelen)
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
|
|
ino = minix_inode_by_name(dentry);
|
|
if (ino)
|
|
inode = minix_iget(dir->i_sb, ino);
|
|
return d_splice_alias(inode, dentry);
|
|
}
|
|
|
|
static int minix_mknod(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode, dev_t rdev)
|
|
{
|
|
int error;
|
|
struct inode *inode;
|
|
|
|
if (!old_valid_dev(rdev))
|
|
return -EINVAL;
|
|
|
|
inode = minix_new_inode(dir, mode, &error);
|
|
|
|
if (inode) {
|
|
minix_set_inode(inode, rdev);
|
|
mark_inode_dirty(inode);
|
|
error = add_nondir(dentry, inode);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int minix_tmpfile(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode)
|
|
{
|
|
int error;
|
|
struct inode *inode = minix_new_inode(dir, mode, &error);
|
|
if (inode) {
|
|
minix_set_inode(inode, 0);
|
|
mark_inode_dirty(inode);
|
|
d_tmpfile(dentry, inode);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int minix_create(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode, bool excl)
|
|
{
|
|
return minix_mknod(mnt_userns, dir, dentry, mode, 0);
|
|
}
|
|
|
|
static int minix_symlink(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, const char *symname)
|
|
{
|
|
int err = -ENAMETOOLONG;
|
|
int i = strlen(symname)+1;
|
|
struct inode * inode;
|
|
|
|
if (i > dir->i_sb->s_blocksize)
|
|
goto out;
|
|
|
|
inode = minix_new_inode(dir, S_IFLNK | 0777, &err);
|
|
if (!inode)
|
|
goto out;
|
|
|
|
minix_set_inode(inode, 0);
|
|
err = page_symlink(inode, symname, i);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
err = add_nondir(dentry, inode);
|
|
out:
|
|
return err;
|
|
|
|
out_fail:
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
goto out;
|
|
}
|
|
|
|
static int minix_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 minix_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode)
|
|
{
|
|
struct inode * inode;
|
|
int err;
|
|
|
|
inode_inc_link_count(dir);
|
|
|
|
inode = minix_new_inode(dir, S_IFDIR | mode, &err);
|
|
if (!inode)
|
|
goto out_dir;
|
|
|
|
minix_set_inode(inode, 0);
|
|
|
|
inode_inc_link_count(inode);
|
|
|
|
err = minix_make_empty(inode, dir);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
err = minix_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 minix_unlink(struct inode * dir, struct dentry *dentry)
|
|
{
|
|
int err = -ENOENT;
|
|
struct inode * inode = d_inode(dentry);
|
|
struct page * page;
|
|
struct minix_dir_entry * de;
|
|
|
|
de = minix_find_entry(dentry, &page);
|
|
if (!de)
|
|
goto end_unlink;
|
|
|
|
err = minix_delete_entry(de, page);
|
|
if (err)
|
|
goto end_unlink;
|
|
|
|
inode->i_ctime = dir->i_ctime;
|
|
inode_dec_link_count(inode);
|
|
end_unlink:
|
|
return err;
|
|
}
|
|
|
|
static int minix_rmdir(struct inode * dir, struct dentry *dentry)
|
|
{
|
|
struct inode * inode = d_inode(dentry);
|
|
int err = -ENOTEMPTY;
|
|
|
|
if (minix_empty_dir(inode)) {
|
|
err = minix_unlink(dir, dentry);
|
|
if (!err) {
|
|
inode_dec_link_count(dir);
|
|
inode_dec_link_count(inode);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int minix_rename(struct user_namespace *mnt_userns,
|
|
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 minix_dir_entry * dir_de = NULL;
|
|
struct page * old_page;
|
|
struct minix_dir_entry * old_de;
|
|
int err = -ENOENT;
|
|
|
|
if (flags & ~RENAME_NOREPLACE)
|
|
return -EINVAL;
|
|
|
|
old_de = minix_find_entry(old_dentry, &old_page);
|
|
if (!old_de)
|
|
goto out;
|
|
|
|
if (S_ISDIR(old_inode->i_mode)) {
|
|
err = -EIO;
|
|
dir_de = minix_dotdot(old_inode, &dir_page);
|
|
if (!dir_de)
|
|
goto out_old;
|
|
}
|
|
|
|
if (new_inode) {
|
|
struct page * new_page;
|
|
struct minix_dir_entry * new_de;
|
|
|
|
err = -ENOTEMPTY;
|
|
if (dir_de && !minix_empty_dir(new_inode))
|
|
goto out_dir;
|
|
|
|
err = -ENOENT;
|
|
new_de = minix_find_entry(new_dentry, &new_page);
|
|
if (!new_de)
|
|
goto out_dir;
|
|
minix_set_link(new_de, new_page, old_inode);
|
|
new_inode->i_ctime = current_time(new_inode);
|
|
if (dir_de)
|
|
drop_nlink(new_inode);
|
|
inode_dec_link_count(new_inode);
|
|
} else {
|
|
err = minix_add_link(new_dentry, old_inode);
|
|
if (err)
|
|
goto out_dir;
|
|
if (dir_de)
|
|
inode_inc_link_count(new_dir);
|
|
}
|
|
|
|
minix_delete_entry(old_de, old_page);
|
|
mark_inode_dirty(old_inode);
|
|
|
|
if (dir_de) {
|
|
minix_set_link(dir_de, dir_page, new_dir);
|
|
inode_dec_link_count(old_dir);
|
|
}
|
|
return 0;
|
|
|
|
out_dir:
|
|
if (dir_de) {
|
|
kunmap(dir_page);
|
|
put_page(dir_page);
|
|
}
|
|
out_old:
|
|
kunmap(old_page);
|
|
put_page(old_page);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* directories can handle most operations...
|
|
*/
|
|
const struct inode_operations minix_dir_inode_operations = {
|
|
.create = minix_create,
|
|
.lookup = minix_lookup,
|
|
.link = minix_link,
|
|
.unlink = minix_unlink,
|
|
.symlink = minix_symlink,
|
|
.mkdir = minix_mkdir,
|
|
.rmdir = minix_rmdir,
|
|
.mknod = minix_mknod,
|
|
.rename = minix_rename,
|
|
.getattr = minix_getattr,
|
|
.tmpfile = minix_tmpfile,
|
|
};
|