36
36
#include <linux/mount.h>
37
37
#include <linux/nfs_idmap.h>
38
38
#include <linux/vfs.h>
39
+ #include <linux/inet.h>
40
+ #include <linux/nfs_xdr.h>
39
41
40
42
#include <asm/system.h>
41
43
#include <asm/uaccess.h>
@@ -1714,6 +1716,10 @@ struct nfs_clone_mount {
1714
1716
const struct dentry * dentry ;
1715
1717
struct nfs_fh * fh ;
1716
1718
struct nfs_fattr * fattr ;
1719
+ char * hostname ;
1720
+ char * mnt_path ;
1721
+ struct sockaddr_in * addr ;
1722
+ rpc_authflavor_t authflavor ;
1717
1723
};
1718
1724
1719
1725
static struct super_block * nfs_clone_generic_sb (struct nfs_clone_mount * data ,
@@ -1724,17 +1730,19 @@ static struct super_block *nfs_clone_generic_sb(struct nfs_clone_mount *data,
1724
1730
struct nfs_server * parent = NFS_SB (data -> sb );
1725
1731
struct super_block * sb = ERR_PTR (- EINVAL );
1726
1732
void * err = ERR_PTR (- ENOMEM );
1733
+ char * hostname ;
1727
1734
int len ;
1728
1735
1729
1736
server = kmalloc (sizeof (struct nfs_server ), GFP_KERNEL );
1730
1737
if (server == NULL )
1731
1738
goto out_err ;
1732
1739
memcpy (server , parent , sizeof (* server ));
1733
- len = strlen (parent -> hostname ) + 1 ;
1740
+ hostname = (data -> hostname != NULL ) ? data -> hostname : parent -> hostname ;
1741
+ len = strlen (hostname ) + 1 ;
1734
1742
server -> hostname = kmalloc (len , GFP_KERNEL );
1735
1743
if (server -> hostname == NULL )
1736
1744
goto free_server ;
1737
- memcpy (server -> hostname , parent -> hostname , len );
1745
+ memcpy (server -> hostname , hostname , len );
1738
1746
if (rpciod_up () != 0 )
1739
1747
goto free_hostname ;
1740
1748
@@ -2458,7 +2466,8 @@ static inline void unregister_nfs4fs(void)
2458
2466
nfs_unregister_sysctl ();
2459
2467
}
2460
2468
#else
2461
- #define nfs4_clone_client (a ,b ) ERR_PTR(-EINVAL)
2469
+ #define nfs4_fill_sb (a ,b ) ERR_PTR(-EINVAL)
2470
+ #define nfs4_fill_super (a ,b ) ERR_PTR(-EINVAL)
2462
2471
#define nfs4_init_once (nfsi ) \
2463
2472
do { } while (0)
2464
2473
#define register_nfs4fs () (0)
@@ -2521,6 +2530,261 @@ struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
2521
2530
return mnt ;
2522
2531
}
2523
2532
2533
+ /* Check if fs_root is valid */
2534
+ static inline char * nfs4_pathname_string (struct nfs4_pathname * pathname , char * buffer , ssize_t buflen )
2535
+ {
2536
+ char * end = buffer + buflen ;
2537
+ int n ;
2538
+
2539
+ * -- end = '\0' ;
2540
+ buflen -- ;
2541
+
2542
+ n = pathname -> ncomponents ;
2543
+ while (-- n >= 0 ) {
2544
+ struct nfs4_string * component = & pathname -> components [n ];
2545
+ buflen -= component -> len + 1 ;
2546
+ if (buflen < 0 )
2547
+ goto Elong ;
2548
+ end -= component -> len ;
2549
+ memcpy (end , component -> data , component -> len );
2550
+ * -- end = '/' ;
2551
+ }
2552
+ return end ;
2553
+ Elong :
2554
+ return ERR_PTR (- ENAMETOOLONG );
2555
+ }
2556
+
2557
+ /* Check if the string represents a "valid" IPv4 address */
2558
+ static inline int valid_ipaddr4 (const char * buf )
2559
+ {
2560
+ int rc , count , in [4 ];
2561
+
2562
+ rc = sscanf (buf , "%d.%d.%d.%d" , & in [0 ], & in [1 ], & in [2 ], & in [3 ]);
2563
+ if (rc != 4 )
2564
+ return - EINVAL ;
2565
+ for (count = 0 ; count < 4 ; count ++ ) {
2566
+ if (in [count ] > 255 )
2567
+ return - EINVAL ;
2568
+ }
2569
+ return 0 ;
2570
+ }
2571
+
2572
+ static struct super_block * nfs4_referral_sb (struct nfs_server * server , struct nfs_clone_mount * data )
2573
+ {
2574
+ struct super_block * sb = ERR_PTR (- ENOMEM );
2575
+ int len ;
2576
+
2577
+ len = strlen (data -> mnt_path ) + 1 ;
2578
+ server -> mnt_path = kmalloc (len , GFP_KERNEL );
2579
+ if (server -> mnt_path == NULL )
2580
+ goto err ;
2581
+ memcpy (server -> mnt_path , data -> mnt_path , len );
2582
+ memcpy (& server -> addr , data -> addr , sizeof (struct sockaddr_in ));
2583
+
2584
+ sb = sget (& nfs4_fs_type , nfs4_compare_super , nfs_set_super , server );
2585
+ if (IS_ERR (sb ) || sb -> s_root )
2586
+ goto free_path ;
2587
+ return sb ;
2588
+ free_path :
2589
+ kfree (server -> mnt_path );
2590
+ err :
2591
+ server -> mnt_path = NULL ;
2592
+ return sb ;
2593
+ }
2594
+
2595
+ static struct nfs_server * nfs4_referral_server (struct super_block * sb , struct nfs_clone_mount * data )
2596
+ {
2597
+ struct nfs_server * server = NFS_SB (sb );
2598
+ struct rpc_timeout timeparms ;
2599
+ int proto , timeo , retrans ;
2600
+ void * err ;
2601
+
2602
+ proto = IPPROTO_TCP ;
2603
+ /* Since we are following a referral and there may be alternatives,
2604
+ set the timeouts and retries to low values */
2605
+ timeo = 2 ;
2606
+ retrans = 1 ;
2607
+ nfs_init_timeout_values (& timeparms , proto , timeo , retrans );
2608
+
2609
+ server -> client = nfs4_create_client (server , & timeparms , proto , data -> authflavor );
2610
+ if (IS_ERR ((err = server -> client )))
2611
+ goto out_err ;
2612
+
2613
+ sb -> s_time_gran = 1 ;
2614
+ sb -> s_op = & nfs4_sops ;
2615
+ err = ERR_PTR (nfs_sb_init (sb , data -> authflavor ));
2616
+ if (!IS_ERR (err ))
2617
+ return server ;
2618
+ out_err :
2619
+ return (struct nfs_server * )err ;
2620
+ }
2621
+
2622
+ static struct super_block * nfs_referral_nfs4_sb (struct file_system_type * fs_type ,
2623
+ int flags , const char * dev_name , void * raw_data )
2624
+ {
2625
+ struct nfs_clone_mount * data = raw_data ;
2626
+ return nfs_clone_generic_sb (data , nfs4_referral_sb , nfs4_referral_server );
2627
+ }
2628
+
2629
+ static struct file_system_type nfs_referral_nfs4_fs_type = {
2630
+ .owner = THIS_MODULE ,
2631
+ .name = "nfs4" ,
2632
+ .get_sb = nfs_referral_nfs4_sb ,
2633
+ .kill_sb = nfs4_kill_super ,
2634
+ .fs_flags = FS_ODD_RENAME |FS_REVAL_DOT |FS_BINARY_MOUNTDATA ,
2635
+ };
2636
+
2637
+ /**
2638
+ * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
2639
+ * @mnt_parent - mountpoint of parent directory
2640
+ * @dentry - parent directory
2641
+ * @fspath - fs path returned in fs_locations
2642
+ * @mntpath - mount path to new server
2643
+ * @hostname - hostname of new server
2644
+ * @addr - host addr of new server
2645
+ *
2646
+ */
2647
+ struct vfsmount * nfs_follow_referral (const struct vfsmount * mnt_parent ,
2648
+ const struct dentry * dentry , struct nfs4_fs_locations * locations )
2649
+ {
2650
+ struct vfsmount * mnt = ERR_PTR (- ENOENT );
2651
+ struct nfs_clone_mount mountdata = {
2652
+ .sb = mnt_parent -> mnt_sb ,
2653
+ .dentry = dentry ,
2654
+ .authflavor = NFS_SB (mnt_parent -> mnt_sb )-> client -> cl_auth -> au_flavor ,
2655
+ };
2656
+ char * page , * page2 ;
2657
+ char * path , * fs_path ;
2658
+ char * devname ;
2659
+ int loc , s ;
2660
+
2661
+ if (locations == NULL || locations -> nlocations <= 0 )
2662
+ goto out ;
2663
+
2664
+ dprintk ("%s: referral at %s/%s\n" , __FUNCTION__ ,
2665
+ dentry -> d_parent -> d_name .name , dentry -> d_name .name );
2666
+
2667
+ /* Ensure fs path is a prefix of current dentry path */
2668
+ page = (char * ) __get_free_page (GFP_USER );
2669
+ if (page == NULL )
2670
+ goto out ;
2671
+ page2 = (char * ) __get_free_page (GFP_USER );
2672
+ if (page2 == NULL )
2673
+ goto out ;
2674
+
2675
+ path = nfs4_path (dentry , page , PAGE_SIZE );
2676
+ if (IS_ERR (path ))
2677
+ goto out_free ;
2678
+
2679
+ fs_path = nfs4_pathname_string (& locations -> fs_path , page2 , PAGE_SIZE );
2680
+ if (IS_ERR (fs_path ))
2681
+ goto out_free ;
2682
+
2683
+ if (strncmp (path , fs_path , strlen (fs_path )) != 0 ) {
2684
+ dprintk ("%s: path %s does not begin with fsroot %s\n" , __FUNCTION__ , path , fs_path );
2685
+ goto out_free ;
2686
+ }
2687
+
2688
+ devname = nfs_devname (mnt_parent , dentry , page , PAGE_SIZE );
2689
+ if (IS_ERR (devname )) {
2690
+ mnt = (struct vfsmount * )devname ;
2691
+ goto out_free ;
2692
+ }
2693
+
2694
+ loc = 0 ;
2695
+ while (loc < locations -> nlocations && IS_ERR (mnt )) {
2696
+ struct nfs4_fs_location * location = & locations -> locations [loc ];
2697
+ char * mnt_path ;
2698
+
2699
+ if (location == NULL || location -> nservers <= 0 ||
2700
+ location -> rootpath .ncomponents == 0 ) {
2701
+ loc ++ ;
2702
+ continue ;
2703
+ }
2704
+
2705
+ mnt_path = nfs4_pathname_string (& location -> rootpath , page2 , PAGE_SIZE );
2706
+ if (IS_ERR (mnt_path )) {
2707
+ loc ++ ;
2708
+ continue ;
2709
+ }
2710
+ mountdata .mnt_path = mnt_path ;
2711
+
2712
+ s = 0 ;
2713
+ while (s < location -> nservers ) {
2714
+ struct sockaddr_in addr = {};
2715
+
2716
+ if (location -> servers [s ].len <= 0 ||
2717
+ valid_ipaddr4 (location -> servers [s ].data ) < 0 ) {
2718
+ s ++ ;
2719
+ continue ;
2720
+ }
2721
+
2722
+ mountdata .hostname = location -> servers [s ].data ;
2723
+ addr .sin_addr .s_addr = in_aton (mountdata .hostname );
2724
+ addr .sin_family = AF_INET ;
2725
+ addr .sin_port = htons (NFS_PORT );
2726
+ mountdata .addr = & addr ;
2727
+
2728
+ mnt = vfs_kern_mount (& nfs_referral_nfs4_fs_type , 0 , devname , & mountdata );
2729
+ if (!IS_ERR (mnt )) {
2730
+ break ;
2731
+ }
2732
+ s ++ ;
2733
+ }
2734
+ loc ++ ;
2735
+ }
2736
+
2737
+ out_free :
2738
+ free_page ((unsigned long )page );
2739
+ free_page ((unsigned long )page2 );
2740
+ out :
2741
+ dprintk ("%s: done\n" , __FUNCTION__ );
2742
+ return mnt ;
2743
+ }
2744
+
2745
+ /*
2746
+ * nfs_do_refmount - handle crossing a referral on server
2747
+ * @dentry - dentry of referral
2748
+ * @nd - nameidata info
2749
+ *
2750
+ */
2751
+ struct vfsmount * nfs_do_refmount (const struct vfsmount * mnt_parent , struct dentry * dentry )
2752
+ {
2753
+ struct vfsmount * mnt = ERR_PTR (- ENOENT );
2754
+ struct dentry * parent ;
2755
+ struct nfs4_fs_locations * fs_locations = NULL ;
2756
+ struct page * page ;
2757
+ int err ;
2758
+
2759
+ /* BUG_ON(IS_ROOT(dentry)); */
2760
+ dprintk ("%s: enter\n" , __FUNCTION__ );
2761
+
2762
+ page = alloc_page (GFP_KERNEL );
2763
+ if (page == NULL )
2764
+ goto out ;
2765
+
2766
+ fs_locations = kmalloc (sizeof (struct nfs4_fs_locations ), GFP_KERNEL );
2767
+ if (fs_locations == NULL )
2768
+ goto out_free ;
2769
+
2770
+ /* Get locations */
2771
+ parent = dget_parent (dentry );
2772
+ dprintk ("%s: getting locations for %s/%s\n" , __FUNCTION__ , parent -> d_name .name , dentry -> d_name .name );
2773
+ err = nfs4_proc_fs_locations (parent -> d_inode , dentry , fs_locations , page );
2774
+ dput (parent );
2775
+ if (err != 0 || fs_locations -> nlocations <= 0 ||
2776
+ fs_locations -> fs_path .ncomponents <= 0 )
2777
+ goto out_free ;
2778
+
2779
+ mnt = nfs_follow_referral (mnt_parent , dentry , fs_locations );
2780
+ out_free :
2781
+ __free_page (page );
2782
+ kfree (fs_locations );
2783
+ out :
2784
+ dprintk ("%s: done\n" , __FUNCTION__ );
2785
+ return mnt ;
2786
+ }
2787
+
2524
2788
extern int nfs_init_nfspagecache (void );
2525
2789
extern void nfs_destroy_nfspagecache (void );
2526
2790
extern int nfs_init_readpagecache (void );
0 commit comments