resolve-symlinks-from-stat-cache-with-just-lookup-permission-20010719
[openafs.git] / src / afs / VNOPS / afs_vnop_lookup.c
index 1778c40..03379a8 100644 (file)
  * afs_index
  */
 
-#include "../afs/param.h"      /* Should be always first */
+#include <afsconfig.h>
+#include "../afs/param.h"
+
+RCSID("$Header$");
+
 #include "../afs/sysincludes.h"        /* Standard vendor system headers */
 #include "../afs/afsincludes.h"        /* Afs-based standard headers */
 #include "../afs/afs_stats.h" /* statistics */
@@ -40,6 +44,8 @@ extern afs_rwlock_t afs_xvcache;
 extern afs_rwlock_t afs_xcbhash;
 extern struct afs_exporter *afs_nfsexporter;
 extern char *afs_sysname;
+extern char *afs_sysnamelist[];
+extern int afs_sysnamecount;
 extern struct afs_q VLRU;                      /*vcache LRU*/
 #ifdef AFS_LINUX22_ENV
 extern struct inode_operations afs_symlink_iops, afs_dir_iops;
@@ -127,7 +133,7 @@ EvalMountPoint(avc, advc, avolpp, areq)
        volnamep = &avc->linkData[1];
        tcell = afs_GetCell(avc->fid.Cell, READ_LOCK);
     }
-    if (!tcell) return ENODEV;
+    if (!tcell) return ENOENT;
 
     mtptCell = tcell->cell;               /* The cell for the mountpoint */
     if (tcell->lcellp) {
@@ -182,7 +188,7 @@ EvalMountPoint(avc, advc, avolpp, areq)
        }
     }
   
-    if (!tvp) return ENOENT;       /* Couldn't find the volume */
+    if (!tvp) return ENODEV;       /* Couldn't find the volume */
 
     /* Don't cross mountpoint from a BK to a BK volume */
     if ((avc->states & CBackup) && (tvp->states & VBackup)) {
@@ -198,7 +204,7 @@ EvalMountPoint(avc, advc, avolpp, areq)
        tfid.Cell       = tvp->cell;
        afs_PutVolume(tvp, WRITE_LOCK);               /* release old volume */
        tvp = afs_GetVolume(&tfid, areq, WRITE_LOCK); /* get the new one */
-       if (!tvp) return ENOENT;                      /* oops, can't do it */
+       if (!tvp) return ENODEV;                      /* oops, can't do it */
     }
 
     if (avc->mvid == 0)
@@ -242,91 +248,92 @@ afs_ENameOK(aname)
     return 1;
 }
 
-Check_AtSys(avc, aname, outb, areq)
-    register struct vcache *avc;
-    char *aname, **outb;
-    struct vrequest *areq;
-{
-    register char *tname;
-    register int error = 0, offset = -1;
-
-    for (tname=aname; *tname; tname++) /*Move to the end of the string*/;
-
-    /*
-     * If the current string is 4 chars long or more, check to see if the
-     * tail end is "@sys".
-     */
-    if ((tname >= aname + 4) && (AFS_EQ_ATSYS(tname-4)))
-       offset = (tname - 4) - aname;
-    if (offset < 0) {
-       tname = aname;
-    }  else {
-       tname = (char *) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
-       if (offset)
-           strncpy(tname, aname, offset);
-       if (!afs_nfsexporter) 
-           strcpy(tname+offset, (afs_sysname ? afs_sysname : SYS_NAME ));
-       else {
-           register struct unixuser *au;
-           register afs_int32 error;
-           au = afs_GetUser(areq->uid, avc->fid.Cell, 0); afs_PutUser(au, 0);  
-           if (au->exporter) {
-               error = EXP_SYSNAME(au->exporter, (char *)0, tname+offset);
-               if (error) 
-                   strcpy(tname+offset, "@sys");
-           } else {
-               strcpy(tname+offset, (afs_sysname ? afs_sysname : SYS_NAME ));
-           }
-       }
-       error = 1;
-    }
-    *outb = tname;
-    return error;
-}
-
-
-char *afs_getsysname(areq, adp)
+afs_getsysname(areq, adp, bufp)
     register struct vrequest *areq;
