/*

Copyright (c) 1992  Microsoft Corporation

Module Name:

	afpinfo.c

Abstract:

	This module contains the routines for manipulating the afpinfo stream.

Author:

	Jameel Hyder (microsoft!jameelh)


Revision History:
	19 Jun 1992		Initial Version

Notes:	Tab stop: 4

--*/


#define	FILENUM	FILE_AFPINFO

#include <afp.h>
#include <fdparm.h>
#include <pathmap.h>
#include <afpinfo.h>
#include <afpadmin.h>

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpSetAfpInfo)
#pragma alloc_text( PAGE, AfpReadAfpInfo)
#pragma alloc_text( PAGE, AfpSetFinderInfoByExtension)
#pragma alloc_text( PAGE, AfpProDosInfoFromFinderInfo)
#pragma alloc_text( PAGE, AfpFinderInfoFromProDosInfo)
#pragma alloc_text( PAGE, AfpSlapOnAfpInfoStream)
#pragma alloc_text( PAGE, AfpCreateAfpInfoStream)
#pragma alloc_text( PAGE, AfpExamineAndClearROAttr)
#pragma alloc_text( PAGE, AfpQueryProDos)
#endif

/***	AfpSetAfpInfo
 *
 *	Sets the values specified by Bitmap in the AFP_AfpInfo stream of a file
 *	or directory.  If FinderInfo is specified without ProDosInfo, or
 *	vice-versa, the one not specified is deduced from the other and also set.
 *	If the file/dir is marked ReadOnly, we must clear the readonly bit in order
 *	to write to the Afp_AfpInfo stream, and then set the RO bit back again.
 *  If pVolDesc is specified, then also update the cached AfpInfo in the
 *  IdDb DFENTRY.
 *
  */
