Blame SOURCES/0028-fs-ntfs3-Rework-file-operations.patch

Kmods SIG 8b815c
From 78ab59fee07f22464f32eafebab2bd97ba94ff2d Mon Sep 17 00:00:00 2001
Kmods SIG 8b815c
From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Kmods SIG 8b815c
Date: Tue, 31 Aug 2021 18:52:39 +0300
Kmods SIG 8b815c
Subject: [Backport 78ab59fee07f] src: Rework file operations
Kmods SIG 8b815c
Kmods SIG 8b815c
Rename now works "Add new name and remove old name".
Kmods SIG 8b815c
"Remove old name and add new name" may result in bad inode
Kmods SIG 8b815c
if we can't add new name and then can't restore (add) old name.
Kmods SIG 8b815c
Kmods SIG 8b815c
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Kmods SIG 8b815c
---
Kmods SIG 8b815c
 src/attrib.c   |  17 +--
Kmods SIG 8b815c
 src/attrlist.c |  26 ++--
Kmods SIG 8b815c
 src/frecord.c  | 342 ++++++++++++++++++++++++++++++++++++--------
Kmods SIG 8b815c
 src/fsntfs.c   |  87 +++++------
Kmods SIG 8b815c
 src/index.c    |  45 +++---
Kmods SIG 8b815c
 src/inode.c    | 275 ++++++++++++-----------------------
Kmods SIG 8b815c
 src/namei.c    | 236 ++++++++----------------------
Kmods SIG 8b815c
 src/ntfs_fs.h  |  31 +++-
Kmods SIG 8b815c
 src/record.c   |   8 +-
Kmods SIG 8b815c
 src/xattr.c    |  25 ++--
Kmods SIG 8b815c
 10 files changed, 563 insertions(+), 529 deletions(-)
Kmods SIG 8b815c
Kmods SIG 8b815c
diff --git a/src/attrib.c b/src/attrib.c
Kmods SIG 8b815c
index 4b285f704e621ef5cbba11f1a006622e6387ea09..ffc323bacc9fe63683395d9535ec65849137551a 100644
Kmods SIG 8b815c
--- a/src/attrib.c
Kmods SIG 8b815c
+++ b/src/attrib.c
Kmods SIG 8b815c
@@ -218,9 +218,11 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 out:
Kmods SIG 8b815c
-	/* Undo. */
Kmods SIG 8b815c
-	run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false);
Kmods SIG 8b815c
-	run_truncate(run, vcn0);
Kmods SIG 8b815c
+	/* Undo 'ntfs_look_for_free_space' */
Kmods SIG 8b815c
+	if (vcn - vcn0) {
Kmods SIG 8b815c
+		run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false);
Kmods SIG 8b815c
+		run_truncate(run, vcn0);
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
@@ -701,7 +703,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 			 * (list entry for std attribute always first).
Kmods SIG 8b815c
 			 * So it is safe to step back.
Kmods SIG 8b815c
 			 */
Kmods SIG 8b815c
-			mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+			mi_remove_attr(NULL, mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 			if (!al_remove_le(ni, le)) {
Kmods SIG 8b815c
 				err = -EINVAL;
Kmods SIG 8b815c
@@ -1004,7 +1006,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
Kmods SIG 8b815c
 			end = next_svcn;
Kmods SIG 8b815c
 		while (end > evcn) {
Kmods SIG 8b815c
 			/* Remove segment [svcn : evcn). */
Kmods SIG 8b815c
-			mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+			mi_remove_attr(NULL, mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 			if (!al_remove_le(ni, le)) {
Kmods SIG 8b815c
 				err = -EINVAL;
Kmods SIG 8b815c
@@ -1600,7 +1602,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
Kmods SIG 8b815c
 			end = next_svcn;
Kmods SIG 8b815c
 		while (end > evcn) {
Kmods SIG 8b815c
 			/* Remove segment [svcn : evcn). */
Kmods SIG 8b815c
-			mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+			mi_remove_attr(NULL, mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 			if (!al_remove_le(ni, le)) {
Kmods SIG 8b815c
 				err = -EINVAL;
Kmods SIG 8b815c
@@ -1836,13 +1838,12 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
Kmods SIG 8b815c
 			u16 le_sz;
Kmods SIG 8b815c
 			u16 roff = le16_to_cpu(attr->nres.run_off);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-			/* run==1 means unpack and deallocate. */
Kmods SIG 8b815c
 			run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn,
Kmods SIG 8b815c
 				      evcn1 - 1, svcn, Add2Ptr(attr, roff),
Kmods SIG 8b815c
 				      le32_to_cpu(attr->size) - roff);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 			/* Delete this attribute segment. */
Kmods SIG 8b815c
-			mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+			mi_remove_attr(NULL, mi, attr);
Kmods SIG 8b815c
 			if (!le)
Kmods SIG 8b815c
 				break;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
diff --git a/src/attrlist.c b/src/attrlist.c
Kmods SIG 8b815c
index 32ca990af64b53b4e9572d5dd0d6ae5f057760c4..fa32399eb5171ce25f037fbd1bd8e483a37b839c 100644
Kmods SIG 8b815c
--- a/src/attrlist.c
Kmods SIG 8b815c
+++ b/src/attrlist.c
Kmods SIG 8b815c
@@ -279,7 +279,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
Kmods SIG 8b815c
 	struct ATTR_LIST_ENTRY *le;
Kmods SIG 8b815c
 	size_t off;
Kmods SIG 8b815c
 	u16 sz;
Kmods SIG 8b815c
-	size_t asize, new_asize;
Kmods SIG 8b815c
+	size_t asize, new_asize, old_size;
Kmods SIG 8b815c
 	u64 new_size;
Kmods SIG 8b815c
 	typeof(ni->attr_list) *al = &ni->attr_list;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -287,8 +287,9 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
Kmods SIG 8b815c
 	 * Compute the size of the new 'le'
Kmods SIG 8b815c
 	 */
Kmods SIG 8b815c
 	sz = le_size(name_len);
Kmods SIG 8b815c
-	new_size = al->size + sz;
Kmods SIG 8b815c
-	asize = al_aligned(al->size);
Kmods SIG 8b815c
+	old_size = al->size;
Kmods SIG 8b815c
+	new_size = old_size + sz;
Kmods SIG 8b815c
+	asize = al_aligned(old_size);
Kmods SIG 8b815c
 	new_asize = al_aligned(new_size);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Scan forward to the point at which the new 'le' should be inserted. */
Kmods SIG 8b815c
@@ -302,13 +303,14 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
Kmods SIG 8b815c
 			return -ENOMEM;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		memcpy(ptr, al->le, off);
Kmods SIG 8b815c
-		memcpy(Add2Ptr(ptr, off + sz), le, al->size - off);
Kmods SIG 8b815c
+		memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
Kmods SIG 8b815c
 		le = Add2Ptr(ptr, off);
Kmods SIG 8b815c
 		kfree(al->le);
Kmods SIG 8b815c
 		al->le = ptr;
Kmods SIG 8b815c
 	} else {
Kmods SIG 8b815c
-		memmove(Add2Ptr(le, sz), le, al->size - off);
Kmods SIG 8b815c
+		memmove(Add2Ptr(le, sz), le, old_size - off);
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
+	*new_le = le;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	al->size = new_size;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -321,23 +323,25 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
Kmods SIG 8b815c
 	le->id = id;
Kmods SIG 8b815c
 	memcpy(le->name, name, sizeof(short) * name_len);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	al->dirty = true;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 	err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
Kmods SIG 8b815c
 			    &new_size, true, &attr);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
+	if (err) {
Kmods SIG 8b815c
+		/* Undo memmove above. */
Kmods SIG 8b815c
+		memmove(le, Add2Ptr(le, sz), old_size - off);
Kmods SIG 8b815c
+		al->size = old_size;
Kmods SIG 8b815c
 		return err;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	al->dirty = true;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (attr && attr->non_res) {
Kmods SIG 8b815c
 		err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
Kmods SIG 8b815c
 					al->size);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			return err;
Kmods SIG 8b815c
+		al->dirty = false;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	al->dirty = false;
Kmods SIG 8b815c
-	*new_le = le;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 	return 0;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
diff --git a/src/frecord.c b/src/frecord.c
Kmods SIG 8b815c
index 9d374827750b2b39261f5985ec2fbb554ac8f6df..3f48b612ec962c6720fb3d3b3e0d63312f389c81 100644
Kmods SIG 8b815c
--- a/src/frecord.c
Kmods SIG 8b815c
+++ b/src/frecord.c
Kmods SIG 8b815c
@@ -163,7 +163,7 @@ int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi)
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
  * ni_load_mi - Load mft_inode corresponded list_entry.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
-int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
+int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
 	       struct mft_inode **mi)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	CLST rno;
Kmods SIG 8b815c
@@ -402,7 +402,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 		if (!attr)
Kmods SIG 8b815c
 			return -ENOENT;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		mi_remove_attr(&ni->mi, attr);
Kmods SIG 8b815c
+		mi_remove_attr(ni, &ni->mi, attr);
Kmods SIG 8b815c
 		return 0;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -441,7 +441,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 		if (!attr)
Kmods SIG 8b815c
 			return -ENOENT;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+		mi_remove_attr(ni, mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		if (PtrOffset(ni->attr_list.le, le) >= ni->attr_list.size)
Kmods SIG 8b815c
 			return 0;
Kmods SIG 8b815c
@@ -454,12 +454,11 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
  *
Kmods SIG 8b815c
  * Return: Not full constructed attribute or NULL if not possible to create.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
-static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni,
Kmods SIG 8b815c
-				      struct mft_inode *mi,
Kmods SIG 8b815c
-				      struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
-				      enum ATTR_TYPE type, const __le16 *name,
Kmods SIG 8b815c
-				      u8 name_len, u32 asize, u16 name_off,
Kmods SIG 8b815c
-				      CLST svcn)
Kmods SIG 8b815c
+static struct ATTRIB *
Kmods SIG 8b815c
+ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi,
Kmods SIG 8b815c
+		struct ATTR_LIST_ENTRY *le, enum ATTR_TYPE type,
Kmods SIG 8b815c
+		const __le16 *name, u8 name_len, u32 asize, u16 name_off,
Kmods SIG 8b815c
+		CLST svcn, struct ATTR_LIST_ENTRY **ins_le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
 	struct ATTRIB *attr;
Kmods SIG 8b815c
@@ -507,6 +506,8 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni,
Kmods SIG 8b815c
 	le->ref = ref;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 out:
Kmods SIG 8b815c
+	if (ins_le)
Kmods SIG 8b815c
+		*ins_le = le;
Kmods SIG 8b815c
 	return attr;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -599,7 +600,7 @@ static int ni_repack(struct ntfs_inode *ni)
Kmods SIG 8b815c
 		if (next_svcn >= evcn + 1) {
Kmods SIG 8b815c
 			/* We can remove this attribute segment. */
Kmods SIG 8b815c
 			al_remove_le(ni, le);
Kmods SIG 8b815c
-			mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+			mi_remove_attr(NULL, mi, attr);
Kmods SIG 8b815c
 			le = le_p;
Kmods SIG 8b815c
 			continue;
Kmods SIG 8b815c
 		}
Kmods SIG 8b815c
@@ -695,8 +696,8 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
Kmods SIG 8b815c
 		free -= asize;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Is seems that attribute list can be removed from primary record. */
Kmods SIG 8b815c
-	mi_remove_attr(&ni->mi, attr_list);
Kmods SIG 8b815c
+	/* It seems that attribute list can be removed from primary record. */
Kmods SIG 8b815c
+	mi_remove_attr(NULL, &ni->mi, attr_list);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/*
Kmods SIG 8b815c
 	 * Repeat the cycle above and move all attributes to primary record.
Kmods SIG 8b815c
@@ -724,7 +725,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
Kmods SIG 8b815c
 		attr_ins->id = id;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		/* Remove from original record. */
Kmods SIG 8b815c
-		mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+		mi_remove_attr(NULL, mi, attr);
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	run_deallocate(sbi, &ni->attr_list.run, true);
Kmods SIG 8b815c
@@ -842,7 +843,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)
Kmods SIG 8b815c
 		memcpy(attr, b, asize);
Kmods SIG 8b815c
 		attr->id = le_b[nb]->id;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		WARN_ON(!mi_remove_attr(&ni->mi, b));
Kmods SIG 8b815c
+		/* Remove from primary record. */
Kmods SIG 8b815c
+		WARN_ON(!mi_remove_attr(NULL, &ni->mi, b));
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		if (to_free <= asize)
Kmods SIG 8b815c
 			break;
Kmods SIG 8b815c
@@ -883,7 +885,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)
Kmods SIG 8b815c
 static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
 			   enum ATTR_TYPE type, const __le16 *name, u8 name_len,
Kmods SIG 8b815c
 			   u32 asize, CLST svcn, u16 name_off, bool force_ext,
Kmods SIG 8b815c
-			   struct ATTRIB **ins_attr, struct mft_inode **ins_mi)
Kmods SIG 8b815c
+			   struct ATTRIB **ins_attr, struct mft_inode **ins_mi,
Kmods SIG 8b815c
+			   struct ATTR_LIST_ENTRY **ins_le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	struct ATTRIB *attr;
Kmods SIG 8b815c
 	struct mft_inode *mi;
Kmods SIG 8b815c
@@ -956,12 +959,14 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		/* Try to insert attribute into this subrecord. */
Kmods SIG 8b815c
 		attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize,
Kmods SIG 8b815c
-				       name_off, svcn);
Kmods SIG 8b815c
+				       name_off, svcn, ins_le);
Kmods SIG 8b815c
 		if (!attr)
