diff options
author | Jeff Layton <jlayton@redhat.com> | 2010-10-06 19:51:11 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2010-10-07 14:18:00 -0400 |
commit | 9d002df492b14c690425d9785530371b6c1ccbca (patch) | |
tree | 6ed1a52d0e348e985f7bd194d22ee6e7854fa9e8 /fs/cifs | |
parent | c9928f7040a6e5f39e028bea500e0fde910d4a96 (diff) |
cifs: add routines to build sessions and tcons on the fly
This patch is rather large, but it's a bit difficult to do piecemeal...
For non-multiuser mounts, everything will basically work as it does
today. A call to cifs_sb_tlink will return the "master" tcon link.
Turn the tcon pointer in the cifs_sb into a radix tree that uses the
fsuid of the process as a key. The value is a new "tcon_link" struct
that contains info about a tcon that's under construction.
When a new process needs a tcon, it'll call cifs_sb_tcon. That will
then look up the tcon_link in the radix tree. If it exists and is
valid, it's returned.
If it doesn't exist, then we stuff a new tcon_link into the tree and
mark it as pending and then go and try to build the session/tcon.
If that works, the tcon pointer in the tcon_link is updated and the
pending flag is cleared.
If the construction fails, then we set the tcon pointer to an ERR_PTR
and clear the pending flag.
If the radix tree is searched and the tcon_link is marked pending
then we go to sleep and wait for the pending flag to be cleared.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 7 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 32 | ||||
-rw-r--r-- | fs/cifs/connect.c | 268 |
3 files changed, 279 insertions, 28 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index e04e6923d354..5ce57bdf1865 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h | |||
@@ -15,6 +15,8 @@ | |||
15 | * the GNU Lesser General Public License for more details. | 15 | * the GNU Lesser General Public License for more details. |
16 | * | 16 | * |
17 | */ | 17 | */ |
18 | #include <linux/radix-tree.h> | ||
19 | |||
18 | #ifndef _CIFS_FS_SB_H | 20 | #ifndef _CIFS_FS_SB_H |
19 | #define _CIFS_FS_SB_H | 21 | #define _CIFS_FS_SB_H |
20 | 22 | ||
@@ -40,8 +42,9 @@ | |||
40 | #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ | 42 | #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ |
41 | 43 | ||
42 | struct cifs_sb_info { | 44 | struct cifs_sb_info { |
43 | struct cifsTconInfo *ptcon; /* primary mount */ | 45 | struct radix_tree_root tlink_tree; |
44 | struct list_head nested_tcon_q; | 46 | #define CIFS_TLINK_MASTER_TAG 0 /* is "master" (mount) tcon */ |
47 | spinlock_t tlink_tree_lock; | ||
45 | struct nls_table *local_nls; | 48 | struct nls_table *local_nls; |
46 | unsigned int rsize; | 49 | unsigned int rsize; |
47 | unsigned int wsize; | 50 | unsigned int wsize; |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d5324853203b..9a7c472a153f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -317,42 +317,36 @@ struct cifsTconInfo { | |||
317 | * "get" on the container. | 317 | * "get" on the container. |
318 | */ | 318 | */ |
319 | struct tcon_link { | 319 | struct tcon_link { |
320 | spinlock_t tl_lock; | 320 | unsigned long tl_index; |
321 | u32 tl_count; | 321 | unsigned long tl_flags; |
322 | u64 tl_time; | 322 | #define TCON_LINK_MASTER 0 |
323 | #define TCON_LINK_PENDING 1 | ||
324 | #define TCON_LINK_IN_TREE 2 | ||
325 | unsigned long tl_time; | ||
326 | atomic_t tl_count; | ||
323 | struct cifsTconInfo *tl_tcon; | 327 | struct cifsTconInfo *tl_tcon; |
324 | }; | 328 | }; |
325 | 329 | ||
326 | static inline struct tcon_link * | 330 | extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); |
327 | cifs_sb_tlink(struct cifs_sb_info *cifs_sb) | ||
328 | { | ||
329 | return (struct tcon_link *)cifs_sb->ptcon; | ||
330 | } | ||
331 | 331 | ||
332 | static inline struct cifsTconInfo * | 332 | static inline struct cifsTconInfo * |
333 | tlink_tcon(struct tcon_link *tlink) | 333 | tlink_tcon(struct tcon_link *tlink) |
334 | { | 334 | { |
335 | return (struct cifsTconInfo *)tlink; | 335 | return tlink->tl_tcon; |
336 | } | 336 | } |
337 | 337 | ||
338 | static inline void | 338 | extern void cifs_put_tlink(struct tcon_link *tlink); |
339 | cifs_put_tlink(struct tcon_link *tlink) | ||
340 | { | ||
341 | return; | ||
342 | } | ||
343 | 339 | ||
344 | static inline struct tcon_link * | 340 | static inline struct tcon_link * |
345 | cifs_get_tlink(struct tcon_link *tlink) | 341 | cifs_get_tlink(struct tcon_link *tlink) |
346 | { | 342 | { |
343 | if (tlink && !IS_ERR(tlink)) | ||
344 | atomic_inc(&tlink->tl_count); | ||
347 | return tlink; | 345 | return tlink; |
348 | } | 346 | } |
349 | 347 | ||
350 | /* This function is always expected to succeed */ | 348 | /* This function is always expected to succeed */ |
351 | static inline struct cifsTconInfo * | 349 | extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); |
352 | cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) | ||
353 | { | ||
354 | return cifs_sb->ptcon; | ||
355 | } | ||
356 | 350 | ||
357 | /* | 351 | /* |
358 | * This info hangs off the cifsFileInfo structure, pointed to by llist. | 352 | * This info hangs off the cifsFileInfo structure, pointed to by llist. |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f6a3091c2874..3156a9de947d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -109,6 +109,9 @@ struct smb_vol { | |||
109 | struct nls_table *local_nls; | 109 | struct nls_table *local_nls; |
110 | }; | 110 | }; |
111 | 111 | ||
112 | #define TLINK_ERROR_EXPIRE (1 * HZ) | ||
113 | |||
114 | |||
112 | static int ipv4_connect(struct TCP_Server_Info *server); | 115 | static int ipv4_connect(struct TCP_Server_Info *server); |
113 | static int ipv6_connect(struct TCP_Server_Info *server); | 116 | static int ipv6_connect(struct TCP_Server_Info *server); |
114 | 117 | ||
@@ -1959,6 +1962,23 @@ out_fail: | |||
1959 | return ERR_PTR(rc); | 1962 | return ERR_PTR(rc); |
1960 | } | 1963 | } |
1961 | 1964 | ||
1965 | void | ||
1966 | cifs_put_tlink(struct tcon_link *tlink) | ||
1967 | { | ||
1968 | if (!tlink || IS_ERR(tlink)) | ||
1969 | return; | ||
1970 | |||
1971 | if (!atomic_dec_and_test(&tlink->tl_count) || | ||
1972 | test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { | ||
1973 | tlink->tl_time = jiffies; | ||
1974 | return; | ||
1975 | } | ||
1976 | |||
1977 | if (!IS_ERR(tlink_tcon(tlink))) | ||
1978 | cifs_put_tcon(tlink_tcon(tlink)); | ||
1979 | kfree(tlink); | ||
1980 | return; | ||
1981 | } | ||
1962 | 1982 | ||
1963 | int | 1983 | int |
1964 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1984 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, |
@@ -2641,6 +2661,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2641 | struct TCP_Server_Info *srvTcp; | 2661 | struct TCP_Server_Info *srvTcp; |
2642 | char *full_path; | 2662 | char *full_path; |
2643 | char *mount_data = mount_data_global; | 2663 | char *mount_data = mount_data_global; |
2664 | struct tcon_link *tlink; | ||
2644 | #ifdef CONFIG_CIFS_DFS_UPCALL | 2665 | #ifdef CONFIG_CIFS_DFS_UPCALL |
2645 | struct dfs_info3_param *referrals = NULL; | 2666 | struct dfs_info3_param *referrals = NULL; |
2646 | unsigned int num_referrals = 0; | 2667 | unsigned int num_referrals = 0; |
@@ -2652,6 +2673,7 @@ try_mount_again: | |||
2652 | pSesInfo = NULL; | 2673 | pSesInfo = NULL; |
2653 | srvTcp = NULL; | 2674 | srvTcp = NULL; |
2654 | full_path = NULL; | 2675 | full_path = NULL; |
2676 | tlink = NULL; | ||
2655 | 2677 | ||
2656 | xid = GetXid(); | 2678 | xid = GetXid(); |
2657 | 2679 | ||
@@ -2727,8 +2749,6 @@ try_mount_again: | |||
2727 | goto remote_path_check; | 2749 | goto remote_path_check; |
2728 | } | 2750 | } |
2729 | 2751 | ||
2730 | cifs_sb->ptcon = tcon; | ||
2731 | |||
2732 | /* do not care if following two calls succeed - informational */ | 2752 | /* do not care if following two calls succeed - informational */ |
2733 | if (!tcon->ipc) { | 2753 | if (!tcon->ipc) { |
2734 | CIFSSMBQFSDeviceInfo(xid, tcon); | 2754 | CIFSSMBQFSDeviceInfo(xid, tcon); |
@@ -2837,6 +2857,35 @@ remote_path_check: | |||
2837 | #endif | 2857 | #endif |
2838 | } | 2858 | } |
2839 | 2859 | ||
2860 | if (rc) | ||
2861 | goto mount_fail_check; | ||
2862 | |||
2863 | /* now, hang the tcon off of the superblock */ | ||
2864 | tlink = kzalloc(sizeof *tlink, GFP_KERNEL); | ||
2865 | if (tlink == NULL) { | ||
2866 | rc = -ENOMEM; | ||
2867 | goto mount_fail_check; | ||
2868 | } | ||
2869 | |||
2870 | tlink->tl_index = pSesInfo->linux_uid; | ||
2871 | tlink->tl_tcon = tcon; | ||
2872 | tlink->tl_time = jiffies; | ||
2873 | set_bit(TCON_LINK_MASTER, &tlink->tl_flags); | ||
2874 | set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); | ||
2875 | |||
2876 | rc = radix_tree_preload(GFP_KERNEL); | ||
2877 | if (rc == -ENOMEM) { | ||
2878 | kfree(tlink); | ||
2879 | goto mount_fail_check; | ||
2880 | } | ||
2881 | |||
2882 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
2883 | radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink); | ||
2884 | radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid, | ||
2885 | CIFS_TLINK_MASTER_TAG); | ||
2886 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
2887 | radix_tree_preload_end(); | ||
2888 | |||
2840 | mount_fail_check: | 2889 | mount_fail_check: |
2841 | /* on error free sesinfo and tcon struct if needed */ | 2890 | /* on error free sesinfo and tcon struct if needed */ |
2842 | if (rc) { | 2891 | if (rc) { |
@@ -3023,19 +3072,37 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
3023 | int | 3072 | int |
3024 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3073 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) |
3025 | { | 3074 | { |
3026 | int rc = 0; | 3075 | int i, ret; |
3027 | char *tmp; | 3076 | char *tmp; |
3028 | struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); | 3077 | struct tcon_link *tlink[8]; |
3078 | unsigned long index = 0; | ||
3079 | |||
3080 | do { | ||
3081 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
3082 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, | ||
3083 | (void **)tlink, index, | ||
3084 | ARRAY_SIZE(tlink)); | ||
3085 | /* increment index for next pass */ | ||
3086 | if (ret > 0) | ||
3087 | index = tlink[ret - 1]->tl_index + 1; | ||
3088 | for (i = 0; i < ret; i++) { | ||
3089 | cifs_get_tlink(tlink[i]); | ||
3090 | clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); | ||
3091 | radix_tree_delete(&cifs_sb->tlink_tree, | ||
3092 | tlink[i]->tl_index); | ||
3093 | } | ||
3094 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
3029 | 3095 | ||
3030 | cifs_put_tcon(tcon); | 3096 | for (i = 0; i < ret; i++) |
3097 | cifs_put_tlink(tlink[i]); | ||
3098 | } while (ret != 0); | ||
3031 | 3099 | ||
3032 | cifs_sb->ptcon = NULL; | ||
3033 | tmp = cifs_sb->prepath; | 3100 | tmp = cifs_sb->prepath; |
3034 | cifs_sb->prepathlen = 0; | 3101 | cifs_sb->prepathlen = 0; |
3035 | cifs_sb->prepath = NULL; | 3102 | cifs_sb->prepath = NULL; |
3036 | kfree(tmp); | 3103 | kfree(tmp); |
3037 | 3104 | ||
3038 | return rc; | 3105 | return 0; |
3039 | } | 3106 | } |
3040 | 3107 | ||
3041 | int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) | 3108 | int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) |
@@ -3096,3 +3163,190 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, | |||
3096 | return rc; | 3163 | return rc; |
3097 | } | 3164 | } |
3098 | 3165 | ||
3166 | struct cifsTconInfo * | ||
3167 | cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) | ||
3168 | { | ||
3169 | struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); | ||
3170 | struct cifsSesInfo *ses; | ||
3171 | struct cifsTconInfo *tcon = NULL; | ||
3172 | struct smb_vol *vol_info; | ||
3173 | char username[MAX_USERNAME_SIZE + 1]; | ||
3174 | |||
3175 | vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); | ||
3176 | if (vol_info == NULL) { | ||
3177 | tcon = ERR_PTR(-ENOMEM); | ||
3178 | goto out; | ||
3179 | } | ||
3180 | |||
3181 | snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); | ||
3182 | vol_info->username = username; | ||
3183 | vol_info->local_nls = cifs_sb->local_nls; | ||
3184 | vol_info->linux_uid = fsuid; | ||
3185 | vol_info->cred_uid = fsuid; | ||
3186 | vol_info->UNC = master_tcon->treeName; | ||
3187 | vol_info->retry = master_tcon->retry; | ||
3188 | vol_info->nocase = master_tcon->nocase; | ||
3189 | vol_info->local_lease = master_tcon->local_lease; | ||
3190 | vol_info->no_linux_ext = !master_tcon->unix_ext; | ||
3191 | |||
3192 | /* FIXME: allow for other secFlg settings */ | ||
3193 | vol_info->secFlg = CIFSSEC_MUST_KRB5; | ||
3194 | |||
3195 | /* get a reference for the same TCP session */ | ||
3196 | write_lock(&cifs_tcp_ses_lock); | ||
3197 | ++master_tcon->ses->server->srv_count; | ||
3198 | write_unlock(&cifs_tcp_ses_lock); | ||
3199 | |||
3200 | ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); | ||
3201 | if (IS_ERR(ses)) { | ||
3202 | tcon = (struct cifsTconInfo *)ses; | ||
3203 | cifs_put_tcp_session(master_tcon->ses->server); | ||
3204 | goto out; | ||
3205 | } | ||
3206 | |||
3207 | tcon = cifs_get_tcon(ses, vol_info); | ||
3208 | if (IS_ERR(tcon)) { | ||
3209 | cifs_put_smb_ses(ses); | ||
3210 | goto out; | ||
3211 | } | ||
3212 | |||
3213 | if (ses->capabilities & CAP_UNIX) | ||
3214 | reset_cifs_unix_caps(0, tcon, NULL, vol_info); | ||
3215 | out: | ||
3216 | kfree(vol_info); | ||
3217 | |||
3218 | return tcon; | ||
3219 | } | ||
3220 | |||
3221 | static struct tcon_link * | ||
3222 | cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) | ||
3223 | { | ||
3224 | struct tcon_link *tlink; | ||
3225 | unsigned int ret; | ||
3226 | |||
3227 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
3228 | ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink, | ||
3229 | 0, 1, CIFS_TLINK_MASTER_TAG); | ||
3230 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
3231 | |||
3232 | /* the master tcon should always be present */ | ||
3233 | if (ret == 0) | ||
3234 | BUG(); | ||
3235 | |||
3236 | return tlink; | ||
3237 | } | ||
3238 | |||
3239 | struct cifsTconInfo * | ||
3240 | cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) | ||
3241 | { | ||
3242 | return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); | ||
3243 | } | ||
3244 | |||
3245 | static int | ||
3246 | cifs_sb_tcon_pending_wait(void *unused) | ||
3247 | { | ||
3248 | schedule(); | ||
3249 | return signal_pending(current) ? -ERESTARTSYS : 0; | ||
3250 | } | ||
3251 | |||
3252 | /* | ||
3253 | * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the | ||
3254 | * current task. | ||
3255 | * | ||
3256 | * If the superblock doesn't refer to a multiuser mount, then just return | ||
3257 | * the master tcon for the mount. | ||
3258 | * | ||
3259 | * First, search the radix tree for an existing tcon for this fsuid. If one | ||
3260 | * exists, then check to see if it's pending construction. If it is then wait | ||
3261 | * for construction to complete. Once it's no longer pending, check to see if | ||
3262 | * it failed and either return an error or retry construction, depending on | ||
3263 | * the timeout. | ||
3264 | * | ||
3265 | * If one doesn't exist then insert a new tcon_link struct into the tree and | ||
3266 | * try to construct a new one. | ||
3267 | */ | ||
3268 | struct tcon_link * | ||
3269 | cifs_sb_tlink(struct cifs_sb_info *cifs_sb) | ||
3270 | { | ||
3271 | int ret; | ||
3272 | unsigned long fsuid = (unsigned long) current_fsuid(); | ||
3273 | struct tcon_link *tlink, *newtlink; | ||
3274 | |||
3275 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | ||
3276 | return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); | ||
3277 | |||
3278 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
3279 | tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); | ||
3280 | if (tlink) | ||
3281 | cifs_get_tlink(tlink); | ||
3282 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
3283 | |||
3284 | if (tlink == NULL) { | ||
3285 | newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); | ||
3286 | if (newtlink == NULL) | ||
3287 | return ERR_PTR(-ENOMEM); | ||
3288 | newtlink->tl_index = fsuid; | ||
3289 | newtlink->tl_tcon = ERR_PTR(-EACCES); | ||
3290 | set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); | ||
3291 | set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); | ||
3292 | cifs_get_tlink(newtlink); | ||
3293 | |||
3294 | ret = radix_tree_preload(GFP_KERNEL); | ||
3295 | if (ret != 0) { | ||
3296 | kfree(newtlink); | ||
3297 | return ERR_PTR(ret); | ||
3298 | } | ||
3299 | |||
3300 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
3301 | /* was one inserted after previous search? */ | ||
3302 | tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); | ||
3303 | if (tlink) { | ||
3304 | cifs_get_tlink(tlink); | ||
3305 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
3306 | radix_tree_preload_end(); | ||
3307 | kfree(newtlink); | ||
3308 | goto wait_for_construction; | ||
3309 | } | ||
3310 | ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink); | ||
3311 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
3312 | radix_tree_preload_end(); | ||
3313 | if (ret) { | ||
3314 | kfree(newtlink); | ||
3315 | return ERR_PTR(ret); | ||
3316 | } | ||
3317 | tlink = newtlink; | ||
3318 | } else { | ||
3319 | wait_for_construction: | ||
3320 | ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, | ||
3321 | cifs_sb_tcon_pending_wait, | ||
3322 | TASK_INTERRUPTIBLE); | ||
3323 | if (ret) { | ||
3324 | cifs_put_tlink(tlink); | ||
3325 | return ERR_PTR(ret); | ||
3326 | } | ||
3327 | |||
3328 | /* if it's good, return it */ | ||
3329 | if (!IS_ERR(tlink->tl_tcon)) | ||
3330 | return tlink; | ||
3331 | |||
3332 | /* return error if we tried this already recently */ | ||
3333 | if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { | ||
3334 | cifs_put_tlink(tlink); | ||
3335 | return ERR_PTR(-EACCES); | ||
3336 | } | ||
3337 | |||
3338 | if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) | ||
3339 | goto wait_for_construction; | ||
3340 | } | ||
3341 | |||
3342 | tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); | ||
3343 | clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); | ||
3344 | wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); | ||
3345 | |||
3346 | if (IS_ERR(tlink->tl_tcon)) { | ||
3347 | cifs_put_tlink(tlink); | ||
3348 | return ERR_PTR(-EACCES); | ||
3349 | } | ||
3350 | |||
3351 | return tlink; | ||
3352 | } | ||