DEVEL15-linux-nfstrans-updates-20080630
[openafs.git] / src / afs / LINUX / osi_misc.c
index 51755ad..bbffb08 100644 (file)
@@ -17,6 +17,7 @@
 RCSID
     ("$Header$");
 
+#include <linux/module.h> /* early to avoid printf->printk mapping */
 #include "afs/sysincludes.h"
 #include "afsincludes.h"
 #include "afs/afs_stats.h"
@@ -25,286 +26,11 @@ RCSID
 #endif
 #if defined(AFS_LINUX26_ENV)
 #include "h/namei.h"
+#include "h/kthread.h"
 #endif
 
-#if defined(AFS_LINUX24_ENV)
-/* Lookup name and return vnode for same. */
-int
-osi_lookupname(char *aname, uio_seg_t seg, int followlink,
-                       vnode_t ** dirvpp, struct dentry **dpp)
-{
-    int code;
-    extern struct nameidata afs_cacheNd;
-    struct nameidata *nd = &afs_cacheNd;
-
-    code = ENOENT;
-    if (seg == AFS_UIOUSER) {
-       code =
-           followlink ? user_path_walk(aname,
-                                       nd) : user_path_walk_link(aname, nd);
-    } else {
-#if defined(AFS_LINUX26_ENV)
-       code = path_lookup(aname, followlink ? LOOKUP_FOLLOW : 0, nd);
-#else
-       if (path_init(aname, followlink ? LOOKUP_FOLLOW : 0, nd))
-           code = path_walk(aname, nd);
-#endif
-    }
-
-    if (!code) {
-       if (nd->dentry->d_inode) {
-           *dpp = dget(nd->dentry);
-           code = 0;
-       } else {
-           code = ENOENT;
-           path_release(nd);
-       }
-    }
-    return code;
-}
-#else
-int
-osi_lookupname(char *aname, uio_seg_t seg, int followlink, vnode_t ** dirvpp,
-              struct dentry **dpp)
-{
-    struct dentry *dp = NULL;
-    int code;
-
-    code = ENOENT;
-    if (seg == AFS_UIOUSER) {
-       dp = followlink ? namei(aname) : lnamei(aname);
-    } else {
-       dp = lookup_dentry(aname, NULL, followlink ? 1 : 0);
-    }
-
-    if (dp && !IS_ERR(dp)) {
-       if (dp->d_inode) {
-           *dpp = dp;
-           code = 0;
-       } else
-           dput(dp);
-    }
-
-    return code;
-}
-#endif
-
-/* Intialize cache device info and fragment size for disk cache partition. */
-int
-osi_InitCacheInfo(char *aname)
-{
-    int code;
-    struct dentry *dp;
-    extern ino_t cacheInode;
-    extern struct osi_dev cacheDev;
-    extern afs_int32 afs_fsfragsize;
-    extern struct super_block *afs_cacheSBp;
-    code = osi_lookupname(aname, AFS_UIOSYS, 1, NULL, &dp);
-    if (code)
-       return ENOENT;
-
-    cacheInode = dp->d_inode->i_ino;
-    cacheDev.dev = dp->d_inode->i_sb->s_dev;
-    afs_fsfragsize = dp->d_inode->i_sb->s_blocksize - 1;
-    afs_cacheSBp = dp->d_inode->i_sb;
-
-    dput(dp);
-
-    return 0;
-}
-
-
-#define FOP_READ(F, B, C) (F)->f_op->read(F, B, (size_t)(C), &(F)->f_pos)
-#define FOP_WRITE(F, B, C) (F)->f_op->write(F, B, (size_t)(C), &(F)->f_pos)
-
-/* osi_rdwr
- * Seek, then read or write to an open inode. addrp points to data in
- * kernel space.
- */
-int
-osi_rdwr(int rw, struct osi_file *file, caddr_t addrp, size_t asize,
-        size_t * resid)
-{
-    int code = 0;
-    KERNEL_SPACE_DECL;
-    struct file *filp = &file->file;
-    off_t offset = file->offset;
-    unsigned long savelim;
-
-    /* Seek to the desired position. Return -1 on error. */
-    if (filp->f_op->llseek) {
-       if (filp->f_op->llseek(filp, (loff_t) offset, 0) != offset)
-           return -1;
-    } else
-       filp->f_pos = offset;
-
-    savelim = current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur;
-    current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
-
-    /* Read/Write the data. */
-    TO_USER_SPACE();
-    if (rw == UIO_READ)
-       code = FOP_READ(filp, addrp, asize);
-    else if (rw == UIO_WRITE)
-       code = FOP_WRITE(filp, addrp, asize);
-    else                       /* all is well? */
-       code = asize;
-    TO_KERNEL_SPACE();
-
-    current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur = savelim;
-
-    if (code >= 0) {
-       *resid = asize - code;
-       return 0;
-    } else
-       return -1;
-}
-
-/* This variant is called from AFS read/write routines and takes a uio
- * struct and, if successful, returns 0.
- */
-int
-osi_file_uio_rdwr(struct osi_file *osifile, uio_t * uiop, int rw)
-{
-    struct file *filp = &osifile->file;
-    KERNEL_SPACE_DECL;
-    int code = 0;
-    struct iovec *iov;
-    int count;
-    unsigned long savelim;
-
-    savelim = current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur;
-    current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
-
-    if (uiop->uio_seg == AFS_UIOSYS)
-       TO_USER_SPACE();
-
-    filp->f_pos = uiop->uio_offset;
-    while (code == 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
-       iov = uiop->uio_iov;
-       count = iov->iov_len;
-       if (count == 0) {
-           uiop->uio_iov++;
-           uiop->uio_iovcnt--;
-           continue;
-       }
-
-       if (rw == UIO_READ)
-           code = FOP_READ(filp, iov->iov_base, count);
-       else
-           code = FOP_WRITE(filp, iov->iov_base, count);
-
-       if (code < 0) {
-           code = -code;
-           break;
-       } else if (code == 0) {
-           /*
-            * This is bad -- we can't read any more data from the
-            * file, but we have no good way of signaling a partial
-            * read either.
-            */
-           code = EIO;
-           break;
-       }
-
-       iov->iov_base += code;
-       iov->iov_len -= code;
-       uiop->uio_resid -= code;
-       uiop->uio_offset += code;
-       code = 0;
-    }
-
-    if (uiop->uio_seg == AFS_UIOSYS)
-       TO_KERNEL_SPACE();
-
-    current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur = savelim;
-
-    return code;
-}
-
-/* setup_uio 
- * Setup a uio struct.
- */
-void
-setup_uio(uio_t * uiop, struct iovec *iovecp, const char *buf, afs_offs_t pos,
-         int count, uio_flag_t flag, uio_seg_t seg)
-{
-    iovecp->iov_base = (char *)buf;
-    iovecp->iov_len = count;
-    uiop->uio_iov = iovecp;
-    uiop->uio_iovcnt = 1;
-    uiop->uio_offset = pos;
-    uiop->uio_seg = seg;
-    uiop->uio_resid = count;
-    uiop->uio_flag = flag;
-}
-
-
-/* uiomove
- * UIO_READ : dp -> uio
- * UIO_WRITE : uio -> dp
- */
-int
-uiomove(char *dp, int length, uio_flag_t rw, uio_t * uiop)
-{
-    int count;
-    struct iovec *iov;
-    int code;
-
-    while (length > 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
-       iov = uiop->uio_iov;
-       count = iov->iov_len;
-
-       if (!count) {
-           uiop->uio_iov++;
-           uiop->uio_iovcnt--;
-           continue;
-       }
-
-       if (count > length)
-           count = length;
-
-       switch (uiop->uio_seg) {
-       case AFS_UIOSYS:
-           switch (rw) {
-           case UIO_READ:
-               memcpy(iov->iov_base, dp, count);
-               break;
-           case UIO_WRITE:
-               memcpy(dp, iov->iov_base, count);
-               break;
-           default:
-               printf("uiomove: Bad rw = %d\n", rw);
-               return -EINVAL;
-           }
-           break;
-       case AFS_UIOUSER:
-           switch (rw) {
-           case UIO_READ:
-               AFS_COPYOUT(dp, iov->iov_base, count, code);
-               break;
-           case UIO_WRITE:
-               AFS_COPYIN(iov->iov_base, dp, count, code);
-               break;
-           default:
-               printf("uiomove: Bad rw = %d\n", rw);
-               return -EINVAL;
-           }
-           break;
-       default:
-           printf("uiomove: Bad seg = %d\n", uiop->uio_seg);
-           return -EINVAL;
-       }
-
-       dp += count;
-       length -= count;
-       iov->iov_base += count;
-       iov->iov_len -= count;
-       uiop->uio_offset += count;
-       uiop->uio_resid -= count;
-    }
-    return 0;
-}
+int afs_osicred_initialized = 0;
+struct AFS_UCRED afs_osi_cred;
 
 void
 afs_osi_SetTime(osi_timeval_t * tvp)