Kmods SIG 8b815c
 			continue;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		if (ins_attr)
Kmods SIG 8b815c
 			*ins_attr = attr;
Kmods SIG 8b815c
+		if (ins_mi)
Kmods SIG 8b815c
+			*ins_mi = mi;
Kmods SIG 8b815c
 		return 0;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -977,7 +982,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize,
Kmods SIG 8b815c
-			       name_off, svcn);
Kmods SIG 8b815c
+			       name_off, svcn, ins_le);
Kmods SIG 8b815c
 	if (!attr)
Kmods SIG 8b815c
 		goto out2;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1016,7 +1021,8 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
 static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 			  const __le16 *name, u8 name_len, u32 asize,
Kmods SIG 8b815c
 			  u16 name_off, CLST svcn, struct ATTRIB **ins_attr,
Kmods SIG 8b815c
-			  struct mft_inode **ins_mi)
Kmods SIG 8b815c
+			  struct mft_inode **ins_mi,
Kmods SIG 8b815c
+			  struct ATTR_LIST_ENTRY **ins_le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	struct ntfs_sb_info *sbi = ni->mi.sbi;
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
@@ -1045,7 +1051,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (asize <= free) {
Kmods SIG 8b815c
 		attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len,
Kmods SIG 8b815c
-				       asize, name_off, svcn);
Kmods SIG 8b815c
+				       asize, name_off, svcn, ins_le);
Kmods SIG 8b815c
 		if (attr) {
Kmods SIG 8b815c
 			if (ins_attr)
Kmods SIG 8b815c
 				*ins_attr = attr;
Kmods SIG 8b815c
@@ -1059,7 +1065,8 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 	if (!is_mft || type != ATTR_DATA || svcn) {
Kmods SIG 8b815c
 		/* This ATTRIB will be external. */
Kmods SIG 8b815c
 		err = ni_ins_attr_ext(ni, NULL, type, name, name_len, asize,
Kmods SIG 8b815c
-				      svcn, name_off, false, ins_attr, ins_mi);
Kmods SIG 8b815c
+				      svcn, name_off, false, ins_attr, ins_mi,
Kmods SIG 8b815c
+				      ins_le);
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1117,7 +1124,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 		t16 = le16_to_cpu(attr->name_off);
Kmods SIG 8b815c
 		err = ni_ins_attr_ext(ni, le, attr->type, Add2Ptr(attr, t16),
Kmods SIG 8b815c
 				      attr->name_len, t32, attr_svcn(attr), t16,
Kmods SIG 8b815c
-				      false, &eattr, NULL);
Kmods SIG 8b815c
+				      false, &eattr, NULL, NULL);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			return err;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1125,8 +1132,8 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 		memcpy(eattr, attr, t32);
Kmods SIG 8b815c
 		eattr->id = id;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		/* Remove attrib from primary record. */
Kmods SIG 8b815c
-		mi_remove_attr(&ni->mi, attr);
Kmods SIG 8b815c
+		/* Remove from primary record. */
Kmods SIG 8b815c
+		mi_remove_attr(NULL, &ni->mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		/* attr now points to next attribute. */
Kmods SIG 8b815c
 		if (attr->type == ATTR_END)
Kmods SIG 8b815c
@@ -1136,7 +1143,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 		;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len, asize,
Kmods SIG 8b815c
-			       name_off, svcn);
Kmods SIG 8b815c
+			       name_off, svcn, ins_le);
Kmods SIG 8b815c
 	if (!attr) {
Kmods SIG 8b815c
 		err = -EINVAL;
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
@@ -1251,7 +1258,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni)
Kmods SIG 8b815c
 	 */
Kmods SIG 8b815c
 	attr = ni_ins_new_attr(ni, mi_min, NULL, ATTR_DATA, NULL, 0,
Kmods SIG 8b815c
 			       SIZEOF_NONRESIDENT + run_size,
Kmods SIG 8b815c
-			       SIZEOF_NONRESIDENT, svcn);
Kmods SIG 8b815c
+			       SIZEOF_NONRESIDENT, svcn, NULL);
Kmods SIG 8b815c
 	if (!attr) {
Kmods SIG 8b815c
 		err = -EINVAL;
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
@@ -1315,14 +1322,15 @@ int ni_expand_list(struct ntfs_inode *ni)
Kmods SIG 8b815c
 		err = ni_ins_attr_ext(ni, le, attr->type, attr_name(attr),
Kmods SIG 8b815c
 				      attr->name_len, asize, attr_svcn(attr),
Kmods SIG 8b815c
 				      le16_to_cpu(attr->name_off), true,
Kmods SIG 8b815c
-				      &ins_attr, NULL);
Kmods SIG 8b815c
+				      &ins_attr, NULL, NULL);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		memcpy(ins_attr, attr, asize);
Kmods SIG 8b815c
 		ins_attr->id = le->id;
Kmods SIG 8b815c
-		mi_remove_attr(&ni->mi, attr);
Kmods SIG 8b815c
+		/* Remove from primary record. */
Kmods SIG 8b815c
+		mi_remove_attr(NULL, &ni->mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		done += asize;
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
@@ -1382,7 +1390,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	err = ni_insert_attr(ni, type, name, name_len, asize, name_off, svcn,
Kmods SIG 8b815c
-			     &attr, mi);
Kmods SIG 8b815c
+			     &attr, mi, NULL);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
@@ -1422,7 +1430,8 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
 int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
Kmods SIG 8b815c
 		       enum ATTR_TYPE type, const __le16 *name, u8 name_len,
Kmods SIG 8b815c
-		       struct ATTRIB **new_attr, struct mft_inode **mi)
Kmods SIG 8b815c
+		       struct ATTRIB **new_attr, struct mft_inode **mi,
Kmods SIG 8b815c
+		       struct ATTR_LIST_ENTRY **le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
 	u32 name_size = ALIGN(name_len * sizeof(short), 8);
Kmods SIG 8b815c
@@ -1430,7 +1439,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
Kmods SIG 8b815c
 	struct ATTRIB *attr;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	err = ni_insert_attr(ni, type, name, name_len, asize, SIZEOF_RESIDENT,
Kmods SIG 8b815c
-			     0, &attr, mi);
Kmods SIG 8b815c
+			     0, &attr, mi, le);
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		return err;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1439,8 +1448,13 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	attr->res.data_size = cpu_to_le32(data_size);
Kmods SIG 8b815c
 	attr->res.data_off = cpu_to_le16(SIZEOF_RESIDENT + name_size);
Kmods SIG 8b815c
-	if (type == ATTR_NAME)
Kmods SIG 8b815c
+	if (type == ATTR_NAME) {
Kmods SIG 8b815c
 		attr->res.flags = RESIDENT_FLAG_INDEXED;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		/* is_attr_indexed(attr)) == true */
Kmods SIG 8b815c
+		le16_add_cpu(&ni->mi.mrec->hard_links, +1);
Kmods SIG 8b815c
+		ni->mi.dirty = true;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
 	attr->res.res = 0;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (new_attr)
Kmods SIG 8b815c
@@ -1452,22 +1466,13 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
  * ni_remove_attr_le - Remove attribute from record.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
-int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
Kmods SIG 8b815c
-		      struct ATTR_LIST_ENTRY *le)
Kmods SIG 8b815c
+void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
Kmods SIG 8b815c
+		       struct mft_inode *mi, struct ATTR_LIST_ENTRY *le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
-	int err;
Kmods SIG 8b815c
-	struct mft_inode *mi;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	err = ni_load_mi(ni, le, &mi);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		return err;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	mi_remove_attr(mi, attr);
Kmods SIG 8b815c
+	mi_remove_attr(ni, mi, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (le)
Kmods SIG 8b815c
 		al_remove_le(ni, le);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	return 0;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
@@ -1549,10 +1554,12 @@ int ni_delete_all(struct ntfs_inode *ni)
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 /* ni_fname_name
Kmods SIG 8b815c
  *
Kmods SIG 8b815c
- *Return: File name attribute by its value. */
Kmods SIG 8b815c
+ * Return: File name attribute by its value.
Kmods SIG 8b815c
+ */
Kmods SIG 8b815c
 struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
Kmods SIG 8b815c
 				     const struct cpu_str *uni,
Kmods SIG 8b815c
 				     const struct MFT_REF *home_dir,
Kmods SIG 8b815c
+				     struct mft_inode **mi,
Kmods SIG 8b815c
 				     struct ATTR_LIST_ENTRY **le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	struct ATTRIB *attr = NULL;
Kmods SIG 8b815c
@@ -1562,7 +1569,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Enumerate all names. */
Kmods SIG 8b815c
 next:
Kmods SIG 8b815c
-	attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, NULL);
Kmods SIG 8b815c
+	attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, mi);
Kmods SIG 8b815c
 	if (!attr)
Kmods SIG 8b815c
 		return NULL;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1592,6 +1599,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
Kmods SIG 8b815c
  * Return: File name attribute with given type.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
 struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
Kmods SIG 8b815c
+				     struct mft_inode **mi,
Kmods SIG 8b815c
 				     struct ATTR_LIST_ENTRY **le)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	struct ATTRIB *attr = NULL;
Kmods SIG 8b815c
@@ -1599,10 +1607,12 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	*le = NULL;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+	if (FILE_NAME_POSIX == name_type)
Kmods SIG 8b815c
+		return NULL;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
 	/* Enumerate all names. */
Kmods SIG 8b815c
 	for (;;) {
Kmods SIG 8b815c
-		attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL,
Kmods SIG 8b815c
-				    NULL);
Kmods SIG 8b815c
+		attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, mi);
Kmods SIG 8b815c
 		if (!attr)
