Skip to content

Commit

Permalink
Merge tag 'ntfs3_for_6.12' of https://github.com/Paragon-Software-Gro…
Browse files Browse the repository at this point in the history
…up/linux-ntfs3

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
  ...
  • Loading branch information
torvalds committed Oct 8, 2024
2 parents b2760b8 + 48dbc12 commit 5b7c893
Show file tree
Hide file tree
Showing 14 changed files with 410 additions and 197 deletions.
96 changes: 86 additions & 10 deletions fs/ntfs3/attrib.c
Original file line number Diff line number Diff line change
Expand Up @@ -976,15 +976,17 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
goto out;

/* Check for compressed frame. */
err = attr_is_frame_compressed(ni, attr, vcn >> NTFS_LZNT_CUNIT, &hint);
err = attr_is_frame_compressed(ni, attr_b, vcn >> NTFS_LZNT_CUNIT,
&hint);
if (err)
goto out;

if (hint) {
/* if frame is compressed - don't touch it. */
*lcn = COMPRESSED_LCN;
*len = hint;
err = -EOPNOTSUPP;
/* length to the end of frame. */
*len = NTFS_LZNT_CLUSTERS - (vcn & (NTFS_LZNT_CLUSTERS - 1));
err = 0;
goto out;
}

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

/* Check if 'vcn' and 'vcn0' in different attribute segments. */
if (vcn < svcn || evcn1 <= vcn) {
/* Load attribute for truncated vcn. */
attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0,
&vcn, &mi);
if (!attr) {
struct ATTRIB *attr2;
/* Load runs for truncated vcn. */
attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
0, &vcn, &mi);
if (!attr2) {
err = -EINVAL;
goto out;
}
svcn = le64_to_cpu(attr->nres.svcn);
evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
err = attr_load_runs(attr, ni, run, NULL);
evcn1 = le64_to_cpu(attr2->nres.evcn) + 1;
err = attr_load_runs(attr2, ni, run, NULL);
if (err)
goto out;
}
Expand Down Expand Up @@ -1517,6 +1519,9 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,

/*
* attr_is_frame_compressed - Used to detect compressed frame.
*
* attr - base (primary) attribute segment.
* Only base segments contains valid 'attr->nres.c_unit'
*/
int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr,
CLST frame, CLST *clst_data)
Expand Down Expand Up @@ -2600,3 +2605,74 @@ int attr_force_nonresident(struct ntfs_inode *ni)

return err;
}

/*
* Change the compression of data attribute
*/
int attr_set_compress(struct ntfs_inode *ni, bool compr)
{
struct ATTRIB *attr;
struct mft_inode *mi;

attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi);
if (!attr)
return -ENOENT;

if (is_attr_compressed(attr) == !!compr) {
/* Already required compressed state. */
return 0;
}

if (attr->non_res) {
u16 run_off;
u32 run_size;
char *run;

if (attr->nres.data_size) {
/*
* There are rare cases when it possible to change
* compress state without big changes.
* TODO: Process these cases.
*/
return -EOPNOTSUPP;
}

run_off = le16_to_cpu(attr->nres.run_off);
run_size = le32_to_cpu(attr->size) - run_off;
run = Add2Ptr(attr, run_off);

if (!compr) {
/* remove field 'attr->nres.total_size'. */
memmove(run - 8, run, run_size);
run_off -= 8;
}

if (!mi_resize_attr(mi, attr, compr ? +8 : -8)) {
/*
* Ignore rare case when there are no 8 bytes in record with attr.
* TODO: split attribute.
*/
return -EOPNOTSUPP;
}

if (compr) {
/* Make a gap for 'attr->nres.total_size'. */
memmove(run + 8, run, run_size);
run_off += 8;
attr->nres.total_size = attr->nres.alloc_size;
}
attr->nres.run_off = cpu_to_le16(run_off);
}

/* Update data attribute flags. */
if (compr) {
attr->flags |= ATTR_FLAG_COMPRESSED;
attr->nres.c_unit = NTFS_LZNT_CUNIT;
} else {
attr->flags &= ~ATTR_FLAG_COMPRESSED;
attr->nres.c_unit = 0;
}
mi->dirty = true;

return 0;
}
53 changes: 0 additions & 53 deletions fs/ntfs3/attrlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,59 +382,6 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
return true;
}

/*
* al_delete_le - Delete first le from the list which matches its parameters.
*/
bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
const __le16 *name, u8 name_len, const struct MFT_REF *ref)
{
u16 size;
struct ATTR_LIST_ENTRY *le;
size_t off;
typeof(ni->attr_list) *al = &ni->attr_list;

/* Scan forward to the first le that matches the input. */
le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
if (!le)
return false;

off = PtrOffset(al->le, le);

next:
if (off >= al->size)
return false;
if (le->type != type)
return false;
if (le->name_len != name_len)
return false;
if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
ni->mi.sbi->upcase, true))
return false;
if (le64_to_cpu(le->vcn) != vcn)
return false;

/*
* The caller specified a segment reference, so we have to
* scan through the matching entries until we find that segment
* reference or we run of matching entries.
*/
if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
off += le16_to_cpu(le->size);
le = Add2Ptr(al->le, off);
goto next;
}

/* Save on stack the size of 'le'. */
size = le16_to_cpu(le->size);
/* Delete the le. */
memmove(le, Add2Ptr(le, size), al->size - (off + size));

al->size -= size;
al->dirty = true;

return true;
}

int al_update(struct ntfs_inode *ni, int sync)
{
int err;
Expand Down
Loading

0 comments on commit 5b7c893

Please sign in to comment.