1676 lines
41 KiB
C
1676 lines
41 KiB
C
/*
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
desktop.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routines for manipulating the desktop database.
|
||
|
||
Author:
|
||
|
||
Jameel Hyder (microsoft!jameelh)
|
||
|
||
|
||
Revision History:
|
||
25 Apr 1992 Initial Version
|
||
|
||
Notes: Tab stop: 4
|
||
--*/
|
||
|
||
#define FILENUM FILE_DESKTOP
|
||
#define DESKTOP_LOCALS
|
||
|
||
#include <afp.h>
|
||
#include <scavengr.h>
|
||
#include <fdparm.h>
|
||
#include <pathmap.h>
|
||
#include <client.h>
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( INIT, AfpDesktopInit)
|
||
#pragma alloc_text( PAGE, AfpAddIcon)
|
||
#pragma alloc_text( PAGE, AfpLookupIcon)
|
||
#pragma alloc_text( PAGE, AfpLookupIconInfo)
|
||
#pragma alloc_text( PAGE, AfpAddAppl)
|
||
#pragma alloc_text( PAGE, AfpLookupAppl)
|
||
#pragma alloc_text( PAGE, AfpRemoveAppl)
|
||
#pragma alloc_text( PAGE, AfpAddComment)
|
||
#pragma alloc_text( PAGE, AfpGetComment)
|
||
#pragma alloc_text( PAGE, AfpRemoveComment)
|
||
#pragma alloc_text( PAGE, AfpAddIconToGlobalList)
|
||
#pragma alloc_text( PAGE, afpLookupIconInGlobalList)
|
||
#pragma alloc_text( PAGE, AfpFreeGlobalIconList)
|
||
#pragma alloc_text( PAGE, afpGetGlobalIconInfo)
|
||
#pragma alloc_text( PAGE, afpReadDesktopFromDisk)
|
||
#pragma alloc_text( PAGE, AfpInitDesktop)
|
||
#pragma alloc_text( PAGE, AfpUpdateDesktop)
|
||
#pragma alloc_text( PAGE, AfpFreeDesktopTables)
|
||
#endif
|
||
|
||
/*** AfpDesktopInit
|
||
*
|
||
* Initialize locks for global icons.
|
||
*/
|
||
NTSTATUS
|
||
AfpDesktopInit(
|
||
VOID
|
||
)
|
||
{
|
||
AfpSwmrInitSwmr(&AfpIconListLock);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
/*** AfpAddIcon
|
||
*
|
||
* Add an icon to the desktop database. The icon is added in such a way that
|
||
* the list is maintained in a sorted fashion - sorted by Creator, Type and
|
||
* IconType
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Exclusive);
|
||
*/
|
||
AFPSTATUS
|
||
AfpAddIcon(
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN DWORD Creator,
|
||
IN DWORD Type,
|
||
IN DWORD Tag,
|
||
IN LONG IconSize,
|
||
IN DWORD IconType,
|
||
IN PBYTE pIcon // The icon bitmap
|
||
)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
PICONINFO * ppIconInfo;
|
||
BOOLEAN Found = False;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireExclusive(&pVolDesc->vds_DtAccessLock);
|
||
ppIconInfo = &pVolDesc->vds_pIconBuckets[HASH_ICON(Creator)];
|
||
do
|
||
{
|
||
// Find the right slot
|
||
for (;(pIconInfo = *ppIconInfo) != NULL;
|
||
ppIconInfo = &pIconInfo->icon_Next)
|
||
{
|
||
if (pIconInfo->icon_Creator < Creator)
|
||
continue;
|
||
if (pIconInfo->icon_Creator > Creator)
|
||
break;
|
||
if (pIconInfo->icon_Type < Type)
|
||
continue;
|
||
if (pIconInfo->icon_Type > Type)
|
||
break;
|
||
if (pIconInfo->icon_IconType < (USHORT)IconType)
|
||
continue;
|
||
if (pIconInfo->icon_IconType > (USHORT)IconType)
|
||
break;
|
||
/*
|
||
* If we come this far, we have hit the bulls eye
|
||
* Make sure the size matches, before we commit
|
||
*/
|
||
if (pIconInfo->icon_Size != IconSize)
|
||
{
|
||
Status = AFP_ERR_ICON_TYPE;
|
||
break;
|
||
}
|
||
Found = True;
|
||
break;
|
||
}
|
||
|
||
if (!Found && (Status == AFP_ERR_NONE))
|
||
{
|
||
// ppIconInfo now points to the right place
|
||
if ((pIconInfo = ALLOC_ICONINFO(IconSize)) != NULL)
|
||
{
|
||
pIconInfo->icon_Next = *ppIconInfo;
|
||
*ppIconInfo = pIconInfo;
|
||
pIconInfo->icon_Creator = Creator;
|
||
pIconInfo->icon_Type = Type;
|
||
pIconInfo->icon_IconType = (USHORT)IconType;
|
||
pIconInfo->icon_Size = (SHORT)IconSize;
|
||
pIconInfo->icon_Tag = Tag;
|
||
pVolDesc->vds_cIconEnts ++;
|
||
Found = True;
|
||
}
|
||
else Status = AFP_ERR_MISC;
|
||
}
|
||
if (Found && (Status == AFP_ERR_NONE))
|
||
{
|
||
RtlCopyMemory((PBYTE)pIconInfo + sizeof(ICONINFO), pIcon, IconSize);
|
||
}
|
||
} while (False);
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpLookupIcon
|
||
*
|
||
* Search the desktop for an icon matching the given search parameters.
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Shared), AfpIconListLock (SWMR, Shared)
|
||
*/
|
||
AFPSTATUS
|
||
AfpLookupIcon(
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN DWORD Creator,
|
||
IN DWORD Type,
|
||
IN LONG Length,
|
||
IN DWORD IconType,
|
||
OUT PLONG pActualLength,
|
||
OUT PBYTE pIconBitMap // Buffer for icon bit map
|
||
)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
LONG LengthToCopy;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
|
||
PAGED_CODE( );
|
||
|
||
LengthToCopy = Length;
|
||
|
||
AfpSwmrAcquireShared(&pVolDesc->vds_DtAccessLock);
|
||
pIconInfo = pVolDesc->vds_pIconBuckets[HASH_ICON(Creator)];
|
||
|
||
// Scan the list looking for the entry
|
||
for (;pIconInfo != NULL; pIconInfo = pIconInfo->icon_Next)
|
||
{
|
||
if (pIconInfo->icon_Creator < Creator)
|
||
continue;
|
||
if (pIconInfo->icon_Creator > Creator)
|
||
{
|
||
pIconInfo = NULL;
|
||
break;
|
||
}
|
||
if (pIconInfo->icon_Type < Type)
|
||
continue;
|
||
if (pIconInfo->icon_Type > Type)
|
||
{
|
||
pIconInfo = NULL;
|
||
break;
|
||
}
|
||
if (pIconInfo->icon_IconType < (USHORT)IconType)
|
||
continue;
|
||
if (pIconInfo->icon_IconType > (USHORT)IconType)
|
||
{
|
||
pIconInfo = NULL;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
// If we did not find it, try the global list
|
||
if (pIconInfo == NULL)
|
||
{
|
||
Status = afpLookupIconInGlobalList(Creator,
|
||
Type,
|
||
IconType,
|
||
&LengthToCopy,
|
||
pIconBitMap);
|
||
}
|
||
else if (Length > 0)
|
||
{
|
||
if ((LONG)(pIconInfo->icon_Size) < Length)
|
||
{
|
||
LengthToCopy = (LONG)(pIconInfo->icon_Size);
|
||
}
|
||
else
|
||
{
|
||
LengthToCopy = Length;
|
||
}
|
||
RtlCopyMemory(pIconBitMap, (PBYTE)pIconInfo + sizeof(ICONINFO), LengthToCopy);
|
||
}
|
||
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
|
||
*pActualLength = LengthToCopy;
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpLookupIconInfo
|
||
*
|
||
* Search the desktop for an icon matching the given Creator. In case of
|
||
* multiple icons corresponding to the same creator, get the nth where n
|
||
* is the index.
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Shared), AfpIconListLock (SWMR, Shared)
|
||
*/
|
||
AFPSTATUS
|
||
AfpLookupIconInfo(
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN DWORD Creator, // Creator associated with the icon
|
||
IN LONG Index, // Index number of Icon
|
||
OUT PDWORD pType, // Place where Type is returned
|
||
OUT PDWORD pIconType, // Icon type e.g. ICN#
|
||
OUT PDWORD pTag, // Arbitrary tag
|
||
OUT PLONG pSize // Size of the icon
|
||
)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
LONG i;
|
||
AFPSTATUS Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireShared(&pVolDesc->vds_DtAccessLock);
|
||
pIconInfo = pVolDesc->vds_pIconBuckets[HASH_ICON(Creator)];
|
||
|
||
// Scan the list looking for the first entry
|
||
for (;pIconInfo != NULL; pIconInfo = pIconInfo->icon_Next)
|
||
{
|
||
if (pIconInfo->icon_Creator == Creator)
|
||
break; // Found the first one
|
||
if (pIconInfo->icon_Creator > Creator)
|
||
{
|
||
pIconInfo = NULL;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* We are now either pointing to the first entry or there are none. In the
|
||
* latter case, we just fall through
|
||
*/
|
||
for (i = 1; pIconInfo != NULL; pIconInfo = pIconInfo->icon_Next)
|
||
{
|
||
if ((pIconInfo->icon_Creator > Creator) || (i > Index))
|
||
{
|
||
pIconInfo = NULL;
|
||
break;
|
||
}
|
||
|
||
if (i == Index)
|
||
break; // Found the right entry
|
||
i++;
|
||
}
|
||
|
||
// If we did find it, extract the information
|
||
if (pIconInfo != NULL)
|
||
{
|
||
*pSize = pIconInfo->icon_Size;
|
||
*pType = pIconInfo->icon_Type;
|
||
*pTag = pIconInfo->icon_Tag;
|
||
*pIconType = pIconInfo->icon_IconType;
|
||
Status = AFP_ERR_NONE;
|
||
}
|
||
|
||
// If we did not find it, try the global list, but only for the first one
|
||
else if (Index == 1)
|
||
{
|
||
Status = afpGetGlobalIconInfo(Creator, pType, pIconType, pTag, pSize);
|
||
}
|
||
else Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpAddAppl
|
||
*
|
||
* Add an APPL mapping to the desktop database. Is added in such a way that
|
||
* the list is maintained in a sorted fashion - sorted by Creator. It is
|
||
* already determined that the application file exists and that the user has
|
||
* appropriate access to it.
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Exclusive);
|
||
*/
|
||
AFPSTATUS
|
||
AfpAddAppl(
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN DWORD Creator,
|
||
IN DWORD ApplTag,
|
||
IN DWORD FileNum, // File number of the associated file
|
||
IN BOOLEAN Internal, // Is the server adding the APPL itself?
|
||
IN DWORD ParentID // DirId of parent dir of the application file
|
||
)
|
||
{
|
||
PAPPLINFO2 pApplInfo, *ppApplInfo;
|
||
BOOLEAN ApplReplace = False, UpdateDT = True;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT(FileNum != 0);
|
||
|
||
AfpSwmrAcquireExclusive(&pVolDesc->vds_DtAccessLock);
|
||
|
||
ppApplInfo = &pVolDesc->vds_pApplBuckets[HASH_APPL(Creator)];
|
||
|
||
// Find the right slot
|
||
for (;(pApplInfo = *ppApplInfo) != NULL; ppApplInfo = &pApplInfo->appl_Next)
|
||
{
|
||
if (pApplInfo->appl_Creator >= Creator)
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* If there is already an entry for this creator, make sure it is not for
|
||
* the same file, if it is replace it.
|
||
*/
|
||
for ( ; pApplInfo != NULL && pApplInfo->appl_Creator == Creator;
|
||
pApplInfo = pApplInfo->appl_Next)
|
||
{
|
||
if (pApplInfo->appl_FileNum == FileNum)
|
||
{
|
||
if (!Internal)
|
||
{
|
||
pApplInfo->appl_Tag = ApplTag;
|
||
}
|
||
else
|
||
{
|
||
if (pApplInfo->appl_ParentID == ParentID)
|
||
UpdateDT = False;
|
||
}
|
||
|
||
pApplInfo->appl_ParentID = ParentID;
|
||
ApplReplace = True;
|
||
}
|
||
}
|
||
|
||
if (!ApplReplace)
|
||
{
|
||
// ppApplInfo now points to the right place
|
||
if ((pApplInfo = ALLOC_APPLINFO()) != NULL)
|
||
{
|
||
pApplInfo->appl_Next = *ppApplInfo;
|
||
*ppApplInfo = pApplInfo;
|
||
pApplInfo->appl_Creator = Creator;
|
||
pApplInfo->appl_Tag = ApplTag;
|
||
pApplInfo->appl_FileNum = FileNum;
|
||
pApplInfo->appl_ParentID = ParentID;
|
||
pVolDesc->vds_cApplEnts ++;
|
||
}
|
||
else Status = AFP_ERR_MISC;
|
||
}
|
||
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpLookupAppl
|
||
*
|
||
* Search the desktop for an appl entry matching the given Creator. In
|
||
* case of multiple appl entries corresponding to the same creator, get
|
||
* the nth where n is the index.
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Shared);
|
||
*/
|
||
AFPSTATUS
|
||
AfpLookupAppl(
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN DWORD Creator,
|
||
IN LONG Index,
|
||
OUT PDWORD pApplTag, // Place holder for Tag
|
||
OUT PDWORD pFileNum, // Place holder for file number
|
||
OUT PDWORD pParentID
|
||
)
|
||
{
|
||
PAPPLINFO2 pApplInfo;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
LONG i;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireShared(&pVolDesc->vds_DtAccessLock);
|
||
pApplInfo = pVolDesc->vds_pApplBuckets[HASH_ICON(Creator)];
|
||
|
||
// Scan the list looking for the entry
|
||
for (;pApplInfo != NULL; pApplInfo = pApplInfo->appl_Next)
|
||
{
|
||
if (pApplInfo->appl_Creator == Creator)
|
||
break;
|
||
if (pApplInfo->appl_Creator > Creator) {
|
||
pApplInfo = NULL;
|
||
break;
|
||
}
|
||
}
|
||
/*
|
||
* We are now either pointing to the first entry or there are none. In the
|
||
* latter case, we just fall through
|
||
*/
|
||
if (Index != 0)
|
||
{
|
||
for (i = 1; pApplInfo!=NULL; i++, pApplInfo = pApplInfo->appl_Next)
|
||
{
|
||
if ((i > Index) || (pApplInfo->appl_Creator != Creator))
|
||
{
|
||
pApplInfo = NULL;
|
||
break;
|
||
}
|
||
if (i == Index)
|
||
break; // Found the right entry
|
||
}
|
||
}
|
||
if (pApplInfo == NULL)
|
||
Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
else
|
||
{
|
||
*pFileNum = pApplInfo->appl_FileNum;
|
||
*pApplTag = pApplInfo->appl_Tag;
|
||
*pParentID = pApplInfo->appl_ParentID;
|
||
}
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpRemoveAppl
|
||
*
|
||
* The entries corresponding to the given Creator in the specified directory
|
||
* is removed from the desktop database. It is already determined that the
|
||
* application file exists and that the user has appropriate access to it.
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Exclusive);
|
||
*/
|
||
AFPSTATUS
|
||
AfpRemoveAppl(
|
||
IN PVOLDESC pVolDesc, // Open Volume descriptor of ref desktop
|
||
IN DWORD Creator,
|
||
IN DWORD FileNum // File number of the associated file
|
||
)
|
||
{
|
||
PAPPLINFO2 pApplInfo, *ppApplInfo;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
BOOLEAN Found = False;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireExclusive(&pVolDesc->vds_DtAccessLock);
|
||
ppApplInfo = &pVolDesc->vds_pApplBuckets[HASH_APPL(Creator)];
|
||
|
||
// Find the APPL entry in the desktop
|
||
for (;(pApplInfo = *ppApplInfo) != NULL; ppApplInfo = &pApplInfo->appl_Next)
|
||
{
|
||
if (pApplInfo->appl_Creator < Creator)
|
||
continue;
|
||
if (pApplInfo->appl_Creator > Creator)
|
||
break;
|
||
/*
|
||
* Check if the File number matches, if it does delete.
|
||
*/
|
||
if (pApplInfo->appl_FileNum == FileNum)
|
||
{
|
||
Found = True;
|
||
*ppApplInfo = pApplInfo->appl_Next;
|
||
AfpFreeMemory(pApplInfo);
|
||
pVolDesc->vds_cApplEnts --;
|
||
break;
|
||
}
|
||
}
|
||
if (!Found)
|
||
Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpAddComment
|
||
*
|
||
* Add the comment to the file or directory in question. Create the comment
|
||
* stream on the entity in question (if it does not already exist), convert
|
||
* the comment to unicode and write it. Update the flag in the DFEntry.
|
||
*/
|
||
AFPSTATUS
|
||
AfpAddComment(
|
||
IN PSDA pSda, // Session Data Area
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN PANSI_STRING Comment, // Comment to associate with the file/dir
|
||
IN PPATHMAPENTITY pPME, // Handle to the entity or its Host Id
|
||
IN BOOLEAN Directory, // True if directory
|
||
IN DWORD AfpId
|
||
)
|
||
{
|
||
UNICODE_STRING UComment;
|
||
WCHAR CommentBuf[AFP_MAXCOMMENTSIZE+1];
|
||
FILESYSHANDLE HandleCommentStream;
|
||
DWORD CreateInfo;
|
||
NTSTATUS Status = AFP_ERR_MISC;
|
||
PDFENTRY pDFE = NULL;
|
||
BOOLEAN RestoreModTime = FALSE;
|
||
AFPTIME aModTime;
|
||
TIME ModTime;
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT (IS_VOLUME_NTFS(pVolDesc));
|
||
|
||
if (Comment->Length == 0)
|
||
{
|
||
AfpRemoveComment(pSda, pVolDesc, pPME, Directory, AfpId);
|
||
return AFP_ERR_NONE;
|
||
}
|
||
|
||
if (Comment->Length > AFP_MAXCOMMENTSIZE)
|
||
{
|
||
// Truncate comment if necessary
|
||
Comment->Length = AFP_MAXCOMMENTSIZE;
|
||
}
|
||
|
||
UComment.Buffer = CommentBuf;
|
||
UComment.MaximumLength = (USHORT)(RtlAnsiStringToUnicodeSize(Comment) + sizeof(WCHAR));
|
||
UComment.Length = 0;
|
||
|
||
AfpConvertStringToUnicode(Comment, &UComment);
|
||
|
||
do
|
||
{
|
||
AfpImpersonateClient(pSda);
|
||
|
||
// Get the last modified time from the file so we can reset it.
|
||
|
||
Status = AfpIoQueryTimesnAttr( &pPME->pme_Handle,
|
||
NULL,
|
||
&ModTime,
|
||
NULL );
|
||
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
RestoreModTime = TRUE;
|
||
aModTime = AfpConvertTimeToMacFormat(&ModTime);
|
||
}
|
||
|
||
// Open the comment stream on the target entity.
|
||
Status = AfpIoCreate(&pPME->pme_Handle,
|
||
AFP_STREAM_COMM,
|
||
&UNullString,
|
||
FILEIO_ACCESS_WRITE,
|
||
FILEIO_DENY_NONE,
|
||
FILEIO_OPEN_FILE,
|
||
FILEIO_CREATE_HARD,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
True,
|
||
NULL,
|
||
&HandleCommentStream,
|
||
&CreateInfo,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
|
||
AfpRevertBack();
|
||
|
||
if (Status != AFP_ERR_NONE) {
|
||
if ((Status = AfpIoConvertNTStatusToAfpStatus(Status)) != AFP_ERR_ACCESS_DENIED)
|
||
Status = AFP_ERR_MISC;
|
||
break;
|
||
}
|
||
|
||
Status = AfpIoWrite(&HandleCommentStream,
|
||
&LIZero,
|
||
(LONG)UComment.Length,
|
||
(PBYTE)UComment.Buffer);
|
||
|
||
AfpIoClose(&HandleCommentStream);
|
||
|
||
if( RestoreModTime )
|
||
{
|
||
AfpIoSetTimesnAttr( &pPME->pme_Handle,
|
||
NULL,
|
||
&aModTime,
|
||
0,
|
||
0,
|
||
NULL,
|
||
NULL );
|
||
}
|
||
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
AfpVolumeSetModifiedTime(pVolDesc);
|
||
|
||
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
||
if ((pDFE = AfpFindDfEntryById(pVolDesc,
|
||
AfpId,
|
||
DFE_ANY)) != NULL)
|
||
{
|
||
pDFE->dfe_Flags |= DFE_FLAGS_HAS_COMMENT;
|
||
}
|
||
else
|
||
{
|
||
Status = AFP_ERR_MISC;
|
||
}
|
||
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
||
}
|
||
} while (False);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpGetComment
|
||
*
|
||
* Extract the comment from the file or directory in question. The comment is
|
||
* copied to the ReplyBuf.
|
||
*/
|
||
AFPSTATUS
|
||
AfpGetComment(
|
||
IN PSDA pSda, // Session Data Area
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN PPATHMAPENTITY pPME, // Handle to the entity or its Host Id
|
||
IN BOOLEAN Directory // True if directory
|
||
)
|
||
{
|
||
NTSTATUS Status = AFP_ERR_MISC;
|
||
LONG SizeRead;
|
||
UNICODE_STRING UComment;
|
||
WCHAR CommentBuf[AFP_MAXCOMMENTSIZE+1];
|
||
ANSI_STRING AComment;
|
||
FILESYSHANDLE HandleCommentStream;
|
||
|
||
PAGED_CODE( );
|
||
|
||
// ASSERT (IS_VOLUME_NTFS(pVolDesc));
|
||
|
||
// Initialize AComment
|
||
AComment.Buffer = pSda->sda_ReplyBuf + 1; // For size of string
|
||
AComment.MaximumLength = AFP_MAXCOMMENTSIZE;
|
||
AComment.Length = 0;
|
||
|
||
UComment.MaximumLength = (AFP_MAXCOMMENTSIZE + 1) * sizeof(WCHAR);
|
||
UComment.Buffer = CommentBuf;
|
||
|
||
do
|
||
{
|
||
AfpImpersonateClient(pSda);
|
||
|
||
// Open the comment stream on the target entity.
|
||
Status = AfpIoOpen(&pPME->pme_Handle,
|
||
AFP_STREAM_COMM,
|
||
FILEIO_OPEN_FILE,
|
||
&UNullString,
|
||
FILEIO_ACCESS_READ,
|
||
FILEIO_DENY_NONE,
|
||
True,
|
||
&HandleCommentStream);
|
||
|
||
AfpRevertBack();
|
||
|
||
if (Status != AFP_ERR_NONE)
|
||
{
|
||
Status = AfpIoConvertNTStatusToAfpStatus(Status);
|
||
if (Status == AFP_ERR_OBJECT_NOT_FOUND)
|
||
Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
else if (Status != AFP_ERR_ACCESS_DENIED)
|
||
Status = AFP_ERR_OBJECT_NOT_FOUND;
|
||
break;
|
||
}
|
||
|
||
Status = AfpIoRead(&HandleCommentStream,
|
||
&LIZero,
|
||
(LONG)UComment.MaximumLength,
|
||
&SizeRead,
|
||
(PBYTE)UComment.Buffer);
|
||
|
||
AfpIoClose(&HandleCommentStream);
|
||
|
||
if (Status == AFP_ERR_NONE)
|
||
{
|
||
UComment.Length = (USHORT) SizeRead;
|
||
AfpConvertStringToAnsi(&UComment, &AComment);
|
||
pSda->sda_ReplyBuf[0] = (BYTE)AComment.Length;
|
||
pSda->sda_ReplySize = AComment.Length + 1;
|
||
}
|
||
} while (False);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpRemoveComment
|
||
*
|
||
* Remove the comment from the file or directory in question. Essentially
|
||
* open the comment stream and set the length to 0.
|
||
*/
|
||
AFPSTATUS
|
||
AfpRemoveComment(
|
||
IN PSDA pSda, // Session Data Area
|
||
IN PVOLDESC pVolDesc, // Volume descriptor of referenced desktop
|
||
IN PPATHMAPENTITY pPME, // Handle to the entity or its Host Id
|
||
IN BOOLEAN Directory, // True if directory
|
||
IN DWORD AfpId
|
||
)
|
||
{
|
||
FILESYSHANDLE HandleCommentStream;
|
||
NTSTATUS Status = AFP_ERR_MISC;
|
||
PDFENTRY pDFE = NULL;
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT (IS_VOLUME_NTFS(pVolDesc));
|
||
|
||
do
|
||
{
|
||
AfpImpersonateClient(pSda);
|
||
|
||
// Open the comment stream on the target entity.
|
||
Status = AfpIoOpen(&pPME->pme_Handle,
|
||
AFP_STREAM_COMM,
|
||
FILEIO_OPEN_FILE,
|
||
&UNullString,
|
||
FILEIO_ACCESS_DELETE,
|
||
FILEIO_DENY_NONE,
|
||
True,
|
||
&HandleCommentStream);
|
||
|
||
AfpRevertBack();
|
||
|
||
if (Status != AFP_ERR_NONE)
|
||
{
|
||
if ((Status = AfpIoConvertNTStatusToAfpStatus(Status)) != AFP_ERR_ACCESS_DENIED)
|
||
Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
break;
|
||
}
|
||
Status = AfpIoMarkFileForDelete(&HandleCommentStream, NULL, NULL, NULL);
|
||
|
||
AfpIoClose(&HandleCommentStream);
|
||
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
AfpVolumeSetModifiedTime(pVolDesc);
|
||
|
||
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
||
if ((pDFE = AfpFindDfEntryById(pVolDesc,
|
||
AfpId,
|
||
DFE_ANY)) != NULL)
|
||
{
|
||
pDFE->dfe_Flags &= ~DFE_FLAGS_HAS_COMMENT;
|
||
}
|
||
else
|
||
{
|
||
Status = AFP_ERR_MISC;
|
||
}
|
||
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
||
}
|
||
} while (False);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpAddIconToGlobalList
|
||
*
|
||
* The global list of icons is a server maintained list updated by the service.
|
||
* This adds an icon to the list. If an icon exists for the given type and
|
||
* creator, it is replaced. This list is maintained via the AfpIconAdd() admin
|
||
* api.
|
||
*
|
||
* LOCKS: AfpIconListLock (SWMR, Exclusive);
|
||
*/
|
||
AFPSTATUS
|
||
AfpAddIconToGlobalList(
|
||
IN DWORD Type,
|
||
IN DWORD Creator,
|
||
IN DWORD IconType,
|
||
IN LONG IconSize,
|
||
IN PBYTE pIconBitMap
|
||
)
|
||
{
|
||
PICONINFO pIconInfo,
|
||
pIconInfoNew,
|
||
*ppIconInfo;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
|
||
PAGED_CODE( );
|
||
|
||
// Pre-allocate memory for the new icon, delete if necessary later
|
||
if ((pIconInfoNew = ALLOC_ICONINFO(IconSize)) == NULL)
|
||
return AFP_ERR_MISC;
|
||
|
||
AfpSwmrAcquireExclusive(&AfpIconListLock);
|
||
ppIconInfo = &AfpGlobalIconList;
|
||
for (; (pIconInfo = *ppIconInfo) != NULL; ppIconInfo = &pIconInfo->icon_Next)
|
||
{
|
||
if ((pIconInfo->icon_Type == Type) &&
|
||
(pIconInfo->icon_Creator == Creator))
|
||
break;
|
||
}
|
||
if (pIconInfo == NULL)
|
||
{
|
||
if (IconSize > 0)
|
||
RtlCopyMemory((PBYTE)pIconInfoNew + sizeof(ICONINFO), pIconBitMap, IconSize);
|
||
pIconInfoNew->icon_Creator = Creator;
|
||
pIconInfoNew->icon_Type = Type;
|
||
pIconInfoNew->icon_IconType = (USHORT)IconType;
|
||
pIconInfoNew->icon_Size = (SHORT)IconSize;
|
||
pIconInfoNew->icon_Tag = 0;
|
||
pIconInfoNew->icon_Next = NULL;
|
||
*ppIconInfo = pIconInfoNew;
|
||
}
|
||
else
|
||
{
|
||
// We do not need the memory any more, release it
|
||
AfpFreeMemory(pIconInfoNew);
|
||
if (pIconInfo->icon_IconType != (USHORT)IconType)
|
||
Status = AFPERR_InvalidParms;
|
||
else if (IconSize > 0)
|
||
RtlCopyMemory((PBYTE)pIconInfo + sizeof(ICONINFO), pIconBitMap, IconSize);
|
||
}
|
||
AfpSwmrRelease(&AfpIconListLock);
|
||
return AFP_ERR_NONE;
|
||
}
|
||
|
||
|
||
/*** afpLookupIconInGlobalList
|
||
*
|
||
* The global list of icons is a server maintained list updates by the service.
|
||
* This is called by AfpLookupIcon() when the specified icon is not found in
|
||
* the volume desktop.
|
||
*
|
||
* LOCKS: AfpIconListLock (SWMR, Shared);
|
||
*/
|
||
LOCAL AFPSTATUS
|
||
afpLookupIconInGlobalList(
|
||
IN DWORD Creator,
|
||
IN DWORD Type,
|
||
IN DWORD IconType,
|
||
IN PLONG pSize,
|
||
OUT PBYTE pBitMap
|
||
)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireShared(&AfpIconListLock);
|
||
pIconInfo = AfpGlobalIconList;
|
||
for (pIconInfo = AfpGlobalIconList;
|
||
pIconInfo != NULL;
|
||
pIconInfo = pIconInfo->icon_Next)
|
||
{
|
||
if ((pIconInfo->icon_Type == Type) &&
|
||
(pIconInfo->icon_Creator == Creator) &&
|
||
(pIconInfo->icon_IconType == (USHORT)IconType))
|
||
break;
|
||
}
|
||
if (pIconInfo == NULL)
|
||
Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
else
|
||
{
|
||
if (*pSize > pIconInfo->icon_Size)
|
||
*pSize = pIconInfo->icon_Size;
|
||
if (*pSize > 0)
|
||
RtlCopyMemory(pBitMap, (PBYTE)pIconInfo + sizeof(ICONINFO), *pSize);
|
||
}
|
||
AfpSwmrRelease(&AfpIconListLock);
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpFreeGlobalIconList
|
||
*
|
||
* Called at server stop time to free the memory allocated for the global
|
||
* icons.
|
||
*
|
||
* LOCKS: AfpIconListLock (SWMR, Exclusive);
|
||
*/
|
||
VOID
|
||
AfpFreeGlobalIconList(
|
||
VOID
|
||
)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireExclusive(&AfpIconListLock);
|
||
|
||
for (pIconInfo = AfpGlobalIconList; pIconInfo != NULL; )
|
||
{
|
||
PICONINFO pFree;
|
||
|
||
pFree = pIconInfo;
|
||
pIconInfo = pIconInfo->icon_Next;
|
||
AfpFreeMemory (pFree);
|
||
}
|
||
|
||
AfpSwmrRelease(&AfpIconListLock);
|
||
}
|
||
|
||
|
||
/*** afpGetGlobalIconInfo
|
||
*
|
||
* The global list of icons is a server maintained list updates by the service.
|
||
* This is called by AfpLookupIconInfo() when the specified icon is not found
|
||
* in the volume desktop.
|
||
*
|
||
* LOCKS: AfpIconListLock (SWMR, Shared)
|
||
*/
|
||
LOCAL AFPSTATUS
|
||
afpGetGlobalIconInfo(
|
||
IN DWORD Creator,
|
||
OUT PDWORD pType,
|
||
OUT PDWORD pIconType,
|
||
OUT PDWORD pTag,
|
||
OUT PLONG pSize
|
||
)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
AFPSTATUS Status = AFP_ERR_NONE;
|
||
|
||
PAGED_CODE( );
|
||
|
||
AfpSwmrAcquireExclusive(&AfpIconListLock);
|
||
pIconInfo = AfpGlobalIconList;
|
||
for (pIconInfo = AfpGlobalIconList;
|
||
pIconInfo != NULL;
|
||
pIconInfo = pIconInfo->icon_Next)
|
||
{
|
||
if (pIconInfo->icon_Creator == Creator)
|
||
break;
|
||
}
|
||
if (pIconInfo == NULL)
|
||
Status = AFP_ERR_ITEM_NOT_FOUND;
|
||
else
|
||
{
|
||
*pType = pIconInfo->icon_Type;
|
||
*pIconType = pIconInfo->icon_IconType;
|
||
*pTag = pIconInfo->icon_Tag;
|
||
*pSize = pIconInfo->icon_Size;
|
||
}
|
||
AfpSwmrRelease(&AfpIconListLock);
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** afpReadDesktopFromDisk
|
||
*
|
||
* Read the desktop database from the desktop stream. No locks are required
|
||
* for this routine since it only operates on volume descriptors which are
|
||
* newly created and not yet linked into the global volume list.
|
||
*/
|
||
LOCAL NTSTATUS
|
||
afpReadDesktopFromDisk(
|
||
IN PVOLDESC pVolDesc,
|
||
IN PFILESYSHANDLE pfshDesktop
|
||
)
|
||
{
|
||
DESKTOP Desktop;
|
||
PAPPLINFO2 *ppApplInfo;
|
||
PICONINFO *ppIconInfo;
|
||
NTSTATUS Status;
|
||
DWORD DskOffst;
|
||
FORKOFFST ForkOffset;
|
||
PBYTE pBuffer;
|
||
LONG i, SizeRead, BufOffst = 0;
|
||
LONG PrevHash, applSize;
|
||
|
||
PAGED_CODE( );
|
||
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO,
|
||
("\tReading Desktop from disk....\n") );
|
||
|
||
// Work with one page of memory and do multiple I/Os to the disk.
|
||
if ((pBuffer = AfpAllocNonPagedMemory(DESKTOPIO_BUFSIZE)) == NULL)
|
||
{
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
ForkOffset.QuadPart = DskOffst = 0;
|
||
|
||
// Read in the desktop header and validate it
|
||
Status = AfpIoRead(pfshDesktop,
|
||
&ForkOffset,
|
||
sizeof(DESKTOP),
|
||
&SizeRead,
|
||
(PBYTE)&Desktop);
|
||
|
||
if (!NT_SUCCESS(Status) ||
|
||
|
||
(SizeRead != sizeof(DESKTOP)) ||
|
||
|
||
(Desktop.dtp_Signature != AFP_SERVER_SIGNATURE) ||
|
||
|
||
((Desktop.dtp_Version != AFP_DESKTOP_VERSION1) &&
|
||
(Desktop.dtp_Version != AFP_DESKTOP_VERSION2)) ||
|
||
|
||
((Desktop.dtp_cApplEnts > 0) &&
|
||
((ULONG_PTR)(Desktop.dtp_pApplInfo) != sizeof(DESKTOP))) ||
|
||
|
||
((Desktop.dtp_cIconEnts > 0) &&
|
||
((ULONG_PTR)(Desktop.dtp_pIconInfo) != sizeof(DESKTOP) +
|
||
(Desktop.dtp_cApplEnts *
|
||
((Desktop.dtp_Version == AFP_DESKTOP_VERSION1) ?
|
||
sizeof(APPLINFO) : sizeof(APPLINFO2))) )) )
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status, NULL, 0,
|
||
&pVolDesc->vds_Name);
|
||
goto desktop_corrupt;
|
||
}
|
||
|
||
switch (Desktop.dtp_Version)
|
||
{
|
||
case AFP_DESKTOP_VERSION1:
|
||
{
|
||
AFPLOG_INFO(AFPSRVMSG_UPDATE_DESKTOP_VERSION,
|
||
STATUS_SUCCESS,
|
||
NULL,
|
||
0,
|
||
&pVolDesc->vds_Name);
|
||
|
||
applSize = sizeof(APPLINFO);
|
||
|
||
break;
|
||
}
|
||
case AFP_DESKTOP_VERSION2:
|
||
{
|
||
applSize = sizeof(APPLINFO2);
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
// This should never happen since it was checked above
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_WARN,
|
||
("afpReadDesktopFromDisk: Unexpected DT version 0x%lx\n", Desktop.dtp_Version) );
|
||
ASSERTMSG("afpReadDesktopFromDisk: Unexpected DT Version", 0);
|
||
goto desktop_corrupt;
|
||
}
|
||
}
|
||
|
||
// Initialize the desktop header. Even though we may be reading a
|
||
// downlevel version database, set the in-memory desktop database
|
||
// version to current version since we are building it with the
|
||
// current appl version structure.
|
||
AfpDtHdrToVolDesc(&Desktop, pVolDesc);
|
||
|
||
ForkOffset.QuadPart = DskOffst = sizeof(DESKTOP);
|
||
SizeRead = 0;
|
||
|
||
// Now read in the APPL entries, if any
|
||
for (i = 0, PrevHash = -1;
|
||
(Status == AFP_ERR_NONE) && (i < Desktop.dtp_cApplEnts);
|
||
i++)
|
||
{
|
||
PAPPLINFO2 pApplInfo;
|
||
|
||
if ((SizeRead - BufOffst) < applSize)
|
||
{
|
||
// We have a partial APPLINFO. Backup and read the whole thing
|
||
DskOffst -= ((DWORD)SizeRead - (DWORD)BufOffst);
|
||
ForkOffset.QuadPart = DskOffst;
|
||
Status = AfpIoRead(pfshDesktop,
|
||
&ForkOffset,
|
||
DESKTOPIO_BUFSIZE,
|
||
&SizeRead,
|
||
pBuffer);
|
||
if ((Status != AFP_ERR_NONE) || (SizeRead < applSize))
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status, &SizeRead,
|
||
sizeof(SizeRead), &pVolDesc->vds_Name);
|
||
Status = STATUS_UNEXPECTED_IO_ERROR;
|
||
break;
|
||
}
|
||
DskOffst += SizeRead;
|
||
ForkOffset.QuadPart = DskOffst;
|
||
BufOffst = 0;
|
||
}
|
||
|
||
if ((pApplInfo = ALLOC_APPLINFO()) == NULL)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status, NULL, 0,
|
||
&pVolDesc->vds_Name);
|
||
break;
|
||
}
|
||
pApplInfo->appl_ParentID = 0;
|
||
// If we are reading downlevel appl structures, they will
|
||
// get read into the first part of the current appl structures.
|
||
// These fields should be identical! If this is the case, the
|
||
// appl_ParentId field will be 0 and the volume marked as needing
|
||
// its appls rebuilt.
|
||
RtlCopyMemory(pApplInfo, pBuffer + BufOffst, applSize);
|
||
pApplInfo->appl_Next = NULL;
|
||
BufOffst += applSize;
|
||
if (PrevHash != (LONG)HASH_APPL(pApplInfo->appl_Creator))
|
||
{
|
||
PrevHash = (LONG)HASH_APPL(pApplInfo->appl_Creator);
|
||
ppApplInfo = &pVolDesc->vds_pApplBuckets[PrevHash];
|
||
}
|
||
*ppApplInfo = pApplInfo;
|
||
ppApplInfo = &pApplInfo->appl_Next;
|
||
}
|
||
|
||
|
||
// Now read in the ICON entries, if any
|
||
|
||
for (i = 0, PrevHash = -1;
|
||
(Status == AFP_ERR_NONE) && (i < Desktop.dtp_cIconEnts);
|
||
i++)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
|
||
if ((SizeRead - BufOffst) < sizeof(ICONINFO))
|
||
{
|
||
// We have a partial ICONINFO. Backup and read the whole thing
|
||
DskOffst -= ((DWORD)SizeRead - (DWORD)BufOffst);
|
||
ForkOffset.QuadPart = DskOffst;
|
||
Status = AfpIoRead(pfshDesktop,
|
||
&ForkOffset,
|
||
DESKTOPIO_BUFSIZE,
|
||
&SizeRead,
|
||
pBuffer);
|
||
if ((Status != AFP_ERR_NONE) || (SizeRead < sizeof(ICONINFO)))
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status, &SizeRead,
|
||
sizeof(SizeRead), &pVolDesc->vds_Name);
|
||
Status = STATUS_UNEXPECTED_IO_ERROR;
|
||
break;
|
||
}
|
||
DskOffst += SizeRead;
|
||
ForkOffset.QuadPart = DskOffst;
|
||
BufOffst = 0;
|
||
}
|
||
|
||
// Validate icon size
|
||
if ((((PICONINFO)(pBuffer + BufOffst))->icon_Size > ICONSIZE_ICN8) ||
|
||
(((PICONINFO)(pBuffer + BufOffst))->icon_Size < ICONSIZE_ICS))
|
||
{
|
||
Status = STATUS_UNEXPECTED_IO_ERROR;
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status,
|
||
&((PICONINFO)(pBuffer + BufOffst))->icon_Size,
|
||
sizeof(((PICONINFO)(0))->icon_Size),
|
||
&pVolDesc->vds_Name);
|
||
break;
|
||
}
|
||
|
||
if ((pIconInfo = ALLOC_ICONINFO(((PICONINFO)(pBuffer + BufOffst))->icon_Size)) == NULL)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status, NULL, 0,
|
||
&pVolDesc->vds_Name);
|
||
break;
|
||
}
|
||
|
||
// First copy the icon header and then link the icon into the hash table
|
||
RtlCopyMemory(pIconInfo, pBuffer + BufOffst, sizeof(ICONINFO));
|
||
|
||
pIconInfo->icon_Next = NULL;
|
||
if (PrevHash != (LONG)HASH_ICON(pIconInfo->icon_Creator))
|
||
{
|
||
PrevHash = (LONG)HASH_ICON(pIconInfo->icon_Creator);
|
||
ppIconInfo = &pVolDesc->vds_pIconBuckets[PrevHash];
|
||
}
|
||
*ppIconInfo = pIconInfo;
|
||
ppIconInfo = &pIconInfo->icon_Next;
|
||
|
||
// Now check if there is sufficient stuff here to get the icon
|
||
BufOffst += sizeof(ICONINFO);
|
||
if ((SizeRead - BufOffst) < pIconInfo->icon_Size)
|
||
{
|
||
LONG Size2Copy;
|
||
|
||
Size2Copy = SizeRead - BufOffst;
|
||
|
||
// Copy what we can first
|
||
RtlCopyMemory((PBYTE)pIconInfo + sizeof(ICONINFO),
|
||
pBuffer + BufOffst, Size2Copy);
|
||
|
||
Status = AfpIoRead(pfshDesktop,
|
||
&ForkOffset,
|
||
DESKTOPIO_BUFSIZE,
|
||
&SizeRead,
|
||
pBuffer);
|
||
if ((Status != AFP_ERR_NONE) ||
|
||
(SizeRead < (pIconInfo->icon_Size - Size2Copy)))
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_READ_DESKTOP, Status, &SizeRead,
|
||
sizeof(SizeRead), &pVolDesc->vds_Name);
|
||
Status = STATUS_UNEXPECTED_IO_ERROR;
|
||
break;
|
||
}
|
||
DskOffst += SizeRead;
|
||
ForkOffset.QuadPart = DskOffst;
|
||
|
||
// Now copy the rest of the icon
|
||
RtlCopyMemory((PBYTE)pIconInfo + sizeof(ICONINFO) + Size2Copy,
|
||
pBuffer,
|
||
pIconInfo->icon_Size - Size2Copy);
|
||
|
||
BufOffst = pIconInfo->icon_Size - Size2Copy;
|
||
}
|
||
else
|
||
{
|
||
RtlCopyMemory((PBYTE)pIconInfo + sizeof(ICONINFO),
|
||
pBuffer + BufOffst,
|
||
pIconInfo->icon_Size);
|
||
|
||
BufOffst += pIconInfo->icon_Size;
|
||
}
|
||
}
|
||
|
||
if (Status != AFP_ERR_NONE)
|
||
{
|
||
AfpFreeDesktopTables(pVolDesc);
|
||
desktop_corrupt:
|
||
// We have essentially ignored the existing data in the stream
|
||
// Initialize the header
|
||
pVolDesc->vds_cApplEnts = 0;
|
||
pVolDesc->vds_cIconEnts = 0;
|
||
|
||
AfpVolDescToDtHdr(pVolDesc, &Desktop);
|
||
Desktop.dtp_pIconInfo = NULL;
|
||
Desktop.dtp_pApplInfo = NULL;
|
||
AfpIoWrite(pfshDesktop,
|
||
&LIZero,
|
||
sizeof(DESKTOP),
|
||
(PBYTE)&Desktop);
|
||
|
||
// Truncate the stream at this point
|
||
AfpIoSetSize(pfshDesktop, sizeof(DESKTOP));
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (pBuffer != NULL)
|
||
AfpFreeMemory(pBuffer);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
/*** AfpInitDesktop
|
||
*
|
||
* This routine initializes the memory image (and all related volume
|
||
* descriptor fields) of the desktop for a newly added volume. If a desktop
|
||
* stream already exists on the disk for the volume root directory, that
|
||
* stream is read in. If this is a newly created volume, the desktop
|
||
* stream is created on the root of the volume. If this is a CD-ROM volume,
|
||
* only the memory image is initialized.
|
||
*
|
||
* No locks are necessary since this routine only operates on volume
|
||
* descriptors which are newly allocated, but not yet linked into the global
|
||
* volume list.
|
||
*/
|
||
AFPSTATUS
|
||
AfpInitDesktop(
|
||
IN PVOLDESC pVolDesc,
|
||
OUT BOOLEAN *pfNewVolume
|
||
)
|
||
{
|
||
BOOLEAN InitHeader = True;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
FILESYSHANDLE fshDesktop;
|
||
|
||
PAGED_CODE( );
|
||
|
||
// for now
|
||
*pfNewVolume = FALSE;
|
||
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO, ("\tInitializing Desktop...\n") );
|
||
AfpSwmrInitSwmr(&(pVolDesc->vds_DtAccessLock));
|
||
|
||
// if this is an NTFS volume, attempt to create the desktop stream.
|
||
// If it already exists, open it and read it in.
|
||
if (IS_VOLUME_NTFS(pVolDesc))
|
||
{
|
||
ULONG CreateInfo;
|
||
|
||
InitHeader = False;
|
||
|
||
Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
|
||
AFP_STREAM_DT,
|
||
&UNullString,
|
||
FILEIO_ACCESS_READWRITE,
|
||
FILEIO_DENY_WRITE,
|
||
FILEIO_OPEN_FILE,
|
||
FILEIO_CREATE_INTERNAL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
False,
|
||
NULL,
|
||
&fshDesktop,
|
||
&CreateInfo,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
if (CreateInfo == FILE_OPENED)
|
||
{
|
||
Status = afpReadDesktopFromDisk(pVolDesc, &fshDesktop);
|
||
AfpIoClose(&fshDesktop);
|
||
}
|
||
else if (CreateInfo != FILE_CREATED)
|
||
{
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_ERR,
|
||
("AfpInitDesktop: Unexpected create action 0x%lx\n", CreateInfo) );
|
||
ASSERT(0); // this should never happen
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
else
|
||
{
|
||
DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
|
||
("AfpInitDesktop: volume %Z is new\n",&pVolDesc->vds_Name));
|
||
|
||
InitHeader = True;
|
||
*pfNewVolume = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_ERR,
|
||
("AfpInitDesktop: AfpIoCreate failed %lx\n", Status));
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
if (InitHeader)
|
||
{
|
||
DESKTOP Desktop;
|
||
|
||
// Initialize the header
|
||
pVolDesc->vds_cApplEnts = 0;
|
||
pVolDesc->vds_cIconEnts = 0;
|
||
|
||
if (IS_VOLUME_NTFS(pVolDesc))
|
||
{
|
||
AfpVolDescToDtHdr(pVolDesc, &Desktop);
|
||
Desktop.dtp_pIconInfo = NULL;
|
||
Desktop.dtp_pApplInfo = NULL;
|
||
AfpIoWrite(&fshDesktop,
|
||
&LIZero,
|
||
sizeof(DESKTOP),
|
||
(PBYTE)&Desktop);
|
||
AfpIoClose(&fshDesktop);
|
||
}
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
|
||
/*** AfpUpdateDesktop
|
||
*
|
||
* Update the desktop database on the volume root. The swmr access is held
|
||
* for read (by the caller) while the update is in progress. It is already
|
||
* determined by the caller that the volume desktop needs to be updated.
|
||
*
|
||
* LOCKS: vds_DtAccessLock (SWMR, Shared)
|
||
*/
|
||
VOID
|
||
AfpUpdateDesktop(
|
||
IN PVOLDESC pVolDesc // Volume Descriptor of the open volume
|
||
)
|
||
{
|
||
AFPSTATUS Status;
|
||
PBYTE pBuffer;
|
||
DWORD Offset = 0, Size;
|
||
LONG i;
|
||
DESKTOP Desktop;
|
||
FORKOFFST ForkOffset;
|
||
FILESYSHANDLE fshDesktop;
|
||
ULONG CreateInfo;
|
||
#ifdef PROFILING
|
||
TIME TimeS, TimeE, TimeD;
|
||
|
||
PAGED_CODE( );
|
||
|
||
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DesktopUpdCount);
|
||
AfpGetPerfCounter(&TimeS);
|
||
#endif
|
||
|
||
// Take the swmr so that nobody can initiate changes to the desktop
|
||
AfpSwmrAcquireShared(&pVolDesc->vds_DtAccessLock);
|
||
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO,
|
||
("\tWriting Desktop to disk....\n") );
|
||
|
||
do
|
||
{
|
||
fshDesktop.fsh_FileHandle = NULL;
|
||
// Work with one page of memory and do multiple I/Os to the disk.
|
||
if ((pBuffer = AfpAllocPagedMemory(DESKTOPIO_BUFSIZE)) == NULL)
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_WRITE_DESKTOP, STATUS_NO_MEMORY, NULL, 0,
|
||
&pVolDesc->vds_Name);
|
||
break;
|
||
}
|
||
|
||
// Open a handle to the desktop stream, denying others read/write
|
||
// access (i.e. backup/restore)
|
||
Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
|
||
AFP_STREAM_DT,
|
||
&UNullString,
|
||
FILEIO_ACCESS_WRITE,
|
||
FILEIO_DENY_ALL,
|
||
FILEIO_OPEN_FILE,
|
||
FILEIO_CREATE_INTERNAL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
False,
|
||
NULL,
|
||
&fshDesktop,
|
||
&CreateInfo,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
if ((CreateInfo != FILE_OPENED) && (CreateInfo != FILE_CREATED))
|
||
{
|
||
// This should never happen!
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_WARN,
|
||
("AfpUpdateDesktop: Unexpected create action 0x%lx\n", CreateInfo) );
|
||
ASSERTMSG("AfpUpdateDesktop: Unexpected create action", 0);
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_WRITE_DESKTOP, Status, NULL, 0,
|
||
&pVolDesc->vds_Name);
|
||
break;
|
||
}
|
||
|
||
// Snapshot the header and write it with an invalid signature. We write
|
||
// the header again later with a valid signature. This protects us from
|
||
// incomplete writes (server crash etc.)
|
||
AfpVolDescToDtHdr(pVolDesc, &Desktop);
|
||
Desktop.dtp_Signature = 0;
|
||
|
||
(ULONG_PTR)(Desktop.dtp_pApplInfo) = 0;
|
||
if (Desktop.dtp_cApplEnts > 0)
|
||
(ULONG_PTR)(Desktop.dtp_pApplInfo) = sizeof(DESKTOP);
|
||
|
||
(ULONG_PTR)(Desktop.dtp_pIconInfo) = 0;
|
||
if (Desktop.dtp_cIconEnts > 0)
|
||
(ULONG_PTR)(Desktop.dtp_pIconInfo) = sizeof(DESKTOP) +
|
||
sizeof(APPLINFO2)*Desktop.dtp_cApplEnts;
|
||
|
||
// Write out the header with invalid signature
|
||
Status = AfpIoWrite(&fshDesktop,
|
||
&LIZero,
|
||
sizeof(DESKTOP),
|
||
(PBYTE)&Desktop);
|
||
|
||
Offset = sizeof(DESKTOP);
|
||
Size = 0;
|
||
|
||
// First write the APPL Entries
|
||
for (i = 0; (Status == AFP_ERR_NONE) && (i < APPL_BUCKETS); i++)
|
||
{
|
||
PAPPLINFO2 pApplInfo;
|
||
|
||
for (pApplInfo = pVolDesc->vds_pApplBuckets[i];
|
||
(Status == AFP_ERR_NONE) && (pApplInfo != NULL);
|
||
pApplInfo = pApplInfo->appl_Next)
|
||
{
|
||
if ((DESKTOPIO_BUFSIZE - Size) < sizeof(APPLINFO2))
|
||
{
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO,
|
||
("afpUpdateDesktop: Writing Appl %ld at %ld\n", Size, Offset));
|
||
|
||
ForkOffset.QuadPart = Offset;
|
||
Status = AfpIoWrite(&fshDesktop,
|
||
&ForkOffset,
|
||
Size,
|
||
pBuffer);
|
||
Size = 0;
|
||
Offset += Size;
|
||
}
|
||
*(PAPPLINFO2)(pBuffer + Size) = *pApplInfo;
|
||
Size += sizeof(APPLINFO2);
|
||
}
|
||
}
|
||
|
||
// And now the ICON entries
|
||
for (i = 0; (Status == AFP_ERR_NONE) && (i < ICON_BUCKETS); i++)
|
||
{
|
||
PICONINFO pIconInfo;
|
||
|
||
for (pIconInfo = pVolDesc->vds_pIconBuckets[i];
|
||
(Status == AFP_ERR_NONE) && (pIconInfo != NULL);
|
||
pIconInfo = pIconInfo->icon_Next)
|
||
{
|
||
if ((DESKTOPIO_BUFSIZE - Size) < (sizeof(ICONINFO) + pIconInfo->icon_Size))
|
||
{
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO,
|
||
("afpUpdateDesktop: Writing icons %ld at %ld\n", Size, Offset));
|
||
|
||
ForkOffset.QuadPart = Offset;
|
||
Status = AfpIoWrite(&fshDesktop,
|
||
&ForkOffset,
|
||
Size,
|
||
pBuffer);
|
||
Offset += Size;
|
||
Size = 0;
|
||
}
|
||
RtlCopyMemory(pBuffer + Size,
|
||
(PBYTE)pIconInfo,
|
||
sizeof(ICONINFO) + pIconInfo->icon_Size);
|
||
Size += sizeof(ICONINFO) + pIconInfo->icon_Size;
|
||
}
|
||
}
|
||
|
||
while (Status == AFP_ERR_NONE)
|
||
{
|
||
if (Size > 0)
|
||
{
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO,
|
||
("afpUpdateDesktop: Writing at end %ld at %ld\n", Size, Offset));
|
||
|
||
ForkOffset.QuadPart = Offset;
|
||
Status = AfpIoWrite(&fshDesktop,
|
||
&ForkOffset,
|
||
Size,
|
||
pBuffer);
|
||
if (Status != AFP_ERR_NONE)
|
||
break;
|
||
}
|
||
|
||
DBGPRINT(DBG_COMP_DESKTOP, DBG_LEVEL_INFO,
|
||
("afpUpdateDesktop: Setting desktop stream size @ %ld\n", Size + Offset));
|
||
// Chop off the stream at this offset.
|
||
Status = AfpIoSetSize(&fshDesktop, Offset + Size);
|
||
|
||
ASSERT (Status == AFP_ERR_NONE);
|
||
|
||
// Write the correct signature back
|
||
Desktop.dtp_Signature = AFP_SERVER_SIGNATURE;
|
||
|
||
Status = AfpIoWrite(&fshDesktop,
|
||
&LIZero,
|
||
sizeof(DESKTOP),
|
||
(PBYTE)&Desktop);
|
||
|
||
// Update the count of changes: vds_cChangesDt is protected by the
|
||
// swmr, the scavenger can set this with READ access. All others
|
||
// MUST hold the swmr for WRITE access to increment the cChangesDt.
|
||
// Scavenger is the only consumer of vds_cScvgrDt, so no lock is
|
||
// really needed for it.
|
||
pVolDesc->vds_cScvgrDt = 0;
|
||
break;
|
||
}
|
||
|
||
if (Status != AFP_ERR_NONE)
|
||
{
|
||
AFPLOG_ERROR(AFPSRVMSG_WRITE_DESKTOP, Status, NULL, 0,
|
||
&pVolDesc->vds_Name);
|
||
}
|
||
|
||
} while (False);
|
||
|
||
if (pBuffer != NULL)
|
||
{
|
||
AfpFreeMemory(pBuffer);
|
||
if (fshDesktop.fsh_FileHandle != NULL)
|
||
AfpIoClose(&fshDesktop);
|
||
}
|
||
#ifdef PROFILING
|
||
AfpGetPerfCounter(&TimeE);
|
||
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
||
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_DesktopUpdTime,
|
||
TimeD,
|
||
&AfpStatisticsLock);
|
||
#endif
|
||
AfpSwmrRelease(&pVolDesc->vds_DtAccessLock);
|
||
}
|
||
|
||
|
||
/*** AfpFreeDesktopTables
|
||
*
|
||
* Free the allocated memory for the volume desktop tables. The volume is
|
||
* about to be deleted. Ensure that either the volume is non-NTFS or it is
|
||
* clean i.e. the scavenger threads have written it back. No locks are needed
|
||
* as this structure is all by itself.
|
||
*/
|
||
VOID
|
||
AfpFreeDesktopTables(
|
||
IN PVOLDESC pVolDesc
|
||
)
|
||
{
|
||
LONG i;
|
||
|
||
PAGED_CODE( );
|
||
|
||
// This should never happen
|
||
ASSERT (!IS_VOLUME_NTFS(pVolDesc) ||
|
||
(pVolDesc->vds_pOpenForkDesc == NULL));
|
||
|
||
// First tackle the ICON list. Traverse each of the hash indices.
|
||
// Note that the icon is allocated as part of the IconInfo structure
|
||
// so free in together.
|
||
for (i = 0; i < ICON_BUCKETS; i++)
|
||
{
|
||
PICONINFO pIconInfo, pFree;
|
||
|
||
for (pIconInfo = pVolDesc->vds_pIconBuckets[i]; pIconInfo != NULL; )
|
||
{
|
||
pFree = pIconInfo;
|
||
pIconInfo = pIconInfo->icon_Next;
|
||
AfpFreeMemory(pFree);
|
||
}
|
||
// In case we ever try to free the table again
|
||
pVolDesc->vds_pIconBuckets[i] = NULL;
|
||
}
|
||
|
||
// Now tackle the APPL list. Traverse each of the hash indices.
|
||
for (i = 0; i < APPL_BUCKETS; i++)
|
||
{
|
||
PAPPLINFO2 pApplInfo, pFree;
|
||
|
||
for (pApplInfo = pVolDesc->vds_pApplBuckets[i]; pApplInfo != NULL; )
|
||
{
|
||
pFree = pApplInfo;
|
||
pApplInfo = pApplInfo->appl_Next;
|
||
AfpFreeMemory(pFree);
|
||
}
|
||
// In case we ever try to free the table again
|
||
pVolDesc->vds_pApplBuckets[i] = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|