mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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:
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,6 +30,7 @@ kafs-y := \
|
||||
server.o \
|
||||
server_list.o \
|
||||
super.o \
|
||||
symlink.o \
|
||||
validation.o \
|
||||
vlclient.o \
|
||||
vl_alias.o \
|
||||
|
||||
+44
-35
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user