AFPSTATUS
AfpSetAfpInfo(
	IN	PFILESYSHANDLE	pfshData,				// handle to data stream of object
	IN	DWORD			Bitmap,
	IN	PFILEDIRPARM	pFDParms,
	IN	PVOLDESC		pVolDesc	OPTIONAL,	// if present, update cached afpinfo
	IN	PDFENTRY	*	ppDFE		OPTIONAL	// pVolDesc must also be specified
)
{
	NTSTATUS		Status;
	DWORD			crinfo, NTAttr = 0;
	AFPINFO			afpinfo;
	FILESYSHANDLE	fshAfpInfo;
	BOOLEAN			isdir, WriteBackROAttr = False, mapprodos = False;
	PDFENTRY		pDfEntry = NULL;

	PAGED_CODE( );

	fshAfpInfo.fsh_FileHandle = NULL;

	isdir = IsDir(pFDParms);

	if (ARGUMENT_PRESENT(pVolDesc))
	{
		ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock));
		pDfEntry = AfpFindDfEntryById(pVolDesc,
									  pFDParms->_fdp_AfpId,
									  isdir ? DFE_DIR : DFE_FILE);
		if (pDfEntry == NULL)
		{
			return AFP_ERR_OBJECT_NOT_FOUND;
		}
	}

	do
	{
		if (!NT_SUCCESS(Status = AfpCreateAfpInfo(pfshData, &fshAfpInfo, &crinfo)))
		{
			if (Status == STATUS_ACCESS_DENIED)
			{
				// We may have failed to open the AFP_Afpinfo stream because
				// the file/dir is marked ReadOnly.  Clear the ReadOnly bit
				// and try to open it again.
				Status = AfpExamineAndClearROAttr(pfshData,
												  &WriteBackROAttr,
												  NULL,
												  NULL);
				if (NT_SUCCESS(Status))
				{
					if (!NT_SUCCESS(Status = AfpCreateAfpInfo(pfshData, &fshAfpInfo, &crinfo)))
					{
						AfpPutBackROAttr(pfshData, WriteBackROAttr);
						Status = AfpIoConvertNTStatusToAfpStatus(Status);
						break;
					}
				}
				else
				{
					Status = AFP_ERR_MISC;
					break;
				}
			}
			else
			{
				Status = AfpIoConvertNTStatusToAfpStatus(Status);
				break;
			}
		}

		// If it was newly created or it existed but was corrupted, then initialize
		// it with default data.  Otherwise read in the current data
		if ((crinfo == FILE_CREATED) ||
			(!NT_SUCCESS(AfpReadAfpInfo(&fshAfpInfo, &afpinfo))))
		{
			UNICODE_STRING	UName;
			WCHAR			NameBuf[AFP_LONGNAME_LEN+1];

			if (crinfo != FILE_CREATED)
			{
				AFPLOG_HERROR(AFPSRVMSG_AFPINFO,
							  0,
							  NULL,
							  0,
							  pfshData->fsh_FileHandle);
			}

			if (!isdir)
			{
				AfpSetEmptyUnicodeString(&UName, sizeof(NameBuf), NameBuf);
				AfpConvertStringToMungedUnicode(&pFDParms->_fdp_LongName, &UName);
			}

			// All callers of this routine must have the FD_BITMAP_LONGNAME
			// bit forced in their bitmap to pathmap, so that in this case
			// where the afpinfo stream must be recreated for a *file*, we
			// will always have a valid _fdp_Longname set in FDParm and can
			// deduce the type/creator
			if (!NT_SUCCESS(AfpSlapOnAfpInfoStream(NULL,
												   NULL,
												   pfshData,
												   &fshAfpInfo,
												   pFDParms->_fdp_AfpId,
												   isdir,
												   isdir ? NULL : &UName,
												   &afpinfo)))
			{
				Status = AFP_ERR_MISC;
				break;
			}
			else if (pDfEntry != NULL)
				DFE_UPDATE_CACHED_AFPINFO(pDfEntry, &afpinfo);
		}

		if (Bitmap & FD_BITMAP_BACKUPTIME)
		{
			afpinfo.afpi_BackupTime = pFDParms->_fdp_BackupTime;
			if (pDfEntry != NULL)
				pDfEntry->dfe_BackupTime = afpinfo.afpi_BackupTime;
		}

		if (Bitmap & FD_BITMAP_FINDERINFO)
		{	// Only map new ProDOS info if there has been a change in the
			// type/creator, and FD_BITMAP_PRODOSINFO is not set (files only)
			if (!(Bitmap & FD_BITMAP_PRODOSINFO) &&
				!isdir &&
				((RtlCompareMemory(afpinfo.afpi_FinderInfo.fd_Type,
								   pFDParms->_fdp_FinderInfo.fd_Type,
								   AFP_TYPE_LEN) != AFP_TYPE_LEN) ||
				 (RtlCompareMemory(afpinfo.afpi_FinderInfo.fd_Creator,
								   pFDParms->_fdp_FinderInfo.fd_Creator,
								   AFP_CREATOR_LEN) != AFP_CREATOR_LEN)))
			{
				mapprodos = True;
			}

			afpinfo.afpi_FinderInfo = pFDParms->_fdp_FinderInfo;

			if (mapprodos)
			{
				AfpProDosInfoFromFinderInfo(&afpinfo.afpi_FinderInfo,
											&afpinfo.afpi_ProDosInfo);
			}

			if (pDfEntry != NULL)
				pDfEntry->dfe_FinderInfo = afpinfo.afpi_FinderInfo;
		}

		if (Bitmap & FD_BITMAP_PRODOSINFO)
		{
			if ((IsDir(pFDParms)) &&
				(pFDParms->_fdp_ProDosInfo.pd_FileType[0] != PRODOS_TYPE_DIR))
			{
				Status = AFP_ERR_ACCESS_DENIED;
				break;
			}

			afpinfo.afpi_ProDosInfo = pFDParms->_fdp_ProDosInfo;

			if (!(Bitmap & FD_BITMAP_FINDERINFO) && !isdir)
			{
				AfpFinderInfoFromProDosInfo(&afpinfo.afpi_ProDosInfo,
											&afpinfo.afpi_FinderInfo);
				if (pDfEntry != NULL)
					pDfEntry->dfe_FinderInfo = afpinfo.afpi_FinderInfo;
			}
		}

		if (Bitmap & FD_BITMAP_ATTR)
		{
			afpinfo.afpi_Attributes =
							pFDParms->_fdp_EffectiveAttr & ~FD_BITMAP_ATTR_SET;
			if (pDfEntry != NULL)
				pDfEntry->dfe_AfpAttr = afpinfo.afpi_Attributes;
		}

		if (Bitmap & DIR_BITMAP_ACCESSRIGHTS)
		{
			ASSERT(isdir == True);
			afpinfo.afpi_AccessOwner = pFDParms->_fdp_OwnerRights;
			afpinfo.afpi_AccessGroup = pFDParms->_fdp_GroupRights;
			afpinfo.afpi_AccessWorld = pFDParms->_fdp_WorldRights;

			if (pDfEntry != NULL)
			{
				DFE_OWNER_ACCESS(pDfEntry) = afpinfo.afpi_AccessOwner;
				DFE_GROUP_ACCESS(pDfEntry) = afpinfo.afpi_AccessGroup;
				DFE_WORLD_ACCESS(pDfEntry) = afpinfo.afpi_AccessWorld;
			}
		}

		// FILE_BITMAP_FILENUM can ONLY be set by the internal CopyFile code
		// and internal ExchangeFiles code
		if (Bitmap & FILE_BITMAP_FILENUM)
		{
			ASSERT(isdir == False);
			afpinfo.afpi_Id = pFDParms->_fdp_AfpId;
		}

		Status = AfpWriteAfpInfo(&fshAfpInfo, &afpinfo);
		if (!NT_SUCCESS(Status))
			Status = AfpIoConvertNTStatusToAfpStatus(Status);
	} while (False);

	AfpPutBackROAttr(pfshData, WriteBackROAttr);
	if (fshAfpInfo.fsh_FileHandle != NULL)
		AfpIoClose(&fshAfpInfo);

	if (ARGUMENT_PRESENT(ppDFE))
	{
		ASSERT(ARGUMENT_PRESENT(pVolDesc));
		*ppDFE = pDfEntry;
	}

	return Status;
}

