Commit d1aabc21 authored by Zhan Xusheng's avatar Zhan Xusheng Committed by Namjae Jeon
Browse files

ntfs: fix missing kstrdup() error check in ntfs_write_volume_label()



ntfs_write_volume_label() does not check the return value of
kstrdup().  If the allocation fails, vol->volume_label is set to
NULL while the function returns success.  A subsequent
FS_IOC_GETFSLABEL then returns an empty string even though the
on-disk label was updated correctly.

Fix by allocating the new label before taking vol_ni->mrec_lock and
updating any on-disk metadata, so an -ENOMEM from kstrdup() leaves
both the in-memory and on-disk labels untouched and consistent.  On
success the preallocated copy replaces the old vol->volume_label.
Also move mark_inode_dirty_sync() into the success path so that it
is not called when no metadata was actually modified.

Fixes: 6251f0b0 ("ntfs: update super block operations")
Suggested-by: default avatarHyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: default avatarZhan Xusheng <zhanxusheng@xiaomi.com>
Reviewed-by: default avatarHyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
parent 3086c49a
Loading
Loading
Loading
Loading
+18 −4
Original line number Diff line number Diff line
@@ -413,6 +413,7 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label)
{
	struct ntfs_inode *vol_ni = NTFS_I(vol->vol_ino);
	struct ntfs_attr_search_ctx *ctx;
	char *new_label;
	__le16 *uname;
	int uname_len, ret;

@@ -433,6 +434,17 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label)
		return -EINVAL;
	}

	/*
	 * Allocate the in-memory label copy up front. If kstrdup() fails we
	 * bail out before touching on-disk metadata, so the in-memory label
	 * and the on-disk label stay in sync.
	 */
	new_label = kstrdup(label, GFP_KERNEL);
	if (!new_label) {
		kvfree(uname);
		return -ENOMEM;
	}

	mutex_lock(&vol_ni->mrec_lock);
	ctx = ntfs_attr_get_search_ctx(vol_ni, NULL);
	if (!ctx) {
@@ -450,12 +462,14 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label)
out:
	mutex_unlock(&vol_ni->mrec_lock);
	kvfree(uname);
	mark_inode_dirty_sync(vol->vol_ino);

	if (ret >= 0) {
		kfree(vol->volume_label);
		vol->volume_label = kstrdup(label, GFP_KERNEL);
		vol->volume_label = new_label;
		mark_inode_dirty_sync(vol->vol_ino);
		ret = 0;
	} else {
		kfree(new_label);
	}
	return ret;
}