Skip to content

Commit 5b7c893

Browse files
committed
Pull ntfs3 updates from Konstantin Komarov: "New: - implement fallocate for compressed files - add support for the compression attribute - optimize large writes to sparse files Fixes: - fix several potential deadlock scenarios - fix various internal bugs detected by syzbot - add checks before accessing NTFS structures during parsing - correct the format of output messages Refactoring: - replace fsparam_flag_no with fsparam_flag in options parser - remove unused functions and macros" * tag 'ntfs3_for_6.12' of https://github.com/Paragon-Software-Group/linux-ntfs3: (25 commits) fs/ntfs3: Format output messages like others fs in kernel fs/ntfs3: Additional check in ntfs_file_release fs/ntfs3: Fix general protection fault in run_is_mapped_full fs/ntfs3: Sequential field availability check in mi_enum_attr() fs/ntfs3: Additional check in ni_clear() fs/ntfs3: Fix possible deadlock in mi_read ntfs3: Change to non-blocking allocation in ntfs_d_hash fs/ntfs3: Remove unused al_delete_le fs/ntfs3: Rename ntfs3_setattr into ntfs_setattr fs/ntfs3: Replace fsparam_flag_no -> fsparam_flag fs/ntfs3: Add support for the compression attribute fs/ntfs3: Implement fallocate for compressed files fs/ntfs3: Make checks in run_unpack more clear fs/ntfs3: Add rough attr alloc_size check fs/ntfs3: Stale inode instead of bad fs/ntfs3: Refactor enum_rstbl to suppress static checker fs/ntfs3: Fix sparse warning in ni_fiemap fs/ntfs3: Fix warning possible deadlock in ntfs_set_state fs/ntfs3: Fix sparse warning for bigendian fs/ntfs3: Separete common code for file_read/write iter/splice ...
2 parents b2760b8 + 48dbc12 commit 5b7c893

14 files changed

Lines changed: 410 additions & 197 deletions

File tree

fs/ntfs3/attrib.c

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -976,15 +976,17 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
976976
goto out;
977977

978978
/* Check for compressed frame. */
979-
err = attr_is_frame_compressed(ni, attr, vcn >> NTFS_LZNT_CUNIT, &hint);
979+
err = attr_is_frame_compressed(ni, attr_b, vcn >> NTFS_LZNT_CUNIT,
980+
&hint);
980981
if (err)
981982
goto out;
982983

983984
if (hint) {
984985
/* if frame is compressed - don't touch it. */
985986
*lcn = COMPRESSED_LCN;
986-
*len = hint;
987-
err = -EOPNOTSUPP;
987+
/* length to the end of frame. */
988+
*len = NTFS_LZNT_CLUSTERS - (vcn & (NTFS_LZNT_CLUSTERS - 1));
989+
err = 0;
988990
goto out;
989991
}
990992

@@ -1027,16 +1029,16 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
10271029

10281030
/* Check if 'vcn' and 'vcn0' in different attribute segments. */
10291031
if (vcn < svcn || evcn1 <= vcn) {
1030-
/* Load attribute for truncated vcn. */
1031-
attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0,
1032-
&vcn, &mi);
1033-
if (!attr) {
1032+
struct ATTRIB *attr2;
1033+
/* Load runs for truncated vcn. */
1034+
attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
1035+
0, &vcn, &mi);
1036+
if (!attr2) {
10341037
err = -EINVAL;
10351038
goto out;
10361039
}
1037-
svcn = le64_to_cpu(attr->nres.svcn);
1038-
evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
1039-
err = attr_load_runs(attr, ni, run, NULL);
1040+
evcn1 = le64_to_cpu(attr2->nres.evcn) + 1;
1041+
err = attr_load_runs(attr2, ni, run, NULL);
10401042
if (err)
10411043
goto out;
10421044
}
@@ -1517,6 +1519,9 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
15171519

15181520
/*
15191521
* attr_is_frame_compressed - Used to detect compressed frame.
1522+
*
1523+
* attr - base (primary) attribute segment.
1524+
* Only base segments contains valid 'attr->nres.c_unit'
15201525
*/
15211526
int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr,
15221527
CLST frame, CLST *clst_data)
@@ -2600,3 +2605,74 @@ int attr_force_nonresident(struct ntfs_inode *ni)
26002605