@@ -339,167 +65,155 @@ afs_osi_SetTime(osi_timeval_t * tvp)
 #endif
 }
 
-/* Free all the pages on any of the vnodes in the vlru. Must be done before
- * freeing all memory.
- */
 void
-osi_linux_free_inode_pages(void)
+osi_linux_mask(void)
 {
-    int i;
-    struct vcache *tvc;
-    struct inode *ip;
-    extern struct vcache *afs_vhashT[VCSIZE];
+    SIG_LOCK(current);
+    sigfillset(&current->blocked);
+    RECALC_SIGPENDING(current);
+    SIG_UNLOCK(current);
+}
 
-    for (i = 0; i < VCSIZE; i++) {
-       for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
-           ip = AFSTOI(tvc);
 #if defined(AFS_LINUX24_ENV)
-           if (ip->i_data.nrpages) {
-#else
-           if (ip->i_nrpages) {
+/* LOOKUP_POSITIVE is becoming the default */
+#ifndef LOOKUP_POSITIVE
+#define LOOKUP_POSITIVE 0
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-               truncate_inode_pages(&ip->i_data, 0);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15)
-               truncate_inode_pages(ip, 0);
+/* Lookup name and return vnode for same. */
+int
+osi_lookupname_internal(char *aname, int followlink, struct vfsmount **mnt,
+                       struct dentry **dpp)
+{
+    int code;
+    struct nameidata nd;
+    int flags = LOOKUP_POSITIVE;
+    code = ENOENT;
+
+    if (followlink)
+       flags |= LOOKUP_FOLLOW;
+#if defined(AFS_LINUX26_ENV)
+    code = path_lookup(aname, flags, &nd);
 #else
-               invalidate_inode_pages(ip);
+    if (path_init(aname, flags, &nd))
+        code = path_walk(aname, &nd);
 #endif
-#if defined(AFS_LINUX24_ENV)
-               if (ip->i_data.nrpages) {
+
+    if (!code) {
+#if defined(STRUCT_NAMEIDATA_HAS_PATH)
+       *dpp = dget(nd.path.dentry);
+        if (mnt)
+           *mnt = mntget(nd.path.mnt);
+       path_put(&nd.path);
 #else
-               if (ip->i_nrpages) {
+       *dpp = dget(nd.dentry);
+        if (mnt)
+           *mnt = mntget(nd.mnt);
+       path_release(&nd);
 #endif
-                   printf("Failed to invalidate all pages on inode 0x%lx\n",
-                          (unsigned long)ip);
-               }
-           }
-       }
     }
+    return code;
 }
-
-void
-osi_clear_inode(struct inode *ip)
+int
+osi_lookupname(char *aname, uio_seg_t seg, int followlink, 
+                       struct dentry **dpp)
 {
-    cred_t *credp = crref();
-    struct vcache *vcp = ITOAFS(ip);
-
-#if defined(AFS_LINUX24_ENV)
-    if (atomic_read(&ip->i_count) > 1)
-       printf("afs_put_inode: ino %ld (0x%lx) has count %ld\n",
-              (long)ip->i_ino, (unsigned long)ip,
-              (long)atomic_read(&ip->i_count));
-#else
-    if (ip->i_count > 1)
-       printf("afs_put_inode: ino %ld (0x%lx) has count %ld\n",
-              (long)ip->i_ino, (unsigned long)ip, (long)ip->i_count);
-#endif
-
-    afs_InactiveVCache(vcp, credp);
-    ObtainWriteLock(&vcp->lock, 504);
-    ip->i_nlink = 0;           /* iput checks this after calling this routine. */
-    ip->i_state = I_CLEAR;
-    ReleaseWriteLock(&vcp->lock);
-    crfree(credp);
+    int code;
+    char *tname;
+    code = ENOENT;
+    if (seg == AFS_UIOUSER) {
+        tname = getname(aname);
+        if (IS_ERR(tname)) 
+            return PTR_ERR(tname);
+    } else {
+        tname = aname;
+    }
+    code = osi_lookupname_internal(tname, followlink, NULL, dpp);   
+    if (seg == AFS_UIOUSER) {
+        putname(tname);
+    }
+    return code;
 }
-
-#if !defined(AFS_LINUX26_ENV)
-/* iput an inode. Since we still have a separate inode pool, we don't want
- * to call iput on AFS inodes, since they would then end up on Linux's
- * inode_unsed list.
- */
-void
-osi_iput(struct inode *ip)
+#else
+int
+osi_lookupname(char *aname, uio_seg_t seg, int followlink, struct dentry **dpp)
 {
-    extern struct vfs *afs_globalVFS;
-
-    AFS_GLOCK();
-
-    if (afs_globalVFS && ip->i_sb != afs_globalVFS)
-       osi_Panic("IPUT Not an afs inode\n");
+    struct dentry *dp = NULL;
+    int code;
 
-#if defined(AFS_LINUX24_ENV)
-    if (atomic_read(&ip->i_count) == 0)
-#else
-    if (ip->i_count == 0)
-#endif
-       osi_Panic("IPUT Bad refCount %d on inode 0x%x\n",
-#if defined(AFS_LINUX24_ENV)
-                 atomic_read(&ip->i_count),
-#else
-                 ip->i_count,
-#endif
-                               ip);
+    code = ENOENT;
+    if (seg == AFS_UIOUSER) {
+       dp = followlink ? namei(aname) : lnamei(aname);
+    } else {
+       dp = lookup_dentry(aname, NULL, followlink ? 1 : 0);
+    }
 
-#if defined(AFS_LINUX24_ENV)
-    if (atomic_dec_and_test(&ip->i_count))
-#else
-    if (!--ip->i_count)
-#endif
-                                          {
-       osi_clear_inode(ip);
-       ip->i_state = 0;
+    if (dp && !IS_ERR(dp)) {
+       if (dp->d_inode) {
+           *dpp = dp;
+           code = 0;
+       } else
+           dput(dp);
     }
-    AFS_GUNLOCK();
+
+    return code;
 }
 #endif
 
-/* check_bad_parent() : Checks if this dentry's vcache is a root vcache
- * that has its mvid (parent dir's fid) pointer set to the wrong directory
- * due to being mounted in multiple points at once. If so, check_bad_parent()
- * calls afs_lookup() to correct the vcache's mvid, as well as the volume's
- * dotdotfid and mtpoint fid members.
- * Parameters:
- *  dp - dentry to be checked.
- * Return Values:
- *  None.
- * Sideeffects:
- *   This dentry's vcache's mvid will be set to the correct parent directory's
- *   fid.
- *   This root vnode's volume will have its dotdotfid and mtpoint fids set
- *    to the correct parent and mountpoint fids.
- */
 
-void
-check_bad_parent(struct dentry *dp)
+#ifdef AFS_LINUX26_ENV
+/* This is right even for Linux 2.4, but on that version d_path is inline
+ * and implemented in terms of __d_path, which is not exported.
+ */
+int osi_abspath(char *aname, char *buf, int buflen,
+               int followlink, char **pathp)
 {
-    cred_t *credp;
-    struct vcache *vcp = ITOAFS(dp->d_inode), *avc = NULL;
-    struct vcache *pvc = ITOAFS(dp->d_parent->d_inode);
-
-    if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) {        /* bad parent */
-       credp = crref();
+    struct dentry *dp = NULL;
+    struct vfsmount *mnt = NULL;
+    char *tname, *path;
+    int code;
 
+    code = ENOENT;
+    tname = getname(aname);
+    if (IS_ERR(tname)) 
+       return -PTR_ERR(tname);
+    code = osi_lookupname_internal(tname, followlink, &mnt, &dp);   
+    if (!code) {
+#if defined(D_PATH_TAKES_STRUCT_PATH)
+       struct path p = { mnt, dp };
+       path = d_path(&p, buf, buflen);
+#else
+       path = d_path(dp, mnt, buf, buflen);
+#endif
 
-       /* force a lookup, so vcp->mvid is fixed up */
-       afs_lookup(pvc, dp->d_name.name, &avc, credp);
-       if (!avc || vcp != avc) {       /* bad, very bad.. */
-           afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
-                      "check_bad_parent: bad pointer returned from afs_lookup origvc newvc dentry",
-                      ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, avc,
-                      ICL_TYPE_POINTER, dp);
+       if (IS_ERR(path)) {
+           code = -PTR_ERR(path);
+       } else {
+           *pathp = path;
        }
-       if (avc)
-           AFS_RELE(avc);
-       crfree(credp);
+
+       dput(dp);
+       mntput(mnt);
     }
-    /* if bad parent */
-    return;
+
+    putname(tname);
+    return code;
 }
 
-struct task_struct *rxk_ListenerTask;
 
-void
-osi_linux_mask(void)
+/* This could use some work, and support on more platforms. */
+int afs_thread_wrapper(void *rock)
 {
-    SIG_LOCK(current);
-    sigfillset(&current->blocked);
-    RECALC_SIGPENDING(current);
-    SIG_UNLOCK(current);
+    void (*proc)(void) = rock;
+    __module_get(THIS_MODULE);
+    AFS_GLOCK();
+    (*proc)();
+    AFS_GUNLOCK();
+    module_put(THIS_MODULE);
+    return 0;
 }
 
-void
-osi_linux_rxkreg(void)
+void afs_start_thread(void (*proc)(void), char *name)
 {
-    rxk_ListenerTask = current;
+    kthread_run(afs_thread_wrapper, proc, "%s", name);
 }
+#endif