NT4/private/ole32/stg/fsstg/stgutil.cxx
2020-09-30 17:12:29 +02:00

763 lines
23 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: stgutil.cxx
//
// Contents: Storage utilities
//
// History: 18-Aug-93 DrewB Created
//
//----------------------------------------------------------------------------
#include "headers.cxx"
#pragma hdrstop
#include <stgutil.hxx>
#include <ntenm.hxx>
#include <iofs.h>
//+---------------------------------------------------------------------------
//
// Function: DetermineHandleStgType, public
//
// Synopsis: Determines the storage type of the given handle
//
// Arguments: [h] - Handle
// [fd] - File/dir type of [h]
// [pdwStgFmt] - Storage type return
//
// Returns: Appropriate status code
//
// Modifies: [pdwStgFmt]
//
// History: 21-Sep-93 DrewB Created
//
//----------------------------------------------------------------------------
SCODE DetermineHandleStgType(HANDLE h,
FILEDIR fd,
DWORD *pdwStgFmt)
{
SCODE sc;
ssDebugOut((DEB_ITRACE, "In DetermineHandleStgType(%p, %d, %p)\n",
h, fd, pdwStgFmt));
sc = S_OK;
switch (fd)
{
case FD_FILE:
sc = DfIsDocfile(h);
if (sc == S_OK)
*pdwStgFmt = STGFMT_DOCUMENT;
else
{
sc = S_OK;
*pdwStgFmt = STGFMT_FILE;
}
break;
case FD_CATALOG:
*pdwStgFmt = STGFMT_CATALOG;
break;
case FD_STORAGE:
*pdwStgFmt = STGFMT_DOCUMENT;
break;
case FD_DIR:
*pdwStgFmt = STGFMT_DIRECTORY;
break;
case FD_JUNCTION:
*pdwStgFmt = STGFMT_JUNCTION;
break;
default:
*pdwStgFmt = STGFMT_DIRECTORY;
}
ssDebugOut((DEB_ITRACE, "Out DetermineHandleStgType => %lX, %d\n",
sc, *pdwStgFmt));
return sc;
}
//+---------------------------------------------------------------------------
//
// Function: DetermineStgType, public
//
// Synopsis: Returns the appropriate STGFMT for the given FS object
// Will also return an open handle so that files don't
// need to be opened twice
//
// Arguments: [hParent] - Parent handle or NULL
// [pwcsName] - Object name or path
// [grfMode] - Access mode
// [pdwStgFmt] - Type return
// [ph] - Handle return or NULL if unneeded
//
// Returns: Appropriate status code
//
// Modifies: [pdwStgFmt]
// [ph]
//
// History: 12-Jul-93 DrewB Created
//
// Notes: BUGBUG - No way to identify summary catalogs
// Waiting on filesystem support
//
//----------------------------------------------------------------------------
SCODE DetermineStgType(HANDLE hParent,
WCHAR const *pwcsName,
DWORD grfMode,
DWORD *pdwStgFmt,
HANDLE *ph)
{
SCODE sc;
SafeNtHandle hChild;
FILEDIR fd;
ssDebugOut((DEB_ITRACE, "In DetermineStgType(%p, %ws, %lX, %p, %p)\n",
hParent, pwcsName, grfMode, pdwStgFmt, ph));
sc = GetFileOrDirHandle(hParent, pwcsName, grfMode, &hChild,NULL,NULL,&fd);
if (SUCCEEDED(sc))
{
sc = DetermineHandleStgType(hChild, fd, pdwStgFmt);
if (SUCCEEDED(sc) && ph != NULL)
hChild.Transfer(ph);
}
ssDebugOut((DEB_ITRACE, "Out DetermineStgType => 0x%lX, %lu, %p\n",
sc, *pdwStgFmt, ph ? *ph : NULL));
return sc;
}
//+---------------------------------------------------------------------------
//
// Function: CheckFsAndOpenAnyStorage, public
//
// Synopsis: Checks the filesystem type and calls the appropriate storage
// open routine
//
// Arguments: [hParent] - Parent handle
// [pwcsName] - Name
// [pstgPriority] - Priority storage
// [grfMode] - Mode
// [snbExclude] - Exclusions
// [fRoot] - TRUE => storage is root storage
// [ppstg] - Storage return
//
// Returns: Appropriate status code
//
// Modifies: [ppstg]
//
// History: 21-Sep-93 DrewB Created
//
//----------------------------------------------------------------------------
SCODE CheckFsAndOpenAnyStorage(HANDLE hParent,
WCHAR const *pwcsName,
IStorage *pstgPriority,
DWORD grfMode,
SNB snbExclude,
BOOL fRoot,
IStorage **ppstg)
{
SCODE sc;
DWORD dwStgFmt;
HANDLE h = NULL;
NTSTATUS nts;
ssDebugOut((DEB_ITRACE, "In CheckFsAndOpenAnyStorage("
"%p, %ws, %p, %lX, %p, %p)\n", hParent, pwcsName,
pstgPriority, grfMode, snbExclude, ppstg));
#ifdef TRANSACT_OLE
const DWORD grfMode2 = grfMode & ~STGM_TRANSACTED;
sc = DetermineStgType(hParent, pwcsName, grfMode2, &dwStgFmt, &h);
#else
sc = DetermineStgType(hParent, pwcsName, grfMode, &dwStgFmt, &h);
#endif
if (SUCCEEDED(sc))
sc = HandleRefersToOfsVolume(h);
if (sc == S_OK)
{
#ifdef TRANSACT_OLE
if (grfMode & STGM_TRANSACTED)
grfMode &= ~STGM_TRANSACTED;
#endif
sc = OfsOpenAnyStorage(hParent, pwcsName, &h, dwStgFmt, pstgPriority,
grfMode, snbExclude, fRoot, ppstg);
}
else if (sc == S_FALSE)
{
sc = OpenAnyStorage(hParent, pwcsName, &h, dwStgFmt, pstgPriority,
grfMode, snbExclude, ppstg);
}
// In the success case, h is passed to and owned by the storage object
// and the last Release() is responsible for closing h
// In the failure case, we close the handle now
//
if (!SUCCEEDED(sc) && h != NULL)
{
if (!NT_SUCCESS(nts = NtClose(h)))
ssDebugOut((DEB_ITRACE,
"CheckFsAndOpenAnyStorage NtClose(%lx)\n",nts));
}
ssDebugOut((DEB_ITRACE, "Out CheckFsAndOpenAnyStorage => 0x%lX, %p\n",
sc, *ppstg));
return sc;
}
SAFE_HEAP_PTR(SafeFrni, FILE_RENAME_INFORMATION);
//+---------------------------------------------------------------------------
//
// Function: SetupRename, public
//
// Synopsis: Allocates and initializes a FILE_RENAME_INFORMATION struct
//
// Arguments: [h] - Value for RootDirectory field.
// [pwcsNewName] -- String for FileName field.
// [pcbFni] -- pointer to buffer for size of structure in bytes.
// [ppfni] -- pointer to buffer for pointer to struct.
//
// Returns: STG_E_INSUFFICIENT_MEMORY or S_OK.
//
// History: 15-May-94 BillMo Created
//
//----------------------------------------------------------------------------
SCODE
SetupRename(HANDLE h,
const WCHAR *pwcsNewName,
ULONG *pcbFni,
FILE_RENAME_INFORMATION ** ppfni)
{
SCODE sc = S_OK;
ULONG cbNewName = lstrlenW(pwcsNewName)*sizeof(WCHAR);
*pcbFni = sizeof(FILE_RENAME_INFORMATION)+cbNewName;
*ppfni = (FILE_RENAME_INFORMATION *)new BYTE[*pcbFni];
olMem((FILE_RENAME_INFORMATION *)*ppfni);
(*ppfni)->ReplaceIfExists = FALSE;
(*ppfni)->RootDirectory = h;
(*ppfni)->FileNameLength = cbNewName;
memcpy((*ppfni)->FileName, pwcsNewName, cbNewName);
EH_Err:
return(sc);
}
//+---------------------------------------------------------------------------
//
// Function: GenericMoveElement, public
//
// Synopsis: Performs MoveElement between any two IStorages
//
// Arguments: [pstgFrom] - Source
// [pwcsName] - Source element
// [pstgTo] - Destination
// [pwcsNewName] - Destination element
// [grfFlags] - Flags
//
// Returns: Appropriate status code
//
// History: 15-Jul-93 DrewB Adapted from docfile code
//
//----------------------------------------------------------------------------
SCODE GenericMoveElement(IStorage *pstgFrom,
WCHAR const *pwcsName,
IStorage *pstgTo,
WCHAR const *pwcsNewName,
DWORD grfFlags)
{
SafeIStorage pstgFromElement;
STATSTG stat, statTmp;
SCODE sc;
ssDebugOut((DEB_ITRACE, "In GenericMoveElement(%p, %ws, %p, %ws, %lX)\n",
pstgFrom, pwcsName, pstgTo, pwcsNewName, grfFlags));
// BUGBUG - Security?
if (grfFlags == STGMOVE_MOVE)
{
//
// Try to use native file system move if supported.
//
SafeINativeFileSystem pnfsFrom;
SafeINativeFileSystem pnfsTo;
if (SUCCEEDED(pstgFrom->QueryInterface(IID_INativeFileSystem,
(VOID**) &pnfsFrom)) &&
SUCCEEDED(pstgTo->QueryInterface(IID_INativeFileSystem,
(VOID**) &pnfsTo)))
{
//
// Both storages support INativeFileSystem so we can
// can try same volume move.
//
// Design Note:
// We allow embeddings to be moved into/out of documents:
// there is no enforcement of the semantics provided by
// docfiles where an embedding must be moved into another
// docfile.
//
NTSTATUS nts;
HANDLE hFrom;
HANDLE hTo;
SafeNtHandle hToMove;
ULONG cbFni;
IO_STATUS_BLOCK iosb;
SafeFrni pfni;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa;
ssChk(pnfsFrom->GetHandle(&hFrom));
ssChk(pnfsTo->GetHandle(&hTo));
us.Length = lstrlenW(pwcsName)*sizeof(WCHAR);
us.MaximumLength = us.Length+sizeof(WCHAR);
us.Buffer = (PWSTR)pwcsName;
InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE, hFrom, NULL);
//
// We open hFrom::pwcsName giving hToMove (the object to move)
//
nts = NtOpenFile(&hToMove,
(ACCESS_MASK)DELETE | SYNCHRONIZE,
&oa,
&iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(nts))
{
sc = NtStatusToScode(nts);
goto EH_Err;
}
//
// We setup hTo::pwcsNewName as destination.
//
ssChk(SetupRename(hTo, pwcsNewName, &cbFni, &pfni));
//
// We move the object hToMove.
//
nts = NtSetInformationFile(hToMove,
&iosb,
pfni,
cbFni,
FileRenameInformation);
if (NT_SUCCESS(nts))
{
sc = S_OK;
goto EH_Ret;
}
if (nts != STATUS_NOT_SAME_DEVICE && nts != STATUS_ACCESS_DENIED)
{
//
// STATUS_OBJECT_NAME_COLLISION -> STG_E_FILEALREADYEXISTS
//
// not successful, and not because of different device.
//
// OFS has problems moving streams and returns
// STATUS_ACCESS_DENIED -- try again
//
// Nt file systems return OBJECT_NAME_COLLISION whenever renaming
// an object on top of an existing one. DocFile returns ACCESS_DENIED.
//
if (nts == STATUS_OBJECT_NAME_COLLISION)
sc = STG_E_ACCESSDENIED;
else
sc = NtStatusToScode(nts);
goto EH_Err;
}
}
}
// Determine source type
sc = GetScode(pstgFrom->OpenStorage(pwcsName, NULL,
STGM_DIRECT | STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
NULL, NULL, &pstgFromElement));
if (SUCCEEDED(sc))
{
SafeIStorage pstgToElement;
// It\'s a storage
ssHChk(pstgFromElement->Stat(&stat, STATFLAG_NONAME));
sc = GetScode(pstgTo->CreateStorage(pwcsNewName,
STGM_DIRECT | STGM_WRITE |
STGM_SHARE_EXCLUSIVE |
STGM_FAILIFTHERE,
stat.STATSTG_dwStgFmt,
0,
&pstgToElement));
if (sc == STG_E_FILEALREADYEXISTS &&
grfFlags == STGMOVE_COPY)
{
sc = GetScode(pstgTo->OpenStorage(pwcsNewName, NULL,
STGM_DIRECT | STGM_WRITE |
STGM_SHARE_EXCLUSIVE, NULL,
0, &pstgToElement));
if (SUCCEEDED(sc))
{
sc = GetScode(pstgTo->Stat(&statTmp, STATFLAG_NONAME));
if ( FAILED(sc) ||
statTmp.STATSTG_dwStgFmt != stat.STATSTG_dwStgFmt )
{
sc = STG_E_INVALIDFUNCTION;
}
}
}
ssChk(sc);
if (grfFlags == STGMOVE_MOVE)
{
sc = GetScode(pstgFromElement->CopyTo(1,
&IID_IEnableObjectIdCopy,
NULL,
pstgToElement));
}
else
{
sc = GetScode(pstgFromElement->CopyTo(0, NULL, NULL,
pstgToElement));
}
// Close the source since it may be deleted later
// and we have exclusive access to it right now
IStorage *pstgFromFree;
pstgFromElement.Transfer(&pstgFromFree);
pstgFromFree->Release();
}
else // if (sc == STG_E_FILENOTFOUND)
{
SafeIStream pstmFromElement, pstmToElement;
// Try opening it as a stream
ssChk(pstgFrom->OpenStream(pwcsName, NULL,
STGM_DIRECT | STGM_READ |
STGM_SHARE_EXCLUSIVE,
NULL, &pstmFromElement));
// It\'s a stream
ssHChk(pstmFromElement->Stat(&stat, STATFLAG_NONAME));
ssHChk(pstgTo->CreateStream(pwcsNewName,
STGM_DIRECT | STGM_WRITE |
STGM_SHARE_EXCLUSIVE |
(grfFlags == STGMOVE_MOVE ?
STGM_FAILIFTHERE :
STGM_CREATE),
0, 0, &pstmToElement));
ULARGE_INTEGER cb = {0xffffffff, 0xffffffff};
sc = GetScode(pstmFromElement->CopyTo(pstmToElement,
cb, NULL, NULL));
}
//else
// ssChk(sc);
if (SUCCEEDED(sc))
{
// Make destination create time match source create time
// Note that we don't really care if this call succeeded.
pstgTo->SetElementTimes(pwcsNewName, &stat.ctime,
NULL, NULL);
if ((grfFlags & STGMOVE_COPY) == STGMOVE_MOVE)
olVerify(SUCCEEDED(pstgFrom->DestroyElement(pwcsName)));
}
else
{
// The copy/move failed, so get rid of the partial result.
pstgTo->DestroyElement(pwcsNewName);
}
ssDebugOut((DEB_ITRACE, "Out GenericMoveElement\n"));
EH_Ret:
EH_Err:
return ssResult(sc);
}
//+---------------------------------------------------------------------------
//
// Function: FindExt, public
//
// Synopsis: Finds the extension for a path
//
// Arguments: [pwcsPath] - Path
//
// Returns: Pointer to extension or NULL
//
// History: 27-Jul-93 DrewB Created from file moniker code
//
//----------------------------------------------------------------------------
WCHAR *FindExt(WCHAR const *pwcsPath)
{
WCHAR const *pwcs = pwcsPath;
ssAssert(pwcs != NULL);
// Move to end of string
pwcs += lstrlenW(pwcs);
ssAssert(*pwcs == 0);
pwcs--;
while (*pwcs != L'.' && *pwcs != L'\\' && *pwcs != L'/' &&
*pwcs != L'!' && pwcs > pwcsPath)
pwcs--;
return *pwcs == L'.' ? (WCHAR *)pwcs : NULL;
}
//+---------------------------------------------------------------------------
//
// Function: DestroyTree, public
//
// Synopsis: Destroys an element and any children
//
// Arguments: [hParent] - Parent handle or NULL
// [pwcsName] - Child name or NULL
// [h] - Handle or NULL if [hParent] and [pwcsName]
// should be used
// [fd] - File/dir status of [h] if given
//
// Returns: Appropriate status code
//
// History: 20-Oct-93 DrewB Created
//
// Notes: Attempts as complete a deletion as possible, returning
// the last error encountered
//
//----------------------------------------------------------------------------
// Avoid Win32 macro problems
#undef DeleteFile
SCODE DestroyTree(HANDLE hParent,
WCHAR const *pwcsName,
HANDLE h,
FILEDIR fd)
{
SCODE sc;
SafeNtHandle hSafe;
IO_STATUS_BLOCK iosb;
NTSTATUS nts;
FILE_DISPOSITION_INFORMATION fdi;
BOOL bOfsHandle;
WCHAR *pwcsNewName = NULL;
ssDebugOut((DEB_ITRACE, "In DestroyTree(%p, %ws, %p, %d)\n",
hParent, pwcsName, h, fd));
if (h == NULL)
{
#ifdef TRANSACT_OLE
// STGM_CREATE is used to set the DELETE access on the handle
if (fd == FD_STREAM) // stream names need a ':' in front
{
ssMem(pwcsNewName = MakeStreamName(pwcsName));
ssChk(GetNtHandle (hParent, pwcsNewName, STGM_CREATE |
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0,CO_OPEN,FD_STREAM,NULL, &hSafe));
}
else
#endif
ssChk(GetFileOrDirHandle(hParent, pwcsName,
STGM_READWRITE | STGM_CREATE,
&hSafe, NULL, NULL, &fd));
h = hSafe;
}
bOfsHandle = HandleRefersToOfsVolume(h) == S_OK ? TRUE : FALSE;
sc = S_OK;
// Destroy all children in directories
if (fd == FD_DIR || fd == FD_STORAGE)
{
int cNameSpaces = (fd == FD_DIR ? 2 : 1);
while (cNameSpaces--)
{
// skip the OLE namespace for non-OFS file handles
if (cNameSpaces == 0 && !bOfsHandle)
break;
CNtEnum nte(cNameSpaces == 0);
if (SUCCEEDED(sc = nte.InitFromHandle(h, TRUE)))
{
STATSTG stat;
WCHAR awcName[_MAX_PATH];
FILEDIR fdChild;
for (;;)
{
sc = nte.Next(&stat, awcName, NTE_BUFFERNAME, &fdChild);
if (FAILED(sc) || sc == S_FALSE)
{
if (sc == S_FALSE)
sc = S_OK;
break;
}
ssAssert((lstrcmpW(awcName, L".") != 0 && lstrcmpW(awcName, L"..") != 0));
sc = DestroyTree(h, awcName, NULL, fdChild);
}
}
}
}
fdi.DeleteFile = TRUE;
nts = NtSetInformationFile(h, &iosb, &fdi,
sizeof(FILE_DISPOSITION_INFORMATION),
FileDispositionInformation);
if (!NT_SUCCESS(nts))
sc = NtStatusToScode(nts);
ssDebugOut((DEB_ITRACE, "Out DestroyTree => %lX\n", sc));
EH_Err:
if (pwcsNewName) delete pwcsNewName;
return sc;
}
//+---------------------------------------------------------------------------
//
// Function: RenameChild, public
//
// Synopsis: Renames a file or directory
//
// Arguments: [hParent] - Parent handle
// [pwcsName] - Name
// [pwcsNewName] - New name
//
// Returns: Appropriate status code
//
// History: 21-Oct-93 DrewB Created
//
//----------------------------------------------------------------------------
SCODE RenameChild(HANDLE hParent,
WCHAR const *pwcsName,
WCHAR const *pwcsNewName)
{
SCODE sc;
SafeNtHandle h;
IO_STATUS_BLOCK iosb;
NTSTATUS nts;
ULONG cbFni;
SafeFrni pfni;
ssDebugOut((DEB_ITRACE, "In RenameChild(%p, %ws, %ws)\n",
hParent, pwcsName, pwcsNewName));
ssChk(SetupRename(NULL, pwcsNewName, &cbFni, &pfni));
// Renames require DELETE access on the handle
ssChk(GetFileOrDirHandle(hParent, pwcsName, STGM_WRITE | STGM_CREATE,
&h, NULL, NULL, NULL));
nts = NtSetInformationFile(h, &iosb, pfni, cbFni, FileRenameInformation);
if (!NT_SUCCESS(nts))
sc = NtStatusToScode(nts);
else
sc = S_OK;
ssDebugOut((DEB_ITRACE, "Out RenameChild\n"));
EH_Err:
return sc;
}
//+---------------------------------------------------------------------------
//
// Function: SetDriveLetter
//
// Synopsis: inserts drive letter into a pathname
//
// Arguments: [pwcsName] - path name with missing drive letter
// [wcDrive] - drive letter or L'\\' for a share
//
// Returns: Appropriate status code
//
// History: 24-Mar-95 HenryLee Created
//
//----------------------------------------------------------------------------
SCODE SetDriveLetter (WCHAR *pwcsName, WCHAR const wcDrive)
{
SCODE sc = S_FALSE;
if (pwcsName != NULL)
{
if (IsCharAlphaW(wcDrive))
{
for (int i=lstrlenW(pwcsName); i >= 0; i--)
pwcsName[i+2] = pwcsName[i];
pwcsName[1] = L':';
pwcsName[0] = wcDrive;
sc = S_OK;
}
else if (wcDrive == L'\\')
{
for (int i=lstrlenW(pwcsName); i >= 0; i--)
pwcsName[i+1] = pwcsName[i];
pwcsName[0] = L'\\';
sc = S_OK;
}
}
return sc;
}
//+---------------------------------------------------------------------------
//
// Function: GetDriveLetter
//
// Synopsis: extracts a drive letter from pathname
//
// Arguments: [pwcsName] - path name with drive letter
//
// Returns: [wcDrive] - drive letter or L'\\' for a share
//
// History: 24-Mar-95 HenryLee Created
//
//----------------------------------------------------------------------------
WCHAR GetDriveLetter (WCHAR const *pwcsName)
{
WCHAR wcDrive = L'\0';
if (pwcsName != NULL)
{
if (pwcsName[0] != NULL && (pwcsName[1] == L':' ||
pwcsName[0] == L'\\' && pwcsName[1] == L'\\' ))
wcDrive = pwcsName[0]; // extract drive from pathname
else
{
WCHAR wcsPath[MAX_PATH]; // no drive letter in pathname
NTSTATUS nts = // get current drive instead
RtlGetCurrentDirectory_U (MAX_PATH*sizeof(WCHAR),wcsPath);
if (NT_SUCCESS(nts))
wcDrive = wcsPath[0];
}
}
return (wcDrive);
};