#include <linux/mm_inline.h>
#endif
#include <linux/pagemap.h>
-#include <linux/smp_lock.h>
#include <linux/writeback.h>
#include <linux/pagevec.h>
#include "afs/lock.h"
extern struct backing_dev_info *afs_backing_dev_info;
extern struct vcache *afs_globalVp;
-extern int afs_notify_change(struct dentry *dp, struct iattr *iattrp);
/* This function converts a positive error code from AFS into a negative
* code suitable for passing into the Linux VFS layer. It checks that the
return afs_convert_code(code);
}
+#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
+static ssize_t
+afs_linux_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long segs, loff_t pos)
+{
+ struct file *fp = iocb->ki_filp;
+ ssize_t code = 0;
+ struct vcache *vcp = VTOAFS(fp->f_dentry->d_inode);
+
+ AFS_GLOCK();
+ afs_Trace4(afs_iclSetp, CM_TRACE_AIOREADOP, ICL_TYPE_POINTER, vcp,
+ ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(pos), ICL_TYPE_INT32, segs, ICL_TYPE_INT32,
+ 99999);
+ code = afs_linux_VerifyVCache(vcp, NULL);
+
+ if (code == 0) {
+ /* Linux's FlushPages implementation doesn't ever use credp,
+ * so we optimise by not using it */
+ osi_FlushPages(vcp, NULL); /* ensure stale pages are gone */
+ AFS_GUNLOCK();
+ code = generic_file_aio_read(iocb, iov, segs, pos);
+ AFS_GLOCK();
+ }
+
+ afs_Trace4(afs_iclSetp, CM_TRACE_AIOREADOP, ICL_TYPE_POINTER, vcp,
+ ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(pos), ICL_TYPE_INT32, segs, ICL_TYPE_INT32,
+ code);
+ AFS_GUNLOCK();
+ return code;
+}
+#else
static ssize_t
afs_linux_read(struct file *fp, char *buf, size_t count, loff_t * offp)
{
AFS_GUNLOCK();
return code;
}
+#endif
-/* Now we have integrated VM for writes as well as reads. generic_file_write
- * also takes care of re-positioning the pointer if file is open in append
+/* Now we have integrated VM for writes as well as reads. the generic write operations
+ * also take care of re-positioning the pointer if file is open in append
* mode. Call fake open/close to ensure we do writes of core dumps.
*/
+#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
+static ssize_t
+afs_linux_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long segs, loff_t pos)
+{
+ ssize_t code = 0;
+ struct vcache *vcp = VTOAFS(iocb->ki_filp->f_dentry->d_inode);
+ cred_t *credp;
+
+ AFS_GLOCK();
+
+ afs_Trace4(afs_iclSetp, CM_TRACE_AIOWRITEOP, ICL_TYPE_POINTER, vcp,
+ ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(pos), ICL_TYPE_INT32, segs, ICL_TYPE_INT32,
+ (iocb->ki_filp->f_flags & O_APPEND) ? 99998 : 99999);
+
+ code = afs_linux_VerifyVCache(vcp, &credp);
+
+ ObtainWriteLock(&vcp->lock, 529);
+ afs_FakeOpen(vcp);
+ ReleaseWriteLock(&vcp->lock);
+ if (code == 0) {
+ AFS_GUNLOCK();
+ code = generic_file_aio_write(iocb, iov, segs, pos);
+ AFS_GLOCK();
+ }
+
+ ObtainWriteLock(&vcp->lock, 530);
+
+ if (vcp->execsOrWriters == 1 && !credp)
+ credp = crref();
+
+ afs_FakeClose(vcp, credp);
+ ReleaseWriteLock(&vcp->lock);
+
+ afs_Trace4(afs_iclSetp, CM_TRACE_AIOWRITEOP, ICL_TYPE_POINTER, vcp,
+ ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(pos), ICL_TYPE_INT32, segs, ICL_TYPE_INT32,
+ code);
+
+ if (credp)
+ crfree(credp);
+ AFS_GUNLOCK();
+ return code;
+}
+#else
static ssize_t
afs_linux_write(struct file *fp, const char *buf, size_t count, loff_t * offp)
{
AFS_GUNLOCK();
return code;
}
+#endif
extern int BlobScan(struct dcache * afile, afs_int32 ablob);
int offset;
int dirpos;
struct DirEntry *de;
+ struct DirBuffer entry;
ino_t ino;
int len;
afs_size_t origOffset, tlen;
code = -ENOENT;
goto out;
}
- ObtainSharedLock(&avc->lock, 810);
- UpgradeSToWLock(&avc->lock, 811);
+ ObtainWriteLock(&avc->lock, 811);
ObtainReadLock(&tdc->lock);
/*
* Make sure that the data in the cache is current. There are two
&& (tdc->dflags & DFFetching)
&& hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
ReleaseReadLock(&tdc->lock);
- ReleaseSharedLock(&avc->lock);
+ ReleaseWriteLock(&avc->lock);
afs_osi_Sleep(&tdc->validPos);
- ObtainSharedLock(&avc->lock, 812);
+ ObtainWriteLock(&avc->lock, 812);
ObtainReadLock(&tdc->lock);
}
if (!(avc->f.states & CStatd)
|| !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
ReleaseReadLock(&tdc->lock);
- ReleaseSharedLock(&avc->lock);
+ ReleaseWriteLock(&avc->lock);
afs_PutDCache(tdc);
goto tagain;
}
if (!dirpos)
break;
- de = afs_dir_GetBlob(tdc, dirpos);
- if (!de)
- break;
-
- ino = afs_calc_inum (avc->f.fid.Fid.Volume, ntohl(de->fid.vnode));
-
- if (de->name)
- len = strlen(de->name);
- else {
- printf("afs_linux_readdir: afs_dir_GetBlob failed, null name (inode %lx, dirpos %d)\n",
- (unsigned long)&tdc->f.inode, dirpos);
- DRelease(de, 0);
+ code = afs_dir_GetVerifiedBlob(tdc, dirpos, &entry);
+ if (code) {
+ afs_warn("Corrupt directory (inode %lx, dirpos %d)",
+ (unsigned long)&tdc->f.inode, dirpos);
ReleaseSharedLock(&avc->lock);
afs_PutDCache(tdc);
code = -ENOENT;
goto out;
- }
+ }
+
+ de = (struct DirEntry *)entry.data;
+ ino = afs_calc_inum (avc->f.fid.Cell, avc->f.fid.Fid.Volume,
+ ntohl(de->fid.vnode));
+ len = strlen(de->name);
/* filldir returns -EINVAL when the buffer is full. */
{
code = (*filldir) (dirbuf, de->name, len, offset, ino, type);
AFS_GLOCK();
}
- DRelease(de, 0);
+ DRelease(&entry, 0);
if (code)
break;
offset = dirpos + 1 + ((len + 16) >> 5);
static int
#if defined(FOP_FSYNC_TAKES_DENTRY)
afs_linux_fsync(struct file *fp, struct dentry *dp, int datasync)
+#elif defined(FOP_FSYNC_TAKES_RANGE)
+afs_linux_fsync(struct file *fp, loff_t start, loff_t end, int datasync)
#else
afs_linux_fsync(struct file *fp, int datasync)
#endif
struct inode *ip = FILE_INODE(fp);
cred_t *credp = crref();
+#if defined(FOP_FSYNC_TAKES_RANGE)
+ mutex_lock(&ip->i_mutex);
+#endif
AFS_GLOCK();
code = afs_fsync(VTOAFS(ip), credp);
AFS_GUNLOCK();
+#if defined(FOP_FSYNC_TAKES_RANGE)
+ mutex_unlock(&ip->i_mutex);
+#endif
crfree(credp);
return afs_convert_code(code);
.open = afs_linux_open,
.release = afs_linux_release,
.llseek = default_llseek,
+#ifdef HAVE_LINUX_NOOP_FSYNC
+ .fsync = noop_fsync,
+#else
+ .fsync = simple_sync_file,
+#endif
};
struct file_operations afs_file_fops = {
+#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
+ .aio_read = afs_linux_aio_read,
+ .aio_write = afs_linux_aio_write,
+#else
.read = afs_linux_read,
.write = afs_linux_write,
-#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
- .aio_read = generic_file_aio_read,
- .aio_write = generic_file_aio_write,
#endif
#ifdef HAVE_UNLOCKED_IOCTL
.unlocked_ioctl = afs_unlocked_xioctl,
.llseek = default_llseek,
};
+static struct dentry *
+canonical_dentry(struct inode *ip)
+{
+ struct vcache *vcp = VTOAFS(ip);
+ struct dentry *first = NULL, *ret = NULL, *cur;
+#if defined(D_ALIAS_IS_HLIST)
+ struct hlist_node *p;
+#endif
+
+ /* general strategy:
+ * if vcp->target_link is set, and can be found in ip->i_dentry, use that.
+ * otherwise, use the first dentry in ip->i_dentry.
+ * if ip->i_dentry is empty, use the 'dentry' argument we were given.
+ */
+ /* note that vcp->target_link specifies which dentry to use, but we have
+ * no reference held on that dentry. so, we cannot use or dereference
+ * vcp->target_link itself, since it may have been freed. instead, we only
+ * use it to compare to pointers in the ip->i_dentry list. */
+
+ d_prune_aliases(ip);
+
+# ifdef HAVE_DCACHE_LOCK
+ spin_lock(&dcache_lock);
+# else
+ spin_lock(&ip->i_lock);
+# endif
+
+#if defined(D_ALIAS_IS_HLIST)
+ hlist_for_each_entry(cur, p, &ip->i_dentry, d_alias) {
+#else
+ list_for_each_entry_reverse(cur, &ip->i_dentry, d_alias) {
+#endif
+
+ if (!vcp->target_link || cur == vcp->target_link) {
+ ret = cur;
+ break;
+ }
+
+ if (!first) {
+ first = cur;
+ }
+ }
+ if (!ret && first) {
+ ret = first;
+ }
+
+ vcp->target_link = ret;
+
+# ifdef HAVE_DCACHE_LOCK
+ if (ret) {
+ dget_locked(ret);
+ }
+ spin_unlock(&dcache_lock);
+# else
+ if (ret) {
+ dget(ret);
+ }
+ spin_unlock(&ip->i_lock);
+# endif
+
+ return ret;
+}
/**********************************************************************
* AFS Linux dentry operations
return afs_convert_code(code);
}
+/* vattr_setattr
+ * Set iattr data into vattr. Assume vattr cleared before call.
+ */
+static void
+iattr2vattr(struct vattr *vattrp, struct iattr *iattrp)
+{
+ vattrp->va_mask = iattrp->ia_valid;
+ if (iattrp->ia_valid & ATTR_MODE)
+ vattrp->va_mode = iattrp->ia_mode;
+ if (iattrp->ia_valid & ATTR_UID)
+ vattrp->va_uid = iattrp->ia_uid;
+ if (iattrp->ia_valid & ATTR_GID)
+ vattrp->va_gid = iattrp->ia_gid;
+ if (iattrp->ia_valid & ATTR_SIZE)
+ vattrp->va_size = iattrp->ia_size;
+ if (iattrp->ia_valid & ATTR_ATIME) {
+ vattrp->va_atime.tv_sec = iattrp->ia_atime.tv_sec;
+ vattrp->va_atime.tv_usec = 0;
+ }
+ if (iattrp->ia_valid & ATTR_MTIME) {
+ vattrp->va_mtime.tv_sec = iattrp->ia_mtime.tv_sec;
+ vattrp->va_mtime.tv_usec = 0;
+ }
+ if (iattrp->ia_valid & ATTR_CTIME) {
+ vattrp->va_ctime.tv_sec = iattrp->ia_ctime.tv_sec;
+ vattrp->va_ctime.tv_usec = 0;
+ }
+}
+
+/* vattr2inode
+ * Rewrite the inode cache from the attr. Assumes all vattr fields are valid.
+ */
+void
+vattr2inode(struct inode *ip, struct vattr *vp)
+{
+ ip->i_ino = vp->va_nodeid;
+#ifdef HAVE_LINUX_SET_NLINK
+ set_nlink(ip, vp->va_nlink);
+#else
+ ip->i_nlink = vp->va_nlink;
+#endif
+ ip->i_blocks = vp->va_blocks;
+#ifdef STRUCT_INODE_HAS_I_BLKBITS
+ ip->i_blkbits = AFS_BLKBITS;
+#endif
+#ifdef STRUCT_INODE_HAS_I_BLKSIZE
+ ip->i_blksize = vp->va_blocksize;
+#endif
+ ip->i_rdev = vp->va_rdev;
+ ip->i_mode = vp->va_mode;
+ ip->i_uid = vp->va_uid;
+ ip->i_gid = vp->va_gid;
+ i_size_write(ip, vp->va_size);
+ ip->i_atime.tv_sec = vp->va_atime.tv_sec;
+ ip->i_atime.tv_nsec = 0;
+ ip->i_mtime.tv_sec = vp->va_mtime.tv_sec;
+ /* Set the mtime nanoseconds to the sysname generation number.
+ * This convinces NFS clients that all directories have changed
+ * any time the sysname list changes.
+ */
+ ip->i_mtime.tv_nsec = afs_sysnamegen;
+ ip->i_ctime.tv_sec = vp->va_ctime.tv_sec;
+ ip->i_ctime.tv_nsec = 0;
+}
+
+/* afs_notify_change
+ * Linux version of setattr call. What to change is in the iattr struct.
+ * We need to set bits in both the Linux inode as well as the vcache.
+ */
+static int
+afs_notify_change(struct dentry *dp, struct iattr *iattrp)
+{
+ struct vattr vattr;
+ cred_t *credp = crref();
+ struct inode *ip = dp->d_inode;
+ int code;
+
+ VATTR_NULL(&vattr);
+ iattr2vattr(&vattr, iattrp); /* Convert for AFS vnodeops call. */
+
+ AFS_GLOCK();
+ code = afs_setattr(VTOAFS(ip), &vattr, credp);
+ if (!code) {
+ afs_getattr(VTOAFS(ip), &vattr, credp);
+ vattr2inode(ip, &vattr);
+ }
+ AFS_GUNLOCK();
+ crfree(credp);
+ return afs_convert_code(code);
+}
+
static int
afs_linux_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
struct afs_fakestat_state fakestate;
int locked = 0;
+#ifdef LOOKUP_RCU
+ /* We don't support RCU path walking */
+ if (nd->flags & LOOKUP_RCU)
+ return -ECHILD;
+#endif
+
afs_InitFakeStat(&fakestate);
if (dp->d_inode) {
- parent = dget_parent(dp);
- pvcp = VTOAFS(parent->d_inode);
vcp = VTOAFS(dp->d_inode);
if (vcp == afs_globalVp)
goto good_dentry;
+ parent = dget_parent(dp);
+ pvcp = VTOAFS(parent->d_inode);
+
if ((vcp->mvstat == 1) || (vcp->mvstat == 2)) { /* need to lock */
credp = crref();
AFS_GLOCK();
}
static int
+#if defined(DOP_D_DELETE_TAKES_CONST)
+afs_dentry_delete(const struct dentry *dp)
+#else
afs_dentry_delete(struct dentry *dp)
+#endif
{
if (dp->d_inode && (VTOAFS(dp->d_inode)->f.states & CUnlinked))
return 1; /* bad inode? */
return 0;
}
+#ifdef STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT
+static struct vfsmount *
+afs_dentry_automount(struct path *path)
+{
+ struct dentry *target;
+
+ target = canonical_dentry(path->dentry->d_inode);
+
+ if (target == path->dentry) {
+ dput(target);
+ target = NULL;
+ }
+
+ if (target) {
+ dput(path->dentry);
+ path->dentry = target;
+
+ } else {
+ spin_lock(&path->dentry->d_lock);
+ path->dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT;
+ spin_unlock(&path->dentry->d_lock);
+ }
+
+ return NULL;
+}
+#endif /* STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT */
+
struct dentry_operations afs_dentry_operations = {
.d_revalidate = afs_linux_dentry_revalidate,
.d_delete = afs_dentry_delete,
.d_iput = afs_dentry_iput,
+#ifdef STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT
+ .d_automount = afs_dentry_automount,
+#endif /* STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT */
};
/**********************************************************************
* name is in kernel space at this point.
*/
static int
+#if defined(IOP_CREATE_TAKES_UMODE_T)
+afs_linux_create(struct inode *dip, struct dentry *dp, umode_t mode,
+ struct nameidata *nd)
+#else
#ifdef IOP_CREATE_TAKES_NAMEIDATA
afs_linux_create(struct inode *dip, struct dentry *dp, int mode,
struct nameidata *nd)
#else
afs_linux_create(struct inode *dip, struct dentry *dp, int mode)
#endif
+#endif
{
struct vattr vattr;
cred_t *credp = crref();
afs_getattr(vcp, &vattr, credp);
afs_fill_inode(ip, &vattr);
insert_inode_hash(ip);
+#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
dp->d_op = &afs_dentry_operations;
+#endif
dp->d_time = hgetlo(VTOAFS(dip)->f.m.DataVersion);
d_instantiate(dp, ip);
}
if (vcp) {
struct vattr vattr;
+ struct vcache *parent_vc = VTOAFS(dip);
+
+ if (parent_vc == vcp) {
+ /* This is possible if the parent dir is a mountpoint to a volume,
+ * and the dir entry we looked up is a mountpoint to the same
+ * volume. Linux cannot cope with this, so return an error instead
+ * of risking a deadlock or panic. */
+ afs_PutVCache(vcp);
+ code = EDEADLK;
+ AFS_GUNLOCK();
+ goto done;
+ }
ip = AFSTOV(vcp);
afs_getattr(vcp, &vattr, credp);
if (hlist_unhashed(&ip->i_hash))
insert_inode_hash(ip);
}
+#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
dp->d_op = &afs_dentry_operations;
+#endif
dp->d_time = hgetlo(VTOAFS(dip)->f.m.DataVersion);
AFS_GUNLOCK();
if (ip && S_ISDIR(ip->i_mode)) {
+ int retry = 1;
struct dentry *alias;
- /* Try to invalidate an existing alias in favor of our new one */
- alias = d_find_alias(ip);
- /* But not if it's disconnected; then we want d_splice_alias below */
- if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
- if (d_invalidate(alias) == 0) {
- dput(alias);
- } else {
- iput(ip);
- crfree(credp);
- return alias;
+ while (retry) {
+ retry = 0;
+
+ /* Try to invalidate an existing alias in favor of our new one */
+ alias = d_find_alias(ip);
+ /* But not if it's disconnected; then we want d_splice_alias below */
+ if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
+ if (d_invalidate(alias) == 0) {
+ /* there may be more aliases; try again until we run out */
+ retry = 1;
+ }
}
+
+ dput(alias);
}
+
+#ifdef STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT
+ ip->i_flags |= S_AUTOMOUNT;
+#endif
}
newdp = d_splice_alias(ip, dp);
+ done:
crfree(credp);
/* It's ok for the file to not be found. That's noted by the caller by
}
static int
+#if defined(IOP_MKDIR_TAKES_UMODE_T)
+afs_linux_mkdir(struct inode *dip, struct dentry *dp, umode_t mode)
+#else
afs_linux_mkdir(struct inode *dip, struct dentry *dp, int mode)
+#endif
{
int code;
cred_t *credp = crref();
afs_getattr(tvcp, &vattr, credp);
afs_fill_inode(ip, &vattr);
+#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
dp->d_op = &afs_dentry_operations;
+#endif
dp->d_time = hgetlo(VTOAFS(dip)->f.m.DataVersion);
d_instantiate(dp, ip);
}
rehash = newdp;
}
+#if defined(D_COUNT_INT)
+ spin_lock(&olddp->d_lock);
+ if (olddp->d_count > 1) {
+ spin_unlock(&olddp->d_lock);
+ shrink_dcache_parent(olddp);
+ } else
+ spin_unlock(&olddp->d_lock);
+#else
if (atomic_read(&olddp->d_count) > 1)
shrink_dcache_parent(olddp);
+#endif
AFS_GLOCK();
code = afs_rename(VTOAFS(oldip), (char *)oldname, VTOAFS(newip), (char *)newname, credp);
{
int code;
cred_t *credp = crref();
- uio_t tuio;
+ struct uio tuio;
struct iovec iov;
setup_uio(&tuio, &iov, target, (afs_offs_t) 0, maxlen, UIO_READ, seg);
int code;
char *name;
- name = osi_Alloc(PATH_MAX);
+ name = kmalloc(PATH_MAX, GFP_NOFS);
if (!name) {
return -EIO;
}
AFS_GUNLOCK();
if (code < 0) {
- goto out;
+ return code;
}
name[code] = '\0';
- code = vfs_follow_link(nd, name);
+ nd_set_link(nd, name);
+ return 0;
+}
-out:
- osi_Free(name, PATH_MAX);
+static void
+afs_linux_put_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *name = nd_get_link(nd);
- return code;
+ if (name && !IS_ERR(name))
+ kfree(name);
}
#endif /* USABLE_KERNEL_PAGE_SYMLINK_CACHE */
int chunk, struct pagevec *lrupv,
struct afs_pagecopy_task *task) {
loff_t offset = page_offset(page);
+ struct inode *cacheinode = cachefp->f_dentry->d_inode;
struct page *newpage, *cachepage;
struct address_space *cachemapping;
int pageindex;
int code = 0;
- cachemapping = cachefp->f_dentry->d_inode->i_mapping;
+ cachemapping = cacheinode->i_mapping;
newpage = NULL;
cachepage = NULL;
+ /* If we're trying to read a page that's past the end of the disk
+ * cache file, then just return a zeroed page */
+ if (AFS_CHUNKOFFSET(offset) >= i_size_read(cacheinode)) {
+ zero_user_segment(page, 0, PAGE_CACHE_SIZE);
+ SetPageUptodate(page);
+ if (task)
+ unlock_page(page);
+ return 0;
+ }
+
/* From our offset, we now need to work out which page in the disk
* file it corresponds to. This will be fun ... */
pageindex = (offset - AFS_CHUNKTOBASE(chunk)) >> PAGE_CACHE_SHIFT;
{
afs_int32 code;
char *address;
- uio_t *auio;
+ struct uio *auio;
struct iovec *iovecp;
struct inode *ip = FILE_INODE(fp);
afs_int32 cnt = page_count(pp);
address = kmap(pp);
ClearPageError(pp);
- auio = osi_Alloc(sizeof(uio_t));
- iovecp = osi_Alloc(sizeof(struct iovec));
+ auio = kmalloc(sizeof(struct uio), GFP_NOFS);
+ iovecp = kmalloc(sizeof(struct iovec), GFP_NOFS);
setup_uio(auio, iovecp, (char *)address, offset, PAGE_SIZE, UIO_READ,
AFS_UIOSYS);
kunmap(pp);
- osi_Free(auio, sizeof(uio_t));
- osi_Free(iovecp, sizeof(struct iovec));
+ kfree(auio);
+ kfree(iovecp);
crfree(credp);
return afs_convert_code(code);
struct list_head *page_list, unsigned num_pages)
{
afs_int32 page_ix;
- uio_t *auio;
+ struct uio *auio;
afs_offs_t offset;
struct iovec* iovecp;
struct nocache_read_request *ancr;
/* background thread must free: iovecp, auio, ancr */
iovecp = osi_Alloc(num_pages * sizeof(struct iovec));
- auio = osi_Alloc(sizeof(uio_t));
+ auio = osi_Alloc(sizeof(struct uio));
auio->uio_iov = iovecp;
auio->uio_iovcnt = num_pages;
auio->uio_flag = UIO_READ;
/* If there is nothing for the background thread to handle,
* it won't be freeing the things that we never gave it */
osi_Free(iovecp, num_pages * sizeof(struct iovec));
- osi_Free(auio, sizeof(uio_t));
+ osi_Free(auio, sizeof(struct uio));
osi_Free(ancr, sizeof(struct nocache_read_request));
}
/* we do not flush, release, or unmap pages--that will be
afs_linux_bypass_readpage(struct file *fp, struct page *pp)
{
cred_t *credp = NULL;
- uio_t *auio;
+ struct uio *auio;
struct iovec *iovecp;
struct nocache_read_request *ancr;
int code;
ClearPageError(pp);
/* receiver frees */
- auio = osi_Alloc(sizeof(uio_t));
+ auio = osi_Alloc(sizeof(struct uio));
iovecp = osi_Alloc(sizeof(struct iovec));
/* address can be NULL, because we overwrite it with 'pp', below */
char *buffer;
afs_offs_t base;
int code = 0;
- uio_t tuio;
+ struct uio tuio;
struct iovec iovec;
int f_flags = 0;
* Check access rights - returns error if can't check or permission denied.
*/
static int
-#ifdef IOP_PERMISSION_TAKES_NAMEIDATA
+#if defined(IOP_PERMISSION_TAKES_FLAGS)
+afs_linux_permission(struct inode *ip, int mode, unsigned int flags)
+#elif defined(IOP_PERMISSION_TAKES_NAMEIDATA)
afs_linux_permission(struct inode *ip, int mode, struct nameidata *nd)
#else
afs_linux_permission(struct inode *ip, int mode)
#endif
{
int code;
- cred_t *credp = crref();
+ cred_t *credp;
int tmp = 0;
+ /* Check for RCU path walking */
+#if defined(IOP_PERMISSION_TAKES_FLAGS)
+ if (flags & IPERM_FLAG_RCU)
+ return -ECHILD;
+#elif defined(MAY_NOT_BLOCK)
+ if (mode & MAY_NOT_BLOCK)
+ return -ECHILD;
+#endif
+
+ credp = crref();
AFS_GLOCK();
if (mode & MAY_EXEC)
tmp |= VEXEC;
}
#endif
+#ifndef STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT
+static void *
+afs_linux_dir_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct dentry **dpp;
+ struct dentry *target;
+
+ target = canonical_dentry(dentry->d_inode);
+
+# ifdef STRUCT_NAMEIDATA_HAS_PATH
+ dpp = &nd->path.dentry;
+# else
+ dpp = &nd->dentry;
+# endif
+
+ dput(*dpp);
+
+ if (target) {
+ *dpp = target;
+ } else {
+ *dpp = dget(dentry);
+ }
+
+ return NULL;
+}
+#endif /* !STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT */
+
static struct inode_operations afs_file_iops = {
.permission = afs_linux_permission,
.rename = afs_linux_rename,
.getattr = afs_linux_getattr,
.permission = afs_linux_permission,
+#ifndef STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT
+ .follow_link = afs_linux_dir_follow_link,
+#endif
};
/* We really need a separate symlink set of ops, since do_follow_link()
#else /* !defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE) */
.readlink = afs_linux_readlink,
.follow_link = afs_linux_follow_link,
+ .put_link = afs_linux_put_link,
#endif /* USABLE_KERNEL_PAGE_SYMLINK_CACHE */
.setattr = afs_notify_change,
};