mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
smb client fixes for v7.3-rc6
- Stop data dirtied past EOF through an mmap from reappearing as file
content once the file is extended by a write, truncate, zero range,
copy range or clone range
- Flush dirty data and drain in-flight I/O before operations that
assume the pagecache and the server agree on the file: querying
allocated ranges, the O_TRUNC open, interior zero range, and
server-side copy/clone
- Stop a genuine size-extending zero range or preallocate from being
refused with -EOPNOTSUPP when the inode is not read caching, by
querying the server's authoritative EOF instead of trusting a stale
cached i_size
- Zero the untransferred tail of a short read, both in the netfs
read-gaps path (where stale folio content could otherwise be written
back to the server) and in the DIO/unbuffered read collector, and
tell a real EOF apart from a stale cached remote_i_size after a lease
downgrade
- Require stable pages on signed connections so a buffered write can't
modify a folio whose signature has already been computed and is in
flight, which the server rejected with STATUS_ACCESS_DENIED and the
client surfaced as -EIO
- Split several cifsFileInfo flags out of a shared bitfield byte so
concurrent updates taken under different locks no longer clobber each
other through a byte-level RMW
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQTcqRusfSdYROJQwGkpVtNKoQNdYwUCasAVCQAKCRApVtNKoQNd
YxLEAQDBsM7NZk2+hUlPZMDq/NK+8C12Vm8WnN8nqb3Dpt8jCwEA2JJGMeO+p9/L
/hwKubpM6CuWiG2yMbqyUHWVZSkOFwQ=
=8v7I
-----END PGP SIGNATURE-----
Merge tag 'cifs-fixes-7.3-rc6' of https://git.manguebit.org/linux
Pull smb client fixes from Paulo Alcantara:
"Fix a series of data corruption and I/O error bugs found by running
generic/363 (fsx) in a loop against Windows Server 2022 and Samba.
- Stop data dirtied past EOF through an mmap from reappearing as file
content once the file is extended by a write, truncate, zero range,
copy range or clone range
- Flush dirty data and drain in-flight I/O before operations that
assume the pagecache and the server agree on the file: querying
allocated ranges, the O_TRUNC open, interior zero range, and
server-side copy/clone
- Stop a genuine size-extending zero range or preallocate from being
refused with -EOPNOTSUPP when the inode is not read caching, by
querying the server's authoritative EOF instead of trusting a stale
cached i_size
- Zero the untransferred tail of a short read, both in the netfs
read-gaps path (where stale folio content could otherwise be
written back to the server) and in the DIO/unbuffered read
collector, and tell a real EOF apart from a stale cached
remote_i_size after a lease downgrade
- Require stable pages on signed connections so a buffered write
can't modify a folio whose signature has already been computed and
is in flight, which the server rejected with STATUS_ACCESS_DENIED
and the client surfaced as -EIO
- Split several cifsFileInfo flags out of a shared bitfield byte so
concurrent updates taken under different locks no longer clobber
each other through a byte-level RMW"
* tag 'cifs-fixes-7.3-rc6' of https://git.manguebit.org/linux:
smb: client: split cifsFileInfo bitfields to avoid shared-byte RMW races
smb: client: require stable pages for signed connections
smb: client: distinguish real EOF from a stale remote_i_size on read
netfs: zero the tail of a short DIO/unbuffered read
smb: client: only require read lease for size-extending preallocate
netfs: zero gaps in read-gaps folio to avoid writing back stale data
smb: client: only require read lease for size-extending zero range
smb: client: drain and invalidate before server-side copy/clone
smb: client: flush dirty data before zeroing a range
smb: client: drain outstanding I/O before truncating on O_TRUNC open
smb: client: flush and commit data before querying allocated ranges
smb: client: discard post-EOF pagecache when extending a file via clone range
smb: client: discard post-EOF pagecache when extending a file via copy range
smb: client: discard post-EOF pagecache when extending a file via zero range
smb: client: clear post-EOF pagecache when extending a file via truncate
netfs: clear post-EOF pagecache when extending a file via write
This commit is contained in:
commit
ff47652a4b
|
|
@ -451,6 +451,32 @@ one.
|
||||||
The inode should be marked ``NETFS_ICTX_SINGLE_NO_UPLOAD`` if this API is to be
|
The inode should be marked ``NETFS_ICTX_SINGLE_NO_UPLOAD`` if this API is to be
|
||||||
used. The writeback function requires the buffer to be of ITER_FOLIOQ type.
|
used. The writeback function requires the buffer to be of ITER_FOLIOQ type.
|
||||||
|
|
||||||
|
Clearing Stale Post-EOF Pagecache
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
When a file is extended, data left in the pagecache past the old EOF by a write
|
||||||
|
through an mmap must not be exposed as file content. Netfslib clears this on
|
||||||
|
its own write paths, and exports a helper so a filesystem can do the same from
|
||||||
|
a resize path (truncate, setattr, fallocate and the like) that holds the
|
||||||
|
inode's ``i_rwsem`` exclusively across the whole resize::
|
||||||
|
|
||||||
|
void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
|
||||||
|
uoff_t to);
|
||||||
|
|
||||||
|
This zeroes any such data within the ``[from, to)`` hole to be made, where
|
||||||
|
@from is the old EOF and @to is the new one, and the caller must have already
|
||||||
|
updated ``i_size`` to @to before calling it. Only the folio straddling @from
|
||||||
|
can hold data written past the EOF through an mmap, as pages wholly beyond the
|
||||||
|
EOF can't be faulted in, so the zeroing is limited to that folio. The folio is
|
||||||
|
zeroed rather than dropped so that a concurrent extending write can't lose
|
||||||
|
data.
|
||||||
|
|
||||||
|
This overlaps with ``pagecache_isize_extended()`` but can't reuse it: that
|
||||||
|
helper is keyed on a sub-page block size and is a no-op when the block size is
|
||||||
|
``>= PAGE_SIZE`` (as on network filesystems), and it doesn't wait for
|
||||||
|
writeback. As both address the same problem, a change to one should probably
|
||||||
|
be reflected in the other to keep them in sync.
|
||||||
|
|
||||||
High-Level VM API
|
High-Level VM API
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,13 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)
|
||||||
|
|
||||||
ret = netfs_wait_for_read(rreq);
|
ret = netfs_wait_for_read(rreq);
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
|
if (ret < flen) {
|
||||||
|
struct iov_iter iter;
|
||||||
|
|
||||||
|
iov_iter_bvec(&iter, ITER_DEST, bvec, i, flen);
|
||||||
|
iov_iter_advance(&iter, ret);
|
||||||
|
iov_iter_zero(flen - ret, &iter);
|
||||||
|
}
|
||||||
if (group)
|
if (group)
|
||||||
folio_change_private(folio, group);
|
folio_change_private(folio, group);
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -469,6 +469,7 @@ ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *fr
|
||||||
struct netfs_group *netfs_group)
|
struct netfs_group *netfs_group)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
|
struct inode *inode = file_inode(file);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
trace_netfs_write_iter(iocb, from);
|
trace_netfs_write_iter(iocb, from);
|
||||||
|
|
@ -481,6 +482,14 @@ ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *fr
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (iocb->ki_pos > i_size_read(inode)) {
|
||||||
|
ret = netfs_clear_stale_pre_isize(inode, i_size_read(inode),
|
||||||
|
iocb->ki_pos,
|
||||||
|
iocb->ki_flags & IOCB_NOWAIT);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return netfs_perform_write(iocb, from, netfs_group);
|
return netfs_perform_write(iocb, from, netfs_group);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(netfs_buffered_write_iter_locked);
|
EXPORT_SYMBOL(netfs_buffered_write_iter_locked);
|
||||||
|
|
|
||||||
|
|
@ -359,6 +359,15 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||||
ret = file_update_time(file);
|
ret = file_update_time(file);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if (iocb->ki_pos > i_size_read(inode)) {
|
||||||
|
ret = netfs_clear_stale_pre_isize(inode, i_size_read(inode),
|
||||||
|
iocb->ki_pos,
|
||||||
|
iocb->ki_flags & IOCB_NOWAIT);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||||
/* We could block if there are any pages in the range. */
|
/* We could block if there are any pages in the range. */
|
||||||
ret = -EAGAIN;
|
ret = -EAGAIN;
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,8 @@ ssize_t netfs_wait_for_write(struct netfs_io_request *rreq);
|
||||||
void netfs_wait_for_paused_read(struct netfs_io_request *rreq);
|
void netfs_wait_for_paused_read(struct netfs_io_request *rreq);
|
||||||
void netfs_wait_for_paused_write(struct netfs_io_request *rreq);
|
void netfs_wait_for_paused_write(struct netfs_io_request *rreq);
|
||||||
void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq);
|
void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq);
|
||||||
|
int netfs_clear_stale_pre_isize(struct inode *inode, uoff_t from,
|
||||||
|
uoff_t to, bool nowait);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* objects.c
|
* objects.c
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/swap.h>
|
#include <linux/swap.h>
|
||||||
|
#include <linux/rmap.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -582,3 +583,101 @@ void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq)
|
||||||
trace_netfs_rreq(rreq, netfs_rreq_trace_waited_put_ra_refs);
|
trace_netfs_rreq(rreq, netfs_rreq_trace_waited_put_ra_refs);
|
||||||
finish_wait(&rreq->waitq, &myself);
|
finish_wait(&rreq->waitq, &myself);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* netfs_clear_stale_isize - Clear stale pagecache in a to-be-created hole
|
||||||
|
* @inode: The inode to act upon.
|
||||||
|
* @from: The base of the hole to be made.
|
||||||
|
* @to: The top of the hole to be made.
|
||||||
|
* @nowait: True to return -EAGAIN rather than block.
|
||||||
|
* @exclusive: True if the caller holds i_rwsem exclusively for the resize.
|
||||||
|
*
|
||||||
|
* Zero any data left in the pagecache within the [@from, @to) hole by a
|
||||||
|
* write through an mmap so that it isn't exposed as file content once the
|
||||||
|
* file is extended. Only the uptodate folio straddling @from can hold such
|
||||||
|
* data as pages wholly beyond the EOF can't be faulted in, so the zeroing
|
||||||
|
* is limited to that folio. The folio is zeroed rather than dropped so
|
||||||
|
* that a concurrent extending write can't lose data.
|
||||||
|
*
|
||||||
|
* If @exclusive is false, @from is re-read from i_size and used to clamp
|
||||||
|
* the zeroed range, for callers that may race with another writer also
|
||||||
|
* extending the file (eg. multiple buffered writes extending the same file
|
||||||
|
* under a shared i_rwsem). If @exclusive is true, for callers that hold
|
||||||
|
* i_rwsem exclusively across the whole resize and have already updated
|
||||||
|
* i_size to @to, staleness is decided from the folio's dirty state instead:
|
||||||
|
* since no genuine concurrent buffered writer can be racing, a lockless
|
||||||
|
* stat() adopting a server-confirmed size mid-resize has no data behind it
|
||||||
|
* and never dirties the folio, so it can't fool this check into skipping
|
||||||
|
* the zeroing the way it could fool the @exclusive false clamp.
|
||||||
|
*
|
||||||
|
* pagecache_isize_extended() can't be reused here: it is keyed on a
|
||||||
|
* sub-page block size (a no-op when the block size is >= PAGE_SIZE, as on
|
||||||
|
* network filesystems), runs after i_size is updated, can't honour
|
||||||
|
* @nowait, and doesn't wait for writeback. Keep the two in sync if either
|
||||||
|
* is changed.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or -EAGAIN if @nowait is set and the folio is
|
||||||
|
* mapped or under writeback and so can't be cleaned without blocking.
|
||||||
|
*/
|
||||||
|
static int netfs_clear_stale_isize(struct inode *inode, uoff_t from,
|
||||||
|
uoff_t to, bool nowait, bool exclusive)
|
||||||
|
{
|
||||||
|
struct address_space *mapping = inode->i_mapping;
|
||||||
|
fgf_t fgp = FGP_LOCK;
|
||||||
|
struct folio *folio;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (from >= to)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (nowait)
|
||||||
|
fgp |= FGP_NOWAIT;
|
||||||
|
|
||||||
|
folio = __filemap_get_folio(mapping, from >> PAGE_SHIFT, fgp, 0);
|
||||||
|
if (IS_ERR(folio))
|
||||||
|
return PTR_ERR(folio) == -EAGAIN ? -EAGAIN : 0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
if (nowait && (folio_mapped(folio) || folio_test_writeback(folio))) {
|
||||||
|
ret = -EAGAIN;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
folio_wait_writeback(folio);
|
||||||
|
|
||||||
|
if (folio_mkclean(folio))
|
||||||
|
folio_mark_dirty(folio);
|
||||||
|
|
||||||
|
if (folio_test_uptodate(folio) &&
|
||||||
|
(!exclusive || folio_test_dirty(folio))) {
|
||||||
|
uoff_t fpos = folio_pos(folio);
|
||||||
|
|
||||||
|
if (!exclusive)
|
||||||
|
from = umax(from, i_size_read(inode));
|
||||||
|
if (from < to && from < fpos + folio_size(folio)) {
|
||||||
|
size_t end = umin(to - fpos, folio_size(folio));
|
||||||
|
size_t offset = from - fpos;
|
||||||
|
|
||||||
|
folio_zero_segment(folio, offset, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
folio_unlock(folio);
|
||||||
|
folio_put(folio);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear stale pagecache before an extending buffered/DIO write. */
|
||||||
|
int netfs_clear_stale_pre_isize(struct inode *inode, uoff_t from,
|
||||||
|
uoff_t to, bool nowait)
|
||||||
|
{
|
||||||
|
return netfs_clear_stale_isize(inode, from, to, nowait, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear stale pagecache when extending a file under an exclusive resize. */
|
||||||
|
void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
|
||||||
|
uoff_t to)
|
||||||
|
{
|
||||||
|
netfs_clear_stale_isize(inode, from, to, false, true);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(netfs_clear_stale_post_isize);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,22 @@ static void netfs_clear_unread(struct netfs_io_subrequest *subreq)
|
||||||
__set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags);
|
__set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netfs_clear_unread_dio(struct netfs_io_subrequest *subreq)
|
||||||
|
{
|
||||||
|
uoff_t pos = subreq->start + subreq->transferred;
|
||||||
|
struct netfs_io_request *rreq = subreq->rreq;
|
||||||
|
size_t fill;
|
||||||
|
|
||||||
|
if (pos >= rreq->i_size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fill = min_t(uoff_t, rreq->i_size - pos,
|
||||||
|
subreq->len - subreq->transferred);
|
||||||
|
|
||||||
|
netfs_reset_iter(subreq);
|
||||||
|
subreq->transferred += iov_iter_zero(fill, &subreq->io_iter);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cancel the copy-to-cache mark on a folio.
|
* Cancel the copy-to-cache mark on a folio.
|
||||||
*/
|
*/
|
||||||
|
|
@ -311,6 +327,14 @@ static void netfs_collect_read_results(struct netfs_io_request *rreq)
|
||||||
test_bit(NETFS_SREQ_HIT_EOF, &front->flags))
|
test_bit(NETFS_SREQ_HIT_EOF, &front->flags))
|
||||||
netfs_read_unlock_folios(rreq, ¬es);
|
netfs_read_unlock_folios(rreq, ¬es);
|
||||||
} else {
|
} else {
|
||||||
|
if (!(notes & HIT_PENDING) &&
|
||||||
|
front->error == 0 &&
|
||||||
|
transferred < front->len &&
|
||||||
|
test_bit(NETFS_SREQ_CLEAR_TAIL, &front->flags)) {
|
||||||
|
netfs_clear_unread_dio(front);
|
||||||
|
transferred = front->transferred;
|
||||||
|
trace_netfs_sreq(front, netfs_sreq_trace_clear);
|
||||||
|
}
|
||||||
stream->collected_to = front->start + transferred;
|
stream->collected_to = front->start + transferred;
|
||||||
rreq->collected_to = stream->collected_to;
|
rreq->collected_to = stream->collected_to;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1412,6 +1412,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
|
||||||
* server could even support copy of range where source = target
|
* server could even support copy of range where source = target
|
||||||
*/
|
*/
|
||||||
lock_two_nondirectories(target_inode, src_inode);
|
lock_two_nondirectories(target_inode, src_inode);
|
||||||
|
filemap_invalidate_lock(target_inode->i_mapping);
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
loff_t src_size = i_size_read(src_inode);
|
loff_t src_size = i_size_read(src_inode);
|
||||||
|
|
@ -1467,9 +1468,15 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
|
||||||
i_size = target_inode->i_size;
|
i_size = target_inode->i_size;
|
||||||
spin_unlock(&target_inode->i_lock);
|
spin_unlock(&target_inode->i_lock);
|
||||||
|
|
||||||
/* Discard all the folios that overlap the destination region. */
|
/*
|
||||||
|
* Discard all the folios that overlap the destination region. Start at
|
||||||
|
* the old EOF when extending so the folio straddling it, which may hold
|
||||||
|
* data written past EOF through an mmap, is dropped too.
|
||||||
|
*/
|
||||||
cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
|
cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
|
||||||
truncate_inode_pages_range(&target_inode->i_data, fstart, fend);
|
truncate_inode_pages_range(&target_inode->i_data,
|
||||||
|
min(fstart, i_size), fend);
|
||||||
|
netfs_wait_for_outstanding_io(target_inode);
|
||||||
|
|
||||||
fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0);
|
fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0);
|
||||||
|
|
||||||
|
|
@ -1508,6 +1515,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
|
||||||
if (rc)
|
if (rc)
|
||||||
CIFS_I(target_inode)->time = 0;
|
CIFS_I(target_inode)->time = 0;
|
||||||
unlock:
|
unlock:
|
||||||
|
filemap_invalidate_unlock(target_inode->i_mapping);
|
||||||
/* although unlocking in the reverse order from locking is not
|
/* although unlocking in the reverse order from locking is not
|
||||||
strictly necessary here it is a little cleaner to be consistent */
|
strictly necessary here it is a little cleaner to be consistent */
|
||||||
unlock_two_nondirectories(src_inode, target_inode);
|
unlock_two_nondirectories(src_inode, target_inode);
|
||||||
|
|
@ -1531,6 +1539,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
|
||||||
struct cifs_tcon *target_tcon;
|
struct cifs_tcon *target_tcon;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
cifs_dbg(FYI, "copychunk range\n");
|
cifs_dbg(FYI, "copychunk range\n");
|
||||||
|
|
||||||
if (!src_file->private_data || !dst_file->private_data) {
|
if (!src_file->private_data || !dst_file->private_data) {
|
||||||
|
|
@ -1560,6 +1571,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
|
||||||
* server could even support copy of range where source = target
|
* server could even support copy of range where source = target
|
||||||
*/
|
*/
|
||||||
lock_two_nondirectories(target_inode, src_inode);
|
lock_two_nondirectories(target_inode, src_inode);
|
||||||
|
filemap_invalidate_lock(target_inode->i_mapping);
|
||||||
|
|
||||||
cifs_dbg(FYI, "about to flush pages\n");
|
cifs_dbg(FYI, "about to flush pages\n");
|
||||||
|
|
||||||
|
|
@ -1581,10 +1593,28 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
|
||||||
/* Flush and invalidate all the folios in the destination region. If
|
/* Flush and invalidate all the folios in the destination region. If
|
||||||
* the copy was successful, then some of the flush is extra overhead,
|
* the copy was successful, then some of the flush is extra overhead,
|
||||||
* but we need to allow for the copy failing in some way (eg. ENOSPC).
|
* but we need to allow for the copy failing in some way (eg. ENOSPC).
|
||||||
|
*
|
||||||
|
* Start at the old EOF when extending so the folio straddling it, which
|
||||||
|
* may hold data written past EOF through an mmap, is dropped too.
|
||||||
*/
|
*/
|
||||||
rc = filemap_invalidate_inode(target_inode, true, destoff, destoff + len - 1);
|
if (target_inode->i_mapping->nrpages) {
|
||||||
if (rc)
|
loff_t fstart = min(destoff, i_size_read(target_inode));
|
||||||
goto unlock;
|
loff_t fend = destoff + len - 1;
|
||||||
|
|
||||||
|
unmap_mapping_pages(target_inode->i_mapping,
|
||||||
|
fstart >> PAGE_SHIFT,
|
||||||
|
(fend >> PAGE_SHIFT) -
|
||||||
|
(fstart >> PAGE_SHIFT) + 1,
|
||||||
|
false);
|
||||||
|
rc = filemap_write_and_wait_range(target_inode->i_mapping,
|
||||||
|
fstart, fend);
|
||||||
|
if (rc)
|
||||||
|
goto unlock;
|
||||||
|
invalidate_inode_pages2_range(target_inode->i_mapping,
|
||||||
|
fstart >> PAGE_SHIFT,
|
||||||
|
fend >> PAGE_SHIFT);
|
||||||
|
}
|
||||||
|
netfs_wait_for_outstanding_io(target_inode);
|
||||||
|
|
||||||
fscache_invalidate(cifs_inode_cookie(target_inode), NULL,
|
fscache_invalidate(cifs_inode_cookie(target_inode), NULL,
|
||||||
i_size_read(target_inode), 0);
|
i_size_read(target_inode), 0);
|
||||||
|
|
@ -1616,6 +1646,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
|
||||||
CIFS_I(target_inode)->time = 0;
|
CIFS_I(target_inode)->time = 0;
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
|
filemap_invalidate_unlock(target_inode->i_mapping);
|
||||||
/* although unlocking in the reverse order from locking is not
|
/* although unlocking in the reverse order from locking is not
|
||||||
* strictly necessary here it is a little cleaner to be consistent
|
* strictly necessary here it is a little cleaner to be consistent
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -146,8 +146,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
|
|
||||||
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
|
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
|
||||||
void cifs_setsize(struct inode *inode, loff_t offset);
|
void cifs_setsize(struct inode *inode, loff_t old_size, loff_t offset);
|
||||||
void cifs_resize_file_locked(struct inode *inode, loff_t offset);
|
void cifs_resize_file_locked(struct inode *inode, loff_t old_size,
|
||||||
|
loff_t offset);
|
||||||
|
|
||||||
struct fs_context;
|
struct fs_context;
|
||||||
struct smb3_fs_context;
|
struct smb3_fs_context;
|
||||||
|
|
|
||||||
|
|
@ -1450,11 +1450,11 @@ struct cifsFileInfo {
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
unsigned int f_flags;
|
unsigned int f_flags;
|
||||||
bool invalidHandle:1; /* file closed via session abend */
|
bool invalidHandle; /* file closed via session abend */
|
||||||
bool swapfile:1;
|
bool swapfile;
|
||||||
bool oplock_break_cancelled:1;
|
bool oplock_break_cancelled;
|
||||||
bool status_file_deleted:1; /* file has been deleted */
|
bool status_file_deleted; /* file has been deleted */
|
||||||
bool offload:1; /* offload final part of _put to a wq */
|
bool offload; /* offload final part of _put to a wq */
|
||||||
__u16 oplock_epoch; /* epoch from the lease break */
|
__u16 oplock_epoch; /* epoch from the lease break */
|
||||||
__u32 oplock_level; /* oplock/lease level from the lease break */
|
__u32 oplock_level; /* oplock/lease level from the lease break */
|
||||||
int count;
|
int count;
|
||||||
|
|
|
||||||
|
|
@ -1012,11 +1012,14 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
|
||||||
}
|
}
|
||||||
mapping_set_error(inode->i_mapping, rc);
|
mapping_set_error(inode->i_mapping, rc);
|
||||||
|
|
||||||
|
netfs_wait_for_outstanding_io(inode);
|
||||||
|
|
||||||
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
|
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
|
||||||
rc = cifs_file_flush(xid, inode, cfile);
|
rc = cifs_file_flush(xid, inode, cfile);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (cfile) {
|
if (cfile) {
|
||||||
struct netfs_inode *ictx = netfs_inode(inode);
|
struct netfs_inode *ictx = netfs_inode(inode);
|
||||||
|
loff_t old_size = i_size_read(inode);
|
||||||
|
|
||||||
tcon = tlink_tcon(cfile->tlink);
|
tcon = tlink_tcon(cfile->tlink);
|
||||||
server = tcon->ses->server;
|
server = tcon->ses->server;
|
||||||
|
|
@ -1025,7 +1028,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
|
||||||
cfile, 0, false);
|
cfile, 0, false);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
netfs_resize_file(&cinode->netfs, 0, true);
|
netfs_resize_file(&cinode->netfs, 0, true);
|
||||||
cifs_setsize(inode, 0);
|
cifs_setsize(inode, old_size, 0);
|
||||||
cifs_invalidate_cache(inode, 0);
|
cifs_invalidate_cache(inode, 0);
|
||||||
}
|
}
|
||||||
netfs_wb_end(ictx);
|
netfs_wb_end(ictx);
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ static void cifs_set_ops(struct inode *inode)
|
||||||
else
|
else
|
||||||
inode->i_data.a_ops = &cifs_addr_ops;
|
inode->i_data.a_ops = &cifs_addr_ops;
|
||||||
mapping_set_large_folios(inode->i_mapping);
|
mapping_set_large_folios(inode->i_mapping);
|
||||||
|
if (tcon->ses->server->sign)
|
||||||
|
mapping_set_stable_writes(inode->i_mapping);
|
||||||
break;
|
break;
|
||||||
case S_IFDIR:
|
case S_IFDIR:
|
||||||
if (IS_AUTOMOUNT(inode)) {
|
if (IS_AUTOMOUNT(inode)) {
|
||||||
|
|
@ -3053,15 +3055,12 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cifs_setsize(struct inode *inode, loff_t offset)
|
void cifs_setsize(struct inode *inode, loff_t old_size, loff_t offset)
|
||||||
{
|
{
|
||||||
loff_t old_size;
|
|
||||||
u64 blocks = CIFS_INO_BLOCKS(offset);
|
u64 blocks = CIFS_INO_BLOCKS(offset);
|
||||||
|
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
old_size = i_size_read(inode);
|
|
||||||
i_size_write(inode, offset);
|
i_size_write(inode, offset);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extending EOF does not allocate the intervening range. Only clamp
|
* Extending EOF does not allocate the intervening range. Only clamp
|
||||||
* i_blocks on shrink; allocation growth comes from writes or from the
|
* i_blocks on shrink; allocation growth comes from writes or from the
|
||||||
|
|
@ -3071,20 +3070,28 @@ void cifs_setsize(struct inode *inode, loff_t offset)
|
||||||
inode->i_blocks = blocks;
|
inode->i_blocks = blocks;
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zero the tail of the folio straddling the old EOF so data dirtied
|
||||||
|
* past EOF through an mmap isn't exposed. truncate_pagecache() then
|
||||||
|
* drops any pagecache beyond the new EOF, as in truncate_setsize().
|
||||||
|
*/
|
||||||
if (offset > old_size)
|
if (offset > old_size)
|
||||||
pagecache_isize_extended(inode, old_size, offset);
|
netfs_clear_stale_post_isize(inode, old_size, offset);
|
||||||
|
|
||||||
truncate_pagecache(inode, offset);
|
truncate_pagecache(inode, offset);
|
||||||
netfs_wait_for_outstanding_io(inode);
|
netfs_wait_for_outstanding_io(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cifs_resize_file_locked(struct inode *inode, loff_t offset)
|
void cifs_resize_file_locked(struct inode *inode, loff_t old_size,
|
||||||
|
loff_t offset)
|
||||||
{
|
{
|
||||||
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
||||||
|
|
||||||
lockdep_assert_held_write(&inode->i_rwsem);
|
lockdep_assert_held_write(&inode->i_rwsem);
|
||||||
|
|
||||||
netfs_resize_file(netfs_inode(inode), offset, true);
|
netfs_resize_file(netfs_inode(inode), offset, true);
|
||||||
cifs_setsize(inode, offset);
|
cifs_setsize(inode, old_size, offset);
|
||||||
|
|
||||||
if (!cookie)
|
if (!cookie)
|
||||||
return;
|
return;
|
||||||
|
|
@ -3101,6 +3108,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
|
||||||
struct inode *inode = d_inode(dentry);
|
struct inode *inode = d_inode(dentry);
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||||
|
loff_t old_size = i_size_read(inode);
|
||||||
struct tcon_link *tlink = NULL;
|
struct tcon_link *tlink = NULL;
|
||||||
struct cifs_tcon *tcon = NULL;
|
struct cifs_tcon *tcon = NULL;
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server;
|
||||||
|
|
@ -3159,7 +3167,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
|
||||||
|
|
||||||
set_size_out:
|
set_size_out:
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
cifs_resize_file_locked(inode, size);
|
cifs_resize_file_locked(inode, old_size, size);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2287,6 +2287,7 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||||
struct duplicate_extents_to_file dup_ext_buf;
|
struct duplicate_extents_to_file dup_ext_buf;
|
||||||
struct timespec64 ts;
|
struct timespec64 ts;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
|
||||||
|
loff_t old_size;
|
||||||
u64 asize;
|
u64 asize;
|
||||||
|
|
||||||
/* server fileays advertise duplicate extent support with this flag */
|
/* server fileays advertise duplicate extent support with this flag */
|
||||||
|
|
@ -2305,11 +2306,12 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||||
trgtfile->fid.volatile_fid, tcon->tid,
|
trgtfile->fid.volatile_fid, tcon->tid,
|
||||||
tcon->ses->Suid, src_off, dest_off, len);
|
tcon->ses->Suid, src_off, dest_off, len);
|
||||||
inode = d_inode(trgtfile->dentry);
|
inode = d_inode(trgtfile->dentry);
|
||||||
if (i_size_read(inode) < dest_off + len) {
|
old_size = i_size_read(inode);
|
||||||
|
if (old_size < dest_off + len) {
|
||||||
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
|
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto duplicate_extents_out;
|
goto duplicate_extents_out;
|
||||||
cifs_resize_file_locked(inode, dest_off + len);
|
cifs_resize_file_locked(inode, old_size, dest_off + len);
|
||||||
}
|
}
|
||||||
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
||||||
trgtfile->fid.volatile_fid,
|
trgtfile->fid.volatile_fid,
|
||||||
|
|
@ -3520,6 +3522,21 @@ static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon,
|
||||||
0, NULL, NULL);
|
0, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long query_server_eof(const unsigned int xid,
|
||||||
|
struct cifs_tcon *tcon,
|
||||||
|
struct cifsFileInfo *cfile,
|
||||||
|
unsigned long long *eof)
|
||||||
|
{
|
||||||
|
struct smb2_file_all_info file_inf = {};
|
||||||
|
long rc;
|
||||||
|
|
||||||
|
rc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid, &file_inf);
|
||||||
|
if (!rc)
|
||||||
|
*eof = le64_to_cpu(file_inf.EndOfFile);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
unsigned long long offset, unsigned long long len,
|
unsigned long long offset, unsigned long long len,
|
||||||
bool keep_size)
|
bool keep_size)
|
||||||
|
|
@ -3547,25 +3564,32 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
filemap_invalidate_lock(inode->i_mapping);
|
filemap_invalidate_lock(inode->i_mapping);
|
||||||
|
|
||||||
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
|
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
|
||||||
if (offset + len >= remote_i_size && offset < i_size) {
|
|
||||||
unsigned long long top = umin(offset + len, i_size);
|
|
||||||
|
|
||||||
rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1);
|
rc = filemap_write_and_wait_range(inode->i_mapping, offset,
|
||||||
if (rc < 0)
|
offset + len - 1);
|
||||||
goto zero_range_exit;
|
if (rc < 0)
|
||||||
}
|
goto zero_range_exit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We zero the range through ioctl, so we need remove the page caches
|
* We zero the range through ioctl, so we need remove the page caches
|
||||||
* first, otherwise the data may be inconsistent with the server.
|
* first, otherwise the data may be inconsistent with the server.
|
||||||
|
*
|
||||||
|
* Start at the old EOF when extending so the folio straddling it, which
|
||||||
|
* may hold data written past EOF through an mmap, is dropped too.
|
||||||
*/
|
*/
|
||||||
truncate_pagecache_range(inode, offset, offset + len - 1);
|
truncate_pagecache_range(inode, min(offset, i_size), offset + len - 1);
|
||||||
netfs_wait_for_outstanding_io(inode);
|
netfs_wait_for_outstanding_io(inode);
|
||||||
|
|
||||||
/* if file not oplocked can't be sure whether asking to extend size */
|
if (!keep_size && !CIFS_CACHE_READ(cifsi)) {
|
||||||
rc = -EOPNOTSUPP;
|
rc = query_server_eof(xid, tcon, cfile, &remote_i_size);
|
||||||
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
|
if (rc)
|
||||||
goto zero_range_exit;
|
goto zero_range_exit;
|
||||||
|
i_size = max(i_size, remote_i_size);
|
||||||
|
if (i_size < new_size) {
|
||||||
|
rc = -EOPNOTSUPP;
|
||||||
|
goto zero_range_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fscache_invalidate(cifs_inode_cookie(inode), NULL,
|
fscache_invalidate(cifs_inode_cookie(inode), NULL,
|
||||||
i_size_read(inode), 0);
|
i_size_read(inode), 0);
|
||||||
|
|
@ -3577,7 +3601,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
/*
|
/*
|
||||||
* do we also need to change the size of the file?
|
* do we also need to change the size of the file?
|
||||||
*/
|
*/
|
||||||
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
|
if (!keep_size && umax(i_size, i_size_read(inode)) < new_size) {
|
||||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||||
cfile->fid.volatile_fid, cfile->pid, new_size);
|
cfile->fid.volatile_fid, cfile->pid, new_size);
|
||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
|
|
@ -3723,7 +3747,8 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
|
||||||
static int smb3_simple_fallocate_range(unsigned int xid,
|
static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
struct cifs_tcon *tcon,
|
struct cifs_tcon *tcon,
|
||||||
struct cifsFileInfo *cfile,
|
struct cifsFileInfo *cfile,
|
||||||
loff_t off, loff_t len)
|
loff_t off, loff_t len,
|
||||||
|
loff_t old_eof)
|
||||||
{
|
{
|
||||||
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
|
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
|
||||||
struct inode *inode = d_inode(cfile->dentry);
|
struct inode *inode = d_inode(cfile->dentry);
|
||||||
|
|
@ -3739,12 +3764,30 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (off >= i_size_read(inode)) {
|
if (off >= old_eof) {
|
||||||
rc = smb3_simple_fallocate_write_range(xid, tcon, cfile,
|
rc = smb3_simple_fallocate_write_range(xid, tcon, cfile,
|
||||||
off, len, buf);
|
off, len, buf);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filemap_invalidate_lock(inode->i_mapping);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush and commit the data to the server, otherwise
|
||||||
|
* FSCTL_QUERY_ALLOCATED_RANGES might report recently written data as
|
||||||
|
* unallocated holes on Windows Servers, and the loop below would
|
||||||
|
* then zero-fill them and corrupt the file.
|
||||||
|
*/
|
||||||
|
rc = filemap_write_and_wait_range(inode->i_mapping, off,
|
||||||
|
off + len - 1);
|
||||||
|
if (rc)
|
||||||
|
goto out_unlock;
|
||||||
|
netfs_wait_for_outstanding_io(inode);
|
||||||
|
rc = SMB2_flush(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid);
|
||||||
|
if (rc)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
in_data.file_offset = cpu_to_le64(off);
|
in_data.file_offset = cpu_to_le64(off);
|
||||||
in_data.length = cpu_to_le64(len);
|
in_data.length = cpu_to_le64(len);
|
||||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
|
@ -3754,7 +3797,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
1024 * sizeof(struct file_allocated_range_buffer),
|
1024 * sizeof(struct file_allocated_range_buffer),
|
||||||
(char **)&out_data, &out_data_len);
|
(char **)&out_data, &out_data_len);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out_unlock;
|
||||||
|
|
||||||
tmp_data = out_data;
|
tmp_data = out_data;
|
||||||
while (len) {
|
while (len) {
|
||||||
|
|
@ -3764,12 +3807,12 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
if (out_data_len == 0) {
|
if (out_data_len == 0) {
|
||||||
rc = smb3_simple_fallocate_write_range(xid, tcon,
|
rc = smb3_simple_fallocate_write_range(xid, tcon,
|
||||||
cfile, off, len, buf);
|
cfile, off, len, buf);
|
||||||
goto out;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
|
if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
range_start = le64_to_cpu(tmp_data->file_offset);
|
range_start = le64_to_cpu(tmp_data->file_offset);
|
||||||
|
|
@ -3777,7 +3820,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
if (check_add_overflow(range_start, range_len, &range_end) ||
|
if (check_add_overflow(range_start, range_len, &range_end) ||
|
||||||
range_end > S64_MAX) {
|
range_end > S64_MAX) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (off < range_start) {
|
if (off < range_start) {
|
||||||
|
|
@ -3792,11 +3835,11 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
rc = smb3_simple_fallocate_write_range(xid, tcon,
|
rc = smb3_simple_fallocate_write_range(xid, tcon,
|
||||||
cfile, off, l, buf);
|
cfile, off, l, buf);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out_unlock;
|
||||||
off = off + l;
|
off = off + l;
|
||||||
len = len - l;
|
len = len - l;
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
goto out;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* We are at a section of allocated data, just skip forward
|
* We are at a section of allocated data, just skip forward
|
||||||
|
|
@ -3815,6 +3858,8 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||||
out_data_len -= sizeof(struct file_allocated_range_buffer);
|
out_data_len -= sizeof(struct file_allocated_range_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
filemap_invalidate_unlock(inode->i_mapping);
|
||||||
out:
|
out:
|
||||||
kfree(out_data);
|
kfree(out_data);
|
||||||
kvfree(buf);
|
kvfree(buf);
|
||||||
|
|
@ -3830,7 +3875,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
struct cifsFileInfo *cfile = file->private_data;
|
struct cifsFileInfo *cfile = file->private_data;
|
||||||
long rc = -EOPNOTSUPP;
|
long rc = -EOPNOTSUPP;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
loff_t old_eof, new_eof;
|
loff_t old_eof, new_eof, local_eof;
|
||||||
struct smb2_file_all_info file_inf;
|
struct smb2_file_all_info file_inf;
|
||||||
u64 asize;
|
u64 asize;
|
||||||
int qrc;
|
int qrc;
|
||||||
|
|
@ -3839,18 +3884,37 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
inode = d_inode(cfile->dentry);
|
inode = d_inode(cfile->dentry);
|
||||||
cifsi = CIFS_I(inode);
|
cifsi = CIFS_I(inode);
|
||||||
old_eof = i_size_read(inode);
|
old_eof = local_eof = i_size_read(inode);
|
||||||
|
|
||||||
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
|
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||||
tcon->ses->Suid, off, len);
|
tcon->ses->Suid, off, len);
|
||||||
/* if file not oplocked can't be sure whether asking to extend size */
|
|
||||||
if (!CIFS_CACHE_READ(cifsi))
|
if (!keep_size && !CIFS_CACHE_READ(cifsi)) {
|
||||||
if (!keep_size) {
|
unsigned long long server_eof;
|
||||||
|
|
||||||
|
rc = filemap_write_and_wait(inode->i_mapping);
|
||||||
|
if (rc) {
|
||||||
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
|
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
|
||||||
tcon->tid, tcon->ses->Suid, off, len, rc);
|
tcon->tid, tcon->ses->Suid, off, len, rc);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
netfs_wait_for_outstanding_io(inode);
|
||||||
|
|
||||||
|
rc = query_server_eof(xid, tcon, cfile, &server_eof);
|
||||||
|
if (rc) {
|
||||||
|
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
|
||||||
|
tcon->tid, tcon->ses->Suid, off, len, rc);
|
||||||
|
free_xid(xid);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Only use the larger EOF to decide whether we're extending.
|
||||||
|
* The pagecache zeroing below must still key off the local
|
||||||
|
* i_size, so keep local_eof for that.
|
||||||
|
*/
|
||||||
|
old_eof = max_t(loff_t, old_eof, server_eof);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extending the file
|
* Extending the file
|
||||||
|
|
@ -3874,7 +3938,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = smb3_simple_fallocate_range(xid, tcon, cfile,
|
rc = smb3_simple_fallocate_range(xid, tcon, cfile,
|
||||||
off, len);
|
off, len, old_eof);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
cifsi->time = 0;
|
cifsi->time = 0;
|
||||||
|
|
@ -3883,7 +3947,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
}
|
}
|
||||||
|
|
||||||
new_eof = off + len;
|
new_eof = off + len;
|
||||||
cifs_resize_file_locked(inode, new_eof);
|
cifs_resize_file_locked(inode, local_eof, new_eof);
|
||||||
|
|
||||||
qrc = SMB2_query_info(xid, tcon,
|
qrc = SMB2_query_info(xid, tcon,
|
||||||
cfile->fid.persistent_fid,
|
cfile->fid.persistent_fid,
|
||||||
|
|
@ -3931,7 +3995,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
cifs_resize_file_locked(inode, new_eof);
|
cifs_resize_file_locked(inode, local_eof, new_eof);
|
||||||
|
|
||||||
qrc = SMB2_query_info(xid, tcon,
|
qrc = SMB2_query_info(xid, tcon,
|
||||||
cfile->fid.persistent_fid,
|
cfile->fid.persistent_fid,
|
||||||
|
|
@ -3976,7 +4040,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
|
if (keep_size || old_eof >= off + len) {
|
||||||
/*
|
/*
|
||||||
* At this point, we are trying to fallocate an internal
|
* At this point, we are trying to fallocate an internal
|
||||||
* regions of a sparse file. Since smb2 does not have a
|
* regions of a sparse file. Since smb2 does not have a
|
||||||
|
|
@ -3993,7 +4057,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
*/
|
*/
|
||||||
if (len <= 1024 * 1024) {
|
if (len <= 1024 * 1024) {
|
||||||
rc = smb3_simple_fallocate_range(xid, tcon, cfile,
|
rc = smb3_simple_fallocate_range(xid, tcon, cfile,
|
||||||
off, len);
|
off, len, old_eof);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4005,7 +4069,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
* ie potentially making a few extra pages at the beginning
|
* ie potentially making a few extra pages at the beginning
|
||||||
* or end of the file non-sparse via set_sparse is harmless.
|
* or end of the file non-sparse via set_sparse is harmless.
|
||||||
*/
|
*/
|
||||||
if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
|
if (off > 8192 || off + len + 8192 < old_eof) {
|
||||||
rc = -EOPNOTSUPP;
|
rc = -EOPNOTSUPP;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4796,14 +4796,20 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
rdata->got_bytes);
|
rdata->got_bytes);
|
||||||
|
|
||||||
if (rdata->result == -ENODATA) {
|
if (rdata->result == -ENODATA) {
|
||||||
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
|
|
||||||
rdata->result = 0;
|
rdata->result = 0;
|
||||||
|
if (rdata->subreq.start + rdata->subreq.transferred >= i_size_read(inode))
|
||||||
|
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
|
||||||
|
else
|
||||||
|
__set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags);
|
||||||
} else {
|
} else {
|
||||||
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
|
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
|
||||||
if (trans < rdata->subreq.len &&
|
if (trans < rdata->subreq.len &&
|
||||||
rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) {
|
rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) {
|
||||||
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
|
|
||||||
rdata->result = 0;
|
rdata->result = 0;
|
||||||
|
if (rdata->subreq.start + trans >= i_size_read(inode))
|
||||||
|
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
|
||||||
|
else
|
||||||
|
__set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags);
|
||||||
}
|
}
|
||||||
if (rdata->got_bytes)
|
if (rdata->got_bytes)
|
||||||
__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
|
__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,8 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
||||||
ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *iter,
|
ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *iter,
|
||||||
struct netfs_group *netfs_group);
|
struct netfs_group *netfs_group);
|
||||||
ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
||||||
|
void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
|
||||||
|
uoff_t to);
|
||||||
|
|
||||||
/* Single, monolithic object read/write API. */
|
/* Single, monolithic object read/write API. */
|
||||||
void netfs_single_mark_inode_dirty(struct inode *inode);
|
void netfs_single_mark_inode_dirty(struct inode *inode);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user