26012606
return err;
26022607
}
2608+
2609+
/*
2610+
* Change the compression of data attribute
2611+
*/
2612+
int attr_set_compress(struct ntfs_inode *ni, bool compr)
2613+
{
2614+
struct ATTRIB *attr;
2615+
struct mft_inode *mi;
2616+
2617+
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi);
2618+
if (!attr)
2619+
return -ENOENT;
2620+
2621+
if (is_attr_compressed(attr) == !!compr) {
2622+
/* Already required compressed state. */
2623+
return 0;
2624+
}
2625+
2626+
if (attr->non_res) {
2627+
u16 run_off;
2628+
u32 run_size;
2629+
char *run;
2630+
2631+
if (attr->nres.data_size) {
2632+
/*
2633+
* There are rare cases when it possible to change
2634+
* compress state without big changes.
2635+
* TODO: Process these cases.
2636+
*/
2637+
return -EOPNOTSUPP;
2638+
}
2639+
2640+
run_off = le16_to_cpu(attr->nres.run_off);
2641+
run_size = le32_to_cpu(attr->size) - run_off;
2642+
run = Add2Ptr(attr, run_off);
2643+
2644+
if (!compr) {
2645+
/* remove field 'attr->nres.total_size'. */
2646+
memmove(run - 8, run, run_size);
2647+
run_off -= 8;
2648+
}
2649+
2650+
if (!mi_resize_attr(mi, attr, compr ? +8 : -8)) {
2651+
/*
2652+
* Ignore rare case when there are no 8 bytes in record with attr.
2653+
* TODO: split attribute.
2654+
*/
2655+
return -EOPNOTSUPP;
2656+
}
2657+
2658+
if (compr) {
2659+
/* Make a gap for 'attr->nres.total_size'. */
2660+
memmove(run + 8, run, run_size);
2661+
run_off += 8;
2662+
attr->nres.total_size = attr->nres.alloc_size;
2663+
}
2664+
attr->nres.run_off = cpu_to_le16(run_off);
2665+
}
2666+
2667+
/* Update data attribute flags. */
2668+
if (compr) {
2669+
attr->flags |= ATTR_FLAG_COMPRESSED;
2670+
attr->nres.c_unit = NTFS_LZNT_CUNIT;
2671+
} else {
2672+
attr->flags &= ~ATTR_FLAG_COMPRESSED;
2673+
attr->nres.c_unit = 0;
2674+
}
2675+
mi->dirty = true;
2676+
2677+
return 0;
2678+
}

fs/ntfs3/attrlist.c

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -382,59 +382,6 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
382382
return true;
383383
}
384384

385-
/*
386-
* al_delete_le - Delete first le from the list which matches its parameters.
387-
*/
388-
bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
389-
const __le16 *name, u8 name_len, const struct MFT_REF *ref)
390-
{
391-
u16 size;
392-
struct ATTR_LIST_ENTRY *le;
393-
size_t off;
394-
typeof(ni->attr_list) *al = &ni->attr_list;
395-
396-
/* Scan forward to the first le that matches the input. */
397-
le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
398-
if (!le)
399-
return false;
400-
401-
off = PtrOffset(al->le, le);
402-
403-
next:
404-
if (off >= al->size)
405-
return false;
406-
if (le->type != type)
407-
return false;
408-
if (le->name_len != name_len)
409-
return false;
410-
if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
411-
ni->mi.sbi->upcase, true))
412-
return false;
413-
if (le64_to_cpu(le->vcn) != vcn)
414-
return false;
415-
416-
/*
417-
* The caller specified a segment reference, so we have to
418-
* scan through the matching entries until we find that segment
419-
* reference or we run of matching entries.
420-
*/
421-
if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
422-
off += le16_to_cpu(le->size);
423-
le = Add2Ptr(al->le, off);
424-
goto next;
425-
}
426-
427-
/* Save on stack the size of 'le'. */
428-
size = le16_to_cpu(le->size);
429-
/* Delete the le. */
430-
memmove(le, Add2Ptr(le, size), al->size - (off + size));
431-
432-
al->size -= size;
433-
al->dirty = true;
434-
435-
return true;
436-
}
437-
438385
int al_update(struct ntfs_inode *ni, int sync)
439386
{
440387
int err;

0 commit comments

Comments
 (0)