/***	AfpReadAfpInfo
 *
 *	When discovering a file/dir that has the AfpInfo stream, read it in
 *
 */
NTSTATUS FASTCALL
AfpReadAfpInfo(
	IN	PFILESYSHANDLE	pfshAfpInfo,
	OUT PAFPINFO		pAfpInfo
)
{
	NTSTATUS	Status;
	LONG		sizeRead;

	PAGED_CODE( );

	Status = AfpIoRead(pfshAfpInfo,
					   &LIZero,
					   sizeof(AFPINFO),
					   &sizeRead,
					   (PBYTE)pAfpInfo);

	if (!NT_SUCCESS(Status)									||
		(sizeRead != sizeof(AFPINFO))						||
		(pAfpInfo->afpi_Signature != AFP_SERVER_SIGNATURE)	||
		(pAfpInfo->afpi_Version != AFP_SERVER_VERSION))
	{
		if (NT_SUCCESS(Status) &&
			(sizeRead != 0)	   &&
			((pAfpInfo->afpi_Signature != AFP_SERVER_SIGNATURE)	||
			 (pAfpInfo->afpi_Version != AFP_SERVER_VERSION)))
		{
			AFPLOG_HERROR(AFPSRVMSG_AFPINFO,
						  Status,
						  NULL,
						  0,
						  pfshAfpInfo->fsh_FileHandle);
		}

		if ((sizeRead != sizeof(AFPINFO)) && (sizeRead != 0))
		{
			DBGPRINT(DBG_COMP_AFPINFO, DBG_LEVEL_ERR,
					 ("AfpReadAfpInfo: sizeRead (%d) != sizeof AFPINFO (%d)",
					 sizeRead, sizeof(AFPINFO)));
		}
		AfpIoSetSize(pfshAfpInfo, 0);
		Status = STATUS_UNSUCCESSFUL;
	}

	return Status;
}

/***	AfpReadCdHfsAfpInfo
 *
 *	This is AfpReadAfpInfo for the CD-HFS
 *	When discovering a file/dir that has the AfpInfo stream, read it in
 *
 */
NTSTATUS FASTCALL
AfpReadCdHfsAfpInfo(
	IN  PFILESYSHANDLE pfshData,
	OUT PHFS_INFORMATION_BUFFER pFinderInfo
)
{
	NTSTATUS		Status;
	IO_STATUS_BLOCK 	IoStsBlk;


	Status = NtFsControlFile( pfshData->fsh_FileHandle,
				  NULL,
				  NULL,
				  NULL,
				  &IoStsBlk,
				  FSCTL_GET_HFS_INFORMATION,
				  NULL,
				  0,
				  pFinderInfo,
				  sizeof(FINDERINFO)
				 );

    if (!NT_SUCCESS(Status))
    {
	    DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
	        ("AfpReadCdHfsAfpInfo failed with %lx\n", Status));
    }

	return Status;
}


/***	AfpSetFinderInfoByExtension
 *
 *	Set the finder info (type/creator) based on the file extension. Only long
 *	name is used for this mapping.
 *
 *	LOCKS: AfpEtcMapLock (SWMR, Shared)
 */
