Merge tag 'vfs-7.1-rc5.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs fixes from Christian Brauner:
 "This contains a fixes for the current development cycle. Note that AI
  related review sometimes delays fixes a bit because we find more fixes
  for the fixes. I might try and send smaller but more fixes PRs if this
  trend keeps up.

   - Fix various netfslib bugs

   - Fix an out-of-bounds write when listing idmappings

   - Fix the return values in jfs_mkdir() and orangefs_mkdir()

   - Fix a writeback writeback array overflow in fuse

   - Fix a forced iversion increment on lazytime timestamp updates

   - Reject a negative timeval component in kern_select()

   - Fix error return when vfs_mkdir() fails in the cachefiles code

   - Fix wrong error code returned for pidns ioctls"

* tag 'vfs-7.1-rc5.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (31 commits)
  cachefiles: Fix error return when vfs_mkdir() fails
  afs: Fix the locking used by afs_get_link()
  netfs, afs: Fix write skipping in dir/link writepages
  netfs: Fix netfs_read_folio() to wait on writeback
  netfs: Fix folio->private handling in netfs_perform_write()
  netfs: Fix partial invalidation of streaming-write folio
  netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages()
  netfs: Fix leak of request in netfs_write_begin() error handling
  netfs: Fix early put of sink folio in netfs_read_gaps()
  netfs: Fix write streaming disablement if fd open O_RDWR
  netfs: Fix read-gaps to remove netfs_folio from filled folio
  netfs: Fix potential deadlock in write-through mode
  netfs: Fix streaming write being overwritten
  netfs: Defer the emission of trace_netfs_folio()
  netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone
  netfs: Fix overrun check in netfs_extract_user_iter()
  netfs: fix error handling in netfs_extract_user_iter()
  netfs: Fix potential uninitialised var in netfs_extract_user_iter()
  netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call
  netfs: Fix zeropoint update where i_size > remote_i_size
  ...
