diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 3628a737d7cc..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; @@ -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 6f8ad8428f49..eb9152e9fa22 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -184,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; @@ -194,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; @@ -267,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; diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 1eda8c31b054..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; @@ -829,6 +831,22 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) 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( @@ -1421,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 a9d1fc0b812a..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; @@ -171,6 +179,7 @@ int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le, { 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,6 +779,7 @@ 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; @@ -917,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; @@ -1069,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; @@ -1215,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; @@ -1347,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; @@ -1419,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); @@ -1488,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) @@ -1519,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) @@ -1543,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) { ; @@ -1620,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; @@ -1661,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; @@ -1691,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; @@ -1772,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)); @@ -2000,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); @@ -2261,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. @@ -2519,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; @@ -2644,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. */ @@ -2706,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); @@ -2756,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; @@ -2803,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: @@ -2814,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; } @@ -2959,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; @@ -3141,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; @@ -3325,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/fsntfs.c b/fs/ntfs3/fsntfs.c index 7c4db816c43d..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); @@ -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/inode.c b/fs/ntfs3/inode.c index aca2ad950d95..4e15f7df89b1 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); @@ -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' */ @@ -144,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'. @@ -230,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; } @@ -253,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; } @@ -501,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; } /* @@ -520,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; } @@ -709,8 +908,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); @@ -804,7 +1003,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; @@ -895,7 +1095,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; @@ -1217,6 +1418,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/namei.c b/fs/ntfs3/namei.c index 937d349e841d..66eba128cc23 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -78,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. @@ -100,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); @@ -173,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. */ @@ -183,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; } @@ -273,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 6bd1a439e92f..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. */ @@ -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); @@ -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/super.c b/fs/ntfs3/super.c index 94cb57007893..ea9dde56f968 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 @@ -271,6 +272,8 @@ enum Opt { Opt_nocase, Opt_delalloc, Opt_delalloc_bool, + Opt_ads, + Opt_ads_bool, Opt_err, }; @@ -297,6 +300,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 +425,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 +802,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; } @@ -1871,6 +1884,7 @@ static int ntfs_init_fs_context(struct fs_context *fc) opts->fs_gid = current_gid(); 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' */