Windows: Restructure cm_LookupInternal addition of Freelance entries
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index 4a13e40..9aabe22 100644 (file)
@@ -483,53 +483,56 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 return 0;
             }
             sp->caseFold = casefold;
+        }
 
-            /* see if we can find it using the directory hash tables.
-               we can only do exact matches, since the hash is case
-               sensitive. */
-            {
-                cm_dirOp_t dirop;
+        /*
+         * see if we can find it using the directory hash tables.
+         * we can only do exact matches, since the hash is case
+         * sensitive.
+         */
+        if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
+        {
+            cm_dirOp_t dirop;
 #ifdef USE_BPLUS
-                int usedBplus = 0;
+            int usedBplus = 0;
 #endif
 
-                code = ENOENT;
+            code = ENOENT;
 
-                code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
-                if (code == 0) {
+            code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+            if (code == 0) {
 
 #ifdef USE_BPLUS
-                    code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
-                    if (code != EINVAL)
-                        usedBplus = 1;
-                    else 
+                code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
+                if (code != EINVAL)
+                    usedBplus = 1;
+                else
 #endif
-                        code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
+                    code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
 
-                    cm_EndDirOp(&dirop);
-                }
+                cm_EndDirOp(&dirop);
+            }
 
-                if (code == 0) {
+            if (code == 0) {
+                /* found it */
+                sp->found = TRUE;
+                sp->ExactFound = TRUE;
+                *retscp = NULL; /* force caller to call cm_GetSCache() */
+                return 0;
+            }
+#ifdef USE_BPLUS
+            if (usedBplus) {
+                if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
                     /* found it */
                     sp->found = TRUE;
-                    sp->ExactFound = TRUE;
+                    sp->ExactFound = FALSE;
                     *retscp = NULL; /* force caller to call cm_GetSCache() */
                     return 0;
                 }
-#ifdef USE_BPLUS
-                if (usedBplus) {
-                    if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
-                        /* found it */
-                        sp->found = TRUE;
-                        sp->ExactFound = FALSE;
-                        *retscp = NULL; /* force caller to call cm_GetSCache() */
-                        return 0;
-                    }
-                    
-                    return CM_ERROR_BPLUS_NOMATCH;
-                }
-#endif 
+
+                return CM_ERROR_BPLUS_NOMATCH;
             }
+#endif
         }
     }  
 
@@ -893,7 +896,6 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 {
     fschar_t *cellNamep = NULL;
     fschar_t *volNamep = NULL;
-    int tlen;
     afs_uint32 code;
     fschar_t *cp;
     fschar_t *mpNamep;
@@ -918,7 +920,6 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     mpNamep = scp->mountPointStringp;
     if (!mpNamep[0])
        return CM_ERROR_NOSUCHPATH;
-    tlen = cm_FsStrLen(scp->mountPointStringp);
     mtType = *scp->mountPointStringp;
 
     cp = cm_FsStrChr(mpNamep, _FS(':'));
@@ -1052,6 +1053,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
     int getroot;
     normchar_t *nnamep = NULL;
     fschar_t *fnamep = NULL;
+    size_t fnlen;
 
     *outScpp = NULL;
 
@@ -1079,6 +1081,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
         goto done;
     }
 
+retry_lookup:
     if (flags & CM_FLAG_NOMOUNTCHASE) {
         /* In this case, we should go and call cm_Dir* functions
            directly since the following cm_ApplyDir() function will
@@ -1116,7 +1119,8 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
                 goto haveFid;
             }
             
-            return CM_ERROR_BPLUS_NOMATCH;
+            code = CM_ERROR_BPLUS_NOMATCH;
+            goto notfound;
         }
 #endif
     }
@@ -1136,7 +1140,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
      * that we stopped early, probably because we found the entry we're
      * looking for.  Any other non-zero code is an error.
      */
-    if (code && code != CM_ERROR_STOPNOW) {
+    if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
         /* if the cm_scache_t we are searching in is not a directory 
          * we must return path not found because the error 
          * is to describe the final component not an intermediary
@@ -1150,6 +1154,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
         goto done;
     }
 
+notfound:
     getroot = (dscp==cm_data.rootSCachep) ;
     if (!rock.found) {
         if (!cm_freelanceEnabled || !getroot) {
@@ -1168,6 +1173,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
             /* nonexistent dir on freelance root, so add it */
             fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
             int  found = 0;
+            int  retry = 0;
 
             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S", 
                      osi_LogSaveClientString(afsd_logp,cnamep));
@@ -1180,28 +1186,71 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
              */
 
             code = -1;
+            fnlen = strlen(fnamep);
+            if ( fnamep[fnlen-1] == '.') {
+                fnamep[fnlen-1] = '\0';
+                fnlen--;
+                retry = 1;
+            }
+
             if (cnamep[0] == '.') {
                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
                     found = 1;
-                    if (!cm_FreelanceMountPointExists(fullname, 0))
-                        code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
-                                                    1, &rock.fid);
-                    if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) && 
-                         !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
-                         !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
-                        code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
+                    code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
+                    if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
+                        /*
+                         * Do not permit symlinks that are one of:
+                         *  . the cellname followed by a dot
+                         *  . the cellname minus a single character
+                         *  . a substring of the cellname that does not consist of full components
+                         */
+                        if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
+                             (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
+                        {
+                            /* do not add; substitute fullname for the search */
+                            free(fnamep);
+                            fnamep = malloc(strlen(fullname)+2);
+                            fnamep[0] = '.';
+                            strncpy(&fnamep[1], fullname, strlen(fullname)+1);
+                            retry = 1;
+                        } else {
+                            code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
+                        }
+                    }
                 }
             } else {
                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
                     found = 1;
-                    if (!cm_FreelanceMountPointExists(fullname, 0))
-                        code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
-                    if ( cm_FsStrCmpI(fnamep, fullname) && 
-                         !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
-                         !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
-                        code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
+                    code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
+                    if ( cm_FsStrCmpI(fnamep, fullname)) {
+                        /*
+                         * Do not permit symlinks that are one of:
+                         *  . the cellname followed by a dot
+                         *  . the cellname minus a single character
+                         *  . a substring of the cellname that does not consist of full components
+                         */
+                        if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
+                             (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
+                        {
+                            /* do not add; substitute fullname for the search */
+                                free(fnamep);
+                                fnamep = strdup(fullname);
+                                code = 0;
+                                retry = 1;
+                        } else {
+                            code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
+                        }
+                    }
                 }
             }
+
+            if (retry) {
+                if (nnamep)
+                    free(nnamep);
+                nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
+                goto retry_lookup;
+            }
+
             if (!found || code) {   /* add mount point failed, so give up */
                 if (flags & CM_FLAG_CHECKPATH)
                     code = CM_ERROR_NOSUCHPATH;
@@ -1219,7 +1268,7 @@ long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_u
         }
     }
 
