vfs-6.18-rc1.afs

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaNZQeQAKCRCRxhvAZXjc
 ohhnAQCrtEWukO2GOLdoZdB9nuijIhkE0kNnq3OoMnD/pWE9hQEA3hjJts0o2QQX
 LcU62eEUg2airLRMSessPxhoMaJnVgo=
 =JTvC
 -----END PGP SIGNATURE-----

Merge tag 'vfs-6.18-rc1.afs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull afs updates from Christian Brauner:
 "This contains the change to enable afs to support RENAME_NOREPLACE and
  RENAME_EXCHANGE if the server supports it"

* tag 'vfs-6.18-rc1.afs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  afs: Add support for RENAME_NOREPLACE and RENAME_EXCHANGE
This commit is contained in:
Linus Torvalds 2025-09-29 10:53:22 -07:00
commit 5484a4ea7a
10 changed files with 480 additions and 64 deletions

View File

@ -1823,7 +1823,8 @@ error:
static void afs_rename_success(struct afs_operation *op)
{
struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
struct afs_vnode *vnode = op->more_files[0].vnode;
struct afs_vnode *new_vnode = op->more_files[1].vnode;
_enter("op=%08x", op->debug_id);
@ -1834,22 +1835,40 @@ static void afs_rename_success(struct afs_operation *op)
op->ctime = op->file[1].scb.status.mtime_client;
afs_vnode_commit_status(op, &op->file[1]);
}
if (op->more_files[0].scb.have_status)
afs_vnode_commit_status(op, &op->more_files[0]);
if (op->more_files[1].scb.have_status)
afs_vnode_commit_status(op, &op->more_files[1]);
/* If we're moving a subdir between dirs, we need to update
* its DV counter too as the ".." will be altered.
*/
if (S_ISDIR(vnode->netfs.inode.i_mode) &&
op->file[0].vnode != op->file[1].vnode) {
u64 new_dv;
if (op->file[0].vnode != op->file[1].vnode) {
if (S_ISDIR(vnode->netfs.inode.i_mode)) {
u64 new_dv;
write_seqlock(&vnode->cb_lock);
write_seqlock(&vnode->cb_lock);
new_dv = vnode->status.data_version + 1;
trace_afs_set_dv(vnode, new_dv);
vnode->status.data_version = new_dv;
inode_set_iversion_raw(&vnode->netfs.inode, new_dv);
new_dv = vnode->status.data_version + 1;
trace_afs_set_dv(vnode, new_dv);
vnode->status.data_version = new_dv;
inode_set_iversion_raw(&vnode->netfs.inode, new_dv);
write_sequnlock(&vnode->cb_lock);
write_sequnlock(&vnode->cb_lock);
}
if ((op->rename.rename_flags & RENAME_EXCHANGE) &&
S_ISDIR(new_vnode->netfs.inode.i_mode)) {
u64 new_dv;
write_seqlock(&new_vnode->cb_lock);
new_dv = new_vnode->status.data_version + 1;
new_vnode->status.data_version = new_dv;
inode_set_iversion_raw(&new_vnode->netfs.inode, new_dv);
write_sequnlock(&new_vnode->cb_lock);
}
}
}
@ -1900,8 +1919,8 @@ static void afs_rename_edit_dir(struct afs_operation *op)
if (S_ISDIR(vnode->netfs.inode.i_mode) &&
new_dvnode != orig_dvnode &&
test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
afs_edit_dir_update_dotdot(vnode, new_dvnode,
afs_edit_dir_for_rename_sub);
afs_edit_dir_update(vnode, &dotdot_name, new_dvnode,
afs_edit_dir_for_rename_sub);
new_inode = d_inode(new_dentry);
if (new_inode) {
@ -1915,9 +1934,6 @@ static void afs_rename_edit_dir(struct afs_operation *op)
/* Now we can update d_fsdata on the dentries to reflect their
* new parent's data_version.
*
* Note that if we ever implement RENAME_EXCHANGE, we'll have
* to update both dentries with opposing dir versions.
*/
afs_update_dentry_version(op, new_dvp, op->dentry);
afs_update_dentry_version(op, new_dvp, op->dentry_2);
@ -1930,6 +1946,67 @@ static void afs_rename_edit_dir(struct afs_operation *op)
fscache_end_operation(&new_cres);
}
static void afs_rename_exchange_edit_dir(struct afs_operation *op)
{
struct afs_vnode_param *orig_dvp = &op->file[0];
struct afs_vnode_param *new_dvp = &op->file[1];
struct afs_vnode *orig_dvnode = orig_dvp->vnode;
struct afs_vnode *new_dvnode = new_dvp->vnode;
struct afs_vnode *old_vnode = op->more_files[0].vnode;
struct afs_vnode *new_vnode = op->more_files[1].vnode;
struct dentry *old_dentry = op->dentry;
struct dentry *new_dentry = op->dentry_2;
_enter("op=%08x", op->debug_id);
if (new_dvnode == orig_dvnode) {
down_write(&orig_dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) &&
orig_dvnode->status.data_version == orig_dvp->dv_before + orig_dvp->dv_delta) {
afs_edit_dir_update(orig_dvnode, &old_dentry->d_name,
new_vnode, afs_edit_dir_for_rename_0);
afs_edit_dir_update(orig_dvnode, &new_dentry->d_name,
old_vnode, afs_edit_dir_for_rename_1);
}
d_exchange(old_dentry, new_dentry);
up_write(&orig_dvnode->validate_lock);
} else {
down_write(&orig_dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) &&
orig_dvnode->status.data_version == orig_dvp->dv_before + orig_dvp->dv_delta)
afs_edit_dir_update(orig_dvnode, &old_dentry->d_name,
new_vnode, afs_edit_dir_for_rename_0);
up_write(&orig_dvnode->validate_lock);
down_write(&new_dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags) &&
new_dvnode->status.data_version == new_dvp->dv_before + new_dvp->dv_delta)
afs_edit_dir_update(new_dvnode, &new_dentry->d_name,
old_vnode, afs_edit_dir_for_rename_1);
if (S_ISDIR(old_vnode->netfs.inode.i_mode) &&
test_bit(AFS_VNODE_DIR_VALID, &old_vnode->flags))
afs_edit_dir_update(old_vnode, &dotdot_name, new_dvnode,
afs_edit_dir_for_rename_sub);
if (S_ISDIR(new_vnode->netfs.inode.i_mode) &&
test_bit(AFS_VNODE_DIR_VALID, &new_vnode->flags))
afs_edit_dir_update(new_vnode, &dotdot_name, orig_dvnode,
afs_edit_dir_for_rename_sub);
/* Now we can update d_fsdata on the dentries to reflect their
* new parents' data_version.
*/
afs_update_dentry_version(op, new_dvp, old_dentry);
afs_update_dentry_version(op, orig_dvp, new_dentry);
d_exchange(old_dentry, new_dentry);
up_write(&new_dvnode->validate_lock);
}
}
static void afs_rename_put(struct afs_operation *op)
{
_enter("op=%08x", op->debug_id);
@ -1948,6 +2025,32 @@ static const struct afs_operation_ops afs_rename_operation = {
.put = afs_rename_put,
};
#if 0 /* Autoswitched in yfs_fs_rename_replace(). */
static const struct afs_operation_ops afs_rename_replace_operation = {
.issue_afs_rpc = NULL,
.issue_yfs_rpc = yfs_fs_rename_replace,
.success = afs_rename_success,
.edit_dir = afs_rename_edit_dir,
.put = afs_rename_put,
};
#endif
static const struct afs_operation_ops afs_rename_noreplace_operation = {
.issue_afs_rpc = NULL,
.issue_yfs_rpc = yfs_fs_rename_noreplace,
.success = afs_rename_success,
.edit_dir = afs_rename_edit_dir,
.put = afs_rename_put,
};
static const struct afs_operation_ops afs_rename_exchange_operation = {
.issue_afs_rpc = NULL,
.issue_yfs_rpc = yfs_fs_rename_exchange,
.success = afs_rename_success,
.edit_dir = afs_rename_exchange_edit_dir,
.put = afs_rename_put,
};
/*
* rename a file in an AFS filesystem and/or move it between directories
*/
@ -1956,10 +2059,10 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *new_dentry, unsigned int flags)
{
struct afs_operation *op;
struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
struct afs_vnode *orig_dvnode, *new_dvnode, *vnode, *new_vnode = NULL;
int ret;
if (flags)
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
return -EINVAL;
/* Don't allow silly-rename files be moved around. */
@ -1969,6 +2072,8 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
vnode = AFS_FS_I(d_inode(old_dentry));
orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir);
if (d_is_positive(new_dentry))
new_vnode = AFS_FS_I(d_inode(new_dentry));
_enter("{%llx:%llu},{%llx:%llu},{%llx:%llu},{%pd}",
orig_dvnode->fid.vid, orig_dvnode->fid.vnode,
@ -1989,6 +2094,11 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
if (ret < 0)
goto error;
ret = -ENOMEM;
op->more_files = kvcalloc(2, sizeof(struct afs_vnode_param), GFP_KERNEL);
if (!op->more_files)
goto error;
afs_op_set_vnode(op, 0, orig_dvnode);
afs_op_set_vnode(op, 1, new_dvnode); /* May be same as orig_dvnode */
op->file[0].dv_delta = 1;
@ -1997,46 +2107,63 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
op->file[1].modification = true;
op->file[0].update_ctime = true;
op->file[1].update_ctime = true;
op->more_files[0].vnode = vnode;
op->more_files[0].speculative = true;
op->more_files[1].vnode = new_vnode;
op->more_files[1].speculative = true;
op->nr_files = 4;
op->dentry = old_dentry;
op->dentry_2 = new_dentry;
op->rename.rename_flags = flags;
op->rename.new_negative = d_is_negative(new_dentry);
op->ops = &afs_rename_operation;
/* For non-directories, check whether the target is busy and if so,
* make a copy of the dentry and then do a silly-rename. If the
* silly-rename succeeds, the copied dentry is hashed and becomes the
* new target.
*/
if (d_is_positive(new_dentry) && !d_is_dir(new_dentry)) {
/* To prevent any new references to the target during the
* rename, we unhash the dentry in advance.
if (flags & RENAME_NOREPLACE) {
op->ops = &afs_rename_noreplace_operation;
} else if (flags & RENAME_EXCHANGE) {
op->ops = &afs_rename_exchange_operation;
d_drop(new_dentry);
} else {
/* If we might displace the target, we might need to do silly
* rename.
*/
if (!d_unhashed(new_dentry)) {
d_drop(new_dentry);
op->rename.rehash = new_dentry;
}
op->ops = &afs_rename_operation;
if (d_count(new_dentry) > 2) {
/* copy the target dentry's name */
op->rename.tmp = d_alloc(new_dentry->d_parent,
&new_dentry->d_name);
if (!op->rename.tmp) {
afs_op_nomem(op);
goto error;
/* For non-directories, check whether the target is busy and if
* so, make a copy of the dentry and then do a silly-rename.
* If the silly-rename succeeds, the copied dentry is hashed
* and becomes the new target.
*/
if (d_is_positive(new_dentry) && !d_is_dir(new_dentry)) {
/* To prevent any new references to the target during
* the rename, we unhash the dentry in advance.
*/
if (!d_unhashed(new_dentry)) {
d_drop(new_dentry);
op->rename.rehash = new_dentry;
}
ret = afs_sillyrename(new_dvnode,
AFS_FS_I(d_inode(new_dentry)),
new_dentry, op->key);
if (ret) {
afs_op_set_error(op, ret);
goto error;
}
if (d_count(new_dentry) > 2) {
/* copy the target dentry's name */
op->rename.tmp = d_alloc(new_dentry->d_parent,
&new_dentry->d_name);
if (!op->rename.tmp) {
afs_op_nomem(op);
goto error;
}
op->dentry_2 = op->rename.tmp;
op->rename.rehash = NULL;
op->rename.new_negative = true;
ret = afs_sillyrename(new_dvnode,
AFS_FS_I(d_inode(new_dentry)),
new_dentry, op->key);
if (ret) {
afs_op_set_error(op, ret);
goto error;
}
op->dentry_2 = op->rename.tmp;
op->rename.rehash = NULL;
op->rename.new_negative = true;
}
}
}
@ -2052,6 +2179,8 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
d_drop(old_dentry);
ret = afs_do_sync_operation(op);
if (ret == -ENOTSUPP)
ret = -EINVAL;
out:
afs_dir_unuse_cookie(orig_dvnode, ret);
if (new_dvnode != orig_dvnode)

View File

@ -522,11 +522,11 @@ error:
}
/*
* Edit a subdirectory that has been moved between directories to update the
* ".." entry.
* Edit an entry in a directory to update the vnode it refers to. This is also
* used to update the ".." entry in a directory.
*/
void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode,
enum afs_edit_dir_reason why)
void afs_edit_dir_update(struct afs_vnode *vnode, const struct qstr *name,
struct afs_vnode *new_dvnode, enum afs_edit_dir_reason why)
{
union afs_xdr_dir_block *block;
union afs_xdr_dirent *de;
@ -557,7 +557,7 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
goto already_invalidated;
slot = afs_dir_scan_block(block, &dotdot_name, b);
slot = afs_dir_scan_block(block, name, b);
if (slot >= 0)
goto found_dirent;
@ -566,7 +566,7 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
/* Didn't find the dirent to clobber. Download the directory again. */
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_nodd,
0, 0, 0, 0, "..");
0, 0, 0, 0, name->name);
afs_invalidate_dir(vnode, afs_dir_invalid_edit_upd_no_dd);
goto out;
@ -576,7 +576,7 @@ found_dirent:
de->u.unique = htonl(new_dvnode->fid.unique);
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_dd, b, slot,
ntohl(de->u.vnode), ntohl(de->u.unique), "..");
ntohl(de->u.vnode), ntohl(de->u.unique), name->name);
kunmap_local(block);
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
@ -589,12 +589,12 @@ out:
already_invalidated:
kunmap_local(block);
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_inval,
0, 0, 0, 0, "..");
0, 0, 0, 0, name->name);
goto out;
error:
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_error,
0, 0, 0, 0, "..");
0, 0, 0, 0, name->name);
goto out;
}

