240 lines
5.8 KiB
C
240 lines
5.8 KiB
C
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
winlink.c
|
|
|
|
Abstract:
|
|
|
|
This module implements Win32 CreateHardLink
|
|
|
|
Author:
|
|
|
|
Felipe Cabrera (cabrera) 28-Feb-1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "basedll.h"
|
|
|
|
BOOL
|
|
APIENTRY
|
|
CreateHardLinkA(
|
|
LPCSTR lpLinkName,
|
|
LPCSTR lpExistingFileName,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to CreateHardLinkW
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
UNICODE_STRING UnicodeExistingFileName;
|
|
BOOL ReturnValue;
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpLinkName );
|
|
if (Unicode == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpExistingFileName) ) {
|
|
if (!Basep8BitStringToDynamicUnicodeString( &UnicodeExistingFileName, lpExistingFileName )) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
UnicodeExistingFileName.Buffer = NULL;
|
|
}
|
|
|
|
ReturnValue = CreateHardLinkW((LPCWSTR)Unicode->Buffer, (LPCWSTR)UnicodeExistingFileName.Buffer, lpSecurityAttributes);
|
|
|
|
RtlFreeUnicodeString(&UnicodeExistingFileName);
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
CreateHardLinkW(
|
|
LPCWSTR lpLinkName,
|
|
LPCWSTR lpExistingFileName,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A file can be made to be a hard link to an existing file.
|
|
The existing file can be a reparse point or not.
|
|
|
|
Arguments:
|
|
|
|
lpLinkName - Supplies the name of a file that is to be to be made a hard link. As
|
|
this is to be a new hard link, there should be no file or directory present
|
|
with this name.
|
|
|
|
lpExistingFileName - Supplies the name of an existing file that is the target for
|
|
the hard link.
|
|
|
|
lpSecurityAttributes - this is currently not used
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful.
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN TranslationStatus;
|
|
UNICODE_STRING OldFileName;
|
|
UNICODE_STRING NewFileName;
|
|
PVOID FreeBuffer;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PFILE_LINK_INFORMATION NewName;
|
|
HANDLE FileHandle = INVALID_HANDLE_VALUE;
|
|
BOOLEAN ReturnValue = FALSE;
|
|
|
|
//
|
|
// Check to see that both names are present.
|
|
//
|
|
|
|
if ( !ARGUMENT_PRESENT(lpLinkName) ||
|
|
!ARGUMENT_PRESENT(lpExistingFileName) ) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
OldFileName.Buffer = NULL;
|
|
NewFileName.Buffer = NULL;
|
|
|
|
try {
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
lpExistingFileName,
|
|
&OldFileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Initialize the object name.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&OldFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Account the inheritance of the security descriptor. Note: this argument has no effect currently
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(lpSecurityAttributes) ) {
|
|
ObjectAttributes.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor;
|
|
}
|
|
|
|
//
|
|
// Notice that FILE_OPEN_REPARSE_POINT inhibits the reparse behavior.
|
|
// Thus, the hard link is established to the local entity, be it a reparse
|
|
// point or not.
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&FileHandle,
|
|
FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_FLAG_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError( Status );
|
|
__leave;
|
|
}
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
lpLinkName,
|
|
&NewFileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
__leave;
|
|
}
|
|
|
|
NewName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), NewFileName.Length+sizeof(*NewName));
|
|
|
|
if ( NewName == NULL ) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
__leave;
|
|
}
|
|
|
|
RtlMoveMemory(NewName->FileName, NewFileName.Buffer, NewFileName.Length);
|
|
NewName->ReplaceIfExists = FALSE;
|
|
NewName->RootDirectory = NULL;
|
|
NewName->FileNameLength = NewFileName.Length;
|
|
|
|
Status = NtSetInformationFile(
|
|
FileHandle,
|
|
&IoStatusBlock,
|
|
NewName,
|
|
NewFileName.Length+sizeof(*NewName),
|
|
FileLinkInformation
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError( Status );
|
|
__leave;
|
|
}
|
|
|
|
ReturnValue = TRUE;
|
|
|
|
} finally {
|
|
|
|
//
|
|
// Cleanup allocate memory and handles
|
|
//
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
NtClose( FileHandle );
|
|
}
|
|
|
|
if (NewFileName.Buffer != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NewFileName.Buffer);
|
|
}
|
|
|
|
if (OldFileName.Buffer != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, OldFileName.Buffer);
|
|
}
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|