-  haveFid:       
+  haveFid:
     if ( !tscp )    /* we did not find it in the dnlc */
     {
         dnlcHit = 0; 
@@ -2342,18 +2391,17 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
                              &volSync, NULL, &cbReq, code));
         code = cm_MapRPCError(code, reqp);
-        if (code)
-            osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
-                     inlinebulk ? "Inline" : "", code);
-        else
-            osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
 
         /* may as well quit on an error, since we're not going to do
          * much better on the next immediate call, either.
          */
         if (code) {
+            osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
+                     inlinebulk ? "Inline" : "", code);
             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
             break;
+        } else {
+            osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
         }
 
         /* otherwise, we should do the merges */
@@ -2377,12 +2425,16 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
              *
              * Right now, be pretty conservative: if there's a
              * callback or a pending call, skip it.
+             * However, if the prior attempt to obtain status
+             * was refused access or the volume is .readonly,
+             * take the data in any case since we have nothing
+             * better for the in flight directory enumeration that
+             * resulted in this function being called.
              */
-            if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
-                 && !(scp->flags &
-                       (CM_SCACHEFLAG_FETCHING
-                         | CM_SCACHEFLAG_STORING
-                         | CM_SCACHEFLAG_SIZESTORING))) {
+            if ((scp->cbServerp == NULL &&
+                !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
+                (scp->flags & CM_SCACHEFLAG_PURERO) ||
+                (scp->flags & CM_SCACHEFLAG_EACCESS)) {
                 cm_EndCallbackGrantingCall(scp, &cbReq,
                                             &bbp->callbacks[j],
                                             &volSync,
@@ -2801,10 +2853,16 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     return code;
 }       
 
-long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
+/*
+ * locked if TRUE means write-locked
+ * else the cm_scache_t rw must not be held
+ */
+long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
 {
     long code;
 
+    if (locked)
+        lock_ReleaseWrite(&scp->rw);
     code = buf_CleanVnode(scp, userp, reqp);
     if (code == 0) {
         lock_ObtainWrite(&scp->rw);
@@ -2819,7 +2877,10 @@ long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
            scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
        }
 
-        lock_ReleaseWrite(&scp->rw);
+        if (!locked)
+            lock_ReleaseWrite(&scp->rw);
+    } else if (locked) {
+        lock_ObtainWrite(&scp->rw);
     }
     return code;
 }
@@ -4844,10 +4905,12 @@ long cm_UnlockByKey(cm_scache_t * scp,
     if (scp->serverLock == LockWrite &&
         scp->exclusiveLocks == 0 &&
         scp->sharedLocks > 0) {
-
         /* The serverLock should be downgraded to LockRead */
         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
 
+        /* Make sure there are no dirty buffers left. */
+        code = cm_FSync(scp, userp, reqp, TRUE);
+
         /* since scp->serverLock looked sane, we are going to assume
            that we have a valid server lock. */
         scp->lockDataVersion = scp->dataVersion;
@@ -4899,6 +4962,11 @@ long cm_UnlockByKey(cm_scache_t * scp,
               scp->sharedLocks == 0) {
         /* The serverLock should be released entirely */
 
+        if (scp->serverLock == LockWrite) {
+            /* Make sure there are no dirty buffers left. */
+            code = cm_FSync(scp, userp, reqp, TRUE);
+        }
+
         code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code == 0)
@@ -5049,6 +5117,9 @@ long cm_Unlock(cm_scache_t *scp,
         /* The serverLock should be downgraded to LockRead */
         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
 
+        /* Make sure there are no dirty buffers left. */
+        code = cm_FSync(scp, userp, reqp, TRUE);
+
         /* Since we already had a lock, we assume that there is a
            valid server lock. */
         scp->lockDataVersion = scp->dataVersion;
@@ -5113,6 +5184,11 @@ long cm_Unlock(cm_scache_t *scp,
               scp->sharedLocks == 0) {
         /* The serverLock should be released entirely */
 
+        if (scp->serverLock == LockWrite) {
+            /* Make sure there are no dirty buffers left. */
+            code = cm_FSync(scp, userp, reqp, TRUE);
+        }
+
         code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code == 0) {