opt
/
kaspersky
/
kav4fs
/
src
/
kernel
/
redirfs
➕ New
📤 Upload
✎ Editing:
rfs_file.c
← Back
/* * RedirFS: Redirecting File System * Written by Frantisek Hrbata <frantisek.hrbata@redirfs.org> * * Copyright 2008 - 2010 Frantisek Hrbata * All rights reserved. * * This file is part of RedirFS. * * RedirFS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RedirFS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RedirFS. If not, see <http://www.gnu.org/licenses/>. */ #include "rfs.h" static rfs_kmem_cache_t* rfs_file_cache = NULL; struct file_operations rfs_file_ops = {.open = rfs_open}; static struct rfs_file* rfs_file_alloc(struct file* file) { struct rfs_file* rfile; rfile = kmem_cache_zalloc(rfs_file_cache, GFP_KERNEL); if (!rfile) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&rfile->rdentry_list); INIT_LIST_HEAD(&rfile->data); rfile->file = file; spin_lock_init(&rfile->lock); atomic_set(&rfile->count, 1); rfile->op_old = fops_get(file->f_op); spin_lock_init(&rfile->lock); if (rfile->op_old) memcpy(&rfile->op_new, rfile->op_old, sizeof(struct file_operations)); rfile->op_new.open = rfs_open; return rfile; } struct rfs_file* rfs_file_get(struct rfs_file* rfile) { if (!rfile || IS_ERR(rfile)) return NULL; spin_lock(&rfile->lock); BUG_ON(!atomic_read(&rfile->count)); atomic_inc(&rfile->count); spin_unlock(&rfile->lock); return rfile; } void rfs_file_put(struct rfs_file* rfile) { if (!rfile || IS_ERR(rfile)) return; spin_lock(&rfile->lock); BUG_ON(!atomic_read(&rfile->count)); if (!atomic_dec_and_test(&rfile->count)) { spin_unlock(&rfile->lock); return; } rfs_dentry_put(rfile->rdentry); fops_put(rfile->op_old); rfs_data_remove(&rfile->data); spin_unlock(&rfile->lock); kmem_cache_free(rfs_file_cache, rfile); } struct rfs_file* rfs_file_add(struct file* file) { struct rfs_file* rfile; rfile = rfs_file_alloc(file); if (IS_ERR(rfile)) return rfile; rfile->rdentry = rfs_dentry_find(file->f_dentry); rfs_dentry_add_rfile(rfile->rdentry, rfile); fops_put(file->f_op); file->f_op = &rfile->op_new; rfs_file_get(rfile); spin_lock(&rfile->rdentry->lock); rfs_file_set_ops(rfile); spin_unlock(&rfile->rdentry->lock); return rfile; } static void rfs_file_del(struct rfs_file* rfile) { rfs_dentry_rem_rfile(rfile); rfile->file->f_op = fops_get(rfile->op_old); rfs_file_put(rfile); } int rfs_file_cache_create(void) { rfs_file_cache = rfs_kmem_cache_create("rfs_file_cache", sizeof(struct rfs_file)); if (!rfs_file_cache) return -ENOMEM; return 0; } void rfs_file_cache_destory(void) { kmem_cache_destroy(rfs_file_cache); } #define IS_NFS4_FILE(inode) \ (S_ISREG((inode)->i_mode) && ((inode)->i_sb->s_magic == 0x6969) && \ (NFS_PROTO(inode)->version == 4)) #define SC_MARKER ((void*)-1) /* * In kernel versions prior to 3.5.0 there was no open * operation for NFS4 files. These files supposed to be * open at the lookup or create stage (atomic open). It * wasn't possible to perform av-check at this stage * due to atomic nature of such open. Thus such lookup * opens in inode->lookup and dentry->d_revalidade are * dropped by redirfs if favor of emulating atomic open * (via dentry->d_revalidate) for NFS4 files at file->open. * * This emulated atomic open generates new file. However * only private_data of this file is saved and attached * to already provided file structure. Then file opened * via emulated atomic open will be dropped. * * Atomic open in turn is invokind lookup_instantiate_filp * which in several kernel versions able to invoke file->open * set to rfs_open, thus leading to recursive stack overflow. * To avoid this, file intented to be open via atomic open * is marked to be open via short circuit path. */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)) struct file* (*rfs_get_empty_filp)(void) = (void*)GET_EMPTY_FILP_ADDR; void (*rfs_release_open_intent)(struct nameidata* nd) = (void*) RELEASE_OPEN_INTENT_ADDR; static struct file_operations rfs_drop_ops = {.release = NULL}; static void rfs_drop_intent_file(struct nameidata* nd) { struct file* f = nd->intent.open.file; fops_put(f->f_op); f->f_op = &rfs_drop_ops; #if defined(IMA_COUNT_LEAVE) && defined(IMA_COUNT_UPDATE) ima_counts_get(f); #endif rfs_release_open_intent(nd); } #define IS_NFS4_OPEN(inode) IS_NFS4_FILE(inode) typedef int (*open_revalidate_t)(struct dentry* dentry, struct nameidata* nd); static int rfs_nfs4_open(struct file* file, open_revalidate_t open_revalidate) { int ret; struct nameidata nd; rfs_set_nameidata_path(&nd, file->f_dentry, file->f_vfsmnt); nd.flags = LOOKUP_OPEN; nd.intent.open = (struct open_intent){.flags = to_open_flags(file->f_flags), .create_mode = 0, .file = rfs_get_empty_filp()}; if (!nd.intent.open.file) return -ENFILE; nd.intent.open.file->private_data = SC_MARKER; ret = open_revalidate(file->f_dentry, &nd); if ((ret > 0) && !IS_ERR_OR_NULL(nd.intent.open.file) && nd.intent.open.file->f_dentry) { file->private_data = nd.intent.open.file->private_data; ret = 0; } else if (ret >= 0) { ret = IS_ERR(nd.intent.open.file) ? PTR_ERR(nd.intent.open.file) : -ESTALE; } if (!IS_ERR_OR_NULL(nd.intent.open.file)) rfs_drop_intent_file(&nd); return ret; } #else #define IS_NFS4_OPEN(inode) 0 #define rfs_nfs4_open(file, open_revalidate) (-EBADF) #endif int rfs_open(struct inode* inode, struct file* file) { struct rfs_file* rfile; struct rfs_dentry* rdentry; struct rfs_inode* rinode; struct rfs_info* rinfo; struct rfs_context rcont; struct redirfs_args rargs; rinode = rfs_inode_find(inode); fops_put(file->f_op); file->f_op = fops_get(rinode->fop_old); rdentry = rfs_dentry_find(file->f_dentry); if (!rdentry) { rfs_inode_put(rinode); if (file->f_op && file->f_op->open) return file->f_op->open(inode, file); return 0; } rinfo = rfs_dentry_get_rinfo(rdentry); rfs_dentry_put(rdentry); rfs_context_init(&rcont, 0); if (S_ISREG(inode->i_mode)) rargs.type.id = REDIRFS_REG_FOP_OPEN; else if (S_ISDIR(inode->i_mode)) rargs.type.id = REDIRFS_DIR_FOP_OPEN; else if (S_ISLNK(inode->i_mode)) rargs.type.id = REDIRFS_LNK_FOP_OPEN; else if (S_ISCHR(inode->i_mode)) rargs.type.id = REDIRFS_CHR_FOP_OPEN; else if (S_ISBLK(inode->i_mode)) rargs.type.id = REDIRFS_BLK_FOP_OPEN; else if (S_ISFIFO(inode->i_mode)) rargs.type.id = REDIRFS_FIFO_FOP_OPEN; rargs.args.f_open.inode = inode; rargs.args.f_open.file = file; /* Run open() in short-circuit if it was invoked via revalidate */ if (IS_NFS4_FILE(inode)) { if (file->private_data == SC_MARKER) { rargs.rv.rv_int = rinode->fop_old->open(rargs.args.f_open.inode, rargs.args.f_open.file); rfs_context_deinit(&rcont); rfs_inode_put(rinode); rfs_info_put(rinfo); return rargs.rv.rv_int; } } if (!rfs_precall_flts(rinfo->rchain, &rcont, &rargs)) { if (IS_NFS4_OPEN(inode)) { rargs.rv.rv_int = rfs_nfs4_open(file, rdentry->op_old->d_revalidate); } else if (rinode->fop_old && rinode->fop_old->open) { rargs.rv.rv_int = rinode->fop_old->open(rargs.args.f_open.inode, rargs.args.f_open.file); } else rargs.rv.rv_int = 0; } if (!rargs.rv.rv_int) { rfile = rfs_file_add(file); if (IS_ERR(rfile)) BUG(); rfs_file_put(rfile); } rfs_postcall_flts(rinfo->rchain, &rcont, &rargs); rfs_context_deinit(&rcont); rfs_inode_put(rinode); rfs_info_put(rinfo); return rargs.rv.rv_int; } static int rfs_release(struct inode* inode, struct file* file) { struct rfs_file* rfile; struct rfs_info* rinfo; struct rfs_context rcont; struct redirfs_args rargs; rfile = rfs_file_find(file); rinfo = rfs_dentry_get_rinfo(rfile->rdentry); rfs_context_init(&rcont, 0); if (S_ISREG(inode->i_mode)) rargs.type.id = REDIRFS_REG_FOP_RELEASE; else if (S_ISDIR(inode->i_mode)) rargs.type.id = REDIRFS_DIR_FOP_RELEASE; else if (S_ISLNK(inode->i_mode)) rargs.type.id = REDIRFS_LNK_FOP_RELEASE; else if (S_ISCHR(inode->i_mode)) rargs.type.id = REDIRFS_CHR_FOP_RELEASE; else if (S_ISBLK(inode->i_mode)) rargs.type.id = REDIRFS_BLK_FOP_RELEASE; else if (S_ISFIFO(inode->i_mode)) rargs.type.id = REDIRFS_FIFO_FOP_RELEASE; rargs.args.f_release.inode = inode; rargs.args.f_release.file = file; if (!rfs_precall_flts(rinfo->rchain, &rcont, &rargs)) { if (rfile->op_old && rfile->op_old->release) rargs.rv.rv_int = rfile->op_old->release(rargs.args.f_release.inode, rargs.args.f_release.file); else rargs.rv.rv_int = 0; } rfs_postcall_flts(rinfo->rchain, &rcont, &rargs); rfs_context_deinit(&rcont); rfs_file_del(rfile); rfs_file_put(rfile); rfs_info_put(rinfo); return rargs.rv.rv_int; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) static int rfs_readdir(struct file* file, void* dirent, filldir_t filldir) { struct rfs_file* rfile; struct rfs_info* rinfo; struct rfs_context rcont; struct redirfs_args rargs; struct dentry* d_last = NULL; if (S_ISDIR(file->f_dentry->d_inode->i_mode)) { DCACHE_LOCK(file->f_dentry); if (!list_empty(&file->f_dentry->d_subdirs)) d_last = rfs_d_child_entry(file->f_dentry->d_subdirs.next); if (d_last) { DENTRY_NESTED(d_last); d_last = dget_dlock(d_last); DENTRY_UNLOCK(d_last); } DCACHE_UNLOCK(file->f_dentry); } rfile = rfs_file_find(file); rinfo = rfs_dentry_get_rinfo(rfile->rdentry); rfs_context_init(&rcont, 0); if (S_ISDIR(file->f_dentry->d_inode->i_mode)) rargs.type.id = REDIRFS_DIR_FOP_READDIR; rargs.args.f_readdir.file = file; rargs.args.f_readdir.dirent = dirent; rargs.args.f_readdir.filldir = filldir; if (!rfs_precall_flts(rinfo->rchain, &rcont, &rargs)) { if (rfile->op_old && rfile->op_old->readdir) rargs.rv.rv_int = rfile->op_old->readdir(rargs.args.f_readdir.file, rargs.args.f_readdir.dirent, rargs.args.f_readdir.filldir); else rargs.rv.rv_int = -ENOTDIR; } rfs_postcall_flts(rinfo->rchain, &rcont, &rargs); rfs_context_deinit(&rcont); if (S_ISDIR(file->f_dentry->d_inode->i_mode)) { LIST_HEAD(sibs); if (!rfs_dcache_get_subs(file->f_dentry, &sibs, d_last)) { struct rfs_dcache_entry* sib; list_for_each_entry(sib, &sibs, list) if (rfs_dcache_rdentry_add(sib->dentry, rinfo)) BUG(); rfs_dcache_entry_free_list(&sibs); } if (d_last) dput(d_last); } rfs_file_put(rfile); rfs_info_put(rinfo); return rargs.rv.rv_int; } #else static int rfs_iterate(struct file* file, struct dir_context* ctx) { struct rfs_file* rfile; struct rfs_info* rinfo; struct rfs_context rcont; struct redirfs_args rargs; struct dentry* d_last = NULL; if (S_ISDIR(file->f_dentry->d_inode->i_mode)) { DCACHE_LOCK(file->f_dentry); if (!list_empty(&file->f_dentry->d_subdirs)) d_last = rfs_d_child_entry(file->f_dentry->d_subdirs.next); if (d_last) { DENTRY_NESTED(d_last); d_last = dget_dlock(d_last); DENTRY_UNLOCK(d_last); } DCACHE_UNLOCK(file->f_dentry); } rfile = rfs_file_find(file); rinfo = rfs_dentry_get_rinfo(rfile->rdentry); rfs_context_init(&rcont, 0); if (S_ISDIR(file->f_dentry->d_inode->i_mode)) rargs.type.id = REDIRFS_DIR_FOP_READDIR; rargs.args.f_iterate.file = file; rargs.args.f_iterate.ctx = ctx; if (!rfs_precall_flts(rinfo->rchain, &rcont, &rargs)) { if (rfile->op_old && rfile->op_old->iterate) rargs.rv.rv_int = rfile->op_old->iterate(rargs.args.f_iterate.file, rargs.args.f_iterate.ctx); else rargs.rv.rv_int = -ENOTDIR; } rfs_postcall_flts(rinfo->rchain, &rcont, &rargs); rfs_context_deinit(&rcont); if (S_ISDIR(file->f_dentry->d_inode->i_mode)) { LIST_HEAD(sibs); if (!rfs_dcache_get_subs(file->f_dentry, &sibs, d_last)) { struct rfs_dcache_entry* sib; list_for_each_entry(sib, &sibs, list) if (rfs_dcache_rdentry_add(sib->dentry, rinfo)) BUG(); rfs_dcache_entry_free_list(&sibs); } if (d_last) dput(d_last); } rfs_file_put(rfile); rfs_info_put(rinfo); return rargs.rv.rv_int; } #endif static void rfs_file_set_ops_reg(struct rfs_file* rfile) { } static void rfs_file_set_ops_dir(struct rfs_file* rfile) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) rfile->op_new.readdir = rfs_readdir; #else rfile->op_new.iterate = rfs_iterate; #endif } static void rfs_file_set_ops_lnk(struct rfs_file* rfile) { } static void rfs_file_set_ops_chr(struct rfs_file* rfile) { } static void rfs_file_set_ops_blk(struct rfs_file* rfile) { } static void rfs_file_set_ops_fifo(struct rfs_file* rfile) { } void rfs_file_set_ops(struct rfs_file* rfile) { umode_t mode; if (!rfile->rdentry->rinode) return; mode = rfile->rdentry->rinode->inode->i_mode; if (S_ISREG(mode)) rfs_file_set_ops_reg(rfile); else if (S_ISDIR(mode)) rfs_file_set_ops_dir(rfile); else if (S_ISLNK(mode)) rfs_file_set_ops_lnk(rfile); else if (S_ISCHR(mode)) rfs_file_set_ops_chr(rfile); else if (S_ISBLK(mode)) rfs_file_set_ops_blk(rfile); else if (S_ISFIFO(mode)) rfs_file_set_ops_fifo(rfile); rfile->op_new.release = rfs_release; }
💾 Save Changes
Cancel
📤 Upload File
×
Select File
Upload
Cancel
➕ Create New
×
Type
📄 File
📁 Folder
Name
Create
Cancel
✎ Rename Item
×
Current Name
New Name
Rename
Cancel
🔐 Change Permissions
×
Target File
Permission (e.g., 0755, 0644)
0755
0644
0777
Apply
Cancel