Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2143 lines
58 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
flinks.c
Abstract:
This module implements a utlity that creates, deletes, renames, lists
symbolic links.
Author:
Felipe Cabrera [cabrera] 17-October-1996
Revision History:
--*/
#include "flinks.h"
//
// We can have: flinks ?
// or, flinks Path1 Path2
// We can also have: flinks [/dyv] Path2
// or, flinks [/cmrv] Path1 Path2
//
unsigned LinkType = IO_REPARSE_TAG_SYMBOLIC_LINK;
void __cdecl
main(
int argc,
char **argv
)
{
NTSTATUS Status;
ATTRIBUTE_TYPE Attributes1, // Attributes of Path1
Attributes2; // Attributes of Path2
char *Path1, // Will point to the full path name.
*Path2; // Will point to the full path name.
Attributes1 = GetFileAttributeError;
Attributes2 = GetFileAttributeError;
//
// Check argument validity and set global action flags.
//
ParseArgs( argc, argv );
//
// Do the actions in turn.
//
if (fCopy) {
//
// Check for the existence of Path1 getting its attributes.
//
IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
//
// Path1 does not exist, hence we cannot copy it.
//
fprintf( stderr, "Cannot copy Path1, it does not exist.\n" );
exit (1);
}
//
// Path1 needs to be a reparse point to copy a symbolic link.
//
if (Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT) {
//
// If Path2 does not exist, create it.
//
IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
//
// Need to create this file preserving the kind (file or directory).
//
Status = CreateEmptyFile( argv[argc - 1], Attributes1, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot create file for symbolic link. Status %x\n", Status );
exit (1);
}
}
//
// Copy into Path2 the symbolic link in Path1.
// Build the full path for Path1 and Path2 and call the copy routine.
//
if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
Path1 = argv[argc - 2];
}
if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
Path2 = argv[argc - 1];
}
Status = CopySymbolicLink( Path1, Path2, Attributes1, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot copy symbolic link. Status %x\n", Status );
}
} else {
fprintf( stderr, "Cannot copy, Path1 is not a symbolic link.\n" );
}
exit (1);
} // fCopy
if (fCreate) {
//
// Check for the existence of Path1 getting its attributes.
//
IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
//
// Need to create this file object. As default we create it as a file.
//
if (fAlternateCreateDefault) {
Attributes1 = FILE_ATTRIBUTE_DIRECTORY;
} else {
//
// We try to create it with the same characteristic of the target,
// when we are able to reach the target. Otherwise we use a file
// as default.
//
Attributes2 = 0xFFFFFFFF;
IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
Attributes1 = FILE_ATTRIBUTE_NORMAL;
} else {
if (Attributes2 & FILE_ATTRIBUTE_DIRECTORY) {
Attributes1 = FILE_ATTRIBUTE_DIRECTORY;
} else {
Attributes1 = FILE_ATTRIBUTE_NORMAL;
}
}
}
Status = CreateEmptyFile( argv[argc - 2], Attributes1, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot create file for symbolic link. Status %x\n", Status );
Attributes1 = FILE_ATTRIBUTE_REPARSE_POINT;
}
}
//
// Path1 needs to be a non-reparse point to create a symbolic link.
//
if (!(Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT)) {
//
// Build the full path for Path1 and Path2.
//
if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
Path1 = argv[argc - 2];
}
// if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
Path2 = argv[argc - 1];
// }
Status = CreateSymbolicLink( Path1, Path2, Attributes1, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot create symbolic link. Status %x\n", Status );
}
} else {
fprintf( stderr, "Cannot create, Path1 is a symbolic link.\n" );
}
exit (1);
} // fCreate
if (fDelete) {
//
// Check existence of Path2 path getting the attributes.
//
IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
fprintf( stderr, "Could not find %s (error = %d)\n", argv[argc - 1], GetLastError() );
exit(1);
}
//
// Path2 needs to be a reparse point to delete a symbolic link.
//
if (Attributes2 & FILE_ATTRIBUTE_REPARSE_POINT) {
//
// Build the full path for Path2, the only path name.
//
if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
Path2 = argv[argc - 1];
}
Status = DeleteSymbolicLink( Path2, Attributes2, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot delete symbolic link. Status %x\n", Status );
}
} else {
fprintf( stderr, "Cannot delete, Path2 is not a symbolic link.\n" );
}
exit (1);
} // fDelete
if (fDisplay) {
//
// Check existence of Path2 path getting the attributes.
//
IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
fprintf( stderr, "Could not find %s (error = %d)\n", argv[argc - 1], GetLastError() );
exit(1);
}
//
// Path2 needs to be a reparse point to display a symbolic link.
//
if (Attributes2 & FILE_ATTRIBUTE_REPARSE_POINT) {
//
// Build the full path for Path2, the only path name.
//
if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
Path2 = argv[argc - 1];
}
Status = DisplaySymbolicLink( Path2, Attributes2, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot display symbolic link. Status %x\n", Status );
}
} else {
fprintf( stderr, "Cannot display, Path2 is not a symbolic link.\n" );
}
exit (1);
} // fDisplay
if (fModify) {
//
// Check for the existence of Path1 getting its attributes.
//
IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
fprintf( stderr, "Could not find Path1 %s (error = %d)\n", argv[argc - 2], GetLastError() );
exit(1);
}
//
// Path1 needs to be a reparse point to modify a symbolic link.
//
if (Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT) {
//
// Build the full path for Path1 and Path2.
//
if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
Path1 = argv[argc - 2];
}
if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
Path2 = argv[argc - 1];
}
Status = CreateSymbolicLink( Path1, Path2, Attributes1, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot modify symbolic link. Status %x\n", Status );
}
} else {
fprintf( stderr, "Cannot modify, Path1 is not a symbolic link.\n" );
}
exit (1);
} // fModify
if (fRename) {
//
// Check for the existence of Path1 getting its attributes.
//
IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
fprintf( stderr, "Could not find Path1 %s (error = %d)\n", argv[argc - 2], GetLastError() );
exit(1);
}
//
// Path1 needs to be a reparse point to rename a symbolic link.
//
if (Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT) {
//
// Build the full path for Path1 and Path2.
//
if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
Path1 = argv[argc - 2];
}
if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
Path2 = argv[argc - 1];
}
Status = RenameSymbolicLink( Path1, Path2, Attributes1, fVerbose );
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Cannot rename symbolic link. Status %x\n", Status );
}
} else {
fprintf( stderr, "Cannot rename, Path1 is not a symbolic link.\n" );
}
exit (1);
} // fRename
//
// We should never go through here ...
//
fprintf( stderr, "flinks : NO ACTION WAS PERFORMED!\n" );
} // main
void
ParseArgs(
int argc,
char *argv[]
)
/*++
Routine Description:
Parses the input setting global flags.
Return Value:
void - no return.
--*/
{
int ArgCount,
FlagCount;
ArgCount = 1;
FlagCount = 0;
//
// Check that the number of arguments is two or more.
//
if (argc < 2) {
fprintf( stderr, "Too few arguments.\n" );
Usage();
}
do {
if (IsFlag( argv[ArgCount] )) {
//
// We want all flags to be immediatelly after the command name flinks and
// before all other arguments.
//
if ((ArgCount > 1) && (FlagCount == 0)) {
fprintf(stderr, "Flags need to precede the path arguments.\n" );
Usage();
}
//
// Verify flag consistency.
//
if ((fCopy) && (fModify)) {
fprintf(stderr, "Cannot do both copy and modify.\n" );
Usage();
}
if ((fCopy) && (fRename)) {
fprintf(stderr, "Cannot do both copy and rename.\n" );
Usage();
}
if ((fCopy) && (fDelete)) {
fprintf(stderr, "Cannot do both copy and delete.\n" );
Usage();
}
if ((fDelete) && (fModify)) {
fprintf(stderr, "Cannot do both delete and modify.\n" );
Usage();
}
if ((fDelete) && (fRename)) {
fprintf(stderr, "Cannot do both delete and rename.\n" );
Usage();
}
if ((fModify) && (fRename)) {
fprintf(stderr, "Cannot do both modify and rename.\n" );
Usage();
}
//
// Account for this flag.
//
FlagCount++;
//
// (IsFlag( argv[ArgCount] ))
//
} else {
//
// No flags were passed as this argument to flinks.
//
// When no flags are present the only valid call is: flinks path1 path2
//
// When flags are present these are valid: flinks -flags- path1
// ' flinks -flags- path1 path2
//
// For starters we only check that we have the correct number.
// We should also check that no more flags are present further along the way ...
//
if (FlagCount == 0) {
if (argc == 2) {
fprintf( stderr, "Too few arguments.\n" );
Usage();
}
if (argc != 3) {
fprintf( stderr, "Wrong number of arguments with flags not preceding path arguments.\n" );
Usage();
}
} else {
if (ArgCount + 3 <= argc) {
fprintf( stderr, "Too many arguments after flags.\n" );
Usage();
}
}
}
} while (ArgCount++ < argc - 1);
//
// When there is only one path argument we have more constraints:
//
if ((ArgCount - FlagCount) == 2) {
if (!fDelete &&
!fDisplay
) {
fprintf( stderr, "One path argument requires the delete or display flag.\n" );
Usage();
}
}
//
// For delete or display we can only have one path name.
//
if (fDelete ||
fDisplay
) {
if ((ArgCount - FlagCount) != 2) {
fprintf( stderr, "Delete or display have only one path argument.\n" );
Usage();
}
}
//
// Set fCreate when there are no flags or no actions.
//
if (FlagCount == 0) {
fCreate = TRUE;
}
if (!fCopy &&
!fDelete &&
!fModify &&
!fRename &&
!fDisplay
) {
fCreate = TRUE;
}
//
// Every argument is correct.
// Print appropriate verbose messages.
//
if (fVVerbose) {
fprintf( stdout, "\n" );
fprintf( stdout, "Very verbose is set.\n" );
}
if (fVerbose) {
if (!fVVerbose) {
fprintf( stdout, "\n" );
}
if (fCopy) {
fprintf( stdout, "Will do verbose copy.\n" );
} else if (fCreate) {
fprintf( stdout, "Will do verbose create.\n" );
} else if (fDelete) {
fprintf( stdout, "Will do verbose delete.\n" );
} else if (fDisplay) {
fprintf( stdout, "Will do verbose display.\n" );
} else if (fModify) {
fprintf( stdout, "Will do verbose modify.\n" );
} else if (fRename) {
fprintf( stdout, "Will do verbose rename.\n" );
}
}
} // ParseArgs
BOOLEAN
IsFlag(
char *argv
)
{
char *TmpArg;
if ((*argv == '/') || (*argv == '-')) {
if (strchr( argv, '?' ))
Usage();
TmpArg = _strlwr(argv);
while (*++TmpArg != '\0') {
switch (*TmpArg) {
case 'a' :
case 'A' :
fAlternateCreateDefault = TRUE;
break;
case 'c' :
case 'C' :
fCopy = TRUE;
break;
case 'd' :
case 'D' :
fDelete = TRUE;
break;
case 'm' :
case 'M' :
fModify = TRUE;
break;
case 'r' :
case 'R' :
fRename = TRUE;
break;
case 's' :
case 'S' :
LinkType = IO_REPARSE_TAG_SIS;
break;
case 'w' :
case 'W' :
fVVerbose = TRUE;
case 'v' :
case 'V' :
fVerbose = TRUE;
break;
case 'y' :
case 'Y' :
fDisplay = TRUE;
break;
case '/' :
case '-' :
break;
default :
fprintf( stderr, "Don't know flag(s) %s\n", argv );
Usage();
}
}
}
else return FALSE;
return TRUE;
} // IsFlag
void
Usage( void )
{
fprintf( stderr, "\n" );
fprintf( stderr, "Usage: flink [/acdmrvy?] [Path1] Path2 \n" );
fprintf( stderr, " flink Path1 Path2 establishes at Path1 a symbolic link to Path2.\n" );
fprintf( stderr, " The file at Path1 is created if it does not exist. \n" );
fprintf( stderr, " /a sets the alternate default of creating a directory \n" );
fprintf( stderr, " /c copies in Path2 the symbolic link in Path1 \n" );
fprintf( stderr, " /d deletes the symbolic link in Path2 \n" );
fprintf( stderr, " /m modifies the symbolic link Path1 to Path2 \n" );
fprintf( stderr, " /r renames the symbolic link Path1 to Path2 \n" );
fprintf( stderr, " /s creates a SIS link rather than a symbolic link \n" );
fprintf( stderr, " /v prints verbose output \n" );
fprintf( stderr, " /w prints very verbose output \n" );
fprintf( stderr, " /y displays the symbolic link in Path2 \n" );
fprintf( stderr, " /? prints this message \n" );
exit(1);
} // Usage
NTSTATUS
CreateSymbolicLink(
CHAR *SourceName,
CHAR *DestinationName,
ATTRIBUTE_TYPE FileAttributes,
BOOLEAN VerboseFlag
)
/*++
Routine Description:
Builds a symbolic link between SourceName and DestinationName.
Opens the file named by SourceName and sets a reparse point of type symbolic link
that points to DestinationName. No checks whatsoever are made in regards to the
destination.
If the symbolic link already exists, this routine will overwrite it.
Return Value:
NTSTATUS - returns the appropriate NT return code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE FileHandle;
ULONG OpenOptions;
UNICODE_STRING uSourceName,
uDestinationName,
uNewName,
uOldName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
//
// Allocate and initialize Unicode strings.
//
RtlCreateUnicodeStringFromAsciiz( &uSourceName, SourceName );
RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
RtlDosPathNameToNtPathName_U(
uSourceName.Buffer,
&uOldName,
NULL,
NULL );
//
// Open the existing (SourceName) pathname.
// Notice that symbolic links in the path they are traversed silently.
//
InitializeObjectAttributes(
&ObjectAttributes,
&uOldName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
if (VerboseFlag) {
fprintf( stdout, "Will set symbolic link from: %Z\n", &uOldName );
}
//
// Make sure that we call open with the appropriate flags for:
//
// (1) directory versus non-directory
//
OpenOptions = FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
OpenOptions |= FILE_DIRECTORY_FILE;
} else {
OpenOptions |= FILE_NON_DIRECTORY_FILE;
}
Status = NtOpenFile(
&FileHandle,
FILE_READ_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
SHARE_ALL,
OpenOptions );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "Open failed %s\n", SourceName );
return Status;
}
//
// Verify that this is an empty file object:
// (a) If it is a file then it should not have data in the unnamed data stream
// nor should it have any named data streams.
// (b) If it is a directory, it has no entries.
// This case does not require code as the NTFS reparse point mechanism
// checks for it.
//
{
FILE_STANDARD_INFORMATION StandardInformation;
PFILE_STREAM_INFORMATION StreamInformation;
CHAR Buffer[2048];
Status = NtQueryInformationFile(
FileHandle,
&IoStatusBlock,
&StandardInformation,
sizeof ( FILE_STANDARD_INFORMATION ),
FileStandardInformation );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "NtQueryInformation for standard information to %Z failed %x\n", &uSourceName, Status );
return Status;
}
if (StandardInformation.EndOfFile.LowPart > 0) {
//
// The unnamed data stream has bytes in it.
//
if (VerboseFlag) {
fprintf( stdout, "The unnamed data stream of %Z has eof of %d\n",
&uOldName, StandardInformation.EndOfFile.LowPart );
}
fprintf( stderr, "Symbolic link not created. File has data.\n" );
return Status;
}
//
// Go and get the stream information.
//
Status = NtQueryInformationFile(
FileHandle,
&IoStatusBlock,
Buffer,
2048,
FileStreamInformation );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "NtQueryInformation for streams to %Z failed %x\n",
&uSourceName, Status );
return Status;
}
//
// Process the Buffer of data.
//
if (VerboseFlag) {
fprintf( stdout, "IoStatusBlock.Status %d IoStatusBlock.Information %d\n",
IoStatusBlock.Status, IoStatusBlock.Information );
}
StreamInformation = (PFILE_STREAM_INFORMATION)Buffer;
if (VerboseFlag) {
fprintf( stdout, "StreamInformation->NextEntryOffset %d StreamInformation->StreamNameLength %d\n",
StreamInformation->NextEntryOffset, StreamInformation->StreamNameLength );
}
//
// There has to be exactly one data stream, the one called ::$DATA whose
// StreamNameLength is 14. If this is not the case fail the request.
//
if (StreamInformation->NextEntryOffset > 0) {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "Symbolic link not created. There are named streams.\n",
&uSourceName, Status );
return Status;
}
}
//
// Build the appropriate target (DestinationName) name.
//
RtlDosPathNameToNtPathName_U(
uDestinationName.Buffer,
&uNewName,
NULL,
NULL );
//
// SIS hack
//
uNewName = uDestinationName;
if (VerboseFlag) {
fprintf( stdout, "Will set symbolic link to: %Z (%Z)\n", &uNewName, &uDestinationName );
}
//
// Verify that the name is not too long for the reparse point.
//
if (uNewName.Length > (MAXIMUM_REPARSE_DATA_BUFFER_SIZE - FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB))) {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "Input length too long %x\n", uNewName.Length );
return STATUS_IO_REPARSE_DATA_INVALID;
}
//
// Verify that the target name:
//
// (1) ends in a trailing backslash only for directories
// (2) does not contain more than one colon (:), thus denoting a complex name
//
{
USHORT Index = (uNewName.Length / 2) - 1;
BOOLEAN SeenFirstColon = FALSE;
if (!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
if (uNewName.Buffer[Index] == L'\\') {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "Name ends in backslash %Z\n", &uNewName );
return STATUS_OBJECT_NAME_INVALID;
}
//
// We have the name of a directory to set a symbolic link.
//
} else {
//
// Preserve the backslash that represents the root directory of a
// volume. We assume that the root of a volume is denoted by an
// identifier (a traditional drive letter) followed by a colon (:).
//
// Silently avoid (delete for practical purposes) the trailing
// backlash file delimiter in all other cases.
// The backslash is two bytes long.
//
if ((uNewName.Buffer[Index - 1] != L':') &&
(uNewName.Buffer[Index] == L'\\')) {
uNewName.Length -= 2;
Index = (uNewName.Length / 2) - 1;
}
if (fVVerbose) {
fprintf( stdout, "Directory name shortened to: %Z\n", &uNewName );
}
}
while (Index > 0) {
if (uNewName.Buffer[Index] == L':') {
if (SeenFirstColon) {
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "More than one colon in the name %Z\n", &uNewName );
return STATUS_OBJECT_NAME_INVALID;
} else {
SeenFirstColon = TRUE;
}
}
Index --;
}
}
//
// Build the reparse point buffer.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
ReparseBufferHeader->ReparseTag = LinkType;
ReparseBufferHeader->ReparseDataLength = uNewName.Length;
ReparseBufferHeader->Reserved = 0xcaf;
RtlCopyMemory( ReparseBufferHeader->RDB,
uNewName.Buffer,
ReparseBufferHeader->ReparseDataLength );
//
// Set a symbolic link reparse point.
//
Status = NtFsControlFile(
FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SET_REPARSE_POINT,
ReparseBuffer,
FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB) + ReparseBufferHeader->ReparseDataLength,
NULL, // Output buffer
0 ); // Output buffer length
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "NtFsControlFile set failed %s\n", DestinationName );
//
// And return after cleaning up.
//
}
//
// Clean up and return.
//
RtlFreeUnicodeString( &uSourceName );
RtlFreeUnicodeString( &uDestinationName );
NtClose( FileHandle );
return Status;
} // CreateSymbolicLink
NTSTATUS
DeleteSymbolicLink(
CHAR *DestinationName,
ATTRIBUTE_TYPE FileAttributes,
BOOLEAN VerboseFlag
)
/*++
Routine Description:
Deletes a symbolic link existing at DestinationName.
DestinationName needs to denote a symbolic link.
Opens the file named by DestinationName and deletes a reparse point of type
symbolic link and also deletes the underlying file.
If the reparse point is not a symbolic link this routine will leave it undisturbed.
Return Value:
NTSTATUS - returns the appropriate NT return code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE FileHandle;
ULONG OpenOptions;
UNICODE_STRING uDestinationName,
uNewName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
FILE_DISPOSITION_INFORMATION DispositionInformation;
BOOLEAN foo = TRUE;
#define REPARSE_BUFFER_LENGTH 45 * sizeof(WCHAR) + sizeof(REPARSE_DATA_BUFFER)
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
UCHAR ReparseBuffer[REPARSE_BUFFER_LENGTH];
//
// Allocate and initialize Unicode strings.
//
RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
RtlDosPathNameToNtPathName_U(
uDestinationName.Buffer,
&uNewName,
NULL,
NULL );
//
// Open the existing (SourceName) pathname.
// Notice that if there are symbolic links in the path they are
// traversed silently.
//
InitializeObjectAttributes(
&ObjectAttributes,
&uNewName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
if (VerboseFlag) {
fprintf( stdout, "Will delete symbolic link in: %Z\n", &uNewName );
}
//
// Make sure that we call open with the appropriate flags for:
//
// (1) directory versus non-directory
// (2) reparse point
//
OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
OpenOptions |= FILE_DIRECTORY_FILE;
} else {
OpenOptions |= FILE_NON_DIRECTORY_FILE;
}
Status = NtOpenFile(
&FileHandle,
(ACCESS_MASK)DELETE | FILE_READ_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
SHARE_ALL,
OpenOptions );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "Open failed %s\n", DestinationName );
return Status;
}
//
// Build the reparse point buffer.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
ReparseBufferHeader->ReparseTag = LinkType;
ReparseBufferHeader->ReparseDataLength = 0;
ReparseBufferHeader->Reserved = 0xcabd;
//
// Delete a symbolic link reparse point.
//
Status = NtFsControlFile(
FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_DELETE_REPARSE_POINT,
ReparseBuffer,
FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB),
NULL, // Output buffer
0 ); // Output buffer length
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "NtFsControlFile delete failed %s\n", DestinationName );
NtClose( FileHandle );
return Status;
}
//
// Change the disposition of the file so as to delete it as well.
//
// Look in flinks.h for the kludge I needed to do to make the following line
// of code work:
// #define DeleteFileA DeleteFile
//
DispositionInformation.DeleteFile = TRUE;
if (VerboseFlag) {
fprintf( stdout, "Will set the delete flag for: %Z\n", &uNewName );
}
Status = NtSetInformationFile(
FileHandle,
&IoStatusBlock,
&DispositionInformation,
sizeof (FILE_DISPOSITION_INFORMATION),
FileDispositionInformation );
//
// Clean up and return.
//
NtClose( FileHandle );
RtlFreeUnicodeString( &uDestinationName );
return Status;
} // DeleteSymbolicLink
NTSTATUS
IntegerToBase36String(
ULONG Value,
char *String,
ULONG MaxLength)
/*++
Routine Description:
This does what RtlIntegerToUnicodeString(Value,36,String) would do if it
handled base 36. We use the same rules for digits as are normally used
in Hex: 0-9, followed by a-z. Note that we're intentionally using Arabic
numerals and English letters here rather than something localized because
this is intended to generate filenames that are never seen by users, and
are constant regardless of the language used on the machine.
Arguments:
Value - The ULONG to be converted into a base36 string
String - A pointer to a string to receive the result
MaxLength - the total size of the area pointed to by String
Return Value:
success or buffer overflow
--*/
{
ULONG numChars;
ULONG ValueCopy = Value;
ULONG currentCharacter;
// First, figure out the length by seeing how many times we can divide 36 into the value
for (numChars = 0; ValueCopy != 0; ValueCopy /= 36, numChars++) {
// No loop body
}
// Special case the value 0.
if (numChars == 0) {
ASSERT(Value == 0);
if (MaxLength < 2)
return STATUS_BUFFER_OVERFLOW;
String[0] = '0';
String[1] = 0;
return STATUS_SUCCESS;
}
// If the string is too short, quit now.
if (numChars * sizeof(char) + 1 > MaxLength) { // The +1 is for the terminating null
return STATUS_BUFFER_OVERFLOW;
}
// Convert the string character-by-character starting at the lowest order (and so rightmost) "digit"
ValueCopy = Value;
for (currentCharacter = 0 ; currentCharacter < numChars; currentCharacter++) {
ULONG digit = ValueCopy % 36;
ASSERT(ValueCopy != 0);
if (digit < 10) {
String[numChars - (currentCharacter + 1)] = (char)('0' + (ValueCopy % 36));
} else {
String[numChars - (currentCharacter + 1)] = (char)('a' + ((ValueCopy % 36) - 10));
}
ValueCopy /= 36;
}
ASSERT(ValueCopy == 0);
//
// Fill in the terminating null and we're done.
//
String[numChars] = 0;
return STATUS_SUCCESS;
}
NTSTATUS
IndexToFileName(
IN PLARGE_INTEGER Index,
OUT char *fileName,
IN ULONG MaxLength
)
/*++
Routine Description:
Given an index, returns the corresponding fully qualified file name.
Arguments:
Index - The CSINDEX to convert
fileName - A pointer to a string to receive the result
MaxLength - The size of the string printed to by fileName
Return Value:
success or buffer overflow
--*/
{
UNICODE_STRING substring;
NTSTATUS status;
ULONG fileNameLength;
//
// We generate the filename as low.high, where low.high is the
// base 36 representation of the CSIndex. We use this bizarre format in order to
// avoid (for as long as possible) filenames that are not unique 8.3 names. ULONGS
// in base 36 have at most 7 characters, so we don't exceed 8.3 until we hit an index
// value of just over 2 * 10^14, which takes over 6000 years at 1 index/millisecond.
//
status = IntegerToBase36String(Index->LowPart,fileName,MaxLength);
if (status != STATUS_SUCCESS) {
return status;
}
fileNameLength = strlen(fileName);
MaxLength -= fileNameLength;
// Stick in the dot in the middle.
if (MaxLength == 0) {
return STATUS_BUFFER_OVERFLOW;
}
*(fileName + strlen(fileName)) = '.';
fileNameLength++;
MaxLength--;
return IntegerToBase36String(Index->HighPart,(fileName + fileNameLength),MaxLength);
}
NTSTATUS
DisplaySymbolicLink(
CHAR *DestinationName,
ATTRIBUTE_TYPE FileAttributes,
BOOLEAN VerboseFlag
)
/*++
Routine Description:
Displays a symbolic link existing at DestinationName.
DestinationName needs to denote a symbolic link.
Opens the file named by DestinationName and gets a reparse point of type
symbolic link.
If the reparse point is not a symbolic link this routine will not display it.
Return Value:
NTSTATUS - returns the appropriate NT return code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE FileHandle;
ULONG OpenOptions;
UNICODE_STRING uDestinationName,
uNewName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
//
// Allocate and initialize Unicode string.
//
RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
RtlDosPathNameToNtPathName_U(
uDestinationName.Buffer,
&uNewName,
NULL,
NULL );
//
// Open the existing (SourceName) pathname.
// Notice that if there are symbolic links in the path they are
// traversed silently.
//
InitializeObjectAttributes(
&ObjectAttributes,
&uNewName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
if (VerboseFlag) {
fprintf( stdout, "Will display symbolic link in: %Z\n", &uNewName );
}
//
// Make sure that we call open with the appropriate flags for:
//
// (1) directory versus non-directory
// (2) reparse point
//
OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
OpenOptions |= FILE_DIRECTORY_FILE;
} else {
OpenOptions |= FILE_NON_DIRECTORY_FILE;
}
Status = NtOpenFile(
&FileHandle,
FILE_READ_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
SHARE_ALL,
OpenOptions );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "Open failed %s\n", DestinationName );
return Status;
}
//
// Get the reparse point.
//
Status = NtFsControlFile(
FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_GET_REPARSE_POINT,
NULL, // Input buffer
0, // Input buffer length
ReparseBuffer, // Output buffer
MAXIMUM_REPARSE_DATA_BUFFER_SIZE ); // Output buffer length
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uDestinationName );
fprintf( stderr, "NtFsControlFile get failed %x %s\n", IoStatusBlock.Information, DestinationName );
return Status;
}
//
// Decode the reparse point buffer to display the data.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
if (ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SIS) {
PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)ReparseBufferHeader->RDB;
char stringBuffer[100];
PCHAR guidString;
FILE_INTERNAL_INFORMATION internalInfo[1];
printf("SIS Reparse point, format version %d\n",sisReparseBuffer->ReparsePointFormatVersion);
if (RPC_S_OK != UuidToString(&sisReparseBuffer->CSid,&guidString)) {
printf("CSid unable to stringify\n");
} else {
printf("CSid %s\n",guidString);
}
if (STATUS_SUCCESS != IndexToFileName(&sisReparseBuffer->LinkIndex,stringBuffer,100)) {
printf("LinkIndex 0x%x.0x%x (unable to stringify)\n",sisReparseBuffer->LinkIndex.HighPart,
sisReparseBuffer->LinkIndex.LowPart);
} else {
printf("LinkIndex 0x%x.0x%x (%s)\n",sisReparseBuffer->LinkIndex.HighPart,
sisReparseBuffer->LinkIndex.LowPart,stringBuffer);
}
Status = NtQueryInformationFile(
FileHandle,
&IoStatusBlock,
internalInfo,
sizeof(FILE_INTERNAL_INFORMATION),
FileInternalInformation);
if (STATUS_SUCCESS != Status) {
printf("LinkFileNtfsId 0x%x.0x%x (unable to query internal info, 0x%x)\n",
sisReparseBuffer->LinkFileNtfsId.HighPart,sisReparseBuffer->LinkFileNtfsId.LowPart,
Status);
} else if (internalInfo->IndexNumber.QuadPart == sisReparseBuffer->LinkFileNtfsId.QuadPart) {
printf("LinkFileNtfsId 0x%x.0x%x (matches actual id)\n",
sisReparseBuffer->LinkFileNtfsId.HighPart,sisReparseBuffer->LinkFileNtfsId.LowPart);
} else {
printf("LinkFileNtfsId 0x%x.0x%x (!= actual Id 0x%x.0x%x)\n",
sisReparseBuffer->LinkFileNtfsId.HighPart,sisReparseBuffer->LinkFileNtfsId.LowPart,
internalInfo->IndexNumber.HighPart,internalInfo->IndexNumber.LowPart);
}
printf("CSFileNtfsId 0x%x.0x%x\n",sisReparseBuffer->CSFileNtfsId.HighPart,sisReparseBuffer->CSFileNtfsId.LowPart);
printf("CSFileChecksum 0x%x.0x%x\n",sisReparseBuffer->CSChecksum.HighPart,sisReparseBuffer->CSChecksum.LowPart);
} else if (ReparseBufferHeader->ReparseTag != LinkType) {
fprintf( stderr, "Reparse point is not a symbolic link: tag %x\n", ReparseBufferHeader->ReparseTag );
Status = STATUS_OBJECT_NAME_INVALID;
} else {
UNICODE_STRING UniString;
UniString.Length = ReparseBufferHeader->ReparseDataLength;
UniString.Buffer = (PWCHAR)&ReparseBufferHeader->RDB[0];
if (fVerbose) {
fprintf( stdout, "The symbolic link is: " );
}
fprintf( stdout, "%Z\n", &UniString );
}
//
// Clean up and return.
//
NtClose( FileHandle );
RtlFreeUnicodeString( &uDestinationName );
return Status;
} // DisplaySymbolicLink
NTSTATUS
CreateEmptyFile(
CHAR *DestinationName,
ATTRIBUTE_TYPE FileAttributes,
BOOLEAN VerboseFlag
)
/*++
Routine Description:
Creates an empty file or directory, according to fileAttributes.
Return Value:
NTSTATUS - returns the appropriate NT return code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE FileHandle;
ULONG DesiredAccess = FILE_READ_DATA | SYNCHRONIZE;
ULONG CreateDisposition = FILE_OPEN_IF | FILE_OPEN;
ULONG CreateOptions;
ULONG ShareAccess = SHARE_ALL;
UNICODE_STRING uDestinationName,
uFileName;
//
// Initialize CreateOptions correctly.
//
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
CreateOptions = FILE_DIRECTORY_FILE;
} else {
CreateOptions = FILE_NON_DIRECTORY_FILE;
}
//
// Allocate and initialize Unicode string.
//
RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
RtlDosPathNameToNtPathName_U(
uDestinationName.Buffer,
&uFileName,
NULL,
NULL );
InitializeObjectAttributes(
&ObjectAttributes,
&uFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
if (VerboseFlag) {
if (CreateOptions & FILE_DIRECTORY_FILE) {
fprintf( stdout, "Will create the empty directory: %Z\n", &uFileName );
} else {
fprintf( stdout, "Will create the empty file: %Z\n", &uFileName );
}
}
Status = NtCreateFile(
&FileHandle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL, // pallocationsize (none!)
FILE_ATTRIBUTE_NORMAL,
ShareAccess,
CreateDisposition,
CreateOptions,
NULL, // EA buffer (none!)
0 );
NtClose( FileHandle );
return Status;
} // CreateEmptyFile
NTSTATUS
CopySymbolicLink(
CHAR *SourceName,
CHAR *DestinationName,
ATTRIBUTE_TYPE FileAttributes,
BOOLEAN VerboseFlag
)
/*++
Routine Description:
Copies the symbolic link existing at SourceName in DestinationName.
SourceName needs to denote a symbolic link.
DestinationName exists, and may or not be a symbolic link.
Return Value:
NTSTATUS - returns the appropriate NT return code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE FileHandle;
ULONG OpenOptions;
UNICODE_STRING uName,
uFinalName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
//
// Allocate and initialize Unicode string for SourceName. We will open it
// and retrieve the symbolic link it stores.
//
RtlCreateUnicodeStringFromAsciiz( &uName, SourceName );
RtlDosPathNameToNtPathName_U(
uName.Buffer,
&uFinalName,
NULL,
NULL );
//
// Open Path1 assuming that it is a reparse point (as it should be).
// Notice that if there are symbolic links in the path they are
// traversed silently.
//
InitializeObjectAttributes(
&ObjectAttributes,
&uFinalName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
if (VerboseFlag) {
fprintf( stdout, "Will retrieve symbolic link in: %Z\n", &uFinalName );
}
//
// Make sure that we call open with the appropriate flags for:
//
// (1) directory versus non-directory
// (2) reparse point
//
OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
OpenOptions |= FILE_DIRECTORY_FILE;
} else {
OpenOptions |= FILE_NON_DIRECTORY_FILE;
}
Status = NtOpenFile(
&FileHandle,
FILE_READ_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
SHARE_ALL,
OpenOptions );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Open as reparse point failed %s\n", SourceName );
return Status;
}
//
// Get the reparse point.
//
Status = NtFsControlFile(
FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_GET_REPARSE_POINT,
NULL, // Input buffer
0, // Input buffer length
ReparseBuffer, // Output buffer
MAXIMUM_REPARSE_DATA_BUFFER_SIZE ); // Output buffer length
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uName );
fprintf( stderr, "NtFsControlFile get failed %x %s\n", IoStatusBlock.Information, SourceName );
return Status;
}
//
// Free the name buffer.
//
RtlFreeUnicodeString( &uName );
//
// Decode the reparse point buffer to display the data.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
if (ReparseBufferHeader->ReparseTag != LinkType) {
fprintf( stderr, "Reparse point is not a symbolic link: tag %x\n", ReparseBufferHeader->ReparseTag );
NtClose( FileHandle );
return STATUS_OBJECT_NAME_INVALID;
} else {
UNICODE_STRING UniString;
UniString.Length = ReparseBufferHeader->ReparseDataLength;
UniString.Buffer = (PWCHAR)&ReparseBufferHeader->RDB[0];
if (fVerbose) {
fprintf( stdout, "The symbolic link is: %Z\n", &UniString );
}
}
//
// Close Path1.
//
NtClose( FileHandle );
//
// We now deal with Path2.
// Allocate and initialize Unicode string for DestinationName. We will open it
// and set a reparse point in it.
//
RtlCreateUnicodeStringFromAsciiz( &uName, DestinationName );
RtlDosPathNameToNtPathName_U(
uName.Buffer,
&uFinalName,
NULL,
NULL );
//
// We fail if this file is not created.
//
InitializeObjectAttributes(
&ObjectAttributes,
&uFinalName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
if (VerboseFlag) {
fprintf( stdout, "Will set symbolic link in: %Z\n", &uFinalName );
}
//
// Make sure that we open with the same options as Path1.
// We first try the reparse point case and trap the corresponsing error code.
//
Status = NtOpenFile(
&FileHandle,
FILE_READ_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
SHARE_ALL,
OpenOptions );
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Open failed %s\n", DestinationName );
return Status;
}
//
// The file in Path2 is open. We set the reparse point of type symbolic link.
//
Status = NtFsControlFile(
FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SET_REPARSE_POINT,
ReparseBuffer,
FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB) + ReparseBufferHeader->ReparseDataLength,
NULL, // Output buffer
0 ); // Output buffer length
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "NtFsControlFile set failed %s\n", DestinationName );
//
// And return after cleaning up.
//
}
//
// Free the name buffer and close Path2.
//
RtlFreeUnicodeString( &uName );
NtClose( FileHandle );
return Status;
} // CopySymbolicLink
NTSTATUS
RenameSymbolicLink(
CHAR *SourceName,
CHAR *DestinationName,
ATTRIBUTE_TYPE FileAttributes,
BOOLEAN VerboseFlag
)
{
NTSTATUS Status = STATUS_SUCCESS;
WCHAR *pch,
ch;
BOOLEAN LoopCondition = TRUE,
TranslationStatus = TRUE;
HANDLE FileHandle,
RootDirHandle;
ULONG OpenOptions;
USHORT Index = 0,
LastIndex = 0;
UNICODE_STRING uName,
uRelative,
uFinalName;
RTL_RELATIVE_NAME_U RelativeName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
FILE_RENAME_INFORMATION *RenameInformation = NULL;
//
// Allocate and initialize Unicode string for SourceName (Path1).
//
RtlCreateUnicodeStringFromAsciiz( &uName, SourceName );
TranslationStatus = RtlDosPathNameToRelativeNtPathName_U( uName.Buffer,
&uFinalName,
NULL,
&RelativeName );
if (!TranslationStatus) {
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Path not translated: %s\n", SourceName );
SetLastError(ERROR_PATH_NOT_FOUND);
return STATUS_OBJECT_NAME_INVALID;
}
//
// Open Path1 as a reparse point; as it needs to be.
// Notice that if there are symbolic links in the path they are
// traversed silently.
//
if (RelativeName.RelativeName.Length) {
uFinalName = *(PUNICODE_STRING)&RelativeName.RelativeName;
if (VerboseFlag) {
fprintf( stdout, "Relative name is: %Z\n", &uFinalName );
}
} else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(
&ObjectAttributes,
&uFinalName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL );
if (VerboseFlag) {
fprintf( stdout, "Will rename symbolic link in: %Z\n", &uFinalName );
}
//
// Make sure that we call open with the appropriate flags for:
//
// (1) directory versus non-directory
// (2) reparse point
//
OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
OpenOptions |= FILE_DIRECTORY_FILE;
} else {
OpenOptions |= FILE_NON_DIRECTORY_FILE;
}
Status = NtOpenFile(
&FileHandle,
(ACCESS_MASK)DELETE | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES| SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
SHARE_ALL,
OpenOptions );
RtlReleaseRelativeName(&RelativeName);
if (!NT_SUCCESS( Status )) {
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Open as reparse point failed %Z\n", &uFinalName );
return Status;
}
//
// Free the name for Path1.
//
RtlFreeUnicodeString( &uName );
//
// We now build the appropriate Unicode name for Path2.
//
RtlCreateUnicodeStringFromAsciiz( &uName, DestinationName );
TranslationStatus = RtlDosPathNameToNtPathName_U(
uName.Buffer,
&uFinalName,
NULL,
NULL );
if (!TranslationStatus) {
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Path not translated: %s\n", DestinationName );
SetLastError(ERROR_PATH_NOT_FOUND);
return STATUS_OBJECT_NAME_INVALID;
}
if (VerboseFlag) {
fprintf( stdout, "The complete destination is: %Z\n", &uFinalName );
}
//
// We use the uFinalName to build the name for the directory where
// the target file resides.
// We will pass the handle in the link information.
// The rest of the path will be given relative to this root.
// We depend on paths looking like "\DosDevices\X:\path".
//
Index = uFinalName.Length / 2; // to account for the Unicode widths
Index -= 1; // as arrays begin from zero
if ((uFinalName.Buffer[Index] == L'\\') || (Index <= 4)) {
//
// Last character is a backslash or the full name is too short;
// this is not a valid name.
//
NtClose( FileHandle );
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Bad Path2, ends in backslash or is too short (Index %d) %s\n", Index, DestinationName );
return STATUS_OBJECT_NAME_INVALID;
}
while ((Index > 0) && LoopCondition) {
if (uFinalName.Buffer[Index] == L'\\') {
LoopCondition = FALSE;
LastIndex = Index;
} else {
Index --;
}
}
uFinalName.Length = 2 * LastIndex;
if (VerboseFlag) {
fprintf( stdout, "The root directory is: %Z\n", &uFinalName );
}
InitializeObjectAttributes(
&ObjectAttributes,
&uFinalName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtCreateFile(
&RootDirHandle,
FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL, // pallocationsize (none!)
FILE_ATTRIBUTE_NORMAL,
SHARE_ALL,
FILE_OPEN_IF | FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT,
NULL, // EA buffer (none!)
0 );
if (!NT_SUCCESS( Status )) {
NtClose( FileHandle );
RtlFreeUnicodeString( &uName );
fprintf( stderr, "Could not get RootDirHandle %s\n", DestinationName );
return Status;
}
//
// Now get the path relative to the root.
//
RtlInitUnicodeString( &uRelative, &uFinalName.Buffer[LastIndex + 1] );
RenameInformation = malloc( sizeof(*RenameInformation) + uRelative.Length );
if (NULL == RenameInformation) {
NtClose( FileHandle );
NtClose( RootDirHandle );
RtlFreeUnicodeString( &uName );
return STATUS_NO_MEMORY;
}
RenameInformation->ReplaceIfExists = TRUE;
RenameInformation->RootDirectory = RootDirHandle;
RenameInformation->FileNameLength = uRelative.Length;
RtlMoveMemory( RenameInformation->FileName,
uRelative.Buffer,
uRelative.Length );
//
// Do the rename.
//
if (VerboseFlag) {
fprintf( stdout, "Will rename symbolic link to: %Z\n", &uRelative );
}
Status = NtSetInformationFile(
FileHandle,
&IoStatusBlock,
RenameInformation,
sizeof (FILE_RENAME_INFORMATION) + RenameInformation->FileNameLength,
FileRenameInformation );
if (Status == STATUS_NOT_SAME_DEVICE) {
fprintf( stderr, "Rename directed to a different device.\n" );
}
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "NtSetInformationFile failed (Status %X) %Z\n", Status, &uRelative );
}
//
// Close Path1 and the root of Path2, free the buffer and return.
//
NtClose( FileHandle );
NtClose( RootDirHandle );
RtlFreeUnicodeString( &uName );
free( RenameInformation );
return Status;
} // RenameSymbolicLink