Add more checks using $AttrDef.
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
---
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