[OpenAFS-devel] Enhancements for AFS web interface

Jeff Riegel riegel@almaden.ibm.com
Wed, 13 Jun 2001 17:55:17 -0700


We have been working on Ufiler, a web interface to AFS, here at IBM Almaden.
The Ufiler server uses Java servlets and a JNI interface to the libuafs
client library, which is included in the openafs distribution.

We have made a number of changes to the AFS client to enhance performance
and stability for the web server.  Most of these enhancements are not
generally applicable to the kernel versions of the client, but may be useful
to others who are trying to serve AFS files over the web using libuafs.

Changes include:

1) Token caching: Functions to set the current "PAG" (not really a process
   authentication group, but a generic identifier for a user session)
   to a specific value, allowing us to switch tokens between different
   threads of the Web server without reauthenticating.

2) Performance enhancements: The main improvement is that we can check
   whether a directory is a mount point in advance and avoid doing a stat
   or contacting the destination cell.  We've also enabled bulk statting
   with kolya's patch from 11/2000.

3) New UAFS API's to call various pioctl functions, etc.

These changes are based on the 5/1/2001 snapshot.  Most of the changes
are enclosed by "#ifdef AFS_WEB_ENHANCEMENTS".

Jeff Riegel
riegel@almaden.ibm.com

diff -Nur --exclude-from=exclude orig-src/afs/UKERNEL/afs_usrops.c src/afs/UKERNEL/afs_usrops.c
--- orig-src/afs/UKERNEL/afs_usrops.c	Sat Apr 14 10:27:43 2001
+++ src/afs/UKERNEL/afs_usrops.c	Wed Jun 13 17:17:13 2001
@@ -637,7 +637,7 @@
     struct usr_inode *ip;
     struct usr_vnode *vp;
 
-    usr_assert(followlink == 0);
+    /*usr_assert(followlink == 0);*/
     usr_assert(dirvpp == NULL);
 
     /*
@@ -2678,6 +2678,7 @@
 	    if (flags & (O_WRONLY|O_RDWR)) {
 		fileMode |= VWRITE;
 	    }
+         if (!fileMode) fileMode = VREAD;  /* since O_RDONLY is 0 */
 	    code = afs_access(fileP, fileMode, u.u_cred);
 	    if (code != 0) {
 		VN_RELE(fileP);
@@ -3807,6 +3808,7 @@
     char *path)
 {
     usr_DIR *dirp;
+    struct usr_vnode *fileP;
     int fd;
 
     /*
@@ -3817,6 +3819,17 @@
 	return NULL;
     }
 
+    fileP = afs_FileTable[fd];
+    if (fileP == NULL) {
+     return NULL;
+    }
+
+    if (fileP->v_type != VDIR) {
+      uafs_close_r(fd);
+      errno = ENOTDIR;
+      return NULL;
+    }
+
     /*
      * Set up the directory structures
      */
@@ -4106,5 +4119,335 @@
     }
     return NULL;
 }
