]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
ceph: quota: don't allow cross-quota renames
authorLuis Henriques <lhenriques@suse.com>
Fri, 5 Jan 2018 10:47:20 +0000 (10:47 +0000)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Tue, 2 Jul 2019 16:30:46 +0000 (18:30 +0200)
BugLink: https://bugs.launchpad.net/bugs/1834235
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>
(cherry picked from commit cafe21a4fb3075fb2980caba8fdb533a1bdb52b0)
Signed-off-by: Connor Kuehl <connor.kuehl@canonical.com>
Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
fs/ceph/dir.c
fs/ceph/quota.c
fs/ceph/super.h

index a6490d002ef596c13542b3a85bd40739d5fbe494..733f21cb8b4dcd2793c40a2ff041a7752d6ccfb6 100644 (file)
@@ -1096,6 +1096,11 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
                else
                        return -EROFS;
        }
                else
                        return -EROFS;
        }
+       /* don't allow cross-quota renames */
+       if ((old_dir != new_dir) &&
+           (!ceph_quota_is_same_realm(old_dir, new_dir)))
+               return -EXDEV;
+
        dout("rename dir %p dentry %p to dir %p dentry %p\n",
             old_dir, old_dentry, new_dir, new_dentry);
        req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
        dout("rename dir %p dentry %p to dir %p dentry %p\n",
             old_dir, old_dentry, new_dir, new_dentry);
        req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
index cf1c78c4a4d29ac574afda868b8e53f5321c714b..5d7dada91a572cc7aab8f24e62a3a7d63b2562e8 100644 (file)
 #include "super.h"
 #include "mds_client.h"
 
 #include "super.h"
 #include "mds_client.h"
 
+static inline bool ceph_has_quota(struct ceph_inode_info *ci)
+{
+       return (ci && (ci->i_max_files || ci->i_max_bytes));
+}
+
 void ceph_handle_quota(struct ceph_mds_client *mdsc,
                       struct ceph_mds_session *session,
                       struct ceph_msg *msg)
 void ceph_handle_quota(struct ceph_mds_client *mdsc,
                       struct ceph_mds_session *session,
                       struct ceph_msg *msg)
@@ -64,6 +69,70 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc,
        iput(inode);
 }
 
        iput(inode);
 }
 
+/*
+ * This function walks through the snaprealm for an inode and returns the
+ * ceph_snap_realm for the first snaprealm that has quotas set (either max_files
+ * or max_bytes).  If the root is reached, return the root ceph_snap_realm
+ * instead.
+ *
+ * Note that the caller is responsible for calling ceph_put_snap_realm() on the
+ * returned realm.
+ */
+static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
+                                              struct inode *inode)
+{
+       struct ceph_inode_info *ci = NULL;
+       struct ceph_snap_realm *realm, *next;
+       struct ceph_vino vino;
+       struct inode *in;
+
+       realm = ceph_inode(inode)->i_snap_realm;
+       ceph_get_snap_realm(mdsc, realm);
+       while (realm) {
+               vino.ino = realm->ino;
+               vino.snap = CEPH_NOSNAP;
+               in = ceph_find_inode(inode->i_sb, vino);
+               if (!in) {
+                       pr_warn("Failed to find inode for %llu\n", vino.ino);
+                       break;
+               }
+               ci = ceph_inode(in);
+               if (ceph_has_quota(ci) || (ci->i_vino.ino == CEPH_INO_ROOT)) {
+                       iput(in);
+                       return realm;
+               }
+               iput(in);
+               next = realm->parent;
+               ceph_get_snap_realm(mdsc, next);
+               ceph_put_snap_realm(mdsc, realm);
+               realm = next;
+       }
+       if (realm)
+               ceph_put_snap_realm(mdsc, realm);
+
+       return NULL;
+}
+
+bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
+{
+       struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
+       struct ceph_snap_realm *old_realm, *new_realm;
+       bool is_same;
+
+       down_read(&mdsc->snap_rwsem);
+       old_realm = get_quota_realm(mdsc, old);
+       new_realm = get_quota_realm(mdsc, new);
+       is_same = (old_realm == new_realm);
+       up_read(&mdsc->snap_rwsem);
+
+       if (old_realm)
+               ceph_put_snap_realm(mdsc, old_realm);
+       if (new_realm)
+               ceph_put_snap_realm(mdsc, new_realm);
+
+       return is_same;
+}
+
 enum quota_check_op {
        QUOTA_CHECK_MAX_FILES_OP        /* check quota max_files limit */
 };
 enum quota_check_op {
        QUOTA_CHECK_MAX_FILES_OP        /* check quota max_files limit */
 };
index 597e8018987c4890186903adbee092ca31898d50..54e7f2ed89635ebd1582c9b64905aea5d7dd4a3c 100644 (file)
@@ -1079,5 +1079,6 @@ extern void ceph_handle_quota(struct ceph_mds_client *mdsc,
                              struct ceph_mds_session *session,
                              struct ceph_msg *msg);
 extern bool ceph_quota_is_max_files_exceeded(struct inode *inode);
                              struct ceph_mds_session *session,
                              struct ceph_msg *msg);
 extern bool ceph_quota_is_max_files_exceeded(struct inode *inode);
+extern bool ceph_quota_is_same_realm(struct inode *old, struct inode *new);
 
 #endif /* _FS_CEPH_SUPER_H */
 
 #endif /* _FS_CEPH_SUPER_H */