@@ -3891,10 +3891,11 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
3891
3891
*/
3892
3892
static char *
3893
3893
build_unc_path_to_root (const struct smb_vol * vol ,
3894
- const struct cifs_sb_info * cifs_sb )
3894
+ const struct cifs_sb_info * cifs_sb , bool useppath )
3895
3895
{
3896
3896
char * full_path , * pos ;
3897
- unsigned int pplen = vol -> prepath ? strlen (vol -> prepath ) + 1 : 0 ;
3897
+ unsigned int pplen = useppath && vol -> prepath ?
3898
+ strlen (vol -> prepath ) + 1 : 0 ;
3898
3899
unsigned int unc_len = strnlen (vol -> UNC , MAX_TREE_SIZE + 1 );
3899
3900
3900
3901
full_path = kmalloc (unc_len + pplen + 1 , GFP_KERNEL );
@@ -3939,7 +3940,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
3939
3940
if (cifs_sb -> mnt_cifs_flags & CIFS_MOUNT_NO_DFS )
3940
3941
return - EREMOTE ;
3941
3942
3942
- full_path = build_unc_path_to_root (volume_info , cifs_sb );
3943
+ full_path = build_unc_path_to_root (volume_info , cifs_sb , true );
3943
3944
if (IS_ERR (full_path ))
3944
3945
return PTR_ERR (full_path );
3945
3946
@@ -3971,6 +3972,143 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
3971
3972
kfree (full_path );
3972
3973
return rc ;
3973
3974
}
3975
+
3976
+ static inline int get_next_dfs_tgt (const char * path ,
3977
+ struct dfs_cache_tgt_list * tgt_list ,
3978
+ struct dfs_cache_tgt_iterator * * tgt_it )
3979
+ {
3980
+ if (!* tgt_it )
3981
+ * tgt_it = dfs_cache_get_tgt_iterator (tgt_list );
3982
+ else
3983
+ * tgt_it = dfs_cache_get_next_tgt (tgt_list , * tgt_it );
3984
+ return !* tgt_it ? - EHOSTDOWN : 0 ;
3985
+ }
3986
+
3987
+ static int update_vol_info (const struct dfs_cache_tgt_iterator * tgt_it ,
3988
+ struct smb_vol * fake_vol , struct smb_vol * vol )
3989
+ {
3990
+ const char * tgt = dfs_cache_get_tgt_name (tgt_it );
3991
+ int len = strlen (tgt ) + 2 ;
3992
+ char * new_unc ;
3993
+
3994
+ new_unc = kmalloc (len , GFP_KERNEL );
3995
+ if (!new_unc )
3996
+ return - ENOMEM ;
3997
+ snprintf (new_unc , len , "\\%s" , tgt );
3998
+
3999
+ kfree (vol -> UNC );
4000
+ vol -> UNC = new_unc ;
4001
+
4002
+ if (fake_vol -> prepath ) {
4003
+ kfree (vol -> prepath );
4004
+ vol -> prepath = fake_vol -> prepath ;
4005
+ fake_vol -> prepath = NULL ;
4006
+ }
4007
+ memcpy (& vol -> dstaddr , & fake_vol -> dstaddr , sizeof (vol -> dstaddr ));
4008
+
4009
+ return 0 ;
4010
+ }
4011
+
4012
+ static int setup_dfs_tgt_conn (const char * path ,
4013
+ const struct dfs_cache_tgt_iterator * tgt_it ,
4014
+ struct cifs_sb_info * cifs_sb ,
4015
+ struct smb_vol * vol ,
4016
+ unsigned int * xid ,
4017
+ struct TCP_Server_Info * * server ,
4018
+ struct cifs_ses * * ses ,
4019
+ struct cifs_tcon * * tcon )
4020
+ {
4021
+ int rc ;
4022
+ struct dfs_info3_param ref = {0 };
4023
+ char * mdata = NULL , * fake_devname = NULL ;
4024
+ struct smb_vol fake_vol = {0 };
4025
+
4026
+ cifs_dbg (FYI , "%s: dfs path: %s\n" , __func__ , path );
4027
+
4028
+ rc = dfs_cache_get_tgt_referral (path , tgt_it , & ref );
4029
+ if (rc )
4030
+ return rc ;
4031
+
4032
+ mdata = cifs_compose_mount_options (cifs_sb -> mountdata , path , & ref ,
4033
+ & fake_devname );
4034
+ free_dfs_info_param (& ref );
4035
+
4036
+ if (IS_ERR (mdata )) {
4037
+ rc = PTR_ERR (mdata );
4038
+ mdata = NULL ;
4039
+ } else {
4040
+ cifs_dbg (FYI , "%s: fake_devname: %s\n" , __func__ , fake_devname );
4041
+ rc = cifs_setup_volume_info (& fake_vol , mdata , fake_devname ,
4042
+ false);
4043
+ }
4044
+ kfree (mdata );
4045
+ kfree (fake_devname );
4046
+
4047
+ if (!rc ) {
4048
+ /*
4049
+ * We use a 'fake_vol' here because we need pass it down to the
4050
+ * mount_{get,put} functions to test connection against new DFS
4051
+ * targets.
4052
+ */
4053
+ mount_put_conns (cifs_sb , * xid , * server , * ses , * tcon );
4054
+ rc = mount_get_conns (& fake_vol , cifs_sb , xid , server , ses ,
4055
+ tcon );
4056
+ if (!rc ) {
4057
+ /*
4058
+ * We were able to connect to new target server.
4059
+ * Update current volume info with new target server.
4060
+ */
4061
+ rc = update_vol_info (tgt_it , & fake_vol , vol );
4062
+ }
4063
+ }
4064
+ cifs_cleanup_volume_info_contents (& fake_vol );
4065
+ return rc ;
4066
+ }
4067
+
4068
+ static int mount_do_dfs_failover (const char * path ,
4069
+ struct cifs_sb_info * cifs_sb ,
4070
+ struct smb_vol * vol ,
4071
+ struct cifs_ses * root_ses ,
4072
+ unsigned int * xid ,
4073
+ struct TCP_Server_Info * * server ,
4074
+ struct cifs_ses * * ses ,
4075
+ struct cifs_tcon * * tcon )
4076
+ {
4077
+ int rc ;
4078
+ struct dfs_cache_tgt_list tgt_list ;
4079
+ struct dfs_cache_tgt_iterator * tgt_it = NULL ;
4080
+
4081
+ if (cifs_sb -> mnt_cifs_flags & CIFS_MOUNT_NO_DFS )
4082
+ return - EOPNOTSUPP ;
4083
+
4084
+ rc = dfs_cache_noreq_find (path , NULL , & tgt_list );
4085
+ if (rc )
4086
+ return rc ;
4087
+
4088
+ for (;;) {
4089
+ /* Get next DFS target server - if any */
4090
+ rc = get_next_dfs_tgt (path , & tgt_list , & tgt_it );
4091
+ if (rc )
4092
+ break ;
4093
+ /* Connect to next DFS target */
4094
+ rc = setup_dfs_tgt_conn (path , tgt_it , cifs_sb , vol , xid , server ,
4095
+ ses , tcon );
4096
+ if (!rc || rc == - EACCES || rc == - EOPNOTSUPP )
4097
+ break ;
4098
+ }
4099
+ if (!rc ) {
4100
+ /*
4101
+ * Update DFS target hint in DFS referral cache with the target
4102
+ * server we successfully reconnected to.
4103
+ */
4104
+ rc = dfs_cache_update_tgthint (* xid , root_ses ? root_ses : * ses ,
4105
+ cifs_sb -> local_nls ,
4106
+ cifs_remap (cifs_sb ), path ,
4107
+ tgt_it );
4108
+ }
4109
+ dfs_cache_free_tgts (& tgt_list );
4110
+ return rc ;
4111
+ }
3974
4112
#endif
3975
4113
3976
4114
static int
@@ -4123,22 +4261,47 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
4123
4261
int rc = 0 ;
4124
4262
unsigned int xid ;
4125
4263
struct cifs_ses * ses ;
4264
+ struct cifs_tcon * root_tcon = NULL ;
4126
4265
struct cifs_tcon * tcon = NULL ;
4127
4266
struct TCP_Server_Info * server ;
4267
+ char * root_path = NULL , * full_path = NULL ;
4128
4268
char * old_mountdata ;
4129
4269
int count ;
4130
4270
4131
4271
rc = mount_get_conns (vol , cifs_sb , & xid , & server , & ses , & tcon );
4132
4272
if (!rc && tcon ) {
4133
- rc = is_path_remote (cifs_sb , vol , xid , server , tcon );
4134
- if (!rc )
4135
- goto out ;
4136
- if (rc != - EREMOTE )
4137
- goto error ;
4273
+ /* If not a standalone DFS root, then check if path is remote */
4274
+ rc = dfs_cache_find (xid , ses , cifs_sb -> local_nls ,
4275
+ cifs_remap (cifs_sb ), vol -> UNC + 1 , NULL ,
4276
+ NULL );
4277
+ if (rc ) {
4278
+ rc = is_path_remote (cifs_sb , vol , xid , server , tcon );
4279
+ if (!rc )
4280
+ goto out ;
4281
+ if (rc != - EREMOTE )
4282
+ goto error ;
4283
+ }
4138
4284
}
4139
- if ((rc == - EACCES ) || (rc == - EOPNOTSUPP ) || (ses == NULL ) || (server == NULL ))
4285
+ /*
4286
+ * If first DFS target server went offline and we failed to connect it,
4287
+ * server and ses pointers are NULL at this point, though we still have
4288
+ * chance to get a cached DFS referral in expand_dfs_referral() and
4289
+ * retry next target available in it.
4290
+ *
4291
+ * If a NULL ses ptr is passed to dfs_cache_find(), a lookup will be
4292
+ * performed against DFS path and *no* requests will be sent to server
4293
+ * for any new DFS referrals. Hence it's safe to skip checking whether
4294
+ * server or ses ptr is NULL.
4295
+ */
4296
+ if (rc == - EACCES || rc == - EOPNOTSUPP )
4140
4297
goto error ;
4141
4298
4299
+ root_path = build_unc_path_to_root (vol , cifs_sb , false);
4300
+ if (IS_ERR (root_path )) {
4301
+ rc = PTR_ERR (root_path );
4302
+ root_path = NULL ;
4303
+ goto error ;
4304
+ }
4142
4305
4143
4306
/*
4144
4307
* Perform an unconditional check for whether there are DFS
@@ -4163,8 +4326,36 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
4163
4326
if (rc ) {
4164
4327
if (rc == - EACCES || rc == - EOPNOTSUPP )
4165
4328
goto error ;
4329
+ /* Perform DFS failover to any other DFS targets */
4330
+ rc = mount_do_dfs_failover (root_path + 1 , cifs_sb , vol , NULL ,
4331
+ & xid , & server , & ses , & tcon );
4332
+ if (rc )
4333
+ goto error ;
4166
4334
}
4167
4335
4336
+ kfree (root_path );
4337
+ root_path = build_unc_path_to_root (vol , cifs_sb , false);
4338
+ if (IS_ERR (root_path )) {
4339
+ rc = PTR_ERR (root_path );
4340
+ root_path = NULL ;
4341
+ goto error ;
4342
+ }
4343
+ /* Cache out resolved root server */
4344
+ (void )dfs_cache_find (xid , ses , cifs_sb -> local_nls , cifs_remap (cifs_sb ),
4345
+ root_path + 1 , NULL , NULL );
4346
+ /*
4347
+ * Save root tcon for additional DFS requests to update or create a new
4348
+ * DFS cache entry, or even perform DFS failover.
4349
+ */
4350
+ spin_lock (& cifs_tcp_ses_lock );
4351
+ tcon -> tc_count ++ ;
4352
+ tcon -> dfs_path = root_path ;
4353
+ root_path = NULL ;
4354
+ tcon -> remap = cifs_remap (cifs_sb );
4355
+ spin_unlock (& cifs_tcp_ses_lock );
4356
+
4357
+ root_tcon = tcon ;
4358
+
4168
4359
for (count = 1 ; ;) {
4169
4360
if (!rc && tcon ) {
4170
4361
rc = is_path_remote (cifs_sb , vol , xid , server , tcon );
@@ -4182,8 +4373,16 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
4182
4373
break ;
4183
4374
}
4184
4375
4376
+ kfree (full_path );
4377
+ full_path = build_unc_path_to_root (vol , cifs_sb , true);
4378
+ if (IS_ERR (full_path )) {
4379
+ rc = PTR_ERR (full_path );
4380
+ full_path = NULL ;
4381
+ break ;
4382
+ }
4383
+
4185
4384
old_mountdata = cifs_sb -> mountdata ;
4186
- rc = expand_dfs_referral (xid , tcon -> ses , vol , cifs_sb ,
4385
+ rc = expand_dfs_referral (xid , root_tcon -> ses , vol , cifs_sb ,
4187
4386
true);
4188
4387
if (rc )
4189
4388
break ;
@@ -4194,11 +4393,18 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
4194
4393
& tcon );
4195
4394
}
4196
4395
if (rc ) {
4396
+ if (rc == - EACCES || rc == - EOPNOTSUPP )
4397
+ break ;
4398
+ /* Perform DFS failover to any other DFS targets */
4399
+ rc = mount_do_dfs_failover (full_path + 1 , cifs_sb , vol ,
4400
+ root_tcon -> ses , & xid ,
4401
+ & server , & ses , & tcon );
4197
4402
if (rc == - EACCES || rc == - EOPNOTSUPP || !server ||
4198
4403
!ses )
4199
4404
goto error ;
4200
4405
}
4201
4406
}
4407
+ cifs_put_tcon (root_tcon );
4202
4408
4203
4409
if (rc )
4204
4410
goto error ;
@@ -4214,6 +4420,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
4214
4420
return mount_setup_tlink (cifs_sb , ses , tcon );
4215
4421
4216
4422
error :
4423
+ kfree (full_path );
4424
+ kfree (root_path );
4217
4425
mount_put_conns (cifs_sb , xid , server , ses , tcon );
4218
4426
return rc ;
4219
4427
}
0 commit comments