+
+#ifdef AFS_WEB_ENHANCEMENTS
+/*
+ * uafs_klog_nopag
+ * klog but don't allocate a new pag
+ */
+int uafs_klog_nopag(
+    char *user,
+    char *cell,
+    char *passwd,
+    char **reason)
+{
+    int code;
+    afs_int32 password_expires = -1;
+
+    usr_mutex_lock(&osi_authenticate_lock);
+    code = ka_UserAuthenticateGeneral(
+      KA_USERAUTH_VERSION  /*+KA_USERAUTH_DOSETPAG2*/, user,
+          NULL, cell, passwd, 0, &password_expires,
+          0, reason);
+    usr_mutex_unlock(&osi_authenticate_lock);
+    return code;
+}
+
+/*
+ * uafs_getcellstatus
+ * get the cell status
+ */
+int uafs_getcellstatus(char *cell, afs_int32 *status)
+{
+  int rc;
+  struct afs_ioctl iob;
+
+  iob.in = cell;
+  iob.in_size = strlen(cell)+1;
+  iob.out = 0;
+  iob.out_size = 0;
+
+  rc = call_syscall(AFSCALL_PIOCTL, /*path*/0, _VICEIOCTL(35),
+                      (long)&iob, 0, 0);
+
+  if (rc < 0) {
+    errno = rc;
+    return -1;
+  }
+
+  *status = iob.out;
+  return 0;
+}
+
+/*
+ * uafs_getvolquota
+ * Get quota of volume associated with path
+ */
+int uafs_getvolquota(char *path, afs_int32 *BlocksInUse, afs_int32 *MaxQuota)
+{
+  int rc;
+  struct afs_ioctl iob;
+  VolumeStatus *status;
+  char buf[1024];
+
+  iob.in = 0;
+  iob.in_size = 0;
+  iob.out = buf;
+  iob.out_size = 1024;
+
+  rc = call_syscall(AFSCALL_PIOCTL, path, _VICEIOCTL(4),
+                      (long)&iob, 0, 0);
+
+  if (rc != 0) {
+    errno = rc;
+    return -1;
+  }
+
+  status = (VolumeStatus *) buf;
+  *BlocksInUse = status->BlocksInUse;
+  *MaxQuota = status->MaxQuota;
+  return 0;
+}
+
+/*
+ * uafs_setvolquota
+ * Set quota of volume associated with path
+ */
+int uafs_setvolquota(char *path, afs_int32 MaxQuota)
+{
+  int rc;
+  struct afs_ioctl iob;
+  VolumeStatus *status;
+  char buf[1024];
+
+  iob.in = buf;
+  iob.in_size = 1024;
+  iob.out = 0;
+  iob.out_size = 0;
+
+  memset(buf, 0, sizeof(VolumeStatus));
+  status = (VolumeStatus *) buf;
+  status->MaxQuota = MaxQuota;
+  status->MinQuota = -1;
+
+  rc = call_syscall(AFSCALL_PIOCTL, path, _VICEIOCTL(5),
+                      (long)&iob, 0, 0);
+
+  if (rc != 0) {
+    errno = rc;
+    return -1;
+  }
+
+  return 0;
+}
+
+/*
+ * uafs_statmountpoint
+ * Determine whether a dir. is a mount point or not
+ * return 1 if mount point, 0 if not
+ */
+int uafs_statmountpoint(char *path)
+{
+    int retval;
+    int code;
+    char buf[256];
+
+    AFS_GLOCK();
+    retval = uafs_statmountpoint_r(path);
+    AFS_GUNLOCK();
+    return retval;
+}
+
+int uafs_statmountpoint_r(char *path)
+{
+    int code;
+    struct vnode *vp;
+    struct vcache *avc;
+    struct vrequest treq;
+    int r;
+
+    code = uafs_LookupName_noEval(path, afs_CurrentDir, &vp, 0);
+    if (code != 0) {
+     errno = code;
+     return -1;
+    }
+
+    avc = (struct vcache *) vp;
+
+    r = avc->mvstat;
+    VN_RELE(vp);
+    return r;
+}
+
+/*
+ * uafs_LookupName_noEval
+ *
+ * Lookup a file or directory given its path.
+ * Call VN_HOLD on the output vnode if successful.
+ * Returns zero on success, error code on failure.
+ *
+ * Call afs_lookup_noEval on the last component to avoid
+ * evaluating if it's a mount point.
+ *
+ * Note: Caller must hold the AFS global lock.
+ */
+int uafs_LookupName_noEval(
+    char *path,
+    struct usr_vnode *parentVp,
+    struct usr_vnode **vpp,
+    int follow)
+{
+    int code;
+    int linkCount;
+    struct usr_vnode *vp;
+    struct usr_vnode *nextVp;
+    struct usr_vnode *linkVp;
+    char *tmpPath;
+    char *pathP;
+    char *nextPathP;
+
+    AFS_ASSERT_GLOCK();
+
+    /*
+     * Absolute paths must start with the AFS mount point.
+     */
+    if (path[0] != '/') {
+     vp = parentVp;
+    } else {
+     path = uafs_afsPathName(path);
+     if (path == NULL) {
+         return ENOENT;
+     }
+     vp = afs_RootVnode;
+    }
+
+    /*
+     * Loop through the path looking for the new directory
+     */
+    tmpPath = afs_osi_Alloc(strlen(path)+1);
+    usr_assert(tmpPath != NULL);
+    strcpy(tmpPath, path);
+    VN_HOLD(vp);
+    pathP = tmpPath;
+    while (pathP != NULL && *pathP != '\0') {
+     usr_assert(*pathP != '/');
+
+     /*
+      * terminate the current component and skip over slashes
+      */
+     nextPathP = strchr(pathP, '/');
+     if (nextPathP != NULL) {
+         while (*nextPathP == '/') {
+          *(nextPathP++) = '\0';
+         }
+     }
+
+     /*
+      * Don't call afs_lookup on non-directories
+      */
+     if (vp->v_type != VDIR) {
+         VN_RELE(vp);
+         afs_osi_Free(tmpPath, strlen(path)+1);
+         return ENOTDIR;
+     }
+
+     if (vp == afs_RootVnode && strcmp(pathP, "..") == 0) {
+         /*
+          * The AFS root is its own parent
+          */
+         nextVp = afs_RootVnode;
+     } else {
+         /*
+          * We need execute permission to search a directory
+          */
+         code = afs_access(vp, VEXEC, u.u_cred);
+         if (code != 0) {
+          VN_RELE(vp);
+          afs_osi_Free(tmpPath, strlen(path)+1);
+          return code;
+         }
+
+         /*
+          * lookup the next component in the path, we can release the
+          * subdirectory since we hold the global lock
+          */
+         nextVp = NULL;
+         if ((nextPathP != NULL && *nextPathP != '\0'))
+           code = afs_lookup(vp, pathP, &nextVp, u.u_cred);
+         else
+           code = afs_lookup_noEval(vp, pathP, &nextVp, u.u_cred);
+         if (code != 0) {
+          VN_RELE(vp);
+          afs_osi_Free(tmpPath, strlen(path)+1);
+          return code;
+         }
+     }
+
+     /*
+      * Follow symbolic links for parent directories and
+      * for leaves when the follow flag is set.
+      */
+     if ((nextPathP != NULL && *nextPathP != '\0') || follow) {
+         linkCount = 0;
+         while(nextVp->v_type == VLNK) {
+          if (++linkCount > MAX_OSI_LINKS) {
+              VN_RELE(vp);
+              VN_RELE(nextVp);
+              afs_osi_Free(tmpPath, strlen(path)+1);
+              return code;
+          }
+          code = uafs_LookupLink(nextVp, vp, &linkVp);
+          if (code) {
+              VN_RELE(vp);
+              VN_RELE(nextVp);
+              afs_osi_Free(tmpPath, strlen(path)+1);
+              return code;
+          }
+          VN_RELE(nextVp);
+          nextVp = linkVp;
+         }
+     }
+
+     VN_RELE(vp);
+     vp = nextVp;
+     pathP = nextPathP;
+    }
+
+    /*
+     * Special case, nextPathP is non-null if pathname ends in slash
+     */
+    if (nextPathP != NULL && vp->v_type != VDIR) {
+     VN_RELE(vp);
+     afs_osi_Free(tmpPath, strlen(path)+1);
+     return ENOTDIR;
+    }
+
+    afs_osi_Free(tmpPath, strlen(path)+1);
+    *vpp = vp;
+    return 0;
+}
+
+/*
+ * uafs_getRights
+ * Get a list of rights for the current user on path.
+ */
+int uafs_getRights(char *path)
+{
+    int code, rc;
+    struct vnode *vp;
+    int afs_rights;
+
+    AFS_GLOCK();
+    code = uafs_LookupName(path, afs_CurrentDir, &vp, 1);
+    if (code != 0) {
+     errno = code;
+     AFS_GUNLOCK();
+     return -1;
+    }
+
+    afs_rights = PRSFS_READ |
+      PRSFS_WRITE |
+      PRSFS_INSERT |
+      PRSFS_LOOKUP |
+      PRSFS_DELETE |
+      PRSFS_LOCK |
+      PRSFS_ADMINISTER;
+
+    afs_rights = afs_getRights (vp, afs_rights, u.u_cred);
+
+    AFS_GUNLOCK();
+    return afs_rights;
+}
+#endif /* AFS_WEB_ENHANCEMENTS */
 
 #endif /* UKERNEL */
