windows-nt/Source/XPSP1/NT/sdktools/winobj/lfn.c
2020-09-26 16:20:57 +08:00

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;
}