// SPDX-License-Identifier: GPL-2.0-or-later
/*
* NTFS kernel file operations.
*
* Copyright (c) 2001-2015 Anton Altaparmakov and Tuxera Inc.
* Copyright (c) 2025 LG Electronics Co., Ltd.
*/
#include <linux/writeback.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
#include <linux/iomap.h>
#include <linux/uio.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/compat.h>
#include <linux/falloc.h>
#include "lcnalloc.h"
#include "ntfs.h"
#include "reparse.h"
#include "ea.h"
#include "iomap.h"
#include "bitmap.h"
#include <linux/filelock.h>
/*
* ntfs_file_open - called when an inode is about to be opened
* @vi: inode to be opened
* @filp: file structure describing the inode
*
* Limit file size to the page cache limit on architectures where unsigned long
* is 32-bits. This is the most we can do for now without overflowing the page
* cache page index. Doing it this way means we don't run into problems because
* of existing too large files. It would be better to allow the user to read
* the beginning of the file but I doubt very much anyone is going to hit this
* check on a 32-bit architecture, so there is no point in adding the extra
* complexity required to support this.
*
* On 64-bit architectures, the check is hopefully optimized away by the
* compiler.
*
* After the check passes, just call generic_file_open() to do its work.
*/
static int ntfs_file_open(struct inode *vi, struct file *filp)
{
struct ntfs_inode *ni = NTFS_I(vi);
if (NVolShutdown(ni->vol))
return -EIO;
if (sizeof(unsigned long) < 8) {
if (i_size_read(vi) > MAX_LFS_FILESIZE)
return -EOVERFLOW;
}
filp->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
return generic_file_open(vi, filp);
}
/*
* Trim preallocated space on file release.
*
* When the preallo_size mount option is set (default 64KB), writes extend
* allocated_size and runlist in units of preallocated size to reduce
* runlist merge overhead for small writes. This can leave
* allocated_size > data_size if not all preallocated space is used.
*
* We perform the trim here because ->release() is called only when
* the file is no longer open. At this point, no further writes can occur,
* so it is safe to reclaim the unused preallocated space.
*
* Returns 0 on success, or negative error on failure.
*/
static int ntfs_trim_prealloc(struct inode *vi)
{
struct ntfs_inode *ni = NTFS_I(vi);
struct ntfs_volume *vol = ni->vol;
struct runlist_element *rl;
s64 aligned_data_size;
s64 vcn_ds, vcn_tr;
ssize_t rc;
int err = 0;
inode_lock(vi);
mutex_lock(&ni->mrec_lock);
down_write(&ni->runlist.lock);
aligned_data_size = round_up(ni->data_size, vol->cluster_size);
if (aligned_data_size >= ni->allocated_size)
goto out_unlock;
vcn_ds = ntfs_bytes_to_cluster(vol, aligned_data_size);
vcn_tr = -1;
rc = ni->runlist.count - 2;
rl = ni->runlist.rl;
while (rc >= 0 && rl[rc].lcn == LCN_HOLE && vcn_ds <= rl[rc].vcn) {
vcn_tr = rl[rc].vcn;
rc--;
}
if (vcn_tr >= 0) {
err = ntfs_rl_truncate_nolock(vol, &ni->runlist, vcn_tr);
if (err) {
kvfree(ni->runlist.rl);
ni->runlist.rl = NULL;
ntfs_error(vol->sb, "Preallocated block rollback failed");
} else {
ni->allocated_size = ntfs_cluster_to_bytes(vol, vcn_tr);
err = ntfs_attr_update_mapping_pairs(ni, 0);
if (err)
ntfs_error(vol->sb,
"Failed to rollback mapping pairs for prealloc");
}
}
out_unlock:
up_