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:
commit
5484a4ea7a
223
fs/afs/dir.c
223
fs/afs/dir.c
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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") \
|
||||
|
|
Loading…
Reference in New Issue