View File

@ -69,6 +69,12 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
if (IS_ERR(op))
return PTR_ERR(op);
op->more_files = kvcalloc(2, sizeof(struct afs_vnode_param), GFP_KERNEL);
if (!op->more_files) {
afs_put_operation(op);
return -ENOMEM;
}
afs_op_set_vnode(op, 0, dvnode);
afs_op_set_vnode(op, 1, dvnode);
op->file[0].dv_delta = 1;
@ -77,6 +83,11 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
op->file[1].modification = true;
op->file[0].update_ctime = true;
op->file[1].update_ctime = true;
op->more_files[0].vnode = AFS_FS_I(d_inode(old));
op->more_files[0].speculative = true;
op->more_files[1].vnode = AFS_FS_I(d_inode(new));
op->more_files[1].speculative = true;
op->nr_files = 4;
op->dentry = old;
op->dentry_2 = new;

View File

@ -562,6 +562,7 @@ struct afs_server {
#define AFS_SERVER_FL_NO_IBULK 17 /* Fileserver doesn't support FS.InlineBulkStatus */
#define AFS_SERVER_FL_NO_RM2 18 /* Fileserver doesn't support YFS.RemoveFile2 */
#define AFS_SERVER_FL_HAS_FS64 19 /* Fileserver supports FS.{Fetch,Store}Data64 */
#define AFS_SERVER_FL_NO_RENAME2 20 /* YFS Fileserver doesn't support enhanced rename */
refcount_t ref; /* Object refcount */
atomic_t active; /* Active user count */
u32 addr_version; /* Address list version */
@ -891,9 +892,10 @@ struct afs_operation {
bool need_rehash;
} unlink;
struct {
struct dentry *rehash;
struct dentry *tmp;
bool new_negative;
struct dentry *rehash;
struct dentry *tmp;
unsigned int rename_flags;
bool new_negative;
} rename;
struct {
struct netfs_io_subrequest *subreq;
@ -1100,8 +1102,8 @@ int afs_single_writepages(struct address_space *mapping,
extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *,
enum afs_edit_dir_reason);
extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason);
void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode,
enum afs_edit_dir_reason why);
void afs_edit_dir_update(struct afs_vnode *vnode, const struct qstr *name,
struct afs_vnode *new_dvnode, enum afs_edit_dir_reason why);
void afs_mkdir_init_dir(struct afs_vnode *dvnode, struct afs_vnode *parent_vnode);
/*
@ -1693,6 +1695,9 @@ extern void yfs_fs_remove_dir(struct afs_operation *);
extern void yfs_fs_link(struct afs_operation *);
extern void yfs_fs_symlink(struct afs_operation *);
extern void yfs_fs_rename(struct afs_operation *);
void yfs_fs_rename_replace(struct afs_operation *op);
void yfs_fs_rename_noreplace(struct afs_operation *op);
void yfs_fs_rename_exchange(struct afs_operation *op);
extern void yfs_fs_store_data(struct afs_operation *);
extern void yfs_fs_setattr(struct afs_operation *);
extern void yfs_fs_get_volume_status(struct afs_operation *);

View File

@ -131,6 +131,7 @@ int afs_abort_to_error(u32 abort_code)
case KRB5_PROG_KEYTYPE_NOSUPP: return -ENOPKG;
case RXGEN_OPCODE: return -ENOTSUPP;
case RX_INVALID_OPERATION: return -ENOTSUPP;
default: return -EREMOTEIO;
}

View File

@ -50,6 +50,9 @@ enum YFS_FS_Operations {
YFSREMOVEACL = 64171,
YFSREMOVEFILE2 = 64173,
YFSSTOREOPAQUEACL2 = 64174,
YFSRENAME_REPLACE = 64176,
YFSRENAME_NOREPLACE = 64177,
YFSRENAME_EXCHANGE = 64187,
YFSINLINEBULKSTATUS = 64536, /* YFS Fetch multiple file statuses with errors */
YFSFETCHDATA64 = 64537, /* YFS Fetch file data */
YFSSTOREDATA64 = 64538, /* YFS Store file data */

