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

Commit 224a7542 authored by Yan, Zheng's avatar Yan, Zheng Committed by Ilya Dryomov
Browse files

ceph: tolerate bad i_size for symlink inode



A mds bug can cause symlink's size to be truncated to zero.

Signed-off-by: default avatarYan, Zheng <zyan@redhat.com>
parent 1b1bc16d
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -582,6 +582,11 @@ int ceph_drop_inode(struct inode *inode)
	return 1;
}

static inline blkcnt_t calc_inode_blocks(u64 size)
{
	return (size + (1<<9) - 1) >> 9;
}

/*
 * Helpers to fill in size, ctime, mtime, and atime.  We have to be
 * careful because either the client or MDS may have more up to date
@@ -604,7 +609,7 @@ int ceph_fill_file_size(struct inode *inode, int issued,
			size = 0;
		}
		i_size_write(inode, size);
		inode->i_blocks = (size + (1<<9) - 1) >> 9;
		inode->i_blocks = calc_inode_blocks(size);
		ci->i_reported_size = size;
		if (truncate_seq != ci->i_truncate_seq) {
			dout("truncate_seq %u -> %u\n",
@@ -863,9 +868,13 @@ static int fill_inode(struct inode *inode, struct page *locked_page,

			spin_unlock(&ci->i_ceph_lock);

			err = -EINVAL;
			if (WARN_ON(symlen != i_size_read(inode)))
				goto out;
			if (symlen != i_size_read(inode)) {
				pr_err("fill_inode %llx.%llx BAD symlink "
					"size %lld\n", ceph_vinop(inode),
					i_size_read(inode));
				i_size_write(inode, symlen);
				inode->i_blocks = calc_inode_blocks(symlen);
			}

			err = -ENOMEM;
			sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS);
@@ -1629,7 +1638,7 @@ int ceph_inode_set_size(struct inode *inode, loff_t size)
	spin_lock(&ci->i_ceph_lock);
	dout("set_size %p %llu -> %llu\n", inode, inode->i_size, size);
	i_size_write(inode, size);
	inode->i_blocks = (size + (1 << 9) - 1) >> 9;
	inode->i_blocks = calc_inode_blocks(size);

	/* tell the MDS if we are approaching max_size */
	if ((size << 1) >= ci->i_max_size &&
@@ -2002,8 +2011,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
		if ((issued & CEPH_CAP_FILE_EXCL) &&
		    attr->ia_size > inode->i_size) {
			i_size_write(inode, attr->ia_size);
			inode->i_blocks =
				(attr->ia_size + (1 << 9) - 1) >> 9;
			inode->i_blocks = calc_inode_blocks(attr->ia_size);
			inode->i_ctime = attr->ia_ctime;
			ci->i_reported_size = attr->ia_size;
			dirtied |= CEPH_CAP_FILE_EXCL;