Kmods SIG 8b815c
 			return NULL;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -2788,6 +2798,222 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+/*
Kmods SIG 8b815c
+ * ni_remove_name - Removes name 'de' from MFT and from directory.
Kmods SIG 8b815c
+ * 'de2' and 'undo_step' are used to restore MFT/dir, if error occurs.
Kmods SIG 8b815c
+ */
Kmods SIG 8b815c
+int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
Kmods SIG 8b815c
+		   struct NTFS_DE *de, struct NTFS_DE **de2, int *undo_step)
Kmods SIG 8b815c
+{
Kmods SIG 8b815c
+	int err;
Kmods SIG 8b815c
+	struct ntfs_sb_info *sbi = ni->mi.sbi;
Kmods SIG 8b815c
+	struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1);
Kmods SIG 8b815c
+	struct ATTR_FILE_NAME *fname;
Kmods SIG 8b815c
+	struct ATTR_LIST_ENTRY *le;
Kmods SIG 8b815c
+	struct mft_inode *mi;
Kmods SIG 8b815c
+	u16 de_key_size = le16_to_cpu(de->key_size);
Kmods SIG 8b815c
+	u8 name_type;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	*undo_step = 0;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Find name in record. */
Kmods SIG 8b815c
+	mi_get_ref(&dir_ni->mi, &de_name->home);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	fname = ni_fname_name(ni, (struct cpu_str *)&de_name->name_len,
Kmods SIG 8b815c
+			      &de_name->home, &mi, &le);
Kmods SIG 8b815c
+	if (!fname)
Kmods SIG 8b815c
+		return -ENOENT;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	memcpy(&de_name->dup, &fname->dup, sizeof(struct NTFS_DUP_INFO));
Kmods SIG 8b815c
+	name_type = paired_name(fname->type);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Mark ntfs as dirty. It will be cleared at umount. */
Kmods SIG 8b815c
+	ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Step 1: Remove name from directory. */
Kmods SIG 8b815c
+	err = indx_delete_entry(&dir_ni->dir, dir_ni, fname, de_key_size, sbi);
Kmods SIG 8b815c
+	if (err)
Kmods SIG 8b815c
+		return err;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Step 2: Remove name from MFT. */
Kmods SIG 8b815c
+	ni_remove_attr_le(ni, attr_from_name(fname), mi, le);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	*undo_step = 2;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Get paired name. */
Kmods SIG 8b815c
+	fname = ni_fname_type(ni, name_type, &mi, &le);
Kmods SIG 8b815c
+	if (fname) {
Kmods SIG 8b815c
+		u16 de2_key_size = fname_full_size(fname);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		*de2 = Add2Ptr(de, 1024);
Kmods SIG 8b815c
+		(*de2)->key_size = cpu_to_le16(de2_key_size);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		memcpy(*de2 + 1, fname, de2_key_size);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		/* Step 3: Remove paired name from directory. */
Kmods SIG 8b815c
+		err = indx_delete_entry(&dir_ni->dir, dir_ni, fname,
Kmods SIG 8b815c
+					de2_key_size, sbi);
Kmods SIG 8b815c
+		if (err)
Kmods SIG 8b815c
+			return err;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		/* Step 4: Remove paired name from MFT. */
Kmods SIG 8b815c
+		ni_remove_attr_le(ni, attr_from_name(fname), mi, le);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		*undo_step = 4;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+	return 0;
Kmods SIG 8b815c
+}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+/*
Kmods SIG 8b815c
+ * ni_remove_name_undo - Paired function for ni_remove_name.
Kmods SIG 8b815c
+ *
Kmods SIG 8b815c
+ * Return: True if ok
Kmods SIG 8b815c
+ */
Kmods SIG 8b815c
+bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
Kmods SIG 8b815c
+			 struct NTFS_DE *de, struct NTFS_DE *de2, int undo_step)
Kmods SIG 8b815c
+{
Kmods SIG 8b815c
+	struct ntfs_sb_info *sbi = ni->mi.sbi;
Kmods SIG 8b815c
+	struct ATTRIB *attr;
Kmods SIG 8b815c
+	u16 de_key_size = de2 ? le16_to_cpu(de2->key_size) : 0;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	switch (undo_step) {
Kmods SIG 8b815c
+	case 4:
Kmods SIG 8b815c
+		if (ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0,
Kmods SIG 8b815c
+				       &attr, NULL, NULL)) {
Kmods SIG 8b815c
+			return false;
Kmods SIG 8b815c
+		}
Kmods SIG 8b815c
+		memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de2 + 1, de_key_size);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		mi_get_ref(&ni->mi, &de2->ref);
Kmods SIG 8b815c
+		de2->size = cpu_to_le16(ALIGN(de_key_size, 8) +
Kmods SIG 8b815c
+					sizeof(struct NTFS_DE));
Kmods SIG 8b815c
+		de2->flags = 0;
Kmods SIG 8b815c
+		de2->res = 0;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		if (indx_insert_entry(&dir_ni->dir, dir_ni, de2, sbi, NULL,
Kmods SIG 8b815c
+				      1)) {
Kmods SIG 8b815c
+			return false;
Kmods SIG 8b815c
+		}
Kmods SIG 8b815c
+		fallthrough;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	case 2:
Kmods SIG 8b815c
+		de_key_size = le16_to_cpu(de->key_size);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		if (ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0,
Kmods SIG 8b815c
+				       &attr, NULL, NULL)) {
Kmods SIG 8b815c
+			return false;
Kmods SIG 8b815c
+		}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de + 1, de_key_size);
Kmods SIG 8b815c
+		mi_get_ref(&ni->mi, &de->ref);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+		if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1)) {
Kmods SIG 8b815c
+			return false;
Kmods SIG 8b815c
+		}
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	return true;
Kmods SIG 8b815c
+}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+/*
Kmods SIG 8b815c
+ * ni_add_name - Add new name in MFT and in directory.
Kmods SIG 8b815c
+ */
Kmods SIG 8b815c
+int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
Kmods SIG 8b815c
+		struct NTFS_DE *de)
Kmods SIG 8b815c
+{
Kmods SIG 8b815c
+	int err;
Kmods SIG 8b815c
+	struct ATTRIB *attr;
Kmods SIG 8b815c
+	struct ATTR_LIST_ENTRY *le;
Kmods SIG 8b815c
+	struct mft_inode *mi;
Kmods SIG 8b815c
+	struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1);
Kmods SIG 8b815c
+	u16 de_key_size = le16_to_cpu(de->key_size);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	mi_get_ref(&ni->mi, &de->ref);
Kmods SIG 8b815c
+	mi_get_ref(&dir_ni->mi, &de_name->home);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Insert new name in MFT. */
Kmods SIG 8b815c
+	err = ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0, &attr,
Kmods SIG 8b815c
+				 &mi, &le);
Kmods SIG 8b815c
+	if (err)
Kmods SIG 8b815c
+		return err;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de_name, de_key_size);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/* Insert new name in directory. */
Kmods SIG 8b815c
+	err = indx_insert_entry(&dir_ni->dir, dir_ni, de, ni->mi.sbi, NULL, 0);
Kmods SIG 8b815c
+	if (err)
Kmods SIG 8b815c
+		ni_remove_attr_le(ni, attr, mi, le);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	return err;
Kmods SIG 8b815c
+}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+/*
Kmods SIG 8b815c
+ * ni_rename - Remove one name and insert new name.
Kmods SIG 8b815c
+ */
Kmods SIG 8b815c
+int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
Kmods SIG 8b815c
+	      struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
Kmods SIG 8b815c
+	      bool *is_bad)
Kmods SIG 8b815c
+{
Kmods SIG 8b815c
+	int err;
Kmods SIG 8b815c
+	struct NTFS_DE *de2 = NULL;
Kmods SIG 8b815c
+	int undo = 0;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/*
Kmods SIG 8b815c
+	 * There are two possible ways to rename:
Kmods SIG 8b815c
+	 * 1) Add new name and remove old name.
Kmods SIG 8b815c
+	 * 2) Remove old name and add new name.
Kmods SIG 8b815c
+	 *
Kmods SIG 8b815c
+	 * In most cases (not all!) adding new name in MFT and in directory can
Kmods SIG 8b815c
+	 * allocate additional cluster(s).
Kmods SIG 8b815c
+	 * Second way may result to bad inode if we can't add new name
Kmods SIG 8b815c
+	 * and then can't restore (add) old name.
Kmods SIG 8b815c
+	 */
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/*
Kmods SIG 8b815c
+	 * Way 1 - Add new + remove old.
Kmods SIG 8b815c
+	 */
Kmods SIG 8b815c
+	err = ni_add_name(new_dir_ni, ni, new_de);
Kmods SIG 8b815c
+	if (!err) {
Kmods SIG 8b815c
+		err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
Kmods SIG 8b815c
+		if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo))
Kmods SIG 8b815c
+			*is_bad = true;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	/*
Kmods SIG 8b815c
+	 * Way 2 - Remove old + add new.
Kmods SIG 8b815c
+	 */
Kmods SIG 8b815c
+	/*
Kmods SIG 8b815c
+	 *	err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
Kmods SIG 8b815c
+	 *	if (!err) {
Kmods SIG 8b815c
+	 *		err = ni_add_name(new_dir_ni, ni, new_de);
Kmods SIG 8b815c
+	 *		if (err && !ni_remove_name_undo(dir_ni, ni, de, de2, undo))
Kmods SIG 8b815c
+	 *			*is_bad = true;
Kmods SIG 8b815c
+	 *	}
Kmods SIG 8b815c
+	 */
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	return err;
Kmods SIG 8b815c
+}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+/*
Kmods SIG 8b815c
+ * ni_is_dirty - Return: True if 'ni' requires ni_write_inode.
Kmods SIG 8b815c
+ */
Kmods SIG 8b815c
+bool ni_is_dirty(struct inode *inode)
Kmods SIG 8b815c
+{
Kmods SIG 8b815c
+	struct ntfs_inode *ni = ntfs_i(inode);
Kmods SIG 8b815c
+	struct rb_node *node;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	if (ni->mi.dirty || ni->attr_list.dirty ||
Kmods SIG 8b815c
+	    (ni->ni_flags & NI_FLAG_UPDATE_PARENT))
Kmods SIG 8b815c
+		return true;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) {
Kmods SIG 8b815c
+		if (rb_entry(node, struct mft_inode, node)->dirty)
Kmods SIG 8b815c
+			return true;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	return false;
Kmods SIG 8b815c
+}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
  * ni_update_parent
Kmods SIG 8b815c
  *
Kmods SIG 8b815c
@@ -2802,8 +3028,6 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
Kmods SIG 8b815c
 	struct ntfs_sb_info *sbi = ni->mi.sbi;
Kmods SIG 8b815c
 	struct super_block *sb = sbi->sb;
Kmods SIG 8b815c
 	bool re_dirty = false;
Kmods SIG 8b815c
-	bool active = sb->s_flags & SB_ACTIVE;
Kmods SIG 8b815c
-	bool upd_parent = ni->ni_flags & NI_FLAG_UPDATE_PARENT;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (ni->mi.mrec->flags & RECORD_FLAG_DIR) {
Kmods SIG 8b815c
 		dup->fa |= FILE_ATTRIBUTE_DIRECTORY;
Kmods SIG 8b815c
@@ -2867,19 +3091,9 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
Kmods SIG 8b815c
 		struct ATTR_FILE_NAME *fname;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
Kmods SIG 8b815c
-		if (!fname)
Kmods SIG 8b815c
+		if (!fname || !memcmp(&fname->dup, dup, sizeof(fname->dup)))
Kmods SIG 8b815c
 			continue;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		if (memcmp(&fname->dup, dup, sizeof(fname->dup))) {
Kmods SIG 8b815c
-			memcpy(&fname->dup, dup, sizeof(fname->dup));
Kmods SIG 8b815c
-			mi->dirty = true;
Kmods SIG 8b815c
-		} else if (!upd_parent) {
Kmods SIG 8b815c
-			continue;
Kmods SIG 8b815c
-		}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-		if (!active)
Kmods SIG 8b815c
-			continue; /* Avoid __wait_on_freeing_inode(inode); */
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 		/* ntfs_iget5 may sleep. */
Kmods SIG 8b815c
 		dir = ntfs_iget5(sb, &fname->home, NULL);
Kmods SIG 8b815c
 		if (IS_ERR(dir)) {
Kmods SIG 8b815c
@@ -2898,6 +3112,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
Kmods SIG 8b815c
 			} else {
Kmods SIG 8b815c
 				indx_update_dup(dir_ni, sbi, fname, dup, sync);
Kmods SIG 8b815c
 				ni_unlock(dir_ni);
Kmods SIG 8b815c
+				memcpy(&fname->dup, dup, sizeof(fname->dup));
Kmods SIG 8b815c
+				mi->dirty = true;
Kmods SIG 8b815c
 			}
Kmods SIG 8b815c
 		}
Kmods SIG 8b815c
 		iput(dir);
Kmods SIG 8b815c
@@ -2969,7 +3185,9 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)
Kmods SIG 8b815c
 			ni->mi.dirty = true;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		if (!ntfs_is_meta_file(sbi, inode->i_ino) &&
Kmods SIG 8b815c
-		    (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))) {
Kmods SIG 8b815c
+		    (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))
Kmods SIG 8b815c
+		    /* Avoid __wait_on_freeing_inode(inode). */
Kmods SIG 8b815c
+		    && (sb->s_flags & SB_ACTIVE)) {
Kmods SIG 8b815c
 			dup.cr_time = std->cr_time;
Kmods SIG 8b815c
 			/* Not critical if this function fail. */
Kmods SIG 8b815c
 			re_dirty = ni_update_parent(ni, &dup, sync);
Kmods SIG 8b815c
@@ -3033,7 +3251,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)
Kmods SIG 8b815c
 		return err;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (re_dirty && (sb->s_flags & SB_ACTIVE))
Kmods SIG 8b815c
+	if (re_dirty)
Kmods SIG 8b815c
 		mark_inode_dirty_sync(inode);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	return 0;
Kmods SIG 8b815c
diff --git a/src/fsntfs.c b/src/fsntfs.c
Kmods SIG 8b815c
index 0edb95ed9717f442f97b3ba3e85b4b6a361e0e27..66924943921771d3cf46f6a762b1ea81ae1e9ca2 100644
Kmods SIG 8b815c
--- a/src/fsntfs.c
Kmods SIG 8b815c
+++ b/src/fsntfs.c
Kmods SIG 8b815c
@@ -358,29 +358,25 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
Kmods SIG 8b815c
 			     enum ALLOCATE_OPT opt)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
+	CLST alen = 0;
Kmods SIG 8b815c
 	struct super_block *sb = sbi->sb;
Kmods SIG 8b815c
-	size_t a_lcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen;
Kmods SIG 8b815c
+	size_t alcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen;
Kmods SIG 8b815c
 	struct wnd_bitmap *wnd = &sbi->used.bitmap;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
