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.
1509 lines
40 KiB
1509 lines
40 KiB
/*
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pathmap.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the routines that manipulate AFP paths.
|
|
|
|
Author:
|
|
|
|
Sue Adams (microsoft!suea)
|
|
|
|
|
|
Revision History:
|
|
04 Jun 1992 Initial Version
|
|
05 Oct 1993 JameelH Performance Changes. Merge cached afpinfo into the
|
|
idindex structure. Make both the ANSI and the UNICODE
|
|
names part of idindex. Added EnumCache for improving
|
|
enumerate perf.
|
|
|
|
Notes: Tab stop: 4
|
|
--*/
|
|
|
|
#define _PATHMAP_LOCALS
|
|
#define FILENUM FILE_PATHMAP
|
|
|
|
#include <afp.h>
|
|
#include <fdparm.h>
|
|
#include <pathmap.h>
|
|
#include <afpinfo.h>
|
|
#include <client.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfpMapAfpPath)
|
|
#pragma alloc_text( PAGE, AfpMapAfpPathForLookup)
|
|
#pragma alloc_text( PAGE, AfpMapAfpIdForLookup)
|
|
#pragma alloc_text( PAGE, afpGetMappedForLookupFDInfo)
|
|
#pragma alloc_text( PAGE, afpMapAfpPathToMappedPath)
|
|
#pragma alloc_text( PAGE, AfpHostPathFromDFEntry)
|
|
#pragma alloc_text( PAGE, AfpCheckParentPermissions)
|
|
#pragma alloc_text( PAGE, afpOpenUserHandle)
|
|
#endif
|
|
|
|
|
|
/*** AfpMapAfpPath
|
|
*
|
|
* If mapping is for lookup operation, a FILESYSHANDLE open in the user's
|
|
* context is returned, The caller MUST close this handle when done with it.
|
|
*
|
|
* If pFDParm is non-null, it will be filled in as appropriate according to Bitmap.
|
|
*
|
|
* If mapping is for create operation, the volume root-relative host pathname
|
|
* (in unicode) of the item we are about to create is returned. For lookup
|
|
* operation the paths refer to the item being pathmapped. This routine
|
|
* always returns the paths in the PME. It is the caller's responsibility
|
|
* to free the Full HostPath Buffer, if it is not supplied already.
|
|
*
|
|
* The caller MUST have the IdDb locked for Exclusive access.
|
|
*
|
|
* LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
|
|
*
|
|
*/
|
|
AFPSTATUS
|
|
AfpMapAfpPath(
|
|
IN PCONNDESC pConnDesc,
|
|
IN DWORD DirId,
|
|
IN PANSI_STRING pPath,
|
|
IN BYTE PathType, // short names or long names
|
|
IN PATHMAP_TYPE MapReason, // for lookup or hard/soft create?
|
|
IN DWORD DFFlag, // map to file? dir? or either?
|
|
IN DWORD Bitmap, // what fields of FDParm to fill in
|
|
OUT PPATHMAPENTITY pPME,
|
|
OUT PFILEDIRPARM pFDParm OPTIONAL // for lookups only
|
|
)
|
|
{
|
|
PVOLDESC pVolDesc;
|
|
MAPPEDPATH mappedpath;
|
|
AFPSTATUS Status;
|
|
#ifdef PROFILING
|
|
TIME TimeS, TimeE, TimeD;
|
|
#endif
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT((pConnDesc != NULL));
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_PathMapCount);
|
|
AfpGetPerfCounter(&TimeS);
|
|
#endif
|
|
|
|
pVolDesc = pConnDesc->cds_pVolDesc;
|
|
ASSERT(IS_VOLUME_NTFS(pVolDesc));
|
|
ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock));
|
|
|
|
// initialize some fields in the PME
|
|
AfpSetEmptyUnicodeString(&pPME->pme_ParentPath, 0, NULL);
|
|
|
|
do
|
|
{
|
|
Status = afpMapAfpPathToMappedPath(pVolDesc,
|
|
DirId,
|
|
pPath,
|
|
PathType,
|
|
MapReason,
|
|
DFFlag,
|
|
True,
|
|
&mappedpath);
|
|
if ((Status != AFP_ERR_NONE) &&
|
|
!((MapReason == HardCreate) &&
|
|
(Status == AFP_ERR_OBJECT_EXISTS) &&
|
|
(DFFlag == DFE_FILE)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ASSERT(pPME != NULL);
|
|
|
|
// Get the volume relative path to the parent directory for
|
|
// creates, or to the item for lookups
|
|
if ((Status = AfpHostPathFromDFEntry(mappedpath.mp_pdfe,
|
|
// since CopyFile and Move have to lookup
|
|
// the destination parent dir paths, we
|
|
// need to allocate extra room for them in
|
|
// the path to append the filename
|
|
(MapReason == Lookup) ?
|
|
(AFP_LONGNAME_LEN + 1) * sizeof(WCHAR):
|
|
mappedpath.mp_Tail.Length + sizeof(WCHAR),
|
|
&pPME->pme_FullPath)) != AFP_ERR_NONE)
|
|
break;
|
|
|
|
// if Pathmap is for hard (files only) or soft create (file or dir)
|
|
if (MapReason != Lookup)
|
|
{
|
|
ASSERT(pFDParm == NULL);
|
|
|
|
// fill in the dfe of parent dir in which create will take place
|
|
pPME->pme_pDfeParent = mappedpath.mp_pdfe;
|
|
|
|
// fill in path to parent
|
|
pPME->pme_ParentPath = pPME->pme_FullPath;
|
|
|
|
// Add a path separator if we are not at the root
|
|
if (pPME->pme_FullPath.Length > 0)
|
|
{
|
|
pPME->pme_FullPath.Buffer[pPME->pme_FullPath.Length / sizeof(WCHAR)] = L'\\';
|
|
pPME->pme_FullPath.Length += sizeof(WCHAR);
|
|
}
|
|
|
|
pPME->pme_UTail.Length = pPME->pme_UTail.MaximumLength = mappedpath.mp_Tail.Length;
|
|
pPME->pme_UTail.Buffer = (PWCHAR)((PBYTE)pPME->pme_FullPath.Buffer +
|
|
pPME->pme_FullPath.Length);
|
|
|
|
Status = RtlAppendUnicodeStringToString(&pPME->pme_FullPath,
|
|
&mappedpath.mp_Tail);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
else // lookup operation
|
|
{
|
|
pPME->pme_pDfEntry = mappedpath.mp_pdfe;
|
|
pPME->pme_UTail.Length = mappedpath.mp_pdfe->dfe_UnicodeName.Length;
|
|
pPME->pme_UTail.Buffer = (PWCHAR)((PBYTE)pPME->pme_FullPath.Buffer +
|
|
pPME->pme_FullPath.Length -
|
|
pPME->pme_UTail.Length);
|
|
|
|
pPME->pme_ParentPath.Length =
|
|
pPME->pme_ParentPath.MaximumLength = pPME->pme_FullPath.Length - pPME->pme_UTail.Length;
|
|
|
|
if (pPME->pme_FullPath.Length > pPME->pme_UTail.Length)
|
|
{
|
|
// subtract the path separator if not in root dir
|
|
pPME->pme_ParentPath.Length -= sizeof(WCHAR);
|
|
ASSERT(pPME->pme_ParentPath.Length >= 0);
|
|
}
|
|
pPME->pme_ParentPath.Buffer = pPME->pme_FullPath.Buffer;
|
|
pPME->pme_UTail.MaximumLength = pPME->pme_FullPath.MaximumLength - pPME->pme_ParentPath.Length;
|
|
|
|
Status = afpGetMappedForLookupFDInfo(pConnDesc,
|
|
mappedpath.mp_pdfe,
|
|
Bitmap,
|
|
pPME,
|
|
pFDParm);
|
|
// if this fails do not free path buffer and set it back to
|
|
// null. We don't know that the path buffer isn't on
|
|
// the callers stack. Caller should always clean it up himself.
|
|
}
|
|
} while (False);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_PathMapTime,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
return Status;
|
|
}
|
|
|
|
/*** AfpMapAfpPathForLookup
|
|
*
|
|
* Maps an AFP dirid/pathname pair to an open handle (in the user's context)
|
|
* to the DATA stream of the file/dir.
|
|
* The DirID database is locked for read for the duration of this
|
|
* routine, unless afpMapAfpPathToMappedPath returns
|
|
* AFP_ERR_WRITE_LOCK_REQUIRED in which case the DirID database will be locked
|
|
* for write. This will only happen the first time a mac tries to access
|
|
* a directory who's files have not yet been cached in.
|
|
*
|
|
* LOCKS: vds_IdDbAccessLock (SWMR, Shared OR Exclusive)
|
|
*/
|
|
AFPSTATUS
|
|
AfpMapAfpPathForLookup(
|
|
IN PCONNDESC pConnDesc,
|
|
IN DWORD DirId,
|
|
IN PANSI_STRING pPath,
|
|
IN BYTE PathType, // short names or long names
|
|
IN DWORD DFFlag,
|
|
IN DWORD Bitmap,
|
|
OUT PPATHMAPENTITY pPME OPTIONAL,
|
|
OUT PFILEDIRPARM pFDParm OPTIONAL
|
|
)
|
|
{
|
|
MAPPEDPATH mappedpath;
|
|
PVOLDESC pVolDesc;
|
|
PSWMR pIdDbLock;
|
|
AFPSTATUS Status;
|
|
BOOLEAN swmrLockedExclusive = False;
|
|
PATHMAP_TYPE mapReason = Lookup;
|
|
#ifdef PROFILING
|
|
TIME TimeS, TimeE, TimeD;
|
|
#endif
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT((pConnDesc != NULL));
|
|
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_PathMapCount);
|
|
AfpGetPerfCounter(&TimeS);
|
|
#endif
|
|
|
|
#ifndef GET_CORRECT_OFFSPRING_COUNTS
|
|
if (pConnDesc->cds_pSda->sda_AfpFunc == _AFP_ENUMERATE)
|
|
{
|
|
mapReason = LookupForEnumerate;
|
|
}
|
|
#endif
|
|
|
|
pVolDesc = pConnDesc->cds_pVolDesc;
|
|
pIdDbLock = &(pVolDesc->vds_IdDbAccessLock);
|
|
|
|
AfpSwmrAcquireShared(pIdDbLock);
|
|
|
|
do
|
|
{
|
|
do
|
|
{
|
|
Status = afpMapAfpPathToMappedPath(pVolDesc,
|
|
DirId,
|
|
pPath,
|
|
PathType,
|
|
mapReason, // lookups only
|
|
DFFlag,
|
|
swmrLockedExclusive,
|
|
&mappedpath);
|
|
|
|
if (Status == AFP_ERR_WRITE_LOCK_REQUIRED)
|
|
{
|
|
ASSERT (!swmrLockedExclusive);
|
|
// Pathmap needed to cache in the files for the last directory
|
|
// in the path but didn't have the write lock to the ID database
|
|
AfpSwmrRelease(pIdDbLock);
|
|
AfpSwmrAcquireExclusive(pIdDbLock);
|
|
swmrLockedExclusive = True;
|
|
continue;
|
|
}
|
|
break;
|
|
} while (True);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DBGPRINT (DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
|
|
("AfpMapAfpPathForLookup: afpMapAfpPathToMappedPath failed: Error = %lx\n", Status));
|
|
break;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pPME))
|
|
{
|
|
pPME->pme_FullPath.Length = 0;
|
|
}
|
|
|
|
if (Bitmap & FD_INTERNAL_BITMAP_RETURN_PMEPATHS)
|
|
{
|
|
ASSERT(ARGUMENT_PRESENT(pPME));
|
|
if ((Status = AfpHostPathFromDFEntry(mappedpath.mp_pdfe,
|
|
(Bitmap & FD_INTERNAL_BITMAP_OPENFORK_RESC) ?
|
|
AfpResourceStream.Length : 0,
|
|
&pPME->pme_FullPath)) != AFP_ERR_NONE)
|
|
break;
|
|
|
|
pPME->pme_UTail.Length = mappedpath.mp_pdfe->dfe_UnicodeName.Length;
|
|
pPME->pme_UTail.Buffer = (PWCHAR)((PBYTE)pPME->pme_FullPath.Buffer +
|
|
pPME->pme_FullPath.Length - pPME->pme_UTail.Length);
|
|
|
|
pPME->pme_ParentPath.Length =
|
|
pPME->pme_ParentPath.MaximumLength = pPME->pme_FullPath.Length - pPME->pme_UTail.Length;
|
|
|
|
if (pPME->pme_FullPath.Length > pPME->pme_UTail.Length)
|
|
{
|
|
// subtract the path separator if not in root dir
|
|
pPME->pme_ParentPath.Length -= sizeof(WCHAR);
|
|
ASSERT(pPME->pme_ParentPath.Length >= 0);
|
|
}
|
|
pPME->pme_ParentPath.Buffer = pPME->pme_FullPath.Buffer;
|
|
pPME->pme_UTail.MaximumLength = pPME->pme_FullPath.MaximumLength - pPME->pme_ParentPath.Length;
|
|
}
|
|
|
|
Status = afpGetMappedForLookupFDInfo(pConnDesc,
|
|
mappedpath.mp_pdfe,
|
|
Bitmap,
|
|
pPME,
|
|
pFDParm);
|
|
} while (False);
|
|
|
|
AfpSwmrRelease(pIdDbLock);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_PathMapTime,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*** AfpMapAfpIdForLookup
|
|
*
|
|
* Maps an AFP id to an open FILESYSTEMHANDLE (in the user's context) to
|
|
* to the DATA stream of the file/dir.
|
|
* The DirID database is locked for shared or exclusive access for the duration
|
|
* of this routine.
|
|
*
|
|
* LOCKS: vds_IdDbAccessLock (SWMR, Shared OR Exclusive)
|
|
*/
|
|
AFPSTATUS
|
|
AfpMapAfpIdForLookup(
|
|
IN PCONNDESC pConnDesc,
|
|
IN DWORD AfpId,
|
|
IN DWORD DFFlag,
|
|
IN DWORD Bitmap,
|
|
OUT PPATHMAPENTITY pPME OPTIONAL,
|
|
OUT PFILEDIRPARM pFDParm OPTIONAL
|
|
)
|
|
{
|
|
PVOLDESC pVolDesc;
|
|
PSWMR pIdDbLock;
|
|
AFPSTATUS Status;
|
|
PDFENTRY pDfEntry;
|
|
BOOLEAN CleanupLock = False;
|
|
#ifdef PROFILING
|
|
TIME TimeS, TimeE, TimeD;
|
|
#endif
|
|
|
|
PAGED_CODE( );
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_PathMapCount);
|
|
AfpGetPerfCounter(&TimeS);
|
|
#endif
|
|
|
|
ASSERT((pConnDesc != NULL));
|
|
|
|
do
|
|
{
|
|
if (AfpId == 0)
|
|
{
|
|
Status = AFP_ERR_PARAM;
|
|
break;
|
|
}
|
|
|
|
pVolDesc = pConnDesc->cds_pVolDesc;
|
|
pIdDbLock = &(pVolDesc->vds_IdDbAccessLock);
|
|
|
|
AfpSwmrAcquireShared(pIdDbLock);
|
|
CleanupLock = True;
|
|
|
|
if ((AfpId == AFP_ID_PARENT_OF_ROOT) ||
|
|
((pDfEntry = AfpFindDfEntryById(pVolDesc, AfpId, DFE_ANY)) == NULL))
|
|
{
|
|
Status = AFP_ERR_OBJECT_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
if (((DFFlag == DFE_DIR) && DFE_IS_FILE(pDfEntry)) ||
|
|
((DFFlag == DFE_FILE) && DFE_IS_DIRECTORY(pDfEntry)))
|
|
{
|
|
Status = AFP_ERR_OBJECT_TYPE;
|
|
break;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pPME))
|
|
{
|
|
pPME->pme_FullPath.Length = 0;
|
|
}
|
|
|
|
Status = afpGetMappedForLookupFDInfo(pConnDesc,
|
|
pDfEntry,
|
|
Bitmap,
|
|
pPME,
|
|
pFDParm);
|
|
} while (False);
|
|
|
|
if (CleanupLock)
|
|
{
|
|
AfpSwmrRelease(pIdDbLock);
|
|
}
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_PathMapTime,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
return Status;
|
|
}
|
|
|
|
/*** afpGetMappedForLookupFDInfo
|
|
*
|
|
* After a pathmap for LOOKUP operation, this routine is called to
|
|
* return various FileDir parm information about the mapped file/dir.
|
|
* The following FileDir information is always returned:
|
|
* AFP DirId/FileId
|
|
* Parent DirId
|
|
* DFE flags (indicating item is a directory, a file, or a file with an ID)
|
|
* Attributes (Inhibit bits and D/R Already open bits normalized with
|
|
* the NTFS attributes for RO, System, Hidden, Archive)
|
|
* BackupTime
|
|
* CreateTime
|
|
* ModifiedTime
|
|
*
|
|
* The following FileDir information is returned according to the flags set
|
|
* in word 0 of the Bitmap parameter (these correspond to the AFP file/dir
|
|
* bitmap):
|
|
* Longname
|
|
* Shortname
|
|
* FinderInfo
|
|
* ProDosInfo
|
|
* Directory Access Rights (as stored in AFP_AfpInfo stream)
|
|
* Directory OwnerId/GroupId
|
|
* Directory Offspring count (file count and dir count are separate)
|
|
*
|
|
* The open access is stored in word 1 of the Bitmap parameter.
|
|
* This is used by AfpOpenUserHandle (for NTFS volumes) or AfpIoOpen (for
|
|
* CDFS volumes) when opening the data stream of the file/dir (under
|
|
* impersonation for NTFS) who's handle will be returned within the
|
|
* pPME parameter if supplied.
|
|
*
|
|
* LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Shared)
|
|
*
|
|
*/
|
|
LOCAL
|
|
AFPSTATUS
|
|
afpGetMappedForLookupFDInfo(
|
|
IN PCONNDESC pConnDesc,
|
|
IN PDFENTRY pDfEntry,
|
|
IN DWORD Bitmap,
|
|
OUT PPATHMAPENTITY pPME OPTIONAL, // Supply for NTFS only if need a
|
|
// handle in user's context, usually
|
|
// for security checking purposes
|
|
OUT PFILEDIRPARM pFDParm OPTIONAL // Supply if want returned FDInfo
|
|
)
|
|
{
|
|
BOOLEAN fNtfsVol;
|
|
AFPSTATUS Status = STATUS_SUCCESS;
|
|
DWORD OpenAccess = FILEIO_ACCESS_NONE;
|
|
FILESYSHANDLE fsh;
|
|
PFILESYSHANDLE pHandle = NULL;
|
|
|
|
PAGED_CODE( );
|
|
|
|
fNtfsVol = IS_VOLUME_NTFS(pConnDesc->cds_pVolDesc);
|
|
if (ARGUMENT_PRESENT(pPME))
|
|
{
|
|
pHandle = &pPME->pme_Handle;
|
|
}
|
|
else if ((fNtfsVol &&
|
|
(Bitmap & (FD_BITMAP_SHORTNAME | FD_BITMAP_PRODOSINFO))))
|
|
{
|
|
pHandle = &fsh;
|
|
}
|
|
|
|
if (pHandle != NULL)
|
|
{
|
|
if (!NT_SUCCESS(Status = afpOpenUserHandle(pConnDesc,
|
|
pDfEntry,
|
|
(ARGUMENT_PRESENT(pPME) &&
|
|
(pPME->pme_FullPath.Buffer != NULL)) ?
|
|
&pPME->pme_FullPath : NULL,
|
|
Bitmap, // encode open/deny modes
|
|
pHandle)))
|
|
{
|
|
if ((Status == AFP_ERR_DENY_CONFLICT) &&
|
|
ARGUMENT_PRESENT(pFDParm))
|
|
{
|
|
// For CreateId/ResolveId/DeleteId
|
|
pFDParm->_fdp_AfpId = pDfEntry->dfe_AfpId;
|
|
pFDParm->_fdp_Flags = (pDfEntry->dfe_Flags & DFE_FLAGS_DFBITS);
|
|
}
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
if (ARGUMENT_PRESENT(pFDParm))
|
|
{
|
|
pFDParm->_fdp_AfpId = pDfEntry->dfe_AfpId;
|
|
pFDParm->_fdp_ParentId = pDfEntry->dfe_Parent->dfe_AfpId;
|
|
|
|
ASSERT(!((pDfEntry->dfe_Flags & DFE_FLAGS_DIR) &&
|
|
(pDfEntry->dfe_Flags & (DFE_FLAGS_FILE_WITH_ID | DFE_FLAGS_FILE_NO_ID))));
|
|
|
|
pFDParm->_fdp_Flags = (pDfEntry->dfe_Flags & DFE_FLAGS_DFBITS);
|
|
|
|
if (Bitmap & FD_BITMAP_FINDERINFO)
|
|
{
|
|
pFDParm->_fdp_FinderInfo = pDfEntry->dfe_FinderInfo;
|
|
}
|
|
|
|
pFDParm->_fdp_Attr = pDfEntry->dfe_AfpAttr;
|
|
AfpNormalizeAfpAttr(pFDParm, pDfEntry->dfe_NtAttr);
|
|
|
|
// The Finder uses the Finder isInvisible flag over
|
|
// the file system Invisible attribute to tell if the thing is
|
|
// displayed or not. If the PC turns off the hidden attribute
|
|
// we should clear the Finder isInvisible flag
|
|
if ((Bitmap & FD_BITMAP_FINDERINFO) &&
|
|
!(pFDParm->_fdp_Attr & FD_BITMAP_ATTR_INVISIBLE))
|
|
{
|
|
pFDParm->_fdp_FinderInfo.fd_Attr1 &= ~FINDER_FLAG_INVISIBLE;
|
|
}
|
|
|
|
pFDParm->_fdp_BackupTime = pDfEntry->dfe_BackupTime;
|
|
pFDParm->_fdp_CreateTime = pDfEntry->dfe_CreateTime;
|
|
pFDParm->_fdp_ModifiedTime = AfpConvertTimeToMacFormat(&pDfEntry->dfe_LastModTime);
|
|
|
|
if (Bitmap & FD_BITMAP_LONGNAME)
|
|
{
|
|
ASSERT((pFDParm->_fdp_LongName.Buffer != NULL) &&
|
|
(pFDParm->_fdp_LongName.MaximumLength >=
|
|
pDfEntry->dfe_UnicodeName.Length/(USHORT)sizeof(WCHAR)));
|
|
AfpConvertMungedUnicodeToAnsi(&pDfEntry->dfe_UnicodeName,
|
|
&pFDParm->_fdp_LongName);
|
|
}
|
|
|
|
if (Bitmap & FD_BITMAP_SHORTNAME)
|
|
{
|
|
ASSERT(pFDParm->_fdp_ShortName.Buffer != NULL);
|
|
|
|
if (!fNtfsVol)
|
|
{
|
|
ASSERT(pFDParm->_fdp_ShortName.MaximumLength >=
|
|
(pDfEntry->dfe_UnicodeName.Length/sizeof(WCHAR)));
|
|
AfpConvertMungedUnicodeToAnsi(&pDfEntry->dfe_UnicodeName,
|
|
&pFDParm->_fdp_ShortName);
|
|
|
|
// if asking for shortname on CDFS, we will fill in the pFDParm
|
|
// shortname with the pDfEntry longname, ONLY if it is an 8.3 name
|
|
if (!AfpIsLegalShortname(&pFDParm->_fdp_ShortName))
|
|
{
|
|
pFDParm->_fdp_ShortName.Length = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get NTFS shortname
|
|
ASSERT(pFDParm->_fdp_ShortName.MaximumLength >= AFP_SHORTNAME_LEN);
|
|
ASSERT(pHandle != NULL);
|
|
|
|
Status = AfpIoQueryShortName(pHandle,
|
|
&pFDParm->_fdp_ShortName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
pFDParm->_fdp_ShortName.Length = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DFE_IS_FILE(pDfEntry))
|
|
{
|
|
if (pDfEntry->dfe_Flags & DFE_FLAGS_D_ALREADYOPEN)
|
|
pFDParm->_fdp_Attr |= FILE_BITMAP_ATTR_DATAOPEN;
|
|
if (pDfEntry->dfe_Flags & DFE_FLAGS_R_ALREADYOPEN)
|
|
pFDParm->_fdp_Attr |= FILE_BITMAP_ATTR_RESCOPEN;
|
|
if (Bitmap & FILE_BITMAP_RESCLEN)
|
|
{
|
|
pFDParm->_fdp_RescForkLen = pDfEntry->dfe_RescLen;
|
|
}
|
|
if (Bitmap & FILE_BITMAP_DATALEN)
|
|
{
|
|
pFDParm->_fdp_DataForkLen = pDfEntry->dfe_DataLen;
|
|
}
|
|
}
|
|
|
|
if (Bitmap & FD_BITMAP_PRODOSINFO)
|
|
{
|
|
if (fNtfsVol)
|
|
{
|
|
ASSERT(pHandle != NULL);
|
|
Status = AfpQueryProDos(pHandle,
|
|
&pFDParm->_fdp_ProDosInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else // CDFS File or Directory
|
|
{
|
|
RtlZeroMemory(&pFDParm->_fdp_ProDosInfo, sizeof(PRODOSINFO));
|
|
if (DFE_IS_FILE(pDfEntry)) // CDFS file
|
|
{
|
|
AfpProDosInfoFromFinderInfo(&pDfEntry->dfe_FinderInfo,
|
|
&pFDParm->_fdp_ProDosInfo);
|
|
}
|
|
else // CDFS Directory
|
|
{
|
|
pFDParm->_fdp_ProDosInfo.pd_FileType[0] = PRODOS_TYPE_DIR;
|
|
pFDParm->_fdp_ProDosInfo.pd_AuxType[1] = PRODOS_AUX_DIR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for dir here since enumerate ANDs the file and dir bitmaps
|
|
if (DFE_IS_DIRECTORY(pDfEntry) &&
|
|
(Bitmap & (DIR_BITMAP_ACCESSRIGHTS |
|
|
DIR_BITMAP_OWNERID |
|
|
DIR_BITMAP_GROUPID)))
|
|
{
|
|
if (fNtfsVol)
|
|
{
|
|
// Because the file and dir bitmaps are OR'd together,
|
|
// and the OwnerId bit is overloaded with the RescLen bit,
|
|
// we don't know if this bit was actually included in the
|
|
// file bitmap or the dir bitmap. The api would have
|
|
// determined whether or not it needed a handle based on
|
|
// these bitmaps, so based on the pPME we can tell if we
|
|
// actually need to query for security or not.
|
|
if (ARGUMENT_PRESENT(pPME))
|
|
{
|
|
pFDParm->_fdp_OwnerRights = DFE_OWNER_ACCESS(pDfEntry);
|
|
pFDParm->_fdp_GroupRights = DFE_GROUP_ACCESS(pDfEntry);
|
|
pFDParm->_fdp_WorldRights = DFE_WORLD_ACCESS(pDfEntry);
|
|
|
|
// Query this user's rights
|
|
Status = AfpQuerySecurityIdsAndRights(pConnDesc->cds_pSda,
|
|
pHandle,
|
|
Bitmap,
|
|
pFDParm);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFDParm->_fdp_OwnerRights =
|
|
pFDParm->_fdp_GroupRights =
|
|
pFDParm->_fdp_WorldRights =
|
|
pFDParm->_fdp_UserRights = (DIR_ACCESS_READ | DIR_ACCESS_SEARCH);
|
|
pFDParm->_fdp_OwnerId = pFDParm->_fdp_GroupId = 0;
|
|
}
|
|
}
|
|
|
|
// Must check for type directory since this Bitmap bit is overloaded
|
|
if (DFE_IS_DIRECTORY(pDfEntry) && (Bitmap & DIR_BITMAP_OFFSPRINGS))
|
|
{
|
|
#ifndef GET_CORRECT_OFFSPRING_COUNTS
|
|
if (!DFE_CHILDREN_ARE_PRESENT(pDfEntry) &&
|
|
(pDfEntry->dfe_DirOffspring == 0))
|
|
{
|
|
// If the files have not yet been cached in for this dir,
|
|
// return non-zero filecount so that system 7.x view by
|
|
// name will enumerate the directory if user clicks the
|
|
// triangle for this dir. If you return zero offspring
|
|
// What might break from lying like this?
|
|
pFDParm->_fdp_FileCount = 1;
|
|
}
|
|
else
|
|
#endif
|
|
pFDParm->_fdp_FileCount = pDfEntry->dfe_FileOffspring;
|
|
|
|
pFDParm->_fdp_DirCount = pDfEntry->dfe_DirOffspring;
|
|
}
|
|
}
|
|
} while (False);
|
|
|
|
if (pHandle == &fsh)
|
|
{
|
|
// if we had to open a handle just to query shortname or ProDOS
|
|
// close it
|
|
AfpIoClose(&fsh);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** afpMapAfpPathToMappedPath
|
|
*
|
|
* Maps an AFP DirId/pathname pair to a MAPPEDPATH structure.
|
|
* The CALLER must have the DirId/FileId database locked for shared
|
|
* access (or Exclusive access if they need that level of lock for other
|
|
* operations on the IDDB, to map a path only requires shared lock)
|
|
*
|
|
* LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Shared OR Exclusive)
|
|
*/
|
|
LOCAL
|
|
AFPSTATUS
|
|
afpMapAfpPathToMappedPath(
|
|
IN PVOLDESC pVolDesc,
|
|
IN DWORD DirId,
|
|
IN PANSI_STRING Path, // relative to DirId
|
|
IN BYTE PathType, // short names or long names
|
|
IN PATHMAP_TYPE MapReason, // for lookup or hard/soft create?
|
|
IN DWORD DFflag, // file, dir or don't know which
|
|
IN BOOLEAN LockedExclusive,
|
|
OUT PMAPPEDPATH pMappedPath
|
|
|
|
)
|
|
{
|
|
PDFENTRY pDFEntry, ptempDFEntry;
|
|
CHAR *position, *tempposition;
|
|
int length, templength;
|
|
ANSI_STRING acomponent;
|
|
CHAR component[AFP_FILENAME_LEN+1];
|
|
BOOLEAN checkEnumForParent = False, checkEnumForDir = False;
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT(pVolDesc != NULL);
|
|
|
|
#ifndef GET_CORRECT_OFFSPRING_COUNTS
|
|
if (MapReason == LookupForEnumerate)
|
|
{
|
|
checkEnumForDir = True;
|
|
MapReason = Lookup;
|
|
}
|
|
#endif
|
|
|
|
// Initialize the returned MappedPath structure
|
|
pMappedPath->mp_pdfe = NULL;
|
|
AfpSetEmptyUnicodeString(&pMappedPath->mp_Tail,
|
|
sizeof(pMappedPath->mp_Tailbuf),
|
|
pMappedPath->mp_Tailbuf);
|
|
|
|
// Lookup the initial DirId in the index database, it better be valid
|
|
if ((pDFEntry = AfpFindDfEntryById(pVolDesc,
|
|
DirId,
|
|
DFE_DIR)) == NULL)
|
|
{
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
}
|
|
|
|
ASSERT(Path != NULL);
|
|
tempposition = position = Path->Buffer;
|
|
templength = length = Path->Length;
|
|
|
|
do
|
|
{
|
|
// Lookup by DirId only?
|
|
if (length == 0) // no path was given
|
|
{
|
|
if (MapReason != Lookup) // mapping is for create
|
|
{
|
|
return AFP_ERR_PARAM; // missing the file or dirname
|
|
}
|
|
else if (DFE_IS_PARENT_OF_ROOT(pDFEntry))
|
|
{
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
pMappedPath->mp_pdfe = pDFEntry;
|
|
#ifdef GET_CORRECT_OFFSPRING_COUNTS
|
|
checkEnumForParent = checkEnumForDir = True;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Pre-scan path to munge for easier component breakdown
|
|
//
|
|
|
|
// Get rid of a leading null to make scanning easier
|
|
if (*position == AFP_PATHSEP)
|
|
{
|
|
length--;
|
|
position++;
|
|
if (length == 0) // The path consisted of just one null byte
|
|
{
|
|
if (MapReason != Lookup)
|
|
{
|
|
return AFP_ERR_PARAM;
|
|
}
|
|
else if (DFE_IS_PARENT_OF_ROOT(pDFEntry))
|
|
{
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
}
|
|
else if (((DFflag == DFE_DIR) && DFE_IS_FILE(pDFEntry)) ||
|
|
((DFflag == DFE_FILE) && DFE_IS_DIRECTORY(pDFEntry)))
|
|
{
|
|
return AFP_ERR_OBJECT_TYPE;
|
|
}
|
|
else
|
|
{
|
|
pMappedPath->mp_pdfe = pDFEntry;
|
|
#ifdef GET_CORRECT_OFFSPRING_COUNTS
|
|
checkEnumForParent = checkEnumForDir = True;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get rid of a trailing null if it is not an "up" token --
|
|
// i.e. preceded by another null.
|
|
// The 2nd array access is ok because we know we have at
|
|
// least 2 chars at that point
|
|
//
|
|
if ((position[length-1] == AFP_PATHSEP) &&
|
|
(position[length-2] != AFP_PATHSEP))
|
|
{
|
|
length--;
|
|
}
|
|
|
|
|
|
// begin parsing out path components, stop when you find the last component
|
|
while (1)
|
|
{
|
|
afpGetNextComponent(position,
|
|
length,
|
|
PathType,
|
|
component,
|
|
&templength);
|
|
if (templength < 0)
|
|
{
|
|
// component was too long or an invalid AFP character was found
|
|
return AFP_ERR_PARAM;
|
|
}
|
|
|
|
length -= templength;
|
|
if (length == 0)
|
|
{
|
|
// we found the last component
|
|
break;
|
|
}
|
|
|
|
position += templength;
|
|
|
|
if (component[0] == AFP_PATHSEP) // moving up?
|
|
{ // make sure you don't go above parent of root!
|
|
if (DFE_IS_PARENT_OF_ROOT(pDFEntry))
|
|
{
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
}
|
|
else pDFEntry = pDFEntry->dfe_Parent; //backup one level
|
|
}
|
|
else // Must be a directory component moving DOWN in tree
|
|
{
|
|
RtlInitString(&acomponent, component);
|
|
AfpConvertStringToMungedUnicode(&acomponent, &pMappedPath->mp_Tail);
|
|
if ((ptempDFEntry = AfpFindEntryByUnicodeName(pVolDesc,
|
|
&pMappedPath->mp_Tail,
|
|
PathType,
|
|
pDFEntry,
|
|
DFE_DIR)) == NULL)
|
|
{
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
pDFEntry = ptempDFEntry;
|
|
}
|
|
}
|
|
} // end while
|
|
|
|
//
|
|
// we have found the last component
|
|
// is the last component an 'up' token?
|
|
//
|
|
if (component[0] == AFP_PATHSEP)
|
|
{
|
|
// don't bother walking up beyond the root
|
|
switch (pDFEntry->dfe_AfpId)
|
|
{
|
|
case AFP_ID_PARENT_OF_ROOT:
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
case AFP_ID_ROOT:
|
|
return ((MapReason == Lookup) ? AFP_ERR_OBJECT_NOT_FOUND :
|
|
AFP_ERR_PARAM);
|
|
default: // backup one level
|
|
pMappedPath->mp_pdfe = pDFEntry->dfe_Parent;
|
|
}
|
|
|
|
// this better be a lookup request
|
|
if (MapReason != Lookup)
|
|
{
|
|
if (DFflag == DFE_DIR)
|
|
{
|
|
return AFP_ERR_OBJECT_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
return AFP_ERR_OBJECT_TYPE;
|
|
}
|
|
}
|
|
|
|
// had to have been a lookup operation
|
|
if (DFflag == DFE_FILE)
|
|
{
|
|
return AFP_ERR_OBJECT_TYPE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef GET_CORRECT_OFFSPRING_COUNTS
|
|
checkEnumForParent = checkEnumForDir = True;
|
|
#endif
|
|
break;
|
|
}
|
|
} // endif last component was an 'up' token
|
|
|
|
// the last component is a file or directory name
|
|
RtlInitString(&acomponent, component);
|
|
AfpConvertStringToMungedUnicode(&acomponent,
|
|
&pMappedPath->mp_Tail);
|
|
|
|
//
|
|
// Before we search our database for the last component of the
|
|
// path, make sure all the files have been cached in for this
|
|
// directory
|
|
//
|
|
if (!DFE_CHILDREN_ARE_PRESENT(pDFEntry))
|
|
{
|
|
if (!LockedExclusive &&
|
|
!AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
|
|
{
|
|
return AFP_ERR_WRITE_LOCK_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS status;
|
|
LockedExclusive = True;
|
|
status = AfpCacheDirectoryTree(pVolDesc,
|
|
pDFEntry,
|
|
GETFILES,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
|
|
("afpMapAfpPathToMappedPath: could not cache dir tree for %Z (0x%lx)\n",
|
|
&(pDFEntry->dfe_UnicodeName), status) );
|
|
return AFP_ERR_MISC;
|
|
}
|
|
}
|
|
}
|
|
|
|
ptempDFEntry = AfpFindEntryByUnicodeName(pVolDesc,
|
|
&pMappedPath->mp_Tail,
|
|
PathType,
|
|
pDFEntry,
|
|
DFE_ANY);
|
|
|
|
if (MapReason == Lookup) // its a lookup request
|
|
{
|
|
if (ptempDFEntry == NULL)
|
|
{
|
|
return AFP_ERR_OBJECT_NOT_FOUND;
|
|
}
|
|
else if (((DFflag == DFE_DIR) && DFE_IS_FILE(ptempDFEntry)) ||
|
|
((DFflag == DFE_FILE) && DFE_IS_DIRECTORY(ptempDFEntry)))
|
|
{
|
|
return AFP_ERR_OBJECT_TYPE;
|
|
}
|
|
else
|
|
{
|
|
pMappedPath->mp_pdfe = ptempDFEntry;
|
|
#ifdef GET_CORRECT_OFFSPRING_COUNTS
|
|
if (DFE_IS_DIRECTORY(ptempDFEntry))
|
|
// we've already made sure this thing's parent was
|
|
// enumerated already above.
|
|
checkEnumForDir = True;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
else // path mapping is for a create
|
|
{
|
|
ASSERT(DFflag != DFE_ANY); // Create must specify the exact type
|
|
|
|
// Save the parent DFEntry
|
|
pMappedPath->mp_pdfe = pDFEntry;
|
|
|
|
if (ptempDFEntry != NULL)
|
|
{
|
|
// A file or dir by that name exists in the database
|
|
// (and we will assume it exists on disk)
|
|
if (MapReason == SoftCreate)
|
|
{
|
|
// Attempting create of a directory, or soft create of a file,
|
|
// and dir OR file by that name exists,
|
|
if ((DFflag == DFE_DIR) || DFE_IS_FILE(ptempDFEntry))
|
|
{
|
|
return AFP_ERR_OBJECT_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
return AFP_ERR_OBJECT_TYPE;
|
|
}
|
|
}
|
|
else if (DFE_IS_FILE(ptempDFEntry))
|
|
{
|
|
// Must be hard create and file by that name exists
|
|
if (ptempDFEntry->dfe_Flags & DFE_FLAGS_OPEN_BITS)
|
|
{
|
|
return AFP_ERR_FILE_BUSY;
|
|
}
|
|
else
|
|
{
|
|
// note we return object_exists instead of no_err
|
|
return AFP_ERR_OBJECT_EXISTS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Attempting hard create of file, but found a directory
|
|
return AFP_ERR_OBJECT_TYPE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return AFP_ERR_NONE;
|
|
}
|
|
}
|
|
|
|
} while (False);
|
|
|
|
// The only way we should have gotten here is if we successfully mapped
|
|
// the path to a DFENTRY for lookup and would return AFP_ERR_NONE
|
|
ASSERT((pMappedPath->mp_pdfe != NULL) && (MapReason == Lookup));
|
|
|
|
#ifdef GET_CORRECT_OFFSPRING_COUNTS
|
|
if (checkEnumForParent)
|
|
{
|
|
if (!DFE_CHILDREN_ARE_PRESENT(pMappedPath->mp_pdfe->dfe_Parent))
|
|
{
|
|
if (!LockedExclusive &&
|
|
!AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
|
|
{
|
|
return AFP_ERR_WRITE_LOCK_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS status;
|
|
LockedExclusive = True;
|
|
status = AfpCacheDirectoryTree(pVolDesc,
|
|
pMappedPath->mp_pdfe->dfe_Parent,
|
|
GETFILES,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
|
|
("afpMapAfpPathToMappedPath: could not cache dir tree for %Z (0x%lx)\n",
|
|
&(pMappedPath->mp_pdfe->dfe_Parent->dfe_UnicodeName), status) );
|
|
return AFP_ERR_MISC;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
if (checkEnumForDir)
|
|
{
|
|
if (!DFE_CHILDREN_ARE_PRESENT(pMappedPath->mp_pdfe))
|
|
{
|
|
if (!LockedExclusive &&
|
|
!AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
|
|
{
|
|
return AFP_ERR_WRITE_LOCK_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS status;
|
|
LockedExclusive = True;
|
|
status = AfpCacheDirectoryTree(pVolDesc,
|
|
pMappedPath->mp_pdfe,
|
|
GETFILES,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
|
|
("afpMapAfpPathToMappedPath: could not cache dir tree for %Z (0x%lx)\n",
|
|
&(pMappedPath->mp_pdfe->dfe_UnicodeName), status) );
|
|
return AFP_ERR_MISC;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return AFP_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*** AfpHostPathFromDFEntry
|
|
*
|
|
* This routine takes a pointer to a DFEntry and builds the full
|
|
* host path (in unicode) to that entity by ascending the ID database
|
|
* tree.
|
|
*
|
|
* IN pDFE -- pointer to DFEntry of which host path is desired
|
|
* IN taillen -- number of extra *bytes*, if any, the caller
|
|
* desires to have allocated for the host path,
|
|
* including room for any path separators
|
|
* OUT ppPath -- pointer to UNICODE string
|
|
*
|
|
* The caller must have the DirID/FileID database locked for read
|
|
* before calling this routine. The caller can supply a buffer which will
|
|
* be used if sufficient. Caller must free the allocated (if any)
|
|
* unicode string buffer.
|
|
*
|
|
* LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Shared)
|
|
*/
|
|
AFPSTATUS
|
|
AfpHostPathFromDFEntry(
|
|
IN PDFENTRY pDFE,
|
|
IN DWORD taillen,
|
|
OUT PUNICODE_STRING pPath
|
|
)
|
|
{
|
|
AFPSTATUS Status = AFP_ERR_NONE;
|
|
DWORD pathlen = taillen;
|
|
PDFENTRY *pdfelist = NULL, curpdfe = NULL;
|
|
PDFENTRY apdfelist[AVERAGE_NODE_DEPTH];
|
|
int counter;
|
|
|
|
PAGED_CODE( );
|
|
|
|
pPath->Length = 0;
|
|
|
|
do
|
|
{
|
|
if (DFE_IS_FILE(pDFE))
|
|
{
|
|
counter = pDFE->dfe_Parent->dfe_DirDepth;
|
|
}
|
|
else // its a DIRECTORY entry
|
|
{
|
|
ASSERT(DFE_IS_DIRECTORY(pDFE));
|
|
if (DFE_IS_ROOT(pDFE))
|
|
{
|
|
if ((pathlen > 0) && (pPath->MaximumLength < pathlen))
|
|
{
|
|
if ((pPath->Buffer = (PWCHAR)AfpAllocNonPagedMemory(pathlen)) == NULL)
|
|
{
|
|
Status = AFP_ERR_MISC;
|
|
break;
|
|
}
|
|
pPath->MaximumLength = (USHORT)pathlen;
|
|
}
|
|
break; // We are done
|
|
}
|
|
|
|
if (DFE_IS_PARENT_OF_ROOT(pDFE))
|
|
{
|
|
Status = AFP_ERR_OBJECT_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
ASSERT(pDFE->dfe_DirDepth >= 1);
|
|
counter = pDFE->dfe_DirDepth - 1;
|
|
}
|
|
|
|
if (counter)
|
|
{
|
|
// if node is within average depth, use the array on the stack,
|
|
// otherwise, allocate an array
|
|
if (counter <= AVERAGE_NODE_DEPTH)
|
|
{
|
|
pdfelist = apdfelist;
|
|
}
|
|
else
|
|
{
|
|
pdfelist = (PDFENTRY *)AfpAllocNonPagedMemory(counter*sizeof(PDFENTRY));
|
|
if (pdfelist == NULL)
|
|
{
|
|
Status = AFP_ERR_MISC;
|
|
break;
|
|
}
|
|
}
|
|
pathlen += counter * sizeof(WCHAR); // room for path separators
|
|
}
|
|
|
|
curpdfe = pDFE;
|
|
pathlen += curpdfe->dfe_UnicodeName.Length;
|
|
|
|
// walk up the tree till you find the root, collecting string lengths
|
|
// and PDFENTRY values as you go...
|
|
while (counter--)
|
|
{
|
|
pdfelist[counter] = curpdfe;
|
|
curpdfe = curpdfe->dfe_Parent;
|
|
pathlen += curpdfe->dfe_UnicodeName.Length;
|
|
}
|
|
|
|
// we are in the root, start building up the host path buffer
|
|
if (pathlen > pPath->MaximumLength)
|
|
{
|
|
pPath->Buffer = (PWCHAR)AfpAllocNonPagedMemory(pathlen);
|
|
if (pPath->Buffer == NULL)
|
|
{
|
|
Status = AFP_ERR_MISC;
|
|
break;
|
|
}
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
|
|
("AfpHostPathFromDFEntry: Allocated path buffer %lx\n",
|
|
pPath->Buffer));
|
|
pPath->MaximumLength = (USHORT)pathlen;
|
|
}
|
|
|
|
counter = 0;
|
|
do
|
|
{
|
|
RtlAppendUnicodeStringToString(pPath, &curpdfe->dfe_UnicodeName);
|
|
if (curpdfe != pDFE)
|
|
{ // add a path separator
|
|
pPath->Buffer[pPath->Length / sizeof(WCHAR)] = L'\\';
|
|
pPath->Length += sizeof(WCHAR);
|
|
curpdfe = pdfelist[counter++];
|
|
continue;
|
|
}
|
|
break;
|
|
} while (True);
|
|
|
|
if (pdfelist && (pdfelist != apdfelist))
|
|
AfpFreeMemory(pdfelist);
|
|
} while (False);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/*** AfpCheckParentPermissions
|
|
*
|
|
* Check if this user has the necessary SeeFiles or SeeFolders permissions
|
|
* to the parent directory of a file or dir we have just pathmapped.
|
|
*
|
|
* LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive or Shared)
|
|
*/
|
|
AFPSTATUS
|
|
AfpCheckParentPermissions(
|
|
IN PCONNDESC pConnDesc,
|
|
IN DWORD ParentDirId,
|
|
IN PUNICODE_STRING pParentPath, // path of dir to check
|
|
IN DWORD RequiredPerms, // seefiles,seefolders,makechanges mask
|
|
OUT PFILESYSHANDLE pHandle OPTIONAL, // return open parent handle?
|
|
OUT PBYTE pUserRights OPTIONAL // return user rights?
|
|
)
|
|
{
|
|
NTSTATUS Status = AFP_ERR_NONE;
|
|
FILEDIRPARM FDParm;
|
|
PATHMAPENTITY PME;
|
|
PVOLDESC pVolDesc = pConnDesc->cds_pVolDesc;
|
|
PDFENTRY pDfEntry;
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT(IS_VOLUME_NTFS(pVolDesc) && (ParentDirId != AFP_ID_PARENT_OF_ROOT));
|
|
ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock) ||
|
|
AfpSwmrLockedShared(&pVolDesc->vds_IdDbAccessLock));
|
|
|
|
do
|
|
{
|
|
PME.pme_Handle.fsh_FileHandle = NULL;
|
|
if (ARGUMENT_PRESENT(pHandle))
|
|
{
|
|
pHandle->fsh_FileHandle = NULL;
|
|
}
|
|
ASSERT(ARGUMENT_PRESENT(pParentPath));
|
|
AfpInitializePME(&PME, pParentPath->MaximumLength, pParentPath->Buffer);
|
|
PME.pme_FullPath.Length = pParentPath->Length;
|
|
|
|
if ((pDfEntry = AfpFindDfEntryById(pVolDesc,
|
|
ParentDirId,
|
|
DFE_DIR)) == NULL)
|
|
{
|
|
Status = AFP_ERR_OBJECT_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
ASSERT(DFE_IS_DIRECTORY(pDfEntry));
|
|
AfpInitializeFDParms(&FDParm);
|
|
|
|
Status = afpGetMappedForLookupFDInfo(pConnDesc,
|
|
pDfEntry,
|
|
DIR_BITMAP_ACCESSRIGHTS |
|
|
FD_INTERNAL_BITMAP_OPENACCESS_READCTRL,
|
|
&PME,
|
|
&FDParm);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (PME.pme_Handle.fsh_FileHandle != NULL)
|
|
{
|
|
AfpIoClose(&PME.pme_Handle);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((FDParm._fdp_UserRights & RequiredPerms) != RequiredPerms)
|
|
{
|
|
Status = AFP_ERR_ACCESS_DENIED;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pHandle) && NT_SUCCESS(Status))
|
|
{
|
|
*pHandle = PME.pme_Handle;
|
|
}
|
|
else
|
|
{
|
|
AfpIoClose(&PME.pme_Handle);
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pUserRights))
|
|
{
|
|
*pUserRights = FDParm._fdp_UserRights;
|
|
}
|
|
|
|
} while (False);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*** afpOpenUserHandle
|
|
*
|
|
* Open a handle to data or resource stream of an entity in the user's
|
|
* context. Only called for NTFS volumes.
|
|
*
|
|
* LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Shared)
|
|
*/
|
|
AFPSTATUS
|
|
afpOpenUserHandle(
|
|
IN PCONNDESC pConnDesc,
|
|
IN PDFENTRY pDfEntry,
|
|
IN PUNICODE_STRING pPath OPTIONAL, // path of file/dir to open
|
|
IN DWORD Bitmap, // to extract the Open access mode
|
|
OUT PFILESYSHANDLE pfshData // Handle of data stream of object
|
|
)
|
|
{
|
|
PVOLDESC pVolDesc = pConnDesc->cds_pVolDesc;
|
|
NTSTATUS Status;
|
|
DWORD OpenAccess;
|
|
DWORD DenyMode;
|
|
BOOLEAN isdir, CheckAccess = False, Revert = False;
|
|
WCHAR HostPathBuf[BIG_PATH_LEN];
|
|
UNICODE_STRING uHostPath;
|
|
|
|
PAGED_CODE( );
|
|
|
|
pfshData->fsh_FileHandle = NULL;
|
|
|
|
isdir = (DFE_IS_DIRECTORY(pDfEntry)) ? True : False;
|
|
OpenAccess = AfpMapFDBitmapOpenAccess(Bitmap, isdir);
|
|
|
|
// Extract the index into the AfpDenyModes array from Bitmap
|
|
DenyMode = AfpDenyModes[(Bitmap & FD_INTERNAL_BITMAP_DENYMODE_ALL) >>
|
|
FD_INTERNAL_BITMAP_DENYMODE_SHIFT];
|
|
|
|
do
|
|
{
|
|
if (ARGUMENT_PRESENT(pPath))
|
|
{
|
|
uHostPath = *pPath;
|
|
}
|
|
else
|
|
{
|
|
AfpSetEmptyUnicodeString(&uHostPath,
|
|
sizeof(HostPathBuf),
|
|
HostPathBuf);
|
|
ASSERT ((Bitmap & FD_INTERNAL_BITMAP_OPENFORK_RESC) == 0);
|
|
if (!NT_SUCCESS(AfpHostPathFromDFEntry(pDfEntry,
|
|
0,
|
|
&uHostPath)))
|
|
{
|
|
Status = AFP_ERR_MISC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CheckAccess = False;
|
|
Revert = False;
|
|
// Don't impersonate or check access if this is ADMIN calling
|
|
// or if volume is CDFS. If this handle will be used for setting
|
|
// permissions, impersonate the user token instead. The caller
|
|
// should have determined by now that this chappie has access
|
|
// to change permissions.
|
|
if (Bitmap & FD_INTERNAL_BITMAP_OPENACCESS_RWCTRL)
|
|
{
|
|
Revert = True;
|
|
AfpImpersonateClient(NULL);
|
|
}
|
|
|
|
else if (!(Bitmap & FD_INTERNAL_BITMAP_SKIP_IMPERSONATION) &&
|
|
(pConnDesc->cds_pSda->sda_ClientType != SDA_CLIENT_ADMIN) &&
|
|
IS_VOLUME_NTFS(pVolDesc))
|
|
{
|
|
CheckAccess = True;
|
|
Revert = True;
|
|
AfpImpersonateClient(pConnDesc->cds_pSda);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_AFPINFO, DBG_LEVEL_INFO,
|
|
("afpOpenUserHandle: OpenMode %lx, DenyMode %lx\n",
|
|
OpenAccess, DenyMode));
|
|
|
|
if (Bitmap & FD_INTERNAL_BITMAP_OPENFORK_RESC)
|
|
{
|
|
DWORD crinfo; // was the Resource fork opened or created?
|
|
|
|
ASSERT(IS_VOLUME_NTFS(pVolDesc));
|
|
ASSERT((uHostPath.MaximumLength - uHostPath.Length) >= AfpResourceStream.Length);
|
|
RtlCopyMemory((PBYTE)(uHostPath.Buffer) + uHostPath.Length,
|
|
AfpResourceStream.Buffer,
|
|
AfpResourceStream.Length);
|
|
uHostPath.Length += AfpResourceStream.Length;
|
|
Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
|
|
AFP_STREAM_DATA,
|
|
&uHostPath,
|
|
OpenAccess,
|
|
DenyMode,
|
|
FILEIO_OPEN_FILE,
|
|
FILEIO_CREATE_INTERNAL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
True,
|
|
NULL,
|
|
pfshData,
|
|
&crinfo,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
|
|
AFP_STREAM_DATA,
|
|
isdir ?
|
|
FILEIO_OPEN_DIR : FILEIO_OPEN_FILE,
|
|
&uHostPath,
|
|
OpenAccess,
|
|
DenyMode,
|
|
CheckAccess,
|
|
pfshData);
|
|
}
|
|
|
|
if (Revert)
|
|
AfpRevertBack();
|
|
|
|
if (!ARGUMENT_PRESENT(pPath))
|
|
{
|
|
if ((uHostPath.Buffer != NULL) && (uHostPath.Buffer != HostPathBuf))
|
|
AfpFreeMemory(uHostPath.Buffer);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
|
|
("afpOpenUserHandle: NtOpenFile/NtCreateFile (Open %lx, Deny %lx) %lx\n",
|
|
OpenAccess, DenyMode, Status));
|
|
Status = AfpIoConvertNTStatusToAfpStatus(Status);
|
|
break;
|
|
}
|
|
|
|
} while (False);
|
|
|
|
if (!NT_SUCCESS(Status) && (pfshData->fsh_FileHandle != NULL))
|
|
{
|
|
AfpIoClose(pfshData);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|