VOID FASTCALL
AfpSetFinderInfoByExtension(
	IN	PUNICODE_STRING	pFileName,
	OUT	PFINDERINFO		pFinderInfo
)
{
	PETCMAPINFO		pEtcMap = NULL;
	PWCHAR			pch;
	DWORD			len, i = AFP_EXTENSION_LEN;
	UCHAR			ext[AFP_EXTENSION_LEN+1];
	WCHAR			wext[AFP_EXTENSION_LEN+1];
	ANSI_STRING		aext;
	UNICODE_STRING	uext;

	PAGED_CODE( );

	ext[AFP_EXTENSION_LEN] = 0;

	ASSERT(pFileName != NULL);

	// Find the last character of the filename
	pch = pFileName->Buffer + (pFileName->Length - sizeof(WCHAR))/sizeof(WCHAR);
	len = pFileName->Length/sizeof(WCHAR);

	AfpSwmrAcquireShared(&AfpEtcMapLock);

	while ((AFP_EXTENSION_LEN - i) < len)
	{
		if (*pch == L'.')
		{
			if (i < AFP_EXTENSION_LEN)
			{
				AfpSetEmptyAnsiString(&aext, sizeof(ext), ext);
				AfpInitUnicodeStringWithNonNullTerm(&uext,
													(USHORT)((AFP_EXTENSION_LEN - i)*sizeof(WCHAR)),
													&wext[i]);
				AfpConvertMungedUnicodeToAnsi(&uext, &aext);
				pEtcMap = AfpLookupEtcMapEntry(ext);
			}
			break;
		}
		if (i == 0)
			break;
		wext[--i] = *(pch--);
	}

	if (pEtcMap == NULL)
		pEtcMap = &AfpDefaultEtcMap;

	RtlCopyMemory(&pFinderInfo->fd_Type, &pEtcMap->etc_type, AFP_TYPE_LEN);
	RtlCopyMemory(&pFinderInfo->fd_Creator, &pEtcMap->etc_creator, AFP_CREATOR_LEN);
	AfpSwmrRelease(&AfpEtcMapLock);
}

/***	AfpProDosInfoFromFinderInfo
 *
 *	Given finder info, deduce the corresponding prodos info. It is up to the
 *	caller to decide whether or not FinderInfo type/creator is actually
 *	changing (if client is just resetting the same values or not), in which
 *	case the prodos info should be left untouched. (Inside Appletalk p. 13-19)
 *	NOTE: see layout of ProDOS info on p. 13-18 of Inside Appletalk, 2nd Ed.)
 */
VOID FASTCALL
AfpProDosInfoFromFinderInfo(
	IN	PFINDERINFO	pFinderInfo,
	OUT PPRODOSINFO pProDosInfo
)
{
	CHAR		buf[3];
	ULONG		filetype;
	NTSTATUS	Status;

	PAGED_CODE( );

	RtlZeroMemory(pProDosInfo, sizeof(PRODOSINFO));
	if (RtlCompareMemory(pFinderInfo->fd_Type, "TEXT", AFP_TYPE_LEN) == AFP_TYPE_LEN)
	{
		pProDosInfo->pd_FileType[0] = PRODOS_TYPE_FILE;
	}
	else if (RtlCompareMemory(pFinderInfo->fd_Creator,
							  "pdos",
							  AFP_CREATOR_LEN) == AFP_CREATOR_LEN)
	{
		if (RtlCompareMemory(pFinderInfo->fd_Type,
							 "PSYS",
							 AFP_TYPE_LEN) == AFP_TYPE_LEN)
		{
			pProDosInfo->pd_FileType[0] = PRODOS_FILETYPE_PSYS;
		}
		else if (RtlCompareMemory(pFinderInfo->fd_Type,
								  "PS16",
								  AFP_TYPE_LEN) == AFP_TYPE_LEN)
		{
			pProDosInfo->pd_FileType[0] = PRODOS_FILETYPE_PS16;
		}
		else if (pFinderInfo->fd_Type[0] == 'p')
		{
			pProDosInfo->pd_FileType[0] = pFinderInfo->fd_Type[1];
			pProDosInfo->pd_AuxType[0] = pFinderInfo->fd_Type[3];
			pProDosInfo->pd_AuxType[1] = pFinderInfo->fd_Type[2];
		}
		else if ((pFinderInfo->fd_Type[2] == ' ') &&
				 (pFinderInfo->fd_Type[3] == ' ') &&
				 (isxdigit(pFinderInfo->fd_Type[0])) &&
				 (isxdigit(pFinderInfo->fd_Type[1])))
		{
			buf[0] = pFinderInfo->fd_Type[0];
			buf[1] = pFinderInfo->fd_Type[1];
			buf[2] = 0;
			Status = RtlCharToInteger(buf, 16, &filetype);
			ASSERT(NT_SUCCESS(Status));
			pProDosInfo->pd_FileType[0] = (BYTE)filetype;
		}
	}
}