-    register struct vcache *adp; {
+    register struct vcache *adp;
+    register char *bufp;
+{
     static char sysname[MAXSYSNAME];
     register struct unixuser *au;
     register afs_int32 error;
 
+    if (!afs_nfsexporter) {
+      strcpy(bufp, afs_sysname);
+      return 0;
+    }
     AFS_STATCNT(getsysname);
-    /* this whole interface is wrong, it should take a buffer ptr and copy
-     * the data out.
-     */
     au = afs_GetUser(areq->uid, adp->fid.Cell, 0);
     afs_PutUser(au, 0);        
     if (au->exporter) {
-      error = EXP_SYSNAME(au->exporter, (char *)0, sysname);
-      if (error) return "@sys";
-      else return sysname;
+      error = EXP_SYSNAME(au->exporter, (char *)0, bufp);
+      if (error) 
+       strcpy(bufp, "@sys");
+      return -1;
     } else {
-      return (afs_sysname == 0? SYS_NAME : afs_sysname);
+      strcpy(bufp, afs_sysname);
+      return 0;
     }
 }
 
-void afs_HandleAtName(aname, aresult, areq, adp)
-    register char *aname;
-    register char *aresult;
-    register struct vrequest *areq;
-    register struct vcache *adp; {
-    register int tlen;
-    AFS_STATCNT(HandleAtName);
-    tlen = strlen(aname);
-    if (tlen >= 4 && strcmp(aname+tlen-4, "@sys")==0) {
-       strncpy(aresult, aname, tlen-4);
-       strcpy(aresult+tlen-4, afs_getsysname(areq, adp));
-    }
-    else strcpy(aresult, aname);
+Check_AtSys(avc, aname, state, areq)
+    register struct vcache *avc;
+    char *aname;
+    struct sysname_info *state;
+    struct vrequest *areq;
+{
+    if (AFS_EQ_ATSYS(aname)) {
+      state->offset = 0;
+      state->name = (char *) osi_AllocLargeSpace(AFS_SMALLOCSIZ);
+      state->allocked = 1;
+      state->index = afs_getsysname(areq, avc, state->name);
+    } else {
+      state->offset = -1;
+      state->allocked = 0;
+      state->index = 0;
+      state->name = aname;
     }
+}
+
+Next_AtSys(avc, areq, state)
+     register struct vcache *avc;
+     struct vrequest *areq;
+     struct sysname_info *state;
+{
+  if (state->index == -1)
+    return 0;  /* No list */
+
+  /* Check for the initial state of aname != "@sys" in Check_AtSys*/
+  if (state->offset == -1 && state->allocked == 0) {
+    register char *tname;
+    /* Check for .*@sys */
+      for (tname=state->name; *tname; tname++)
+       /*Move to the end of the string*/;
+      if ((tname > state->name + 4) && (AFS_EQ_ATSYS(tname-4))) {
+       state->offset = (tname - 4) - state->name;
+       tname = (char *) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
+       strncpy(tname, state->name, state->offset);
+       state->name = tname;
+       state->allocked = 1;
+       state->index = afs_getsysname(areq, avc, state->name+state->offset);
+       return 1;
+      } else
+       return 0; /* .*@sys doesn't match either */
+  } else if (++(state->index) >= afs_sysnamecount
+            || !afs_sysnamelist[state->index])
+    return 0;  /* end of list */
+  strcpy(state->name+state->offset, afs_sysnamelist[state->index]);
+  return 1;
+}
 
 #if (defined(AFS_SGI62_ENV) || defined(AFS_SUN57_64BIT_ENV))
 extern int BlobScan(ino64_t *afile, afs_int32 ablob);
 #else
+#if defined AFS_LINUX_64BIT_KERNEL
+extern int BlobScan(long *afile, afs_int32 ablob);
+#else
 extern int BlobScan(afs_int32 *afile, afs_int32 ablob);
 #endif
+#endif
 
 
 /* called with an unlocked directory and directory cookie.  Areqp
@@ -637,6 +644,7 @@ tagain:
       /* actually a serious error, probably should panic. Probably will 
        * panic soon, oh well. */
       ReleaseReadLock(&afs_xvcache);
+      afs_warnuser("afs_DoBulkStat: VLRU empty!");
       goto done;
     }
     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
@@ -739,12 +747,15 @@ tagain:
           refpanic ("Bulkstat VLRU inconsistent6");
        ReleaseWriteLock(&afs_xvcache);
 
+       ObtainWriteLock(&afs_xcbhash, 494);
+
        /* We need to check the flags again. We may have missed
         * something while we were waiting for a lock.
         */
        if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
            flagIndex++;
            ReleaseWriteLock(&tvcp->lock);
+           ReleaseWriteLock(&afs_xcbhash);
            afs_PutVCache(tvcp);
            continue;
        }
@@ -761,19 +772,6 @@ tagain:
            tvcp->v.v_op = &afs_symlink_iops;
 #endif
 
-       ObtainWriteLock(&afs_xcbhash, 494);
-
-       /* We need to check the flags once more. We may have missed
-        * something while we were waiting for a lock.
-        */
-       if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
-           flagIndex++;
-           ReleaseWriteLock(&afs_xcbhash);
-           ReleaseWriteLock(&tvcp->lock);
-           afs_PutVCache(tvcp);
-           continue;
-       }
-
        /* do some accounting for bulk stats: mark this entry as
         * loaded, so we can tell if we use it before it gets
         * recycled.
@@ -872,8 +870,13 @@ afs_lookup(OSI_VC_ARG(adp), aname, avcp, pnp, flags, rdir, acred)
     int flags;
     struct vnode *rdir;
 #else
+#if defined(UKERNEL)
+afs_lookup(adp, aname, avcp, acred, flags)
+    int flags;
+#else    
 afs_lookup(adp, aname, avcp, acred)
-#endif
+#endif /* UKERNEL */
+#endif /* SUN5 || SGI */
     OSI_VC_DECL(adp);
     struct vcache **avcp;
     char *aname;
