#include "afs/sysincludes.h"
#include "afsincludes.h"
#include "afs/afs_stats.h"
-#include "afs/afs_osidnlc.h"
#include "h/mm.h"
#ifdef HAVE_MM_INLINE_H
#include "h/mm_inline.h"
extern int afs_xioctl(struct inode *ip, struct file *fp, unsigned int com,
unsigned long arg);
+#if defined(HAVE_UNLOCKED_IOCTL) || defined(HAVE_COMPAT_IOCTL)
+static long afs_unlocked_xioctl(struct file *fp, unsigned int com,
+ unsigned long arg) {
+ return afs_xioctl(FILE_INODE(fp), fp, com, arg);
+
+}
+#endif
/* We need to detect unmap's after close. To do that, we need our own
* vm_operations_struct's. And we need to set them up for both the
return -code;
}
-/* afs_Close is called from release, since release is used to handle all
- * file closings. In addition afs_linux_flush is called from sys_close to
- * handle flushing the data back to the server. The kicker is that we could
- * ignore flush completely if only sys_close took it's return value from
- * fput. See afs_linux_flush for notes on interactions between release and
- * flush.
- */
static int
afs_linux_release(struct inode *ip, struct file *fp)
{
- int code = 0;
- cred_t *credp = crref();
struct vcache *vcp = ITOAFS(ip);
+ cred_t *credp = crref();
+ int code = 0;
#ifdef AFS_LINUX24_ENV
lock_kernel();
#endif
AFS_GLOCK();
- if (vcp->flushcnt) {
- vcp->flushcnt--; /* protected by AFS global lock. */
- } else {
- code = afs_close(vcp, fp->f_flags, credp);
- }
+ code = afs_close(vcp, fp->f_flags, credp);
AFS_GUNLOCK();
#ifdef AFS_LINUX24_ENV
unlock_kernel();
}
/* afs_linux_flush
- * flush is called from sys_close. We could ignore it, but sys_close return
- * code comes from flush, not release. We need to use release to keep
- * the vcache open count correct. Note that flush is called before release
- * (via fput) in sys_close. vcp->flushcnt is a bit of ugliness to avoid
- * races and also avoid calling afs_close twice when closing the file.
- * If we merely checked for opens > 0 in afs_linux_release, then if an
- * new open occurred when storing back the file, afs_linux_release would
- * incorrectly close the file and decrement the opens count. Calling afs_close
- * on the just flushed file is wasteful, since the background daemon will
- * execute the code that finally decides there is nothing to do.
+ * essentially the same as afs_fsync() but we need to get the return
+ * code for the sys_close() here, not afs_linux_release(), so call
+ * afs_StoreAllSegments() with AFS_LASTSTORE
*/
int
afs_linux_flush(struct file *fp)
{
+ struct vrequest treq;
struct vcache *vcp = ITOAFS(FILE_INODE(fp));
- int code = 0;
- cred_t *credp;
+ cred_t *credp = crref();
+ int code;
- /* Only do this on the last close of the file pointer. */
-#if defined(AFS_LINUX24_ENV)
- if (atomic_read(&fp->f_count) > 1)
-#else
- if (fp->f_count > 1)
-#endif
- return 0;
+ AFS_GLOCK();
- credp = crref();
+ code = afs_InitReq(&treq, credp);
+ if (code)
+ goto out;
- AFS_GLOCK();
- code = afs_close(vcp, fp->f_flags, credp);
- vcp->flushcnt++; /* protected by AFS global lock. */
+ ObtainSharedLock(&vcp->lock, 535);
+ if (vcp->execsOrWriters > 0) {
+ UpgradeSToWLock(&vcp->lock, 536);
+ code = afs_StoreAllSegments(vcp, &treq, AFS_SYNC | AFS_LASTSTORE);
+ ConvertWToSLock(&vcp->lock);
+ }
+ code = afs_CheckCode(code, &treq, 54);
+ ReleaseSharedLock(&vcp->lock);
+
+out:
AFS_GUNLOCK();
crfree(credp);
.read = generic_read_dir,
#endif
.readdir = afs_linux_readdir,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = afs_unlocked_xioctl,
+#else
.ioctl = afs_xioctl,
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+ .compat_ioctl = afs_unlocked_xioctl,
+#endif
.open = afs_linux_open,
.release = afs_linux_release,
};
struct file_operations afs_file_fops = {
.read = afs_linux_read,
.write = afs_linux_write,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = afs_unlocked_xioctl,
+#else
.ioctl = afs_xioctl,
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+ .compat_ioctl = afs_unlocked_xioctl,
+#endif
.mmap = afs_linux_mmap,
.open = afs_linux_open,
.flush = afs_linux_flush,
afs_linux_dentry_revalidate(struct dentry *dp)
#endif
{
- char *name = NULL;
- cred_t *credp = crref();
struct vrequest treq;
- struct vcache *lookupvcp = NULL;
- int code, bad_dentry = 1;
- struct sysname_info sysState;
- struct vcache *vcp, *parentvcp;
-
- sysState.allocked = 0;
+ cred_t *credp = NULL;
+ struct vcache *vcp, *pvcp, *tvc = NULL;
+ int valid;
#ifdef AFS_LINUX24_ENV
lock_kernel();
AFS_GLOCK();
vcp = ITOAFS(dp->d_inode);
- parentvcp = ITOAFS(dp->d_parent->d_inode);
+ pvcp = ITOAFS(dp->d_parent->d_inode); /* dget_parent()? */
- /* If it's a negative dentry, then there's nothing to do. */
- if (!vcp || !parentvcp)
- goto done;
+ if (vcp) {
- /* If it is the AFS root, then there's no chance it needs
- * revalidating */
- if (vcp == afs_globalVp) {
- bad_dentry = 0;
- goto done;
- }
+ /* If it's the AFS root no chance it needs revalidating */
+ if (vcp == afs_globalVp)
+ goto good_dentry;
- if ((code = afs_InitReq(&treq, credp)))
- goto done;
-
- Check_AtSys(parentvcp, dp->d_name.name, &sysState, &treq);
- name = sysState.name;
-
- /* First try looking up the DNLC */
- if ((lookupvcp = osi_dnlc_lookup(parentvcp, name, WRITE_LOCK))) {
- /* Verify that the dentry does not point to an old inode */
- if (vcp != lookupvcp)
- goto done;
- /* Check and correct mvid */
- if (*name != '/' && vcp->mvstat == 2)
- check_bad_parent(dp);
- vcache2inode(vcp);
- bad_dentry = 0;
- goto done;
- }
+ /* check_bad_parent()? */
- /* A DNLC lookup failure cannot be trusted. Try a real lookup.
- Make sure to try the real name and not the @sys expansion;
- afs_lookup will expand @sys itself. */
-
- code = afs_lookup(parentvcp, dp->d_name.name, &lookupvcp, credp);
+#ifdef notdef
+ /* If the last looker changes, we should make sure the current
+ * looker still has permission to examine this file. This would
+ * always require a crref() which would be "slow".
+ */
+ if (vcp->last_looker != treq.uid) {
+ if (!afs_AccessOK(vcp, (vType(vcp) == VREG) ? PRSFS_READ : PRSFS_LOOKUP, &treq, CHECK_MODE_BITS))
+ goto bad_dentry;
- /* Verify that the dentry does not point to an old inode */
- if (vcp != lookupvcp)
- goto done;
+ vcp->last_looker = treq.uid;
+ }
+#endif
- bad_dentry = 0;
+ /* If the parent's DataVersion has changed or the vnode
+ * is longer valid, we need to do a full lookup. VerifyVCache
+ * isn't enough since the vnode may have been renamed.
+ */
+ if (hgetlo(pvcp->m.DataVersion) > dp->d_time || !(vcp->states & CStatd)) {
+
+ credp = crref();
+ if (afs_InitReq(&treq, credp))
+ goto bad_dentry;
+ afs_lookup(pvcp, dp->d_name.name, &tvc, credp);
+ if (!tvc || tvc != vcp)
+ goto bad_dentry;
+ if (afs_VerifyVCache(vcp, &treq)) /* update inode attributes */
+ goto bad_dentry;
+
+ dp->d_time = hgetlo(pvcp->m.DataVersion);
+ }
+
+ } else {
+ if (hgetlo(pvcp->m.DataVersion) > dp->d_time)
+ goto bad_dentry;
+
+ /* No change in parent's DataVersion so this negative
+ * lookup is still valid.
+ */
+ }
+
+ good_dentry:
+ valid = 1;
done:
/* Clean up */
- if (lookupvcp)
- afs_PutVCache(lookupvcp);
- if (sysState.allocked)
- osi_FreeLargeSpace(name);
-
+ if (tvc)
+ afs_PutVCache(tvc);
AFS_GUNLOCK();
+ if (credp)
+ crfree(credp);
- if (bad_dentry) {
+ if (!valid) {
shrink_dcache_parent(dp);
d_drop(dp);
}
-
#ifdef AFS_LINUX24_ENV
unlock_kernel();
#endif
- crfree(credp);
+ return valid;
- return !bad_dentry;
+ bad_dentry:
+ valid = 0;
+ goto done;
}
-#if !defined(AFS_LINUX26_ENV)
+#if !defined(AFS_LINUX24_ENV)
/* afs_dentry_iput */
static void
afs_dentry_iput(struct dentry *dp, struct inode *ip)
{
- int isglock;
-
- if (ICL_SETACTIVE(afs_iclSetp)) {
- isglock = ISAFS_GLOCK();
- if (!isglock) AFS_GLOCK();
- afs_Trace3(afs_iclSetp, CM_TRACE_DENTRYIPUT, ICL_TYPE_POINTER, ip,
- ICL_TYPE_STRING, dp->d_parent->d_name.name,
- ICL_TYPE_STRING, dp->d_name.name);
- if (!isglock) AFS_GUNLOCK();
- }
-
osi_iput(ip);
}
#endif
static int
afs_dentry_delete(struct dentry *dp)
{
- int isglock;
- if (ICL_SETACTIVE(afs_iclSetp)) {
- isglock = ISAFS_GLOCK();
- if (!isglock) AFS_GLOCK();
- afs_Trace3(afs_iclSetp, CM_TRACE_DENTRYDELETE, ICL_TYPE_POINTER,
- dp->d_inode, ICL_TYPE_STRING, dp->d_parent->d_name.name,
- ICL_TYPE_STRING, dp->d_name.name);
- if (!isglock) AFS_GUNLOCK();
- }
-
if (dp->d_inode && (ITOAFS(dp->d_inode)->states & CUnlinked))
return 1; /* bad inode? */
struct dentry_operations afs_dentry_operations = {
.d_revalidate = afs_linux_dentry_revalidate,
.d_delete = afs_dentry_delete,
-#if !defined(AFS_LINUX26_ENV)
+#if !defined(AFS_LINUX24_ENV)
.d_iput = afs_dentry_iput,
#endif
};
#endif
dp->d_op = &afs_dentry_operations;
+ dp->d_time = hgetlo(ITOAFS(dip)->m.DataVersion);
d_instantiate(dp, ip);
}
cred_t *credp = crref();
struct vcache *vcp = NULL;
const char *comp = dp->d_name.name;
+#if 1
+ struct dentry *res = 0;
+#endif
#if defined(AFS_LINUX26_ENV)
lock_kernel();
} else if (S_ISDIR(ip->i_mode)) {
ip->i_op = &afs_dir_iops;
ip->i_fop = &afs_dir_fops;
+ d_prune_aliases(ip);
+ res = d_find_alias(ip);
} else if (S_ISLNK(ip->i_mode)) {
ip->i_op = &afs_symlink_iops;
ip->i_data.a_ops = &afs_symlink_aops;
#endif
}
dp->d_op = &afs_dentry_operations;
+ dp->d_time = hgetlo(ITOAFS(dip)->m.DataVersion);
+#if defined(AFS_LINUX24_ENV)
+ if (res) {
+ if (d_unhashed(res))
+ d_rehash(res);
+ } else
+#endif
d_add(dp, AFSTOI(vcp));
#if defined(AFS_LINUX26_ENV)
/* It's ok for the file to not be found. That's noted by the caller by
* seeing that the dp->d_inode field is NULL.
*/
+#if defined(AFS_LINUX24_ENV)
+ if (code == 0)
+ return res;
+#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,10)
if (code == ENOENT)
return ERR_PTR(0);
}
AFS_GUNLOCK();
- if (!code)
+ if (!code) {
+ __dp->d_time = hgetlo(ITOAFS(dip)->m.DataVersion);
d_move(dp, __dp);
+ }
dput(__dp);
goto out;
tvcp->v.v_fop = &afs_dir_fops;
#endif
dp->d_op = &afs_dentry_operations;
+ dp->d_time = hgetlo(ITOAFS(dip)->m.DataVersion);
d_instantiate(dp, AFSTOI(tvcp));
}
if (pp->index >= end_index + 1 || !offset)
return -EIO;
do_it:
- AFS_GLOCK();
status = afs_linux_writepage_sync(inode, pp, 0, offset);
- AFS_GUNLOCK();
SetPageUptodate(pp);
UnlockPage(pp);
if (status == offset)
base = (pp->index << PAGE_CACHE_SHIFT) + offset;
credp = crref();
+ lock_kernel();
+ AFS_GLOCK();
afs_Trace4(afs_iclSetp, CM_TRACE_UPDATEPAGE, ICL_TYPE_POINTER, vcp,
ICL_TYPE_POINTER, pp, ICL_TYPE_INT32, page_count(pp),
ICL_TYPE_INT32, 99999);
ICL_TYPE_POINTER, pp, ICL_TYPE_INT32, page_count(pp),
ICL_TYPE_INT32, code);
+ AFS_GUNLOCK();
+ unlock_kernel();
crfree(credp);
kunmap(pp);
return code;
}
-static int
-afs_linux_updatepage(struct file *file, struct page *page,
- unsigned long offset, unsigned int count)
-{
- struct dentry *dentry = file->f_dentry;
-
- return afs_linux_writepage_sync(dentry->d_inode, page, offset, count);
-}
#else
/* afs_linux_updatepage
* What one would have thought was writepage - write dirty page to file.
{
int code;
- lock_kernel();
- AFS_GLOCK();
- code = afs_linux_updatepage(file, page, offset, to - offset);
- AFS_GUNLOCK();
- unlock_kernel();
+ code = afs_linux_writepage_sync(file->f_dentry->d_inode, page,
+ offset, to - offset);
+#if !defined(AFS_LINUX26_ENV)
kunmap(page);
+#endif
return code;
}
afs_linux_prepare_write(struct file *file, struct page *page, unsigned from,
unsigned to)
{
+/* sometime between 2.4.0 and 2.4.19, the callers of prepare_write began to
+ call kmap directly instead of relying on us to do it */
+#if !defined(AFS_LINUX26_ENV)
kmap(page);
+#endif
return 0;
}
.follow_link = page_follow_link,
#else
.follow_link = page_follow_link_light,
+ .put_link = page_put_link,
#endif
.setattr = afs_notify_change,
#else