Commit e5fef2a9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-next-20190507' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS updates from David Howells:
 "A set of fix and development patches for AFS for 5.2.

  Summary:

   - Fix the AFS file locking so that sqlite can run on an AFS mount and
     also so that firefox and gnome can use a homedir that's mounted
     through AFS.

     This required emulation of fine-grained locking when the server
     will only support whole-file locks and no upgrade/downgrade. Four
     modes are provided, settable by mount parameter:

       "flock=local"   - No reference to the server

       "flock=openafs" - Fine-grained locks are local-only, whole-file
                         locks require sufficient server locks

       "flock=strict"  - All locks require sufficient server locks

       "flock=write"   - Always get an exclusive server lock

     If the volume is a read-only or backup volume, then flock=local for
     that volume.

   - Log extra information for a couple of cases where the client mucks
     up somehow: AFS vnode with undefined type and dir check failure -
     in both cases we seem to end up with unfilled data, but the issues
     happen infrequently and are difficult to reproduce at will.

   - Implement silly rename for unlink() and rename().

   - Set i_blocks so that du can get some information about usage.

   - Fix xattr handlers to return the right amount of data and to not
     overflow buffers.

   - Implement getting/setting raw AFS and YFS ACLs as xattrs"

* tag 'afs-next-20190507' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Implement YFS ACL setting
  afs: Get YFS ACLs and information through xattrs
  afs: implement acl setting
  afs: Get an AFS3 ACL as an xattr
  afs: Fix getting the afs.fid xattr
  afs: Fix the afs.cell and afs.volume xattr handlers
  afs: Calculate i_blocks based on file size
  afs: Log more information for "kAFS: AFS vnode with undefined type\n"
  afs: Provide mount-time configurable byte-range file locking emulation
  afs: Add more tracepoints
  afs: Implement sillyrename for unlink and rename
  afs: Add directory reload tracepoint
  afs: Handle lock rpc ops failing on a file that got deleted
  afs: Improve dir check failure reports
  afs: Add file locking tracepoints
  afs: Further fix file locking
  afs: Fix AFS file locking to allow fine grained locks
  afs: Calculate lock extend timer from set/extend reply reception
  afs: Split wait from afs_make_call()
