Messages in this thread Patch in this message | | | Date | Wed, 17 Apr 2024 16:09:00 +0300 | Subject | [PATCH 10/11] fs/ntfs3: Remove cached label from sbi | From | Konstantin Komarov <> |
| |
Add more checks using $AttrDef.
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> --- fs/ntfs3/attrib.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ntfs3/fsntfs.c | 17 ++++------- fs/ntfs3/ntfs_fs.h | 27 +++++++++++++++++- fs/ntfs3/super.c | 49 +++++++++++++++++++------------- 4 files changed, 132 insertions(+), 32 deletions(-)
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index aedae36b91d0..acee4644fd8d 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -228,6 +228,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, u64 new_size, struct runs_tree *run, struct ATTRIB **ins_attr, struct page *page) { + const struct ATTR_DEF_ENTRY_SMALL *q; struct ntfs_sb_info *sbi; struct ATTRIB *attr_s; struct MFT_REC *rec; @@ -243,6 +244,22 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, }
sbi = mi->sbi; + + /* Check if we can use nonresident form. */ + q = ntfs_query_def(sbi, attr->type); + if (!q) { + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + return -EINVAL; + } + + /* Check resident form. */ + if (q->flags & NTFS_ATTR_MUST_BE_RESIDENT) { + ntfs_warn(sbi->sb, + "attribute %x is not allowed to be nonresident", + le32_to_cpu(attr->type)); + return -EINVAL; + } + rec = mi->mrec; attr_s = NULL; used = le32_to_cpu(rec->used); @@ -2589,4 +2606,58 @@ int attr_force_nonresident(struct ntfs_inode *ni) up_write(&ni->file.run_lock);
return err; +} + +/* + * Returns true if attribute is ok + */ +bool attr_check(const struct ATTRIB *attr, struct ntfs_sb_info *sbi, + struct ntfs_inode *ni) +{ + u64 size; + const char *hint; + const struct ATTR_DEF_ENTRY_SMALL *q = ntfs_query_def(sbi, attr->type); + + if (!q) { + hint = "unknown"; + goto out; + } + + /* Check resident form. */ + if ((q->flags & NTFS_ATTR_MUST_BE_RESIDENT) && attr->non_res) { + hint = "must be resident"; + goto out; + } + + /* Check name. */ + if ((q->flags & NTFS_ATTR_MUST_BE_NAMED) && !attr->name_len) { + hint = "must be named"; + goto out; + } + + /* Check size. */ + size = attr_size(attr); + if (size < q->min_sz) { + hint = "minimum size"; + goto out; + } + + if (size > q->max_sz) { + hint = "maximum size"; + goto out; + } + + /* ok. */ + return true; + +out: + if (ni) + ntfs_inode_err(&ni->vfs_inode, "attribute type=%x, %s", + le32_to_cpu(attr->type), hint); + else + ntfs_err(sbi->sb, "attribute type=%x, %s", + le32_to_cpu(attr->type), hint); + + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + return false; } \ No newline at end of file diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index f9c60f3cadaf..5dacb8301202 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2650,8 +2650,8 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) { int err; struct ATTRIB *attr; + u32 uni_bytes; struct ntfs_inode *ni = sbi->volume.ni; - const u8 max_ulen = 0x80; /* TODO: use attrdef to get maximum length */ /* Allocate PATH_MAX bytes. */ struct cpu_str *uni = __getname();
@@ -2663,7 +2663,8 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) if (err < 0) goto out;
- if (uni->len > max_ulen) { + uni_bytes = uni->len * sizeof(u16); + if (uni_bytes > sbi->attrdef.label_max_size) { ntfs_warn(sbi->sb, "new label is too long"); err = -EFBIG; goto out; @@ -2674,19 +2675,13 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) /* Ignore any errors. */ ni_remove_attr(ni, ATTR_LABEL, NULL, 0, false, NULL);
- err = ni_insert_resident(ni, uni->len * sizeof(u16), ATTR_LABEL, NULL, - 0, &attr, NULL, NULL); + err = ni_insert_resident(ni, uni_bytes, ATTR_LABEL, NULL, 0, &attr, + NULL, NULL); if (err < 0) goto unlock_out;
/* write new label in on-disk struct. */ - memcpy(resident_data(attr), uni->name, uni->len * sizeof(u16)); - - /* update cached value of current label. */ - if (len >= ARRAY_SIZE(sbi->volume.label)) - len = ARRAY_SIZE(sbi->volume.label) - 1; - memcpy(sbi->volume.label, label, len); - sbi->volume.label[len] = 0; + memcpy(resident_data(attr), uni->name, uni_bytes); mark_inode_dirty_sync(&ni->vfs_inode);
unlock_out: diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 1d4fb6f87dea..12c392db5b08 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -293,7 +293,6 @@ struct ntfs_sb_info { __le16 flags; // Cached current VOLUME_INFO::flags, VOLUME_FLAG_DIRTY. u8 major_ver; u8 minor_ver; - char label[256]; bool real_dirty; // Real fs state. } volume;
@@ -465,6 +464,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size); int attr_force_nonresident(struct ntfs_inode *ni); +bool attr_check(const struct ATTRIB *attr, struct ntfs_sb_info *sbi, + struct ntfs_inode *ni);
/* Functions from attrlist.c */ void al_destroy(struct ntfs_inode *ni); @@ -1152,4 +1153,28 @@ static inline void le64_sub_cpu(__le64 *var, u64 val) *var = cpu_to_le64(le64_to_cpu(*var) - val); }
+/* + * Attributes types: 0x10, 0x20, 0x30.... + * indexes in attribute table: 0, 1, 2... + */ +static inline const struct ATTR_DEF_ENTRY_SMALL * +ntfs_query_def(const struct ntfs_sb_info *sbi, enum ATTR_TYPE type) +{ + const struct ATTR_DEF_ENTRY_SMALL *q; + u32 idx = (le32_to_cpu(type) >> 4) - 1; + + if (idx >= sbi->attrdef.entries) { + /* such attribute is not allowed in this ntfs. */ + return NULL; + } + + q = sbi->attrdef.table + idx; + if (!q->type) { + /* such attribute is not allowed in this ntfs. */ + return NULL; + } + + return q; +} + #endif /* _LINUX_NTFS3_NTFS_FS_H */ diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 8beefbca5769..dae961d2d6f8 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -481,11 +481,39 @@ static int ntfs3_volinfo_open(struct inode *inode, struct file *file) /* read /proc/fs/ntfs3/<dev>/label */ static int ntfs3_label_show(struct seq_file *m, void *o) { + int len; struct super_block *sb = m->private; struct ntfs_sb_info *sbi = sb->s_fs_info; + struct ATTRIB *attr; + u8 *label = kmalloc(PAGE_SIZE, GFP_NOFS); + + if (!label) + return -ENOMEM; + + attr = ni_find_attr(sbi->volume.ni, NULL, NULL, ATTR_LABEL, NULL, 0, + NULL, NULL); + + if (!attr) { + /* It is ok if no ATTR_LABEL */ + label[0] = 0; + len = 0; + } else if (!attr_check(attr, sbi, sbi->volume.ni)) { + len = sprintf(label, "%pg: failed to get label", sb->s_bdev); + } else { + len = ntfs_utf16_to_nls(sbi, resident_data(attr), + le32_to_cpu(attr->res.data_size) >> 1, + label, PAGE_SIZE); + if (len < 0) { + label[0] = 0; + len = 0; + } else if (len >= PAGE_SIZE) { + len = PAGE_SIZE - 1; + } + }
- seq_printf(m, "%s\n", sbi->volume.label); + seq_printf(m, "%.*s\n", len, label);
+ kfree(label); return 0; }
@@ -1210,25 +1238,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
ni = ntfs_i(inode);
- /* Load and save label (not necessary). */ - attr = ni_find_attr(ni, NULL, NULL, ATTR_LABEL, NULL, 0, NULL, NULL); - - if (!attr) { - /* It is ok if no ATTR_LABEL */ - } else if (!attr->non_res && !is_attr_ext(attr)) { - /* $AttrDef allows labels to be up to 128 symbols. */ - err = utf16s_to_utf8s(resident_data(attr), - le32_to_cpu(attr->res.data_size) >> 1, - UTF16_LITTLE_ENDIAN, sbi->volume.label, - sizeof(sbi->volume.label)); - if (err < 0) - sbi->volume.label[0] = 0; - } else { - /* Should we break mounting here? */ - //err = -EINVAL; - //goto put_inode_out; - } - attr = ni_find_attr(ni, attr, NULL, ATTR_VOL_INFO, NULL, 0, NULL, NULL); if (!attr || is_attr_ext(attr) || !(info = resident_data_ex(attr, SIZEOF_ATTRIBUTE_VOLUME_INFO))) { -- 2.34.1
| |