This commit is contained in:
Linus Torvalds
2026-05-18 07:30:31 -07:00
44 changed files with 1172 additions and 438 deletions
-13
View File
@@ -75,17 +75,4 @@ static inline void v9fs_invalidate_inode_attr(struct inode *inode)
int v9fs_open_to_dotl_flags(int flags);
static inline void v9fs_i_size_write(struct inode *inode, loff_t i_size)
{
/*
* 32-bit need the lock, concurrent updates could break the
* sequences and make i_size_read() loop forever.
* 64-bit updates are atomic and can skip the locking.
*/
if (sizeof(i_size) > sizeof(long))
spin_lock(&inode->i_lock);
i_size_write(inode, i_size);
if (sizeof(i_size) > sizeof(long))
spin_unlock(&inode->i_lock);
}
#endif
+4 -2
View File
@@ -1141,11 +1141,13 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;
v9inode->netfs.remote_i_size = stat->length;
spin_lock(&inode->i_lock);
netfs_write_remote_i_size(inode, stat->length);
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
v9fs_i_size_write(inode, stat->length);
i_size_write(inode, stat->length);
/* not real number of blocks, but 512 byte ones ... */
inode->i_blocks = (stat->length + 512 - 1) >> 9;
spin_unlock(&inode->i_lock);
v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
}
+8 -4
View File
@@ -634,10 +634,12 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;
v9inode->netfs.remote_i_size = stat->st_size;
spin_lock(&inode->i_lock);
netfs_write_remote_i_size(inode, stat->st_size);
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
v9fs_i_size_write(inode, stat->st_size);
i_size_write(inode, stat->st_size);
inode->i_blocks = stat->st_blocks;
spin_unlock(&inode->i_lock);
} else {
if (stat->st_result_mask & P9_STATS_ATIME) {
inode_set_atime(inode, stat->st_atime_sec,
@@ -662,13 +664,15 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;
}
spin_lock(&inode->i_lock);
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
stat->st_result_mask & P9_STATS_SIZE) {
v9inode->netfs.remote_i_size = stat->st_size;
v9fs_i_size_write(inode, stat->st_size);
netfs_write_remote_i_size(inode, stat->st_size);
i_size_write(inode, stat->st_size);
}
if (stat->st_result_mask & P9_STATS_BLOCKS)
inode->i_blocks = stat->st_blocks;
spin_unlock(&inode->i_lock);
}
if (stat->st_result_mask & P9_STATS_GEN)
inode->i_generation = stat->st_gen;
+1
View File
@@ -30,6 +30,7 @@ kafs-y := \
server.o \
server_list.o \
super.o \
symlink.o \
validation.o \
vlclient.o \
vl_alias.o \
+44 -35
View File
@@ -44,6 +44,8 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags);
static int afs_dir_writepages(struct address_space *mapping,
struct writeback_control *wbc);
const struct file_operations afs_dir_file_operations = {
.open = afs_dir_open,
@@ -68,7 +70,7 @@ const struct inode_operations afs_dir_inode_operations = {
};
const struct address_space_operations afs_dir_aops = {
.writepages = afs_single_writepages,
.writepages = afs_dir_writepages,
};
const struct dentry_operations afs_fs_dentry_operations = {
@@ -233,22 +235,13 @@ static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file)
struct iov_iter iter;
ssize_t ret;
loff_t i_size;
bool is_dir = (S_ISDIR(dvnode->netfs.inode.i_mode) &&
!test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags));
i_size = i_size_read(&dvnode->netfs.inode);
if (is_dir) {
if (i_size < AFS_DIR_BLOCK_SIZE)
return afs_bad(dvnode, afs_file_error_dir_small);
if (i_size > AFS_DIR_BLOCK_SIZE * 1024) {
trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
return -EFBIG;
}
} else {
if (i_size > AFSPATHMAX) {
trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
return -EFBIG;
}
if (i_size < AFS_DIR_BLOCK_SIZE)
return afs_bad(dvnode, afs_file_error_dir_small);
if (i_size > AFS_DIR_BLOCK_SIZE * 1024) {
trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
return -EFBIG;
}
/* Expand the storage. TODO: Shrink the storage too. */
@@ -277,24 +270,18 @@ static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file)
* buffer.
*/
ret = -ESTALE;
} else if (is_dir) {
} else {
int ret2 = afs_dir_check(dvnode);
if (ret2 < 0)
ret = ret2;
} else if (i_size < folioq_folio_size(dvnode->directory, 0)) {
/* NUL-terminate a symlink. */
char *symlink = kmap_local_folio(folioq_folio(dvnode->directory, 0), 0);
symlink[i_size] = 0;
kunmap_local(symlink);
}
}
return ret;
}
ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file)
static ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file)
{
ssize_t ret;
@@ -1763,13 +1750,20 @@ error:
return ret;
}
static void afs_symlink_put(struct afs_operation *op)
{
kfree(op->create.symlink);
op->create.symlink = NULL;
afs_create_put(op);
}
static const struct afs_operation_ops afs_symlink_operation = {
.issue_afs_rpc = afs_fs_symlink,
.issue_yfs_rpc = yfs_fs_symlink,
.success = afs_create_success,
.aborted = afs_check_for_remote_deletion,
.edit_dir = afs_create_edit_dir,
.put = afs_create_put,
.put = afs_symlink_put,
};
/*
@@ -1779,7 +1773,9 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *content)
{
struct afs_operation *op;
struct afs_symlink *symlink;
struct afs_vnode *dvnode = AFS_FS_I(dir);
size_t clen = strlen(content);
int ret;
_enter("{%llx:%llu},{%pd},%s",
@@ -1791,12 +1787,20 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
goto error;
ret = -EINVAL;
if (strlen(content) >= AFSPATHMAX)
if (clen >= AFSPATHMAX)
goto error;
ret = -ENOMEM;
symlink = kmalloc_flex(struct afs_symlink, content, clen + 1, GFP_KERNEL);
if (!symlink)
goto error;
refcount_set(&symlink->ref, 1);
memcpy(symlink->content, content, clen + 1);
op = afs_alloc_operation(NULL, dvnode->volume);
if (IS_ERR(op)) {
ret = PTR_ERR(op);
kfree(symlink);
goto error;
}
@@ -1808,7 +1812,7 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
op->dentry = dentry;
op->ops = &afs_symlink_operation;
op->create.reason = afs_edit_dir_for_symlink;
op->create.symlink = content;
op->create.symlink = symlink;
op->mtime = current_time(dir);
ret = afs_do_sync_operation(op);
afs_dir_unuse_cookie(dvnode, ret);
@@ -2192,28 +2196,33 @@ error:
}
/*
* Write the file contents to the cache as a single blob.
* Write the directory contents to the cache as a single blob.
*/
int afs_single_writepages(struct address_space *mapping,
struct writeback_control *wbc)
static int afs_dir_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct afs_vnode *dvnode = AFS_FS_I(mapping->host);
struct iov_iter iter;
bool is_dir = (S_ISDIR(dvnode->netfs.inode.i_mode) &&
!test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags));
int ret = 0;
/* Need to lock to prevent the folio queue and folios from being thrown
* away.
*/
down_read(&dvnode->validate_lock);
if (!down_read_trylock(&dvnode->validate_lock)) {
if (wbc->sync_mode == WB_SYNC_NONE) {
/* The VFS will have undirtied the inode. */
netfs_single_mark_inode_dirty(&dvnode->netfs.inode);
return 0;
}
down_read(&dvnode->validate_lock);
}
if (is_dir ?
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) :
atomic64_read(&dvnode->cb_expires_at) != AFS_NO_CB_PROMISE) {
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0,
i_size_read(&dvnode->netfs.inode));
ret = netfs_writeback_single(mapping, wbc, &iter);
if (ret == 1)
ret = 0; /* Skipped write due to lock conflict. */
}
up_read(&dvnode->validate_lock);
+19 -5
View File
@@ -427,21 +427,35 @@ static void afs_free_request(struct netfs_io_request *rreq)
afs_put_wb_key(rreq->netfs_priv2);
}
static void afs_update_i_size(struct inode *inode, loff_t new_i_size)
/*
* Set the file size and block count, taking ->cb_lock and ->i_lock to maintain
* coherency and prevent 64-bit tearing on 32-bit arches.
*
* Also, estimate the number of 512 bytes blocks used, rounded up to nearest 1K
* for consistency with other AFS clients.
*/
void afs_set_i_size(struct afs_vnode *vnode, loff_t new_i_size)
{
struct afs_vnode *vnode = AFS_FS_I(inode);
struct inode *inode = &vnode->netfs.inode;
loff_t i_size;
write_seqlock(&vnode->cb_lock);
i_size = i_size_read(&vnode->netfs.inode);
spin_lock(&inode->i_lock);
i_size = i_size_read(inode);
if (new_i_size > i_size) {
i_size_write(&vnode->netfs.inode, new_i_size);
inode_set_bytes(&vnode->netfs.inode, new_i_size);
i_size_write(inode, new_i_size);
inode_set_bytes(inode, round_up(new_i_size, 1024));
}
spin_unlock(&inode->i_lock);
write_sequnlock(&vnode->cb_lock);
fscache_update_cookie(afs_vnode_cache(vnode), NULL, &new_i_size);
}
static void afs_update_i_size(struct inode *inode, loff_t new_i_size)
{
afs_set_i_size(AFS_FS_I(inode), new_i_size);
}
static void afs_netfs_invalidate_cache(struct netfs_io_request *wreq)
{
struct afs_vnode *vnode = AFS_FS_I(wreq->inode);
+2 -2
View File
@@ -886,7 +886,7 @@ void afs_fs_symlink(struct afs_operation *op)
namesz = name->len;
padsz = (4 - (namesz & 3)) & 3;
c_namesz = strlen(op->create.symlink);
c_namesz = strlen(op->create.symlink->content);
c_padsz = (4 - (c_namesz & 3)) & 3;
reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
@@ -910,7 +910,7 @@ void afs_fs_symlink(struct afs_operation *op)
bp = (void *) bp + padsz;
}
*bp++ = htonl(c_namesz);
memcpy(bp, op->create.symlink, c_namesz);
memcpy(bp, op->create.symlink->content, c_namesz);
bp = (void *) bp + c_namesz;
if (c_padsz > 0) {
memset(bp, 0, c_padsz);
+26 -101
View File
@@ -25,96 +25,6 @@
#include "internal.h"
#include "afs_fs.h"
void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op)
{
size_t size = strlen(op->create.symlink) + 1;
size_t dsize = 0;
char *p;
if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size,
mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0)
return;
vnode->directory_size = dsize;
p = kmap_local_folio(folioq_folio(vnode->directory, 0), 0);
memcpy(p, op->create.symlink, size);
kunmap_local(p);
set_bit(AFS_VNODE_DIR_READ, &vnode->flags);
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
}
static void afs_put_link(void *arg)
{
struct folio *folio = virt_to_folio(arg);
kunmap_local(arg);
folio_put(folio);
}
const char *afs_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *callback)
{
struct afs_vnode *vnode = AFS_FS_I(inode);
struct folio *folio;
char *content;
ssize_t ret;
if (!dentry) {
/* RCU pathwalk. */
if (!test_bit(AFS_VNODE_DIR_READ, &vnode->flags) || !afs_check_validity(vnode))
return ERR_PTR(-ECHILD);
goto good;
}
if (test_bit(AFS_VNODE_DIR_READ, &vnode->flags))
goto fetch;
ret = afs_validate(vnode, NULL);
if (ret < 0)
return ERR_PTR(ret);
if (!test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
test_bit(AFS_VNODE_DIR_READ, &vnode->flags))
goto good;
fetch:
ret = afs_read_single(vnode, NULL);
if (ret < 0)
return ERR_PTR(ret);
set_bit(AFS_VNODE_DIR_READ, &vnode->flags);
good:
folio = folioq_folio(vnode->directory, 0);
folio_get(folio);
content = kmap_local_folio(folio, 0);
set_delayed_call(callback, afs_put_link, content);
return content;
}
int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
DEFINE_DELAYED_CALL(done);
const char *content;
int len;
content = afs_get_link(dentry, d_inode(dentry), &done);
if (IS_ERR(content)) {
do_delayed_call(&done);
return PTR_ERR(content);
}
len = umin(strlen(content), buflen);
if (copy_to_user(buffer, content, len))
len = -EFAULT;
do_delayed_call(&done);
return len;
}
static const struct inode_operations afs_symlink_inode_operations = {
.get_link = afs_get_link,
.readlink = afs_readlink,
};
static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *parent_vnode)
{
static unsigned long once_only;
@@ -214,7 +124,7 @@ static int afs_inode_init_from_status(struct afs_operation *op,
inode->i_mode = S_IFLNK | status->mode;
inode->i_op = &afs_symlink_inode_operations;
}
inode->i_mapping->a_ops = &afs_dir_aops;
inode->i_mapping->a_ops = &afs_symlink_aops;
inode_nohighmem(inode);
mapping_set_release_always(inode->i_mapping);
break;
@@ -224,7 +134,8 @@ static int afs_inode_init_from_status(struct afs_operation *op,
return afs_protocol_error(NULL, afs_eproto_file_type);
}
afs_set_i_size(vnode, status->size);
i_size_write(inode, status->size);
inode_set_bytes(inode, status->size);
afs_set_netfs_context(vnode);
vnode->invalid_before = status->data_version;
@@ -253,7 +164,8 @@ static void afs_apply_status(struct afs_operation *op,
{
struct afs_file_status *status = &vp->scb.status;
struct afs_vnode *vnode = vp->vnode;
struct inode *inode = &vnode->netfs.inode;
struct netfs_inode *ictx = &vnode->netfs;
struct inode *inode = &ictx->inode;
struct timespec64 t;
umode_t mode;
bool unexpected_jump = false;
@@ -336,6 +248,8 @@ static void afs_apply_status(struct afs_operation *op,
}
if (data_changed) {
unsigned long long zero_point, size = status->size;
inode_set_iversion_raw(inode, status->data_version);
/* Only update the size if the data version jumped. If the
@@ -343,16 +257,25 @@ static void afs_apply_status(struct afs_operation *op,
* idea of what the size should be that's not the same as
* what's on the server.
*/
vnode->netfs.remote_i_size = status->size;
if (change_size || status->size > i_size_read(inode)) {
afs_set_i_size(vnode, status->size);
spin_lock(&inode->i_lock);
if (change_size || size > i_size_read(inode)) {
/* We can read the sizes directly as we hold i_lock. */
zero_point = ictx->_zero_point;
if (unexpected_jump)
vnode->netfs.zero_point = status->size;
zero_point = size;
netfs_write_sizes(inode, size, size, zero_point);
inode_set_bytes(inode, size);
inode_set_ctime_to_ts(inode, t);
inode_set_atime_to_ts(inode, t);
} else {
netfs_write_remote_i_size(inode, size);
}
spin_unlock(&inode->i_lock);
if (op->ops == &afs_fetch_data_operation)
op->fetch.subreq->rreq->i_size = status->size;
op->fetch.subreq->rreq->i_size = size;
}
}
@@ -709,7 +632,7 @@ int afs_getattr(struct mnt_idmap *idmap, const struct path *path,
* it, but we need to give userspace the server's size.
*/
if (S_ISDIR(inode->i_mode))
stat->size = vnode->netfs.remote_i_size;
stat->size = netfs_read_remote_i_size(inode);
} while (read_seqretry(&vnode->cb_lock, seq));
return 0;
@@ -756,12 +679,14 @@ void afs_evict_inode(struct inode *inode)
.range_end = LLONG_MAX,
};
afs_single_writepages(inode->i_mapping, &wbc);
inode->i_mapping->a_ops->writepages(inode->i_mapping, &wbc);
}
netfs_wait_for_outstanding_io(inode);
truncate_inode_pages_final(&inode->i_data);
netfs_free_folioq_buffer(vnode->directory);
if (vnode->symlink)
afs_evict_symlink(vnode);
afs_set_cache_aux(vnode, &aux);
netfs_clear_inode_writeback(inode, &aux);
@@ -889,7 +814,7 @@ int afs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
*/
if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) &&
attr->ia_size < i_size &&
attr->ia_size > vnode->netfs.remote_i_size) {
attr->ia_size > netfs_read_remote_i_size(inode)) {
truncate_setsize(inode, attr->ia_size);
netfs_resize_file(&vnode->netfs, size, false);
fscache_resize_cookie(afs_vnode_cache(vnode),
+27 -18
View File
@@ -710,6 +710,7 @@ struct afs_vnode {
#define AFS_VNODE_DIR_READ 11 /* Set if we've read a dir's contents */
struct folio_queue *directory; /* Directory contents */
struct afs_symlink __rcu *symlink; /* Symlink content */
struct list_head wb_keys; /* List of keys available for writeback */
struct list_head pending_locks; /* locks waiting to be granted */
struct list_head granted_locks; /* locks granted on this file */
@@ -776,6 +777,15 @@ struct afs_permits {
struct afs_permit permits[] __counted_by(nr_permits); /* List of permits sorted by key pointer */
};
/*
* Copy of symlink content for normal use.
*/
struct afs_symlink {
struct rcu_head rcu;
refcount_t ref;
char content[];
};
/*
* Error prioritisation and accumulation.
*/
@@ -887,7 +897,7 @@ struct afs_operation {
struct {
int reason; /* enum afs_edit_dir_reason */
mode_t mode;
const char *symlink;
struct afs_symlink *symlink;
} create;
struct {
bool need_rehash;
@@ -1098,13 +1108,10 @@ extern const struct inode_operations afs_dir_inode_operations;
extern const struct address_space_operations afs_dir_aops;
extern const struct dentry_operations afs_fs_dentry_operations;
ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file);
ssize_t afs_read_dir(struct afs_vnode *dvnode, struct file *file)
__acquires(&dvnode->validate_lock);
extern void afs_d_release(struct dentry *);
extern void afs_check_for_remote_deletion(struct afs_operation *);
int afs_single_writepages(struct address_space *mapping,
struct writeback_control *wbc);
/*
* dir_edit.c
@@ -1157,6 +1164,7 @@ extern int afs_open(struct inode *, struct file *);
extern int afs_release(struct inode *, struct file *);
void afs_fetch_data_async_rx(struct work_struct *work);
void afs_fetch_data_immediate_cancel(struct afs_call *call);
void afs_set_i_size(struct afs_vnode *vnode, loff_t new_i_size);
/*
* flock.c
@@ -1246,10 +1254,6 @@ extern void afs_fs_probe_cleanup(struct afs_net *);
*/
extern const struct afs_operation_ops afs_fetch_status_operation;
void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op);
const char *afs_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *callback);
int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen);
extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *);
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
extern int afs_ilookup5_test_by_fid(struct inode *, void *);
@@ -1599,6 +1603,21 @@ void afs_detach_volume_from_servers(struct afs_volume *volume, struct afs_server
extern int __init afs_fs_init(void);
extern void afs_fs_exit(void);
/*
* symlink.c
*/
extern const struct inode_operations afs_symlink_inode_operations;
extern const struct address_space_operations afs_symlink_aops;
void afs_invalidate_symlink(struct afs_vnode *vnode);
void afs_evict_symlink(struct afs_vnode *vnode);
void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op);
const char *afs_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *callback);
int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen);
int afs_symlink_writepages(struct address_space *mapping,
struct writeback_control *wbc);
/*
* validation.c
*/
@@ -1758,16 +1777,6 @@ static inline void afs_update_dentry_version(struct afs_operation *op,
(void *)(unsigned long)dir_vp->scb.status.data_version;
}
/*
* Set the file size and block count. Estimate the number of 512 bytes blocks
* used, rounded up to nearest 1K for consistency with other AFS clients.
*/
static inline void afs_set_i_size(struct afs_vnode *vnode, u64 size)
{
i_size_write(&vnode->netfs.inode, size);
vnode->netfs.inode.i_blocks = ((size + 1023) >> 10) << 1;
}
/*
* Check for a conflicting operation on a directory that we just unlinked from.
* If someone managed to sneak a link or an unlink in on the file we just
+278
View File
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* AFS filesystem symbolic link handling
*
* Copyright (C) 2026 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/iov_iter.h>
#include "internal.h"
static void afs_put_symlink(struct afs_symlink *symlink)
{
if (refcount_dec_and_test(&symlink->ref))
kfree_rcu(symlink, rcu);
}
static void afs_replace_symlink(struct afs_vnode *vnode, struct afs_symlink *symlink)
{
struct afs_symlink *old;
old = rcu_replace_pointer(vnode->symlink, symlink,
lockdep_is_held(&vnode->validate_lock));
if (old)
afs_put_symlink(old);
}
/*
* In the event that a third-party update of a symlink occurs, dispose of the
* copy of the old contents. Called under ->validate_lock.
*/
void afs_invalidate_symlink(struct afs_vnode *vnode)
{
afs_replace_symlink(vnode, NULL);
}
/*
* Dispose of a symlink copy during inode deletion.
*/
void afs_evict_symlink(struct afs_vnode *vnode)
{
struct afs_symlink *old;
old = rcu_replace_pointer(vnode->symlink, NULL, true);
if (old)
afs_put_symlink(old);
}
/*
* Set up a locally created symlink inode for immediate write to the cache.
*/
void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op)
{
struct afs_symlink *symlink = op->create.symlink;
size_t dsize = 0;
size_t size = strlen(symlink->content) + 1;
char *p;
rcu_assign_pointer(vnode->symlink, symlink);
op->create.symlink = NULL;
if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs)))
return;
if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size,
mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0)
return;
vnode->directory_size = dsize;
p = kmap_local_folio(folioq_folio(vnode->directory, 0), 0);
memcpy(p, symlink->content, size);
kunmap_local(p);
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
}
/*
* Read a symlink in a single download.
*/
static ssize_t afs_do_read_symlink(struct afs_vnode *vnode)
{
struct afs_symlink *symlink;
struct iov_iter iter;
ssize_t ret;
loff_t i_size;
i_size = i_size_read(&vnode->netfs.inode);
if (i_size > PAGE_SIZE - 1) {
trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big);
return -EFBIG;
}
if (!vnode->directory) {
size_t cur_size = 0;
ret = netfs_alloc_folioq_buffer(NULL,
&vnode->directory, &cur_size, PAGE_SIZE,
mapping_gfp_mask(vnode->netfs.inode.i_mapping));
vnode->directory_size = PAGE_SIZE - 1;
if (ret < 0)
return ret;
}
iov_iter_folio_queue(&iter, ITER_DEST, vnode->directory, 0, 0, PAGE_SIZE);
/* AFS requires us to perform the read of a symlink as a single unit to
* avoid issues with the content being changed between reads.
*/
ret = netfs_read_single(&vnode->netfs.inode, NULL, &iter);
if (ret >= 0) {
i_size = ret;
if (i_size > PAGE_SIZE - 1) {
trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big);
return -EFBIG;
}
vnode->directory_size = i_size;
/* Copy the symlink. */
symlink = kmalloc_flex(struct afs_symlink, content, i_size + 1,
GFP_KERNEL);
if (!symlink)
return -ENOMEM;
refcount_set(&symlink->ref, 1);
symlink->content[i_size] = 0;
const char *s = kmap_local_folio(folioq_folio(vnode->directory, 0), 0);
memcpy(symlink->content, s, i_size);
kunmap_local(s);
afs_replace_symlink(vnode, symlink);
}
if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs))) {
netfs_free_folioq_buffer(vnode->directory);
vnode->directory = NULL;
vnode->directory_size = 0;
}
return ret;
}
static ssize_t afs_read_symlink(struct afs_vnode *vnode)
{
ssize_t ret;
fscache_use_cookie(afs_vnode_cache(vnode), false);
ret = afs_do_read_symlink(vnode);
fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL);
return ret;
}
static void afs_put_link(void *arg)
{
afs_put_symlink(arg);
}
const char *afs_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *callback)
{
struct afs_symlink *symlink;
struct afs_vnode *vnode = AFS_FS_I(inode);
ssize_t ret;
if (!dentry) {
/* RCU pathwalk. */
symlink = rcu_dereference(vnode->symlink);
if (!symlink || !afs_check_validity(vnode))
return ERR_PTR(-ECHILD);
set_delayed_call(callback, NULL, NULL);
return symlink->content;
}
if (vnode->symlink) {
ret = afs_validate(vnode, NULL);
if (ret < 0)
return ERR_PTR(ret);
down_read(&vnode->validate_lock);
if (vnode->symlink)
goto good;
up_read(&vnode->validate_lock);
}
if (down_write_killable(&vnode->validate_lock) < 0)
return ERR_PTR(-ERESTARTSYS);
if (!vnode->symlink) {
ret = afs_read_symlink(vnode);
if (ret < 0) {
up_write(&vnode->validate_lock);
return ERR_PTR(ret);
}
}
downgrade_write(&vnode->validate_lock);
good:
symlink = rcu_dereference_protected(vnode->symlink,
lockdep_is_held(&vnode->validate_lock));
refcount_inc(&symlink->ref);
up_read(&vnode->validate_lock);
set_delayed_call(callback, afs_put_link, symlink);
return symlink->content;
}
int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
DEFINE_DELAYED_CALL(done);
const char *content;
int len;
content = afs_get_link(dentry, d_inode(dentry), &done);
if (IS_ERR(content)) {
do_delayed_call(&done);
return PTR_ERR(content);
}
len = umin(strlen(content), buflen);
if (copy_to_user(buffer, content, len))
len = -EFAULT;
do_delayed_call(&done);
return len;
}
/*
* Write the symlink contents to the cache as a single blob. We then throw
* away the page we used to receive it.
*/
int afs_symlink_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct iov_iter iter;
int ret = 0;
if (!down_read_trylock(&vnode->validate_lock)) {
if (wbc->sync_mode == WB_SYNC_NONE) {
/* The VFS will have undirtied the inode. */
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
return 0;
}
down_read(&vnode->validate_lock);
}
if (vnode->directory &&
atomic64_read(&vnode->cb_expires_at) != AFS_NO_CB_PROMISE) {
iov_iter_folio_queue(&iter, ITER_SOURCE, vnode->directory, 0, 0,
i_size_read(&vnode->netfs.inode));
ret = netfs_writeback_single(mapping, wbc, &iter);
}
if (ret == 0) {
mutex_lock(&vnode->netfs.wb_lock);
netfs_free_folioq_buffer(vnode->directory);
vnode->directory = NULL;
vnode->directory_size = 0;
mutex_unlock(&vnode->netfs.wb_lock);
} else if (ret == 1) {
ret = 0; /* Skipped write due to lock conflict. */
}
up_read(&vnode->validate_lock);
return ret;
}
const struct inode_operations afs_symlink_inode_operations = {
.get_link = afs_get_link,
.readlink = afs_readlink,
};
const struct address_space_operations afs_symlink_aops = {
.writepages = afs_symlink_writepages,
};
+10 -4
View File
@@ -465,11 +465,17 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
vnode->cb_ro_snapshot = cb_ro_snapshot;
vnode->cb_scrub = cb_scrub;
/* if the vnode's data version number changed then its contents are
* different */
/* If the vnode's data version number changed then its contents are
* different. Note that afs_apply_status() doesn't set ZAP_DATA on
* directories.
*/
zap |= test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
if (zap)
afs_zap_data(vnode);
if (zap) {
if (S_ISREG(vnode->netfs.inode.i_mode))
afs_zap_data(vnode);
else if (S_ISLNK(vnode->netfs.inode.i_mode))
afs_invalidate_symlink(vnode);
}
up_write(&vnode->validate_lock);
_leave(" = 0");
return 0;
+1 -1
View File
@@ -142,7 +142,7 @@ static void afs_issue_write_worker(struct work_struct *work)
afs_begin_vnode_operation(op);
op->store.write_iter = &subreq->io_iter;
op->store.i_size = umax(pos + len, vnode->netfs.remote_i_size);
op->store.i_size = umax(pos + len, netfs_read_remote_i_size(&vnode->netfs.inode));
op->mtime = inode_get_mtime(&vnode->netfs.inode);
afs_wait_for_operation(op);
+2 -2
View File
@@ -960,7 +960,7 @@ void yfs_fs_symlink(struct afs_operation *op)
_enter("");
contents_sz = strlen(op->create.symlink);
contents_sz = strlen(op->create.symlink->content);
call = afs_alloc_flat_call(op->net, &yfs_RXYFSSymlink,
sizeof(__be32) +
sizeof(struct yfs_xdr_RPCFlags) +
@@ -981,7 +981,7 @@ void yfs_fs_symlink(struct afs_operation *op)
bp = xdr_encode_u32(bp, 0); /* RPC flags */
bp = xdr_encode_YFSFid(bp, &dvp->fid);
bp = xdr_encode_name(bp, name);
bp = xdr_encode_string(bp, op->create.symlink, contents_sz);
bp = xdr_encode_string(bp, op->create.symlink->content, contents_sz);
bp = xdr_encode_YFSStoreStatus(bp, &mode, &op->mtime);
yfs_check_req(call, bp);
+2
View File
@@ -130,6 +130,8 @@ retry:
ret = cachefiles_inject_write_error();
if (ret == 0) {
subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
if (IS_ERR(subdir))
ret = PTR_ERR(subdir);
} else {
end_creating(subdir);
subdir = ERR_PTR(ret);
+4 -1
View File
@@ -2176,7 +2176,10 @@ static bool fuse_folios_need_send(struct fuse_conn *fc, loff_t pos,
WARN_ON(!ap->num_folios);
/* Reached max pages */
/* Reached max pages or max folio slots */
if (ap->num_folios >= fc->max_pages)
return true;
if (DIV_ROUND_UP(bytes, PAGE_SIZE) > fc->max_pages)
return true;
+7 -1
View File
@@ -2124,7 +2124,13 @@ static int inode_update_cmtime(struct inode *inode, unsigned int flags)
inode_iversion_need_inc(inode))
return -EAGAIN;
} else {
if (inode_maybe_inc_iversion(inode, !!dirty))
/*
* Don't force iversion increment for pure lazytime
* updates (I_DIRTY_TIME only), let I_VERSION_QUERIED
* dictate whether the increment is needed.
*/
if (inode_maybe_inc_iversion(inode,
dirty != I_DIRTY_TIME))
dirty |= I_DIRTY_SYNC;
}
}
+1 -1
View File
@@ -309,7 +309,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip,
out1:
jfs_info("jfs_mkdir: rc:%d", rc);
return ERR_PTR(rc);
return rc ? ERR_PTR(rc) : NULL;
}
/*
+2
View File
@@ -375,6 +375,8 @@ int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_
continue;
seq_printf(seq, "%u %u %u", extent->first, lower, extent->count);
if (seq_has_overflowed(seq))
return -EAGAIN;
seq->count++; /* mappings are separated by \0 */
if (seq_has_overflowed(seq))
+39 -34
View File
@@ -156,9 +156,8 @@ static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq,
netfs_cache_read_terminated, subreq);
}
static void netfs_queue_read(struct netfs_io_request *rreq,
struct netfs_io_subrequest *subreq,
bool last_subreq)
void netfs_queue_read(struct netfs_io_request *rreq,
struct netfs_io_subrequest *subreq)
{
struct netfs_io_stream *stream = &rreq->io_streams[0];
@@ -169,7 +168,8 @@ static void netfs_queue_read(struct netfs_io_request *rreq,
* remove entries off of the front.
*/
spin_lock(&rreq->lock);
list_add_tail(&subreq->rreq_link, &stream->subrequests);
/* Write IN_PROGRESS before pointer to new subreq */
list_add_tail_release(&subreq->rreq_link, &stream->subrequests);
if (list_is_first(&subreq->rreq_link, &stream->subrequests)) {
if (!stream->active) {
stream->collected_to = subreq->start;
@@ -178,11 +178,6 @@ static void netfs_queue_read(struct netfs_io_request *rreq,
}
}
if (last_subreq) {
smp_wmb(); /* Write lists before ALL_QUEUED. */
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
}
spin_unlock(&rreq->lock);
}
@@ -214,7 +209,6 @@ static void netfs_issue_read(struct netfs_io_request *rreq,
static void netfs_read_to_pagecache(struct netfs_io_request *rreq,
struct readahead_control *ractl)
{
struct netfs_inode *ictx = netfs_inode(rreq->inode);
unsigned long long start = rreq->start;
ssize_t size = rreq->len;
int ret = 0;
@@ -233,10 +227,13 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq,
subreq->start = start;
subreq->len = size;
netfs_queue_read(rreq, subreq);
source = netfs_cache_prepare_read(rreq, subreq, rreq->i_size);
subreq->source = source;
if (source == NETFS_DOWNLOAD_FROM_SERVER) {
unsigned long long zp = umin(ictx->zero_point, rreq->i_size);
unsigned long long zero_point = netfs_read_zero_point(rreq->inode);
unsigned long long zp = umin(zero_point, rreq->i_size);
size_t len = subreq->len;
if (unlikely(rreq->origin == NETFS_READ_SINGLE))
@@ -252,7 +249,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq,
pr_err("ZERO-LEN READ: R=%08x[%x] l=%zx/%zx s=%llx z=%llx i=%llx",
rreq->debug_id, subreq->debug_index,
subreq->len, size,
subreq->start, ictx->zero_point, rreq->i_size);
subreq->start, zero_point, rreq->i_size);
netfs_cancel_read(subreq, ret);
break;
}
subreq->len = len;
@@ -261,12 +259,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq,
if (rreq->netfs_ops->prepare_read) {
ret = rreq->netfs_ops->prepare_read(subreq);
if (ret < 0) {
subreq->error = ret;
/* Not queued - release both refs. */
netfs_put_subrequest(subreq,
netfs_sreq_trace_put_cancel);
netfs_put_subrequest(subreq,
netfs_sreq_trace_put_cancel);
netfs_cancel_read(subreq, ret);
break;
}
trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
@@ -289,24 +282,29 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq,
pr_err("Unexpected read source %u\n", source);
WARN_ON_ONCE(1);
netfs_cancel_read(subreq, ret);
break;
issue:
slice = netfs_prepare_read_iterator(subreq, ractl);
if (slice < 0) {
ret = slice;
subreq->error = ret;
trace_netfs_sreq(subreq, netfs_sreq_trace_cancel);
/* Not queued - release both refs. */
netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel);
netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel);
netfs_cancel_read(subreq, ret);
break;
}
size -= slice;
start += slice;
size -= slice;
if (size <= 0) {
smp_wmb(); /* Write lists before ALL_QUEUED. */
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
}
netfs_queue_read(rreq, subreq, size <= 0);
netfs_issue_read(rreq, subreq);
if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags))
netfs_wait_for_paused_read(rreq);
if (test_bit(NETFS_RREQ_FAILED, &rreq->flags))
break;
cond_resched();
} while (size > 0);
@@ -397,6 +395,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)
{
struct netfs_io_request *rreq;
struct address_space *mapping = folio->mapping;
struct netfs_group *group = netfs_folio_group(folio);
struct netfs_folio *finfo = netfs_folio_info(folio);
struct netfs_inode *ctx = netfs_inode(mapping->host);
struct folio *sink = NULL;
@@ -458,14 +457,20 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)
netfs_read_to_pagecache(rreq, NULL);
if (sink)
folio_put(sink);
ret = netfs_wait_for_read(rreq);
if (ret >= 0) {
if (group)
folio_change_private(folio, group);
else
folio_detach_private(folio);
kfree(finfo);
trace_netfs_folio(folio, netfs_folio_trace_filled_gaps);
flush_dcache_folio(folio);
folio_mark_uptodate(folio);
}
if (sink)
folio_put(sink);
folio_unlock(folio);
netfs_put_request(rreq, netfs_rreq_trace_put_return);
return ret < 0 ? ret : 0;
@@ -498,10 +503,10 @@ int netfs_read_folio(struct file *file, struct folio *folio)
struct netfs_inode *ctx = netfs_inode(mapping->host);
int ret;
if (folio_test_dirty(folio)) {
trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
folio_wait_writeback(folio);
if (folio_test_dirty(folio))
return netfs_read_gaps(file, folio);
}
_enter("%lx", folio->index);
@@ -667,7 +672,7 @@ retry:
ret = PTR_ERR(rreq);
goto error;
}
rreq->no_unlock_folio = folio->index;
rreq->no_unlock_folio = folio;
__set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
ret = netfs_begin_cache_read(rreq, ctx);
@@ -684,9 +689,9 @@ retry:
netfs_read_to_pagecache(rreq, NULL);
ret = netfs_wait_for_read(rreq);
netfs_put_request(rreq, netfs_rreq_trace_put_return);
if (ret < 0)
goto error;
netfs_put_request(rreq, netfs_rreq_trace_put_return);
have_folio:
ret = folio_wait_private_2_killable(folio);
@@ -733,7 +738,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
goto error;
}
rreq->no_unlock_folio = folio->index;
rreq->no_unlock_folio = folio;
__set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
ret = netfs_begin_cache_read(rreq, ctx);
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
+111 -63
View File
@@ -12,24 +12,6 @@
#include <linux/slab.h>
#include "internal.h"
static void __netfs_set_group(struct folio *folio, struct netfs_group *netfs_group)
{
if (netfs_group)
folio_attach_private(folio, netfs_get_group(netfs_group));
}
static void netfs_set_group(struct folio *folio, struct netfs_group *netfs_group)
{
void *priv = folio_get_private(folio);
if (unlikely(priv != netfs_group)) {
if (netfs_group && (!priv || priv == NETFS_FOLIO_COPY_TO_CACHE))
folio_attach_private(folio, netfs_get_group(netfs_group));
else if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE)
folio_detach_private(folio);
}
}
/*
* Grab a folio for writing and lock it. Attempt to allocate as large a folio
* as possible to hold as much of the remaining length as possible in one go.
@@ -149,6 +131,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
}
do {
enum netfs_folio_trace trace;
struct netfs_folio *finfo;
struct netfs_group *group;
unsigned long long fpos;
@@ -156,6 +139,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
size_t offset; /* Offset into pagecache folio */
size_t part; /* Bytes to write to folio */
size_t copied; /* Bytes copied from user */
void *priv;
offset = pos & (max_chunk - 1);
part = min(max_chunk - offset, iov_iter_count(iter));
@@ -201,73 +185,99 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
goto error_folio_unlock;
}
/* Decide how we should modify a folio. We might be attempting
* to do write-streaming, in which case we don't want to a
* local RMW cycle if we can avoid it. If we're doing local
* caching or content crypto, we award that priority over
* avoiding RMW. If the file is open readably, then we also
* assume that we may want to read what we wrote.
*/
finfo = netfs_folio_info(folio);
group = netfs_folio_group(folio);
/* If the requested group differs from the group set on the
* page, then we need to flush out the folio if it has a group
* set (ie. is non-NULL). Note that COPY_TO_CACHE is a special
* case, being a netfs annotation rather than an actual group.
*
* The filesystem isn't permitted to mix writes with groups and
* writes without groups as the NULL group is used to indicate
* that no group is set.
*/
if (unlikely(group != netfs_group) &&
group != NETFS_FOLIO_COPY_TO_CACHE)
group != NETFS_FOLIO_COPY_TO_CACHE &&
group) {
WARN_ON_ONCE(!netfs_group);
goto flush_content;
}
/* Decide how we should modify a folio. We might be attempting
* to do write-streaming, as we don't want to a local RMW cycle
* if we can avoid it. If we're doing local caching or content
* crypto, we award that priority over avoiding RMW. If the
* file is open readably, then we let ->read_folio() fill in
* the gaps.
*/
if (folio_test_uptodate(folio)) {
if (mapping_writably_mapped(mapping))
flush_dcache_folio(folio);
copied = copy_folio_from_iter_atomic(folio, offset, part, iter);
if (unlikely(copied == 0))
goto copy_failed;
netfs_set_group(folio, netfs_group);
trace_netfs_folio(folio, netfs_folio_is_uptodate);
goto copied;
trace = netfs_folio_is_uptodate;
goto copied_uptodate;
}
/* If the page is above the zero-point then we assume that the
* server would just return a block of zeros or a short read if
* we try to read it.
*/
if (fpos >= ctx->zero_point) {
if (fpos >= netfs_read_zero_point(inode)) {
folio_zero_segment(folio, 0, offset);
copied = copy_folio_from_iter_atomic(folio, offset, part, iter);
if (unlikely(copied == 0))
goto copy_failed;
folio_zero_segment(folio, offset + copied, flen);
__netfs_set_group(folio, netfs_group);
folio_mark_uptodate(folio);
trace_netfs_folio(folio, netfs_modify_and_clear);
goto copied;
if (finfo)
trace = netfs_modify_and_clear_rm_finfo;
else
trace = netfs_modify_and_clear;
goto mark_uptodate;
}
/* See if we can write a whole folio in one go. */
if (!maybe_trouble && offset == 0 && part >= flen) {
copied = copy_folio_from_iter_atomic(folio, offset, part, iter);
if (unlikely(copied == 0))
if (likely(copied == part)) {
if (finfo)
trace = netfs_whole_folio_modify_filled;
else
trace = netfs_whole_folio_modify;
goto mark_uptodate;
}
if (copied == 0)
goto copy_failed;
if (unlikely(copied < part)) {
if (!finfo || copied <= finfo->dirty_offset) {
maybe_trouble = true;
iov_iter_revert(iter, copied);
copied = 0;
folio_unlock(folio);
goto retry;
}
__netfs_set_group(folio, netfs_group);
folio_mark_uptodate(folio);
trace_netfs_folio(folio, netfs_whole_folio_modify);
/* We overwrote some existing dirty data, so we have to
* accept the partial write.
*/
finfo->dirty_len += finfo->dirty_offset;
if (finfo->dirty_len == flen) {
trace = netfs_whole_folio_modify_filled_efault;
goto mark_uptodate;
}
if (copied > finfo->dirty_len)
finfo->dirty_len = copied;
finfo->dirty_offset = 0;
trace = netfs_whole_folio_modify_efault;
goto copied;
}
/* We don't want to do a streaming write on a file that loses
* caching service temporarily because the backing store got
* culled and we don't really want to get a streaming write on
* a file that's open for reading as ->read_folio() then has to
* be able to flush it.
* culled.
*/
if ((file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {
if (netfs_is_cache_enabled(ctx)) {
if (finfo) {
netfs_stat(&netfs_n_wh_wstream_conflict);
goto flush_content;
@@ -282,11 +292,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
copied = copy_folio_from_iter_atomic(folio, offset, part, iter);
if (unlikely(copied == 0))
goto copy_failed;
netfs_set_group(folio, netfs_group);
trace_netfs_folio(folio, netfs_just_prefetch);
goto copied;
trace = netfs_just_prefetch;
goto copied_uptodate;
}
/* Do a streaming write on a folio that has nothing in it yet. */
if (!finfo) {
ret = -EIO;
if (WARN_ON(folio_get_private(folio)))
@@ -295,10 +305,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
if (unlikely(copied == 0))
goto copy_failed;
if (offset == 0 && copied == flen) {
__netfs_set_group(folio, netfs_group);
folio_mark_uptodate(folio);
trace_netfs_folio(folio, netfs_streaming_filled_page);
goto copied;
trace = netfs_streaming_filled_page;
goto mark_uptodate;
}
finfo = kzalloc_obj(*finfo);
@@ -312,7 +320,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
finfo->dirty_len = copied;
folio_attach_private(folio, (void *)((unsigned long)finfo |
NETFS_FOLIO_INFO));
trace_netfs_folio(folio, netfs_streaming_write);
trace = netfs_streaming_write;
goto copied;
}
@@ -326,16 +334,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
goto copy_failed;
finfo->dirty_len += copied;
if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) {
if (finfo->netfs_group)
folio_change_private(folio, finfo->netfs_group);
else
folio_detach_private(folio);
folio_mark_uptodate(folio);
kfree(finfo);
trace_netfs_folio(folio, netfs_streaming_cont_filled_page);
} else {
trace_netfs_folio(folio, netfs_streaming_write_cont);
trace = netfs_streaming_cont_filled_page;
goto mark_uptodate;
}
trace = netfs_streaming_write_cont;
goto copied;
}
@@ -349,7 +351,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
goto out;
continue;
/* Mark a folio as being up to data when we've filled it
* completely. If the folio has a group attached, then it must
* be the same group, otherwise we should have flushed it out
* above. We have to get rid of the netfs_folio struct if
* there was one.
*/
mark_uptodate:
folio_mark_uptodate(folio);
copied_uptodate:
priv = folio_get_private(folio);
if (likely(priv == netfs_group)) {
/* Already set correctly; no change required. */
} else if (priv == NETFS_FOLIO_COPY_TO_CACHE) {
if (!netfs_group)
folio_detach_private(folio);
else
folio_change_private(folio, netfs_get_group(netfs_group));
} else if (!priv) {
folio_attach_private(folio, netfs_get_group(netfs_group));
} else {
WARN_ON_ONCE(!finfo);
if (netfs_group)
/* finfo->netfs_group has a ref */
folio_change_private(folio, netfs_group);
else
folio_detach_private(folio);
kfree(finfo);
}
copied:
trace_netfs_folio(folio, trace);
flush_dcache_folio(folio);
/* Update the inode size if we moved the EOF marker */
@@ -510,6 +543,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr
struct inode *inode = file_inode(file);
struct netfs_inode *ictx = netfs_inode(inode);
vm_fault_t ret = VM_FAULT_NOPAGE;
void *priv;
int err;
_enter("%lx", folio->index);
@@ -530,7 +564,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr
}
group = netfs_folio_group(folio);
if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE) {
if (group &&
group != netfs_group &&
group != NETFS_FOLIO_COPY_TO_CACHE) {
folio_unlock(folio);
err = filemap_fdatawrite_range(mapping,
folio_pos(folio),
@@ -552,7 +588,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr
trace_netfs_folio(folio, netfs_folio_trace_mkwrite_plus);
else
trace_netfs_folio(folio, netfs_folio_trace_mkwrite);
netfs_set_group(folio, netfs_group);
priv = folio_get_private(folio);
if (priv != netfs_group) {
if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE)
folio_detach_private(folio);
else if (netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE)
folio_change_private(folio, netfs_get_group(netfs_group));
else if (netfs_group && !priv)
folio_attach_private(folio, netfs_get_group(netfs_group));
else
WARN_ON_ONCE(1);
}
file_update_time(file);
set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags);
if (ictx->ops->post_modify)

Some files were not shown because too many files have changed in this diff Show More