diff -Nur --exclude-from=exclude orig-src/afs/UKERNEL/sysincludes.h src/afs/UKERNEL/sysincludes.h
--- orig-src/afs/UKERNEL/sysincludes.h	Sat Apr 14 10:27:44 2001
+++ src/afs/UKERNEL/sysincludes.h	Mon Jun 11 19:41:41 2001
@@ -932,6 +932,8 @@
 #define usr_cond_signal(A)	assert(pthread_cond_signal(A) == 0)
 #define usr_cond_broadcast(A)	assert(pthread_cond_broadcast(A) == 0)
 #define usr_cond_wait(A,B)	pthread_cond_wait(A,B)
+#define usr_cond_timedwait(A,B,C)  pthread_cond_timedwait(A,B,C)
+
 #define usr_thread_create(A,B,C) \
     do { \
 	pthread_attr_t attr; \
diff -Nur --exclude-from=exclude orig-src/afs/VNOPS/afs_vnop_access.c src/afs/VNOPS/afs_vnop_access.c
--- orig-src/afs/VNOPS/afs_vnop_access.c	Sat Nov  4 02:03:29 2000
+++ src/afs/VNOPS/afs_vnop_access.c	Wed Jun 13 17:28:21 2001
@@ -273,3 +273,28 @@
     }
 }
 
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+/*
+ * afs_getRights
+ * This function is just an interface to afs_GetAccessBits
+ */
+int afs_getRights(OSI_VC_ARG(avc), arights, acred)
+    OSI_VC_DECL(avc);
+    register afs_int32 arights;
+    struct AFS_UCRED *acred;
+{
+    register afs_int32 code;
+    struct vrequest treq;
+    OSI_VC_CONVERT(avc)
+
+    if (code = afs_InitReq(&treq, acred)) return code;
+
+    code = afs_VerifyVCache(avc, &treq);
+    if (code) {
+      code = afs_CheckCode(code, &treq, 16);
+      return code; 
+    }
+
+    return afs_GetAccessBits(avc, arights, &treq);
+}
+#endif /* defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS) */
diff -Nur --exclude-from=exclude orig-src/afs/VNOPS/afs_vnop_create.c src/afs/VNOPS/afs_vnop_create.c
--- orig-src/afs/VNOPS/afs_vnop_create.c	Mon Mar 26 23:06:38 2001
+++ src/afs/VNOPS/afs_vnop_create.c	Fri Jun  1 20:04:30 2001
@@ -177,6 +177,10 @@
 		if ((amode & VWRITE) || len != 0xffffffff) 
 #endif
 		  {
+              /* these lines are needed for write access check */
+              tvc->parentVnode = adp->fid.Fid.Vnode;
+              tvc->parentUnique = adp->fid.Fid.Unique;
+
 		    /* need write mode for these guys */
 		    if (!afs_AccessOK(tvc, PRSFS_WRITE, &treq, CHECK_MODE_BITS)) {
 			afs_PutVCache(tvc, READ_LOCK);
diff -Nur --exclude-from=exclude orig-src/afs/VNOPS/afs_vnop_lookup.c src/afs/VNOPS/afs_vnop_lookup.c
--- orig-src/afs/VNOPS/afs_vnop_lookup.c	Sat Feb 24 07:35:05 2001
+++ src/afs/VNOPS/afs_vnop_lookup.c	Mon Jun 11 17:33:18 2001
@@ -883,6 +883,7 @@
     extern afs_int32 afs_mariner;			/*Writing activity to log?*/
     OSI_VC_CONVERT(adp)
     afs_hyper_t versionNo;
+    int no_read_access = 0;
 
     AFS_STATCNT(afs_lookup);
 #ifdef	AFS_OSF_ENV
@@ -990,6 +991,10 @@
        else adp->last_looker = treq.uid;
     } 
 
+    /* Check for read access as well.  We need read access in order to
+       stat files, but not to stat subdirectories. */
+    if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
+      no_read_access = 1;
 
     /* special case lookup of ".".  Can we check for it sooner in this code,
      * for instance, way up before "redo:" ??
@@ -1011,8 +1016,15 @@
 
     tvc = osi_dnlc_lookup (adp, tname, WRITE_LOCK);
     *avcp = tvc;  /* maybe wasn't initialized, but it is now */
-#ifdef AFS_LINUX22_ENV
     if (tvc) {
+      if (no_read_access && vType(tvc) != VDIR) {
+        /* need read access on dir to stat non-directory */
+        afs_PutVCache(tvc, WRITE_LOCK);
+        *avcp = (struct vcache *)0;
+        code = EACCES;
+        goto done;
+      }
+#ifdef AFS_LINUX22_ENV
       if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
 	AFS_RELE(tvc);
 	*avcp = 0;
@@ -1022,14 +1034,12 @@
 	hit = 1;
 	goto done;
       }
-    }
 #else /* non - LINUX */