/***	AfpFinderInfoFromProDosInfo
 *
 *	Given the prodos info, deduce the corresponding finder info.
 */
VOID FASTCALL
AfpFinderInfoFromProDosInfo(
	IN	PPRODOSINFO	pProDosInfo,
	OUT PFINDERINFO	pFinderInfo
)
{
	PAGED_CODE( );

	RtlCopyMemory(pFinderInfo->fd_Creator,"pdos",AFP_CREATOR_LEN);
	if ((pProDosInfo->pd_FileType[0] == PRODOS_TYPE_FILE) &&
		(pProDosInfo->pd_AuxType[0] == 0) &&
		(pProDosInfo->pd_AuxType[1] == 0))
	{
		RtlCopyMemory(&pFinderInfo->fd_Type,"TEXT",AFP_TYPE_LEN);
	}
	else if (pProDosInfo->pd_FileType[0] == PRODOS_FILETYPE_PSYS)
	{
		RtlCopyMemory(&pFinderInfo->fd_Type,"PSYS",AFP_TYPE_LEN);
	}
	else if (pProDosInfo->pd_FileType[0] == PRODOS_FILETYPE_PS16)
	{
		RtlCopyMemory(&pFinderInfo->fd_Type,"PS16",AFP_TYPE_LEN);
	}
	else if (pProDosInfo->pd_FileType[0] == 0)
	{
		RtlCopyMemory(&pFinderInfo->fd_Type,"BINA",AFP_TYPE_LEN);
	}
	else
	{
		pFinderInfo->fd_Type[0] = 'p';
		pFinderInfo->fd_Type[1] = pProDosInfo->pd_FileType[0];
		pFinderInfo->fd_Type[2] = pProDosInfo->pd_AuxType[1];
		pFinderInfo->fd_Type[3] = pProDosInfo->pd_AuxType[0];
	}
}

/***	AfpSlapOnAfpInfoStream
 *
 *	When creating a file or directory, this is called to add the AFP_AfpInfo
 *	stream.  No client impersonation is done to open/read/write this stream.
 *	If pfshAfpInfoStream is supplied, that handle is used, else a handle is
 *	opened (and pfshData MUST be supplied);
 */
