diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-12-04 06:40:59 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-12-04 06:40:59 -0500 |
commit | b0e6989c965dda2f2b65a2abb04f5337b497f4a2 (patch) | |
tree | eb70ca5f8fc50688f879d1c851fa3f09a6c68850 /fs | |
parent | 20ce902978a70ab51ad9ed645f636805f3ff2b0d (diff) | |
parent | 29e248829dc7d44248c69bbd5d40eca152a50cab (diff) |
Merge tag 'asoc-v3.13-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Fixes for v3.13
A smattering of fixes here, some core ones for the rate combination
issues for things other than simple bitmasks, for readback of byte
controls and for updating the power of value muxes plus a bunch of
driver fixes of varying severity.
The warning fix in the i.MX FIQ driver is fixing a warning introduced
by a previous fix.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/affs/Changes | 2 | ||||
-rw-r--r-- | fs/ceph/addr.c | 2 | ||||
-rw-r--r-- | fs/ceph/cache.c | 3 | ||||
-rw-r--r-- | fs/ceph/caps.c | 27 | ||||
-rw-r--r-- | fs/ceph/dir.c | 11 | ||||
-rw-r--r-- | fs/ceph/inode.c | 49 | ||||
-rw-r--r-- | fs/ceph/mds_client.c | 61 | ||||
-rw-r--r-- | fs/ceph/mds_client.h | 1 | ||||
-rw-r--r-- | fs/ceph/super.h | 8 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
-rw-r--r-- | fs/cifs/ioctl.c | 6 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 99 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 92 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 12 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 1 | ||||
-rw-r--r-- | fs/cifs/smbfsctl.h | 2 | ||||
-rw-r--r-- | fs/namei.c | 3 | ||||
-rw-r--r-- | fs/sysfs/file.c | 22 |
18 files changed, 332 insertions, 70 deletions
diff --git a/fs/affs/Changes b/fs/affs/Changes index a29409c1ffe0..b41c2c9792ff 100644 --- a/fs/affs/Changes +++ b/fs/affs/Changes | |||
@@ -91,7 +91,7 @@ more 2.4 fixes: [Roman Zippel] | |||
91 | Version 3.11 | 91 | Version 3.11 |
92 | ------------ | 92 | ------------ |
93 | 93 | ||
94 | - Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>] | 94 | - Converted to use 2.3.x page cache [Dave Jones] |
95 | - Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>] | 95 | - Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>] |
96 | 96 | ||
97 | Version 3.10 | 97 | Version 3.10 |
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 6df8bd481425..1e561c059539 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c | |||
@@ -216,7 +216,7 @@ static int readpage_nounlock(struct file *filp, struct page *page) | |||
216 | } | 216 | } |
217 | SetPageUptodate(page); | 217 | SetPageUptodate(page); |
218 | 218 | ||
219 | if (err == 0) | 219 | if (err >= 0) |
220 | ceph_readpage_to_fscache(inode, page); | 220 | ceph_readpage_to_fscache(inode, page); |
221 | 221 | ||
222 | out: | 222 | out: |
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c index 7db2e6ca4b8f..8c44fdd4e1c3 100644 --- a/fs/ceph/cache.c +++ b/fs/ceph/cache.c | |||
@@ -324,6 +324,9 @@ void ceph_invalidate_fscache_page(struct inode* inode, struct page *page) | |||
324 | { | 324 | { |
325 | struct ceph_inode_info *ci = ceph_inode(inode); | 325 | struct ceph_inode_info *ci = ceph_inode(inode); |
326 | 326 | ||
327 | if (!PageFsCache(page)) | ||
328 | return; | ||
329 | |||
327 | fscache_wait_on_page_write(ci->fscache, page); | 330 | fscache_wait_on_page_write(ci->fscache, page); |
328 | fscache_uncache_page(ci->fscache, page); | 331 | fscache_uncache_page(ci->fscache, page); |
329 | } | 332 | } |
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 13976c33332e..3c0a4bd74996 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c | |||
@@ -897,7 +897,7 @@ static int __ceph_is_any_caps(struct ceph_inode_info *ci) | |||
897 | * caller should hold i_ceph_lock. | 897 | * caller should hold i_ceph_lock. |
898 | * caller will not hold session s_mutex if called from destroy_inode. | 898 | * caller will not hold session s_mutex if called from destroy_inode. |
899 | */ | 899 | */ |
900 | void __ceph_remove_cap(struct ceph_cap *cap) | 900 | void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) |
901 | { | 901 | { |
902 | struct ceph_mds_session *session = cap->session; | 902 | struct ceph_mds_session *session = cap->session; |
903 | struct ceph_inode_info *ci = cap->ci; | 903 | struct ceph_inode_info *ci = cap->ci; |
@@ -909,6 +909,16 @@ void __ceph_remove_cap(struct ceph_cap *cap) | |||
909 | 909 | ||
910 | /* remove from session list */ | 910 | /* remove from session list */ |
911 | spin_lock(&session->s_cap_lock); | 911 | spin_lock(&session->s_cap_lock); |
912 | /* | ||
913 | * s_cap_reconnect is protected by s_cap_lock. no one changes | ||
914 | * s_cap_gen while session is in the reconnect state. | ||
915 | */ | ||
916 | if (queue_release && | ||
917 | (!session->s_cap_reconnect || | ||
918 | cap->cap_gen == session->s_cap_gen)) | ||
919 | __queue_cap_release(session, ci->i_vino.ino, cap->cap_id, | ||
920 | cap->mseq, cap->issue_seq); | ||
921 | |||
912 | if (session->s_cap_iterator == cap) { | 922 | if (session->s_cap_iterator == cap) { |
913 | /* not yet, we are iterating over this very cap */ | 923 | /* not yet, we are iterating over this very cap */ |
914 | dout("__ceph_remove_cap delaying %p removal from session %p\n", | 924 | dout("__ceph_remove_cap delaying %p removal from session %p\n", |
@@ -1023,7 +1033,6 @@ void __queue_cap_release(struct ceph_mds_session *session, | |||
1023 | struct ceph_mds_cap_release *head; | 1033 | struct ceph_mds_cap_release *head; |
1024 | struct ceph_mds_cap_item *item; | 1034 | struct ceph_mds_cap_item *item; |
1025 | 1035 | ||
1026 | spin_lock(&session->s_cap_lock); | ||
1027 | BUG_ON(!session->s_num_cap_releases); | 1036 | BUG_ON(!session->s_num_cap_releases); |
1028 | msg = list_first_entry(&session->s_cap_releases, | 1037 | msg = list_first_entry(&session->s_cap_releases, |
1029 | struct ceph_msg, list_head); | 1038 | struct ceph_msg, list_head); |
@@ -1052,7 +1061,6 @@ void __queue_cap_release(struct ceph_mds_session *session, | |||
1052 | (int)CEPH_CAPS_PER_RELEASE, | 1061 | (int)CEPH_CAPS_PER_RELEASE, |
1053 | (int)msg->front.iov_len); | 1062 | (int)msg->front.iov_len); |
1054 | } | 1063 | } |
1055 | spin_unlock(&session->s_cap_lock); | ||
1056 | } | 1064 | } |
1057 | 1065 | ||
1058 | /* | 1066 | /* |
@@ -1067,12 +1075,8 @@ void ceph_queue_caps_release(struct inode *inode) | |||
1067 | p = rb_first(&ci->i_caps); | 1075 | p = rb_first(&ci->i_caps); |
1068 | while (p) { | 1076 | while (p) { |
1069 | struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); | 1077 | struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); |
1070 | struct ceph_mds_session *session = cap->session; | ||
1071 | |||
1072 | __queue_cap_release(session, ceph_ino(inode), cap->cap_id, | ||
1073 | cap->mseq, cap->issue_seq); | ||
1074 | p = rb_next(p); | 1078 | p = rb_next(p); |
1075 | __ceph_remove_cap(cap); | 1079 | __ceph_remove_cap(cap, true); |
1076 | } | 1080 | } |
1077 | } | 1081 | } |
1078 | 1082 | ||
@@ -2791,7 +2795,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, | |||
2791 | } | 2795 | } |
2792 | spin_unlock(&mdsc->cap_dirty_lock); | 2796 | spin_unlock(&mdsc->cap_dirty_lock); |
2793 | } | 2797 | } |
2794 | __ceph_remove_cap(cap); | 2798 | __ceph_remove_cap(cap, false); |
2795 | } | 2799 | } |
2796 | /* else, we already released it */ | 2800 | /* else, we already released it */ |
2797 | 2801 | ||
@@ -2931,9 +2935,12 @@ void ceph_handle_caps(struct ceph_mds_session *session, | |||
2931 | if (!inode) { | 2935 | if (!inode) { |
2932 | dout(" i don't have ino %llx\n", vino.ino); | 2936 | dout(" i don't have ino %llx\n", vino.ino); |
2933 | 2937 | ||
2934 | if (op == CEPH_CAP_OP_IMPORT) | 2938 | if (op == CEPH_CAP_OP_IMPORT) { |
2939 | spin_lock(&session->s_cap_lock); | ||
2935 | __queue_cap_release(session, vino.ino, cap_id, | 2940 | __queue_cap_release(session, vino.ino, cap_id, |
2936 | mseq, seq); | 2941 | mseq, seq); |
2942 | spin_unlock(&session->s_cap_lock); | ||
2943 | } | ||
2937 | goto flush_cap_releases; | 2944 | goto flush_cap_releases; |
2938 | } | 2945 | } |
2939 | 2946 | ||
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 868b61d56cac..2a0bcaeb189a 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -352,8 +352,18 @@ more: | |||
352 | } | 352 | } |
353 | 353 | ||
354 | /* note next offset and last dentry name */ | 354 | /* note next offset and last dentry name */ |
355 | rinfo = &req->r_reply_info; | ||
356 | if (le32_to_cpu(rinfo->dir_dir->frag) != frag) { | ||
357 | frag = le32_to_cpu(rinfo->dir_dir->frag); | ||
358 | if (ceph_frag_is_leftmost(frag)) | ||
359 | fi->next_offset = 2; | ||
360 | else | ||
361 | fi->next_offset = 0; | ||
362 | off = fi->next_offset; | ||
363 | } | ||
355 | fi->offset = fi->next_offset; | 364 | fi->offset = fi->next_offset; |
356 | fi->last_readdir = req; | 365 | fi->last_readdir = req; |
366 | fi->frag = frag; | ||
357 | 367 | ||
358 | if (req->r_reply_info.dir_end) { | 368 | if (req->r_reply_info.dir_end) { |
359 | kfree(fi->last_name); | 369 | kfree(fi->last_name); |
@@ -363,7 +373,6 @@ more: | |||
363 | else | 373 | else |
364 | fi->next_offset = 0; | 374 | fi->next_offset = 0; |
365 | } else { | 375 | } else { |
366 | rinfo = &req->r_reply_info; | ||
367 | err = note_last_dentry(fi, | 376 | err = note_last_dentry(fi, |
368 | rinfo->dir_dname[rinfo->dir_nr-1], | 377 | rinfo->dir_dname[rinfo->dir_nr-1], |
369 | rinfo->dir_dname_len[rinfo->dir_nr-1]); | 378 | rinfo->dir_dname_len[rinfo->dir_nr-1]); |
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 8549a48115f7..9a8e396aed89 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
@@ -577,6 +577,8 @@ static int fill_inode(struct inode *inode, | |||
577 | int issued = 0, implemented; | 577 | int issued = 0, implemented; |
578 | struct timespec mtime, atime, ctime; | 578 | struct timespec mtime, atime, ctime; |
579 | u32 nsplits; | 579 | u32 nsplits; |
580 | struct ceph_inode_frag *frag; | ||
581 | struct rb_node *rb_node; | ||
580 | struct ceph_buffer *xattr_blob = NULL; | 582 | struct ceph_buffer *xattr_blob = NULL; |
581 | int err = 0; | 583 | int err = 0; |
582 | int queue_trunc = 0; | 584 | int queue_trunc = 0; |
@@ -751,15 +753,38 @@ no_change: | |||
751 | /* FIXME: move me up, if/when version reflects fragtree changes */ | 753 | /* FIXME: move me up, if/when version reflects fragtree changes */ |
752 | nsplits = le32_to_cpu(info->fragtree.nsplits); | 754 | nsplits = le32_to_cpu(info->fragtree.nsplits); |
753 | mutex_lock(&ci->i_fragtree_mutex); | 755 | mutex_lock(&ci->i_fragtree_mutex); |
756 | rb_node = rb_first(&ci->i_fragtree); | ||
754 | for (i = 0; i < nsplits; i++) { | 757 | for (i = 0; i < nsplits; i++) { |
755 | u32 id = le32_to_cpu(info->fragtree.splits[i].frag); | 758 | u32 id = le32_to_cpu(info->fragtree.splits[i].frag); |
756 | struct ceph_inode_frag *frag = __get_or_create_frag(ci, id); | 759 | frag = NULL; |
757 | 760 | while (rb_node) { | |
758 | if (IS_ERR(frag)) | 761 | frag = rb_entry(rb_node, struct ceph_inode_frag, node); |
759 | continue; | 762 | if (ceph_frag_compare(frag->frag, id) >= 0) { |
763 | if (frag->frag != id) | ||
764 | frag = NULL; | ||
765 | else | ||
766 | rb_node = rb_next(rb_node); | ||
767 | break; | ||
768 | } | ||
769 | rb_node = rb_next(rb_node); | ||
770 | rb_erase(&frag->node, &ci->i_fragtree); | ||
771 | kfree(frag); | ||
772 | frag = NULL; | ||
773 | } | ||
774 | if (!frag) { | ||
775 | frag = __get_or_create_frag(ci, id); | ||
776 | if (IS_ERR(frag)) | ||
777 | continue; | ||
778 | } | ||
760 | frag->split_by = le32_to_cpu(info->fragtree.splits[i].by); | 779 | frag->split_by = le32_to_cpu(info->fragtree.splits[i].by); |
761 | dout(" frag %x split by %d\n", frag->frag, frag->split_by); | 780 | dout(" frag %x split by %d\n", frag->frag, frag->split_by); |
762 | } | 781 | } |
782 | while (rb_node) { | ||
783 | frag = rb_entry(rb_node, struct ceph_inode_frag, node); | ||
784 | rb_node = rb_next(rb_node); | ||
785 | rb_erase(&frag->node, &ci->i_fragtree); | ||
786 | kfree(frag); | ||
787 | } | ||
763 | mutex_unlock(&ci->i_fragtree_mutex); | 788 | mutex_unlock(&ci->i_fragtree_mutex); |
764 | 789 | ||
765 | /* were we issued a capability? */ | 790 | /* were we issued a capability? */ |
@@ -1250,8 +1275,20 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, | |||
1250 | int err = 0, i; | 1275 | int err = 0, i; |
1251 | struct inode *snapdir = NULL; | 1276 | struct inode *snapdir = NULL; |
1252 | struct ceph_mds_request_head *rhead = req->r_request->front.iov_base; | 1277 | struct ceph_mds_request_head *rhead = req->r_request->front.iov_base; |
1253 | u64 frag = le32_to_cpu(rhead->args.readdir.frag); | ||
1254 | struct ceph_dentry_info *di; | 1278 | struct ceph_dentry_info *di; |
1279 | u64 r_readdir_offset = req->r_readdir_offset; | ||
1280 | u32 frag = le32_to_cpu(rhead->args.readdir.frag); | ||
1281 | |||
1282 | if (rinfo->dir_dir && | ||
1283 | le32_to_cpu(rinfo->dir_dir->frag) != frag) { | ||
1284 | dout("readdir_prepopulate got new frag %x -> %x\n", | ||
1285 | frag, le32_to_cpu(rinfo->dir_dir->frag)); | ||
1286 | frag = le32_to_cpu(rinfo->dir_dir->frag); | ||
1287 | if (ceph_frag_is_leftmost(frag)) | ||
1288 | r_readdir_offset = 2; | ||
1289 | else | ||
1290 | r_readdir_offset = 0; | ||
1291 | } | ||
1255 | 1292 | ||
1256 | if (req->r_aborted) | 1293 | if (req->r_aborted) |
1257 | return readdir_prepopulate_inodes_only(req, session); | 1294 | return readdir_prepopulate_inodes_only(req, session); |
@@ -1315,7 +1352,7 @@ retry_lookup: | |||
1315 | } | 1352 | } |
1316 | 1353 | ||
1317 | di = dn->d_fsdata; | 1354 | di = dn->d_fsdata; |
1318 | di->offset = ceph_make_fpos(frag, i + req->r_readdir_offset); | 1355 | di->offset = ceph_make_fpos(frag, i + r_readdir_offset); |
1319 | 1356 | ||
1320 | /* inode */ | 1357 | /* inode */ |
1321 | if (dn->d_inode) { | 1358 | if (dn->d_inode) { |
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index b7bda5d9611d..d90861f45210 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c | |||
@@ -43,6 +43,7 @@ | |||
43 | */ | 43 | */ |
44 | 44 | ||
45 | struct ceph_reconnect_state { | 45 | struct ceph_reconnect_state { |
46 | int nr_caps; | ||
46 | struct ceph_pagelist *pagelist; | 47 | struct ceph_pagelist *pagelist; |
47 | bool flock; | 48 | bool flock; |
48 | }; | 49 | }; |
@@ -443,6 +444,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, | |||
443 | INIT_LIST_HEAD(&s->s_waiting); | 444 | INIT_LIST_HEAD(&s->s_waiting); |
444 | INIT_LIST_HEAD(&s->s_unsafe); | 445 | INIT_LIST_HEAD(&s->s_unsafe); |
445 | s->s_num_cap_releases = 0; | 446 | s->s_num_cap_releases = 0; |
447 | s->s_cap_reconnect = 0; | ||
446 | s->s_cap_iterator = NULL; | 448 | s->s_cap_iterator = NULL; |
447 | INIT_LIST_HEAD(&s->s_cap_releases); | 449 | INIT_LIST_HEAD(&s->s_cap_releases); |
448 | INIT_LIST_HEAD(&s->s_cap_releases_done); | 450 | INIT_LIST_HEAD(&s->s_cap_releases_done); |
@@ -642,6 +644,8 @@ static void __unregister_request(struct ceph_mds_client *mdsc, | |||
642 | req->r_unsafe_dir = NULL; | 644 | req->r_unsafe_dir = NULL; |
643 | } | 645 | } |
644 | 646 | ||
647 | complete_all(&req->r_safe_completion); | ||
648 | |||
645 | ceph_mdsc_put_request(req); | 649 | ceph_mdsc_put_request(req); |
646 | } | 650 | } |
647 | 651 | ||
@@ -986,7 +990,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, | |||
986 | dout("removing cap %p, ci is %p, inode is %p\n", | 990 | dout("removing cap %p, ci is %p, inode is %p\n", |
987 | cap, ci, &ci->vfs_inode); | 991 | cap, ci, &ci->vfs_inode); |
988 | spin_lock(&ci->i_ceph_lock); | 992 | spin_lock(&ci->i_ceph_lock); |
989 | __ceph_remove_cap(cap); | 993 | __ceph_remove_cap(cap, false); |
990 | if (!__ceph_is_any_real_caps(ci)) { | 994 | if (!__ceph_is_any_real_caps(ci)) { |
991 | struct ceph_mds_client *mdsc = | 995 | struct ceph_mds_client *mdsc = |
992 | ceph_sb_to_client(inode->i_sb)->mdsc; | 996 | ceph_sb_to_client(inode->i_sb)->mdsc; |
@@ -1231,9 +1235,7 @@ static int trim_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg) | |||
1231 | session->s_trim_caps--; | 1235 | session->s_trim_caps--; |
1232 | if (oissued) { | 1236 | if (oissued) { |
1233 | /* we aren't the only cap.. just remove us */ | 1237 | /* we aren't the only cap.. just remove us */ |
1234 | __queue_cap_release(session, ceph_ino(inode), cap->cap_id, | 1238 | __ceph_remove_cap(cap, true); |
1235 | cap->mseq, cap->issue_seq); | ||
1236 | __ceph_remove_cap(cap); | ||
1237 | } else { | 1239 | } else { |
1238 | /* try to drop referring dentries */ | 1240 | /* try to drop referring dentries */ |
1239 | spin_unlock(&ci->i_ceph_lock); | 1241 | spin_unlock(&ci->i_ceph_lock); |
@@ -1416,7 +1418,6 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc, | |||
1416 | unsigned num; | 1418 | unsigned num; |
1417 | 1419 | ||
1418 | dout("discard_cap_releases mds%d\n", session->s_mds); | 1420 | dout("discard_cap_releases mds%d\n", session->s_mds); |
1419 | spin_lock(&session->s_cap_lock); | ||
1420 | 1421 | ||
1421 | /* zero out the in-progress message */ | 1422 | /* zero out the in-progress message */ |
1422 | msg = list_first_entry(&session->s_cap_releases, | 1423 | msg = list_first_entry(&session->s_cap_releases, |
@@ -1443,8 +1444,6 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc, | |||
1443 | msg->front.iov_len = sizeof(*head); | 1444 | msg->front.iov_len = sizeof(*head); |
1444 | list_add(&msg->list_head, &session->s_cap_releases); | 1445 | list_add(&msg->list_head, &session->s_cap_releases); |
1445 | } | 1446 | } |
1446 | |||
1447 | spin_unlock(&session->s_cap_lock); | ||
1448 | } | 1447 | } |
1449 | 1448 | ||
1450 | /* | 1449 | /* |
@@ -1875,8 +1874,11 @@ static int __do_request(struct ceph_mds_client *mdsc, | |||
1875 | int mds = -1; | 1874 | int mds = -1; |
1876 | int err = -EAGAIN; | 1875 | int err = -EAGAIN; |
1877 | 1876 | ||
1878 | if (req->r_err || req->r_got_result) | 1877 | if (req->r_err || req->r_got_result) { |
1878 | if (req->r_aborted) | ||
1879 | __unregister_request(mdsc, req); | ||
1879 | goto out; | 1880 | goto out; |
1881 | } | ||
1880 | 1882 | ||
1881 | if (req->r_timeout && | 1883 | if (req->r_timeout && |
1882 | time_after_eq(jiffies, req->r_started + req->r_timeout)) { | 1884 | time_after_eq(jiffies, req->r_started + req->r_timeout)) { |
@@ -2186,7 +2188,6 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) | |||
2186 | if (head->safe) { | 2188 | if (head->safe) { |
2187 | req->r_got_safe = true; | 2189 | req->r_got_safe = true; |
2188 | __unregister_request(mdsc, req); | 2190 | __unregister_request(mdsc, req); |
2189 | complete_all(&req->r_safe_completion); | ||
2190 | 2191 | ||
2191 | if (req->r_got_unsafe) { | 2192 | if (req->r_got_unsafe) { |
2192 | /* | 2193 | /* |
@@ -2238,8 +2239,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) | |||
2238 | err = ceph_fill_trace(mdsc->fsc->sb, req, req->r_session); | 2239 | err = ceph_fill_trace(mdsc->fsc->sb, req, req->r_session); |
2239 | if (err == 0) { | 2240 | if (err == 0) { |
2240 | if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR || | 2241 | if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR || |
2241 | req->r_op == CEPH_MDS_OP_LSSNAP) && | 2242 | req->r_op == CEPH_MDS_OP_LSSNAP)) |
2242 | rinfo->dir_nr) | ||
2243 | ceph_readdir_prepopulate(req, req->r_session); | 2243 | ceph_readdir_prepopulate(req, req->r_session); |
2244 | ceph_unreserve_caps(mdsc, &req->r_caps_reservation); | 2244 | ceph_unreserve_caps(mdsc, &req->r_caps_reservation); |
2245 | } | 2245 | } |
@@ -2490,6 +2490,7 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, | |||
2490 | cap->seq = 0; /* reset cap seq */ | 2490 | cap->seq = 0; /* reset cap seq */ |
2491 | cap->issue_seq = 0; /* and issue_seq */ | 2491 | cap->issue_seq = 0; /* and issue_seq */ |
2492 | cap->mseq = 0; /* and migrate_seq */ | 2492 | cap->mseq = 0; /* and migrate_seq */ |
2493 | cap->cap_gen = cap->session->s_cap_gen; | ||
2493 | 2494 | ||
2494 | if (recon_state->flock) { | 2495 | if (recon_state->flock) { |
2495 | rec.v2.cap_id = cpu_to_le64(cap->cap_id); | 2496 | rec.v2.cap_id = cpu_to_le64(cap->cap_id); |
@@ -2552,6 +2553,8 @@ encode_again: | |||
2552 | } else { | 2553 | } else { |
2553 | err = ceph_pagelist_append(pagelist, &rec, reclen); | 2554 | err = ceph_pagelist_append(pagelist, &rec, reclen); |
2554 | } | 2555 | } |
2556 | |||
2557 | recon_state->nr_caps++; | ||
2555 | out_free: | 2558 | out_free: |
2556 | kfree(path); | 2559 | kfree(path); |
2557 | out_dput: | 2560 | out_dput: |
@@ -2579,6 +2582,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, | |||
2579 | struct rb_node *p; | 2582 | struct rb_node *p; |
2580 | int mds = session->s_mds; | 2583 | int mds = session->s_mds; |
2581 | int err = -ENOMEM; | 2584 | int err = -ENOMEM; |
2585 | int s_nr_caps; | ||
2582 | struct ceph_pagelist *pagelist; | 2586 | struct ceph_pagelist *pagelist; |
2583 | struct ceph_reconnect_state recon_state; | 2587 | struct ceph_reconnect_state recon_state; |
2584 | 2588 | ||
@@ -2610,20 +2614,38 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, | |||
2610 | dout("session %p state %s\n", session, | 2614 | dout("session %p state %s\n", session, |
2611 | session_state_name(session->s_state)); | 2615 | session_state_name(session->s_state)); |
2612 | 2616 | ||
2617 | spin_lock(&session->s_gen_ttl_lock); | ||
2618 | session->s_cap_gen++; | ||
2619 | spin_unlock(&session->s_gen_ttl_lock); | ||
2620 | |||
2621 | spin_lock(&session->s_cap_lock); | ||
2622 | /* | ||
2623 | * notify __ceph_remove_cap() that we are composing cap reconnect. | ||
2624 | * If a cap get released before being added to the cap reconnect, | ||
2625 | * __ceph_remove_cap() should skip queuing cap release. | ||
2626 | */ | ||
2627 | session->s_cap_reconnect = 1; | ||
2613 | /* drop old cap expires; we're about to reestablish that state */ | 2628 | /* drop old cap expires; we're about to reestablish that state */ |
2614 | discard_cap_releases(mdsc, session); | 2629 | discard_cap_releases(mdsc, session); |
2630 | spin_unlock(&session->s_cap_lock); | ||
2615 | 2631 | ||
2616 | /* traverse this session's caps */ | 2632 | /* traverse this session's caps */ |
2617 | err = ceph_pagelist_encode_32(pagelist, session->s_nr_caps); | 2633 | s_nr_caps = session->s_nr_caps; |
2634 | err = ceph_pagelist_encode_32(pagelist, s_nr_caps); | ||
2618 | if (err) | 2635 | if (err) |
2619 | goto fail; | 2636 | goto fail; |
2620 | 2637 | ||
2638 | recon_state.nr_caps = 0; | ||
2621 | recon_state.pagelist = pagelist; | 2639 | recon_state.pagelist = pagelist; |
2622 | recon_state.flock = session->s_con.peer_features & CEPH_FEATURE_FLOCK; | 2640 | recon_state.flock = session->s_con.peer_features & CEPH_FEATURE_FLOCK; |
2623 | err = iterate_session_caps(session, encode_caps_cb, &recon_state); | 2641 | err = iterate_session_caps(session, encode_caps_cb, &recon_state); |
2624 | if (err < 0) | 2642 | if (err < 0) |
2625 | goto fail; | 2643 | goto fail; |
2626 | 2644 | ||
2645 | spin_lock(&session->s_cap_lock); | ||
2646 | session->s_cap_reconnect = 0; | ||
2647 | spin_unlock(&session->s_cap_lock); | ||
2648 | |||
2627 | /* | 2649 | /* |
2628 | * snaprealms. we provide mds with the ino, seq (version), and | 2650 | * snaprealms. we provide mds with the ino, seq (version), and |
2629 | * parent for all of our realms. If the mds has any newer info, | 2651 | * parent for all of our realms. If the mds has any newer info, |
@@ -2646,11 +2668,18 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, | |||
2646 | 2668 | ||
2647 | if (recon_state.flock) | 2669 | if (recon_state.flock) |
2648 | reply->hdr.version = cpu_to_le16(2); | 2670 | reply->hdr.version = cpu_to_le16(2); |
2649 | if (pagelist->length) { | 2671 | |
2650 | /* set up outbound data if we have any */ | 2672 | /* raced with cap release? */ |
2651 | reply->hdr.data_len = cpu_to_le32(pagelist->length); | 2673 | if (s_nr_caps != recon_state.nr_caps) { |
2652 | ceph_msg_data_add_pagelist(reply, pagelist); | 2674 | struct page *page = list_first_entry(&pagelist->head, |
2675 | struct page, lru); | ||
2676 | __le32 *addr = kmap_atomic(page); | ||
2677 | *addr = cpu_to_le32(recon_state.nr_caps); | ||
2678 | kunmap_atomic(addr); | ||
2653 | } | 2679 | } |
2680 | |||
2681 | reply->hdr.data_len = cpu_to_le32(pagelist->length); | ||
2682 | ceph_msg_data_add_pagelist(reply, pagelist); | ||
2654 | ceph_con_send(&session->s_con, reply); | 2683 | ceph_con_send(&session->s_con, reply); |
2655 | 2684 | ||
2656 | mutex_unlock(&session->s_mutex); | 2685 | mutex_unlock(&session->s_mutex); |
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index c2a19fbbe517..4c053d099ae4 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h | |||
@@ -132,6 +132,7 @@ struct ceph_mds_session { | |||
132 | struct list_head s_caps; /* all caps issued by this session */ | 132 | struct list_head s_caps; /* all caps issued by this session */ |
133 | int s_nr_caps, s_trim_caps; | 133 | int s_nr_caps, s_trim_caps; |
134 | int s_num_cap_releases; | 134 | int s_num_cap_releases; |
135 | int s_cap_reconnect; | ||
135 | struct list_head s_cap_releases; /* waiting cap_release messages */ | 136 | struct list_head s_cap_releases; /* waiting cap_release messages */ |
136 | struct list_head s_cap_releases_done; /* ready to send */ | 137 | struct list_head s_cap_releases_done; /* ready to send */ |
137 | struct ceph_cap *s_cap_iterator; | 138 | struct ceph_cap *s_cap_iterator; |
diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 6014b0a3c405..ef4ac38bb614 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h | |||
@@ -741,13 +741,7 @@ extern int ceph_add_cap(struct inode *inode, | |||
741 | int fmode, unsigned issued, unsigned wanted, | 741 | int fmode, unsigned issued, unsigned wanted, |
742 | unsigned cap, unsigned seq, u64 realmino, int flags, | 742 | unsigned cap, unsigned seq, u64 realmino, int flags, |
743 | struct ceph_cap_reservation *caps_reservation); | 743 | struct ceph_cap_reservation *caps_reservation); |
744 | extern void __ceph_remove_cap(struct ceph_cap *cap); | 744 | extern void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release); |
745 | static inline void ceph_remove_cap(struct ceph_cap *cap) | ||
746 | { | ||
747 | spin_lock(&cap->ci->i_ceph_lock); | ||
748 | __ceph_remove_cap(cap); | ||
749 | spin_unlock(&cap->ci->i_ceph_lock); | ||
750 | } | ||
751 | extern void ceph_put_cap(struct ceph_mds_client *mdsc, | 745 | extern void ceph_put_cap(struct ceph_mds_client *mdsc, |
752 | struct ceph_cap *cap); | 746 | struct ceph_cap *cap); |
753 | 747 | ||
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d9ea7ada1378..f918a998a087 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -384,6 +384,7 @@ struct smb_version_operations { | |||
384 | int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, | 384 | int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, |
385 | struct cifsFileInfo *target_file, u64 src_off, u64 len, | 385 | struct cifsFileInfo *target_file, u64 src_off, u64 len, |
386 | u64 dest_off); | 386 | u64 dest_off); |
387 | int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); | ||
387 | }; | 388 | }; |
388 | 389 | ||
389 | struct smb_version_values { | 390 | struct smb_version_values { |
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 409b45eefe70..77492301cc2b 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c | |||
@@ -26,13 +26,15 @@ | |||
26 | #include <linux/mount.h> | 26 | #include <linux/mount.h> |
27 | #include <linux/mm.h> | 27 | #include <linux/mm.h> |
28 | #include <linux/pagemap.h> | 28 | #include <linux/pagemap.h> |
29 | #include <linux/btrfs.h> | ||
30 | #include "cifspdu.h" | 29 | #include "cifspdu.h" |
31 | #include "cifsglob.h" | 30 | #include "cifsglob.h" |
32 | #include "cifsproto.h" | 31 | #include "cifsproto.h" |
33 | #include "cifs_debug.h" | 32 | #include "cifs_debug.h" |
34 | #include "cifsfs.h" | 33 | #include "cifsfs.h" |
35 | 34 | ||
35 | #define CIFS_IOCTL_MAGIC 0xCF | ||
36 | #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) | ||
37 | |||
36 | static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, | 38 | static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, |
37 | unsigned long srcfd, u64 off, u64 len, u64 destoff) | 39 | unsigned long srcfd, u64 off, u64 len, u64 destoff) |
38 | { | 40 | { |
@@ -213,7 +215,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) | |||
213 | cifs_dbg(FYI, "set compress flag rc %d\n", rc); | 215 | cifs_dbg(FYI, "set compress flag rc %d\n", rc); |
214 | } | 216 | } |
215 | break; | 217 | break; |
216 | case BTRFS_IOC_CLONE: | 218 | case CIFS_IOC_COPYCHUNK_FILE: |
217 | rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); | 219 | rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); |
218 | break; | 220 | break; |
219 | default: | 221 | default: |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 11dde4b24f8a..757da3e54d3d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid, | |||
532 | int rc; | 532 | int rc; |
533 | unsigned int ret_data_len; | 533 | unsigned int ret_data_len; |
534 | struct copychunk_ioctl *pcchunk; | 534 | struct copychunk_ioctl *pcchunk; |
535 | char *retbuf = NULL; | 535 | struct copychunk_ioctl_rsp *retbuf = NULL; |
536 | struct cifs_tcon *tcon; | ||
537 | int chunks_copied = 0; | ||
538 | bool chunk_sizes_updated = false; | ||
536 | 539 | ||
537 | pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); | 540 | pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); |
538 | 541 | ||
@@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid, | |||
547 | 550 | ||
548 | /* Note: request_res_key sets res_key null only if rc !=0 */ | 551 | /* Note: request_res_key sets res_key null only if rc !=0 */ |
549 | if (rc) | 552 | if (rc) |
550 | return rc; | 553 | goto cchunk_out; |
551 | 554 | ||
552 | /* For now array only one chunk long, will make more flexible later */ | 555 | /* For now array only one chunk long, will make more flexible later */ |
553 | pcchunk->ChunkCount = __constant_cpu_to_le32(1); | 556 | pcchunk->ChunkCount = __constant_cpu_to_le32(1); |
554 | pcchunk->Reserved = 0; | 557 | pcchunk->Reserved = 0; |
555 | pcchunk->SourceOffset = cpu_to_le64(src_off); | ||
556 | pcchunk->TargetOffset = cpu_to_le64(dest_off); | ||
557 | pcchunk->Length = cpu_to_le32(len); | ||
558 | pcchunk->Reserved2 = 0; | 558 | pcchunk->Reserved2 = 0; |
559 | 559 | ||
560 | /* Request that server copy to target from src file identified by key */ | 560 | tcon = tlink_tcon(trgtfile->tlink); |
561 | rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink), | ||
562 | trgtfile->fid.persistent_fid, | ||
563 | trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, | ||
564 | true /* is_fsctl */, (char *)pcchunk, | ||
565 | sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len); | ||
566 | 561 | ||
567 | /* BB need to special case rc = EINVAL to alter chunk size */ | 562 | while (len > 0) { |
563 | pcchunk->SourceOffset = cpu_to_le64(src_off); | ||
564 | pcchunk->TargetOffset = cpu_to_le64(dest_off); | ||
565 | pcchunk->Length = | ||
566 | cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); | ||
568 | 567 | ||
569 | cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len); | 568 | /* Request server copy to target from src identified by key */ |
569 | rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, | ||
570 | trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, | ||
571 | true /* is_fsctl */, (char *)pcchunk, | ||
572 | sizeof(struct copychunk_ioctl), (char **)&retbuf, | ||
573 | &ret_data_len); | ||
574 | if (rc == 0) { | ||
575 | if (ret_data_len != | ||
576 | sizeof(struct copychunk_ioctl_rsp)) { | ||
577 | cifs_dbg(VFS, "invalid cchunk response size\n"); | ||
578 | rc = -EIO; | ||
579 | goto cchunk_out; | ||
580 | } | ||
581 | if (retbuf->TotalBytesWritten == 0) { | ||
582 | cifs_dbg(FYI, "no bytes copied\n"); | ||
583 | rc = -EIO; | ||
584 | goto cchunk_out; | ||
585 | } | ||
586 | /* | ||
587 | * Check if server claimed to write more than we asked | ||
588 | */ | ||
589 | if (le32_to_cpu(retbuf->TotalBytesWritten) > | ||
590 | le32_to_cpu(pcchunk->Length)) { | ||
591 | cifs_dbg(VFS, "invalid copy chunk response\n"); | ||
592 | rc = -EIO; | ||
593 | goto cchunk_out; | ||
594 | } | ||
595 | if (le32_to_cpu(retbuf->ChunksWritten) != 1) { | ||
596 | cifs_dbg(VFS, "invalid num chunks written\n"); | ||
597 | rc = -EIO; | ||
598 | goto cchunk_out; | ||
599 | } | ||
600 | chunks_copied++; | ||
601 | |||
602 | src_off += le32_to_cpu(retbuf->TotalBytesWritten); | ||
603 | dest_off += le32_to_cpu(retbuf->TotalBytesWritten); | ||
604 | len -= le32_to_cpu(retbuf->TotalBytesWritten); | ||
605 | |||
606 | cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", | ||
607 | le32_to_cpu(retbuf->ChunksWritten), | ||
608 | le32_to_cpu(retbuf->ChunkBytesWritten), | ||
609 | le32_to_cpu(retbuf->TotalBytesWritten)); | ||
610 | } else if (rc == -EINVAL) { | ||
611 | if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) | ||
612 | goto cchunk_out; | ||
613 | |||
614 | cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", | ||
615 | le32_to_cpu(retbuf->ChunksWritten), | ||
616 | le32_to_cpu(retbuf->ChunkBytesWritten), | ||
617 | le32_to_cpu(retbuf->TotalBytesWritten)); | ||
618 | |||
619 | /* | ||
620 | * Check if this is the first request using these sizes, | ||
621 | * (ie check if copy succeed once with original sizes | ||
622 | * and check if the server gave us different sizes after | ||
623 | * we already updated max sizes on previous request). | ||
624 | * if not then why is the server returning an error now | ||
625 | */ | ||
626 | if ((chunks_copied != 0) || chunk_sizes_updated) | ||
627 | goto cchunk_out; | ||
628 | |||
629 | /* Check that server is not asking us to grow size */ | ||
630 | if (le32_to_cpu(retbuf->ChunkBytesWritten) < | ||
631 | tcon->max_bytes_chunk) | ||
632 | tcon->max_bytes_chunk = | ||
633 | le32_to_cpu(retbuf->ChunkBytesWritten); | ||
634 | else | ||
635 | goto cchunk_out; /* server gave us bogus size */ | ||
636 | |||
637 | /* No need to change MaxChunks since already set to 1 */ | ||
638 | chunk_sizes_updated = true; | ||
639 | } | ||
640 | } | ||
570 | 641 | ||
642 | cchunk_out: | ||
571 | kfree(pcchunk); | 643 | kfree(pcchunk); |
572 | return rc; | 644 | return rc; |
573 | } | 645 | } |
@@ -1247,6 +1319,7 @@ struct smb_version_operations smb30_operations = { | |||
1247 | .create_lease_buf = smb3_create_lease_buf, | 1319 | .create_lease_buf = smb3_create_lease_buf, |
1248 | .parse_lease_buf = smb3_parse_lease_buf, | 1320 | .parse_lease_buf = smb3_parse_lease_buf, |
1249 | .clone_range = smb2_clone_range, | 1321 | .clone_range = smb2_clone_range, |
1322 | .validate_negotiate = smb3_validate_negotiate, | ||
1250 | }; | 1323 | }; |
1251 | 1324 | ||
1252 | struct smb_version_values smb20_values = { | 1325 | struct smb_version_values smb20_values = { |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d65270c290a1..2013234b73ad 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -454,6 +454,81 @@ neg_exit: | |||
454 | return rc; | 454 | return rc; |
455 | } | 455 | } |
456 | 456 | ||
457 | int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) | ||
458 | { | ||
459 | int rc = 0; | ||
460 | struct validate_negotiate_info_req vneg_inbuf; | ||
461 | struct validate_negotiate_info_rsp *pneg_rsp; | ||
462 | u32 rsplen; | ||
463 | |||
464 | cifs_dbg(FYI, "validate negotiate\n"); | ||
465 | |||
466 | /* | ||
467 | * validation ioctl must be signed, so no point sending this if we | ||
468 | * can not sign it. We could eventually change this to selectively | ||
469 | * sign just this, the first and only signed request on a connection. | ||
470 | * This is good enough for now since a user who wants better security | ||
471 | * would also enable signing on the mount. Having validation of | ||
472 | * negotiate info for signed connections helps reduce attack vectors | ||
473 | */ | ||
474 | if (tcon->ses->server->sign == false) | ||
475 | return 0; /* validation requires signing */ | ||
476 | |||
477 | vneg_inbuf.Capabilities = | ||
478 | cpu_to_le32(tcon->ses->server->vals->req_capabilities); | ||
479 | memcpy(vneg_inbuf.Guid, cifs_client_guid, SMB2_CLIENT_GUID_SIZE); | ||
480 | |||
481 | if (tcon->ses->sign) | ||
482 | vneg_inbuf.SecurityMode = | ||
483 | cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); | ||
484 | else if (global_secflags & CIFSSEC_MAY_SIGN) | ||
485 | vneg_inbuf.SecurityMode = | ||
486 | cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); | ||
487 | else | ||
488 | vneg_inbuf.SecurityMode = 0; | ||
489 | |||
490 | vneg_inbuf.DialectCount = cpu_to_le16(1); | ||
491 | vneg_inbuf.Dialects[0] = | ||
492 | cpu_to_le16(tcon->ses->server->vals->protocol_id); | ||
493 | |||
494 | rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, | ||
495 | FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */, | ||
496 | (char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req), | ||
497 | (char **)&pneg_rsp, &rsplen); | ||
498 | |||
499 | if (rc != 0) { | ||
500 | cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); | ||
501 | return -EIO; | ||
502 | } | ||
503 | |||
504 | if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { | ||
505 | cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); | ||
506 | return -EIO; | ||
507 | } | ||
508 | |||
509 | /* check validate negotiate info response matches what we got earlier */ | ||
510 | if (pneg_rsp->Dialect != | ||
511 | cpu_to_le16(tcon->ses->server->vals->protocol_id)) | ||
512 | goto vneg_out; | ||
513 | |||
514 | if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) | ||
515 | goto vneg_out; | ||
516 | |||
517 | /* do not validate server guid because not saved at negprot time yet */ | ||
518 | |||
519 | if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | | ||
520 | SMB2_LARGE_FILES) != tcon->ses->server->capabilities) | ||
521 | goto vneg_out; | ||
522 | |||
523 | /* validate negotiate successful */ | ||
524 | cifs_dbg(FYI, "validate negotiate info successful\n"); | ||
525 | return 0; | ||
526 | |||
527 | vneg_out: | ||
528 | cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); | ||
529 | return -EIO; | ||
530 | } | ||
531 | |||
457 | int | 532 | int |
458 | SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | 533 | SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, |
459 | const struct nls_table *nls_cp) | 534 | const struct nls_table *nls_cp) |
@@ -829,6 +904,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, | |||
829 | ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) | 904 | ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) |
830 | cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); | 905 | cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); |
831 | init_copy_chunk_defaults(tcon); | 906 | init_copy_chunk_defaults(tcon); |
907 | if (tcon->ses->server->ops->validate_negotiate) | ||
908 | rc = tcon->ses->server->ops->validate_negotiate(xid, tcon); | ||
832 | tcon_exit: | 909 | tcon_exit: |
833 | free_rsp_buf(resp_buftype, rsp); | 910 | free_rsp_buf(resp_buftype, rsp); |
834 | kfree(unc_path); | 911 | kfree(unc_path); |
@@ -1214,10 +1291,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, | |||
1214 | rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); | 1291 | rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); |
1215 | rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; | 1292 | rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; |
1216 | 1293 | ||
1217 | if (rc != 0) { | 1294 | if ((rc != 0) && (rc != -EINVAL)) { |
1218 | if (tcon) | 1295 | if (tcon) |
1219 | cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); | 1296 | cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); |
1220 | goto ioctl_exit; | 1297 | goto ioctl_exit; |
1298 | } else if (rc == -EINVAL) { | ||
1299 | if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && | ||
1300 | (opcode != FSCTL_SRV_COPYCHUNK)) { | ||
1301 | if (tcon) | ||
1302 | cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); | ||
1303 | goto ioctl_exit; | ||
1304 | } | ||
1221 | } | 1305 | } |
1222 | 1306 | ||
1223 | /* check if caller wants to look at return data or just return rc */ | 1307 | /* check if caller wants to look at return data or just return rc */ |
@@ -2154,11 +2238,9 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, | |||
2154 | rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0); | 2238 | rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0); |
2155 | rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; | 2239 | rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; |
2156 | 2240 | ||
2157 | if (rc != 0) { | 2241 | if (rc != 0) |
2158 | cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); | 2242 | cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); |
2159 | goto out; | 2243 | |
2160 | } | ||
2161 | out: | ||
2162 | free_rsp_buf(resp_buftype, rsp); | 2244 | free_rsp_buf(resp_buftype, rsp); |
2163 | kfree(iov); | 2245 | kfree(iov); |
2164 | return rc; | 2246 | return rc; |
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index f88320bbb477..2022c542ea3a 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -577,13 +577,19 @@ struct copychunk_ioctl_rsp { | |||
577 | __le32 TotalBytesWritten; | 577 | __le32 TotalBytesWritten; |
578 | } __packed; | 578 | } __packed; |
579 | 579 | ||
580 | /* Response and Request are the same format */ | 580 | struct validate_negotiate_info_req { |
581 | struct validate_negotiate_info { | ||
582 | __le32 Capabilities; | 581 | __le32 Capabilities; |
583 | __u8 Guid[SMB2_CLIENT_GUID_SIZE]; | 582 | __u8 Guid[SMB2_CLIENT_GUID_SIZE]; |
584 | __le16 SecurityMode; | 583 | __le16 SecurityMode; |
585 | __le16 DialectCount; | 584 | __le16 DialectCount; |
586 | __le16 Dialect[1]; | 585 | __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ |
586 | } __packed; | ||
587 | |||
588 | struct validate_negotiate_info_rsp { | ||
589 | __le32 Capabilities; | ||
590 | __u8 Guid[SMB2_CLIENT_GUID_SIZE]; | ||
591 | __le16 SecurityMode; | ||
592 | __le16 Dialect; /* Dialect in use for the connection */ | ||
587 | } __packed; | 593 | } __packed; |
588 | 594 | ||
589 | #define RSS_CAPABLE 0x00000001 | 595 | #define RSS_CAPABLE 0x00000001 |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index b4eea105b08c..93adc64666f3 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -162,5 +162,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, | |||
162 | struct smb2_lock_element *buf); | 162 | struct smb2_lock_element *buf); |
163 | extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, | 163 | extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, |
164 | __u8 *lease_key, const __le32 lease_state); | 164 | __u8 *lease_key, const __le32 lease_state); |
165 | extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); | ||
165 | 166 | ||
166 | #endif /* _SMB2PROTO_H */ | 167 | #endif /* _SMB2PROTO_H */ |
diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index a4b2391fe66e..0e538b5c9622 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h | |||
@@ -90,7 +90,7 @@ | |||
90 | #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */ | 90 | #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */ |
91 | #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ | 91 | #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ |
92 | #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ | 92 | #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ |
93 | #define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ | 93 | #define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 |
94 | /* Perform server-side data movement */ | 94 | /* Perform server-side data movement */ |
95 | #define FSCTL_SRV_COPYCHUNK 0x001440F2 | 95 | #define FSCTL_SRV_COPYCHUNK 0x001440F2 |
96 | #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 | 96 | #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 |
diff --git a/fs/namei.c b/fs/namei.c index 8f77a8cea289..c53d3a9547f9 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -513,8 +513,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) | |||
513 | 513 | ||
514 | if (!lockref_get_not_dead(&parent->d_lockref)) { | 514 | if (!lockref_get_not_dead(&parent->d_lockref)) { |
515 | nd->path.dentry = NULL; | 515 | nd->path.dentry = NULL; |
516 | rcu_read_unlock(); | 516 | goto out; |
517 | return -ECHILD; | ||
518 | } | 517 | } |
519 | 518 | ||
520 | /* | 519 | /* |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 79b5da2acbe1..b94f93685093 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -609,7 +609,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) | |||
609 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 609 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
610 | struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; | 610 | struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; |
611 | struct sysfs_open_file *of; | 611 | struct sysfs_open_file *of; |
612 | bool has_read, has_write; | 612 | bool has_read, has_write, has_mmap; |
613 | int error = -EACCES; | 613 | int error = -EACCES; |
614 | 614 | ||
615 | /* need attr_sd for attr and ops, its parent for kobj */ | 615 | /* need attr_sd for attr and ops, its parent for kobj */ |
@@ -621,6 +621,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) | |||
621 | 621 | ||
622 | has_read = battr->read || battr->mmap; | 622 | has_read = battr->read || battr->mmap; |
623 | has_write = battr->write || battr->mmap; | 623 | has_write = battr->write || battr->mmap; |
624 | has_mmap = battr->mmap; | ||
624 | } else { | 625 | } else { |
625 | const struct sysfs_ops *ops = sysfs_file_ops(attr_sd); | 626 | const struct sysfs_ops *ops = sysfs_file_ops(attr_sd); |
626 | 627 | ||
@@ -632,6 +633,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) | |||
632 | 633 | ||
633 | has_read = ops->show; | 634 | has_read = ops->show; |
634 | has_write = ops->store; | 635 | has_write = ops->store; |
636 | has_mmap = false; | ||
635 | } | 637 | } |
636 | 638 | ||
637 | /* check perms and supported operations */ | 639 | /* check perms and supported operations */ |
@@ -649,7 +651,23 @@ static int sysfs_open_file(struct inode *inode, struct file *file) | |||
649 | if (!of) | 651 | if (!of) |
650 | goto err_out; | 652 | goto err_out; |
651 | 653 | ||
652 | mutex_init(&of->mutex); | 654 | /* |
655 | * The following is done to give a different lockdep key to | ||
656 | * @of->mutex for files which implement mmap. This is a rather | ||
657 | * crude way to avoid false positive lockdep warning around | ||
658 | * mm->mmap_sem - mmap nests @of->mutex under mm->mmap_sem and | ||
659 | * reading /sys/block/sda/trace/act_mask grabs sr_mutex, under | ||
660 | * which mm->mmap_sem nests, while holding @of->mutex. As each | ||
661 | * open file has a separate mutex, it's okay as long as those don't | ||
662 | * happen on the same file. At this point, we can't easily give | ||
663 | * each file a separate locking class. Let's differentiate on | ||
664 | * whether the file has mmap or not for now. | ||
665 | */ | ||
666 | if (has_mmap) | ||
667 | mutex_init(&of->mutex); | ||
668 | else | ||
669 | mutex_init(&of->mutex); | ||
670 | |||
653 | of->sd = attr_sd; | 671 | of->sd = attr_sd; |
654 | of->file = file; | 672 | of->file = file; |
655 | 673 | ||