Kmods SIG 8b815c
 	if (opt & ALLOCATE_MFT) {
Kmods SIG 8b815c
-		CLST alen;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 		zlen = wnd_zone_len(wnd);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		if (!zlen) {
Kmods SIG 8b815c
 			err = ntfs_refresh_zone(sbi);
Kmods SIG 8b815c
 			if (err)
Kmods SIG 8b815c
 				goto out;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 			zlen = wnd_zone_len(wnd);
Kmods SIG 8b815c
+		}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-			if (!zlen) {
Kmods SIG 8b815c
-				ntfs_err(sbi->sb,
Kmods SIG 8b815c
-					 "no free space to extend mft");
Kmods SIG 8b815c
-				err = -ENOSPC;
Kmods SIG 8b815c
-				goto out;
Kmods SIG 8b815c
-			}
Kmods SIG 8b815c
+		if (!zlen) {
Kmods SIG 8b815c
+			ntfs_err(sbi->sb, "no free space to extend mft");
Kmods SIG 8b815c
+			goto out;
Kmods SIG 8b815c
 		}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		lcn = wnd_zone_bit(wnd);
Kmods SIG 8b815c
@@ -389,14 +385,13 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
Kmods SIG 8b815c
 		wnd_zone_set(wnd, lcn + alen, zlen - alen);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		err = wnd_set_used(wnd, lcn, alen);
Kmods SIG 8b815c
-		if (err)
Kmods SIG 8b815c
-			goto out;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-		*new_lcn = lcn;
Kmods SIG 8b815c
-		*new_len = alen;
Kmods SIG 8b815c
-		goto ok;
Kmods SIG 8b815c
+		if (err) {
Kmods SIG 8b815c
+			up_write(&wnd->rw_lock);
Kmods SIG 8b815c
+			return err;
Kmods SIG 8b815c
+		}
Kmods SIG 8b815c
+		alcn = lcn;
Kmods SIG 8b815c
+		goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 	/*
Kmods SIG 8b815c
 	 * 'Cause cluster 0 is always used this value means that we should use
Kmods SIG 8b815c
 	 * cached value of 'next_free_lcn' to improve performance.
Kmods SIG 8b815c
@@ -407,22 +402,17 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
Kmods SIG 8b815c
 	if (lcn >= wnd->nbits)
Kmods SIG 8b815c
 		lcn = 0;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	*new_len = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &a_lcn);
Kmods SIG 8b815c
-	if (*new_len) {
Kmods SIG 8b815c
-		*new_lcn = a_lcn;
Kmods SIG 8b815c
-		goto ok;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
+	alen = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &alcn);
Kmods SIG 8b815c
+	if (alen)
Kmods SIG 8b815c
+		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Try to use clusters from MftZone. */
Kmods SIG 8b815c
 	zlen = wnd_zone_len(wnd);
Kmods SIG 8b815c
 	zeroes = wnd_zeroes(wnd);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Check too big request. */
Kmods SIG 8b815c
-	if (len > zeroes + zlen)
Kmods SIG 8b815c
-		goto no_space;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (zlen <= NTFS_MIN_MFT_ZONE)
Kmods SIG 8b815c
-		goto no_space;
Kmods SIG 8b815c
+	/* Check too big request */
Kmods SIG 8b815c
+	if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE)
Kmods SIG 8b815c
+		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* How many clusters to cat from zone. */
Kmods SIG 8b815c
 	zlcn = wnd_zone_bit(wnd);
Kmods SIG 8b815c
@@ -439,31 +429,24 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
Kmods SIG 8b815c
 	wnd_zone_set(wnd, zlcn, new_zlen);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Allocate continues clusters. */
Kmods SIG 8b815c
-	*new_len =
Kmods SIG 8b815c
-		wnd_find(wnd, len, 0,
Kmods SIG 8b815c
-			 BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &a_lcn);
Kmods SIG 8b815c
-	if (*new_len) {
Kmods SIG 8b815c
-		*new_lcn = a_lcn;
Kmods SIG 8b815c
-		goto ok;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-no_space:
Kmods SIG 8b815c
-	up_write(&wnd->rw_lock);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	return -ENOSPC;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-ok:
Kmods SIG 8b815c
-	err = 0;
Kmods SIG 8b815c
+	alen = wnd_find(wnd, len, 0,
Kmods SIG 8b815c
+			BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	ntfs_unmap_meta(sb, *new_lcn, *new_len);
Kmods SIG 8b815c
+out:
Kmods SIG 8b815c
+	if (alen) {
Kmods SIG 8b815c
+		err = 0;
Kmods SIG 8b815c
+		*new_len = alen;
Kmods SIG 8b815c
+		*new_lcn = alcn;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (opt & ALLOCATE_MFT)
Kmods SIG 8b815c
-		goto out;
Kmods SIG 8b815c
+		ntfs_unmap_meta(sb, alcn, alen);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Set hint for next requests. */
Kmods SIG 8b815c
-	sbi->used.next_free_lcn = *new_lcn + *new_len;
Kmods SIG 8b815c
+		/* Set hint for next requests. */
Kmods SIG 8b815c
+		if (!(opt & ALLOCATE_MFT))
Kmods SIG 8b815c
+			sbi->used.next_free_lcn = alcn + alen;
Kmods SIG 8b815c
+	} else {
Kmods SIG 8b815c
+		err = -ENOSPC;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-out:
Kmods SIG 8b815c
 	up_write(&wnd->rw_lock);
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
@@ -2226,7 +2209,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
Kmods SIG 8b815c
 	sii_e.sec_id = d_security->key.sec_id;
Kmods SIG 8b815c
 	memcpy(&sii_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL);
Kmods SIG 8b815c
+	err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL, 0);
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -2247,7 +2230,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	fnd_clear(fnd_sdh);
Kmods SIG 8b815c
 	err = indx_insert_entry(indx_sdh, ni, &sdh_e.de, (void *)(size_t)1,
Kmods SIG 8b815c
-				fnd_sdh);
Kmods SIG 8b815c
+				fnd_sdh, 0);
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -2385,7 +2368,7 @@ int ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	err = indx_insert_entry(indx, ni, &re.de, NULL, NULL);
Kmods SIG 8b815c
+	err = indx_insert_entry(indx, ni, &re.de, NULL, NULL, 0);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	mark_inode_dirty(&ni->vfs_inode);
Kmods SIG 8b815c
 	ni_unlock(ni);
Kmods SIG 8b815c
diff --git a/src/index.c b/src/index.c
Kmods SIG 8b815c
index 70ef59455b72c927b40bbb76c5420b8dfc72e24a..1224b8e42b3e3d07938310739843f0ab7195f326 100644
Kmods SIG 8b815c
--- a/src/index.c
Kmods SIG 8b815c
+++ b/src/index.c
Kmods SIG 8b815c
@@ -1427,7 +1427,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 	alloc->nres.valid_size = alloc->nres.data_size = cpu_to_le64(data_size);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	err = ni_insert_resident(ni, bitmap_size(1), ATTR_BITMAP, in->name,
Kmods SIG 8b815c
-				 in->name_len, &bitmap, NULL);
Kmods SIG 8b815c
+				 in->name_len, &bitmap, NULL, NULL);
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		goto out2;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1443,7 +1443,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 	return 0;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 out2:
Kmods SIG 8b815c
-	mi_remove_attr(&ni->mi, alloc);
Kmods SIG 8b815c
+	mi_remove_attr(NULL, &ni->mi, alloc);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 out1:
Kmods SIG 8b815c
 	run_deallocate(sbi, &run, false);
Kmods SIG 8b815c
@@ -1529,24 +1529,24 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
  * indx_insert_into_root - Attempt to insert an entry into the index root.
Kmods SIG 8b815c
  *
Kmods SIG 8b815c
+ * @undo - True if we undoing previous remove.
Kmods SIG 8b815c
  * If necessary, it will twiddle the index b-tree.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
 static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 				 const struct NTFS_DE *new_de,
Kmods SIG 8b815c
 				 struct NTFS_DE *root_de, const void *ctx,
Kmods SIG 8b815c
-				 struct ntfs_fnd *fnd)
Kmods SIG 8b815c
+				 struct ntfs_fnd *fnd, bool undo)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err = 0;
Kmods SIG 8b815c
 	struct NTFS_DE *e, *e0, *re;
Kmods SIG 8b815c
 	struct mft_inode *mi;
Kmods SIG 8b815c
 	struct ATTRIB *attr;
Kmods SIG 8b815c
-	struct MFT_REC *rec;
Kmods SIG 8b815c
 	struct INDEX_HDR *hdr;
Kmods SIG 8b815c
 	struct indx_node *n;
Kmods SIG 8b815c
 	CLST new_vbn;
Kmods SIG 8b815c
 	__le64 *sub_vbn, t_vbn;
Kmods SIG 8b815c
 	u16 new_de_size;
Kmods SIG 8b815c
-	u32 hdr_used, hdr_total, asize, used, to_move;
Kmods SIG 8b815c
+	u32 hdr_used, hdr_total, asize, to_move;
Kmods SIG 8b815c
 	u32 root_size, new_root_size;
Kmods SIG 8b815c
 	struct ntfs_sb_info *sbi;
Kmods SIG 8b815c
 	int ds_root;
Kmods SIG 8b815c
@@ -1559,12 +1559,11 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/*
Kmods SIG 8b815c
 	 * Try easy case:
Kmods SIG 8b815c
-	 * hdr_insert_de will succeed if there's room the root for the new entry.
Kmods SIG 8b815c
+	 * hdr_insert_de will succeed if there's
Kmods SIG 8b815c
+	 * room the root for the new entry.
Kmods SIG 8b815c
 	 */
Kmods SIG 8b815c
 	hdr = &root->ihdr;
Kmods SIG 8b815c
 	sbi = ni->mi.sbi;
Kmods SIG 8b815c
-	rec = mi->mrec;
Kmods SIG 8b815c
-	used = le32_to_cpu(rec->used);
Kmods SIG 8b815c
 	new_de_size = le16_to_cpu(new_de->size);
Kmods SIG 8b815c
 	hdr_used = le32_to_cpu(hdr->used);
Kmods SIG 8b815c
 	hdr_total = le32_to_cpu(hdr->total);
Kmods SIG 8b815c
@@ -1573,9 +1572,9 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	ds_root = new_de_size + hdr_used - hdr_total;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (used + ds_root < sbi->max_bytes_per_attr) {
Kmods SIG 8b815c
-		/* Make a room for new elements. */
Kmods SIG 8b815c
-		mi_resize_attr(mi, attr, ds_root);
Kmods SIG 8b815c
+	/* If 'undo' is set then reduce requirements. */
Kmods SIG 8b815c
+	if ((undo || asize + ds_root < sbi->max_bytes_per_attr) &&
Kmods SIG 8b815c
+	    mi_resize_attr(mi, attr, ds_root)) {
Kmods SIG 8b815c
 		hdr->total = cpu_to_le32(hdr_total + ds_root);
Kmods SIG 8b815c
 		e = hdr_insert_de(indx, hdr, new_de, root_de, ctx);
Kmods SIG 8b815c
 		WARN_ON(!e);
Kmods SIG 8b815c
@@ -1629,7 +1628,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 			sizeof(u64);
Kmods SIG 8b815c
 	ds_root = new_root_size - root_size;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (ds_root > 0 && used + ds_root > sbi->max_bytes_per_attr) {
Kmods SIG 8b815c
+	if (ds_root > 0 && asize + ds_root > sbi->max_bytes_per_attr) {
Kmods SIG 8b815c
 		/* Make root external. */
Kmods SIG 8b815c
 		err = -EOPNOTSUPP;
Kmods SIG 8b815c
 		goto out_free_re;
Kmods SIG 8b815c
@@ -1710,7 +1709,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		put_indx_node(n);
Kmods SIG 8b815c
 		fnd_clear(fnd);
Kmods SIG 8b815c
-		err = indx_insert_entry(indx, ni, new_de, ctx, fnd);
Kmods SIG 8b815c
+		err = indx_insert_entry(indx, ni, new_de, ctx, fnd, undo);
Kmods SIG 8b815c
 		goto out_free_root;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1854,7 +1853,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 	 */
Kmods SIG 8b815c
 	if (!level) {
Kmods SIG 8b815c
 		/* Insert in root. */
Kmods SIG 8b815c
-		err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd);
Kmods SIG 8b815c
+		err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
 	} else {
Kmods SIG 8b815c
@@ -1876,10 +1875,12 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
  * indx_insert_entry - Insert new entry into index.
Kmods SIG 8b815c
+ *
Kmods SIG 8b815c
+ * @undo - True if we undoing previous remove.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
 int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 		      const struct NTFS_DE *new_de, const void *ctx,
Kmods SIG 8b815c
-		      struct ntfs_fnd *fnd)
Kmods SIG 8b815c
+		      struct ntfs_fnd *fnd, bool undo)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
 	int diff;
Kmods SIG 8b815c
@@ -1925,7 +1926,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 		 * new entry into it.
Kmods SIG 8b815c
 		 */
Kmods SIG 8b815c
 		err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx,
Kmods SIG 8b815c
-					    fnd);
Kmods SIG 8b815c
+					    fnd, undo);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
 	} else {
Kmods SIG 8b815c
@@ -2302,7 +2303,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 							      fnd->level - 1,
Kmods SIG 8b815c
 							      fnd)
