diff options
author | Luis Henriques <lhenriques@suse.com> | 2018-01-05 05:47:20 -0500 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2018-04-02 05:17:52 -0400 |
commit | cafe21a4fb3075fb2980caba8fdb533a1bdb52b0 (patch) | |
tree | ccdcecba6b1fb66f93c95032fb2be668e10aa7fe /fs | |
parent | b7a2921765cf796280baf653a52b22b52e0ba266 (diff) |
ceph: quota: don't allow cross-quota renames
This patch changes ceph_rename so that -EXDEV is returned if an attempt is
made to mv a file between two different dir trees with different quotas
setup.
Signed-off-by: Luis Henriques <lhenriques@suse.com>
Reviewed-by: "Yan, Zheng" <zyan@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ceph/dir.c | 5 | ||||
-rw-r--r-- | fs/ceph/quota.c | 69 | ||||
-rw-r--r-- | fs/ceph/super.h | 1 |
3 files changed, 75 insertions, 0 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 7d9851cd51bb..1f60498c4631 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -1080,6 +1080,11 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1080 | else | 1080 | else |
1081 | return -EROFS; | 1081 | return -EROFS; |
1082 | } | 1082 | } |
1083 | /* don't allow cross-quota renames */ | ||
1084 | if ((old_dir != new_dir) && | ||
1085 | (!ceph_quota_is_same_realm(old_dir, new_dir))) | ||
1086 | return -EXDEV; | ||
1087 | |||
1083 | dout("rename dir %p dentry %p to dir %p dentry %p\n", | 1088 | dout("rename dir %p dentry %p to dir %p dentry %p\n", |
1084 | old_dir, old_dentry, new_dir, new_dentry); | 1089 | old_dir, old_dentry, new_dir, new_dentry); |
1085 | req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); | 1090 | req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); |
diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c index cf1c78c4a4d2..5d7dada91a57 100644 --- a/fs/ceph/quota.c +++ b/fs/ceph/quota.c | |||
@@ -21,6 +21,11 @@ | |||
21 | #include "super.h" | 21 | #include "super.h" |
22 | #include "mds_client.h" | 22 | #include "mds_client.h" |
23 | 23 | ||
24 | static inline bool ceph_has_quota(struct ceph_inode_info *ci) | ||
25 | { | ||
26 | return (ci && (ci->i_max_files || ci->i_max_bytes)); | ||
27 | } | ||
28 | |||
24 | void ceph_handle_quota(struct ceph_mds_client *mdsc, | 29 | void ceph_handle_quota(struct ceph_mds_client *mdsc, |
25 | struct ceph_mds_session *session, | 30 | struct ceph_mds_session *session, |
26 | struct ceph_msg *msg) | 31 | struct ceph_msg *msg) |
@@ -64,6 +69,70 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc, | |||
64 | iput(inode); | 69 | iput(inode); |
65 | } | 70 | } |
66 | 71 | ||
72 | /* | ||
73 | * This function walks through the snaprealm for an inode and returns the | ||
74 | * ceph_snap_realm for the first snaprealm that has quotas set (either max_files | ||
75 | * or max_bytes). If the root is reached, return the root ceph_snap_realm | ||
76 | * instead. | ||
77 | * | ||
78 | * Note that the caller is responsible for calling ceph_put_snap_realm() on the | ||
79 | * returned realm. | ||
80 | */ | ||
81 | static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc, | ||
82 | struct inode *inode) | ||
83 | { | ||
84 | struct ceph_inode_info *ci = NULL; | ||
85 | struct ceph_snap_realm *realm, *next; | ||
86 | struct ceph_vino vino; | ||
87 | struct inode *in; | ||
88 | |||
89 | realm = ceph_inode(inode)->i_snap_realm; | ||
90 | ceph_get_snap_realm(mdsc, realm); | ||
91 | while (realm) { | ||
92 | vino.ino = realm->ino; | ||
93 | vino.snap = CEPH_NOSNAP; | ||
94 | in = ceph_find_inode(inode->i_sb, vino); | ||
95 | if (!in) { | ||
96 | pr_warn("Failed to find inode for %llu\n", vino.ino); | ||
97 | break; | ||
98 | } | ||
99 | ci = ceph_inode(in); | ||
100 | if (ceph_has_quota(ci) || (ci->i_vino.ino == CEPH_INO_ROOT)) { | ||
101 | iput(in); | ||
102 | return realm; | ||
103 | } | ||
104 | iput(in); | ||
105 | next = realm->parent; | ||
106 | ceph_get_snap_realm(mdsc, next); | ||
107 | ceph_put_snap_realm(mdsc, realm); | ||
108 | realm = next; | ||
109 | } | ||
110 | if (realm) | ||
111 | ceph_put_snap_realm(mdsc, realm); | ||
112 | |||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | bool ceph_quota_is_same_realm(struct inode *old, struct inode *new) | ||
117 | { | ||
118 | struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc; | ||
119 | struct ceph_snap_realm *old_realm, *new_realm; | ||
120 | bool is_same; | ||
121 | |||
122 | down_read(&mdsc->snap_rwsem); | ||
123 | old_realm = get_quota_realm(mdsc, old); | ||
124 | new_realm = get_quota_realm(mdsc, new); | ||
125 | is_same = (old_realm == new_realm); | ||
126 | up_read(&mdsc->snap_rwsem); | ||
127 | |||
128 | if (old_realm) | ||
129 | ceph_put_snap_realm(mdsc, old_realm); | ||
130 | if (new_realm) | ||
131 | ceph_put_snap_realm(mdsc, new_realm); | ||
132 | |||
133 | return is_same; | ||
134 | } | ||
135 | |||
67 | enum quota_check_op { | 136 | enum quota_check_op { |
68 | QUOTA_CHECK_MAX_FILES_OP /* check quota max_files limit */ | 137 | QUOTA_CHECK_MAX_FILES_OP /* check quota max_files limit */ |
69 | }; | 138 | }; |
diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 4afc6cca8786..eea6f70f3bf9 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h | |||
@@ -1078,5 +1078,6 @@ extern void ceph_handle_quota(struct ceph_mds_client *mdsc, | |||
1078 | struct ceph_mds_session *session, | 1078 | struct ceph_mds_session *session, |
1079 | struct ceph_msg *msg); | 1079 | struct ceph_msg *msg); |
1080 | extern bool ceph_quota_is_max_files_exceeded(struct inode *inode); | 1080 | extern bool ceph_quota_is_max_files_exceeded(struct inode *inode); |
1081 | extern bool ceph_quota_is_same_realm(struct inode *old, struct inode *new); | ||
1081 | 1082 | ||
1082 | #endif /* _FS_CEPH_SUPER_H */ | 1083 | #endif /* _FS_CEPH_SUPER_H */ |