View File

@ -432,6 +432,16 @@ bool afs_select_fileserver(struct afs_operation *op)
afs_op_set_error(op, -EDQUOT);
goto failed_but_online;
case RX_INVALID_OPERATION:
case RXGEN_OPCODE:
/* Handle downgrading to an older operation. */
afs_op_set_error(op, -ENOTSUPP);
if (op->flags & AFS_OPERATION_DOWNGRADE) {
op->flags &= ~AFS_OPERATION_DOWNGRADE;
goto go_again;
}
goto failed_but_online;
default:
afs_op_accumulate_error(op, error, abort_code);
failed_but_online:
@ -620,12 +630,13 @@ iterate_address:
op->addr_index = addr_index;
set_bit(addr_index, &op->addr_tried);
op->volsync.creation = TIME64_MIN;
op->volsync.update = TIME64_MIN;
op->call_responded = false;
_debug("address [%u] %u/%u %pISp",
op->server_index, addr_index, alist->nr_addrs,
rxrpc_kernel_remote_addr(alist->addrs[op->addr_index].peer));
go_again:
op->volsync.creation = TIME64_MIN;
op->volsync.update = TIME64_MIN;
op->call_responded = false;
_leave(" = t");
return true;

View File

@ -1042,6 +1042,9 @@ void yfs_fs_rename(struct afs_operation *op)
_enter("");
if (!test_bit(AFS_SERVER_FL_NO_RENAME2, &op->server->flags))
return yfs_fs_rename_replace(op);
call = afs_alloc_flat_call(op->net, &yfs_RXYFSRename,
sizeof(__be32) +
sizeof(struct yfs_xdr_RPCFlags) +
@ -1070,6 +1073,252 @@ void yfs_fs_rename(struct afs_operation *op)
afs_make_op_call(op, call, GFP_NOFS);
}
/*
* Deliver reply data to a YFS.Rename_NoReplace operation. This does not
* return the status of a displaced target inode as there cannot be one.
*/
static int yfs_deliver_fs_rename_1(struct afs_call *call)
{
struct afs_operation *op = call->op;
struct afs_vnode_param *orig_dvp = &op->file[0];
struct afs_vnode_param *new_dvp = &op->file[1];
struct afs_vnode_param *old_vp = &op->more_files[0];
const __be32 *bp;
int ret;
_enter("{%u}", call->unmarshall);
ret = afs_transfer_reply(call);
if (ret < 0)
return ret;
bp = call->buffer;
/* If the two dirs are the same, we have two copies of the same status
* report, so we just decode it twice.
*/
xdr_decode_YFSFetchStatus(&bp, call, &orig_dvp->scb);
xdr_decode_YFSFid(&bp, &old_vp->fid);
xdr_decode_YFSFetchStatus(&bp, call, &old_vp->scb);
xdr_decode_YFSFetchStatus(&bp, call, &new_dvp->scb);
xdr_decode_YFSVolSync(&bp, &op->volsync);
_leave(" = 0 [done]");
return 0;
}
/*
* Deliver reply data to a YFS.Rename_Replace or a YFS.Rename_Exchange
* operation. These return the status of the displaced target inode if there
* was one.
*/
static int yfs_deliver_fs_rename_2(struct afs_call *call)
{
struct afs_operation *op = call->op;
struct afs_vnode_param *orig_dvp = &op->file[0];
struct afs_vnode_param *new_dvp = &op->file[1];
struct afs_vnode_param *old_vp = &op->more_files[0];
struct afs_vnode_param *new_vp = &op->more_files[1];
const __be32 *bp;
int ret;
_enter("{%u}", call->unmarshall);
ret = afs_transfer_reply(call);
if (ret < 0)
return ret;
bp = call->buffer;
/* If the two dirs are the same, we have two copies of the same status
* report, so we just decode it twice.
*/
xdr_decode_YFSFetchStatus(&bp, call, &orig_dvp->scb);
xdr_decode_YFSFid(&bp, &old_vp->fid);
xdr_decode_YFSFetchStatus(&bp, call, &old_vp->scb);
xdr_decode_YFSFetchStatus(&bp, call, &new_dvp->scb);
xdr_decode_YFSFid(&bp, &new_vp->fid);
xdr_decode_YFSFetchStatus(&bp, call, &new_vp->scb);
xdr_decode_YFSVolSync(&bp, &op->volsync);
_leave(" = 0 [done]");
return 0;
}
static void yfs_done_fs_rename_replace(struct afs_call *call)
{
if (call->error == -ECONNABORTED &&
(call->abort_code == RX_INVALID_OPERATION ||
call->abort_code == RXGEN_OPCODE)) {
set_bit(AFS_SERVER_FL_NO_RENAME2, &call->op->server->flags);
call->op->flags |= AFS_OPERATION_DOWNGRADE;
}
}
/*
* YFS.Rename_Replace operation type
*/
static const struct afs_call_type yfs_RXYFSRename_Replace = {
.name = "FS.Rename_Replace",
.op = yfs_FS_Rename_Replace,
.deliver = yfs_deliver_fs_rename_2,
.done = yfs_done_fs_rename_replace,
.destructor = afs_flat_call_destructor,
};
/*
* YFS.Rename_NoReplace operation type
*/
static const struct afs_call_type yfs_RXYFSRename_NoReplace = {
.name = "FS.Rename_NoReplace",
.op = yfs_FS_Rename_NoReplace,
.deliver = yfs_deliver_fs_rename_1,
.destructor = afs_flat_call_destructor,
};
/*
* YFS.Rename_Exchange operation type
*/
static const struct afs_call_type yfs_RXYFSRename_Exchange = {
.name = "FS.Rename_Exchange",
.op = yfs_FS_Rename_Exchange,
.deliver = yfs_deliver_fs_rename_2,
.destructor = afs_flat_call_destructor,
};
/*
* Rename a file or directory, replacing the target if it exists. The status
* of a displaced target is returned.
*/
void yfs_fs_rename_replace(struct afs_operation *op)
{
struct afs_vnode_param *orig_dvp = &op->file[0];
struct afs_vnode_param *new_dvp = &op->file[1];
const struct qstr *orig_name = &op->dentry->d_name;
const struct qstr *new_name = &op->dentry_2->d_name;
struct afs_call *call;
__be32 *bp;
_enter("");
call = afs_alloc_flat_call(op->net, &yfs_RXYFSRename_Replace,
sizeof(__be32) +
sizeof(struct yfs_xdr_RPCFlags) +
sizeof(struct yfs_xdr_YFSFid) +
xdr_strlen(orig_name->len) +
sizeof(struct yfs_xdr_YFSFid) +
xdr_strlen(new_name->len),
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFid) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFid) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSVolSync));
if (!call)
return afs_op_nomem(op);
/* Marshall the parameters. */
bp = call->request;
bp = xdr_encode_u32(bp, YFSRENAME_REPLACE);
bp = xdr_encode_u32(bp, 0); /* RPC flags */
bp = xdr_encode_YFSFid(bp, &orig_dvp->fid);
bp = xdr_encode_name(bp, orig_name);
bp = xdr_encode_YFSFid(bp, &new_dvp->fid);
bp = xdr_encode_name(bp, new_name);
yfs_check_req(call, bp);
call->fid = orig_dvp->fid;
trace_afs_make_fs_call2(call, &orig_dvp->fid, orig_name, new_name);
afs_make_op_call(op, call, GFP_NOFS);
}
/*
* Rename a file or directory, failing if the target dirent exists.
*/
void yfs_fs_rename_noreplace(struct afs_operation *op)
{
struct afs_vnode_param *orig_dvp = &op->file[0];
struct afs_vnode_param *new_dvp = &op->file[1];
const struct qstr *orig_name = &op->dentry->d_name;
const struct qstr *new_name = &op->dentry_2->d_name;
struct afs_call *call;
__be32 *bp;
_enter("");
call = afs_alloc_flat_call(op->net, &yfs_RXYFSRename_NoReplace,
sizeof(__be32) +
sizeof(struct yfs_xdr_RPCFlags) +
sizeof(struct yfs_xdr_YFSFid) +
xdr_strlen(orig_name->len) +
sizeof(struct yfs_xdr_YFSFid) +
xdr_strlen(new_name->len),
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFid) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSVolSync));
if (!call)
return afs_op_nomem(op);
/* Marshall the parameters. */
bp = call->request;
bp = xdr_encode_u32(bp, YFSRENAME_NOREPLACE);
bp = xdr_encode_u32(bp, 0); /* RPC flags */
bp = xdr_encode_YFSFid(bp, &orig_dvp->fid);
bp = xdr_encode_name(bp, orig_name);
bp = xdr_encode_YFSFid(bp, &new_dvp->fid);
bp = xdr_encode_name(bp, new_name);
yfs_check_req(call, bp);
call->fid = orig_dvp->fid;
trace_afs_make_fs_call2(call, &orig_dvp->fid, orig_name, new_name);
afs_make_op_call(op, call, GFP_NOFS);
}
/*
* Exchange a pair of files directories.
*/
void yfs_fs_rename_exchange(struct afs_operation *op)
{
struct afs_vnode_param *orig_dvp = &op->file[0];
struct afs_vnode_param *new_dvp = &op->file[1];
const struct qstr *orig_name = &op->dentry->d_name;
const struct qstr *new_name = &op->dentry_2->d_name;
struct afs_call *call;
__be32 *bp;
_enter("");
call = afs_alloc_flat_call(op->net, &yfs_RXYFSRename_Exchange,
sizeof(__be32) +
sizeof(struct yfs_xdr_RPCFlags) +
sizeof(struct yfs_xdr_YFSFid) +
xdr_strlen(orig_name->len) +
sizeof(struct yfs_xdr_YFSFid) +
xdr_strlen(new_name->len),
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFid) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSFid) +
sizeof(struct yfs_xdr_YFSFetchStatus) +
sizeof(struct yfs_xdr_YFSVolSync));
if (!call)
return afs_op_nomem(op);
/* Marshall the parameters. */
bp = call->request;
bp = xdr_encode_u32(bp, YFSRENAME_EXCHANGE);
bp = xdr_encode_u32(bp, 0); /* RPC flags */
bp = xdr_encode_YFSFid(bp, &orig_dvp->fid);
bp = xdr_encode_name(bp, orig_name);
bp = xdr_encode_YFSFid(bp, &new_dvp->fid);
bp = xdr_encode_name(bp, new_name);
yfs_check_req(call, bp);
call->fid = orig_dvp->fid;
trace_afs_make_fs_call2(call, &orig_dvp->fid, orig_name, new_name);
afs_make_op_call(op, call, GFP_NOFS);
}
/*
* YFS.StoreData64 operation type.
*/

