diff options
author | Sage Weil <sage@newdream.net> | 2011-07-26 14:30:29 -0400 |
---|---|---|
committer | Sage Weil <sage@newdream.net> | 2011-07-26 14:30:29 -0400 |
commit | 5f21c96dd5c615341963036ae8f5e4f5227a818d (patch) | |
tree | 898c5781623b68527427d201e3f975827d08935c | |
parent | 48d0cbd1242aac969560ef8b90f26ee3b09a6a5c (diff) |
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>
-rw-r--r-- | fs/ceph/dir.c | 15 | ||||
-rw-r--r-- | fs/ceph/file.c | 8 | ||||
-rw-r--r-- | fs/ceph/inode.c | 4 | ||||
-rw-r--r-- | fs/ceph/ioctl.c | 4 | ||||
-rw-r--r-- | fs/ceph/super.h | 9 | ||||
-rw-r--r-- | fs/ceph/xattr.c | 8 |
6 files changed, 33 insertions, 15 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 883c9546111d..ed296ec121d1 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -71,6 +71,21 @@ out_unlock: | |||
71 | return 0; | 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 | /* |
diff --git a/fs/ceph/file.c b/fs/ceph/file.c index f34d47d66e7c..45fbd69daabe 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c | |||
@@ -122,7 +122,7 @@ int ceph_open(struct inode *inode, struct file *file) | |||
122 | struct ceph_mds_client *mdsc = fsc->mdsc; | 122 | struct ceph_mds_client *mdsc = fsc->mdsc; |
123 | struct ceph_mds_request *req; | 123 | struct ceph_mds_request *req; |
124 | struct ceph_file_info *cf = file->private_data; | 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 | int err; | 126 | int err; |
127 | int flags, fmode, wanted; | 127 | int flags, fmode, wanted; |
128 | 128 | ||
@@ -194,8 +194,10 @@ int ceph_open(struct inode *inode, struct file *file) | |||
194 | req->r_inode = inode; | 194 | req->r_inode = inode; |
195 | ihold(inode); | 195 | ihold(inode); |
196 | req->r_num_caps = 1; | 196 | req->r_num_caps = 1; |
197 | err = ceph_mdsc_do_request(mdsc, (flags & (O_CREAT|O_TRUNC)) ? | 197 | if (flags & (O_CREAT|O_TRUNC)) |
198 | parent_inode : NULL, req); | 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 | if (!err) | 201 | if (!err) |
200 | err = ceph_init_file(inode, file, req->r_fmode); | 202 | err = ceph_init_file(inode, file, req->r_fmode); |
201 | ceph_mdsc_put_request(req); | 203 | ceph_mdsc_put_request(req); |
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 2717dc4e443c..a7db56f1523b 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
@@ -1562,7 +1562,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) | |||
1562 | { | 1562 | { |
1563 | struct inode *inode = dentry->d_inode; | 1563 | struct inode *inode = dentry->d_inode; |
1564 | struct ceph_inode_info *ci = ceph_inode(inode); | 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 | const unsigned int ia_valid = attr->ia_valid; | 1566 | const unsigned int ia_valid = attr->ia_valid; |
1567 | struct ceph_mds_request *req; | 1567 | struct ceph_mds_request *req; |
1568 | struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; | 1568 | struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; |
@@ -1745,7 +1745,9 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) | |||
1745 | req->r_inode_drop = release; | 1745 | req->r_inode_drop = release; |
1746 | req->r_args.setattr.mask = cpu_to_le32(mask); | 1746 | req->r_args.setattr.mask = cpu_to_le32(mask); |
1747 | req->r_num_caps = 1; | 1747 | req->r_num_caps = 1; |
1748 | parent_inode = ceph_get_dentry_parent_inode(dentry); | ||
1748 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); | 1749 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
1750 | iput(parent_inode); | ||
1749 | } | 1751 | } |
1750 | dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err, | 1752 | dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err, |
1751 | ceph_cap_string(dirtied), mask); | 1753 | ceph_cap_string(dirtied), mask); |
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index a757a5680578..3b256b50f7d8 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c | |||
@@ -38,7 +38,7 @@ static long ceph_ioctl_get_layout(struct file *file, void __user *arg) | |||
38 | static long ceph_ioctl_set_layout(struct file *file, void __user *arg) | 38 | static long ceph_ioctl_set_layout(struct file *file, void __user *arg) |
39 | { | 39 | { |
40 | struct inode *inode = file->f_dentry->d_inode; | 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 | struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; | 42 | struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; |
43 | struct ceph_mds_request *req; | 43 | struct ceph_mds_request *req; |
44 | struct ceph_ioctl_layout l; | 44 | struct ceph_ioctl_layout l; |
@@ -87,7 +87,9 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) | |||
87 | req->r_args.setlayout.layout.fl_pg_preferred = | 87 | req->r_args.setlayout.layout.fl_pg_preferred = |
88 | cpu_to_le32(l.preferred_osd); | 88 | cpu_to_le32(l.preferred_osd); |
89 | 89 | ||
90 | parent_inode = ceph_get_dentry_parent_inode(file->f_dentry); | ||
90 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); | 91 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
92 | iput(parent_inode); | ||
91 | ceph_mdsc_put_request(req); | 93 | ceph_mdsc_put_request(req); |
92 | return err; | 94 | return err; |
93 | } | 95 | } |
diff --git a/fs/ceph/super.h b/fs/ceph/super.h index c24891a5bec2..c1eb9a014b74 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h | |||
@@ -801,6 +801,7 @@ extern void ceph_dentry_lru_touch(struct dentry *dn); | |||
801 | extern void ceph_dentry_lru_del(struct dentry *dn); | 801 | extern void ceph_dentry_lru_del(struct dentry *dn); |
802 | extern void ceph_invalidate_dentry_lease(struct dentry *dentry); | 802 | extern void ceph_invalidate_dentry_lease(struct dentry *dentry); |
803 | extern unsigned ceph_dentry_hash(struct dentry *dn); | 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 | * our d_ops vary depending on whether the inode is live, | 807 | * our d_ops vary depending on whether the inode is live, |
@@ -823,14 +824,6 @@ extern int ceph_encode_locks(struct inode *i, struct ceph_pagelist *p, | |||
823 | int p_locks, int f_locks); | 824 | int p_locks, int f_locks); |
824 | extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c); | 825 | extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c); |
825 | 826 | ||
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 | |||
834 | /* debugfs.c */ | 827 | /* debugfs.c */ |
835 | extern int ceph_fs_debugfs_init(struct ceph_fs_client *client); | 828 | extern int ceph_fs_debugfs_init(struct ceph_fs_client *client); |
836 | extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client); | 829 | extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client); |
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index f42d730f1b66..96c6739a0280 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c | |||
@@ -629,7 +629,7 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name, | |||
629 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); | 629 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); |
630 | struct inode *inode = dentry->d_inode; | 630 | struct inode *inode = dentry->d_inode; |
631 | struct ceph_inode_info *ci = ceph_inode(inode); | 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 | struct ceph_mds_request *req; | 633 | struct ceph_mds_request *req; |
634 | struct ceph_mds_client *mdsc = fsc->mdsc; | 634 | struct ceph_mds_client *mdsc = fsc->mdsc; |
635 | int err; | 635 | int err; |
@@ -677,7 +677,9 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name, | |||
677 | req->r_data_len = size; | 677 | req->r_data_len = size; |
678 | 678 | ||
679 | dout("xattr.ver (before): %lld\n", ci->i_xattrs.version); | 679 | dout("xattr.ver (before): %lld\n", ci->i_xattrs.version); |
680 | parent_inode = ceph_get_dentry_parent_inode(dentry); | ||
680 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); | 681 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
682 | iput(parent_inode); | ||
681 | ceph_mdsc_put_request(req); | 683 | ceph_mdsc_put_request(req); |
682 | dout("xattr.ver (after): %lld\n", ci->i_xattrs.version); | 684 | dout("xattr.ver (after): %lld\n", ci->i_xattrs.version); |
683 | 685 | ||
@@ -788,7 +790,7 @@ static int ceph_send_removexattr(struct dentry *dentry, const char *name) | |||
788 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); | 790 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); |
789 | struct ceph_mds_client *mdsc = fsc->mdsc; | 791 | struct ceph_mds_client *mdsc = fsc->mdsc; |
790 | struct inode *inode = dentry->d_inode; | 792 | struct inode *inode = dentry->d_inode; |
791 | struct inode *parent_inode = dentry->d_parent->d_inode; | 793 | struct inode *parent_inode; |
792 | struct ceph_mds_request *req; | 794 | struct ceph_mds_request *req; |
793 | int err; | 795 | int err; |
794 | 796 | ||
@@ -802,7 +804,9 @@ static int ceph_send_removexattr(struct dentry *dentry, const char *name) | |||
802 | req->r_num_caps = 1; | 804 | req->r_num_caps = 1; |
803 | req->r_path2 = kstrdup(name, GFP_NOFS); | 805 | req->r_path2 = kstrdup(name, GFP_NOFS); |
804 | 806 | ||
807 | parent_inode = ceph_get_dentry_parent_inode(dentry); | ||
805 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); | 808 | err = ceph_mdsc_do_request(mdsc, parent_inode, req); |
809 | iput(parent_inode); | ||
806 | ceph_mdsc_put_request(req); | 810 | ceph_mdsc_put_request(req); |
807 | return err; | 811 | return err; |
808 | } | 812 | } |