VOID PvfsInitializeZctSupport( IN PPVFS_CCB pCcb, IN IO_FILE_HANDLE FileHandle ) { PVFS_ZCT_MODE ZctMode = gPvfsDriverConfig.ZctMode; LW_ZCT_ENTRY_MASK zctReadMask = 0; LW_ZCT_ENTRY_MASK zctWriteMask = 0; if (!PVFS_IS_DIR(pCcb)) { switch (ZctMode) { case PVFS_ZCT_MODE_MEMORY: zctReadMask = zctWriteMask = LW_ZCT_ENTRY_MASK_MEMORY; break; #ifdef HAVE_SPLICE case PVFS_ZCT_MODE_SPLICE: zctReadMask = zctWriteMask = LW_ZCT_ENTRY_MASK_FD_PIPE; break; #endif default: break; } if (zctReadMask || zctWriteMask) { IoFileSetZctSupportMask(FileHandle, zctReadMask, zctWriteMask); } } }
NTSTATUS PvfsCcbQueryFileStandardInformation( PPVFS_CCB pCcb, PFILE_STANDARD_INFORMATION pFileInfo ) { NTSTATUS ntError = STATUS_SUCCESS; PVFS_STAT Stat = {0}; BOOLEAN bDeletePending = FALSE; LONG64 allocationSize = 0; ntError = PvfsSysFstat(pCcb->fd, &Stat); BAIL_ON_NT_STATUS(ntError); bDeletePending = PvfsScbIsPendingDelete(pCcb->pScb); if (PVFS_IS_DIR(pCcb)) { /* NTFS reports the allocation and end-of-file on directories as 0. smbtorture cares about this even if no apps that I know of do. */ pFileInfo->AllocationSize = 0; pFileInfo->EndOfFile = 0; pFileInfo->NumberOfLinks = bDeletePending ? 0 : 1; } else { allocationSize = PvfsGetScbAllocationSize(pCcb->pScb); pFileInfo->EndOfFile = Stat.s_size; pFileInfo->AllocationSize = PVFS_MAX((Stat.s_alloc > Stat.s_size ? Stat.s_alloc : Stat.s_size), allocationSize); pFileInfo->NumberOfLinks = bDeletePending ? Stat.s_nlink - 1 : Stat.s_nlink; } pFileInfo->DeletePending = bDeletePending; pFileInfo->Directory = S_ISDIR(Stat.s_mode) ? TRUE : FALSE; cleanup: return ntError; error: goto cleanup; }
NTSTATUS PvfsReadDirectoryChange( PPVFS_IRP_CONTEXT pIrpContext ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PIRP pIrp = pIrpContext->pIrp; IRP_ARGS_READ_DIRECTORY_CHANGE Args = pIrp->Args.ReadDirectoryChange; PPVFS_CCB pCcb = NULL; PULONG pMaxBufferSize = Args.MaxBufferSize; /* Sanity checks */ ntError = PvfsAcquireCCB(pIrp->FileHandle, &pCcb); BAIL_ON_NT_STATUS(ntError); if (!IsSetFlag(pCcb->Flags, PVFS_CCB_FLAG_CREATE_COMPLETE)) { ntError = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntError); } if (!PVFS_IS_DIR(pCcb)) { ntError = STATUS_NOT_A_DIRECTORY; BAIL_ON_NT_STATUS(ntError); } ntError = PvfsAccessCheckFileHandle(pCcb, FILE_LIST_DIRECTORY); BAIL_ON_NT_STATUS(ntError); BAIL_ON_INVALID_PTR(Args.Buffer, ntError); BAIL_ON_ZERO_LENGTH(Args.Length, ntError); /* If we have something in the buffer, return that immediately. Else register a notification filter */ LWIO_ASSERT(pCcb->pScb->pOwnerFcb); ntError = PvfsNotifyReportBufferedChanges( pCcb, pCcb->pScb->pOwnerFcb, pIrpContext); if (ntError == STATUS_NOT_FOUND) { PvfsIrpMarkPending(pIrpContext, PvfsQueueCancelIrp, pIrpContext); LWIO_ASSERT(pCcb->pScb->pOwnerFcb); ntError = PvfsNotifyAddFilter( pCcb->pScb->pOwnerFcb, pIrpContext, pCcb, Args.NotifyFilter, Args.WatchTree, pMaxBufferSize); if (ntError == STATUS_SUCCESS) { pIrpContext->QueueType = PVFS_QUEUE_TYPE_NOTIFY; if (!pIrpContext->pScb) { pIrpContext->pScb = PvfsReferenceSCB(pCcb->pScb); } /* Allow the call to be cancelled while in the queue */ PvfsIrpContextClearFlag(pIrpContext, PVFS_IRP_CTX_FLAG_ACTIVE); ntError = STATUS_PENDING; goto cleanup; } } BAIL_ON_NT_STATUS(ntError); cleanup: if (pCcb) { PvfsReleaseCCB(pCcb); } return ntError; error: if (PvfsIrpContextCheckFlag(pIrpContext, PVFS_IRP_CTX_FLAG_PENDED)) { pIrpContext->pIrp->IoStatusBlock.Status = ntError; PvfsCompleteIrpContext(pIrpContext); } goto cleanup; }
static NTSTATUS PvfsQueryFileIdFullDirInfo( PPVFS_IRP_CONTEXT pIrpContext ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PIRP pIrp = pIrpContext->pIrp; PPVFS_CCB pCcb = NULL; PFILE_ID_FULL_DIR_INFORMATION pFileInfo = NULL; PFILE_ID_FULL_DIR_INFORMATION pPrevFileInfo = NULL; IRP_ARGS_QUERY_DIRECTORY Args = pIrpContext->pIrp->Args.QueryDirectory; PVOID pBuffer = NULL; DWORD dwBufLen = 0; DWORD dwOffset = 0; DWORD dwConsumed = 0; BOOLEAN bLocked = FALSE; /* Sanity checks */ ntError = PvfsAcquireCCB(pIrp->FileHandle, &pCcb); BAIL_ON_NT_STATUS(ntError); if (!PVFS_IS_DIR(pCcb)) { ntError = STATUS_NOT_A_DIRECTORY; BAIL_ON_NT_STATUS(ntError); } ntError = PvfsAccessCheckFileHandle(pCcb, FILE_LIST_DIRECTORY); BAIL_ON_NT_STATUS(ntError); BAIL_ON_INVALID_PTR(Args.FileInformation, ntError); if (Args.Length < sizeof(*pFileInfo)) { ntError = STATUS_BUFFER_TOO_SMALL; BAIL_ON_NT_STATUS(ntError); } pFileInfo = (PFILE_ID_FULL_DIR_INFORMATION)Args.FileInformation; /* Scen the first time through */ ntError = STATUS_SUCCESS; /* Critical region to prevent inteleaving directory enumeration */ LWIO_LOCK_MUTEX(bLocked, &pCcb->FileMutex); if (!pCcb->pDirContext->bScanned) { ntError = PvfsEnumerateDirectory( pCcb, pIrp->Args.QueryDirectory.FileSpec, -1, FALSE); } LWIO_UNLOCK_MUTEX(bLocked, &pCcb->FileMutex); BAIL_ON_NT_STATUS(ntError); /* Check for ending condition */ if (pCcb->pDirContext->dwIndex == pCcb->pDirContext->dwNumEntries) { ntError = STATUS_NO_MORE_MATCHES; BAIL_ON_NT_STATUS(ntError); } /* Fill in the buffer */ pBuffer = Args.FileInformation; dwBufLen = Args.Length; dwOffset = 0; pFileInfo = NULL; pPrevFileInfo = NULL; do { PPVFS_DIRECTORY_ENTRY pEntry = NULL; DWORD dwIndex; pFileInfo = (PFILE_ID_FULL_DIR_INFORMATION)(pBuffer + dwOffset); pFileInfo->NextEntryOffset = 0; dwIndex = pCcb->pDirContext->dwIndex; pEntry = &pCcb->pDirContext->pDirEntries[dwIndex]; ntError = FillFileIdFullDirInfoBuffer( pFileInfo, dwBufLen - dwOffset, pCcb->pszFilename, pEntry, &dwConsumed); /* If we ran out of buffer space, reset pointer to previous entry and break out of loop */ if (ntError == STATUS_BUFFER_TOO_SMALL) { pFileInfo = pPrevFileInfo; break; } /* OBJECT_NAME_NOT_FOUND - This deals with a possible race where the directory contents was read but the file was removed before we could stat() it. INSUFFICIENT_RESOURCES - Invalid UTF-8 name. ACCESS_DENIED - Special cases like $HOME/.gvfs that can't be read by root. Possibly just an Ubuntu bug but don't fail on it. https://bugs.launchpad.net/ubuntu/+source/gvfs/+bug/227724 Just skip the file and move on. */ if (ntError == STATUS_OBJECT_NAME_NOT_FOUND || ntError == STATUS_INSUFFICIENT_RESOURCES || ntError == STATUS_ACCESS_DENIED) { pFileInfo = pPrevFileInfo; pCcb->pDirContext->dwIndex++; continue; } /* Catch any other errors and bail */ BAIL_ON_NT_STATUS(ntError); pFileInfo->NextEntryOffset = dwConsumed; dwOffset += dwConsumed; pCcb->pDirContext->dwIndex++; pPrevFileInfo = pFileInfo; if (Args.ReturnSingleEntry) { break; } } /* Exit loop when we are out of buffer or out of entries. The filling function can also break us out of the loop. */ while (((dwBufLen - dwOffset) > sizeof(FILE_ID_FULL_DIR_INFORMATION)) && (pCcb->pDirContext->dwIndex < pCcb->pDirContext->dwNumEntries)); /* Update final offset */ if (pFileInfo) { pFileInfo->NextEntryOffset = 0; } pIrp->IoStatusBlock.BytesTransferred = dwOffset; ntError = STATUS_SUCCESS; cleanup: if (pCcb) { PvfsReleaseCCB(pCcb); } return ntError; error: goto cleanup; }
NTSTATUS PvfsRenameCCB( IN PPVFS_CCB pCcb, IN PPVFS_FILE_NAME pDestFileName ) { NTSTATUS ntError = STATUS_SUCCESS; PVFS_FILE_NAME srcFileName = { 0 }; ntError = PvfsValidatePathSCB(pCcb->pScb, &pCcb->FileId); BAIL_ON_NT_STATUS(ntError); ntError = PvfsBuildFileNameFromScb(&srcFileName, pCcb->pScb); BAIL_ON_NT_STATUS(ntError); if (!PvfsIsDefaultStreamName(&srcFileName) && !PvfsIsDefaultStreamName(pDestFileName)) { // Two named streams if (LwRtlCStringIsEqual( PvfsGetCStringBaseStreamName(&srcFileName), PvfsGetCStringBaseStreamName(pDestFileName), FALSE)) { // Both src and dst stream names are the same // renaming the underlying file object ntError = PvfsRenameFile(pCcb, pDestFileName); } else if (LwRtlCStringIsEqual( PvfsGetCStringBaseFileName(&srcFileName), PvfsGetCStringBaseFileName(pDestFileName), FALSE)) { // Renaming the named stream, file name stays the same ntError = PvfsRenameStream(pCcb, pDestFileName); } else { // Don't allow renaming both the file name and stream name at the // same time (yet) ntError = STATUS_OBJECT_NAME_INVALID; } } else if (PvfsIsDefaultStreamName(&srcFileName) && PvfsIsDefaultStreamName(pDestFileName)) { // Two default streams rename object itself ntError = PvfsRenameFile(pCcb, pDestFileName); } else if (!PvfsIsDefaultStreamName(&srcFileName) && PvfsIsDefaultStreamName(pDestFileName)) { // rename name stream -> default stream // A stream on a directory cannot be renamed to the default data stream if (!LwRtlCStringIsEqual( PvfsGetCStringBaseFileName(&srcFileName), PvfsGetCStringBaseFileName(pDestFileName), FALSE) || PVFS_IS_DIR(pCcb)) { ntError = STATUS_OBJECT_NAME_INVALID; } else { ntError = PvfsRenameStream(pCcb, pDestFileName); } } else { // disallow rename object->stream as what smbtorture expects // TODO: // we may want to allow "Renaming" the default data stream // it is not a true rename, and it leaves behind a zero-length default data streams ntError = STATUS_OBJECT_NAME_INVALID; } BAIL_ON_NT_STATUS(ntError); error: PvfsDestroyFileName(&srcFileName); return ntError; }
static NTSTATUS PvfsReadInternal( PPVFS_IRP_CONTEXT pIrpContext ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PIRP pIrp = pIrpContext->pIrp; PPVFS_CCB pCcb = NULL; PPVFS_PENDING_READ pReadCtx = NULL; /* Sanity checks */ ntError = PvfsAcquireCCB(pIrp->FileHandle, &pCcb); BAIL_ON_NT_STATUS(ntError); if (PVFS_IS_DIR(pCcb)) { ntError = STATUS_FILE_IS_A_DIRECTORY; BAIL_ON_NT_STATUS(ntError); } #if 0xFFFFFFFF > SSIZE_MAX if ((size_t)pIrp->Args.ReadWrite.Length > (size_t) SSIZE_MAX) { ntError = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntError); } #endif /* Check the right permissions based on the PagingIo flag */ if (pIrp->Args.ReadWrite.IsPagingIo) { ntError = PvfsAccessCheckAnyFileHandle( pCcb, FILE_READ_DATA|FILE_EXECUTE); } else { ntError = PvfsAccessCheckFileHandle( pCcb, FILE_READ_DATA); } BAIL_ON_NT_STATUS(ntError); ntError = PvfsCreateReadContext(&pReadCtx, pIrpContext, pCcb); BAIL_ON_NT_STATUS(ntError); ntError = PvfsOplockBreakIfLocked(pIrpContext, pCcb, pCcb->pScb); switch (ntError) { case STATUS_SUCCESS: ntError = PvfsReadFileWithContext(pReadCtx); break; case STATUS_OPLOCK_BREAK_IN_PROGRESS: ntError = PvfsPendOplockBreakTest( pReadCtx->pCcb->pScb, pIrpContext, pReadCtx->pCcb, PvfsReadFileWithContext, PvfsFreeReadContext, (PVOID)pReadCtx); if (ntError == STATUS_PENDING) { pReadCtx = NULL; } break; case STATUS_PENDING: ntError = PvfsAddItemPendingOplockBreakAck( pReadCtx->pCcb->pScb, pIrpContext, PvfsReadFileWithContext, PvfsFreeReadContext, (PVOID)pReadCtx); if (ntError == STATUS_PENDING) { pReadCtx = NULL; } break; } BAIL_ON_NT_STATUS(ntError); cleanup: PvfsFreeReadContext(OUT_PPVOID(&pReadCtx)); if (pCcb) { PvfsReleaseCCB(pCcb); } return ntError; error: goto cleanup; }