NTSTATUS
AfpSlapOnAfpInfoStream(
	IN	PVOLDESC	   	pVolDesc			OPTIONAL,	// only if catching
	IN	PUNICODE_STRING	pNotifyPath			OPTIONAL,	// changes to size of
	                                                    // Afpinfo stream
	IN	PFILESYSHANDLE	pfshData			OPTIONAL,
	IN	PFILESYSHANDLE	pfshAfpInfoStream	OPTIONAL,
	IN	DWORD			AfpId,
	IN	BOOLEAN			IsDirectory,
	IN	PUNICODE_STRING	pName				OPTIONAL,	// needed for files
	OUT PAFPINFO		pAfpInfo
)
{
	NTSTATUS		Status;
	FILESYSHANDLE	fshAfpInfo;
	BOOLEAN			WriteBackROAttr = False;

	PAGED_CODE( );

	ASSERT((pfshData != NULL) || (pfshAfpInfoStream != NULL));

	if (!ARGUMENT_PRESENT(pfshAfpInfoStream))
	{
		if (!NT_SUCCESS(Status = AfpCreateAfpInfo(pfshData, &fshAfpInfo, NULL)))
		{
			if (Status == STATUS_ACCESS_DENIED)
			{
				// We may have failed to open the AFP_Afpinfo stream because
				// the file/dir is marked ReadOnly.  Clear the ReadOnly bit
				// and try to open it again.
				Status = AfpExamineAndClearROAttr(pfshData,
												  &WriteBackROAttr,
												  pVolDesc,
												  pNotifyPath);
				if (NT_SUCCESS(Status))
				{
					if (!NT_SUCCESS(Status = AfpCreateAfpInfo(pfshData,
															  &fshAfpInfo,
															  NULL)))
					{
						AfpPutBackROAttr(pfshData, WriteBackROAttr);
					}
				}
			}
			if (!NT_SUCCESS(Status))
				return Status;
		}

	}
	else fshAfpInfo = *pfshAfpInfoStream;

	AfpInitAfpInfo(pAfpInfo, AfpId, IsDirectory, BEGINNING_OF_TIME);
	if (!IsDirectory)
	{
		ASSERT(pName != NULL);
		AfpSetFinderInfoByExtension(pName,
									&pAfpInfo->afpi_FinderInfo);
		AfpProDosInfoFromFinderInfo(&pAfpInfo->afpi_FinderInfo,
									&pAfpInfo->afpi_ProDosInfo);
	}

	AfpIoSetSize(&fshAfpInfo, 0);
	Status = AfpWriteAfpInfo(&fshAfpInfo, pAfpInfo);
	if (NT_SUCCESS(Status) &&
		ARGUMENT_PRESENT(pVolDesc) &&
		ARGUMENT_PRESENT(pNotifyPath))
	{
		// Do both FILE_ACTION_MODIFIED_STREAM and FILE_ACTION_MODIFIED in one go
		AfpQueueOurChange(pVolDesc,
				          FILE_ACTION_MODIFIED_STREAM,
						  pNotifyPath,
						  pNotifyPath);
	}

	if (!ARGUMENT_PRESENT(pfshAfpInfoStream))
	{
		AfpIoClose(&fshAfpInfo);
		AfpPutBackROAttr(pfshData, WriteBackROAttr);
	}

	return Status;
}


/***	AfpCreateAfpInfoStream
 *
 *	Similar to AfpSlapOnAfpInfoStream but tuned to Create file/directory case.
 */
NTSTATUS
AfpCreateAfpInfoStream(
	IN  PVOLDESC		pVolDesc,
	IN	PFILESYSHANDLE	pfshData,
	IN	DWORD			AfpId,
	IN	BOOLEAN			IsDirectory,
	IN	PUNICODE_STRING	pName			OPTIONAL,	// only needed for files
	IN	PUNICODE_STRING	pNotifyPath,
	OUT PAFPINFO		pAfpInfo,
	OUT	PFILESYSHANDLE	pfshAfpInfo
)
{
	NTSTATUS		Status;
	BOOLEAN			WriteBackROAttr = False;
	DWORD			crinfo;

	PAGED_CODE( );

	ASSERT((pfshData != NULL) && (pfshAfpInfo != NULL));

	do
	{
		if (!NT_SUCCESS(Status = AfpCreateAfpInfo(pfshData, pfshAfpInfo, &crinfo)))
		{
			if (Status == STATUS_ACCESS_DENIED)
			{
				// We may have failed to open the AFP_Afpinfo stream because
				// the file/dir is marked ReadOnly.  Clear the ReadOnly bit
				// and try to open it again.
				Status = AfpExamineAndClearROAttr(pfshData,
												  &WriteBackROAttr,
												  pVolDesc,
												  pNotifyPath);
				if (NT_SUCCESS(Status))
				{
					if (!NT_SUCCESS(Status = AfpCreateAfpInfo(pfshData,
															  pfshAfpInfo,
															  &crinfo)))
					{
						AfpPutBackROAttr(pfshData, WriteBackROAttr);
					}
				}
			}
			if (!NT_SUCCESS(Status))
				break;
		}

		AfpInitAfpInfo(pAfpInfo, AfpId, IsDirectory, BEGINNING_OF_TIME);
		if (!IsDirectory)
		{
			ASSERT(pName != NULL);
			AfpSetFinderInfoByExtension(pName,
										&pAfpInfo->afpi_FinderInfo);
			AfpProDosInfoFromFinderInfo(&pAfpInfo->afpi_FinderInfo,
										&pAfpInfo->afpi_ProDosInfo);
		}

		Status = AfpWriteAfpInfo(pfshAfpInfo, pAfpInfo);
		if (NT_SUCCESS(Status) && (crinfo == FILE_CREATED))
		{
			// Do both FILE_ACTION_MODIFIED_STREAM and FILE_ACTION_MODIFIED in one go
			AfpQueueOurChange(pVolDesc,
					          FILE_ACTION_MODIFIED_STREAM,
							  pNotifyPath,
							  pNotifyPath);
		}
		AfpPutBackROAttr(pfshData, WriteBackROAttr);
	} while (False);

	return Status;
}


