Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2494 lines
62 KiB

/*
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;
}