/*++ 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 = NULL; 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 (NewName != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, NewName); } 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; }