1054 lines
27 KiB
C
1054 lines
27 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
linkd.c
|
||
|
||
Simple utility to manipulate name graftings at directories.
|
||
|
||
Author:
|
||
|
||
Felipe Cabrera (Cabrera) 271-Aug-1997
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#define UNICODE
|
||
#define _UNICODE
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h> // exit
|
||
#include <io.h> // _get_osfhandle
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#include <ntioapi.h>
|
||
|
||
#include <windows.h>
|
||
#include <locale.h> // setlocale
|
||
|
||
|
||
//
|
||
// Functions forward referenced.
|
||
//
|
||
|
||
|
||
void
|
||
ScanArgs (
|
||
int argc,
|
||
char **argv
|
||
);
|
||
|
||
void
|
||
__cdecl
|
||
printmessage (
|
||
DWORD messageID,
|
||
...
|
||
);
|
||
|
||
void
|
||
SzToWsz (
|
||
OUT WCHAR *Unicode,
|
||
IN char *Ansi
|
||
);
|
||
|
||
BOOL
|
||
MassageLinkValue (
|
||
IN LPCWSTR lpLinkName,
|
||
IN LPCWSTR lpLinkValue,
|
||
OUT PUNICODE_STRING NtLinkName,
|
||
OUT PUNICODE_STRING NtLinkValue,
|
||
OUT PUNICODE_STRING DosLinkValue
|
||
);
|
||
|
||
void
|
||
__cdecl
|
||
printmessage (
|
||
DWORD messageID,
|
||
...
|
||
);
|
||
|
||
void
|
||
__cdecl
|
||
DisplayMsg (
|
||
DWORD MsgNum,
|
||
...
|
||
);
|
||
|
||
int
|
||
FileIsConsole (
|
||
int fh
|
||
);
|
||
|
||
//
|
||
// I/O stream handles and variables.
|
||
//
|
||
|
||
HANDLE hInput;
|
||
HANDLE hOutput;
|
||
HANDLE hError;
|
||
|
||
#define STDIN 0
|
||
#define STDOUT 1
|
||
#define STDERR 2
|
||
|
||
BOOL ConsoleInput;
|
||
BOOL ConsoleOutput;
|
||
BOOL ConsoleError;
|
||
|
||
//
|
||
// Core control state vars
|
||
//
|
||
|
||
BOOL NeedHelp;
|
||
BOOL DoCreate;
|
||
BOOL DoDelete;
|
||
BOOL DoQuery;
|
||
BOOL DoEnumerate;
|
||
|
||
#include "linkdmsg.h"
|
||
|
||
TCHAR Buf[1024]; // for displaying stuff
|
||
|
||
//
|
||
// Main
|
||
//
|
||
|
||
void
|
||
__cdecl
|
||
main(
|
||
int argc,
|
||
char **argv
|
||
)
|
||
|
||
{
|
||
CHAR lBuf[16];
|
||
DWORD dwCodePage;
|
||
LANGID LangId;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE Handle;
|
||
|
||
UNICODE_STRING UnicodeName;
|
||
UNICODE_STRING NtLinkName;
|
||
UNICODE_STRING NtLinkValue;
|
||
UNICODE_STRING DosLinkValue;
|
||
|
||
WCHAR FullPathLinkValue[ DOS_MAX_PATH_LENGTH+1 ];
|
||
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
BOOL TranslationStatus;
|
||
|
||
PVOID FreeBuffer;
|
||
PVOID FreeBuffer2;
|
||
|
||
FILE_DISPOSITION_INFORMATION Disposition;
|
||
|
||
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
|
||
PCHAR ReparseBuffer = NULL;
|
||
ULONG ReparsePointTag = IO_REPARSE_TAG_RESERVED_ZERO;
|
||
USHORT ReparseDataLength = 0;
|
||
|
||
WCHAR WFileName[MAX_PATH + 2];
|
||
WCHAR WFileNameTwo[MAX_PATH + 2];
|
||
UCHAR Command[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||
|
||
ULONG FsControlCode = 0;
|
||
ULONG CreateOptions = 0;
|
||
ULONG CreateDisposition = 0;
|
||
ULONG DesiredAccess = SYNCHRONIZE;
|
||
|
||
//
|
||
// Build up state vector in global booleans.
|
||
//
|
||
|
||
ScanArgs(argc, argv);
|
||
|
||
//
|
||
// printf( "argc = %d NeedHelp = %d DoCreate = %d DoDelete = %d DoQuery = %d DoEnumerate = %d\n",
|
||
// argc, NeedHelp, DoCreate, DoDelete, DoQuery, DoEnumerate );
|
||
//
|
||
|
||
//
|
||
// Since FormatMessage checks the current TEB's locale, and the Locale for
|
||
// CHCP is initialized when the message class is initialized, the TEB has to
|
||
// be updated after the code page is changed successfully.
|
||
//
|
||
// Why are we doing this, you ask. Well, the FE guys have plans to add
|
||
// more than one set of language resources to this module, but not all
|
||
// the possible resources. So this limited set is what they plan for.
|
||
// If FormatMessage can't find the right language, it falls back to
|
||
// something hopefully useful.
|
||
//
|
||
|
||
dwCodePage = GetConsoleOutputCP();
|
||
|
||
sprintf(lBuf, ".%d", dwCodePage);
|
||
|
||
switch( dwCodePage )
|
||
{
|
||
case 437:
|
||
LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
|
||
break;
|
||
case 932:
|
||
LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT );
|
||
break;
|
||
case 949:
|
||
LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN );
|
||
break;
|
||
case 936:
|
||
LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
|
||
break;
|
||
case 950:
|
||
LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL );
|
||
break;
|
||
default:
|
||
LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT );
|
||
lBuf[0] = '\0';
|
||
break;
|
||
}
|
||
|
||
SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
|
||
setlocale(LC_ALL, lBuf);
|
||
|
||
//
|
||
// Set the appropriate handles.
|
||
//
|
||
|
||
hInput = GetStdHandle(STD_INPUT_HANDLE);
|
||
ConsoleInput = FileIsConsole(STDIN) ? TRUE : FALSE;
|
||
|
||
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||
ConsoleOutput = FileIsConsole(STDOUT) ? TRUE : FALSE;
|
||
|
||
hError = GetStdHandle(STD_ERROR_HANDLE);
|
||
ConsoleError = FileIsConsole(STDERR) ? TRUE : FALSE;
|
||
|
||
//
|
||
// OK, we know the state of the command, do work
|
||
//
|
||
|
||
//
|
||
// printf( "The parameters specified were: [0]%s [1]%s\n", argv[0], argv[1] );
|
||
//
|
||
|
||
//
|
||
// If they asked for help, or did something that indicates they don't
|
||
// understand how the program works, print help and exit.
|
||
//
|
||
|
||
if (NeedHelp) {
|
||
printmessage( MSG_LINKD_HELP );
|
||
exit(1);
|
||
}
|
||
|
||
//
|
||
// The enumeration of all mount points is not yet supported.
|
||
//
|
||
|
||
if (DoEnumerate) {
|
||
printmessage( MSG_LINKD_HELP );
|
||
exit(1);
|
||
}
|
||
|
||
//
|
||
// The following three calls require, at least, to have the SourceName of the operation.
|
||
// Thus, we have one NT file name that we will operate on.
|
||
// Change the string to Unicode and store it locally. Use it latter to open the file.
|
||
//
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
WFileName,
|
||
&UnicodeName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if (!TranslationStatus) {
|
||
printmessage( MSG_LINKD_WRONG_NAME );
|
||
exit (1);
|
||
}
|
||
|
||
FreeBuffer = UnicodeName.Buffer;
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&UnicodeName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// printf( "Transformed unicode str is %Z\n", &UnicodeName );
|
||
//
|
||
|
||
//
|
||
// Now go do the appropriate actions.
|
||
//
|
||
|
||
if (DoCreate) {
|
||
|
||
//
|
||
// Set the code of the FSCTL operation.
|
||
//
|
||
|
||
FsControlCode = FSCTL_SET_REPARSE_POINT;
|
||
|
||
//
|
||
// Set the open/create options for a directory.
|
||
//
|
||
|
||
CreateOptions = FILE_OPEN_REPARSE_POINT;
|
||
|
||
//
|
||
// Set the tag to mount point.
|
||
//
|
||
|
||
ReparsePointTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||
|
||
//
|
||
// Open to set the reparse point.
|
||
//
|
||
|
||
DesiredAccess |= FILE_WRITE_DATA;
|
||
CreateDisposition = FILE_OPEN; // the file must be present
|
||
|
||
Status = NtCreateFile(
|
||
&Handle,
|
||
DesiredAccess,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
NULL, // pallocationsize (none!)
|
||
FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
CreateDisposition,
|
||
CreateOptions,
|
||
NULL, // EA buffer (none!)
|
||
0
|
||
);
|
||
|
||
//
|
||
// Create a directory if you do not find it.
|
||
//
|
||
|
||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
DesiredAccess = SYNCHRONIZE;
|
||
CreateDisposition = FILE_CREATE;
|
||
CreateOptions = FILE_DIRECTORY_FILE;
|
||
|
||
Status = NtCreateFile(
|
||
&Handle,
|
||
DesiredAccess,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
NULL, // pallocationsize (none!)
|
||
FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
CreateDisposition,
|
||
CreateOptions,
|
||
NULL, // EA buffer (none!)
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
printmessage( MSG_LINKD_CREATE_FAILED );
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// Close the handle and re-open.
|
||
//
|
||
|
||
NtClose( Handle );
|
||
|
||
CreateOptions = FILE_OPEN_REPARSE_POINT;
|
||
DesiredAccess |= FILE_WRITE_DATA;
|
||
CreateDisposition = FILE_OPEN; // the file must be present
|
||
|
||
Status = NtCreateFile(
|
||
&Handle,
|
||
DesiredAccess,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
NULL, // pallocationsize (none!)
|
||
FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
CreateDisposition,
|
||
CreateOptions,
|
||
NULL, // EA buffer (none!)
|
||
0
|
||
);
|
||
|
||
}
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
|
||
// printmessage( MSG_LINKD_OPEN_FAILED );
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// Build the appropriate buffer for mount points and for symbolic links.
|
||
//
|
||
|
||
if ((ReparsePointTag == IO_REPARSE_TAG_MOUNT_POINT) ||
|
||
(ReparsePointTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
|
||
|
||
//
|
||
// The value of the mount point or of the symbolic link comes in argv[2].
|
||
//
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
SzToWsz( WFileNameTwo, argv[2] );
|
||
|
||
//
|
||
// Innitialize the DosName buffer.
|
||
//
|
||
|
||
DosLinkValue.Buffer = FullPathLinkValue;
|
||
DosLinkValue.MaximumLength = sizeof( FullPathLinkValue );
|
||
DosLinkValue.Length = 0;
|
||
|
||
//
|
||
// Massage all the names.
|
||
//
|
||
|
||
if (!MassageLinkValue( WFileName, WFileNameTwo, &NtLinkName, &NtLinkValue, &DosLinkValue )) {
|
||
|
||
if (DosLinkValue.Length == 0) {
|
||
printmessage( MSG_LINKD_WRONG_NAME );
|
||
}
|
||
else {
|
||
printmessage( MSG_LINKD_PATH_NOT_FOUND );
|
||
}
|
||
|
||
RtlFreeUnicodeString( &NtLinkName );
|
||
RtlFreeUnicodeString( &NtLinkValue );
|
||
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// printf( "NtLinkName %Z\n", &NtLinkName );
|
||
// printf( "NtLinkValue %Z\n", &NtLinkValue );
|
||
// printf( "DosLinkValue is %Z\n", &DosLinkValue );
|
||
// printf( "NtLinkValue.Length %d DosLinkValue.Length %d sizeof(UNICODE_NULL) %d\n",
|
||
// NtLinkValue.Length, DosLinkValue.Length, sizeof(UNICODE_NULL) );
|
||
//
|
||
|
||
RtlFreeUnicodeString( &NtLinkName );
|
||
|
||
//
|
||
// Set the reparse point with mount point or symbolic link tag and determine
|
||
// the appropriate length of the buffer.
|
||
//
|
||
|
||
//
|
||
// printf( "FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE %d\n",
|
||
// (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE) );
|
||
//
|
||
|
||
ReparseDataLength = (USHORT)((FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
|
||
REPARSE_DATA_BUFFER_HEADER_SIZE) +
|
||
NtLinkValue.Length + sizeof(UNICODE_NULL) +
|
||
DosLinkValue.Length + sizeof(UNICODE_NULL));
|
||
|
||
//
|
||
// printf( "ReparseDataLength %d\n", ReparseDataLength );
|
||
//
|
||
|
||
//
|
||
// Allocate a buffer to set the reparse point.
|
||
//
|
||
|
||
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)RtlAllocateHeap(
|
||
RtlProcessHeap(),
|
||
HEAP_ZERO_MEMORY,
|
||
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataLength
|
||
);
|
||
|
||
if (ReparseBufferHeader == NULL) {
|
||
|
||
NtClose( Handle );
|
||
RtlFreeUnicodeString( &NtLinkValue );
|
||
printmessage( MSG_LINKD_NO_MEMORY );
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// Setting the buffer is common for both tags as their buffers have identical fields.
|
||
//
|
||
|
||
ReparseBufferHeader->ReparseDataLength = (USHORT)ReparseDataLength;
|
||
ReparseBufferHeader->Reserved = 0;
|
||
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
|
||
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength = NtLinkValue.Length;
|
||
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameOffset = NtLinkValue.Length + sizeof( UNICODE_NULL );
|
||
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength = DosLinkValue.Length;
|
||
RtlCopyMemory(
|
||
ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer,
|
||
NtLinkValue.Buffer,
|
||
NtLinkValue.Length
|
||
);
|
||
RtlCopyMemory(
|
||
(PCHAR)(ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer)+
|
||
NtLinkValue.Length + sizeof(UNICODE_NULL),
|
||
DosLinkValue.Buffer,
|
||
DosLinkValue.Length
|
||
);
|
||
|
||
RtlFreeUnicodeString( &NtLinkValue );
|
||
}
|
||
|
||
//
|
||
// Set the tag in common, once for all possible cases.
|
||
//
|
||
|
||
ReparseBufferHeader->ReparseTag = ReparsePointTag;
|
||
|
||
//
|
||
// Set the reparse point.
|
||
//
|
||
|
||
Status = NtFsControlFile(
|
||
Handle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FsControlCode,
|
||
ReparseBufferHeader,
|
||
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseBufferHeader->ReparseDataLength,
|
||
NULL, // no output buffer
|
||
0 // output buffer length
|
||
);
|
||
|
||
//
|
||
// Close the file.
|
||
//
|
||
|
||
NtClose( Handle );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_SET_OPERATION_FAILED, Buf);
|
||
// printmessage( MSG_LINKD_SET_OPERATION_FAILED );
|
||
exit (1);
|
||
}
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_CREATE_OPERATION_SUCCESS, Buf);
|
||
// printmessage( MSG_LINKD_CREATE_OPERATION_SUCCESS );
|
||
}
|
||
|
||
if (DoDelete) {
|
||
|
||
FILE_DISPOSITION_INFORMATION Disposition = {TRUE};
|
||
|
||
//
|
||
// Open the file for delete access.
|
||
// Inhibit the reparse behavior using FILE_OPEN_REPARSE_POINT.
|
||
// This will get a handle to the entity whether the appropriate filter is or not in place.
|
||
//
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
(ACCESS_MASK)DELETE,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
|
||
// printmessage( MSG_LINKD_OPEN_FAILED );
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
exit(1);
|
||
}
|
||
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
|
||
//
|
||
// Delete the file
|
||
//
|
||
|
||
Status = NtSetInformationFile(
|
||
Handle,
|
||
&IoStatusBlock,
|
||
&Disposition,
|
||
sizeof(Disposition),
|
||
FileDispositionInformation
|
||
);
|
||
|
||
NtClose(Handle);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
printmessage( MSG_LINKD_DELETE_OPERATION_FAILED );
|
||
exit(1);
|
||
}
|
||
|
||
printmessage( MSG_LINKD_DELETE_OPERATION_SUCCESS );
|
||
}
|
||
|
||
if (DoQuery) {
|
||
|
||
//
|
||
// Set the code of the FSCTL operation.
|
||
//
|
||
|
||
FsControlCode = FSCTL_GET_REPARSE_POINT;
|
||
|
||
//
|
||
// We do not specify whether it is a file or a directory to get either.
|
||
//
|
||
|
||
DesiredAccess = FILE_READ_DATA | SYNCHRONIZE;
|
||
CreateOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
|
||
|
||
//
|
||
// Open the reparse point for query.
|
||
//
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
DesiredAccess,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
CreateOptions
|
||
);
|
||
//
|
||
// Free the name buffer as we are done with it.
|
||
//
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
|
||
// printmessage( MSG_LINKD_OPEN_FAILED );
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// Query the reparse point.
|
||
//
|
||
// We are use the approach of passing a buffer of well-known length:
|
||
// MAXIMUM_REPARSE_DATA_BUFFER_SIZE
|
||
|
||
//
|
||
// Allocate a buffer and get the information.
|
||
//
|
||
|
||
ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
||
ReparseBuffer = RtlAllocateHeap(
|
||
RtlProcessHeap(),
|
||
HEAP_ZERO_MEMORY,
|
||
ReparseDataLength
|
||
);
|
||
|
||
if (ReparseBuffer == NULL) {
|
||
printmessage( MSG_LINKD_NO_MEMORY );
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// Now go and get the data.
|
||
//
|
||
|
||
Status = NtFsControlFile(
|
||
Handle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FsControlCode, // no input buffer
|
||
NULL, // input buffer length
|
||
0,
|
||
(PVOID)ReparseBuffer,
|
||
ReparseDataLength
|
||
);
|
||
|
||
//
|
||
// printf( "Status %x IoStatusBlock.Status %x IoStatusBlock.Information %x\n", Status, IoStatusBlock.Status, IoStatusBlock.Information );
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_GET_OPERATION_FAILED, Buf);
|
||
NtClose( Handle );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
|
||
exit (1);
|
||
}
|
||
|
||
//
|
||
// Close the file and free the buffer.
|
||
//
|
||
|
||
NtClose( Handle );
|
||
|
||
//
|
||
// Display the buffer.
|
||
//
|
||
|
||
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
|
||
|
||
if ((ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) ||
|
||
(ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
|
||
|
||
USHORT Offset = 0;
|
||
|
||
NtLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[Offset];
|
||
NtLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||
Offset = NtLinkValue.Length + sizeof(UNICODE_NULL);
|
||
DosLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[Offset/sizeof(WCHAR)];
|
||
DosLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength;
|
||
|
||
//
|
||
// printf( "NtLinkValue.Length %d DosLinkValue.Length %d\n", NtLinkValue.Length, DosLinkValue.Length );
|
||
// printf( " NtLinkValue: %Z\n", &NtLinkValue );
|
||
// printf( "DosLinkValue: %Z\n", &DosLinkValue );
|
||
//
|
||
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_DISPLAY_NL_A, Buf);
|
||
|
||
swprintf(&Buf[0], TEXT("%s"), DosLinkValue.Buffer);
|
||
DisplayMsg(MSG_LINKD_DISPLAY_NL, Buf);
|
||
}
|
||
|
||
else {
|
||
SzToWsz( WFileName, argv[1] );
|
||
swprintf(&Buf[0], TEXT("%s"), WFileName);
|
||
DisplayMsg(MSG_LINKD_GET_OPERATION_FAILED, Buf);
|
||
}
|
||
|
||
//
|
||
// Free the buffer.
|
||
//
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
|
||
}
|
||
|
||
//
|
||
// Final exit point.
|
||
//
|
||
|
||
exit (0);
|
||
} // main
|
||
|
||
|
||
|
||
|
||
//
|
||
// Changing a file name to wide characters.
|
||
//
|
||
|
||
void
|
||
SzToWsz (
|
||
OUT WCHAR *Unicode,
|
||
IN char *Ansi
|
||
)
|
||
{
|
||
while (*Unicode++ = *Ansi++)
|
||
;
|
||
} // SzToWsz
|
||
|
||
|
||
//
|
||
// Name transformations to create a legitimate mount point or a symbolic link.
|
||
//
|
||
|
||
BOOL
|
||
MassageLinkValue(
|
||
IN LPCWSTR lpLinkName,
|
||
IN LPCWSTR lpLinkValue,
|
||
OUT PUNICODE_STRING NtLinkName,
|
||
OUT PUNICODE_STRING NtLinkValue,
|
||
OUT PUNICODE_STRING DosLinkValue
|
||
)
|
||
{
|
||
PWSTR FilePart;
|
||
PWSTR s, sBegin, sBackupLimit, sLinkName;
|
||
NTSTATUS Status;
|
||
USHORT nSaveNtNameLength;
|
||
ULONG nLevels;
|
||
|
||
//
|
||
// Initialize output variables to NULL
|
||
//
|
||
|
||
RtlInitUnicodeString( NtLinkName, NULL );
|
||
RtlInitUnicodeString( NtLinkValue, NULL );
|
||
|
||
//
|
||
// Translate link name into full NT path.
|
||
//
|
||
|
||
if (!RtlDosPathNameToNtPathName_U( lpLinkName,
|
||
NtLinkName,
|
||
&sLinkName,
|
||
NULL
|
||
)
|
||
) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// All done if no link value.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT( lpLinkValue )) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// If the target is a device, do not allow the link.
|
||
//
|
||
|
||
if (RtlIsDosDeviceName_U( (PWSTR)lpLinkValue )) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Convert to DOS path to full path, and get Nt representation
|
||
// of DOS path.
|
||
//
|
||
|
||
if (!RtlGetFullPathName_U( lpLinkValue,
|
||
DosLinkValue->MaximumLength,
|
||
DosLinkValue->Buffer,
|
||
NULL
|
||
)
|
||
) {
|
||
return FALSE;
|
||
}
|
||
DosLinkValue->Length = wcslen( DosLinkValue->Buffer ) * sizeof( WCHAR );
|
||
|
||
//
|
||
// Verify that the link value is a valid NT name.
|
||
//
|
||
|
||
if (!RtlDosPathNameToNtPathName_U( DosLinkValue->Buffer,
|
||
NtLinkValue,
|
||
NULL,
|
||
NULL
|
||
)
|
||
) {
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
} // MassageLinkValue
|
||
|
||
|
||
VOID
|
||
ScanArgs(
|
||
int argc,
|
||
char **argv
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ScanArgs - parse command line arguments, and set control flags
|
||
to reflect what we find.
|
||
|
||
Sets:
|
||
NeedHelp;
|
||
DoCreate;
|
||
DoDelete;
|
||
DoQuery;
|
||
DoEnumerate;
|
||
|
||
Arguments:
|
||
|
||
argc - count of command line args
|
||
|
||
argv - argument vector
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
int i;
|
||
|
||
NeedHelp = FALSE;
|
||
DoCreate = FALSE;
|
||
DoDelete = FALSE;
|
||
DoQuery = FALSE;
|
||
DoEnumerate = FALSE;
|
||
|
||
//
|
||
// The valid calls are:
|
||
//
|
||
// linkd sourceName valueName -- create a directory name grafting
|
||
// linkd sourceName /d -- delete a directory name grafting
|
||
// linkd sourceName -- to query the value of the name grafting
|
||
// linkd /? -- print help
|
||
// linkd -- to enumerate all the name graftings
|
||
//
|
||
|
||
|
||
if (argc > 3) {
|
||
NeedHelp = TRUE;
|
||
goto done;
|
||
}
|
||
|
||
if (argc == 1) {
|
||
DoEnumerate = TRUE;
|
||
goto done;
|
||
}
|
||
|
||
if (argc == 2) {
|
||
|
||
if ( (argv[1][0] == '/') &&
|
||
(argv[1][1] == '?') &&
|
||
(strlen(argv[1]) == 2) ) {
|
||
NeedHelp = TRUE;
|
||
goto done;
|
||
}
|
||
|
||
DoQuery = TRUE;
|
||
goto done;
|
||
}
|
||
|
||
if (argc == 3) {
|
||
|
||
if ( (argv[2][0] == '/') &&
|
||
((argv[2][1] == 'd') || (argv[2][1] == 'D')) &&
|
||
(strlen(argv[2]) == 2) ) {
|
||
DoDelete = TRUE;
|
||
goto done;
|
||
}
|
||
|
||
DoCreate = TRUE;
|
||
goto done;
|
||
}
|
||
|
||
done:
|
||
return;
|
||
} // ScanArgs
|
||
|
||
|
||
//
|
||
// Call FormatMessage and dump the result. All messages to Stdout
|
||
//
|
||
void
|
||
__cdecl
|
||
printmessage (
|
||
DWORD messageID,
|
||
...
|
||
)
|
||
{
|
||
unsigned short messagebuffer[4096];
|
||
va_list ap;
|
||
|
||
va_start(ap, messageID);
|
||
|
||
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, messageID, 0,
|
||
messagebuffer, 4095, &ap);
|
||
|
||
wprintf(messagebuffer);
|
||
|
||
va_end(ap);
|
||
} // printmessage
|
||
|
||
|
||
TCHAR DisplayBuffer[4096];
|
||
CHAR DisplayBuffer2[4096];
|
||
|
||
void
|
||
__cdecl
|
||
DisplayMsg (
|
||
DWORD MsgNum,
|
||
...
|
||
)
|
||
{
|
||
DWORD len, bytes_written;
|
||
BOOL success;
|
||
DWORD status;
|
||
va_list ap;
|
||
|
||
va_start(ap, MsgNum);
|
||
|
||
len = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, MsgNum, 0,
|
||
DisplayBuffer, 4096, &ap);
|
||
|
||
if (ConsoleOutput) {
|
||
success = WriteConsole(hOutput, (LPVOID)DisplayBuffer, len,
|
||
&bytes_written, NULL);
|
||
|
||
} else {
|
||
CharToOem(DisplayBuffer, DisplayBuffer2);
|
||
success = WriteFile(hOutput, (LPVOID)DisplayBuffer2, len,
|
||
&bytes_written, NULL);
|
||
}
|
||
|
||
if (!success || bytes_written != len) {
|
||
status = GetLastError();
|
||
}
|
||
|
||
va_end(ap);
|
||
} // DisplayMsg
|
||
|
||
|
||
int
|
||
FileIsConsole(int fh)
|
||
{
|
||
unsigned htype;
|
||
DWORD dwMode;
|
||
HANDLE hFile;
|
||
|
||
hFile = (HANDLE)_get_osfhandle(fh);
|
||
htype = GetFileType(hFile);
|
||
htype &= ~FILE_TYPE_REMOTE;
|
||
|
||
if (FILE_TYPE_CHAR == htype) {
|
||
|
||
switch (fh) {
|
||
case STDIN:
|
||
hFile = GetStdHandle(STD_INPUT_HANDLE);
|
||
break;
|
||
case STDOUT:
|
||
hFile = GetStdHandle(STD_OUTPUT_HANDLE);
|
||
break;
|
||
case STDERR:
|
||
hFile = GetStdHandle(STD_ERROR_HANDLE);
|
||
break;
|
||
}
|
||
|
||
if (GetConsoleMode(hFile, &dwMode)) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
} // FileIsConsole
|