Commit 5f21c96dd5c615341963036ae8f5e4f5227a818d
1 parent
48d0cbd124
Exists in
master
and in
4 other branches
ceph: protect access to d_parent
d_parent is protected by d_lock: use it when looking up a dentry's parent directory inode. Also take a reference and drop it in the caller to avoid a use-after-free. Reported-by: Al Viro <viro@ZenIV.linux.org.uk> Reviewed-by: Yehuda Sadeh <yehuda@hq.newdream.net> Signed-off-by: Sage Weil <sage@newdream.net>
Showing 6 changed files with 33 additions and 15 deletions Side-by-side Diff
fs/ceph/dir.c
... | ... | @@ -71,6 +71,21 @@ |
71 | 71 | return 0; |
72 | 72 | } |
73 | 73 | |
74 | +struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry) | |
75 | +{ | |
76 | + struct inode *inode = NULL; | |
77 | + | |
78 | + if (!dentry) | |
79 | + return NULL; | |
80 | + | |
81 | + spin_lock(&dentry->d_lock); | |
82 | + if (dentry->d_parent) { | |
83 | + inode = dentry->d_parent->d_inode; | |
84 | + ihold(inode); | |
85 | + } | |
86 | + spin_unlock(&dentry->d_lock); | |
87 | + return inode; | |
88 | +} | |
74 | 89 | |
75 | 90 | |
76 | 91 | /* |
fs/ceph/file.c
... | ... | @@ -122,7 +122,7 @@ |
122 | 122 | struct ceph_mds_client *mdsc = fsc->mdsc; |
123 | 123 | struct ceph_mds_request *req; |
124 | 124 | struct ceph_file_info *cf = file->private_data; |
125 | - struct inode *parent_inode = file->f_dentry->d_parent->d_inode; | |
125 | + struct inode *parent_inode = NULL; | |
126 | 126 | int err; |
127 | 127 | int flags, fmode, wanted; |
128 | 128 | |
... | ... | @@ -194,8 +194,10 @@ |
194 | 194 | req->r_inode = inode; |
195 | 195 | ihold(inode); |
196 | 196 | req->r_num_caps = 1; |
197 | - err = ceph_mdsc_do_request(mdsc, (flags & (O_CREAT|O_TRUNC)) ? | |
198 | - parent_inode : NULL, req); | |
197 | + if (flags & (O_CREAT|O_TRUNC)) | |
198 | + parent_inode = ceph_get_dentry_parent_inode(file->f_dentry); | |
199 | + err = ceph_mdsc_do_request(mdsc, parent_inode, req); | |
200 | + iput(parent_inode); | |
199 | 201 | if (!err) |
200 | 202 | err = ceph_init_file(inode, file, req->r_fmode); |
201 | 203 | ceph_mdsc_put_request(req); |
fs/ceph/inode.c
... | ... | @@ -1562,7 +1562,7 @@ |
1562 | 1562 | { |
1563 | 1563 | struct inode *inode = dentry->d_inode; |
1564 | 1564 | struct ceph_inode_info *ci = ceph_inode(inode); |
1565 | - struct inode *parent_inode = dentry->d_parent->d_inode; | |
1565 | + struct inode *parent_inode; | |
1566 | 1566 | const unsigned int ia_valid = attr->ia_valid; |
1567 | 1567 | struct ceph_mds_request *req; |
1568 | 1568 | struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; |
1569 | 1569 | |
... | ... | @@ -1745,7 +1745,9 @@ |
1745 | 1745 | req->r_inode_drop = release; |
1746 | 1746 | req->r_args.setattr.mask = cpu_to_le32(mask); |
1747 | 1747 | req->r_num_caps = 1; |
1748 | + parent_inode = ceph_get_dentry_parent_inode(dentry); | |
1748 | 1749 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
1750 | + iput(parent_inode); | |
1749 | 1751 | } |
1750 | 1752 | dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err, |
1751 | 1753 | ceph_cap_string(dirtied), mask); |
fs/ceph/ioctl.c
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | static long ceph_ioctl_set_layout(struct file *file, void __user *arg) |
39 | 39 | { |
40 | 40 | struct inode *inode = file->f_dentry->d_inode; |
41 | - struct inode *parent_inode = file->f_dentry->d_parent->d_inode; | |
41 | + struct inode *parent_inode; | |
42 | 42 | struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; |
43 | 43 | struct ceph_mds_request *req; |
44 | 44 | struct ceph_ioctl_layout l; |
45 | 45 | |
... | ... | @@ -87,7 +87,9 @@ |
87 | 87 | req->r_args.setlayout.layout.fl_pg_preferred = |
88 | 88 | cpu_to_le32(l.preferred_osd); |
89 | 89 | |
90 | + parent_inode = ceph_get_dentry_parent_inode(file->f_dentry); | |
90 | 91 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
92 | + iput(parent_inode); | |
91 | 93 | ceph_mdsc_put_request(req); |
92 | 94 | return err; |
93 | 95 | } |
fs/ceph/super.h
... | ... | @@ -801,6 +801,7 @@ |
801 | 801 | extern void ceph_dentry_lru_del(struct dentry *dn); |
802 | 802 | extern void ceph_invalidate_dentry_lease(struct dentry *dentry); |
803 | 803 | extern unsigned ceph_dentry_hash(struct dentry *dn); |
804 | +extern struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry); | |
804 | 805 | |
805 | 806 | /* |
806 | 807 | * our d_ops vary depending on whether the inode is live, |
... | ... | @@ -822,14 +823,6 @@ |
822 | 823 | extern int ceph_encode_locks(struct inode *i, struct ceph_pagelist *p, |
823 | 824 | int p_locks, int f_locks); |
824 | 825 | extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c); |
825 | - | |
826 | -static inline struct inode *get_dentry_parent_inode(struct dentry *dentry) | |
827 | -{ | |
828 | - if (dentry && dentry->d_parent) | |
829 | - return dentry->d_parent->d_inode; | |
830 | - | |
831 | - return NULL; | |
832 | -} | |
833 | 826 | |
834 | 827 | /* debugfs.c */ |
835 | 828 | extern int ceph_fs_debugfs_init(struct ceph_fs_client *client); |
fs/ceph/xattr.c
... | ... | @@ -629,7 +629,7 @@ |
629 | 629 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); |
630 | 630 | struct inode *inode = dentry->d_inode; |
631 | 631 | struct ceph_inode_info *ci = ceph_inode(inode); |
632 | - struct inode *parent_inode = dentry->d_parent->d_inode; | |
632 | + struct inode *parent_inode; | |
633 | 633 | struct ceph_mds_request *req; |
634 | 634 | struct ceph_mds_client *mdsc = fsc->mdsc; |
635 | 635 | int err; |
636 | 636 | |
... | ... | @@ -677,7 +677,9 @@ |
677 | 677 | req->r_data_len = size; |
678 | 678 | |
679 | 679 | dout("xattr.ver (before): %lld\n", ci->i_xattrs.version); |
680 | + parent_inode = ceph_get_dentry_parent_inode(dentry); | |
680 | 681 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
682 | + iput(parent_inode); | |
681 | 683 | ceph_mdsc_put_request(req); |
682 | 684 | dout("xattr.ver (after): %lld\n", ci->i_xattrs.version); |
683 | 685 | |
... | ... | @@ -788,7 +790,7 @@ |
788 | 790 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); |
789 | 791 | struct ceph_mds_client *mdsc = fsc->mdsc; |
790 | 792 | struct inode *inode = dentry->d_inode; |
791 | - struct inode *parent_inode = dentry->d_parent->d_inode; | |
793 | + struct inode *parent_inode; | |
792 | 794 | struct ceph_mds_request *req; |
793 | 795 | int err; |
794 | 796 | |
795 | 797 | |
... | ... | @@ -802,7 +804,9 @@ |
802 | 804 | req->r_num_caps = 1; |
803 | 805 | req->r_path2 = kstrdup(name, GFP_NOFS); |
804 | 806 | |
807 | + parent_inode = ceph_get_dentry_parent_inode(dentry); | |
805 | 808 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
809 | + iput(parent_inode); | |
806 | 810 | ceph_mdsc_put_request(req); |
807 | 811 | return err; |
808 | 812 | } |