diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index c621a4c582f9..b1c315206ffa 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -278,7 +278,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, next = Add2Ptr(attr, asize); aoff = PtrOffset(rec, attr); rsize = le32_to_cpu(attr->res.data_size); - is_data = attr->type == ATTR_DATA && !attr->name_len; + is_data = attr->type == ATTR_DATA; /* len - how many clusters required to store 'rsize' bytes */ if (is_attr_compressed(attr)) { @@ -433,6 +433,7 @@ int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type, struct ATTRIB **ret, bool no_da) { int err = 0; + struct ntfs_inode *nb = ni->base; struct ntfs_sb_info *sbi = ni->mi.sbi; u8 cluster_bits = sbi->cluster_bits; bool is_mft = ni->mi.rno == MFT_REC_MFT && type == ATTR_DATA && @@ -703,8 +704,8 @@ int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type, goto again; } - if (!ni->attr_list.size) { - err = ni_create_attr_list(ni); + if (!nb->attr_list.size) { + err = ni_create_attr_list(nb); /* In case of error layout of records is not changed. */ if (err) goto undo_2; @@ -877,8 +878,7 @@ int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type, if (ret) *ret = attr_b; - if (((type == ATTR_DATA && !name_len) || - (type == ATTR_ALLOC && name == I30_NAME))) { + if ((type == ATTR_DATA || (type == ATTR_ALLOC && name == I30_NAME))) { /* Update inode_set_bytes. */ if (attr_b->non_res && inode_get_bytes(&ni->vfs_inode) != new_alloc) { @@ -1001,7 +1001,6 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, struct ATTRIB *attr, *attr_b; struct ATTR_LIST_ENTRY *le, *le_b; struct mft_inode *mi, *mi_b; - struct page *page; CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0; CLST alloc, evcn; unsigned fr; @@ -1026,7 +1025,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, step = 0; le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -ENOENT; goto out; @@ -1036,11 +1036,15 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, u32 data_size = le32_to_cpu(attr_b->res.data_size); *lcn = RESIDENT_LCN; *len = data_size; - if (res && data_size) { - page = alloc_page(GFP_KERNEL); - if (!page) { - err = -ENOMEM; - } else { + if (res) { + *res = NULL; + if (data_size) { + struct page *page = alloc_page(GFP_KERNEL); + if (!page) { + err = -ENOMEM; + goto out; + } + *res = page_address(page); memcpy(*res, resident_data(attr_b), data_size); } @@ -1067,7 +1071,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, mi = mi_b; if (le_b && (vcn < svcn || evcn1 <= vcn)) { - attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, + attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, &vcn, &mi); if (!attr) { err = -EINVAL; @@ -1140,8 +1145,9 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, if (vcn < svcn || evcn1 <= vcn) { struct ATTRIB *attr2; /* Load runs for truncated vcn. */ - attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL, - 0, &vcn, &mi); + attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, + ni->file.ads.name, + ni->file.ads.len, &vcn, &mi); if (!attr2) { err = -EINVAL; goto out; @@ -1155,8 +1161,9 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, if (vcn0 < svcn || evcn1 <= vcn0) { struct ATTRIB *attr2; - attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL, - 0, &vcn0, &mi); + attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, + ni->file.ads.name, + ni->file.ads.len, &vcn0, &mi); if (!attr2) { err = -EINVAL; goto out; @@ -1269,8 +1276,9 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, goto undo1; /* Layout of records is changed. */ le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, - 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, + ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -ENOENT; goto out; @@ -1300,7 +1308,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, svcn = evcn1; /* Estimate next attribute. */ - attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); + attr = ni_find_attr(ni, attr, &le, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &svcn, &mi); if (!attr) { /* Insert new attribute segment. */ @@ -1333,7 +1342,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, goto out; } - attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, NULL, 0, &le->id); + attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &le->id); if (!attr) { err = -EINVAL; goto out; @@ -1362,9 +1372,10 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, ins_ext: if (evcn1 > next_svcn) { - err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, - next_svcn, evcn1 - next_svcn, - attr_b->flags, &attr, &mi, NULL); + err = ni_insert_nonresident(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, next_svcn, + evcn1 - next_svcn, attr_b->flags, + &attr, &mi, NULL); if (err) goto out; } @@ -1398,7 +1409,8 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio) struct ATTRIB *attr; u32 data_size; - attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi); + attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi); if (!attr) return -EINVAL; @@ -1515,7 +1527,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, u8 bytes_per_off; char *addr; struct folio *folio; - int i, err; + int i, err = 0; __le32 *off32; __le64 *off64; @@ -1775,7 +1787,8 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, u64 total_size, valid_size, data_size; le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) return -ENOENT; @@ -1798,7 +1811,8 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, goto out; } else { le = le_b; - attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, + attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, &vcn, &mi); if (!attr) { err = -EINVAL; @@ -1885,8 +1899,9 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, goto out; /* Layout of records is changed. */ le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, - 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, + ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -ENOENT; goto out; @@ -1902,7 +1917,8 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, svcn = evcn1; /* Estimate next attribute. */ - attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); + attr = ni_find_attr(ni, attr, &le, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &svcn, &mi); if (attr) { CLST alloc = bytes_to_cluster( @@ -1931,7 +1947,8 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, goto out; } - attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, NULL, 0, + attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, &le->id); if (!attr) { err = -EINVAL; @@ -1962,9 +1979,10 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, } ins_ext: if (evcn1 > next_svcn) { - err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, - next_svcn, evcn1 - next_svcn, - attr_b->flags, &attr, &mi, NULL); + err = ni_insert_nonresident(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, next_svcn, + evcn1 - next_svcn, attr_b->flags, + &attr, &mi, NULL); if (err) goto out; } @@ -2007,7 +2025,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) return 0; le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) return -ENOENT; @@ -2037,7 +2056,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) /* Simple truncate file at 'vbo'. */ truncate_setsize(&ni->vfs_inode, vbo); - err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, vbo, + err = attr_set_size(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &ni->file.run, vbo, &valid_size, true); if (!err && valid_size < ni->i_valid) @@ -2061,7 +2081,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) /* * The requested range is full in delayed clusters. */ - err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, run, + err = attr_set_size_ex(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, i_size - bytes, NULL, false, NULL, true); goto out; @@ -2074,7 +2095,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) /* Layout of records maybe changed. */ le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, NULL, &mi_b); if (!attr_b || !attr_b->non_res) { err = -ENOENT; @@ -2105,7 +2127,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) } le = le_b; - attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, &mi); + attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &vcn, &mi); if (!attr) { err = -EINVAL; goto out; @@ -2169,7 +2192,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) next_svcn = le64_to_cpu(attr->nres.evcn) + 1; if (next_svcn + eat + done < evcn1) { err = ni_insert_nonresident( - ni, ATTR_DATA, NULL, 0, run, next_svcn, + ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, next_svcn, evcn1 - eat - next_svcn, a_flags, &attr, &mi, &le); if (err) @@ -2209,7 +2233,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) /* Look for required attribute. */ attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, - NULL, 0, &le->id); + ni->file.ads.name, + ni->file.ads.len, &le->id); if (!attr) { err = -EINVAL; goto out; @@ -2232,7 +2257,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) if (!attr_b) { le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -ENOENT; @@ -2293,7 +2319,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) return 0; le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) return -ENOENT; @@ -2364,7 +2391,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) goto bad_inode; } else { le = le_b; - attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, + attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, &vcn, &mi); if (!attr) { err = -EINVAL; @@ -2416,10 +2444,10 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) next_svcn = le64_to_cpu(attr->nres.evcn) + 1; if (next_svcn < evcn1) { /* Insert new attribute segment. */ - err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, - next_svcn, - evcn1 - next_svcn, a_flags, - &attr, &mi, &le); + err = ni_insert_nonresident( + ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, next_svcn, + evcn1 - next_svcn, a_flags, &attr, &mi, &le); if (err) goto undo_punch; @@ -2454,7 +2482,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) goto out; if (!attr_b) { - attr_b = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, + attr_b = ni_find_attr(ni, NULL, NULL, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -EINVAL; @@ -2512,7 +2541,8 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) return 0; le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi_b); if (!attr_b) return -ENOENT; @@ -2559,11 +2589,13 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) down_write(&ni->file.run_lock); if (!attr_b->non_res) { - err = attr_set_size(ni, ATTR_DATA, NULL, 0, run, - data_size + bytes, NULL, false); + err = attr_set_size(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, data_size + bytes, + NULL, false); le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -EINVAL; @@ -2604,7 +2636,8 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) goto bad_inode; } else { le = le_b; - attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, + attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, &vcn, &mi); if (!attr) { err = -EINVAL; @@ -2647,12 +2680,14 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) } if (next_svcn < evcn1 + len) { - err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, - next_svcn, evcn1 + len - next_svcn, - a_flags, NULL, NULL, NULL); + err = ni_insert_nonresident(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, run, next_svcn, + evcn1 + len - next_svcn, a_flags, + NULL, NULL, NULL); le_b = NULL; - attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, NULL, &mi_b); if (!attr_b) { err = -EINVAL; @@ -2709,7 +2744,8 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) goto bad_inode; } else { le = le_b; - attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, + attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, &vcn, &mi); if (!attr) { goto bad_inode; @@ -2754,7 +2790,8 @@ int attr_force_nonresident(struct ntfs_inode *ni) struct ATTR_LIST_ENTRY *le = NULL; struct mft_inode *mi; - attr = ni_find_attr(ni, NULL, &le, ATTR_DATA, NULL, 0, NULL, &mi); + attr = ni_find_attr(ni, NULL, &le, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, NULL, &mi); if (!attr) { _ntfs_bad_inode(&ni->vfs_inode); return -ENOENT; diff --git a/fs/ntfs3/attrlist.c b/fs/ntfs3/attrlist.c index 270a29323530..8710560cf294 100644 --- a/fs/ntfs3/attrlist.c +++ b/fs/ntfs3/attrlist.c @@ -19,6 +19,7 @@ static inline bool al_is_valid_le(const struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) { + ni = ni->base; if (!le || !ni->attr_list.le || !ni->attr_list.size) return false; @@ -28,6 +29,7 @@ static inline bool al_is_valid_le(const struct ntfs_inode *ni, void al_destroy(struct ntfs_inode *ni) { + ni = ni->base; run_close(&ni->attr_list.run); kvfree(ni->attr_list.le); ni->attr_list.le = NULL; @@ -47,6 +49,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) size_t lsize; void *le = NULL; + ni = ni->base; if (ni->attr_list.size) return 0; @@ -199,6 +202,7 @@ struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *ret = NULL; u32 type_in = le32_to_cpu(type); + ni = ni->base; while ((le = al_enumerate(ni, le))) { u64 le_vcn; int diff = le32_to_cpu(le->type) - type_in; @@ -256,6 +260,7 @@ static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le = NULL, *prev; u32 type_in = le32_to_cpu(type); + ni = ni->base; /* List entries are sorted by type, name and VCN. */ while ((le = al_enumerate(ni, prev = le))) { int diff = le32_to_cpu(le->type) - type_in; @@ -305,6 +310,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, u64 new_size; typeof(ni->attr_list) *al = &ni->attr_list; + ni = ni->base; /* * Compute the size of the new 'le' */ @@ -374,8 +380,10 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) { u16 size; size_t off; - typeof(ni->attr_list) *al = &ni->attr_list; + typeof(ni->attr_list) *al; + ni = ni->base; + al = &ni->attr_list; if (!al_is_valid_le(ni, le)) return false; @@ -395,7 +403,10 @@ int al_update(struct ntfs_inode *ni, int sync) { int err; struct ATTRIB *attr; - typeof(ni->attr_list) *al = &ni->attr_list; + typeof(ni->attr_list) *al; + + ni = ni->base; + al = &ni->attr_list; if (!al->dirty || !al->size) return 0; diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index 873d52233003..eb9152e9fa22 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -25,6 +25,11 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len, static_assert(sizeof(wchar_t) == sizeof(__le16)); + if (buf_len <= 0) + return -EINVAL; + + buf_len -= 1; + if (!nls) { /* UTF-16 -> UTF-8 */ ret = utf16s_to_utf8s((wchar_t *)name, len, UTF16_LITTLE_ENDIAN, @@ -179,7 +184,7 @@ int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, struct cpu_str *uni, u32 max_ulen, enum utf16_endian endian) { - int ret, slen; + int ret, slen, i; const u8 *end; struct nls_table *nls = sbi->options->nls; u16 *uname = uni->name; @@ -189,50 +194,83 @@ int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, if (!nls) { /* utf8 -> utf16 */ ret = _utf8s_to_utf16s(name, name_len, endian, uname, max_ulen); - uni->len = ret; - return ret; - } + } else { + for (ret = 0, end = name + name_len; name < end; + ret++, name += slen) { + if (ret >= max_ulen) + return -ENAMETOOLONG; - for (ret = 0, end = name + name_len; name < end; ret++, name += slen) { - if (ret >= max_ulen) - return -ENAMETOOLONG; - - slen = nls->char2uni(name, end - name, uname + ret); - if (!slen) - return -EINVAL; - if (slen < 0) - return slen; - } + slen = nls->char2uni(name, end - name, uname + ret); + if (!slen) + return -EINVAL; + if (slen < 0) + return slen; + } #ifdef __BIG_ENDIAN - if (endian == UTF16_LITTLE_ENDIAN) { - int i = ret; + if (endian == UTF16_LITTLE_ENDIAN) { + i = ret; - while (i--) { - __cpu_to_le16s(uname); - uname++; + while (i--) { + __cpu_to_le16s(uname); + uname++; + } } - } #else - if (endian == UTF16_BIG_ENDIAN) { - int i = ret; + if (endian == UTF16_BIG_ENDIAN) { + i = ret; - while (i--) { - __cpu_to_be16s(uname); - uname++; + while (i--) { + __cpu_to_be16s(uname); + uname++; + } } - } #endif + } uni->len = ret; + uni->ads_len = 0; + if (ret > 0 && sbi->options->ads) { + uname = uni->name; + /* Find delimiter in range [1 : ret-2). */ + for (i = 1; i + 1 < ret; i++) { + if (uname[i] == ':') { + uni->ads_len = ret - i - 1; + uni->len = i; + uname[i] = 0; + ret = i; + + uname += i + 1; + i = uni->ads_len; + /* Return ADS name as little endian. Always */ +#ifdef __BIG_ENDIAN + if (endian == UTF16_LITTLE_ENDIAN) { + while (i--) { + __cpu_to_le16s(uname); + uname++; + } + } +#else + if (endian == UTF16_BIG_ENDIAN) { + while (i--) { + __cpu_to_be16s(uname); + uname++; + } + } +#endif + break; + } + } + } + return ret; } /* * dir_search_u - Helper function. */ -struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, - struct ntfs_fnd *fnd) +struct inode *dir_search_flags(struct inode *dir, const struct cpu_str *uni, + struct ntfs_fnd *fnd, u32 flags) { int err = 0; struct super_block *sb = dir->i_sb; @@ -262,7 +300,7 @@ struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, goto out; } - inode = ntfs_iget5(sb, &e->ref, uni); + inode = ntfs_iget5_flags(sb, &e->ref, uni, flags); if (!IS_ERR(inode) && is_bad_inode(inode)) { iput(inode); err = -EINVAL; @@ -273,6 +311,12 @@ struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode; } +static inline bool de_fname_fits(const struct NTFS_DE *e, u32 e_size, + const struct ATTR_FILE_NAME *fname) +{ + return sizeof(struct NTFS_DE) + fname_full_size(fname) <= e_size; +} + /* * returns false if 'ctx' if full */ @@ -281,7 +325,7 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi, u8 *name, struct dir_context *ctx) { const struct ATTR_FILE_NAME *fname; - unsigned long ino; + u64 ino; int name_len; u32 dt_type; @@ -305,15 +349,13 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi, if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) return true; - if (sizeof(struct NTFS_DE) + - offsetof(struct ATTR_FILE_NAME, name) + - fname->name_len * sizeof(short) > le16_to_cpu(e->size)) + if (!de_fname_fits(e, le16_to_cpu(e->size), fname)) return true; name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name, PATH_MAX); if (name_len <= 0) { - ntfs_warn(sbi->sb, "failed to convert name for inode %lx.", + ntfs_warn(sbi->sb, "failed to convert name for inode %llx.", ino); return true; } @@ -576,6 +618,23 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) return err; } +/* + * Return fname when @e passes the same checks as ntfs_dir_emit() before + * exposing an entry (valid key, non-DOS, fname fits in e->size). + */ +static inline const struct ATTR_FILE_NAME * +de_countable_fname(const struct NTFS_DE *e, u32 e_size) +{ + const struct ATTR_FILE_NAME *fname; + + fname = de_get_fname(e); + if (!fname || fname->type == FILE_NAME_DOS || + !de_fname_fits(e, e_size, fname)) + return NULL; + + return fname; +} + static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, size_t *files) { @@ -615,13 +674,10 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, if (de_is_last(e)) break; - fname = de_get_fname(e); + fname = de_countable_fname(e, e_size); if (!fname) continue; - if (fname->type == FILE_NAME_DOS) - continue; - if (is_empty) { *is_empty = false; if (!dirs && !files) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index d601f088618c..2abf334bfa0c 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -753,7 +753,9 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, setattr_copy(idmap, inode, attr); - if (mode != inode->i_mode) { + if (!is_ni_base(ni)) { + ia_valid &= ~ATTR_SIZE; + } else if (mode != inode->i_mode) { err = ntfs_acl_chmod(idmap, dentry); if (err) goto out; @@ -820,15 +822,31 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) size_t bytes = iov_iter_count(iter); loff_t valid, i_size, vbo, end; unsigned int dio_flags; - ssize_t err; + ssize_t ret; - err = check_read_restriction(inode); - if (err) - return err; + ret = check_read_restriction(inode); + if (ret) + return ret; if (!bytes) return 0; /* skip atime */ + if (ni->file.ads.len == ARRAY_SIZE(QUERY_STREAMS) && + !memcmp(ni->file.ads.name, QUERY_STREAMS, sizeof(QUERY_STREAMS))) { + /* Query ADS. */ + if (unlikely(iocb->ki_flags & IOCB_DIRECT)) { + ntfs_inode_warn( + inode, + "direct I/O for streams is not supported"); + return -EOPNOTSUPP; + } + + inode_lock_shared(inode); + ret = ni_query_ads(ni, &iocb->ki_pos, iter); + inode_unlock_shared(inode); + return ret; + } + if (is_compressed(ni)) { if (iocb->ki_flags & IOCB_DIRECT) { ntfs_inode_warn( @@ -867,17 +885,17 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) if (ni->file.run_da.count) { /* Direct I/O is not compatible with delalloc. */ - err = ni_allocate_da_blocks(ni); - if (err) + ret = ni_allocate_da_blocks(ni); + if (ret) goto out; } - err = iomap_dio_rw(iocb, iter, &ntfs_iomap_ops, NULL, dio_flags, + ret = iomap_dio_rw(iocb, iter, &ntfs_iomap_ops, NULL, dio_flags, NULL, 0); - if (err <= 0) + if (ret <= 0) goto out; - end = vbo + err; + end = vbo + ret; if (valid < end) { size_t to_zero = end - valid; /* Fix iter. */ @@ -889,35 +907,36 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) bytes = i_size - vbo; iov_iter_zero(bytes, iter); iocb->ki_pos += bytes; - err = bytes; + ret = bytes; } out: inode_unlock_shared(inode); - file_accessed(iocb->ki_filp); - return err; + file_accessed(file); + return ret; } /* * ntfs_file_splice_read - file_operations::splice_read */ -static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos, +static ssize_t ntfs_file_splice_read(struct file *file, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { - struct inode *inode = file_inode(in); - ssize_t err; + struct inode *inode = file_inode(file); + struct ntfs_inode *ni = ntfs_i(inode); + ssize_t ret; - err = check_read_restriction(inode); - if (err) - return err; + ret = check_read_restriction(inode); + if (ret) + return ret; - if (is_compressed(ntfs_i(inode))) { + if (is_compressed(ni)) { /* Turn off readahead for compressed files. */ - in->f_ra.ra_pages = 0; + file->f_ra.ra_pages = 0; } - return filemap_splice_read(in, ppos, pipe, len, flags); + return filemap_splice_read(file, ppos, pipe, len, flags); } /* @@ -1420,7 +1439,8 @@ static int ntfs_file_release(struct inode *inode, struct file *file) down_write(&ni->file.run_lock); /* Deallocate preallocated. */ - err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, + err = attr_set_size_ex(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &ni->file.run, inode->i_size, &ni->i_valid, false, NULL, true); diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 2b49bc077558..bead01a953f3 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -132,6 +132,13 @@ void ni_clear(struct ntfs_inode *ni) ni->file.offs_folio = NULL; } #endif + kfree(ni->file.ads.name); + ni->file.ads.name = NULL; + } + + if (ni->base && ni->base != ni) { + iput(&ni->base->vfs_inode); + ni->base = NULL; } mi_clear(&ni->mi); @@ -145,6 +152,7 @@ int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) int err; struct mft_inode *r; + ni = ni->base; r = ni_find_mi(ni, rno); if (r) goto out; @@ -169,8 +177,9 @@ int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le, struct mft_inode **mi) { - CLST rno; + u64 rno; + ni = ni->base; if (!le) { *mi = &ni->mi; return 0; @@ -197,6 +206,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, struct ATTR_LIST_ENTRY *le; struct mft_inode *m; + ni = ni->base; if (!ni->attr_list.size || (!name_len && (type == ATTR_LIST || type == ATTR_STD))) { if (le_o) @@ -257,6 +267,7 @@ struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, struct mft_inode *mi2; struct ATTR_LIST_ENTRY *le2; + ni = ni->base; /* Do we have an attribute list? */ if (!ni->attr_list.size) { *le = NULL; @@ -290,6 +301,7 @@ int ni_load_all_mi(struct ntfs_inode *ni) int err; struct ATTR_LIST_ENTRY *le; + ni = ni->base; if (!ni->attr_list.size) return 0; @@ -316,6 +328,7 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) { struct mft_inode *m; + ni = ni->base; m = kzalloc_obj(struct mft_inode, GFP_NOFS); if (!m) return false; @@ -348,6 +361,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, u32 type_in; int diff; + ni = ni->base; if (base_only || type == ATTR_LIST || !ni->attr_list.size) { attr = mi_find_attr(ni, &ni->mi, NULL, type, name, name_len, id); @@ -417,6 +431,7 @@ ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi, bool le_added = false; struct MFT_REF ref; + ni = ni->base; mi_get_ref(mi, &ref); if (type != ATTR_LIST && !le && ni->attr_list.size) { @@ -618,6 +633,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) struct MFT_REC *mrec; __le16 id; + ni = ni->base; if (!ni->attr_list.dirty) return 0; @@ -763,15 +779,29 @@ int ni_create_attr_list(struct ntfs_inode *ni) u32 free_b, nb, to_free, rs; u16 sz; + ni = ni->base; is_mft = ni->mi.rno == MFT_REC_MFT; rec = ni->mi.mrec; rs = sbi->record_size; /* - * Skip estimating exact memory requirement. - * Looks like one record_size is always enough. + * Compute the exact size of the attribute list. Each attribute in the + * record yields one ATTR_LIST_ENTRY of le_size(name_len) bytes. The + * minimum on-disk attribute is SIZEOF_RESIDENT (0x18) bytes, but an + * unnamed one expands to le_size(0) (0x20) here, so a record crafted + * with many such attributes needs more than a single record_size; the + * previous fixed kzalloc(record_size) could therefore be overflowed by + * an attacker-controlled record. */ - le = kzalloc(al_aligned(rs), GFP_NOFS); + lsize = 0; + attr = NULL; + while ((attr = mi_enum_attr(ni, &ni->mi, attr))) + lsize += le_size(attr->name_len); + + if (!lsize) + return -EINVAL; + + le = kzalloc(al_aligned(lsize), GFP_NOFS); if (!le) return -ENOMEM; @@ -781,7 +811,6 @@ int ni_create_attr_list(struct ntfs_inode *ni) attr = NULL; nb = 0; free_b = 0; - attr = NULL; for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) { sz = le_size(attr->name_len); @@ -905,6 +934,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, bool is_mft, is_mft_data; struct ntfs_sb_info *sbi = ni->mi.sbi; + ni = ni->base; is_mft = ni->mi.rno == MFT_REC_MFT; is_mft_data = is_mft && type == ATTR_DATA && !name_len; @@ -1057,6 +1087,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, __le16 id; u16 t16; + ni = ni->base; is_mft = ni->mi.rno == MFT_REC_MFT; rec = ni->mi.mrec; @@ -1203,6 +1234,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) struct mft_inode *mi, *mi_min, *mi_new; struct ntfs_sb_info *sbi = ni->mi.sbi; + ni = ni->base; /* Find the nearest MFT. */ mft_min = 0; mft_new = 0; @@ -1335,6 +1367,7 @@ int ni_expand_list(struct ntfs_inode *ni) bool is_mft = ni->mi.rno == MFT_REC_MFT; struct MFT_REF ref; + ni = ni->base; mi_get_ref(&ni->mi, &ref); le = NULL; @@ -1407,6 +1440,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, u32 run_size, asize; struct ntfs_sb_info *sbi = ni->mi.sbi; + ni = ni->base; /* Estimate packed size (run_buf=NULL). */ err = run_pack(run, svcn, len, NULL, sbi->max_bytes_per_attr - run_off, &plen); @@ -1476,6 +1510,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, u32 asize = SIZEOF_RESIDENT + name_size + ALIGN(data_size, 8); struct ATTRIB *attr; + ni = ni->base; err = ni_insert_attr(ni, type, name, name_len, asize, SIZEOF_RESIDENT, 0, &attr, mi, le); if (err) @@ -1507,6 +1542,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, struct mft_inode *mi, struct ATTR_LIST_ENTRY *le) { + ni = ni->base; mi_remove_attr(ni, mi, attr); if (le) @@ -1531,6 +1567,7 @@ int ni_delete_all(struct ntfs_inode *ni) bool nt3 = is_ntfs3(sbi); struct MFT_REF ref; + ni = ni->base; while ((attr = ni_enum_attr_ex(ni, attr, &le, NULL))) { if (!nt3 || attr->name_len) { ; @@ -1608,6 +1645,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, struct ATTRIB *attr = NULL; struct ATTR_FILE_NAME *fname; + ni = ni->base; if (le) *le = NULL; @@ -1649,6 +1687,7 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, struct ATTR_FILE_NAME *fname; *le = NULL; + ni = ni->base; if (name_type == FILE_NAME_POSIX) return NULL; @@ -1679,6 +1718,7 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) __le16 new_aflags; u32 new_asize; + ni = ni->base; attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi); if (!attr) return -EINVAL; @@ -1760,6 +1800,7 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, u16 len; typeof(rp->CompressReparseBuffer) *cmpr; + ni = ni->base; /* Try to estimate reparse point. */ if (!attr->non_res) { rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER)); @@ -1988,6 +2029,7 @@ int ni_decompress_file(struct ntfs_inode *ni) struct mft_inode *mi; int err; + ni = ni->base; /* Clusters for decompressed data. */ cend = bytes_to_cluster(sbi, i_size); @@ -2249,6 +2291,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, struct ATTRIB *attr; CLST frame, clst_data; + ni = ni->base; /* * To simplify decompress algorithm do vmap for source * and target pages. @@ -2443,6 +2486,15 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, err = unc_size; else if (!unc_size || unc_size > frame_size) err = -EINVAL; + else if (unc_size < frame_size) { + /* + * Partial decompress: zero the [unc_size, frame_size) + * tail. decompress_lznt() leaves it untouched, so + * without this the freshly vmapped pages would expose + * uninitialized kernel memory to userspace. + */ + memset(frame_mem + unc_size, 0, frame_size - unc_size); + } } if (!err && valid_size < frame_vbo + frame_size) { size_t ok = valid_size - frame_vbo; @@ -2498,6 +2550,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, size_t compr_size, ondisk_size; struct lznt *lznt; + ni = ni->base; attr = ni_find_attr(ni, NULL, &le, ATTR_DATA, NULL, 0, NULL, &mi); if (!attr) { err = -ENOENT; @@ -2623,6 +2676,7 @@ int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, u16 de_key_size = le16_to_cpu(de->key_size); u8 name_type; + ni = ni->base; *undo_step = 0; /* Find name in record. */ @@ -2685,6 +2739,7 @@ bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, struct ATTRIB *attr; u16 de_key_size; + ni = ni->base; switch (undo_step) { case 4: de_key_size = le16_to_cpu(de2->key_size); @@ -2735,6 +2790,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1); u16 de_key_size = le16_to_cpu(de->key_size); + ni = ni->base; if (sbi->options->windows_names && !valid_windows_name(sbi, (struct le_str *)&de_name->name_len)) return -EINVAL; @@ -2782,6 +2838,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, int err; struct NTFS_DE *de2 = NULL; int undo = 0; + const int way = 1; /* Hope compiler removes below 'else'. */ /* * There are two possible ways to rename: @@ -2793,29 +2850,30 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, * 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) { + if (way == 1) { + /* + * 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)) + _ntfs_bad_inode(&ni->vfs_inode); + } + } else { + /* + * Way 2 - Remove old + add new. + */ err = ni_remove_name(dir_ni, ni, de, &de2, &undo); - if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)) - _ntfs_bad_inode(&ni->vfs_inode); + if (!err) { + err = ni_add_name(new_dir_ni, ni, new_de); + if (err && + !ni_remove_name_undo(dir_ni, ni, de, de2, undo)) + _ntfs_bad_inode(&ni->vfs_inode); + } } - /* - * 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; } @@ -2919,7 +2977,6 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) break; } } - } vbo = (u64)vcn << cluster_bits; @@ -2939,6 +2996,9 @@ int ni_write_parents(struct ntfs_inode *ni, int sync) struct ntfs_sb_info *sbi = ni->mi.sbi; struct super_block *sb = sbi->sb; + if (!is_ni_base(ni)) + return 0; + while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL, NULL))) { struct inode *dir; @@ -2961,8 +3021,8 @@ int ni_write_parents(struct ntfs_inode *ni, int sync) if (IS_ERR(dir)) { ntfs_inode_warn( &ni->vfs_inode, - "failed to open parent directory r=%lx to write", - (long)ino_get(&fname->home)); + "failed to open parent directory r=%llx to write", + (u64)ino_get(&fname->home)); continue; } @@ -3081,8 +3141,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, if (IS_ERR(dir)) { ntfs_inode_warn( &ni->vfs_inode, - "failed to open parent directory r=%lx to update", - (long)ino_get(&fname->home)); + "failed to open parent directory r=%llx to update", + (u64)ino_get(&fname->home)); continue; } @@ -3121,6 +3181,9 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) if (is_bad_inode(inode) || sb_rdonly(sb)) return 0; + if (!is_ni_base(ni)) + return 0; + /* Avoid any operation if inode is bad. */ if (unlikely(is_bad_ni(ni))) return -EINVAL; @@ -3305,10 +3368,84 @@ int ni_allocate_da_blocks_locked(struct ntfs_inode *ni) /* * Normal file allocates clusters in 'attr_set_size' */ - err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, + err = attr_set_size_ex(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, &ni->file.run, ni->vfs_inode.i_size, &ni->i_valid, false, NULL, true); } return err; } + +/* + * Helper function to read ADS. + * bytes = iov_iter_count(iter) is not 0. Checked by caller; + */ +ssize_t ni_query_ads(struct ntfs_inode *ni, loff_t *pos, struct iov_iter *iter) +{ + ssize_t ret = 0; + struct ntfs_sb_info *sbi = ni->mi.sbi; + size_t bytes = iov_iter_count(iter); + loff_t end = *pos + bytes; + char *buf = NULL; + struct ATTR_LIST_ENTRY *le = NULL; + u64 vbo = 0; + struct ATTRIB *attr; + size_t done, used; + int err; + + /* + * Enumerate ADS. + */ + ni = ni->base; + attr = NULL; + while ((attr = ni_enum_attr_ex(ni, attr, &le, NULL))) { + if (attr->type != ATTR_DATA || !attr->name_len) + continue; + + if (!buf) { + buf = kmalloc(PAGE_SIZE, GFP_NOFS); + if (!buf) + return -ENOMEM; + } + + /* attr - named DATA attribute (ADS). */ + err = ntfs_utf16_to_nls(sbi, attr_name(attr), attr->name_len, + buf, PAGE_SIZE); + if (err < 0) { + ret = err; + break; + } + + /* + * err is the length of ADS name in bytes. + * Copy pseudo data interval [vbo, err + 1). + * into 'iter': [*pos, bytes) + */ + /* Add \n as streams name separator. */ + buf[err++] = '\n'; + + if (vbo >= end) { + iov_iter_zero(bytes, iter); + break; + } + + if (vbo + err > *pos) { + size_t off = *pos - vbo; + used = err - off; + done = copy_to_iter(Add2Ptr(buf, off), min(used, bytes), + iter); + } else { + done = 0; + } + + ret += done; + *pos += done; + if (done >= bytes) + break; + bytes -= done; + vbo += err; + } + kfree(buf); + return ret; +} diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index f038c799e7ac..ed50c1d0c23e 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -647,6 +647,14 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) return NULL; } +/* + * dp_range_ok - true if [j, j + count) fits in a page_lcns[cap] array. + */ +static inline bool dp_range_ok(size_t j, u32 count, u32 cap) +{ + return j < cap && count <= cap - j; +} + /* * find_dp - Search for a @vcn in Dirty Page Table. */ @@ -789,6 +797,20 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) return true; } +static bool check_dp_table(const struct RESTART_TABLE *dptbl) +{ + u32 rsize = le16_to_cpu(dptbl->size); + struct DIR_PAGE_ENTRY *dp = NULL; + + while ((dp = enum_rstbl((struct RESTART_TABLE *)dptbl, dp))) { + if (struct_size(dp, page_lcns, le32_to_cpu(dp->lcns_follow)) > + rsize) + return false; + } + + return true; +} + /* * free_rsttbl_idx - Free a previously allocated index a Restart Table. */ @@ -853,6 +875,9 @@ static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl, u32 used = le16_to_cpu(tbl->used); struct RESTART_TABLE *rt; + if (used + add > U16_MAX) + return NULL; + rt = init_rsttbl(esize, used + add); if (!rt) return NULL; @@ -2276,7 +2301,15 @@ static int read_log_rec_buf(struct ntfs_log *log, */ for (;;) { bool usa_error; - u32 tail = log->page_size - off; + u32 tail; + + /* off comes from the on-disk restart area; bound it. */ + if (off > log->page_size) { + err = -EINVAL; + goto out; + } + + tail = log->page_size - off; if (tail >= data_len) tail = data_len; @@ -2613,7 +2646,6 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) const bool has_subnode = hdr_has_subnode(hdr); __le16 mask; u32 min_de, de_off, used, total; - const struct NTFS_DE *e; if (has_subnode) { min_de = sizeof(struct NTFS_DE) + sizeof(u64); @@ -2632,8 +2664,8 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) return false; } - e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); for (;;) { + const struct NTFS_DE *e = Add2Ptr(hdr, de_off); u16 esize = le16_to_cpu(e->size); u16 key_size = le16_to_cpu(e->key_size); u16 data_size; @@ -2649,7 +2681,6 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) if (de_is_last(e)) { if (key_size) return false; - break; } @@ -2658,7 +2689,6 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) return false; de_off += esize; - e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); } return true; @@ -3544,8 +3574,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, * bound here so the memmove cannot reach past the entry. */ if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || - le16_to_cpu(e->view.data_off) + dlen > - le16_to_cpu(e->size)) + le16_to_cpu(e->view.data_off) + dlen > le16_to_cpu(e->size)) goto dirty_vol; memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); @@ -3756,8 +3785,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, /* See UpdateRecordDataRoot for the rationale. */ if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || - le16_to_cpu(e->view.data_off) + dlen > - le16_to_cpu(e->size)) + le16_to_cpu(e->view.data_off) + dlen > le16_to_cpu(e->size)) goto dirty_vol; memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); @@ -4295,6 +4323,11 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) goto out; } + if (!check_dp_table(rt)) { + err = -EINVAL; + goto out; + } + dptbl = kmemdup(rt, t32, GFP_NOFS); if (!dptbl) { err = -ENOMEM; @@ -4653,11 +4686,11 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) } /* - * find_dp() only validates that target_vcn is the first - * cluster covered by dp. The walk through lrh->lcns_follow - * further entries must stay within the allocated - * dp->page_lcns[] array, which is sized by dp->lcns_follow. - */ + * find_dp() only validates that target_vcn is the first + * cluster covered by dp. The walk through lrh->lcns_follow + * further entries must stay within the allocated + * dp->page_lcns[] array, which is sized by dp->lcns_follow. + */ if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 > le32_to_cpu(dp->lcns_follow)) { err = -EINVAL; @@ -5087,6 +5120,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) /* Shorten length by any Lcns which were deleted. */ saved_len = dlen; + if (!dp_range_ok(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn), + le16_to_cpu(lrh->lcns_follow), + le32_to_cpu(dp->lcns_follow))) { + err = -EINVAL; + goto out; + } + for (i = le16_to_cpu(lrh->lcns_follow); i; i--) { size_t j; u32 alen, voff; diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index bc7469d0a34d..97c04ab2763a 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -88,6 +88,13 @@ const __le16 SQ_NAME[2] = { const __le16 SR_NAME[2] = { cpu_to_le16('$'), cpu_to_le16('R'), }; +const __le16 QUERY_STREAMS[13] = { + cpu_to_le16('q'), cpu_to_le16('u'), cpu_to_le16('e'), cpu_to_le16('r'), + cpu_to_le16('y'), cpu_to_le16('_'), cpu_to_le16('s'), cpu_to_le16('t'), + cpu_to_le16('r'), cpu_to_le16('e'), cpu_to_le16('a'), cpu_to_le16('m'), + cpu_to_le16('s'), +}; + #ifdef CONFIG_NTFS3_LZX_XPRESS const __le16 WOF_NAME[17] = { @@ -122,7 +129,6 @@ static const __le16 COM_NAME[3] = { static const __le16 LPT_NAME[3] = { cpu_to_le16('L'), cpu_to_le16('P'), cpu_to_le16('T'), }; - // clang-format on /* @@ -236,7 +242,7 @@ int ntfs_extend_init(struct ntfs_sb_info *sbi) } /* Try to find $ObjId */ - inode2 = dir_search_u(inode, &NAME_OBJID, NULL); + inode2 = dir_search(inode, &NAME_OBJID); if (inode2 && !IS_ERR(inode2)) { if (is_bad_inode(inode2)) { iput(inode2); @@ -247,21 +253,21 @@ int ntfs_extend_init(struct ntfs_sb_info *sbi) } /* Try to find $Quota */ - inode2 = dir_search_u(inode, &NAME_QUOTA, NULL); + inode2 = dir_search(inode, &NAME_QUOTA); if (inode2 && !IS_ERR(inode2)) { sbi->quota_no = inode2->i_ino; iput(inode2); } /* Try to find $Reparse */ - inode2 = dir_search_u(inode, &NAME_REPARSE, NULL); + inode2 = dir_search(inode, &NAME_REPARSE); if (inode2 && !IS_ERR(inode2)) { sbi->reparse.ni = ntfs_i(inode2); sbi->reparse_no = inode2->i_ino; } /* Try to find $UsnJrnl */ - inode2 = dir_search_u(inode, &NAME_USNJRNL, NULL); + inode2 = dir_search(inode, &NAME_USNJRNL); if (inode2 && !IS_ERR(inode2)) { sbi->usn_jrnl_no = inode2->i_ino; iput(inode2); @@ -475,7 +481,7 @@ bool ntfs_check_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen, avail = free - (zlen + clen); - /* + /* * When delalloc is active then keep in mind some reserved space. * The worst case: 1 mft record per each ~500 clusters. */ @@ -1705,6 +1711,8 @@ struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, goto out; } + ni->base = ni; + out: if (err) { make_bad_inode(inode); @@ -2302,8 +2310,8 @@ int ntfs_reparse_init(struct ntfs_sb_info *sbi) goto out; } - root_r = resident_data(attr); - if (root_r->type != ATTR_ZERO || + root_r = resident_data_ex(attr, sizeof(struct INDEX_ROOT)); + if (!root_r || root_r->type != ATTR_ZERO || root_r->rule != NTFS_COLLATION_TYPE_UINTS) { err = -EINVAL; goto out; @@ -2340,8 +2348,8 @@ int ntfs_objid_init(struct ntfs_sb_info *sbi) goto out; } - root = resident_data(attr); - if (root->type != ATTR_ZERO || + root = resident_data_ex(attr, sizeof(struct INDEX_ROOT)); + if (!root || root->type != ATTR_ZERO || root->rule != NTFS_COLLATION_TYPE_UINTS) { err = -EINVAL; goto out; @@ -2664,6 +2672,12 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) if (err < 0) goto out; + if (uni->ads_len) { + /* Undo delimiter parse */ + uni->len += uni->ads_len + 1; + uni->ads_len = 0; + } + uni_bytes = uni->len * sizeof(u16); if (uni_bytes > NTFS_LABEL_MAX_LENGTH * sizeof(u16)) { ntfs_warn(sbi->sb, "new label is too long"); diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 2b439ac04356..689712d3463d 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -612,8 +612,8 @@ static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr, static bool index_hdr_check(const struct INDEX_HDR *hdr, u32 bytes) { const bool has_subnode = hdr_has_subnode(hdr); - const u16 min_size = sizeof(struct NTFS_DE) + - (has_subnode ? sizeof(u64) : 0); + const u16 min_size = + sizeof(struct NTFS_DE) + (has_subnode ? sizeof(u64) : 0); u32 end = le32_to_cpu(hdr->used); u32 tot = le32_to_cpu(hdr->total); u32 off = le32_to_cpu(hdr->de_off); @@ -1325,6 +1325,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, /* Pop one level. */ if (n) { fnd_pop(fnd); + kfree(n->index); kfree(n); } @@ -2131,8 +2132,7 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, if (err) return ERR_PTR(err); - r = indx_find_buffer(indx, ni, root, vbn, n, - depth + 1); + r = indx_find_buffer(indx, ni, root, vbn, n, depth + 1); if (r) return r; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 6ffe99da4d2a..56b4f6469a28 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -18,12 +18,16 @@ #include "ntfs.h" #include "ntfs_fs.h" +struct IGET5_PARAM { + const struct MFT_REF *ref; + const struct cpu_str *name; +}; + /* * ntfs_read_mft - Read record and parse MFT. */ -static struct inode *ntfs_read_mft(struct inode *inode, - const struct cpu_str *name, - const struct MFT_REF *ref) +static int ntfs_read_mft(struct inode *inode, const struct cpu_str *name, + const struct MFT_REF *ref) { int err = 0; struct ntfs_inode *ni = ntfs_i(inode); @@ -36,7 +40,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, bool is_match = false; bool is_root = false; bool is_dir; - unsigned long ino = inode->i_ino; + u64 ino = inode->i_ino; u32 rp_fa = 0, asize, t32; u16 roff, rsize, names = 0, links = 0; const struct ATTR_FILE_NAME *fname = NULL; @@ -46,6 +50,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, struct MFT_REC *rec; struct runs_tree *run; struct timespec64 ts; + const __le16 *aname; inode->i_op = NULL; /* Setup 'uid' and 'gid' */ @@ -79,7 +84,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, ; } else if (ref->seq != rec->seq) { err = -EINVAL; - ntfs_err(sb, "MFT: r=%lx, expect seq=%x instead of %x!", ino, + ntfs_err(sb, "MFT: r=%llx, expect seq=%x instead of %x!", ino, le16_to_cpu(ref->seq), le16_to_cpu(rec->seq)); goto out; } else if (!is_rec_inuse(rec)) { @@ -127,10 +132,16 @@ static struct inode *ntfs_read_mft(struct inode *inode, if (le && le->vcn) { /* This is non primary attribute segment. Ignore if not MFT. */ - if (ino != MFT_REC_MFT || attr->type != ATTR_DATA) + if (ino != MFT_REC_MFT) + goto next_attr; + + if (attr->type == ATTR_DATA) + run = &ni->file.run; + else if (attr->type == ATTR_BITMAP) + run = &sbi->mft.bitmap.run; + else goto next_attr; - run = &ni->file.run; asize = le32_to_cpu(attr->size); goto attr_unpack_run; } @@ -138,6 +149,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, roff = attr->non_res ? 0 : le16_to_cpu(attr->res.data_off); rsize = attr->non_res ? 0 : le32_to_cpu(attr->res.data_size); asize = le32_to_cpu(attr->size); + aname = attr_name(attr); /* * Really this check was done in 'ni_enum_attr_ex' -> ... 'mi_enum_attr'. @@ -224,10 +236,10 @@ static struct inode *ntfs_read_mft(struct inode *inode, if (attr->name_len && ((ino != MFT_REC_BADCLUST || !attr->non_res || attr->name_len != ARRAY_SIZE(BAD_NAME) || - memcmp(attr_name(attr), BAD_NAME, sizeof(BAD_NAME))) && + memcmp(aname, BAD_NAME, sizeof(BAD_NAME))) && (ino != MFT_REC_SECURE || !attr->non_res || attr->name_len != ARRAY_SIZE(SDS_NAME) || - memcmp(attr_name(attr), SDS_NAME, sizeof(SDS_NAME))))) { + memcmp(aname, SDS_NAME, sizeof(SDS_NAME))))) { /* File contains stream attribute. Ignore it. */ goto next_attr; } @@ -247,14 +259,11 @@ static struct inode *ntfs_read_mft(struct inode *inode, else ni->std_fa &= ~FILE_ATTRIBUTE_ENCRYPTED; - if (!attr->non_res) { - ni->i_valid = inode->i_size = rsize; - inode_set_bytes(inode, rsize); - } - mode = S_IFREG | (0777 & sbi->options->fs_fmask_inv); if (!attr->non_res) { + ni->i_valid = inode->i_size = rsize; + inode_set_bytes(inode, rsize); ni->ni_flags |= NI_FLAG_RESIDENT; goto next_attr; } @@ -495,16 +504,136 @@ static struct inode *ntfs_read_mft(struct inode *inode, if (ino == MFT_REC_MFT && !sb->s_root) sbi->mft.ni = NULL; - unlock_new_inode(inode); - - return inode; + return 0; out: if (ino == MFT_REC_MFT && !sb->s_root) sbi->mft.ni = NULL; - iget_failed(inode); - return ERR_PTR(err); + return err; +} + +/* + * ntfs_init_ads_node + * + * This function scans base inode for given ADS. + * And init inode associated with this ADS + */ +static int ntfs_init_ads_node(struct inode *inode, const __le16 *ads_name, + u8 ads_len, u32 flags) +{ + int err = -EINVAL; + struct ntfs_inode *ni = ntfs_i(inode); + struct ntfs_inode *nb = ni->base; + struct ntfs_sb_info *sbi = nb->mi.sbi; + struct ATTR_LIST_ENTRY *le = NULL; + struct ATTRIB *attr = NULL; + u16 roff, asize; + u64 svcn; + + if (nb->ni_flags & NI_FLAG_DIR) + return -EINVAL; /* no ADS for directories. */ + + ni->mi.sbi = sbi; + ni->mi.rno = inode->i_ino; + + if (ads_len == ARRAY_SIZE(QUERY_STREAMS) && + !memcmp(ads_name, QUERY_STREAMS, sizeof(QUERY_STREAMS))) { + goto ok; /* use goto to reduce tab pressure. */ + } + + /* Enumerate all attributes in record. */ + while ((attr = ni_enum_attr_ex(nb, attr, &le, NULL))) { + if (attr->type == ATTR_DATA && attr->name_len && + ads_len == attr->name_len && + !memcmp(ads_name, attr_name(attr), ads_len * sizeof(u16))) { + /* We have found the ADS to open. */ + break; + } + } + + if (!attr) { + if (!(flags & LOOKUP_CREATE)) { + /* Do not create ADS. */ + return -ENOENT; + } + + /* Create new ADS. */ + err = ni_insert_resident(nb, 0, ATTR_DATA, ads_name, ads_len, + &attr, NULL, NULL); + if (err) { + /* Looks like the only reasons: ENOSPC/ENOMEM .*/ + return err; + } + } + + if (is_attr_sparsed(attr)) + ni->std_fa |= FILE_ATTRIBUTE_SPARSE_FILE; + else + ni->std_fa &= ~FILE_ATTRIBUTE_SPARSE_FILE; + + if (is_attr_compressed(attr)) + ni->std_fa |= FILE_ATTRIBUTE_COMPRESSED; + else + ni->std_fa &= ~FILE_ATTRIBUTE_COMPRESSED; + + if (is_attr_encrypted(attr)) + ni->std_fa |= FILE_ATTRIBUTE_ENCRYPTED; + else + ni->std_fa &= ~FILE_ATTRIBUTE_ENCRYPTED; + + if (!attr->non_res) { + ni->ni_flags |= NI_FLAG_RESIDENT; + ni->i_valid = inode->i_size = le32_to_cpu(attr->res.data_size); + inode_set_bytes(inode, inode->i_size); + goto ok; + } + + inode_set_bytes(inode, attr_ondisk_size(attr)); + ni->i_valid = le64_to_cpu(attr->nres.valid_size); + inode->i_size = le64_to_cpu(attr->nres.data_size); + + if (!attr->nres.alloc_size) + goto ok; + + roff = le16_to_cpu(attr->nres.run_off); + asize = le32_to_cpu(attr->size); + + if (roff > asize) { + /* This case should be checked in mi_enum_attr */ + return -EINVAL; + } + + svcn = le64_to_cpu(attr->nres.svcn); + err = run_unpack_ex(&ni->file.run, sbi, ni->mi.rno, svcn, + le64_to_cpu(attr->nres.evcn), svcn, + Add2Ptr(attr, roff), asize - roff); + if (err < 0) { + /* run_unpack_ex marks volume dirty, if logical error. */ + return err; + } + +ok: + /* Keep ADS name (little endian). */ + ni->file.ads.name = kmemdup(ads_name, ads_len * sizeof(u16), GFP_NOFS); + if (!ni->file.ads.name) + return -ENOMEM; + ni->file.ads.len = ads_len; + + set_nlink(inode, 1); + + init_rwsem(&ni->file.run_lock); + /* Most fields are the same as the base's? */ + inode->i_op = nb->vfs_inode.i_op; + inode->i_fop = nb->vfs_inode.i_fop; + inode->i_mapping->a_ops = nb->vfs_inode.i_mapping->a_ops; + inode->i_flags = nb->vfs_inode.i_flags; + inode->i_mode = nb->vfs_inode.i_mode; + inode->i_uid = nb->vfs_inode.i_uid; + inode->i_gid = nb->vfs_inode.i_gid; + inode->i_generation = nb->vfs_inode.i_generation; + + return 0; } /* @@ -514,44 +643,120 @@ static struct inode *ntfs_read_mft(struct inode *inode, */ static int ntfs_test_inode(struct inode *inode, void *data) { - struct MFT_REF *ref = data; + const struct IGET5_PARAM *ig5 = data; + struct ntfs_inode *ni; + const struct cpu_str *name; - return ino_get(ref) == inode->i_ino; + if (ino_get(ig5->ref) != inode->i_ino) + return 0; + + ni = ntfs_i(inode); + + if (ni->ni_flags & NI_FLAG_DIR) { + /* No ads for directories. */ + return 1; + } + + name = ig5->name; + if (!name || !name->ads_len) { + if (!ni->file.ads.len) { + /* default file (not ads) match. */ + return 1; + } + } else if (ni->file.ads.len == name->ads_len && + !memcmp(ni->file.ads.name, &name->name[name->len + 1], + name->ads_len * sizeof(u16))) { + /* ads name match. */ + return 1; + } + + return 0; } static int ntfs_set_inode(struct inode *inode, void *data) { - const struct MFT_REF *ref = data; + const struct IGET5_PARAM *ig5 = data; - inode->i_ino = ino_get(ref); + inode->i_ino = ino_get(ig5->ref); return 0; } -struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, - const struct cpu_str *name) +struct inode *ntfs_iget5_flags(struct super_block *sb, + const struct MFT_REF *ref, + const struct cpu_str *name, u32 flags) { - struct inode *inode; + int err; + /* Pack params to pass in iget5_locked. */ + struct IGET5_PARAM ig5 = { ref, name }; + u64 ino = ino_get(ref); + struct inode *inode, *base = NULL; + bool ads = name && name->ads_len; + struct ntfs_inode *ni; - inode = iget5_locked(sb, ino_get(ref), ntfs_test_inode, ntfs_set_inode, - (void *)ref); - if (unlikely(!inode)) - return ERR_PTR(-ENOMEM); + if (ads) { + /* First get base inode */ + base = ntfs_iget5_flags(sb, ref, NULL, 0); + if (IS_ERR(base)) + return base; + } + + inode = iget5_locked(sb, ino, ntfs_test_inode, ntfs_set_inode, &ig5); + if (unlikely(!inode)) { + err = -ENOMEM; + goto out; + } + + ni = ntfs_i(inode); /* If this is a freshly allocated inode, need to read it now. */ - if (inode_state_read_once(inode) & I_NEW) - inode = ntfs_read_mft(inode, name, ref); - else if (ref->seq != ntfs_i(inode)->mi.mrec->seq) { + if (inode_state_read_once(inode) & I_NEW) { + if (!base) { + /* default inode. generic file/dir. */ + ni->base = ni; + } else { + /* inode + ads */ + ni->base = ntfs_i(base); + base = NULL; /* keep reference incremented (instead of ihold). */ + } + + if (ads) { + /* base record is loaded. Init ads node. */ + err = ntfs_init_ads_node( + inode, (__le16 *)&name->name[name->len + 1], + name->ads_len, flags); + } else { + err = ntfs_read_mft(inode, name, ref); + } + + if (!err) { + unlock_new_inode(inode); + } else { + iget_failed(inode); + /* Do not mark volume dirty if ADS not found. */ + if (ads) + goto out; + } + } else if (!ads && ref->seq != ni->mi.mrec->seq) { /* * Sequence number is not expected. * Looks like inode was reused but caller uses the old reference */ iput(inode); - inode = ERR_PTR(-ESTALE); + err = -ESTALE; + } else { + err = 0; } - if (IS_ERR(inode)) + if (err) ntfs_set_state(sb->s_fs_info, NTFS_DIRTY_ERROR); +out: + if (base) + iput(base); + + if (err) + return ERR_PTR(err); + return inode; } @@ -606,15 +811,17 @@ static void ntfs_iomap_read_end_io(struct bio *bio) } static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) + struct iomap_read_folio_ctx *ctx) { iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io); } +// clang-format off static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { .read_folio_range = iomap_bio_read_folio_range, .submit_read = ntfs_iomap_bio_submit_read, }; +// clang-format on static int ntfs_read_folio(struct file *file, struct folio *folio) { @@ -698,8 +905,8 @@ int ntfs_set_size(struct inode *inode, u64 new_size) ni->i_valid = new_size; /* last 'true' means keep preallocated. */ - err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size, - &ni->i_valid, true); + err = attr_set_size(ni, ATTR_DATA, ni->file.ads.name, ni->file.ads.len, + &ni->file.run, new_size, &ni->i_valid, true); up_write(&ni->file.run_lock); ni_unlock(ni); @@ -793,7 +1000,8 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (lcn == RESIDENT_LCN) { if (offset >= clen) { - __free_page(virt_to_page(res)); + if (res) + __free_page(virt_to_page(res)); if (flags & IOMAP_REPORT) { /* special code for report. */ return -ENOENT; @@ -884,7 +1092,8 @@ static int ntfs_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct ATTRIB *attr; struct mft_inode *mi; - attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, + attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, + ni->file.ads.name, ni->file.ads.len, NULL, &mi); if (!attr || attr->non_res) { err = -EINVAL; @@ -1206,6 +1415,15 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, if (!fnd) ni_lock_dir(dir_ni); + if (sbi->options->ads) { + const char *ads = strchr(name->name + 1, ':'); + if (ads && ads[1]) { + ntfs_warn(sb, "failed to create ads"); + err = -EINVAL; + goto out1; + } + } + dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL); if (!dir_root) { err = -EINVAL; diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c index f818d9785004..5dcb7674790c 100644 --- a/fs/ntfs3/lznt.c +++ b/fs/ntfs3/lznt.c @@ -240,8 +240,10 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, if (up - unc > LZNT_CHUNK_SIZE) return -EINVAL; /* Correct index */ - while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up) + while (index < ARRAY_SIZE(s_max_off) - 1 && + unc + s_max_off[index] < up) { index += 1; + } /* Check the current flag for zero. */ if (!(ch & (1 << bit))) { diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 5ab45d17333e..ec59bbabd3c5 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -22,7 +22,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, { int err; struct NTFS_DE *e = buf; - u16 data_size; + u16 data_size, real_size, aligned_size; struct ATTR_FILE_NAME *fname = (struct ATTR_FILE_NAME *)(e + 1); #ifndef CONFIG_NTFS3_64BIT_CLUSTER @@ -53,7 +53,12 @@ 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(ALIGN(data_size, 8) + sizeof(struct NTFS_DE)); + real_size = data_size + sizeof(struct NTFS_DE); + aligned_size = ALIGN(data_size, 8) + sizeof(struct NTFS_DE); + if (aligned_size > real_size) + memset((char *)buf + real_size, 0, aligned_size - real_size); + + e->size = cpu_to_le16(aligned_size); e->key_size = cpu_to_le16(data_size); e->flags = 0; e->res = 0; @@ -73,21 +78,23 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, 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_dir(ni); - inode = dir_search_u(dir, uni, NULL); - ni_unlock(ni); - } + return ERR_PTR(-ENOMEM); + + 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) { kfree(uni); + return ERR_PTR(err); } + ni_lock_dir(ni); + inode = dir_search_flags(dir, uni, NULL, flags); + ni_unlock(ni); + + kfree(uni); + /* * Check for a null pointer * If the MFT record of ntfs inode is not a base record, inode->i_op can be NULL. @@ -95,7 +102,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, */ if (!IS_ERR_OR_NULL(inode) && !inode->i_op) { iput(inode); - inode = ERR_PTR(-EINVAL); + return ERR_PTR(-EINVAL); } return d_splice_alias(inode, dentry); @@ -168,7 +175,9 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) */ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) { - struct ntfs_inode *ni = ntfs_i(dir); + struct ntfs_inode *dir_ni = ntfs_i(dir); + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = ntfs_i(inode); int err; /* Avoid any operation if inode is bad. */ @@ -178,11 +187,21 @@ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) if (unlikely(ntfs3_forced_shutdown(dir->i_sb))) return -EIO; - ni_lock_dir(ni); + if (likely(is_ni_base(ni))) { + ni_lock_dir(dir_ni); + /* Remove general file/dir. */ + err = ntfs_unlink_inode(dir, dentry); + ni_unlock(dir_ni); + } else { + ni_lock(ni); + /* Remove ADS. */ + err = ni_remove_attr(ni, ATTR_DATA, ni->file.ads.name, + ni->file.ads.len, false, NULL); + ni_unlock(ni); - err = ntfs_unlink_inode(dir, dentry); - - ni_unlock(ni); + if (!err) + drop_nlink(inode); + } return err; } @@ -268,6 +287,11 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, 1024); static_assert(PATH_MAX >= 4 * 1024); + if (!is_ni_base(ni)) { + /* No rename for ADS. */ + return -EOPNOTSUPP; + } + /* Avoid any operation if inode is bad. */ if (unlikely(is_bad_ni(ni))) return -EINVAL; diff --git a/fs/ntfs3/ntfs.h b/fs/ntfs3/ntfs.h index 892f13e65d42..4589b16329c9 100644 --- a/fs/ntfs3/ntfs.h +++ b/fs/ntfs3/ntfs.h @@ -58,7 +58,7 @@ struct GUID { */ struct cpu_str { u8 len; - u8 unused; + u8 ads_len; u16 name[]; }; @@ -170,6 +170,7 @@ extern const __le16 SDH_NAME[4]; extern const __le16 SO_NAME[2]; extern const __le16 SQ_NAME[2]; extern const __le16 SR_NAME[2]; +extern const __le16 QUERY_STREAMS[13]; extern const __le16 BAD_NAME[4]; extern const __le16 SDS_NAME[4]; diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index d98d7e474476..5811d89d67b3 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -110,6 +110,7 @@ struct ntfs_mount_options { unsigned prealloc : 1; /* Preallocate space when file is growing. */ unsigned nocase : 1; /* case insensitive. */ unsigned delalloc : 1; /* delay allocation. */ + unsigned ads : 1; /* ads support. */ }; /* Special value to unpack and deallocate. */ @@ -401,7 +402,7 @@ struct ntfs_inode { struct rw_semaphore run_lock; /* Unpacked runs from just one record. */ struct runs_tree run; - /* + /* * Pairs [vcn, len] for all delay allocated clusters. * Normal file always contains delayed clusters in one fragment. * TODO: use 2 CLST per pair instead of 3. @@ -410,6 +411,11 @@ struct ntfs_inode { #ifdef CONFIG_NTFS3_LZX_XPRESS struct folio *offs_folio; #endif + /* Alternative data stream */ + struct { + __le16 *name; + u8 len; + } ads; } file; }; @@ -421,6 +427,7 @@ struct ntfs_inode { } attr_list; size_t ni_flags; // NI_FLAG_XXX + struct ntfs_inode *base; /* ADS: points to base inode. Other: this. */ struct inode vfs_inode; }; @@ -444,6 +451,11 @@ enum REPARSE_SIGN { REPARSE_LINK = 3 }; +static inline bool is_ni_base(const struct ntfs_inode *ni) +{ + return ni == ni->base; +} + /* Functions from attrib.c */ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, struct runs_tree *run_da, CLST vcn, CLST lcn, @@ -526,8 +538,14 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len, int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, struct cpu_str *uni, u32 max_ulen, enum utf16_endian endian); -struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, - struct ntfs_fnd *fnd); +struct inode *dir_search_flags(struct inode *dir, const struct cpu_str *uni, + struct ntfs_fnd *fnd, u32 flags); +static inline struct inode *dir_search(struct inode *dir, + const struct cpu_str *uni) +{ + return dir_search_flags(dir, uni, NULL, 0); +} + bool dir_is_empty(struct inode *dir); extern const struct file_operations ntfs_dir_operations; @@ -622,6 +640,7 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data); int ni_write_parents(struct ntfs_inode *ni, int sync); int ni_allocate_da_blocks(struct ntfs_inode *ni); int ni_allocate_da_blocks_locked(struct ntfs_inode *ni); +ssize_t ni_query_ads(struct ntfs_inode *ni, loff_t *pos, struct iov_iter *iter); /* Globals from fslog.c */ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes); @@ -679,7 +698,6 @@ static inline int ntfs_read_bh(struct ntfs_sb_info *sbi, { return ntfs_read_bh_ra(sbi, run, vbo, rhdr, bytes, nb, NULL); } - int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, u32 bytes, struct ntfs_buffers *nb); int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr, @@ -772,8 +790,15 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, const struct NTFS_DUP_INFO *dup, int sync); /* Globals from inode.c */ -struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, - const struct cpu_str *name); +struct inode *ntfs_iget5_flags(struct super_block *sb, + const struct MFT_REF *ref, + const struct cpu_str *name, u32 flags); +static inline struct inode *ntfs_iget5(struct super_block *sb, + const struct MFT_REF *ref, + const struct cpu_str *name) +{ + return ntfs_iget5_flags(sb, ref, name, 0); +} int ntfs_set_size(struct inode *inode, u64 new_size); int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc); int ntfs_sync_inode(struct inode *inode); @@ -886,8 +911,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, #else #define run_unpack_ex run_unpack #endif -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, - u64 *highest_vcn); +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn); int run_clone(const struct runs_tree *run, struct runs_tree *new_run); bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done); CLST run_len(const struct runs_tree *run); @@ -1219,27 +1244,27 @@ static inline void mi_clear(struct mft_inode *mi) static inline void ni_lock(struct ntfs_inode *ni) { - mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_NORMAL); + mutex_lock_nested(&ni->base->ni_lock, NTFS_INODE_MUTEX_NORMAL); } static inline void ni_lock_dir(struct ntfs_inode *ni) { - mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_PARENT); + mutex_lock_nested(&ni->base->ni_lock, NTFS_INODE_MUTEX_PARENT); } static inline void ni_lock_dir2(struct ntfs_inode *ni) { - mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_PARENT2); + mutex_lock_nested(&ni->base->ni_lock, NTFS_INODE_MUTEX_PARENT2); } static inline void ni_unlock(struct ntfs_inode *ni) { - mutex_unlock(&ni->ni_lock); + mutex_unlock(&ni->base->ni_lock); } static inline int ni_trylock(struct ntfs_inode *ni) { - return mutex_trylock(&ni->ni_lock); + return mutex_trylock(&ni->base->ni_lock); } static inline int attr_load_runs_attr(struct ntfs_inode *ni, diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c index 32bdb034c2a3..4f12ce15b03b 100644 --- a/fs/ntfs3/record.c +++ b/fs/ntfs3/record.c @@ -202,7 +202,7 @@ struct ATTRIB *mi_enum_attr(struct ntfs_inode *ni, struct mft_inode *mi, u32 used = le32_to_cpu(rec->used); u32 t32, off, asize, prev_type; u16 t16; - u64 data_size, alloc_size, tot_size; + u64 svcn, evcn, data_size, alloc_size, tot_size; if (!attr) { u32 total = le32_to_cpu(rec->total); @@ -310,10 +310,38 @@ struct ATTRIB *mi_enum_attr(struct ntfs_inode *ni, struct mft_inode *mi, if (t32 && le16_to_cpu(attr->name_off) + t32 > t16) goto out; - /* Check start/end vcn. */ - if (le64_to_cpu(attr->nres.svcn) > le64_to_cpu(attr->nres.evcn) + 1) + /* + * Check start/end vcn. svcn == 0 with evcn == -1 (U64_MAX) is the + * sentinel for an empty non-resident attribute (no allocated + * clusters) and must be accepted: "svcn > evcn + 1" tolerates it, + * since "(u64)-1 + 1" is 0 and "0 > 0" is false. + * + * For a non-empty attribute evcn is a cluster index and must lie + * within the volume (sbi->used.bitmap.nbits, set up in + * ntfs_init_from_boot() before any caller of mi_enum_attr() runs). + * Bounding evcn also prevents a malformed value close to U64_MAX + * from slipping through the near-wrap "evcn + 1" upper bound. + */ + svcn = le64_to_cpu(attr->nres.svcn); + evcn = le64_to_cpu(attr->nres.evcn); + if (svcn > evcn + 1) goto out; + if (is_attr_ext(attr)) { + /* sparsed/compressed attribute. */ +#ifdef CONFIG_NTFS3_64BIT_CLUSTER + /* No limits. */ +#else + /* Check evcn fits into 32 bits. */ + if (evcn != U64_MAX && evcn >= (1ull << 32)) + goto out; +#endif + } else { + /* Check out of volume for normal attribute. */ + if (evcn != U64_MAX && evcn >= mi->sbi->used.bitmap.nbits) + goto out; + } + data_size = le64_to_cpu(attr->nres.data_size); if (le64_to_cpu(attr->nres.valid_size) > data_size) goto out; diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 3ebf0154eda3..6e3ef89fc666 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1265,8 +1265,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Return the highest vcn from a mapping pairs array * it used while replaying log file. */ -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, - u64 *highest_vcn) +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn) { const u8 *run_last = run_buf + run_buf_size; u64 vcn64 = vcn; @@ -1279,7 +1279,7 @@ int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, if (size_size > 8 || offset_size > 8) return -EINVAL; - if (run_buf + size_size + offset_size > run_last) + if (run_buf + size_size + offset_size > run_last) return -EINVAL; len = run_unpack_s64(run_buf, size_size, 0); @@ -1357,7 +1357,8 @@ bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done) if (r_end > end) { /* Remove a middle part, split. */ CLST tail_lcn = r->lcn == SPARSE_LCN ? - SPARSE_LCN : (r->lcn + (end - r->vcn)); + SPARSE_LCN : + (r->lcn + (end - r->vcn)); *done += len; r->len = d; diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 3305fe406cb2..f4a42a0c73a4 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -23,6 +23,7 @@ * allocated_size - Total size of clusters allocated for non-resident content * total_size - Actual size of allocated clusters for sparse or compressed attributes * - Constraint: valid_size <= data_size <= allocated_size + * ADS - Alternative data stream: Named data attribute (0x80) * * WSL - Windows Subsystem for Linux * https://docs.microsoft.com/en-us/windows/wsl/file-permissions @@ -65,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -271,6 +273,8 @@ enum Opt { Opt_nocase, Opt_delalloc, Opt_delalloc_bool, + Opt_ads, + Opt_ads_bool, Opt_err, }; @@ -297,6 +301,8 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { fsparam_flag("nocase", Opt_nocase), fsparam_flag("delalloc", Opt_delalloc), fsparam_bool("delalloc", Opt_delalloc_bool), + fsparam_flag("ads", Opt_ads), + fsparam_bool("ads", Opt_ads_bool), {} }; // clang-format on @@ -420,6 +426,12 @@ static int ntfs_fs_parse_param(struct fs_context *fc, case Opt_delalloc_bool: opts->delalloc = result.boolean; break; + case Opt_ads: + opts->ads = 1; + break; + case Opt_ads_bool: + opts->ads = result.boolean; + break; default: /* Should not be here unless we forget add case. */ return -EINVAL; @@ -791,6 +803,8 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) seq_puts(m, ",nocase"); if (opts->delalloc) seq_puts(m, ",delalloc"); + if (opts->ads) + seq_puts(m, ",ads"); return 0; } @@ -957,7 +971,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, struct ntfs_sb_info *sbi = sb->s_fs_info; int err; u32 mb, gb, boot_sector_size, sct_per_clst, record_size; - u64 sectors, clusters, mlcn, mlcn2, dev_size0; + u64 sectors, clusters, mlcn, mlcn2, mft_pos, mft2_pos, dev_size0; struct NTFS_BOOT *boot; struct buffer_head *bh; struct MFT_REC *rec; @@ -1026,7 +1040,15 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, mlcn2 = le64_to_cpu(boot->mft2_clst); sectors = le64_to_cpu(boot->sectors_per_volume); - if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) { + /* + * Convert mlcn/mlcn2 to sector positions before comparing with + * 'sectors'. All three are u64 values that come from the boot + * sector, so use check_mul_overflow() to keep a wraparound from + * silently bypassing the comparison. + */ + if (check_mul_overflow(mlcn, (u64)sct_per_clst, &mft_pos) || + check_mul_overflow(mlcn2, (u64)sct_per_clst, &mft2_pos) || + mft_pos >= sectors || mft2_pos >= sectors) { ntfs_err( sb, "%s: start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.", @@ -1189,7 +1211,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, #ifdef CONFIG_NTFS3_64BIT_CLUSTER if (clusters >= (1ull << (64 - cluster_bits))) sbi->maxbytes = -1; - sbi->maxbytes_sparse = -1; + sbi->maxbytes_sparse = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE; #else /* Maximum size for sparse file. */ @@ -1458,7 +1480,10 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) Add2Ptr(a, roff), le32_to_cpu(a->size) - roff); if (err < 0) { - ntfs_err(sb, "Failed to unpack $MFT bitmap extent (%d).", err); + ntfs_err( + sb, + "Failed to unpack $MFT bitmap extent (%d).", + err); goto put_inode_out; } err = 0; @@ -1866,9 +1891,9 @@ static int ntfs_init_fs_context(struct fs_context *fc) /* Default options. */ opts->fs_uid = current_uid(); opts->fs_gid = current_gid(); - opts->fs_fmask_inv = ~current_umask(); - opts->fs_dmask_inv = ~current_umask(); + opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask(); opts->prealloc = 1; + opts->ads = 1; #ifdef CONFIG_NTFS3_FS_POSIX_ACL /* Set the default value 'acl' */ @@ -1928,7 +1953,6 @@ static struct file_system_type ntfs_fs_type = { .kill_sb = ntfs3_kill_sb, .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; - // clang-format on static int __init init_ntfs_fs(void) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 04814dd29375..594ef6860b93 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -146,26 +146,29 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, for (off = 0; off < size; off += ea_size) { const struct EA_FULL *ef = Add2Ptr(ea_p, off); u32 bytes = size - off; + size_t need; /* Check if we can use field ea->size. */ if (bytes < sizeof(ef->size)) goto out1; - if (ef->size) { - ea_size = le32_to_cpu(ef->size); - if (ea_size > bytes) - goto out1; - continue; - } - /* Check if we can use fields ef->name_len and ef->elength. */ if (bytes < offsetof(struct EA_FULL, name)) goto out1; - ea_size = ALIGN(struct_size(ef, name, - 1 + ef->name_len + - le16_to_cpu(ef->elength)), - 4); + /* Size needed to hold this record's name and value. */ + need = struct_size(ef, name, + 1 + ef->name_len + le16_to_cpu(ef->elength)); + + if (ef->size) { + ea_size = le32_to_cpu(ef->size); + /* ef->size must fit the list and cover the record. */ + if (ea_size > bytes || ea_size < need) + goto out1; + continue; + } + + ea_size = ALIGN(need, 4); if (ea_size > bytes) goto out1; } @@ -660,7 +663,6 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, inode->i_mode = old_mode; goto out; } - inode->i_mode = mode; } set_cached_acl(inode, type, acl); inode_set_ctime_current(inode);