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 := \ ...@@ -13,6 +13,7 @@ kafs-y := \
cmservice.o \ cmservice.o \
dir.o \ dir.o \
dir_edit.o \ dir_edit.o \
dir_silly.o \
dynroot.o \ dynroot.o \
file.o \ file.o \
flock.o \ flock.o \
......
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
enum AFS_FS_Operations { enum AFS_FS_Operations {
FSFETCHDATA = 130, /* AFS Fetch file data */ FSFETCHDATA = 130, /* AFS Fetch file data */
FSFETCHACL = 131, /* AFS Fetch file ACL */
FSFETCHSTATUS = 132, /* AFS Fetch file status */ FSFETCHSTATUS = 132, /* AFS Fetch file status */
FSSTOREDATA = 133, /* AFS Store file data */ FSSTOREDATA = 133, /* AFS Store file data */
FSSTOREACL = 134, /* AFS Store file ACL */
FSSTORESTATUS = 135, /* AFS Store file status */ FSSTORESTATUS = 135, /* AFS Store file status */
FSREMOVEFILE = 136, /* AFS Remove a file */ FSREMOVEFILE = 136, /* AFS Remove a file */
FSCREATEFILE = 137, /* AFS Create a file */ FSCREATEFILE = 137, /* AFS Create a file */
......
...@@ -26,6 +26,7 @@ static int afs_dir_open(struct inode *inode, struct file *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_readdir(struct file *file, struct dir_context *ctx);
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
static int afs_d_delete(const struct dentry *dentry); 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, static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
loff_t fpos, u64 ino, unsigned dtype); loff_t fpos, u64 ino, unsigned dtype);
static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen, 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 = { ...@@ -85,6 +86,7 @@ const struct dentry_operations afs_fs_dentry_operations = {
.d_delete = afs_d_delete, .d_delete = afs_d_delete,
.d_release = afs_d_release, .d_release = afs_d_release,
.d_automount = afs_d_automount, .d_automount = afs_d_automount,
.d_iput = afs_d_iput,
}; };
struct afs_lookup_one_cookie { struct afs_lookup_one_cookie {
...@@ -159,6 +161,38 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page, ...@@ -159,6 +161,38 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page,
return false; 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 * open an AFS directory file
*/ */
...@@ -277,6 +311,7 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key) ...@@ -277,6 +311,7 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
goto error; goto error;
if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
trace_afs_reload_dir(dvnode);
ret = afs_fetch_data(dvnode, key, req); ret = afs_fetch_data(dvnode, key, req);
if (ret < 0) if (ret < 0)
goto error_unlock; goto error_unlock;
...@@ -288,10 +323,8 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key) ...@@ -288,10 +323,8 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
/* Validate the data we just read. */ /* Validate the data we just read. */
ret = -EIO; ret = -EIO;
for (i = 0; i < req->nr_pages; i++) if (!afs_dir_check_pages(dvnode, req))
if (!afs_dir_check_page(dvnode, req->pages[i], goto error_unlock;
req->actual_len))
goto error_unlock;
// TODO: Trim excess pages // TODO: Trim excess pages
...@@ -743,7 +776,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, ...@@ -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], ti = afs_iget(dir->i_sb, key, &cookie->fids[i],
&cookie->statuses[i], &cookie->statuses[i],
&cookie->callbacks[i], &cookie->callbacks[i],
cbi); cbi, dvnode);
if (i == 0) { if (i == 0) {
inode = ti; inode = ti;
} else { } else {
...@@ -875,8 +908,14 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -875,8 +908,14 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
(void *)(unsigned long)dvnode->status.data_version; (void *)(unsigned long)dvnode->status.data_version;
} }
d = d_splice_alias(inode, dentry); d = d_splice_alias(inode, dentry);
if (!IS_ERR_OR_NULL(d)) if (!IS_ERR_OR_NULL(d)) {
d->d_fsdata = dentry->d_fsdata; 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; return d;
} }
...@@ -1052,6 +1091,16 @@ static int afs_d_delete(const struct dentry *dentry) ...@@ -1052,6 +1091,16 @@ static int afs_d_delete(const struct dentry *dentry)
return 1; 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 * handle dentry release
*/ */
...@@ -1076,7 +1125,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, ...@@ -1076,7 +1125,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
return; return;
inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key, 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)) { if (IS_ERR(inode)) {
/* ENOMEM or EINTR at a really inconvenient time - just abandon /* ENOMEM or EINTR at a really inconvenient time - just abandon
* the new directory on the server. * the new directory on the server.
...@@ -1194,6 +1243,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1194,6 +1243,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
goto error_key; goto error_key;
} }
if (vnode) {
ret = down_write_killable(&vnode->rmdir_lock);
if (ret < 0)
goto error_key;
}
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
...@@ -1212,6 +1267,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1212,6 +1267,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
} }
} }
if (vnode)
up_write(&vnode->rmdir_lock);
error_key: error_key:
key_put(key); key_put(key);
error: error:
...@@ -1228,9 +1285,9 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -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 * 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... * outstanding on a different server, then it won't break it either...
*/ */
static int afs_dir_remove_link(struct dentry *dentry, struct key *key, int afs_dir_remove_link(struct dentry *dentry, struct key *key,
unsigned long d_version_before, unsigned long d_version_before,
unsigned long d_version_after) unsigned long d_version_after)
{ {
bool dir_valid; bool dir_valid;
int ret = 0; int ret = 0;
...@@ -1277,6 +1334,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -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 afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
struct key *key; struct key *key;
unsigned long d_version = (unsigned long)dentry->d_fsdata; unsigned long d_version = (unsigned long)dentry->d_fsdata;
bool need_rehash = false;
u64 data_version = dvnode->status.data_version; u64 data_version = dvnode->status.data_version;
int ret; int ret;
...@@ -1300,6 +1358,21 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1300,6 +1358,21 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
goto error_key; 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; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
...@@ -1331,6 +1404,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1331,6 +1404,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
afs_edit_dir_for_unlink); afs_edit_dir_for_unlink);
} }
if (need_rehash && ret < 0 && ret != -ENOENT)
d_rehash(dentry);
error_key: error_key:
key_put(key); key_put(key);
error: error:
...@@ -1551,6 +1627,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1551,6 +1627,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
{ {
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
struct dentry *tmp = NULL, *rehash = NULL;
struct inode *new_inode;
struct key *key; struct key *key;
u64 orig_data_version, new_data_version; u64 orig_data_version, new_data_version;
bool new_negative = d_is_negative(new_dentry); bool new_negative = d_is_negative(new_dentry);
...@@ -1559,6 +1637,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1559,6 +1637,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (flags) if (flags)
return -EINVAL; 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)); vnode = AFS_FS_I(d_inode(old_dentry));
orig_dvnode = AFS_FS_I(old_dir); orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir); new_dvnode = AFS_FS_I(new_dir);
...@@ -1577,12 +1659,48 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1577,12 +1659,48 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto error; 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; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) { if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) {
if (orig_dvnode != new_dvnode) { if (orig_dvnode != new_dvnode) {
if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) { if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc); afs_end_vnode_operation(&fc);
goto error_key; goto error_rehash;
} }
} }
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
...@@ -1599,25 +1717,42 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1599,25 +1717,42 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
mutex_unlock(&new_dvnode->io_lock); mutex_unlock(&new_dvnode->io_lock);
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret < 0) if (ret < 0)
goto error_key; goto error_rehash;
} }
if (ret == 0) { if (ret == 0) {
if (rehash)
d_rehash(rehash);
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags)) if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags))
afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
afs_edit_dir_for_rename); afs_edit_dir_for_rename_0);
if (!new_negative && if (!new_negative &&
test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, 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)) if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_add(new_dvnode, &new_dentry->d_name, 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); key_put(key);
error: error:
_leave(" = %d", ret); _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, ...@@ -141,8 +141,8 @@ static int afs_do_probe_fileserver(struct afs_net *net,
struct afs_addr_cursor ac = { struct afs_addr_cursor ac = {
.index = 0, .index = 0,
}; };
struct afs_call *call;
bool in_progress = false; bool in_progress = false;
int err;
_enter("%pU", &server->uuid); _enter("%pU", &server->uuid);
...@@ -156,12 +156,13 @@ static int afs_do_probe_fileserver(struct afs_net *net, ...@@ -156,12 +156,13 @@ static int afs_do_probe_fileserver(struct afs_net *net,
server->probe.rtt = UINT_MAX; server->probe.rtt = UINT_MAX;
for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) { for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
err = afs_fs_get_capabilities(net, server, &ac, key, server_index, call = afs_fs_get_capabilities(net, server, &ac, key, server_index);
true); if (!IS_ERR(call)) {
if (err == -EINPROGRESS) afs_put_call(call);
in_progress = true; in_progress = true;
else } else {
afs_prioritise_error(_e, err, ac.abort_code); afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
}
} }
if (!in_progress) if (!in_progress)
......
This diff is collapsed.
...@@ -29,10 +29,36 @@ static const struct inode_operations afs_symlink_inode_operations = { ...@@ -29,10 +29,36 @@ static const struct inode_operations afs_symlink_inode_operations = {
.listxattr = afs_listxattr, .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. * 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); 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) ...@@ -80,12 +106,16 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key)
inode_nohighmem(inode); inode_nohighmem(inode);
break; break;
default: default:
printk("kAFS: AFS vnode with undefined type\n"); dump_vnode(vnode, parent_vnode);
read_sequnlock_excl(&vnode->cb_lock); read_sequnlock_excl(&vnode->cb_lock);
return afs_protocol_error(NULL, -EBADMSG, afs_eproto_file_type); 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; vnode->invalid_before = vnode->status.data_version;
read_sequnlock_excl(&vnode->cb_lock); read_sequnlock_excl(&vnode->cb_lock);
...@@ -270,7 +300,8 @@ static void afs_get_inode_cache(struct afs_vnode *vnode) ...@@ -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 inode *afs_iget(struct super_block *sb, struct key *key,
struct afs_fid *fid, struct afs_file_status *status, 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_iget_data data = { .fid = *fid };
struct afs_super_info *as; struct afs_super_info *as;
...@@ -327,7 +358,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -327,7 +358,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
vnode->cb_expires_at += ktime_get_real_seconds(); 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) if (ret < 0)
goto bad_inode; goto bad_inode;
...@@ -543,6 +574,8 @@ void afs_evict_inode(struct inode *inode) ...@@ -543,6 +574,8 @@ void afs_evict_inode(struct inode *inode)
#endif #endif
afs_put_permits(rcu_access_pointer(vnode->permit_cache)); afs_put_permits(rcu_access_pointer(vnode->permit_cache));
key_put(vnode->silly_key);
vnode->silly_key = NULL;
key_put(vnode->lock_key); key_put(vnode->lock_key);
vnode->lock_key = NULL; vnode->lock_key = NULL;
_leave(""); _leave("");
......
...@@ -36,11 +36,24 @@ ...@@ -36,11 +36,24 @@
struct pagevec; struct pagevec;
struct afs_call; 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 { struct afs_fs_context {
bool force; /* T to force cell type */ bool force; /* T to force cell type */
bool autocell; /* T if set auto mount operation */ bool autocell; /* T if set auto mount operation */
bool dyn_root; /* T if dynamic root */ bool dyn_root; /* T if dynamic root */
bool no_cell; /* T if the source is "none" (for dynroot) */ 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 */ afs_voltype_t type; /* type of volume requested */
unsigned int volnamesz; /* size of volume name */ unsigned int volnamesz; /* size of volume name */
const char *volname; /* name of volume to mount */ const char *volname; /* name of volume to mount */
...@@ -221,6 +234,7 @@ struct afs_super_info { ...@@ -221,6 +234,7 @@ struct afs_super_info {
struct net *net_ns; /* Network namespace */ struct net *net_ns; /* Network namespace */
struct afs_cell *cell; /* The cell in which the volume resides */ struct afs_cell *cell; /* The cell in which the volume resides */
struct afs_volume *volume; /* volume record */ struct afs_volume *volume; /* volume record */
enum afs_flock_mode flock_mode:8; /* File locking emulation mode */
bool dyn_root; /* True if dynamic root */ bool dyn_root; /* True if dynamic root */
}; };
...@@ -599,6 +613,7 @@ enum afs_lock_state { ...@@ -599,6 +613,7 @@ enum afs_lock_state {
AFS_VNODE_LOCK_EXTENDING, /* We're extending a lock on the server */ 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_NEED_UNLOCK, /* We need to unlock on the server */
AFS_VNODE_LOCK_UNLOCKING, /* We're telling the server to unlock */ 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 { ...@@ -620,6 +635,8 @@ struct afs_vnode {
struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */ 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 mutex io_lock; /* Lock for serialising I/O on this mutex */
struct rw_semaphore validate_lock; /* lock for validating this vnode */ 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 wb_lock; /* lock for wb_keys */
spinlock_t lock; /* waitqueue/flags lock */ spinlock_t lock; /* waitqueue/flags lock */
unsigned long flags; unsigned long flags;
...@@ -638,6 +655,7 @@ struct afs_vnode { ...@@ -638,6 +655,7 @@ struct afs_vnode {
struct list_head granted_locks; /* locks granted on this file */ struct list_head granted_locks; /* locks granted on this file */
struct delayed_work lock_work; /* work to be done in locking */ struct delayed_work lock_work; /* work to be done in locking */
struct key *lock_key; /* Key to be used in lock ops */ 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; enum afs_lock_state lock_state : 8;
afs_lock_type_t lock_type : 8; afs_lock_type_t lock_type : 8;
...@@ -864,6 +882,7 @@ extern const struct address_space_operations afs_dir_aops; ...@@ -864,6 +882,7 @@ extern const struct address_space_operations afs_dir_aops;
extern const struct dentry_operations afs_fs_dentry_operations; extern const struct dentry_operations afs_fs_dentry_operations;
extern void afs_d_release(struct dentry *); extern void afs_d_release(struct dentry *);
extern int afs_dir_remove_link(struct dentry *, struct key *, unsigned long, unsigned long);
/* /*
* dir_edit.c * dir_edit.c
...@@ -872,6 +891,13 @@ extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid * ...@@ -872,6 +891,13 @@ extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *
enum afs_edit_dir_reason); enum afs_edit_dir_reason);
extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, 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 * dynroot.c
*/ */
...@@ -905,6 +931,7 @@ extern void afs_put_read(struct afs_read *); ...@@ -905,6 +931,7 @@ extern void afs_put_read(struct afs_read *);
*/ */
extern struct workqueue_struct *afs_lock_manager; 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_work(struct work_struct *);
extern void afs_lock_may_be_available(struct afs_vnode *); extern void afs_lock_may_be_available(struct afs_vnode *);
extern int afs_lock(struct file *, int, struct file_lock *); extern int afs_lock(struct file *, int, struct file_lock *);
...@@ -939,8 +966,9 @@ extern int afs_fs_extend_lock(struct afs_fs_cursor *); ...@@ -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_release_lock(struct afs_fs_cursor *);
extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *, extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *); struct afs_addr_cursor *, struct key *);
extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *, extern struct afs_call *afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *, unsigned int, bool); struct afs_addr_cursor *, struct key *,
unsigned int);
extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *, extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_file_status *, struct afs_fid *, struct afs_file_status *,
struct afs_callback *, unsigned int, struct afs_callback *, unsigned int,
...@@ -949,6 +977,14 @@ extern int afs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *, ...@@ -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_fid *, struct afs_file_status *,
struct afs_callback *, struct afs_volsync *); 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 * fs_probe.c
*/ */
...@@ -965,7 +1001,8 @@ extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool); ...@@ -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 *, extern struct inode *afs_iget(struct super_block *, struct key *,
struct afs_fid *, struct afs_file_status *, struct afs_fid *, struct afs_file_status *,
struct afs_callback *, struct afs_callback *,
struct afs_cb_interest *); struct afs_cb_interest *,
struct afs_vnode *);
extern void afs_zap_data(struct afs_vnode *); extern void afs_zap_data(struct afs_vnode *);
extern int afs_validate(struct afs_vnode *, struct key *); extern int afs_validate(struct afs_vnode *, struct key *);
extern int afs_getattr(const struct path *, struct kstat *, u32, unsigned int); 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 *); ...@@ -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 __net_exit afs_close_socket(struct afs_net *);
extern void afs_charge_preallocation(struct work_struct *); extern void afs_charge_preallocation(struct work_struct *);
extern void afs_put_call(struct afs_call *); 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 *, extern struct afs_call *afs_alloc_flat_call(struct afs_net *,
const struct afs_call_type *, const struct afs_call_type *,
size_t, size_t); size_t, size_t);
...@@ -1218,8 +1256,8 @@ extern void afs_fs_exit(void); ...@@ -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 *, extern struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *,
const char *, int); const char *, int);
extern struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *, const uuid_t *); 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 *, extern struct afs_call *afs_vl_get_capabilities(struct afs_net *, struct afs_addr_cursor *,
struct afs_vlserver *, unsigned int, bool); struct key *, struct afs_vlserver *, unsigned int);
extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *, const uuid_t *); 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 *, ...@@ -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_callback *, unsigned int,
struct afs_volsync *); 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. * Miscellaneous inline functions.
*/ */
......
...@@ -31,9 +31,9 @@ enum YFS_CM_Operations { ...@@ -31,9 +31,9 @@ enum YFS_CM_Operations {
}; };
enum YFS_FS_Operations { enum YFS_FS_Operations {
YFSFETCHACL = 64131, /* YFS Fetch file ACL */ YFSFETCHACL = 64131, /* YFS Fetch file AFS3 ACL */
YFSFETCHSTATUS = 64132, /* YFS Fetch file status */ 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 */ YFSSTORESTATUS = 64135, /* YFS Store file status */
YFSREMOVEFILE = 64136, /* YFS Remove a file */ YFSREMOVEFILE = 64136, /* YFS Remove a file */
YFSCREATEFILE = 64137, /* YFS Create a file */ YFSCREATEFILE = 64137, /* YFS Create a file */
...@@ -49,7 +49,7 @@ enum YFS_FS_Operations { ...@@ -49,7 +49,7 @@ enum YFS_FS_Operations {
YFSRELEASELOCK = 64158, /* YFS Release a file lock */ YFSRELEASELOCK = 64158, /* YFS Release a file lock */
YFSLOOKUP = 64161, /* YFS lookup file in directory */ YFSLOOKUP = 64161, /* YFS lookup file in directory */
YFSFLUSHCPS = 64165, YFSFLUSHCPS = 64165,
YFSFETCHOPAQUEACL = 64168, YFSFETCHOPAQUEACL = 64168, /* YFS Fetch file YFS ACL */
YFSWHOAMI = 64170, YFSWHOAMI = 64170,
YFSREMOVEACL = 64171, YFSREMOVEACL = 64171,
YFSREMOVEFILE2 = 64173, YFSREMOVEFILE2 = 64173,
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
struct workqueue_struct *afs_async_calls; struct workqueue_struct *afs_async_calls;
static void afs_wake_up_call_waiter(struct sock *, struct rxrpc_call *, unsigned long); 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_wake_up_async_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_delete_async_call(struct work_struct *); static void afs_delete_async_call(struct work_struct *);
static void afs_process_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) ...@@ -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, void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
gfp_t gfp, bool async)
{ {
struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index]; struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index];
struct rxrpc_call *rxcall; struct rxrpc_call *rxcall;
...@@ -382,7 +381,6 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, ...@@ -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), call, call->type->name, key_serial(call->key),
atomic_read(&call->net->nr_outstanding_calls)); atomic_read(&call->net->nr_outstanding_calls));
call->async = async;
call->addr_ix = ac->index; call->addr_ix = ac->index;
call->alist = afs_get_addrlist(ac->alist); call->alist = afs_get_addrlist(ac->alist);
...@@ -415,7 +413,7 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, ...@@ -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, rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key,
(unsigned long)call, (unsigned long)call,
tx_total_len, gfp, tx_total_len, gfp,
(async ? (call->async ?
afs_wake_up_async_call : afs_wake_up_async_call :
afs_wake_up_call_waiter), afs_wake_up_call_waiter),
call->upgrade, call->upgrade,
...@@ -453,13 +451,11 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, ...@@ -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 /* Note that at this point, we may have received the reply or an abort
* - and an asynchronous call may already have completed. * - 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) { return;
afs_put_call(call);
return -EINPROGRESS;
}
return afs_wait_for_call_to_complete(call, ac);
error_do_abort: error_do_abort:
if (ret != -ECONNABORTED) { if (ret != -ECONNABORTED) {
...@@ -495,9 +491,7 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, ...@@ -495,9 +491,7 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
ac->error = ret; ac->error = ret;
call->state = AFS_CALL_COMPLETE; call->state = AFS_CALL_COMPLETE;
afs_put_call(call);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret;
} }
/* /*
...@@ -604,10 +598,10 @@ static void afs_deliver_to_call(struct afs_call *call) ...@@ -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, long afs_wait_for_call_to_complete(struct afs_call *call,
struct afs_addr_cursor *ac) struct afs_addr_cursor *ac)
{ {
signed long rtt2, timeout; signed long rtt2, timeout;
long ret; long ret;
...@@ -620,6 +614,10 @@ static long afs_wait_for_call_to_complete(struct afs_call *call, ...@@ -620,6 +614,10 @@ static long afs_wait_for_call_to_complete(struct afs_call *call,
_enter(""); _enter("");
ret = call->error;
if (ret < 0)
goto out;
rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall); rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall);
rtt2 = nsecs_to_jiffies64(rtt) * 2; rtt2 = nsecs_to_jiffies64(rtt) * 2;
if (rtt2 < 2) if (rtt2 < 2)
...@@ -703,6 +701,7 @@ static long afs_wait_for_call_to_complete(struct afs_call *call, ...@@ -703,6 +701,7 @@ static long afs_wait_for_call_to_complete(struct afs_call *call,
break; break;
} }
out:
_debug("call complete"); _debug("call complete");
afs_put_call(call); afs_put_call(call);
_leave(" = %p", (void *)ret); _leave(" = %p", (void *)ret);
......
...@@ -46,7 +46,7 @@ struct file_system_type afs_fs_type = { ...@@ -46,7 +46,7 @@ struct file_system_type afs_fs_type = {
.init_fs_context = afs_init_fs_context, .init_fs_context = afs_init_fs_context,
.parameters = &afs_fs_parameters, .parameters = &afs_fs_parameters,
.kill_sb = afs_kill_super, .kill_sb = afs_kill_super,
.fs_flags = 0, .fs_flags = FS_RENAME_DOES_D_MOVE,
}; };
MODULE_ALIAS_FS("afs"); MODULE_ALIAS_FS("afs");
...@@ -69,19 +69,30 @@ static atomic_t afs_count_active_inodes; ...@@ -69,19 +69,30 @@ static atomic_t afs_count_active_inodes;
enum afs_param { enum afs_param {
Opt_autocell, Opt_autocell,
Opt_dyn, Opt_dyn,
Opt_flock,
Opt_source, Opt_source,
}; };
static const struct fs_parameter_spec afs_param_specs[] = { static const struct fs_parameter_spec afs_param_specs[] = {
fsparam_flag ("autocell", Opt_autocell), fsparam_flag ("autocell", Opt_autocell),
fsparam_flag ("dyn", Opt_dyn), fsparam_flag ("dyn", Opt_dyn),
fsparam_enum ("flock", Opt_flock),
fsparam_string("source", Opt_source), 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 = { static const struct fs_parameter_description afs_fs_parameters = {
.name = "kAFS", .name = "kAFS",
.specs = afs_param_specs, .specs = afs_param_specs,
.enums = afs_param_enums,
}; };
/* /*
...@@ -184,11 +195,22 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) ...@@ -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) static int afs_show_options(struct seq_file *m, struct dentry *root)
{ {
struct afs_super_info *as = AFS_FS_S(root->d_sb); struct afs_super_info *as = AFS_FS_S(root->d_sb);
const char *p = NULL;
if (as->dyn_root) if (as->dyn_root)
seq_puts(m, ",dyn"); seq_puts(m, ",dyn");
if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
seq_puts(m, ",autocell"); 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; return 0;
} }
...@@ -317,6 +339,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -317,6 +339,10 @@ static int afs_parse_param(struct fs_context *fc, struct fs_parameter *param)
ctx->dyn_root = true; ctx->dyn_root = true;
break; break;
case Opt_flock:
ctx->flock_mode = result.uint_32;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -429,7 +455,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx) ...@@ -429,7 +455,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
fid.vnode = 1; fid.vnode = 1;
fid.vnode_hi = 0; fid.vnode_hi = 0;
fid.unique = 1; 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)) if (IS_ERR(inode))
...@@ -468,6 +494,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) ...@@ -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); as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (as) { if (as) {
as->net_ns = get_net(fc->net_ns); as->net_ns = get_net(fc->net_ns);
as->flock_mode = ctx->flock_mode;
if (ctx->dyn_root) { if (ctx->dyn_root) {
as->dyn_root = true; as->dyn_root = true;
} else { } else {
...@@ -552,6 +579,7 @@ static int afs_get_tree(struct fs_context *fc) ...@@ -552,6 +579,7 @@ static int afs_get_tree(struct fs_context *fc)
} }
fc->root = dget(sb->s_root); fc->root = dget(sb->s_root);
trace_afs_get_tree(as->cell, as->volume);
_leave(" = 0 [%p]", sb); _leave(" = 0 [%p]", sb);
return 0; return 0;
...@@ -658,6 +686,8 @@ static struct inode *afs_alloc_inode(struct super_block *sb) ...@@ -658,6 +686,8 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
vnode->cb_type = 0; vnode->cb_type = 0;
vnode->lock_state = AFS_VNODE_LOCK_NONE; vnode->lock_state = AFS_VNODE_LOCK_NONE;
init_rwsem(&vnode->rmdir_lock);
_leave(" = %p", &vnode->vfs_inode); _leave(" = %p", &vnode->vfs_inode);
return &vnode->vfs_inode; return &vnode->vfs_inode;
} }
......
...@@ -141,8 +141,8 @@ static bool afs_do_probe_vlserver(struct afs_net *net, ...@@ -141,8 +141,8 @@ static bool afs_do_probe_vlserver(struct afs_net *net,
struct afs_addr_cursor ac = { struct afs_addr_cursor ac = {
.index = 0, .index = 0,
}; };
struct afs_call *call;
bool in_progress = false; bool in_progress = false;
int err;
_enter("%s", server->name); _enter("%s", server->name);
...@@ -156,12 +156,14 @@ static bool afs_do_probe_vlserver(struct afs_net *net, ...@@ -156,12 +156,14 @@ static bool afs_do_probe_vlserver(struct afs_net *net,
server->probe.rtt = UINT_MAX; server->probe.rtt = UINT_MAX;
for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) { for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
err = afs_vl_get_capabilities(net, &ac, key, server, call = afs_vl_get_capabilities(net, &ac, key, server,
server_index, true); server_index);
if (err == -EINPROGRESS) if (!IS_ERR(call)) {
afs_put_call(call);
in_progress = true; in_progress = true;
else } else {
afs_prioritise_error(_e, err, ac.abort_code); afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
}
} }
if (!in_progress) if (!in_progress)
......
...@@ -167,7 +167,8 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc, ...@@ -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); memset((void *)bp + volnamesz, 0, padsz);
trace_afs_make_vl_call(call); 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, ...@@ -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]); r->uuid.node[i] = htonl(u->node[i]);
trace_afs_make_vl_call(call); 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 = { ...@@ -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 * We use this to probe for service upgrade to determine what the server at the
* other end supports. * other end supports.
*/ */
int afs_vl_get_capabilities(struct afs_net *net, struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
struct afs_addr_cursor *ac, struct afs_addr_cursor *ac,
struct key *key, struct key *key,
struct afs_vlserver *server, struct afs_vlserver *server,
unsigned int server_index, unsigned int server_index)
bool async)
{ {
struct afs_call *call; struct afs_call *call;
__be32 *bp; __be32 *bp;
...@@ -394,13 +395,14 @@ int afs_vl_get_capabilities(struct afs_net *net, ...@@ -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); call = afs_alloc_flat_call(net, &afs_RXVLGetCapabilities, 1 * 4, 16 * 4);
if (!call) if (!call)
return -ENOMEM; return ERR_PTR(-ENOMEM);
call->key = key; call->key = key;
call->reply[0] = afs_get_vlserver(server); call->reply[0] = afs_get_vlserver(server);
call->reply[1] = (void *)(long)server_index; call->reply[1] = (void *)(long)server_index;
call->upgrade = true; call->upgrade = true;
call->want_reply_time = true; call->want_reply_time = true;
call->async = true;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -408,7 +410,8 @@ int afs_vl_get_capabilities(struct afs_net *net, ...@@ -408,7 +410,8 @@ int afs_vl_get_capabilities(struct afs_net *net,
/* Can't take a ref on server */ /* Can't take a ref on server */
trace_afs_make_vl_call(call); 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, ...@@ -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 */ memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */
trace_afs_make_vl_call(call); 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 @@ ...@@ -16,9 +16,14 @@
#include "internal.h" #include "internal.h"
static const char afs_xattr_list[] = static const char afs_xattr_list[] =
"afs.acl\0"
"afs.cell\0" "afs.cell\0"
"afs.fid\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. * Retrieve a list of the supported xattrs.
...@@ -33,6 +38,248 @@ ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size) ...@@ -33,6 +38,248 @@ ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size)
return sizeof(afs_xattr_list); 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. * 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, ...@@ -50,7 +297,7 @@ static int afs_xattr_get_cell(const struct xattr_handler *handler,
return namelen; return namelen;
if (namelen > size) if (namelen > size)
return -ERANGE; return -ERANGE;
memcpy(buffer, cell->name, size); memcpy(buffer, cell->name, namelen);
return namelen; return namelen;
} }
...@@ -69,11 +316,20 @@ static int afs_xattr_get_fid(const struct xattr_handler *handler, ...@@ -69,11 +316,20 @@ static int afs_xattr_get_fid(const struct xattr_handler *handler,
void *buffer, size_t size) void *buffer, size_t size)
{ {
struct afs_vnode *vnode = AFS_FS_I(inode); 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; size_t len;
len = sprintf(text, "%llx:%llx:%x", /* The volume ID is 64-bit, the vnode ID is 96-bit and the
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); * 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) if (size == 0)
return len; return len;
if (len > size) if (len > size)
...@@ -104,7 +360,7 @@ static int afs_xattr_get_volume(const struct xattr_handler *handler, ...@@ -104,7 +360,7 @@ static int afs_xattr_get_volume(const struct xattr_handler *handler,
return namelen; return namelen;
if (namelen > size) if (namelen > size)
return -ERANGE; return -ERANGE;
memcpy(buffer, volname, size); memcpy(buffer, volname, namelen);
return namelen; return namelen;
} }
...@@ -114,8 +370,10 @@ static const struct xattr_handler afs_xattr_afs_volume_handler = { ...@@ -114,8 +370,10 @@ static const struct xattr_handler afs_xattr_afs_volume_handler = {
}; };
const struct xattr_handler *afs_xattr_handlers[] = { const struct xattr_handler *afs_xattr_handlers[] = {
&afs_xattr_afs_acl_handler,
&afs_xattr_afs_cell_handler, &afs_xattr_afs_cell_handler,
&afs_xattr_afs_fid_handler, &afs_xattr_afs_fid_handler,
&afs_xattr_afs_volume_handler, &afs_xattr_afs_volume_handler,
&afs_xattr_yfs_handler, /* afs.yfs. prefix */
NULL NULL
}; };
This diff is collapsed.
...@@ -1098,6 +1098,7 @@ struct file_lock { ...@@ -1098,6 +1098,7 @@ struct file_lock {
struct { struct {
struct list_head link; /* link in AFS vnode's pending_locks list */ struct list_head link; /* link in AFS vnode's pending_locks list */
int state; /* state of grant or error if -ve */ int state; /* state of grant or error if -ve */
unsigned int debug_id;
} afs; } afs;
} fl_u; } fl_u;
} __randomize_layout; } __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