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.
1676 lines
42 KiB
1676 lines
42 KiB
/*
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|