summaryrefslogtreecommitdiff
path: root/fs/ntfs3/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ntfs3/file.c')
-rw-r--r--fs/ntfs3/file.c289
1 files changed, 183 insertions, 106 deletions
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index e370eaf9bfe2..2e7b2e566ebe 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -19,6 +19,12 @@
#include "ntfs.h"
#include "ntfs_fs.h"
+/*
+ * cifx, btrfs, exfat, ext4, f2fs use this constant.
+ * Hope this value will become common to all fs.
+ */
+#define NTFS3_IOC_SHUTDOWN _IOR('X', 125, __u32)
+
static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
{
struct fstrim_range __user *user_range;
@@ -49,70 +55,61 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
return 0;
}
-/*
- * ntfs_fileattr_get - inode_operations::fileattr_get
- */
-int ntfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+static int ntfs_ioctl_get_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
{
- struct inode *inode = d_inode(dentry);
- struct ntfs_inode *ni = ntfs_i(inode);
- u32 flags = 0;
+ if (copy_to_user(buf, sbi->volume.label, FSLABEL_MAX))
+ return -EFAULT;
- if (inode->i_flags & S_IMMUTABLE)
- flags |= FS_IMMUTABLE_FL;
+ return 0;
+}
- if (inode->i_flags & S_APPEND)
- flags |= FS_APPEND_FL;
+static int ntfs_ioctl_set_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
+{
+ u8 user[FSLABEL_MAX] = { 0 };
+ int len;
- if (is_compressed(ni))
- flags |= FS_COMPR_FL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
- if (is_encrypted(ni))
- flags |= FS_ENCRYPT_FL;
+ if (copy_from_user(user, buf, FSLABEL_MAX))
+ return -EFAULT;
- fileattr_fill_flags(fa, flags);
+ len = strnlen(user, FSLABEL_MAX);
- return 0;
+ return ntfs_set_label(sbi, user, len);
}
/*
- * ntfs_fileattr_set - inode_operations::fileattr_set
+ * ntfs_force_shutdown - helper function. Called from ioctl
*/
-int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
- struct fileattr *fa)
+static int ntfs_force_shutdown(struct super_block *sb, u32 flags)
{
- 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 | FS_COMPR_FL))
- return -EOPNOTSUPP;
+ int err;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
- if (flags & FS_IMMUTABLE_FL)
- new_fl |= S_IMMUTABLE;
+ if (unlikely(ntfs3_forced_shutdown(sb)))
+ return 0;
- if (flags & FS_APPEND_FL)
- new_fl |= S_APPEND;
+ /* No additional options yet (flags). */
+ err = bdev_freeze(sb->s_bdev);
+ if (err)
+ return err;
+ set_bit(NTFS_FLAGS_SHUTDOWN_BIT, &sbi->flags);
+ bdev_thaw(sb->s_bdev);
+ return 0;
+}
- /* 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;
- }
+static int ntfs_ioctl_shutdown(struct super_block *sb, unsigned long arg)
+{
+ u32 flags;
- inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND);
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
- inode_set_ctime_current(inode);
- mark_inode_dirty(inode);
+ if (get_user(flags, (__u32 __user *)arg))
+ return -EFAULT;
- return 0;
+ return ntfs_force_shutdown(sb, flags);
}
/*
@@ -121,11 +118,22 @@ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
- struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
+ struct super_block *sb = inode->i_sb;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
+
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ntfs_i(inode))))
+ return -EINVAL;
switch (cmd) {
case FITRIM:
return ntfs_ioctl_fitrim(sbi, arg);
+ case FS_IOC_GETFSLABEL:
+ return ntfs_ioctl_get_volume_label(sbi, (u8 __user *)arg);
+ case FS_IOC_SETFSLABEL:
+ return ntfs_ioctl_set_volume_label(sbi, (u8 __user *)arg);
+ case NTFS3_IOC_SHUTDOWN:
+ return ntfs_ioctl_shutdown(sb, arg);
}
return -ENOTTY; /* Inappropriate ioctl for device. */
}
@@ -147,6 +155,10 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
struct inode *inode = d_inode(path->dentry);
struct ntfs_inode *ni = ntfs_i(inode);
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
stat->result_mask |= STATX_BTIME;
stat->btime = ni->i_crtime;
stat->blksize = ni->mi.sbi->cluster_size; /* 512, 1K, ..., 2M */
@@ -182,13 +194,15 @@ static int ntfs_extend_initialized_size(struct file *file,
loff_t pos = valid;
int err;
+ if (valid >= new_valid)
+ return 0;
+
if (is_resident(ni)) {
ni->i_valid = new_valid;
return 0;
}
WARN_ON(is_compressed(ni));
- WARN_ON(valid >= new_valid);
for (;;) {
u32 zerofrom, len;
@@ -218,13 +232,13 @@ static int ntfs_extend_initialized_size(struct file *file,
if (pos + len > new_valid)
len = new_valid - pos;
- err = ntfs_write_begin(file, mapping, pos, len, &folio, NULL);
+ err = ntfs_write_begin(NULL, mapping, pos, len, &folio, NULL);
if (err)
goto out;
- folio_zero_range(folio, zerofrom, folio_size(folio));
+ folio_zero_range(folio, zerofrom, folio_size(folio) - zerofrom);
- err = ntfs_write_end(file, mapping, pos, len, len, folio, NULL);
+ err = ntfs_write_end(NULL, mapping, pos, len, len, folio, NULL);
if (err < 0)
goto out;
pos += len;
@@ -325,16 +339,21 @@ out:
}
/*
- * ntfs_file_mmap - file_operations::mmap
+ * ntfs_file_mmap_prepare - file_operations::mmap_prepare
*/
-static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
+static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
{
+ struct file *file = desc->file;
struct inode *inode = file_inode(file);
struct ntfs_inode *ni = ntfs_i(inode);
- u64 from = ((u64)vma->vm_pgoff << PAGE_SHIFT);
- bool rw = vma->vm_flags & VM_WRITE;
+ u64 from = ((u64)desc->pgoff << PAGE_SHIFT);
+ bool rw = desc->vm_flags & VM_WRITE;
int err;
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
@@ -348,14 +367,19 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
return -EOPNOTSUPP;
}
- if (is_compressed(ni) && rw) {
- ntfs_inode_warn(inode, "mmap(write) compressed not supported");
- return -EOPNOTSUPP;
+ if (is_compressed(ni)) {
+ if (rw) {
+ ntfs_inode_warn(inode,
+ "mmap(write) compressed not supported");
+ return -EOPNOTSUPP;
+ }
+ /* Turn off readahead for compressed files. */
+ file->f_ra.ra_pages = 0;
}
if (rw) {
u64 to = min_t(loff_t, i_size_read(inode),
- from + vma->vm_end - vma->vm_start);
+ from + vma_desc_size(desc));
if (is_sparsed(ni)) {
/* Allocate clusters for rw map. */
@@ -374,7 +398,10 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
}
if (ni->i_valid < to) {
- inode_lock(inode);
+ if (!inode_trylock(inode)) {
+ err = -EAGAIN;
+ goto out;
+ }
err = ntfs_extend_initialized_size(file, ni,
ni->i_valid, to);
inode_unlock(inode);
@@ -383,7 +410,7 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
}
}
- err = generic_file_mmap(file, vma);
+ err = generic_file_mmap_prepare(desc);
out:
return err;
}
@@ -523,8 +550,6 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
if (dirty)
mark_inode_dirty(inode);
- /*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/
-
return 0;
}
@@ -799,6 +824,10 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
umode_t mode = inode->i_mode;
int err;
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
@@ -859,6 +888,10 @@ static int check_read_restriction(struct inode *inode)
{
struct ntfs_inode *ni = ntfs_i(inode);
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
@@ -898,9 +931,24 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
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;
+ if (is_compressed(ni)) {
+ if (iocb->ki_flags & IOCB_DIRECT) {
+ ntfs_inode_warn(
+ inode, "direct i/o + compressed not supported");
+ return -EOPNOTSUPP;
+ }
+ /* Turn off readahead for compressed files. */
+ file->f_ra.ra_pages = 0;
+ }
+
+ /* Check minimum alignment for dio. */
+ if (iocb->ki_flags & IOCB_DIRECT) {
+ struct super_block *sb = inode->i_sb;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
+ if ((iocb->ki_pos | iov_iter_alignment(iter)) &
+ sbi->bdev_blocksize_mask) {
+ iocb->ki_flags &= ~IOCB_DIRECT;
+ }
}
return generic_file_read_iter(iocb, iter);
@@ -920,6 +968,11 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
if (err)
return err;
+ if (is_compressed(ntfs_i(inode))) {
+ /* Turn off readahead for compressed files. */
+ in->f_ra.ra_pages = 0;
+ }
+
return filemap_splice_read(in, ppos, pipe, len, flags);
}
@@ -977,7 +1030,8 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
struct ntfs_inode *ni = ntfs_i(inode);
u64 valid = ni->i_valid;
struct ntfs_sb_info *sbi = ni->mi.sbi;
- struct page *page, **pages = NULL;
+ struct page **pages = NULL;
+ struct folio *folio;
size_t written = 0;
u8 frame_bits = NTFS_LZNT_CUNIT + sbi->cluster_bits;
u32 frame_size = 1u << frame_bits;
@@ -1037,12 +1091,12 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
if (!frame_uptodate && off) {
err = ni_read_frame(ni, frame_vbo, pages,
- pages_per_frame);
+ pages_per_frame, 0);
if (err) {
for (ip = 0; ip < pages_per_frame; ip++) {
- page = pages[ip];
- unlock_page(page);
- put_page(page);
+ folio = page_folio(pages[ip]);
+ folio_unlock(folio);
+ folio_put(folio);
}
goto out;
}
@@ -1051,10 +1105,10 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
ip = off >> PAGE_SHIFT;
off = offset_in_page(valid);
for (; ip < pages_per_frame; ip++, off = 0) {
- page = pages[ip];
- zero_user_segment(page, off, PAGE_SIZE);
- flush_dcache_page(page);
- SetPageUptodate(page);
+ folio = page_folio(pages[ip]);
+ folio_zero_segment(folio, off, PAGE_SIZE);
+ flush_dcache_folio(folio);
+ folio_mark_uptodate(folio);
}
ni_lock(ni);
@@ -1062,10 +1116,10 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
ni_unlock(ni);
for (ip = 0; ip < pages_per_frame; ip++) {
- page = pages[ip];
- SetPageUptodate(page);
- unlock_page(page);
- put_page(page);
+ folio = page_folio(pages[ip]);
+ folio_mark_uptodate(folio);
+ folio_unlock(folio);
+ folio_put(folio);
}
if (err)
@@ -1102,13 +1156,13 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
if (off || (to < i_size && (to & (frame_size - 1)))) {
err = ni_read_frame(ni, frame_vbo, pages,
- pages_per_frame);
+ pages_per_frame, 0);
if (err) {
for (ip = 0; ip < pages_per_frame;
ip++) {
- page = pages[ip];
- unlock_page(page);
- put_page(page);
+ folio = page_folio(pages[ip]);
+ folio_unlock(folio);
+ folio_put(folio);
}
goto out;
}
@@ -1124,10 +1178,10 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
for (;;) {
size_t cp, tail = PAGE_SIZE - off;
- page = pages[ip];
- cp = copy_page_from_iter_atomic(page, off,
- min(tail, bytes), from);
- flush_dcache_page(page);
+ folio = page_folio(pages[ip]);
+ cp = copy_folio_from_iter_atomic(
+ folio, off, min(tail, bytes), from);
+ flush_dcache_folio(folio);
copied += cp;
bytes -= cp;
@@ -1147,11 +1201,11 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
ni_unlock(ni);
for (ip = 0; ip < pages_per_frame; ip++) {
- page = pages[ip];
- ClearPageDirty(page);
- SetPageUptodate(page);
- unlock_page(page);
- put_page(page);
+ folio = page_folio(pages[ip]);
+ folio_clear_dirty(folio);
+ folio_mark_uptodate(folio);
+ folio_unlock(folio);
+ folio_put(folio);
}
if (err)
@@ -1193,6 +1247,10 @@ static int check_write_restriction(struct inode *inode)
{
struct ntfs_inode *ni = ntfs_i(inode);
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
@@ -1220,21 +1278,22 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
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;
- }
-
if (!inode_trylock(inode)) {
if (iocb->ki_flags & IOCB_NOWAIT)
return -EAGAIN;
inode_lock(inode);
}
+ ret = check_write_restriction(inode);
+ if (ret)
+ goto out;
+
+ if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
+ ntfs_inode_warn(inode, "direct i/o + compressed not supported");
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
ret = generic_write_checks(iocb, from);
if (ret <= 0)
goto out;
@@ -1274,6 +1333,10 @@ int ntfs_file_open(struct inode *inode, struct file *file)
{
struct ntfs_inode *ni = ntfs_i(inode);
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
@@ -1314,7 +1377,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
if (sbi->options->prealloc &&
((file->f_mode & FMODE_WRITE) &&
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.
*
@@ -1343,6 +1406,10 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
int err;
struct ntfs_inode *ni = ntfs_i(inode);
+ /* Avoid any operation if inode is bad. */
+ if (unlikely(is_bad_ni(ni)))
+ return -EINVAL;
+
err = fiemap_prep(inode, fieinfo, start, &len, ~FIEMAP_FLAG_XATTR);
if (err)
return err;
@@ -1373,6 +1440,18 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
return iter_file_splice_write(pipe, file, ppos, len, flags);
}
+/*
+ * ntfs_file_fsync - file_operations::fsync
+ */
+static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct inode *inode = file_inode(file);
+ if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
+ return -EIO;
+
+ return generic_file_fsync(file, start, end, datasync);
+}
+
// clang-format off
const struct inode_operations ntfs_file_inode_operations = {
.getattr = ntfs_getattr,
@@ -1381,8 +1460,6 @@ const struct inode_operations ntfs_file_inode_operations = {
.get_acl = ntfs_get_acl,
.set_acl = ntfs_set_acl,
.fiemap = ntfs_fiemap,
- .fileattr_get = ntfs_fileattr_get,
- .fileattr_set = ntfs_fileattr_set,
};
const struct file_operations ntfs_file_operations = {
@@ -1395,9 +1472,9 @@ const struct file_operations ntfs_file_operations = {
#endif
.splice_read = ntfs_file_splice_read,
.splice_write = ntfs_file_splice_write,
- .mmap = ntfs_file_mmap,
+ .mmap_prepare = ntfs_file_mmap_prepare,
.open = ntfs_file_open,
- .fsync = generic_file_fsync,
+ .fsync = ntfs_file_fsync,
.fallocate = ntfs_fallocate,
.release = ntfs_file_release,
};