NTSTATUS PvfsFileBasename( PSTR *ppszFilename, PCSTR pszPath ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PSTR pszCursor = NULL; if ((pszCursor = strrchr(pszPath, '/')) != NULL) { /* Assume there is never a trailing '/' since that should be handled by PvfsCanonicalPathName() */ pszCursor++; } if (pszCursor != NULL) { ntError = RtlCStringDuplicate(ppszFilename, pszCursor); } else { ntError = RtlCStringDuplicate(ppszFilename, pszPath); } BAIL_ON_NT_STATUS(ntError); cleanup: return ntError; error: goto cleanup; }
NTSTATUS LwRtlAnsiStringAllocateFromUnicodeString( OUT PANSI_STRING pNewString, IN PUNICODE_STRING pString ) { NTSTATUS status = 0; PSTR pszNewString = NULL; ANSI_STRING newString = { 0 }; status = RtlCStringDuplicate(&pszNewString, pszString); GOTO_CLEANUP_ON_STATUS(status); newString.Buffer = pszNewString; pszNewString = 0; newString.Length = wc16slen(newString.Buffer) * sizeof(newString.Buffer[0]); newString.MaximumLength = newString.Length + sizeof(newString.Buffer[0]); cleanup: if (status) { RtlCStringFree(&pszNewString); RtlAnsiStringFree(&newString); } *pString = newString; return status; }
NTSTATUS LwRtlAnsiStringAllocateFromCString( OUT PANSI_STRING pNewString, IN PCSTR pszString ) { NTSTATUS status = 0; PSTR pszNewString = NULL; ANSI_STRING newString = { 0 }; status = RtlCStringDuplicate(&pszNewString, pszString); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlAnsiStringInitEx(&newString, pszNewString); GOTO_CLEANUP_ON_STATUS(status); pszNewString = 0; cleanup: if (status) { RtlCStringFree(&pszNewString); RtlAnsiStringFree(&newString); } *pNewString = newString; return status; }
static NTSTATUS AllocateCStringFileSpec( PSTR *ppszPattern, PIO_MATCH_FILE_SPEC pFileSpec ) { NTSTATUS ntError = STATUS_INSUFFICIENT_RESOURCES; if (pFileSpec == NULL) { ntError = RtlCStringDuplicate(ppszPattern, "*"); BAIL_ON_NT_STATUS(ntError); goto cleanup; } ntError = RtlCStringAllocateFromUnicodeString(ppszPattern, &pFileSpec->Pattern); BAIL_ON_NT_STATUS(ntError); cleanup: return ntError; error: goto cleanup; }
NTSTATUS PvfsFileDirname( PSTR *ppszDirname, PCSTR pszPath ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PSTR pszCursor = NULL; PSTR pszNewString = NULL; /* Case #1: No '/' so just return '.' */ if (((pszCursor = strrchr(pszPath, '/')) == NULL)) { ntError = RtlCStringDuplicate(ppszDirname, "."); goto cleanup; } /* Case #2: only one '/' (at beginning of path) */ if (pszCursor == pszPath) { ntError = RtlCStringDuplicate(ppszDirname, "/"); goto cleanup; } /* Case #3: Real dirname and file name components */ ntError = RTL_ALLOCATE(&pszNewString, CHAR, PVFS_PTR_DIFF(pszPath,pszCursor) + 1); BAIL_ON_NT_STATUS(ntError); RtlCopyMemory(pszNewString, pszPath, PVFS_PTR_DIFF(pszPath,pszCursor)); *ppszDirname = pszNewString; ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; }
NTSTATUS PvfsDirContextAddEntry( PPVFS_DIRECTORY_CONTEXT pDirCtx, PCSTR pszPathname ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; DWORD dwCurrent = 0; PPVFS_DIRECTORY_ENTRY pList = NULL; if ((pDirCtx->ulAllocated == 0) || ((pDirCtx->dwNumEntries+1) == pDirCtx->ulAllocated)) { pDirCtx->ulAllocated += 256; ntError = PvfsReallocateMemory( OUT_PPVOID(&pDirCtx->pDirEntries), sizeof(PVFS_DIRECTORY_ENTRY)*pDirCtx->ulAllocated); BAIL_ON_NT_STATUS(ntError); } dwCurrent = pDirCtx->dwNumEntries; pList = pDirCtx->pDirEntries; pList[dwCurrent].bValidStat = FALSE; ntError = RtlCStringDuplicate(&pList[dwCurrent].pszFilename, pszPathname); BAIL_ON_NT_STATUS(ntError); pDirCtx->dwNumEntries++; ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; }
BOOLEAN PvfsWildcardMatch( IN PSTR pszPathname, IN PSTR pszPattern, IN BOOLEAN bCaseSensitive ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PSTR pszString = NULL; PSTR pszMatch = NULL; PSTR pszPathUpper = NULL; PSTR pszPatternUpper = NULL; BOOLEAN bMatched = FALSE; /* Quick check for an exact match */ if (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')) { return RtlCStringIsEqual(pszPathname, pszPattern, bCaseSensitive); } /* If we have a case insensitive search, upper case the Pathname and Pattern for easier comparison */ pszString = pszPathname; pszMatch = pszPattern; if (!bCaseSensitive) { ntError = RtlCStringDuplicate(&pszPathUpper, pszPathname); BAIL_ON_NT_STATUS(ntError); ntError = RtlCStringDuplicate(&pszPatternUpper, pszPattern); BAIL_ON_NT_STATUS(ntError); PvfsCStringUpper(pszPathUpper); PvfsCStringUpper(pszPatternUpper); pszString = pszPathUpper; pszMatch = pszPatternUpper; } /* Enter state machine */ for (/* already performed init */; PVFS_CSTRING_NON_NULL(pszString) && PVFS_CSTRING_NON_NULL(pszMatch); pszString++) { PVFS_WILDCARD_TYPE eState = 0; CHAR cSrc = '\0'; CHAR cMatch = '\0'; DWORD dwCount = 0; /* Save the current CHAR */ cSrc = *pszString; cMatch = *pszMatch; /* Consumes the pattern from pszMatch */ eState = NextMatchState(&pszMatch, &dwCount); switch (eState) { case PVFS_WILDCARD_TYPE_NONE: if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } break; case PVFS_WILDCARD_TYPE_SPLAT: { PSTR pszCursor = NULL; /* We are done if this is the last character in the pattern */ if (!PVFS_CSTRING_NON_NULL(pszMatch)) { pszString = NULL; goto cleanup; } /* If we don't find a match for the next character in the pattern, then fail */ if ((pszCursor = strchr(pszString, *pszMatch)) == NULL) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } /* Have to consume at least one character here */ if (pszString == pszCursor) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } /* Set to the previous character so that pszString is incremented properly next pass of the loop */ pszString = pszCursor-1; break; } case PVFS_WILDCARD_TYPE_SINGLE: { DWORD i = 0; /* Consume dwCount characters */ for (i=0; i<dwCount && PVFS_CSTRING_NON_NULL(pszString); i++, pszString++) { /* no loop body */; } if (*pszString == '\0') { /* backup so pszString incrent in outer loop works out */ pszString--; } break; } case PVFS_WILDCARD_TYPE_DOT: /* For now deal with the '.' as just another character */ if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } break; case PVFS_WILDCARD_TYPE_SPLAT_DOT: { PSTR pszCursor = NULL; /* Similar to "A*B" except we search for the '.' from the end. */ if ((pszCursor = strrchr(pszString, '.')) == NULL) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } pszString = pszCursor; break; } case PVFS_WILDCARD_TYPE_SINGLE_DOT: { DWORD i = 0; /* We can match 0 - dwCount characters up to the last '.' This is really a hold over from DOS 8.3 filenames */ for (i=0; i<dwCount && PVFS_CSTRING_NON_NULL(pszString) && (*pszString != '.'); i++, pszString++) { /* no loop body */; } /* If we any path lefft, it better be on '.' for a match */ if (pszString && *pszString != '.') { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } break; } } } cleanup: /* We matched if pszString is empty AND either pszMatch is empty OR only contains wildcard characters */ if (!PVFS_CSTRING_NON_NULL(pszString) && (!PVFS_CSTRING_NON_NULL(pszMatch) || (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')))) { bMatched = TRUE; } if (!bCaseSensitive) { PVFS_FREE(&pszPathUpper); PVFS_FREE(&pszPatternUpper); } /* If we have any string left to parse, we don't have a match */ return bMatched; error: goto cleanup; }
NTSTATUS PvfsCreateFCB( OUT PPVFS_FCB *ppFcb, IN PCSTR pszFilename, IN BOOLEAN bCheckShareAccess, IN FILE_SHARE_FLAGS SharedAccess, IN ACCESS_MASK DesiredAccess ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PPVFS_FCB pFcb = NULL; BOOLEAN bBucketLocked = FALSE; PPVFS_CB_TABLE_ENTRY pBucket = NULL; PPVFS_FCB pParentFcb = NULL; ntError = PvfsFindParentFCB(&pParentFcb, pszFilename); BAIL_ON_NT_STATUS(ntError); ntError = PvfsCbTableGetBucket(&pBucket, &gPvfsDriverState.FcbTable, (PVOID)pszFilename); BAIL_ON_NT_STATUS(ntError); /* Protect against adding a duplicate */ LWIO_LOCK_RWMUTEX_EXCLUSIVE(bBucketLocked, &pBucket->rwLock); ntError = PvfsCbTableLookup_inlock( (PPVFS_CONTROL_BLOCK*)OUT_PPVOID(&pFcb), pBucket, pszFilename); if (ntError == STATUS_SUCCESS) { LWIO_UNLOCK_RWMUTEX(bBucketLocked, &pBucket->rwLock); /* If we have success, then we are good. If we have a sharing violation, give the caller a chance to break the oplock and we'll try again when the create is resumed. */ if (ntError == STATUS_SUCCESS || ntError == STATUS_SHARING_VIOLATION) { *ppFcb = PvfsReferenceFCB(pFcb); } goto cleanup; } ntError = PvfsAllocateFCB(&pFcb); BAIL_ON_NT_STATUS(ntError); ntError = RtlCStringDuplicate(&pFcb->pszFilename, pszFilename); BAIL_ON_NT_STATUS(ntError); pFcb->pParentFcb = pParentFcb ? PvfsReferenceFCB(pParentFcb) : NULL; // New FCB so BaseControlBlock.Mutext locking is not necessary ntError = PvfsCbTableAdd_inlock( pBucket, pFcb->pszFilename, (PPVFS_CONTROL_BLOCK)pFcb); BAIL_ON_NT_STATUS(ntError); /* Return a reference to the FCB */ *ppFcb = PvfsReferenceFCB(pFcb); cleanup: LWIO_UNLOCK_RWMUTEX(bBucketLocked, &pBucket->rwLock); if (pParentFcb) { PvfsReleaseFCB(&pParentFcb); } if (pFcb) { PvfsReleaseFCB(&pFcb); } return ntError; error: goto cleanup; }
BOOLEAN PvfsWildcardMatch( IN PSTR pszPathname, IN PSTR pszPattern, IN BOOLEAN bCaseSensitive ) { NTSTATUS ntError = STATUS_SUCCESS; PSTR pszString = NULL; PSTR pszMatch = NULL; PSTR pszPathUpper = NULL; PSTR pszPatternUpper = NULL; BOOLEAN bMatched = FALSE; LW_LIST_LINKS Stack; // Quick check for an exact match if (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')) { return RtlCStringIsEqual(pszPathname, pszPattern, bCaseSensitive); } // If we have a case insensitive search, upper case the Pathname // and Pattern for easier comparison pszString = pszPathname; pszMatch = pszPattern; if (!bCaseSensitive) { ntError = RtlCStringDuplicate(&pszPathUpper, pszPathname); BAIL_ON_NT_STATUS(ntError); ntError = RtlCStringDuplicate(&pszPatternUpper, pszPattern); BAIL_ON_NT_STATUS(ntError); PvfsCStringUpper(pszPathUpper); PvfsCStringUpper(pszPatternUpper); pszString = pszPathUpper; pszMatch = pszPatternUpper; } // Enter state machine LwListInit(&Stack); reset_state: while (PVFS_CSTRING_NON_NULL(pszString) && PVFS_CSTRING_NON_NULL(pszMatch)) { PVFS_WILDCARD_TYPE eState = 0; CHAR cSrc = '\0'; CHAR cMatch = '\0'; DWORD dwCount = 0; // Save the current CHAR cSrc = *pszString; cMatch = *pszMatch; // Certain characters should never match and hence allow us to filter // certain file patterns if (cSrc == PVFS_STREAM_DELIMINATOR_C) { bMatched = FALSE; goto cleanup; } // Consumes the pattern from pszMatch eState = NextMatchState(&pszMatch, &dwCount); switch (eState) { case PVFS_WILDCARD_TYPE_NONE: if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; pszString = NULL; } else { pszString++; } break; case PVFS_WILDCARD_TYPE_SPLAT: { // We are done if this is the last character in the pattern if (!PVFS_CSTRING_NON_NULL(pszMatch)) { pszString = NULL; } else { // Be greedy - Consume as much of the string using the '*' // as possible. This will require a stack in order to backtrack // on a failure. pszString = PvfsWildcardEatString(pszString, pszMatch); if (!pszString) { ntError = STATUS_NO_MATCH; } else { // Add next character past "String" match and the previously // match "Pattern" state (so we pick up the '*' when we pop // the stack ntError = PvfsWildcardStackPush( &Stack, pszString+1, pszMatch-1); BAIL_ON_NT_STATUS(ntError); } } break; } case PVFS_WILDCARD_TYPE_SINGLE: { DWORD i = 0; // Consume dwCount characters for (i=0; (i<dwCount) && PVFS_CSTRING_NON_NULL(pszString); i++, pszString++) { // no loop body ; } break; } case PVFS_WILDCARD_TYPE_DOT: // For now deal with the '.' as just another character if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; pszString = NULL; } else { pszString++; } break; case PVFS_WILDCARD_TYPE_SPLAT_DOT: { PSTR pszCursor = NULL; // Similar to "A*B" except we search for the '.' from the end if ((pszCursor = strrchr(pszString, '.')) == NULL) { ntError = STATUS_NO_MATCH; pszString = NULL; } else { pszString = pszCursor + 1; } break; } case PVFS_WILDCARD_TYPE_SINGLE_DOT: { DWORD i = 0; // We can match 0 - dwCount characters up to the last '.' // This is really a hold over from DOS 8.3 filenames for (i=0; i<dwCount && PVFS_CSTRING_NON_NULL(pszString) && (*pszString != '.'); i++, pszString++) { // no loop body ; } // If we any path left, it better be on '.' for a match if (*pszString == '.') { pszString++; } else { ntError = STATUS_NO_MATCH; pszString = NULL; } break; } } // end of switch {...} } // end of for {...} // We matched if pszString is empty AND either pszMatch is empty // OR only contains wildcard characters if ((ntError == STATUS_SUCCESS) && !PVFS_CSTRING_NON_NULL(pszString) && (!PVFS_CSTRING_NON_NULL(pszMatch) || PvfsCStringOnlyContainsChars(pszMatch, "?*"))) { bMatched = TRUE; } else if (!LwListIsEmpty(&Stack)) { // Pop back to the earlier state, consume one character (*from '*') // and try another path ntError = PvfsWildcardStackPop(&Stack, &pszString, &pszMatch); BAIL_ON_NT_STATUS(ntError); pszString++; goto reset_state; } cleanup: PvfsWildcardStackDestroy(&Stack); if (!bCaseSensitive) { PVFS_FREE(&pszPathUpper); PVFS_FREE(&pszPatternUpper); } // If we have any string left to parse, we don't have a match return bMatched; error: goto cleanup; }
static NTSTATUS PvfsResolvePath( PSTR *ppszResolvedPath, PCSTR pszLookupPath ) { NTSTATUS ntError = STATUS_SUCCESS; PSTR pszCursor = NULL; PSTR pszComponent = NULL; PSTR pszPath = NULL; PVFS_STAT Stat = {0}; PSTR pszResolvedPath = NULL; PSTR pszResWorkingPath = NULL; PSTR pszWorkingPath = NULL; DWORD Length = PATH_MAX; DIR *pDir = NULL; struct dirent *pDirEntry = NULL; if (*pszLookupPath != '/') { ntError = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntError); } ntError = RTL_ALLOCATE(&pszResolvedPath, CHAR, Length); BAIL_ON_NT_STATUS(ntError); ntError = RTL_ALLOCATE(&pszWorkingPath, CHAR, PATH_MAX); BAIL_ON_NT_STATUS(ntError); ntError = RtlCStringDuplicate(&pszPath, pszLookupPath); BAIL_ON_NT_STATUS(ntError); pszComponent = pszPath + 1; while (pszComponent && (Length > 0)) { pDir = NULL; if ((pszCursor = strchr(pszComponent, '/')) != NULL) { *pszCursor = '\0'; } snprintf(pszWorkingPath, PATH_MAX-1, "%s/%s", pszResolvedPath, pszComponent); /* Try cache first */ ntError = PvfsPathCacheLookup(&pszResWorkingPath, pszWorkingPath); if(ntError == STATUS_SUCCESS) { strncpy(pszResolvedPath, pszResWorkingPath, PATH_MAX-1); Length = PATH_MAX - RtlCStringNumChars(pszResolvedPath); RtlCStringFree(&pszResWorkingPath); } /* Maybe an exact match on disk? */ else if (PvfsSysStat(pszWorkingPath, &Stat) == STATUS_SUCCESS) { strncpy(pszResolvedPath, pszWorkingPath, PATH_MAX-1); Length = PATH_MAX - RtlCStringNumChars(pszResolvedPath); RtlCStringFree(&pszResWorkingPath); ntError = PvfsPathCacheAdd(pszResolvedPath); BAIL_ON_NT_STATUS(ntError); } /* Do the work ourselves */ else { /* Enumerate directory entries and look for a match */ ntError = PvfsSysOpenDir(pszResolvedPath, &pDir); if (ntError == STATUS_NOT_A_DIRECTORY) { ntError = STATUS_OBJECT_PATH_NOT_FOUND; } BAIL_ON_NT_STATUS(ntError); for(ntError = PvfsSysReadDir(pDir, &pDirEntry); pDirEntry; ntError = PvfsSysReadDir(pDir, &pDirEntry)) { /* First check the error return */ BAIL_ON_NT_STATUS(ntError); if (RtlCStringIsEqual(pszComponent, pDirEntry->d_name, FALSE)) { break; } } /* Did we find a match? */ if (!pDirEntry) { /* Return code depends on whether the last component was not found or if an intermediate component was invalid */ if (pszCursor == NULL) { ntError = STATUS_OBJECT_NAME_NOT_FOUND; } else { ntError = STATUS_OBJECT_PATH_NOT_FOUND; } BAIL_ON_NT_STATUS(ntError); } strncat(pszResolvedPath, "/", Length-1); Length -= 1; strncat(pszResolvedPath, pDirEntry->d_name, Length-1); Length -= RtlCStringNumChars(pDirEntry->d_name); ntError = PvfsSysCloseDir(pDir); pDir = NULL; BAIL_ON_NT_STATUS(ntError); ntError = PvfsPathCacheAdd(pszResolvedPath); BAIL_ON_NT_STATUS(ntError); } /* Cleanup for next loop */ if (pszCursor) { *pszCursor = '/'; } if ((pszComponent = strchr(pszComponent, '/')) != NULL) { pszComponent++; } } /* Did we finish? */ if ((Length <= 0) && (pszComponent != NULL)) { ntError = STATUS_NAME_TOO_LONG; BAIL_ON_NT_STATUS(ntError); } *ppszResolvedPath = pszResolvedPath; pszResolvedPath = NULL; ntError = STATUS_SUCCESS; cleanup: RtlCStringFree(&pszPath); RtlCStringFree(&pszWorkingPath); RtlCStringFree(&pszResWorkingPath); RtlCStringFree(&pszResolvedPath); if (pDir) { PvfsSysCloseDir(pDir); } return ntError; error: goto cleanup; }
NTSTATUS PvfsLookupPath( PSTR *ppszDiskPath, PCSTR pszPath, BOOLEAN bCaseSensitive ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PVFS_STAT Stat = {0}; PSTR pszDiskPath = NULL; /* Check the cache */ ntError = PvfsPathCacheLookup(&pszDiskPath, pszPath); if (ntError == STATUS_SUCCESS) { /* Check that the path is still good. If not fallback to manual checks */ ntError = PvfsSysStat(pszDiskPath, &Stat); if (ntError == STATUS_SUCCESS) { *ppszDiskPath = pszDiskPath; pszDiskPath = NULL; goto cleanup; } LwRtlCStringFree(&pszDiskPath); pszDiskPath = NULL; } /* See if we are lucky */ ntError = PvfsSysStat(pszPath, &Stat); if (ntError == STATUS_SUCCESS) { ntError = RtlCStringDuplicate(ppszDiskPath, pszPath); BAIL_ON_NT_STATUS(ntError); ntError = PvfsPathCacheAdd(*ppszDiskPath); BAIL_ON_NT_STATUS(ntError); goto cleanup; } /* Done if use case sensitive matches */ if (bCaseSensitive) { ntError = STATUS_OBJECT_NAME_NOT_FOUND; BAIL_ON_NT_STATUS(ntError); } /* Resolve the path */ ntError = PvfsResolvePath(ppszDiskPath, pszPath); BAIL_ON_NT_STATUS(ntError); /* This should succeed now */ ntError = PvfsSysStat(*ppszDiskPath, &Stat); BAIL_ON_NT_STATUS(ntError); cleanup: LwRtlCStringFree(&pszDiskPath); return ntError; error: LwRtlCStringFree(ppszDiskPath); goto cleanup; }