@@ -888,6 +891,8 @@ afs_lookup(adp, aname, avcp, acred)
     extern afs_int32 afs_mariner;                      /*Writing activity to log?*/
     OSI_VC_CONVERT(adp)
     afs_hyper_t versionNo;
+    int no_read_access = 0;
+    struct sysname_info sysState;   /* used only for @sys checking */
 
     AFS_STATCNT(afs_lookup);
 #ifdef AFS_OSF_ENV
@@ -902,39 +907,6 @@ afs_lookup(adp, aname, avcp, 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 */
 
@@ -948,7 +920,7 @@ afs_lookup(adp, aname, avcp, acred)
     else code = 0;
 
     /* watch for ".." in a volume root */
-    if (adp->mvstat == 2 && tname[0] == '.' && tname[1] == '.' && !tname[2]) {
+    if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
        /* looking up ".." in root via special hacks */
        if (adp->mvid == (struct VenusFid *) 0 || adp->mvid->Fid.Volume == 0) {
 #ifdef AFS_OSF_ENV
@@ -995,13 +967,17 @@ afs_lookup(adp, aname, avcp, acred)
        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 */
+    if (aname[0] == '.' && !aname[1]) { /* special case */
        ObtainReadLock(&afs_xvcache);   
        osi_vnhold(adp, 0);
        ReleaseReadLock(&afs_xvcache);  
@@ -1014,27 +990,42 @@ afs_lookup(adp, aname, avcp, acred)
       goto done;
     }
 
+    Check_AtSys(adp, aname, &sysState, &treq);
+    tname = sysState.name;
+
+    /* 1st Check_AtSys and lookup by tname is required here, for now,
+       because the dnlc is *not* told to remove entries for the parent
+       dir of file/dir op that afs_LocalHero likes, but dnlc is informed
+       if the cached entry for the parent dir is invalidated for a
+       non-local change.
+       Otherwise, we'd be able to do a dnlc lookup on an entry ending
+       w/@sys and know the dnlc was consistent with reality. */
     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 (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
-       AFS_RELE(tvc);
-       *avcp = 0;
-      }
-      else {  
+       if (no_read_access && vType(tvc) != VDIR && vType(tvc) != VLNK) {
+           /* need read access on dir to stat non-directory / non-link */
+           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;
-      }
-    }
-#else /* non - LINUX */
-    if (tvc) {
-      code = 0;
-      hit = 1;
-      goto done;
-    }
 #endif /* linux22 */
+    }
 
     {
     register struct dcache *tdc;
@@ -1052,7 +1043,6 @@ afs_lookup(adp, aname, avcp, acred)
 
     /* 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);
 
     /*
@@ -1079,17 +1069,28 @@ afs_lookup(adp, aname, avcp, acred)
     /* Save the version number for when we call osi_dnlc_enter */
     hset(versionNo, tdc->f.versionNo);
 
+    /*
+     * 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...
+     */
+    /* above now implemented by Check_AtSys and Next_AtSys */
+
+    /* lookup the name in the appropriate dir, and return a cache entry
+       on the resulting fid */
     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);
-      }
+    code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
+
+    /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
+    while (code == ENOENT && Next_AtSys(adp, &treq, &sysState)) {
+      code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
     }
+    tname = sysState.name;
+
     ReleaseReadLock(&adp->lock);
     afs_PutDCache(tdc);
 
@@ -1160,6 +1161,11 @@ afs_lookup(adp, aname, avcp, acred)
        tvc->parentVnode = adp->fid.Fid.Vnode;
        tvc->parentUnique = adp->fid.Fid.Unique;
        tvc->states &= ~CBulkStat;
+
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+        if (!(flags & AFS_LOOKUP_NOEVAL))
+          /* don't eval mount points */
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
        if (tvc->mvstat == 1) {
          /* a mt point, possibly unevaluated */
          struct volume *tvolp;
@@ -1167,6 +1173,12 @@ afs_lookup(adp, aname, avcp, acred)
            ObtainWriteLock(&tvc->lock,133);
            code = EvalMountPoint(tvc, adp, &tvolp, &treq);
            ReleaseWriteLock(&tvc->lock);
+
+           if (code) {
+               if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
+               goto done;
+           }
+
            /* next, we want to continue using the target of the mt point */
            if (tvc->mvid && (tvc->states & CMValid)) {
              struct vcache *uvc;
@@ -1262,6 +1274,14 @@ done:
 
        if (afs_mariner)
          afs_AddMarinerName(aname, tvc); 
+
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
+        if (!(flags & AFS_LOOKUP_NOEVAL))
+       /* 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. */
+#endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
        if (!hit) {
          osi_dnlc_enter (adp, aname, tvc, &versionNo);
        }