NTSTATUS PvfsGetFileAttributes( IN PPVFS_CCB pCcb, OUT PFILE_ATTRIBUTES pAttributes ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; *pAttributes = 0; #ifdef HAVE_EA_SUPPORT ntError = PvfsGetFileAttributesXattr(pCcb, pAttributes); #endif /* Fallback to generating a default attribute set */ if (!NT_SUCCESS(ntError)) { PSTR pszRelativeFilename = NULL; ntError = PvfsFileBasename(&pszRelativeFilename, pCcb->pszFilename); BAIL_ON_NT_STATUS(ntError); *pAttributes |= FILE_ATTRIBUTE_ARCHIVE; /* Hide 'dot' files (except "." and "..") */ if (!RtlCStringIsEqual(pszRelativeFilename, ".", FALSE) && !RtlCStringIsEqual(pszRelativeFilename, "..", FALSE)) { if (*pszRelativeFilename == '.') { *pAttributes |= FILE_ATTRIBUTE_HIDDEN; } } RTL_FREE(&pszRelativeFilename); } /* Some attributes are properties on the file and not not settable. Make sure to clear and then reset if necessary */ *pAttributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY); if (pCcb->CreateOptions & FILE_DIRECTORY_FILE) { *pAttributes |= FILE_ATTRIBUTE_DIRECTORY; } /* FILE_ATTRIBUTE_NORMAL is only valid when the file has no other attributes set (according to SetFileAttribues() in the MS SDK) */ if (*pAttributes == 0) { *pAttributes = FILE_ATTRIBUTE_NORMAL; } /* This will always succeed somehow */ 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; }
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; }
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; }
NTSTATUS PvfsEnumerateDirectory( PPVFS_CCB pCcb, PIO_MATCH_FILE_SPEC pFileSpec, LONG Count, BOOLEAN bRescan ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; DIR *pDir = NULL; struct dirent *pDirEntry = NULL; struct dirent dirEntry = { 0 }; PSTR pszPattern = NULL; BOOLEAN bCaseSensitive = FALSE; PSTR pszDiskFilename = NULL; PVFS_STAT Stat = { 0 }; PSTR pszResolvedDirname = NULL; PSTR pszResolvedFilename = NULL; if (Count == 0) { ntError = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntError); } ntError = AllocateCStringFileSpec(&pszPattern, pFileSpec); BAIL_ON_NT_STATUS(ntError); if (bRescan) { if (pCcb->pDirContext->pDir) { PvfsSysCloseDir(pCcb->pDirContext->pDir); } PvfsFreeDirectoryContext(pCcb->pDirContext); pCcb->pDirContext = NULL; ntError = PvfsAllocateMemory( (PVOID)&pCcb->pDirContext, sizeof(PVFS_DIRECTORY_CONTEXT), TRUE); BAIL_ON_NT_STATUS(ntError); pCcb->pDirContext->bScanned = FALSE; } if (!pCcb->pDirContext->bScanned) { if (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')) { // A single file name match is the equivalent of a Windows Stat() ntError = PvfsLookupFile( &pszDiskFilename, &Stat, pCcb->pScb->pOwnerFcb->pszFilename, pszPattern, pFileSpec->Options & IO_NAME_OPTION_CASE_SENSITIVE); BAIL_ON_NT_STATUS(ntError); ntError = PvfsFileSplitPath( &pszResolvedDirname, &pszResolvedFilename, pszDiskFilename); BAIL_ON_NT_STATUS(ntError); if (IsSetFlag(pCcb->Flags, PVFS_CCB_FLAG_ENABLE_ABE)) { ntError = PvfsAccessCheckFileEnumerate( pCcb, pszResolvedFilename); BAIL_ON_NT_STATUS(ntError); } ntError = PvfsDirContextAddEntry( pCcb->pDirContext, pszResolvedFilename); BAIL_ON_NT_STATUS(ntError); pCcb->pDirContext->bScanned = TRUE; // Success goto cleanup; } else { // Prepare to enumerate the entire directory ntError = PvfsSysOpenDir( pCcb->pScb->pOwnerFcb->pszFilename, &pCcb->pDirContext->pDir); BAIL_ON_NT_STATUS(ntError); pCcb->pDirContext->bScanned = TRUE; } } /* Loop to read entries */ pDir = pCcb->pDirContext->pDir; /* -1 means to fill in whatever you can including current and parent directory entries */ if (Count == -1) { /* Always add '.' and '..' first if thet match */ if (PvfsWildcardMatch(".", pszPattern, FALSE)) { ntError = PvfsDirContextAddEntry(pCcb->pDirContext, "."); BAIL_ON_NT_STATUS(ntError); } if (PvfsWildcardMatch("..", pszPattern, FALSE)) { ntError = PvfsDirContextAddEntry(pCcb->pDirContext, ".."); BAIL_ON_NT_STATUS(ntError); } } /* Loop through directory entries */ for(ntError = PvfsSysReadDir(pDir, &dirEntry, &pDirEntry); pDirEntry && ((Count == -1) || (pCcb->pDirContext->dwNumEntries < Count)); ntError = PvfsSysReadDir(pDir, &dirEntry, &pDirEntry)) { /* First check the error return */ BAIL_ON_NT_STATUS(ntError); /* We've already added the "." and ".." directories */ if (RtlCStringIsEqual(pDirEntry->d_name, ".", FALSE) || RtlCStringIsEqual(pDirEntry->d_name, "..", FALSE)) { continue; } if (PvfsWildcardMatch(pDirEntry->d_name, pszPattern, bCaseSensitive)) { if (IsSetFlag(pCcb->Flags, PVFS_CCB_FLAG_ENABLE_ABE)) { ntError = PvfsAccessCheckFileEnumerate( pCcb, pDirEntry->d_name); if (ntError != STATUS_SUCCESS) { continue; } } ntError = PvfsDirContextAddEntry(pCcb->pDirContext, pDirEntry->d_name); BAIL_ON_NT_STATUS(ntError); } } /* Bail if there were no matches */ if (pCcb->pDirContext->dwNumEntries == 0) { ntError = STATUS_NO_SUCH_FILE; BAIL_ON_NT_STATUS(ntError); } cleanup: RtlCStringFree(&pszResolvedFilename); RtlCStringFree(&pszResolvedDirname); RtlCStringFree(&pszDiskFilename); RtlCStringFree(&pszPattern); return ntError; error: goto cleanup; }