AFS fixes
-----BEGIN PGP SIGNATURE----- iQIVAwUAXN3U4fu3V2unywtrAQKXoA/+LPCttnaq9GpTiVlfPXsBHnbphs0gjZtK gN4utmmsLx7sKLCwxRYdOhTSMlShby+hd9FZ71rsEppiF6hATXyIwK7UPD8+D83l 4eNT7RECPaQtBGDsw4tYd1AA8OVRM6v/+r2AhWpwYrZXIEOkYKJ0st9HLz63M64X HOPOVabEVBlTmsbKRULBgZdFPXhQiZWsJHPINkIegPi21KETLb2KVBEciBNKI1iX Jb5eAb8tO1a1y2vJNG/YJn1HzVo0gMzzo/dTgmaIkyu+5ULGkqFk/OLZHJ6rwLwd peqIzbdtmBNpd43u942zbo2Tx3jegIa9y5dg/WT2NnIUJ5FAfysxXiMi1AnhSbjc NRRwUVK1XBZCGZeGBIKtfY1CfgRGAq2rmr1MyXgt++Vciz9BekXgzB7GAqmMHvA0 6Ud5j6oCqrQhxt/mIPvfJcqnuguuTgadwgHoani/366t0gCRT/lxswbrh4Nv86l9 CDSRFEBjkSJAwV07xqX37ppoMtP+iECHl6elkLf5HYh9vFI223fK9Jpo6eoUsRj8 YLYOLtV0LBeZ2Wj4+rFxbDVvUCWz4Gh3hr/YyLwtyVmyuoE0LstHIn51WZ646NOa l0Jyjf1Q/CqF98uP65H2USTxInNnZRV2Du8qmtv4MELITPm1MfGUk/nd9oMipGaq smKgUe0M7cA= =NstZ -----END PGP SIGNATURE----- Merge tag 'afs-fixes-b-20190516' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs Pull AFS callback promise fixes from David Howells: "This series fixes a bunch of problems in callback promise handling, where a callback promise indicates a promise on the part of the server to notify the client in the event of some sort of change to a file or volume. In the event of a break, the client has to go and refetch the client status from the server and discard any cached permission information as the ACL might have changed. The problem in the current code is that changes made by other clients aren't always noticed, primarily because the file status information and the callback information aren't updated in the same critical section, even if these are carried in the same reply from an RPC operation, and so the AFS_VNODE_CB_PROMISED flag is unreliable. Arranging for them to be done in the same critical section during reply decoding is tricky because of the FS.InlineBulkStatus op - which has all the statuses in the reply arriving and then all the callbacks, so they have to be buffered. It simplifies things a lot to move the critical section out of the decode phase and do it after the RPC function returns. Also new inodes (either newly fetched or newly created) aren't properly managed against a callback break happening before we get the local inode up and running. Fix this by: - There's now a combined file status and callback record (struct afs_status_cb) to carry both plus some flags. - Each operation wrapper function allocates sufficient afs_status_cb records for all the vnodes it is interested in and passes them into RPC operations to be filled in from the reply. - The FileStatus and CallBack record decoders no longer apply the new/revised status and callback information to the inode/vnode at the point of decoding and instead store the information into the record from (2). - afs_vnode_commit_status() then revises the file status, detects deletion and notes callback information inside of a single critical section. It also checks the callback break counters and cancels the callback promise if they changed during the operation. [*] Note that "callback break counters" are counters of server events that cancel one or more callback promises that the client thinks it has. The client counts the events and compares the counters before and after an operation to see if the callback promise it thinks it just got evaporated before it got recorded under lock. - Volume and server callback break counters are passed into afs_iget() allowing callback breaks concurrent with inode set up to be detected and the callback promise thence to be cancelled. - AFS validation checks are now done under RCU conditions using a read lock on cb_lock. This requires vnode->cb_interest to be made RCU safe. - If the checks in (6) fail, the callback breaker is then called under write lock on the cb_lock - but only if the callback break counter didn't change from the value read before the checks were made. - Results from FS.InlineBulkStatus that correspond to inodes we currently have in memory are now used to update those inodes' status and callback information rather than being discarded. This requires those inodes to be looked up before the RPC op is made and all their callback break values saved. To aid in this, the following changes have also been made: - Don't pass the vnode into the reply delivery functions or the decoders. The vnode shouldn't be altered anywhere in those paths. The only exception, for the moment, is for the call done hook for file lock ops that wants access to both the vnode and the call - this can be fixed at a later time. - Get rid of the call->reply[] void* array and replace it with named and typed members. This avoids confusion since different ops were mapping different reply[] members to different things. - Fix an order-1 kmalloc allocation in afs_do_lookup() and replace it with kvcalloc(). - Always get the reply time. Since callback, lock and fileserver record expiry times are calculated for several RPCs, make this mandatory. - Call afs_pages_written_back() from the operation wrapper rather than from the delivery function. - Don't store the version and type from a callback promise in a reply as the information in them is of very limited use" * tag 'afs-fixes-b-20190516' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: afs: Fix application of the results of a inline bulk status fetch afs: Pass pre-fetch server and volume break counts into afs_iget5_set() afs: Fix unlink to handle YFS.RemoveFile2 better afs: Clear AFS_VNODE_CB_PROMISED if we detect callback expiry afs: Make vnode->cb_interest RCU safe afs: Split afs_validate() so first part can be used under LOOKUP_RCU afs: Don't save callback version and type fields afs: Fix application of status and callback to be under same lock afs: Always get the reply time afs: Fix order-1 allocation in afs_do_lookup() afs: Get rid of afs_call::reply[] afs: Don't pass the vnode pointer through into the inline bulk status op
This commit is contained in:
commit
0d74471924
13
fs/afs/afs.h
13
fs/afs/afs.h
|
@ -72,8 +72,8 @@ typedef enum {
|
|||
|
||||
struct afs_callback {
|
||||
time64_t expires_at; /* Time at which expires */
|
||||
unsigned version; /* Callback version */
|
||||
afs_callback_type_t type; /* Type of callback */
|
||||
//unsigned version; /* Callback version */
|
||||
//afs_callback_type_t type; /* Type of callback */
|
||||
};
|
||||
|
||||
struct afs_callback_break {
|
||||
|
@ -147,6 +147,15 @@ struct afs_file_status {
|
|||
u32 abort_code; /* Abort if bulk-fetching this failed */
|
||||
};
|
||||
|
||||
struct afs_status_cb {
|
||||
struct afs_file_status status;
|
||||
struct afs_callback callback;
|
||||
unsigned int cb_break; /* Pre-op callback break counter */
|
||||
bool have_status; /* True if status record was retrieved */
|
||||
bool have_cb; /* True if cb record was retrieved */
|
||||
bool have_error; /* True if status.abort_code indicates an error */
|
||||
};
|
||||
|
||||
/*
|
||||
* AFS file status change request
|
||||
*/
|
||||
|
|
|
@ -94,15 +94,15 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
|
|||
struct afs_server *server = entry->server;
|
||||
|
||||
again:
|
||||
if (vnode->cb_interest &&
|
||||
likely(vnode->cb_interest == entry->cb_interest))
|
||||
vcbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->io_lock));
|
||||
if (vcbi && likely(vcbi == entry->cb_interest))
|
||||
return 0;
|
||||
|
||||
read_lock(&slist->lock);
|
||||
cbi = afs_get_cb_interest(entry->cb_interest);
|
||||
read_unlock(&slist->lock);
|
||||
|
||||
vcbi = vnode->cb_interest;
|
||||
if (vcbi) {
|
||||
if (vcbi == cbi) {
|
||||
afs_put_cb_interest(afs_v2net(vnode), cbi);
|
||||
|
@ -114,8 +114,9 @@ again:
|
|||
*/
|
||||
if (cbi && vcbi->server == cbi->server) {
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
old = vnode->cb_interest;
|
||||
vnode->cb_interest = cbi;
|
||||
old = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
rcu_assign_pointer(vnode->cb_interest, cbi);
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
afs_put_cb_interest(afs_v2net(vnode), old);
|
||||
return 0;
|
||||
|
@ -160,8 +161,9 @@ again:
|
|||
*/
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
old = vnode->cb_interest;
|
||||
vnode->cb_interest = cbi;
|
||||
old = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
rcu_assign_pointer(vnode->cb_interest, cbi);
|
||||
vnode->cb_s_break = cbi->server->cb_s_break;
|
||||
vnode->cb_v_break = vnode->volume->cb_v_break;
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
|
@ -191,10 +193,11 @@ void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
|
|||
vi = NULL;
|
||||
|
||||
write_unlock(&cbi->server->cb_break_lock);
|
||||
kfree(vi);
|
||||
if (vi)
|
||||
kfree_rcu(vi, rcu);
|
||||
afs_put_server(net, cbi->server);
|
||||
}
|
||||
kfree(cbi);
|
||||
kfree_rcu(cbi, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ static int afs_find_cm_server_by_peer(struct afs_call *call)
|
|||
return 0;
|
||||
}
|
||||
|
||||
call->cm_server = server;
|
||||
call->server = server;
|
||||
return afs_record_cm_probe(call, server);
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ static int afs_find_cm_server_by_uuid(struct afs_call *call,
|
|||
return 0;
|
||||
}
|
||||
|
||||
call->cm_server = server;
|
||||
call->server = server;
|
||||
return afs_record_cm_probe(call, server);
|
||||
}
|
||||
|
||||
|
@ -260,8 +260,8 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
|
|||
* server holds up change visibility till it receives our reply so as
|
||||
* to maintain cache coherency.
|
||||
*/
|
||||
if (call->cm_server)
|
||||
afs_break_callbacks(call->cm_server, call->count, call->request);
|
||||
if (call->server)
|
||||
afs_break_callbacks(call->server, call->count, call->request);
|
||||
|
||||
afs_send_empty_reply(call);
|
||||
afs_put_call(call);
|
||||
|
@ -376,10 +376,10 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
|
|||
{
|
||||
struct afs_call *call = container_of(work, struct afs_call, work);
|
||||
|
||||
_enter("{%p}", call->cm_server);
|
||||
_enter("{%p}", call->server);
|
||||
|
||||
if (call->cm_server)
|
||||
afs_init_callback_state(call->cm_server);
|
||||
if (call->server)
|
||||
afs_init_callback_state(call->server);
|
||||
afs_send_empty_reply(call);
|
||||
afs_put_call(call);
|
||||
_leave("");
|
||||
|
|
357
fs/afs/dir.c
357
fs/afs/dir.c
|
@ -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,
|
||||
|
@ -102,8 +103,8 @@ struct afs_lookup_cookie {
|
|||
bool found;
|
||||
bool one_only;
|
||||
unsigned short nr_fids;
|
||||
struct afs_file_status *statuses;
|
||||
struct afs_callback *callbacks;
|
||||
struct inode **inodes;
|
||||
struct afs_status_cb *statuses;
|
||||
struct afs_fid fids[50];
|
||||
};
|
||||
|
||||
|
@ -638,12 +639,14 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
|
|||
struct key *key)
|
||||
{
|
||||
struct afs_lookup_cookie *cookie;
|
||||
struct afs_cb_interest *cbi = NULL;
|
||||
struct afs_cb_interest *dcbi, *cbi = NULL;
|
||||
struct afs_super_info *as = dir->i_sb->s_fs_info;
|
||||
struct afs_iget_data data;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_iget_data iget_data;
|
||||
struct afs_fs_cursor fc;
|
||||
struct afs_vnode *dvnode = AFS_FS_I(dir);
|
||||
struct inode *inode = NULL;
|
||||
struct afs_server *server;
|
||||
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
|
||||
struct inode *inode = NULL, *ti;
|
||||
int ret, i;
|
||||
|
||||
_enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);
|
||||
|
@ -657,10 +660,14 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
|
|||
cookie->nr_fids = 1; /* slot 0 is saved for the fid we actually want */
|
||||
|
||||
read_seqlock_excl(&dvnode->cb_lock);
|
||||
if (dvnode->cb_interest &&
|
||||
dvnode->cb_interest->server &&
|
||||
test_bit(AFS_SERVER_FL_NO_IBULK, &dvnode->cb_interest->server->flags))
|
||||
cookie->one_only = true;
|
||||
dcbi = rcu_dereference_protected(dvnode->cb_interest,
|
||||
lockdep_is_held(&dvnode->cb_lock.lock));
|
||||
if (dcbi) {
|
||||
server = dcbi->server;
|
||||
if (server &&
|
||||
test_bit(AFS_SERVER_FL_NO_IBULK, &server->flags))
|
||||
cookie->one_only = true;
|
||||
}
|
||||
read_sequnlock_excl(&dvnode->cb_lock);
|
||||
|
||||
for (i = 0; i < 50; i++)
|
||||
|
@ -678,24 +685,43 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
|
|||
goto out;
|
||||
|
||||
/* Check to see if we already have an inode for the primary fid. */
|
||||
data.volume = dvnode->volume;
|
||||
data.fid = cookie->fids[0];
|
||||
inode = ilookup5(dir->i_sb, cookie->fids[0].vnode, afs_iget5_test, &data);
|
||||
iget_data.fid = cookie->fids[0];
|
||||
iget_data.volume = dvnode->volume;
|
||||
iget_data.cb_v_break = dvnode->volume->cb_v_break;
|
||||
iget_data.cb_s_break = 0;
|
||||
inode = ilookup5(dir->i_sb, cookie->fids[0].vnode,
|
||||
afs_iget5_test, &iget_data);
|
||||
if (inode)
|
||||
goto out;
|
||||
|
||||
/* Need space for examining all the selected files */
|
||||
inode = ERR_PTR(-ENOMEM);
|
||||
cookie->statuses = kcalloc(cookie->nr_fids, sizeof(struct afs_file_status),
|
||||
GFP_KERNEL);
|
||||
cookie->statuses = kvcalloc(cookie->nr_fids, sizeof(struct afs_status_cb),
|
||||
GFP_KERNEL);
|
||||
if (!cookie->statuses)
|
||||
goto out;
|
||||
|
||||
cookie->callbacks = kcalloc(cookie->nr_fids, sizeof(struct afs_callback),
|
||||
GFP_KERNEL);
|
||||
if (!cookie->callbacks)
|
||||
cookie->inodes = kcalloc(cookie->nr_fids, sizeof(struct inode *),
|
||||
GFP_KERNEL);
|
||||
if (!cookie->inodes)
|
||||
goto out_s;
|
||||
|
||||
for (i = 1; i < cookie->nr_fids; i++) {
|
||||
scb = &cookie->statuses[i];
|
||||
|
||||
/* Find any inodes that already exist and get their
|
||||
* callback counters.
|
||||
*/
|
||||
iget_data.fid = cookie->fids[i];
|
||||
ti = ilookup5_nowait(dir->i_sb, iget_data.fid.vnode,
|
||||
afs_iget5_test, &iget_data);
|
||||
if (!IS_ERR_OR_NULL(ti)) {
|
||||
vnode = AFS_FS_I(ti);
|
||||
scb->cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
cookie->inodes[i] = ti;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try FS.InlineBulkStatus first. Abort codes for the individual
|
||||
* lookups contained therein are stored in the reply without aborting
|
||||
* the whole operation.
|
||||
|
@ -712,11 +738,12 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
|
|||
fc.ac.error = -ECONNABORTED;
|
||||
break;
|
||||
}
|
||||
iget_data.cb_v_break = dvnode->volume->cb_v_break;
|
||||
iget_data.cb_s_break = fc.cbi->server->cb_s_break;
|
||||
afs_fs_inline_bulk_status(&fc,
|
||||
afs_v2net(dvnode),
|
||||
cookie->fids,
|
||||
cookie->statuses,
|
||||
cookie->callbacks,
|
||||
cookie->nr_fids, NULL);
|
||||
}
|
||||
|
||||
|
@ -737,15 +764,16 @@ no_inline_bulk_status:
|
|||
* any of the lookups fails - so, for the moment, revert to
|
||||
* FS.FetchStatus for just the primary fid.
|
||||
*/
|
||||
cookie->nr_fids = 1;
|
||||
inode = ERR_PTR(-ERESTARTSYS);
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
iget_data.cb_v_break = dvnode->volume->cb_v_break;
|
||||
iget_data.cb_s_break = fc.cbi->server->cb_s_break;
|
||||
scb = &cookie->statuses[0];
|
||||
afs_fs_fetch_status(&fc,
|
||||
afs_v2net(dvnode),
|
||||
cookie->fids,
|
||||
cookie->statuses,
|
||||
cookie->callbacks,
|
||||
scb,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
@ -757,26 +785,36 @@ no_inline_bulk_status:
|
|||
if (IS_ERR(inode))
|
||||
goto out_c;
|
||||
|
||||
for (i = 0; i < cookie->nr_fids; i++)
|
||||
cookie->statuses[i].abort_code = 0;
|
||||
|
||||
success:
|
||||
/* Turn all the files into inodes and save the first one - which is the
|
||||
* one we actually want.
|
||||
*/
|
||||
if (cookie->statuses[0].abort_code != 0)
|
||||
inode = ERR_PTR(afs_abort_to_error(cookie->statuses[0].abort_code));
|
||||
scb = &cookie->statuses[0];
|
||||
if (scb->status.abort_code != 0)
|
||||
inode = ERR_PTR(afs_abort_to_error(scb->status.abort_code));
|
||||
|
||||
for (i = 0; i < cookie->nr_fids; i++) {
|
||||
struct inode *ti;
|
||||
struct afs_status_cb *scb = &cookie->statuses[i];
|
||||
|
||||
if (cookie->statuses[i].abort_code != 0)
|
||||
if (!scb->have_status && !scb->have_error)
|
||||
continue;
|
||||
|
||||
ti = afs_iget(dir->i_sb, key, &cookie->fids[i],
|
||||
&cookie->statuses[i],
|
||||
&cookie->callbacks[i],
|
||||
cbi, dvnode);
|
||||
if (cookie->inodes[i]) {
|
||||
afs_vnode_commit_status(&fc, AFS_FS_I(cookie->inodes[i]),
|
||||
scb->cb_break, NULL, scb);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scb->status.abort_code != 0)
|
||||
continue;
|
||||
|
||||
iget_data.fid = cookie->fids[i];
|
||||
ti = afs_iget(dir->i_sb, key, &iget_data, scb, cbi, dvnode);
|
||||
if (!IS_ERR(ti))
|
||||
afs_cache_permit(AFS_FS_I(ti), key,
|
||||
0 /* Assume vnode->cb_break is 0 */ +
|
||||
iget_data.cb_v_break,
|
||||
scb);
|
||||
if (i == 0) {
|
||||
inode = ti;
|
||||
} else {
|
||||
|
@ -787,9 +825,13 @@ success:
|
|||
|
||||
out_c:
|
||||
afs_put_cb_interest(afs_v2net(dvnode), cbi);
|
||||
kfree(cookie->callbacks);
|
||||
if (cookie->inodes) {
|
||||
for (i = 0; i < cookie->nr_fids; i++)
|
||||
iput(cookie->inodes[i]);
|
||||
kfree(cookie->inodes);
|
||||
}
|
||||
out_s:
|
||||
kfree(cookie->statuses);
|
||||
kvfree(cookie->statuses);
|
||||
out:
|
||||
kfree(cookie);
|
||||
return inode;
|
||||
|
@ -1114,9 +1156,8 @@ 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_iget_data *new_data,
|
||||
struct afs_status_cb *new_scb)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
|
@ -1125,7 +1166,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);
|
||||
new_data, 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.
|
||||
|
@ -1136,22 +1177,29 @@ 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);
|
||||
}
|
||||
|
||||
static void afs_prep_for_new_inode(struct afs_fs_cursor *fc,
|
||||
struct afs_iget_data *iget_data)
|
||||
{
|
||||
iget_data->volume = fc->vnode->volume;
|
||||
iget_data->cb_v_break = fc->vnode->volume->cb_v_break;
|
||||
iget_data->cb_s_break = fc->cbi->server->cb_s_break;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a directory on an AFS filesystem
|
||||
*/
|
||||
static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct afs_file_status newstatus;
|
||||
struct afs_iget_data iget_data;
|
||||
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;
|
||||
|
@ -1159,23 +1207,32 @@ 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_prep_for_new_inode(&fc, &iget_data);
|
||||
afs_fs_create(&fc, dentry->d_name.name, mode,
|
||||
&scb[0], &iget_data.fid, &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, &iget_data, &scb[1]);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
if (ret < 0)
|
||||
goto error_key;
|
||||
|
@ -1185,15 +1242,18 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
|
||||
if (ret == 0 &&
|
||||
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
|
||||
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
|
||||
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
|
||||
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);
|
||||
|
@ -1220,15 +1280,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);
|
||||
|
@ -1251,13 +1315,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);
|
||||
|
@ -1272,6 +1338,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
error_key:
|
||||
key_put(key);
|
||||
error:
|
||||
kfree(scb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1285,32 +1352,27 @@ error:
|
|||
* However, if we didn't have a callback promise outstanding, or it was
|
||||
* outstanding on a different server, then it won't break it either...
|
||||
*/
|
||||
int afs_dir_remove_link(struct dentry *dentry, struct key *key,
|
||||
unsigned long d_version_before,
|
||||
unsigned long d_version_after)
|
||||
static int afs_dir_remove_link(struct afs_vnode *dvnode, struct dentry *dentry,
|
||||
struct key *key)
|
||||
{
|
||||
bool dir_valid;
|
||||
int ret = 0;
|
||||
|
||||
/* There were no intervening changes on the server if the version
|
||||
* number we got back was incremented by exactly 1.
|
||||
*/
|
||||
dir_valid = (d_version_after == d_version_before + 1);
|
||||
|
||||
if (d_really_is_positive(dentry)) {
|
||||
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
|
||||
|
||||
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
|
||||
/* Already done */
|
||||
} else if (dir_valid) {
|
||||
} else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
drop_nlink(&vnode->vfs_inode);
|
||||
if (vnode->vfs_inode.i_nlink == 0) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
__afs_break_callback(vnode);
|
||||
}
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
ret = 0;
|
||||
} else {
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
afs_break_callback(vnode);
|
||||
|
||||
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
|
||||
kdebug("AFS_VNODE_DELETED");
|
||||
|
@ -1331,11 +1393,10 @@ 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}",
|
||||
|
@ -1344,10 +1405,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. */
|
||||
|
@ -1375,29 +1441,33 @@ 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;
|
||||
afs_dataversion_t data_version_2 = vnode->status.data_version;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
|
||||
|
||||
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 != -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]);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break_2,
|
||||
&data_version_2, &scb[1]);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
if (ret == 0)
|
||||
ret = afs_dir_remove_link(
|
||||
dentry, key, d_version,
|
||||
(unsigned long)dvnode->status.data_version);
|
||||
if (ret == 0 && !(scb[1].have_status || scb[1].have_error))
|
||||
ret = afs_dir_remove_link(dvnode, dentry, key);
|
||||
if (ret == 0 &&
|
||||
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
|
||||
afs_edit_dir_remove(dvnode, &dentry->d_name,
|
||||
|
@ -1409,6 +1479,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;
|
||||
|
@ -1420,13 +1492,11 @@ error:
|
|||
static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool excl)
|
||||
{
|
||||
struct afs_iget_data iget_data;
|
||||
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;
|
||||
|
@ -1444,17 +1514,26 @@ 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_prep_for_new_inode(&fc, &iget_data);
|
||||
afs_fs_create(&fc, dentry->d_name.name, mode,
|
||||
&scb[0], &iget_data.fid, &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, &iget_data, &scb[1]);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
if (ret < 0)
|
||||
goto error_key;
|
||||
|
@ -1463,13 +1542,16 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
}
|
||||
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
|
||||
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
|
||||
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
|
||||
afs_edit_dir_for_create);
|
||||
|
||||
kfree(scb);
|
||||
key_put(key);
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error_key:
|
||||
key_put(key);
|
||||
error:
|
||||
|
@ -1485,15 +1567,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,
|
||||
|
@ -1503,14 +1582,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;
|
||||
|
@ -1519,11 +1605,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);
|
||||
|
||||
|
@ -1540,11 +1629,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);
|
||||
|
@ -1557,12 +1649,11 @@ error:
|
|||
static int afs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *content)
|
||||
{
|
||||
struct afs_iget_data iget_data;
|
||||
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",
|
||||
|
@ -1577,24 +1668,32 @@ 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_prep_for_new_inode(&fc, &iget_data);
|
||||
afs_fs_symlink(&fc, dentry->d_name.name, content,
|
||||
&scb[0], &iget_data.fid, &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, &iget_data, &scb[1]);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
if (ret < 0)
|
||||
goto error_key;
|
||||
|
@ -1603,15 +1702,18 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
|
||||
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
|
||||
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
|
||||
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);
|
||||
|
@ -1626,11 +1728,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;
|
||||
|
||||
|
@ -1644,8 +1746,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,
|
||||
|
@ -1653,10 +1753,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,
|
||||
|
@ -1690,31 +1795,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;
|
||||
|
@ -1754,6 +1871,8 @@ error_tmp:
|
|||
if (tmp)
|
||||
dput(tmp);
|
||||
key_put(key);
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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->reply[0];
|
||||
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;
|
||||
|
|
|
@ -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->reply[0];
|
||||
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;
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ static bool afs_fs_probe_done(struct afs_server *server)
|
|||
void afs_fileserver_probe_result(struct afs_call *call)
|
||||
{
|
||||
struct afs_addr_list *alist = call->alist;
|
||||
struct afs_server *server = call->reply[0];
|
||||
unsigned int server_index = (long)call->reply[1];
|
||||
struct afs_server *server = call->server;
|
||||
unsigned int server_index = call->server_index;
|
||||
unsigned int index = call->addr_ix;
|
||||
unsigned int rtt = UINT_MAX;
|
||||
bool have_result = false;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
445
fs/afs/inode.c
445
fs/afs/inode.c
|
@ -23,6 +23,7 @@
|
|||
#include <linux/namei.h>
|
||||
#include <linux/iversion.h>
|
||||
#include "internal.h"
|
||||
#include "afs_fs.h"
|
||||
|
||||
static const struct inode_operations afs_symlink_inode_operations = {
|
||||
.get_link = page_get_link,
|
||||
|
@ -58,38 +59,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 +112,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 +120,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 +129,175 @@ 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_expires_at = ktime_get_real_seconds();
|
||||
} else {
|
||||
vnode->cb_expires_at = scb->callback.expires_at;
|
||||
old_cbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
if (cbi != old_cbi)
|
||||
rcu_assign_pointer(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_expires_at = cb->expires_at;
|
||||
old = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
if (old != fc->cbi) {
|
||||
rcu_assign_pointer(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);
|
||||
|
||||
if (scb->have_error) {
|
||||
if (scb->status.abort_code == VNOVNODE) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_nlink(&vnode->vfs_inode);
|
||||
__afs_break_callback(vnode);
|
||||
}
|
||||
} else {
|
||||
if (scb->have_status)
|
||||
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 && scb->have_status)
|
||||
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 +306,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;
|
||||
}
|
||||
|
@ -156,10 +347,10 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
|
|||
*/
|
||||
int afs_iget5_test(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_iget_data *data = opaque;
|
||||
struct afs_iget_data *iget_data = opaque;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
return memcmp(&vnode->fid, &data->fid, sizeof(data->fid)) == 0;
|
||||
return memcmp(&vnode->fid, &iget_data->fid, sizeof(iget_data->fid)) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -177,17 +368,19 @@ static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
|
|||
*/
|
||||
static int afs_iget5_set(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_iget_data *data = opaque;
|
||||
struct afs_iget_data *iget_data = opaque;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
vnode->fid = data->fid;
|
||||
vnode->volume = data->volume;
|
||||
vnode->fid = iget_data->fid;
|
||||
vnode->volume = iget_data->volume;
|
||||
vnode->cb_v_break = iget_data->cb_v_break;
|
||||
vnode->cb_s_break = iget_data->cb_s_break;
|
||||
|
||||
/* YFS supports 96-bit vnode IDs, but Linux only supports
|
||||
* 64-bit inode numbers.
|
||||
*/
|
||||
inode->i_ino = data->fid.vnode;
|
||||
inode->i_generation = data->fid.unique;
|
||||
inode->i_ino = iget_data->fid.vnode;
|
||||
inode->i_generation = iget_data->fid.unique;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -197,38 +390,42 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
|
|||
*/
|
||||
struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
|
||||
{
|
||||
struct afs_iget_data data;
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
static atomic_t afs_autocell_ino;
|
||||
|
||||
struct afs_iget_data iget_data = {
|
||||
.cb_v_break = 0,
|
||||
.cb_s_break = 0,
|
||||
};
|
||||
|
||||
_enter("");
|
||||
|
||||
as = sb->s_fs_info;
|
||||
if (as->volume) {
|
||||
data.volume = as->volume;
|
||||
data.fid.vid = as->volume->vid;
|
||||
iget_data.volume = as->volume;
|
||||
iget_data.fid.vid = as->volume->vid;
|
||||
}
|
||||
if (root) {
|
||||
data.fid.vnode = 1;
|
||||
data.fid.unique = 1;
|
||||
iget_data.fid.vnode = 1;
|
||||
iget_data.fid.unique = 1;
|
||||
} else {
|
||||
data.fid.vnode = atomic_inc_return(&afs_autocell_ino);
|
||||
data.fid.unique = 0;
|
||||
iget_data.fid.vnode = atomic_inc_return(&afs_autocell_ino);
|
||||
iget_data.fid.unique = 0;
|
||||
}
|
||||
|
||||
inode = iget5_locked(sb, data.fid.vnode,
|
||||
inode = iget5_locked(sb, iget_data.fid.vnode,
|
||||
afs_iget5_pseudo_dir_test, afs_iget5_set,
|
||||
&data);
|
||||
&iget_data);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
|
||||
inode, inode->i_ino, data.fid.vid, data.fid.vnode,
|
||||
data.fid.unique);
|
||||
inode, inode->i_ino, iget_data.fid.vid, iget_data.fid.vnode,
|
||||
iget_data.fid.unique);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
|
@ -299,23 +496,24 @@ 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_iget_data *iget_data,
|
||||
struct afs_status_cb *scb,
|
||||
struct afs_cb_interest *cbi,
|
||||
struct afs_vnode *parent_vnode)
|
||||
{
|
||||
struct afs_iget_data data = { .fid = *fid };
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode *vnode;
|
||||
struct afs_fid *fid = &iget_data->fid;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
_enter(",{%llx:%llu.%u},,", fid->vid, fid->vnode, fid->unique);
|
||||
|
||||
as = sb->s_fs_info;
|
||||
data.volume = as->volume;
|
||||
iget_data->volume = as->volume;
|
||||
|
||||
inode = iget5_locked(sb, fid->vnode, afs_iget5_test, afs_iget5_set,
|
||||
&data);
|
||||
iget_data);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
@ -332,43 +530,25 @@ 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 */
|
||||
clear_bit(AFS_VNODE_UNSET, &vnode->flags);
|
||||
inode->i_flags |= S_NOATIME;
|
||||
unlock_new_inode(inode);
|
||||
_leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type);
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
|
||||
/* failure */
|
||||
|
@ -399,6 +579,66 @@ void afs_zap_data(struct afs_vnode *vnode)
|
|||
invalidate_inode_pages2(vnode->vfs_inode.i_mapping);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the validity of a vnode/inode.
|
||||
*/
|
||||
bool afs_check_validity(struct afs_vnode *vnode)
|
||||
{
|
||||
struct afs_cb_interest *cbi;
|
||||
struct afs_server *server;
|
||||
struct afs_volume *volume = vnode->volume;
|
||||
time64_t now = ktime_get_real_seconds();
|
||||
bool valid, need_clear = false;
|
||||
unsigned int cb_break, cb_s_break, cb_v_break;
|
||||
int seq = 0;
|
||||
|
||||
do {
|
||||
read_seqbegin_or_lock(&vnode->cb_lock, &seq);
|
||||
cb_v_break = READ_ONCE(volume->cb_v_break);
|
||||
cb_break = vnode->cb_break;
|
||||
|
||||
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
||||
cbi = rcu_dereference(vnode->cb_interest);
|
||||
server = rcu_dereference(cbi->server);
|
||||
cb_s_break = READ_ONCE(server->cb_s_break);
|
||||
|
||||
if (vnode->cb_s_break != cb_s_break ||
|
||||
vnode->cb_v_break != cb_v_break) {
|
||||
vnode->cb_s_break = cb_s_break;
|
||||
vnode->cb_v_break = cb_v_break;
|
||||
need_clear = true;
|
||||
valid = false;
|
||||
} else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
|
||||
need_clear = true;
|
||||
valid = false;
|
||||
} else if (vnode->cb_expires_at - 10 <= now) {
|
||||
need_clear = true;
|
||||
valid = false;
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
} else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
|
||||
valid = true;
|
||||
} else {
|
||||
vnode->cb_v_break = cb_v_break;
|
||||
valid = false;
|
||||
}
|
||||
|
||||
} while (need_seqretry(&vnode->cb_lock, seq));
|
||||
|
||||
done_seqretry(&vnode->cb_lock, seq);
|
||||
|
||||
if (need_clear) {
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
if (cb_break == vnode->cb_break)
|
||||
__afs_break_callback(vnode);
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/*
|
||||
* validate a vnode/inode
|
||||
* - there are several things we need to check
|
||||
|
@ -410,7 +650,6 @@ void afs_zap_data(struct afs_vnode *vnode)
|
|||
*/
|
||||
int afs_validate(struct afs_vnode *vnode, struct key *key)
|
||||
{
|
||||
time64_t now = ktime_get_real_seconds();
|
||||
bool valid;
|
||||
int ret;
|
||||
|
||||
|
@ -418,33 +657,9 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
|
|||
vnode->fid.vid, vnode->fid.vnode, vnode->flags,
|
||||
key_serial(key));
|
||||
|
||||
/* Quickly check the callback state. Ideally, we'd use read_seqbegin
|
||||
* here, but we have no way to pass the net namespace to the RCU
|
||||
* cleanup for the server record.
|
||||
*/
|
||||
read_seqlock_excl(&vnode->cb_lock);
|
||||
|
||||
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
||||
if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break ||
|
||||
vnode->cb_v_break != vnode->volume->cb_v_break) {
|
||||
vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
|
||||
vnode->cb_v_break = vnode->volume->cb_v_break;
|
||||
valid = false;
|
||||
} else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
|
||||
valid = false;
|
||||
} else if (vnode->cb_expires_at - 10 <= now) {
|
||||
valid = false;
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
} else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
|
||||
valid = true;
|
||||
} else {
|
||||
vnode->cb_v_break = vnode->volume->cb_v_break;
|
||||
valid = false;
|
||||
}
|
||||
|
||||
read_sequnlock_excl(&vnode->cb_lock);
|
||||
rcu_read_lock();
|
||||
valid = afs_check_validity(vnode);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
|
||||
clear_nlink(&vnode->vfs_inode);
|
||||
|
@ -460,7 +675,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);
|
||||
|
@ -531,6 +746,7 @@ int afs_drop_inode(struct inode *inode)
|
|||
*/
|
||||
void afs_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct afs_cb_interest *cbi;
|
||||
struct afs_vnode *vnode;
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
@ -547,10 +763,14 @@ void afs_evict_inode(struct inode *inode)
|
|||
truncate_inode_pages_final(&inode->i_data);
|
||||
clear_inode(inode);
|
||||
|
||||
if (vnode->cb_interest) {
|
||||
afs_put_cb_interest(afs_i2net(inode), vnode->cb_interest);
|
||||
vnode->cb_interest = NULL;
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
cbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
if (cbi) {
|
||||
afs_put_cb_interest(afs_i2net(inode), cbi);
|
||||
rcu_assign_pointer(vnode->cb_interest, NULL);
|
||||
}
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
|
||||
while (!list_empty(&vnode->wb_keys)) {
|
||||
struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next,
|
||||
|
@ -585,9 +805,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 +820,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 +834,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;
|
||||
|
|
|
@ -66,6 +66,8 @@ struct afs_fs_context {
|
|||
struct afs_iget_data {
|
||||
struct afs_fid fid;
|
||||
struct afs_volume *volume; /* volume on which resides */
|
||||
unsigned int cb_v_break; /* Pre-fetch volume break count */
|
||||
unsigned int cb_s_break; /* Pre-fetch server break count */
|
||||
};
|
||||
|
||||
enum afs_call_state {
|
||||
|
@ -111,8 +113,12 @@ struct afs_call {
|
|||
struct rxrpc_call *rxcall; /* RxRPC call handle */
|
||||
struct key *key; /* security for this call */
|
||||
struct afs_net *net; /* The network namespace */
|
||||
struct afs_server *cm_server; /* Server affected by incoming CM call */
|
||||
union {
|
||||
struct afs_server *server;
|
||||
struct afs_vlserver *vlserver;
|
||||
};
|
||||
struct afs_cb_interest *cbi; /* Callback interest for server used */
|
||||
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 */
|
||||
|
@ -122,7 +128,20 @@ struct afs_call {
|
|||
struct bio_vec bvec[1];
|
||||
};
|
||||
void *buffer; /* reply receive buffer */
|
||||
void *reply[4]; /* Where to put the reply */
|
||||
union {
|
||||
long ret0; /* Value to reply with instead of 0 */
|
||||
struct afs_addr_list *ret_alist;
|
||||
struct afs_vldb_entry *ret_vldb;
|
||||
struct afs_acl *ret_acl;
|
||||
};
|
||||
struct afs_fid *out_fid;
|
||||
struct afs_status_cb *out_dir_scb;
|
||||
struct afs_status_cb *out_scb;
|
||||
struct yfs_acl *out_yacl;
|
||||
struct afs_volsync *out_volsync;
|
||||
struct afs_volume_status *out_volstatus;
|
||||
struct afs_read *read_request;
|
||||
unsigned int server_index;
|
||||
pgoff_t first; /* first page in mapping to deal with */
|
||||
pgoff_t last; /* last page in mapping to deal with */
|
||||
atomic_t usage;
|
||||
|
@ -135,7 +154,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 */
|
||||
|
@ -146,9 +164,8 @@ struct afs_call {
|
|||
bool send_pages; /* T if data from mapping should be sent */
|
||||
bool need_attention; /* T if RxRPC poked us */
|
||||
bool async; /* T if asynchronous */
|
||||
bool ret_reply0; /* T if should return reply[0] on success */
|
||||
bool upgrade; /* T to request service upgrade */
|
||||
bool want_reply_time; /* T if want reply_time */
|
||||
bool have_reply_time; /* T if have got reply_time */
|
||||
bool intr; /* T if interruptible */
|
||||
u16 service_id; /* Actual service ID (after upgrade) */
|
||||
unsigned int debug_id; /* Trace ID */
|
||||
|
@ -161,8 +178,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 */
|
||||
};
|
||||
|
||||
|
@ -223,7 +238,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[];
|
||||
};
|
||||
|
@ -540,7 +556,10 @@ struct afs_server {
|
|||
struct afs_vol_interest {
|
||||
struct hlist_node srv_link; /* Link in server->cb_volumes */
|
||||
struct hlist_head cb_interests; /* List of callback interests on the server */
|
||||
afs_volid_t vid; /* Volume ID to match */
|
||||
union {
|
||||
struct rcu_head rcu;
|
||||
afs_volid_t vid; /* Volume ID to match */
|
||||
};
|
||||
unsigned int usage;
|
||||
};
|
||||
|
||||
|
@ -552,7 +571,10 @@ struct afs_cb_interest {
|
|||
struct afs_vol_interest *vol_interest;
|
||||
struct afs_server *server; /* Server on which this interest resides */
|
||||
struct super_block *sb; /* Superblock on which inodes reside */
|
||||
afs_volid_t vid; /* Volume ID to match */
|
||||
union {
|
||||
struct rcu_head rcu;
|
||||
afs_volid_t vid; /* Volume ID to match */
|
||||
};
|
||||
refcount_t usage;
|
||||
};
|
||||
|
||||
|
@ -662,15 +684,13 @@ struct afs_vnode {
|
|||
afs_lock_type_t lock_type : 8;
|
||||
|
||||
/* outstanding callback notification on this file */
|
||||
struct afs_cb_interest *cb_interest; /* Server on which this resides */
|
||||
struct afs_cb_interest __rcu *cb_interest; /* Server on which this resides */
|
||||
unsigned int cb_s_break; /* Mass break counter on ->server */
|
||||
unsigned int cb_v_break; /* Mass break counter on ->volume */
|
||||
unsigned int cb_break; /* Break counter on vnode */
|
||||
seqlock_t cb_lock; /* Lock for ->cb_interest, ->status, ->cb_*break */
|
||||
|
||||
time64_t cb_expires_at; /* time at which callback expires */
|
||||
unsigned cb_version; /* callback version */
|
||||
afs_callback_type_t cb_type; /* type of callback */
|
||||
};
|
||||
|
||||
static inline struct fscache_cookie *afs_vnode_cache(struct afs_vnode *vnode)
|
||||
|
@ -757,6 +777,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) */
|
||||
|
@ -885,7 +906,6 @@ extern const struct address_space_operations afs_dir_aops;
|
|||
extern const struct dentry_operations afs_fs_dentry_operations;
|
||||
|
||||
extern void afs_d_release(struct dentry *);
|
||||
extern int afs_dir_remove_link(struct dentry *, struct key *, unsigned long, unsigned long);
|
||||
|
||||
/*
|
||||
* dir_edit.c
|
||||
|
@ -943,50 +963,48 @@ 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 *,
|
||||
struct afs_addr_cursor *, struct key *,
|
||||
unsigned int);
|
||||
extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_file_status *,
|
||||
struct afs_callback *, unsigned int,
|
||||
struct afs_volsync *);
|
||||
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
|
||||
|
@ -998,15 +1016,20 @@ 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_iget_data *, struct afs_status_cb *,
|
||||
struct afs_cb_interest *,
|
||||
struct afs_vnode *);
|
||||
extern void afs_zap_data(struct afs_vnode *);
|
||||
extern bool afs_check_validity(struct afs_vnode *);
|
||||
extern int afs_validate(struct afs_vnode *, struct key *);
|
||||
extern int afs_getattr(const struct path *, struct kstat *, u32, unsigned int);
|
||||
extern int afs_setattr(struct dentry *, struct iattr *);
|
||||
|
@ -1127,6 +1150,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)
|
||||
|
@ -1209,7 +1233,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 *);
|
||||
|
@ -1335,7 +1360,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);
|
||||
|
@ -1351,33 +1375,36 @@ 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_file_status *,
|
||||
struct afs_callback *, unsigned int,
|
||||
struct afs_volsync *);
|
||||
struct afs_fid *, struct afs_status_cb *,
|
||||
unsigned int, struct afs_volsync *);
|
||||
|
||||
struct yfs_acl {
|
||||
struct afs_acl *acl; /* Dir/file/symlink ACL */
|
||||
|
@ -1390,8 +1417,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.
|
||||
|
@ -1406,14 +1435,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)
|
||||
{
|
||||
|
|
|
@ -66,7 +66,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc,
|
|||
fc->untried = (1UL << fc->server_list->nr_servers) - 1;
|
||||
fc->index = READ_ONCE(fc->server_list->preferred);
|
||||
|
||||
cbi = vnode->cb_interest;
|
||||
cbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->io_lock));
|
||||
if (cbi) {
|
||||
/* See if the vnode's preferred record is still available */
|
||||
for (i = 0; i < fc->server_list->nr_servers; i++) {
|
||||
|
@ -87,8 +88,8 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc,
|
|||
|
||||
/* Note that the callback promise is effectively broken */
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
ASSERTCMP(cbi, ==, vnode->cb_interest);
|
||||
vnode->cb_interest = NULL;
|
||||
ASSERTCMP(cbi, ==, rcu_access_pointer(vnode->cb_interest));
|
||||
rcu_assign_pointer(vnode->cb_interest, NULL);
|
||||
if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
|
||||
vnode->cb_break++;
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
|
@ -417,7 +418,9 @@ selected_server:
|
|||
if (error < 0)
|
||||
goto failed_set_error;
|
||||
|
||||
fc->cbi = afs_get_cb_interest(vnode->cb_interest);
|
||||
fc->cbi = afs_get_cb_interest(
|
||||
rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->io_lock)));
|
||||
|
||||
read_lock(&server->fs_lock);
|
||||
alist = rcu_dereference_protected(server->addresses,
|
||||
|
@ -487,12 +490,15 @@ failed:
|
|||
bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
|
||||
{
|
||||
struct afs_vnode *vnode = fc->vnode;
|
||||
struct afs_cb_interest *cbi = vnode->cb_interest;
|
||||
struct afs_cb_interest *cbi;
|
||||
struct afs_addr_list *alist;
|
||||
int error = fc->ac.error;
|
||||
|
||||
_enter("");
|
||||
|
||||
cbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->io_lock));
|
||||
|
||||
switch (error) {
|
||||
case SHRT_MAX:
|
||||
if (!cbi) {
|
||||
|
@ -501,7 +507,7 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
|
|||
return false;
|
||||
}
|
||||
|
||||
fc->cbi = afs_get_cb_interest(vnode->cb_interest);
|
||||
fc->cbi = afs_get_cb_interest(cbi);
|
||||
|
||||
read_lock(&cbi->server->fs_lock);
|
||||
alist = rcu_dereference_protected(cbi->server->addresses,
|
||||
|
|
|
@ -188,7 +188,7 @@ void afs_put_call(struct afs_call *call)
|
|||
if (call->type->destructor)
|
||||
call->type->destructor(call);
|
||||
|
||||
afs_put_server(call->net, call->cm_server);
|
||||
afs_put_server(call->net, call->server);
|
||||
afs_put_cb_interest(call->net, call->cbi);
|
||||
afs_put_addrlist(call->alist);
|
||||
kfree(call->request);
|
||||
|
@ -534,11 +534,11 @@ static void afs_deliver_to_call(struct afs_call *call)
|
|||
return;
|
||||
}
|
||||
|
||||
if (call->want_reply_time &&
|
||||
if (!call->have_reply_time &&
|
||||
rxrpc_kernel_get_reply_time(call->net->socket,
|
||||
call->rxcall,
|
||||
&call->reply_time))
|
||||
call->want_reply_time = false;
|
||||
call->have_reply_time = true;
|
||||
|
||||
ret = call->type->deliver(call);
|
||||
state = READ_ONCE(call->state);
|
||||
|
@ -696,10 +696,9 @@ long afs_wait_for_call_to_complete(struct afs_call *call,
|
|||
ret = ac->error;
|
||||
switch (ret) {
|
||||
case 0:
|
||||
if (call->ret_reply0) {
|
||||
ret = (long)call->reply[0];
|
||||
call->reply[0] = NULL;
|
||||
}
|
||||
ret = call->ret0;
|
||||
call->ret0 = 0;
|
||||
|
||||
/* Fall through */
|
||||
case -ECONNABORTED:
|
||||
ac->responded = true;
|
||||
|
|
|
@ -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;
|
||||
|
@ -146,7 +146,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
|
|||
}
|
||||
|
||||
if (afs_cb_is_broken(cb_break, vnode,
|
||||
vnode->cb_interest)) {
|
||||
rcu_dereference(vnode->cb_interest))) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
|
|||
}
|
||||
}
|
||||
|
||||
if (afs_cb_is_broken(cb_break, vnode, vnode->cb_interest))
|
||||
if (afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)))
|
||||
goto someone_else_changed_it;
|
||||
|
||||
/* We need a ref on any permits list we want to copy as we'll have to
|
||||
|
@ -253,14 +253,16 @@ found:
|
|||
|
||||
kfree(new);
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock(&vnode->lock);
|
||||
zap = rcu_access_pointer(vnode->permit_cache);
|
||||
if (!afs_cb_is_broken(cb_break, vnode, vnode->cb_interest) &&
|
||||
if (!afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)) &&
|
||||
zap == permits)
|
||||
rcu_assign_pointer(vnode->permit_cache, replacement);
|
||||
else
|
||||
zap = replacement;
|
||||
spin_unlock(&vnode->lock);
|
||||
rcu_read_unlock();
|
||||
afs_put_permits(zap);
|
||||
out_put:
|
||||
afs_put_permits(permits);
|
||||
|
@ -320,13 +322,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);
|
||||
|
|
|
@ -426,7 +426,7 @@ static int afs_set_super(struct super_block *sb, struct fs_context *fc)
|
|||
static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(sb);
|
||||
struct afs_fid fid;
|
||||
struct afs_iget_data iget_data;
|
||||
struct inode *inode = NULL;
|
||||
int ret;
|
||||
|
||||
|
@ -451,11 +451,13 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
|
|||
} else {
|
||||
sprintf(sb->s_id, "%llu", as->volume->vid);
|
||||
afs_activate_volume(as->volume);
|
||||
fid.vid = as->volume->vid;
|
||||
fid.vnode = 1;
|
||||
fid.vnode_hi = 0;
|
||||
fid.unique = 1;
|
||||
inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL, NULL);
|
||||
iget_data.fid.vid = as->volume->vid;
|
||||
iget_data.fid.vnode = 1;
|
||||
iget_data.fid.vnode_hi = 0;
|
||||
iget_data.fid.unique = 1;
|
||||
iget_data.cb_v_break = as->volume->cb_v_break;
|
||||
iget_data.cb_s_break = 0;
|
||||
inode = afs_iget(sb, ctx->key, &iget_data, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (IS_ERR(inode))
|
||||
|
@ -677,13 +679,12 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
|
|||
vnode->volume = NULL;
|
||||
vnode->lock_key = NULL;
|
||||
vnode->permit_cache = NULL;
|
||||
vnode->cb_interest = NULL;
|
||||
RCU_INIT_POINTER(vnode->cb_interest, NULL);
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
vnode->cache = NULL;
|
||||
#endif
|
||||
|
||||
vnode->flags = 1 << AFS_VNODE_UNSET;
|
||||
vnode->cb_type = 0;
|
||||
vnode->lock_state = AFS_VNODE_LOCK_NONE;
|
||||
|
||||
init_rwsem(&vnode->rmdir_lock);
|
||||
|
@ -708,7 +709,7 @@ static void afs_destroy_inode(struct inode *inode)
|
|||
|
||||
_debug("DESTROY INODE %p", inode);
|
||||
|
||||
ASSERTCMP(vnode->cb_interest, ==, NULL);
|
||||
ASSERTCMP(rcu_access_pointer(vnode->cb_interest), ==, NULL);
|
||||
|
||||
atomic_dec(&afs_count_active_inodes);
|
||||
}
|
||||
|
@ -749,7 +750,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ static bool afs_vl_probe_done(struct afs_vlserver *server)
|
|||
void afs_vlserver_probe_result(struct afs_call *call)
|
||||
{
|
||||
struct afs_addr_list *alist = call->alist;
|
||||
struct afs_vlserver *server = call->reply[0];
|
||||
unsigned int server_index = (long)call->reply[1];
|
||||
struct afs_vlserver *server = call->vlserver;
|
||||
unsigned int server_index = call->server_index;
|
||||
unsigned int index = call->addr_ix;
|
||||
unsigned int rtt = UINT_MAX;
|
||||
bool have_result = false;
|
||||
|
|
|
@ -34,7 +34,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
uvldb = call->buffer;
|
||||
entry = call->reply[0];
|
||||
entry = call->ret_vldb;
|
||||
|
||||
nr_servers = ntohl(uvldb->nServers);
|
||||
if (nr_servers > AFS_NMAXNSERVERS)
|
||||
|
@ -110,7 +110,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
|
|||
|
||||
static void afs_destroy_vl_get_entry_by_name_u(struct afs_call *call)
|
||||
{
|
||||
kfree(call->reply[0]);
|
||||
kfree(call->ret_vldb);
|
||||
afs_flat_call_destructor(call);
|
||||
}
|
||||
|
||||
|
@ -155,8 +155,7 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc,
|
|||
}
|
||||
|
||||
call->key = vc->key;
|
||||
call->reply[0] = entry;
|
||||
call->ret_reply0 = true;
|
||||
call->ret_vldb = entry;
|
||||
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
||||
|
||||
/* Marshall the parameters */
|
||||
|
@ -215,7 +214,7 @@ static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
|
|||
if (!alist)
|
||||
return -ENOMEM;
|
||||
alist->version = uniquifier;
|
||||
call->reply[0] = alist;
|
||||
call->ret_alist = alist;
|
||||
call->count = count;
|
||||
call->count2 = nentries;
|
||||
call->unmarshall++;
|
||||
|
@ -230,7 +229,7 @@ static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
alist = call->reply[0];
|
||||
alist = call->ret_alist;
|
||||
bp = call->buffer;
|
||||
count = min(call->count, 4U);
|
||||
for (i = 0; i < count; i++)
|
||||
|
@ -250,8 +249,7 @@ static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
|
|||
|
||||
static void afs_vl_get_addrs_u_destructor(struct afs_call *call)
|
||||
{
|
||||
afs_put_server(call->net, (struct afs_server *)call->reply[0]);
|
||||
kfree(call->reply[1]);
|
||||
afs_put_addrlist(call->ret_alist);
|
||||
return afs_flat_call_destructor(call);
|
||||
}
|
||||
|
||||
|
@ -288,8 +286,7 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
call->key = vc->key;
|
||||
call->reply[0] = NULL;
|
||||
call->ret_reply0 = true;
|
||||
call->ret_alist = NULL;
|
||||
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
||||
|
||||
/* Marshall the parameters */
|
||||
|
@ -360,9 +357,7 @@ static int afs_deliver_vl_get_capabilities(struct afs_call *call)
|
|||
|
||||
static void afs_destroy_vl_get_capabilities(struct afs_call *call)
|
||||
{
|
||||
struct afs_vlserver *server = call->reply[0];
|
||||
|
||||
afs_put_vlserver(call->net, server);
|
||||
afs_put_vlserver(call->net, call->vlserver);
|
||||
afs_flat_call_destructor(call);
|
||||
}
|
||||
|
||||
|
@ -400,10 +395,9 @@ struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
call->key = key;
|
||||
call->reply[0] = afs_get_vlserver(server);
|
||||
call->reply[1] = (void *)(long)server_index;
|
||||
call->vlserver = afs_get_vlserver(server);
|
||||
call->server_index = server_index;
|
||||
call->upgrade = true;
|
||||
call->want_reply_time = true;
|
||||
call->async = true;
|
||||
call->max_lifespan = AFS_PROBE_MAX_LIFESPAN;
|
||||
|
||||
|
@ -463,7 +457,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
|
|||
if (!alist)
|
||||
return -ENOMEM;
|
||||
alist->version = uniquifier;
|
||||
call->reply[0] = alist;
|
||||
call->ret_alist = alist;
|
||||
|
||||
if (call->count == 0)
|
||||
goto extract_volendpoints;
|
||||
|
@ -491,7 +485,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
alist = call->reply[0];
|
||||
alist = call->ret_alist;
|
||||
bp = call->buffer;
|
||||
switch (call->count2) {
|
||||
case YFS_ENDPOINT_IPV4:
|
||||
|
@ -612,7 +606,6 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
|
|||
break;
|
||||
}
|
||||
|
||||
alist = call->reply[0];
|
||||
_leave(" = 0 [done]");
|
||||
return 0;
|
||||
}
|
||||
|
@ -647,8 +640,7 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
call->key = vc->key;
|
||||
call->reply[0] = NULL;
|
||||
call->ret_reply0 = true;
|
||||
call->ret_alist = NULL;
|
||||
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
||||
|
||||
/* Marshall the parameters */
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
103
fs/afs/xattr.c
103
fs/afs/xattr.c
|
@ -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
Loading…
Reference in New Issue