Kmods SIG 8b815c
 				    : indx_insert_into_root(indx, ni, re, e,
Kmods SIG 8b815c
-							    ctx, fnd);
Kmods SIG 8b815c
+							    ctx, fnd, 0);
Kmods SIG 8b815c
 			kfree(re);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 			if (err)
Kmods SIG 8b815c
@@ -2507,7 +2508,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 		 * Re-insert the entry into the tree.
Kmods SIG 8b815c
 		 * Find the spot the tree where we want to insert the new entry.
Kmods SIG 8b815c
 		 */
Kmods SIG 8b815c
-		err = indx_insert_entry(indx, ni, me, ctx, fnd);
Kmods SIG 8b815c
+		err = indx_insert_entry(indx, ni, me, ctx, fnd, 0);
Kmods SIG 8b815c
 		kfree(me);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
@@ -2595,10 +2596,8 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi,
Kmods SIG 8b815c
 	struct ntfs_index *indx = &ni->dir;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	fnd = fnd_get();
Kmods SIG 8b815c
-	if (!fnd) {
Kmods SIG 8b815c
-		err = -ENOMEM;
Kmods SIG 8b815c
-		goto out1;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
+	if (!fnd)
Kmods SIG 8b815c
+		return -ENOMEM;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	root = indx_get_root(indx, ni, NULL, &mi);
Kmods SIG 8b815c
 	if (!root) {
Kmods SIG 8b815c
@@ -2645,7 +2644,5 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 out:
Kmods SIG 8b815c
 	fnd_put(fnd);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-out1:
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
diff --git a/src/inode.c b/src/inode.c
Kmods SIG 8b815c
index b86ec7dd731cea9fd0b41abb40fe4d81ca965c13..8f72066b3229a3f87b7b0b0e770bce20a4b06edd 100644
Kmods SIG 8b815c
--- a/src/inode.c
Kmods SIG 8b815c
+++ b/src/inode.c
Kmods SIG 8b815c
@@ -399,6 +399,12 @@ static struct inode *ntfs_read_mft(struct inode *inode,
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+	if (names != le16_to_cpu(rec->hard_links)) {
Kmods SIG 8b815c
+		/* Correct minor error on the fly. Do not mark inode as dirty. */
Kmods SIG 8b815c
+		rec->hard_links = cpu_to_le16(names);
Kmods SIG 8b815c
+		ni->mi.dirty = true;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
 	set_nlink(inode, names);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (S_ISDIR(mode)) {
Kmods SIG 8b815c
@@ -1279,6 +1285,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 	inode = &ni->vfs_inode;
Kmods SIG 8b815c
 	inode_init_owner(mnt_userns, inode, dir, mode);
Kmods SIG 8b815c
+	mode = inode->i_mode;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	inode->i_atime = inode->i_mtime = inode->i_ctime = ni->i_crtime =
Kmods SIG 8b815c
 		current_time(inode);
Kmods SIG 8b815c
@@ -1371,6 +1378,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 		attr = Add2Ptr(attr, asize);
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+	attr->id = cpu_to_le16(aid++);
Kmods SIG 8b815c
 	if (fa & FILE_ATTRIBUTE_DIRECTORY) {
Kmods SIG 8b815c
 		/*
Kmods SIG 8b815c
 		 * Regular directory or symlink to directory.
Kmods SIG 8b815c
@@ -1381,7 +1389,6 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		attr->type = ATTR_ROOT;
Kmods SIG 8b815c
 		attr->size = cpu_to_le32(asize);
Kmods SIG 8b815c
-		attr->id = cpu_to_le16(aid++);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		attr->name_len = ARRAY_SIZE(I30_NAME);
Kmods SIG 8b815c
 		attr->name_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
@@ -1412,52 +1419,46 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 		/* Insert empty ATTR_DATA */
Kmods SIG 8b815c
 		attr->type = ATTR_DATA;
Kmods SIG 8b815c
 		attr->size = cpu_to_le32(SIZEOF_RESIDENT);
Kmods SIG 8b815c
-		attr->id = cpu_to_le16(aid++);
Kmods SIG 8b815c
 		attr->name_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
 		attr->res.data_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
-	} else {
Kmods SIG 8b815c
+	} else if (S_ISREG(mode)) {
Kmods SIG 8b815c
 		/*
Kmods SIG 8b815c
-		 * Regular file or node.
Kmods SIG 8b815c
+		 * Regular file. Create empty non resident data attribute.
Kmods SIG 8b815c
 		 */
Kmods SIG 8b815c
 		attr->type = ATTR_DATA;
Kmods SIG 8b815c
-		attr->id = cpu_to_le16(aid++);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-		if (S_ISREG(mode)) {
Kmods SIG 8b815c
-			/* Create empty non resident data attribute. */
Kmods SIG 8b815c
-			attr->non_res = 1;
Kmods SIG 8b815c
-			attr->nres.evcn = cpu_to_le64(-1ll);
Kmods SIG 8b815c
-			if (fa & FILE_ATTRIBUTE_SPARSE_FILE) {
Kmods SIG 8b815c
-				attr->size =
Kmods SIG 8b815c
-					cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
Kmods SIG 8b815c
-				attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
Kmods SIG 8b815c
-				attr->flags = ATTR_FLAG_SPARSED;
Kmods SIG 8b815c
-				asize = SIZEOF_NONRESIDENT_EX + 8;
Kmods SIG 8b815c
-			} else if (fa & FILE_ATTRIBUTE_COMPRESSED) {
Kmods SIG 8b815c
-				attr->size =
Kmods SIG 8b815c
-					cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
Kmods SIG 8b815c
-				attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
Kmods SIG 8b815c
-				attr->flags = ATTR_FLAG_COMPRESSED;
Kmods SIG 8b815c
-				attr->nres.c_unit = COMPRESSION_UNIT;
Kmods SIG 8b815c
-				asize = SIZEOF_NONRESIDENT_EX + 8;
Kmods SIG 8b815c
-			} else {
Kmods SIG 8b815c
-				attr->size =
Kmods SIG 8b815c
-					cpu_to_le32(SIZEOF_NONRESIDENT + 8);
Kmods SIG 8b815c
-				attr->name_off = SIZEOF_NONRESIDENT_LE;
Kmods SIG 8b815c
-				asize = SIZEOF_NONRESIDENT + 8;
Kmods SIG 8b815c
-			}
Kmods SIG 8b815c
-			attr->nres.run_off = attr->name_off;
Kmods SIG 8b815c
+		attr->non_res = 1;
Kmods SIG 8b815c
+		attr->nres.evcn = cpu_to_le64(-1ll);
Kmods SIG 8b815c
+		if (fa & FILE_ATTRIBUTE_SPARSE_FILE) {
Kmods SIG 8b815c
+			attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
Kmods SIG 8b815c
+			attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
Kmods SIG 8b815c
+			attr->flags = ATTR_FLAG_SPARSED;
Kmods SIG 8b815c
+			asize = SIZEOF_NONRESIDENT_EX + 8;
Kmods SIG 8b815c
+		} else if (fa & FILE_ATTRIBUTE_COMPRESSED) {
Kmods SIG 8b815c
+			attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
Kmods SIG 8b815c
+			attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
Kmods SIG 8b815c
+			attr->flags = ATTR_FLAG_COMPRESSED;
Kmods SIG 8b815c
+			attr->nres.c_unit = COMPRESSION_UNIT;
Kmods SIG 8b815c
+			asize = SIZEOF_NONRESIDENT_EX + 8;
Kmods SIG 8b815c
 		} else {
Kmods SIG 8b815c
-			/* Create empty resident data attribute. */
Kmods SIG 8b815c
-			attr->size = cpu_to_le32(SIZEOF_RESIDENT);
Kmods SIG 8b815c
-			attr->name_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
-			if (fa & FILE_ATTRIBUTE_SPARSE_FILE)
Kmods SIG 8b815c
-				attr->flags = ATTR_FLAG_SPARSED;
Kmods SIG 8b815c
-			else if (fa & FILE_ATTRIBUTE_COMPRESSED)
Kmods SIG 8b815c
-				attr->flags = ATTR_FLAG_COMPRESSED;
Kmods SIG 8b815c
-			attr->res.data_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
-			asize = SIZEOF_RESIDENT;
Kmods SIG 8b815c
-			ni->ni_flags |= NI_FLAG_RESIDENT;
Kmods SIG 8b815c
+			attr->size = cpu_to_le32(SIZEOF_NONRESIDENT + 8);
Kmods SIG 8b815c
+			attr->name_off = SIZEOF_NONRESIDENT_LE;
Kmods SIG 8b815c
+			asize = SIZEOF_NONRESIDENT + 8;
Kmods SIG 8b815c
 		}
Kmods SIG 8b815c
+		attr->nres.run_off = attr->name_off;
Kmods SIG 8b815c
+	} else {
Kmods SIG 8b815c
+		/*
Kmods SIG 8b815c
+		 * Node. Create empty resident data attribute.
Kmods SIG 8b815c
+		 */
Kmods SIG 8b815c
+		attr->type = ATTR_DATA;
Kmods SIG 8b815c
+		attr->size = cpu_to_le32(SIZEOF_RESIDENT);
Kmods SIG 8b815c
+		attr->name_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
+		if (fa & FILE_ATTRIBUTE_SPARSE_FILE)
Kmods SIG 8b815c
+			attr->flags = ATTR_FLAG_SPARSED;
Kmods SIG 8b815c
+		else if (fa & FILE_ATTRIBUTE_COMPRESSED)
Kmods SIG 8b815c
+			attr->flags = ATTR_FLAG_COMPRESSED;
Kmods SIG 8b815c
+		attr->res.data_off = SIZEOF_RESIDENT_LE;
Kmods SIG 8b815c
+		asize = SIZEOF_RESIDENT;
Kmods SIG 8b815c
+		ni->ni_flags |= NI_FLAG_RESIDENT;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (S_ISDIR(mode)) {
Kmods SIG 8b815c
@@ -1485,7 +1486,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 		asize = ALIGN(SIZEOF_RESIDENT + nsize, 8);
Kmods SIG 8b815c
 		t16 = PtrOffset(rec, attr);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		if (asize + t16 + 8 > sbi->record_size) {
Kmods SIG 8b815c
+		/* 0x78 - the size of EA + EAINFO to store WSL */
Kmods SIG 8b815c
+		if (asize + t16 + 0x78 + 8 > sbi->record_size) {
Kmods SIG 8b815c
 			CLST alen;
Kmods SIG 8b815c
 			CLST clst = bytes_to_cluster(sbi, nsize);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1545,20 +1547,15 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 	rec->next_attr_id = cpu_to_le16(aid);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Step 2: Add new name in index. */
Kmods SIG 8b815c
-	err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd);
Kmods SIG 8b815c
+	err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd, 0);
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		goto out6;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Update current directory record. */
Kmods SIG 8b815c
-	mark_inode_dirty(dir);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
 	inode->i_generation = le16_to_cpu(rec->seq);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	dir->i_mtime = dir->i_ctime = inode->i_atime;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (S_ISDIR(mode)) {
Kmods SIG 8b815c
-		if (dir->i_mode & S_ISGID)
Kmods SIG 8b815c
-			mode |= S_ISGID;
Kmods SIG 8b815c
 		inode->i_op = &ntfs_dir_inode_operations;
Kmods SIG 8b815c
 		inode->i_fop = &ntfs_dir_operations;
Kmods SIG 8b815c
 	} else if (S_ISLNK(mode)) {
Kmods SIG 8b815c
@@ -1601,8 +1598,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 	d_instantiate(dentry, inode);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	ntfs_save_wsl_perm(inode);
Kmods SIG 8b815c
-	mark_inode_dirty(inode);
Kmods SIG 8b815c
 	mark_inode_dirty(dir);
Kmods SIG 8b815c
+	mark_inode_dirty(inode);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Normal exit. */
Kmods SIG 8b815c
 	goto out2;
Kmods SIG 8b815c
@@ -1646,61 +1643,36 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 int ntfs_link_inode(struct inode *inode, struct dentry *dentry)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
-	struct inode *dir = d_inode(dentry->d_parent);
Kmods SIG 8b815c
-	struct ntfs_inode *dir_ni = ntfs_i(dir);
Kmods SIG 8b815c
 	struct ntfs_inode *ni = ntfs_i(inode);
Kmods SIG 8b815c
-	struct super_block *sb = inode->i_sb;
Kmods SIG 8b815c
-	struct ntfs_sb_info *sbi = sb->s_fs_info;
Kmods SIG 8b815c
-	const struct qstr *name = &dentry->d_name;
Kmods SIG 8b815c
-	struct NTFS_DE *new_de = NULL;
Kmods SIG 8b815c
-	struct ATTR_FILE_NAME *fname;
Kmods SIG 8b815c
-	struct ATTRIB *attr;
Kmods SIG 8b815c
-	u16 key_size;
Kmods SIG 8b815c
-	struct INDEX_ROOT *dir_root;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL);
Kmods SIG 8b815c
-	if (!dir_root)
Kmods SIG 8b815c
-		return -EINVAL;
Kmods SIG 8b815c
+	struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
Kmods SIG 8b815c
+	struct NTFS_DE *de;
Kmods SIG 8b815c
+	struct ATTR_FILE_NAME *de_name;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Allocate PATH_MAX bytes. */
Kmods SIG 8b815c
-	new_de = __getname();
Kmods SIG 8b815c
-	if (!new_de)
Kmods SIG 8b815c
+	de = __getname();
Kmods SIG 8b815c
+	if (!de)
Kmods SIG 8b815c
 		return -ENOMEM;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Mark rw ntfs as dirty.  It will be cleared at umount. */
Kmods SIG 8b815c
-	ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Insert file name. */
Kmods SIG 8b815c
-	err = fill_name_de(sbi, new_de, name, NULL);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	key_size = le16_to_cpu(new_de->key_size);
Kmods SIG 8b815c
-	err = ni_insert_resident(ni, key_size, ATTR_NAME, NULL, 0, &attr, NULL);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	mi_get_ref(&ni->mi, &new_de->ref);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	fname = (struct ATTR_FILE_NAME *)(new_de + 1);
Kmods SIG 8b815c
-	mi_get_ref(&dir_ni->mi, &fname->home);
Kmods SIG 8b815c
-	fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time =
Kmods SIG 8b815c
-		fname->dup.a_time = kernel2nt(&inode->i_ctime);
Kmods SIG 8b815c
-	fname->dup.alloc_size = fname->dup.data_size = 0;
Kmods SIG 8b815c
-	fname->dup.fa = ni->std_fa;
Kmods SIG 8b815c
-	fname->dup.ea_size = fname->dup.reparse = 0;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), fname, key_size);
Kmods SIG 8b815c
+	/* Mark rw ntfs as dirty. It will be cleared at umount. */
Kmods SIG 8b815c
+	ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, NULL);
Kmods SIG 8b815c
+	/* Construct 'de'. */
Kmods SIG 8b815c
+	err = fill_name_de(sbi, de, &dentry->d_name, NULL);
Kmods SIG 8b815c
 	if (err)
Kmods SIG 8b815c
 		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	le16_add_cpu(&ni->mi.mrec->hard_links, 1);
Kmods SIG 8b815c
-	ni->mi.dirty = true;
Kmods SIG 8b815c
+	de_name = (struct ATTR_FILE_NAME *)(de + 1);
Kmods SIG 8b815c
+	/* Fill duplicate info. */
Kmods SIG 8b815c
+	de_name->dup.cr_time = de_name->dup.m_time = de_name->dup.c_time =
Kmods SIG 8b815c
+		de_name->dup.a_time = kernel2nt(&inode->i_ctime);
Kmods SIG 8b815c
+	de_name->dup.alloc_size = de_name->dup.data_size =
Kmods SIG 8b815c
+		cpu_to_le64(inode->i_size);
Kmods SIG 8b815c
+	de_name->dup.fa = ni->std_fa;
Kmods SIG 8b815c
+	de_name->dup.ea_size = de_name->dup.reparse = 0;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+	err = ni_add_name(ntfs_i(d_inode(dentry->d_parent)), ni, de);
Kmods SIG 8b815c
 out:
Kmods SIG 8b815c
-	__putname(new_de);
Kmods SIG 8b815c
+	__putname(de);
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -1713,113 +1685,56 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry)
Kmods SIG 8b815c
 int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
-	struct super_block *sb = dir->i_sb;
Kmods SIG 8b815c
-	struct ntfs_sb_info *sbi = sb->s_fs_info;
Kmods SIG 8b815c
+	struct ntfs_sb_info *sbi = dir->i_sb->s_fs_info;
Kmods SIG 8b815c
 	struct inode *inode = d_inode(dentry);
Kmods SIG 8b815c
 	struct ntfs_inode *ni = ntfs_i(inode);
Kmods SIG 8b815c
-	const struct qstr *name = &dentry->d_name;
Kmods SIG 8b815c
 	struct ntfs_inode *dir_ni = ntfs_i(dir);
Kmods SIG 8b815c
-	struct ntfs_index *indx = &dir_ni->dir;
Kmods SIG 8b815c
-	struct cpu_str *uni = NULL;
Kmods SIG 8b815c
-	struct ATTR_FILE_NAME *fname;
Kmods SIG 8b815c
-	u8 name_type;
Kmods SIG 8b815c
-	struct ATTR_LIST_ENTRY *le;
Kmods SIG 8b815c
-	struct MFT_REF ref;
Kmods SIG 8b815c
-	bool is_dir = S_ISDIR(inode->i_mode);
Kmods SIG 8b815c
-	struct INDEX_ROOT *dir_root;
Kmods SIG 8b815c
+	struct NTFS_DE *de, *de2 = NULL;
Kmods SIG 8b815c
+	int undo_remove;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	dir_root = indx_get_root(indx, dir_ni, NULL, NULL);
Kmods SIG 8b815c
-	if (!dir_root)
Kmods SIG 8b815c
+	if (ntfs_is_meta_file(sbi, ni->mi.rno))
Kmods SIG 8b815c
 		return -EINVAL;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+	/* Allocate PATH_MAX bytes. */
Kmods SIG 8b815c
+	de = __getname();
Kmods SIG 8b815c
+	if (!de)
Kmods SIG 8b815c
+		return -ENOMEM;
Kmods SIG 8b815c
+
Kmods SIG 8b815c
 	ni_lock(ni);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (is_dir && !dir_is_empty(inode)) {
Kmods SIG 8b815c
+	if (S_ISDIR(inode->i_mode) && !dir_is_empty(inode)) {
Kmods SIG 8b815c
 		err = -ENOTEMPTY;
Kmods SIG 8b815c
-		goto out1;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (ntfs_is_meta_file(sbi, inode->i_ino)) {
Kmods SIG 8b815c
-		err = -EINVAL;
Kmods SIG 8b815c
-		goto out1;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Allocate PATH_MAX bytes. */
Kmods SIG 8b815c
-	uni = __getname();
Kmods SIG 8b815c
-	if (!uni) {
Kmods SIG 8b815c
-		err = -ENOMEM;
Kmods SIG 8b815c
-		goto out1;
Kmods SIG 8b815c
+		goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Convert input string to unicode. */
Kmods SIG 8b815c
-	err = ntfs_nls_to_utf16(sbi, name->name, name->len, uni, NTFS_NAME_LEN,
Kmods SIG 8b815c
-				UTF16_HOST_ENDIAN);
Kmods SIG 8b815c
+	err = fill_name_de(sbi, de, &dentry->d_name, NULL);
Kmods SIG 8b815c
 	if (err < 0)
Kmods SIG 8b815c
-		goto out2;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Mark rw ntfs as dirty.  It will be cleared at umount. */
Kmods SIG 8b815c
-	ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Find name in record. */
Kmods SIG 8b815c
-	mi_get_ref(&dir_ni->mi, &ref;;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	le = NULL;
Kmods SIG 8b815c
-	fname = ni_fname_name(ni, uni, &ref, &le);
Kmods SIG 8b815c
-	if (!fname) {
Kmods SIG 8b815c
-		err = -ENOENT;
Kmods SIG 8b815c
-		goto out3;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	name_type = paired_name(fname->type);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	err = indx_delete_entry(indx, dir_ni, fname, fname_full_size(fname),
Kmods SIG 8b815c
-				sbi);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out3;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Then remove name from MFT. */
Kmods SIG 8b815c
-	ni_remove_attr_le(ni, attr_from_name(fname), le);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	le16_add_cpu(&ni->mi.mrec->hard_links, -1);
Kmods SIG 8b815c
-	ni->mi.dirty = true;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (name_type != FILE_NAME_POSIX) {
Kmods SIG 8b815c
-		/* Now we should delete name by type. */
Kmods SIG 8b815c
-		fname = ni_fname_type(ni, name_type, &le);
Kmods SIG 8b815c
-		if (fname) {
Kmods SIG 8b815c
-			err = indx_delete_entry(indx, dir_ni, fname,
Kmods SIG 8b815c
-						fname_full_size(fname), sbi);
Kmods SIG 8b815c
-			if (err)
Kmods SIG 8b815c
-				goto out3;
Kmods SIG 8b815c
+		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-			ni_remove_attr_le(ni, attr_from_name(fname), le);
Kmods SIG 8b815c
+	undo_remove = 0;
Kmods SIG 8b815c
+	err = ni_remove_name(dir_ni, ni, de, &de2, &undo_remove);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-			le16_add_cpu(&ni->mi.mrec->hard_links, -1);
Kmods SIG 8b815c
-		}
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-out3:
Kmods SIG 8b815c
-	switch (err) {
Kmods SIG 8b815c
-	case 0:
Kmods SIG 8b815c
+	if (!err) {
Kmods SIG 8b815c
 		drop_nlink(inode);
Kmods SIG 8b815c
-		break;
Kmods SIG 8b815c
-	case -ENOTEMPTY:
Kmods SIG 8b815c
-	case -ENOSPC:
Kmods SIG 8b815c
-	case -EROFS:
Kmods SIG 8b815c
-		break;
Kmods SIG 8b815c
-	default:
Kmods SIG 8b815c
+		dir->i_mtime = dir->i_ctime = current_time(dir);
Kmods SIG 8b815c
+		mark_inode_dirty(dir);
Kmods SIG 8b815c
+		inode->i_ctime = dir->i_ctime;
Kmods SIG 8b815c
+		if (inode->i_nlink)
Kmods SIG 8b815c
+			mark_inode_dirty(inode);
Kmods SIG 8b815c
+	} else if (!ni_remove_name_undo(dir_ni, ni, de, de2, undo_remove)) {
Kmods SIG 8b815c
 		make_bad_inode(inode);
Kmods SIG 8b815c
+		ntfs_inode_err(inode, "failed to undo unlink");
Kmods SIG 8b815c
+		ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
Kmods SIG 8b815c
+	} else {
Kmods SIG 8b815c
+		if (ni_is_dirty(dir))
Kmods SIG 8b815c
+			mark_inode_dirty(dir);
Kmods SIG 8b815c
+		if (ni_is_dirty(inode))
Kmods SIG 8b815c
+			mark_inode_dirty(inode);
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	dir->i_mtime = dir->i_ctime = current_time(dir);
Kmods SIG 8b815c
-	mark_inode_dirty(dir);
Kmods SIG 8b815c
-	inode->i_ctime = dir->i_ctime;
Kmods SIG 8b815c
-	if (inode->i_nlink)
Kmods SIG 8b815c
-		mark_inode_dirty(inode);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-out2:
Kmods SIG 8b815c
-	__putname(uni);
Kmods SIG 8b815c
-out1:
Kmods SIG 8b815c
+out:
Kmods SIG 8b815c
 	ni_unlock(ni);
Kmods SIG 8b815c
+	__putname(de);
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
diff --git a/src/namei.c b/src/namei.c
Kmods SIG 8b815c
index f79a399bd015056f320714c91b935afac16eb488..e58415d0713280297df1b7bac30eb17642431fa0 100644
Kmods SIG 8b815c
--- a/src/namei.c
Kmods SIG 8b815c
+++ b/src/namei.c
Kmods SIG 8b815c
@@ -152,12 +152,14 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de)
Kmods SIG 8b815c
 	if (inode != dir)
Kmods SIG 8b815c
 		ni_lock(ni);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	dir->i_ctime = dir->i_mtime = inode->i_ctime = current_time(inode);
Kmods SIG 8b815c
 	inc_nlink(inode);
Kmods SIG 8b815c
 	ihold(inode);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	err = ntfs_link_inode(inode, de);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
 	if (!err) {
Kmods SIG 8b815c
+		dir->i_ctime = dir->i_mtime = inode->i_ctime =
Kmods SIG 8b815c
+			current_time(dir);
Kmods SIG 8b815c
 		mark_inode_dirty(inode);
Kmods SIG 8b815c
 		mark_inode_dirty(dir);
Kmods SIG 8b815c
 		d_instantiate(de, inode);
Kmods SIG 8b815c
@@ -249,25 +251,26 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry)
Kmods SIG 8b815c
 /*
Kmods SIG 8b815c
  * ntfs_rename - inode_operations::rename
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
-static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
Kmods SIG 8b815c
-		       struct dentry *old_dentry, struct inode *new_dir,
Kmods SIG 8b815c
+static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *dir,
Kmods SIG 8b815c
+		       struct dentry *dentry, struct inode *new_dir,
Kmods SIG 8b815c
 		       struct dentry *new_dentry, u32 flags)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
-	struct super_block *sb = old_dir->i_sb;
Kmods SIG 8b815c
+	struct super_block *sb = dir->i_sb;
Kmods SIG 8b815c
 	struct ntfs_sb_info *sbi = sb->s_fs_info;
Kmods SIG 8b815c
-	struct ntfs_inode *old_dir_ni = ntfs_i(old_dir);
Kmods SIG 8b815c
+	struct ntfs_inode *dir_ni = ntfs_i(dir);
Kmods SIG 8b815c
 	struct ntfs_inode *new_dir_ni = ntfs_i(new_dir);
Kmods SIG 8b815c
-	struct ntfs_inode *old_ni;
Kmods SIG 8b815c
-	struct ATTR_FILE_NAME *old_name, *new_name, *fname;
Kmods SIG 8b815c
-	u8 name_type;
Kmods SIG 8b815c
-	bool is_same;
Kmods SIG 8b815c
-	struct inode *old_inode, *new_inode;
Kmods SIG 8b815c
-	struct NTFS_DE *old_de, *new_de;
Kmods SIG 8b815c
-	struct ATTRIB *attr;
Kmods SIG 8b815c
-	struct ATTR_LIST_ENTRY *le;
Kmods SIG 8b815c
-	u16 new_de_key_size;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
+	struct inode *inode = d_inode(dentry);
Kmods SIG 8b815c
+	struct ntfs_inode *ni = ntfs_i(inode);
Kmods SIG 8b815c
+	struct inode *new_inode = d_inode(new_dentry);
Kmods SIG 8b815c
+	struct NTFS_DE *de, *new_de;
Kmods SIG 8b815c
+	bool is_same, is_bad;
Kmods SIG 8b815c
+	/*
Kmods SIG 8b815c
+	 * de		- memory of PATH_MAX bytes:
Kmods SIG 8b815c
+	 * [0-1024)	- original name (dentry->d_name)
Kmods SIG 8b815c
+	 * [1024-2048)	- paired to original name, usually DOS variant of dentry->d_name
Kmods SIG 8b815c
+	 * [2048-3072)	- new name (new_dentry->d_name)
Kmods SIG 8b815c
+	 */
Kmods SIG 8b815c
 	static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + SIZEOF_RESIDENT < 1024);
Kmods SIG 8b815c
 	static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + sizeof(struct NTFS_DE) <
Kmods SIG 8b815c
 		      1024);
Kmods SIG 8b815c
@@ -276,24 +279,18 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
Kmods SIG 8b815c
 	if (flags & ~RENAME_NOREPLACE)
Kmods SIG 8b815c
 		return -EINVAL;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	old_inode = d_inode(old_dentry);
Kmods SIG 8b815c
-	new_inode = d_inode(new_dentry);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	old_ni = ntfs_i(old_inode);
Kmods SIG 8b815c
+	is_same = dentry->d_name.len == new_dentry->d_name.len &&
Kmods SIG 8b815c
+		  !memcmp(dentry->d_name.name, new_dentry->d_name.name,
Kmods SIG 8b815c
+			  dentry->d_name.len);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	is_same = old_dentry->d_name.len == new_dentry->d_name.len &&
Kmods SIG 8b815c
-		  !memcmp(old_dentry->d_name.name, new_dentry->d_name.name,
Kmods SIG 8b815c
-			  old_dentry->d_name.len);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (is_same && old_dir == new_dir) {
Kmods SIG 8b815c
+	if (is_same && dir == new_dir) {
Kmods SIG 8b815c
 		/* Nothing to do. */
Kmods SIG 8b815c
-		err = 0;
Kmods SIG 8b815c
-		goto out;
Kmods SIG 8b815c
+		return 0;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (ntfs_is_meta_file(sbi, old_inode->i_ino)) {
Kmods SIG 8b815c
-		err = -EINVAL;
Kmods SIG 8b815c
-		goto out;
Kmods SIG 8b815c
+	if (ntfs_is_meta_file(sbi, inode->i_ino)) {
Kmods SIG 8b815c
+		/* Should we print an error? */
Kmods SIG 8b815c
+		return -EINVAL;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (new_inode) {
Kmods SIG 8b815c
@@ -304,168 +301,61 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
Kmods SIG 8b815c
 		ni_unlock(new_dir_ni);
Kmods SIG 8b815c
 		dput(new_dentry);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
-			goto out;
Kmods SIG 8b815c
+			return err;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	/* Allocate PATH_MAX bytes. */
Kmods SIG 8b815c
-	old_de = __getname();
Kmods SIG 8b815c
-	if (!old_de) {
Kmods SIG 8b815c
-		err = -ENOMEM;
Kmods SIG 8b815c
-		goto out;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
+	de = __getname();
Kmods SIG 8b815c
+	if (!de)
Kmods SIG 8b815c
+		return -ENOMEM;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	err = fill_name_de(sbi, old_de, &old_dentry->d_name, NULL);
Kmods SIG 8b815c
+	/* Translate dentry->d_name into unicode form. */
Kmods SIG 8b815c
+	err = fill_name_de(sbi, de, &dentry->d_name, NULL);
Kmods SIG 8b815c
 	if (err < 0)
Kmods SIG 8b815c
-		goto out1;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	old_name = (struct ATTR_FILE_NAME *)(old_de + 1);
Kmods SIG 8b815c
+		goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (is_same) {
Kmods SIG 8b815c
-		new_de = old_de;
Kmods SIG 8b815c
+		/* Reuse 'de'. */
Kmods SIG 8b815c
+		new_de = de;
Kmods SIG 8b815c
 	} else {
Kmods SIG 8b815c
-		new_de = Add2Ptr(old_de, 1024);
Kmods SIG 8b815c
+		/* Translate new_dentry->d_name into unicode form. */
Kmods SIG 8b815c
+		new_de = Add2Ptr(de, 2048);
Kmods SIG 8b815c
 		err = fill_name_de(sbi, new_de, &new_dentry->d_name, NULL);
Kmods SIG 8b815c
 		if (err < 0)
Kmods SIG 8b815c
-			goto out1;
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	ni_lock_dir(old_dir_ni);
Kmods SIG 8b815c
-	ni_lock(old_ni);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	mi_get_ref(&old_dir_ni->mi, &old_name->home);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Get pointer to file_name in MFT. */
Kmods SIG 8b815c
-	fname = ni_fname_name(old_ni, (struct cpu_str *)&old_name->name_len,
Kmods SIG 8b815c
-			      &old_name->home, &le);
Kmods SIG 8b815c
-	if (!fname) {
Kmods SIG 8b815c
-		err = -EINVAL;
Kmods SIG 8b815c
-		goto out2;
Kmods SIG 8b815c
+			goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	/* Copy fname info from record into new fname. */
Kmods SIG 8b815c
-	new_name = (struct ATTR_FILE_NAME *)(new_de + 1);
Kmods SIG 8b815c
-	memcpy(&new_name->dup, &fname->dup, sizeof(fname->dup));
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	name_type = paired_name(fname->type);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Remove first name from directory. */
Kmods SIG 8b815c
-	err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1,
Kmods SIG 8b815c
-				le16_to_cpu(old_de->key_size), sbi);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out3;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Remove first name from MFT. */
Kmods SIG 8b815c
-	err = ni_remove_attr_le(old_ni, attr_from_name(fname), le);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out4;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	le16_add_cpu(&old_ni->mi.mrec->hard_links, -1);
Kmods SIG 8b815c
-	old_ni->mi.dirty = true;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (name_type != FILE_NAME_POSIX) {
Kmods SIG 8b815c
-		/* Get paired name. */
Kmods SIG 8b815c
-		fname = ni_fname_type(old_ni, name_type, &le);
Kmods SIG 8b815c
-		if (fname) {
Kmods SIG 8b815c
-			/* Remove second name from directory. */
Kmods SIG 8b815c
-			err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni,
Kmods SIG 8b815c
-						fname, fname_full_size(fname),
Kmods SIG 8b815c
-						sbi);
Kmods SIG 8b815c
-			if (err)
Kmods SIG 8b815c
-				goto out5;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-			/* Remove second name from MFT. */
Kmods SIG 8b815c
-			err = ni_remove_attr_le(old_ni, attr_from_name(fname),
Kmods SIG 8b815c
-						le);
Kmods SIG 8b815c
-			if (err)
Kmods SIG 8b815c
-				goto out6;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-			le16_add_cpu(&old_ni->mi.mrec->hard_links, -1);
Kmods SIG 8b815c
-			old_ni->mi.dirty = true;
Kmods SIG 8b815c
+	ni_lock_dir(dir_ni);
Kmods SIG 8b815c
+	ni_lock(ni);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+	is_bad = false;
Kmods SIG 8b815c
+	err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de, &is_bad);
Kmods SIG 8b815c
+	if (is_bad) {
Kmods SIG 8b815c
+		/* Restore after failed rename failed too. */
Kmods SIG 8b815c
+		make_bad_inode(inode);
Kmods SIG 8b815c
+		ntfs_inode_err(inode, "failed to undo rename");
Kmods SIG 8b815c
+		ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
Kmods SIG 8b815c
+	} else if (!err) {
Kmods SIG 8b815c
+		inode->i_ctime = dir->i_ctime = dir->i_mtime =
Kmods SIG 8b815c
+			current_time(dir);
Kmods SIG 8b815c
+		mark_inode_dirty(inode);
Kmods SIG 8b815c
+		mark_inode_dirty(dir);
Kmods SIG 8b815c
+		if (dir != new_dir) {
Kmods SIG 8b815c
+			new_dir->i_mtime = new_dir->i_ctime = dir->i_ctime;
Kmods SIG 8b815c
+			mark_inode_dirty(new_dir);
Kmods SIG 8b815c
 		}
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Add new name. */
Kmods SIG 8b815c
-	mi_get_ref(&old_ni->mi, &new_de->ref);
Kmods SIG 8b815c
-	mi_get_ref(&ntfs_i(new_dir)->mi, &new_name->home);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	new_de_key_size = le16_to_cpu(new_de->key_size);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Insert new name in MFT. */
Kmods SIG 8b815c
-	err = ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0,
Kmods SIG 8b815c
-				 &attr, NULL);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out7;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	attr->res.flags = RESIDENT_FLAG_INDEXED;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), new_name, new_de_key_size);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	le16_add_cpu(&old_ni->mi.mrec->hard_links, 1);
Kmods SIG 8b815c
-	old_ni->mi.dirty = true;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	/* Insert new name in directory. */
Kmods SIG 8b815c
-	err = indx_insert_entry(&new_dir_ni->dir, new_dir_ni, new_de, sbi,
Kmods SIG 8b815c
-				NULL);
Kmods SIG 8b815c
-	if (err)
Kmods SIG 8b815c
-		goto out8;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (IS_DIRSYNC(new_dir))
Kmods SIG 8b815c
-		err = ntfs_sync_inode(old_inode);
Kmods SIG 8b815c
-	else
Kmods SIG 8b815c
-		mark_inode_dirty(old_inode);
Kmods SIG 8b815c
+		if (IS_DIRSYNC(dir))
Kmods SIG 8b815c
+			ntfs_sync_inode(dir);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
Kmods SIG 8b815c
-	if (IS_DIRSYNC(old_dir))
Kmods SIG 8b815c
-		(void)ntfs_sync_inode(old_dir);
Kmods SIG 8b815c
-	else
Kmods SIG 8b815c
-		mark_inode_dirty(old_dir);
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (old_dir != new_dir) {
Kmods SIG 8b815c
-		new_dir->i_mtime = new_dir->i_ctime = old_dir->i_ctime;
Kmods SIG 8b815c
-		mark_inode_dirty(new_dir);
Kmods SIG 8b815c
-	}
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-	if (old_inode) {
Kmods SIG 8b815c
-		old_inode->i_ctime = old_dir->i_ctime;
Kmods SIG 8b815c
-		mark_inode_dirty(old_inode);
Kmods SIG 8b815c
+		if (IS_DIRSYNC(new_dir))
Kmods SIG 8b815c
+			ntfs_sync_inode(inode);
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	err = 0;
Kmods SIG 8b815c
-	/* Normal way* */
Kmods SIG 8b815c
-	goto out2;
Kmods SIG 8b815c
-
Kmods SIG 8b815c
-out8:
Kmods SIG 8b815c
-	/* undo
Kmods SIG 8b815c
-	 * ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0,
Kmods SIG 8b815c
-	 *			 &attr, NULL);
Kmods SIG 8b815c
-	 */
Kmods SIG 8b815c
-	mi_remove_attr(&old_ni->mi, attr);
Kmods SIG 8b815c
-out7:
Kmods SIG 8b815c
-	/* undo
Kmods SIG 8b815c
-	 * ni_remove_attr_le(old_ni, attr_from_name(fname), le);
Kmods SIG 8b815c
-	 */
Kmods SIG 8b815c
-out6:
Kmods SIG 8b815c
-	/* undo
Kmods SIG 8b815c
-	 * indx_delete_entry(&old_dir_ni->dir, old_dir_ni,
Kmods SIG 8b815c
-	 *					fname, fname_full_size(fname),
Kmods SIG 8b815c
-	 *					sbi);
Kmods SIG 8b815c
-	 */
Kmods SIG 8b815c
-out5:
Kmods SIG 8b815c
-	/* undo
Kmods SIG 8b815c
-	 * ni_remove_attr_le(old_ni, attr_from_name(fname), le);
Kmods SIG 8b815c
-	 */
Kmods SIG 8b815c
-out4:
Kmods SIG 8b815c
-	/* undo:
Kmods SIG 8b815c
-	 * indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1,
Kmods SIG 8b815c
-	 *			old_de->key_size, NULL);
Kmods SIG 8b815c
-	 */
Kmods SIG 8b815c
-out3:
Kmods SIG 8b815c
-out2:
Kmods SIG 8b815c
-	ni_unlock(old_ni);
Kmods SIG 8b815c
-	ni_unlock(old_dir_ni);
Kmods SIG 8b815c
-out1:
Kmods SIG 8b815c
-	__putname(old_de);
Kmods SIG 8b815c
+	ni_unlock(ni);
Kmods SIG 8b815c
+	ni_unlock(dir_ni);
Kmods SIG 8b815c
 out:
Kmods SIG 8b815c
+	__putname(de);
Kmods SIG 8b815c
 	return err;
Kmods SIG 8b815c
 }
Kmods SIG 8b815c
 
Kmods SIG 8b815c
diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h
Kmods SIG 8b815c
index 64ef92e16363c4973be6438efdeea0663ce10e84..f9436cbbc347b617b458bb21d6c7229b467ca289 100644
Kmods SIG 8b815c
--- a/src/ntfs_fs.h
Kmods SIG 8b815c
+++ b/src/ntfs_fs.h
Kmods SIG 8b815c
@@ -478,7 +478,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni);
Kmods SIG 8b815c
 struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni);
Kmods SIG 8b815c
 void ni_clear(struct ntfs_inode *ni);
Kmods SIG 8b815c
 int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi);
Kmods SIG 8b815c
-int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
+int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le,
Kmods SIG 8b815c
 	       struct mft_inode **mi);
Kmods SIG 8b815c
 struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr,
Kmods SIG 8b815c
 			    struct ATTR_LIST_ENTRY **entry_o,
Kmods SIG 8b815c
@@ -505,15 +505,18 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type,
Kmods SIG 8b815c
 			  struct mft_inode **mi);
Kmods SIG 8b815c
 int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
Kmods SIG 8b815c
 		       enum ATTR_TYPE type, const __le16 *name, u8 name_len,
Kmods SIG 8b815c
-		       struct ATTRIB **new_attr, struct mft_inode **mi);
Kmods SIG 8b815c
-int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
Kmods SIG 8b815c
-		      struct ATTR_LIST_ENTRY *le);
Kmods SIG 8b815c
+		       struct ATTRIB **new_attr, struct mft_inode **mi,
Kmods SIG 8b815c
+		       struct ATTR_LIST_ENTRY **le);
Kmods SIG 8b815c
+void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
Kmods SIG 8b815c
+		       struct mft_inode *mi, struct ATTR_LIST_ENTRY *le);
Kmods SIG 8b815c
 int ni_delete_all(struct ntfs_inode *ni);
Kmods SIG 8b815c
 struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
Kmods SIG 8b815c
 				     const struct cpu_str *uni,
Kmods SIG 8b815c
 				     const struct MFT_REF *home,
Kmods SIG 8b815c
+				     struct mft_inode **mi,
Kmods SIG 8b815c
 				     struct ATTR_LIST_ENTRY **entry);
Kmods SIG 8b815c
 struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
Kmods SIG 8b815c
+				     struct mft_inode **mi,
Kmods SIG 8b815c
 				     struct ATTR_LIST_ENTRY **entry);
Kmods SIG 8b815c
 int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa);
Kmods SIG 8b815c
 enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
Kmods SIG 8b815c
@@ -528,6 +531,21 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
Kmods SIG 8b815c
 		  u32 pages_per_frame);
Kmods SIG 8b815c
 int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
Kmods SIG 8b815c
 		   u32 pages_per_frame);
Kmods SIG 8b815c
+int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
Kmods SIG 8b815c
+		   struct NTFS_DE *de, struct NTFS_DE **de2, int *undo_step);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
Kmods SIG 8b815c
+			 struct NTFS_DE *de, struct NTFS_DE *de2,
Kmods SIG 8b815c
+			 int undo_step);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
Kmods SIG 8b815c
+		struct NTFS_DE *de);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
Kmods SIG 8b815c
+	      struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
Kmods SIG 8b815c
+	      bool *is_bad);
Kmods SIG 8b815c
+
Kmods SIG 8b815c
+bool ni_is_dirty(struct inode *inode);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 /* Globals from fslog.c */
Kmods SIG 8b815c
 int log_replay(struct ntfs_inode *ni, bool *initialized);
