📄 vfs.c
字号:
/* * vfs.c * * This file implements kernel downcalls from lento. * * Author: Rob Simmonds <simmonds@stelias.com> * Andreas Dilger <adilger@stelias.com> * Copyright (C) 2000 Stelias Computing Inc * Copyright (C) 2000 Red Hat Inc. * * Extended attribute support * Copyright (C) 2001 Shirish H. Phatak, Tacit Networks, Inc. * * This code is based on code from namei.c in the linux file system; * see copyright notice below. *//** namei.c copyright **//* * linux/fs/namei.c * * Copyright (C) 1991, 1992 Linus Torvalds *//* * Some corrections by tytso. *//* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname * lookup logic. *//** end of namei.c copyright **/#include <linux/mm.h>#include <linux/proc_fs.h>#include <linux/smp_lock.h>#include <linux/quotaops.h>#include <asm/uaccess.h>#include <asm/unaligned.h>#include <asm/semaphore.h>#include <asm/pgtable.h>#include <linux/file.h>#include <linux/fs.h>#include <linux/blk.h>#include <linux/intermezzo_fs.h>#include <linux/intermezzo_upcall.h>#include <linux/intermezzo_psdev.h>#include <linux/intermezzo_kml.h>#ifdef CONFIG_FS_EXT_ATTR#include <linux/ext_attr.h>#ifdef CONFIG_FS_POSIX_ACL#include <linux/posix_acl.h>#endif#endifextern struct inode_operations presto_sym_iops;/* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. */static inline int check_sticky(struct inode *dir, struct inode *inode){ if (!(dir->i_mode & S_ISVTX)) return 0; if (inode->i_uid == current->fsuid) return 0; if (dir->i_uid == current->fsuid) return 0; return !capable(CAP_FOWNER);}/* from linux/fs/namei.c */static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir){ int error; if (!victim->d_inode || victim->d_parent->d_inode != dir) return -ENOENT; error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) return -EPERM; if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| IS_IMMUTABLE(victim->d_inode)) return -EPERM; if (isdir) { if (!S_ISDIR(victim->d_inode->i_mode)) return -ENOTDIR; if (IS_ROOT(victim)) return -EBUSY; } else if (S_ISDIR(victim->d_inode->i_mode)) return -EISDIR; return 0;}/* from linux/fs/namei.c */static inline int may_create(struct inode *dir, struct dentry *child) { if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; return permission(dir,MAY_WRITE | MAY_EXEC);}#ifdef PRESTO_DEBUG/* The loop_discard_io() function is available via a kernel patch to the * loop block device. It "works" by accepting writes, but throwing them * away, rather than trying to write them to disk. The old method worked * by setting the underlying device read-only, but that has the problem * that dirty buffers are kept in memory, and ext3 didn't like that at all. */#ifdef CONFIG_LOOP_DISCARD#define BLKDEV_FAIL(dev,fail) loop_discard_io(dev,fail)#else#define BLKDEV_FAIL(dev,fail) set_device_ro(dev, 1)#endif/* If a breakpoint has been set via /proc/sys/intermezzo/intermezzoX/errorval, * that is the same as "value", the underlying device will "fail" now. */inline void presto_debug_fail_blkdev(struct presto_file_set *fset, unsigned long value){ int minor = presto_f2m(fset); int errorval = upc_comms[minor].uc_errorval; kdev_t dev = fset->fset_mtpt->d_inode->i_dev; if (errorval && errorval == (long)value && !is_read_only(dev)) { CDEBUG(D_SUPER, "setting device %s read only\n", kdevname(dev)); BLKDEV_FAIL(dev, 1); upc_comms[minor].uc_errorval = -dev; }}#else#define presto_debug_fail_blkdev(dev,value) do {} while (0)#endifstatic inline int presto_do_kml(struct lento_vfs_context *info, struct inode* inode){ if ( ! (info->flags & LENTO_FL_KML) ) return 0; if ( inode->i_gid == presto_excluded_gid ) return 0; return 1;}static inline int presto_do_expect(struct lento_vfs_context *info, struct inode *inode){ if ( ! (info->flags & LENTO_FL_EXPECT) ) return 0; if ( inode->i_gid == presto_excluded_gid ) return 0; return 1;}/* XXX fixme: this should not fail, all these dentries are in memory when _we_ call this */int presto_settime(struct presto_file_set *fset, struct dentry *dentry, struct lento_vfs_context *ctx, int valid){ int error; struct inode *inode = dentry->d_inode; struct inode_operations *iops; struct iattr iattr; ENTRY; if (ctx->flags & LENTO_FL_IGNORE_TIME ) { EXIT; return 0; } iattr.ia_ctime = ctx->updated_time; iattr.ia_mtime = ctx->updated_time; iattr.ia_valid = valid; error = -EROFS; if (IS_RDONLY(inode)) { EXIT; return -EROFS; } if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { EXIT; return -EPERM; } error = -EPERM; iops = filter_c2cdiops(fset->fset_cache->cache_filter); if (!iops) { EXIT; return error; } if (iops->setattr != NULL) error = iops->setattr(dentry, &iattr); else { error = 0; inode_setattr(dentry->d_inode, &iattr); } EXIT; return error;}int presto_do_setattr(struct presto_file_set *fset, struct dentry *dentry, struct iattr *iattr, struct lento_vfs_context *info){ struct rec_info rec; struct inode *inode = dentry->d_inode; struct inode_operations *iops; int error; struct presto_version old_ver, new_ver; void *handle; off_t old_size=inode->i_size; ENTRY; error = -EROFS; if (IS_RDONLY(inode)) { EXIT; return -EROFS; } if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { EXIT; return -EPERM; } presto_getversion(&old_ver, dentry->d_inode); error = -EPERM; iops = filter_c2cdiops(fset->fset_cache->cache_filter); if (!iops && !iops->setattr) { EXIT; return error; } error = presto_reserve_space(fset->fset_cache, 2*PRESTO_REQHIGH); if (error) { EXIT; return error; } if (iattr->ia_valid & ATTR_SIZE) { handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_TRUNC); } else { handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_SETATTR); } if ( IS_ERR(handle) ) { printk("presto_do_setattr: no space for transaction\n"); presto_release_space(fset->fset_cache, 2*PRESTO_REQHIGH); return -ENOSPC; } if (dentry->d_inode && iops->setattr) { error = iops->setattr(dentry, iattr); } else { error = inode_change_ok(dentry->d_inode, iattr); if (!error) inode_setattr(inode, iattr); } if (!error && (iattr->ia_valid & ATTR_SIZE)) vmtruncate(inode, iattr->ia_size); if (error) { EXIT; goto exit; } presto_debug_fail_blkdev(fset, PRESTO_OP_SETATTR | 0x10); if ( presto_do_kml(info, dentry->d_inode) ) { if ((iattr->ia_valid & ATTR_SIZE) && (old_size != inode->i_size)) { struct file file; /* Journal a close whenever we see a potential truncate * At the receiving end, lento should explicitly remove * ATTR_SIZE from the list of valid attributes */ presto_getversion(&new_ver, inode); file.private_data = NULL; file.f_dentry = dentry; error=presto_journal_close(&rec, fset, &file, dentry, &new_ver); } if (!error) error = presto_journal_setattr(&rec, fset, dentry, &old_ver, iattr); } presto_debug_fail_blkdev(fset, PRESTO_OP_SETATTR | 0x20); if ( presto_do_expect(info, dentry->d_inode) ) error = presto_write_last_rcvd(&rec, fset, info); presto_debug_fail_blkdev(fset, PRESTO_OP_SETATTR | 0x30); EXIT;exit: presto_release_space(fset->fset_cache, 2*PRESTO_REQHIGH); presto_trans_commit(fset, handle); return error;}int lento_setattr(const char *name, struct iattr *iattr, struct lento_vfs_context *info){ struct nameidata nd; struct dentry *dentry; struct presto_file_set *fset; int error;#ifdef CONFIG_FS_POSIX_ACL int (*set_posix_acl)(struct inode *, int type, posix_acl_t *)=NULL;#endif ENTRY; CDEBUG(D_PIOCTL,"name %s, valid %#x, mode %#o, uid %d, gid %d, size %Ld\n", name, iattr->ia_valid, iattr->ia_mode, iattr->ia_uid, iattr->ia_gid, iattr->ia_size); CDEBUG(D_PIOCTL, "atime %#lx, mtime %#lx, ctime %#lx, attr_flags %#x\n", iattr->ia_atime, iattr->ia_mtime, iattr->ia_ctime, iattr->ia_attr_flags); CDEBUG(D_PIOCTL, "offset %d, recno %d, flags %#x\n", info->slot_offset, info->recno, info->flags); lock_kernel(); error = presto_walk(name, &nd); if (error) { EXIT; goto exit; } dentry = nd.dentry; fset = presto_fset(dentry); error = -EINVAL; if ( !fset ) { printk("No fileset!\n"); EXIT; goto exit_lock; } /* NOTE: this prevents us from changing the filetype on setattr, * as we normally only want to change permission bits. * If this is not correct, then we need to fix the perl code * to always send the file type OR'ed with the permission. */ if (iattr->ia_valid & ATTR_MODE) { int set_mode = iattr->ia_mode; iattr->ia_mode = (iattr->ia_mode & S_IALLUGO) | (dentry->d_inode->i_mode & ~S_IALLUGO); CDEBUG(D_PIOCTL, "chmod: orig %#o, set %#o, result %#o\n", dentry->d_inode->i_mode, set_mode, iattr->ia_mode);#ifdef CONFIG_FS_POSIX_ACL /* ACl code interacts badly with setattr * since it tries to modify the ACL using * set_ext_attr which recurses back into presto. * This only happens if ATTR_MODE is set. * Here we are doing a "forced" mode set * (initiated by lento), so we disable the * set_posix_acl operation which * prevents such recursion. -SHP * * This will probably still be required when native * acl journalling is in place. */ set_posix_acl=dentry->d_inode->i_op->set_posix_acl; dentry->d_inode->i_op->set_posix_acl=NULL;#endif } error = presto_do_setattr(fset, dentry, iattr, info);#ifdef CONFIG_FS_POSIX_ACL /* restore the inode_operations if we changed them*/ if (iattr->ia_valid & ATTR_MODE) dentry->d_inode->i_op->set_posix_acl=set_posix_acl;#endif EXIT;exit_lock: path_release(&nd);exit: unlock_kernel(); return error;}int presto_do_create(struct presto_file_set *fset, struct dentry *dir, struct dentry *dentry, int mode, struct lento_vfs_context *info){ struct rec_info rec; int error; struct presto_version tgt_dir_ver, new_file_ver; struct inode_operations *iops; void *handle; ENTRY; mode &= S_IALLUGO; mode |= S_IFREG; down(&dir->d_inode->i_zombie); error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH); if (error) { EXIT; up(&dir->d_inode->i_zombie); return error; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -