afs: Fix application of status and callback to be under same lock

When applying the status and callback in the response of an operation,
apply them in the same critical section so that there's no race between
checking the callback state and checking status-dependent state (such as
the data version).

Fix this by:

 (1) Allocating a joint {status,callback} record (afs_status_cb) before
     calling the RPC function for each vnode for which the RPC reply
     contains a status or a status plus a callback.  A flag is set in the
     record to indicate if a callback was actually received.

 (2) These records are passed into the RPC functions to be filled in.  The
     afs_decode_status() and yfs_decode_status() functions are removed and
     the cb_lock is no longer taken.

 (3) xdr_decode_AFSFetchStatus() and xdr_decode_YFSFetchStatus() no longer
     update the vnode.

 (4) xdr_decode_AFSCallBack() and xdr_decode_YFSCallBack() no longer update
     the vnode.

 (5) vnodes, expected data-version numbers and callback break counters
     (cb_break) no longer need to be passed to the reply delivery
     functions.

     Note that, for the moment, the file locking functions still need
     access to both the call and the vnode at the same time.

 (6) afs_vnode_commit_status() is now given the cb_break value and the
     expected data_version and the task of applying the status and the
     callback to the vnode are now done here.

     This is done under a single taking of vnode->cb_lock.

 (7) afs_pages_written_back() is now called by afs_store_data() rather than
     by the reply delivery function.

     afs_pages_written_back() has been moved to before the call point and
     is now given the first and last page numbers rather than a pointer to
     the call.

 (8) The indicator from YFS.RemoveFile2 as to whether the target file
     actually got removed (status.abort_code == VNOVNODE) rather than
     merely dropping a link is now checked in afs_unlink rather than in
     xdr_decode_YFSFetchStatus().

Supplementary fixes:

 (*) afs_cache_permit() now gets the caller_access mask from the
     afs_status_cb object rather than picking it out of the vnode's status
     record.  afs_fetch_status() returns caller_access through its argument
     list for this purpose also.

 (*) afs_inode_init_from_status() now uses a write lock on cb_lock rather
     than a read lock and now sets the callback inside the same critical
     section.

Fixes: c435ee3455 ("afs: Overhaul the callback handling")
Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells 2019-05-09 15:16:10 +01:00
parent 4571577f16
commit a58823ac45
12 changed files with 920 additions and 1003 deletions

View File

@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/task_io_accounting_ops.h>
#include "internal.h"
#include "afs_fs.h"
#include "xdr_fs.h"
static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
@ -739,8 +740,7 @@ no_inline_bulk_status:
afs_fs_fetch_status(&fc,
afs_v2net(dvnode),
cookie->fids,
&scb->status,
&scb->callback,
scb,
NULL);
}
@ -771,9 +771,7 @@ success:
continue;
ti = afs_iget(dir->i_sb, key, &cookie->fids[i],
&scb->status,
&scb->callback,
cbi, dvnode);
scb, cbi, dvnode);
if (i == 0) {
inode = ti;
} else {
@ -1110,8 +1108,7 @@ void afs_d_release(struct dentry *dentry)
static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
struct dentry *new_dentry,
struct afs_fid *newfid,
struct afs_file_status *newstatus,
struct afs_callback *newcb)
struct afs_status_cb *new_scb)
{
struct afs_vnode *vnode;
struct inode *inode;
@ -1120,7 +1117,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
return;
inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key,
newfid, newstatus, newcb, fc->cbi, fc->vnode);
newfid, new_scb, fc->cbi, fc->vnode);
if (IS_ERR(inode)) {
/* ENOMEM or EINTR at a really inconvenient time - just abandon
* the new directory on the server.
@ -1131,7 +1128,8 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
vnode = AFS_FS_I(inode);
set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
afs_vnode_commit_status(fc, vnode, 0);
if (fc->ac.error == 0)
afs_cache_permit(vnode, fc->key, vnode->cb_break, new_scb);
d_instantiate(new_dentry, inode);
}
@ -1140,13 +1138,11 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
*/
static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
struct afs_file_status newstatus;
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
struct afs_callback newcb;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid;
struct key *key;
u64 data_version = dvnode->status.data_version;
int ret;
mode |= S_IFDIR;
@ -1154,23 +1150,31 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
_enter("{%llx:%llu},{%pd},%ho",
dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
ret = -ENOMEM;
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error;
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
&newfid, &newstatus, &newcb);
afs_fs_create(&fc, dentry->d_name.name, mode,
&scb[0], &newfid, &scb[1]);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, &newcb);
afs_check_for_remote_deletion(&fc, dvnode);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, &scb[0]);
afs_vnode_new_inode(&fc, dentry, &newfid, &scb[1]);
ret = afs_end_vnode_operation(&fc);
if (ret < 0)
goto error_key;
@ -1184,11 +1188,14 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
afs_edit_dir_for_create);
key_put(key);
kfree(scb);
_leave(" = 0");
return 0;
error_key:
key_put(key);
error_scb:
kfree(scb);
error:
d_drop(dentry);
_leave(" = %d", ret);
@ -1215,15 +1222,19 @@ static void afs_dir_remove_subdir(struct dentry *dentry)
*/
static int afs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
struct key *key;
u64 data_version = dvnode->status.data_version;
int ret;
_enter("{%llx:%llu},{%pd}",
dvnode->fid.vid, dvnode->fid.vnode, dentry);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
@ -1246,13 +1257,15 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_remove(&fc, vnode, dentry->d_name.name, true,
data_version);
afs_fs_remove(&fc, vnode, dentry->d_name.name, true, scb);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
if (ret == 0) {
afs_dir_remove_subdir(dentry);
@ -1267,6 +1280,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
error_key:
key_put(key);
error:
kfree(scb);
return ret;
}
@ -1326,11 +1340,11 @@ int afs_dir_remove_link(struct dentry *dentry, struct key *key,
static int afs_unlink(struct inode *dir, struct dentry *dentry)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
struct key *key;
unsigned long d_version = (unsigned long)dentry->d_fsdata;
bool need_rehash = false;
u64 data_version = dvnode->status.data_version;
int ret;
_enter("{%llx:%llu},{%pd}",
@ -1339,10 +1353,15 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
if (dentry->d_name.len >= AFSNAMEMAX)
return -ENAMETOOLONG;
ret = -ENOMEM;
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error;
goto error_scb;
}
/* Try to make sure we have a callback promise on the victim. */
@ -1370,24 +1389,32 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
!test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
data_version);
&scb[0], &scb[1]);
if (fc.ac.error == 0 &&
scb[1].status.abort_code == VNOVNODE) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
afs_break_callback(vnode);
}
if (fc.ac.error != -ECONNABORTED ||
fc.ac.abort_code != RXGEN_OPCODE)
continue;
set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
}
afs_fs_remove(&fc, vnode, dentry->d_name.name, false,
data_version);
afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, &scb[0]);
ret = afs_end_vnode_operation(&fc);
if (ret == 0)
ret = afs_dir_remove_link(
@ -1404,6 +1431,8 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
error_key:
key_put(key);
error_scb:
kfree(scb);
error:
_leave(" = %d", ret);
return ret;
@ -1416,12 +1445,10 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl)
{
struct afs_fs_cursor fc;
struct afs_file_status newstatus;
struct afs_callback newcb;
struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid;
struct key *key;
u64 data_version = dvnode->status.data_version;
int ret;
mode |= S_IFREG;
@ -1439,17 +1466,25 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
goto error;
}
ret = -ENOMEM;
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error_scb;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
&newfid, &newstatus, &newcb);
afs_fs_create(&fc, dentry->d_name.name, mode,
&scb[0], &newfid, &scb[1]);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, &newcb);
afs_check_for_remote_deletion(&fc, dvnode);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, &scb[0]);
afs_vnode_new_inode(&fc, dentry, &newfid, &scb[1]);
ret = afs_end_vnode_operation(&fc);
if (ret < 0)
goto error_key;
@ -1461,10 +1496,13 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
afs_edit_dir_for_create);
kfree(scb);
key_put(key);
_leave(" = 0");
return 0;
error_scb:
kfree(scb);
error_key:
key_put(key);
error:
@ -1480,15 +1518,12 @@ static int afs_link(struct dentry *from, struct inode *dir,
struct dentry *dentry)
{
struct afs_fs_cursor fc;
struct afs_vnode *dvnode, *vnode;
struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_vnode *vnode = AFS_FS_I(d_inode(from));
struct key *key;
u64 data_version;
int ret;
vnode = AFS_FS_I(d_inode(from));
dvnode = AFS_FS_I(dir);
data_version = dvnode->status.data_version;
_enter("{%llx:%llu},{%llx:%llu},{%pd}",
vnode->fid.vid, vnode->fid.vnode,
dvnode->fid.vid, dvnode->fid.vnode,
@ -1498,14 +1533,21 @@ static int afs_link(struct dentry *from, struct inode *dir,
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
ret = -ENOMEM;
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error;
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc);
goto error_key;
@ -1514,11 +1556,14 @@ static int afs_link(struct dentry *from, struct inode *dir,
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
afs_fs_link(&fc, vnode, dentry->d_name.name, data_version);
afs_fs_link(&fc, vnode, dentry->d_name.name,
&scb[0], &scb[1]);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break_2);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, &scb[0]);
afs_vnode_commit_status(&fc, vnode, fc.cb_break_2,
NULL, &scb[1]);
ihold(&vnode->vfs_inode);
d_instantiate(dentry, &vnode->vfs_inode);
@ -1535,11 +1580,14 @@ static int afs_link(struct dentry *from, struct inode *dir,
afs_edit_dir_for_link);
key_put(key);
kfree(scb);
_leave(" = 0");
return 0;
error_key:
key_put(key);
error_scb:
kfree(scb);
error:
d_drop(dentry);
_leave(" = %d", ret);
@ -1553,11 +1601,10 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
const char *content)
{
struct afs_fs_cursor fc;
struct afs_file_status newstatus;
struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid;
struct key *key;
u64 data_version = dvnode->status.data_version;
int ret;
_enter("{%llx:%llu},{%pd},%s",
@ -1572,24 +1619,31 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
if (strlen(content) >= AFSPATHMAX)
goto error;
ret = -ENOMEM;
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error;
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_symlink(&fc, dentry->d_name.name,
content, data_version,
&newfid, &newstatus);
afs_fs_symlink(&fc, dentry->d_name.name, content,
&scb[0], &newfid, &scb[1]);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, NULL);
afs_check_for_remote_deletion(&fc, dvnode);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, &scb[0]);
afs_vnode_new_inode(&fc, dentry, &newfid, &scb[1]);
ret = afs_end_vnode_operation(&fc);
if (ret < 0)
goto error_key;
@ -1602,11 +1656,14 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
afs_edit_dir_for_symlink);
key_put(key);
kfree(scb);
_leave(" = 0");
return 0;
error_key:
key_put(key);
error_scb:
kfree(scb);
error:
d_drop(dentry);
_leave(" = %d", ret);
@ -1621,11 +1678,11 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
unsigned int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
struct dentry *tmp = NULL, *rehash = NULL;
struct inode *new_inode;
struct key *key;
u64 orig_data_version, new_data_version;
bool new_negative = d_is_negative(new_dentry);
int ret;
@ -1639,8 +1696,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
vnode = AFS_FS_I(d_inode(old_dentry));
orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir);
orig_data_version = orig_dvnode->status.data_version;
new_data_version = new_dvnode->status.data_version;
_enter("{%llx:%llu},{%llx:%llu},{%llx:%llu},{%pd}",
orig_dvnode->fid.vid, orig_dvnode->fid.vnode,
@ -1648,10 +1703,15 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dvnode->fid.vid, new_dvnode->fid.vnode,
new_dentry);
ret = -ENOMEM;
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
key = afs_request_key(orig_dvnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error;
goto error_scb;
}
/* For non-directories, check whether the target is busy and if so,
@ -1685,31 +1745,43 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dentry = tmp;
rehash = NULL;
new_negative = true;
orig_data_version = orig_dvnode->status.data_version;
new_data_version = new_dvnode->status.data_version;
}
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, orig_dvnode, key, true)) {
afs_dataversion_t orig_data_version;
afs_dataversion_t new_data_version;
struct afs_status_cb *new_scb = &scb[1];
orig_data_version = orig_dvnode->status.data_version + 1;
if (orig_dvnode != new_dvnode) {
if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc);
goto error_rehash;
}
new_data_version = new_dvnode->status.data_version;
} else {
new_data_version = orig_data_version;
new_scb = &scb[0];
}
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode);
fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
afs_fs_rename(&fc, old_dentry->d_name.name,
new_dvnode, new_dentry->d_name.name,
orig_data_version, new_data_version);
&scb[0], new_scb);
}
afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, new_dvnode, fc.cb_break_2);
if (orig_dvnode != new_dvnode)
afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break,
&orig_data_version, &scb[0]);
if (new_dvnode != orig_dvnode) {
afs_vnode_commit_status(&fc, new_dvnode, fc.cb_break_2,
&new_data_version, &scb[1]);
mutex_unlock(&new_dvnode->io_lock);
}
ret = afs_end_vnode_operation(&fc);
if (ret < 0)
goto error_rehash;
@ -1749,6 +1821,8 @@ error_tmp:
if (tmp)
dput(tmp);
key_put(key);
error_scb:
kfree(scb);
error:
_leave(" = %d", ret);
return ret;

View File

@ -24,21 +24,28 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
struct key *key)
{
struct afs_fs_cursor fc;
u64 dir_data_version = dvnode->status.data_version;
struct afs_status_cb *scb;
int ret = -ERESTARTSYS;
_enter("%pd,%pd", old, new);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
trace_afs_silly_rename(vnode, false);
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_rename(&fc, old->d_name.name,
dvnode, new->d_name.name,
dir_data_version, dir_data_version);
scb, scb);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&dir_data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
@ -64,6 +71,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
fsnotify_nameremove(old, 0);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -143,31 +151,37 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
struct dentry *dentry, struct key *key)
{
struct afs_fs_cursor fc;
u64 dir_data_version = dvnode->status.data_version;
struct afs_status_cb *scb;
int ret = -ERESTARTSYS;
_enter("");
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
trace_afs_silly_rename(vnode, true);
if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
!test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
dir_data_version);
&scb[0], &scb[1]);
if (fc.ac.error != -ECONNABORTED ||
fc.ac.abort_code != RXGEN_OPCODE)
continue;
set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
}
afs_fs_remove(&fc, vnode, dentry->d_name.name, false,
dir_data_version);
afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&dir_data_version, &scb[0]);
ret = afs_end_vnode_operation(&fc);
if (ret == 0) {
drop_nlink(&vnode->vfs_inode);
@ -182,6 +196,7 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
afs_edit_dir_for_unlink);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}

View File

@ -228,6 +228,7 @@ static void afs_file_readpage_read_complete(struct page *page,
int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *desc)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
int ret;
_enter("%s{%llx:%llu.%u},%x,,,",
@ -237,15 +238,22 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_fetch_data(&fc, desc);
afs_fs_fetch_data(&fc, scb, desc);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
@ -255,6 +263,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
&afs_v2net(vnode)->n_fetch_bytes);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -405,10 +414,10 @@ static int afs_readpage(struct file *file, struct page *page)
/*
* Make pages available as they're filled.
*/
static void afs_readpages_page_done(struct afs_call *call, struct afs_read *req)
static void afs_readpages_page_done(struct afs_read *req)
{
#ifdef CONFIG_AFS_FSCACHE
struct afs_vnode *vnode = call->xvnode;
struct afs_vnode *vnode = req->vnode;
#endif
struct page *page = req->pages[req->index];
@ -462,6 +471,7 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping,
return -ENOMEM;
refcount_set(&req->usage, 1);
req->vnode = vnode;
req->page_done = afs_readpages_page_done;
req->pos = first->index;
req->pos <<= PAGE_SHIFT;

View File

@ -74,7 +74,7 @@ static void afs_schedule_lock_extension(struct afs_vnode *vnode)
*/
void afs_lock_op_done(struct afs_call *call)
{
struct afs_vnode *vnode = call->xvnode;
struct afs_vnode *vnode = call->lvnode;
if (call->error == 0) {
spin_lock(&vnode->lock);
@ -182,6 +182,7 @@ static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
@ -192,18 +193,23 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
vnode->fid.unique,
key_serial(key), type);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_set_lock(&fc, type);
afs_fs_set_lock(&fc, type, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -213,6 +219,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
*/
static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
@ -223,18 +230,23 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
while (afs_select_current_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_extend_lock(&fc);
afs_fs_extend_lock(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -244,6 +256,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
*/
static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
@ -254,18 +267,23 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
while (afs_select_current_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_release_lock(&fc);
afs_fs_release_lock(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -733,7 +751,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
posix_test_lock(file, fl);
if (fl->fl_type == F_UNLCK) {
/* no local locks; consult the server */
ret = afs_fetch_status(vnode, key, false);
ret = afs_fetch_status(vnode, key, false, NULL);
if (ret < 0)
goto error;

File diff suppressed because it is too large Load Diff

View File

@ -58,38 +58,50 @@ static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *paren
* Initialise an inode from the vnode status.
*/
static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
struct afs_vnode *parent_vnode)
struct afs_cb_interest *cbi,
struct afs_vnode *parent_vnode,
struct afs_status_cb *scb)
{
struct afs_cb_interest *old_cbi = NULL;
struct afs_file_status *status = &scb->status;
struct inode *inode = AFS_VNODE_TO_I(vnode);
struct timespec64 t;
_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
vnode->status.type,
vnode->status.nlink,
(unsigned long long) vnode->status.size,
vnode->status.data_version,
vnode->status.mode);
status->type,
status->nlink,
(unsigned long long) status->size,
status->data_version,
status->mode);
read_seqlock_excl(&vnode->cb_lock);
write_seqlock(&vnode->cb_lock);
afs_update_inode_from_status(vnode, &vnode->status, NULL,
AFS_VNODE_NOT_YET_SET);
vnode->status = *status;
switch (vnode->status.type) {
t = status->mtime_client;
inode->i_ctime = t;
inode->i_mtime = t;
inode->i_atime = t;
inode->i_uid = make_kuid(&init_user_ns, status->owner);
inode->i_gid = make_kgid(&init_user_ns, status->group);
set_nlink(&vnode->vfs_inode, status->nlink);
switch (status->type) {
case AFS_FTYPE_FILE:
inode->i_mode = S_IFREG | vnode->status.mode;
inode->i_mode = S_IFREG | status->mode;
inode->i_op = &afs_file_inode_operations;
inode->i_fop = &afs_file_operations;
inode->i_mapping->a_ops = &afs_fs_aops;
break;
case AFS_FTYPE_DIR:
inode->i_mode = S_IFDIR | vnode->status.mode;
inode->i_mode = S_IFDIR | status->mode;
inode->i_op = &afs_dir_inode_operations;
inode->i_fop = &afs_dir_file_operations;
inode->i_mapping->a_ops = &afs_dir_aops;
break;
case AFS_FTYPE_SYMLINK:
/* Symlinks with a mode of 0644 are actually mountpoints. */
if ((vnode->status.mode & 0777) == 0644) {
if ((status->mode & 0777) == 0644) {
inode->i_flags |= S_AUTOMOUNT;
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
@ -99,7 +111,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
inode->i_fop = &afs_mntpt_file_operations;
inode->i_mapping->a_ops = &afs_fs_aops;
} else {
inode->i_mode = S_IFLNK | vnode->status.mode;
inode->i_mode = S_IFLNK | status->mode;
inode->i_op = &afs_symlink_inode_operations;
inode->i_mapping->a_ops = &afs_fs_aops;
}
@ -107,7 +119,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
break;
default:
dump_vnode(vnode, parent_vnode);
read_sequnlock_excl(&vnode->cb_lock);
write_sequnlock(&vnode->cb_lock);
return afs_protocol_error(NULL, -EBADMSG, afs_eproto_file_type);
}
@ -116,17 +128,170 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
* for consistency with other AFS clients.
*/
inode->i_blocks = ((i_size_read(inode) + 1023) >> 10) << 1;
vnode->invalid_before = vnode->status.data_version;
i_size_write(&vnode->vfs_inode, status->size);
read_sequnlock_excl(&vnode->cb_lock);
vnode->invalid_before = status->data_version;
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
if (!scb->have_cb) {
/* it's a symlink we just created (the fileserver
* didn't give us a callback) */
vnode->cb_version = 0;
vnode->cb_type = 0;
vnode->cb_expires_at = ktime_get_real_seconds();
} else {
vnode->cb_version = scb->callback.version;
vnode->cb_type = scb->callback.type;
vnode->cb_expires_at = scb->callback.expires_at;
old_cbi = vnode->cb_interest;
if (cbi != old_cbi)
vnode->cb_interest = afs_get_cb_interest(cbi);
else
old_cbi = NULL;
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), old_cbi);
return 0;
}
/*
* Update the core inode struct from a returned status record.
*/
static void afs_apply_status(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
struct afs_status_cb *scb,
const afs_dataversion_t *expected_version)
{
struct afs_file_status *status = &scb->status;
struct timespec64 t;
umode_t mode;
bool data_changed = false;
BUG_ON(test_bit(AFS_VNODE_UNSET, &vnode->flags));
if (status->type != vnode->status.type) {
pr_warning("Vnode %llx:%llx:%x changed type %u to %u\n",
vnode->fid.vid,
vnode->fid.vnode,
vnode->fid.unique,
status->type, vnode->status.type);
afs_protocol_error(NULL, -EBADMSG, afs_eproto_bad_status);
return;
}
if (status->nlink != vnode->status.nlink)
set_nlink(&vnode->vfs_inode, status->nlink);
if (status->owner != vnode->status.owner)
vnode->vfs_inode.i_uid = make_kuid(&init_user_ns, status->owner);
if (status->group != vnode->status.group)
vnode->vfs_inode.i_gid = make_kgid(&init_user_ns, status->group);
if (status->mode != vnode->status.mode) {
mode = vnode->vfs_inode.i_mode;
mode &= ~S_IALLUGO;
mode |= status->mode;
WRITE_ONCE(vnode->vfs_inode.i_mode, mode);
}
t = status->mtime_client;
vnode->vfs_inode.i_ctime = t;
vnode->vfs_inode.i_mtime = t;
vnode->vfs_inode.i_atime = t;
if (vnode->status.data_version != status->data_version)
data_changed = true;
vnode->status = *status;
if (expected_version &&
*expected_version != status->data_version) {
kdebug("vnode modified %llx on {%llx:%llu} [exp %llx] %s",
(unsigned long long) status->data_version,
vnode->fid.vid, vnode->fid.vnode,
(unsigned long long) *expected_version,
fc->type ? fc->type->name : "???");
vnode->invalid_before = status->data_version;
if (vnode->status.type == AFS_FTYPE_DIR) {
if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
afs_stat_v(vnode, n_inval);
} else {
set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
}
} else if (vnode->status.type == AFS_FTYPE_DIR) {
/* Expected directory change is handled elsewhere so
* that we can locally edit the directory and save on a
* download.
*/
if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
data_changed = false;
}
if (data_changed) {
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
i_size_write(&vnode->vfs_inode, status->size);
}
}
/*
* Apply a callback to a vnode.
*/
static void afs_apply_callback(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
struct afs_status_cb *scb,
unsigned int cb_break)
{
struct afs_cb_interest *old;
struct afs_callback *cb = &scb->callback;
if (!afs_cb_is_broken(cb_break, vnode, fc->cbi)) {
vnode->cb_version = cb->version;
vnode->cb_type = cb->type;
vnode->cb_expires_at = cb->expires_at;
old = vnode->cb_interest;
if (old != fc->cbi) {
vnode->cb_interest = afs_get_cb_interest(fc->cbi);
afs_put_cb_interest(afs_v2net(vnode), old);
}
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
}
/*
* Apply the received status and callback to an inode all in the same critical
* section to avoid races with afs_validate().
*/
void afs_vnode_commit_status(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
unsigned int cb_break,
const afs_dataversion_t *expected_version,
struct afs_status_cb *scb)
{
if (fc->ac.error != 0)
return;
write_seqlock(&vnode->cb_lock);
afs_apply_status(fc, vnode, scb, expected_version);
if (scb->have_cb)
afs_apply_callback(fc, vnode, scb, cb_break);
write_sequnlock(&vnode->cb_lock);
if (fc->ac.error == 0)
afs_cache_permit(vnode, fc->key, cb_break, scb);
}
/*
* Fetch file status from the volume.
*/
int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool is_new,
afs_access_t *_caller_access)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
@ -135,18 +300,38 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
vnode->flags);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_fetch_file_status(&fc, NULL, new_inode);
afs_fs_fetch_file_status(&fc, scb, NULL);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
if (fc.error) {
/* Do nothing. */
} else if (is_new) {
ret = afs_inode_init_from_status(vnode, key, fc.cbi,
NULL, scb);
fc.error = ret;
if (ret == 0)
afs_cache_permit(vnode, key, fc.cb_break, scb);
} else {
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
}
afs_check_for_remote_deletion(&fc, vnode);
ret = afs_end_vnode_operation(&fc);
}
if (ret == 0 && _caller_access)
*_caller_access = scb->status.caller_access;
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -299,8 +484,8 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
* inode retrieval
*/
struct inode *afs_iget(struct super_block *sb, struct key *key,
struct afs_fid *fid, struct afs_file_status *status,
struct afs_callback *cb, struct afs_cb_interest *cbi,
struct afs_fid *fid, struct afs_status_cb *scb,
struct afs_cb_interest *cbi,
struct afs_vnode *parent_vnode)
{
struct afs_iget_data data = { .fid = *fid };
@ -332,36 +517,18 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
return inode;
}
if (!status) {
if (!scb) {
/* it's a remotely extant inode */
ret = afs_fetch_status(vnode, key, true);
ret = afs_fetch_status(vnode, key, true, NULL);
if (ret < 0)
goto bad_inode;
} else {
/* it's an inode we just created */
memcpy(&vnode->status, status, sizeof(vnode->status));
if (!cb) {
/* it's a symlink we just created (the fileserver
* didn't give us a callback) */
vnode->cb_version = 0;
vnode->cb_type = 0;
vnode->cb_expires_at = ktime_get();
} else {
vnode->cb_version = cb->version;
vnode->cb_type = cb->type;
vnode->cb_expires_at = cb->expires_at;
vnode->cb_interest = afs_get_cb_interest(cbi);
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
vnode->cb_expires_at += ktime_get_real_seconds();
ret = afs_inode_init_from_status(vnode, key, cbi, parent_vnode,
scb);
if (ret < 0)
goto bad_inode;
}
ret = afs_inode_init_from_status(vnode, key, parent_vnode);
if (ret < 0)
goto bad_inode;
afs_get_inode_cache(vnode);
/* success */
@ -460,7 +627,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
* access */
if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
_debug("not promised");
ret = afs_fetch_status(vnode, key, false);
ret = afs_fetch_status(vnode, key, false, NULL);
if (ret < 0) {
if (ret == -ENOENT) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
@ -585,9 +752,10 @@ void afs_evict_inode(struct inode *inode)
int afs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
struct key *key;
int ret;
int ret = -ENOMEM;
_enter("{%llx:%llu},{n=%pd},%x",
vnode->fid.vid, vnode->fid.vnode, dentry,
@ -599,6 +767,10 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
return 0;
}
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
/* flush any dirty data outstanding on a regular file */
if (S_ISREG(vnode->vfs_inode.i_mode))
filemap_write_and_wait(vnode->vfs_inode.i_mapping);
@ -609,25 +781,33 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error;
goto error_scb;
}
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
afs_dataversion_t data_version = vnode->status.data_version;
if (attr->ia_valid & ATTR_SIZE)
data_version++;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_setattr(&fc, attr);
afs_fs_setattr(&fc, attr, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
if (!(attr->ia_valid & ATTR_FILE))
key_put(key);
error_scb:
kfree(scb);
error:
_leave(" = %d", ret);
return ret;

View File

@ -116,8 +116,7 @@ struct afs_call {
struct afs_vlserver *vlserver;
};
struct afs_cb_interest *cbi; /* Callback interest for server used */
struct afs_vnode *dvnode; /* Directory vnode of call */
struct afs_vnode *xvnode; /* Other vnode of call */
struct afs_vnode *lvnode; /* vnode being locked */
void *request; /* request data (first part) */
struct address_space *mapping; /* Pages being written from */
struct iov_iter iter; /* Buffer iterator */
@ -134,9 +133,7 @@ struct afs_call {
struct afs_acl *ret_acl;
};
struct afs_fid *out_fid;
struct afs_file_status *out_vnode_status;
struct afs_file_status *out_extra_status;
struct afs_callback *out_cb;
struct afs_status_cb *out_dir_scb;
struct afs_status_cb *out_scb;
struct yfs_acl *out_yacl;
struct afs_volsync *out_volsync;
@ -155,7 +152,6 @@ struct afs_call {
unsigned request_size; /* size of request data */
unsigned reply_max; /* maximum size of reply */
unsigned first_offset; /* offset into mapping[first] */
unsigned int cb_break; /* cb_break + cb_s_break before the call */
union {
unsigned last_to; /* amount of mapping[last] */
unsigned count2; /* count used in unmarshalling */
@ -180,8 +176,6 @@ struct afs_call {
} __attribute__((packed));
__be64 tmp64;
};
afs_dataversion_t expected_version; /* Updated version expected from store */
afs_dataversion_t expected_version_2; /* 2nd updated version expected from store */
ktime_t reply_time; /* Time of first reply packet */
};
@ -242,7 +236,8 @@ struct afs_read {
unsigned int index; /* Which page we're reading into */
unsigned int nr_pages;
unsigned int offset; /* offset into current page */
void (*page_done)(struct afs_call *, struct afs_read *);
struct afs_vnode *vnode;
void (*page_done)(struct afs_read *);
struct page **pages;
struct page *array[];
};
@ -776,6 +771,7 @@ struct afs_vl_cursor {
* Cursor for iterating over a set of fileservers.
*/
struct afs_fs_cursor {
const struct afs_call_type *type; /* Type of call done */
struct afs_addr_cursor ac;
struct afs_vnode *vnode;
struct afs_server_list *server_list; /* Current server list (pins ref) */
@ -962,30 +958,28 @@ extern int afs_flock(struct file *, int, struct file_lock *);
/*
* fsclient.c
*/
#define AFS_VNODE_NOT_YET_SET 0x01
#define AFS_VNODE_META_CHANGED 0x02
#define AFS_VNODE_DATA_CHANGED 0x04
extern void afs_update_inode_from_status(struct afs_vnode *, struct afs_file_status *,
const afs_dataversion_t *, u8);
extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *, bool);
extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *,
struct afs_volsync *);
extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *);
extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_read *);
extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, u64,
struct afs_fid *, struct afs_file_status *, struct afs_callback *);
extern int afs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool, u64);
extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, u64);
extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64,
struct afs_fid *, struct afs_file_status *);
extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *);
extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t,
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
extern int afs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool,
struct afs_status_cb *);
extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
extern int afs_fs_rename(struct afs_fs_cursor *, const char *,
struct afs_vnode *, const char *, u64, u64);
struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
pgoff_t, pgoff_t, unsigned, unsigned);
extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *);
pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *);
extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *, struct afs_status_cb *);
extern int afs_fs_get_volume_status(struct afs_fs_cursor *, struct afs_volume_status *);
extern int afs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t);
extern int afs_fs_extend_lock(struct afs_fs_cursor *);
extern int afs_fs_release_lock(struct afs_fs_cursor *);
extern int afs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t, struct afs_status_cb *);
extern int afs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int afs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *);
extern struct afs_call *afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
@ -995,16 +989,17 @@ extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_status_cb *,
unsigned int, struct afs_volsync *);
extern int afs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_file_status *,
struct afs_callback *, struct afs_volsync *);
struct afs_fid *, struct afs_status_cb *,
struct afs_volsync *);
struct afs_acl {
u32 size;
u8 data[];
};
extern struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *);
extern int afs_fs_store_acl(struct afs_fs_cursor *, const struct afs_acl *);
extern struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *, struct afs_status_cb *);
extern int afs_fs_store_acl(struct afs_fs_cursor *, const struct afs_acl *,
struct afs_status_cb *);
/*
* fs_probe.c
@ -1016,12 +1011,16 @@ extern int afs_wait_for_fs_probes(struct afs_server_list *, unsigned long);
/*
* inode.c
*/
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool);
extern void afs_vnode_commit_status(struct afs_fs_cursor *,
struct afs_vnode *,
unsigned int,
const afs_dataversion_t *,
struct afs_status_cb *);
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
extern int afs_iget5_test(struct inode *, void *);
extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
extern struct inode *afs_iget(struct super_block *, struct key *,
struct afs_fid *, struct afs_file_status *,
struct afs_callback *,
struct afs_fid *, struct afs_status_cb *,
struct afs_cb_interest *,
struct afs_vnode *);
extern void afs_zap_data(struct afs_vnode *);
@ -1145,6 +1144,7 @@ extern int afs_protocol_error(struct afs_call *, int, enum afs_eproto_cause);
static inline void afs_set_fc_call(struct afs_call *call, struct afs_fs_cursor *fc)
{
call->intr = fc->flags & AFS_FS_CURSOR_INTR;
fc->type = call->type;
}
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
@ -1227,7 +1227,8 @@ static inline void afs_set_call_complete(struct afs_call *call,
*/
extern void afs_put_permits(struct afs_permits *);
extern void afs_clear_permits(struct afs_vnode *);
extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int);
extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int,
struct afs_status_cb *);
extern void afs_zap_permits(struct rcu_head *);
extern struct key *afs_request_key(struct afs_cell *);
extern int afs_check_permit(struct afs_vnode *, struct key *, afs_access_t *);
@ -1353,7 +1354,6 @@ extern int afs_write_end(struct file *file, struct address_space *mapping,
struct page *page, void *fsdata);
extern int afs_writepage(struct page *, struct writeback_control *);
extern int afs_writepages(struct address_space *, struct writeback_control *);
extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
extern int afs_fsync(struct file *, loff_t, loff_t, int);
extern vm_fault_t afs_page_mkwrite(struct vm_fault *vmf);
@ -1369,29 +1369,33 @@ extern ssize_t afs_listxattr(struct dentry *, char *, size_t);
/*
* yfsclient.c
*/
extern int yfs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *, bool);
extern int yfs_fs_fetch_data(struct afs_fs_cursor *, struct afs_read *);
extern int yfs_fs_create_file(struct afs_fs_cursor *, const char *, umode_t, u64,
struct afs_fid *, struct afs_file_status *, struct afs_callback *);
extern int yfs_fs_make_dir(struct afs_fs_cursor *, const char *, umode_t, u64,
struct afs_fid *, struct afs_file_status *, struct afs_callback *);
extern int yfs_fs_remove_file2(struct afs_fs_cursor *, struct afs_vnode *, const char *, u64);
extern int yfs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool, u64);
extern int yfs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, u64);
extern int yfs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64,
struct afs_fid *, struct afs_file_status *);
extern int yfs_fs_rename(struct afs_fs_cursor *, const char *,
struct afs_vnode *, const char *, u64, u64);
extern int yfs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *,
struct afs_volsync *);
extern int yfs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *);
extern int yfs_fs_create_file(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *,
struct afs_fid *, struct afs_status_cb *);
extern int yfs_fs_make_dir(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *,
struct afs_fid *, struct afs_status_cb *);
extern int yfs_fs_remove_file2(struct afs_fs_cursor *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int yfs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool,
struct afs_status_cb *);
extern int yfs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int yfs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
extern int yfs_fs_rename(struct afs_fs_cursor *, const char *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int yfs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
pgoff_t, pgoff_t, unsigned, unsigned);
extern int yfs_fs_setattr(struct afs_fs_cursor *, struct iattr *);
pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *);
extern int yfs_fs_setattr(struct afs_fs_cursor *, struct iattr *, struct afs_status_cb *);
extern int yfs_fs_get_volume_status(struct afs_fs_cursor *, struct afs_volume_status *);
extern int yfs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t);
extern int yfs_fs_extend_lock(struct afs_fs_cursor *);
extern int yfs_fs_release_lock(struct afs_fs_cursor *);
extern int yfs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t, struct afs_status_cb *);
extern int yfs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int yfs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int yfs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_file_status *,
struct afs_callback *, struct afs_volsync *);
struct afs_fid *, struct afs_status_cb *,
struct afs_volsync *);
extern int yfs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_status_cb *,
unsigned int, struct afs_volsync *);
@ -1407,8 +1411,10 @@ struct yfs_acl {
};
extern void yfs_free_opaque_acl(struct yfs_acl *);
extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *, struct yfs_acl *);
extern int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *, const struct afs_acl *);
extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *, struct yfs_acl *,
struct afs_status_cb *);
extern int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *, const struct afs_acl *,
struct afs_status_cb *);
/*
* Miscellaneous inline functions.
@ -1423,14 +1429,6 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
return &vnode->vfs_inode;
}
static inline void afs_vnode_commit_status(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
unsigned int cb_break)
{
if (fc->ac.error == 0)
afs_cache_permit(vnode, fc->key, cb_break);
}
static inline void afs_check_for_remote_deletion(struct afs_fs_cursor *fc,
struct afs_vnode *vnode)
{

View File

@ -116,10 +116,10 @@ static void afs_hash_permits(struct afs_permits *permits)
* as the ACL *may* have changed.
*/
void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
unsigned int cb_break)
unsigned int cb_break, struct afs_status_cb *scb)
{
struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL;
afs_access_t caller_access = READ_ONCE(vnode->status.caller_access);
afs_access_t caller_access = scb->status.caller_access;
size_t size = 0;
bool changed = false;
int i, j;
@ -320,13 +320,12 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
*/
_debug("no valid permit");
ret = afs_fetch_status(vnode, key, false);
ret = afs_fetch_status(vnode, key, false, _access);
if (ret < 0) {
*_access = 0;
_leave(" = %d", ret);
return ret;
}
*_access = vnode->status.caller_access;
}
_leave(" = 0 [access %x]", *_access);

View File

@ -455,7 +455,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
fid.vnode = 1;
fid.vnode_hi = 0;
fid.unique = 1;
inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL, NULL);
inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL);
}
if (IS_ERR(inode))
@ -749,7 +749,6 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}

View File

@ -313,6 +313,46 @@ static void afs_redirty_pages(struct writeback_control *wbc,
_leave("");
}
/*
* completion of write to server
*/
static void afs_pages_written_back(struct afs_vnode *vnode,
pgoff_t first, pgoff_t last)
{
struct pagevec pv;
unsigned long priv;
unsigned count, loop;
_enter("{%llx:%llu},{%lx-%lx}",
vnode->fid.vid, vnode->fid.vnode, first, last);
pagevec_init(&pv);
do {
_debug("done %lx-%lx", first, last);
count = last - first + 1;
if (count > PAGEVEC_SIZE)
count = PAGEVEC_SIZE;
pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
first, count, pv.pages);
ASSERTCMP(pv.nr, ==, count);
for (loop = 0; loop < count; loop++) {
priv = page_private(pv.pages[loop]);
trace_afs_page_dirty(vnode, tracepoint_string("clear"),
pv.pages[loop]->index, priv);
set_page_private(pv.pages[loop], 0);
end_page_writeback(pv.pages[loop]);
}
first += count;
__pagevec_release(&pv);
} while (first <= last);
afs_prune_wb_keys(vnode);
_leave("");
}
/*
* write to a file
*/
@ -322,6 +362,7 @@ static int afs_store_data(struct address_space *mapping,
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_wb_key *wbk = NULL;
struct list_head *p;
int ret = -ENOKEY, ret2;
@ -333,6 +374,10 @@ static int afs_store_data(struct address_space *mapping,
vnode->fid.unique,
first, last, offset, to);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
return -ENOMEM;
spin_lock(&vnode->wb_lock);
p = vnode->wb_keys.next;
@ -351,6 +396,7 @@ try_next_key:
spin_unlock(&vnode->wb_lock);
afs_put_wb_key(wbk);
kfree(scb);
_leave(" = %d [no keys]", ret);
return ret;
@ -362,13 +408,18 @@ found_key:
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, wbk->key, false)) {
afs_dataversion_t data_version = vnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_data(&fc, mapping, first, last, offset, to);
afs_fs_store_data(&fc, mapping, first, last, offset, to, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
if (fc.ac.error == 0)
afs_pages_written_back(vnode, first, last);
ret = afs_end_vnode_operation(&fc);
}
@ -393,6 +444,7 @@ found_key:
}
afs_put_wb_key(wbk);
kfree(scb);
_leave(" = %d", ret);
return ret;
}
@ -678,46 +730,6 @@ int afs_writepages(struct address_space *mapping,
return ret;
}
/*
* completion of write to server
*/
void afs_pages_written_back(struct afs_vnode *vnode, struct afs_call *call)
{
struct pagevec pv;
unsigned long priv;
unsigned count, loop;
pgoff_t first = call->first, last = call->last;
_enter("{%llx:%llu},{%lx-%lx}",
vnode->fid.vid, vnode->fid.vnode, first, last);
pagevec_init(&pv);
do {
_debug("done %lx-%lx", first, last);
count = last - first + 1;
if (count > PAGEVEC_SIZE)
count = PAGEVEC_SIZE;
pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
first, count, pv.pages);
ASSERTCMP(pv.nr, ==, count);
for (loop = 0; loop < count; loop++) {
priv = page_private(pv.pages[loop]);
trace_afs_page_dirty(vnode, tracepoint_string("clear"),
pv.pages[loop]->index, priv);
set_page_private(pv.pages[loop], 0);
end_page_writeback(pv.pages[loop]);
}
first += count;
__pagevec_release(&pv);
} while (first <= last);
afs_prune_wb_keys(vnode);
_leave("");
}
/*
* write to an AFS file
*/

View File

@ -47,24 +47,34 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
int ret = -ENOMEM;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
acl = afs_fs_fetch_acl(&fc);
acl = afs_fs_fetch_acl(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
@ -80,6 +90,9 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
}
key_put(key);
error_scb:
kfree(scb);
error:
return ret;
}
@ -92,22 +105,27 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
int ret = -ENOMEM;
if (flags == XATTR_CREATE)
return -EINVAL;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
key_put(key);
return -ENOMEM;
if (!acl)
goto error_scb;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_acl;
}
acl->size = size;
@ -115,18 +133,25 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler,
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_acl(&fc, acl);
afs_fs_store_acl(&fc, acl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(acl);
key_put(key);
error_acl:
kfree(acl);
error_scb:
kfree(scb);
error:
return ret;
}
@ -145,6 +170,7 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct yfs_acl *yacl = NULL;
struct key *key;
@ -171,21 +197,28 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
else if (which == 3)
yacl->flags |= YFS_ACL_WANT_VOL_ACL;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error_yacl;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_yacl;
goto error_scb;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_fetch_opaque_acl(&fc, yacl);
yfs_fs_fetch_opaque_acl(&fc, yacl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
@ -225,6 +258,8 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
error_key:
key_put(key);
error_scb:
kfree(scb);
error_yacl:
yfs_free_opaque_acl(yacl);
error:
@ -240,42 +275,54 @@ static int afs_xattr_set_yfs(const struct xattr_handler *handler,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
int ret = -ENOMEM;
if (flags == XATTR_CREATE ||
strcmp(name, "acl") != 0)
return -EINVAL;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
key_put(key);
return -ENOMEM;
}
if (!acl)
goto error_scb;
acl->size = size;
memcpy(acl->data, buffer, size);
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_acl;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_store_opaque_acl2(&fc, acl);
yfs_fs_store_opaque_acl2(&fc, acl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
error_acl:
kfree(acl);
key_put(key);
error_scb:
kfree(scb);
error:
return ret;
}

File diff suppressed because it is too large Load Diff