summaryrefslogtreecommitdiff
path: root/fs/ntfs3/inode.c
diff options
context:
space:
mode:
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>2021-08-31 18:52:39 +0300
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>2021-08-31 19:07:11 +0300
commit78ab59fee07f22464f32eafebab2bd97ba94ff2d (patch)
tree8c906238a8ffbc639ce1911295f6e961e6212f6e /fs/ntfs3/inode.c
parenta97131c29c997e81b6fa1d1adf8f3ca07b63a2e1 (diff)
downloadlinux-78ab59fee07f22464f32eafebab2bd97ba94ff2d.tar.xz
fs/ntfs3: Rework file operations
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>
Diffstat (limited to 'fs/ntfs3/inode.c')
-rw-r--r--fs/ntfs3/inode.c275
1 files changed, 95 insertions, 180 deletions
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);
-
- /* 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;
}
@@ -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;
- }
-
- 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;
}