-    if (tvc) {
       code = 0;
       hit = 1;
       goto done;
-    }
 #endif /* linux22 */
+    }
 
     {
     register struct dcache *tdc;
@@ -1285,3 +1295,401 @@
 
     return code;
 }
+
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+/* afs_lookup_noEval
+   This function is identical to afs_lookup, except we avoid evaluating
+   mount points.  This is a significant performance enhancement, since
+   we can then list mount point directories without reading their mount
+   locations. */
+#ifdef	AFS_OSF_ENV
+afs_lookup_noEval(adp, ndp)
+    struct vcache *adp;
+    struct nameidata *ndp; {
+    char aname[MAXNAMLEN+1];	/* XXX */
+    struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
+    struct ucred *acred = ndp->ni_cred;
+    int wantparent = ndp->ni_nameiop & WANTPARENT;
+    int opflag = ndp->ni_nameiop & OPFLAG;
+#else	/* AFS_OSF_ENV */
+#if	defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
+afs_lookup_noEval(OSI_VC_ARG(adp), aname, avcp, pnp, flags, rdir, acred)
+    struct pathname *pnp;
+    int flags;
+    struct vnode *rdir;
+#else
+afs_lookup_noEval(adp, aname, avcp, acred)
+#endif
+    OSI_VC_DECL(adp);
+    struct vcache **avcp;
+    char *aname;
+    struct AFS_UCRED *acred; {
+#endif
+    struct vrequest treq;
+    char *tname = (char *)0;
+    register struct vcache *tvc=0;
+    register afs_int32 code;
+    int pass = 0, hit = 0;
+    long dirCookie;
+    extern afs_int32 afs_mariner;			/*Writing activity to log?*/
+    OSI_VC_CONVERT(adp)
+    afs_hyper_t versionNo;
+    int no_read_access = 0;
+
+    AFS_STATCNT(afs_lookup);
+#ifdef	AFS_OSF_ENV
+    ndp->ni_dvp = (struct vnode *)adp;
+    bcopy(ndp->ni_ptr, aname, ndp->ni_namelen);
+    aname[ndp->ni_namelen] = '\0';
+#endif	/* AFS_OSF_ENV */
+
+    *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
+
+    if (code = afs_InitReq(&treq, acred)) { 
+      goto done;
+    }
+
+    /* lookup the name aname in the appropriate dir, and return a cache entry
+      on the resulting fid */
+
+    /*
+     * check for, and handle "@sys" if it's there.  We should be able
+     * to avoid the alloc and the strcpy with a little work, but it's
+     * not pressing.  If there aren't any remote users (ie, via the 
+     * NFS translator), we have a slightly easier job.
+     * the faster way to do this is to check for *aname == '@' and if 
+     * it's there, check for @sys, otherwise, assume there's no @sys 
+     * then, if the lookup fails, check for .*@sys...
+     */
+    if (!AFS_EQ_ATSYS(aname)) {
+      tname = aname;
+    }
+    else {
+	tname = (char *) osi_AllocLargeSpace(AFS_SMALLOCSIZ);
+	if (!afs_nfsexporter) 
+	  strcpy(tname, (afs_sysname ? afs_sysname : SYS_NAME ));
+	else {
+	  register struct unixuser *au;
+	  register afs_int32 error;
+	  au = afs_GetUser(treq.uid, adp->fid.Cell, 0); afs_PutUser(au, 0);	
+	  if (au->exporter) {
+	    error = EXP_SYSNAME(au->exporter, (char *)0, tname);
+	    if (error) 
+	      strcpy(tname, "@sys");
+	  } else {
+	      strcpy(tname, (afs_sysname ? afs_sysname : SYS_NAME ));
+	  }
+	}
+      }
+
+    /* come back to here if we encounter a non-existent object in a read-only
+       volume's directory */
+
+  redo:
+    *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
+
+    if (!(adp->states & CStatd)) {
+	if (code = afs_VerifyVCache2(adp, &treq))
+	  goto done;
+    }
+    else code = 0;
+
+    /* watch for ".." in a volume root */
+    if (adp->mvstat == 2 && tname[0] == '.' && tname[1] == '.' && !tname[2]) {
+	/* looking up ".." in root via special hacks */
+	if (adp->mvid == (struct VenusFid *) 0 || adp->mvid->Fid.Volume == 0) {
+#ifdef	AFS_OSF_ENV
+	    extern struct vcache *afs_globalVp;
+	    if (adp == afs_globalVp) {
+		struct vnode *rvp = (struct vnode *)adp;
+/*
+		ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
+		ndp->ni_dvp = ndp->ni_vp;
+		VN_HOLD(*avcp);
+*/
+		code = ENODEV;
+		goto done;
+	    }
+#endif
+	    code = ENODEV;
+	    goto done;
+	}
+	/* otherwise we have the fid here, so we use it */
+	tvc = afs_GetVCache(adp->mvid, &treq, (afs_int32 *)0,
+			    (struct vcache*)0, 0);
+	afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT,
+		   ICL_TYPE_FID, adp->mvid, ICL_TYPE_POINTER, tvc, 
+		   ICL_TYPE_INT32,  code);
+	*avcp = tvc;
+	code = (tvc ? 0 : ENOENT);
+	hit = 1;
+	if (tvc && !tvc->vrefCount) {
+	    osi_Panic("TT1");
+	}
+	if (code) {
+	    /*printf("LOOKUP GETVCDOTDOT -> %d\n", code);*/
+	}
+	goto done;
+    }
+
+    /* now check the access */
+    if (treq.uid != adp->last_looker) {  
+       if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
+	 *avcp = (struct vcache *)0;
+	 code = EACCES;
+	 goto done;
+       }
+       else adp->last_looker = treq.uid;
+    } 
+
+    /* Check for read access as well.  We need read access in order to
+       stat files, but not to stat subdirectories. */
+    if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
+      no_read_access = 1;
+
+    /* special case lookup of ".".  Can we check for it sooner in this code,
+     * for instance, way up before "redo:" ??
+     * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
+     * invent a lightweight version of GetVCache.
+     */
+    if (tname[0] == '.' && !tname[1]) { /* special case */
+	ObtainReadLock(&afs_xvcache);	
+	osi_vnhold(adp, 0);
+	ReleaseReadLock(&afs_xvcache);	
+      code = 0;
+      *avcp = tvc = adp;
+      hit = 1;
+	if (adp && !adp->vrefCount) {
+	    osi_Panic("TT2");
+	}
+      goto done;
+    }
+
+    tvc = osi_dnlc_lookup (adp, tname, WRITE_LOCK);
+    *avcp = tvc;  /* maybe wasn't initialized, but it is now */
+    if (tvc) {
+      if (no_read_access && vType(tvc) != VDIR) {
+        /* need read access on dir to stat non-directory */
+        afs_PutVCache(tvc, WRITE_LOCK);
+        *avcp = (struct vcache *)0;
+        code = EACCES;
+        goto done;
+      }
+#ifdef AFS_LINUX22_ENV
+      if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
+	AFS_RELE(tvc);
+	*avcp = 0;
+      }
+      else {  
+	code = 0;
+	hit = 1;
+	goto done;
+      }
+#else /* non - LINUX */
+      code = 0;
+      hit = 1;
+      goto done;
+#endif /* linux22 */
+    }
+
+    {
+    register struct dcache *tdc;
+    afs_int32 dirOffset, dirLen;
+    ino_t theDir;
+    struct VenusFid tfid;
+
+    /* now we have to lookup the next fid */
+    tdc = afs_GetDCache(adp, 0, &treq, &dirOffset, &dirLen, 1);
+    if (!tdc) {
+      *avcp = (struct vcache *)0;  /* redundant, but harmless */
+      code = EIO;
+      goto done;
+    }
+
+    /* now we will just call dir package with appropriate inode.
+      Dirs are always fetched in their entirety for now */
+    /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
+    ObtainReadLock(&adp->lock);
+
+    /*
+     * Make sure that the data in the cache is current. There are two
+     * cases we need to worry about:
+     * 1. The cache data is being fetched by another process.
+     * 2. The cache data is no longer valid
+     */
+    while ((adp->states & CStatd)
+	   && (tdc->flags & DFFetching)
+	   && hsame(adp->m.DataVersion, tdc->f.versionNo)) {
+	tdc->flags |= DFWaiting;
+	ReleaseReadLock(&adp->lock);
+	afs_osi_Sleep(&tdc->validPos);
+	ObtainReadLock(&adp->lock);
+    }
+    if (!(adp->states & CStatd)
+	|| !hsame(adp->m.DataVersion, tdc->f.versionNo)) {
+	ReleaseReadLock(&adp->lock);
+	afs_PutDCache(tdc);
+	goto redo;
+    }
+
+    /* Save the version number for when we call osi_dnlc_enter */
+    hset(versionNo, tdc->f.versionNo);
+
+    theDir = tdc->f.inode;
+    code = afs_dir_LookupOffset(&theDir, tname, &tfid.Fid, &dirCookie);
+    if (code == ENOENT && tname == aname) {
+      int len;
+      len = strlen(aname);
+      if (len >= 4 && AFS_EQ_ATSYS(aname+len-4)) {
+	tname = (char *) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
+	afs_HandleAtName(aname, tname, &treq, adp);
+	code = afs_dir_LookupOffset(&theDir, tname, &tfid.Fid, &dirCookie);
+      }
+    }
+    ReleaseReadLock(&adp->lock);
+    afs_PutDCache(tdc);
+
+    /* new fid has same cell and volume */
+    tfid.Cell = adp->fid.Cell;
+    tfid.Fid.Volume = adp->fid.Fid.Volume;
+    afs_Trace4(afs_iclSetp, CM_TRACE_LOOKUP, ICL_TYPE_POINTER, adp, 
+	       ICL_TYPE_STRING, tname,
+	       ICL_TYPE_FID, &tfid, ICL_TYPE_INT32, code);
+
+    if (code) {
+	if (code != ENOENT) {
+	    printf("LOOKUP dirLookupOff -> %d\n", code);
+	}
+	goto done;
+    }  
+
+    /* prefetch some entries, if the dir is currently open.  The variable
+     * dirCookie tells us where to start prefetching from.
+     */
+    if (AFSDOBULK && adp->opens > 0 && !(adp->states & CForeign)) {
+        afs_int32 retry;
+	/* if the entry is not in the cache, or is in the cache,
+	 * but hasn't been statd, then do a bulk stat operation.
+	 */
+        do {
+	   retry = 0;
+	   ObtainReadLock(&afs_xvcache);	
+	   tvc = afs_FindVCache(&tfid, 1, 0, &retry, 0/* !stats,!lru */);
+	   ReleaseReadLock(&afs_xvcache);	
+        } while (tvc && retry);
+
+	if (!tvc || !(tvc->states & CStatd)) {
+	    afs_DoBulkStat(adp, dirCookie, &treq);
+	}
+
+	/* if the vcache isn't usable, release it */
+	if (tvc && !(tvc->states & CStatd)) {
+	    afs_PutVCache(tvc);
+	    tvc = (struct vcache *) 0;
+	}
+    }
+    else tvc = (struct vcache *) 0;
+    
+    /* now get the status info, if we don't already have it */
+    /* This is kind of weird, but we might wind up accidentally calling
+     * RXAFS_Lookup because we happened upon a file which legitimately
+     * has a 0 uniquifier. That is the result of allowing unique to wrap
+     * to 0. This was fixed in AFS 3.4. For CForeigh, Unique == 0 means that
+     * the file has not yet been looked up.
+     */
+    if (!tvc) {
+       afs_int32 cached = 0;
+       if (!tfid.Fid.Unique && (adp->states & CForeign)) {
+	    tvc = afs_LookupVCache(&tfid, &treq, &cached, WRITE_LOCK, 
+				   adp, tname);
+       } 
+       if (!tvc) {  /* lookup failed or wasn't called */
+	    tvc = afs_GetVCache(&tfid, &treq, &cached, (struct vcache*)0,
+				WRITE_LOCK);
+       }
+    } /* if !tvc */
+    } /* sub-block just to reduce stack usage */
+
+    if (tvc) {
+       if (adp->states & CForeign)
+	   tvc->states |= CForeign;
+	tvc->parentVnode = adp->fid.Fid.Vnode;
+	tvc->parentUnique = adp->fid.Fid.Unique;
+	tvc->states &= ~CBulkStat;
+
+	/* we don't evaluate mount points here */
+
+	*avcp = tvc;
+	if (tvc && !tvc->vrefCount) {
+	    osi_Panic("TT3");
+	}
+	code = 0;
+    }
+    else {
+	/* if we get here, we found something in a directory that couldn't
+	   be located (a Multics "connection failure").  If the volume is
+	   read-only, we try flushing this entry from the cache and trying
+	   again. */
+	if (pass == 0) {
+	    struct volume *tv;
+	    tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
+	    if (tv) {
+		if (tv->states & VRO) {
+		    pass = 1;			/* try this *once* */
+		    ObtainWriteLock(&afs_xcbhash, 495);
+		    afs_DequeueCallback(adp);
+		    /* re-stat to get later version */
+		    adp->states &= ~CStatd;
+		    ReleaseWriteLock(&afs_xcbhash);
+		    osi_dnlc_purgedp(adp);
+		    afs_PutVolume(tv, READ_LOCK);
+		    goto redo;
+		}
+		afs_PutVolume(tv, READ_LOCK);
+	    }
+	}
+	code = ENOENT;
+    }
+
+done:
+    /* put the network buffer back, if need be */
+    if (tname != aname && tname) osi_FreeLargeSpace(tname);
+    if (code == 0) {
+#ifdef	AFS_OSF_ENV
+	/* Handle RENAME; only need to check rename "."  */
+	if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
+	    if (!FidCmp(&(tvc->fid), &(adp->fid))) { 
+		afs_PutVCache(*avcp, WRITE_LOCK);
+		*avcp = NULL;
+		return afs_CheckCode(EISDIR, &treq, 18);
+	    }
+	}
+#endif	/* AFS_OSF_ENV */
+
+	if (afs_mariner)
+	  afs_AddMarinerName(aname, tvc); 
+
+	/* Here we don't enter the name into the DNLC because we want the
+        evaluated mount dir to be there (the vcache for the mounted volume)
+        rather than the vc of the mount point itself.  we can still find the
+        mount point's vc in the vcache by its fid. */
+	if (hit) {
+#ifdef AFS_LINUX20_ENV
+	    /* So Linux inode cache is up to date. */
+	    code = afs_VerifyVCache(tvc, &treq);
+#else
+	    return 0;  /* can't have been any errors if hit and !code */
+#endif
+	}
+    }
+    code = afs_CheckCode(code, &treq, 19);
+    if (code) {
+       /* If there is an error, make sure *avcp is null.
+	* Alphas panic otherwise - defect 10719.
+	*/
+       *avcp = (struct vcache *)0;
+    }
+
+    return code;
+}
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
diff -Nur --exclude-from=exclude orig-src/afs/afs_osi_pag.c src/afs/afs_osi_pag.c
--- orig-src/afs/afs_osi_pag.c	Sat Apr 14 10:27:32 2001
+++ src/afs/afs_osi_pag.c	Wed Jun 13 17:23:05 2001
@@ -33,7 +33,11 @@
 
 /* Exported variables */
 afs_uint32 pag_epoch;
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+afs_uint32 pagCounter = 1;
+#else
 afs_uint32 pagCounter = 0;
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
 
 /* Local variables */
 
