2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-07-17 00:39:13 +04:00
/*
* Copyright ( c ) 2012 Bryan Schumaker < bjschuma @ netapp . com >
*/
# include <linux/init.h>
2012-07-17 00:39:20 +04:00
# include <linux/module.h>
2019-12-10 15:31:13 +03:00
# include <linux/mount.h>
2012-07-17 00:39:20 +04:00
# include <linux/nfs4_mount.h>
2012-07-17 00:39:14 +04:00
# include <linux/nfs_fs.h>
2020-10-19 06:42:49 +03:00
# include <linux/nfs_ssc.h>
2012-07-31 00:05:21 +04:00
# include "delegation.h"
2012-07-17 00:39:20 +04:00
# include "internal.h"
2012-07-17 00:39:14 +04:00
# include "nfs4_fs.h"
2015-04-15 20:00:05 +03:00
# include "nfs4idmap.h"
2013-06-01 19:50:58 +04:00
# include "dns_resolve.h"
2012-07-31 00:05:21 +04:00
# include "pnfs.h"
2012-07-31 00:05:16 +04:00
# include "nfs.h"
2012-07-17 00:39:13 +04:00
2012-07-17 00:39:20 +04:00
# define NFSDBG_FACILITY NFSDBG_VFS
2012-07-31 00:05:21 +04:00
static int nfs4_write_inode ( struct inode * inode , struct writeback_control * wbc ) ;
static void nfs4_evict_inode ( struct inode * inode ) ;
2012-07-17 00:39:20 +04:00
static const struct super_operations nfs4_sops = {
. alloc_inode = nfs_alloc_inode ,
2019-04-16 03:19:40 +03:00
. free_inode = nfs_free_inode ,
2012-07-17 00:39:20 +04:00
. write_inode = nfs4_write_inode ,
2012-12-14 23:36:36 +04:00
. drop_inode = nfs_drop_inode ,
2012-07-17 00:39:20 +04:00
. statfs = nfs_statfs ,
. evict_inode = nfs4_evict_inode ,
. umount_begin = nfs_umount_begin ,
. show_options = nfs_show_options ,
. show_devname = nfs_show_devname ,
. show_path = nfs_show_path ,
. show_stats = nfs_show_stats ,
} ;
2012-07-31 00:05:16 +04:00
struct nfs_subversion nfs_v4 = {
2019-12-10 15:31:14 +03:00
. owner = THIS_MODULE ,
. nfs_fs = & nfs4_fs_type ,
. rpc_vers = & nfs_version4 ,
. rpc_ops = & nfs_v4_clientops ,
. sops = & nfs4_sops ,
. xattr = nfs4_xattr_handlers ,
2012-07-31 00:05:16 +04:00
} ;
2012-07-31 00:05:21 +04:00
static int nfs4_write_inode ( struct inode * inode , struct writeback_control * wbc )
{
int ret = nfs_write_inode ( inode , wbc ) ;
2014-01-13 22:34:36 +04:00
if ( ret = = 0 )
ret = pnfs_layoutcommit_inode ( inode ,
wbc - > sync_mode = = WB_SYNC_ALL ) ;
2012-07-31 00:05:21 +04:00
return ret ;
}
/*
* Clean out any remaining NFSv4 state that might be left over due
* to open ( ) calls that passed nfs_atomic_lookup , but failed to call
* nfs_open ( ) .
*/
static void nfs4_evict_inode ( struct inode * inode )
{
2014-04-04 01:47:49 +04:00
truncate_inode_pages_final ( & inode - > i_data ) ;
2012-07-31 00:05:21 +04:00
clear_inode ( inode ) ;
2019-10-21 21:04:00 +03:00
/* If we are holding a delegation, return and free it */
nfs_inode_evict_delegation ( inode ) ;
2015-03-25 20:26:18 +03:00
/* Note that above delegreturn would trigger pnfs return-on-close */
pnfs_return_layout ( inode ) ;
2020-11-25 20:06:14 +03:00
pnfs_destroy_layout_final ( NFS_I ( inode ) ) ;
2012-07-31 00:05:21 +04:00
/* First call standard NFS clear_inode() code */
nfs_clear_inode ( inode ) ;
2020-06-24 01:39:04 +03:00
nfs4_xattr_cache_zap ( inode ) ;
2012-07-31 00:05:21 +04:00
}
2012-07-17 00:39:20 +04:00
struct nfs_referral_count {
struct list_head list ;
const struct task_struct * task ;
unsigned int referral_count ;
} ;
static LIST_HEAD ( nfs_referral_count_list ) ;
static DEFINE_SPINLOCK ( nfs_referral_count_list_lock ) ;
static struct nfs_referral_count * nfs_find_referral_count ( void )
{
struct nfs_referral_count * p ;
list_for_each_entry ( p , & nfs_referral_count_list , list ) {
if ( p - > task = = current )
return p ;
}
return NULL ;
}
# define NFS_MAX_NESTED_REFERRALS 2
static int nfs_referral_loop_protect ( void )
{
struct nfs_referral_count * p , * new ;
int ret = - ENOMEM ;
new = kmalloc ( sizeof ( * new ) , GFP_KERNEL ) ;
if ( ! new )
goto out ;
new - > task = current ;
new - > referral_count = 1 ;
ret = 0 ;
spin_lock ( & nfs_referral_count_list_lock ) ;
p = nfs_find_referral_count ( ) ;
if ( p ! = NULL ) {
if ( p - > referral_count > = NFS_MAX_NESTED_REFERRALS )
ret = - ELOOP ;
else
p - > referral_count + + ;
} else {
list_add ( & new - > list , & nfs_referral_count_list ) ;
new = NULL ;
}
spin_unlock ( & nfs_referral_count_list_lock ) ;
kfree ( new ) ;
out :
return ret ;
}
static void nfs_referral_loop_unprotect ( void )
{
struct nfs_referral_count * p ;
spin_lock ( & nfs_referral_count_list_lock ) ;
p = nfs_find_referral_count ( ) ;
p - > referral_count - - ;
if ( p - > referral_count = = 0 )
list_del ( & p - > list ) ;
else
p = NULL ;
spin_unlock ( & nfs_referral_count_list_lock ) ;
kfree ( p ) ;
}
2019-12-10 15:31:13 +03:00
static int do_nfs4_mount ( struct nfs_server * server ,
struct fs_context * fc ,
const char * hostname ,
const char * export_path )
2012-07-17 00:39:20 +04:00
{
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * root_ctx ;
2024-01-31 12:49:17 +03:00
struct nfs_fs_context * ctx ;
2019-12-10 15:31:13 +03:00
struct fs_context * root_fc ;
2019-12-10 15:30:54 +03:00
struct vfsmount * root_mnt ;
2012-07-17 00:39:20 +04:00
struct dentry * dentry ;
2019-12-10 15:30:54 +03:00
size_t len ;
2019-12-10 15:31:13 +03:00
int ret ;
struct fs_parameter param = {
. key = " source " ,
. type = fs_value_is_string ,
. dirfd = - 1 ,
} ;
2019-12-10 15:30:54 +03:00
2024-01-31 12:49:17 +03:00
struct fs_parameter param_fsc = {
. key = " fsc " ,
. type = fs_value_is_string ,
. dirfd = - 1 ,
} ;
2019-12-10 15:30:54 +03:00
if ( IS_ERR ( server ) )
2019-12-10 15:31:13 +03:00
return PTR_ERR ( server ) ;
2019-12-10 15:30:54 +03:00
2019-12-10 15:31:13 +03:00
root_fc = vfs_dup_fs_context ( fc ) ;
if ( IS_ERR ( root_fc ) ) {
2019-12-10 15:30:54 +03:00
nfs_free_server ( server ) ;
2019-12-10 15:31:13 +03:00
return PTR_ERR ( root_fc ) ;
}
kfree ( root_fc - > source ) ;
root_fc - > source = NULL ;
2024-01-31 12:49:17 +03:00
ctx = nfs_fc2context ( fc ) ;
2019-12-10 15:31:13 +03:00
root_ctx = nfs_fc2context ( root_fc ) ;
root_ctx - > internal = true ;
2019-12-10 15:31:14 +03:00
root_ctx - > server = server ;
2024-01-31 12:49:17 +03:00
if ( ctx - > fscache_uniq ) {
len = strlen ( ctx - > fscache_uniq ) ;
param_fsc . size = len ;
param_fsc . string = kmemdup_nul ( ctx - > fscache_uniq , len , GFP_KERNEL ) ;
if ( param_fsc . string = = NULL ) {
put_fs_context ( root_fc ) ;
return - ENOMEM ;
}
ret = vfs_parse_fs_param ( root_fc , & param_fsc ) ;
kfree ( param_fsc . string ) ;
if ( ret < 0 ) {
put_fs_context ( root_fc ) ;
return ret ;
}
}
2019-12-10 15:31:13 +03:00
/* We leave export_path unset as it's not used to find the root. */
len = strlen ( hostname ) + 5 ;
param . string = kmalloc ( len , GFP_KERNEL ) ;
if ( param . string = = NULL ) {
put_fs_context ( root_fc ) ;
return - ENOMEM ;
2019-12-10 15:30:54 +03:00
}
/* Does hostname needs to be enclosed in brackets? */
if ( strchr ( hostname , ' : ' ) )
2019-12-10 15:31:13 +03:00
param . size = snprintf ( param . string , len , " [%s]:/ " , hostname ) ;
2019-12-10 15:30:54 +03:00
else
2019-12-10 15:31:13 +03:00
param . size = snprintf ( param . string , len , " %s:/ " , hostname ) ;
ret = vfs_parse_fs_param ( root_fc , & param ) ;
kfree ( param . string ) ;
if ( ret < 0 ) {
put_fs_context ( root_fc ) ;
return ret ;
}
root_mnt = fc_mount ( root_fc ) ;
put_fs_context ( root_fc ) ;
2012-07-17 00:39:20 +04:00
if ( IS_ERR ( root_mnt ) )
2019-12-10 15:31:13 +03:00
return PTR_ERR ( root_mnt ) ;
2012-07-17 00:39:20 +04:00
2019-12-10 15:31:13 +03:00
ret = nfs_referral_loop_protect ( ) ;
if ( ret ) {
2012-07-17 00:39:20 +04:00
mntput ( root_mnt ) ;
2019-12-10 15:31:13 +03:00
return ret ;
2012-07-17 00:39:20 +04:00
}
dentry = mount_subtree ( root_mnt , export_path ) ;
nfs_referral_loop_unprotect ( ) ;
2019-12-10 15:31:13 +03:00
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
fc - > root = dentry ;
return 0 ;
2012-07-17 00:39:20 +04:00
}
2019-12-10 15:31:13 +03:00
int nfs4_try_get_tree ( struct fs_context * fc )
2012-07-17 00:39:20 +04:00
{
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
int err ;
2012-07-17 00:39:20 +04:00
2019-12-10 15:31:13 +03:00
dfprintk ( MOUNT , " --> nfs4_try_get_tree() \n " ) ;
2012-07-17 00:39:20 +04:00
2019-12-10 15:31:13 +03:00
/* We create a mount for the server's root, walk to the requested
* location and then create another mount for that .
*/
2019-12-10 15:31:14 +03:00
err = do_nfs4_mount ( nfs4_create_server ( fc ) ,
2019-12-10 15:31:13 +03:00
fc , ctx - > nfs_server . hostname ,
ctx - > nfs_server . export_path ) ;
if ( err ) {
2021-01-05 16:54:32 +03:00
nfs_ferrorf ( fc , MOUNT , " NFS4: Couldn't follow remote path " ) ;
2019-12-10 15:31:13 +03:00
dfprintk ( MOUNT , " <-- nfs4_try_get_tree() = %d [error] \n " , err ) ;
} else {
dfprintk ( MOUNT , " <-- nfs4_try_get_tree() = 0 \n " ) ;
}
return err ;
2012-07-17 00:39:20 +04:00
}
/*
* Create an NFS4 server record on referral traversal
*/
2019-12-10 15:31:13 +03:00
int nfs4_get_referral_tree ( struct fs_context * fc )
2012-07-17 00:39:20 +04:00
{
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
int err ;
2012-07-17 00:39:20 +04:00
dprintk ( " --> nfs4_referral_mount() \n " ) ;
2019-12-10 15:31:13 +03:00
/* create a new volume representation */
2019-12-10 15:31:14 +03:00
err = do_nfs4_mount ( nfs4_create_referral_server ( fc ) ,
2019-12-10 15:31:13 +03:00
fc , ctx - > nfs_server . hostname ,
ctx - > nfs_server . export_path ) ;
if ( err ) {
2021-01-05 16:54:32 +03:00
nfs_ferrorf ( fc , MOUNT , " NFS4: Couldn't follow remote path " ) ;
2019-12-10 15:31:13 +03:00
dfprintk ( MOUNT , " <-- nfs4_get_referral_tree() = %d [error] \n " , err ) ;
} else {
dfprintk ( MOUNT , " <-- nfs4_get_referral_tree() = 0 \n " ) ;
}
return err ;
2012-07-17 00:39:20 +04:00
}
2012-07-31 00:05:25 +04:00
static int __init init_nfs_v4 ( void )
2012-07-17 00:39:13 +04:00
{
int err ;
2013-06-01 19:50:58 +04:00
err = nfs_dns_resolver_init ( ) ;
2012-07-17 00:39:13 +04:00
if ( err )
goto out ;
2013-06-01 19:50:58 +04:00
err = nfs_idmap_init ( ) ;
2012-07-17 00:39:14 +04:00
if ( err )
goto out1 ;
2020-06-24 01:39:04 +03:00
# ifdef CONFIG_NFS_V4_2
err = nfs4_xattr_cache_init ( ) ;
if ( err )
goto out2 ;
# endif
2013-06-01 19:50:58 +04:00
err = nfs4_register_sysctl ( ) ;
if ( err )
goto out2 ;
2020-10-19 06:42:49 +03:00
# ifdef CONFIG_NFS_V4_2
nfs42_ssc_register_ops ( ) ;
# endif
2012-07-31 00:05:16 +04:00
register_nfs_version ( & nfs_v4 ) ;
2012-07-17 00:39:13 +04:00
return 0 ;
2013-06-01 19:50:58 +04:00
out2 :
2012-07-17 00:39:14 +04:00
nfs_idmap_quit ( ) ;
2013-06-01 19:50:58 +04:00
out1 :
nfs_dns_resolver_destroy ( ) ;
2012-07-17 00:39:13 +04:00
out :
return err ;
}
2012-07-31 00:05:25 +04:00
static void __exit exit_nfs_v4 ( void )
2012-07-17 00:39:13 +04:00
{
2014-05-30 14:15:59 +04:00
/* Not called in the _init(), conditionally loaded */
nfs4_pnfs_v3_ds_connect_unload ( ) ;
2012-07-31 00:05:16 +04:00
unregister_nfs_version ( & nfs_v4 ) ;
2020-06-24 01:39:04 +03:00
# ifdef CONFIG_NFS_V4_2
nfs4_xattr_cache_exit ( ) ;
2020-10-19 06:42:49 +03:00
nfs42_ssc_unregister_ops ( ) ;
2020-06-24 01:39:04 +03:00
# endif
2012-07-17 00:39:14 +04:00
nfs4_unregister_sysctl ( ) ;
2012-07-17 00:39:13 +04:00
nfs_idmap_quit ( ) ;
2013-06-01 19:50:58 +04:00
nfs_dns_resolver_destroy ( ) ;
2012-07-17 00:39:13 +04:00
}
2012-07-31 00:05:25 +04:00
MODULE_LICENSE ( " GPL " ) ;
module_init ( init_nfs_v4 ) ;
module_exit ( exit_nfs_v4 ) ;