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