@@ -61,14 +65,15 @@
  * anyway, so the pag is an alternative handle which is somewhat more
  * secure (although of course not absolutely secure).
 */
+#if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
 afs_uint32 genpag(void) {
     AFS_STATCNT(genpag);
 #ifdef AFS_LINUX20_ENV
     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
     return (('A' << 24) + ((pag_epoch + pagCounter++) & 0xffffff));
-#else
+#else /* AFS_LINUX20_ENV */
     return (('A' << 24) + (pagCounter++ & 0xffffff));
-#endif
+#endif /* AFS_LINUX20_ENV */
 }
 
 afs_uint32 getpag(void) {
@@ -81,6 +86,30 @@
 #endif
 }
 
+#else
+
+/* Web enhancement: we don't need to restrict pags to 41XXXXXX since
+ * we are not sharing the space with anyone.  So we use the full 32 bits. */
+
+afs_uint32 genpag(void) {
+    AFS_STATCNT(genpag);
+#ifdef AFS_LINUX20_ENV
+    return (pag_epoch + pagCounter++);
+#else
+    return (pagCounter++);
+#endif /* AFS_LINUX20_ENV */
+}
+
+afs_uint32 getpag(void) {
+    AFS_STATCNT(getpag);
+#ifdef AFS_LINUX20_ENV
+    /* Ensure unique PAG's (mod 200 days) when reloading the client. */
+    return (pag_epoch + pagCounter);
+#else
+    return (pagCounter);
+#endif
+}
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
 
 /* used to require 10 seconds between each setpag to guarantee that
  * PAGs never wrap - which would be a security hole.  If we presume
@@ -182,6 +211,115 @@
 #endif
 }
 
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+/*
+ * afs_setpag_val
+ * This function is like setpag but sets the current thread's pag id to a
+ * caller-provided value instead of calling genpag().  This implements a
+ * form of token caching since the caller can recall a particular pag value
+ * for the thread to restore tokens, rather than reauthenticating.
+ */
+int
+#if	defined(AFS_SUN5_ENV)
+afs_setpag_val (struct AFS_UCRED **credpp, int pagval)
+#elif  defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
+afs_setpag_val (struct proc *p, void *args, int *retval, int pagval)
+#else
+afs_setpag_val (int pagval) 
+#endif
+{
+    int code = 0;
+
+#if defined(AFS_SGI53_ENV) && defined(MP)
+    /* This is our first chance to get the global lock. */
+    AFS_GLOCK();
+#endif /* defined(AFS_SGI53_ENV) && defined(MP) */    
+
+    AFS_STATCNT(afs_setpag);
+#ifdef AFS_SUN5_ENV
+    if (!afs_suser(*credpp))
+#else
+    if (!afs_suser())
+#endif
+    {
+	while (osi_Time() - pag_epoch < pagCounter ) {
+	    afs_osi_Wait(1000, (struct afs_osi_WaitHandle *) 0, 0);
+	}	
+    }
+
+#if	defined(AFS_SUN5_ENV)
+    code = AddPag(pagval, credpp);
+#elif	defined(AFS_OSF_ENV) || defined(AFS_FBSD_ENV)
+    code = AddPag(p, pagval, &p->p_rcred);
+#elif	defined(AFS_AIX41_ENV)
+    {
+	struct ucred *credp;
+	struct ucred *credp0;
+	
+	credp = crref();
+	credp0 = credp;
+	code = AddPag(pagval, &credp);
+	/* If AddPag() didn't make a new cred, then free our cred ref */
+	if (credp == credp0) {
+	    crfree(credp);
+	}
+    }
+#elif	defined(AFS_HPUX110_ENV)
+    {
+	struct ucred *credp = p_cred(u.u_procp);
+	code = AddPag(pagval, &credp);
+    }
+#elif	defined(AFS_SGI_ENV)
+    {
+	cred_t *credp;
+	credp = OSI_GET_CURRENT_CRED();
+	code = AddPag(pagval, &credp);
+    }
+#elif	defined(AFS_LINUX20_ENV)
+    {
+	struct AFS_UCRED *credp = crref();
+	code = AddPag(pagval, &credp);
+	crfree(credp);
+    }
+#elif defined(AFS_DARWIN_ENV)  || defined(AFS_FBSD_ENV)
+    {
+       struct ucred *credp=crdup(p->p_cred->pc_ucred);
+       code=AddPag(p, pagval, &credp);
+       crfree(credp);
+    }
+#else
+    code = AddPag(pagval, &u.u_cred);
+#endif
+
+    afs_Trace1(afs_iclSetp, CM_TRACE_SETPAG, ICL_TYPE_INT32, code);
+#if	defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_OSF_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
+#if defined(AFS_SGI53_ENV) && defined(MP)
+    AFS_GUNLOCK();
+#endif /* defined(AFS_SGI53_ENV) && defined(MP) */    
+    return (code);
+#else
+    if (!getuerror())
+ 	setuerror(code);
+    return (code);
+#endif
+}
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
+
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+int afs_getpag_val()
+{
+  int pagvalue;
+  struct AFS_UCRED *credp = u.u_cred;
+  int gidset0, gidset1;
+
+  gidset0 = credp->cr_groups[0];
+  gidset1 = credp->cr_groups[1];
+  pagvalue=afs_get_pag_from_groups(gidset0, gidset1);
+  return pagvalue;
+}
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
+
+
 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
 int AddPag(struct proc *p, afs_int32 aval, struct AFS_UCRED **credpp)
 #else	/* AFS_OSF_ENV || AFS_FBSD_ENV */
