729 lines
17 KiB
C
729 lines
17 KiB
C
|
|
/* lfn.c -
|
|
*
|
|
* This file contains code that combines winnet long filename API's and
|
|
* the DOS INT 21h API's into a single interface. Thus, other parts of
|
|
* Winfile call a single piece of code with no worries about the
|
|
* underlying interface.
|
|
*/
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include "winfile.h"
|
|
#include "object.h"
|
|
#include "lfn.h" // lfn includes
|
|
#include "dosfunc.h"
|
|
#include "winnet.h"
|
|
#include "wnetcaps.h" // WNetGetCaps()
|
|
#include "wfcopy.h"
|
|
|
|
BOOL APIENTRY IsFATName(LPSTR pName);
|
|
|
|
#define CDRIVEMAX 26
|
|
|
|
#define BUFSIZE 2048 // ff/fn buffer size
|
|
|
|
#define MAXFILES 1024
|
|
|
|
/* this is the internal buffer maintained for ff/fn operations
|
|
*/
|
|
typedef struct _find {
|
|
HANDLE hDir; // search handle
|
|
WORD cbBuffer; // buffer size
|
|
WORD nEntriesLeft; // remaining entries
|
|
WORD ibEntry; // offset of next entry to return
|
|
FILEFINDBUF2 rgFindBuf[1]; // array of find entries
|
|
} FIND, * LPFIND;
|
|
|
|
|
|
/* this structure contains an array of drives types (ie, unknown, FAT, LFN)
|
|
* and a pointer to each of the driver functions. It is declared this way
|
|
* in order to get function prototypes.
|
|
*/
|
|
typedef struct _lfninfo {
|
|
UINT hDriver;
|
|
INT rgVolType[CDRIVEMAX];
|
|
FARPROC lpfnQueryAbort;
|
|
WORD ( APIENTRY *lpFindFirst)(LPSTR,WORD,LPINT,LPINT,WORD,PFILEFINDBUF2);
|
|
WORD ( APIENTRY *lpFindNext)(HANDLE,LPINT,WORD,PFILEFINDBUF2);
|
|
WORD ( APIENTRY *lpFindClose)(HANDLE);
|
|
WORD ( APIENTRY *lpGetAttribute)(LPSTR,LPINT);
|
|
WORD ( APIENTRY *lpSetAttribute)(LPSTR,WORD);
|
|
WORD ( APIENTRY *lpCopy)(LPSTR,LPSTR,PQUERYPROC);
|
|
WORD ( APIENTRY *lpMove)(LPSTR,LPSTR);
|
|
WORD ( APIENTRY *lpDelete)(LPSTR);
|
|
WORD ( APIENTRY *lpMKDir)(LPSTR);
|
|
WORD ( APIENTRY *lpRMDir)(LPSTR);
|
|
WORD ( APIENTRY *lpGetVolumeLabel)(WORD,LPSTR);
|
|
WORD ( APIENTRY *lpSetVolumeLabel)(WORD,LPSTR);
|
|
WORD ( APIENTRY *lpParse)(LPSTR,LPSTR,LPSTR);
|
|
WORD ( APIENTRY *lpVolumeType)(WORD,LPINT);
|
|
} LFNINFO, * PLFNINFO;
|
|
|
|
/* pointer to lfn information, so we don't take up a lot of space on a
|
|
* nonlfn system
|
|
*/
|
|
PLFNINFO pLFN = NULL;
|
|
|
|
|
|
VOID HandleSymbolicLink(HANDLE DirectoryHandle, PCHAR ObjectName);
|
|
|
|
|
|
/* WFFindFirst -
|
|
*
|
|
* returns:
|
|
* TRUE for success - lpFind->fd,hFindFileset,attrFilter set.
|
|
* FALSE for failure
|
|
*
|
|
* Performs the FindFirst operation and the first WFFindNext.
|
|
*/
|
|
|
|
BOOL
|
|
APIENTRY
|
|
WFFindFirst(
|
|
LPLFNDTA lpFind,
|
|
LPSTR lpName,
|
|
DWORD dwAttrFilter
|
|
)
|
|
{
|
|
// We OR these additional bits because of the way DosFindFirst works
|
|
// in Windows. It returns the files that are specified by the attrfilter
|
|
// and ORDINARY files too.
|
|
|
|
#define BUFFERSIZE 1024
|
|
|
|
|
|
|
|
#define Error(N,S) { \
|
|
DbgPrint(#N); \
|
|
DbgPrint(" Error %08lX\n", S); \
|
|
}
|
|
|
|
CHAR Buffer[BUFFERSIZE];
|
|
NTSTATUS Status;
|
|
HANDLE DirectoryHandle;
|
|
ULONG Context = 0;
|
|
ULONG ReturnedLength;
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|
POBJECT_NAME_INFORMATION NameInfo;
|
|
INT length;
|
|
|
|
lpFind->hFindFile = INVALID_HANDLE_VALUE;
|
|
//DbgPrint("Find first : <%s>\n", lpName);
|
|
|
|
// Remove drive letter
|
|
while ((*lpName != 0) && (*lpName != '\\')) {
|
|
lpName ++;
|
|
}
|
|
strcpy(Buffer, lpName);
|
|
length = strlen(Buffer);
|
|
length -= 4; // Remove '\'*.*
|
|
if (length == 0) {
|
|
length = 1; // Replace the '\'
|
|
}
|
|
Buffer[length] = 0; // Truncate the string at the appropriate point
|
|
|
|
//DbgPrint("Find first modified : <%s>\n\r", Buffer);
|
|
|
|
|
|
#define NEW
|
|
#ifdef NEW
|
|
|
|
|
|
//
|
|
// Open the directory for list directory access
|
|
//
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
ANSI_STRING DirectoryName;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
|
|
RtlInitAnsiString(&DirectoryName, Buffer);
|
|
Status = RtlAnsiStringToUnicodeString( &UnicodeString,
|
|
&DirectoryName,
|
|
TRUE );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
InitializeObjectAttributes( &Attributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
if (!NT_SUCCESS( Status = NtOpenDirectoryObject( &DirectoryHandle,
|
|
STANDARD_RIGHTS_READ |
|
|
DIRECTORY_QUERY |
|
|
DIRECTORY_TRAVERSE,
|
|
&Attributes ) )) {
|
|
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
if (Status == STATUS_OBJECT_TYPE_MISMATCH) {
|
|
DbgPrint("%Z is not a valid Object Directory Object name\n",
|
|
&DirectoryName );
|
|
} else {
|
|
DbgPrint("%Z - ", &DirectoryName );
|
|
Error( OpenDirectory, Status );
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
}
|
|
|
|
Status = NtQueryDirectoryObject( DirectoryHandle,
|
|
Buffer,
|
|
BUFFERSIZE,
|
|
TRUE,
|
|
TRUE,
|
|
&Context,
|
|
&ReturnedLength );
|
|
if (!NT_SUCCESS( Status )) {
|
|
Error(Find_First_QueryDirectory, Status);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// For every record in the buffer type out the directory information
|
|
//
|
|
|
|
//
|
|
// Point to the first record in the buffer, we are guaranteed to have
|
|
// one otherwise Status would have been No More Files
|
|
//
|
|
|
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
|
|
|
|
//
|
|
// Check if there is another record. If there isn't, then get out
|
|
// of the loop now
|
|
//
|
|
|
|
if (DirInfo->Name.Length == 0) {
|
|
DbgPrint("FindFirst - name length = 0\n\r");
|
|
return (FALSE);
|
|
}
|
|
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
AnsiString.Buffer = lpFind->fd.cFileName;
|
|
AnsiString.MaximumLength = sizeof(lpFind->fd.cFileName);
|
|
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiString, &(DirInfo->Name), FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
//DbgPrint("FindFirst returning <%s>\n\r", lpFind->fd.cFileName);
|
|
|
|
// Calculate the attribute field
|
|
|
|
lpFind->fd.dwFileAttributes = CalcAttributes(&DirInfo->TypeName);
|
|
|
|
#ifdef LATER
|
|
if (lpFind->fd.dwFileAttributes == ATTR_SYMLINK) {
|
|
HandleSymbolicLink(DirectoryHandle, lpFind->fd.cFileName);
|
|
}
|
|
|
|
// Label an unknown object type
|
|
if (lpFind->fd.dwFileAttributes == 0) { // Unknown type
|
|
strncat(lpFind->fd.cFileName, " (", MAX_PATH - strlen(lpFind->fd.cFileName));
|
|
strncat(lpFind->fd.cFileName, DirInfo->TypeName.Buffer,
|
|
MAX_PATH - strlen(lpFind->fd.cFileName));
|
|
strncat(lpFind->fd.cFileName, ")", MAX_PATH - strlen(lpFind->fd.cFileName));
|
|
}
|
|
#endif
|
|
|
|
// Save our search context
|
|
|
|
lpFind->hFindFile = DirectoryHandle;
|
|
lpFind->err = Context;
|
|
|
|
return (TRUE);
|
|
|
|
#else
|
|
dwAttrFilter |= ATTR_ARCHIVE | ATTR_READONLY | ATTR_NORMAL;
|
|
lpFind->hFindFile = FindFirstFile(lpName, &lpFind->fd);
|
|
if (lpFind->hFindFile != (HANDLE)0xFFFFFFFF) {
|
|
lpFind->dwAttrFilter = dwAttrFilter;
|
|
if ((~dwAttrFilter & lpFind->fd.dwFileAttributes) == 0L ||
|
|
WFFindNext(lpFind)) {
|
|
PRINT(BF_PARMTRACE, "WFFindFirst:%s", &lpFind->fd.cFileName);
|
|
return (TRUE);
|
|
} else {
|
|
lpFind->err = GetLastError();
|
|
WFFindClose(lpFind);
|
|
return (FALSE);
|
|
}
|
|
} else {
|
|
return (FALSE);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* WFFindNext -
|
|
*
|
|
* Performs a single file FindNext operation. Only returns TRUE if a
|
|
* file matching the dwAttrFilter is found. On failure WFFindClose is
|
|
* called.
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
WFFindNext(
|
|
LPLFNDTA lpFind
|
|
)
|
|
{
|
|
CHAR Buffer[BUFFERSIZE];
|
|
NTSTATUS Status;
|
|
HANDLE DirectoryHandle = lpFind->hFindFile;
|
|
ULONG Context = lpFind->err;
|
|
ULONG ReturnedLength;
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|
POBJECT_NAME_INFORMATION NameInfo;
|
|
|
|
#ifdef NEW
|
|
|
|
//ASSERT(lpFind->hFindFile != (HANDLE)0xFFFFFFFF);
|
|
|
|
Status = NtQueryDirectoryObject( DirectoryHandle,
|
|
Buffer,
|
|
BUFFERSIZE,
|
|
TRUE,
|
|
FALSE,
|
|
&Context,
|
|
&ReturnedLength );
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (Status != STATUS_NO_MORE_ENTRIES) {
|
|
Error(FindNext_QueryDirectory, Status);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// For every record in the buffer type out the directory information
|
|
//
|
|
|
|
//
|
|
// Point to the first record in the buffer, we are guaranteed to have
|
|
// one otherwise Status would have been No More Files
|
|
//
|
|
|
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
|
|
|
|
//
|
|
// Check if there is another record. If there isn't, then get out
|
|
// of the loop now
|
|
//
|
|
|
|
if (DirInfo->Name.Length == 0) {
|
|
DbgPrint("FindNext - name length = 0\n\r");
|
|
return (FALSE);
|
|
}
|
|
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
AnsiString.Buffer = lpFind->fd.cFileName;
|
|
AnsiString.MaximumLength = sizeof(lpFind->fd.cFileName);
|
|
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiString, &(DirInfo->Name), FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
//DbgPrint("FindNext returning <%s>\n\r", lpFind->fd.cFileName);
|
|
|
|
// Calculate the attribute field
|
|
|
|
lpFind->fd.dwFileAttributes = CalcAttributes(&DirInfo->TypeName);
|
|
|
|
#ifdef LATER
|
|
if (lpFind->fd.dwFileAttributes == ATTR_SYMLINK) {
|
|
HandleSymbolicLink(DirectoryHandle, lpFind->fd.cFileName);
|
|
}
|
|
|
|
// Label an unknown object type
|
|
if (lpFind->fd.dwFileAttributes == 0) { // Unknown type
|
|
strncat(lpFind->fd.cFileName, " (", MAX_PATH - strlen(lpFind->fd.cFileName));
|
|
strncat(lpFind->fd.cFileName, DirInfo->TypeName.Buffer,
|
|
MAX_PATH - strlen(lpFind->fd.cFileName));
|
|
strncat(lpFind->fd.cFileName, ")", MAX_PATH - strlen(lpFind->fd.cFileName));
|
|
}
|
|
#endif
|
|
|
|
// Save our search context
|
|
|
|
lpFind->err = Context;
|
|
|
|
return (TRUE);
|
|
|
|
#else
|
|
#ifdef DBG
|
|
if (lpFind->hFindFile == (HANDLE)0xFFFFFFFF) {
|
|
DebugBreak();
|
|
return (FALSE);
|
|
}
|
|
#endif
|
|
while (FindNextFile(lpFind->hFindFile, &lpFind->fd)) {
|
|
if ((lpFind->fd.dwFileAttributes & ~lpFind->dwAttrFilter) != 0)
|
|
continue; // only pick files that fit attr filter
|
|
PRINT(BF_PARMTRACE, "WFFindNext:%s", &lpFind->fd.cFileName);
|
|
return (TRUE);
|
|
}
|
|
lpFind->err = GetLastError();
|
|
return (FALSE);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
/* WFFindClose -
|
|
*
|
|
* performs the find close operation
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
WFFindClose(
|
|
LPLFNDTA lpFind
|
|
)
|
|
{
|
|
HANDLE DirectoryHandle = lpFind->hFindFile;
|
|
BOOL bRet;
|
|
|
|
#ifdef NEW
|
|
if (lpFind->hFindFile != INVALID_HANDLE_VALUE) {
|
|
(VOID) NtClose( DirectoryHandle );
|
|
lpFind->hFindFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return (TRUE);
|
|
|
|
#else
|
|
ENTER("WFFindClose");
|
|
// ASSERT(lpFind->hFindFile != (HANDLE)0xFFFFFFFF);
|
|
#ifdef DBG
|
|
if (lpFind->hFindFile == (HANDLE)0xFFFFFFFF) {
|
|
PRINT(BF_PARMTRACE, "WFFindClose:Invalid hFindFile = 0xFFFFFFFF","");
|
|
return (FALSE);
|
|
}
|
|
#endif
|
|
|
|
bRet = FindClose(lpFind->hFindFile);
|
|
#ifdef DBG
|
|
lpFind->hFindFile = (HANDLE)0xFFFFFFFF;
|
|
#endif
|
|
|
|
LEAVE("WFFindClose");
|
|
return (bRet);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
HandleSymbolicLink(
|
|
HANDLE DirectoryHandle,
|
|
PCHAR ObjectName
|
|
) // Assumes this points at a MAX_PATH length buffer
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Object_Attributes;
|
|
HANDLE LinkHandle;
|
|
STRING String;
|
|
WCHAR UnicodeBuffer[MAX_PATH];
|
|
UNICODE_STRING UnicodeString;
|
|
INT Length;
|
|
|
|
RtlInitString(&String, ObjectName);
|
|
Status = RtlAnsiStringToUnicodeString( &UnicodeString,
|
|
&String,
|
|
TRUE );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
InitializeObjectAttributes(&Object_Attributes,
|
|
&UnicodeString,
|
|
0,
|
|
DirectoryHandle,
|
|
NULL
|
|
);
|
|
|
|
// Open the given symbolic link object
|
|
Status = NtOpenSymbolicLinkObject(&LinkHandle,
|
|
GENERIC_ALL,
|
|
&Object_Attributes);
|
|
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("HandleSymbolicLink : open symbolic link failed, status = %lx\n\r", Status);
|
|
return;
|
|
}
|
|
|
|
|
|
strcat(ObjectName, " => ");
|
|
Length = strlen(ObjectName);
|
|
|
|
// Set up our String variable to point at the remains of the object name buffer
|
|
String.Length = 0;
|
|
String.MaximumLength = (USHORT)(MAX_PATH - Length);
|
|
String.Buffer = &(ObjectName[Length]);
|
|
|
|
// Go get the target of the symbolic link
|
|
UnicodeString.Buffer = UnicodeBuffer;
|
|
UnicodeString.MaximumLength = sizeof(UnicodeBuffer);
|
|
|
|
Status = NtQuerySymbolicLinkObject(LinkHandle, &UnicodeString, NULL);
|
|
|
|
NtClose(LinkHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("HandleSymbolicLink : query symbolic link failed, status = %lx\n\r", Status);
|
|
return;
|
|
}
|
|
|
|
// Copy the symbolic target into return buffer
|
|
Status = RtlUnicodeStringToAnsiString(&String, &UnicodeString, FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
// Add NULL terminator
|
|
String.Buffer[String.Length] = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* WFIsDir
|
|
*
|
|
* Determines if the specified path is a directory
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
WFIsDir(
|
|
LPSTR lpDir
|
|
)
|
|
{
|
|
DWORD attr = GetFileAttributes(lpDir);
|
|
|
|
if (attr & 0x8000) // BUG: what is this constant???
|
|
return FALSE;
|
|
|
|
if (attr & ATTR_DIR)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* LFNQueryAbort -
|
|
*
|
|
* wraps around WFQueryAbort and is exported/makeprocinstanced
|
|
*/
|
|
|
|
BOOL
|
|
APIENTRY
|
|
LFNQueryAbort(
|
|
VOID
|
|
)
|
|
{
|
|
return WFQueryAbort();
|
|
}
|
|
|
|
/* LFNInit -
|
|
*
|
|
* Initializes stuff for LFN access
|
|
*/
|
|
|
|
VOID
|
|
APIENTRY
|
|
LFNInit()
|
|
{
|
|
INT i;
|
|
|
|
/* find out if long names are supported.
|
|
*/
|
|
if (!(WNetGetCaps(WNNC_ADMIN) & WNNC_ADM_LONGNAMES))
|
|
return;
|
|
|
|
/* get the buffer
|
|
*/
|
|
pLFN = (PLFNINFO)LocalAlloc(LPTR,sizeof(LFNINFO));
|
|
if (!pLFN)
|
|
return;
|
|
|
|
/* get the handle to the driver
|
|
*/
|
|
if (!(pLFN->hDriver = WNetGetCaps((WORD)0xFFFF))) {
|
|
LocalFree((HANDLE)pLFN);
|
|
pLFN = NULL;
|
|
return;
|
|
}
|
|
|
|
/* set all the volume types to unknown
|
|
*/
|
|
for (i = 0; i < CDRIVEMAX; i++) {
|
|
pLFN->rgVolType[i] = -1;
|
|
}
|
|
}
|
|
|
|
/* GetNameType -
|
|
*
|
|
* Shell around LFNParse. Classifies name.
|
|
*
|
|
* NOTE: this should work on unqualified names. currently this isn't
|
|
* very useful.
|
|
*/
|
|
WORD
|
|
APIENTRY
|
|
GetNameType(
|
|
LPSTR lpName
|
|
)
|
|
{
|
|
if (*(lpName+1) == ':') {
|
|
if (!IsLFNDrive(lpName))
|
|
return FILE_83_CI;
|
|
} else if (IsFATName(lpName))
|
|
return FILE_83_CI;
|
|
|
|
return (FILE_LONG);
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
IsFATName(
|
|
LPSTR pName
|
|
)
|
|
{
|
|
INT cdots = 0;
|
|
INT cb;
|
|
INT i;
|
|
INT iFirstDot;
|
|
|
|
|
|
cb = lstrlen(pName);
|
|
if (cb > 12) {
|
|
return FALSE;
|
|
} else {
|
|
for (i = 0; i < cb; i++) {
|
|
if (pName[i] == '.') {
|
|
iFirstDot = cdots ? iFirstDot : i;
|
|
cdots++;
|
|
}
|
|
}
|
|
|
|
if (cdots == 0 && cb <= 8)
|
|
return TRUE;
|
|
else if (cdots != 1)
|
|
return FALSE;
|
|
else if (cdots == 1 && iFirstDot > 8)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
IsLFN(
|
|
LPSTR pName
|
|
)
|
|
{
|
|
return !IsFATName(pName);
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
LFNMergePath(
|
|
LPSTR pTo,
|
|
LPSTR pFrom
|
|
)
|
|
{
|
|
PRINT(BF_PARMTRACE, "LFNMergePath:basically a NOP", "");
|
|
pTo; pFrom;
|
|
return (FALSE);
|
|
}
|
|
|
|
/* InvalidateVolTypes -
|
|
*
|
|
* This function sets all drive types to unknown. It should be called
|
|
* whenever the drive list is refreshed.
|
|
*/
|
|
|
|
VOID
|
|
APIENTRY
|
|
InvalidateVolTypes( VOID )
|
|
{
|
|
INT i;
|
|
|
|
if (!pLFN)
|
|
return;
|
|
|
|
for (i = 0; i < CDRIVEMAX; i++)
|
|
pLFN->rgVolType[i] = -1;
|
|
}
|
|
|
|
|
|
/* WFCopy
|
|
*
|
|
* Copies files
|
|
*/
|
|
WORD
|
|
APIENTRY
|
|
WFCopy(
|
|
PSTR pszFrom,
|
|
PSTR pszTo
|
|
)
|
|
{
|
|
WORD wRet;
|
|
|
|
Notify(hdlgProgress, IDS_COPYINGMSG, pszFrom, pszTo);
|
|
|
|
wRet = FileCopy(pszFrom,pszTo);
|
|
|
|
if (!wRet)
|
|
ChangeFileSystem(FSC_CREATE,pszTo,NULL);
|
|
|
|
return wRet;
|
|
}
|
|
|
|
/* WFRemove
|
|
*
|
|
* Deletes files
|
|
*/
|
|
WORD
|
|
APIENTRY
|
|
WFRemove(
|
|
PSTR pszFile
|
|
)
|
|
{
|
|
WORD wRet;
|
|
|
|
wRet = FileRemove(pszFile);
|
|
if (!wRet)
|
|
ChangeFileSystem(FSC_DELETE,pszFile,NULL);
|
|
|
|
return wRet;
|
|
}
|
|
|
|
/* WFMove
|
|
*
|
|
* Moves files on a volume
|
|
*/
|
|
WORD
|
|
APIENTRY
|
|
WFMove(
|
|
PSTR pszFrom,
|
|
PSTR pszTo
|
|
)
|
|
{
|
|
WORD wRet;
|
|
|
|
wRet = FileMove(pszFrom,pszTo);
|
|
if (!wRet)
|
|
ChangeFileSystem(FSC_RENAME,pszFrom,pszTo);
|
|
|
|
return wRet;
|
|
}
|