diff options
-rw-r--r-- | fs/ntfs3/attrib.c | 96 | ||||
-rw-r--r-- | fs/ntfs3/attrlist.c | 53 | ||||
-rw-r--r-- | fs/ntfs3/file.c | 185 | ||||
-rw-r--r-- | fs/ntfs3/frecord.c | 97 | ||||
-rw-r--r-- | fs/ntfs3/fslog.c | 19 | ||||
-rw-r--r-- | fs/ntfs3/inode.c | 20 | ||||
-rw-r--r-- | fs/ntfs3/lib/lzx_decompress.c | 3 | ||||
-rw-r--r-- | fs/ntfs3/lznt.c | 3 | ||||
-rw-r--r-- | fs/ntfs3/namei.c | 10 | ||||
-rw-r--r-- | fs/ntfs3/ntfs_fs.h | 10 | ||||
-rw-r--r-- | fs/ntfs3/record.c | 31 | ||||
-rw-r--r-- | fs/ntfs3/run.c | 8 | ||||
-rw-r--r-- | fs/ntfs3/super.c | 70 | ||||
-rw-r--r-- | fs/ntfs3/xattr.c | 2 |
14 files changed, 410 insertions, 197 deletions
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 6ede3e924dec..0763202d00c9 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -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; } @@ -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; } @@ -1517,6 +1519,9 @@ out: /* * 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) @@ -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; +} diff --git a/fs/ntfs3/attrlist.c b/fs/ntfs3/attrlist.c index 9f4bd8d26090..a4d74bed74fa 100644 --- a/fs/ntfs3/attrlist.c +++ b/fs/ntfs3/attrlist.c @@ -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; diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 6202895a4542..e370eaf9bfe2 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -82,13 +82,14 @@ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa) { struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = ntfs_i(inode); u32 flags = fa->flags; unsigned int new_fl = 0; if (fileattr_has_fsx(fa)) return -EOPNOTSUPP; - if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL)) + if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_COMPR_FL)) return -EOPNOTSUPP; if (flags & FS_IMMUTABLE_FL) @@ -97,6 +98,15 @@ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, if (flags & FS_APPEND_FL) new_fl |= S_APPEND; + /* Allowed to change compression for empty files and for directories only. */ + if (!is_dedup(ni) && !is_encrypted(ni) && + (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { + /* Change compress state. */ + int err = ni_set_compress(inode, flags & FS_COMPR_FL); + if (err) + return err; + } + inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND); inode_set_ctime_current(inode); @@ -407,6 +417,42 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count, err = 0; } + if (file && is_sparsed(ni)) { + /* + * This code optimizes large writes to sparse file. + * TODO: merge this fragment with fallocate fragment. + */ + struct ntfs_sb_info *sbi = ni->mi.sbi; + CLST vcn = pos >> sbi->cluster_bits; + CLST cend = bytes_to_cluster(sbi, end); + CLST cend_v = bytes_to_cluster(sbi, ni->i_valid); + CLST lcn, clen; + bool new; + + if (cend_v > cend) + cend_v = cend; + + /* + * Allocate and zero new clusters. + * Zeroing these clusters may be too long. + */ + for (; vcn < cend_v; vcn += clen) { + err = attr_data_get_block(ni, vcn, cend_v - vcn, &lcn, + &clen, &new, true); + if (err) + goto out; + } + /* + * Allocate but not zero new clusters. + */ + for (; vcn < cend; vcn += clen) { + err = attr_data_get_block(ni, vcn, cend - vcn, &lcn, + &clen, &new, false); + if (err) + goto out; + } + } + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); mark_inode_dirty(inode); @@ -483,7 +529,7 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size) } /* - * ntfs_fallocate + * ntfs_fallocate - file_operations::ntfs_fallocate * * Preallocate space for a file. This implements ntfs's fallocate file * operation, which gets called from sys_fallocate system call. User @@ -618,6 +664,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) ni_lock(ni); err = attr_collapse_range(ni, vbo, len); ni_unlock(ni); + if (err) + goto out; } else if (mode & FALLOC_FL_INSERT_RANGE) { /* Check new size. */ err = inode_newsize_ok(inode, new_size); @@ -740,10 +788,10 @@ out: } /* - * ntfs3_setattr - inode_operations::setattr + * ntfs_setattr - inode_operations::setattr */ -int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, - struct iattr *attr) +int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) { struct inode *inode = d_inode(dentry); struct ntfs_inode *ni = ntfs_i(inode); @@ -803,10 +851,12 @@ out: return err; } -static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) +/* + * check_read_restriction: + * common code for ntfs_file_read_iter and ntfs_file_splice_read + */ +static int check_read_restriction(struct inode *inode) { - struct file *file = iocb->ki_filp; - struct inode *inode = file_inode(file); struct ntfs_inode *ni = ntfs_i(inode); if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) @@ -817,11 +867,6 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) return -EOPNOTSUPP; } - if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) { - ntfs_inode_warn(inode, "direct i/o + compressed not supported"); - return -EOPNOTSUPP; - } - #ifndef CONFIG_NTFS3_LZX_XPRESS if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) { ntfs_inode_warn( @@ -836,37 +881,44 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) return -EOPNOTSUPP; } - return generic_file_read_iter(iocb, iter); + return 0; } -static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +/* + * ntfs_file_read_iter - file_operations::read_iter + */ +static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) { - struct inode *inode = file_inode(in); + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); struct ntfs_inode *ni = ntfs_i(inode); + ssize_t err; - if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) - return -EIO; + err = check_read_restriction(inode); + if (err) + return err; - if (is_encrypted(ni)) { - ntfs_inode_warn(inode, "encrypted i/o not supported"); + if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) { + ntfs_inode_warn(inode, "direct i/o + compressed not supported"); return -EOPNOTSUPP; } -#ifndef CONFIG_NTFS3_LZX_XPRESS - if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) { - ntfs_inode_warn( - inode, - "activate CONFIG_NTFS3_LZX_XPRESS to read external compressed files"); - return -EOPNOTSUPP; - } -#endif + return generic_file_read_iter(iocb, iter); +} - if (is_dedup(ni)) { - ntfs_inode_warn(inode, "read deduplicated not supported"); - return -EOPNOTSUPP; - } +/* + * ntfs_file_splice_read - file_operations::splice_read + */ +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 inode *inode = file_inode(in); + ssize_t err; + + err = check_read_restriction(inode); + if (err) + return err; return filemap_splice_read(in, ppos, pipe, len, flags); } @@ -1134,14 +1186,11 @@ out: } /* - * ntfs_file_write_iter - file_operations::write_iter + * check_write_restriction: + * common code for ntfs_file_write_iter and ntfs_file_splice_write */ -static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +static int check_write_restriction(struct inode *inode) { - struct file *file = iocb->ki_filp; - struct inode *inode = file_inode(file); - ssize_t ret; - int err; struct ntfs_inode *ni = ntfs_i(inode); if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) @@ -1152,13 +1201,31 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) return -EOPNOTSUPP; } - if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) { - ntfs_inode_warn(inode, "direct i/o + compressed not supported"); + if (is_dedup(ni)) { + ntfs_inode_warn(inode, "write into deduplicated not supported"); return -EOPNOTSUPP; } - if (is_dedup(ni)) { - ntfs_inode_warn(inode, "write into deduplicated not supported"); + return 0; +} + +/* + * ntfs_file_write_iter - file_operations::write_iter + */ +static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct ntfs_inode *ni = ntfs_i(inode); + ssize_t ret; + int err; + + err = check_write_restriction(inode); + if (err) + return err; + + if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) { + ntfs_inode_warn(inode, "direct i/o + compressed not supported"); return -EOPNOTSUPP; } @@ -1246,7 +1313,14 @@ static int ntfs_file_release(struct inode *inode, struct file *file) /* If we are last writer on the inode, drop the block reservation. */ if (sbi->options->prealloc && ((file->f_mode & FMODE_WRITE) && - atomic_read(&inode->i_writecount) == 1)) { + atomic_read(&inode->i_writecount) == 1) + /* + * The only file when inode->i_fop = &ntfs_file_operations and + * init_rwsem(&ni->file.run_lock) is not called explicitly is MFT. + * + * Add additional check here. + */ + && inode->i_ino != MFT_REC_MFT) { ni_lock(ni); down_write(&ni->file.run_lock); @@ -1282,10 +1356,27 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, return err; } +/* + * ntfs_file_splice_write - file_operations::splice_write + */ +static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe, + struct file *file, loff_t *ppos, + size_t len, unsigned int flags) +{ + ssize_t err; + struct inode *inode = file_inode(file); + + err = check_write_restriction(inode); + if (err) + return err; + + return iter_file_splice_write(pipe, file, ppos, len, flags); +} + // clang-format off const struct inode_operations ntfs_file_inode_operations = { .getattr = ntfs_getattr, - .setattr = ntfs3_setattr, + .setattr = ntfs_setattr, .listxattr = ntfs_listxattr, .get_acl = ntfs_get_acl, .set_acl = ntfs_set_acl, @@ -1303,10 +1394,10 @@ const struct file_operations ntfs_file_operations = { .compat_ioctl = ntfs_compat_ioctl, #endif .splice_read = ntfs_file_splice_read, + .splice_write = ntfs_file_splice_write, .mmap = ntfs_file_mmap, .open = ntfs_file_open, .fsync = generic_file_fsync, - .splice_write = iter_file_splice_write, .fallocate = ntfs_fallocate, .release = ntfs_file_release, }; diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index a469c608a394..41c7ffad2790 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -102,7 +102,9 @@ void ni_clear(struct ntfs_inode *ni) { struct rb_node *node; - if (!ni->vfs_inode.i_nlink && ni->mi.mrec && is_rec_inuse(ni->mi.mrec)) + if (!ni->vfs_inode.i_nlink && ni->mi.mrec && + is_rec_inuse(ni->mi.mrec) && + !(ni->mi.sbi->flags & NTFS_FLAGS_LOG_REPLAYING)) ni_delete_all(ni); al_destroy(ni); @@ -1900,13 +1902,13 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, /* * fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent - * but it accepts kernel address for fi_extents_start + * but it uses 'fe_k' instead of fieinfo->fi_extents_start */ static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo, - u64 logical, u64 phys, u64 len, u32 flags) + struct fiemap_extent *fe_k, u64 logical, + u64 phys, u64 len, u32 flags) { struct fiemap_extent extent; - struct fiemap_extent __user *dest = fieinfo->fi_extents_start; /* only count the extents */ if (fieinfo->fi_extents_max == 0) { @@ -1930,8 +1932,7 @@ static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo, extent.fe_length = len; extent.fe_flags = flags; - dest += fieinfo->fi_extents_mapped; - memcpy(dest, &extent, sizeof(extent)); + memcpy(fe_k + fieinfo->fi_extents_mapped, &extent, sizeof(extent)); fieinfo->fi_extents_mapped++; if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max) @@ -1949,7 +1950,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, __u64 vbo, __u64 len) { int err = 0; - struct fiemap_extent __user *fe_u = fieinfo->fi_extents_start; struct fiemap_extent *fe_k = NULL; struct ntfs_sb_info *sbi = ni->mi.sbi; u8 cluster_bits = sbi->cluster_bits; @@ -2008,7 +2008,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, err = -ENOMEM; goto out; } - fieinfo->fi_extents_start = fe_k; end = vbo + len; alloc_size = le64_to_cpu(attr->nres.alloc_size); @@ -2098,8 +2097,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (vbo + dlen >= end) flags |= FIEMAP_EXTENT_LAST; - err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, dlen, - flags); + err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, + dlen, flags); if (err < 0) break; @@ -2120,7 +2119,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (vbo + bytes >= end) flags |= FIEMAP_EXTENT_LAST; - err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, bytes, + err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, bytes, flags); if (err < 0) break; @@ -2137,15 +2136,13 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, /* * Copy to user memory out of lock */ - if (copy_to_user(fe_u, fe_k, + if (copy_to_user(fieinfo->fi_extents_start, fe_k, fieinfo->fi_extents_max * sizeof(struct fiemap_extent))) { err = -EFAULT; } out: - /* Restore original pointer. */ - fieinfo->fi_extents_start = fe_u; kfree(fe_k); return err; } @@ -3455,3 +3452,75 @@ out: return 0; } + +/* + * ni_set_compress + * + * Helper for 'ntfs_fileattr_set'. + * Changes compression for empty files and directories only. + */ +int ni_set_compress(struct inode *inode, bool compr) +{ + int err; + struct ntfs_inode *ni = ntfs_i(inode); + struct ATTR_STD_INFO *std; + const char *bad_inode; + + if (is_compressed(ni) == !!compr) + return 0; + + if (is_sparsed(ni)) { + /* sparse and compress not compatible. */ + return -EOPNOTSUPP; + } + + if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) { + /*Skip other inodes. (symlink,fifo,...) */ + return -EOPNOTSUPP; + } + + bad_inode = NULL; + + ni_lock(ni); + + std = ni_std(ni); + if (!std) { + bad_inode = "no std"; + goto out; + } + + if (S_ISREG(inode->i_mode)) { + err = attr_set_compress(ni, compr); + if (err) { + if (err == -ENOENT) { + /* Fix on the fly? */ + /* Each file must contain data attribute. */ + bad_inode = "no data attribute"; + } + goto out; + } + } + + ni->std_fa = std->fa; + if (compr) + std->fa |= FILE_ATTRIBUTE_COMPRESSED; + else + std->fa &= ~FILE_ATTRIBUTE_COMPRESSED; + + if (ni->std_fa != std->fa) { + ni->std_fa = std->fa; + ni->mi.dirty = true; + } + /* update duplicate information and directory entries in ni_write_inode.*/ + ni->ni_flags |= NI_FLAG_UPDATE_PARENT; + err = 0; + +out: + ni_unlock(ni); + if (bad_inode) { + ntfs_bad_inode(inode, bad_inode); + err = -EINVAL; + } + + return err; +} diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index c64dd114ac65..d0d530f4e2b9 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -609,14 +609,29 @@ static inline void add_client(struct CLIENT_REC *ca, u16 index, __le16 *head) *head = cpu_to_le16(index); } +/* + * Enumerate restart table. + * + * @t - table to enumerate. + * @c - current enumerated element. + * + * enumeration starts with @c == NULL + * returns next element or NULL + */ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) { __le32 *e; u32 bprt; - u16 rsize = t ? le16_to_cpu(t->size) : 0; + u16 rsize; + + if (!t) + return NULL; + + rsize = le16_to_cpu(t->size); if (!c) { - if (!t || !t->total) + /* start enumeration. */ + if (!t->total) return NULL; e = Add2Ptr(t, sizeof(struct RESTART_TABLE)); } else { diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index f672072e6bd4..be04d2845bb7 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -536,11 +536,15 @@ struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, if (inode->i_state & I_NEW) inode = ntfs_read_mft(inode, name, ref); else if (ref->seq != ntfs_i(inode)->mi.mrec->seq) { - /* Inode overlaps? */ - _ntfs_bad_inode(inode); + /* + * Sequence number is not expected. + * Looks like inode was reused but caller uses the old reference + */ + iput(inode); + inode = ERR_PTR(-ESTALE); } - if (IS_ERR(inode) && name) + if (IS_ERR(inode)) ntfs_set_state(sb->s_fs_info, NTFS_DIRTY_ERROR); return inode; @@ -605,7 +609,8 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, bytes = ((u64)len << cluster_bits) - off; - if (lcn == SPARSE_LCN) { + if (lcn >= sbi->used.bitmap.nbits) { + /* This case includes resident/compressed/sparse. */ if (!create) { if (bh->b_size > bytes) bh->b_size = bytes; @@ -1672,7 +1677,10 @@ out6: attr = ni_find_attr(ni, NULL, NULL, ATTR_EA, NULL, 0, NULL, NULL); if (attr && attr->non_res) { /* Delete ATTR_EA, if non-resident. */ - attr_set_size(ni, ATTR_EA, NULL, 0, NULL, 0, NULL, false, NULL); + struct runs_tree run; + run_init(&run); + attr_set_size(ni, ATTR_EA, NULL, 0, &run, 0, NULL, false, NULL); + run_close(&run); } if (rp_inserted) @@ -2076,7 +2084,7 @@ static const char *ntfs_get_link(struct dentry *de, struct inode *inode, // clang-format off const struct inode_operations ntfs_link_inode_operations = { .get_link = ntfs_get_link, - .setattr = ntfs3_setattr, + .setattr = ntfs_setattr, .listxattr = ntfs_listxattr, }; diff --git a/fs/ntfs3/lib/lzx_decompress.c b/fs/ntfs3/lib/lzx_decompress.c index 6b16f07073c1..4d5701024f83 100644 --- a/fs/ntfs3/lib/lzx_decompress.c +++ b/fs/ntfs3/lib/lzx_decompress.c @@ -512,8 +512,7 @@ static int lzx_decompress_block(const struct lzx_decompressor *d, * the same code. (For R0, the swap is a no-op.) */ match_offset = recent_offsets[offset_slot]; - recent_offsets[offset_slot] = recent_offsets[0]; - recent_offsets[0] = match_offset; + swap(recent_offsets[offset_slot], recent_offsets[0]); } else { /* Explicit offset */ diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c index 4aae598d6d88..fdc9b2ebf341 100644 --- a/fs/ntfs3/lznt.c +++ b/fs/ntfs3/lznt.c @@ -236,6 +236,9 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, /* Do decompression until pointers are inside range. */ while (up < unc_end && cmpr < cmpr_end) { + // return err if more than LZNT_CHUNK_SIZE bytes are written + if (up - unc > LZNT_CHUNK_SIZE) + return -EINVAL; /* Correct index */ while (unc + s_max_off[index] < up) index += 1; diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index f16d318c4372..abf7e81584a9 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -81,7 +81,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, if (err < 0) inode = ERR_PTR(err); else { - ni_lock(ni); + ni_lock_dir(ni); inode = dir_search_u(dir, uni, NULL); ni_unlock(ni); } @@ -395,7 +395,7 @@ static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name) /* * Try slow way with current upcase table */ - uni = __getname(); + uni = kmem_cache_alloc(names_cachep, GFP_NOWAIT); if (!uni) return -ENOMEM; @@ -417,7 +417,7 @@ static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name) err = 0; out: - __putname(uni); + kmem_cache_free(names_cachep, uni); return err; } @@ -503,7 +503,7 @@ const struct inode_operations ntfs_dir_inode_operations = { .rename = ntfs_rename, .get_acl = ntfs_get_acl, .set_acl = ntfs_set_acl, - .setattr = ntfs3_setattr, + .setattr = ntfs_setattr, .getattr = ntfs_getattr, .listxattr = ntfs_listxattr, .fiemap = ntfs_fiemap, @@ -512,7 +512,7 @@ const struct inode_operations ntfs_dir_inode_operations = { }; const struct inode_operations ntfs_special_inode_operations = { - .setattr = ntfs3_setattr, + .setattr = ntfs_setattr, .getattr = ntfs_getattr, .listxattr = ntfs_listxattr, .get_acl = ntfs_get_acl, diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 584f814715f4..26e1e1379c04 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -334,7 +334,7 @@ struct mft_inode { /* Nested class for ntfs_inode::ni_lock. */ enum ntfs_inode_mutex_lock_class { - NTFS_INODE_MUTEX_DIRTY, + NTFS_INODE_MUTEX_DIRTY = 1, NTFS_INODE_MUTEX_SECURITY, NTFS_INODE_MUTEX_OBJID, NTFS_INODE_MUTEX_REPARSE, @@ -453,6 +453,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size); int attr_force_nonresident(struct ntfs_inode *ni); +int attr_set_compress(struct ntfs_inode *ni, bool compr); /* Functions from attrlist.c */ void al_destroy(struct ntfs_inode *ni); @@ -471,8 +472,6 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref, struct ATTR_LIST_ENTRY **new_le); bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le); -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); int al_update(struct ntfs_inode *ni, int sync); static inline size_t al_aligned(size_t size) { @@ -502,8 +501,8 @@ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa); 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); +int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); 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); @@ -588,6 +587,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, bool *is_bad); bool ni_is_dirty(struct inode *inode); +int ni_set_compress(struct inode *inode, bool compr); /* Globals from fslog.c */ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes); diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c index 6c76503edc20..f810f0419d25 100644 --- a/fs/ntfs3/record.c +++ b/fs/ntfs3/record.c @@ -223,29 +223,21 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) prev_type = 0; attr = Add2Ptr(rec, off); } else { - /* Check if input attr inside record. */ + /* + * We don't need to check previous attr here. There is + * a bounds checking in the previous round. + */ off = PtrOffset(rec, attr); - if (off >= used) - return NULL; asize = le32_to_cpu(attr->size); - if (asize < SIZEOF_RESIDENT) { - /* Impossible 'cause we should not return such attribute. */ - return NULL; - } - - /* Overflow check. */ - if (off + asize < off) - return NULL; prev_type = le32_to_cpu(attr->type); attr = Add2Ptr(attr, asize); off += asize; } - asize = le32_to_cpu(attr->size); - /* Can we use the first field (attr->type). */ + /* NOTE: this code also checks attr->size availability. */ if (off + 8 > used) { static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8); return NULL; @@ -265,6 +257,8 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) if (t32 < prev_type) return NULL; + asize = le32_to_cpu(attr->size); + /* Check overflow and boundary. */ if (off + asize < off || off + asize > used) return NULL; @@ -293,6 +287,10 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) if (attr->non_res != 1) return NULL; + /* Can we use memory including attr->nres.valid_size? */ + if (asize < SIZEOF_NONRESIDENT) + return NULL; + t16 = le16_to_cpu(attr->nres.run_off); if (t16 > asize) return NULL; @@ -319,7 +317,8 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) if (!attr->nres.svcn && is_attr_ext(attr)) { /* First segment of sparse/compressed attribute */ - if (asize + 8 < SIZEOF_NONRESIDENT_EX) + /* Can we use memory including attr->nres.total_size? */ + if (asize < SIZEOF_NONRESIDENT_EX) return NULL; tot_size = le64_to_cpu(attr->nres.total_size); @@ -329,10 +328,10 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) if (tot_size > alloc_size) return NULL; } else { - if (asize + 8 < SIZEOF_NONRESIDENT) + if (attr->nres.c_unit) return NULL; - if (attr->nres.c_unit) + if (alloc_size > mi->sbi->volume.size) return NULL; } diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index cb8cf0161177..58e988cd8049 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -959,7 +959,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Large positive number requires to store 5 bytes * e.g.: 05 FF 7E FF FF 00 00 00 */ - if (size_size > 8) + if (size_size > sizeof(len)) return -EINVAL; len = run_unpack_s64(run_buf, size_size, 0); @@ -971,7 +971,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, if (!offset_size) lcn = SPARSE_LCN64; - else if (offset_size <= 8) { + else if (offset_size <= sizeof(s64)) { s64 dlcn; /* Initial value of dlcn is -1 or 0. */ @@ -984,8 +984,10 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, return -EINVAL; lcn = prev_lcn + dlcn; prev_lcn = lcn; - } else + } else { + /* The size of 'dlcn' can't be > 8. */ return -EINVAL; + } next_vcn = vcn64 + len; /* Check boundary. */ diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index a8758b85803f..6a0f6b0a3ab2 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -90,7 +90,7 @@ void ntfs_printk(const struct super_block *sb, const char *fmt, ...) level = printk_get_level(fmt); vaf.fmt = printk_skip_level(fmt); vaf.va = &args; - printk("%c%cntfs3: %s: %pV\n", KERN_SOH_ASCII, level, sb->s_id, &vaf); + printk("%c%cntfs3(%s): %pV\n", KERN_SOH_ASCII, level, sb->s_id, &vaf); va_end(args); } @@ -124,10 +124,15 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) struct dentry *de = d_find_alias(inode); if (de) { + int len; spin_lock(&de->d_lock); - snprintf(name, sizeof(s_name_buf), " \"%s\"", - de->d_name.name); + len = snprintf(name, sizeof(s_name_buf), " \"%s\"", + de->d_name.name); spin_unlock(&de->d_lock); + if (len <= 0) + name[0] = 0; + else if (len >= sizeof(s_name_buf)) + name[sizeof(s_name_buf) - 1] = 0; } else { name[0] = 0; } @@ -140,7 +145,7 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) vaf.fmt = printk_skip_level(fmt); vaf.va = &args; - printk("%c%cntfs3: %s: ino=%lx,%s %pV\n", KERN_SOH_ASCII, level, + printk("%c%cntfs3(%s): ino=%lx,%s %pV\n", KERN_SOH_ASCII, level, sb->s_id, inode->i_ino, name ? name : "", &vaf); va_end(args); @@ -259,23 +264,23 @@ enum Opt { // clang-format off static const struct fs_parameter_spec ntfs_fs_parameters[] = { - fsparam_uid("uid", Opt_uid), - fsparam_gid("gid", Opt_gid), - fsparam_u32oct("umask", Opt_umask), - fsparam_u32oct("dmask", Opt_dmask), - fsparam_u32oct("fmask", Opt_fmask), - fsparam_flag_no("sys_immutable", Opt_immutable), - fsparam_flag_no("discard", Opt_discard), - fsparam_flag_no("force", Opt_force), - fsparam_flag_no("sparse", Opt_sparse), - fsparam_flag_no("hidden", Opt_nohidden), - fsparam_flag_no("hide_dot_files", Opt_hide_dot_files), - fsparam_flag_no("windows_names", Opt_windows_names), - fsparam_flag_no("showmeta", Opt_showmeta), - fsparam_flag_no("acl", Opt_acl), - fsparam_string("iocharset", Opt_iocharset), - fsparam_flag_no("prealloc", Opt_prealloc), - fsparam_flag_no("case", Opt_nocase), + fsparam_uid("uid", Opt_uid), + fsparam_gid("gid", Opt_gid), + fsparam_u32oct("umask", Opt_umask), + fsparam_u32oct("dmask", Opt_dmask), + fsparam_u32oct("fmask", Opt_fmask), + fsparam_flag("sys_immutable", Opt_immutable), + fsparam_flag("discard", Opt_discard), + fsparam_flag("force", Opt_force), + fsparam_flag("sparse", Opt_sparse), + fsparam_flag("nohidden", Opt_nohidden), + fsparam_flag("hide_dot_files", Opt_hide_dot_files), + fsparam_flag("windows_names", Opt_windows_names), + fsparam_flag("showmeta", Opt_showmeta), + fsparam_flag("acl", Opt_acl), + fsparam_string("iocharset", Opt_iocharset), + fsparam_flag("prealloc", Opt_prealloc), + fsparam_flag("nocase", Opt_nocase), {} }; // clang-format on @@ -345,28 +350,28 @@ static int ntfs_fs_parse_param(struct fs_context *fc, opts->fmask = 1; break; case Opt_immutable: - opts->sys_immutable = result.negated ? 0 : 1; + opts->sys_immutable = 1; break; case Opt_discard: - opts->discard = result.negated ? 0 : 1; + opts->discard = 1; break; case Opt_force: - opts->force = result.negated ? 0 : 1; + opts->force = 1; break; case Opt_sparse: - opts->sparse = result.negated ? 0 : 1; + opts->sparse = 1; break; case Opt_nohidden: - opts->nohidden = result.negated ? 1 : 0; + opts->nohidden = 1; break; case Opt_hide_dot_files: - opts->hide_dot_files = result.negated ? 0 : 1; + opts->hide_dot_files = 1; break; case Opt_windows_names: - opts->windows_names = result.negated ? 0 : 1; + opts->windows_names = 1; break; case Opt_showmeta: - opts->showmeta = result.negated ? 0 : 1; + opts->showmeta = 1; break; case Opt_acl: if (!result.negated) @@ -385,10 +390,10 @@ static int ntfs_fs_parse_param(struct fs_context *fc, param->string = NULL; break; case Opt_prealloc: - opts->prealloc = result.negated ? 0 : 1; + opts->prealloc = 1; break; case Opt_nocase: - opts->nocase = result.negated ? 1 : 0; + opts->nocase = 1; break; default: /* Should not be here unless we forget add case. */ @@ -1491,11 +1496,10 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) #ifdef __BIG_ENDIAN { - const __le16 *src = sbi->upcase; u16 *dst = sbi->upcase; for (i = 0; i < 0x10000; i++) - *dst++ = le16_to_cpu(*src++); + __swab16s(dst++); } #endif diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 0703e1ae32b2..e0055dcf8fe3 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -705,7 +705,7 @@ int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, #endif /* - * ntfs_acl_chmod - Helper for ntfs3_setattr(). + * ntfs_acl_chmod - Helper for ntfs_setattr(). */ int ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry) { |