mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-21 05:14:52 +08:00
driver ntfs3 for linux 6.6
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEh0DEKNP0I9IjwfWEqbAzH4MkB7YFAmUwyyAACgkQqbAzH4Mk B7aQsg/9FohvuET2YOqYlmtgbq1YjFlWkVxoNB2Be4gxKEqRcFN4V/O8PrOD0ZBX KH8ASWGp9EKQlVKucAPUhSaZMv4u2HaKTVyRnH5KpUmNG7rC7EzRgYDY4xA5RfjL aIxgVOhaSibFBGfys2KoS8MHFgZ9mcCWZZ1154ueSkZfX9ghytZcql5i4Ahi2OGQ u+aa/tkhoIS9ZKWp/uIz4R+fDClrG8UoiWmJ1LoOHQ5YM3enboOld6Zz1CjbC9yN ldInX7klL9aHRf3SoAeRVCyL23e4GB5R4+QmBhZQemhwy0jF0/7E0kCmCK0wYFWn 3t1M/+i0S7o7rI+utTA2mTuZzxnNPMXJsn2TnpH9vv1o633WOlL0mrvsM7Z/NJDz VShPinQ9eJGvZQ9kYuMfZODtK0BP7OV8mRi7aXinzHucG+ISB2fBGxsnA0lX9EfT fHix1oNI33lfazVzaStJ3hs/n9KXwP/9P49rBcLh75O2t3TBKc2T84qpywNx+W6S 9GDMV5V2FNWf/hMCL8pcTtab3MvpbeU5DqKnxW1D1vu2vdGiHVum/Aq26aE5W7nl EcE5pjKppdNZgk3Qwj190vym7d8lbK4Esua6ieNM/qvWb7hAGoZO7Tjcypvj+YMY W4aiJTW/jULPT2tkUvLvHg/r5DFHi30v6eCeTCrPfivxFRIAzBQ= =ScrJ -----END PGP SIGNATURE----- Merge tag 'ntfs3_for_6.6' of https://github.com/Paragon-Software-Group/linux-ntfs3 Pull ntfs3 fixes from Konstantin Komarov: - memory leak - some logic errors, NULL dereferences - some code was refactored - more sanity checks * tag 'ntfs3_for_6.6' of https://github.com/Paragon-Software-Group/linux-ntfs3: fs/ntfs3: Avoid possible memory leak fs/ntfs3: Fix directory element type detection fs/ntfs3: Fix possible null-pointer dereference in hdr_find_e() fs/ntfs3: Fix OOB read in ntfs_init_from_boot fs/ntfs3: fix panic about slab-out-of-bounds caused by ntfs_list_ea() fs/ntfs3: Fix NULL pointer dereference on error in attr_allocate_frame() fs/ntfs3: Fix possible NULL-ptr-deref in ni_readpage_cmpr() fs/ntfs3: Do not allow to change label if volume is read-only fs/ntfs3: Add more info into /proc/fs/ntfs3/<dev>/volinfo fs/ntfs3: Refactoring and comments fs/ntfs3: Fix alternative boot searching fs/ntfs3: Allow repeated call to ntfs3_put_sbi fs/ntfs3: Use inode_set_ctime_to_ts instead of inode_set_ctime fs/ntfs3: Fix shift-out-of-bounds in ntfs_fill_super fs/ntfs3: fix deadlock in mark_as_free_ex fs/ntfs3: Add more attributes checks in mi_enum_attr() fs/ntfs3: Use kvmalloc instead of kmalloc(... __GFP_NOWARN) fs/ntfs3: Write immediately updated ntfs state fs/ntfs3: Add ckeck in ni_update_parent()
This commit is contained in:
commit
f69d00d12f
@ -1106,10 +1106,10 @@ repack:
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* The code below may require additional cluster (to extend attribute list)
|
||||
* and / or one MFT record
|
||||
* It is too complex to undo operations if -ENOSPC occurs deep inside
|
||||
* and / or one MFT record
|
||||
* It is too complex to undo operations if -ENOSPC occurs deep inside
|
||||
* in 'ni_insert_nonresident'.
|
||||
* Return in advance -ENOSPC here if there are no free cluster and no free MFT.
|
||||
*/
|
||||
@ -1736,10 +1736,8 @@ repack:
|
||||
le_b = NULL;
|
||||
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL,
|
||||
0, NULL, &mi_b);
|
||||
if (!attr_b) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!attr_b)
|
||||
return -ENOENT;
|
||||
|
||||
attr = attr_b;
|
||||
le = le_b;
|
||||
|
@ -52,7 +52,8 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
|
||||
|
||||
if (!attr->non_res) {
|
||||
lsize = le32_to_cpu(attr->res.data_size);
|
||||
le = kmalloc(al_aligned(lsize), GFP_NOFS | __GFP_NOWARN);
|
||||
/* attr is resident: lsize < record_size (1K or 4K) */
|
||||
le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
|
||||
if (!le) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
@ -80,7 +81,17 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
le = kmalloc(al_aligned(lsize), GFP_NOFS | __GFP_NOWARN);
|
||||
/* attr is nonresident.
|
||||
* The worst case:
|
||||
* 1T (2^40) extremely fragmented file.
|
||||
* cluster = 4K (2^12) => 2^28 fragments
|
||||
* 2^9 fragments per one record => 2^19 records
|
||||
* 2^5 bytes of ATTR_LIST_ENTRY per one record => 2^24 bytes.
|
||||
*
|
||||
* the result is 16M bytes per attribute list.
|
||||
* Use kvmalloc to allocate in range [several Kbytes - dozen Mbytes]
|
||||
*/
|
||||
le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
|
||||
if (!le) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
|
@ -125,6 +125,7 @@ void wnd_close(struct wnd_bitmap *wnd)
|
||||
struct rb_node *node, *next;
|
||||
|
||||
kfree(wnd->free_bits);
|
||||
wnd->free_bits = NULL;
|
||||
run_close(&wnd->run);
|
||||
|
||||
node = rb_first(&wnd->start_tree);
|
||||
@ -659,7 +660,8 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits)
|
||||
wnd->bits_last = wbits;
|
||||
|
||||
wnd->free_bits =
|
||||
kcalloc(wnd->nwnd, sizeof(u16), GFP_NOFS | __GFP_NOWARN);
|
||||
kvmalloc_array(wnd->nwnd, sizeof(u16), GFP_KERNEL | __GFP_ZERO);
|
||||
|
||||
if (!wnd->free_bits)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -309,7 +309,11 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
|
||||
return 0;
|
||||
}
|
||||
|
||||
dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
|
||||
/* NTFS: symlinks are "dir + reparse" or "file + reparse" */
|
||||
if (fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
dt_type = DT_LNK;
|
||||
else
|
||||
dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
|
||||
|
||||
return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
|
||||
}
|
||||
|
@ -745,8 +745,8 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
}
|
||||
|
||||
static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
|
||||
struct pipe_inode_info *pipe,
|
||||
size_t len, unsigned int flags)
|
||||
struct pipe_inode_info *pipe, size_t len,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct inode *inode = in->f_mapping->host;
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
|
@ -2148,7 +2148,7 @@ out1:
|
||||
|
||||
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);
|
||||
@ -3208,6 +3208,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)) {
|
||||
|
@ -2168,8 +2168,10 @@ file_is_valid:
|
||||
|
||||
if (!page) {
|
||||
page = kmalloc(log->page_size, GFP_NOFS);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (!page) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -983,18 +983,11 @@ out:
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mark_inode_dirty(&ni->vfs_inode);
|
||||
mark_inode_dirty_sync(&ni->vfs_inode);
|
||||
/* verify(!ntfs_update_mftmirr()); */
|
||||
|
||||
/*
|
||||
* If we used wait=1, sync_inode_metadata waits for the io for the
|
||||
* inode to finish. It hangs when media is removed.
|
||||
* So wait=0 is sent down to sync_inode_metadata
|
||||
* and filemap_fdatawrite is used for the data blocks.
|
||||
*/
|
||||
err = sync_inode_metadata(&ni->vfs_inode, 0);
|
||||
if (!err)
|
||||
err = filemap_fdatawrite(ni->vfs_inode.i_mapping);
|
||||
/* write mft record on disk. */
|
||||
err = _ni_write_inode(&ni->vfs_inode, 1);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -2461,10 +2454,12 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim)
|
||||
{
|
||||
CLST end, i, zone_len, zlen;
|
||||
struct wnd_bitmap *wnd = &sbi->used.bitmap;
|
||||
bool dirty = false;
|
||||
|
||||
down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
|
||||
if (!wnd_is_used(wnd, lcn, len)) {
|
||||
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
|
||||
/* mark volume as dirty out of wnd->rw_lock */
|
||||
dirty = true;
|
||||
|
||||
end = lcn + len;
|
||||
len = 0;
|
||||
@ -2518,6 +2513,8 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim)
|
||||
|
||||
out:
|
||||
up_write(&wnd->rw_lock);
|
||||
if (dirty)
|
||||
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -729,6 +729,9 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx,
|
||||
u32 total = le32_to_cpu(hdr->total);
|
||||
u16 offs[128];
|
||||
|
||||
if (unlikely(!cmp))
|
||||
return NULL;
|
||||
|
||||
fill_table:
|
||||
if (end > total)
|
||||
return NULL;
|
||||
|
@ -170,8 +170,8 @@ next_attr:
|
||||
nt2kernel(std5->cr_time, &ni->i_crtime);
|
||||
#endif
|
||||
nt2kernel(std5->a_time, &inode->i_atime);
|
||||
ctime = inode_get_ctime(inode);
|
||||
nt2kernel(std5->c_time, &ctime);
|
||||
inode_set_ctime_to_ts(inode, ctime);
|
||||
nt2kernel(std5->m_time, &inode->i_mtime);
|
||||
|
||||
ni->std_fa = std5->fa;
|
||||
@ -1660,7 +1660,8 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
/* Set original time. inode times (i_ctime) may be changed in ntfs_init_acl. */
|
||||
inode->i_atime = inode->i_mtime = inode_set_ctime_to_ts(inode, ni->i_crtime);
|
||||
inode->i_atime = inode->i_mtime =
|
||||
inode_set_ctime_to_ts(inode, ni->i_crtime);
|
||||
dir->i_mtime = inode_set_ctime_to_ts(dir, ni->i_crtime);
|
||||
|
||||
mark_inode_dirty(dir);
|
||||
|
@ -156,8 +156,8 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de)
|
||||
err = ntfs_link_inode(inode, de);
|
||||
|
||||
if (!err) {
|
||||
dir->i_mtime = inode_set_ctime_to_ts(inode,
|
||||
inode_set_ctime_current(dir));
|
||||
dir->i_mtime = inode_set_ctime_to_ts(
|
||||
inode, inode_set_ctime_current(dir));
|
||||
mark_inode_dirty(inode);
|
||||
mark_inode_dirty(dir);
|
||||
d_instantiate(de, inode);
|
||||
@ -373,7 +373,7 @@ static int ntfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
|
||||
if (IS_POSIXACL(dir)) {
|
||||
/*
|
||||
/*
|
||||
* Load in cache current acl to avoid ni_lock(dir):
|
||||
* ntfs_create_inode -> ntfs_init_acl -> posix_acl_create ->
|
||||
* ntfs_get_acl -> ntfs_get_acl_ex -> ni_lock
|
||||
|
@ -847,7 +847,7 @@ struct OBJECT_ID {
|
||||
// Birth Volume Id is the Object Id of the Volume on.
|
||||
// which the Object Id was allocated. It never changes.
|
||||
struct GUID BirthVolumeId; //0x10:
|
||||
|
||||
|
||||
// Birth Object Id is the first Object Id that was
|
||||
// ever assigned to this MFT Record. I.e. If the Object Id
|
||||
// is changed for some reason, this field will reflect the
|
||||
|
@ -42,9 +42,11 @@ enum utf16_endian;
|
||||
#define MINUS_ONE_T ((size_t)(-1))
|
||||
/* Biggest MFT / smallest cluster */
|
||||
#define MAXIMUM_BYTES_PER_MFT 4096
|
||||
#define MAXIMUM_SHIFT_BYTES_PER_MFT 12
|
||||
#define NTFS_BLOCKS_PER_MFT_RECORD (MAXIMUM_BYTES_PER_MFT / 512)
|
||||
|
||||
#define MAXIMUM_BYTES_PER_INDEX 4096
|
||||
#define MAXIMUM_SHIFT_BYTES_PER_INDEX 12
|
||||
#define NTFS_BLOCKS_PER_INODE (MAXIMUM_BYTES_PER_INDEX / 512)
|
||||
|
||||
/* NTFS specific error code when fixup failed. */
|
||||
@ -495,8 +497,6 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, u32 request_mask, u32 flags);
|
||||
int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr);
|
||||
void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn,
|
||||
CLST len);
|
||||
int ntfs_file_open(struct inode *inode, struct file *file);
|
||||
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
__u64 start, __u64 len);
|
||||
|
@ -189,12 +189,19 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
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);
|
||||
u32 t32, off, asize;
|
||||
u32 t32, off, asize, prev_type;
|
||||
u16 t16;
|
||||
u64 data_size, alloc_size, tot_size;
|
||||
|
||||
if (!attr) {
|
||||
u32 total = le32_to_cpu(rec->total);
|
||||
@ -213,6 +220,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
||||
if (!is_rec_inuse(rec))
|
||||
return NULL;
|
||||
|
||||
prev_type = 0;
|
||||
attr = Add2Ptr(rec, off);
|
||||
} else {
|
||||
/* Check if input attr inside record. */
|
||||
@ -226,11 +234,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (off + asize < off) {
|
||||
/* Overflow check. */
|
||||
/* Overflow check. */
|
||||
if (off + asize < off)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prev_type = le32_to_cpu(attr->type);
|
||||
attr = Add2Ptr(attr, asize);
|
||||
off += asize;
|
||||
}
|
||||
@ -250,7 +258,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
||||
|
||||
/* 0x100 is last known attribute for now. */
|
||||
t32 = le32_to_cpu(attr->type);
|
||||
if ((t32 & 0xf) || (t32 > 0x100))
|
||||
if (!t32 || (t32 & 0xf) || (t32 > 0x100))
|
||||
return NULL;
|
||||
|
||||
/* attributes in record must be ordered by type */
|
||||
if (t32 < prev_type)
|
||||
return NULL;
|
||||
|
||||
/* Check overflow and boundary. */
|
||||
@ -259,16 +271,15 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
||||
|
||||
/* Check size of attribute. */
|
||||
if (!attr->non_res) {
|
||||
/* Check resident fields. */
|
||||
if (asize < SIZEOF_RESIDENT)
|
||||
return NULL;
|
||||
|
||||
t16 = le16_to_cpu(attr->res.data_off);
|
||||
|
||||
if (t16 > asize)
|
||||
return NULL;
|
||||
|
||||
t32 = le32_to_cpu(attr->res.data_size);
|
||||
if (t16 + t32 > asize)
|
||||
if (t16 + le32_to_cpu(attr->res.data_size) > asize)
|
||||
return NULL;
|
||||
|
||||
t32 = sizeof(short) * attr->name_len;
|
||||
@ -278,21 +289,52 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
||||
return attr;
|
||||
}
|
||||
|
||||
/* Check some nonresident fields. */
|
||||
if (attr->name_len &&
|
||||
le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len >
|
||||
le16_to_cpu(attr->nres.run_off)) {
|
||||
/* Check nonresident fields. */
|
||||
if (attr->non_res != 1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (attr->nres.svcn || !is_attr_ext(attr)) {
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
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 */
|
||||
if (asize + 8 < SIZEOF_NONRESIDENT_EX)
|
||||
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 {
|
||||
if (asize + 8 < SIZEOF_NONRESIDENT)
|
||||
return NULL;
|
||||
|
||||
if (attr->nres.c_unit)
|
||||
return NULL;
|
||||
} else if (asize + 8 < SIZEOF_NONRESIDENT_EX)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
104
fs/ntfs3/super.c
104
fs/ntfs3/super.c
@ -453,15 +453,23 @@ static struct proc_dir_entry *proc_info_root;
|
||||
* ntfs3.1
|
||||
* cluster size
|
||||
* number of clusters
|
||||
* total number of mft records
|
||||
* number of used mft records ~= number of files + folders
|
||||
* real state of ntfs "dirty"/"clean"
|
||||
* current state of ntfs "dirty"/"clean"
|
||||
*/
|
||||
static int ntfs3_volinfo(struct seq_file *m, void *o)
|
||||
{
|
||||
struct super_block *sb = m->private;
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
|
||||
seq_printf(m, "ntfs%d.%d\n%u\n%zu\n", sbi->volume.major_ver,
|
||||
sbi->volume.minor_ver, sbi->cluster_size,
|
||||
sbi->used.bitmap.nbits);
|
||||
seq_printf(m, "ntfs%d.%d\n%u\n%zu\n\%zu\n%zu\n%s\n%s\n",
|
||||
sbi->volume.major_ver, sbi->volume.minor_ver,
|
||||
sbi->cluster_size, sbi->used.bitmap.nbits,
|
||||
sbi->mft.bitmap.nbits,
|
||||
sbi->mft.bitmap.nbits - wnd_zeroes(&sbi->mft.bitmap),
|
||||
sbi->volume.real_dirty ? "dirty" : "clean",
|
||||
(sbi->volume.flags & VOLUME_FLAG_DIRTY) ? "dirty" : "clean");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -488,9 +496,13 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
|
||||
{
|
||||
int err;
|
||||
struct super_block *sb = pde_data(file_inode(file));
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
ssize_t ret = count;
|
||||
u8 *label = kmalloc(count, GFP_NOFS);
|
||||
u8 *label;
|
||||
|
||||
if (sb_rdonly(sb))
|
||||
return -EROFS;
|
||||
|
||||
label = kmalloc(count, GFP_NOFS);
|
||||
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
@ -502,7 +514,7 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
|
||||
while (ret > 0 && label[ret - 1] == '\n')
|
||||
ret -= 1;
|
||||
|
||||
err = ntfs_set_label(sbi, label, ret);
|
||||
err = ntfs_set_label(sb->s_fs_info, label, ret);
|
||||
|
||||
if (err < 0) {
|
||||
ntfs_err(sb, "failed (%d) to write label", err);
|
||||
@ -576,20 +588,30 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)
|
||||
wnd_close(&sbi->mft.bitmap);
|
||||
wnd_close(&sbi->used.bitmap);
|
||||
|
||||
if (sbi->mft.ni)
|
||||
if (sbi->mft.ni) {
|
||||
iput(&sbi->mft.ni->vfs_inode);
|
||||
sbi->mft.ni = NULL;
|
||||
}
|
||||
|
||||
if (sbi->security.ni)
|
||||
if (sbi->security.ni) {
|
||||
iput(&sbi->security.ni->vfs_inode);
|
||||
sbi->security.ni = NULL;
|
||||
}
|
||||
|
||||
if (sbi->reparse.ni)
|
||||
if (sbi->reparse.ni) {
|
||||
iput(&sbi->reparse.ni->vfs_inode);
|
||||
sbi->reparse.ni = NULL;
|
||||
}
|
||||
|
||||
if (sbi->objid.ni)
|
||||
if (sbi->objid.ni) {
|
||||
iput(&sbi->objid.ni->vfs_inode);
|
||||
sbi->objid.ni = NULL;
|
||||
}
|
||||
|
||||
if (sbi->volume.ni)
|
||||
if (sbi->volume.ni) {
|
||||
iput(&sbi->volume.ni->vfs_inode);
|
||||
sbi->volume.ni = NULL;
|
||||
}
|
||||
|
||||
ntfs_update_mftmirr(sbi, 0);
|
||||
|
||||
@ -836,7 +858,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
int err;
|
||||
u32 mb, gb, boot_sector_size, sct_per_clst, record_size;
|
||||
u64 sectors, clusters, mlcn, mlcn2;
|
||||
u64 sectors, clusters, mlcn, mlcn2, dev_size0;
|
||||
struct NTFS_BOOT *boot;
|
||||
struct buffer_head *bh;
|
||||
struct MFT_REC *rec;
|
||||
@ -845,6 +867,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
u32 boot_off = 0;
|
||||
const char *hint = "Primary boot";
|
||||
|
||||
/* Save original dev_size. Used with alternative boot. */
|
||||
dev_size0 = dev_size;
|
||||
|
||||
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
|
||||
|
||||
bh = ntfs_bread(sb, 0);
|
||||
@ -853,6 +878,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
check_boot:
|
||||
err = -EINVAL;
|
||||
|
||||
/* Corrupted image; do not read OOB */
|
||||
if (bh->b_size - sizeof(*boot) < boot_off)
|
||||
goto out;
|
||||
|
||||
boot = (struct NTFS_BOOT *)Add2Ptr(bh->b_data, boot_off);
|
||||
|
||||
if (memcmp(boot->system_id, "NTFS ", sizeof("NTFS ") - 1)) {
|
||||
@ -899,9 +929,17 @@ check_boot:
|
||||
goto out;
|
||||
}
|
||||
|
||||
sbi->record_size = record_size =
|
||||
boot->record_size < 0 ? 1 << (-boot->record_size) :
|
||||
(u32)boot->record_size << cluster_bits;
|
||||
if (boot->record_size >= 0) {
|
||||
record_size = (u32)boot->record_size << cluster_bits;
|
||||
} else if (-boot->record_size <= MAXIMUM_SHIFT_BYTES_PER_MFT) {
|
||||
record_size = 1u << (-boot->record_size);
|
||||
} else {
|
||||
ntfs_err(sb, "%s: invalid record size %d.", hint,
|
||||
boot->record_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sbi->record_size = record_size;
|
||||
sbi->record_bits = blksize_bits(record_size);
|
||||
sbi->attr_size_tr = (5 * record_size >> 4); // ~320 bytes
|
||||
|
||||
@ -918,9 +956,15 @@ check_boot:
|
||||
goto out;
|
||||
}
|
||||
|
||||
sbi->index_size = boot->index_size < 0 ?
|
||||
1u << (-boot->index_size) :
|
||||
(u32)boot->index_size << cluster_bits;
|
||||
if (boot->index_size >= 0) {
|
||||
sbi->index_size = (u32)boot->index_size << cluster_bits;
|
||||
} else if (-boot->index_size <= MAXIMUM_SHIFT_BYTES_PER_INDEX) {
|
||||
sbi->index_size = 1u << (-boot->index_size);
|
||||
} else {
|
||||
ntfs_err(sb, "%s: invalid index size %d.", hint,
|
||||
boot->index_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check index record size. */
|
||||
if (sbi->index_size < SECTOR_SIZE || !is_power_of_2(sbi->index_size)) {
|
||||
@ -1055,17 +1099,17 @@ check_boot:
|
||||
|
||||
if (bh->b_blocknr && !sb_rdonly(sb)) {
|
||||
/*
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Do not update primary boot here 'cause it may be faked boot.
|
||||
* Let ntfs to be mounted and update boot later.
|
||||
*/
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Do not update primary boot here 'cause it may be faked boot.
|
||||
* Let ntfs to be mounted and update boot later.
|
||||
*/
|
||||
*boot2 = kmemdup(boot, sizeof(*boot), GFP_NOFS | __GFP_NOWARN);
|
||||
}
|
||||
|
||||
out:
|
||||
if (err == -EINVAL && !bh->b_blocknr && dev_size > PAGE_SHIFT) {
|
||||
if (err == -EINVAL && !bh->b_blocknr && dev_size0 > PAGE_SHIFT) {
|
||||
u32 block_size = min_t(u32, sector_size, PAGE_SIZE);
|
||||
u64 lbo = dev_size - sizeof(*boot);
|
||||
u64 lbo = dev_size0 - sizeof(*boot);
|
||||
|
||||
/*
|
||||
* Try alternative boot (last sector)
|
||||
@ -1079,6 +1123,7 @@ out:
|
||||
|
||||
boot_off = lbo & (block_size - 1);
|
||||
hint = "Alternative boot";
|
||||
dev_size = dev_size0; /* restore original size. */
|
||||
goto check_boot;
|
||||
}
|
||||
brelse(bh);
|
||||
@ -1367,7 +1412,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
}
|
||||
|
||||
bytes = inode->i_size;
|
||||
sbi->def_table = t = kmalloc(bytes, GFP_NOFS | __GFP_NOWARN);
|
||||
sbi->def_table = t = kvmalloc(bytes, GFP_KERNEL);
|
||||
if (!t) {
|
||||
err = -ENOMEM;
|
||||
goto put_inode_out;
|
||||
@ -1521,9 +1566,9 @@ load_root:
|
||||
|
||||
if (boot2) {
|
||||
/*
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Volume is recognized as NTFS. Update primary boot.
|
||||
*/
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Volume is recognized as NTFS. Update primary boot.
|
||||
*/
|
||||
struct buffer_head *bh0 = sb_getblk(sb, 0);
|
||||
if (bh0) {
|
||||
if (buffer_locked(bh0))
|
||||
@ -1564,6 +1609,7 @@ put_inode_out:
|
||||
out:
|
||||
ntfs3_put_sbi(sbi);
|
||||
kfree(boot2);
|
||||
ntfs3_put_sbi(sbi);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1757,7 +1803,6 @@ static int __init init_ntfs_fs(void)
|
||||
if (IS_ENABLED(CONFIG_NTFS3_LZX_XPRESS))
|
||||
pr_info("ntfs3: Read-only LZX/Xpress compression included\n");
|
||||
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
/* Create "/proc/fs/ntfs3" */
|
||||
proc_info_root = proc_mkdir("fs/ntfs3", NULL);
|
||||
@ -1799,7 +1844,6 @@ static void __exit exit_ntfs_fs(void)
|
||||
if (proc_info_root)
|
||||
remove_proc_entry("fs/ntfs3", NULL);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -211,7 +211,8 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
|
||||
size = le32_to_cpu(info->size);
|
||||
|
||||
/* Enumerate all xattrs. */
|
||||
for (ret = 0, off = 0; off < size; off += ea_size) {
|
||||
ret = 0;
|
||||
for (off = 0; off + sizeof(struct EA_FULL) < size; off += ea_size) {
|
||||
ea = Add2Ptr(ea_all, off);
|
||||
ea_size = unpacked_ea_size(ea);
|
||||
|
||||
@ -219,6 +220,10 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
|
||||
break;
|
||||
|
||||
if (buffer) {
|
||||
/* Check if we can use field ea->name */
|
||||
if (off + ea_size > size)
|
||||
break;
|
||||
|
||||
if (ret + ea->name_len + 1 > bytes_per_buffer) {
|
||||
err = -ERANGE;
|
||||
goto out;
|
||||
|
Loading…
Reference in New Issue
Block a user