Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cafe21a4 authored by Luis Henriques's avatar Luis Henriques Committed by Ilya Dryomov
Browse files

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: default avatarLuis Henriques <lhenriques@suse.com>
Reviewed-by: default avatar"Yan, Zheng" <zyan@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent b7a29217
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1080,6 +1080,11 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
		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);
+69 −0
Original line number Diff line number Diff line
@@ -21,6 +21,11 @@
#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)
@@ -64,6 +69,70 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc,
	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 */
};
+1 −0
Original line number Diff line number Diff line
@@ -1078,5 +1078,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);
extern bool ceph_quota_is_same_realm(struct inode *old, struct inode *new);

#endif /* _FS_CEPH_SUPER_H */