/* Copyright (c) 1992 Microsoft Corporation Module Name: fileio.c Abstract: This module contains the routines for performing file system functions. No other part of the server should be calling filesystem NtXXX routines directly Author: Jameel Hyder (microsoft!jameelh) Revision History: 18 Jun 1992 Initial Version Notes: Tab stop: 4 --*/ #define FILEIO_LOCALS #define FILENUM FILE_FILEIO #include #include #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, AfpFileIoInit) #pragma alloc_text( PAGE, AfpIoOpen) #pragma alloc_text( PAGE, AfpIoCreate) #pragma alloc_text( PAGE, AfpIoRead) #pragma alloc_text( PAGE, AfpIoWrite) #pragma alloc_text( PAGE, AfpIoQuerySize) #pragma alloc_text( PAGE, AfpIoSetSize) #pragma alloc_text( PAGE, AfpIoChangeNTModTime) #pragma alloc_text( PAGE, AfpIoQueryTimesnAttr) #pragma alloc_text( PAGE, AfpIoSetTimesnAttr) #pragma alloc_text( PAGE, AfpIoQueryLongName) #pragma alloc_text( PAGE, AfpIoQueryShortName) #pragma alloc_text( PAGE, AfpIoQueryStreams) #pragma alloc_text( PAGE, AfpIoMarkFileForDelete) #pragma alloc_text( PAGE, AfpIoQueryDirectoryFile) #pragma alloc_text( PAGE, AfpIoQueryBasicInfo) #pragma alloc_text( PAGE, AfpIoClose) #pragma alloc_text( PAGE, AfpIoQueryVolumeSize) #pragma alloc_text( PAGE, AfpIoMoveAndOrRename) #pragma alloc_text( PAGE, AfpIoCopyFile1) #pragma alloc_text( PAGE, AfpIoCopyFile2) #pragma alloc_text( PAGE, AfpIoWait) #pragma alloc_text( PAGE, AfpIoConvertNTStatusToAfpStatus) #pragma alloc_text( PAGE, AfpQueryPath) #pragma alloc_text( PAGE, AfpIoIsSupportedDevice) #endif /*** AfpFileIoInit * * Initialize various strings that we use for stream names etc. */ NTSTATUS AfpFileIoInit( VOID ) { // NTFS Stream names RtlInitUnicodeString(&AfpIdDbStream, AFP_IDDB_STREAM); RtlInitUnicodeString(&AfpDesktopStream, AFP_DT_STREAM); RtlInitUnicodeString(&AfpResourceStream, AFP_RESC_STREAM); RtlInitUnicodeString(&AfpInfoStream, AFP_INFO_STREAM); RtlInitUnicodeString(&AfpCommentStream, AFP_COMM_STREAM); RtlInitUnicodeString(&AfpDataStream, L""); // Directory enumeration names to ignore RtlInitUnicodeString(&Dot,L"."); RtlInitUnicodeString(&DotDot,L".."); // Supported file systems RtlInitUnicodeString(&afpNTFSName, AFP_NTFS); RtlInitUnicodeString(&afpCDFSName, AFP_CDFS); RtlInitUnicodeString(&afpAHFSName, AFP_AHFS); // Prepended to full path names originating at drive letter RtlInitUnicodeString(&DosDevices, AFP_DOSDEVICES); // CopyFile stream not to create RtlInitUnicodeString(&DataStreamName, FULL_DATA_STREAM_NAME); RtlInitUnicodeString(&FullCommentStreamName, FULL_COMMENT_STREAM_NAME); RtlInitUnicodeString(&FullResourceStreamName, FULL_RESOURCE_STREAM_NAME); RtlInitUnicodeString(&FullInfoStreamName, FULL_INFO_STREAM_NAME); // ExchangeFiles temporary filename RtlInitUnicodeString(&AfpExchangeName, AFP_TEMP_EXCHANGE_NAME); return STATUS_SUCCESS; } /*** AfpIoOpen * * Perform a file/stream open. The stream is specified by a manifest rather * than a name. The entity can only be opened by name (Not by ID). * If a stream other than the DATA stream is to be opened, then * the phRelative handle MUST be that of the unnamed (that is, DATA) stream * of the file/dir itself. */ NTSTATUS AfpIoOpen( IN PFILESYSHANDLE phRelative OPTIONAL, IN DWORD StreamId, IN DWORD OpenOptions, IN PUNICODE_STRING pObject, IN DWORD AfpAccess, // FILEIO_ACCESS_XXX desired access IN DWORD AfpDenyMode, // FILIEO_DENY_XXX IN BOOLEAN CheckAccess, OUT PFILESYSHANDLE pNewHandle ) { OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStsBlk; NTSTATUS Status; NTSTATUS Status2; UNICODE_STRING UName; HANDLE hRelative = NULL; BOOLEAN FreeBuf = False; #ifdef PROFILING TIME TimeS, TimeE, TimeD; AfpGetPerfCounter(&TimeS); #endif PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoOpen entered\n")); ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); #if DBG pNewHandle->Signature = FSH_SIGNATURE; #endif // Assume Failure pNewHandle->fsh_FileHandle = NULL; if (phRelative != NULL) { ASSERT(VALID_FSH(phRelative)); hRelative = phRelative->fsh_FileHandle; } ASSERT (StreamId < AFP_STREAM_MAX); ASSERT ((pObject->Length > 0) || (phRelative != NULL)); if (StreamId != AFP_STREAM_DATA) { if (pObject->Length > 0) { UName.Length = UName.MaximumLength = pObject->Length + AFP_MAX_STREAMNAME*sizeof(WCHAR); UName.Buffer = (LPWSTR)AfpAllocNonPagedMemory(UName.Length); if (UName.Buffer == NULL) { return STATUS_NO_MEMORY; } AfpCopyUnicodeString(&UName, pObject); RtlAppendUnicodeStringToString(&UName, &AfpStreams[StreamId]); pObject = &UName; FreeBuf = True; } else { pObject = &AfpStreams[StreamId]; } } InitializeObjectAttributes(&ObjAttr, pObject, OBJ_CASE_INSENSITIVE, hRelative, NULL); // no security descriptor ObjAttr.SecurityQualityOfService = &AfpSecurityQOS; DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoOpen: about to call NtOpenFile\n")); // If we are opening for RWCTRL, then specify to use privilege. if (AfpAccess & (WRITE_DAC | WRITE_OWNER)) { OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT; } Status = IoCreateFile(&pNewHandle->fsh_FileHandle, AfpAccess, &ObjAttr, &IoStsBlk, NULL, 0, AfpDenyMode, FILE_OPEN, OpenOptions, (PVOID)NULL, 0L, CreateFileTypeNone, (PVOID)NULL, CheckAccess ? IO_FORCE_ACCESS_CHECK : 0); if (Status != 0) DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoOpen: IoCreateFile returned 0x%lx, %Z\n",Status, ObjAttr.ObjectName)); if (FreeBuf) AfpFreeMemory(UName.Buffer); if (NT_SUCCESS(Status)) { Status = ObReferenceObjectByHandle(pNewHandle->fsh_FileHandle, AfpAccess, NULL, KernelMode, (PVOID *)(&pNewHandle->fsh_FileObject), NULL); if (!NT_SUCCESS(Status)) { ASSERT(VALID_FSH((PFILESYSHANDLE)&pNewHandle->fsh_FileHandle)) ; Status2 = NtClose(pNewHandle->fsh_FileHandle); pNewHandle->fsh_FileHandle = NULL; ASSERT(NT_SUCCESS(Status2)); } else { pNewHandle->fsh_DeviceObject = IoGetRelatedDeviceObject(pNewHandle->fsh_FileObject); (ULONG_PTR)(pNewHandle->fsh_FileObject) |= 1; ASSERT(NT_SUCCESS(Status)); afpUpdateOpenFiles(True, True); #ifdef PROFILING AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; if (OpenOptions == FILEIO_OPEN_DIR) { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_OpenCountDR); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_OpenTimeDR, TimeD, &AfpStatisticsLock); } else { if ((AfpAccess & FILEIO_ACCESS_DELETE) == FILEIO_ACCESS_DELETE) { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_OpenCountDL); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_OpenTimeDL, TimeD, &AfpStatisticsLock); } else if (((AfpAccess & FILEIO_ACCESS_READWRITE) == FILEIO_ACCESS_READ) || ((AfpAccess & FILEIO_ACCESS_READWRITE) == FILEIO_ACCESS_WRITE) || ((AfpAccess & FILEIO_ACCESS_READWRITE) == FILEIO_ACCESS_READWRITE)) { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_OpenCountRW); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_OpenTimeRW, TimeD, &AfpStatisticsLock); } else if (AfpAccess & (WRITE_DAC | WRITE_OWNER)) { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_OpenCountWC); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_OpenTimeWC, TimeD, &AfpStatisticsLock); } else if (AfpAccess & READ_CONTROL) { INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_OpenCountRC); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_OpenTimeRC, TimeD, &AfpStatisticsLock); } else // Ought to be read-attributes or write-attributes { ASSERT ((AfpAccess == FILEIO_ACCESS_NONE) || (AfpAccess == (FILEIO_ACCESS_NONE | FILE_WRITE_ATTRIBUTES))); INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_OpenCountRA); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_OpenTimeRA, TimeD, &AfpStatisticsLock); } } #endif } } return Status; } /*** AfpIoCreate * * Perform a file/stream create. The stream is specified by a manifest rather * than a name. If a stream other than the DATA stream is to be created, then * the phRelative handle MUST be that of either the Parent directory, or the * unnamed (that is, DATA) stream of the file/dir itself because we only use * a buffer large enough for a AFP filename plus the maximum stream name * length. */ NTSTATUS AfpIoCreate( IN PFILESYSHANDLE phRelative, // create relative to this IN DWORD StreamId, // Id of stream to create IN PUNICODE_STRING pObject, // Name of file IN DWORD AfpAccess, // FILEIO_ACCESS_XXX desired access IN DWORD AfpDenyMode, // FILEIO_DENY_XXX IN DWORD CreateOptions, // File/Directory etc. IN DWORD Disposition, // Soft or hard create IN DWORD Attributes, // hidden, archive, normal, etc. IN BOOLEAN CheckAccess, // If TRUE, enforce security IN PSECURITY_DESCRIPTOR pSecDesc OPTIONAL, // Security descriptor to slap on OUT PFILESYSHANDLE pNewHandle, // Place holder for the handle OUT PDWORD pInformation OPTIONAL, // file opened, created, etc. IN PVOLDESC pVolDesc OPTIONAL, // only if NotifyPath IN PUNICODE_STRING pNotifyPath OPTIONAL, IN PUNICODE_STRING pNotifyParentPath OPTIONAL ) { NTSTATUS Status; NTSTATUS Status2; OBJECT_ATTRIBUTES ObjAttr; UNICODE_STRING RealName; IO_STATUS_BLOCK IoStsBlk; HANDLE hRelative; WCHAR NameBuffer[AFP_FILENAME_LEN + 1 + AFP_MAX_STREAMNAME]; BOOLEAN Queue = False; #ifdef PROFILING TIME TimeS, TimeE, TimeD; AfpGetPerfCounter(&TimeS); #endif PAGED_CODE( ); ASSERT(pObject != NULL && phRelative != NULL && StreamId < AFP_STREAM_MAX); ASSERT(VALID_FSH(phRelative) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); #if DBG pNewHandle->Signature = FSH_SIGNATURE; #endif hRelative = phRelative->fsh_FileHandle; // Assume Failure pNewHandle->fsh_FileHandle = NULL; if (StreamId != AFP_STREAM_DATA) { ASSERT(pObject->Length <= (AFP_FILENAME_LEN*sizeof(WCHAR))); // Construct the name to pass to NtCreateFile AfpSetEmptyUnicodeString(&RealName, sizeof(NameBuffer), NameBuffer); AfpCopyUnicodeString(&RealName, pObject); RtlAppendUnicodeStringToString(&RealName, &AfpStreams[StreamId]); pObject = &RealName; } InitializeObjectAttributes(&ObjAttr, pObject, OBJ_CASE_INSENSITIVE, hRelative, pSecDesc); ObjAttr.SecurityQualityOfService = &AfpSecurityQOS; // Do not queue our changes for exclusive volumes since no notifies are posted if (ARGUMENT_PRESENT(pNotifyPath) && !EXCLUSIVE_VOLUME(pVolDesc) && (StreamId == AFP_STREAM_DATA)) { ASSERT(VALID_VOLDESC(pVolDesc)); ASSERT((Disposition == FILEIO_CREATE_HARD) || (Disposition == FILEIO_CREATE_SOFT)); Queue = True; // Queue a change for both cases. AfpQueueOurChange(pVolDesc, FILE_ACTION_ADDED, pNotifyPath, pNotifyParentPath); AfpQueueOurChange(pVolDesc, FILE_ACTION_MODIFIED, pNotifyPath, NULL); } Status = IoCreateFile(&pNewHandle->fsh_FileHandle, AfpAccess, &ObjAttr, &IoStsBlk, NULL, Attributes, AfpDenyMode, Disposition, CreateOptions, NULL, 0, CreateFileTypeNone, (PVOID)NULL, CheckAccess ? IO_FORCE_ACCESS_CHECK : 0); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoCreate: IoCreateFile returned 0x%lx\n", Status) ); if (Queue) { if (NT_SUCCESS(Status)) { ASSERT((IoStsBlk.Information == FILE_CREATED) || (IoStsBlk.Information == FILE_SUPERSEDED)); // Dequeue the extra change that was queued AfpDequeueOurChange(pVolDesc, (IoStsBlk.Information == FILE_CREATED) ? FILE_ACTION_MODIFIED : FILE_ACTION_ADDED, pNotifyPath, NULL); } else { AfpDequeueOurChange(pVolDesc, FILE_ACTION_ADDED, pNotifyPath, pNotifyParentPath); AfpDequeueOurChange(pVolDesc, FILE_ACTION_MODIFIED, pNotifyPath, NULL); } } if (NT_SUCCESS(Status)) { if (ARGUMENT_PRESENT(pInformation)) { *pInformation = (ULONG)(IoStsBlk.Information); } Status = ObReferenceObjectByHandle(pNewHandle->fsh_FileHandle, AfpAccess, NULL, KernelMode, (PVOID *)(&pNewHandle->fsh_FileObject), NULL); if (!NT_SUCCESS(Status)) { ASSERT(VALID_FSH((PFILESYSHANDLE)&pNewHandle->fsh_FileHandle)) ; Status2 = NtClose(pNewHandle->fsh_FileHandle); pNewHandle->fsh_FileHandle = NULL; ASSERT(NT_SUCCESS(Status2)); } else { ASSERT(NT_SUCCESS(Status)); pNewHandle->fsh_DeviceObject = IoGetRelatedDeviceObject(pNewHandle->fsh_FileObject); (ULONG_PTR)(pNewHandle->fsh_FileObject) |= 1; afpUpdateOpenFiles(True, True); #ifdef PROFILING AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; if (StreamId == AFP_STREAM_DATA) { if (CreateOptions == FILEIO_OPEN_FILE) { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_CreateCountFIL); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_CreateTimeFIL, TimeD, &AfpStatisticsLock); } else { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_CreateCountDIR); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_CreateTimeDIR, TimeD, &AfpStatisticsLock); } } else { INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_CreateCountSTR); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_CreateTimeSTR, TimeD, &AfpStatisticsLock); } #endif } } return Status; } /*** AfpIoRead * * Perform file read. Just a wrapper over NtReadFile. */ AFPSTATUS AfpIoRead( IN PFILESYSHANDLE pFileHandle, IN PFORKOFFST pForkOffset, IN LONG SizeReq, OUT PLONG pSizeRead, OUT PBYTE pBuffer ) { NTSTATUS Status; DWORD Key = 0; IO_STATUS_BLOCK IoStsBlk; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoRead Entered, Size %lx, Key %lx\n", SizeReq, Key)); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() == LOW_LEVEL)); ASSERT (INTERNAL_HANDLE(pFileHandle)); *pSizeRead = 0; Status = NtReadFile(pFileHandle->fsh_FileHandle, NULL, NULL, NULL, &IoStsBlk, pBuffer, (DWORD)SizeReq, pForkOffset, &Key); if (NT_SUCCESS(Status)) { *pSizeRead = (LONG)IoStsBlk.Information; INTERLOCKED_ADD_STATISTICS(&AfpServerStatistics.stat_DataReadInternal, (ULONG)(IoStsBlk.Information), &AfpStatisticsLock); } else { if (Status == STATUS_FILE_LOCK_CONFLICT) Status = AFP_ERR_LOCK; else if (Status == STATUS_END_OF_FILE) Status = AFP_ERR_EOF; else { AFPLOG_HERROR(AFPSRVMSG_CANT_READ, Status, NULL, 0, pFileHandle->fsh_FileHandle); Status = AFP_ERR_MISC; } } return Status; } /*** AfpIoWrite * * Perform file write. Just a wrapper over NtWriteFile. */ AFPSTATUS AfpIoWrite( IN PFILESYSHANDLE pFileHandle, IN PFORKOFFST pForkOffset, IN LONG SizeWrite, OUT PBYTE pBuffer ) { NTSTATUS Status; DWORD Key = 0; IO_STATUS_BLOCK IoStsBlk; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoWrite Entered, Size %lx, Key %lx\n", SizeWrite, Key)); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() == LOW_LEVEL)); ASSERT (INTERNAL_HANDLE(pFileHandle)); Status = NtWriteFile(pFileHandle->fsh_FileHandle, NULL, NULL, NULL, &IoStsBlk, pBuffer, (DWORD)SizeWrite, pForkOffset, &Key); if (NT_SUCCESS(Status)) { INTERLOCKED_ADD_STATISTICS(&AfpServerStatistics.stat_DataWrittenInternal, SizeWrite, &AfpStatisticsLock); } else { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoWrite: NtWriteFile returned 0x%lx\n",Status)); if (Status == STATUS_FILE_LOCK_CONFLICT) Status = AFP_ERR_LOCK; else { AFPLOG_HERROR(AFPSRVMSG_CANT_WRITE, Status, NULL, 0, pFileHandle->fsh_FileHandle); Status = AfpIoConvertNTStatusToAfpStatus(Status); } } return Status; } /*** AfpIoQuerySize * * Get the current size of the fork. */ AFPSTATUS FASTCALL AfpIoQuerySize( IN PFILESYSHANDLE pFileHandle, OUT PFORKSIZE pForkLength ) { FILE_STANDARD_INFORMATION FStdInfo; NTSTATUS Status; IO_STATUS_BLOCK IoStsBlk; PFAST_IO_DISPATCH fastIoDispatch; PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); fastIoDispatch = pFileHandle->fsh_DeviceObject->DriverObject->FastIoDispatch; if (fastIoDispatch && fastIoDispatch->FastIoQueryStandardInfo && fastIoDispatch->FastIoQueryStandardInfo(AfpGetRealFileObject(pFileHandle->fsh_FileObject), True, &FStdInfo, &IoStsBlk, pFileHandle->fsh_DeviceObject)) { Status = IoStsBlk.Status; #ifdef PROFILING // The fast I/O path worked. Update statistics INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded)); #endif } else { #ifdef PROFILING INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed)); #endif Status = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, &FStdInfo, sizeof(FStdInfo), FileStandardInformation); } if (!NT_SUCCESS((NTSTATUS)Status)) { AFPLOG_HERROR(AFPSRVMSG_CANT_GET_FILESIZE, Status, NULL, 0, pFileHandle->fsh_FileHandle); return AFP_ERR_MISC; // What else can we do } *pForkLength = FStdInfo.EndOfFile; return AFP_ERR_NONE; } /*** AfpIoSetSize * * Set the size of the open fork to the value specified. * * ISSUE: * We can check the locks and resolve any lock conflicts before we go * to the filesystem. The issue that needs to be resolved here is: * Is it OK to truncate the file such that our own locks cause * conflict ? */ AFPSTATUS FASTCALL AfpIoSetSize( IN PFILESYSHANDLE pFileHandle, IN LONG ForkLength ) { NTSTATUS Status; FILE_END_OF_FILE_INFORMATION FEofInfo; IO_STATUS_BLOCK IoStsBlk; PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); FEofInfo.EndOfFile.QuadPart = ForkLength; Status = NtSetInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, &FEofInfo, sizeof(FEofInfo), FileEndOfFileInformation); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoSetSize: NtSetInformationFile returned 0x%lx\n",Status)); if (Status != STATUS_FILE_LOCK_CONFLICT) AFPLOG_HERROR(AFPSRVMSG_CANT_SET_FILESIZE, Status, &ForkLength, sizeof(ForkLength), pFileHandle->fsh_FileHandle); if (Status == STATUS_DISK_FULL) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR,("AfpIoSetSize: DISK_FULL error\n")); ASSERT(0); Status = AFP_ERR_DISK_FULL; } else if (Status == STATUS_FILE_LOCK_CONFLICT) Status = AFP_ERR_LOCK; // Default error code. What else can it be ? else Status = AFP_ERR_MISC; } return Status; } /*** AfpIoChangeNTModTime * * Get the NTFS ChangeTime of a file/dir. If it is larger than the * NTFS LastWriteTime, set NTFS LastWriteTime to this time. * Return the resultant LastWriteTime in pModTime (whether changed or not). * This is used to update the modified time when the resource fork is changed * or when some other attribute changes that should cause the timestamp on * the file to change as viewed by mac. * */ AFPSTATUS AfpIoChangeNTModTime( IN PFILESYSHANDLE pFileHandle, OUT PTIME pModTime ) { FILE_BASIC_INFORMATION FBasicInfo; NTSTATUS Status; IO_STATUS_BLOCK IoStsBlk; PFAST_IO_DISPATCH fastIoDispatch; PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() == LOW_LEVEL)); // Set all times/attr to Zero (this will cause NTFS to update LastModTime // if there are any writes pending) RtlZeroMemory(&FBasicInfo, sizeof(FBasicInfo)); Status = NtSetInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, (PVOID)&FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoChangeNTModTime: NtSetInformationFile failed with 0x%lx\n",Status)); AFPLOG_HERROR(AFPSRVMSG_CANT_SET_TIMESNATTR, Status, NULL, 0, pFileHandle->fsh_FileHandle); return AFP_ERR_MISC; } // now, go and query for the updated times Status = AfpIoQueryTimesnAttr( pFileHandle, NULL, pModTime, NULL ); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoChangeNTModTime: AfpIoQueryTimesnAttr returned 0x%lx\n",Status)); } return Status; } /*** AfpIoQueryTimesnAttr * * Get the times associated with the file. */ AFPSTATUS AfpIoQueryTimesnAttr( IN PFILESYSHANDLE pFileHandle, OUT PDWORD pCreatTime OPTIONAL, OUT PTIME pModTime OPTIONAL, OUT PDWORD pAttr OPTIONAL ) { FILE_BASIC_INFORMATION FBasicInfo; NTSTATUS Status; IO_STATUS_BLOCK IoStsBlk; PFAST_IO_DISPATCH fastIoDispatch; #ifdef PROFILING TIME TimeS, TimeE, TimeD; AfpGetPerfCounter(&TimeS); #endif PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); // Atleast something should be queried. ASSERT((pCreatTime != NULL) || (pModTime != NULL) || (pAttr != NULL)); fastIoDispatch = pFileHandle->fsh_DeviceObject->DriverObject->FastIoDispatch; if (fastIoDispatch && fastIoDispatch->FastIoQueryBasicInfo && fastIoDispatch->FastIoQueryBasicInfo(AfpGetRealFileObject(pFileHandle->fsh_FileObject), True, &FBasicInfo, &IoStsBlk, pFileHandle->fsh_DeviceObject)) { Status = IoStsBlk.Status; #ifdef PROFILING // The fast I/O path worked. Update statistics INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded)); #endif } else { #ifdef PROFILING INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed)); #endif Status = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, &FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); } if (NT_SUCCESS((NTSTATUS)Status)) { if (pModTime != NULL) *pModTime = FBasicInfo.LastWriteTime; if (pCreatTime != NULL) *pCreatTime = AfpConvertTimeToMacFormat(&FBasicInfo.CreationTime); if (pAttr != NULL) *pAttr = FBasicInfo.FileAttributes; #ifdef PROFILING AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_GetInfoCount); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_GetInfoTime, TimeD, &AfpStatisticsLock); #endif } else { AFPLOG_HERROR(AFPSRVMSG_CANT_GET_TIMESNATTR, Status, NULL, 0, pFileHandle->fsh_FileHandle); Status = AFP_ERR_MISC; // What else can we do } return Status; } /*** AfpIoSetTimesnAttr * * Set the times and attributes associated with the file. */ AFPSTATUS AfpIoSetTimesnAttr( IN PFILESYSHANDLE pFileHandle, IN AFPTIME * pCreateTime OPTIONAL, IN AFPTIME * pModTime OPTIONAL, IN DWORD AttrSet, IN DWORD AttrClear, IN PVOLDESC pVolDesc OPTIONAL, // only if NotifyPath IN PUNICODE_STRING pNotifyPath OPTIONAL ) { NTSTATUS Status; FILE_BASIC_INFORMATION FBasicInfo; IO_STATUS_BLOCK IoStsBlk; PFAST_IO_DISPATCH fastIoDispatch; BOOLEAN Queue = False; #ifdef PROFILING TIME TimeS, TimeE, TimeD; #endif PAGED_CODE( ); #ifdef PROFILING AfpGetPerfCounter(&TimeS); #endif DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoSetTimesnAttr entered\n")); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); // At least something should be set ASSERT((pCreateTime != NULL) || (pModTime != NULL) || (AttrSet != 0) || (AttrClear != 0)); // First query the information fastIoDispatch = pFileHandle->fsh_DeviceObject->DriverObject->FastIoDispatch; if (fastIoDispatch && fastIoDispatch->FastIoQueryBasicInfo && fastIoDispatch->FastIoQueryBasicInfo(AfpGetRealFileObject(pFileHandle->fsh_FileObject), True, &FBasicInfo, &IoStsBlk, pFileHandle->fsh_DeviceObject)) { Status = IoStsBlk.Status; #ifdef PROFILING // The fast I/O path worked. Update statistics INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded)); #endif } else { #ifdef PROFILING INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed)); #endif Status = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, &FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); } DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoSetTimesnAttr: NtQueryInformationFile returned 0x%lx\n",Status)); if (!NT_SUCCESS((NTSTATUS)Status)) { AFPLOG_HERROR(AFPSRVMSG_CANT_GET_TIMESNATTR, Status, NULL, 0, pFileHandle->fsh_FileHandle); return AFP_ERR_MISC; // What else can we do } // Set all times to Zero. This will not change it. Then set the times that are to // change FBasicInfo.CreationTime = LIZero; FBasicInfo.ChangeTime = LIZero; FBasicInfo.LastWriteTime = LIZero; FBasicInfo.LastAccessTime = LIZero; if (pCreateTime != NULL) AfpConvertTimeFromMacFormat(*pCreateTime, &FBasicInfo.CreationTime); if (pModTime != NULL) { AfpConvertTimeFromMacFormat(*pModTime, &FBasicInfo.LastWriteTime); FBasicInfo.ChangeTime = FBasicInfo.LastWriteTime; } // NTFS is not returning FILE_ATTRIBUTE_NORMAL if it is a file, // therefore we may end up trying to set attributes of 0 when we // want to clear all attributes. 0 is taken to mean you do not want // to set any attributes, so it is ignored all together by NTFS. In // this case, just tack on the normal bit so that our request is not // ignored. if (!(FBasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { FBasicInfo.FileAttributes |= FILE_ATTRIBUTE_NORMAL; } FBasicInfo.FileAttributes |= AttrSet; FBasicInfo.FileAttributes &= ~AttrClear; // Do not queue our changes for exclusive volumes since no notifies are posted if (ARGUMENT_PRESENT(pNotifyPath) && !EXCLUSIVE_VOLUME(pVolDesc)) { ASSERT(VALID_VOLDESC(pVolDesc)); Queue = True; AfpQueueOurChange(pVolDesc, FILE_ACTION_MODIFIED, pNotifyPath, NULL); } Status = NtSetInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, (PVOID)&FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoSetTimesnAttr: NtSetInformationFile returned 0x%lx\n",Status)); if (!NT_SUCCESS(Status)) { if (Queue) { AfpDequeueOurChange(pVolDesc, FILE_ACTION_MODIFIED, pNotifyPath, NULL); } AFPLOG_HERROR(AFPSRVMSG_CANT_SET_TIMESNATTR, Status, NULL, 0, pFileHandle->fsh_FileHandle); return AFP_ERR_MISC; } else { #ifdef PROFILING AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SetInfoCount); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_SetInfoTime, TimeD, &AfpStatisticsLock); #endif } return AFP_ERR_NONE; } /*** AfpIoRestoreTimeStamp * * When we don't want to change the modification timestamp, we call this function * in 2 steps: first time, it queries the original Mod time; second time, it sets it */ AFPSTATUS AfpIoRestoreTimeStamp( IN PFILESYSHANDLE pFileHandle, IN OUT PTIME pOriginalModTime, IN DWORD dwFlag ) { NTSTATUS Status; DWORD NTAttr = 0; FILE_BASIC_INFORMATION FBasicInfo; IO_STATUS_BLOCK IoStsBlk; PFAST_IO_DISPATCH fastIoDispatch; // if we are asked to retrieve the original timestamp, do that and return if (dwFlag == AFP_RETRIEVE_MODTIME) { Status = AfpIoQueryTimesnAttr(pFileHandle, NULL, pOriginalModTime, &NTAttr); return(Status); } // // we've been asked to restore the timestamp: let's do that! // ASSERT(dwFlag == AFP_RESTORE_MODTIME); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); // this will cause file system to flush any timestamps RtlZeroMemory(&FBasicInfo, sizeof(FBasicInfo)); Status = NtSetInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, (PVOID)&FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoRestoreTimeStamp: NtSetInformationFile failed with 0x%lx\n",Status)); } // First query the information fastIoDispatch = pFileHandle->fsh_DeviceObject->DriverObject->FastIoDispatch; if (fastIoDispatch && fastIoDispatch->FastIoQueryBasicInfo && fastIoDispatch->FastIoQueryBasicInfo(AfpGetRealFileObject(pFileHandle->fsh_FileObject), True, &FBasicInfo, &IoStsBlk, pFileHandle->fsh_DeviceObject)) { Status = IoStsBlk.Status; } else { Status = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, &FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); } if (!NT_SUCCESS((NTSTATUS)Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoRestoreTimeStamp: NtQueryInformationFile returned 0x%lx\n",Status)); return AFP_ERR_MISC; // What else can we do } // // Set times to Zero for the ones we don't want to restore, so that those don't change // FBasicInfo.CreationTime = LIZero; FBasicInfo.LastAccessTime = LIZero; FBasicInfo.LastWriteTime = *pOriginalModTime; FBasicInfo.ChangeTime = *pOriginalModTime; // see AfpIoSetTimesnAttr() if (!(FBasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { FBasicInfo.FileAttributes |= FILE_ATTRIBUTE_NORMAL; } Status = NtSetInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, (PVOID)&FBasicInfo, sizeof(FBasicInfo), FileBasicInformation); if (!NT_SUCCESS(Status)) { return AFP_ERR_MISC; } return AFP_ERR_NONE; } /*** AfpIoQueryLongName * * Get the long name associated with a file. Caller makes sure that * the buffer is big enough to handle the long name. The only caller of this * should be the AfpFindEntryByName routine when looking up a name by * SHORTNAME. If it dosn't find it in the database by shortname (i.e. * shortname == longname), then it queries for the longname so it can look * in the database by longname (since all database entries are stored with * longname only). * The Admin Get/SetDirectoryInfo calls may also call this if they find a * ~ in a path component, then it will assume that it got a shortname. */ NTSTATUS AfpIoQueryLongName( IN PFILESYSHANDLE pFileHandle, IN PUNICODE_STRING pShortname, OUT PUNICODE_STRING pLongName ) { LONGLONG Infobuf[(sizeof(FILE_BOTH_DIR_INFORMATION) + MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR))/sizeof(LONGLONG) + 1]; NTSTATUS Status; IO_STATUS_BLOCK IoStsBlk; UNICODE_STRING uName; PFILE_BOTH_DIR_INFORMATION pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)Infobuf; PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); Status = NtQueryDirectoryFile(pFileHandle->fsh_FileHandle, NULL,NULL,NULL, &IoStsBlk, Infobuf, sizeof(Infobuf), FileBothDirectoryInformation, True, pShortname, False); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("NtQueryDirectoryFile returned 0x%lx\n",Status) ); // Do not errorlog if an error occurs (usually STATUS_NO_SUCH_FILE) because // this normally happens when someone is creating a file/dir by SHORTNAME // and it does not yet exist. This would not be an error. if (NT_SUCCESS(Status)) { uName.Length = uName.MaximumLength = (USHORT)pFBDInfo->FileNameLength; uName.Buffer = pFBDInfo->FileName; //if (pFBDInfo->FileNameLength/sizeof(WCHAR) > AFP_FILENAME_LEN) if ((RtlUnicodeStringToAnsiSize(&uName)-1) > AFP_FILENAME_LEN) { // NTFS name is longer than 31 chars, use the shortname uName.Length = uName.MaximumLength = (USHORT)pFBDInfo->ShortNameLength; uName.Buffer = pFBDInfo->ShortName; } else { uName.Length = uName.MaximumLength = (USHORT)pFBDInfo->FileNameLength; uName.Buffer = pFBDInfo->FileName; } AfpCopyUnicodeString(pLongName, &uName); ASSERT(pLongName->Length == uName.Length); } return Status; } /*** AfpIoQueryShortName * * Get the short name associated with a file. Caller makes sure that * the buffer is big enough to handle the short name. */ AFPSTATUS FASTCALL AfpIoQueryShortName( IN PFILESYSHANDLE pFileHandle, OUT PANSI_STRING pName ) { LONGLONG ShortNameBuf[(sizeof(FILE_NAME_INFORMATION) + AFP_SHORTNAME_LEN * sizeof(WCHAR))/sizeof(LONGLONG) + 1]; NTSTATUS Status; IO_STATUS_BLOCK IoStsBlk; UNICODE_STRING uName; PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); // Query for the alternate name Status = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, ShortNameBuf, sizeof(ShortNameBuf), FileAlternateNameInformation); if (!NT_SUCCESS(Status)) { AFPLOG_ERROR(AFPSRVMSG_CANT_GET_FILENAME, Status, NULL, 0, NULL); Status = AFP_ERR_MISC; // What else can we do } else { uName.Length = uName.MaximumLength = (USHORT)(((PFILE_NAME_INFORMATION)ShortNameBuf)->FileNameLength); uName.Buffer = ((PFILE_NAME_INFORMATION)ShortNameBuf)->FileName; if (!NT_SUCCESS(AfpConvertMungedUnicodeToAnsi(&uName, pName))) Status = AFP_ERR_MISC; // What else can we do } return Status; } /*** AfpIoQueryStreams * * Get the names of all the streams that a file has. Memory is allocated out * of non-paged pool to hold the stream names. These have to be freed by the * caller. */ PSTREAM_INFO FASTCALL AfpIoQueryStreams( IN PFILESYSHANDLE pFileHandle ) { PFILE_STREAM_INFORMATION pStreamBuf; PBYTE pBuffer; NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK IoStsBlk; DWORD BufferSize; LONGLONG Buffer[512/sizeof(LONGLONG) + 1]; PSTREAM_INFO pStreams = NULL; PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); pBuffer = (PBYTE)Buffer; BufferSize = sizeof(Buffer); do { if (Status != STATUS_SUCCESS) { if (pBuffer != (PBYTE)Buffer) AfpFreeMemory(pBuffer); BufferSize *= 2; if ((pBuffer = AfpAllocNonPagedMemory(BufferSize)) == NULL) { Status = STATUS_NO_MEMORY; break; } } // Query for the stream information Status = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, pBuffer, BufferSize, FileStreamInformation); } while ((Status != STATUS_SUCCESS) && ((Status == STATUS_BUFFER_OVERFLOW) || (Status == STATUS_MORE_ENTRIES))); if (NT_SUCCESS(Status)) do { USHORT i, NumStreams = 1; USHORT TotalBufferSize = 0; PBYTE NamePtr; // Make a pass thru the buffer and figure out the # of streams and then // allocate memory to hold the information pStreamBuf = (PFILE_STREAM_INFORMATION)pBuffer; if (IoStsBlk.Information != 0) { pStreamBuf = (PFILE_STREAM_INFORMATION)pBuffer; for (NumStreams = 1, TotalBufferSize = (USHORT)(pStreamBuf->StreamNameLength + sizeof(WCHAR)); NOTHING; NumStreams++) { if (pStreamBuf->NextEntryOffset == 0) break; pStreamBuf = (PFILE_STREAM_INFORMATION)((PBYTE)pStreamBuf + pStreamBuf->NextEntryOffset); TotalBufferSize += (USHORT)(pStreamBuf->StreamNameLength + sizeof(WCHAR)); } NumStreams ++; } // Now allocate space for the streams if ((pStreams = (PSTREAM_INFO)AfpAllocNonPagedMemory(TotalBufferSize + (NumStreams * sizeof(STREAM_INFO)))) == NULL) { Status = AFP_ERR_MISC; break; } // The end is marked by an empty string pStreams[NumStreams-1].si_StreamName.Buffer = NULL; pStreams[NumStreams-1].si_StreamName.Length = pStreams[NumStreams-1].si_StreamName.MaximumLength = 0; pStreams[NumStreams-1].si_StreamSize.QuadPart = 0; // Now initialize the array NamePtr = (PBYTE)pStreams + (NumStreams * sizeof(STREAM_INFO)); pStreamBuf = (PFILE_STREAM_INFORMATION)pBuffer; for (i = 0; NumStreams-1 != 0; i++) { PUNICODE_STRING pStream; pStream = &pStreams[i].si_StreamName; pStream->Buffer = (LPWSTR)NamePtr; pStream->Length = (USHORT)(pStreamBuf->StreamNameLength); pStream->MaximumLength = pStream->Length + sizeof(WCHAR); pStreams[i].si_StreamSize = pStreamBuf->StreamSize; RtlCopyMemory(NamePtr, pStreamBuf->StreamName, pStreamBuf->StreamNameLength); NamePtr += pStream->MaximumLength; if (pStreamBuf->NextEntryOffset == 0) break; pStreamBuf = (PFILE_STREAM_INFORMATION)((PBYTE)pStreamBuf + pStreamBuf->NextEntryOffset); } } while (False); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoQueryStreams: Failed %lx\n", Status)); // Free up any memory that has been allocated if (pStreams != NULL) AfpFreeMemory(pStreams); // We get the following error for non-NTFS volumes, if this case simply assume it to be // CDFS and return the data stream. if (Status == STATUS_INVALID_PARAMETER) { if ((pStreams = (PSTREAM_INFO)AfpAllocNonPagedMemory((2*sizeof(STREAM_INFO)) + DataStreamName.MaximumLength)) != NULL) { pStreams[0].si_StreamName.Buffer = (PWCHAR)((PBYTE)pStreams + 2*sizeof(STREAM_INFO)); pStreams[0].si_StreamName.Length = DataStreamName.Length; pStreams[0].si_StreamName.MaximumLength = DataStreamName.MaximumLength; RtlCopyMemory(pStreams[0].si_StreamName.Buffer, DataStreamName.Buffer, DataStreamName.MaximumLength); AfpIoQuerySize(pFileHandle, &pStreams[0].si_StreamSize); pStreams[1].si_StreamName.Length = pStreams[1].si_StreamName.MaximumLength = 0; pStreams[1].si_StreamName.Buffer = NULL; } } else { AFPLOG_HERROR(AFPSRVMSG_CANT_GET_STREAMS, Status, NULL, 0, pFileHandle->fsh_FileHandle); } } if ((pBuffer != NULL) && (pBuffer != (PBYTE)Buffer)) AfpFreeMemory(pBuffer); return pStreams; } /*** AfpIoMarkFileForDelete * * Mark an open file as deleted. Returns NTSTATUS, not AFPSTATUS. */ NTSTATUS AfpIoMarkFileForDelete( IN PFILESYSHANDLE pFileHandle, IN PVOLDESC pVolDesc OPTIONAL, // only if pNotifyPath IN PUNICODE_STRING pNotifyPath OPTIONAL, IN PUNICODE_STRING pNotifyParentPath OPTIONAL ) { NTSTATUS rc; IO_STATUS_BLOCK IoStsBlk; FILE_DISPOSITION_INFORMATION fdispinfo; #ifdef PROFILING TIME TimeS, TimeE, TimeD; AfpGetPerfCounter(&TimeS); #endif PAGED_CODE( ); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); fdispinfo.DeleteFile = True; rc = NtSetInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, &fdispinfo, sizeof(fdispinfo), FileDispositionInformation); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoMarkFileForDelete: NtSetInfoFile returned 0x%lx\n",rc) ); if (ARGUMENT_PRESENT(pNotifyPath) && !EXCLUSIVE_VOLUME(pVolDesc)) { ASSERT(VALID_VOLDESC(pVolDesc)); // Do not queue for exclusive volumes if (NT_SUCCESS(rc)) { AfpQueueOurChange(pVolDesc, FILE_ACTION_REMOVED, pNotifyPath, pNotifyParentPath); } } #ifdef PROFILING AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DeleteCount); INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_DeleteTime, TimeD, &AfpStatisticsLock); #endif return rc; } /*** AfpIoQueryDirectoryFile * * Enumerate a directory. * Note this must return NTSTATUS in order for the caller to know when to * stop enumerating. */ NTSTATUS AfpIoQueryDirectoryFile( IN PFILESYSHANDLE pFileHandle, OUT PVOID Enumbuf, // type depends on FileInfoClass IN ULONG Enumbuflen, IN ULONG FileInfoClass, IN BOOLEAN ReturnSingleEntry, IN BOOLEAN RestartScan, IN PUNICODE_STRING pString OPTIONAL ) { NTSTATUS rc; IO_STATUS_BLOCK IoStsBlk; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoQueryDirectoryFile entered\n")); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); rc = NtQueryDirectoryFile(pFileHandle->fsh_FileHandle, NULL, NULL, NULL, &IoStsBlk, Enumbuf, Enumbuflen, FileInfoClass, ReturnSingleEntry, pString, RestartScan); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("NtQueryDirectoryFile returned 0x%lx\n",rc) ); return rc; } /*** AfpIoQueryBasicInfo * * Query FILE_BASIC_INFO for a handle. */ NTSTATUS AfpIoQueryBasicInfo( IN PFILESYSHANDLE pFileHandle, OUT PVOID BasicInfobuf ) { NTSTATUS rc; IO_STATUS_BLOCK IoStsBlk; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoQueryBasicInfo entered\n")); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); rc = NtQueryInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, BasicInfobuf, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoQuerybasicInfo: NtQueryInformationFile returned 0x%lx\n",rc) ); return rc; } /*** AfpIoClose * * Close the File/Fork/Directory. */ AFPSTATUS FASTCALL AfpIoClose( IN PFILESYSHANDLE pFileHandle ) { NTSTATUS Status; BOOLEAN Internal; #ifdef PROFILING TIME TimeS, TimeE, TimeD; INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_CloseCount); AfpGetPerfCounter(&TimeS); #endif PAGED_CODE (); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoClose entered\n")); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); Internal = INTERNAL_HANDLE(pFileHandle); afpUpdateOpenFiles(Internal, False); ObDereferenceObject(AfpGetRealFileObject(pFileHandle->fsh_FileObject)); Status = NtClose(pFileHandle->fsh_FileHandle); pFileHandle->fsh_FileHandle = NULL; ASSERT(NT_SUCCESS(Status)); #ifdef PROFILING AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_CloseTime, TimeD, &AfpStatisticsLock); #endif return AFP_ERR_NONE; } /*** AfpIoQueryVolumeSize * * Get the volume size and free space. * * Called by Admin thread and Scavenger thread */ AFPSTATUS AfpIoQueryVolumeSize( IN PVOLDESC pVolDesc, OUT LARGE_INTEGER *pFreeBytes, OUT LARGE_INTEGER *pVolumeSize OPTIONAL ) { FILE_FS_SIZE_INFORMATION fssizeinfo; IO_STATUS_BLOCK IoStsBlk; NTSTATUS rc; LONG BytesPerAllocationUnit; LARGE_INTEGER FreeBytes, VolumeSize; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoQueryVolumeSize entered\n")); ASSERT(VALID_VOLDESC(pVolDesc) && VALID_FSH(&pVolDesc->vds_hRootDir) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); rc = NtQueryVolumeInformationFile(pVolDesc->vds_hRootDir.fsh_FileHandle, &IoStsBlk, (PVOID)&fssizeinfo, sizeof(fssizeinfo), FileFsSizeInformation); if (!NT_SUCCESS(rc)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoQueryVolumeSize: NtQueryVolInfoFile returned 0x%lx\n",rc)); return rc; } BytesPerAllocationUnit = (LONG)(fssizeinfo.BytesPerSector * fssizeinfo.SectorsPerAllocationUnit); if (ARGUMENT_PRESENT(pVolumeSize)) { VolumeSize = RtlExtendedIntegerMultiply(fssizeinfo.TotalAllocationUnits, BytesPerAllocationUnit); *pVolumeSize = VolumeSize; } FreeBytes = RtlExtendedIntegerMultiply(fssizeinfo.AvailableAllocationUnits, BytesPerAllocationUnit); *pFreeBytes = FreeBytes; DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoQueryVolumeSize: volume size=%lu, freebytes=%lu\n", VolumeSize.LowPart, FreeBytes.LowPart)); pVolDesc->vds_AllocationBlockSize = BytesPerAllocationUnit; return STATUS_SUCCESS; } /*** AfpIoMoveAndOrRename * * Calls NtSetInformationFile with name information in order to rename, move, * or move AND rename a file or directory. pNewName must be a node name. * The pfshNewDir parameter is required for a Move operation, and is * an open handle to the target parent directory of the item to be moved. * * Retain the last change/modified time in this case. */ AFPSTATUS AfpIoMoveAndOrRename( IN PFILESYSHANDLE pfshFile, IN PFILESYSHANDLE pfshNewParent OPTIONAL, // Supply for Move operation IN PUNICODE_STRING pNewName, IN PVOLDESC pVolDesc OPTIONAL, // only if NotifyPath IN PUNICODE_STRING pNotifyPath1 OPTIONAL, // REMOVE or RENAME action IN PUNICODE_STRING pNotifyParentPath1 OPTIONAL, IN PUNICODE_STRING pNotifyPath2 OPTIONAL, // ADDED action IN PUNICODE_STRING pNotifyParentPath2 OPTIONAL ) { NTSTATUS Status; IO_STATUS_BLOCK iosb; BOOLEAN Queue = False; PFILE_RENAME_INFORMATION pFRenameInfo; // this has to be at least as big as AfpExchangeName BYTE buffer[sizeof(FILE_RENAME_INFORMATION) + 42 * sizeof(WCHAR)]; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoMoveAndOrRename entered\n")); ASSERT(VALID_FSH(pfshFile) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); pFRenameInfo = (PFILE_RENAME_INFORMATION)buffer; pFRenameInfo->RootDirectory = NULL; if (ARGUMENT_PRESENT(pfshNewParent)) { // its a move operation ASSERT(VALID_FSH(pfshNewParent)); pFRenameInfo->RootDirectory = pfshNewParent->fsh_FileHandle; } pFRenameInfo->FileNameLength = pNewName->Length; RtlCopyMemory(pFRenameInfo->FileName, pNewName->Buffer, pNewName->Length); pFRenameInfo->ReplaceIfExists = False; // Do not queue for exclusive volumes if (ARGUMENT_PRESENT(pNotifyPath1) && !EXCLUSIVE_VOLUME(pVolDesc)) { ASSERT(VALID_VOLDESC(pVolDesc)); Queue = True; if (ARGUMENT_PRESENT(pNotifyPath2)) { // move operation ASSERT(ARGUMENT_PRESENT(pfshNewParent)); AfpQueueOurChange(pVolDesc, FILE_ACTION_REMOVED, pNotifyPath1, pNotifyParentPath1); AfpQueueOurChange(pVolDesc, FILE_ACTION_ADDED, pNotifyPath2, pNotifyParentPath2); } else { // rename operation ASSERT(!ARGUMENT_PRESENT(pfshNewParent)); AfpQueueOurChange(pVolDesc, FILE_ACTION_RENAMED_OLD_NAME, pNotifyPath1, pNotifyParentPath1); } } Status = NtSetInformationFile(pfshFile->fsh_FileHandle, &iosb, pFRenameInfo, sizeof(*pFRenameInfo) + pFRenameInfo->FileNameLength, FileRenameInformation); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoMoveAndOrRename: NtSetInfoFile returned 0x%lx\n",Status)); } if (Queue) { // Undo on failure if (!NT_SUCCESS(Status)) { if (ARGUMENT_PRESENT(pNotifyPath2)) { // move operation ASSERT(ARGUMENT_PRESENT(pfshNewParent)); AfpDequeueOurChange(pVolDesc, FILE_ACTION_REMOVED, pNotifyPath1, pNotifyParentPath1); AfpDequeueOurChange(pVolDesc, FILE_ACTION_ADDED, pNotifyPath2, pNotifyParentPath2); } else { // rename operation ASSERT(!ARGUMENT_PRESENT(pfshNewParent)); AfpDequeueOurChange(pVolDesc, FILE_ACTION_RENAMED_OLD_NAME, pNotifyPath1, pNotifyParentPath1); } } } if (!NT_SUCCESS(Status)) Status = AfpIoConvertNTStatusToAfpStatus(Status); return Status; } /*** AfpIoCopyFile1 * * Copy phSrcFile to phDstDir directory with the name of pNewName. Returns * the handles to the streams on the newly created file (open with DELETE access. * Caller must close all the handles after copying the data. */ AFPSTATUS AfpIoCopyFile1( IN PFILESYSHANDLE phSrcFile, IN PFILESYSHANDLE phDstDir, IN PUNICODE_STRING pNewName, IN PVOLDESC pVolDesc OPTIONAL, // only if pNotifyPath IN PUNICODE_STRING pNotifyPath OPTIONAL, IN PUNICODE_STRING pNotifyParentPath OPTIONAL, OUT PCOPY_FILE_INFO pCopyFileInfo ) { NTSTATUS Status = STATUS_SUCCESS; PUNICODE_STRING pStreamName; PSTREAM_INFO pStreams = NULL, pCurStream; DWORD CreateTime = 0, ModTime = 0; FILESYSHANDLE hDstFile; LONG NumStreams, i; IO_STATUS_BLOCK IoStsBlk; PAGED_CODE( ); ASSERT(VALID_FSH(phDstDir) && VALID_FSH(phSrcFile)); do { hDstFile.fsh_FileHandle = NULL; // Create (soft) the destination file Status = AfpIoCreate(phDstDir, AFP_STREAM_DATA, pNewName, FILEIO_ACCESS_WRITE | FILEIO_ACCESS_DELETE, FILEIO_DENY_NONE, FILEIO_OPEN_FILE, FILEIO_CREATE_SOFT, FILE_ATTRIBUTE_ARCHIVE, True, NULL, &hDstFile, NULL, pVolDesc, pNotifyPath, pNotifyParentPath); if (!NT_SUCCESS(Status)) { break; } // Get a list of all stream names of the source file if ((pStreams = AfpIoQueryStreams(phSrcFile)) != NULL) { for (pCurStream = pStreams, NumStreams = 0; pCurStream->si_StreamName.Buffer != NULL; pCurStream++, NumStreams ++) NOTHING; // Allocate an array of handles for storing stream handles as we create them if (((pCopyFileInfo->cfi_SrcStreamHandle = (PFILESYSHANDLE) AfpAllocNonPagedMemory(sizeof(FILESYSHANDLE)*NumStreams)) == NULL) || ((pCopyFileInfo->cfi_DstStreamHandle = (PFILESYSHANDLE) AfpAllocNonPagedMemory(sizeof(FILESYSHANDLE)*NumStreams)) == NULL)) { if (pCopyFileInfo->cfi_SrcStreamHandle != NULL) { AfpFreeMemory(pCopyFileInfo->cfi_SrcStreamHandle); pCopyFileInfo->cfi_SrcStreamHandle = NULL; } Status = STATUS_INSUFFICIENT_RESOURCES; break; } else { RtlZeroMemory(pCopyFileInfo->cfi_SrcStreamHandle, sizeof(FILESYSHANDLE)*NumStreams); RtlZeroMemory(pCopyFileInfo->cfi_DstStreamHandle, sizeof(FILESYSHANDLE)*NumStreams); pCopyFileInfo->cfi_SrcStreamHandle[0] = *phSrcFile; pCopyFileInfo->cfi_DstStreamHandle[0] = hDstFile; pCopyFileInfo->cfi_NumStreams = 1; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; break; } for (pCurStream = pStreams, i = 1; // Start index NT_SUCCESS(Status) && ((pStreamName = &pCurStream->si_StreamName)->Buffer != NULL); pCurStream++) { PFILESYSHANDLE phdst; // For each stream, create it on the destination, open it on src, // set the size and lock the range. We already have the data forks // open, ignore Afp_AfpInfo streams since we are going to re-create // it again soon. Also ignore streams of 0 size. if (IS_INFO_STREAM(pStreamName) || (pCurStream->si_StreamSize.QuadPart == 0)) { continue; } if (!IS_DATA_STREAM(pStreamName)) { Status = AfpIoOpen( phSrcFile, AFP_STREAM_DATA, FILEIO_OPEN_FILE, pStreamName, FILEIO_ACCESS_READ, FILEIO_DENY_READ | FILEIO_DENY_WRITE, True, &pCopyFileInfo->cfi_SrcStreamHandle[i]); if (!NT_SUCCESS(Status)) { break; } Status = AfpIoCreate(&hDstFile, AFP_STREAM_DATA, pStreamName, FILEIO_ACCESS_WRITE, FILEIO_DENY_READ | FILEIO_DENY_WRITE, FILEIO_OPEN_FILE, FILEIO_CREATE_SOFT, 0, True, NULL, &pCopyFileInfo->cfi_DstStreamHandle[i], NULL, NULL, NULL, NULL); if (!NT_SUCCESS(Status)) { break; } phdst = &pCopyFileInfo->cfi_DstStreamHandle[i]; pCopyFileInfo->cfi_NumStreams ++; i ++; // Onto the next stream } else // IS_DATA_STREAM(pStreamName) { phdst = &hDstFile; } // Set the size of the new stream and lock it down Status = AfpIoSetSize(phdst, pCurStream->si_StreamSize.LowPart); if (!NT_SUCCESS(Status)) { break; } NtLockFile(phdst, NULL, NULL, NULL, &IoStsBlk, &LIZero, &pCurStream->si_StreamSize, 0, True, True); } // We failed to create/open a stream if (!NT_SUCCESS(Status)) { // Delete the new file we just created. The handle is closed below. AfpIoMarkFileForDelete(&hDstFile, pVolDesc, pNotifyPath, pNotifyParentPath); // Close all the handles, Free the handle space. // DO NOT FREE THE SRC FILE HANDLE IN THE ERROR CASE. // The Destination has already been deleted above. for (i = 1; i < NumStreams; i++) { if (pCopyFileInfo->cfi_SrcStreamHandle[i].fsh_FileHandle != NULL) { AfpIoClose(&pCopyFileInfo->cfi_SrcStreamHandle[i]); } if (pCopyFileInfo->cfi_DstStreamHandle[i].fsh_FileHandle != NULL) { AfpIoClose(&pCopyFileInfo->cfi_DstStreamHandle[i]); } } if (pCopyFileInfo->cfi_SrcStreamHandle != NULL) AfpFreeMemory(pCopyFileInfo->cfi_SrcStreamHandle); if (pCopyFileInfo->cfi_DstStreamHandle) AfpFreeMemory(pCopyFileInfo->cfi_DstStreamHandle); RtlZeroMemory(pCopyFileInfo, sizeof(COPY_FILE_INFO)); } } while (False); if (pStreams != NULL) AfpFreeMemory(pStreams); if (!NT_SUCCESS(Status) && (hDstFile.fsh_FileHandle != NULL)) { AfpIoClose(&hDstFile); } if (!NT_SUCCESS(Status)) Status = AfpIoConvertNTStatusToAfpStatus(Status); return Status; } /*** AfpIoCopyFile2 * * Phase 2 of the copy file. See AfpIoCopyFile1( above. * The physical data is copied here. * The relevant streams have been already created and locked. * Destination file acquires the CreateTime and ModTime of the source file. */ AFPSTATUS AfpIoCopyFile2( IN PCOPY_FILE_INFO pCopyFileInfo, IN PVOLDESC pVolDesc OPTIONAL, // only if pNotifyPath IN PUNICODE_STRING pNotifyPath OPTIONAL, IN PUNICODE_STRING pNotifyParentPath OPTIONAL ) { NTSTATUS Status = STATUS_SUCCESS; PBYTE RWbuf; DWORD CreateTime = 0; TIME ModTime; LONG i; #define RWBUFSIZE 1500 // So we can use secondary buffer from IO Pool. PAGED_CODE( ); do { if ((RWbuf = AfpIOAllocBuffer(RWBUFSIZE)) == NULL) { Status = STATUS_NO_MEMORY; break; } for (i = 0; i < pCopyFileInfo->cfi_NumStreams; i++) { while (NT_SUCCESS(Status)) { LONG bytesRead; bytesRead = 0; // Read from src, write to dst Status = AfpIoRead(&pCopyFileInfo->cfi_SrcStreamHandle[i], NULL, RWBUFSIZE, &bytesRead, RWbuf); if (Status == AFP_ERR_EOF) { Status = STATUS_SUCCESS; break; } else if (NT_SUCCESS(Status)) { Status = AfpIoWrite(&pCopyFileInfo->cfi_DstStreamHandle[i], NULL, bytesRead, RWbuf); } } } if (!NT_SUCCESS(Status)) { // We failed to read/write a stream // Delete the new file we just created AfpIoMarkFileForDelete(&pCopyFileInfo->cfi_DstStreamHandle[0], pVolDesc, pNotifyPath, pNotifyParentPath); } } while (False); if (RWbuf != NULL) AfpIOFreeBuffer(RWbuf); if (!NT_SUCCESS(Status)) Status = AfpIoConvertNTStatusToAfpStatus(Status); return Status; } /*** AfpIoWait * * Wait on a single object. This is a wrapper over KeWaitForSingleObject. */ NTSTATUS FASTCALL AfpIoWait( IN PVOID pObject, IN PLARGE_INTEGER pTimeOut OPTIONAL ) { NTSTATUS Status; PAGED_CODE( ); Status = KeWaitForSingleObject( pObject, UserRequest, KernelMode, False, pTimeOut); if (!NT_SUCCESS(Status)) { AFPLOG_DDERROR(AFPSRVMSG_WAIT4SINGLE, Status, NULL, 0, NULL); } return Status; } /*** AfpUpgradeHandle * * Change a handles type from internal to client. */ VOID FASTCALL AfpUpgradeHandle( IN PFILESYSHANDLE pFileHandle ) { KIRQL OldIrql; UPGRADE_HANDLE(pFileHandle); ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql); AfpServerStatistics.stat_CurrentFilesOpen ++; AfpServerStatistics.stat_TotalFilesOpened ++; if (AfpServerStatistics.stat_CurrentFilesOpen > AfpServerStatistics.stat_MaxFilesOpened) AfpServerStatistics.stat_MaxFilesOpened = AfpServerStatistics.stat_CurrentFilesOpen; AfpServerStatistics.stat_CurrentInternalOpens --; RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql); } /*** afpUpdateOpenFiles * * Update statistics to indicate number of open files. */ LOCAL VOID FASTCALL afpUpdateOpenFiles( IN BOOLEAN Internal, // True for internal handles IN BOOLEAN Open // True for open, False for close ) { KIRQL OldIrql; ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql); if (Open) { if (!Internal) { AfpServerStatistics.stat_CurrentFilesOpen ++; AfpServerStatistics.stat_TotalFilesOpened ++; if (AfpServerStatistics.stat_CurrentFilesOpen > AfpServerStatistics.stat_MaxFilesOpened) AfpServerStatistics.stat_MaxFilesOpened = AfpServerStatistics.stat_CurrentFilesOpen; } else { AfpServerStatistics.stat_CurrentInternalOpens ++; AfpServerStatistics.stat_TotalInternalOpens ++; if (AfpServerStatistics.stat_CurrentInternalOpens > AfpServerStatistics.stat_MaxInternalOpens) AfpServerStatistics.stat_MaxInternalOpens = AfpServerStatistics.stat_CurrentInternalOpens; } } else { if (!Internal) AfpServerStatistics.stat_CurrentFilesOpen --; else AfpServerStatistics.stat_CurrentInternalOpens --; } RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql); } /*** AfpIoConvertNTStatusToAfpStatus * * Map NT Status code to the closest AFP equivalents. Currently it only handles * error codes from NtOpenFile and NtCreateFile. */ AFPSTATUS FASTCALL AfpIoConvertNTStatusToAfpStatus( IN NTSTATUS Status ) { AFPSTATUS RetCode; PAGED_CODE( ); ASSERT (!NT_SUCCESS(Status)); if ((Status >= AFP_ERR_PWD_NEEDS_CHANGE) && (Status <= AFP_ERR_ACCESS_DENIED)) { // Status is already in mac format DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoConvertNTStatusToAfpStatus: Status (%d) already in mac format!!\n", Status)); return Status; } switch (Status) { case STATUS_OBJECT_PATH_INVALID: case STATUS_OBJECT_NAME_INVALID: RetCode = AFP_ERR_PARAM; break; case STATUS_OBJECT_PATH_NOT_FOUND: case STATUS_OBJECT_NAME_NOT_FOUND: RetCode = AFP_ERR_OBJECT_NOT_FOUND; break; case STATUS_OBJECT_NAME_COLLISION: case STATUS_OBJECT_NAME_EXISTS: RetCode = AFP_ERR_OBJECT_EXISTS; break; case STATUS_ACCESS_DENIED: RetCode = AFP_ERR_ACCESS_DENIED; break; case STATUS_QUOTA_EXCEEDED: case STATUS_DISK_FULL: RetCode = AFP_ERR_DISK_FULL; break; case STATUS_DIRECTORY_NOT_EMPTY: RetCode = AFP_ERR_DIR_NOT_EMPTY; break; case STATUS_SHARING_VIOLATION: RetCode = AFP_ERR_DENY_CONFLICT; break; default: RetCode = AFP_ERR_MISC; break; } return RetCode; } /*** AfpQueryPath * * Given a file handle, get the full pathname of the file/dir. If the * name is longer than MaximumBuf, then forget it and return an error. * Caller must free the pPath.Buffer. */ NTSTATUS AfpQueryPath( IN HANDLE FileHandle, IN PUNICODE_STRING pPath, IN ULONG MaximumBuf ) { PFILE_NAME_INFORMATION pNameinfo; IO_STATUS_BLOCK iosb; NTSTATUS Status; PAGED_CODE( ); do { if ((pNameinfo = (PFILE_NAME_INFORMATION)AfpAllocNonPagedMemory(MaximumBuf)) == NULL) { Status = STATUS_NO_MEMORY; break; } Status = NtQueryInformationFile(FileHandle, &iosb, pNameinfo, MaximumBuf, FileNameInformation); if (!NT_SUCCESS(Status)) { AfpFreeMemory(pNameinfo); break; } pPath->Length = pPath->MaximumLength = (USHORT) pNameinfo->FileNameLength; // Shift the name to the front of the buffer RtlMoveMemory(pNameinfo, &pNameinfo->FileName[0], pNameinfo->FileNameLength); pPath->Buffer = (PWCHAR)pNameinfo; } while (False); return Status; } /*** AfpIoIsSupportedDevice * * AFP volumes can only be created on local disk or cdrom devices. * (i.e. not network, virtual, etc. devices */ BOOLEAN FASTCALL AfpIoIsSupportedDevice( IN PFILESYSHANDLE pFileHandle, OUT PDWORD pFlags ) { IO_STATUS_BLOCK IoStsBlk; FILE_FS_DEVICE_INFORMATION DevInfo; PFILE_FS_ATTRIBUTE_INFORMATION pFSAttrInfo; LONGLONG Buffer[(sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + AFP_FSNAME_BUFLEN)/sizeof(LONGLONG) + 1]; UNICODE_STRING uFsName; NTSTATUS Status; BOOLEAN RetCode = False; PAGED_CODE( ); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoIsSupportedDevice entered\n")); ASSERT(VALID_FSH(pFileHandle) && (KeGetCurrentIrql() < DISPATCH_LEVEL)); do { Status = NtQueryVolumeInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, (PVOID)&DevInfo, sizeof(DevInfo), FileFsDeviceInformation); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoIsSupportedDevice: NtQueryVolInfFile returned 0x%lx\n", Status)); if (!NT_SUCCESS(Status) || ((DevInfo.DeviceType != FILE_DEVICE_DISK) && (DevInfo.DeviceType != FILE_DEVICE_CD_ROM))) { break; } // need to check if this is NTFS, CDFS or unsupported FS pFSAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; Status = NtQueryVolumeInformationFile(pFileHandle->fsh_FileHandle, &IoStsBlk, (PVOID)pFSAttrInfo, sizeof(Buffer), FileFsAttributeInformation); DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_INFO, ("AfpIoIsSupportedDevice: NtQueryVolInfFile returned 0x%lx\n", Status)); if (!NT_SUCCESS(Status)) { break; } if (pFSAttrInfo->FileSystemAttributes & FILE_VOLUME_QUOTAS) { *pFlags |= VOLUME_DISKQUOTA_ENABLED; } // convert returned non-null terminated file system name to counted unicode AfpInitUnicodeStringWithNonNullTerm(&uFsName, (USHORT)pFSAttrInfo->FileSystemNameLength, pFSAttrInfo->FileSystemName); if (EQUAL_UNICODE_STRING(&afpNTFSName, &uFsName, True)) { // its an NTFS volume *pFlags |= VOLUME_NTFS; RetCode = True; } else if (EQUAL_UNICODE_STRING(&afpCDFSName, &uFsName, True)) { // its a CDFS volume *pFlags |= AFP_VOLUME_READONLY; RetCode = True; } else if (EQUAL_UNICODE_STRING(&afpAHFSName, &uFsName, True)) { // its a volume on CD with HFS support *pFlags |= (AFP_VOLUME_READONLY | VOLUME_CD_HFS); RetCode = True; } else { // an unsupported file system DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR, ("AfpIoIsSupportedDevice: unsupported file system: name=%Z, CDString=%Z\n", &uFsName, &afpCDFSName)); break; } } while (False); if (!NT_SUCCESS(Status)) { AFPLOG_HERROR(AFPSRVMSG_CANT_GET_FSNAME, Status, NULL, 0, pFileHandle->fsh_FileHandle); } return RetCode; }