Kmods SIG 8b815c
@@ -631,7 +649,7 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 		  size_t *off, struct ntfs_fnd *fnd);
Kmods SIG 8b815c
 int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 		      const struct NTFS_DE *new_de, const void *param,
Kmods SIG 8b815c
-		      struct ntfs_fnd *fnd);
Kmods SIG 8b815c
+		      struct ntfs_fnd *fnd, bool undo);
Kmods SIG 8b815c
 int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
Kmods SIG 8b815c
 		      const void *key, u32 key_len, const void *param);
Kmods SIG 8b815c
 int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi,
Kmods SIG 8b815c
@@ -694,7 +712,8 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
Kmods SIG 8b815c
 			      const __le16 *name, u8 name_len, u32 asize,
Kmods SIG 8b815c
 			      u16 name_off);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr);
Kmods SIG 8b815c
+bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
Kmods SIG 8b815c
+		    struct ATTRIB *attr);
Kmods SIG 8b815c
 bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes);
Kmods SIG 8b815c
 int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
Kmods SIG 8b815c
 		 struct runs_tree *run, CLST len);
Kmods SIG 8b815c
diff --git a/src/record.c b/src/record.c
Kmods SIG 8b815c
index d48a5e6c5045c5f154f3c1aba4108f458e9676b0..61e3f2fb619f698ffdf5d81ca6f488008002de52 100644
Kmods SIG 8b815c
--- a/src/record.c
Kmods SIG 8b815c
+++ b/src/record.c
Kmods SIG 8b815c
@@ -489,7 +489,8 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
Kmods SIG 8b815c
  *
