Rename now works "Add new name and remove old name".
"Remove old name and add new name" may result in bad inode
if we can't add new name and then can't restore (add) old name.
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
}
out:
- /* Undo. */
- run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false);
- run_truncate(run, vcn0);
+ /* Undo 'ntfs_look_for_free_space' */
+ if (vcn - vcn0) {
+ run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false);
+ run_truncate(run, vcn0);
+ }
return err;
}
* (list entry for std attribute always first).
* So it is safe to step back.
*/
- mi_remove_attr(mi, attr);
+ mi_remove_attr(NULL, mi, attr);
if (!al_remove_le(ni, le)) {
err = -EINVAL;
end = next_svcn;
while (end > evcn) {
/* Remove segment [svcn : evcn). */
- mi_remove_attr(mi, attr);
+ mi_remove_attr(NULL, mi, attr);
if (!al_remove_le(ni, le)) {
err = -EINVAL;
end = next_svcn;
while (end > evcn) {
/* Remove segment [svcn : evcn). */
- mi_remove_attr(mi, attr);
+ mi_remove_attr(NULL, mi, attr);
if (!al_remove_le(ni, le)) {
err = -EINVAL;
u16 le_sz;
u16 roff = le16_to_cpu(attr->nres.run_off);
- /* run==1 means unpack and deallocate. */
run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn,
evcn1 - 1, svcn, Add2Ptr(attr, roff),
le32_to_cpu(attr->size) - roff);
/* Delete this attribute segment. */
- mi_remove_attr(mi, attr);
+ mi_remove_attr(NULL, mi, attr);
if (!le)
break;
struct ATTR_LIST_ENTRY *le;
size_t off;
u16 sz;
- size_t asize, new_asize;
+ size_t asize, new_asize, old_size;
u64 new_size;
typeof(ni->attr_list) *al = &ni->attr_list;
* Compute the size of the new 'le'
*/
sz = le_size(name_len);
- new_size = al->size + sz;
- asize = al_aligned(al->size);
+ old_size = al->size;
+ new_size = old_size + sz;
+ asize = al_aligned(old_size);
new_asize = al_aligned(new_size);
/* Scan forward to the point at which the new 'le' should be inserted. */
return -ENOMEM;
memcpy(ptr, al->le, off);
- memcpy(Add2Ptr(ptr, off + sz), le, al->size - off);
+ memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
le = Add2Ptr(ptr, off);
kfree(al->le);
al->le = ptr;
} else {
- memmove(Add2Ptr(le, sz), le, al->size - off);
+ memmove(Add2Ptr(le, sz), le, old_size - off);
}
+ *new_le = le;
al->size = new_size;
le->id = id;
memcpy(le->name, name, sizeof(short) * name_len);
- al->dirty = true;
-
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
&new_size, true, &attr);
- if (err)
+ if (err) {
+ /* Undo memmove above. */
+ memmove(le, Add2Ptr(le, sz), old_size - off);
+ al->size = old_size;
return err;
+ }
+
+ al->dirty = true;
if (attr && attr->non_res) {
err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
al->size);
if (err)
return err;
+ al->dirty = false;
}
- al->dirty = false;
- *new_le = le;
-
return 0;
}
/*
* ni_load_mi - Load mft_inode corresponded list_entry.
*/
-int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
+int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le,
struct mft_inode **mi)
{
CLST rno;
if (!attr)
return -ENOENT;
- mi_remove_attr(&ni->mi, attr);
+ mi_remove_attr(ni, &ni->mi, attr);
return 0;
}
if (!attr)
return -ENOENT;
- mi_remove_attr(mi, attr);
+ mi_remove_attr(ni, mi, attr);
if (PtrOffset(ni->attr_list.le, le) >= ni->attr_list.size)
return 0;
*
* Return: Not full constructed attribute or NULL if not possible to create.
*/
-static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni,
- struct mft_inode *mi,
- struct ATTR_LIST_ENTRY *le,
- enum ATTR_TYPE type, const __le16 *name,
- u8 name_len, u32 asize, u16 name_off,
- CLST svcn)
+static struct ATTRIB *
+ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTR_LIST_ENTRY *le, enum ATTR_TYPE type,
+ const __le16 *name, u8 name_len, u32 asize, u16 name_off,
+ CLST svcn, struct ATTR_LIST_ENTRY **ins_le)
{
int err;
struct ATTRIB *attr;
le->ref = ref;
out:
+ if (ins_le)
+ *ins_le = le;
return attr;
}
if (next_svcn >= evcn + 1) {
/* We can remove this attribute segment. */
al_remove_le(ni, le);
- mi_remove_attr(mi, attr);
+ mi_remove_attr(NULL, mi, attr);
le = le_p;
continue;
}
free -= asize;
}
- /* Is seems that attribute list can be removed from primary record. */
- mi_remove_attr(&ni->mi, attr_list);
+ /* It seems that attribute list can be removed from primary record. */
+ mi_remove_attr(NULL, &ni->mi, attr_list);
/*
* Repeat the cycle above and move all attributes to primary record.
attr_ins->id = id;
/* Remove from original record. */
- mi_remove_attr(mi, attr);
+ mi_remove_attr(NULL, mi, attr);
}
run_deallocate(sbi, &ni->attr_list.run, true);
memcpy(attr, b, asize);
attr->id = le_b[nb]->id;
- WARN_ON(!mi_remove_attr(&ni->mi, b));
+ /* Remove from primary record. */
+ WARN_ON(!mi_remove_attr(NULL, &ni->mi, b));
if (to_free <= asize)
break;
static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
enum ATTR_TYPE type, const __le16 *name, u8 name_len,
u32 asize, CLST svcn, u16 name_off, bool force_ext,
- struct ATTRIB **ins_attr, struct mft_inode **ins_mi)
+ struct ATTRIB **ins_attr, struct mft_inode **ins_mi,
+ struct ATTR_LIST_ENTRY **ins_le)
{
struct ATTRIB *attr;
struct mft_inode *mi;
/* Try to insert attribute into this subrecord. */
attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize,
- name_off, svcn);
+ name_off, svcn, ins_le);
if (!attr)
continue;
if (ins_attr)
*ins_attr = attr;
+ if (ins_mi)
+ *ins_mi = mi;
return 0;
}
}
attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize,
- name_off, svcn);
+ name_off, svcn, ins_le);
if (!attr)
goto out2;
static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, u32 asize,
u16 name_off, CLST svcn, struct ATTRIB **ins_attr,
- struct mft_inode **ins_mi)
+ struct mft_inode **ins_mi,
+ struct ATTR_LIST_ENTRY **ins_le)
{
struct ntfs_sb_info *sbi = ni->mi.sbi;
int err;
if (asize <= free) {
attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len,
- asize, name_off, svcn);
+ asize, name_off, svcn, ins_le);
if (attr) {
if (ins_attr)
*ins_attr = attr;
if (!is_mft || type != ATTR_DATA || svcn) {
/* This ATTRIB will be external. */
err = ni_ins_attr_ext(ni, NULL, type, name, name_len, asize,
- svcn, name_off, false, ins_attr, ins_mi);
+ svcn, name_off, false, ins_attr, ins_mi,
+ ins_le);
goto out;
}
t16 = le16_to_cpu(attr->name_off);
err = ni_ins_attr_ext(ni, le, attr->type, Add2Ptr(attr, t16),
attr->name_len, t32, attr_svcn(attr), t16,
- false, &eattr, NULL);
+ false, &eattr, NULL, NULL);
if (err)
return err;
memcpy(eattr, attr, t32);
eattr->id = id;
- /* Remove attrib from primary record. */
- mi_remove_attr(&ni->mi, attr);
+ /* Remove from primary record. */
+ mi_remove_attr(NULL, &ni->mi, attr);
/* attr now points to next attribute. */
if (attr->type == ATTR_END)
;
attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len, asize,
- name_off, svcn);
+ name_off, svcn, ins_le);
if (!attr) {
err = -EINVAL;
goto out;
*/
attr = ni_ins_new_attr(ni, mi_min, NULL, ATTR_DATA, NULL, 0,
SIZEOF_NONRESIDENT + run_size,
- SIZEOF_NONRESIDENT, svcn);
+ SIZEOF_NONRESIDENT, svcn, NULL);
if (!attr) {
err = -EINVAL;
goto out;
err = ni_ins_attr_ext(ni, le, attr->type, attr_name(attr),
attr->name_len, asize, attr_svcn(attr),
le16_to_cpu(attr->name_off), true,
- &ins_attr, NULL);
+ &ins_attr, NULL, NULL);
if (err)
goto out;
memcpy(ins_attr, attr, asize);
ins_attr->id = le->id;
- mi_remove_attr(&ni->mi, attr);
+ /* Remove from primary record. */
+ mi_remove_attr(NULL, &ni->mi, attr);
done += asize;
goto out;
}
err = ni_insert_attr(ni, type, name, name_len, asize, name_off, svcn,
- &attr, mi);
+ &attr, mi, NULL);
if (err)
goto out;
*/
int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
enum ATTR_TYPE type, const __le16 *name, u8 name_len,
- struct ATTRIB **new_attr, struct mft_inode **mi)
+ struct ATTRIB **new_attr, struct mft_inode **mi,
+ struct ATTR_LIST_ENTRY **le)
{
int err;
u32 name_size = ALIGN(name_len * sizeof(short), 8);
struct ATTRIB *attr;
err = ni_insert_attr(ni, type, name, name_len, asize, SIZEOF_RESIDENT,
- 0, &attr, mi);
+ 0, &attr, mi, le);
if (err)
return err;
attr->res.data_size = cpu_to_le32(data_size);
attr->res.data_off = cpu_to_le16(SIZEOF_RESIDENT + name_size);
- if (type == ATTR_NAME)
+ if (type == ATTR_NAME) {
attr->res.flags = RESIDENT_FLAG_INDEXED;
+
+ /* is_attr_indexed(attr)) == true */
+ le16_add_cpu(&ni->mi.mrec->hard_links, +1);
+ ni->mi.dirty = true;
+ }
attr->res.res = 0;
if (new_attr)
/*
* ni_remove_attr_le - Remove attribute from record.
*/
-int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
- struct ATTR_LIST_ENTRY *le)
+void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
+ struct mft_inode *mi, struct ATTR_LIST_ENTRY *le)
{
- int err;
- struct mft_inode *mi;
-
- err = ni_load_mi(ni, le, &mi);
- if (err)
- return err;
-
- mi_remove_attr(mi, attr);
+ mi_remove_attr(ni, mi, attr);
if (le)
al_remove_le(ni, le);
-
- return 0;
}
/*
/* ni_fname_name
*
- *Return: File name attribute by its value. */
+ * Return: File name attribute by its value.
+ */
struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
const struct cpu_str *uni,
const struct MFT_REF *home_dir,
+ struct mft_inode **mi,
struct ATTR_LIST_ENTRY **le)
{
struct ATTRIB *attr = NULL;
/* Enumerate all names. */
next:
- attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, NULL);
+ attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, mi);
if (!attr)
return NULL;
* Return: File name attribute with given type.
*/
struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
+ struct mft_inode **mi,
struct ATTR_LIST_ENTRY **le)
{
struct ATTRIB *attr = NULL;
*le = NULL;
+ if (FILE_NAME_POSIX == name_type)
+ return NULL;
+
/* Enumerate all names. */
for (;;) {
- attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL,
- NULL);
+ attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, mi);
if (!attr)
return NULL;
return err;
}
+/*
+ * ni_remove_name - Removes name 'de' from MFT and from directory.
+ * 'de2' and 'undo_step' are used to restore MFT/dir, if error occurs.
+ */
+int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
+ struct NTFS_DE *de, struct NTFS_DE **de2, int *undo_step)
+{
+ int err;
+ struct ntfs_sb_info *sbi = ni->mi.sbi;
+ struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1);
+ struct ATTR_FILE_NAME *fname;
+ struct ATTR_LIST_ENTRY *le;
+ struct mft_inode *mi;
+ u16 de_key_size = le16_to_cpu(de->key_size);
+ u8 name_type;
+
+ *undo_step = 0;
+
+ /* Find name in record. */
+ mi_get_ref(&dir_ni->mi, &de_name->home);
+
+ fname = ni_fname_name(ni, (struct cpu_str *)&de_name->name_len,
+ &de_name->home, &mi, &le);
+ if (!fname)
+ return -ENOENT;
+
+ memcpy(&de_name->dup, &fname->dup, sizeof(struct NTFS_DUP_INFO));
+ name_type = paired_name(fname->type);
+
+ /* Mark ntfs as dirty. It will be cleared at umount. */
+ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
+
+ /* Step 1: Remove name from directory. */
+ err = indx_delete_entry(&dir_ni->dir, dir_ni, fname, de_key_size, sbi);
+ if (err)
+ return err;
+
+ /* Step 2: Remove name from MFT. */
+ ni_remove_attr_le(ni, attr_from_name(fname), mi, le);
+
+ *undo_step = 2;
+
+ /* Get paired name. */
+ fname = ni_fname_type(ni, name_type, &mi, &le);
+ if (fname) {
+ u16 de2_key_size = fname_full_size(fname);
+
+ *de2 = Add2Ptr(de, 1024);
+ (*de2)->key_size = cpu_to_le16(de2_key_size);
+
+ memcpy(*de2 + 1, fname, de2_key_size);
+
+ /* Step 3: Remove paired name from directory. */
+ err = indx_delete_entry(&dir_ni->dir, dir_ni, fname,
+ de2_key_size, sbi);
+ if (err)
+ return err;
+
+ /* Step 4: Remove paired name from MFT. */
+ ni_remove_attr_le(ni, attr_from_name(fname), mi, le);
+
+ *undo_step = 4;
+ }
+ return 0;
+}
+
+/*
+ * ni_remove_name_undo - Paired function for ni_remove_name.
+ *
+ * Return: True if ok
+ */
+bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
+ struct NTFS_DE *de, struct NTFS_DE *de2, int undo_step)
+{
+ struct ntfs_sb_info *sbi = ni->mi.sbi;
+ struct ATTRIB *attr;
+ u16 de_key_size = de2 ? le16_to_cpu(de2->key_size) : 0;
+
+ switch (undo_step) {
+ case 4:
+ if (ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0,
+ &attr, NULL, NULL)) {
+ return false;
+ }
+ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de2 + 1, de_key_size);
+
+ mi_get_ref(&ni->mi, &de2->ref);
+ de2->size = cpu_to_le16(ALIGN(de_key_size, 8) +
+ sizeof(struct NTFS_DE));
+ de2->flags = 0;
+ de2->res = 0;
+
+ if (indx_insert_entry(&dir_ni->dir, dir_ni, de2, sbi, NULL,
+ 1)) {
+ return false;
+ }
+ fallthrough;
+
+ case 2:
+ de_key_size = le16_to_cpu(de->key_size);
+
+ if (ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0,
+ &attr, NULL, NULL)) {
+ return false;
+ }
+
+ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de + 1, de_key_size);
+ mi_get_ref(&ni->mi, &de->ref);
+
+ if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * ni_add_name - Add new name in MFT and in directory.
+ */
+int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
+ struct NTFS_DE *de)
+{
+ int err;
+ struct ATTRIB *attr;
+ struct ATTR_LIST_ENTRY *le;
+ struct mft_inode *mi;
+ struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1);
+ u16 de_key_size = le16_to_cpu(de->key_size);
+
+ mi_get_ref(&ni->mi, &de->ref);
+ mi_get_ref(&dir_ni->mi, &de_name->home);
+
+ /* Insert new name in MFT. */
+ err = ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0, &attr,
+ &mi, &le);
+ if (err)
+ return err;
+
+ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de_name, de_key_size);
+
+ /* Insert new name in directory. */
+ err = indx_insert_entry(&dir_ni->dir, dir_ni, de, ni->mi.sbi, NULL, 0);
+ if (err)
+ ni_remove_attr_le(ni, attr, mi, le);
+
+ return err;
+}
+
+/*
+ * ni_rename - Remove one name and insert new name.
+ */
+int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
+ struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
+ bool *is_bad)
+{
+ int err;
+ struct NTFS_DE *de2 = NULL;
+ int undo = 0;
+
+ /*
+ * There are two possible ways to rename:
+ * 1) Add new name and remove old name.
+ * 2) Remove old name and add new name.
+ *
+ * In most cases (not all!) adding new name in MFT and in directory can
+ * allocate additional cluster(s).
+ * Second way may result to bad inode if we can't add new name
+ * and then can't restore (add) old name.
+ */
+
+ /*
+ * Way 1 - Add new + remove old.
+ */
+ err = ni_add_name(new_dir_ni, ni, new_de);
+ if (!err) {
+ err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
+ if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo))
+ *is_bad = true;
+ }
+
+ /*
+ * Way 2 - Remove old + add new.
+ */
+ /*
+ * err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
+ * if (!err) {
+ * err = ni_add_name(new_dir_ni, ni, new_de);
+ * if (err && !ni_remove_name_undo(dir_ni, ni, de, de2, undo))
+ * *is_bad = true;
+ * }
+ */
+
+ return err;
+}
+
+/*
+ * ni_is_dirty - Return: True if 'ni' requires ni_write_inode.
+ */
+bool ni_is_dirty(struct inode *inode)
+{
+ struct ntfs_inode *ni = ntfs_i(inode);
+ struct rb_node *node;
+
+ if (ni->mi.dirty || ni->attr_list.dirty ||
+ (ni->ni_flags & NI_FLAG_UPDATE_PARENT))
+ return true;
+
+ for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) {
+ if (rb_entry(node, struct mft_inode, node)->dirty)
+ return true;
+ }
+
+ return false;
+}
+
/*
* ni_update_parent
*
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct super_block *sb = sbi->sb;
bool re_dirty = false;
- bool active = sb->s_flags & SB_ACTIVE;
- bool upd_parent = ni->ni_flags & NI_FLAG_UPDATE_PARENT;
if (ni->mi.mrec->flags & RECORD_FLAG_DIR) {
dup->fa |= FILE_ATTRIBUTE_DIRECTORY;
struct ATTR_FILE_NAME *fname;
fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
- if (!fname)
+ if (!fname || !memcmp(&fname->dup, dup, sizeof(fname->dup)))
continue;
- if (memcmp(&fname->dup, dup, sizeof(fname->dup))) {
- memcpy(&fname->dup, dup, sizeof(fname->dup));
- mi->dirty = true;
- } else if (!upd_parent) {
- continue;
- }
-
- if (!active)
- continue; /* Avoid __wait_on_freeing_inode(inode); */
-
/* ntfs_iget5 may sleep. */
dir = ntfs_iget5(sb, &fname->home, NULL);
if (IS_ERR(dir)) {
} else {
indx_update_dup(dir_ni, sbi, fname, dup, sync);
ni_unlock(dir_ni);
+ memcpy(&fname->dup, dup, sizeof(fname->dup));
+ mi->dirty = true;
}
}
iput(dir);
ni->mi.dirty = true;
if (!ntfs_is_meta_file(sbi, inode->i_ino) &&
- (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))) {
+ (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))
+ /* Avoid __wait_on_freeing_inode(inode). */
+ && (sb->s_flags & SB_ACTIVE)) {
dup.cr_time = std->cr_time;
/* Not critical if this function fail. */
re_dirty = ni_update_parent(ni, &dup, sync);
return err;
}
- if (re_dirty && (sb->s_flags & SB_ACTIVE))
+ if (re_dirty)
mark_inode_dirty_sync(inode);
return 0;
enum ALLOCATE_OPT opt)
{
int err;
+ CLST alen = 0;
struct super_block *sb = sbi->sb;
- size_t a_lcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen;
+ size_t alcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen;
struct wnd_bitmap *wnd = &sbi->used.bitmap;
down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
if (opt & ALLOCATE_MFT) {
- CLST alen;
-
zlen = wnd_zone_len(wnd);
if (!zlen) {
err = ntfs_refresh_zone(sbi);
if (err)
goto out;
-
zlen = wnd_zone_len(wnd);
+ }
- if (!zlen) {
- ntfs_err(sbi->sb,
- "no free space to extend mft");
- err = -ENOSPC;
- goto out;
- }
+ if (!zlen) {
+ ntfs_err(sbi->sb, "no free space to extend mft");
+ goto out;
}
lcn = wnd_zone_bit(wnd);
wnd_zone_set(wnd, lcn + alen, zlen - alen);
err = wnd_set_used(wnd, lcn, alen);
- if (err)
- goto out;
-
- *new_lcn = lcn;
- *new_len = alen;
- goto ok;
+ if (err) {
+ up_write(&wnd->rw_lock);
+ return err;
+ }
+ alcn = lcn;
+ goto out;
}
-
/*
* 'Cause cluster 0 is always used this value means that we should use
* cached value of 'next_free_lcn' to improve performance.
if (lcn >= wnd->nbits)
lcn = 0;
- *new_len = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &a_lcn);
- if (*new_len) {
- *new_lcn = a_lcn;
- goto ok;
- }
+ alen = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &alcn);
+ if (alen)
+ goto out;
/* Try to use clusters from MftZone. */
zlen = wnd_zone_len(wnd);
zeroes = wnd_zeroes(wnd);
- /* Check too big request. */
- if (len > zeroes + zlen)
- goto no_space;
-
- if (zlen <= NTFS_MIN_MFT_ZONE)
- goto no_space;
+ /* Check too big request */
+ if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE)
+ goto out;
/* How many clusters to cat from zone. */
zlcn = wnd_zone_bit(wnd);
wnd_zone_set(wnd, zlcn, new_zlen);
/* Allocate continues clusters. */
- *new_len =
- wnd_find(wnd, len, 0,
- BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &a_lcn);
- if (*new_len) {
- *new_lcn = a_lcn;
- goto ok;
- }
-
-no_space:
- up_write(&wnd->rw_lock);
-
- return -ENOSPC;
-
-ok:
- err = 0;
+ alen = wnd_find(wnd, len, 0,
+ BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn);
- ntfs_unmap_meta(sb, *new_lcn, *new_len);
+out:
+ if (alen) {
+ err = 0;
+ *new_len = alen;
+ *new_lcn = alcn;
- if (opt & ALLOCATE_MFT)
- goto out;
+ ntfs_unmap_meta(sb, alcn, alen);
- /* Set hint for next requests. */
- sbi->used.next_free_lcn = *new_lcn + *new_len;
+ /* Set hint for next requests. */
+ if (!(opt & ALLOCATE_MFT))
+ sbi->used.next_free_lcn = alcn + alen;
+ } else {
+ err = -ENOSPC;
+ }
-out:
up_write(&wnd->rw_lock);
return err;
}
sii_e.sec_id = d_security->key.sec_id;
memcpy(&sii_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR);
- err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL);
+ err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL, 0);
if (err)
goto out;
fnd_clear(fnd_sdh);
err = indx_insert_entry(indx_sdh, ni, &sdh_e.de, (void *)(size_t)1,
- fnd_sdh);
+ fnd_sdh, 0);
if (err)
goto out;
mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE);
- err = indx_insert_entry(indx, ni, &re.de, NULL, NULL);
+ err = indx_insert_entry(indx, ni, &re.de, NULL, NULL, 0);
mark_inode_dirty(&ni->vfs_inode);
ni_unlock(ni);
alloc->nres.valid_size = alloc->nres.data_size = cpu_to_le64(data_size);
err = ni_insert_resident(ni, bitmap_size(1), ATTR_BITMAP, in->name,
- in->name_len, &bitmap, NULL);
+ in->name_len, &bitmap, NULL, NULL);
if (err)
goto out2;
return 0;
out2:
- mi_remove_attr(&ni->mi, alloc);
+ mi_remove_attr(NULL, &ni->mi, alloc);
out1:
run_deallocate(sbi, &run, false);
/*
* indx_insert_into_root - Attempt to insert an entry into the index root.
*
+ * @undo - True if we undoing previous remove.
* If necessary, it will twiddle the index b-tree.
*/
static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
const struct NTFS_DE *new_de,
struct NTFS_DE *root_de, const void *ctx,
- struct ntfs_fnd *fnd)
+ struct ntfs_fnd *fnd, bool undo)
{
int err = 0;
struct NTFS_DE *e, *e0, *re;
struct mft_inode *mi;
struct ATTRIB *attr;
- struct MFT_REC *rec;
struct INDEX_HDR *hdr;
struct indx_node *n;
CLST new_vbn;
__le64 *sub_vbn, t_vbn;
u16 new_de_size;
- u32 hdr_used, hdr_total, asize, used, to_move;
+ u32 hdr_used, hdr_total, asize, to_move;
u32 root_size, new_root_size;
struct ntfs_sb_info *sbi;
int ds_root;
/*
* Try easy case:
- * hdr_insert_de will succeed if there's room the root for the new entry.
+ * hdr_insert_de will succeed if there's
+ * room the root for the new entry.
*/
hdr = &root->ihdr;
sbi = ni->mi.sbi;
- rec = mi->mrec;
- used = le32_to_cpu(rec->used);
new_de_size = le16_to_cpu(new_de->size);
hdr_used = le32_to_cpu(hdr->used);
hdr_total = le32_to_cpu(hdr->total);
ds_root = new_de_size + hdr_used - hdr_total;
- if (used + ds_root < sbi->max_bytes_per_attr) {
- /* Make a room for new elements. */
- mi_resize_attr(mi, attr, ds_root);
+ /* If 'undo' is set then reduce requirements. */
+ if ((undo || asize + ds_root < sbi->max_bytes_per_attr) &&
+ mi_resize_attr(mi, attr, ds_root)) {
hdr->total = cpu_to_le32(hdr_total + ds_root);
e = hdr_insert_de(indx, hdr, new_de, root_de, ctx);
WARN_ON(!e);
sizeof(u64);
ds_root = new_root_size - root_size;
- if (ds_root > 0 && used + ds_root > sbi->max_bytes_per_attr) {
+ if (ds_root > 0 && asize + ds_root > sbi->max_bytes_per_attr) {
/* Make root external. */
err = -EOPNOTSUPP;
goto out_free_re;
put_indx_node(n);
fnd_clear(fnd);
- err = indx_insert_entry(indx, ni, new_de, ctx, fnd);
+ err = indx_insert_entry(indx, ni, new_de, ctx, fnd, undo);
goto out_free_root;
}
*/
if (!level) {
/* Insert in root. */
- err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd);
+ err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0);
if (err)
goto out;
} else {
/*
* indx_insert_entry - Insert new entry into index.
+ *
+ * @undo - True if we undoing previous remove.
*/
int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
const struct NTFS_DE *new_de, const void *ctx,
- struct ntfs_fnd *fnd)
+ struct ntfs_fnd *fnd, bool undo)
{
int err;
int diff;
* new entry into it.
*/
err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx,
- fnd);
+ fnd, undo);
if (err)
goto out;
} else {
fnd->level - 1,
fnd)
: indx_insert_into_root(indx, ni, re, e,
- ctx, fnd);
+ ctx, fnd, 0);
kfree(re);
if (err)
* Re-insert the entry into the tree.
* Find the spot the tree where we want to insert the new entry.
*/
- err = indx_insert_entry(indx, ni, me, ctx, fnd);
+ err = indx_insert_entry(indx, ni, me, ctx, fnd, 0);
kfree(me);
if (err)
goto out;
struct ntfs_index *indx = &ni->dir;
fnd = fnd_get();
- if (!fnd) {
- err = -ENOMEM;
- goto out1;
- }
+ if (!fnd)
+ return -ENOMEM;
root = indx_get_root(indx, ni, NULL, &mi);
if (!root) {
out:
fnd_put(fnd);
-
-out1:
return err;
}
goto out;
}
+ if (names != le16_to_cpu(rec->hard_links)) {
+ /* Correct minor error on the fly. Do not mark inode as dirty. */
+ rec->hard_links = cpu_to_le16(names);
+ ni->mi.dirty = true;
+ }
+
set_nlink(inode, names);
if (S_ISDIR(mode)) {
}
inode = &ni->vfs_inode;
inode_init_owner(mnt_userns, inode, dir, mode);
+ mode = inode->i_mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = ni->i_crtime =
current_time(inode);
attr = Add2Ptr(attr, asize);
}
+ attr->id = cpu_to_le16(aid++);
if (fa & FILE_ATTRIBUTE_DIRECTORY) {
/*
* Regular directory or symlink to directory.
attr->type = ATTR_ROOT;
attr->size = cpu_to_le32(asize);
- attr->id = cpu_to_le16(aid++);
attr->name_len = ARRAY_SIZE(I30_NAME);
attr->name_off = SIZEOF_RESIDENT_LE;
/* Insert empty ATTR_DATA */
attr->type = ATTR_DATA;
attr->size = cpu_to_le32(SIZEOF_RESIDENT);
- attr->id = cpu_to_le16(aid++);
attr->name_off = SIZEOF_RESIDENT_LE;
attr->res.data_off = SIZEOF_RESIDENT_LE;
- } else {
+ } else if (S_ISREG(mode)) {
/*
- * Regular file or node.
+ * Regular file. Create empty non resident data attribute.
*/
attr->type = ATTR_DATA;
- attr->id = cpu_to_le16(aid++);
-
- if (S_ISREG(mode)) {
- /* Create empty non resident data attribute. */
- attr->non_res = 1;
- attr->nres.evcn = cpu_to_le64(-1ll);
- if (fa & FILE_ATTRIBUTE_SPARSE_FILE) {
- attr->size =
- cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
- attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
- attr->flags = ATTR_FLAG_SPARSED;
- asize = SIZEOF_NONRESIDENT_EX + 8;
- } else if (fa & FILE_ATTRIBUTE_COMPRESSED) {
- attr->size =
- cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
- attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
- attr->flags = ATTR_FLAG_COMPRESSED;
- attr->nres.c_unit = COMPRESSION_UNIT;
- asize = SIZEOF_NONRESIDENT_EX + 8;
- } else {
- attr->size =
- cpu_to_le32(SIZEOF_NONRESIDENT + 8);
- attr->name_off = SIZEOF_NONRESIDENT_LE;
- asize = SIZEOF_NONRESIDENT + 8;
- }
- attr->nres.run_off = attr->name_off;
+ attr->non_res = 1;
+ attr->nres.evcn = cpu_to_le64(-1ll);
+ if (fa & FILE_ATTRIBUTE_SPARSE_FILE) {
+ attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
+ attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
+ attr->flags = ATTR_FLAG_SPARSED;
+ asize = SIZEOF_NONRESIDENT_EX + 8;
+ } else if (fa & FILE_ATTRIBUTE_COMPRESSED) {
+ attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8);
+ attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
+ attr->flags = ATTR_FLAG_COMPRESSED;
+ attr->nres.c_unit = COMPRESSION_UNIT;
+ asize = SIZEOF_NONRESIDENT_EX + 8;
} else {
- /* Create empty resident data attribute. */
- attr->size = cpu_to_le32(SIZEOF_RESIDENT);
- attr->name_off = SIZEOF_RESIDENT_LE;
- if (fa & FILE_ATTRIBUTE_SPARSE_FILE)
- attr->flags = ATTR_FLAG_SPARSED;
- else if (fa & FILE_ATTRIBUTE_COMPRESSED)
- attr->flags = ATTR_FLAG_COMPRESSED;
- attr->res.data_off = SIZEOF_RESIDENT_LE;
- asize = SIZEOF_RESIDENT;
- ni->ni_flags |= NI_FLAG_RESIDENT;
+ attr->size = cpu_to_le32(SIZEOF_NONRESIDENT + 8);
+ attr->name_off = SIZEOF_NONRESIDENT_LE;
+ asize = SIZEOF_NONRESIDENT + 8;
}
+ attr->nres.run_off = attr->name_off;
+ } else {
+ /*
+ * Node. Create empty resident data attribute.
+ */
+ attr->type = ATTR_DATA;
+ attr->size = cpu_to_le32(SIZEOF_RESIDENT);
+ attr->name_off = SIZEOF_RESIDENT_LE;
+ if (fa & FILE_ATTRIBUTE_SPARSE_FILE)
+ attr->flags = ATTR_FLAG_SPARSED;
+ else if (fa & FILE_ATTRIBUTE_COMPRESSED)
+ attr->flags = ATTR_FLAG_COMPRESSED;
+ attr->res.data_off = SIZEOF_RESIDENT_LE;
+ asize = SIZEOF_RESIDENT;
+ ni->ni_flags |= NI_FLAG_RESIDENT;
}
if (S_ISDIR(mode)) {
asize = ALIGN(SIZEOF_RESIDENT + nsize, 8);
t16 = PtrOffset(rec, attr);
- if (asize + t16 + 8 > sbi->record_size) {
+ /* 0x78 - the size of EA + EAINFO to store WSL */
+ if (asize + t16 + 0x78 + 8 > sbi->record_size) {
CLST alen;
CLST clst = bytes_to_cluster(sbi, nsize);
rec->next_attr_id = cpu_to_le16(aid);
/* Step 2: Add new name in index. */
- err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd);
+ err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd, 0);
if (err)
goto out6;
- /* Update current directory record. */
- mark_inode_dirty(dir);
-
inode->i_generation = le16_to_cpu(rec->seq);
dir->i_mtime = dir->i_ctime = inode->i_atime;
if (S_ISDIR(mode)) {
- if (dir->i_mode & S_ISGID)
- mode |= S_ISGID;
inode->i_op = &ntfs_dir_inode_operations;
inode->i_fop = &ntfs_dir_operations;
} else if (S_ISLNK(mode)) {
d_instantiate(dentry, inode);
ntfs_save_wsl_perm(inode);
- mark_inode_dirty(inode);
mark_inode_dirty(dir);
+ mark_inode_dirty(inode);
/* Normal exit. */
goto out2;
int ntfs_link_inode(struct inode *inode, struct dentry *dentry)
{
int err;
- struct inode *dir = d_inode(dentry->d_parent);
- struct ntfs_inode *dir_ni = ntfs_i(dir);
struct ntfs_inode *ni = ntfs_i(inode);
- struct super_block *sb = inode->i_sb;
- struct ntfs_sb_info *sbi = sb->s_fs_info;
- const struct qstr *name = &dentry->d_name;
- struct NTFS_DE *new_de = NULL;
- struct ATTR_FILE_NAME *fname;
- struct ATTRIB *attr;
- u16 key_size;
- struct INDEX_ROOT *dir_root;
-
- dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL);
- if (!dir_root)
- return -EINVAL;
+ struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
+ struct NTFS_DE *de;
+ struct ATTR_FILE_NAME *de_name;
/* Allocate PATH_MAX bytes. */
- new_de = __getname();
- if (!new_de)
+ de = __getname();
+ if (!de)
return -ENOMEM;
- /* Mark rw ntfs as dirty. It will be cleared at umount. */
- ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY);
-
- /* Insert file name. */
- err = fill_name_de(sbi, new_de, name, NULL);
- if (err)
- goto out;
-
- key_size = le16_to_cpu(new_de->key_size);
- err = ni_insert_resident(ni, key_size, ATTR_NAME, NULL, 0, &attr, NULL);
- if (err)
- goto out;
-
- mi_get_ref(&ni->mi, &new_de->ref);
-
- fname = (struct ATTR_FILE_NAME *)(new_de + 1);
- mi_get_ref(&dir_ni->mi, &fname->home);
- fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time =
- fname->dup.a_time = kernel2nt(&inode->i_ctime);
- fname->dup.alloc_size = fname->dup.data_size = 0;
- fname->dup.fa = ni->std_fa;
- fname->dup.ea_size = fname->dup.reparse = 0;
-
- memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), fname, key_size);
+ /* Mark rw ntfs as dirty. It will be cleared at umount. */
+ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
- err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, NULL);
+ /* Construct 'de'. */
+ err = fill_name_de(sbi, de, &dentry->d_name, NULL);
if (err)
goto out;
- le16_add_cpu(&ni->mi.mrec->hard_links, 1);
- ni->mi.dirty = true;
+ de_name = (struct ATTR_FILE_NAME *)(de + 1);
+ /* Fill duplicate info. */
+ de_name->dup.cr_time = de_name->dup.m_time = de_name->dup.c_time =
+ de_name->dup.a_time = kernel2nt(&inode->i_ctime);
+ de_name->dup.alloc_size = de_name->dup.data_size =
+ cpu_to_le64(inode->i_size);
+ de_name->dup.fa = ni->std_fa;
+ de_name->dup.ea_size = de_name->dup.reparse = 0;
+ err = ni_add_name(ntfs_i(d_inode(dentry->d_parent)), ni, de);
out:
- __putname(new_de);
+ __putname(de);
return err;
}
int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry)
{
int err;
- struct super_block *sb = dir->i_sb;
- struct ntfs_sb_info *sbi = sb->s_fs_info;
+ struct ntfs_sb_info *sbi = dir->i_sb->s_fs_info;
struct inode *inode = d_inode(dentry);
struct ntfs_inode *ni = ntfs_i(inode);
- const struct qstr *name = &dentry->d_name;
struct ntfs_inode *dir_ni = ntfs_i(dir);
- struct ntfs_index *indx = &dir_ni->dir;
- struct cpu_str *uni = NULL;
- struct ATTR_FILE_NAME *fname;
- u8 name_type;
- struct ATTR_LIST_ENTRY *le;
- struct MFT_REF ref;
- bool is_dir = S_ISDIR(inode->i_mode);
- struct INDEX_ROOT *dir_root;
+ struct NTFS_DE *de, *de2 = NULL;
+ int undo_remove;
- dir_root = indx_get_root(indx, dir_ni, NULL, NULL);
- if (!dir_root)
+ if (ntfs_is_meta_file(sbi, ni->mi.rno))
return -EINVAL;
+ /* Allocate PATH_MAX bytes. */
+ de = __getname();
+ if (!de)
+ return -ENOMEM;
+
ni_lock(ni);
- if (is_dir && !dir_is_empty(inode)) {
+ if (S_ISDIR(inode->i_mode) && !dir_is_empty(inode)) {
err = -ENOTEMPTY;
- goto out1;
- }
-
- if (ntfs_is_meta_file(sbi, inode->i_ino)) {
- err = -EINVAL;
- goto out1;
- }
-
- /* Allocate PATH_MAX bytes. */
- uni = __getname();
- if (!uni) {
- err = -ENOMEM;
- goto out1;
+ goto out;
}
- /* Convert input string to unicode. */
- err = ntfs_nls_to_utf16(sbi, name->name, name->len, uni, NTFS_NAME_LEN,
- UTF16_HOST_ENDIAN);
+ err = fill_name_de(sbi, de, &dentry->d_name, NULL);
if (err < 0)
- goto out2;
-
- /* Mark rw ntfs as dirty. It will be cleared at umount. */
- ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
-
- /* Find name in record. */
- mi_get_ref(&dir_ni->mi, &ref);
-
- le = NULL;
- fname = ni_fname_name(ni, uni, &ref, &le);
- if (!fname) {
- err = -ENOENT;
- goto out3;
- }
-
- name_type = paired_name(fname->type);
-
- err = indx_delete_entry(indx, dir_ni, fname, fname_full_size(fname),
- sbi);
- if (err)
- goto out3;
-
- /* Then remove name from MFT. */
- ni_remove_attr_le(ni, attr_from_name(fname), le);
-
- le16_add_cpu(&ni->mi.mrec->hard_links, -1);
- ni->mi.dirty = true;
-
- if (name_type != FILE_NAME_POSIX) {
- /* Now we should delete name by type. */
- fname = ni_fname_type(ni, name_type, &le);
- if (fname) {
- err = indx_delete_entry(indx, dir_ni, fname,
- fname_full_size(fname), sbi);
- if (err)
- goto out3;
+ goto out;
- ni_remove_attr_le(ni, attr_from_name(fname), le);
+ undo_remove = 0;
+ err = ni_remove_name(dir_ni, ni, de, &de2, &undo_remove);
- le16_add_cpu(&ni->mi.mrec->hard_links, -1);
- }
- }
-out3:
- switch (err) {
- case 0:
+ if (!err) {
drop_nlink(inode);
- break;
- case -ENOTEMPTY:
- case -ENOSPC:
- case -EROFS:
- break;
- default:
+ dir->i_mtime = dir->i_ctime = current_time(dir);
+ mark_inode_dirty(dir);
+ inode->i_ctime = dir->i_ctime;
+ if (inode->i_nlink)
+ mark_inode_dirty(inode);
+ } else if (!ni_remove_name_undo(dir_ni, ni, de, de2, undo_remove)) {
make_bad_inode(inode);
+ ntfs_inode_err(inode, "failed to undo unlink");
+ ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
+ } else {
+ if (ni_is_dirty(dir))
+ mark_inode_dirty(dir);
+ if (ni_is_dirty(inode))
+ mark_inode_dirty(inode);
}
- dir->i_mtime = dir->i_ctime = current_time(dir);
- mark_inode_dirty(dir);
- inode->i_ctime = dir->i_ctime;
- if (inode->i_nlink)
- mark_inode_dirty(inode);
-
-out2:
- __putname(uni);
-out1:
+out:
ni_unlock(ni);
+ __putname(de);
return err;
}
if (inode != dir)
ni_lock(ni);
- dir->i_ctime = dir->i_mtime = inode->i_ctime = current_time(inode);
inc_nlink(inode);
ihold(inode);
err = ntfs_link_inode(inode, de);
+
if (!err) {
+ dir->i_ctime = dir->i_mtime = inode->i_ctime =
+ current_time(dir);
mark_inode_dirty(inode);
mark_inode_dirty(dir);
d_instantiate(de, inode);
/*
* ntfs_rename - inode_operations::rename
*/
-static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
- struct dentry *old_dentry, struct inode *new_dir,
+static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *dir,
+ struct dentry *dentry, struct inode *new_dir,
struct dentry *new_dentry, u32 flags)
{
int err;
- struct super_block *sb = old_dir->i_sb;
+ struct super_block *sb = dir->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
- struct ntfs_inode *old_dir_ni = ntfs_i(old_dir);
+ struct ntfs_inode *dir_ni = ntfs_i(dir);
struct ntfs_inode *new_dir_ni = ntfs_i(new_dir);
- struct ntfs_inode *old_ni;
- struct ATTR_FILE_NAME *old_name, *new_name, *fname;
- u8 name_type;
- bool is_same;
- struct inode *old_inode, *new_inode;
- struct NTFS_DE *old_de, *new_de;
- struct ATTRIB *attr;
- struct ATTR_LIST_ENTRY *le;
- u16 new_de_key_size;
-
+ struct inode *inode = d_inode(dentry);
+ struct ntfs_inode *ni = ntfs_i(inode);
+ struct inode *new_inode = d_inode(new_dentry);
+ struct NTFS_DE *de, *new_de;
+ bool is_same, is_bad;
+ /*
+ * de - memory of PATH_MAX bytes:
+ * [0-1024) - original name (dentry->d_name)
+ * [1024-2048) - paired to original name, usually DOS variant of dentry->d_name
+ * [2048-3072) - new name (new_dentry->d_name)
+ */
static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + SIZEOF_RESIDENT < 1024);
static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + sizeof(struct NTFS_DE) <
1024);
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;
- old_inode = d_inode(old_dentry);
- new_inode = d_inode(new_dentry);
-
- old_ni = ntfs_i(old_inode);
+ is_same = dentry->d_name.len == new_dentry->d_name.len &&
+ !memcmp(dentry->d_name.name, new_dentry->d_name.name,
+ dentry->d_name.len);
- is_same = old_dentry->d_name.len == new_dentry->d_name.len &&
- !memcmp(old_dentry->d_name.name, new_dentry->d_name.name,
- old_dentry->d_name.len);
-
- if (is_same && old_dir == new_dir) {
+ if (is_same && dir == new_dir) {
/* Nothing to do. */
- err = 0;
- goto out;
+ return 0;
}
- if (ntfs_is_meta_file(sbi, old_inode->i_ino)) {
- err = -EINVAL;
- goto out;
+ if (ntfs_is_meta_file(sbi, inode->i_ino)) {
+ /* Should we print an error? */
+ return -EINVAL;
}
if (new_inode) {
ni_unlock(new_dir_ni);
dput(new_dentry);
if (err)
- goto out;
+ return err;
}
/* Allocate PATH_MAX bytes. */
- old_de = __getname();
- if (!old_de) {
- err = -ENOMEM;
- goto out;
- }
+ de = __getname();
+ if (!de)
+ return -ENOMEM;
- err = fill_name_de(sbi, old_de, &old_dentry->d_name, NULL);
+ /* Translate dentry->d_name into unicode form. */
+ err = fill_name_de(sbi, de, &dentry->d_name, NULL);
if (err < 0)
- goto out1;
-
- old_name = (struct ATTR_FILE_NAME *)(old_de + 1);
+ goto out;
if (is_same) {
- new_de = old_de;
+ /* Reuse 'de'. */
+ new_de = de;
} else {
- new_de = Add2Ptr(old_de, 1024);
+ /* Translate new_dentry->d_name into unicode form. */
+ new_de = Add2Ptr(de, 2048);
err = fill_name_de(sbi, new_de, &new_dentry->d_name, NULL);
if (err < 0)
- goto out1;
- }
-
- ni_lock_dir(old_dir_ni);
- ni_lock(old_ni);
-
- mi_get_ref(&old_dir_ni->mi, &old_name->home);
-
- /* Get pointer to file_name in MFT. */
- fname = ni_fname_name(old_ni, (struct cpu_str *)&old_name->name_len,
- &old_name->home, &le);
- if (!fname) {
- err = -EINVAL;
- goto out2;
+ goto out;
}
- /* Copy fname info from record into new fname. */
- new_name = (struct ATTR_FILE_NAME *)(new_de + 1);
- memcpy(&new_name->dup, &fname->dup, sizeof(fname->dup));
-
- name_type = paired_name(fname->type);
-
- /* Remove first name from directory. */
- err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1,
- le16_to_cpu(old_de->key_size), sbi);
- if (err)
- goto out3;
-
- /* Remove first name from MFT. */
- err = ni_remove_attr_le(old_ni, attr_from_name(fname), le);
- if (err)
- goto out4;
-
- le16_add_cpu(&old_ni->mi.mrec->hard_links, -1);
- old_ni->mi.dirty = true;
-
- if (name_type != FILE_NAME_POSIX) {
- /* Get paired name. */
- fname = ni_fname_type(old_ni, name_type, &le);
- if (fname) {
- /* Remove second name from directory. */
- err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni,
- fname, fname_full_size(fname),
- sbi);
- if (err)
- goto out5;
-
- /* Remove second name from MFT. */
- err = ni_remove_attr_le(old_ni, attr_from_name(fname),
- le);
- if (err)
- goto out6;
-
- le16_add_cpu(&old_ni->mi.mrec->hard_links, -1);
- old_ni->mi.dirty = true;
+ ni_lock_dir(dir_ni);
+ ni_lock(ni);
+
+ is_bad = false;
+ err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de, &is_bad);
+ if (is_bad) {
+ /* Restore after failed rename failed too. */
+ make_bad_inode(inode);
+ ntfs_inode_err(inode, "failed to undo rename");
+ ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
+ } else if (!err) {
+ inode->i_ctime = dir->i_ctime = dir->i_mtime =
+ current_time(dir);
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
+ if (dir != new_dir) {
+ new_dir->i_mtime = new_dir->i_ctime = dir->i_ctime;
+ mark_inode_dirty(new_dir);
}
- }
-
- /* Add new name. */
- mi_get_ref(&old_ni->mi, &new_de->ref);
- mi_get_ref(&ntfs_i(new_dir)->mi, &new_name->home);
-
- new_de_key_size = le16_to_cpu(new_de->key_size);
-
- /* Insert new name in MFT. */
- err = ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0,
- &attr, NULL);
- if (err)
- goto out7;
-
- attr->res.flags = RESIDENT_FLAG_INDEXED;
-
- memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), new_name, new_de_key_size);
-
- le16_add_cpu(&old_ni->mi.mrec->hard_links, 1);
- old_ni->mi.dirty = true;
-
- /* Insert new name in directory. */
- err = indx_insert_entry(&new_dir_ni->dir, new_dir_ni, new_de, sbi,
- NULL);
- if (err)
- goto out8;
- if (IS_DIRSYNC(new_dir))
- err = ntfs_sync_inode(old_inode);
- else
- mark_inode_dirty(old_inode);
+ if (IS_DIRSYNC(dir))
+ ntfs_sync_inode(dir);
- old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
- if (IS_DIRSYNC(old_dir))
- (void)ntfs_sync_inode(old_dir);
- else
- mark_inode_dirty(old_dir);
-
- if (old_dir != new_dir) {
- new_dir->i_mtime = new_dir->i_ctime = old_dir->i_ctime;
- mark_inode_dirty(new_dir);
- }
-
- if (old_inode) {
- old_inode->i_ctime = old_dir->i_ctime;
- mark_inode_dirty(old_inode);
+ if (IS_DIRSYNC(new_dir))
+ ntfs_sync_inode(inode);
}
- err = 0;
- /* Normal way* */
- goto out2;
-
-out8:
- /* undo
- * ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0,
- * &attr, NULL);
- */
- mi_remove_attr(&old_ni->mi, attr);
-out7:
- /* undo
- * ni_remove_attr_le(old_ni, attr_from_name(fname), le);
- */
-out6:
- /* undo
- * indx_delete_entry(&old_dir_ni->dir, old_dir_ni,
- * fname, fname_full_size(fname),
- * sbi);
- */
-out5:
- /* undo
- * ni_remove_attr_le(old_ni, attr_from_name(fname), le);
- */
-out4:
- /* undo:
- * indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1,
- * old_de->key_size, NULL);
- */
-out3:
-out2:
- ni_unlock(old_ni);
- ni_unlock(old_dir_ni);
-out1:
- __putname(old_de);
+ ni_unlock(ni);
+ ni_unlock(dir_ni);
out:
+ __putname(de);
return err;
}
struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni);
void ni_clear(struct ntfs_inode *ni);
int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi);
-int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
+int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le,
struct mft_inode **mi);
struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr,
struct ATTR_LIST_ENTRY **entry_o,
struct mft_inode **mi);
int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
enum ATTR_TYPE type, const __le16 *name, u8 name_len,
- struct ATTRIB **new_attr, struct mft_inode **mi);
-int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
- struct ATTR_LIST_ENTRY *le);
+ struct ATTRIB **new_attr, struct mft_inode **mi,
+ struct ATTR_LIST_ENTRY **le);
+void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
+ struct mft_inode *mi, struct ATTR_LIST_ENTRY *le);
int ni_delete_all(struct ntfs_inode *ni);
struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
const struct cpu_str *uni,
const struct MFT_REF *home,
+ struct mft_inode **mi,
struct ATTR_LIST_ENTRY **entry);
struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
+ struct mft_inode **mi,
struct ATTR_LIST_ENTRY **entry);
int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa);
enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
u32 pages_per_frame);
int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
u32 pages_per_frame);
+int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
+ struct NTFS_DE *de, struct NTFS_DE **de2, int *undo_step);
+
+bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
+ struct NTFS_DE *de, struct NTFS_DE *de2,
+ int undo_step);
+
+int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
+ struct NTFS_DE *de);
+
+int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
+ struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
+ bool *is_bad);
+
+bool ni_is_dirty(struct inode *inode);
/* Globals from fslog.c */
int log_replay(struct ntfs_inode *ni, bool *initialized);
size_t *off, struct ntfs_fnd *fnd);
int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
const struct NTFS_DE *new_de, const void *param,
- struct ntfs_fnd *fnd);
+ struct ntfs_fnd *fnd, bool undo);
int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
const void *key, u32 key_len, const void *param);
int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi,
const __le16 *name, u8 name_len, u32 asize,
u16 name_off);
-bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr);
+bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTRIB *attr);
bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes);
int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
struct runs_tree *run, CLST len);
*
* NOTE: The source attr will point to next attribute.
*/
-bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr)
+bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTRIB *attr)
{
struct MFT_REC *rec = mi->mrec;
u32 aoff = PtrOffset(rec, attr);
if (aoff + asize > used)
return false;
+ if (ni && is_attr_indexed(attr)) {
+ le16_add_cpu(&ni->mi.mrec->hard_links, -1);
+ ni->mi.dirty = true;
+ }
+
used -= asize;
memmove(attr, Add2Ptr(attr, asize), used - aoff);
rec->used = cpu_to_le32(used);
}
err = ni_insert_resident(ni, sizeof(struct EA_INFO),
- ATTR_EA_INFO, NULL, 0, NULL, NULL);
+ ATTR_EA_INFO, NULL, 0, NULL, NULL,
+ NULL);
if (err)
goto out;
- err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL);
+ err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL,
+ NULL);
if (err)
goto out;
}
if (!size) {
/* Delete xattr, ATTR_EA_INFO */
- err = ni_remove_attr_le(ni, attr, le);
- if (err)
- goto out;
+ ni_remove_attr_le(ni, attr, mi, le);
} else {
p = resident_data_ex(attr, sizeof(struct EA_INFO));
if (!p) {
if (!size) {
/* Delete xattr, ATTR_EA */
- err = ni_remove_attr_le(ni, attr, le);
- if (err)
- goto out;
+ ni_remove_attr_le(ni, attr, mi, le);
} else if (attr->non_res) {
err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size);
if (err)
goto out;
}
- err = ntfs_set_ea(inode, name, name_len, value, size,
- acl ? 0 : XATTR_REPLACE, locked);
+ err = ntfs_set_ea(inode, name, name_len, value, size, 0, locked);
if (!err)
set_cached_acl(inode, type, acl);
struct posix_acl *acl;
int err;
- if (!(inode->i_sb->s_flags & SB_POSIXACL))
+ if (!(inode->i_sb->s_flags & SB_POSIXACL)) {
+ ntfs_inode_warn(inode, "add mount option \"acl\" to use acl");
return -EOPNOTSUPP;
+ }
acl = ntfs_get_acl(inode, type);
if (IS_ERR(acl))
struct posix_acl *acl;
int err;
- if (!(inode->i_sb->s_flags & SB_POSIXACL))
+ if (!(inode->i_sb->s_flags & SB_POSIXACL)) {
+ ntfs_inode_warn(inode, "add mount option \"acl\" to use acl");
return -EOPNOTSUPP;
+ }
if (!inode_owner_or_capable(mnt_userns, inode))
return -EPERM;