2021-08-13 07:21:29 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
|
|
|
|
#include "debug.h"
|
|
|
|
#include "ntfs.h"
|
|
|
|
#include "ntfs_fs.h"
|
|
|
|
|
|
|
|
static inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type,
|
|
|
|
const __le16 *name, u8 name_len,
|
|
|
|
const u16 *upcase)
|
|
|
|
{
|
2021-08-03 04:57:09 -07:00
|
|
|
/* First, compare the type codes. */
|
2021-08-13 07:21:29 -07:00
|
|
|
int diff = le32_to_cpu(left->type) - le32_to_cpu(type);
|
|
|
|
|
|
|
|
if (diff)
|
|
|
|
return diff;
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* They have the same type code, so we have to compare the names. */
|
2021-08-13 07:21:29 -07:00
|
|
|
return ntfs_cmp_names(attr_name(left), left->name_len, name, name_len,
|
|
|
|
upcase, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* mi_new_attt_id
|
|
|
|
*
|
2021-08-03 04:57:09 -07:00
|
|
|
* Return: Unused attribute id that is less than mrec->next_attr_id.
|
2021-08-13 07:21:29 -07:00
|
|
|
*/
|
|
|
|
static __le16 mi_new_attt_id(struct mft_inode *mi)
|
|
|
|
{
|
|
|
|
u16 free_id, max_id, t16;
|
|
|
|
struct MFT_REC *rec = mi->mrec;
|
|
|
|
struct ATTRIB *attr;
|
|
|
|
__le16 id;
|
|
|
|
|
|
|
|
id = rec->next_attr_id;
|
|
|
|
free_id = le16_to_cpu(id);
|
|
|
|
if (free_id < 0x7FFF) {
|
|
|
|
rec->next_attr_id = cpu_to_le16(free_id + 1);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* One record can store up to 1024/24 ~= 42 attributes. */
|
2021-08-13 07:21:29 -07:00
|
|
|
free_id = 0;
|
|
|
|
max_id = 0;
|
|
|
|
|
|
|
|
attr = NULL;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
attr = mi_enum_attr(mi, attr);
|
|
|
|
if (!attr) {
|
|
|
|
rec->next_attr_id = cpu_to_le16(max_id + 1);
|
|
|
|
mi->dirty = true;
|
|
|
|
return cpu_to_le16(free_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
t16 = le16_to_cpu(attr->id);
|
|
|
|
if (t16 == free_id) {
|
|
|
|
free_id += 1;
|
|
|
|
attr = NULL;
|
|
|
|
} else if (max_id < t16)
|
|
|
|
max_id = t16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi)
|
|
|
|
{
|
|
|
|
int err;
|
2021-08-24 11:37:07 -07:00
|
|
|
struct mft_inode *m = kzalloc(sizeof(struct mft_inode), GFP_NOFS);
|
2021-08-13 07:21:29 -07:00
|
|
|
|
|
|
|
if (!m)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
err = mi_init(m, sbi, rno);
|
|
|
|
if (err) {
|
2021-08-24 11:37:07 -07:00
|
|
|
kfree(m);
|
2021-08-13 07:21:29 -07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = mi_read(m, false);
|
|
|
|
if (err) {
|
|
|
|
mi_put(m);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
*mi = m;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mi_put(struct mft_inode *mi)
|
|
|
|
{
|
|
|
|
mi_clear(mi);
|
2021-08-24 11:37:07 -07:00
|
|
|
kfree(mi);
|
2021-08-13 07:21:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno)
|
|
|
|
{
|
|
|
|
mi->sbi = sbi;
|
|
|
|
mi->rno = rno;
|
2021-08-24 11:37:07 -07:00
|
|
|
mi->mrec = kmalloc(sbi->record_size, GFP_NOFS);
|
2021-08-13 07:21:29 -07:00
|
|
|
if (!mi->mrec)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-08-03 04:57:09 -07:00
|
|
|
* mi_read - Read MFT data.
|
2021-08-13 07:21:29 -07:00
|
|
|
*/
|
|
|
|
int mi_read(struct mft_inode *mi, bool is_mft)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct MFT_REC *rec = mi->mrec;
|
|
|
|
struct ntfs_sb_info *sbi = mi->sbi;
|
|
|
|
u32 bpr = sbi->record_size;
|
|
|
|
u64 vbo = (u64)mi->rno << sbi->record_bits;
|
|
|
|
struct ntfs_inode *mft_ni = sbi->mft.ni;
|
|
|
|
struct runs_tree *run = mft_ni ? &mft_ni->file.run : NULL;
|
|
|
|
struct rw_semaphore *rw_lock = NULL;
|
|
|
|
|
|
|
|
if (is_mounted(sbi)) {
|
fs: ntfs3: Fix possible null-pointer dereferences in mi_read()
In a previous commit 2681631c2973 ("fs/ntfs3: Add null pointer check to
attr_load_runs_vcn"), ni can be NULL in attr_load_runs_vcn(), and thus it
should be checked before being used.
However, in the call stack of this commit, mft_ni in mi_read() is
aliased with ni in attr_load_runs_vcn(), and it is also used in
mi_read() at two places:
mi_read()
rw_lock = &mft_ni->file.run_lock -> No check
attr_load_runs_vcn(mft_ni, ...)
ni (namely mft_ni) is checked in the previous commit
attr_load_runs_vcn(..., &mft_ni->file.run) -> No check
Thus, to avoid possible null-pointer dereferences, the related checks
should be added.
These bugs are reported by a static analysis tool implemented by myself,
and they are found by extending a known bug fixed in the previous commit.
Thus, they could be theoretical bugs.
Signed-off-by: Jia-Ju Bai <baijiaju@buaa.edu.cn>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2023-03-21 06:22:11 -07:00
|
|
|
if (!is_mft && mft_ni) {
|
2021-08-13 07:21:29 -07:00
|
|
|
rw_lock = &mft_ni->file.run_lock;
|
|
|
|
down_read(rw_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb);
|
|
|
|
if (rw_lock)
|
|
|
|
up_read(rw_lock);
|
|
|
|
if (!err)
|
|
|
|
goto ok;
|
|
|
|
|
|
|
|
if (err == -E_NTFS_FIXUP) {
|
|
|
|
mi->dirty = true;
|
|
|
|
goto ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != -ENOENT)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (rw_lock) {
|
|
|
|
ni_lock(mft_ni);
|
|
|
|
down_write(rw_lock);
|
|
|
|
}
|
fs: ntfs3: Fix possible null-pointer dereferences in mi_read()
In a previous commit 2681631c2973 ("fs/ntfs3: Add null pointer check to
attr_load_runs_vcn"), ni can be NULL in attr_load_runs_vcn(), and thus it
should be checked before being used.
However, in the call stack of this commit, mft_ni in mi_read() is
aliased with ni in attr_load_runs_vcn(), and it is also used in
mi_read() at two places:
mi_read()
rw_lock = &mft_ni->file.run_lock -> No check
attr_load_runs_vcn(mft_ni, ...)
ni (namely mft_ni) is checked in the previous commit
attr_load_runs_vcn(..., &mft_ni->file.run) -> No check
Thus, to avoid possible null-pointer dereferences, the related checks
should be added.
These bugs are reported by a static analysis tool implemented by myself,
and they are found by extending a known bug fixed in the previous commit.
Thus, they could be theoretical bugs.
Signed-off-by: Jia-Ju Bai <baijiaju@buaa.edu.cn>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2023-03-21 06:22:11 -07:00
|
|
|
err = attr_load_runs_vcn(mft_ni, ATTR_DATA, NULL, 0, run,
|
2021-08-13 07:21:29 -07:00
|
|
|
vbo >> sbi->cluster_bits);
|
|
|
|
if (rw_lock) {
|
|
|
|
up_write(rw_lock);
|
|
|
|
ni_unlock(mft_ni);
|
|
|
|
}
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (rw_lock)
|
|
|
|
down_read(rw_lock);
|
|
|
|
err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb);
|
|
|
|
if (rw_lock)
|
|
|
|
up_read(rw_lock);
|
|
|
|
|
|
|
|
if (err == -E_NTFS_FIXUP) {
|
|
|
|
mi->dirty = true;
|
|
|
|
goto ok;
|
|
|
|
}
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ok:
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Check field 'total' only here. */
|
2021-08-13 07:21:29 -07:00
|
|
|
if (le32_to_cpu(rec->total) != bpr) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
2023-05-08 00:36:28 -07:00
|
|
|
if (err == -E_NTFS_CORRUPT) {
|
|
|
|
ntfs_err(sbi->sb, "mft corrupted");
|
|
|
|
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
|
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-08-13 07:21:29 -07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2023-09-25 00:47:07 -07:00
|
|
|
/*
|
|
|
|
* mi_enum_attr - start/continue attributes enumeration in record.
|
|
|
|
*
|
|
|
|
* NOTE: mi->mrec - memory of size sbi->record_size
|
|
|
|
* here we sure that mi->mrec->total == sbi->record_size (see mi_read)
|
|
|
|
*/
|
2021-08-13 07:21:29 -07:00
|
|
|
struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|
|
|
{
|
|
|
|
const struct MFT_REC *rec = mi->mrec;
|
|
|
|
u32 used = le32_to_cpu(rec->used);
|
2023-06-30 05:17:02 -07:00
|
|
|
u32 t32, off, asize, prev_type;
|
2021-08-13 07:21:29 -07:00
|
|
|
u16 t16;
|
2023-06-30 05:17:02 -07:00
|
|
|
u64 data_size, alloc_size, tot_size;
|
2021-08-13 07:21:29 -07:00
|
|
|
|
|
|
|
if (!attr) {
|
|
|
|
u32 total = le32_to_cpu(rec->total);
|
|
|
|
|
|
|
|
off = le16_to_cpu(rec->attr_off);
|
|
|
|
|
|
|
|
if (used > total)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 ||
|
fs/ntfs3: Use kernel ALIGN macros over driver specific
The static checkers (Smatch) were complaining because QuadAlign() was
buggy. If you try to align something higher than UINT_MAX it got
truncated to a u32.
Smatch warning was:
fs/ntfs3/attrib.c:383 attr_set_size_res()
warn: was expecting a 64 bit value instead of '~7'
So that this will not happen again we will change all these macros to
kernel made ones. This can also help some other static analyzing tools
to give us better warnings.
Patch was generated with Coccinelle script and after that some style
issue was hand fixed.
Coccinelle script:
virtual patch
@alloc depends on patch@
expression x;
@@
(
- #define QuadAlign(n) (((n) + 7u) & (~7u))
|
- QuadAlign(x)
+ ALIGN(x, 8)
|
- #define IsQuadAligned(n) (!((size_t)(n)&7u))
|
- IsQuadAligned(x)
+ IS_ALIGNED(x, 8)
|
- #define Quad2Align(n) (((n) + 15u) & (~15u))
|
- Quad2Align(x)
+ ALIGN(x, 16)
|
- #define IsQuad2Aligned(n) (!((size_t)(n)&15u))
|
- IsQuad2Aligned(x)
+ IS_ALIGNED(x, 16)
|
- #define Quad4Align(n) (((n) + 31u) & (~31u))
|
- Quad4Align(x)
+ ALIGN(x, 32)
|
- #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1)))
|
- IsSizeTAligned(x)
+ IS_ALIGNED(x, sizeof(size_t))
|
- #define DwordAlign(n) (((n) + 3u) & (~3u))
|
- DwordAlign(x)
+ ALIGN(x, 4)
|
- #define IsDwordAligned(n) (!((size_t)(n)&3u))
|
- IsDwordAligned(x)
+ IS_ALIGNED(x, 4)
|
- #define WordAlign(n) (((n) + 1u) & (~1u))
|
- WordAlign(x)
+ ALIGN(x, 2)
|
- #define IsWordAligned(n) (!((size_t)(n)&1u))
|
- IsWordAligned(x)
+ IS_ALIGNED(x, 2)
|
)
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2021-08-26 01:56:29 -07:00
|
|
|
!IS_ALIGNED(off, 4)) {
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Skip non-resident records. */
|
2021-08-13 07:21:29 -07:00
|
|
|
if (!is_rec_inuse(rec))
|
|
|
|
return NULL;
|
|
|
|
|
2023-06-30 05:17:02 -07:00
|
|
|
prev_type = 0;
|
2021-08-13 07:21:29 -07:00
|
|
|
attr = Add2Ptr(rec, off);
|
|
|
|
} else {
|
2024-08-23 06:39:44 -07:00
|
|
|
/*
|
|
|
|
* We don't need to check previous attr here. There is
|
|
|
|
* a bounds checking in the previous round.
|
|
|
|
*/
|
2021-08-13 07:21:29 -07:00
|
|
|
off = PtrOffset(rec, attr);
|
|
|
|
|
|
|
|
asize = le32_to_cpu(attr->size);
|
2022-12-30 03:09:44 -07:00
|
|
|
|
2023-06-30 05:17:02 -07:00
|
|
|
prev_type = le32_to_cpu(attr->type);
|
2021-08-13 07:21:29 -07:00
|
|
|
attr = Add2Ptr(attr, asize);
|
|
|
|
off += asize;
|
|
|
|
}
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Can we use the first field (attr->type). */
|
2024-09-05 05:03:48 -07:00
|
|
|
/* NOTE: this code also checks attr->size availability. */
|
2021-08-13 07:21:29 -07:00
|
|
|
if (off + 8 > used) {
|
fs/ntfs3: Use kernel ALIGN macros over driver specific
The static checkers (Smatch) were complaining because QuadAlign() was
buggy. If you try to align something higher than UINT_MAX it got
truncated to a u32.
Smatch warning was:
fs/ntfs3/attrib.c:383 attr_set_size_res()
warn: was expecting a 64 bit value instead of '~7'
So that this will not happen again we will change all these macros to
kernel made ones. This can also help some other static analyzing tools
to give us better warnings.
Patch was generated with Coccinelle script and after that some style
issue was hand fixed.
Coccinelle script:
virtual patch
@alloc depends on patch@
expression x;
@@
(
- #define QuadAlign(n) (((n) + 7u) & (~7u))
|
- QuadAlign(x)
+ ALIGN(x, 8)
|
- #define IsQuadAligned(n) (!((size_t)(n)&7u))
|
- IsQuadAligned(x)
+ IS_ALIGNED(x, 8)
|
- #define Quad2Align(n) (((n) + 15u) & (~15u))
|
- Quad2Align(x)
+ ALIGN(x, 16)
|
- #define IsQuad2Aligned(n) (!((size_t)(n)&15u))
|
- IsQuad2Aligned(x)
+ IS_ALIGNED(x, 16)
|
- #define Quad4Align(n) (((n) + 31u) & (~31u))
|
- Quad4Align(x)
+ ALIGN(x, 32)
|
- #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1)))
|
- IsSizeTAligned(x)
+ IS_ALIGNED(x, sizeof(size_t))
|
- #define DwordAlign(n) (((n) + 3u) & (~3u))
|
- DwordAlign(x)
+ ALIGN(x, 4)
|
- #define IsDwordAligned(n) (!((size_t)(n)&3u))
|
- IsDwordAligned(x)
+ IS_ALIGNED(x, 4)
|
- #define WordAlign(n) (((n) + 1u) & (~1u))
|
- WordAlign(x)
+ ALIGN(x, 2)
|
- #define IsWordAligned(n) (!((size_t)(n)&1u))
|
- IsWordAligned(x)
+ IS_ALIGNED(x, 2)
|
)
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2021-08-26 01:56:29 -07:00
|
|
|
static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8);
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attr->type == ATTR_END) {
|
2021-08-03 04:57:09 -07:00
|
|
|
/* End of enumeration. */
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* 0x100 is last known attribute for now. */
|
2021-08-13 07:21:29 -07:00
|
|
|
t32 = le32_to_cpu(attr->type);
|
2023-06-30 05:17:02 -07:00
|
|
|
if (!t32 || (t32 & 0xf) || (t32 > 0x100))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* attributes in record must be ordered by type */
|
|
|
|
if (t32 < prev_type)
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
|
2024-08-23 06:39:44 -07:00
|
|
|
asize = le32_to_cpu(attr->size);
|
|
|
|
|
2022-10-27 08:33:37 -07:00
|
|
|
/* Check overflow and boundary. */
|
|
|
|
if (off + asize < off || off + asize > used)
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Check size of attribute. */
|
2021-08-13 07:21:29 -07:00
|
|
|
if (!attr->non_res) {
|
2023-06-30 05:17:02 -07:00
|
|
|
/* Check resident fields. */
|
2021-08-13 07:21:29 -07:00
|
|
|
if (asize < SIZEOF_RESIDENT)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
t16 = le16_to_cpu(attr->res.data_off);
|
|
|
|
if (t16 > asize)
|
|
|
|
return NULL;
|
|
|
|
|
2024-01-26 01:14:31 -07:00
|
|
|
if (le32_to_cpu(attr->res.data_size) > asize - t16)
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
|
2022-10-11 10:21:03 -07:00
|
|
|
t32 = sizeof(short) * attr->name_len;
|
|
|
|
if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
|
2022-09-22 09:50:23 -07:00
|
|
|
return NULL;
|
|
|
|
|
2021-08-13 07:21:29 -07:00
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
2023-06-30 05:17:02 -07:00
|
|
|
/* Check nonresident fields. */
|
|
|
|
if (attr->non_res != 1)
|
|
|
|
return NULL;
|
|
|
|
|
2024-09-05 05:03:48 -07:00
|
|
|
/* Can we use memory including attr->nres.valid_size? */
|
|
|
|
if (asize < SIZEOF_NONRESIDENT)
|
|
|
|
return NULL;
|
|
|
|
|
2023-06-30 05:17:02 -07:00
|
|
|
t16 = le16_to_cpu(attr->nres.run_off);
|
|
|
|
if (t16 > asize)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
t32 = sizeof(short) * attr->name_len;
|
|
|
|
if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Check start/end vcn. */
|
|
|
|
if (le64_to_cpu(attr->nres.svcn) > le64_to_cpu(attr->nres.evcn) + 1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
data_size = le64_to_cpu(attr->nres.data_size);
|
|
|
|
if (le64_to_cpu(attr->nres.valid_size) > data_size)
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
|
2023-06-30 05:17:02 -07:00
|
|
|
alloc_size = le64_to_cpu(attr->nres.alloc_size);
|
|
|
|
if (data_size > alloc_size)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
t32 = mi->sbi->cluster_mask;
|
|
|
|
if (alloc_size & t32)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!attr->nres.svcn && is_attr_ext(attr)) {
|
|
|
|
/* First segment of sparse/compressed attribute */
|
2024-09-05 05:03:48 -07:00
|
|
|
/* Can we use memory including attr->nres.total_size? */
|
|
|
|
if (asize < SIZEOF_NONRESIDENT_EX)
|
2023-06-30 05:17:02 -07:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tot_size = le64_to_cpu(attr->nres.total_size);
|
|
|
|
if (tot_size & t32)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (tot_size > alloc_size)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
2021-08-13 07:21:29 -07:00
|
|
|
if (attr->nres.c_unit)
|
|
|
|
return NULL;
|
2024-08-19 06:26:59 -07:00
|
|
|
|
|
|
|
if (alloc_size > mi->sbi->volume.size)
|
|
|
|
return NULL;
|
2023-06-30 05:17:02 -07:00
|
|
|
}
|
2021-08-13 07:21:29 -07:00
|
|
|
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-08-03 04:57:09 -07:00
|
|
|
* mi_find_attr - Find the attribute by type and name and id.
|
2021-08-13 07:21:29 -07:00
|
|
|
*/
|
|
|
|
struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr,
|
|
|
|
enum ATTR_TYPE type, const __le16 *name,
|
2023-05-08 01:59:06 -07:00
|
|
|
u8 name_len, const __le16 *id)
|
2021-08-13 07:21:29 -07:00
|
|
|
{
|
|
|
|
u32 type_in = le32_to_cpu(type);
|
|
|
|
u32 atype;
|
|
|
|
|
|
|
|
next_attr:
|
|
|
|
attr = mi_enum_attr(mi, attr);
|
|
|
|
if (!attr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
atype = le32_to_cpu(attr->type);
|
|
|
|
if (atype > type_in)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (atype < type_in)
|
|
|
|
goto next_attr;
|
|
|
|
|
|
|
|
if (attr->name_len != name_len)
|
|
|
|
goto next_attr;
|
|
|
|
|
|
|
|
if (name_len && memcmp(attr_name(attr), name, name_len * sizeof(short)))
|
|
|
|
goto next_attr;
|
|
|
|
|
|
|
|
if (id && *id != attr->id)
|
|
|
|
goto next_attr;
|
|
|
|
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mi_write(struct mft_inode *mi, int wait)
|
|
|
|
{
|
|
|
|
struct MFT_REC *rec;
|
|
|
|
int err;
|
|
|
|
struct ntfs_sb_info *sbi;
|
|
|
|
|
|
|
|
if (!mi->dirty)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
sbi = mi->sbi;
|
|
|
|
rec = mi->mrec;
|
|
|
|
|
|
|
|
err = ntfs_write_bh(sbi, &rec->rhdr, &mi->nb, wait);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (mi->rno < sbi->mft.recs_mirr)
|
|
|
|
sbi->flags |= NTFS_FLAGS_MFTMIRR;
|
|
|
|
|
|
|
|
mi->dirty = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno,
|
|
|
|
__le16 flags, bool is_mft)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 seq = 1;
|
|
|
|
struct MFT_REC *rec;
|
|
|
|
u64 vbo = (u64)rno << sbi->record_bits;
|
|
|
|
|
|
|
|
err = mi_init(mi, sbi, rno);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
rec = mi->mrec;
|
|
|
|
|
|
|
|
if (rno == MFT_REC_MFT) {
|
|
|
|
;
|
|
|
|
} else if (rno < MFT_REC_FREE) {
|
|
|
|
seq = rno;
|
|
|
|
} else if (rno >= sbi->mft.used) {
|
|
|
|
;
|
|
|
|
} else if (mi_read(mi, is_mft)) {
|
|
|
|
;
|
|
|
|
} else if (rec->rhdr.sign == NTFS_FILE_SIGNATURE) {
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Record is reused. Update its sequence number. */
|
2021-08-13 07:21:29 -07:00
|
|
|
seq = le16_to_cpu(rec->seq) + 1;
|
|
|
|
if (!seq)
|
|
|
|
seq = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(rec, sbi->new_rec, sbi->record_size);
|
|
|
|
|
|
|
|
rec->seq = cpu_to_le16(seq);
|
|
|
|
rec->flags = RECORD_FLAG_IN_USE | flags;
|
2023-05-08 02:37:22 -07:00
|
|
|
if (MFTRECORD_FIXUP_OFFSET == MFTRECORD_FIXUP_OFFSET_3)
|
|
|
|
rec->mft_record = cpu_to_le32(rno);
|
2021-08-13 07:21:29 -07:00
|
|
|
|
|
|
|
mi->dirty = true;
|
|
|
|
|
|
|
|
if (!mi->nb.nbufs) {
|
|
|
|
struct ntfs_inode *ni = sbi->mft.ni;
|
|
|
|
bool lock = false;
|
|
|
|
|
|
|
|
if (is_mounted(sbi) && !is_mft) {
|
|
|
|
down_read(&ni->file.run_lock);
|
|
|
|
lock = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ntfs_get_bh(sbi, &ni->file.run, vbo, sbi->record_size,
|
|
|
|
&mi->nb);
|
|
|
|
if (lock)
|
|
|
|
up_read(&ni->file.run_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-08-03 04:57:09 -07:00
|
|
|
* mi_insert_attr - Reserve space for new attribute.
|
2021-08-13 07:21:29 -07:00
|
|
|
*
|
2021-08-03 04:57:09 -07:00
|
|
|
* Return: Not full constructed attribute or NULL if not possible to create.
|
2021-08-13 07:21:29 -07:00
|
|
|
*/
|
|
|
|
struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
|
|
|
|
const __le16 *name, u8 name_len, u32 asize,
|
|
|
|
u16 name_off)
|
|
|
|
{
|
|
|
|
size_t tail;
|
|
|
|
struct ATTRIB *attr;
|
|
|
|
__le16 id;
|
|
|
|
struct MFT_REC *rec = mi->mrec;
|
|
|
|
struct ntfs_sb_info *sbi = mi->sbi;
|
|
|
|
u32 used = le32_to_cpu(rec->used);
|
|
|
|
const u16 *upcase = sbi->upcase;
|
|
|
|
|
|
|
|
/* Can we insert mi attribute? */
|
2023-01-17 04:01:00 -07:00
|
|
|
if (used + asize > sbi->record_size)
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan through the list of attributes to find the point
|
|
|
|
* at which we should insert it.
|
|
|
|
*/
|
|
|
|
attr = NULL;
|
|
|
|
while ((attr = mi_enum_attr(mi, attr))) {
|
2023-01-17 04:01:00 -07:00
|
|
|
int diff = compare_attr(attr, type, name, name_len, upcase);
|
2022-05-12 02:25:48 -07:00
|
|
|
|
2021-08-13 07:21:29 -07:00
|
|
|
if (diff < 0)
|
|
|
|
continue;
|
|
|
|
|
2022-05-12 02:25:48 -07:00
|
|
|
if (!diff && !is_attr_indexed(attr))
|
2021-08-13 07:21:29 -07:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!attr) {
|
2023-01-17 04:01:00 -07:00
|
|
|
/* Append. */
|
|
|
|
tail = 8;
|
2021-08-13 07:21:29 -07:00
|
|
|
attr = Add2Ptr(rec, used - 8);
|
|
|
|
} else {
|
2023-01-17 04:01:00 -07:00
|
|
|
/* Insert before 'attr'. */
|
2021-08-13 07:21:29 -07:00
|
|
|
tail = used - PtrOffset(rec, attr);
|
|
|
|
}
|
|
|
|
|
|
|
|
id = mi_new_attt_id(mi);
|
|
|
|
|
|
|
|
memmove(Add2Ptr(attr, asize), attr, tail);
|
|
|
|
memset(attr, 0, asize);
|
|
|
|
|
|
|
|
attr->type = type;
|
|
|
|
attr->size = cpu_to_le32(asize);
|
|
|
|
attr->name_len = name_len;
|
|
|
|
attr->name_off = cpu_to_le16(name_off);
|
|
|
|
attr->id = id;
|
|
|
|
|
|
|
|
memmove(Add2Ptr(attr, name_off), name, name_len * sizeof(short));
|
|
|
|
rec->used = cpu_to_le32(used + asize);
|
|
|
|
|
|
|
|
mi->dirty = true;
|
|
|
|
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-08-03 04:57:09 -07:00
|
|
|
* mi_remove_attr - Remove the attribute from record.
|
2021-08-13 07:21:29 -07:00
|
|
|
*
|
2021-08-03 04:57:09 -07:00
|
|
|
* NOTE: The source attr will point to next attribute.
|
2021-08-13 07:21:29 -07:00
|
|
|
*/
|
2021-08-31 08:52:39 -07:00
|
|
|
bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
|
|
|
|
struct ATTRIB *attr)
|
2021-08-13 07:21:29 -07:00
|
|
|
{
|
|
|
|
struct MFT_REC *rec = mi->mrec;
|
|
|
|
u32 aoff = PtrOffset(rec, attr);
|
|
|
|
u32 used = le32_to_cpu(rec->used);
|
|
|
|
u32 asize = le32_to_cpu(attr->size);
|
|
|
|
|
|
|
|
if (aoff + asize > used)
|
|
|
|
return false;
|
|
|
|
|
2024-04-17 00:33:06 -07:00
|
|
|
if (ni && is_attr_indexed(attr) && attr->type == ATTR_NAME) {
|
2023-11-24 01:26:31 -07:00
|
|
|
u16 links = le16_to_cpu(ni->mi.mrec->hard_links);
|
2024-04-17 00:33:06 -07:00
|
|
|
if (!links) {
|
2023-11-24 01:26:31 -07:00
|
|
|
/* minor error. Not critical. */
|
|
|
|
} else {
|
|
|
|
ni->mi.mrec->hard_links = cpu_to_le16(links - 1);
|
|
|
|
ni->mi.dirty = true;
|
|
|
|
}
|
2021-08-31 08:52:39 -07:00
|
|
|
}
|
|
|
|
|
2021-08-13 07:21:29 -07:00
|
|
|
used -= asize;
|
|
|
|
memmove(attr, Add2Ptr(attr, asize), used - aoff);
|
|
|
|
rec->used = cpu_to_le32(used);
|
|
|
|
mi->dirty = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bytes = "new attribute size" - "old attribute size" */
|
|
|
|
bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes)
|
|
|
|
{
|
|
|
|
struct MFT_REC *rec = mi->mrec;
|
|
|
|
u32 aoff = PtrOffset(rec, attr);
|
|
|
|
u32 total, used = le32_to_cpu(rec->used);
|
|
|
|
u32 nsize, asize = le32_to_cpu(attr->size);
|
|
|
|
u32 rsize = le32_to_cpu(attr->res.data_size);
|
|
|
|
int tail = (int)(used - aoff - asize);
|
|
|
|
int dsize;
|
|
|
|
char *next;
|
|
|
|
|
|
|
|
if (tail < 0 || aoff >= used)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!bytes)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
total = le32_to_cpu(rec->total);
|
|
|
|
next = Add2Ptr(attr, asize);
|
|
|
|
|
|
|
|
if (bytes > 0) {
|
fs/ntfs3: Use kernel ALIGN macros over driver specific
The static checkers (Smatch) were complaining because QuadAlign() was
buggy. If you try to align something higher than UINT_MAX it got
truncated to a u32.
Smatch warning was:
fs/ntfs3/attrib.c:383 attr_set_size_res()
warn: was expecting a 64 bit value instead of '~7'
So that this will not happen again we will change all these macros to
kernel made ones. This can also help some other static analyzing tools
to give us better warnings.
Patch was generated with Coccinelle script and after that some style
issue was hand fixed.
Coccinelle script:
virtual patch
@alloc depends on patch@
expression x;
@@
(
- #define QuadAlign(n) (((n) + 7u) & (~7u))
|
- QuadAlign(x)
+ ALIGN(x, 8)
|
- #define IsQuadAligned(n) (!((size_t)(n)&7u))
|
- IsQuadAligned(x)
+ IS_ALIGNED(x, 8)
|
- #define Quad2Align(n) (((n) + 15u) & (~15u))
|
- Quad2Align(x)
+ ALIGN(x, 16)
|
- #define IsQuad2Aligned(n) (!((size_t)(n)&15u))
|
- IsQuad2Aligned(x)
+ IS_ALIGNED(x, 16)
|
- #define Quad4Align(n) (((n) + 31u) & (~31u))
|
- Quad4Align(x)
+ ALIGN(x, 32)
|
- #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1)))
|
- IsSizeTAligned(x)
+ IS_ALIGNED(x, sizeof(size_t))
|
- #define DwordAlign(n) (((n) + 3u) & (~3u))
|
- DwordAlign(x)
+ ALIGN(x, 4)
|
- #define IsDwordAligned(n) (!((size_t)(n)&3u))
|
- IsDwordAligned(x)
+ IS_ALIGNED(x, 4)
|
- #define WordAlign(n) (((n) + 1u) & (~1u))
|
- WordAlign(x)
+ ALIGN(x, 2)
|
- #define IsWordAligned(n) (!((size_t)(n)&1u))
|
- IsWordAligned(x)
+ IS_ALIGNED(x, 2)
|
)
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2021-08-26 01:56:29 -07:00
|
|
|
dsize = ALIGN(bytes, 8);
|
2021-08-13 07:21:29 -07:00
|
|
|
if (used + dsize > total)
|
|
|
|
return false;
|
|
|
|
nsize = asize + dsize;
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Move tail */
|
2021-08-13 07:21:29 -07:00
|
|
|
memmove(next + dsize, next, tail);
|
|
|
|
memset(next, 0, dsize);
|
|
|
|
used += dsize;
|
|
|
|
rsize += dsize;
|
|
|
|
} else {
|
fs/ntfs3: Use kernel ALIGN macros over driver specific
The static checkers (Smatch) were complaining because QuadAlign() was
buggy. If you try to align something higher than UINT_MAX it got
truncated to a u32.
Smatch warning was:
fs/ntfs3/attrib.c:383 attr_set_size_res()
warn: was expecting a 64 bit value instead of '~7'
So that this will not happen again we will change all these macros to
kernel made ones. This can also help some other static analyzing tools
to give us better warnings.
Patch was generated with Coccinelle script and after that some style
issue was hand fixed.
Coccinelle script:
virtual patch
@alloc depends on patch@
expression x;
@@
(
- #define QuadAlign(n) (((n) + 7u) & (~7u))
|
- QuadAlign(x)
+ ALIGN(x, 8)
|
- #define IsQuadAligned(n) (!((size_t)(n)&7u))
|
- IsQuadAligned(x)
+ IS_ALIGNED(x, 8)
|
- #define Quad2Align(n) (((n) + 15u) & (~15u))
|
- Quad2Align(x)
+ ALIGN(x, 16)
|
- #define IsQuad2Aligned(n) (!((size_t)(n)&15u))
|
- IsQuad2Aligned(x)
+ IS_ALIGNED(x, 16)
|
- #define Quad4Align(n) (((n) + 31u) & (~31u))
|
- Quad4Align(x)
+ ALIGN(x, 32)
|
- #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1)))
|
- IsSizeTAligned(x)
+ IS_ALIGNED(x, sizeof(size_t))
|
- #define DwordAlign(n) (((n) + 3u) & (~3u))
|
- DwordAlign(x)
+ ALIGN(x, 4)
|
- #define IsDwordAligned(n) (!((size_t)(n)&3u))
|
- IsDwordAligned(x)
+ IS_ALIGNED(x, 4)
|
- #define WordAlign(n) (((n) + 1u) & (~1u))
|
- WordAlign(x)
+ ALIGN(x, 2)
|
- #define IsWordAligned(n) (!((size_t)(n)&1u))
|
- IsWordAligned(x)
+ IS_ALIGNED(x, 2)
|
)
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2021-08-26 01:56:29 -07:00
|
|
|
dsize = ALIGN(-bytes, 8);
|
2021-08-13 07:21:29 -07:00
|
|
|
if (dsize > asize)
|
|
|
|
return false;
|
|
|
|
nsize = asize - dsize;
|
|
|
|
memmove(next - dsize, next, tail);
|
|
|
|
used -= dsize;
|
|
|
|
rsize -= dsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
rec->used = cpu_to_le32(used);
|
|
|
|
attr->size = cpu_to_le32(nsize);
|
|
|
|
if (!attr->non_res)
|
|
|
|
attr->res.data_size = cpu_to_le32(rsize);
|
|
|
|
mi->dirty = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-09 09:12:31 -07:00
|
|
|
/*
|
|
|
|
* Pack runs in MFT record.
|
|
|
|
* If failed record is not changed.
|
|
|
|
*/
|
2021-08-13 07:21:29 -07:00
|
|
|
int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
|
|
|
|
struct runs_tree *run, CLST len)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct ntfs_sb_info *sbi = mi->sbi;
|
|
|
|
u32 new_run_size;
|
|
|
|
CLST plen;
|
|
|
|
struct MFT_REC *rec = mi->mrec;
|
|
|
|
CLST svcn = le64_to_cpu(attr->nres.svcn);
|
|
|
|
u32 used = le32_to_cpu(rec->used);
|
|
|
|
u32 aoff = PtrOffset(rec, attr);
|
|
|
|
u32 asize = le32_to_cpu(attr->size);
|
|
|
|
char *next = Add2Ptr(attr, asize);
|
|
|
|
u16 run_off = le16_to_cpu(attr->nres.run_off);
|
|
|
|
u32 run_size = asize - run_off;
|
|
|
|
u32 tail = used - aoff - asize;
|
|
|
|
u32 dsize = sbi->record_size - used;
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Make a maximum gap in current record. */
|
2021-08-13 07:21:29 -07:00
|
|
|
memmove(next + dsize, next, tail);
|
|
|
|
|
2021-08-03 04:57:09 -07:00
|
|
|
/* Pack as much as possible. */
|
2021-08-13 07:21:29 -07:00
|
|
|
err = run_pack(run, svcn, len, Add2Ptr(attr, run_off), run_size + dsize,
|
|
|
|
&plen);
|
|
|
|
if (err < 0) {
|
|
|
|
memmove(next, next + dsize, tail);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
fs/ntfs3: Use kernel ALIGN macros over driver specific
The static checkers (Smatch) were complaining because QuadAlign() was
buggy. If you try to align something higher than UINT_MAX it got
truncated to a u32.
Smatch warning was:
fs/ntfs3/attrib.c:383 attr_set_size_res()
warn: was expecting a 64 bit value instead of '~7'
So that this will not happen again we will change all these macros to
kernel made ones. This can also help some other static analyzing tools
to give us better warnings.
Patch was generated with Coccinelle script and after that some style
issue was hand fixed.
Coccinelle script:
virtual patch
@alloc depends on patch@
expression x;
@@
(
- #define QuadAlign(n) (((n) + 7u) & (~7u))
|
- QuadAlign(x)
+ ALIGN(x, 8)
|
- #define IsQuadAligned(n) (!((size_t)(n)&7u))
|
- IsQuadAligned(x)
+ IS_ALIGNED(x, 8)
|
- #define Quad2Align(n) (((n) + 15u) & (~15u))
|
- Quad2Align(x)
+ ALIGN(x, 16)
|
- #define IsQuad2Aligned(n) (!((size_t)(n)&15u))
|
- IsQuad2Aligned(x)
+ IS_ALIGNED(x, 16)
|
- #define Quad4Align(n) (((n) + 31u) & (~31u))
|
- Quad4Align(x)
+ ALIGN(x, 32)
|
- #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1)))
|
- IsSizeTAligned(x)
+ IS_ALIGNED(x, sizeof(size_t))
|
- #define DwordAlign(n) (((n) + 3u) & (~3u))
|
- DwordAlign(x)
+ ALIGN(x, 4)
|
- #define IsDwordAligned(n) (!((size_t)(n)&3u))
|
- IsDwordAligned(x)
+ IS_ALIGNED(x, 4)
|
- #define WordAlign(n) (((n) + 1u) & (~1u))
|
- WordAlign(x)
+ ALIGN(x, 2)
|
- #define IsWordAligned(n) (!((size_t)(n)&1u))
|
- IsWordAligned(x)
+ IS_ALIGNED(x, 2)
|
)
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2021-08-26 01:56:29 -07:00
|
|
|
new_run_size = ALIGN(err, 8);
|
2021-08-13 07:21:29 -07:00
|
|
|
|
|
|
|
memmove(next + new_run_size - run_size, next + dsize, tail);
|
|
|
|
|
|
|
|
attr->size = cpu_to_le32(asize + new_run_size - run_size);
|
|
|
|
attr->nres.evcn = cpu_to_le64(svcn + plen - 1);
|
|
|
|
rec->used = cpu_to_le32(used + new_run_size - run_size);
|
|
|
|
mi->dirty = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|