411 lines
12 KiB
C
411 lines
12 KiB
C
|
#define UNICODE
|
||
|
#define _UNICODE
|
||
|
|
||
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <tchar.h>
|
||
|
|
||
|
#include "compdir.h"
|
||
|
|
||
|
#define SHARE_ALL (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
|
||
|
#define lstrchr wcschr
|
||
|
|
||
|
typedef VOID (*RtlFreeUnicodeStringFunction)();
|
||
|
typedef ULONG (*RtlNtStatusToDosErrorFunction)();
|
||
|
typedef NTSTATUS (*NtCloseFunction)();
|
||
|
typedef NTSTATUS (*NtSetInformationFileFunction)();
|
||
|
typedef VOID (*RtlInitUnicodeStringFunction)();
|
||
|
typedef NTSTATUS (*NtOpenFileFunction)();
|
||
|
typedef BOOLEAN (*RtlCreateUnicodeStringFromAsciizFunction)();
|
||
|
typedef NTSTATUS (*NtQueryInformationFileFunction)();
|
||
|
typedef NTSTATUS (*NtFsControlFileFunction)();
|
||
|
typedef NTSTATUS (*RtlDosPathNameToNtPathName_UFunction)();
|
||
|
|
||
|
RtlFreeUnicodeStringFunction CDRtlFreeUnicodeString;
|
||
|
RtlNtStatusToDosErrorFunction CDRtlNtStatusToDosError;
|
||
|
NtCloseFunction CDNtClose;
|
||
|
NtSetInformationFileFunction CDNtSetInformationFile;
|
||
|
RtlInitUnicodeStringFunction CDRtlInitUnicodeString;
|
||
|
NtOpenFileFunction CDNtOpenFile;
|
||
|
RtlCreateUnicodeStringFromAsciizFunction CDRtlCreateUnicodeStringFromAsciiz;
|
||
|
NtQueryInformationFileFunction CDNtQueryInformationFile;
|
||
|
NtFsControlFileFunction CDNtFsControlFile;
|
||
|
RtlDosPathNameToNtPathName_UFunction CDRtlDosPathNameToNtPathName_U;
|
||
|
|
||
|
|
||
|
BOOL InitializeNtDllFunctions()
|
||
|
{
|
||
|
CDRtlFreeUnicodeString = (RtlFreeUnicodeStringFunction) GetProcAddress( NtDll, "RtlFreeUnicodeString");
|
||
|
if (CDRtlFreeUnicodeString == NULL) return FALSE;
|
||
|
CDRtlNtStatusToDosError = (RtlNtStatusToDosErrorFunction) GetProcAddress( NtDll, "RtlNtStatusToDosError");
|
||
|
if (CDRtlNtStatusToDosError == NULL) return FALSE;
|
||
|
CDNtClose = (NtCloseFunction) GetProcAddress( NtDll, "NtClose");
|
||
|
if (CDNtClose == NULL) return FALSE;
|
||
|
CDNtSetInformationFile = (NtSetInformationFileFunction) GetProcAddress( NtDll, "NtSetInformationFile");
|
||
|
if (CDNtSetInformationFile == NULL) return FALSE;
|
||
|
CDRtlInitUnicodeString = (RtlInitUnicodeStringFunction) GetProcAddress( NtDll, "RtlInitUnicodeString");
|
||
|
if (CDRtlInitUnicodeString == NULL) return FALSE;
|
||
|
CDNtOpenFile = (NtOpenFileFunction) GetProcAddress( NtDll, "NtOpenFile");
|
||
|
if (CDNtOpenFile == NULL) return FALSE;
|
||
|
CDRtlCreateUnicodeStringFromAsciiz = (RtlCreateUnicodeStringFromAsciizFunction) GetProcAddress( NtDll, "RtlCreateUnicodeStringFromAsciiz");
|
||
|
if (CDRtlCreateUnicodeStringFromAsciiz == NULL) return FALSE;
|
||
|
CDNtQueryInformationFile = (NtQueryInformationFileFunction) GetProcAddress( NtDll, "NtQueryInformationFile");
|
||
|
if (CDNtQueryInformationFile == NULL) return FALSE;
|
||
|
CDNtFsControlFile = (NtFsControlFileFunction)GetProcAddress( NtDll, "NtFsControlFile");
|
||
|
if (CDNtFsControlFile == NULL) return FALSE;
|
||
|
CDRtlDosPathNameToNtPathName_U = (RtlDosPathNameToNtPathName_UFunction)GetProcAddress( NtDll, "RtlDosPathNameToNtPathName_U");
|
||
|
if (CDRtlDosPathNameToNtPathName_U == NULL) return FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL MakeLink( char *src, char *dst, BOOL Output)
|
||
|
{
|
||
|
WCHAR OldNameBuf[MAX_PATH + 50];
|
||
|
WCHAR NewNameBuf[MAX_PATH + 50];
|
||
|
|
||
|
HANDLE FileHandle,
|
||
|
NewFileHandle,
|
||
|
RootDirHandle;
|
||
|
|
||
|
NTSTATUS Status;
|
||
|
IO_STATUS_BLOCK Iosb;
|
||
|
OBJECT_ATTRIBUTES Obj;
|
||
|
|
||
|
PFILE_LINK_INFORMATION pLinkInfo;
|
||
|
|
||
|
UNICODE_STRING u,
|
||
|
uRel;
|
||
|
|
||
|
WCHAR *pch, ch;
|
||
|
UNICODE_STRING uOldName;
|
||
|
UNICODE_STRING uNewName;
|
||
|
|
||
|
UNICODE_STRING uSrc, uDst;
|
||
|
|
||
|
(CDRtlCreateUnicodeStringFromAsciiz)( &uSrc, src);
|
||
|
(CDRtlCreateUnicodeStringFromAsciiz)( &uDst, dst);
|
||
|
|
||
|
lstrcpy( OldNameBuf, L"\\??\\");
|
||
|
lstrcat( OldNameBuf, uSrc.Buffer);
|
||
|
(CDRtlInitUnicodeString)( &uOldName, OldNameBuf);
|
||
|
|
||
|
lstrcpy( NewNameBuf, L"\\??\\");
|
||
|
lstrcat( NewNameBuf, uDst.Buffer);
|
||
|
(CDRtlInitUnicodeString)( &uNewName, NewNameBuf);
|
||
|
|
||
|
//
|
||
|
// Open the existing pathname.
|
||
|
//
|
||
|
|
||
|
InitializeObjectAttributes( &Obj, &uOldName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
|
||
|
Status = (CDNtOpenFile)( &FileHandle, SYNCHRONIZE, &Obj, &Iosb,
|
||
|
SHARE_ALL, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
||
|
|
||
|
if ( !NT_SUCCESS( Status))
|
||
|
{
|
||
|
SetLastError( ( CDRtlNtStatusToDosError)( Status));
|
||
|
if ( Output)
|
||
|
{
|
||
|
fprintf( stderr, "Could not open %s", src);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we need to get a handle on the root directory of the 'new'
|
||
|
// pathname; we'll pass that in the link information, and the
|
||
|
// rest of the path will be given relative to the root. We
|
||
|
// depend on paths looking like "\DosDevices\X:\path".
|
||
|
//
|
||
|
|
||
|
pch = lstrchr( uNewName.Buffer + 1, '\\');
|
||
|
ASSERT( NULL != pch);
|
||
|
pch = lstrchr( pch + 1, '\\');
|
||
|
if (!pch) {
|
||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||
|
if ( Output)
|
||
|
{
|
||
|
fprintf( stderr, "Invalid path %S", uNewName.Buffer);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
ch = pch[1];
|
||
|
pch[1] = '\0';
|
||
|
uNewName.Length = (USHORT)(lstrlen( uNewName.Buffer) * sizeof( WCHAR));
|
||
|
|
||
|
InitializeObjectAttributes( &Obj, &uNewName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
|
||
|
Status = (CDNtOpenFile)( &RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
|
||
|
SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
|
||
|
|
||
|
pch[1] = ch;
|
||
|
|
||
|
if ( !NT_SUCCESS( Status))
|
||
|
{
|
||
|
SetLastError( (CDRtlNtStatusToDosError)( Status));
|
||
|
if ( Output)
|
||
|
{
|
||
|
fprintf( stderr, "Could not get directory handle for %s", dst);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now get the path relative to the root.
|
||
|
//
|
||
|
|
||
|
(CDRtlInitUnicodeString)( &uRel, &pch[1]);
|
||
|
|
||
|
pLinkInfo = _alloca( sizeof( *pLinkInfo) + uRel.Length);
|
||
|
if ( NULL == pLinkInfo)
|
||
|
{
|
||
|
(CDNtClose)( RootDirHandle);
|
||
|
(CDNtClose)( FileHandle);
|
||
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RtlMoveMemory( pLinkInfo->FileName, uRel.Buffer, uRel.Length);
|
||
|
pLinkInfo->FileNameLength = uRel.Length;
|
||
|
|
||
|
pLinkInfo->ReplaceIfExists = TRUE;
|
||
|
pLinkInfo->RootDirectory = RootDirHandle;
|
||
|
|
||
|
Status = (CDNtSetInformationFile)( FileHandle, &Iosb, pLinkInfo,
|
||
|
sizeof( *pLinkInfo) + uRel.Length, FileLinkInformation);
|
||
|
|
||
|
// If file is already linked to an open file try to delete it
|
||
|
|
||
|
if ( Status == STATUS_ACCESS_DENIED)
|
||
|
{
|
||
|
_unlink( dst);
|
||
|
Status = (CDNtSetInformationFile)( FileHandle, &Iosb, pLinkInfo,
|
||
|
sizeof( *pLinkInfo) + uRel.Length, FileLinkInformation);
|
||
|
}
|
||
|
|
||
|
(CDNtClose)( RootDirHandle);
|
||
|
(CDNtClose)( FileHandle);
|
||
|
|
||
|
if ( !NT_SUCCESS( Status))
|
||
|
{
|
||
|
SetLastError( (CDRtlNtStatusToDosError)( Status));
|
||
|
if ( Output)
|
||
|
{
|
||
|
fprintf( stderr, "Could not create link for %s", dst);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
(CDRtlFreeUnicodeString)( &uSrc);
|
||
|
(CDRtlFreeUnicodeString)( &uDst);
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
int NumberOfLinks( char *FileName)
|
||
|
{
|
||
|
|
||
|
FILE_STANDARD_INFORMATION FileInfo;
|
||
|
|
||
|
WCHAR FileNameBuf[MAX_PATH + 50];
|
||
|
|
||
|
HANDLE FileHandle;
|
||
|
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
IO_STATUS_BLOCK Iosb;
|
||
|
|
||
|
OBJECT_ATTRIBUTES Obj;
|
||
|
|
||
|
UNICODE_STRING uPrelimFileName,
|
||
|
uFileName;
|
||
|
|
||
|
(CDRtlCreateUnicodeStringFromAsciiz)( &uPrelimFileName, FileName);
|
||
|
|
||
|
lstrcpy( FileNameBuf, L"\\??\\");
|
||
|
lstrcat( FileNameBuf, uPrelimFileName.Buffer);
|
||
|
(CDRtlInitUnicodeString)( &uFileName, FileNameBuf);
|
||
|
|
||
|
InitializeObjectAttributes( &Obj, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
|
||
|
Status = (CDNtOpenFile)( &FileHandle, SYNCHRONIZE, &Obj, &Iosb,
|
||
|
SHARE_ALL, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
||
|
|
||
|
if ( !NT_SUCCESS( Status))
|
||
|
{
|
||
|
SetLastError( (CDRtlNtStatusToDosError)( Status));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Status = (CDNtQueryInformationFile)( FileHandle, &Iosb, &FileInfo,
|
||
|
sizeof( FileInfo), FileStandardInformation);
|
||
|
|
||
|
(CDNtClose)( FileHandle);
|
||
|
|
||
|
if ( !NT_SUCCESS( Status))
|
||
|
{
|
||
|
SetLastError( (CDRtlNtStatusToDosError)( Status));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return FileInfo.NumberOfLinks;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SisCopyFile(
|
||
|
LPCSTR lpExistingFileName,
|
||
|
LPCSTR lpNewFileName,
|
||
|
BOOL bFailIfExists,
|
||
|
LPBOOL fTrySis
|
||
|
)
|
||
|
{
|
||
|
BOOL ok;
|
||
|
DWORD err;
|
||
|
NTSTATUS Status;
|
||
|
HANDLE volHandle;
|
||
|
UNICODE_STRING srcFileName, dstFileName;
|
||
|
UNICODE_STRING srcDosFileName, dstDosFileName;
|
||
|
PSI_COPYFILE copyFile;
|
||
|
UCHAR Buffer[(sizeof(SI_COPYFILE) + MAX_PATH * 2) * sizeof(WCHAR)];
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
int i;
|
||
|
|
||
|
copyFile = (PSI_COPYFILE)Buffer;
|
||
|
|
||
|
srcFileName.Buffer = NULL;
|
||
|
dstFileName.Buffer = NULL;
|
||
|
srcDosFileName.Buffer = NULL;
|
||
|
srcDosFileName.Buffer = NULL;
|
||
|
|
||
|
//
|
||
|
// Convert the ansii names to unicode and place in the copyFile buffer.
|
||
|
//
|
||
|
|
||
|
ok = CDRtlCreateUnicodeStringFromAsciiz( &srcDosFileName, lpExistingFileName );
|
||
|
if (!ok) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
ok = CDRtlDosPathNameToNtPathName_U( srcDosFileName.Buffer, &srcFileName, NULL, NULL );
|
||
|
if (!ok) {
|
||
|
goto error;
|
||
|
}
|
||
|
CDRtlFreeUnicodeString( &srcDosFileName );
|
||
|
|
||
|
ok = CDRtlCreateUnicodeStringFromAsciiz( &dstDosFileName, lpNewFileName );
|
||
|
if (!ok) {
|
||
|
goto error;
|
||
|
}
|
||
|
ok = CDRtlDosPathNameToNtPathName_U( dstDosFileName.Buffer, &dstFileName, NULL, NULL );
|
||
|
if (!ok) {
|
||
|
goto error;
|
||
|
}
|
||
|
CDRtlFreeUnicodeString( &dstDosFileName );
|
||
|
|
||
|
copyFile->SourceFileNameLength = srcFileName.Length + sizeof(WCHAR);
|
||
|
copyFile->DestinationFileNameLength = dstFileName.Length + sizeof(WCHAR);
|
||
|
copyFile->Flags = bFailIfExists ? 0 : COPYFILE_SIS_REPLACE;
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
©File->FileNameBuffer[0],
|
||
|
srcFileName.Buffer,
|
||
|
copyFile->SourceFileNameLength);
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
©File->FileNameBuffer[copyFile->SourceFileNameLength / sizeof(WCHAR)],
|
||
|
dstFileName.Buffer,
|
||
|
copyFile->DestinationFileNameLength);
|
||
|
|
||
|
CDRtlFreeUnicodeString( &dstFileName );
|
||
|
|
||
|
#define copyFileSize (FIELD_OFFSET(SI_COPYFILE, FileNameBuffer) + \
|
||
|
copyFile->SourceFileNameLength + \
|
||
|
copyFile->DestinationFileNameLength)
|
||
|
|
||
|
//
|
||
|
// Get a handle to the source file's containing directory to pass into
|
||
|
// FSCTL_SIS_COPYFILE,
|
||
|
//
|
||
|
|
||
|
for (i = srcFileName.Length / sizeof(WCHAR) - 1;
|
||
|
i >= 0 && srcFileName.Buffer[i] != '\\';
|
||
|
--i)
|
||
|
continue;
|
||
|
|
||
|
srcFileName.Length = (USHORT)(i * sizeof(WCHAR));
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&objectAttributes,
|
||
|
&srcFileName,
|
||
|
OBJ_CASE_INSENSITIVE,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
|
||
|
//
|
||
|
// Need to use NtOpenFile because Win32 doesn't let you open a directory.
|
||
|
//
|
||
|
Status = CDNtOpenFile(
|
||
|
&volHandle,
|
||
|
GENERIC_READ | SYNCHRONIZE,
|
||
|
&objectAttributes,
|
||
|
&ioStatusBlock,
|
||
|
SHARE_ALL,
|
||
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
||
|
|
||
|
CDRtlFreeUnicodeString( &srcFileName );
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
SetLastError(CDRtlNtStatusToDosError(Status));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Invoke the SIS CopyFile FsCtrl.
|
||
|
//
|
||
|
|
||
|
Status = CDNtFsControlFile(
|
||
|
volHandle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&ioStatusBlock,
|
||
|
FSCTL_SIS_COPYFILE,
|
||
|
copyFile, // Input buffer
|
||
|
copyFileSize, // Input buffer length
|
||
|
NULL, // Output buffer
|
||
|
0 ); // Output buffer length
|
||
|
|
||
|
CloseHandle( volHandle );
|
||
|
|
||
|
if (NT_SUCCESS( Status )) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
if ((Status == STATUS_INVALID_DEVICE_REQUEST) || (Status == STATUS_INVALID_PARAMETER)) {
|
||
|
*fTrySis = FALSE;
|
||
|
}
|
||
|
|
||
|
SetLastError(CDRtlNtStatusToDosError(Status));
|
||
|
return FALSE;
|
||
|
|
||
|
error:
|
||
|
|
||
|
err = GetLastError();
|
||
|
|
||
|
CDRtlFreeUnicodeString( &srcDosFileName );
|
||
|
CDRtlFreeUnicodeString( &dstDosFileName );
|
||
|
CDRtlFreeUnicodeString( &srcFileName );
|
||
|
CDRtlFreeUnicodeString( &dstDosFileName );
|
||
|
|
||
|
SetLastError(err);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|