View File

@ -2922,6 +2922,7 @@ void d_exchange(struct dentry *dentry1, struct dentry *dentry2)
write_sequnlock(&rename_lock);
}
EXPORT_SYMBOL(d_exchange);
/**
* d_ancestor - search for an ancestor

View File

@ -69,6 +69,9 @@ enum afs_fs_operation {
yfs_FS_RemoveACL = 64171,
yfs_FS_RemoveFile2 = 64173,
yfs_FS_StoreOpaqueACL2 = 64174,
yfs_FS_Rename_Replace = 64176,
yfs_FS_Rename_NoReplace = 64177,
yfs_FS_Rename_Exchange = 64187,
yfs_FS_InlineBulkStatus = 64536, /* YFS Fetch multiple file statuses with errors */
yfs_FS_FetchData64 = 64537, /* YFS Fetch file data */
yfs_FS_StoreData64 = 64538, /* YFS Store file data */
@ -300,6 +303,9 @@ enum yfs_cm_operation {
EM(yfs_FS_RemoveACL, "YFS.RemoveACL") \
EM(yfs_FS_RemoveFile2, "YFS.RemoveFile2") \
EM(yfs_FS_StoreOpaqueACL2, "YFS.StoreOpaqueACL2") \
EM(yfs_FS_Rename_Replace, "YFS.Rename_Replace") \
EM(yfs_FS_Rename_NoReplace, "YFS.Rename_NoReplace") \
EM(yfs_FS_Rename_Exchange, "YFS.Rename_Exchange") \
EM(yfs_FS_InlineBulkStatus, "YFS.InlineBulkStatus") \
EM(yfs_FS_FetchData64, "YFS.FetchData64") \
EM(yfs_FS_StoreData64, "YFS.StoreData64") \