parents 149e703c f5e45463
......@@ -13,6 +13,7 @@ kafs-y := \
cmservice.o \
dir.o \
dir_edit.o \
dir_silly.o \
dynroot.o \
file.o \
flock.o \
......
......@@ -17,8 +17,10 @@
enum AFS_FS_Operations {
FSFETCHDATA = 130, /* AFS Fetch file data */
FSFETCHACL = 131, /* AFS Fetch file ACL */
FSFETCHSTATUS = 132, /* AFS Fetch file status */
FSSTOREDATA = 133, /* AFS Store file data */
FSSTOREACL = 134, /* AFS Store file ACL */
FSSTORESTATUS = 135, /* AFS Store file status */
FSREMOVEFILE = 136, /* AFS Remove a file */
FSCREATEFILE = 137, /* AFS Create a file */
......
......@@ -26,6 +26,7 @@ static int afs_dir_open(struct inode *inode, struct file *file);
static int afs_readdir(struct file *file, struct dir_context *ctx);
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
static int afs_d_delete(const struct dentry *dentry);
static void afs_d_iput(struct dentry *dentry, struct inode *inode);
static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
loff_t fpos, u64 ino, unsigned dtype);
static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen,
......@@ -85,6 +86,7 @@ const struct dentry_operations afs_fs_dentry_operations = {
.d_delete = afs_d_delete,
.d_release = afs_d_release,
.d_automount = afs_d_automount,
.d_iput = afs_d_iput,
};
struct afs_lookup_one_cookie {
......@@ -159,6 +161,38 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page,
return false;
}
/*
* Check the contents of a directory that we've just read.
*/
static bool afs_dir_check_pages(struct afs_vnode *dvnode, struct afs_read *req)
{
struct afs_xdr_dir_page *dbuf;
unsigned int i, j, qty = PAGE_SIZE / sizeof(union afs_xdr_dir_block);
for (i = 0; i < req->nr_pages; i++)
if (!afs_dir_check_page(dvnode, req->pages[i], req->actual_len))
goto bad;
return true;
bad:
pr_warn("DIR %llx:%llx f=%llx l=%llx al=%llx r=%llx\n",
dvnode->fid.vid, dvnode->fid.vnode,
req->file_size, req->len, req->actual_len, req->remain);
pr_warn("DIR %llx %x %x %x\n",
req->pos, req->index, req->nr_pages, req->offset);
for (i = 0; i < req->nr_pages; i++) {
dbuf = kmap(req->pages[i]);
for (j = 0; j < qty; j++) {
union afs_xdr_dir_block *block = &dbuf->blocks[j];
pr_warn("[%02x] %32phN\n", i * qty + j, block);
}
kunmap(req->pages[i]);
}
return false;
}
/*
* open an AFS directory file
*/
......@@ -277,6 +311,7 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
goto error;
if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
trace_afs_reload_dir(dvnode);
ret = afs_fetch_data(dvnode, key, req);
if (ret < 0)
goto error_unlock;
......@@ -288,10 +323,8 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
/* Validate the data we just read. */
ret = -EIO;
for (i = 0; i < req->nr_pages; i++)
if (!afs_dir_check_page(dvnode, req->pages[i],
req->actual_len))
goto error_unlock;
if (!afs_dir_check_pages(dvnode, req))
goto error_unlock;
// TODO: Trim excess pages
......@@ -743,7 +776,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
ti = afs_iget(dir->i_sb, key, &cookie->fids[i],
&cookie->statuses[i],
&cookie->callbacks[i],
cbi);
cbi, dvnode);
if (i == 0) {
inode = ti;
} else {
......@@ -875,8 +908,14 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
(void *)(unsigned long)dvnode->status.data_version;
}
d = d_splice_alias(inode, dentry);
if (!IS_ERR_OR_NULL(d))
if (!IS_ERR_OR_NULL(d)) {
d->d_fsdata = dentry->d_fsdata;
trace_afs_lookup(dvnode, &d->d_name,
inode ? AFS_FS_I(inode) : NULL);
} else {
trace_afs_lookup(dvnode, &dentry->d_name,
inode ? AFS_FS_I(inode) : NULL);
}
return d;
}
......@@ -1052,6 +1091,16 @@ static int afs_d_delete(const struct dentry *dentry)
return 1;
}
/*
* Clean up sillyrename files on dentry removal.
*/
static void afs_d_iput(struct dentry *dentry, struct inode *inode)
{
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
afs_silly_iput(dentry, inode);
iput(inode);
}
/*
* handle dentry release
*/
......@@ -1076,7 +1125,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);
newfid, newstatus, newcb, fc->cbi, fc->vnode);
if (IS_ERR(inode)) {
/* ENOMEM or EINTR at a really inconvenient time - just abandon
* the new directory on the server.
......@@ -1194,6 +1243,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
goto error_key;
}
if (vnode) {
ret = down_write_killable(&vnode->rmdir_lock);
if (ret < 0)
goto error_key;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) {
......@@ -1212,6 +1267,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
}
}
if (vnode)
up_write(&vnode->rmdir_lock);
error_key:
key_put(key);
error:
......@@ -1228,9 +1285,9 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
* 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...
*/
static int afs_dir_remove_link(struct dentry *dentry, struct key *key,
unsigned long d_version_before,
unsigned long d_version_after)
int afs_dir_remove_link(struct dentry *dentry, struct key *key,
unsigned long d_version_before,
unsigned long d_version_after)
{
bool dir_valid;
int ret = 0;
......@@ -1277,6 +1334,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
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;
......@@ -1300,6 +1358,21 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
goto error_key;
}
spin_lock(&dentry->d_lock);
if (vnode && d_count(dentry) > 1) {
spin_unlock(&dentry->d_lock);
/* Start asynchronous writeout of the inode */
write_inode_now(d_inode(dentry), 0);
ret = afs_sillyrename(dvnode, vnode, dentry, key);
goto error_key;
}
if (!d_unhashed(dentry)) {
/* Prevent a race with RCU lookup. */
__d_drop(dentry);
need_rehash = true;
}
spin_unlock(&dentry->d_lock);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) {
......@@ -1331,6 +1404,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
afs_edit_dir_for_unlink);
}
if (need_rehash && ret < 0 && ret != -ENOENT)
d_rehash(dentry);
error_key:
key_put(key);
error:
......@@ -1551,6 +1627,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
{
struct afs_fs_cursor fc;
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);
......@@ -1559,6 +1637,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (flags)
return -EINVAL;
/* Don't allow silly-rename files be moved around. */
if (old_dentry->d_flags & DCACHE_NFSFS_RENAMED)
return -EINVAL;
vnode = AFS_FS_I(d_inode(old_dentry));
orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir);
......@@ -1577,12 +1659,48 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
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);
rehash = new_dentry;
}
if (d_count(new_dentry) > 2) {
/* copy the target dentry's name */
ret = -ENOMEM;
tmp = d_alloc(new_dentry->d_parent,
&new_dentry->d_name);
if (!tmp)
goto error_rehash;
ret = afs_sillyrename(new_dvnode,
AFS_FS_I(d_inode(new_dentry)),
new_dentry, key);
if (ret)
goto error_rehash;
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)) {
if (orig_dvnode != new_dvnode) {
if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc);
goto error_key;
goto error_rehash;
}
}
while (afs_select_fileserver(&fc)) {
......@@ -1599,25 +1717,42 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
mutex_unlock(&new_dvnode->io_lock);
ret = afs_end_vnode_operation(&fc);
if (ret < 0)
goto error_key;
goto error_rehash;
}
if (ret == 0) {
if (rehash)
d_rehash(rehash);
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags))
afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
afs_edit_dir_for_rename);
afs_edit_dir_for_rename_0);
if (!new_negative &&
test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
afs_edit_dir_for_rename);
afs_edit_dir_for_rename_1);
if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_add(new_dvnode, &new_dentry->d_name,
&vnode->fid, afs_edit_dir_for_rename);
&vnode->fid, afs_edit_dir_for_rename_2);
new_inode = d_inode(new_dentry);
if (new_inode) {
spin_lock(&new_inode->i_lock);
if (new_inode->i_nlink > 0)
drop_nlink(new_inode);
spin_unlock(&new_inode->i_lock);
}
d_move(old_dentry, new_dentry);
goto error_tmp;
}
error_key:
error_rehash:
if (rehash)
d_rehash(rehash);
error_tmp:
if (tmp)
dput(tmp);
key_put(key);
error:
_leave(" = %d", ret);
......
/* AFS silly rename handling
*
* Copyright (C) 2019 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* - Derived from NFS's sillyrename.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/fsnotify.h>
#include "internal.h"
/*
* Actually perform the silly rename step.
*/
static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
struct dentry *old, struct dentry *new,
struct key *key)
{
struct afs_fs_cursor fc;
u64 dir_data_version = dvnode->status.data_version;
int ret = -ERESTARTSYS;
_enter("%pd,%pd", old, new);
trace_afs_silly_rename(vnode, false);
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
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);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}
if (ret == 0) {
spin_lock(&old->d_lock);
old->d_flags |= DCACHE_NFSFS_RENAMED;
spin_unlock(&old->d_lock);
if (dvnode->silly_key != key) {
key_put(dvnode->silly_key);
dvnode->silly_key = key_get(key);
}
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &old->d_name,
afs_edit_dir_for_silly_0);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &new->d_name,
&vnode->fid, afs_edit_dir_for_silly_1);
/* vfs_unlink and the like do not issue this when a file is
* sillyrenamed, so do it here.
*/
fsnotify_nameremove(old, 0);
}
_leave(" = %d", ret);
return ret;
}
/**
* afs_sillyrename - Perform a silly-rename of a dentry
*
* AFS is stateless and the server doesn't know when the client is holding a
* file open. To prevent application problems when a file is unlinked while
* it's still open, the client performs a "silly-rename". That is, it renames
* the file to a hidden file in the same directory, and only performs the
* unlink once the last reference to it is put.
*
* The final cleanup is done during dentry_iput.
*/
int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
struct dentry *dentry, struct key *key)
{
static unsigned int sillycounter;
struct dentry *sdentry = NULL;
unsigned char silly[16];
int ret = -EBUSY;
_enter("");
/* We don't allow a dentry to be silly-renamed twice. */
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
return -EBUSY;
sdentry = NULL;
do {
int slen;
dput(sdentry);
sillycounter++;
/* Create a silly name. Note that the ".__afs" prefix is
* understood by the salvager and must not be changed.
*/
slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter);
sdentry = lookup_one_len(silly, dentry->d_parent, slen);
/* N.B. Better to return EBUSY here ... it could be dangerous
* to delete the file while it's in use.
*/
if (IS_ERR(sdentry))
goto out;
} while (!d_is_negative(sdentry));
ihold(&vnode->vfs_inode);
ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key);
switch (ret) {
case 0:
/* The rename succeeded. */
d_move(dentry, sdentry);
break;
case -ERESTARTSYS:
/* The result of the rename is unknown. Play it safe by forcing
* a new lookup.
*/
d_drop(dentry);
d_drop(sdentry);
}
iput(&vnode->vfs_inode);
dput(sdentry);
out:
_leave(" = %d", ret);
return ret;
}
/*
* Tell the server to remove a sillyrename file.
*/
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;
int ret = -ERESTARTSYS;
_enter("");
trace_afs_silly_rename(vnode, true);
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
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);
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_vnode_commit_status(&fc, dvnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
if (ret == 0) {
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);
}
}
if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);
}
_leave(" = %d", ret);
return ret;
}
/*
* Remove sillyrename file on iput.
*/
int afs_silly_iput(struct dentry *dentry, struct inode *inode)
{
struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent));
struct afs_vnode *vnode = AFS_FS_I(inode);
struct dentry *alias;
int ret;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
_enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
down_read(&dvnode->rmdir_lock);
alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq);
if (IS_ERR(alias)) {
up_read(&dvnode->rmdir_lock);
return 0;
}
if (!d_in_lookup(alias)) {
/* We raced with lookup... See if we need to transfer the
* sillyrename information to the aliased dentry.
*/
ret = 0;
spin_lock(&alias->d_lock);
if (d_really_is_positive(alias) &&
!(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
alias->d_flags |= DCACHE_NFSFS_RENAMED;
ret = 1;
}
spin_unlock(&alias->d_lock);
up_read(&dvnode->rmdir_lock);
dput(alias);
return ret;
}
/* Stop lock-release from complaining. */
spin_lock(&vnode->lock);
vnode->lock_state = AFS_VNODE_LOCK_DELETED;
trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0);
spin_unlock(&vnode->lock);
afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key);
up_read(&dvnode->rmdir_lock);
d_lookup_done(alias);
dput(alias);
return 1;
}
This diff is collapsed.
......@@ -141,8 +141,8 @@ static int afs_do_probe_fileserver(struct afs_net *net,
struct afs_addr_cursor ac = {
.index = 0,
};
struct afs_call *call;
bool in_progress = false;
int err;
_enter("%pU", &server->uuid);
......@@ -156,12 +156,13 @@ static int afs_do_probe_fileserver(struct afs_net *net,
server->probe.rtt = UINT_MAX;
for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
err = afs_fs_get_capabilities(net, server, &ac, key, server_index,
true);
if (err == -EINPROGRESS)
call = afs_fs_get_capabilities(net, server, &ac, key, server_index);
if (!IS_ERR(call)) {
afs_put_call(call);
in_progress = true;
else
afs_prioritise_error(_e, err, ac.abort_code);
} else {
afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
}
}
if (!in_progress)
......
This diff is collapsed.
......@@ -29,10 +29,36 @@ static const struct inode_operations afs_symlink_inode_operations = {
.listxattr = afs_listxattr,
};
static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *parent_vnode)
{
static unsigned long once_only;
pr_warn("kAFS: AFS vnode with undefined type %u\n",
vnode->status.type);
pr_warn("kAFS: A=%d m=%o s=%llx v=%llx\n",
vnode->status.abort_code,
vnode->status.mode,
vnode->status.size,
vnode->status.data_version);
pr_warn("kAFS: vnode %llx:%llx:%x\n",
vnode->fid.vid,
vnode->fid.vnode,
vnode->fid.unique);
if (parent_vnode)
pr_warn("kAFS: dir %llx:%llx:%x\n",
parent_vnode->fid.vid,
parent_vnode->fid.vnode,
parent_vnode->fid.unique);
if (!test_and_set_bit(0, &once_only))
dump_stack();
}
/*
* Initialise an inode from the vnode status.
*/
static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key)
static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
struct afs_vnode *parent_vnode)
{
struct inode *inode = AFS_VNODE_TO_I(vnode);
......@@ -80,12 +106,16 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key)
inode_nohighmem(inode);
break;
default:
printk("kAFS: AFS vnode with undefined type\n");
dump_vnode(vnode, parent_vnode);
read_sequnlock_excl(&vnode->cb_lock);
return afs_protocol_error(NULL, -EBADMSG, afs_eproto_file_type);
}
inode->i_blocks = 0;
/*
* Estimate 512 bytes blocks used, rounded up to nearest 1K
* for consistency with other AFS clients.
*/
inode->i_blocks = ((i_size_read(inode) + 1023) >> 10) << 1;
vnode->invalid_before = vnode->status.data_version;
read_sequnlock_excl(&vnode->cb_lock);
......@@ -270,7 +300,8 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
*/
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_callback *cb, struct afs_cb_interest *cbi,
struct afs_vnode *parent_vnode)
{
struct afs_iget_data data = { .fid = *fid };
struct afs_super_info *as;
......@@ -327,7 +358,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
vnode->cb_expires_at += ktime_get_real_seconds();
}
ret = afs_inode_init_from_status(vnode, key);
ret = afs_inode_init_from_status(vnode, key, parent_vnode);
if (ret < 0)
goto bad_inode;
......@@ -543,6 +574,8 @@ void afs_evict_inode(struct inode *inode)
#endif
afs_put_permits(rcu_access_pointer(vnode->permit_cache));
key_put(vnode->silly_key);
vnode->silly_key = NULL;
key_put(vnode->lock_key);
vnode->lock_key = NULL;
_leave("");
......
......@@ -36,11 +36,24 @@
struct pagevec;
struct afs_call;
/*
* Partial file-locking emulation mode. (The problem being that AFS3 only
* allows whole-file locks and no upgrading/downgrading).
*/
enum afs_flock_mode {
afs_flock_mode_unset,
afs_flock_mode_local, /* Local locking only */
afs_flock_mode_openafs, /* Don't get server lock for a partial lock */
afs_flock_mode_strict, /* Always get a server lock for a partial lock */
afs_flock_mode_write, /* Get an exclusive server lock for a partial lock */
};
struct afs_fs_context {
bool force; /* T to force cell type */
bool autocell; /* T if set auto mount operation */
bool dyn_root; /* T if dynamic root */
bool no_cell; /* T if the source is "none" (for dynroot) */
enum afs_flock_mode flock_mode; /* Partial file-locking emulation mode */
afs_voltype_t type; /* type of volume requested */
unsigned int volnamesz; /* size of volume name */
const char *volname; /* name of volume to mount */
......@@ -221,6 +234,7 @@ struct afs_super_info {
struct net *net_ns; /* Network namespace */
struct afs_cell *cell; /* The cell in which the volume resides */
struct afs_volume *volume; /* volume record */
enum afs_flock_mode flock_mode:8; /* File locking emulation mode */
bool dyn_root; /* True if dynamic root */
};
......@@ -599,6 +613,7 @@ enum afs_lock_state {
AFS_VNODE_LOCK_EXTENDING, /* We're extending a lock on the server */
AFS_VNODE_LOCK_NEED_UNLOCK, /* We need to unlock on the server */
AFS_VNODE_LOCK_UNLOCKING, /* We're telling the server to unlock */
AFS_VNODE_LOCK_DELETED, /* The vnode has been deleted whilst we have a lock */
};
/*
......@@ -620,6 +635,8 @@ struct afs_vnode {
struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */
struct mutex io_lock; /* Lock for serialising I/O on this mutex */
struct rw_semaphore validate_lock; /* lock for validating this vnode */
struct rw_semaphore rmdir_lock; /* Lock for rmdir vs sillyrename */
struct key *silly_key; /* Silly rename key */
spinlock_t wb_lock; /* lock for wb_keys */
spinlock_t lock; /* waitqueue/flags lock */
unsigned long flags;
......@@ -638,6 +655,7 @@ struct afs_vnode {
struct list_head granted_locks; /* locks granted on this file */
struct delayed_work lock_work; /* work to be done in locking */
struct key *lock_key; /* Key to be used in lock ops */
ktime_t locked_at; /* Time at which lock obtained */
enum afs_lock_state lock_state : 8;
afs_lock_type_t lock_type : 8;
......@@ -864,6 +882,7 @@ 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
......@@ -872,6 +891,13 @@ 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);
/*
* dir_silly.c
*/
extern int afs_sillyrename(struct afs_vnode *, struct afs_vnode *,
struct dentry *, struct key *);
extern int afs_silly_iput(struct dentry *, struct inode *);
/*
* dynroot.c
*/
......@@ -905,6 +931,7 @@ extern void afs_put_read(struct afs_read *);
*/
extern struct workqueue_struct *afs_lock_manager;
extern void afs_lock_op_done(struct afs_call *);
extern void afs_lock_work(struct work_struct *);
extern void afs_lock_may_be_available(struct afs_vnode *);
extern int afs_lock(struct file *, int, struct file_lock *);
......@@ -939,8 +966,9 @@ extern int afs_fs_extend_lock(struct afs_fs_cursor *);
extern int afs_fs_release_lock(struct afs_fs_cursor *);
extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *);
extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *, unsigned int, bool);
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,
......@@ -949,6 +977,14 @@ 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_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 *);
/*
* fs_probe.c
*/
......@@ -965,7 +1001,8 @@ 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_cb_interest *);
struct afs_cb_interest *,
struct afs_vnode *);
extern void afs_zap_data(struct afs_vnode *);
extern int afs_validate(struct afs_vnode *, struct key *);
extern int afs_getattr(const struct path *, struct kstat *, u32, unsigned int);
......@@ -1073,7 +1110,8 @@ extern int __net_init afs_open_socket(struct afs_net *);
extern void __net_exit afs_close_socket(struct afs_net *);
extern void afs_charge_preallocation(struct work_struct *);
extern void afs_put_call(struct afs_call *);
extern long afs_make_call(struct afs_addr_cursor *, struct afs_call *, gfp_t, bool);
extern void afs_make_call(struct afs_addr_cursor *, struct afs_call *, gfp_t);
extern long afs_wait_for_call_to_complete(struct afs_call *, struct afs_addr_cursor *);
extern struct afs_call *afs_alloc_flat_call(struct afs_net *,
const struct afs_call_type *,
size_t, size_t);
......@@ -1218,8 +1256,8 @@ extern void afs_fs_exit(void);
extern struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *,
const char *, int);
extern struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *, const uuid_t *);
extern int afs_vl_get_capabilities(struct afs_net *, struct afs_addr_cursor *, struct key *,
struct afs_vlserver *, unsigned int, bool);
extern struct afs_call *afs_vl_get_capabilities(struct afs_net *, struct afs_addr_cursor *,
struct key *, struct afs_vlserver *, unsigned int);
extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *, const uuid_t *);
/*
......@@ -1333,6 +1371,20 @@ extern int yfs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_callback *, unsigned int,
struct afs_volsync *);
struct yfs_acl {
struct afs_acl *acl; /* Dir/file/symlink ACL */
struct afs_acl *vol_acl; /* Whole volume ACL */
u32 inherit_flag; /* True if ACL is inherited from parent dir */
u32 num_cleaned; /* Number of ACEs removed due to subject removal */
unsigned int flags;
#define YFS_ACL_WANT_ACL 0x01 /* Set if caller wants ->acl */
#define YFS_ACL_WANT_VOL_ACL 0x02 /* Set if caller wants ->vol_acl */
};
extern void yfs_free_opaque_acl(struct yfs_acl *);
extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *, unsigned int);
extern int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *, const struct afs_acl *);
/*
* Miscellaneous inline functions.
*/
......
......@@ -31,9 +31,9 @@ enum YFS_CM_Operations {
};
enum YFS_FS_Operations {
YFSFETCHACL = 64131, /* YFS Fetch file ACL */
YFSFETCHACL = 64131, /* YFS Fetch file AFS3 ACL */
YFSFETCHSTATUS = 64132, /* YFS Fetch file status */
YFSSTOREACL = 64134, /* YFS Store file ACL */
YFSSTOREACL = 64134, /* YFS Store file AFS3 ACL */
YFSSTORESTATUS = 64135, /* YFS Store file status */
YFSREMOVEFILE = 64136, /* YFS Remove a file */
YFSCREATEFILE = 64137, /* YFS Create a file */
......@@ -49,7 +49,7 @@ enum YFS_FS_Operations {
YFSRELEASELOCK = 64158, /* YFS Release a file lock */
YFSLOOKUP = 64161, /* YFS lookup file in directory */
YFSFLUSHCPS = 64165,
YFSFETCHOPAQUEACL = 64168,
YFSFETCHOPAQUEACL = 64168, /* YFS Fetch file YFS ACL */
YFSWHOAMI = 64170,
YFSREMOVEACL = 64171,
YFSREMOVEFILE2 = 64173,
......
......@@ -21,7 +21,6 @@
struct workqueue_struct *afs_async_calls;
static void afs_wake_up_call_waiter(struct sock *, struct rxrpc_call *, unsigned long);
static long afs_wait_for_call_to_complete(struct afs_call *, struct afs_addr_cursor *);
static void afs_wake_up_async_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_delete_async_call(struct work_struct *);
static void afs_process_async_call(struct work_struct *);
......@@ -361,10 +360,10 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
}
/*
* initiate a call
* Initiate a call and synchronously queue up the parameters for dispatch. Any
* error is stored into the call struct, which the caller must check for.
*/
long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
gfp_t gfp, bool async)
void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
{
struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index];
struct rxrpc_call *rxcall;
......@@ -382,7 +381,6 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
call, call->type->name, key_serial(call->key),
atomic_read(&call->net->nr_outstanding_calls));
call->async = async;
call->addr_ix = ac->index;
call->alist = afs_get_addrlist(ac->alist);
......@@ -415,7 +413,7 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key,
(unsigned long)call,
tx_total_len, gfp,
(async ?
(call->async ?
afs_wake_up_async_call :
afs_wake_up_call_waiter),
call->upgrade,
......@@ -453,13 +451,11 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
/* Note that at this point, we may have received the reply or an abort
* - and an asynchronous call may already have completed.
*
* afs_wait_for_call_to_complete(call, ac)
* must be called to synchronously clean up.
*/
if (call->async) {
afs_put_call(call);
return -EINPROGRESS;
}
return afs_wait_for_call_to_complete(call, ac);
return;
error_do_abort:
if (ret != -ECONNABORTED) {
......@@ -495,9 +491,7 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
ac->error = ret;
call->state = AFS_CALL_COMPLETE;
afs_put_call(call);
_leave(" = %d", ret);
return ret;
}
/*
......@@ -604,10 +598,10 @@ static void afs_deliver_to_call(struct afs_call *call)
}
/*
* wait synchronously for a call to complete
* Wait synchronously for a call to complete and clean up the call struct.
*/
static long afs_wait_for_call_to_complete(struct afs_call *call,
struct afs_addr_cursor *ac)
long afs_wait_for_call_to_complete(struct afs_call *call,
struct afs_addr_cursor *ac)
{
signed long rtt2, timeout;
long ret;
......@@ -620,6 +614,10 @@ static long afs_wait_for_call_to_complete(struct afs_call *call,
_enter("");
ret = call->error;
if (ret < 0)
goto out;
rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall);
rtt2 = nsecs_to_jiffies64(rtt) * 2;
if (rtt2 < 2)
......@@ -703,6 +701,7 @@ static long afs_wait_for_call_to_complete(struct afs_call *call,
break;
}
out:
_debug("call complete");
afs_put_call(call);
_leave(" = %p", (void *)ret);
......
......@@ -46,7 +46,7 @@ struct file_system_type afs_fs_type = {
.init_fs_context = afs_init_fs_context,
.parameters = &afs_fs_parameters,
.kill_sb = afs_kill_super,
.fs_flags = 0,
.fs_flags = FS_RENAME_DOES_D_MOVE,
};
MODULE_ALIAS_FS("afs");
......@@ -69,19 +69,30 @@ static atomic_t afs_count_active_inodes;
enum afs_param {
Opt_autocell,
Opt_dyn,
Opt_flock,
Opt_source,
};
static const struct fs_parameter_spec afs_param_specs[] = {
fsparam_flag ("autocell", Opt_autocell),
fsparam_flag ("dyn", Opt_dyn),
fsparam_enum ("flock", Opt_flock),
fsparam_string("source", Opt_source),
{}
};
static const struct fs_parameter_enum afs_param_enums[] = {
{ Opt_flock, "local", afs_flock_mode_local },
{ Opt_flock, "openafs", afs_flock_mode_openafs },
{ Opt_flock, "strict", afs_flock_mode_strict },
{ Opt_flock, "write", afs_flock_mode_write },
{}
};
static const struct fs_parameter_description afs_fs_parameters = {
.name = "kAFS",
.specs = afs_param_specs,
.enums = afs_param_enums,
};
/*
......@@ -184,11 +195,22 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
static int afs_show_options(struct seq_file *m, struct dentry *root)
{
struct afs_super_info *as = AFS_FS_S(root->d_sb);
const char *p = NULL;
if (as->dyn_root)
seq_puts(m, ",dyn");
if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
seq_puts(m, ",autocell");
switch (as->flock_mode) {
case afs_flock_mode_unset: break;
case afs_flock_mode_local: p = "local"; break;
case afs_flock_mode_openafs: p = "openafs"; break;
case afs_flock_mode_strict: p = "strict"; break;
case afs_flock_mode_write: p = "write"; break;
}
if (p)
seq_printf(m, ",flock=%s", p);
return 0;
}
......@@ -317,6 +339,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param)
ctx->dyn_root = true;
break;
case Opt_flock:
ctx->flock_mode = result.uint_32;
break;
default:
return -EINVAL;
}
......@@ -429,7 +455,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
fid.vnode = 1;
fid.vnode_hi = 0;
fid.unique = 1;
inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL);
inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL, NULL);
}
if (IS_ERR(inode))
......@@ -468,6 +494,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (as) {
as->net_ns = get_net(fc->net_ns);
as->flock_mode = ctx->flock_mode;
if (ctx->dyn_root) {
as->dyn_root = true;
} else {
......@@ -552,6 +579,7 @@ static int afs_get_tree(struct fs_context *fc)
}
fc->root = dget(sb->s_root);
trace_afs_get_tree(as->cell, as->volume);
_leave(" = 0 [%p]", sb);
return 0;
......@@ -658,6 +686,8 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
vnode->cb_type = 0;
vnode->lock_state = AFS_VNODE_LOCK_NONE;
init_rwsem(&vnode->rmdir_lock);
_leave(" = %p", &vnode->vfs_inode);
return &vnode->vfs_inode;
}
......
......@@ -141,8 +141,8 @@ static bool afs_do_probe_vlserver(struct afs_net *net,
struct afs_addr_cursor ac = {
.index = 0,
};
struct afs_call *call;
bool in_progress = false;
int err;
_enter("%s", server->name);
......@@ -156,12 +156,14 @@ static bool afs_do_probe_vlserver(struct afs_net *net,
server->probe.rtt = UINT_MAX;
for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
err = afs_vl_get_capabilities(net, &ac, key, server,
server_index, true);
if (err == -EINPROGRESS)
call = afs_vl_get_capabilities(net, &ac, key, server,
server_index);
if (!IS_ERR(call)) {
afs_put_call(call);
in_progress = true;
else
afs_prioritise_error(_e, err, ac.abort_code);
} else {
afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
}
}
if (!in_progress)
......
......@@ -167,7 +167,8 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc,
memset((void *)bp + volnamesz, 0, padsz);
trace_afs_make_vl_call(call);
return (struct afs_vldb_entry *)afs_make_call(&vc->ac, call, GFP_KERNEL, false);
afs_make_call(&vc->ac, call, GFP_KERNEL);
return (struct afs_vldb_entry *)afs_wait_for_call_to_complete(call, &vc->ac);
}
/*
......@@ -306,7 +307,8 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc,
r->uuid.node[i] = htonl(u->node[i]);
trace_afs_make_vl_call(call);
return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false);
afs_make_call(&vc->ac, call, GFP_KERNEL);
return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
}
/*
......@@ -380,12 +382,11 @@ static const struct afs_call_type afs_RXVLGetCapabilities = {
* We use this to probe for service upgrade to determine what the server at the
* other end supports.
*/
int afs_vl_get_capabilities(struct afs_net *net,
struct afs_addr_cursor *ac,
struct key *key,
struct afs_vlserver *server,
unsigned int server_index,
bool async)
struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
struct afs_addr_cursor *ac,
struct key *key,
struct afs_vlserver *server,
unsigned int server_index)
{
struct afs_call *call;
__be32 *bp;
......@@ -394,13 +395,14 @@ int afs_vl_get_capabilities(struct afs_net *net,
call = afs_alloc_flat_call(net, &afs_RXVLGetCapabilities, 1 * 4, 16 * 4);
if (!call)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
call->key = key;
call->reply[0] = afs_get_vlserver(server);
call->reply[1] = (void *)(long)server_index;
call->upgrade = true;
call->want_reply_time = true;
call->async = true;
/* marshall the parameters */
bp = call->request;
......@@ -408,7 +410,8 @@ int afs_vl_get_capabilities(struct afs_net *net,
/* Can't take a ref on server */
trace_afs_make_vl_call(call);
return afs_make_call(ac, call, GFP_KERNEL, async);
afs_make_call(ac, call, GFP_KERNEL);
return call;
}
/*
......@@ -651,5 +654,6 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc,
memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */
trace_afs_make_vl_call(call);
return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false);
afs_make_call(&vc->ac, call, GFP_KERNEL);
return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
}
......@@ -16,9 +16,14 @@
#include "internal.h"
static const char afs_xattr_list[] =
"afs.acl\0"
"afs.cell\0"
"afs.fid\0"
"afs.volume";
"afs.volume\0"
"afs.yfs.acl\0"
"afs.yfs.acl_inherited\0"
"afs.yfs.acl_num_cleaned\0"
"afs.yfs.vol_acl";
/*
* Retrieve a list of the supported xattrs.
......@@ -33,6 +38,248 @@ ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size)
return sizeof(afs_xattr_list);
}
/*
* Get a file's ACL.
*/
static int afs_xattr_get_acl(const struct xattr_handler *handler,
struct dentry *dentry,
struct inode *inode, const char *name,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
acl = afs_fs_fetch_acl(&fc);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}
if (ret == 0) {
ret = acl->size;
if (size > 0) {
ret = -ERANGE;
if (acl->size > size)
return -ERANGE;
memcpy(buffer, acl->data, acl->size);
ret = acl->size;
}
kfree(acl);
}
key_put(key);
return ret;
}
/*
* Set a file's AFS3 ACL.
*/
static int afs_xattr_set_acl(const struct xattr_handler *handler,
struct dentry *dentry,
struct inode *inode, const char *name,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
if (flags == XATTR_CREATE)
return -EINVAL;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
key_put(key);
return -ENOMEM;
}
acl->size = size;
memcpy(acl->data, buffer, size);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_acl(&fc, acl);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}
kfree(acl);
key_put(key);
return ret;
}
static const struct xattr_handler afs_xattr_afs_acl_handler = {
.name = "afs.acl",
.get = afs_xattr_get_acl,
.set = afs_xattr_set_acl,
};
/*
* Get a file's YFS ACL.
*/
static int afs_xattr_get_yfs(const struct xattr_handler *handler,
struct dentry *dentry,
struct inode *inode, const char *name,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct yfs_acl *yacl = NULL;
struct key *key;
unsigned int flags = 0;
char buf[16], *data;
int which = 0, dsize, ret;
if (strcmp(name, "acl") == 0)
which = 0;
else if (strcmp(name, "acl_inherited") == 0)
which = 1;
else if (strcmp(name, "acl_num_cleaned") == 0)
which = 2;
else if (strcmp(name, "vol_acl") == 0)
which = 3;
else
return -EOPNOTSUPP;
if (which == 0)
flags |= YFS_ACL_WANT_ACL;
else if (which == 3)
flags |= YFS_ACL_WANT_VOL_ACL;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yacl = yfs_fs_fetch_opaque_acl(&fc, flags);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}
if (ret == 0) {
switch (which) {
case 0:
data = yacl->acl->data;
dsize = yacl->acl->size;
break;
case 1:
data = buf;
dsize = snprintf(buf, sizeof(buf), "%u",
yacl->inherit_flag);
break;
case 2:
data = buf;
dsize = snprintf(buf, sizeof(buf), "%u",
yacl->num_cleaned);
break;
case 3:
data = yacl->vol_acl->data;
dsize = yacl->vol_acl->size;
break;
default:
ret = -EOPNOTSUPP;
goto out;
}
ret = dsize;
if (size > 0) {
if (dsize > size) {
ret = -ERANGE;
goto out;
}
memcpy(buffer, data, dsize);
}
}
out:
yfs_free_opaque_acl(yacl);
key_put(key);
return ret;
}
/*
* Set a file's YFS ACL.
*/
static int afs_xattr_set_yfs(const struct xattr_handler *handler,
struct dentry *dentry,
struct inode *inode, const char *name,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret;
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);
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
key_put(key);
return -ENOMEM;
}
acl->size = size;
memcpy(acl->data, buffer, size);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_store_opaque_acl2(&fc, acl);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc);
}
kfree(acl);
key_put(key);
return ret;
}
static const struct xattr_handler afs_xattr_yfs_handler = {
.prefix = "afs.yfs.",
.get = afs_xattr_get_yfs,
.set = afs_xattr_set_yfs,
};
/*
* Get the name of the cell on which a file resides.
*/
......@@ -50,7 +297,7 @@ static int afs_xattr_get_cell(const struct xattr_handler *handler,
return namelen;
if (namelen > size)
return -ERANGE;
memcpy(buffer, cell->name, size);
memcpy(buffer, cell->name, namelen);
return namelen;
}
......@@ -69,11 +316,20 @@ static int afs_xattr_get_fid(const struct xattr_handler *handler,
void *buffer, size_t size)
{
struct afs_vnode *vnode = AFS_FS_I(inode);
char text[8 + 1 + 8 + 1 + 8 + 1];
char text[16 + 1 + 24 + 1 + 8 + 1];
size_t len;
len = sprintf(text, "%llx:%llx:%x",
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
/* The volume ID is 64-bit, the vnode ID is 96-bit and the
* uniquifier is 32-bit.
*/
len = sprintf(text, "%llx:", vnode->fid.vid);
if (vnode->fid.vnode_hi)
len += sprintf(text + len, "%x%016llx",
vnode->fid.vnode_hi, vnode->fid.vnode);
else
len += sprintf(text + len, "%llx", vnode->fid.vnode);
len += sprintf(text + len, ":%x", vnode->fid.unique);
if (size == 0)
return len;
if (len > size)
......@@ -104,7 +360,7 @@ static int afs_xattr_get_volume(const struct xattr_handler *handler,
return namelen;
if (namelen > size)
return -ERANGE;
memcpy(buffer, volname, size);
memcpy(buffer, volname, namelen);
return namelen;
}
......@@ -114,8 +370,10 @@ static const struct xattr_handler afs_xattr_afs_volume_handler = {
};
const struct xattr_handler *afs_xattr_handlers[] = {
&afs_xattr_afs_acl_handler,
&afs_xattr_afs_cell_handler,
&afs_xattr_afs_fid_handler,
&afs_xattr_afs_volume_handler,
&afs_xattr_yfs_handler, /* afs.yfs. prefix */
NULL
};
This diff is collapsed.
......@@ -1098,6 +1098,7 @@ struct file_lock {
struct {
struct list_head link; /* link in AFS vnode's pending_locks list */
int state; /* state of grant or error if -ve */
unsigned int debug_id;
} afs;
} fl_u;
} __randomize_layout;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment