|
|
/*
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 <afp.h>
#include <client.h>
#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; }
|