diff options
-rw-r--r-- | fs/cifs/cifs_debug.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 1 | ||||
-rw-r--r-- | fs/cifs/cifs_ioctl.h | 8 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 123 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 11 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 50 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 4 | ||||
-rw-r--r-- | fs/cifs/connect.c | 43 | ||||
-rw-r--r-- | fs/cifs/file.c | 105 | ||||
-rw-r--r-- | fs/cifs/ioctl.c | 16 | ||||
-rw-r--r-- | fs/cifs/misc.c | 15 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 6 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 6 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 16 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 62 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 587 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 2 | ||||
-rw-r--r-- | fs/cifs/xattr.c | 62 |
19 files changed, 824 insertions, 296 deletions
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 6c58e13fed2f..3d03e48a9213 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c | |||
@@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) | |||
152 | list_for_each(tmp1, &cifs_tcp_ses_list) { | 152 | list_for_each(tmp1, &cifs_tcp_ses_list) { |
153 | server = list_entry(tmp1, struct TCP_Server_Info, | 153 | server = list_entry(tmp1, struct TCP_Server_Info, |
154 | tcp_ses_list); | 154 | tcp_ses_list); |
155 | seq_printf(m, "\nNumber of credits: %d", server->credits); | ||
155 | i++; | 156 | i++; |
156 | list_for_each(tmp2, &server->smb_ses_list) { | 157 | list_for_each(tmp2, &server->smb_ses_list) { |
157 | ses = list_entry(tmp2, struct cifs_ses, | 158 | ses = list_entry(tmp2, struct cifs_ses, |
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 1418daa03d95..07ed81cf1552 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h | |||
@@ -49,6 +49,7 @@ | |||
49 | #define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible | 49 | #define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible |
50 | * root mountable | 50 | * root mountable |
51 | */ | 51 | */ |
52 | #define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */ | ||
52 | 53 | ||
53 | struct cifs_sb_info { | 54 | struct cifs_sb_info { |
54 | struct rb_root tlink_tree; | 55 | struct rb_root tlink_tree; |
diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h index 0065256881d8..57ff0756e30c 100644 --- a/fs/cifs/cifs_ioctl.h +++ b/fs/cifs/cifs_ioctl.h | |||
@@ -36,7 +36,15 @@ struct smb_mnt_fs_info { | |||
36 | __u64 cifs_posix_caps; | 36 | __u64 cifs_posix_caps; |
37 | } __packed; | 37 | } __packed; |
38 | 38 | ||
39 | struct smb_snapshot_array { | ||
40 | __u32 number_of_snapshots; | ||
41 | __u32 number_of_snapshots_returned; | ||
42 | __u32 snapshot_array_size; | ||
43 | /* snapshots[]; */ | ||
44 | } __packed; | ||
45 | |||
39 | #define CIFS_IOCTL_MAGIC 0xCF | 46 | #define CIFS_IOCTL_MAGIC 0xCF |
40 | #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) | 47 | #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) |
41 | #define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) | 48 | #define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) |
42 | #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) | 49 | #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) |
50 | #define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) | ||
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 71e8a56e9479..15bac390dff9 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c | |||
@@ -42,6 +42,35 @@ static const struct cifs_sid sid_authusers = { | |||
42 | /* group users */ | 42 | /* group users */ |
43 | static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; | 43 | static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; |
44 | 44 | ||
45 | /* S-1-22-1 Unmapped Unix users */ | ||
46 | static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, | ||
47 | {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | ||
48 | |||
49 | /* S-1-22-2 Unmapped Unix groups */ | ||
50 | static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, | ||
51 | {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | ||
52 | |||
53 | /* | ||
54 | * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx | ||
55 | */ | ||
56 | |||
57 | /* S-1-5-88 MS NFS and Apple style UID/GID/mode */ | ||
58 | |||
59 | /* S-1-5-88-1 Unix uid */ | ||
60 | static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, | ||
61 | {cpu_to_le32(88), | ||
62 | cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | ||
63 | |||
64 | /* S-1-5-88-2 Unix gid */ | ||
65 | static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, | ||
66 | {cpu_to_le32(88), | ||
67 | cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | ||
68 | |||
69 | /* S-1-5-88-3 Unix mode */ | ||
70 | static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, | ||
71 | {cpu_to_le32(88), | ||
72 | cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | ||
73 | |||
45 | static const struct cred *root_cred; | 74 | static const struct cred *root_cred; |
46 | 75 | ||
47 | static int | 76 | static int |
@@ -183,6 +212,62 @@ compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) | |||
183 | return 0; /* sids compare/match */ | 212 | return 0; /* sids compare/match */ |
184 | } | 213 | } |
185 | 214 | ||
215 | static bool | ||
216 | is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group) | ||
217 | { | ||
218 | int i; | ||
219 | int num_subauth; | ||
220 | const struct cifs_sid *pwell_known_sid; | ||
221 | |||
222 | if (!psid || (puid == NULL)) | ||
223 | return false; | ||
224 | |||
225 | num_subauth = psid->num_subauth; | ||
226 | |||
227 | /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */ | ||
228 | if (num_subauth == 2) { | ||
229 | if (is_group) | ||
230 | pwell_known_sid = &sid_unix_groups; | ||
231 | else | ||
232 | pwell_known_sid = &sid_unix_users; | ||
233 | } else if (num_subauth == 3) { | ||
234 | if (is_group) | ||
235 | pwell_known_sid = &sid_unix_NFS_groups; | ||
236 | else | ||
237 | pwell_known_sid = &sid_unix_NFS_users; | ||
238 | } else | ||
239 | return false; | ||
240 | |||
241 | /* compare the revision */ | ||
242 | if (psid->revision != pwell_known_sid->revision) | ||
243 | return false; | ||
244 | |||
245 | /* compare all of the six auth values */ | ||
246 | for (i = 0; i < NUM_AUTHS; ++i) { | ||
247 | if (psid->authority[i] != pwell_known_sid->authority[i]) { | ||
248 | cifs_dbg(FYI, "auth %d did not match\n", i); | ||
249 | return false; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | if (num_subauth == 2) { | ||
254 | if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) | ||
255 | return false; | ||
256 | |||
257 | *puid = le32_to_cpu(psid->sub_auth[1]); | ||
258 | } else /* 3 subauths, ie Windows/Mac style */ { | ||
259 | *puid = le32_to_cpu(psid->sub_auth[0]); | ||
260 | if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) || | ||
261 | (psid->sub_auth[1] != pwell_known_sid->sub_auth[1])) | ||
262 | return false; | ||
263 | |||
264 | *puid = le32_to_cpu(psid->sub_auth[2]); | ||
265 | } | ||
266 | |||
267 | cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid); | ||
268 | return true; /* well known sid found, uid returned */ | ||
269 | } | ||
270 | |||
186 | static void | 271 | static void |
187 | cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) | 272 | cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) |
188 | { | 273 | { |
@@ -276,6 +361,43 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, | |||
276 | return -EIO; | 361 | return -EIO; |
277 | } | 362 | } |
278 | 363 | ||
364 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) { | ||
365 | uint32_t unix_id; | ||
366 | bool is_group; | ||
367 | |||
368 | if (sidtype != SIDOWNER) | ||
369 | is_group = true; | ||
370 | else | ||
371 | is_group = false; | ||
372 | |||
373 | if (is_well_known_sid(psid, &unix_id, is_group) == false) | ||
374 | goto try_upcall_to_get_id; | ||
375 | |||
376 | if (is_group) { | ||
377 | kgid_t gid; | ||
378 | gid_t id; | ||
379 | |||
380 | id = (gid_t)unix_id; | ||
381 | gid = make_kgid(&init_user_ns, id); | ||
382 | if (gid_valid(gid)) { | ||
383 | fgid = gid; | ||
384 | goto got_valid_id; | ||
385 | } | ||
386 | } else { | ||
387 | kuid_t uid; | ||
388 | uid_t id; | ||
389 | |||
390 | id = (uid_t)unix_id; | ||
391 | uid = make_kuid(&init_user_ns, id); | ||
392 | if (uid_valid(uid)) { | ||
393 | fuid = uid; | ||
394 | goto got_valid_id; | ||
395 | } | ||
396 | } | ||
397 | /* If unable to find uid/gid easily from SID try via upcall */ | ||
398 | } | ||
399 | |||
400 | try_upcall_to_get_id: | ||
279 | sidstr = sid_to_key_str(psid, sidtype); | 401 | sidstr = sid_to_key_str(psid, sidtype); |
280 | if (!sidstr) | 402 | if (!sidstr) |
281 | return -ENOMEM; | 403 | return -ENOMEM; |
@@ -329,6 +451,7 @@ out_revert_creds: | |||
329 | * Note that we return 0 here unconditionally. If the mapping | 451 | * Note that we return 0 here unconditionally. If the mapping |
330 | * fails then we just fall back to using the mnt_uid/mnt_gid. | 452 | * fails then we just fall back to using the mnt_uid/mnt_gid. |
331 | */ | 453 | */ |
454 | got_valid_id: | ||
332 | if (sidtype == SIDOWNER) | 455 | if (sidtype == SIDOWNER) |
333 | fattr->cf_uid = fuid; | 456 | fattr->cf_uid = fuid; |
334 | else | 457 | else |
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index cca04e710421..15261ba464c5 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -64,15 +64,15 @@ unsigned int global_secflags = CIFSSEC_DEF; | |||
64 | unsigned int sign_CIFS_PDUs = 1; | 64 | unsigned int sign_CIFS_PDUs = 1; |
65 | static const struct super_operations cifs_super_ops; | 65 | static const struct super_operations cifs_super_ops; |
66 | unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; | 66 | unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; |
67 | module_param(CIFSMaxBufSize, uint, 0); | 67 | module_param(CIFSMaxBufSize, uint, 0444); |
68 | MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). " | 68 | MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). " |
69 | "Default: 16384 Range: 8192 to 130048"); | 69 | "Default: 16384 Range: 8192 to 130048"); |
70 | unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; | 70 | unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; |
71 | module_param(cifs_min_rcv, uint, 0); | 71 | module_param(cifs_min_rcv, uint, 0444); |
72 | MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " | 72 | MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " |
73 | "1 to 64"); | 73 | "1 to 64"); |
74 | unsigned int cifs_min_small = 30; | 74 | unsigned int cifs_min_small = 30; |
75 | module_param(cifs_min_small, uint, 0); | 75 | module_param(cifs_min_small, uint, 0444); |
76 | MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " | 76 | MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " |
77 | "Range: 2 to 256"); | 77 | "Range: 2 to 256"); |
78 | unsigned int cifs_max_pending = CIFS_MAX_REQ; | 78 | unsigned int cifs_max_pending = CIFS_MAX_REQ; |
@@ -271,7 +271,7 @@ cifs_alloc_inode(struct super_block *sb) | |||
271 | cifs_inode->createtime = 0; | 271 | cifs_inode->createtime = 0; |
272 | cifs_inode->epoch = 0; | 272 | cifs_inode->epoch = 0; |
273 | #ifdef CONFIG_CIFS_SMB2 | 273 | #ifdef CONFIG_CIFS_SMB2 |
274 | get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); | 274 | generate_random_uuid(cifs_inode->lease_key); |
275 | #endif | 275 | #endif |
276 | /* | 276 | /* |
277 | * Can not set i_flags here - they get immediately overwritten to zero | 277 | * Can not set i_flags here - they get immediately overwritten to zero |
@@ -469,6 +469,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) | |||
469 | seq_puts(s, ",posixpaths"); | 469 | seq_puts(s, ",posixpaths"); |
470 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) | 470 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) |
471 | seq_puts(s, ",setuids"); | 471 | seq_puts(s, ",setuids"); |
472 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) | ||
473 | seq_puts(s, ",idsfromsid"); | ||
472 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) | 474 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) |
473 | seq_puts(s, ",serverino"); | 475 | seq_puts(s, ",serverino"); |
474 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) | 476 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) |
@@ -1262,7 +1264,6 @@ init_cifs(void) | |||
1262 | GlobalTotalActiveXid = 0; | 1264 | GlobalTotalActiveXid = 0; |
1263 | GlobalMaxActiveXid = 0; | 1265 | GlobalMaxActiveXid = 0; |
1264 | spin_lock_init(&cifs_tcp_ses_lock); | 1266 | spin_lock_init(&cifs_tcp_ses_lock); |
1265 | spin_lock_init(&cifs_file_list_lock); | ||
1266 | spin_lock_init(&GlobalMid_Lock); | 1267 | spin_lock_init(&GlobalMid_Lock); |
1267 | 1268 | ||
1268 | get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret)); | 1269 | get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret)); |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 8f1d8c1e72be..1f17f6bd7a60 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -75,6 +75,18 @@ | |||
75 | #define SMB_ECHO_INTERVAL_MAX 600 | 75 | #define SMB_ECHO_INTERVAL_MAX 600 |
76 | #define SMB_ECHO_INTERVAL_DEFAULT 60 | 76 | #define SMB_ECHO_INTERVAL_DEFAULT 60 |
77 | 77 | ||
78 | /* | ||
79 | * Default number of credits to keep available for SMB3. | ||
80 | * This value is chosen somewhat arbitrarily. The Windows client | ||
81 | * defaults to 128 credits, the Windows server allows clients up to | ||
82 | * 512 credits (or 8K for later versions), and the NetApp server | ||
83 | * does not limit clients at all. Choose a high enough default value | ||
84 | * such that the client shouldn't limit performance, but allow mount | ||
85 | * to override (until you approach 64K, where we limit credits to 65000 | ||
86 | * to reduce possibility of seeing more server credit overflow bugs. | ||
87 | */ | ||
88 | #define SMB2_MAX_CREDITS_AVAILABLE 32000 | ||
89 | |||
78 | #include "cifspdu.h" | 90 | #include "cifspdu.h" |
79 | 91 | ||
80 | #ifndef XATTR_DOS_ATTRIB | 92 | #ifndef XATTR_DOS_ATTRIB |
@@ -376,6 +388,8 @@ struct smb_version_operations { | |||
376 | int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); | 388 | int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); |
377 | int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, | 389 | int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, |
378 | struct cifsFileInfo *src_file); | 390 | struct cifsFileInfo *src_file); |
391 | int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, | ||
392 | struct cifsFileInfo *src_file, void __user *); | ||
379 | int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, | 393 | int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, |
380 | struct cifs_sb_info *, const unsigned char *, | 394 | struct cifs_sb_info *, const unsigned char *, |
381 | char *, unsigned int *); | 395 | char *, unsigned int *); |
@@ -464,6 +478,7 @@ struct smb_vol { | |||
464 | bool retry:1; | 478 | bool retry:1; |
465 | bool intr:1; | 479 | bool intr:1; |
466 | bool setuids:1; | 480 | bool setuids:1; |
481 | bool setuidfromacl:1; | ||
467 | bool override_uid:1; | 482 | bool override_uid:1; |
468 | bool override_gid:1; | 483 | bool override_gid:1; |
469 | bool dynperm:1; | 484 | bool dynperm:1; |
@@ -510,6 +525,7 @@ struct smb_vol { | |||
510 | struct sockaddr_storage srcaddr; /* allow binding to a local IP */ | 525 | struct sockaddr_storage srcaddr; /* allow binding to a local IP */ |
511 | struct nls_table *local_nls; | 526 | struct nls_table *local_nls; |
512 | unsigned int echo_interval; /* echo interval in secs */ | 527 | unsigned int echo_interval; /* echo interval in secs */ |
528 | unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ | ||
513 | }; | 529 | }; |
514 | 530 | ||
515 | #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ | 531 | #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ |
@@ -567,7 +583,8 @@ struct TCP_Server_Info { | |||
567 | bool noblocksnd; /* use blocking sendmsg */ | 583 | bool noblocksnd; /* use blocking sendmsg */ |
568 | bool noautotune; /* do not autotune send buf sizes */ | 584 | bool noautotune; /* do not autotune send buf sizes */ |
569 | bool tcp_nodelay; | 585 | bool tcp_nodelay; |
570 | int credits; /* send no more requests at once */ | 586 | unsigned int credits; /* send no more requests at once */ |
587 | unsigned int max_credits; /* can override large 32000 default at mnt */ | ||
571 | unsigned int in_flight; /* number of requests on the wire to server */ | 588 | unsigned int in_flight; /* number of requests on the wire to server */ |
572 | spinlock_t req_lock; /* protect the two values above */ | 589 | spinlock_t req_lock; /* protect the two values above */ |
573 | struct mutex srv_mutex; | 590 | struct mutex srv_mutex; |
@@ -833,6 +850,7 @@ struct cifs_tcon { | |||
833 | struct list_head tcon_list; | 850 | struct list_head tcon_list; |
834 | int tc_count; | 851 | int tc_count; |
835 | struct list_head openFileList; | 852 | struct list_head openFileList; |
853 | spinlock_t open_file_lock; /* protects list above */ | ||
836 | struct cifs_ses *ses; /* pointer to session associated with */ | 854 | struct cifs_ses *ses; /* pointer to session associated with */ |
837 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ | 855 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ |
838 | char *nativeFileSystem; | 856 | char *nativeFileSystem; |
@@ -889,7 +907,7 @@ struct cifs_tcon { | |||
889 | #endif /* CONFIG_CIFS_STATS2 */ | 907 | #endif /* CONFIG_CIFS_STATS2 */ |
890 | __u64 bytes_read; | 908 | __u64 bytes_read; |
891 | __u64 bytes_written; | 909 | __u64 bytes_written; |
892 | spinlock_t stat_lock; | 910 | spinlock_t stat_lock; /* protects the two fields above */ |
893 | #endif /* CONFIG_CIFS_STATS */ | 911 | #endif /* CONFIG_CIFS_STATS */ |
894 | FILE_SYSTEM_DEVICE_INFO fsDevInfo; | 912 | FILE_SYSTEM_DEVICE_INFO fsDevInfo; |
895 | FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ | 913 | FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ |
@@ -1040,20 +1058,24 @@ struct cifs_fid_locks { | |||
1040 | }; | 1058 | }; |
1041 | 1059 | ||
1042 | struct cifsFileInfo { | 1060 | struct cifsFileInfo { |
1061 | /* following two lists are protected by tcon->open_file_lock */ | ||
1043 | struct list_head tlist; /* pointer to next fid owned by tcon */ | 1062 | struct list_head tlist; /* pointer to next fid owned by tcon */ |
1044 | struct list_head flist; /* next fid (file instance) for this inode */ | 1063 | struct list_head flist; /* next fid (file instance) for this inode */ |
1064 | /* lock list below protected by cifsi->lock_sem */ | ||
1045 | struct cifs_fid_locks *llist; /* brlocks held by this fid */ | 1065 | struct cifs_fid_locks *llist; /* brlocks held by this fid */ |
1046 | kuid_t uid; /* allows finding which FileInfo structure */ | 1066 | kuid_t uid; /* allows finding which FileInfo structure */ |
1047 | __u32 pid; /* process id who opened file */ | 1067 | __u32 pid; /* process id who opened file */ |
1048 | struct cifs_fid fid; /* file id from remote */ | 1068 | struct cifs_fid fid; /* file id from remote */ |
1069 | struct list_head rlist; /* reconnect list */ | ||
1049 | /* BB add lock scope info here if needed */ ; | 1070 | /* BB add lock scope info here if needed */ ; |
1050 | /* lock scope id (0 if none) */ | 1071 | /* lock scope id (0 if none) */ |
1051 | struct dentry *dentry; | 1072 | struct dentry *dentry; |
1052 | unsigned int f_flags; | ||
1053 | struct tcon_link *tlink; | 1073 | struct tcon_link *tlink; |
1074 | unsigned int f_flags; | ||
1054 | bool invalidHandle:1; /* file closed via session abend */ | 1075 | bool invalidHandle:1; /* file closed via session abend */ |
1055 | bool oplock_break_cancelled:1; | 1076 | bool oplock_break_cancelled:1; |
1056 | int count; /* refcount protected by cifs_file_list_lock */ | 1077 | int count; |
1078 | spinlock_t file_info_lock; /* protects four flag/count fields above */ | ||
1057 | struct mutex fh_mutex; /* prevents reopen race after dead ses*/ | 1079 | struct mutex fh_mutex; /* prevents reopen race after dead ses*/ |
1058 | struct cifs_search_info srch_inf; | 1080 | struct cifs_search_info srch_inf; |
1059 | struct work_struct oplock_break; /* work for oplock breaks */ | 1081 | struct work_struct oplock_break; /* work for oplock breaks */ |
@@ -1120,7 +1142,7 @@ struct cifs_writedata { | |||
1120 | 1142 | ||
1121 | /* | 1143 | /* |
1122 | * Take a reference on the file private data. Must be called with | 1144 | * Take a reference on the file private data. Must be called with |
1123 | * cifs_file_list_lock held. | 1145 | * cfile->file_info_lock held. |
1124 | */ | 1146 | */ |
1125 | static inline void | 1147 | static inline void |
1126 | cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) | 1148 | cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) |
@@ -1514,8 +1536,10 @@ require use of the stronger protocol */ | |||
1514 | * GlobalMid_Lock protects: | 1536 | * GlobalMid_Lock protects: |
1515 | * list operations on pending_mid_q and oplockQ | 1537 | * list operations on pending_mid_q and oplockQ |
1516 | * updates to XID counters, multiplex id and SMB sequence numbers | 1538 | * updates to XID counters, multiplex id and SMB sequence numbers |
1517 | * cifs_file_list_lock protects: | 1539 | * tcp_ses_lock protects: |
1518 | * list operations on tcp and SMB session lists and tCon lists | 1540 | * list operations on tcp and SMB session lists |
1541 | * tcon->open_file_lock protects the list of open files hanging off the tcon | ||
1542 | * cfile->file_info_lock protects counters and fields in cifs file struct | ||
1519 | * f_owner.lock protects certain per file struct operations | 1543 | * f_owner.lock protects certain per file struct operations |
1520 | * mapping->page_lock protects certain per page operations | 1544 | * mapping->page_lock protects certain per page operations |
1521 | * | 1545 | * |
@@ -1547,18 +1571,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; | |||
1547 | * tcp session, and the list of tcon's per smb session. It also protects | 1571 | * tcp session, and the list of tcon's per smb session. It also protects |
1548 | * the reference counters for the server, smb session, and tcon. Finally, | 1572 | * the reference counters for the server, smb session, and tcon. Finally, |
1549 | * changes to the tcon->tidStatus should be done while holding this lock. | 1573 | * changes to the tcon->tidStatus should be done while holding this lock. |
1574 | * generally the locks should be taken in order tcp_ses_lock before | ||
1575 | * tcon->open_file_lock and that before file->file_info_lock since the | ||
1576 | * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file | ||
1550 | */ | 1577 | */ |
1551 | GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock; | 1578 | GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock; |
1552 | 1579 | ||
1553 | /* | ||
1554 | * This lock protects the cifs_file->llist and cifs_file->flist | ||
1555 | * list operations, and updates to some flags (cifs_file->invalidHandle) | ||
1556 | * It will be moved to either use the tcon->stat_lock or equivalent later. | ||
1557 | * If cifs_tcp_ses_lock and the lock below are both needed to be held, then | ||
1558 | * the cifs_tcp_ses_lock must be grabbed first and released last. | ||
1559 | */ | ||
1560 | GLOBAL_EXTERN spinlock_t cifs_file_list_lock; | ||
1561 | |||
1562 | #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */ | 1580 | #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */ |
1563 | /* Outstanding dir notify requests */ | 1581 | /* Outstanding dir notify requests */ |
1564 | GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; | 1582 | GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4ead72a001f9..ced0e42ce460 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -193,6 +193,8 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data, | |||
193 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); | 193 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); |
194 | extern void cifs_umount(struct cifs_sb_info *); | 194 | extern void cifs_umount(struct cifs_sb_info *); |
195 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); | 195 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); |
196 | extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); | ||
197 | |||
196 | extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, | 198 | extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, |
197 | __u64 length, __u8 type, | 199 | __u64 length, __u8 type, |
198 | struct cifsLockInfo **conf_lock, | 200 | struct cifsLockInfo **conf_lock, |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f82d2823622f..3f3185febc58 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) | |||
98 | struct list_head *tmp1; | 98 | struct list_head *tmp1; |
99 | 99 | ||
100 | /* list all files open on tree connection and mark them invalid */ | 100 | /* list all files open on tree connection and mark them invalid */ |
101 | spin_lock(&cifs_file_list_lock); | 101 | spin_lock(&tcon->open_file_lock); |
102 | list_for_each_safe(tmp, tmp1, &tcon->openFileList) { | 102 | list_for_each_safe(tmp, tmp1, &tcon->openFileList) { |
103 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); | 103 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); |
104 | open_file->invalidHandle = true; | 104 | open_file->invalidHandle = true; |
105 | open_file->oplock_break_cancelled = true; | 105 | open_file->oplock_break_cancelled = true; |
106 | } | 106 | } |
107 | spin_unlock(&cifs_file_list_lock); | 107 | spin_unlock(&tcon->open_file_lock); |
108 | /* | 108 | /* |
109 | * BB Add call to invalidate_inodes(sb) for all superblocks mounted | 109 | * BB Add call to invalidate_inodes(sb) for all superblocks mounted |
110 | * to this tcon. | 110 | * to this tcon. |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2e4f4bad8b1e..aab5227979e2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -63,7 +63,6 @@ extern mempool_t *cifs_req_poolp; | |||
63 | #define TLINK_IDLE_EXPIRE (600 * HZ) | 63 | #define TLINK_IDLE_EXPIRE (600 * HZ) |
64 | 64 | ||
65 | enum { | 65 | enum { |
66 | |||
67 | /* Mount options that take no arguments */ | 66 | /* Mount options that take no arguments */ |
68 | Opt_user_xattr, Opt_nouser_xattr, | 67 | Opt_user_xattr, Opt_nouser_xattr, |
69 | Opt_forceuid, Opt_noforceuid, | 68 | Opt_forceuid, Opt_noforceuid, |
@@ -76,7 +75,7 @@ enum { | |||
76 | Opt_noposixpaths, Opt_nounix, | 75 | Opt_noposixpaths, Opt_nounix, |
77 | Opt_nocase, | 76 | Opt_nocase, |
78 | Opt_brl, Opt_nobrl, | 77 | Opt_brl, Opt_nobrl, |
79 | Opt_forcemandatorylock, Opt_setuids, | 78 | Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids, |
80 | Opt_nosetuids, Opt_dynperm, Opt_nodynperm, | 79 | Opt_nosetuids, Opt_dynperm, Opt_nodynperm, |
81 | Opt_nohard, Opt_nosoft, | 80 | Opt_nohard, Opt_nosoft, |
82 | Opt_nointr, Opt_intr, | 81 | Opt_nointr, Opt_intr, |
@@ -95,7 +94,7 @@ enum { | |||
95 | Opt_cruid, Opt_gid, Opt_file_mode, | 94 | Opt_cruid, Opt_gid, Opt_file_mode, |
96 | Opt_dirmode, Opt_port, | 95 | Opt_dirmode, Opt_port, |
97 | Opt_rsize, Opt_wsize, Opt_actimeo, | 96 | Opt_rsize, Opt_wsize, Opt_actimeo, |
98 | Opt_echo_interval, | 97 | Opt_echo_interval, Opt_max_credits, |
99 | 98 | ||
100 | /* Mount options which take string value */ | 99 | /* Mount options which take string value */ |
101 | Opt_user, Opt_pass, Opt_ip, | 100 | Opt_user, Opt_pass, Opt_ip, |
@@ -148,6 +147,7 @@ static const match_table_t cifs_mount_option_tokens = { | |||
148 | { Opt_forcemandatorylock, "forcemand" }, | 147 | { Opt_forcemandatorylock, "forcemand" }, |
149 | { Opt_setuids, "setuids" }, | 148 | { Opt_setuids, "setuids" }, |
150 | { Opt_nosetuids, "nosetuids" }, | 149 | { Opt_nosetuids, "nosetuids" }, |
150 | { Opt_setuidfromacl, "idsfromsid" }, | ||
151 | { Opt_dynperm, "dynperm" }, | 151 | { Opt_dynperm, "dynperm" }, |
152 | { Opt_nodynperm, "nodynperm" }, | 152 | { Opt_nodynperm, "nodynperm" }, |
153 | { Opt_nohard, "nohard" }, | 153 | { Opt_nohard, "nohard" }, |
@@ -190,6 +190,7 @@ static const match_table_t cifs_mount_option_tokens = { | |||
190 | { Opt_wsize, "wsize=%s" }, | 190 | { Opt_wsize, "wsize=%s" }, |
191 | { Opt_actimeo, "actimeo=%s" }, | 191 | { Opt_actimeo, "actimeo=%s" }, |
192 | { Opt_echo_interval, "echo_interval=%s" }, | 192 | { Opt_echo_interval, "echo_interval=%s" }, |
193 | { Opt_max_credits, "max_credits=%s" }, | ||
193 | 194 | ||
194 | { Opt_blank_user, "user=" }, | 195 | { Opt_blank_user, "user=" }, |
195 | { Opt_blank_user, "username=" }, | 196 | { Opt_blank_user, "username=" }, |
@@ -1376,6 +1377,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, | |||
1376 | case Opt_nosetuids: | 1377 | case Opt_nosetuids: |
1377 | vol->setuids = 0; | 1378 | vol->setuids = 0; |
1378 | break; | 1379 | break; |
1380 | case Opt_setuidfromacl: | ||
1381 | vol->setuidfromacl = 1; | ||
1382 | break; | ||
1379 | case Opt_dynperm: | 1383 | case Opt_dynperm: |
1380 | vol->dynperm = true; | 1384 | vol->dynperm = true; |
1381 | break; | 1385 | break; |
@@ -1586,6 +1590,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, | |||
1586 | } | 1590 | } |
1587 | vol->echo_interval = option; | 1591 | vol->echo_interval = option; |
1588 | break; | 1592 | break; |
1593 | case Opt_max_credits: | ||
1594 | if (get_option_ul(args, &option) || (option < 20) || | ||
1595 | (option > 60000)) { | ||
1596 | cifs_dbg(VFS, "%s: Invalid max_credits value\n", | ||
1597 | __func__); | ||
1598 | goto cifs_parse_mount_err; | ||
1599 | } | ||
1600 | vol->max_credits = option; | ||
1601 | break; | ||
1589 | 1602 | ||
1590 | /* String Arguments */ | 1603 | /* String Arguments */ |
1591 | 1604 | ||
@@ -2163,7 +2176,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
2163 | memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, | 2176 | memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, |
2164 | sizeof(tcp_ses->dstaddr)); | 2177 | sizeof(tcp_ses->dstaddr)); |
2165 | #ifdef CONFIG_CIFS_SMB2 | 2178 | #ifdef CONFIG_CIFS_SMB2 |
2166 | get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE); | 2179 | generate_random_uuid(tcp_ses->client_guid); |
2167 | #endif | 2180 | #endif |
2168 | /* | 2181 | /* |
2169 | * at this point we are the only ones with the pointer | 2182 | * at this point we are the only ones with the pointer |
@@ -3270,6 +3283,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, | |||
3270 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | 3283 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; |
3271 | if (pvolume_info->setuids) | 3284 | if (pvolume_info->setuids) |
3272 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | 3285 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; |
3286 | if (pvolume_info->setuidfromacl) | ||
3287 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL; | ||
3273 | if (pvolume_info->server_ino) | 3288 | if (pvolume_info->server_ino) |
3274 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | 3289 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; |
3275 | if (pvolume_info->remap) | 3290 | if (pvolume_info->remap) |
@@ -3598,7 +3613,11 @@ try_mount_again: | |||
3598 | bdi_destroy(&cifs_sb->bdi); | 3613 | bdi_destroy(&cifs_sb->bdi); |
3599 | goto out; | 3614 | goto out; |
3600 | } | 3615 | } |
3601 | 3616 | if ((volume_info->max_credits < 20) || | |
3617 | (volume_info->max_credits > 60000)) | ||
3618 | server->max_credits = SMB2_MAX_CREDITS_AVAILABLE; | ||
3619 | else | ||
3620 | server->max_credits = volume_info->max_credits; | ||
3602 | /* get a reference to a SMB session */ | 3621 | /* get a reference to a SMB session */ |
3603 | ses = cifs_get_smb_ses(server, volume_info); | 3622 | ses = cifs_get_smb_ses(server, volume_info); |
3604 | if (IS_ERR(ses)) { | 3623 | if (IS_ERR(ses)) { |
@@ -3688,14 +3707,16 @@ remote_path_check: | |||
3688 | goto mount_fail_check; | 3707 | goto mount_fail_check; |
3689 | } | 3708 | } |
3690 | 3709 | ||
3691 | rc = cifs_are_all_path_components_accessible(server, | 3710 | if (rc != -EREMOTE) { |
3711 | rc = cifs_are_all_path_components_accessible(server, | ||
3692 | xid, tcon, cifs_sb, | 3712 | xid, tcon, cifs_sb, |
3693 | full_path); | 3713 | full_path); |
3694 | if (rc != 0) { | 3714 | if (rc != 0) { |
3695 | cifs_dbg(VFS, "cannot query dirs between root and final path, " | 3715 | cifs_dbg(VFS, "cannot query dirs between root and final path, " |
3696 | "enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); | 3716 | "enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); |
3697 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; | 3717 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; |
3698 | rc = 0; | 3718 | rc = 0; |
3719 | } | ||
3699 | } | 3720 | } |
3700 | kfree(full_path); | 3721 | kfree(full_path); |
3701 | } | 3722 | } |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a95fe8b1afe9..7f5f6176c6f1 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
305 | cfile->tlink = cifs_get_tlink(tlink); | 305 | cfile->tlink = cifs_get_tlink(tlink); |
306 | INIT_WORK(&cfile->oplock_break, cifs_oplock_break); | 306 | INIT_WORK(&cfile->oplock_break, cifs_oplock_break); |
307 | mutex_init(&cfile->fh_mutex); | 307 | mutex_init(&cfile->fh_mutex); |
308 | spin_lock_init(&cfile->file_info_lock); | ||
308 | 309 | ||
309 | cifs_sb_active(inode->i_sb); | 310 | cifs_sb_active(inode->i_sb); |
310 | 311 | ||
@@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
317 | oplock = 0; | 318 | oplock = 0; |
318 | } | 319 | } |
319 | 320 | ||
320 | spin_lock(&cifs_file_list_lock); | 321 | spin_lock(&tcon->open_file_lock); |
321 | if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) | 322 | if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) |
322 | oplock = fid->pending_open->oplock; | 323 | oplock = fid->pending_open->oplock; |
323 | list_del(&fid->pending_open->olist); | 324 | list_del(&fid->pending_open->olist); |
@@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
326 | server->ops->set_fid(cfile, fid, oplock); | 327 | server->ops->set_fid(cfile, fid, oplock); |
327 | 328 | ||
328 | list_add(&cfile->tlist, &tcon->openFileList); | 329 | list_add(&cfile->tlist, &tcon->openFileList); |
330 | |||
329 | /* if readable file instance put first in list*/ | 331 | /* if readable file instance put first in list*/ |
330 | if (file->f_mode & FMODE_READ) | 332 | if (file->f_mode & FMODE_READ) |
331 | list_add(&cfile->flist, &cinode->openFileList); | 333 | list_add(&cfile->flist, &cinode->openFileList); |
332 | else | 334 | else |
333 | list_add_tail(&cfile->flist, &cinode->openFileList); | 335 | list_add_tail(&cfile->flist, &cinode->openFileList); |
334 | spin_unlock(&cifs_file_list_lock); | 336 | spin_unlock(&tcon->open_file_lock); |
335 | 337 | ||
336 | if (fid->purge_cache) | 338 | if (fid->purge_cache) |
337 | cifs_zap_mapping(inode); | 339 | cifs_zap_mapping(inode); |
@@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
343 | struct cifsFileInfo * | 345 | struct cifsFileInfo * |
344 | cifsFileInfo_get(struct cifsFileInfo *cifs_file) | 346 | cifsFileInfo_get(struct cifsFileInfo *cifs_file) |
345 | { | 347 | { |
346 | spin_lock(&cifs_file_list_lock); | 348 | spin_lock(&cifs_file->file_info_lock); |
347 | cifsFileInfo_get_locked(cifs_file); | 349 | cifsFileInfo_get_locked(cifs_file); |
348 | spin_unlock(&cifs_file_list_lock); | 350 | spin_unlock(&cifs_file->file_info_lock); |
349 | return cifs_file; | 351 | return cifs_file; |
350 | } | 352 | } |
351 | 353 | ||
352 | /* | 354 | /* |
353 | * Release a reference on the file private data. This may involve closing | 355 | * Release a reference on the file private data. This may involve closing |
354 | * the filehandle out on the server. Must be called without holding | 356 | * the filehandle out on the server. Must be called without holding |
355 | * cifs_file_list_lock. | 357 | * tcon->open_file_lock and cifs_file->file_info_lock. |
356 | */ | 358 | */ |
357 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | 359 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file) |
358 | { | 360 | { |
@@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | |||
367 | struct cifs_pending_open open; | 369 | struct cifs_pending_open open; |
368 | bool oplock_break_cancelled; | 370 | bool oplock_break_cancelled; |
369 | 371 | ||
370 | spin_lock(&cifs_file_list_lock); | 372 | spin_lock(&tcon->open_file_lock); |
373 | |||
374 | spin_lock(&cifs_file->file_info_lock); | ||
371 | if (--cifs_file->count > 0) { | 375 | if (--cifs_file->count > 0) { |
372 | spin_unlock(&cifs_file_list_lock); | 376 | spin_unlock(&cifs_file->file_info_lock); |
377 | spin_unlock(&tcon->open_file_lock); | ||
373 | return; | 378 | return; |
374 | } | 379 | } |
380 | spin_unlock(&cifs_file->file_info_lock); | ||
375 | 381 | ||
376 | if (server->ops->get_lease_key) | 382 | if (server->ops->get_lease_key) |
377 | server->ops->get_lease_key(inode, &fid); | 383 | server->ops->get_lease_key(inode, &fid); |
@@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | |||
395 | set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); | 401 | set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); |
396 | cifs_set_oplock_level(cifsi, 0); | 402 | cifs_set_oplock_level(cifsi, 0); |
397 | } | 403 | } |
398 | spin_unlock(&cifs_file_list_lock); | 404 | |
405 | spin_unlock(&tcon->open_file_lock); | ||
399 | 406 | ||
400 | oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); | 407 | oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); |
401 | 408 | ||
@@ -732,6 +739,15 @@ reopen_success: | |||
732 | * to the server to get the new inode info. | 739 | * to the server to get the new inode info. |
733 | */ | 740 | */ |
734 | 741 | ||
742 | /* | ||
743 | * If the server returned a read oplock and we have mandatory brlocks, | ||
744 | * set oplock level to None. | ||
745 | */ | ||
746 | if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { | ||
747 | cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); | ||
748 | oplock = 0; | ||
749 | } | ||
750 | |||
735 | server->ops->set_fid(cfile, &cfile->fid, oplock); | 751 | server->ops->set_fid(cfile, &cfile->fid, oplock); |
736 | if (oparms.reconnect) | 752 | if (oparms.reconnect) |
737 | cifs_relock_file(cfile); | 753 | cifs_relock_file(cfile); |
@@ -753,6 +769,36 @@ int cifs_close(struct inode *inode, struct file *file) | |||
753 | return 0; | 769 | return 0; |
754 | } | 770 | } |
755 | 771 | ||
772 | void | ||
773 | cifs_reopen_persistent_handles(struct cifs_tcon *tcon) | ||
774 | { | ||
775 | struct cifsFileInfo *open_file; | ||
776 | struct list_head *tmp; | ||
777 | struct list_head *tmp1; | ||
778 | struct list_head tmp_list; | ||
779 | |||
780 | cifs_dbg(FYI, "Reopen persistent handles"); | ||
781 | INIT_LIST_HEAD(&tmp_list); | ||
782 | |||
783 | /* list all files open on tree connection, reopen resilient handles */ | ||
784 | spin_lock(&tcon->open_file_lock); | ||
785 | list_for_each(tmp, &tcon->openFileList) { | ||
786 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); | ||
787 | if (!open_file->invalidHandle) | ||
788 | continue; | ||
789 | cifsFileInfo_get(open_file); | ||
790 | list_add_tail(&open_file->rlist, &tmp_list); | ||
791 | } | ||
792 | spin_unlock(&tcon->open_file_lock); | ||
793 | |||
794 | list_for_each_safe(tmp, tmp1, &tmp_list) { | ||
795 | open_file = list_entry(tmp, struct cifsFileInfo, rlist); | ||
796 | cifs_reopen_file(open_file, false /* do not flush */); | ||
797 | list_del_init(&open_file->rlist); | ||
798 | cifsFileInfo_put(open_file); | ||
799 | } | ||
800 | } | ||
801 | |||
756 | int cifs_closedir(struct inode *inode, struct file *file) | 802 | int cifs_closedir(struct inode *inode, struct file *file) |
757 | { | 803 | { |
758 | int rc = 0; | 804 | int rc = 0; |
@@ -772,10 +818,10 @@ int cifs_closedir(struct inode *inode, struct file *file) | |||
772 | server = tcon->ses->server; | 818 | server = tcon->ses->server; |
773 | 819 | ||
774 | cifs_dbg(FYI, "Freeing private data in close dir\n"); | 820 | cifs_dbg(FYI, "Freeing private data in close dir\n"); |
775 | spin_lock(&cifs_file_list_lock); | 821 | spin_lock(&cfile->file_info_lock); |
776 | if (server->ops->dir_needs_close(cfile)) { | 822 | if (server->ops->dir_needs_close(cfile)) { |
777 | cfile->invalidHandle = true; | 823 | cfile->invalidHandle = true; |
778 | spin_unlock(&cifs_file_list_lock); | 824 | spin_unlock(&cfile->file_info_lock); |
779 | if (server->ops->close_dir) | 825 | if (server->ops->close_dir) |
780 | rc = server->ops->close_dir(xid, tcon, &cfile->fid); | 826 | rc = server->ops->close_dir(xid, tcon, &cfile->fid); |
781 | else | 827 | else |
@@ -784,7 +830,7 @@ int cifs_closedir(struct inode *inode, struct file *file) | |||
784 | /* not much we can do if it fails anyway, ignore rc */ | 830 | /* not much we can do if it fails anyway, ignore rc */ |
785 | rc = 0; | 831 | rc = 0; |
786 | } else | 832 | } else |
787 | spin_unlock(&cifs_file_list_lock); | 833 | spin_unlock(&cfile->file_info_lock); |
788 | 834 | ||
789 | buf = cfile->srch_inf.ntwrk_buf_start; | 835 | buf = cfile->srch_inf.ntwrk_buf_start; |
790 | if (buf) { | 836 | if (buf) { |
@@ -1728,12 +1774,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, | |||
1728 | { | 1774 | { |
1729 | struct cifsFileInfo *open_file = NULL; | 1775 | struct cifsFileInfo *open_file = NULL; |
1730 | struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); | 1776 | struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); |
1777 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | ||
1731 | 1778 | ||
1732 | /* only filter by fsuid on multiuser mounts */ | 1779 | /* only filter by fsuid on multiuser mounts */ |
1733 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | 1780 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) |
1734 | fsuid_only = false; | 1781 | fsuid_only = false; |
1735 | 1782 | ||
1736 | spin_lock(&cifs_file_list_lock); | 1783 | spin_lock(&tcon->open_file_lock); |
1737 | /* we could simply get the first_list_entry since write-only entries | 1784 | /* we could simply get the first_list_entry since write-only entries |
1738 | are always at the end of the list but since the first entry might | 1785 | are always at the end of the list but since the first entry might |
1739 | have a close pending, we go through the whole list */ | 1786 | have a close pending, we go through the whole list */ |
@@ -1744,8 +1791,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, | |||
1744 | if (!open_file->invalidHandle) { | 1791 | if (!open_file->invalidHandle) { |
1745 | /* found a good file */ | 1792 | /* found a good file */ |
1746 | /* lock it so it will not be closed on us */ | 1793 | /* lock it so it will not be closed on us */ |
1747 | cifsFileInfo_get_locked(open_file); | 1794 | cifsFileInfo_get(open_file); |
1748 | spin_unlock(&cifs_file_list_lock); | 1795 | spin_unlock(&tcon->open_file_lock); |
1749 | return open_file; | 1796 | return open_file; |
1750 | } /* else might as well continue, and look for | 1797 | } /* else might as well continue, and look for |
1751 | another, or simply have the caller reopen it | 1798 | another, or simply have the caller reopen it |
@@ -1753,7 +1800,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, | |||
1753 | } else /* write only file */ | 1800 | } else /* write only file */ |
1754 | break; /* write only files are last so must be done */ | 1801 | break; /* write only files are last so must be done */ |
1755 | } | 1802 | } |
1756 | spin_unlock(&cifs_file_list_lock); | 1803 | spin_unlock(&tcon->open_file_lock); |
1757 | return NULL; | 1804 | return NULL; |
1758 | } | 1805 | } |
1759 | 1806 | ||
@@ -1762,6 +1809,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, | |||
1762 | { | 1809 | { |
1763 | struct cifsFileInfo *open_file, *inv_file = NULL; | 1810 | struct cifsFileInfo *open_file, *inv_file = NULL; |
1764 | struct cifs_sb_info *cifs_sb; | 1811 | struct cifs_sb_info *cifs_sb; |
1812 | struct cifs_tcon *tcon; | ||
1765 | bool any_available = false; | 1813 | bool any_available = false; |
1766 | int rc; | 1814 | int rc; |
1767 | unsigned int refind = 0; | 1815 | unsigned int refind = 0; |
@@ -1777,15 +1825,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, | |||
1777 | } | 1825 | } |
1778 | 1826 | ||
1779 | cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); | 1827 | cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); |
1828 | tcon = cifs_sb_master_tcon(cifs_sb); | ||
1780 | 1829 | ||
1781 | /* only filter by fsuid on multiuser mounts */ | 1830 | /* only filter by fsuid on multiuser mounts */ |
1782 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | 1831 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) |
1783 | fsuid_only = false; | 1832 | fsuid_only = false; |
1784 | 1833 | ||
1785 | spin_lock(&cifs_file_list_lock); | 1834 | spin_lock(&tcon->open_file_lock); |
1786 | refind_writable: | 1835 | refind_writable: |
1787 | if (refind > MAX_REOPEN_ATT) { | 1836 | if (refind > MAX_REOPEN_ATT) { |
1788 | spin_unlock(&cifs_file_list_lock); | 1837 | spin_unlock(&tcon->open_file_lock); |
1789 | return NULL; | 1838 | return NULL; |
1790 | } | 1839 | } |
1791 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { | 1840 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { |
@@ -1796,8 +1845,8 @@ refind_writable: | |||
1796 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { | 1845 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { |
1797 | if (!open_file->invalidHandle) { | 1846 | if (!open_file->invalidHandle) { |
1798 | /* found a good writable file */ | 1847 | /* found a good writable file */ |
1799 | cifsFileInfo_get_locked(open_file); | 1848 | cifsFileInfo_get(open_file); |
1800 | spin_unlock(&cifs_file_list_lock); | 1849 | spin_unlock(&tcon->open_file_lock); |
1801 | return open_file; | 1850 | return open_file; |
1802 | } else { | 1851 | } else { |
1803 | if (!inv_file) | 1852 | if (!inv_file) |
@@ -1813,24 +1862,24 @@ refind_writable: | |||
1813 | 1862 | ||
1814 | if (inv_file) { | 1863 | if (inv_file) { |
1815 | any_available = false; | 1864 | any_available = false; |
1816 | cifsFileInfo_get_locked(inv_file); | 1865 | cifsFileInfo_get(inv_file); |
1817 | } | 1866 | } |
1818 | 1867 | ||
1819 | spin_unlock(&cifs_file_list_lock); | 1868 | spin_unlock(&tcon->open_file_lock); |
1820 | 1869 | ||
1821 | if (inv_file) { | 1870 | if (inv_file) { |
1822 | rc = cifs_reopen_file(inv_file, false); | 1871 | rc = cifs_reopen_file(inv_file, false); |
1823 | if (!rc) | 1872 | if (!rc) |
1824 | return inv_file; | 1873 | return inv_file; |
1825 | else { | 1874 | else { |
1826 | spin_lock(&cifs_file_list_lock); | 1875 | spin_lock(&tcon->open_file_lock); |
1827 | list_move_tail(&inv_file->flist, | 1876 | list_move_tail(&inv_file->flist, |
1828 | &cifs_inode->openFileList); | 1877 | &cifs_inode->openFileList); |
1829 | spin_unlock(&cifs_file_list_lock); | 1878 | spin_unlock(&tcon->open_file_lock); |
1830 | cifsFileInfo_put(inv_file); | 1879 | cifsFileInfo_put(inv_file); |
1831 | spin_lock(&cifs_file_list_lock); | ||
1832 | ++refind; | 1880 | ++refind; |
1833 | inv_file = NULL; | 1881 | inv_file = NULL; |
1882 | spin_lock(&tcon->open_file_lock); | ||
1834 | goto refind_writable; | 1883 | goto refind_writable; |
1835 | } | 1884 | } |
1836 | } | 1885 | } |
@@ -3612,15 +3661,17 @@ static int cifs_readpage(struct file *file, struct page *page) | |||
3612 | static int is_inode_writable(struct cifsInodeInfo *cifs_inode) | 3661 | static int is_inode_writable(struct cifsInodeInfo *cifs_inode) |
3613 | { | 3662 | { |
3614 | struct cifsFileInfo *open_file; | 3663 | struct cifsFileInfo *open_file; |
3664 | struct cifs_tcon *tcon = | ||
3665 | cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb)); | ||
3615 | 3666 | ||
3616 | spin_lock(&cifs_file_list_lock); | 3667 | spin_lock(&tcon->open_file_lock); |
3617 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { | 3668 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { |
3618 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { | 3669 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { |
3619 | spin_unlock(&cifs_file_list_lock); | 3670 | spin_unlock(&tcon->open_file_lock); |
3620 | return 1; | 3671 | return 1; |
3621 | } | 3672 | } |
3622 | } | 3673 | } |
3623 | spin_unlock(&cifs_file_list_lock); | 3674 | spin_unlock(&tcon->open_file_lock); |
3624 | return 0; | 3675 | return 0; |
3625 | } | 3676 | } |
3626 | 3677 | ||
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 7a3b84e300f8..9f51b81119f2 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c | |||
@@ -189,7 +189,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) | |||
189 | xid = get_xid(); | 189 | xid = get_xid(); |
190 | 190 | ||
191 | cifs_sb = CIFS_SB(inode->i_sb); | 191 | cifs_sb = CIFS_SB(inode->i_sb); |
192 | 192 | cifs_dbg(VFS, "cifs ioctl 0x%x\n", command); | |
193 | switch (command) { | 193 | switch (command) { |
194 | case FS_IOC_GETFLAGS: | 194 | case FS_IOC_GETFLAGS: |
195 | if (pSMBFile == NULL) | 195 | if (pSMBFile == NULL) |
@@ -267,11 +267,23 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) | |||
267 | tcon = tlink_tcon(pSMBFile->tlink); | 267 | tcon = tlink_tcon(pSMBFile->tlink); |
268 | rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); | 268 | rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); |
269 | break; | 269 | break; |
270 | case CIFS_ENUMERATE_SNAPSHOTS: | ||
271 | if (arg == 0) { | ||
272 | rc = -EINVAL; | ||
273 | goto cifs_ioc_exit; | ||
274 | } | ||
275 | tcon = tlink_tcon(pSMBFile->tlink); | ||
276 | if (tcon->ses->server->ops->enum_snapshots) | ||
277 | rc = tcon->ses->server->ops->enum_snapshots(xid, tcon, | ||
278 | pSMBFile, (void __user *)arg); | ||
279 | else | ||
280 | rc = -EOPNOTSUPP; | ||
281 | break; | ||
270 | default: | 282 | default: |
271 | cifs_dbg(FYI, "unsupported ioctl\n"); | 283 | cifs_dbg(FYI, "unsupported ioctl\n"); |
272 | break; | 284 | break; |
273 | } | 285 | } |
274 | 286 | cifs_ioc_exit: | |
275 | free_xid(xid); | 287 | free_xid(xid); |
276 | return rc; | 288 | return rc; |
277 | } | 289 | } |
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 813fe13c2ae1..c6729156f9a0 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -120,6 +120,7 @@ tconInfoAlloc(void) | |||
120 | ++ret_buf->tc_count; | 120 | ++ret_buf->tc_count; |
121 | INIT_LIST_HEAD(&ret_buf->openFileList); | 121 | INIT_LIST_HEAD(&ret_buf->openFileList); |
122 | INIT_LIST_HEAD(&ret_buf->tcon_list); | 122 | INIT_LIST_HEAD(&ret_buf->tcon_list); |
123 | spin_lock_init(&ret_buf->open_file_lock); | ||
123 | #ifdef CONFIG_CIFS_STATS | 124 | #ifdef CONFIG_CIFS_STATS |
124 | spin_lock_init(&ret_buf->stat_lock); | 125 | spin_lock_init(&ret_buf->stat_lock); |
125 | #endif | 126 | #endif |
@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
465 | continue; | 466 | continue; |
466 | 467 | ||
467 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 468 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); |
468 | spin_lock(&cifs_file_list_lock); | 469 | spin_lock(&tcon->open_file_lock); |
469 | list_for_each(tmp2, &tcon->openFileList) { | 470 | list_for_each(tmp2, &tcon->openFileList) { |
470 | netfile = list_entry(tmp2, struct cifsFileInfo, | 471 | netfile = list_entry(tmp2, struct cifsFileInfo, |
471 | tlist); | 472 | tlist); |
@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
495 | &netfile->oplock_break); | 496 | &netfile->oplock_break); |
496 | netfile->oplock_break_cancelled = false; | 497 | netfile->oplock_break_cancelled = false; |
497 | 498 | ||
498 | spin_unlock(&cifs_file_list_lock); | 499 | spin_unlock(&tcon->open_file_lock); |
499 | spin_unlock(&cifs_tcp_ses_lock); | 500 | spin_unlock(&cifs_tcp_ses_lock); |
500 | return true; | 501 | return true; |
501 | } | 502 | } |
502 | spin_unlock(&cifs_file_list_lock); | 503 | spin_unlock(&tcon->open_file_lock); |
503 | spin_unlock(&cifs_tcp_ses_lock); | 504 | spin_unlock(&cifs_tcp_ses_lock); |
504 | cifs_dbg(FYI, "No matching file for oplock break\n"); | 505 | cifs_dbg(FYI, "No matching file for oplock break\n"); |
505 | return true; | 506 | return true; |
@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb) | |||
613 | void | 614 | void |
614 | cifs_del_pending_open(struct cifs_pending_open *open) | 615 | cifs_del_pending_open(struct cifs_pending_open *open) |
615 | { | 616 | { |
616 | spin_lock(&cifs_file_list_lock); | 617 | spin_lock(&tlink_tcon(open->tlink)->open_file_lock); |
617 | list_del(&open->olist); | 618 | list_del(&open->olist); |
618 | spin_unlock(&cifs_file_list_lock); | 619 | spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); |
619 | } | 620 | } |
620 | 621 | ||
621 | void | 622 | void |
@@ -635,7 +636,7 @@ void | |||
635 | cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, | 636 | cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, |
636 | struct cifs_pending_open *open) | 637 | struct cifs_pending_open *open) |
637 | { | 638 | { |
638 | spin_lock(&cifs_file_list_lock); | 639 | spin_lock(&tlink_tcon(tlink)->open_file_lock); |
639 | cifs_add_pending_open_locked(fid, tlink, open); | 640 | cifs_add_pending_open_locked(fid, tlink, open); |
640 | spin_unlock(&cifs_file_list_lock); | 641 | spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); |
641 | } | 642 | } |
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 65cf85dcda09..8f6a2a5863b9 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
@@ -597,14 +597,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, | |||
597 | is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { | 597 | is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { |
598 | /* close and restart search */ | 598 | /* close and restart search */ |
599 | cifs_dbg(FYI, "search backing up - close and restart search\n"); | 599 | cifs_dbg(FYI, "search backing up - close and restart search\n"); |
600 | spin_lock(&cifs_file_list_lock); | 600 | spin_lock(&cfile->file_info_lock); |
601 | if (server->ops->dir_needs_close(cfile)) { | 601 | if (server->ops->dir_needs_close(cfile)) { |
602 | cfile->invalidHandle = true; | 602 | cfile->invalidHandle = true; |
603 | spin_unlock(&cifs_file_list_lock); | 603 | spin_unlock(&cfile->file_info_lock); |
604 | if (server->ops->close_dir) | 604 | if (server->ops->close_dir) |
605 | server->ops->close_dir(xid, tcon, &cfile->fid); | 605 | server->ops->close_dir(xid, tcon, &cfile->fid); |
606 | } else | 606 | } else |
607 | spin_unlock(&cifs_file_list_lock); | 607 | spin_unlock(&cfile->file_info_lock); |
608 | if (cfile->srch_inf.ntwrk_buf_start) { | 608 | if (cfile->srch_inf.ntwrk_buf_start) { |
609 | cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); | 609 | cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); |
610 | if (cfile->srch_inf.smallBuf) | 610 | if (cfile->srch_inf.smallBuf) |
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 4f0231e685a9..1238cd3552f9 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c | |||
@@ -266,9 +266,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path, | |||
266 | struct tcon_link *tlink; | 266 | struct tcon_link *tlink; |
267 | int rc; | 267 | int rc; |
268 | 268 | ||
269 | if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && | ||
270 | (buf->LastWriteTime == 0) && (buf->ChangeTime) && | ||
271 | (buf->Attributes == 0)) | ||
272 | return 0; /* would be a no op, no sense sending this */ | ||
273 | |||
269 | tlink = cifs_sb_tlink(cifs_sb); | 274 | tlink = cifs_sb_tlink(cifs_sb); |
270 | if (IS_ERR(tlink)) | 275 | if (IS_ERR(tlink)) |
271 | return PTR_ERR(tlink); | 276 | return PTR_ERR(tlink); |
277 | |||
272 | rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path, | 278 | rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path, |
273 | FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, | 279 | FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, |
274 | SMB2_OP_SET_INFO); | 280 | SMB2_OP_SET_INFO); |
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 389fb9f8c84e..3d383489b9cf 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
@@ -549,19 +549,19 @@ smb2_is_valid_lease_break(char *buffer) | |||
549 | list_for_each(tmp1, &server->smb_ses_list) { | 549 | list_for_each(tmp1, &server->smb_ses_list) { |
550 | ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); | 550 | ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); |
551 | 551 | ||
552 | spin_lock(&cifs_file_list_lock); | ||
553 | list_for_each(tmp2, &ses->tcon_list) { | 552 | list_for_each(tmp2, &ses->tcon_list) { |
554 | tcon = list_entry(tmp2, struct cifs_tcon, | 553 | tcon = list_entry(tmp2, struct cifs_tcon, |
555 | tcon_list); | 554 | tcon_list); |
555 | spin_lock(&tcon->open_file_lock); | ||
556 | cifs_stats_inc( | 556 | cifs_stats_inc( |
557 | &tcon->stats.cifs_stats.num_oplock_brks); | 557 | &tcon->stats.cifs_stats.num_oplock_brks); |
558 | if (smb2_tcon_has_lease(tcon, rsp, lw)) { | 558 | if (smb2_tcon_has_lease(tcon, rsp, lw)) { |
559 | spin_unlock(&cifs_file_list_lock); | 559 | spin_unlock(&tcon->open_file_lock); |
560 | spin_unlock(&cifs_tcp_ses_lock); | 560 | spin_unlock(&cifs_tcp_ses_lock); |
561 | return true; | 561 | return true; |
562 | } | 562 | } |
563 | spin_unlock(&tcon->open_file_lock); | ||
563 | } | 564 | } |
564 | spin_unlock(&cifs_file_list_lock); | ||
565 | } | 565 | } |
566 | } | 566 | } |
567 | spin_unlock(&cifs_tcp_ses_lock); | 567 | spin_unlock(&cifs_tcp_ses_lock); |
@@ -603,7 +603,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
603 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); | 603 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); |
604 | 604 | ||
605 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 605 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); |
606 | spin_lock(&cifs_file_list_lock); | 606 | spin_lock(&tcon->open_file_lock); |
607 | list_for_each(tmp2, &tcon->openFileList) { | 607 | list_for_each(tmp2, &tcon->openFileList) { |
608 | cfile = list_entry(tmp2, struct cifsFileInfo, | 608 | cfile = list_entry(tmp2, struct cifsFileInfo, |
609 | tlist); | 609 | tlist); |
@@ -615,7 +615,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
615 | 615 | ||
616 | cifs_dbg(FYI, "file id match, oplock break\n"); | 616 | cifs_dbg(FYI, "file id match, oplock break\n"); |
617 | cinode = CIFS_I(d_inode(cfile->dentry)); | 617 | cinode = CIFS_I(d_inode(cfile->dentry)); |
618 | 618 | spin_lock(&cfile->file_info_lock); | |
619 | if (!CIFS_CACHE_WRITE(cinode) && | 619 | if (!CIFS_CACHE_WRITE(cinode) && |
620 | rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) | 620 | rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) |
621 | cfile->oplock_break_cancelled = true; | 621 | cfile->oplock_break_cancelled = true; |
@@ -637,14 +637,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
637 | clear_bit( | 637 | clear_bit( |
638 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, | 638 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, |
639 | &cinode->flags); | 639 | &cinode->flags); |
640 | 640 | spin_unlock(&cfile->file_info_lock); | |
641 | queue_work(cifsiod_wq, &cfile->oplock_break); | 641 | queue_work(cifsiod_wq, &cfile->oplock_break); |
642 | 642 | ||
643 | spin_unlock(&cifs_file_list_lock); | 643 | spin_unlock(&tcon->open_file_lock); |
644 | spin_unlock(&cifs_tcp_ses_lock); | 644 | spin_unlock(&cifs_tcp_ses_lock); |
645 | return true; | 645 | return true; |
646 | } | 646 | } |
647 | spin_unlock(&cifs_file_list_lock); | 647 | spin_unlock(&tcon->open_file_lock); |
648 | spin_unlock(&cifs_tcp_ses_lock); | 648 | spin_unlock(&cifs_tcp_ses_lock); |
649 | cifs_dbg(FYI, "No matching file for oplock break\n"); | 649 | cifs_dbg(FYI, "No matching file for oplock break\n"); |
650 | return true; | 650 | return true; |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index d203c0329626..5d456ebb3813 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "cifs_unicode.h" | 28 | #include "cifs_unicode.h" |
29 | #include "smb2status.h" | 29 | #include "smb2status.h" |
30 | #include "smb2glob.h" | 30 | #include "smb2glob.h" |
31 | #include "cifs_ioctl.h" | ||
31 | 32 | ||
32 | static int | 33 | static int |
33 | change_conf(struct TCP_Server_Info *server) | 34 | change_conf(struct TCP_Server_Info *server) |
@@ -70,6 +71,10 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, | |||
70 | spin_lock(&server->req_lock); | 71 | spin_lock(&server->req_lock); |
71 | val = server->ops->get_credits_field(server, optype); | 72 | val = server->ops->get_credits_field(server, optype); |
72 | *val += add; | 73 | *val += add; |
74 | if (*val > 65000) { | ||
75 | *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ | ||
76 | printk_once(KERN_WARNING "server overflowed SMB3 credits\n"); | ||
77 | } | ||
73 | server->in_flight--; | 78 | server->in_flight--; |
74 | if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP) | 79 | if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP) |
75 | rc = change_conf(server); | 80 | rc = change_conf(server); |
@@ -287,7 +292,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) | |||
287 | cifs_dbg(FYI, "Link Speed %lld\n", | 292 | cifs_dbg(FYI, "Link Speed %lld\n", |
288 | le64_to_cpu(out_buf->LinkSpeed)); | 293 | le64_to_cpu(out_buf->LinkSpeed)); |
289 | } | 294 | } |
290 | 295 | kfree(out_buf); | |
291 | return rc; | 296 | return rc; |
292 | } | 297 | } |
293 | #endif /* STATS2 */ | 298 | #endif /* STATS2 */ |
@@ -541,6 +546,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) | |||
541 | server->ops->set_oplock_level(cinode, oplock, fid->epoch, | 546 | server->ops->set_oplock_level(cinode, oplock, fid->epoch, |
542 | &fid->purge_cache); | 547 | &fid->purge_cache); |
543 | cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); | 548 | cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); |
549 | memcpy(cfile->fid.create_guid, fid->create_guid, 16); | ||
544 | } | 550 | } |
545 | 551 | ||
546 | static void | 552 | static void |
@@ -699,6 +705,7 @@ smb2_clone_range(const unsigned int xid, | |||
699 | 705 | ||
700 | cchunk_out: | 706 | cchunk_out: |
701 | kfree(pcchunk); | 707 | kfree(pcchunk); |
708 | kfree(retbuf); | ||
702 | return rc; | 709 | return rc; |
703 | } | 710 | } |
704 | 711 | ||
@@ -823,7 +830,6 @@ smb2_duplicate_extents(const unsigned int xid, | |||
823 | { | 830 | { |
824 | int rc; | 831 | int rc; |
825 | unsigned int ret_data_len; | 832 | unsigned int ret_data_len; |
826 | char *retbuf = NULL; | ||
827 | struct duplicate_extents_to_file dup_ext_buf; | 833 | struct duplicate_extents_to_file dup_ext_buf; |
828 | struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); | 834 | struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); |
829 | 835 | ||
@@ -849,7 +855,7 @@ smb2_duplicate_extents(const unsigned int xid, | |||
849 | FSCTL_DUPLICATE_EXTENTS_TO_FILE, | 855 | FSCTL_DUPLICATE_EXTENTS_TO_FILE, |
850 | true /* is_fsctl */, (char *)&dup_ext_buf, | 856 | true /* is_fsctl */, (char *)&dup_ext_buf, |
851 | sizeof(struct duplicate_extents_to_file), | 857 | sizeof(struct duplicate_extents_to_file), |
852 | (char **)&retbuf, | 858 | NULL, |
853 | &ret_data_len); | 859 | &ret_data_len); |
854 | 860 | ||
855 | if (ret_data_len > 0) | 861 | if (ret_data_len > 0) |
@@ -872,7 +878,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, | |||
872 | struct cifsFileInfo *cfile) | 878 | struct cifsFileInfo *cfile) |
873 | { | 879 | { |
874 | struct fsctl_set_integrity_information_req integr_info; | 880 | struct fsctl_set_integrity_information_req integr_info; |
875 | char *retbuf = NULL; | ||
876 | unsigned int ret_data_len; | 881 | unsigned int ret_data_len; |
877 | 882 | ||
878 | integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); | 883 | integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); |
@@ -884,9 +889,53 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, | |||
884 | FSCTL_SET_INTEGRITY_INFORMATION, | 889 | FSCTL_SET_INTEGRITY_INFORMATION, |
885 | true /* is_fsctl */, (char *)&integr_info, | 890 | true /* is_fsctl */, (char *)&integr_info, |
886 | sizeof(struct fsctl_set_integrity_information_req), | 891 | sizeof(struct fsctl_set_integrity_information_req), |
892 | NULL, | ||
893 | &ret_data_len); | ||
894 | |||
895 | } | ||
896 | |||
897 | static int | ||
898 | smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, | ||
899 | struct cifsFileInfo *cfile, void __user *ioc_buf) | ||
900 | { | ||
901 | char *retbuf = NULL; | ||
902 | unsigned int ret_data_len = 0; | ||
903 | int rc; | ||
904 | struct smb_snapshot_array snapshot_in; | ||
905 | |||
906 | rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, | ||
907 | cfile->fid.volatile_fid, | ||
908 | FSCTL_SRV_ENUMERATE_SNAPSHOTS, | ||
909 | true /* is_fsctl */, NULL, 0 /* no input data */, | ||
887 | (char **)&retbuf, | 910 | (char **)&retbuf, |
888 | &ret_data_len); | 911 | &ret_data_len); |
912 | cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", | ||
913 | rc, ret_data_len); | ||
914 | if (rc) | ||
915 | return rc; | ||
889 | 916 | ||
917 | if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { | ||
918 | /* Fixup buffer */ | ||
919 | if (copy_from_user(&snapshot_in, ioc_buf, | ||
920 | sizeof(struct smb_snapshot_array))) { | ||
921 | rc = -EFAULT; | ||
922 | kfree(retbuf); | ||
923 | return rc; | ||
924 | } | ||
925 | if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) { | ||
926 | rc = -ERANGE; | ||
927 | return rc; | ||
928 | } | ||
929 | |||
930 | if (ret_data_len > snapshot_in.snapshot_array_size) | ||
931 | ret_data_len = snapshot_in.snapshot_array_size; | ||
932 | |||
933 | if (copy_to_user(ioc_buf, retbuf, ret_data_len)) | ||
934 | rc = -EFAULT; | ||
935 | } | ||
936 | |||
937 | kfree(retbuf); | ||
938 | return rc; | ||
890 | } | 939 | } |
891 | 940 | ||
892 | static int | 941 | static int |
@@ -1041,7 +1090,7 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) | |||
1041 | static void | 1090 | static void |
1042 | smb2_new_lease_key(struct cifs_fid *fid) | 1091 | smb2_new_lease_key(struct cifs_fid *fid) |
1043 | { | 1092 | { |
1044 | get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); | 1093 | generate_random_uuid(fid->lease_key); |
1045 | } | 1094 | } |
1046 | 1095 | ||
1047 | #define SMB2_SYMLINK_STRUCT_SIZE \ | 1096 | #define SMB2_SYMLINK_STRUCT_SIZE \ |
@@ -1654,6 +1703,7 @@ struct smb_version_operations smb21_operations = { | |||
1654 | .clone_range = smb2_clone_range, | 1703 | .clone_range = smb2_clone_range, |
1655 | .wp_retry_size = smb2_wp_retry_size, | 1704 | .wp_retry_size = smb2_wp_retry_size, |
1656 | .dir_needs_close = smb2_dir_needs_close, | 1705 | .dir_needs_close = smb2_dir_needs_close, |
1706 | .enum_snapshots = smb3_enum_snapshots, | ||
1657 | }; | 1707 | }; |
1658 | 1708 | ||
1659 | struct smb_version_operations smb30_operations = { | 1709 | struct smb_version_operations smb30_operations = { |
@@ -1740,6 +1790,7 @@ struct smb_version_operations smb30_operations = { | |||
1740 | .wp_retry_size = smb2_wp_retry_size, | 1790 | .wp_retry_size = smb2_wp_retry_size, |
1741 | .dir_needs_close = smb2_dir_needs_close, | 1791 | .dir_needs_close = smb2_dir_needs_close, |
1742 | .fallocate = smb3_fallocate, | 1792 | .fallocate = smb3_fallocate, |
1793 | .enum_snapshots = smb3_enum_snapshots, | ||
1743 | }; | 1794 | }; |
1744 | 1795 | ||
1745 | #ifdef CONFIG_CIFS_SMB311 | 1796 | #ifdef CONFIG_CIFS_SMB311 |
@@ -1827,6 +1878,7 @@ struct smb_version_operations smb311_operations = { | |||
1827 | .wp_retry_size = smb2_wp_retry_size, | 1878 | .wp_retry_size = smb2_wp_retry_size, |
1828 | .dir_needs_close = smb2_dir_needs_close, | 1879 | .dir_needs_close = smb2_dir_needs_close, |
1829 | .fallocate = smb3_fallocate, | 1880 | .fallocate = smb3_fallocate, |
1881 | .enum_snapshots = smb3_enum_snapshots, | ||
1830 | }; | 1882 | }; |
1831 | #endif /* CIFS_SMB311 */ | 1883 | #endif /* CIFS_SMB311 */ |
1832 | 1884 | ||
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 29e06db5f187..5ca5ea4668a1 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -100,7 +100,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , | |||
100 | hdr->ProtocolId = SMB2_PROTO_NUMBER; | 100 | hdr->ProtocolId = SMB2_PROTO_NUMBER; |
101 | hdr->StructureSize = cpu_to_le16(64); | 101 | hdr->StructureSize = cpu_to_le16(64); |
102 | hdr->Command = smb2_cmd; | 102 | hdr->Command = smb2_cmd; |
103 | hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */ | 103 | if (tcon && tcon->ses && tcon->ses->server) { |
104 | struct TCP_Server_Info *server = tcon->ses->server; | ||
105 | |||
106 | spin_lock(&server->req_lock); | ||
107 | /* Request up to 2 credits but don't go over the limit. */ | ||
108 | if (server->credits >= server->max_credits) | ||
109 | hdr->CreditRequest = cpu_to_le16(0); | ||
110 | else | ||
111 | hdr->CreditRequest = cpu_to_le16( | ||
112 | min_t(int, server->max_credits - | ||
113 | server->credits, 2)); | ||
114 | spin_unlock(&server->req_lock); | ||
115 | } else { | ||
116 | hdr->CreditRequest = cpu_to_le16(2); | ||
117 | } | ||
104 | hdr->ProcessId = cpu_to_le32((__u16)current->tgid); | 118 | hdr->ProcessId = cpu_to_le32((__u16)current->tgid); |
105 | 119 | ||
106 | if (!tcon) | 120 | if (!tcon) |
@@ -236,8 +250,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | |||
236 | } | 250 | } |
237 | 251 | ||
238 | cifs_mark_open_files_invalid(tcon); | 252 | cifs_mark_open_files_invalid(tcon); |
253 | |||
239 | rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); | 254 | rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); |
240 | mutex_unlock(&tcon->ses->session_mutex); | 255 | mutex_unlock(&tcon->ses->session_mutex); |
256 | |||
257 | if (tcon->use_persistent) | ||
258 | cifs_reopen_persistent_handles(tcon); | ||
259 | |||
241 | cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); | 260 | cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); |
242 | if (rc) | 261 | if (rc) |
243 | goto out; | 262 | goto out; |
@@ -574,59 +593,42 @@ vneg_out: | |||
574 | return -EIO; | 593 | return -EIO; |
575 | } | 594 | } |
576 | 595 | ||
577 | int | 596 | struct SMB2_sess_data { |
578 | SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | 597 | unsigned int xid; |
579 | const struct nls_table *nls_cp) | 598 | struct cifs_ses *ses; |
599 | struct nls_table *nls_cp; | ||
600 | void (*func)(struct SMB2_sess_data *); | ||
601 | int result; | ||
602 | u64 previous_session; | ||
603 | |||
604 | /* we will send the SMB in three pieces: | ||
605 | * a fixed length beginning part, an optional | ||
606 | * SPNEGO blob (which can be zero length), and a | ||
607 | * last part which will include the strings | ||
608 | * and rest of bcc area. This allows us to avoid | ||
609 | * a large buffer 17K allocation | ||
610 | */ | ||
611 | int buf0_type; | ||
612 | struct kvec iov[2]; | ||
613 | }; | ||
614 | |||
615 | static int | ||
616 | SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) | ||
580 | { | 617 | { |
618 | int rc; | ||
619 | struct cifs_ses *ses = sess_data->ses; | ||
581 | struct smb2_sess_setup_req *req; | 620 | struct smb2_sess_setup_req *req; |
582 | struct smb2_sess_setup_rsp *rsp = NULL; | ||
583 | struct kvec iov[2]; | ||
584 | int rc = 0; | ||
585 | int resp_buftype = CIFS_NO_BUFFER; | ||
586 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ | ||
587 | struct TCP_Server_Info *server = ses->server; | 621 | struct TCP_Server_Info *server = ses->server; |
588 | u16 blob_length = 0; | ||
589 | struct key *spnego_key = NULL; | ||
590 | char *security_blob = NULL; | ||
591 | unsigned char *ntlmssp_blob = NULL; | ||
592 | bool use_spnego = false; /* else use raw ntlmssp */ | ||
593 | |||
594 | cifs_dbg(FYI, "Session Setup\n"); | ||
595 | |||
596 | if (!server) { | ||
597 | WARN(1, "%s: server is NULL!\n", __func__); | ||
598 | return -EIO; | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * If we are here due to reconnect, free per-smb session key | ||
603 | * in case signing was required. | ||
604 | */ | ||
605 | kfree(ses->auth_key.response); | ||
606 | ses->auth_key.response = NULL; | ||
607 | |||
608 | /* | ||
609 | * If memory allocation is successful, caller of this function | ||
610 | * frees it. | ||
611 | */ | ||
612 | ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); | ||
613 | if (!ses->ntlmssp) | ||
614 | return -ENOMEM; | ||
615 | ses->ntlmssp->sesskey_per_smbsess = true; | ||
616 | |||
617 | /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ | ||
618 | if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP) | ||
619 | ses->sectype = RawNTLMSSP; | ||
620 | |||
621 | ssetup_ntlmssp_authenticate: | ||
622 | if (phase == NtLmChallenge) | ||
623 | phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ | ||
624 | 622 | ||
625 | rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); | 623 | rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); |
626 | if (rc) | 624 | if (rc) |
627 | return rc; | 625 | return rc; |
628 | 626 | ||
629 | req->hdr.SessionId = 0; /* First session, not a reauthenticate */ | 627 | req->hdr.SessionId = 0; /* First session, not a reauthenticate */ |
628 | |||
629 | /* if reconnect, we need to send previous sess id, otherwise it is 0 */ | ||
630 | req->PreviousSessionId = sess_data->previous_session; | ||
631 | |||
630 | req->Flags = 0; /* MBZ */ | 632 | req->Flags = 0; /* MBZ */ |
631 | /* to enable echos and oplocks */ | 633 | /* to enable echos and oplocks */ |
632 | req->hdr.CreditRequest = cpu_to_le16(3); | 634 | req->hdr.CreditRequest = cpu_to_le16(3); |
@@ -642,199 +644,368 @@ ssetup_ntlmssp_authenticate: | |||
642 | req->Capabilities = 0; | 644 | req->Capabilities = 0; |
643 | req->Channel = 0; /* MBZ */ | 645 | req->Channel = 0; /* MBZ */ |
644 | 646 | ||
645 | iov[0].iov_base = (char *)req; | 647 | sess_data->iov[0].iov_base = (char *)req; |
646 | /* 4 for rfc1002 length field and 1 for pad */ | 648 | /* 4 for rfc1002 length field and 1 for pad */ |
647 | iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; | 649 | sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; |
650 | /* | ||
651 | * This variable will be used to clear the buffer | ||
652 | * allocated above in case of any error in the calling function. | ||
653 | */ | ||
654 | sess_data->buf0_type = CIFS_SMALL_BUFFER; | ||
648 | 655 | ||
649 | if (ses->sectype == Kerberos) { | 656 | return 0; |
650 | #ifdef CONFIG_CIFS_UPCALL | 657 | } |
651 | struct cifs_spnego_msg *msg; | ||
652 | 658 | ||
653 | spnego_key = cifs_get_spnego_key(ses); | 659 | static void |
654 | if (IS_ERR(spnego_key)) { | 660 | SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) |
655 | rc = PTR_ERR(spnego_key); | 661 | { |
656 | spnego_key = NULL; | 662 | free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base); |
657 | goto ssetup_exit; | 663 | sess_data->buf0_type = CIFS_NO_BUFFER; |
658 | } | 664 | } |
659 | 665 | ||
660 | msg = spnego_key->payload.data[0]; | 666 | static int |
661 | /* | 667 | SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) |
662 | * check version field to make sure that cifs.upcall is | 668 | { |
663 | * sending us a response in an expected form | 669 | int rc; |
664 | */ | 670 | struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; |
665 | if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { | ||
666 | cifs_dbg(VFS, | ||
667 | "bad cifs.upcall version. Expected %d got %d", | ||
668 | CIFS_SPNEGO_UPCALL_VERSION, msg->version); | ||
669 | rc = -EKEYREJECTED; | ||
670 | goto ssetup_exit; | ||
671 | } | ||
672 | ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, | ||
673 | GFP_KERNEL); | ||
674 | if (!ses->auth_key.response) { | ||
675 | cifs_dbg(VFS, | ||
676 | "Kerberos can't allocate (%u bytes) memory", | ||
677 | msg->sesskey_len); | ||
678 | rc = -ENOMEM; | ||
679 | goto ssetup_exit; | ||
680 | } | ||
681 | ses->auth_key.len = msg->sesskey_len; | ||
682 | blob_length = msg->secblob_len; | ||
683 | iov[1].iov_base = msg->data + msg->sesskey_len; | ||
684 | iov[1].iov_len = blob_length; | ||
685 | #else | ||
686 | rc = -EOPNOTSUPP; | ||
687 | goto ssetup_exit; | ||
688 | #endif /* CONFIG_CIFS_UPCALL */ | ||
689 | } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */ | ||
690 | ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), | ||
691 | GFP_KERNEL); | ||
692 | if (ntlmssp_blob == NULL) { | ||
693 | rc = -ENOMEM; | ||
694 | goto ssetup_exit; | ||
695 | } | ||
696 | build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); | ||
697 | if (use_spnego) { | ||
698 | /* blob_length = build_spnego_ntlmssp_blob( | ||
699 | &security_blob, | ||
700 | sizeof(struct _NEGOTIATE_MESSAGE), | ||
701 | ntlmssp_blob); */ | ||
702 | /* BB eventually need to add this */ | ||
703 | cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); | ||
704 | rc = -EOPNOTSUPP; | ||
705 | kfree(ntlmssp_blob); | ||
706 | goto ssetup_exit; | ||
707 | } else { | ||
708 | blob_length = sizeof(struct _NEGOTIATE_MESSAGE); | ||
709 | /* with raw NTLMSSP we don't encapsulate in SPNEGO */ | ||
710 | security_blob = ntlmssp_blob; | ||
711 | } | ||
712 | iov[1].iov_base = security_blob; | ||
713 | iov[1].iov_len = blob_length; | ||
714 | } else if (phase == NtLmAuthenticate) { | ||
715 | req->hdr.SessionId = ses->Suid; | ||
716 | rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, | ||
717 | nls_cp); | ||
718 | if (rc) { | ||
719 | cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", | ||
720 | rc); | ||
721 | goto ssetup_exit; /* BB double check error handling */ | ||
722 | } | ||
723 | if (use_spnego) { | ||
724 | /* blob_length = build_spnego_ntlmssp_blob( | ||
725 | &security_blob, | ||
726 | blob_length, | ||
727 | ntlmssp_blob); */ | ||
728 | cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); | ||
729 | rc = -EOPNOTSUPP; | ||
730 | kfree(ntlmssp_blob); | ||
731 | goto ssetup_exit; | ||
732 | } else { | ||
733 | security_blob = ntlmssp_blob; | ||
734 | } | ||
735 | iov[1].iov_base = security_blob; | ||
736 | iov[1].iov_len = blob_length; | ||
737 | } else { | ||
738 | cifs_dbg(VFS, "illegal ntlmssp phase\n"); | ||
739 | rc = -EIO; | ||
740 | goto ssetup_exit; | ||
741 | } | ||
742 | 671 | ||
743 | /* Testing shows that buffer offset must be at location of Buffer[0] */ | 672 | /* Testing shows that buffer offset must be at location of Buffer[0] */ |
744 | req->SecurityBufferOffset = | 673 | req->SecurityBufferOffset = |
745 | cpu_to_le16(sizeof(struct smb2_sess_setup_req) - | 674 | cpu_to_le16(sizeof(struct smb2_sess_setup_req) - |
746 | 1 /* pad */ - 4 /* rfc1001 len */); | 675 | 1 /* pad */ - 4 /* rfc1001 len */); |
747 | req->SecurityBufferLength = cpu_to_le16(blob_length); | 676 | req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); |
748 | 677 | ||
749 | inc_rfc1001_len(req, blob_length - 1 /* pad */); | 678 | inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */); |
750 | 679 | ||
751 | /* BB add code to build os and lm fields */ | 680 | /* BB add code to build os and lm fields */ |
752 | 681 | ||
753 | rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, | 682 | rc = SendReceive2(sess_data->xid, sess_data->ses, |
754 | CIFS_LOG_ERROR | CIFS_NEG_OP); | 683 | sess_data->iov, 2, |
684 | &sess_data->buf0_type, | ||
685 | CIFS_LOG_ERROR | CIFS_NEG_OP); | ||
755 | 686 | ||
756 | kfree(security_blob); | 687 | return rc; |
757 | rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; | 688 | } |
758 | ses->Suid = rsp->hdr.SessionId; | 689 | |
759 | if (resp_buftype != CIFS_NO_BUFFER && | 690 | static int |
760 | rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { | 691 | SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) |
761 | if (phase != NtLmNegotiate) { | 692 | { |
762 | cifs_dbg(VFS, "Unexpected more processing error\n"); | 693 | int rc = 0; |
763 | goto ssetup_exit; | 694 | struct cifs_ses *ses = sess_data->ses; |
764 | } | 695 | |
765 | if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != | 696 | mutex_lock(&ses->server->srv_mutex); |
766 | le16_to_cpu(rsp->SecurityBufferOffset)) { | 697 | if (ses->server->sign && ses->server->ops->generate_signingkey) { |
767 | cifs_dbg(VFS, "Invalid security buffer offset %d\n", | 698 | rc = ses->server->ops->generate_signingkey(ses); |
768 | le16_to_cpu(rsp->SecurityBufferOffset)); | 699 | kfree(ses->auth_key.response); |
769 | rc = -EIO; | 700 | ses->auth_key.response = NULL; |
770 | goto ssetup_exit; | 701 | if (rc) { |
702 | cifs_dbg(FYI, | ||
703 | "SMB3 session key generation failed\n"); | ||
704 | mutex_unlock(&ses->server->srv_mutex); | ||
705 | goto keygen_exit; | ||
771 | } | 706 | } |
707 | } | ||
708 | if (!ses->server->session_estab) { | ||
709 | ses->server->sequence_number = 0x2; | ||
710 | ses->server->session_estab = true; | ||
711 | } | ||
712 | mutex_unlock(&ses->server->srv_mutex); | ||
713 | |||
714 | cifs_dbg(FYI, "SMB2/3 session established successfully\n"); | ||
715 | spin_lock(&GlobalMid_Lock); | ||
716 | ses->status = CifsGood; | ||
717 | ses->need_reconnect = false; | ||
718 | spin_unlock(&GlobalMid_Lock); | ||
772 | 719 | ||
773 | /* NTLMSSP Negotiate sent now processing challenge (response) */ | 720 | keygen_exit: |
774 | phase = NtLmChallenge; /* process ntlmssp challenge */ | 721 | if (!ses->server->sign) { |
775 | rc = 0; /* MORE_PROCESSING is not an error here but expected */ | 722 | kfree(ses->auth_key.response); |
776 | rc = decode_ntlmssp_challenge(rsp->Buffer, | 723 | ses->auth_key.response = NULL; |
777 | le16_to_cpu(rsp->SecurityBufferLength), ses); | 724 | } |
725 | return rc; | ||
726 | } | ||
727 | |||
728 | #ifdef CONFIG_CIFS_UPCALL | ||
729 | static void | ||
730 | SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) | ||
731 | { | ||
732 | int rc; | ||
733 | struct cifs_ses *ses = sess_data->ses; | ||
734 | struct cifs_spnego_msg *msg; | ||
735 | struct key *spnego_key = NULL; | ||
736 | struct smb2_sess_setup_rsp *rsp = NULL; | ||
737 | |||
738 | rc = SMB2_sess_alloc_buffer(sess_data); | ||
739 | if (rc) | ||
740 | goto out; | ||
741 | |||
742 | spnego_key = cifs_get_spnego_key(ses); | ||
743 | if (IS_ERR(spnego_key)) { | ||
744 | rc = PTR_ERR(spnego_key); | ||
745 | spnego_key = NULL; | ||
746 | goto out; | ||
778 | } | 747 | } |
779 | 748 | ||
749 | msg = spnego_key->payload.data[0]; | ||
780 | /* | 750 | /* |
781 | * BB eventually add code for SPNEGO decoding of NtlmChallenge blob, | 751 | * check version field to make sure that cifs.upcall is |
782 | * but at least the raw NTLMSSP case works. | 752 | * sending us a response in an expected form |
783 | */ | 753 | */ |
754 | if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { | ||
755 | cifs_dbg(VFS, | ||
756 | "bad cifs.upcall version. Expected %d got %d", | ||
757 | CIFS_SPNEGO_UPCALL_VERSION, msg->version); | ||
758 | rc = -EKEYREJECTED; | ||
759 | goto out_put_spnego_key; | ||
760 | } | ||
761 | |||
762 | ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, | ||
763 | GFP_KERNEL); | ||
764 | if (!ses->auth_key.response) { | ||
765 | cifs_dbg(VFS, | ||
766 | "Kerberos can't allocate (%u bytes) memory", | ||
767 | msg->sesskey_len); | ||
768 | rc = -ENOMEM; | ||
769 | goto out_put_spnego_key; | ||
770 | } | ||
771 | ses->auth_key.len = msg->sesskey_len; | ||
772 | |||
773 | sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; | ||
774 | sess_data->iov[1].iov_len = msg->secblob_len; | ||
775 | |||
776 | rc = SMB2_sess_sendreceive(sess_data); | ||
777 | if (rc) | ||
778 | goto out_put_spnego_key; | ||
779 | |||
780 | rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; | ||
781 | ses->Suid = rsp->hdr.SessionId; | ||
782 | |||
783 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); | ||
784 | if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) | ||
785 | cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); | ||
786 | |||
787 | rc = SMB2_sess_establish_session(sess_data); | ||
788 | out_put_spnego_key: | ||
789 | key_invalidate(spnego_key); | ||
790 | key_put(spnego_key); | ||
791 | out: | ||
792 | sess_data->result = rc; | ||
793 | sess_data->func = NULL; | ||
794 | SMB2_sess_free_buffer(sess_data); | ||
795 | } | ||
796 | #else | ||
797 | static void | ||
798 | SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) | ||
799 | { | ||
800 | cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); | ||
801 | sess_data->result = -EOPNOTSUPP; | ||
802 | sess_data->func = NULL; | ||
803 | } | ||
804 | #endif | ||
805 | |||
806 | static void | ||
807 | SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); | ||
808 | |||
809 | static void | ||
810 | SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) | ||
811 | { | ||
812 | int rc; | ||
813 | struct cifs_ses *ses = sess_data->ses; | ||
814 | struct smb2_sess_setup_rsp *rsp = NULL; | ||
815 | char *ntlmssp_blob = NULL; | ||
816 | bool use_spnego = false; /* else use raw ntlmssp */ | ||
817 | u16 blob_length = 0; | ||
818 | |||
784 | /* | 819 | /* |
785 | * No tcon so can't do | 820 | * If memory allocation is successful, caller of this function |
786 | * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); | 821 | * frees it. |
787 | */ | 822 | */ |
788 | if (rc != 0) | 823 | ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); |
789 | goto ssetup_exit; | 824 | if (!ses->ntlmssp) { |
825 | rc = -ENOMEM; | ||
826 | goto out_err; | ||
827 | } | ||
828 | ses->ntlmssp->sesskey_per_smbsess = true; | ||
829 | |||
830 | rc = SMB2_sess_alloc_buffer(sess_data); | ||
831 | if (rc) | ||
832 | goto out_err; | ||
833 | |||
834 | ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), | ||
835 | GFP_KERNEL); | ||
836 | if (ntlmssp_blob == NULL) { | ||
837 | rc = -ENOMEM; | ||
838 | goto out; | ||
839 | } | ||
840 | |||
841 | build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); | ||
842 | if (use_spnego) { | ||
843 | /* BB eventually need to add this */ | ||
844 | cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); | ||
845 | rc = -EOPNOTSUPP; | ||
846 | goto out; | ||
847 | } else { | ||
848 | blob_length = sizeof(struct _NEGOTIATE_MESSAGE); | ||
849 | /* with raw NTLMSSP we don't encapsulate in SPNEGO */ | ||
850 | } | ||
851 | sess_data->iov[1].iov_base = ntlmssp_blob; | ||
852 | sess_data->iov[1].iov_len = blob_length; | ||
853 | |||
854 | rc = SMB2_sess_sendreceive(sess_data); | ||
855 | rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; | ||
856 | |||
857 | /* If true, rc here is expected and not an error */ | ||
858 | if (sess_data->buf0_type != CIFS_NO_BUFFER && | ||
859 | rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) | ||
860 | rc = 0; | ||
861 | |||
862 | if (rc) | ||
863 | goto out; | ||
864 | |||
865 | if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != | ||
866 | le16_to_cpu(rsp->SecurityBufferOffset)) { | ||
867 | cifs_dbg(VFS, "Invalid security buffer offset %d\n", | ||
868 | le16_to_cpu(rsp->SecurityBufferOffset)); | ||
869 | rc = -EIO; | ||
870 | goto out; | ||
871 | } | ||
872 | rc = decode_ntlmssp_challenge(rsp->Buffer, | ||
873 | le16_to_cpu(rsp->SecurityBufferLength), ses); | ||
874 | if (rc) | ||
875 | goto out; | ||
876 | |||
877 | cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); | ||
878 | |||
790 | 879 | ||
880 | ses->Suid = rsp->hdr.SessionId; | ||
791 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); | 881 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); |
792 | if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) | 882 | if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) |
793 | cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); | 883 | cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); |
794 | ssetup_exit: | ||
795 | free_rsp_buf(resp_buftype, rsp); | ||
796 | |||
797 | /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ | ||
798 | if ((phase == NtLmChallenge) && (rc == 0)) | ||
799 | goto ssetup_ntlmssp_authenticate; | ||
800 | 884 | ||
885 | out: | ||
886 | kfree(ntlmssp_blob); | ||
887 | SMB2_sess_free_buffer(sess_data); | ||
801 | if (!rc) { | 888 | if (!rc) { |
802 | mutex_lock(&server->srv_mutex); | 889 | sess_data->result = 0; |
803 | if (server->sign && server->ops->generate_signingkey) { | 890 | sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; |
804 | rc = server->ops->generate_signingkey(ses); | 891 | return; |
805 | kfree(ses->auth_key.response); | ||
806 | ses->auth_key.response = NULL; | ||
807 | if (rc) { | ||
808 | cifs_dbg(FYI, | ||
809 | "SMB3 session key generation failed\n"); | ||
810 | mutex_unlock(&server->srv_mutex); | ||
811 | goto keygen_exit; | ||
812 | } | ||
813 | } | ||
814 | if (!server->session_estab) { | ||
815 | server->sequence_number = 0x2; | ||
816 | server->session_estab = true; | ||
817 | } | ||
818 | mutex_unlock(&server->srv_mutex); | ||
819 | |||
820 | cifs_dbg(FYI, "SMB2/3 session established successfully\n"); | ||
821 | spin_lock(&GlobalMid_Lock); | ||
822 | ses->status = CifsGood; | ||
823 | ses->need_reconnect = false; | ||
824 | spin_unlock(&GlobalMid_Lock); | ||
825 | } | 892 | } |
893 | out_err: | ||
894 | kfree(ses->ntlmssp); | ||
895 | ses->ntlmssp = NULL; | ||
896 | sess_data->result = rc; | ||
897 | sess_data->func = NULL; | ||
898 | } | ||
826 | 899 | ||
827 | keygen_exit: | 900 | static void |
828 | if (!server->sign) { | 901 | SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) |
829 | kfree(ses->auth_key.response); | 902 | { |
830 | ses->auth_key.response = NULL; | 903 | int rc; |
904 | struct cifs_ses *ses = sess_data->ses; | ||
905 | struct smb2_sess_setup_req *req; | ||
906 | struct smb2_sess_setup_rsp *rsp = NULL; | ||
907 | unsigned char *ntlmssp_blob = NULL; | ||
908 | bool use_spnego = false; /* else use raw ntlmssp */ | ||
909 | u16 blob_length = 0; | ||
910 | |||
911 | rc = SMB2_sess_alloc_buffer(sess_data); | ||
912 | if (rc) | ||
913 | goto out; | ||
914 | |||
915 | req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; | ||
916 | req->hdr.SessionId = ses->Suid; | ||
917 | |||
918 | rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, | ||
919 | sess_data->nls_cp); | ||
920 | if (rc) { | ||
921 | cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); | ||
922 | goto out; | ||
831 | } | 923 | } |
832 | if (spnego_key) { | 924 | |
833 | key_invalidate(spnego_key); | 925 | if (use_spnego) { |
834 | key_put(spnego_key); | 926 | /* BB eventually need to add this */ |
927 | cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); | ||
928 | rc = -EOPNOTSUPP; | ||
929 | goto out; | ||
835 | } | 930 | } |
931 | sess_data->iov[1].iov_base = ntlmssp_blob; | ||
932 | sess_data->iov[1].iov_len = blob_length; | ||
933 | |||
934 | rc = SMB2_sess_sendreceive(sess_data); | ||
935 | if (rc) | ||
936 | goto out; | ||
937 | |||
938 | rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; | ||
939 | |||
940 | ses->Suid = rsp->hdr.SessionId; | ||
941 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); | ||
942 | if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) | ||
943 | cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); | ||
944 | |||
945 | rc = SMB2_sess_establish_session(sess_data); | ||
946 | out: | ||
947 | kfree(ntlmssp_blob); | ||
948 | SMB2_sess_free_buffer(sess_data); | ||
836 | kfree(ses->ntlmssp); | 949 | kfree(ses->ntlmssp); |
950 | ses->ntlmssp = NULL; | ||
951 | sess_data->result = rc; | ||
952 | sess_data->func = NULL; | ||
953 | } | ||
837 | 954 | ||
955 | static int | ||
956 | SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) | ||
957 | { | ||
958 | if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP) | ||
959 | ses->sectype = RawNTLMSSP; | ||
960 | |||
961 | switch (ses->sectype) { | ||
962 | case Kerberos: | ||
963 | sess_data->func = SMB2_auth_kerberos; | ||
964 | break; | ||
965 | case RawNTLMSSP: | ||
966 | sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; | ||
967 | break; | ||
968 | default: | ||
969 | cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype); | ||
970 | return -EOPNOTSUPP; | ||
971 | } | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | int | ||
977 | SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | ||
978 | const struct nls_table *nls_cp) | ||
979 | { | ||
980 | int rc = 0; | ||
981 | struct TCP_Server_Info *server = ses->server; | ||
982 | struct SMB2_sess_data *sess_data; | ||
983 | |||
984 | cifs_dbg(FYI, "Session Setup\n"); | ||
985 | |||
986 | if (!server) { | ||
987 | WARN(1, "%s: server is NULL!\n", __func__); | ||
988 | return -EIO; | ||
989 | } | ||
990 | |||
991 | sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); | ||
992 | if (!sess_data) | ||
993 | return -ENOMEM; | ||
994 | |||
995 | rc = SMB2_select_sec(ses, sess_data); | ||
996 | if (rc) | ||
997 | goto out; | ||
998 | sess_data->xid = xid; | ||
999 | sess_data->ses = ses; | ||
1000 | sess_data->buf0_type = CIFS_NO_BUFFER; | ||
1001 | sess_data->nls_cp = (struct nls_table *) nls_cp; | ||
1002 | |||
1003 | while (sess_data->func) | ||
1004 | sess_data->func(sess_data); | ||
1005 | |||
1006 | rc = sess_data->result; | ||
1007 | out: | ||
1008 | kfree(sess_data); | ||
838 | return rc; | 1009 | return rc; |
839 | } | 1010 | } |
840 | 1011 | ||
@@ -1164,7 +1335,7 @@ create_durable_v2_buf(struct cifs_fid *pfid) | |||
1164 | 1335 | ||
1165 | buf->dcontext.Timeout = 0; /* Should this be configurable by workload */ | 1336 | buf->dcontext.Timeout = 0; /* Should this be configurable by workload */ |
1166 | buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); | 1337 | buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); |
1167 | get_random_bytes(buf->dcontext.CreateGuid, 16); | 1338 | generate_random_uuid(buf->dcontext.CreateGuid); |
1168 | memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); | 1339 | memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); |
1169 | 1340 | ||
1170 | /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ | 1341 | /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ |
@@ -2057,6 +2228,7 @@ smb2_async_readv(struct cifs_readdata *rdata) | |||
2057 | if (rdata->credits) { | 2228 | if (rdata->credits) { |
2058 | buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, | 2229 | buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, |
2059 | SMB2_MAX_BUFFER_SIZE)); | 2230 | SMB2_MAX_BUFFER_SIZE)); |
2231 | buf->CreditRequest = buf->CreditCharge; | ||
2060 | spin_lock(&server->req_lock); | 2232 | spin_lock(&server->req_lock); |
2061 | server->credits += rdata->credits - | 2233 | server->credits += rdata->credits - |
2062 | le16_to_cpu(buf->CreditCharge); | 2234 | le16_to_cpu(buf->CreditCharge); |
@@ -2243,6 +2415,7 @@ smb2_async_writev(struct cifs_writedata *wdata, | |||
2243 | if (wdata->credits) { | 2415 | if (wdata->credits) { |
2244 | req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, | 2416 | req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, |
2245 | SMB2_MAX_BUFFER_SIZE)); | 2417 | SMB2_MAX_BUFFER_SIZE)); |
2418 | req->hdr.CreditRequest = req->hdr.CreditCharge; | ||
2246 | spin_lock(&server->req_lock); | 2419 | spin_lock(&server->req_lock); |
2247 | server->credits += wdata->credits - | 2420 | server->credits += wdata->credits - |
2248 | le16_to_cpu(req->hdr.CreditCharge); | 2421 | le16_to_cpu(req->hdr.CreditCharge); |
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index ff88d9feb01e..fd3709e8de33 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -276,7 +276,7 @@ struct smb2_sess_setup_req { | |||
276 | __le32 Channel; | 276 | __le32 Channel; |
277 | __le16 SecurityBufferOffset; | 277 | __le16 SecurityBufferOffset; |
278 | __le16 SecurityBufferLength; | 278 | __le16 SecurityBufferLength; |
279 | __le64 PreviousSessionId; | 279 | __u64 PreviousSessionId; |
280 | __u8 Buffer[1]; /* variable length GSS security buffer */ | 280 | __u8 Buffer[1]; /* variable length GSS security buffer */ |
281 | } __packed; | 281 | } __packed; |
282 | 282 | ||
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 5e23f64c0804..20af5187ba63 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c | |||
@@ -33,7 +33,8 @@ | |||
33 | 33 | ||
34 | #define MAX_EA_VALUE_SIZE 65535 | 34 | #define MAX_EA_VALUE_SIZE 65535 |
35 | #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" | 35 | #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" |
36 | 36 | #define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ | |
37 | #define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ | ||
37 | /* BB need to add server (Samba e.g) support for security and trusted prefix */ | 38 | /* BB need to add server (Samba e.g) support for security and trusted prefix */ |
38 | 39 | ||
39 | enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT }; | 40 | enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT }; |
@@ -144,6 +145,54 @@ out: | |||
144 | return rc; | 145 | return rc; |
145 | } | 146 | } |
146 | 147 | ||
148 | static int cifs_attrib_get(struct dentry *dentry, | ||
149 | struct inode *inode, void *value, | ||
150 | size_t size) | ||
151 | { | ||
152 | ssize_t rc; | ||
153 | __u32 *pattribute; | ||
154 | |||
155 | rc = cifs_revalidate_dentry_attr(dentry); | ||
156 | |||
157 | if (rc) | ||
158 | return rc; | ||
159 | |||
160 | if ((value == NULL) || (size == 0)) | ||
161 | return sizeof(__u32); | ||
162 | else if (size < sizeof(__u32)) | ||
163 | return -ERANGE; | ||
164 | |||
165 | /* return dos attributes as pseudo xattr */ | ||
166 | pattribute = (__u32 *)value; | ||
167 | *pattribute = CIFS_I(inode)->cifsAttrs; | ||
168 | |||
169 | return sizeof(__u32); | ||
170 | } | ||
171 | |||
172 | static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, | ||
173 | void *value, size_t size) | ||
174 | { | ||
175 | ssize_t rc; | ||
176 | __u64 * pcreatetime; | ||
177 | |||
178 | rc = cifs_revalidate_dentry_attr(dentry); | ||
179 | if (rc) | ||
180 | return rc; | ||
181 | |||
182 | if ((value == NULL) || (size == 0)) | ||
183 | return sizeof(__u64); | ||
184 | else if (size < sizeof(__u64)) | ||
185 | return -ERANGE; | ||
186 | |||
187 | /* return dos attributes as pseudo xattr */ | ||
188 | pcreatetime = (__u64 *)value; | ||
189 | *pcreatetime = CIFS_I(inode)->createtime; | ||
190 | return sizeof(__u64); | ||
191 | |||
192 | return rc; | ||
193 | } | ||
194 | |||
195 | |||
147 | static int cifs_xattr_get(const struct xattr_handler *handler, | 196 | static int cifs_xattr_get(const struct xattr_handler *handler, |
148 | struct dentry *dentry, struct inode *inode, | 197 | struct dentry *dentry, struct inode *inode, |
149 | const char *name, void *value, size_t size) | 198 | const char *name, void *value, size_t size) |
@@ -168,10 +217,19 @@ static int cifs_xattr_get(const struct xattr_handler *handler, | |||
168 | rc = -ENOMEM; | 217 | rc = -ENOMEM; |
169 | goto out; | 218 | goto out; |
170 | } | 219 | } |
171 | /* return dos attributes as pseudo xattr */ | 220 | |
172 | /* return alt name if available as pseudo attr */ | 221 | /* return alt name if available as pseudo attr */ |
173 | switch (handler->flags) { | 222 | switch (handler->flags) { |
174 | case XATTR_USER: | 223 | case XATTR_USER: |
224 | cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); | ||
225 | if (strcmp(name, CIFS_XATTR_ATTRIB) == 0) { | ||
226 | rc = cifs_attrib_get(dentry, inode, value, size); | ||
227 | break; | ||
228 | } else if (strcmp(name, CIFS_XATTR_CREATETIME) == 0) { | ||
229 | rc = cifs_creation_time_get(dentry, inode, value, size); | ||
230 | break; | ||
231 | } | ||
232 | |||
175 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) | 233 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
176 | goto out; | 234 | goto out; |
177 | 235 | ||