Skip to content

Commit

Permalink
Merge tag 'afs-next-20190507' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/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()
  • Loading branch information
Linus Torvalds committed May 8, 2019
2 parents 149e703 + f5e4546 commit e5fef2a
Show file tree
Hide file tree
Showing 18 changed files with 2,142 additions and 378 deletions.
1 change: 1 addition & 0 deletions fs/afs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ kafs-y := \
cmservice.o \
dir.o \
dir_edit.o \
dir_silly.o \
dynroot.o \
file.o \
flock.o \
Expand Down
2 changes: 2 additions & 0 deletions fs/afs/afs_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
167 changes: 151 additions & 16 deletions fs/afs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
Expand All @@ -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

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
*/
Expand All @@ -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.
Expand Down Expand Up @@ -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)) {
Expand All @@ -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:
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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)) {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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)) {
Expand All @@ -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);
Expand Down
Loading

0 comments on commit e5fef2a

Please sign in to comment.