* fs/sharefs/file.c
*
* Copyright (c) 1998-2022 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2022 Stony Brook University
* Copyright (c) 2003-2022 The Research Foundation of SUNY
* Copyright (c) 2023 Huawei Device Co., Ltd.
*/
#include "sharefs.h"
static int sharefs_readdir(struct file *file, struct dir_context *ctx)
{
int err;
struct file *lower_file = NULL;
struct dentry *dentry = file->f_path.dentry;
lower_file = sharefs_lower_file(file);
err = iterate_dir(lower_file, ctx);
file->f_pos = lower_file->f_pos;
if (err >= 0)
fsstack_copy_attr_atime(d_inode(dentry),
file_inode(lower_file));
return err;
}
static int sharefs_open(struct inode *inode, struct file *file)
{
int err = 0;
struct file *lower_file = NULL;
struct path lower_path;
if (d_unhashed(file->f_path.dentry)) {
err = -ENOENT;
goto out_err;
}
file->private_data =
kzalloc(sizeof(struct sharefs_file_info), GFP_KERNEL);
if (!SHAREFS_F(file)) {
err = -ENOMEM;
goto out_err;
}
sharefs_get_lower_path(file->f_path.dentry, &lower_path);
lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
path_put(&lower_path);
if (IS_ERR(lower_file)) {
err = PTR_ERR(lower_file);
lower_file = sharefs_lower_file(file);
if (lower_file) {
sharefs_set_lower_file(file, NULL);
fput(lower_file);
}
} else {
sharefs_set_lower_file(file, lower_file);
}
if (err) {
kfree(SHAREFS_F(file));
} else {
kuid_t uid = inode->i_uid;
kgid_t gid = inode->i_gid;
mode_t mode = inode->i_mode;
fsstack_copy_attr_all(inode, sharefs_lower_inode(inode));
inode->i_uid = uid;
inode->i_gid = gid;
inode->i_mode = mode;
}
out_err:
return err;
}
static int sharefs_flush(struct file *file, fl_owner_t id)
{
int err = 0;
struct file *lower_file = NULL;
lower_file = sharefs_lower_file(file);
if (lower_file && lower_file->f_op && lower_file->f_op->flush) {
filemap_write_and_wait(file->f_mapping);
err = lower_file->f_op->flush(lower_file, id);
}
return err;
}
static int sharefs_file_release(struct inode *inode, struct file *file)
{
struct file *lower_file;
lower_file = sharefs_lower_file(file);
if (lower_file) {
sharefs_set_lower_file(file, NULL);
fput(lower_file);
}
kfree(SHAREFS_F(file));
return 0;
}
static int sharefs_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
int err;
struct file *lower_file;
struct path lower_path;
struct dentry *dentry = file->f_path.dentry;
err = __generic_file_fsync(file, start, end, datasync);
if (err)
goto out;
lower_file = sharefs_lower_file(file);
sharefs_get_lower_path(dentry, &lower_path);
err = vfs_fsync_range(lower_file, start, end, datasync);
sharefs_put_lower_path(dentry, &lower_path);
out:
return err;
}
static int sharefs_fasync(int fd, struct file *file, int flag)
{
int err = 0;
struct file *lower_file = NULL;
lower_file = sharefs_lower_file(file);
if (lower_file->f_op && lower_file->f_op->fasync)
err = lower_file->f_op->fasync(fd, lower_file, flag);
return err;
}
* Sharefs cannot use generic_file_llseek as ->llseek, because it would
* only set the offset of the upper file. So we have to implement our
* own method to set both the upper and lower file offsets
* consistently.
*/
static loff_t sharefs_file_llseek(struct file *file, loff_t offset, int whence)
{
loff_t err;
struct file *lower_file;
lower_file = sharefs_lower_file(file);
lower_file->f_pos = file->f_pos;
err = generic_file_llseek(lower_file, offset, whence);
file->f_pos = lower_file->f_pos;
return err;
}
* Sharefs read_iter, redirect modified iocb to lower read_iter
*/
ssize_t sharefs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
int err;
struct file *file = iocb->ki_filp;
struct file *lower_file;
lower_file = sharefs_lower_file(file);
if (!lower_file->f_op->read_iter) {
err = -EINVAL;
goto out;
}
get_file(lower_file);
iocb->ki_filp = lower_file;
err = lower_file->f_op->read_iter(iocb, iter);
iocb->ki_filp = file;
fput(lower_file);
if (err >= 0 || err == -EIOCBQUEUED)
fsstack_copy_attr_atime(d_inode(file->f_path.dentry),
file_inode(lower_file));
out:
return err;
}
* Sharefs write_iter, redirect modified iocb to lower write_iter
*/
ssize_t sharefs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
int err;
struct file *file = iocb->ki_filp;
struct file *lower_file;
lower_file = sharefs_lower_file(file);
if (!lower_file->f_op->write_iter) {
err = -EINVAL;
goto out;
}
get_file(lower_file);
iocb->ki_filp = lower_file;
err = lower_file->f_op->write_iter(iocb, iter);
iocb->ki_filp = file;
fput(lower_file);
if (err >= 0 || err == -EIOCBQUEUED) {
fsstack_copy_inode_size(d_inode(file->f_path.dentry),
file_inode(lower_file));
fsstack_copy_attr_times(d_inode(file->f_path.dentry),
file_inode(lower_file));
}
out:
return err;
}
int sharefs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
int err = 0;
struct file *lower_file;
lower_file = sharefs_lower_file(file);
if (!lower_file)
return -EINVAL;
if (!lower_file->f_op->mmap)
return -ENODEV;
if (WARN_ON(file != vma->vm_file))
return -EIO;
vma->vm_file = get_file(lower_file);
err = call_mmap(vma->vm_file, vma);
if (err)
fput(lower_file);
else
fput(file);
file_accessed(file);
return err;
}
const struct file_operations sharefs_main_fops = {
.llseek = sharefs_file_llseek,
.open = sharefs_open,
.flush = sharefs_flush,
.release = sharefs_file_release,
.fsync = sharefs_fsync,
.fasync = sharefs_fasync,
.read_iter = sharefs_read_iter,
.write_iter = sharefs_write_iter,
.mmap = sharefs_file_mmap,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
};
const struct file_operations sharefs_dir_fops = {
.llseek = sharefs_file_llseek,
.read = generic_read_dir,
.iterate = sharefs_readdir,
.open = sharefs_open,
.release = sharefs_file_release,
.flush = sharefs_flush,
.fsync = sharefs_fsync,
.fasync = sharefs_fasync,
};