/***	AfpExamineAndClearROAttr
 *
 *	If the ReadOnly attribute is set on a file or directory, clear it.
 *	pWriteBackROAttr is a boolean indicating whether or not the caller must
 *	subsequently reset the Readonly bit on the file/dir. (see AfpPutBackROAttr)
 */
NTSTATUS FASTCALL
AfpExamineAndClearROAttr(
	IN	PFILESYSHANDLE	pfshData,
	OUT	PBOOLEAN		pWriteBackROAttr,
	IN	PVOLDESC		pVolDesc		OPTIONAL,
	IN	PUNICODE_STRING	pPath			OPTIONAL
)
{
	NTSTATUS	Status;
	DWORD		NTAttr = 0;

	PAGED_CODE( );

	ASSERT(VALID_FSH(pfshData));

	*pWriteBackROAttr = False;
	if (NT_SUCCESS(Status = AfpIoQueryTimesnAttr(pfshData, NULL, NULL, &NTAttr)) &&
		(NTAttr & FILE_ATTRIBUTE_READONLY))
	{
		// We need to clear the readonly bit.
		if (NT_SUCCESS(Status = AfpIoSetTimesnAttr(pfshData,
												   NULL,
												   NULL,
												   0,
												   FILE_ATTRIBUTE_READONLY,
												   pVolDesc,
												   pPath)))
		{
			*pWriteBackROAttr = True;
		}
	}
	return Status;
}

/***	AfpQueryProDos
 *
 *	Open the afpinfo stream relative to the file's Data handle, and
 *  read the ProDOS info out of it.  If the AfpInfo stream does not
 *  exist, return an error.
 *
 */
AFPSTATUS FASTCALL
AfpQueryProDos(
	IN	PFILESYSHANDLE	pfshData,
	OUT	PPRODOSINFO		pProDosInfo
)
{
	AFPSTATUS		Status = AFP_ERR_NONE;
	FILESYSHANDLE	hAfpInfo;
	AFPINFO			afpinfo;

	Status = AfpIoOpen(pfshData,
					   AFP_STREAM_INFO,
					   FILEIO_OPEN_FILE,
					   &UNullString,
					   FILEIO_ACCESS_READ,
					   FILEIO_DENY_NONE,
					   False,
					   &hAfpInfo);
    if (NT_SUCCESS(Status))
	{
		if (NT_SUCCESS(AfpReadAfpInfo(&hAfpInfo, &afpinfo)))
		{
			*pProDosInfo = afpinfo.afpi_ProDosInfo;
		}
		else
		{
			Status = AFP_ERR_MISC;
		}

		AfpIoClose(&hAfpInfo);
	}
	else
		Status = AfpIoConvertNTStatusToAfpStatus(Status);

	return Status;
}


/***	AfpUpdateIdInAfpInfo
 *
 *	Update the afpid in the afpinfo stream.
 *
 */
AFPSTATUS FASTCALL
AfpUpdateIdInAfpInfo(
	IN	PVOLDESC		pVolDesc,
	IN	PDFENTRY		pDfEntry
)
{
	FILESYSHANDLE	fshAfpInfo;
	AFPINFO			AfpInfo;
	AFPSTATUS		Status;
	UNICODE_STRING	Path;

	AfpSetEmptyUnicodeString(&Path, 0, NULL);
    Status = AfpHostPathFromDFEntry(pDfEntry, 0, &Path);
	if (NT_SUCCESS(Status))
	{
		// Open the afpinfo stream
		Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
						   AFP_STREAM_INFO,
						   FILEIO_OPEN_FILE,
						   &Path,
						   FILEIO_ACCESS_READWRITE,
						   FILEIO_DENY_NONE,
						   False,
						   &fshAfpInfo);
		if (NT_SUCCESS(Status))
		{
			Status = AfpReadAfpInfo(&fshAfpInfo, &AfpInfo);
			if (NT_SUCCESS(Status))
			{
				AfpInfo.afpi_Id = pDfEntry->dfe_AfpId;
				AfpWriteAfpInfo(&fshAfpInfo, &AfpInfo);
			}
			AfpIoClose(&fshAfpInfo);
		}
	
		AfpFreeMemory(Path.Buffer);
	}

	return Status;
}