@@ -244,11 +382,15 @@
 	h = (g0 >> 14);
 	h = (g1 >> 14) + h + h + h;
 	ret = ((h << 28) | l);
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+	return ret;
+#else
 	/* Additional testing */
 	if (((ret >> 24) & 0xff) == 'A')
 	    return ret;
 	else
 	    return NOPAG;
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
     }
     return NOPAG;
 }
@@ -260,7 +402,9 @@
 
 
     AFS_STATCNT(afs_get_groups_from_pag);
+#if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
     pag &= 0x7fffffff;
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
     g0 = 0x3fff & (pag >> 14);
     g1 = 0x3fff & pag;
     g0 |= ((pag >> 28) / 3) << 14;
diff -Nur --exclude-from=exclude orig-src/afs/afs_pioctl.c src/afs/afs_pioctl.c
--- orig-src/afs/afs_pioctl.c	Mon Apr 30 15:08:40 2001
+++ src/afs/afs_pioctl.c	Fri Jun  1 20:00:47 2001
@@ -1845,6 +1845,13 @@
 	    afs_ResetUserConns(tu);
 	    tu->refCount--;
 	    ObtainWriteLock(&afs_xuser,228);
+#ifdef UKERNEL
+            /* set the expire times to 0, causes
+             * afs_GCUserData to remove this entry
+             */
+            tu->ct.EndTimestamp = 0;
+            tu->tokenTime = 0;
+#endif  /* UKERNEL */
 	}
     }
     ReleaseWriteLock(&afs_xuser);