Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

445 lines (380 sloc) 10.498 kB
/*
* This file is part of the LZPL: Linux ZFS Posix Layer
*
* Copyright (c) 2010 Knowledge Quest Infotech Pvt. Ltd.
* Produced at Knowledge Quest Infotech Pvt. Ltd.
* Written by: Knowledge Quest Infotech Pvt. Ltd.
* zfs@kqinfotech.com
*
* This 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 2 of the License, or
* (at your option) any later version.
*
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/backing-dev.h>
#include <linux/sched.h>
#include <linux/parser.h>
#include <linux/statfs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <spl-debug.h>
#include <lzfs_inode.h>
#include <lzfs_snap.h>
#include <lzfs_exportfs.h>
#include <lzfs_xattr.h>
#include <linux/version.h>
#include <sys/mntent.h>
#include <spl_config.h>
#ifdef SS_DEBUG_SUBSYS
#undef SS_DEBUG_SUBSYS
#endif
/*
* Log LZFS debug messages as the spl SS_USER2 subsystem.
*/
#define SS_DEBUG_SUBSYS SS_USER2
/* symbol exported by zfs module */
extern int zfs_domount(vfs_t *vfsp, char *data);
extern int zfs_root(vfs_t *vfsp, vnode_t **vvp);
extern int zfs_umount(vfs_t *vfsp, int fflags, cred_t *cr);
extern int zfs_statvfs(vfs_t *vfsp, struct statvfs64 *statp);
extern void lzfs_zfsctl_create(vfs_t *);
extern void lzfs_zfsctl_destroy(vfs_t *);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
/* TODO
* Following checking needs part of lzfs/spl configuration step.
*/
static void lzfs_delete_vnode(struct inode *inode)
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)
loff_t oldsize = i_size_read(inode);
i_size_write(inode, 0);
truncate_pagecache(inode, oldsize, 0);
clear_inode(inode);
#else
struct address_space *mapping = inode->i_mapping;
loff_t new = 0;
i_size_write(inode, 0);
unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1);
truncate_inode_pages(mapping, new);
unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1);
clear_inode(inode);
#endif
}
static void
lzfs_clear_vnode(struct inode *inode)
{
vnode_t *vp;
SENTRY;
vp = LZFS_ITOV(inode);
ASSERT(vp->v_count == 1);
/* znode associated with this vnode is freed by zfs_inactive.
*
* each snapshot psuedo-inode does not contain corresponding
* znode on-disk hence we need not call zfs_inactive for those
* inodes i_private field of those inode is set to struct inode
* and is not NULL to distinguish them with other inodes having
* znode
*/
if(inode->i_ino != LZFS_ZFSCTL_INO_ROOT
&& inode->i_ino != LZFS_ZFSCTL_INO_SNAPDIR
&& inode->i_private == NULL ) {
zfs_inactive(vp, NULL, NULL);
}
vp->v_data = NULL;
SEXIT;
}
#else
static void
lzfs_evict_vnode(struct inode *inode)
{
loff_t oldsize = i_size_read(inode);
vnode_t *vp;
i_size_write(inode, 0);
truncate_pagecache(inode, oldsize, 0);
end_writeback(inode);
if (inode->i_nlink)
return;
/* delete the inode */
vp = LZFS_ITOV(inode);
ASSERT(vp->v_count == 1);
if(inode->i_ino != LZFS_ZFSCTL_INO_ROOT
&& inode->i_ino != LZFS_ZFSCTL_INO_SNAPDIR
&& inode->i_private == NULL ) {
zfs_inactive(vp, NULL, NULL);
}
vp->v_data = NULL;
}
#endif
static void
lzfs_put_super(struct super_block *sb)
{
struct dentry *mntpnt = ((vfs_t *)sb->s_fs_info)->vfs_mntpt;
SENTRY;
zfs_umount(sb->s_fs_info, 0, NULL);
if(((vfs_t *)sb->s_fs_info)->is_snap) {
d_invalidate(mntpnt);
}
kfree(sb->s_fs_info);
SEXIT;
}
static struct inode *
lzfs_alloc_vnode(struct super_block *sb)
{
vnode_t *vp = NULL;
SENTRY;
vp = vn_alloc(KM_SLEEP);
bzero(vp, sizeof(vnode_t));
mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
inode_init_once(LZFS_VTOI(vp));
LZFS_VTOI(vp)->i_version = 1;
SEXIT;
return LZFS_VTOI(vp);
}
static void
lzfs_destroy_vnode(struct inode *inode)
{
mutex_destroy(&(LZFS_ITOV(inode))->v_lock);
vn_free(LZFS_ITOV(inode));
}
/* Structure to keep all the zfs related callback routines.
*/
static inline vfs_t* lzfs_super(struct super_block *sb)
{
return sb->s_fs_info;
}
static int lzfs_statfs(struct dentry *dentry, struct kstatfs *statfs)
{
struct super_block *sb = dentry->d_sb;
vfs_t *vfsp = lzfs_super(sb);
struct statvfs64 stat;
BUG_ON(zfs_statvfs(vfsp, &stat));
statfs->f_type = vfsp->vfs_magic;
statfs->f_bsize = stat.f_frsize;
statfs->f_blocks = stat.f_blocks;
statfs->f_bfree = stat.f_bfree;
statfs->f_bavail = stat.f_bavail;
statfs->f_files = stat.f_files;
statfs->f_ffree = stat.f_ffree;
statfs->f_namelen = stat.f_namemax;
statfs->f_fsid.val[0] = (u32)stat.f_fsid;
statfs->f_fsid.val[1] = (u32)(stat.f_fsid >> 32);
return 0;
}
static int lzfs_show_options(struct seq_file *seq, struct vfsmount *vfsmnt)
{
vfs_t *vfsp = lzfs_super(vfsmnt->mnt_sb);
/*
if (vfs_isreadonly(vfsp))
seq_printf(seq, ",%s", MNTOPT_RO);
else
seq_printf(seq, ",%s", MNTOPT_RW);
*/
if (vfs_isatime(vfsp))
seq_printf(seq, ",%s", MNTOPT_ATIME);
else {
/* Linux Kernel Displays noatime when set, we need not do it */
// seq_printf(seq, ",%s", MNTOPT_NOATIME);
}
if (vfs_isxattr(vfsp))
seq_printf(seq, ",%s", MNTOPT_XATTR);
else
seq_printf(seq, ",%s", MNTOPT_NOXATTR);
if (!vfs_issuid(vfsp)) {
seq_printf(seq, ",%s", MNTOPT_NOSUID);
// seq_printf(seq, ",%s", MNTOPT_NODEVICES);
// seq_printf(seq, ",%s", MNTOPT_NOSETUID);
} else {
seq_printf(seq, ",%s", MNTOPT_SUID);
if (vfs_isdevice(vfsp))
seq_printf(seq, ",%s", MNTOPT_DEVICES);
else {
/* Linux Kernel Displays nodev by default */
// seq_printf(seq, ",%s", MNTOPT_NODEVICES);
}
if (vfs_issetuid(vfsp))
seq_printf(seq, ",%s", MNTOPT_SETUID);
else
seq_printf(seq, ",%s", MNTOPT_NOSETUID);
}
if (vfs_isexec(vfsp))
seq_printf(seq, ",%s", MNTOPT_EXEC);
else {
/* Linux Kernel Displays noexec by default */
// seq_printf(seq, ",%s", MNTOPT_NOEXEC);
}
return 0;
}
static const struct super_operations lzfs_ops = {
.alloc_inode = lzfs_alloc_vnode,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
.clear_inode = lzfs_clear_vnode,
.delete_inode = lzfs_delete_vnode,
#else
.evict_inode = lzfs_evict_vnode,
#endif
.destroy_inode = lzfs_destroy_vnode,
.put_super = lzfs_put_super,
.statfs = lzfs_statfs,
.show_options = lzfs_show_options,
};
static int
lzfs_fill_super(struct super_block *sb, void *data, int silent)
{
int error = 0;
vfs_t *vfsp = NULL;
vnode_t *root_vnode = NULL;
struct inode *root_inode = NULL;
struct dentry *root_dentry = NULL;
long ret = -EINVAL;
SENTRY;
vfsp = (vfs_t *) kzalloc(sizeof(vfs_t), KM_SLEEP);
vfsp->vfs_set_inode_ops = lzfs_set_inode_ops;
vfsp->vfs_super = sb;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_op = &lzfs_ops;
sb->s_time_gran = 1;
sb->s_flags = MS_ACTIVE;
sb->s_flags |= MS_POSIXACL;
sb->s_export_op = &zfs_export_ops;
sb->s_xattr = lzfs_xattr_handlers;
error = zfs_domount(vfsp, data);
if (error) {
printk(KERN_WARNING "mount failed to open the pool!!\n");
goto mount_failed;
}
vfsp->vfs_magic = (uint32_t) ZFS_MAGIC;
sb->s_fs_info = vfsp;
sb->s_magic = vfsp->vfs_magic;
if (!strchr((char *) data, '@')) {
vfsp->is_snap = 0;
} else {
vfsp->is_snap = 1;
}
sb->s_blocksize = vfsp->vfs_bsize;
sb->s_blocksize_bits = ilog2(vfsp->vfs_bsize);
sb->s_time_gran = 1;
zfs_root(sb->s_fs_info, &root_vnode);
if (!root_vnode) {
printk(KERN_WARNING "root inode failed to allocate");
goto mount_failed;
}
// root_inode = &root_vnode->v_inode;
root_inode = LZFS_VTOI(root_vnode);
root_dentry = d_alloc_root(root_inode);
if (!root_dentry) {
printk(KERN_WARNING "chkneep4: %s\n", __FUNCTION__);
goto mount_failed;
}
sb->s_root = root_dentry;
if (!strchr((char *) data, '@')) {
lzfs_zfsctl_create(vfsp);
}
SEXIT;
return 0;
mount_failed:
sb->s_fs_info = NULL;
kfree(vfsp);
SEXIT;
return (ret);
}
extern int zfs_register_callbacks(vfs_t *vfsp);
static int
lzfs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data, struct vfsmount *mnt)
{
int rc;
vfs_t *vfsp = NULL;
/* get the pool/file-system name in the dev_name
* There is no need for a block device for this file system.
* Let's call get_sb_nodev.
*/
SENTRY;
rc = get_sb_nodev(fs_type, flags, (void *)dev_name,
lzfs_fill_super, mnt);
if (rc)
return rc;
vfsp = lzfs_super(mnt->mnt_sb);
vfsp->vfsmnt = mnt;
/* copy the mount flags information (from Linux Kernel) to
* zfs file system
* */
if (flags & MS_RDONLY)
vfsp->vfs_flag |= VFS_RDONLY;
else
vfsp->vfs_flag &= ~VFS_RDONLY;
if (flags & MS_NOSUID)
vfsp->vfs_flag &= ~VFS_SUID;
else
vfsp->vfs_flag |= VFS_SUID;
if (flags & MS_NODEV)
vfsp->vfs_flag |= VFS_NODEVICES;
else
vfsp->vfs_flag &= ~VFS_NODEVICES;
if (flags & MS_NOEXEC)
vfsp->vfs_flag |= VFS_NOEXEC;
else
vfsp->vfs_flag &= ~VFS_NOEXEC;
if (flags & MS_NOATIME)
vfsp->vfs_flag &= ~VFS_ATIME;
else
vfsp->vfs_flag |= VFS_ATIME;
if(!vfsp->is_snap) {
if ((rc = zfs_register_callbacks(vfsp)))
lzfs_zfsctl_destroy(vfsp->vfs_super->s_fs_info);
}
SEXIT;
return rc;
}
static void
lzfs_kill_sb(struct super_block *sb)
{
vfs_t *vfsp;
SENTRY;
if(sb->s_fs_info) {
vfsp = (vfs_t *) sb->s_fs_info;
if (!vfsp->is_snap) {
lzfs_zfsctl_destroy(sb->s_fs_info);
}
}
kill_anon_super(sb);
SEXIT;
}
struct file_system_type lzfs_fs_type = {
.owner = THIS_MODULE,
.name = "zfs",
.get_sb = lzfs_get_sb,
.kill_sb = lzfs_kill_sb,
};
static int
init_lzfs_fs(void)
{
return register_filesystem(&lzfs_fs_type);
}
static void __exit
exit_lzfs_fs(void)
{
unregister_filesystem(&lzfs_fs_type);
}
module_init(init_lzfs_fs)
module_exit(exit_lzfs_fs)
MODULE_LICENSE("GPL");
//MODULE_LICENSE("Proprietary");
Jump to Line
Something went wrong with that request. Please try again.