Skip to content
Snippets Groups Projects
inode.c 62.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (!src->len)
    		return ERR_PTR(-EINVAL);
    	if (src->len < maxlen)
    		maxlen = src->len;
    	if (dst == NULL) {
    		p = dst = kmalloc(maxlen + 1, GFP_KERNEL);
    		if (p == NULL)
    			return ERR_PTR(-ENOMEM);
    	}
    	if (copy_from_user(dst, src->data, maxlen)) {
    
    Jesper Juhl's avatar
    Jesper Juhl committed
    		kfree(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return ERR_PTR(-EFAULT);
    	}
    	dst[maxlen] = '\0';
    	return dst;
    }
    
    static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
    	int flags, const char *dev_name, void *raw_data)
    {
    	int error;
    	struct nfs_server *server;
    	struct super_block *s;
    	struct nfs4_mount_data *data = raw_data;
    	void *p;
    
    
    	if (data == NULL) {
    		dprintk("%s: missing data argument\n", __FUNCTION__);
    		return ERR_PTR(-EINVAL);
    	}
    	if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) {
    		dprintk("%s: bad mount version\n", __FUNCTION__);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return ERR_PTR(-EINVAL);
    	}
    
    
    	server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!server)
    		return ERR_PTR(-ENOMEM);
    	/* Zero out the NFS state stuff */
    	init_nfsv4_state(server);
    
    	server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	p = nfs_copy_user_string(NULL, &data->hostname, 256);
    	if (IS_ERR(p))
    		goto out_err;
    	server->hostname = p;
    
    	p = nfs_copy_user_string(NULL, &data->mnt_path, 1024);
    	if (IS_ERR(p))
    		goto out_err;
    	server->mnt_path = p;
    
    	p = nfs_copy_user_string(server->ip_addr, &data->client_addr,
    			sizeof(server->ip_addr) - 1);
    	if (IS_ERR(p))
    		goto out_err;
    
    	/* We now require that the mount process passes the remote address */
    	if (data->host_addrlen != sizeof(server->addr)) {
    		s = ERR_PTR(-EINVAL);
    		goto out_free;
    	}
    	if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) {
    		s = ERR_PTR(-EFAULT);
    		goto out_free;
    	}
    	if (server->addr.sin_family != AF_INET ||
    	    server->addr.sin_addr.s_addr == INADDR_ANY) {
    
    		dprintk("%s: mount program didn't pass remote IP address!\n",
    				__FUNCTION__);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		s = ERR_PTR(-EINVAL);
    		goto out_free;
    	}
    
    
    	/* Fire up rpciod if not yet running */
    	s = ERR_PTR(rpciod_up());
    	if (IS_ERR(s)) {
    		dprintk("%s: couldn't start rpciod! Error = %ld\n",
    				__FUNCTION__, PTR_ERR(s));
    		goto out_free;
    	}
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
    
    	if (IS_ERR(s) || s->s_root)
    		goto out_free;
    
    	s->s_flags = flags;
    
    
    	error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (error) {
    		up_write(&s->s_umount);
    		deactivate_super(s);
    		return ERR_PTR(error);
    	}
    	s->s_flags |= MS_ACTIVE;
    	return s;
    out_err:
    	s = (struct super_block *)p;
    out_free:
    
    Jesper Juhl's avatar
    Jesper Juhl committed
    	kfree(server->mnt_path);
    	kfree(server->hostname);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	kfree(server);
    	return s;
    }
    
    static void nfs4_kill_super(struct super_block *sb)
    {
    	struct nfs_server *server = NFS_SB(sb);
    
    	nfs_return_all_delegations(sb);
    	kill_anon_super(sb);
    
    	nfs4_renewd_prepare_shutdown(server);
    
    	if (server->client != NULL && !IS_ERR(server->client))
    		rpc_shutdown_client(server->client);
    
    	destroy_nfsv4_state(server);
    
    
    	nfs_free_iostats(server->io_stats);
    
    Jesper Juhl's avatar
    Jesper Juhl committed
    	kfree(server->hostname);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	kfree(server);
    }
    
    static struct file_system_type nfs4_fs_type = {
    	.owner		= THIS_MODULE,
    	.name		= "nfs4",
    	.get_sb		= nfs4_get_sb,
    	.kill_sb	= nfs4_kill_super,
    	.fs_flags	= FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
    };
    
    
    static const int nfs_set_port_min = 0;
    static const int nfs_set_port_max = 65535;
    static int param_set_port(const char *val, struct kernel_param *kp)
    {
    	char *endp;
    	int num = simple_strtol(val, &endp, 0);
    	if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
    		return -EINVAL;
    	*((int *)kp->arg) = num;
    	return 0;
    }
    
    module_param_call(callback_tcpport, param_set_port, param_get_int,
    		 &nfs_callback_set_tcpport, 0644);
    
    
    static int param_set_idmap_timeout(const char *val, struct kernel_param *kp)
    {
    	char *endp;
    	int num = simple_strtol(val, &endp, 0);
    	int jif = num * HZ;
    	if (endp == val || *endp || num < 0 || jif < num)
    		return -EINVAL;
    	*((int *)kp->arg) = jif;
    	return 0;
    }
    
    module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int,
    		 &nfs_idmap_cache_timeout, 0644);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #define nfs4_init_once(nfsi) \
    	do { \
    		INIT_LIST_HEAD(&(nfsi)->open_states); \
    		nfsi->delegation = NULL; \
    		nfsi->delegation_state = 0; \
    		init_rwsem(&nfsi->rwsem); \
    	} while(0)
    
    
    static inline int register_nfs4fs(void)
    {
    	int ret;
    
    	ret = nfs_register_sysctl();
    	if (ret != 0)
    		return ret;
    	ret = register_filesystem(&nfs4_fs_type);
    	if (ret != 0)
    		nfs_unregister_sysctl();
    	return ret;
    }
    
    static inline void unregister_nfs4fs(void)
    {
    	unregister_filesystem(&nfs4_fs_type);
    	nfs_unregister_sysctl();
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #else
    #define nfs4_init_once(nfsi) \
    	do { } while (0)
    #define register_nfs4fs() (0)
    #define unregister_nfs4fs()
    #endif
    
    extern int nfs_init_nfspagecache(void);
    extern void nfs_destroy_nfspagecache(void);
    extern int nfs_init_readpagecache(void);
    extern void nfs_destroy_readpagecache(void);
    extern int nfs_init_writepagecache(void);
    extern void nfs_destroy_writepagecache(void);
    #ifdef CONFIG_NFS_DIRECTIO
    extern int nfs_init_directcache(void);
    extern void nfs_destroy_directcache(void);
    #endif
    
    static kmem_cache_t * nfs_inode_cachep;
    
    static struct inode *nfs_alloc_inode(struct super_block *sb)
    {
    	struct nfs_inode *nfsi;
    	nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL);
    	if (!nfsi)
    		return NULL;
    
    	nfsi->flags = 0UL;
    	nfsi->cache_validity = 0UL;
    
    	nfsi->cache_change_attribute = jiffies;
    
    #ifdef CONFIG_NFS_V3_ACL
    	nfsi->acl_access = ERR_PTR(-EAGAIN);
    	nfsi->acl_default = ERR_PTR(-EAGAIN);
    #endif
    
    #ifdef CONFIG_NFS_V4
    	nfsi->nfs4_acl = NULL;
    #endif /* CONFIG_NFS_V4 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return &nfsi->vfs_inode;
    }
    
    static void nfs_destroy_inode(struct inode *inode)
    {
    	kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
    }
    
    static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
    {
    	struct nfs_inode *nfsi = (struct nfs_inode *) foo;
    
    	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
    	    SLAB_CTOR_CONSTRUCTOR) {
    		inode_init_once(&nfsi->vfs_inode);
    		spin_lock_init(&nfsi->req_lock);
    		INIT_LIST_HEAD(&nfsi->dirty);
    		INIT_LIST_HEAD(&nfsi->commit);
    		INIT_LIST_HEAD(&nfsi->open_files);
    		INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
    		atomic_set(&nfsi->data_updates, 0);
    		nfsi->ndirty = 0;
    		nfsi->ncommit = 0;
    		nfsi->npages = 0;
    		nfs4_init_once(nfsi);
    	}
    }
     
    
    static int nfs_init_inodecache(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
    					     sizeof(struct nfs_inode),
    
    					     0, (SLAB_RECLAIM_ACCOUNT|
    						SLAB_MEM_SPREAD),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					     init_once, NULL);
    	if (nfs_inode_cachep == NULL)
    		return -ENOMEM;
    
    	return 0;
    }
    
    
    static void nfs_destroy_inodecache(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	if (kmem_cache_destroy(nfs_inode_cachep))
    		printk(KERN_INFO "nfs_inode_cache: not all structures were freed\n");
    }
    
    /*
     * Initialize NFS
     */
    static int __init init_nfs_fs(void)
    {
    	int err;
    
    	err = nfs_init_nfspagecache();
    	if (err)
    		goto out4;
    
    	err = nfs_init_inodecache();
    	if (err)
    		goto out3;
    
    	err = nfs_init_readpagecache();
    	if (err)
    		goto out2;
    
    	err = nfs_init_writepagecache();
    	if (err)
    		goto out1;
    
    #ifdef CONFIG_NFS_DIRECTIO
    	err = nfs_init_directcache();
    	if (err)
    		goto out0;
    #endif
    
    #ifdef CONFIG_PROC_FS
    	rpc_proc_register(&nfs_rpcstat);
    #endif
            err = register_filesystem(&nfs_fs_type);
    	if (err)
    		goto out;
    	if ((err = register_nfs4fs()) != 0)
    		goto out;
    	return 0;
    out:
    #ifdef CONFIG_PROC_FS
    	rpc_proc_unregister("nfs");
    #endif
    #ifdef CONFIG_NFS_DIRECTIO
    	nfs_destroy_directcache();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #endif
    
    	nfs_destroy_writepagecache();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out1:
    	nfs_destroy_readpagecache();
    out2:
    	nfs_destroy_inodecache();
    out3:
    	nfs_destroy_nfspagecache();
    out4:
    	return err;
    }
    
    static void __exit exit_nfs_fs(void)
    {
    #ifdef CONFIG_NFS_DIRECTIO
    	nfs_destroy_directcache();
    #endif
    	nfs_destroy_writepagecache();
    	nfs_destroy_readpagecache();
    	nfs_destroy_inodecache();
    	nfs_destroy_nfspagecache();
    #ifdef CONFIG_PROC_FS
    	rpc_proc_unregister("nfs");
    #endif
    	unregister_filesystem(&nfs_fs_type);
    	unregister_nfs4fs();
    }
    
    /* Not quite true; I just maintain it */
    MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
    MODULE_LICENSE("GPL");
    
    module_init(init_nfs_fs)
    module_exit(exit_nfs_fs)