diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 4b285f704e62..ffc323bacc9f 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -218,9 +218,11 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, } 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; } @@ -701,7 +703,7 @@ pack_runs: * (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; @@ -1004,7 +1006,7 @@ repack: 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; @@ -1600,7 +1602,7 @@ repack: 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; @@ -1836,13 +1838,12 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) 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; diff --git a/fs/ntfs3/attrlist.c b/fs/ntfs3/attrlist.c index 32ca990af64b..fa32399eb517 100644 --- a/fs/ntfs3/attrlist.c +++ b/fs/ntfs3/attrlist.c @@ -279,7 +279,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, 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; @@ -287,8 +287,9 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, * 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. */ @@ -302,13 +303,14 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, 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; @@ -321,23 +323,25 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, 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; } diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 9d374827750b..3f48b612ec96 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -163,7 +163,7 @@ out: /* * 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; @@ -402,7 +402,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, if (!attr) return -ENOENT; - mi_remove_attr(&ni->mi, attr); + mi_remove_attr(ni, &ni->mi, attr); return 0; } @@ -441,7 +441,7 @@ next_le2: 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; @@ -454,12 +454,11 @@ next_le2: * * 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; @@ -507,6 +506,8 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, le->ref = ref; out: + if (ins_le) + *ins_le = le; return attr; } @@ -599,7 +600,7 @@ static int ni_repack(struct ntfs_inode *ni) 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; } @@ -695,8 +696,8 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) 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. @@ -724,7 +725,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) 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); @@ -842,7 +843,8 @@ int ni_create_attr_list(struct ntfs_inode *ni) 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; @@ -883,7 +885,8 @@ out: 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; @@ -956,12 +959,14 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, /* 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; } @@ -977,7 +982,7 @@ insert_ext: } 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; @@ -1016,7 +1021,8 @@ out: 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; @@ -1045,7 +1051,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, 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; @@ -1059,7 +1065,8 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, 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; } @@ -1117,7 +1124,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, 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; @@ -1125,8 +1132,8 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, 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) @@ -1136,7 +1143,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, ; 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; @@ -1251,7 +1258,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) */ 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; @@ -1315,14 +1322,15 @@ int ni_expand_list(struct ntfs_inode *ni) 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; @@ -1382,7 +1390,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, } err = ni_insert_attr(ni, type, name, name_len, asize, name_off, svcn, - &attr, mi); + &attr, mi, NULL); if (err) goto out; @@ -1422,7 +1430,8 @@ 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); @@ -1430,7 +1439,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, 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; @@ -1439,8 +1448,13 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, 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) @@ -1452,22 +1466,13 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, /* * 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; } /* @@ -1549,10 +1554,12 @@ int ni_delete_all(struct ntfs_inode *ni) /* 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; @@ -1562,7 +1569,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, /* 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; @@ -1592,6 +1599,7 @@ next: * 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; @@ -1599,10 +1607,12 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, *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; @@ -2788,6 +2798,222 @@ out: 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 * @@ -2802,8 +3028,6 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, 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; @@ -2867,19 +3091,9 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, 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)) { @@ -2898,6 +3112,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, } 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); @@ -2969,7 +3185,9 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) 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); @@ -3033,7 +3251,7 @@ out: return err; } - if (re_dirty && (sb->s_flags & SB_ACTIVE)) + if (re_dirty) mark_inode_dirty_sync(inode); return 0; diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 0edb95ed9717..669249439217 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -358,29 +358,25 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, 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); @@ -389,14 +385,13 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, 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. @@ -407,22 +402,17 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, 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); @@ -439,31 +429,24 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, 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; - - ntfs_unmap_meta(sb, *new_lcn, *new_len); - - if (opt & ALLOCATE_MFT) - goto out; - - /* Set hint for next requests. */ - sbi->used.next_free_lcn = *new_lcn + *new_len; + alen = wnd_find(wnd, len, 0, + BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn); out: + if (alen) { + err = 0; + *new_len = alen; + *new_lcn = alcn; + + ntfs_unmap_meta(sb, alcn, alen); + + /* Set hint for next requests. */ + if (!(opt & ALLOCATE_MFT)) + sbi->used.next_free_lcn = alcn + alen; + } else { + err = -ENOSPC; + } + up_write(&wnd->rw_lock); return err; } @@ -2226,7 +2209,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, 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; @@ -2247,7 +2230,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, 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; @@ -2385,7 +2368,7 @@ int ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag, 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); diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 70ef59455b72..1224b8e42b3e 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1427,7 +1427,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *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; @@ -1443,7 +1443,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, return 0; out2: - mi_remove_attr(&ni->mi, alloc); + mi_remove_attr(NULL, &ni->mi, alloc); out1: run_deallocate(sbi, &run, false); @@ -1529,24 +1529,24 @@ out1: /* * 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; @@ -1559,12 +1559,11 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, /* * 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); @@ -1573,9 +1572,9 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, 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); @@ -1629,7 +1628,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, 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; @@ -1710,7 +1709,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, 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; } @@ -1854,7 +1853,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, */ 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 { @@ -1876,10 +1875,12 @@ out: /* * 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; @@ -1925,7 +1926,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, * 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 { @@ -2302,7 +2303,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, fnd->level - 1, fnd) : indx_insert_into_root(indx, ni, re, e, - ctx, fnd); + ctx, fnd, 0); kfree(re); if (err) @@ -2507,7 +2508,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, * 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; @@ -2595,10 +2596,8 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, 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) { @@ -2645,7 +2644,5 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, out: fnd_put(fnd); - -out1: return err; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index b86ec7dd731c..8f72066b3229 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -399,6 +399,12 @@ end_enum: 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)) { @@ -1279,6 +1285,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, } 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); @@ -1371,6 +1378,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, attr = Add2Ptr(attr, asize); } + attr->id = cpu_to_le16(aid++); if (fa & FILE_ATTRIBUTE_DIRECTORY) { /* * Regular directory or symlink to directory. @@ -1381,7 +1389,6 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, 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; @@ -1412,52 +1419,46 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, /* 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)) { @@ -1485,7 +1486,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, 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); @@ -1545,20 +1547,15 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, 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)) { @@ -1601,8 +1598,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, 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; @@ -1646,61 +1643,36 @@ out1: 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); + /* Mark rw ntfs as dirty. It will be cleared at umount. */ + ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); - /* Insert file name. */ - err = fill_name_de(sbi, new_de, name, NULL); + /* Construct 'de'. */ + err = fill_name_de(sbi, de, &dentry->d_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); - - err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, 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; } @@ -1713,113 +1685,56 @@ out: 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; + goto out; } - 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; - } - - /* 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; + goto out; - /* Mark rw ntfs as dirty. It will be cleared at umount. */ - ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); + undo_remove = 0; + err = ni_remove_name(dir_ni, ni, de, &de2, &undo_remove); - /* 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; - - ni_remove_attr_le(ni, attr_from_name(fname), le); - - 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; } diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index f79a399bd015..e58415d07132 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -152,12 +152,14 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) 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); @@ -249,25 +251,26 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) /* * 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); @@ -276,24 +279,18 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, if (flags & ~RENAME_NOREPLACE) return -EINVAL; - old_inode = d_inode(old_dentry); - new_inode = d_inode(new_dentry); + 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); - old_ni = ntfs_i(old_inode); - - 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) { @@ -304,168 +301,61 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, 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; + goto out; } - ni_lock_dir(old_dir_ni); - ni_lock(old_ni); + ni_lock_dir(dir_ni); + ni_lock(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; - } - - /* 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; + 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); } + + if (IS_DIRSYNC(dir)) + ntfs_sync_inode(dir); + + if (IS_DIRSYNC(new_dir)) + ntfs_sync_inode(inode); } - /* 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); - - 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); - } - - 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; } diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 64ef92e16363..f9436cbbc347 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -478,7 +478,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni); 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, @@ -505,15 +505,18 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, 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, @@ -528,6 +531,21 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, 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); @@ -631,7 +649,7 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, 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, @@ -694,7 +712,8 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, 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); diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c index d48a5e6c5045..61e3f2fb619f 100644 --- a/fs/ntfs3/record.c +++ b/fs/ntfs3/record.c @@ -489,7 +489,8 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, * * 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); @@ -499,6 +500,11 @@ bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *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); diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index b4c921e4bc1a..22fd5eb32c5b 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -395,11 +395,13 @@ update_ea: } 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; } @@ -419,9 +421,7 @@ update_ea: 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) { @@ -441,9 +441,7 @@ update_ea: 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) @@ -605,8 +603,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, 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); @@ -632,8 +629,10 @@ static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns, 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)) @@ -655,8 +654,10 @@ static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns, 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;