Commit f69d00d1 authored by Linus Torvalds's avatar Linus Torvalds

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()
parents 7cf4bea7 e4494770
...@@ -1736,10 +1736,8 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, ...@@ -1736,10 +1736,8 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
le_b = NULL; le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL,
0, NULL, &mi_b); 0, NULL, &mi_b);
if (!attr_b) { if (!attr_b)
err = -ENOENT; return -ENOENT;
goto out;
}
attr = attr_b; attr = attr_b;
le = le_b; le = le_b;
......
...@@ -52,7 +52,8 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) ...@@ -52,7 +52,8 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
if (!attr->non_res) { if (!attr->non_res) {
lsize = le32_to_cpu(attr->res.data_size); 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) { if (!le) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
...@@ -80,7 +81,17 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) ...@@ -80,7 +81,17 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
if (err < 0) if (err < 0)
goto out; 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) { if (!le) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
......
...@@ -125,6 +125,7 @@ void wnd_close(struct wnd_bitmap *wnd) ...@@ -125,6 +125,7 @@ void wnd_close(struct wnd_bitmap *wnd)
struct rb_node *node, *next; struct rb_node *node, *next;
kfree(wnd->free_bits); kfree(wnd->free_bits);
wnd->free_bits = NULL;
run_close(&wnd->run); run_close(&wnd->run);
node = rb_first(&wnd->start_tree); node = rb_first(&wnd->start_tree);
...@@ -659,7 +660,8 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits) ...@@ -659,7 +660,8 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits)
wnd->bits_last = wbits; wnd->bits_last = wbits;
wnd->free_bits = 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) if (!wnd->free_bits)
return -ENOMEM; return -ENOMEM;
......
...@@ -309,6 +309,10 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, ...@@ -309,6 +309,10 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
return 0; return 0;
} }
/* 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; dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type); 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) ...@@ -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, static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, struct pipe_inode_info *pipe, size_t len,
size_t len, unsigned int flags) unsigned int flags)
{ {
struct inode *inode = in->f_mapping->host; struct inode *inode = in->f_mapping->host;
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
......
...@@ -2148,7 +2148,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) ...@@ -2148,7 +2148,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
for (i = 0; i < pages_per_frame; i++) { for (i = 0; i < pages_per_frame; i++) {
pg = pages[i]; pg = pages[i];
if (i == idx) if (i == idx || !pg)
continue; continue;
unlock_page(pg); unlock_page(pg);
put_page(pg); put_page(pg);
...@@ -3208,6 +3208,12 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, ...@@ -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))) if (!fname || !memcmp(&fname->dup, dup, sizeof(fname->dup)))
continue; 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. */ /* ntfs_iget5 may sleep. */
dir = ntfs_iget5(sb, &fname->home, NULL); dir = ntfs_iget5(sb, &fname->home, NULL);
if (IS_ERR(dir)) { if (IS_ERR(dir)) {
......
...@@ -2168,8 +2168,10 @@ static int last_log_lsn(struct ntfs_log *log) ...@@ -2168,8 +2168,10 @@ static int last_log_lsn(struct ntfs_log *log)
if (!page) { if (!page) {
page = kmalloc(log->page_size, GFP_NOFS); page = kmalloc(log->page_size, GFP_NOFS);
if (!page) if (!page) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
} }
/* /*
......
...@@ -983,18 +983,11 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) ...@@ -983,18 +983,11 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty)
if (err) if (err)
return err; return err;
mark_inode_dirty(&ni->vfs_inode); mark_inode_dirty_sync(&ni->vfs_inode);
/* verify(!ntfs_update_mftmirr()); */ /* verify(!ntfs_update_mftmirr()); */
/* /* write mft record on disk. */
* If we used wait=1, sync_inode_metadata waits for the io for the err = _ni_write_inode(&ni->vfs_inode, 1);
* 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);
return err; return err;
} }
...@@ -2461,10 +2454,12 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim) ...@@ -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; CLST end, i, zone_len, zlen;
struct wnd_bitmap *wnd = &sbi->used.bitmap; struct wnd_bitmap *wnd = &sbi->used.bitmap;
bool dirty = false;
down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
if (!wnd_is_used(wnd, lcn, len)) { 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; end = lcn + len;
len = 0; len = 0;
...@@ -2518,6 +2513,8 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim) ...@@ -2518,6 +2513,8 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim)
out: out:
up_write(&wnd->rw_lock); 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, ...@@ -729,6 +729,9 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx,
u32 total = le32_to_cpu(hdr->total); u32 total = le32_to_cpu(hdr->total);
u16 offs[128]; u16 offs[128];
if (unlikely(!cmp))
return NULL;
fill_table: fill_table:
if (end > total) if (end > total)
return NULL; return NULL;
......
...@@ -170,8 +170,8 @@ static struct inode *ntfs_read_mft(struct inode *inode, ...@@ -170,8 +170,8 @@ static struct inode *ntfs_read_mft(struct inode *inode,
nt2kernel(std5->cr_time, &ni->i_crtime); nt2kernel(std5->cr_time, &ni->i_crtime);
#endif #endif
nt2kernel(std5->a_time, &inode->i_atime); nt2kernel(std5->a_time, &inode->i_atime);
ctime = inode_get_ctime(inode);
nt2kernel(std5->c_time, &ctime); nt2kernel(std5->c_time, &ctime);
inode_set_ctime_to_ts(inode, ctime);
nt2kernel(std5->m_time, &inode->i_mtime); nt2kernel(std5->m_time, &inode->i_mtime);
ni->std_fa = std5->fa; ni->std_fa = std5->fa;
...@@ -1660,7 +1660,8 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, ...@@ -1660,7 +1660,8 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
/* Set original time. inode times (i_ctime) may be changed in ntfs_init_acl. */ /* 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); dir->i_mtime = inode_set_ctime_to_ts(dir, ni->i_crtime);
mark_inode_dirty(dir); mark_inode_dirty(dir);
......
...@@ -156,8 +156,8 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) ...@@ -156,8 +156,8 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de)
err = ntfs_link_inode(inode, de); err = ntfs_link_inode(inode, de);
if (!err) { if (!err) {
dir->i_mtime = inode_set_ctime_to_ts(inode, dir->i_mtime = inode_set_ctime_to_ts(
inode_set_ctime_current(dir)); inode, inode_set_ctime_current(dir));
mark_inode_dirty(inode); mark_inode_dirty(inode);
mark_inode_dirty(dir); mark_inode_dirty(dir);
d_instantiate(de, inode); d_instantiate(de, inode);
......
...@@ -42,9 +42,11 @@ enum utf16_endian; ...@@ -42,9 +42,11 @@ enum utf16_endian;
#define MINUS_ONE_T ((size_t)(-1)) #define MINUS_ONE_T ((size_t)(-1))
/* Biggest MFT / smallest cluster */ /* Biggest MFT / smallest cluster */
#define MAXIMUM_BYTES_PER_MFT 4096 #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 NTFS_BLOCKS_PER_MFT_RECORD (MAXIMUM_BYTES_PER_MFT / 512)
#define MAXIMUM_BYTES_PER_INDEX 4096 #define MAXIMUM_BYTES_PER_INDEX 4096
#define MAXIMUM_SHIFT_BYTES_PER_INDEX 12
#define NTFS_BLOCKS_PER_INODE (MAXIMUM_BYTES_PER_INDEX / 512) #define NTFS_BLOCKS_PER_INODE (MAXIMUM_BYTES_PER_INDEX / 512)
/* NTFS specific error code when fixup failed. */ /* NTFS specific error code when fixup failed. */
...@@ -495,8 +497,6 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, ...@@ -495,8 +497,6 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
struct kstat *stat, u32 request_mask, u32 flags); struct kstat *stat, u32 request_mask, u32 flags);
int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr); 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_file_open(struct inode *inode, struct file *file);
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len); __u64 start, __u64 len);
......
...@@ -189,12 +189,19 @@ int mi_read(struct mft_inode *mi, bool is_mft) ...@@ -189,12 +189,19 @@ int mi_read(struct mft_inode *mi, bool is_mft)
return err; 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) struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
{ {
const struct MFT_REC *rec = mi->mrec; const struct MFT_REC *rec = mi->mrec;
u32 used = le32_to_cpu(rec->used); u32 used = le32_to_cpu(rec->used);
u32 t32, off, asize; u32 t32, off, asize, prev_type;
u16 t16; u16 t16;
u64 data_size, alloc_size, tot_size;
if (!attr) { if (!attr) {
u32 total = le32_to_cpu(rec->total); u32 total = le32_to_cpu(rec->total);
...@@ -213,6 +220,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ...@@ -213,6 +220,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
if (!is_rec_inuse(rec)) if (!is_rec_inuse(rec))
return NULL; return NULL;
prev_type = 0;
attr = Add2Ptr(rec, off); attr = Add2Ptr(rec, off);
} else { } else {
/* Check if input attr inside record. */ /* Check if input attr inside record. */
...@@ -226,11 +234,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ...@@ -226,11 +234,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
return NULL; return NULL;
} }
if (off + asize < off) {
/* Overflow check. */ /* Overflow check. */
if (off + asize < off)
return NULL; return NULL;
}
prev_type = le32_to_cpu(attr->type);
attr = Add2Ptr(attr, asize); attr = Add2Ptr(attr, asize);
off += asize; off += asize;
} }
...@@ -250,7 +258,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ...@@ -250,7 +258,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
/* 0x100 is last known attribute for now. */ /* 0x100 is last known attribute for now. */
t32 = le32_to_cpu(attr->type); 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; return NULL;
/* Check overflow and boundary. */ /* Check overflow and boundary. */
...@@ -259,16 +271,15 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ...@@ -259,16 +271,15 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
/* Check size of attribute. */ /* Check size of attribute. */
if (!attr->non_res) { if (!attr->non_res) {
/* Check resident fields. */
if (asize < SIZEOF_RESIDENT) if (asize < SIZEOF_RESIDENT)
return NULL; return NULL;
t16 = le16_to_cpu(attr->res.data_off); t16 = le16_to_cpu(attr->res.data_off);
if (t16 > asize) if (t16 > asize)
return NULL; return NULL;
t32 = le32_to_cpu(attr->res.data_size); if (t16 + le32_to_cpu(attr->res.data_size) > asize)
if (t16 + t32 > asize)
return NULL; return NULL;
t32 = sizeof(short) * attr->name_len; t32 = sizeof(short) * attr->name_len;
...@@ -278,21 +289,52 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ...@@ -278,21 +289,52 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
return attr; return attr;
} }
/* Check some nonresident fields. */ /* Check nonresident fields. */
if (attr->name_len && if (attr->non_res != 1)
le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len >
le16_to_cpu(attr->nres.run_off)) {
return NULL; 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) if (asize + 8 < SIZEOF_NONRESIDENT)
return NULL; return NULL;
if (attr->nres.c_unit) if (attr->nres.c_unit)
return NULL; return NULL;
} else if (asize + 8 < SIZEOF_NONRESIDENT_EX) }
return NULL;
return attr; return attr;
} }
......
...@@ -453,15 +453,23 @@ static struct proc_dir_entry *proc_info_root; ...@@ -453,15 +453,23 @@ static struct proc_dir_entry *proc_info_root;
* ntfs3.1 * ntfs3.1
* cluster size * cluster size
* number of clusters * 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) static int ntfs3_volinfo(struct seq_file *m, void *o)
{ {
struct super_block *sb = m->private; struct super_block *sb = m->private;
struct ntfs_sb_info *sbi = sb->s_fs_info; struct ntfs_sb_info *sbi = sb->s_fs_info;
seq_printf(m, "ntfs%d.%d\n%u\n%zu\n", sbi->volume.major_ver, seq_printf(m, "ntfs%d.%d\n%u\n%zu\n\%zu\n%zu\n%s\n%s\n",
sbi->volume.minor_ver, sbi->cluster_size, sbi->volume.major_ver, sbi->volume.minor_ver,
sbi->used.bitmap.nbits); 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; return 0;
} }
...@@ -488,9 +496,13 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer, ...@@ -488,9 +496,13 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
{ {
int err; int err;
struct super_block *sb = pde_data(file_inode(file)); struct super_block *sb = pde_data(file_inode(file));
struct ntfs_sb_info *sbi = sb->s_fs_info;
ssize_t ret = count; 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) if (!label)
return -ENOMEM; return -ENOMEM;
...@@ -502,7 +514,7 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer, ...@@ -502,7 +514,7 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
while (ret > 0 && label[ret - 1] == '\n') while (ret > 0 && label[ret - 1] == '\n')
ret -= 1; ret -= 1;
err = ntfs_set_label(sbi, label, ret); err = ntfs_set_label(sb->s_fs_info, label, ret);
if (err < 0) { if (err < 0) {
ntfs_err(sb, "failed (%d) to write label", err); ntfs_err(sb, "failed (%d) to write label", err);
...@@ -576,20 +588,30 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi) ...@@ -576,20 +588,30 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)
wnd_close(&sbi->mft.bitmap); wnd_close(&sbi->mft.bitmap);
wnd_close(&sbi->used.bitmap); wnd_close(&sbi->used.bitmap);
if (sbi->mft.ni) if (sbi->mft.ni) {
iput(&sbi->mft.ni->vfs_inode); iput(&sbi->mft.ni->vfs_inode);
sbi->mft.ni = NULL;
}
if (sbi->security.ni) if (sbi->security.ni) {
iput(&sbi->security.ni->vfs_inode); iput(&sbi->security.ni->vfs_inode);
sbi->security.ni = NULL;
}
if (sbi->reparse.ni) if (sbi->reparse.ni) {
iput(&sbi->reparse.ni->vfs_inode); iput(&sbi->reparse.ni->vfs_inode);
sbi->reparse.ni = NULL;
}
if (sbi->objid.ni) if (sbi->objid.ni) {
iput(&sbi->objid.ni->vfs_inode); iput(&sbi->objid.ni->vfs_inode);
sbi->objid.ni = NULL;
}
if (sbi->volume.ni) if (sbi->volume.ni) {
iput(&sbi->volume.ni->vfs_inode); iput(&sbi->volume.ni->vfs_inode);
sbi->volume.ni = NULL;
}
ntfs_update_mftmirr(sbi, 0); ntfs_update_mftmirr(sbi, 0);
...@@ -836,7 +858,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -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; struct ntfs_sb_info *sbi = sb->s_fs_info;
int err; int err;
u32 mb, gb, boot_sector_size, sct_per_clst, record_size; 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 NTFS_BOOT *boot;
struct buffer_head *bh; struct buffer_head *bh;
struct MFT_REC *rec; struct MFT_REC *rec;
...@@ -845,6 +867,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -845,6 +867,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
u32 boot_off = 0; u32 boot_off = 0;
const char *hint = "Primary boot"; const char *hint = "Primary boot";
/* Save original dev_size. Used with alternative boot. */
dev_size0 = dev_size;
sbi->volume.blocks = dev_size >> PAGE_SHIFT; sbi->volume.blocks = dev_size >> PAGE_SHIFT;
bh = ntfs_bread(sb, 0); bh = ntfs_bread(sb, 0);
...@@ -853,6 +878,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -853,6 +878,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
check_boot: check_boot:
err = -EINVAL; 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); boot = (struct NTFS_BOOT *)Add2Ptr(bh->b_data, boot_off);
if (memcmp(boot->system_id, "NTFS ", sizeof("NTFS ") - 1)) { if (memcmp(boot->system_id, "NTFS ", sizeof("NTFS ") - 1)) {
...@@ -899,9 +929,17 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -899,9 +929,17 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
goto out; goto out;
} }
sbi->record_size = record_size = if (boot->record_size >= 0) {
boot->record_size < 0 ? 1 << (-boot->record_size) : record_size = (u32)boot->record_size << cluster_bits;
(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->record_bits = blksize_bits(record_size);
sbi->attr_size_tr = (5 * record_size >> 4); // ~320 bytes sbi->attr_size_tr = (5 * record_size >> 4); // ~320 bytes
...@@ -918,9 +956,15 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -918,9 +956,15 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
goto out; goto out;
} }
sbi->index_size = boot->index_size < 0 ? if (boot->index_size >= 0) {
1u << (-boot->index_size) : sbi->index_size = (u32)boot->index_size << cluster_bits;
(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. */ /* Check index record size. */
if (sbi->index_size < SECTOR_SIZE || !is_power_of_2(sbi->index_size)) { if (sbi->index_size < SECTOR_SIZE || !is_power_of_2(sbi->index_size)) {
...@@ -1063,9 +1107,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -1063,9 +1107,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
} }
out: 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); 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) * Try alternative boot (last sector)
...@@ -1079,6 +1123,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -1079,6 +1123,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
boot_off = lbo & (block_size - 1); boot_off = lbo & (block_size - 1);
hint = "Alternative boot"; hint = "Alternative boot";
dev_size = dev_size0; /* restore original size. */
goto check_boot; goto check_boot;
} }
brelse(bh); brelse(bh);
...@@ -1367,7 +1412,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) ...@@ -1367,7 +1412,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
} }
bytes = inode->i_size; bytes = inode->i_size;
sbi->def_table = t = kmalloc(bytes, GFP_NOFS | __GFP_NOWARN); sbi->def_table = t = kvmalloc(bytes, GFP_KERNEL);
if (!t) { if (!t) {
err = -ENOMEM; err = -ENOMEM;
goto put_inode_out; goto put_inode_out;
...@@ -1564,6 +1609,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) ...@@ -1564,6 +1609,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
out: out:
ntfs3_put_sbi(sbi); ntfs3_put_sbi(sbi);
kfree(boot2); kfree(boot2);
ntfs3_put_sbi(sbi);
return err; return err;
} }
...@@ -1757,7 +1803,6 @@ static int __init init_ntfs_fs(void) ...@@ -1757,7 +1803,6 @@ static int __init init_ntfs_fs(void)
if (IS_ENABLED(CONFIG_NTFS3_LZX_XPRESS)) if (IS_ENABLED(CONFIG_NTFS3_LZX_XPRESS))
pr_info("ntfs3: Read-only LZX/Xpress compression included\n"); pr_info("ntfs3: Read-only LZX/Xpress compression included\n");
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
/* Create "/proc/fs/ntfs3" */ /* Create "/proc/fs/ntfs3" */
proc_info_root = proc_mkdir("fs/ntfs3", NULL); proc_info_root = proc_mkdir("fs/ntfs3", NULL);
...@@ -1799,7 +1844,6 @@ static void __exit exit_ntfs_fs(void) ...@@ -1799,7 +1844,6 @@ static void __exit exit_ntfs_fs(void)
if (proc_info_root) if (proc_info_root)
remove_proc_entry("fs/ntfs3", NULL); remove_proc_entry("fs/ntfs3", NULL);
#endif #endif
} }
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -211,7 +211,8 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, ...@@ -211,7 +211,8 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
size = le32_to_cpu(info->size); size = le32_to_cpu(info->size);
/* Enumerate all xattrs. */ /* 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 = Add2Ptr(ea_all, off);
ea_size = unpacked_ea_size(ea); ea_size = unpacked_ea_size(ea);
...@@ -219,6 +220,10 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, ...@@ -219,6 +220,10 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
break; break;
if (buffer) { 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) { if (ret + ea->name_len + 1 > bytes_per_buffer) {
err = -ERANGE; err = -ERANGE;
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment