diff options
Diffstat (limited to 'fs/ntfs3/frecord.c')
| -rw-r--r-- | fs/ntfs3/frecord.c | 475 |
1 files changed, 189 insertions, 286 deletions
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 16bd9faa2d28..641ddaf8d4a0 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -75,7 +75,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni) { const struct ATTRIB *attr; - attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL); + attr = mi_find_attr(ni, &ni->mi, NULL, ATTR_STD, NULL, 0, NULL); return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO)) : NULL; } @@ -89,7 +89,7 @@ struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni) { const struct ATTRIB *attr; - attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL); + attr = mi_find_attr(ni, &ni->mi, NULL, ATTR_STD, NULL, 0, NULL); return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO5)) : NULL; @@ -102,7 +102,9 @@ void ni_clear(struct ntfs_inode *ni) { struct rb_node *node; - if (!ni->vfs_inode.i_nlink && ni->mi.mrec && is_rec_inuse(ni->mi.mrec)) + if (!ni->vfs_inode.i_nlink && ni->mi.mrec && + is_rec_inuse(ni->mi.mrec) && + !(ni->mi.sbi->flags & NTFS_FLAGS_LOG_REPLAYING)) ni_delete_all(ni); al_destroy(ni); @@ -122,10 +124,10 @@ void ni_clear(struct ntfs_inode *ni) else { run_close(&ni->file.run); #ifdef CONFIG_NTFS3_LZX_XPRESS - if (ni->file.offs_page) { + if (ni->file.offs_folio) { /* On-demand allocated page for offsets. */ - put_page(ni->file.offs_page); - ni->file.offs_page = NULL; + folio_put(ni->file.offs_folio); + ni->file.offs_folio = NULL; } #endif } @@ -146,8 +148,10 @@ int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) goto out; err = mi_get(ni->mi.sbi, rno, &r); - if (err) + if (err) { + _ntfs_bad_inode(&ni->vfs_inode); return err; + } ni_add_mi(ni, r); @@ -199,7 +203,8 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, *mi = &ni->mi; /* Look for required attribute in primary record. */ - return mi_find_attr(&ni->mi, attr, type, name, name_len, NULL); + return mi_find_attr(ni, &ni->mi, attr, type, name, name_len, + NULL); } /* First look for list entry of required type. */ @@ -215,7 +220,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, return NULL; /* Look for required attribute. */ - attr = mi_find_attr(m, NULL, type, name, name_len, &le->id); + attr = mi_find_attr(ni, m, NULL, type, name, name_len, &le->id); if (!attr) goto out; @@ -236,8 +241,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, return attr; out: - ntfs_inode_err(&ni->vfs_inode, "failed to parse mft record"); - ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR); + _ntfs_bad_inode(&ni->vfs_inode); return NULL; } @@ -257,7 +261,7 @@ struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, if (mi) *mi = &ni->mi; /* Enum attributes in primary record. */ - return mi_enum_attr(&ni->mi, attr); + return mi_enum_attr(ni, &ni->mi, attr); } /* Get next list entry. */ @@ -273,62 +277,7 @@ struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, *mi = mi2; /* Find attribute in loaded record. */ - return rec_find_attr_le(mi2, le2); -} - -/* - * ni_load_attr - Load attribute that contains given VCN. - */ -struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, - const __le16 *name, u8 name_len, CLST vcn, - struct mft_inode **pmi) -{ - struct ATTR_LIST_ENTRY *le; - struct ATTRIB *attr; - struct mft_inode *mi; - struct ATTR_LIST_ENTRY *next; - - if (!ni->attr_list.size) { - if (pmi) - *pmi = &ni->mi; - return mi_find_attr(&ni->mi, NULL, type, name, name_len, NULL); - } - - le = al_find_ex(ni, NULL, type, name, name_len, NULL); - if (!le) - return NULL; - - /* - * Unfortunately ATTR_LIST_ENTRY contains only start VCN. - * So to find the ATTRIB segment that contains 'vcn' we should - * enumerate some entries. - */ - if (vcn) { - for (;; le = next) { - next = al_find_ex(ni, le, type, name, name_len, NULL); - if (!next || le64_to_cpu(next->vcn) > vcn) - break; - } - } - - if (ni_load_mi(ni, le, &mi)) - return NULL; - - if (pmi) - *pmi = mi; - - attr = mi_find_attr(mi, NULL, type, name, name_len, &le->id); - if (!attr) - return NULL; - - if (!attr->non_res) - return attr; - - if (le64_to_cpu(attr->nres.svcn) <= vcn && - vcn <= le64_to_cpu(attr->nres.evcn)) - return attr; - - return NULL; + return rec_find_attr_le(ni, mi2, le2); } /* @@ -376,8 +325,10 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) mi_get_ref(&ni->mi, &m->mrec->parent_ref); - ni_add_mi(ni, m); - *mi = m; + *mi = ni_ins_mi(ni, &ni->mi_tree, m->rno, &m->node); + if (*mi != m) + mi_put(m); + return true; } @@ -396,7 +347,8 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, int diff; if (base_only || type == ATTR_LIST || !ni->attr_list.size) { - attr = mi_find_attr(&ni->mi, NULL, type, name, name_len, id); + attr = mi_find_attr(ni, &ni->mi, NULL, type, name, name_len, + id); if (!attr) return -ENOENT; @@ -435,7 +387,7 @@ next_le2: al_remove_le(ni, le); - attr = mi_find_attr(mi, NULL, type, name, name_len, id); + attr = mi_find_attr(ni, mi, NULL, type, name, name_len, id); if (!attr) return -ENOENT; @@ -483,7 +435,7 @@ ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi, name = le->name; } - attr = mi_insert_attr(mi, type, name, name_len, asize, name_off); + attr = mi_insert_attr(ni, mi, type, name, name_len, asize, name_off); if (!attr) { if (le_added) al_remove_le(ni, le); @@ -671,7 +623,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) if (err) return err; - attr_list = mi_find_attr(&ni->mi, NULL, ATTR_LIST, NULL, 0, NULL); + attr_list = mi_find_attr(ni, &ni->mi, NULL, ATTR_LIST, NULL, 0, NULL); if (!attr_list) return 0; @@ -693,7 +645,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) if (!mi) return 0; - attr = mi_find_attr(mi, NULL, le->type, le_name(le), + attr = mi_find_attr(ni, mi, NULL, le->type, le_name(le), le->name_len, &le->id); if (!attr) return 0; @@ -729,7 +681,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) goto out; } - attr = mi_find_attr(mi, NULL, le->type, le_name(le), + attr = mi_find_attr(ni, mi, NULL, le->type, le_name(le), le->name_len, &le->id); if (!attr) { /* Should never happened, 'cause already checked. */ @@ -738,7 +690,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) asize = le32_to_cpu(attr->size); /* Insert into primary record. */ - attr_ins = mi_insert_attr(&ni->mi, le->type, le_name(le), + attr_ins = mi_insert_attr(ni, &ni->mi, le->type, le_name(le), le->name_len, asize, le16_to_cpu(attr->name_off)); if (!attr_ins) { @@ -766,7 +718,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) if (!mi) continue; - attr = mi_find_attr(mi, NULL, le->type, le_name(le), + attr = mi_find_attr(ni, mi, NULL, le->type, le_name(le), le->name_len, &le->id); if (!attr) continue; @@ -778,7 +730,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) run_deallocate(sbi, &ni->attr_list.run, true); run_close(&ni->attr_list.run); ni->attr_list.size = 0; - kfree(ni->attr_list.le); + kvfree(ni->attr_list.le); ni->attr_list.le = NULL; ni->attr_list.dirty = false; @@ -817,7 +769,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) * Skip estimating exact memory requirement. * Looks like one record_size is always enough. */ - le = kmalloc(al_aligned(rs), GFP_NOFS); + le = kzalloc(al_aligned(rs), GFP_NOFS); if (!le) return -ENOMEM; @@ -829,7 +781,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) free_b = 0; attr = NULL; - for (; (attr = mi_enum_attr(&ni->mi, attr)); le = Add2Ptr(le, sz)) { + for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) { sz = le_size(attr->name_len); le->type = attr->type; le->size = cpu_to_le16(sz); @@ -884,7 +836,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) u32 asize = le32_to_cpu(b->size); u16 name_off = le16_to_cpu(b->name_off); - attr = mi_insert_attr(mi, b->type, Add2Ptr(b, name_off), + attr = mi_insert_attr(ni, mi, b->type, Add2Ptr(b, name_off), b->name_len, asize, name_off); if (!attr) goto out; @@ -907,7 +859,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) goto out; } - attr = mi_insert_attr(&ni->mi, ATTR_LIST, NULL, 0, + attr = mi_insert_attr(ni, &ni->mi, ATTR_LIST, NULL, 0, lsize + SIZEOF_RESIDENT, SIZEOF_RESIDENT); if (!attr) goto out; @@ -927,7 +879,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) return 0; out: - kfree(ni->attr_list.le); + kvfree(ni->attr_list.le); ni->attr_list.le = NULL; ni->attr_list.size = 0; return err; @@ -991,13 +943,13 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, mi = rb_entry(node, struct mft_inode, node); if (is_mft_data && - (mi_enum_attr(mi, NULL) || + (mi_enum_attr(ni, mi, NULL) || vbo <= ((u64)mi->rno << sbi->record_bits))) { /* We can't accept this record 'cause MFT's bootstrapping. */ continue; } if (is_mft && - mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, NULL)) { + mi_find_attr(ni, mi, NULL, ATTR_DATA, NULL, 0, NULL)) { /* * This child record already has a ATTR_DATA. * So it can't accept any other records. @@ -1006,7 +958,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, } if ((type != ATTR_NAME || name_len) && - mi_find_attr(mi, NULL, type, name, name_len, NULL)) { + mi_find_attr(ni, mi, NULL, type, name, name_len, NULL)) { /* Only indexed attributes can share same record. */ continue; } @@ -1065,9 +1017,9 @@ insert_ext: out2: ni_remove_mi(ni, mi); - mi_put(mi); out1: + mi_put(mi); ntfs_mark_rec_free(sbi, rno, is_mft); out: @@ -1155,7 +1107,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, /* Estimate the result of moving all possible attributes away. */ attr = NULL; - while ((attr = mi_enum_attr(&ni->mi, attr))) { + while ((attr = mi_enum_attr(ni, &ni->mi, attr))) { if (attr->type == ATTR_STD) continue; if (attr->type == ATTR_LIST) @@ -1173,7 +1125,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, attr = NULL; for (;;) { - attr = mi_enum_attr(&ni->mi, attr); + attr = mi_enum_attr(ni, &ni->mi, attr); if (!attr) { /* We should never be here 'cause we have already check this case. */ err = -EINVAL; @@ -1257,7 +1209,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) { mi = rb_entry(node, struct mft_inode, node); - attr = mi_enum_attr(mi, NULL); + attr = mi_enum_attr(ni, mi, NULL); if (!attr) { mft_min = mi->rno; @@ -1278,7 +1230,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) ni_remove_mi(ni, mi_new); } - attr = mi_find_attr(&ni->mi, NULL, ATTR_DATA, NULL, 0, NULL); + attr = mi_find_attr(ni, &ni->mi, NULL, ATTR_DATA, NULL, 0, NULL); if (!attr) { err = -EINVAL; goto out; @@ -1395,7 +1347,7 @@ int ni_expand_list(struct ntfs_inode *ni) continue; /* Find attribute in primary record. */ - attr = rec_find_attr_le(&ni->mi, le); + attr = rec_find_attr_le(ni, &ni->mi, le); if (!attr) { err = -EINVAL; goto out; @@ -1501,7 +1453,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, if (is_ext) { if (flags & ATTR_FLAG_COMPRESSED) - attr->nres.c_unit = COMPRESSION_UNIT; + attr->nres.c_unit = NTFS_LZNT_CUNIT; attr->nres.total_size = attr->nres.alloc_size; } @@ -1601,8 +1553,10 @@ int ni_delete_all(struct ntfs_inode *ni) asize = le32_to_cpu(attr->size); roff = le16_to_cpu(attr->nres.run_off); - if (roff > asize) - return -EINVAL; + if (roff > asize) { + /* ni_enum_attr_ex checks this case. */ + continue; + } /* run==1 means unpack and deallocate. */ run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn, @@ -1908,8 +1862,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, int err = 0; struct ntfs_sb_info *sbi = ni->mi.sbi; u8 cluster_bits = sbi->cluster_bits; - struct runs_tree *run; - struct rw_semaphore *run_lock; + struct runs_tree run; struct ATTRIB *attr; CLST vcn = vbo >> cluster_bits; CLST lcn, clen; @@ -1920,13 +1873,11 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, u32 flags; bool ok; + run_init(&run); if (S_ISDIR(ni->vfs_inode.i_mode)) { - run = &ni->dir.alloc_run; attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME, ARRAY_SIZE(I30_NAME), NULL, NULL); - run_lock = &ni->dir.run_lock; } else { - run = &ni->file.run; attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL); if (!attr) { @@ -1941,7 +1892,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, "fiemap is not supported for compressed file (cp -r)"); goto out; } - run_lock = &ni->file.run_lock; } if (!attr || !attr->non_res) { @@ -1958,35 +1908,27 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (end > alloc_size) end = alloc_size; - down_read(run_lock); - while (vbo < end) { if (idx == -1) { - ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); + ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx); } else { CLST vcn_next = vcn; - ok = run_get_entry(run, ++idx, &vcn, &lcn, &clen) && + ok = run_get_entry(&run, ++idx, &vcn, &lcn, &clen) && vcn == vcn_next; if (!ok) vcn = vcn_next; } if (!ok) { - up_read(run_lock); - down_write(run_lock); - err = attr_load_runs_vcn(ni, attr->type, attr_name(attr), - attr->name_len, run, vcn); - - up_write(run_lock); - down_read(run_lock); + attr->name_len, &run, vcn); if (err) break; - ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); + ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx); if (!ok) { err = -EINVAL; @@ -2011,8 +1953,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, } else if (is_attr_compressed(attr)) { CLST clst_data; - err = attr_is_frame_compressed( - ni, attr, vcn >> attr->nres.c_unit, &clst_data); + err = attr_is_frame_compressed(ni, attr, + vcn >> attr->nres.c_unit, + &clst_data, &run); if (err) break; if (clst_data < NTFS_LZNT_CLUSTERS) @@ -2043,6 +1986,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen, flags); + if (err < 0) break; if (err == 1) { @@ -2073,24 +2017,46 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, vbo += bytes; } - up_read(run_lock); - out: + run_close(&run); return err; } +static struct page *ntfs_lock_new_page(struct address_space *mapping, + pgoff_t index, gfp_t gfp) +{ + struct folio *folio = __filemap_get_folio(mapping, index, + FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); + struct page *page; + + if (IS_ERR(folio)) + return ERR_CAST(folio); + + if (!folio_test_uptodate(folio)) + return folio_file_page(folio, index); + + /* Use a temporary page to avoid data corruption */ + folio_unlock(folio); + folio_put(folio); + page = alloc_page(gfp); + if (!page) + return ERR_PTR(-ENOMEM); + __SetPageLocked(page); + return page; +} + /* * ni_readpage_cmpr * * When decompressing, we typically obtain more than one page per reference. * We inject the additional pages into the page cache. */ -int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) +int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio) { int err; struct ntfs_sb_info *sbi = ni->mi.sbi; - struct address_space *mapping = page->mapping; - pgoff_t index = page->index; + struct address_space *mapping = folio->mapping; + pgoff_t index = folio->index; u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT; struct page **pages = NULL; /* Array of at most 16 pages. stack? */ u8 frame_bits; @@ -2099,8 +2065,9 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) gfp_t gfp_mask; struct page *pg; - if (vbo >= ni->vfs_inode.i_size) { - SetPageUptodate(page); + if (vbo >= i_size_read(&ni->vfs_inode)) { + folio_zero_range(folio, 0, folio_size(folio)); + folio_mark_uptodate(folio); err = 0; goto out; } @@ -2124,7 +2091,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) goto out; } - pages[idx] = page; + pages[idx] = &folio->page; index = frame_vbo >> PAGE_SHIFT; gfp_mask = mapping_gfp_mask(mapping); @@ -2132,23 +2099,20 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) if (i == idx) continue; - pg = find_or_create_page(mapping, index, gfp_mask); - if (!pg) { - err = -ENOMEM; + pg = ntfs_lock_new_page(mapping, index, gfp_mask); + if (IS_ERR(pg)) { + err = PTR_ERR(pg); goto out1; } pages[i] = pg; } - err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame); + err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0); out1: - if (err) - SetPageError(page); - for (i = 0; i < pages_per_frame; i++) { pg = pages[i]; - if (i == idx) + if (i == idx || !pg) continue; unlock_page(pg); put_page(pg); @@ -2157,7 +2121,7 @@ out1: out: /* At this point, err contains 0 or -EIO depending on the "critical" page. */ kfree(pages); - unlock_page(page); + folio_unlock(folio); return err; } @@ -2173,7 +2137,7 @@ int ni_decompress_file(struct ntfs_inode *ni) { struct ntfs_sb_info *sbi = ni->mi.sbi; struct inode *inode = &ni->vfs_inode; - loff_t i_size = inode->i_size; + loff_t i_size = i_size_read(inode); struct address_space *mapping = inode->i_mapping; gfp_t gfp_mask = mapping_gfp_mask(mapping); struct page **pages = NULL; @@ -2213,17 +2177,9 @@ int ni_decompress_file(struct ntfs_inode *ni) */ index = 0; for (vbo = 0; vbo < i_size; vbo += bytes) { - u32 nr_pages; bool new; - if (vbo + frame_size > i_size) { - bytes = i_size - vbo; - nr_pages = (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; - } else { - nr_pages = pages_per_frame; - bytes = frame_size; - } - + bytes = vbo + frame_size > i_size ? (i_size - vbo) : frame_size; end = bytes_to_cluster(sbi, vbo + bytes); for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) { @@ -2236,27 +2192,19 @@ int ni_decompress_file(struct ntfs_inode *ni) for (i = 0; i < pages_per_frame; i++, index++) { struct page *pg; - pg = find_or_create_page(mapping, index, gfp_mask); - if (!pg) { + pg = ntfs_lock_new_page(mapping, index, gfp_mask); + if (IS_ERR(pg)) { while (i--) { unlock_page(pages[i]); put_page(pages[i]); } - err = -ENOMEM; + err = PTR_ERR(pg); goto out; } pages[i] = pg; } - err = ni_read_frame(ni, vbo, pages, pages_per_frame); - - if (!err) { - down_read(&ni->file.run_lock); - err = ntfs_bio_pages(sbi, &ni->file.run, pages, - nr_pages, vbo, bytes, - REQ_OP_WRITE); - up_read(&ni->file.run_lock); - } + err = ni_read_frame(ni, vbo, pages, pages_per_frame, 1); for (i = 0; i < pages_per_frame; i++) { unlock_page(pages[i]); @@ -2362,9 +2310,9 @@ remove_wof: /* Clear cached flag. */ ni->ni_flags &= ~NI_FLAG_COMPRESSED_MASK; - if (ni->file.offs_page) { - put_page(ni->file.offs_page); - ni->file.offs_page = NULL; + if (ni->file.offs_folio) { + folio_put(ni->file.offs_folio); + ni->file.offs_folio = NULL; } mapping->a_ops = &ntfs_aops; @@ -2446,20 +2394,19 @@ out2: * Pages - Array of locked pages. */ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, - u32 pages_per_frame) + u32 pages_per_frame, int copy) { int err; struct ntfs_sb_info *sbi = ni->mi.sbi; u8 cluster_bits = sbi->cluster_bits; char *frame_ondisk = NULL; char *frame_mem = NULL; - struct page **pages_disk = NULL; struct ATTR_LIST_ENTRY *le = NULL; struct runs_tree *run = &ni->file.run; u64 valid_size = ni->i_valid; u64 vbo_disk; size_t unc_size; - u32 frame_size, i, npages_disk, ondisk_size; + u32 frame_size, i, ondisk_size; struct page *pg; struct ATTRIB *attr; CLST frame, clst_data; @@ -2468,9 +2415,6 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, * To simplify decompress algorithm do vmap for source * and target pages. */ - for (i = 0; i < pages_per_frame; i++) - kmap(pages[i]); - frame_size = pages_per_frame << PAGE_SHIFT; frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL); if (!frame_mem) { @@ -2508,6 +2452,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, err = -EOPNOTSUPP; goto out1; #else + loff_t i_size = i_size_read(&ni->vfs_inode); u32 frame_bits = ni_ext_compress_bits(ni); u64 frame64 = frame_vbo >> frame_bits; u64 frames, vbo_data; @@ -2548,16 +2493,15 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, } } - frames = (ni->vfs_inode.i_size - 1) >> frame_bits; + frames = (i_size - 1) >> frame_bits; err = attr_wof_frame_info(ni, attr, run, frame64, frames, frame_bits, &ondisk_size, &vbo_data); if (err) - goto out2; + goto out1; if (frame64 == frames) { - unc_size = 1 + ((ni->vfs_inode.i_size - 1) & - (frame_size - 1)); + unc_size = 1 + ((i_size - 1) & (frame_size - 1)); ondisk_size = attr_size(attr) - vbo_data; } else { unc_size = frame_size; @@ -2565,7 +2509,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, if (ondisk_size > frame_size) { err = -EINVAL; - goto out2; + goto out1; } if (!attr->non_res) { @@ -2586,10 +2530,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, ARRAY_SIZE(WOF_NAME), run, vbo_disk, vbo_data + ondisk_size); if (err) - goto out2; - npages_disk = (ondisk_size + (vbo_disk & (PAGE_SIZE - 1)) + - PAGE_SIZE - 1) >> - PAGE_SHIFT; + goto out1; #endif } else if (is_attr_compressed(attr)) { /* LZNT compression. */ @@ -2606,7 +2547,8 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, down_write(&ni->file.run_lock); run_truncate_around(run, le64_to_cpu(attr->nres.svcn)); frame = frame_vbo >> (cluster_bits + NTFS_LZNT_CUNIT); - err = attr_is_frame_compressed(ni, attr, frame, &clst_data); + err = attr_is_frame_compressed(ni, attr, frame, &clst_data, + run); up_write(&ni->file.run_lock); if (err) goto out1; @@ -2622,61 +2564,37 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, if (clst_data >= NTFS_LZNT_CLUSTERS) { /* Frame is not compressed. */ down_read(&ni->file.run_lock); - err = ntfs_bio_pages(sbi, run, pages, pages_per_frame, - frame_vbo, ondisk_size, - REQ_OP_READ); + err = ntfs_read_run(sbi, run, frame_mem, frame_vbo, + ondisk_size); up_read(&ni->file.run_lock); goto out1; } vbo_disk = frame_vbo; - npages_disk = (ondisk_size + PAGE_SIZE - 1) >> PAGE_SHIFT; } else { __builtin_unreachable(); err = -EINVAL; goto out1; } - pages_disk = kzalloc(npages_disk * sizeof(struct page *), GFP_NOFS); - if (!pages_disk) { + /* Allocate memory to read compressed data to. */ + frame_ondisk = kvmalloc(ondisk_size, GFP_KERNEL); + if (!frame_ondisk) { err = -ENOMEM; - goto out2; - } - - for (i = 0; i < npages_disk; i++) { - pg = alloc_page(GFP_KERNEL); - if (!pg) { - err = -ENOMEM; - goto out3; - } - pages_disk[i] = pg; - lock_page(pg); - kmap(pg); + goto out1; } /* Read 'ondisk_size' bytes from disk. */ down_read(&ni->file.run_lock); - err = ntfs_bio_pages(sbi, run, pages_disk, npages_disk, vbo_disk, - ondisk_size, REQ_OP_READ); + err = ntfs_read_run(sbi, run, frame_ondisk, vbo_disk, ondisk_size); up_read(&ni->file.run_lock); if (err) - goto out3; - - /* - * To simplify decompress algorithm do vmap for source and target pages. - */ - frame_ondisk = vmap(pages_disk, npages_disk, VM_MAP, PAGE_KERNEL_RO); - if (!frame_ondisk) { - err = -ENOMEM; - goto out3; - } + goto out2; - /* Decompress: Frame_ondisk -> frame_mem. */ #ifdef CONFIG_NTFS3_LZX_XPRESS if (run != &ni->file.run) { /* LZX or XPRESS */ - err = decompress_lzx_xpress( - sbi, frame_ondisk + (vbo_disk & (PAGE_SIZE - 1)), - ondisk_size, frame_mem, unc_size, frame_size); + err = decompress_lzx_xpress(sbi, frame_ondisk, ondisk_size, + frame_mem, unc_size, frame_size); } else #endif { @@ -2694,31 +2612,25 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, memset(frame_mem + ok, 0, frame_size - ok); } - vunmap(frame_ondisk); - -out3: - for (i = 0; i < npages_disk; i++) { - pg = pages_disk[i]; - if (pg) { - kunmap(pg); - unlock_page(pg); - put_page(pg); - } - } - kfree(pages_disk); - out2: + kvfree(frame_ondisk); +out1: #ifdef CONFIG_NTFS3_LZX_XPRESS if (run != &ni->file.run) run_free(run); + if (!err && copy) { + /* We are called from 'ni_decompress_file' */ + /* Copy decompressed LZX or XPRESS data into new place. */ + down_read(&ni->file.run_lock); + err = ntfs_write_run(sbi, &ni->file.run, frame_mem, frame_vbo, + frame_size); + up_read(&ni->file.run_lock); + } #endif -out1: vunmap(frame_mem); out: for (i = 0; i < pages_per_frame; i++) { pg = pages[i]; - kunmap(pg); - ClearPageError(pg); SetPageUptodate(pg); } @@ -2735,18 +2647,16 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, { int err; struct ntfs_sb_info *sbi = ni->mi.sbi; + struct folio *folio = page_folio(pages[0]); u8 frame_bits = NTFS_LZNT_CUNIT + sbi->cluster_bits; u32 frame_size = sbi->cluster_size << NTFS_LZNT_CUNIT; - u64 frame_vbo = (u64)pages[0]->index << PAGE_SHIFT; + u64 frame_vbo = folio_pos(folio); CLST frame = frame_vbo >> frame_bits; char *frame_ondisk = NULL; - struct page **pages_disk = NULL; struct ATTR_LIST_ENTRY *le = NULL; char *frame_mem; struct ATTRIB *attr; struct mft_inode *mi; - u32 i; - struct page *pg; size_t compr_size, ondisk_size; struct lznt *lznt; @@ -2781,38 +2691,18 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, goto out; } - pages_disk = kcalloc(pages_per_frame, sizeof(struct page *), GFP_NOFS); - if (!pages_disk) { - err = -ENOMEM; - goto out; - } - - for (i = 0; i < pages_per_frame; i++) { - pg = alloc_page(GFP_KERNEL); - if (!pg) { - err = -ENOMEM; - goto out1; - } - pages_disk[i] = pg; - lock_page(pg); - kmap(pg); - } - - /* To simplify compress algorithm do vmap for source and target pages. */ - frame_ondisk = vmap(pages_disk, pages_per_frame, VM_MAP, PAGE_KERNEL); + /* Allocate memory to write compressed data to. */ + frame_ondisk = kvmalloc(frame_size, GFP_KERNEL); if (!frame_ondisk) { err = -ENOMEM; - goto out1; + goto out; } - for (i = 0; i < pages_per_frame; i++) - kmap(pages[i]); - /* Map in-memory frame for read-only. */ frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL_RO); if (!frame_mem) { err = -ENOMEM; - goto out2; + goto out1; } mutex_lock(&sbi->compress.mtx_lznt); @@ -2828,7 +2718,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, if (!lznt) { mutex_unlock(&sbi->compress.mtx_lznt); err = -ENOMEM; - goto out3; + goto out2; } sbi->compress.lznt = lznt; @@ -2865,30 +2755,16 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, goto out2; down_read(&ni->file.run_lock); - err = ntfs_bio_pages(sbi, &ni->file.run, - ondisk_size < frame_size ? pages_disk : pages, - pages_per_frame, frame_vbo, ondisk_size, - REQ_OP_WRITE); + err = ntfs_write_run(sbi, &ni->file.run, + ondisk_size < frame_size ? frame_ondisk : + frame_mem, + frame_vbo, ondisk_size); up_read(&ni->file.run_lock); -out3: - vunmap(frame_mem); - out2: - for (i = 0; i < pages_per_frame; i++) - kunmap(pages[i]); - - vunmap(frame_ondisk); + vunmap(frame_mem); out1: - for (i = 0; i < pages_per_frame; i++) { - pg = pages_disk[i]; - if (pg) { - kunmap(pg); - unlock_page(pg); - put_page(pg); - } - } - kfree(pages_disk); + kvfree(frame_ondisk); out: return err; } @@ -3063,8 +2939,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, * ni_rename - Remove one name and insert new name. */ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, - struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de, - bool *is_bad) + struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de) { int err; struct NTFS_DE *de2 = NULL; @@ -3087,8 +2962,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, 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)) - *is_bad = true; + WARN_ON(err && + ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)); } /* @@ -3179,11 +3054,22 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, } } - /* TODO: Fill reparse info. */ - dup->reparse = 0; - dup->ea_size = 0; + dup->extend_data = 0; + + if (dup->fa & FILE_ATTRIBUTE_REPARSE_POINT) { + attr = ni_find_attr(ni, NULL, NULL, ATTR_REPARSE, NULL, 0, NULL, + NULL); + + if (attr) { + const struct REPARSE_POINT *rp; - if (ni->ni_flags & NI_FLAG_EA) { + rp = resident_data_ex(attr, + sizeof(struct REPARSE_POINT)); + /* If ATTR_REPARSE exists 'rp' can't be NULL. */ + if (rp) + dup->extend_data = rp->ReparseTag; + } + } else if (ni->ni_flags & NI_FLAG_EA) { attr = ni_find_attr(ni, attr, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL); if (attr) { @@ -3192,7 +3078,7 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, info = resident_data_ex(attr, sizeof(struct EA_INFO)); /* If ATTR_EA_INFO exists 'info' can't be NULL. */ if (info) - dup->ea_size = info->size_pack; + dup->extend_data = info->size; } } @@ -3208,6 +3094,12 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, if (!fname || !memcmp(&fname->dup, dup, sizeof(fname->dup))) continue; + /* Check simple case when parent inode equals current inode. */ + if (ino_get(&fname->home) == ni->vfs_inode.i_ino) { + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + continue; + } + /* ntfs_iget5 may sleep. */ dir = ntfs_iget5(sb, &fname->home, NULL); if (IS_ERR(dir)) { @@ -3253,6 +3145,13 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) if (is_bad_inode(inode) || sb_rdonly(sb)) return 0; + /* Avoid any operation if inode is bad. */ + if (unlikely(is_bad_ni(ni))) + return -EINVAL; + + if (unlikely(ntfs3_forced_shutdown(sb))) + return -EIO; + if (!ni_trylock(ni)) { /* 'ni' is under modification, skip for now. */ mark_inode_dirty_sync(inode); @@ -3265,6 +3164,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) if (is_rec_inuse(ni->mi.mrec) && !(sbi->flags & NTFS_FLAGS_LOG_REPLAYING) && inode->i_nlink) { bool modified = false; + struct timespec64 ts; /* Update times in standard attribute. */ std = ni_std(ni); @@ -3274,19 +3174,22 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) } /* Update the access times if they have changed. */ - dup.m_time = kernel2nt(&inode->i_mtime); + ts = inode_get_mtime(inode); + dup.m_time = kernel2nt(&ts); if (std->m_time != dup.m_time) { std->m_time = dup.m_time; modified = true; } - dup.c_time = kernel2nt(&inode->i_ctime); + ts = inode_get_ctime(inode); + dup.c_time = kernel2nt(&ts); if (std->c_time != dup.c_time) { std->c_time = dup.c_time; modified = true; } - dup.a_time = kernel2nt(&inode->i_atime); + ts = inode_get_atime(inode); + dup.a_time = kernel2nt(&ts); if (std->a_time != dup.a_time) { std->a_time = dup.a_time; modified = true; @@ -3339,7 +3242,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) if (!mi->dirty) continue; - is_empty = !mi_enum_attr(mi, NULL); + is_empty = !mi_enum_attr(ni, mi, NULL); if (is_empty) clear_rec_inuse(mi->mrec); |