Kmods SIG 8b815c
  * NOTE: The source attr will point to next attribute.
Kmods SIG 8b815c
  */
Kmods SIG 8b815c
-bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr)
Kmods SIG 8b815c
+bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
Kmods SIG 8b815c
+		    struct ATTRIB *attr)
Kmods SIG 8b815c
 {
Kmods SIG 8b815c
 	struct MFT_REC *rec = mi->mrec;
Kmods SIG 8b815c
 	u32 aoff = PtrOffset(rec, attr);
Kmods SIG 8b815c
@@ -499,6 +500,11 @@ bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr)
Kmods SIG 8b815c
 	if (aoff + asize > used)
Kmods SIG 8b815c
 		return false;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
+	if (ni && is_attr_indexed(attr)) {
Kmods SIG 8b815c
+		le16_add_cpu(&ni->mi.mrec->hard_links, -1);
Kmods SIG 8b815c
+		ni->mi.dirty = true;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
+
Kmods SIG 8b815c
 	used -= asize;
Kmods SIG 8b815c
 	memmove(attr, Add2Ptr(attr, asize), used - aoff);
Kmods SIG 8b815c
 	rec->used = cpu_to_le32(used);
Kmods SIG 8b815c
diff --git a/src/xattr.c b/src/xattr.c
Kmods SIG 8b815c
index b4c921e4bc1a1b4bbb1c51ef7cef6677967b8e13..22fd5eb32c5be7f53b3fd8076599f7dbaf84e2cf 100644
Kmods SIG 8b815c
--- a/src/xattr.c
Kmods SIG 8b815c
+++ b/src/xattr.c
Kmods SIG 8b815c
@@ -395,11 +395,13 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
Kmods SIG 8b815c
 		}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 		err = ni_insert_resident(ni, sizeof(struct EA_INFO),
Kmods SIG 8b815c
-					 ATTR_EA_INFO, NULL, 0, NULL, NULL);
Kmods SIG 8b815c
+					 ATTR_EA_INFO, NULL, 0, NULL, NULL,
Kmods SIG 8b815c
+					 NULL);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-		err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL);
Kmods SIG 8b815c
+		err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL,
Kmods SIG 8b815c
+					 NULL);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
@@ -419,9 +421,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (!size) {
Kmods SIG 8b815c
 		/* Delete xattr, ATTR_EA_INFO */
Kmods SIG 8b815c
-		err = ni_remove_attr_le(ni, attr, le);
Kmods SIG 8b815c
-		if (err)
Kmods SIG 8b815c
-			goto out;
Kmods SIG 8b815c
+		ni_remove_attr_le(ni, attr, mi, le);
Kmods SIG 8b815c
 	} else {
Kmods SIG 8b815c
 		p = resident_data_ex(attr, sizeof(struct EA_INFO));
Kmods SIG 8b815c
 		if (!p) {
Kmods SIG 8b815c
@@ -441,9 +441,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (!size) {
Kmods SIG 8b815c
 		/* Delete xattr, ATTR_EA */
Kmods SIG 8b815c
-		err = ni_remove_attr_le(ni, attr, le);
Kmods SIG 8b815c
-		if (err)
Kmods SIG 8b815c
-			goto out;
Kmods SIG 8b815c
+		ni_remove_attr_le(ni, attr, mi, le);
Kmods SIG 8b815c
 	} else if (attr->non_res) {
Kmods SIG 8b815c
 		err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size);
Kmods SIG 8b815c
 		if (err)
Kmods SIG 8b815c
@@ -605,8 +603,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 			goto out;
Kmods SIG 8b815c
 	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	err = ntfs_set_ea(inode, name, name_len, value, size,
Kmods SIG 8b815c
-			  acl ? 0 : XATTR_REPLACE, locked);
Kmods SIG 8b815c
+	err = ntfs_set_ea(inode, name, name_len, value, size, 0, locked);
Kmods SIG 8b815c
 	if (!err)
Kmods SIG 8b815c
 		set_cached_acl(inode, type, acl);
Kmods SIG 8b815c
 
Kmods SIG 8b815c
@@ -632,8 +629,10 @@ static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 	struct posix_acl *acl;
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (!(inode->i_sb->s_flags & SB_POSIXACL))
Kmods SIG 8b815c
+	if (!(inode->i_sb->s_flags & SB_POSIXACL)) {
Kmods SIG 8b815c
+		ntfs_inode_warn(inode, "add mount option \"acl\" to use acl");
Kmods SIG 8b815c
 		return -EOPNOTSUPP;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	acl = ntfs_get_acl(inode, type);
Kmods SIG 8b815c
 	if (IS_ERR(acl))
Kmods SIG 8b815c
@@ -655,8 +654,10 @@ static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns,
Kmods SIG 8b815c
 	struct posix_acl *acl;
Kmods SIG 8b815c
 	int err;
Kmods SIG 8b815c
 
Kmods SIG 8b815c
-	if (!(inode->i_sb->s_flags & SB_POSIXACL))
Kmods SIG 8b815c
+	if (!(inode->i_sb->s_flags & SB_POSIXACL)) {
Kmods SIG 8b815c
+		ntfs_inode_warn(inode, "add mount option \"acl\" to use acl");
Kmods SIG 8b815c
 		return -EOPNOTSUPP;
Kmods SIG 8b815c
+	}
Kmods SIG 8b815c
 
Kmods SIG 8b815c
 	if (!inode_owner_or_capable(mnt_userns, inode))
Kmods SIG 8b815c
 		return -EPERM;
Kmods SIG 8b815c
-- 
Kmods SIG 8b815c
2.31.1
Kmods SIG 8b815c