From 4342306f0f0d5ff4315a204d315c1b51b914fca5 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 13 Aug 2021 17:21:29 +0300 Subject: fs/ntfs3: Add file operations and implementation This adds file operations and implementation Signed-off-by: Konstantin Komarov --- fs/ntfs3/namei.c | 539 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 fs/ntfs3/namei.c (limited to 'fs/ntfs3/namei.c') diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c new file mode 100644 index 000000000000..b1ccd66172f2 --- /dev/null +++ b/fs/ntfs3/namei.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "ntfs.h" +#include "ntfs_fs.h" + +/* + * fill_name_de + * + * formats NTFS_DE in 'buf' + */ +int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, + const struct cpu_str *uni) +{ + int err; + struct NTFS_DE *e = buf; + u16 data_size; + struct ATTR_FILE_NAME *fname = (struct ATTR_FILE_NAME *)(e + 1); + +#ifndef CONFIG_NTFS3_64BIT_CLUSTER + e->ref.high = fname->home.high = 0; +#endif + if (uni) { +#ifdef __BIG_ENDIAN + int ulen = uni->len; + __le16 *uname = fname->name; + const u16 *name_cpu = uni->name; + + while (ulen--) + *uname++ = cpu_to_le16(*name_cpu++); +#else + memcpy(fname->name, uni->name, uni->len * sizeof(u16)); +#endif + fname->name_len = uni->len; + + } else { + /* Convert input string to unicode */ + err = ntfs_nls_to_utf16(sbi, name->name, name->len, + (struct cpu_str *)&fname->name_len, + NTFS_NAME_LEN, UTF16_LITTLE_ENDIAN); + if (err < 0) + return err; + } + + fname->type = FILE_NAME_POSIX; + data_size = fname_full_size(fname); + + e->size = cpu_to_le16(QuadAlign(data_size) + sizeof(struct NTFS_DE)); + e->key_size = cpu_to_le16(data_size); + e->flags = 0; + e->res = 0; + + return 0; +} + +/* + * ntfs_lookup + * + * inode_operations::lookup + */ +static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, + u32 flags) +{ + struct ntfs_inode *ni = ntfs_i(dir); + struct cpu_str *uni = __getname(); + struct inode *inode; + int err; + + if (!uni) + inode = ERR_PTR(-ENOMEM); + else { + err = ntfs_nls_to_utf16(ni->mi.sbi, dentry->d_name.name, + dentry->d_name.len, uni, NTFS_NAME_LEN, + UTF16_HOST_ENDIAN); + if (err < 0) + inode = ERR_PTR(err); + else { + ni_lock(ni); + inode = dir_search_u(dir, uni, NULL); + ni_unlock(ni); + } + __putname(uni); + } + + return d_splice_alias(inode, dentry); +} + +/* + * ntfs_create + * + * inode_operations::create + */ +static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) +{ + struct ntfs_inode *ni = ntfs_i(dir); + struct inode *inode; + + ni_lock_dir(ni); + + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFREG | mode, + 0, NULL, 0, NULL); + + ni_unlock(ni); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +} + +/* + * ntfs_mknod + * + * inode_operations::mknod + */ +static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t rdev) +{ + struct ntfs_inode *ni = ntfs_i(dir); + struct inode *inode; + + ni_lock_dir(ni); + + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, mode, rdev, + NULL, 0, NULL); + + ni_unlock(ni); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +} + +/* + * ntfs_link + * + * inode_operations::link + */ +static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) +{ + int err; + struct inode *inode = d_inode(ode); + struct ntfs_inode *ni = ntfs_i(inode); + + if (S_ISDIR(inode->i_mode)) + return -EPERM; + + if (inode->i_nlink >= NTFS_LINK_MAX) + return -EMLINK; + + ni_lock_dir(ntfs_i(dir)); + 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) { + mark_inode_dirty(inode); + mark_inode_dirty(dir); + d_instantiate(de, inode); + } else { + drop_nlink(inode); + iput(inode); + } + + if (inode != dir) + ni_unlock(ni); + ni_unlock(ntfs_i(dir)); + + return err; +} + +/* + * ntfs_unlink + * + * inode_operations::unlink + */ +static int ntfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct ntfs_inode *ni = ntfs_i(dir); + int err; + + ni_lock_dir(ni); + + err = ntfs_unlink_inode(dir, dentry); + + ni_unlock(ni); + + return err; +} + +/* + * ntfs_symlink + * + * inode_operations::symlink + */ +static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, const char *symname) +{ + u32 size = strlen(symname); + struct inode *inode; + struct ntfs_inode *ni = ntfs_i(dir); + + ni_lock_dir(ni); + + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFLNK | 0777, + 0, symname, size, NULL); + + ni_unlock(ni); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +} + +/* + * ntfs_mkdir + * + * inode_operations::mkdir + */ +static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct inode *inode; + struct ntfs_inode *ni = ntfs_i(dir); + + ni_lock_dir(ni); + + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFDIR | mode, + 0, NULL, 0, NULL); + + ni_unlock(ni); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +} + +/* + * ntfs_rmdir + * + * inode_operations::rm_dir + */ +static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct ntfs_inode *ni = ntfs_i(dir); + int err; + + ni_lock_dir(ni); + + err = ntfs_unlink_inode(dir, dentry); + + ni_unlock(ni); + + return err; +} + +/* + * 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, + struct dentry *new_dentry, u32 flags) +{ + int err; + struct super_block *sb = old_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 *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; + + static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + SIZEOF_RESIDENT < 1024); + static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + sizeof(struct NTFS_DE) < + 1024); + static_assert(PATH_MAX >= 4 * 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 = 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) { + /* Nothing to do */ + err = 0; + goto out; + } + + if (ntfs_is_meta_file(sbi, old_inode->i_ino)) { + err = -EINVAL; + goto out; + } + + if (new_inode) { + /*target name exists. unlink it*/ + dget(new_dentry); + ni_lock_dir(new_dir_ni); + err = ntfs_unlink_inode(new_dir, new_dentry); + ni_unlock(new_dir_ni); + dput(new_dentry); + if (err) + goto out; + } + + /* allocate PATH_MAX bytes */ + old_de = __getname(); + if (!old_de) { + err = -ENOMEM; + goto out; + } + + err = fill_name_de(sbi, old_de, &old_dentry->d_name, NULL); + if (err < 0) + goto out1; + + old_name = (struct ATTR_FILE_NAME *)(old_de + 1); + + if (is_same) { + new_de = old_de; + } else { + new_de = Add2Ptr(old_de, 1024); + 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; + } + + /* 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; + } + } + + /* 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); +out: + return err; +} + +struct dentry *ntfs3_get_parent(struct dentry *child) +{ + struct inode *inode = d_inode(child); + struct ntfs_inode *ni = ntfs_i(inode); + + struct ATTR_LIST_ENTRY *le = NULL; + struct ATTRIB *attr = NULL; + struct ATTR_FILE_NAME *fname; + + while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL, + NULL))) { + fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); + if (!fname) + continue; + + return d_obtain_alias( + ntfs_iget5(inode->i_sb, &fname->home, NULL)); + } + + return ERR_PTR(-ENOENT); +} + +// clang-format off +const struct inode_operations ntfs_dir_inode_operations = { + .lookup = ntfs_lookup, + .create = ntfs_create, + .link = ntfs_link, + .unlink = ntfs_unlink, + .symlink = ntfs_symlink, + .mkdir = ntfs_mkdir, + .rmdir = ntfs_rmdir, + .mknod = ntfs_mknod, + .rename = ntfs_rename, + .permission = ntfs_permission, + .get_acl = ntfs_get_acl, + .set_acl = ntfs_set_acl, + .setattr = ntfs3_setattr, + .getattr = ntfs_getattr, + .listxattr = ntfs_listxattr, + .fiemap = ntfs_fiemap, +}; + +const struct inode_operations ntfs_special_inode_operations = { + .setattr = ntfs3_setattr, + .getattr = ntfs_getattr, + .listxattr = ntfs_listxattr, + .get_acl = ntfs_get_acl, + .set_acl = ntfs_set_acl, +}; +// clang-format on -- cgit v1.2.3 From fa3cacf544636b2dc48cfb2f277a2071f14d66a2 Mon Sep 17 00:00:00 2001 From: Kari Argillander Date: Thu, 26 Aug 2021 11:56:29 +0300 Subject: fs/ntfs3: Use kernel ALIGN macros over driver specific The static checkers (Smatch) were complaining because QuadAlign() was buggy. If you try to align something higher than UINT_MAX it got truncated to a u32. Smatch warning was: fs/ntfs3/attrib.c:383 attr_set_size_res() warn: was expecting a 64 bit value instead of '~7' So that this will not happen again we will change all these macros to kernel made ones. This can also help some other static analyzing tools to give us better warnings. Patch was generated with Coccinelle script and after that some style issue was hand fixed. Coccinelle script: virtual patch @alloc depends on patch@ expression x; @@ ( - #define QuadAlign(n) (((n) + 7u) & (~7u)) | - QuadAlign(x) + ALIGN(x, 8) | - #define IsQuadAligned(n) (!((size_t)(n)&7u)) | - IsQuadAligned(x) + IS_ALIGNED(x, 8) | - #define Quad2Align(n) (((n) + 15u) & (~15u)) | - Quad2Align(x) + ALIGN(x, 16) | - #define IsQuad2Aligned(n) (!((size_t)(n)&15u)) | - IsQuad2Aligned(x) + IS_ALIGNED(x, 16) | - #define Quad4Align(n) (((n) + 31u) & (~31u)) | - Quad4Align(x) + ALIGN(x, 32) | - #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1))) | - IsSizeTAligned(x) + IS_ALIGNED(x, sizeof(size_t)) | - #define DwordAlign(n) (((n) + 3u) & (~3u)) | - DwordAlign(x) + ALIGN(x, 4) | - #define IsDwordAligned(n) (!((size_t)(n)&3u)) | - IsDwordAligned(x) + IS_ALIGNED(x, 4) | - #define WordAlign(n) (((n) + 1u) & (~1u)) | - WordAlign(x) + ALIGN(x, 2) | - #define IsWordAligned(n) (!((size_t)(n)&1u)) | - IsWordAligned(x) + IS_ALIGNED(x, 2) | ) Reported-by: Dan Carpenter Signed-off-by: Kari Argillander Signed-off-by: Konstantin Komarov --- fs/ntfs3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/ntfs3/namei.c') diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index b1ccd66172f2..0626844e6bdc 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -57,7 +57,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, fname->type = FILE_NAME_POSIX; data_size = fname_full_size(fname); - e->size = cpu_to_le16(QuadAlign(data_size) + sizeof(struct NTFS_DE)); + e->size = cpu_to_le16(ALIGN(data_size, 8) + sizeof(struct NTFS_DE)); e->key_size = cpu_to_le16(data_size); e->flags = 0; e->res = 0; -- cgit v1.2.3 From e8b8e97f91b80f08a2f1b7ea4f81e7af61b2cc2f Mon Sep 17 00:00:00 2001 From: Kari Argillander Date: Tue, 3 Aug 2021 14:57:09 +0300 Subject: fs/ntfs3: Restyle comments to better align with kernel-doc Capitalize comments and end with period for better reading. Also function comments are now little more kernel-doc style. This way we can easily convert them to kernel-doc style if we want. Note that these are not yet complete with this style. Example function comments start with /* and in kernel-doc style they start /**. Use imperative mood in function descriptions. Change words like ntfs -> NTFS, linux -> Linux. Use "we" not "I" when commenting code. Signed-off-by: Kari Argillander Signed-off-by: Konstantin Komarov --- fs/ntfs3/namei.c | 66 +++++++++++++++++++++----------------------------------- 1 file changed, 24 insertions(+), 42 deletions(-) (limited to 'fs/ntfs3/namei.c') diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 0626844e6bdc..f79a399bd015 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -17,9 +17,7 @@ #include "ntfs_fs.h" /* - * fill_name_de - * - * formats NTFS_DE in 'buf' + * fill_name_de - Format NTFS_DE in @buf. */ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, const struct cpu_str *uni) @@ -46,7 +44,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, fname->name_len = uni->len; } else { - /* Convert input string to unicode */ + /* Convert input string to unicode. */ err = ntfs_nls_to_utf16(sbi, name->name, name->len, (struct cpu_str *)&fname->name_len, NTFS_NAME_LEN, UTF16_LITTLE_ENDIAN); @@ -66,9 +64,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, } /* - * ntfs_lookup - * - * inode_operations::lookup + * ntfs_lookup - inode_operations::lookup */ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, u32 flags) @@ -98,9 +94,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, } /* - * ntfs_create - * - * inode_operations::create + * ntfs_create - inode_operations::create */ static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) @@ -140,9 +134,7 @@ static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, } /* - * ntfs_link - * - * inode_operations::link + * ntfs_link - inode_operations::link */ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) { @@ -182,9 +174,7 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) } /* - * ntfs_unlink - * - * inode_operations::unlink + * ntfs_unlink - inode_operations::unlink */ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) { @@ -201,9 +191,7 @@ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) } /* - * ntfs_symlink - * - * inode_operations::symlink + * ntfs_symlink - inode_operations::symlink */ static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, const char *symname) @@ -223,9 +211,7 @@ static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, } /* - * ntfs_mkdir - * - * inode_operations::mkdir + * ntfs_mkdir- inode_operations::mkdir */ static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode) @@ -244,9 +230,7 @@ static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, } /* - * ntfs_rmdir - * - * inode_operations::rm_dir + * ntfs_rmdir - inode_operations::rm_dir */ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) { @@ -263,9 +247,7 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) } /* - * ntfs_rename - * - * inode_operations::rename + * 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, @@ -304,7 +286,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, old_dentry->d_name.len); if (is_same && old_dir == new_dir) { - /* Nothing to do */ + /* Nothing to do. */ err = 0; goto out; } @@ -315,7 +297,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, } if (new_inode) { - /*target name exists. unlink it*/ + /* Target name exists. Unlink it. */ dget(new_dentry); ni_lock_dir(new_dir_ni); err = ntfs_unlink_inode(new_dir, new_dentry); @@ -325,7 +307,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, goto out; } - /* allocate PATH_MAX bytes */ + /* Allocate PATH_MAX bytes. */ old_de = __getname(); if (!old_de) { err = -ENOMEM; @@ -352,7 +334,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, mi_get_ref(&old_dir_ni->mi, &old_name->home); - /*get pointer to file_name in mft*/ + /* 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) { @@ -360,19 +342,19 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, goto out2; } - /* Copy fname info from record into new fname */ + /* 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 */ + /* 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 */ + /* Remove first name from MFT. */ err = ni_remove_attr_le(old_ni, attr_from_name(fname), le); if (err) goto out4; @@ -381,17 +363,17 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, old_ni->mi.dirty = true; if (name_type != FILE_NAME_POSIX) { - /* get paired name */ + /* Get paired name. */ fname = ni_fname_type(old_ni, name_type, &le); if (fname) { - /* remove second name from directory */ + /* 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 */ + /* Remove second name from MFT. */ err = ni_remove_attr_le(old_ni, attr_from_name(fname), le); if (err) @@ -402,13 +384,13 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, } } - /* Add new name */ + /* 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 */ + /* Insert new name in MFT. */ err = ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0, &attr, NULL); if (err) @@ -421,7 +403,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, le16_add_cpu(&old_ni->mi.mrec->hard_links, 1); old_ni->mi.dirty = true; - /* insert new name in directory */ + /* Insert new name in directory. */ err = indx_insert_entry(&new_dir_ni->dir, new_dir_ni, new_de, sbi, NULL); if (err) @@ -449,7 +431,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, } err = 0; - /* normal way */ + /* Normal way* */ goto out2; out8: -- cgit v1.2.3 From 78ab59fee07f22464f32eafebab2bd97ba94ff2d Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 31 Aug 2021 18:52:39 +0300 Subject: 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 --- fs/ntfs3/namei.c | 236 +++++++++++++++---------------------------------------- 1 file changed, 63 insertions(+), 173 deletions(-) (limited to 'fs/ntfs3/namei.c') 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); - - 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) { @@ -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; - } - - 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; } -- cgit v1.2.3