From: Jeffrey Altman Date: Sat, 17 Nov 2012 03:27:02 +0000 (-0500) Subject: Windows: Add Hard Link support to AFS Redirector X-Git-Tag: upstream/1.8.0_pre1^2~1788 X-Git-Url: https://git.michaelhowe.org/gitweb/?a=commitdiff_plain;h=2b86ae33fead57696488e4b8c425cf8ae1bdd0f1;p=packages%2Fo%2Fopenafs.git Windows: Add Hard Link support to AFS Redirector Both Windows and AFS support the notion of hard links to files. Add an implementation to the AFS redirector. The body of the functionality and the IOCTL to the service permits the specification of hard links to files across directory boundaries. There is a restriction within AFSSetFileLinkInfo() which prevents cross-directory requests. However, this can be taken out if AFS ever permits them. Decrement object information link counts on directory entry deletions. Do not delete object information context blocks if the link count is greater than 0. Increment link counts when hard links are added. A subsequent patchset will implement the afsd_service support. Change-Id: Iffabf480c0b43ab76feb5bdf3464a0bf1324e642 Reviewed-on: http://gerrit.openafs.org/8481 Tested-by: BuildBot Reviewed-by: Jeffrey Altman Tested-by: Jeffrey Altman --- diff --git a/src/WINNT/afsrdr/common/AFSRedirCommonDefines.h b/src/WINNT/afsrdr/common/AFSRedirCommonDefines.h index d98c015ce..3493c096c 100644 --- a/src/WINNT/afsrdr/common/AFSRedirCommonDefines.h +++ b/src/WINNT/afsrdr/common/AFSRedirCommonDefines.h @@ -127,6 +127,8 @@ #define AFS_AG_ENTRY_CB_TAG 'GAFA' #define AFS_PROCESS_AG_CB_TAG 'APFA' #define AFS_BYTERANGE_TAG '_RBA' +#define AFS_HARDLINK_REQUEST_TAG 'LFFA' + #define __Enter #define try_return(S) { S; goto try_exit; } diff --git a/src/WINNT/afsrdr/common/AFSUserDefines.h b/src/WINNT/afsrdr/common/AFSUserDefines.h index b3037b1a6..b63714893 100644 --- a/src/WINNT/afsrdr/common/AFSUserDefines.h +++ b/src/WINNT/afsrdr/common/AFSUserDefines.h @@ -91,6 +91,7 @@ #define AFS_REQUEST_TYPE_CREATE_SYMLINK 0x00000020 #define AFS_REQUEST_TYPE_RELEASE_FILE_ACCESS 0x00000021 #define AFS_REQUEST_TYPE_GET_VOLUME_SIZE_INFO 0x00000022 +#define AFS_REQUEST_TYPE_HARDLINK_FILE 0x00000023 // // Request Flags, these are passed up from the file system @@ -272,6 +273,7 @@ #define FILE_VOLUME_QUOTAS 0x00000020 // winnt #define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 // winnt #define FILE_SUPPORTS_OBJECT_IDS 0x00010000 // winnt +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 // winnt #endif diff --git a/src/WINNT/afsrdr/common/AFSUserStructs.h b/src/WINNT/afsrdr/common/AFSUserStructs.h index 8911a6308..ab550d4c6 100644 --- a/src/WINNT/afsrdr/common/AFSUserStructs.h +++ b/src/WINNT/afsrdr/common/AFSUserStructs.h @@ -602,6 +602,39 @@ typedef struct _AFS_FILE_RENAME_RESULT_CB } AFSFileRenameResultCB; +// +// File Hard Link CB +// + +typedef struct _AFS_FILE_HARDLINK_CB +{ + + AFSFileID SourceParentId; /* Must be directory */ + + AFSFileID TargetParentId; /* Must be directory */ + + BOOLEAN bReplaceIfExists; + + /* Source Name and FileID in Common Request Block */ + + USHORT TargetNameLength; + + WCHAR TargetName[ 1]; + +} AFSFileHardLinkCB; + +typedef struct _AFS_FILE_HARDLINK_RESULT_CB +{ + + LARGE_INTEGER SourceParentDataVersion; + + LARGE_INTEGER TargetParentDataVersion; + + AFSDirEnumEntry DirEnum; + +} AFSFileHardLinkResultCB; + + // // Control structures for AFS_REQUEST_TYPE_EVAL_TARGET_BY_ID // and AFS_REQUEST_TYPE_EVAL_TARGET_BY_NAME diff --git a/src/WINNT/afsrdr/kernel/lib/AFSCommSupport.cpp b/src/WINNT/afsrdr/kernel/lib/AFSCommSupport.cpp index 47d160f6d..90f944486 100644 --- a/src/WINNT/afsrdr/kernel/lib/AFSCommSupport.cpp +++ b/src/WINNT/afsrdr/kernel/lib/AFSCommSupport.cpp @@ -2066,6 +2066,385 @@ try_exit: return ntStatus; } + +NTSTATUS +AFSNotifyHardLink( IN AFSObjectInfoCB *ObjectInfo, + IN GUID *AuthGroup, + IN AFSObjectInfoCB *ParentObjectInfo, + IN AFSObjectInfoCB *TargetParentObjectInfo, + IN AFSDirectoryCB *SourceDirectoryCB, + IN UNICODE_STRING *TargetName, + IN BOOLEAN bReplaceIfExists, + OUT AFSDirectoryCB **TargetDirectoryCB) +{ + + NTSTATUS ntStatus = STATUS_SUCCESS; + AFSFileHardLinkCB *pHardLinkCB = NULL; + AFSFileHardLinkResultCB *pResultCB = NULL; + ULONG ulResultLen = 0; + AFSDirectoryCB *pDirNode = NULL; + ULONG ulCRC = 0; + BOOLEAN bReleaseParentLock = FALSE, bReleaseTargetParentLock = FALSE; + AFSDeviceExt *pDevExt = (AFSDeviceExt *) AFSRDRDeviceObject->DeviceExtension; + + __Enter + { + + // + // Init the control block for the request + // + + pHardLinkCB = (AFSFileHardLinkCB *)AFSExAllocatePoolWithTag( PagedPool, + PAGE_SIZE, + AFS_HARDLINK_REQUEST_TAG); + + if( pHardLinkCB == NULL) + { + + try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES); + } + + RtlZeroMemory( pHardLinkCB, + PAGE_SIZE); + + pHardLinkCB->SourceParentId = ParentObjectInfo->FileId; + + pHardLinkCB->TargetParentId = TargetParentObjectInfo->FileId; + + pHardLinkCB->TargetNameLength = TargetName->Length; + + RtlCopyMemory( pHardLinkCB->TargetName, + TargetName->Buffer, + TargetName->Length); + + pHardLinkCB->bReplaceIfExists = bReplaceIfExists; + + // + // Use the same buffer for the result control block + // + + pResultCB = (AFSFileHardLinkResultCB *)pHardLinkCB; + + ulResultLen = PAGE_SIZE; + + ntStatus = AFSProcessRequest( AFS_REQUEST_TYPE_HARDLINK_FILE, + AFS_REQUEST_FLAG_SYNCHRONOUS, + AuthGroup, + &SourceDirectoryCB->NameInformation.FileName, + &ObjectInfo->FileId, + pHardLinkCB, + sizeof( AFSFileHardLinkCB) + TargetName->Length, + pResultCB, + &ulResultLen); + + if( ntStatus != STATUS_SUCCESS) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSNotifyHardLink failed FID %08lX-%08lX-%08lX-%08lX Status %08lX\n", + ObjectInfo->FileId.Cell, + ObjectInfo->FileId.Volume, + ObjectInfo->FileId.Vnode, + ObjectInfo->FileId.Unique, + ntStatus); + + try_return( ntStatus); + } + + // + // Update the information from the returned data + // + + if ( ParentObjectInfo != TargetParentObjectInfo) + { + + AFSAcquireExcl( ParentObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock, + TRUE); + + bReleaseParentLock = TRUE; + + if ( ParentObjectInfo->DataVersion.QuadPart == pResultCB->SourceParentDataVersion.QuadPart - 1) + { + + ParentObjectInfo->DataVersion = pResultCB->SourceParentDataVersion; + } + else + { + + SetFlag( ParentObjectInfo->Flags, AFS_OBJECT_FLAGS_VERIFY); + + ParentObjectInfo->DataVersion.QuadPart = (ULONGLONG)-1; + } + } + + AFSAcquireExcl( TargetParentObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock, + TRUE); + + bReleaseTargetParentLock = TRUE; + + if ( TargetParentObjectInfo->DataVersion.QuadPart == pResultCB->TargetParentDataVersion.QuadPart - 1) + { + + TargetParentObjectInfo->DataVersion = pResultCB->TargetParentDataVersion; + } + else + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_WARNING, + "AFSNotifyHardLink Raced with an invalidate call and a re-enumeration for entry %wZ ParentFID %08lX-%08lX-%08lX-%08lX Version (%08lX:%08lX != %08lX:%08lX - 1)\n", + TargetName, + TargetParentObjectInfo->FileId.Cell, + TargetParentObjectInfo->FileId.Volume, + TargetParentObjectInfo->FileId.Vnode, + TargetParentObjectInfo->FileId.Unique, + TargetParentObjectInfo->DataVersion.HighPart, + TargetParentObjectInfo->DataVersion.LowPart, + pResultCB->TargetParentDataVersion.HighPart, + pResultCB->TargetParentDataVersion.LowPart); + + // + // We raced so go and lookup the directory entry in the parent + // + + ulCRC = AFSGenerateCRC( TargetName, + FALSE); + + AFSLocateCaseSensitiveDirEntry( TargetParentObjectInfo->Specific.Directory.DirectoryNodeHdr.CaseSensitiveTreeHead, + ulCRC, + &pDirNode); + + if( pDirNode != NULL) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink Located dir entry %p for file %wZ\n", + pDirNode, + TargetName); + + if ( AFSIsEqualFID( &pDirNode->ObjectInformation->FileId, + &pResultCB->DirEnum.FileId)) + { + + InterlockedIncrement( &pDirNode->OpenReferenceCount); + + AFSDbgLogMsg( AFS_SUBSYSTEM_DIRENTRY_REF_COUNTING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink Increment count on %wZ DE %p Cnt %d\n", + &pDirNode->NameInformation.FileName, + pDirNode, + pDirNode->OpenReferenceCount); + + AFSReleaseResource( TargetParentObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock); + + try_return( ntStatus = STATUS_REPARSE); + } + else + { + + // + // We found an entry that matches the desired name but it is not the + // same as the one that was created for us by the file server. + // + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSNotifyHardLink Found matching name entry %wZ DE %p FID %08lX-%08lX-%08lX-%08lX != FID %08lX-%08lX-%08lX-%08lX\n", + TargetName, + pDirNode, + pDirNode->ObjectInformation->FileId.Cell, + pDirNode->ObjectInformation->FileId.Volume, + pDirNode->ObjectInformation->FileId.Vnode, + pDirNode->ObjectInformation->FileId.Unique, + pResultCB->DirEnum.FileId.Cell, + pResultCB->DirEnum.FileId.Volume, + pResultCB->DirEnum.FileId.Vnode, + pResultCB->DirEnum.FileId.Unique); + + if( pDirNode->OpenReferenceCount == 0) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink Different FIDs - Deleting DE %p for %wZ Old FID %08lX-%08lX-%08lX-%08lX New FID %08lX-%08lX-%08lX-%08lX\n", + pDirNode, + &pDirNode->NameInformation.FileName, + pDirNode->ObjectInformation->FileId.Cell, + pDirNode->ObjectInformation->FileId.Volume, + pDirNode->ObjectInformation->FileId.Vnode, + pDirNode->ObjectInformation->FileId.Unique, + pResultCB->DirEnum.FileId.Cell, + pResultCB->DirEnum.FileId.Volume, + pResultCB->DirEnum.FileId.Vnode, + pResultCB->DirEnum.FileId.Unique); + + AFSDeleteDirEntry( TargetParentObjectInfo, + pDirNode); + } + else + { + + SetFlag( pDirNode->Flags, AFS_DIR_ENTRY_DELETED); + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink Different FIDs - Removing DE %p for %wZ Old FID %08lX-%08lX-%08lX-%08lX New FID %08lX-%08lX-%08lX-%08lX\n", + pDirNode, + &pDirNode->NameInformation.FileName, + pDirNode->ObjectInformation->FileId.Cell, + pDirNode->ObjectInformation->FileId.Volume, + pDirNode->ObjectInformation->FileId.Vnode, + pDirNode->ObjectInformation->FileId.Unique, + pResultCB->DirEnum.FileId.Cell, + pResultCB->DirEnum.FileId.Volume, + pResultCB->DirEnum.FileId.Vnode, + pResultCB->DirEnum.FileId.Unique); + + AFSRemoveNameEntry( TargetParentObjectInfo, + pDirNode); + } + + pDirNode = NULL; + } + } + + // + // We are unsure of our current data so set the verify flag. It may already be set + // but no big deal to reset it + // + + SetFlag( TargetParentObjectInfo->Flags, AFS_OBJECT_FLAGS_VERIFY); + + TargetParentObjectInfo->DataVersion.QuadPart = (ULONGLONG)-1; + } + + // + // Create the hard link entry + // + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink Creating new entry %wZ\n", + TargetName); + + // + // Initialize the directory entry + // + + pDirNode = AFSInitDirEntry( TargetParentObjectInfo, + TargetName, + NULL, + &pResultCB->DirEnum, + (ULONG)InterlockedIncrement( &TargetParentObjectInfo->Specific.Directory.DirectoryNodeHdr.ContentIndex)); + + if( pDirNode == NULL) + { + + SetFlag( TargetParentObjectInfo->Flags, AFS_OBJECT_FLAGS_VERIFY); + + TargetParentObjectInfo->DataVersion.QuadPart = (ULONGLONG)-1; + + AFSReleaseResource( TargetParentObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock); + + try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Init the short name if we have one + // + + if( !BooleanFlagOn( pDevExt->DeviceFlags, AFS_DEVICE_FLAG_DISABLE_SHORTNAMES) && + pResultCB->DirEnum.ShortNameLength > 0) + { + + UNICODE_STRING uniShortName; + + pDirNode->NameInformation.ShortNameLength = pResultCB->DirEnum.ShortNameLength; + + RtlCopyMemory( pDirNode->NameInformation.ShortName, + pResultCB->DirEnum.ShortName, + pDirNode->NameInformation.ShortNameLength); + + // + // Generate the short name index + // + + uniShortName.Length = pDirNode->NameInformation.ShortNameLength; + uniShortName.Buffer = pDirNode->NameInformation.ShortName; + + pDirNode->Type.Data.ShortNameTreeEntry.HashIndex = AFSGenerateCRC( &uniShortName, + TRUE); + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink Initialized short name %wZ for DE %p for %wZ\n", + &uniShortName, + pDirNode, + &pDirNode->NameInformation.FileName); + } + else + { + // + // No short name or short names are disabled + // + + pDirNode->Type.Data.ShortNameTreeEntry.HashIndex = 0; + } + + if ( !BooleanFlagOn( TargetParentObjectInfo->Flags, AFS_OBJECT_FLAGS_VERIFY)) + { + + // + // Update the target parent data version + // + + TargetParentObjectInfo->DataVersion = pResultCB->TargetParentDataVersion; + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSNotifyHardLink entry %wZ ParentFID %08lX-%08lX-%08lX-%08lX Version %08lX:%08lX\n", + TargetName, + TargetParentObjectInfo->FileId.Cell, + TargetParentObjectInfo->FileId.Volume, + TargetParentObjectInfo->FileId.Vnode, + TargetParentObjectInfo->FileId.Unique, + TargetParentObjectInfo->DataVersion.QuadPart); + } + +try_exit: + + if ( bReleaseTargetParentLock) + { + + AFSReleaseResource( TargetParentObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock); + } + + if ( bReleaseParentLock) + { + + AFSReleaseResource( ParentObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock); + } + + if( pHardLinkCB != NULL) + { + + AFSExFreePoolWithTag( pHardLinkCB, AFS_HARDLINK_REQUEST_TAG); + } + + if ( TargetDirectoryCB) + { + + *TargetDirectoryCB = pDirNode; + } + } + + return ntStatus; +} + + + NTSTATUS AFSNotifyRename( IN AFSObjectInfoCB *ObjectInfo, IN GUID *AuthGroup, diff --git a/src/WINNT/afsrdr/kernel/lib/AFSFileInfo.cpp b/src/WINNT/afsrdr/kernel/lib/AFSFileInfo.cpp index 526144e00..ab13757a3 100644 --- a/src/WINNT/afsrdr/kernel/lib/AFSFileInfo.cpp +++ b/src/WINNT/afsrdr/kernel/lib/AFSFileInfo.cpp @@ -661,7 +661,7 @@ AFSSetFileInfo( IN PDEVICE_OBJECT LibDeviceObject, case FileLinkInformation: { - ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ntStatus = AFSSetFileLinkInfo( Irp); break; } @@ -2048,6 +2048,13 @@ AFSSetDispositionInfo( IN PIRP Irp, if( pFcb->Header.NodeTypeCode == AFS_DIRECTORY_FCB) { + // + // Reduce the Link count in the object information block + // to correspond with the deletion of the directory entry. + // + + pFcb->ObjectInformation->Links--; + // // Check if this is a directory that there are not currently other opens // @@ -2154,6 +2161,355 @@ try_exit: return ntStatus; } +NTSTATUS +AFSSetFileLinkInfo( IN PIRP Irp) +{ + + NTSTATUS ntStatus = STATUS_SUCCESS; + AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension; + PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation( Irp); + IO_STATUS_BLOCK stIoSb = {0,0}; + PFILE_LINK_INFORMATION pFileLinkInfo = NULL; + PFILE_OBJECT pSrcFileObj = NULL; + PFILE_OBJECT pTargetFileObj = pIrpSp->Parameters.SetFile.FileObject; + AFSFcb *pSrcFcb = NULL, *pTargetDcb = NULL, *pTargetFcb = NULL; + AFSCcb *pSrcCcb = NULL, *pTargetDirCcb = NULL; + AFSObjectInfoCB *pSrcObject = NULL, *pTargetObject = NULL; + AFSObjectInfoCB *pSrcParentObject = NULL, *pTargetParentObject = NULL; + UNICODE_STRING uniSourceName, uniTargetName; + UNICODE_STRING uniFullTargetName, uniTargetParentName; + UNICODE_STRING uniShortName; + BOOLEAN bCommonParent = FALSE; + AFSDirectoryCB *pTargetDirEntry = NULL; + AFSDirectoryCB *pNewTargetDirEntry = NULL; + ULONG ulTargetCRC; + BOOLEAN bTargetEntryExists = FALSE; + LONG lCount; + BOOLEAN bReleaseTargetDirLock = FALSE; + AFSFileID stNewFid; + ULONG ulNotificationAction = 0, ulNotifyFilter = 0; + + __Enter + { + + pSrcFileObj = pIrpSp->FileObject; + + pSrcFcb = (AFSFcb *)pSrcFileObj->FsContext; + pSrcCcb = (AFSCcb *)pSrcFileObj->FsContext2; + + pSrcObject = pSrcFcb->ObjectInformation; + pSrcParentObject = pSrcFcb->ObjectInformation->ParentObjectInformation; + + pFileLinkInfo = (PFILE_LINK_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + // + // Perform some basic checks to ensure FS integrity + // + + if( pSrcFcb->Header.NodeTypeCode != AFS_FILE_FCB) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Attempt to non-file (INVALID_PARAMETER)\n"); + + try_return( ntStatus = STATUS_INVALID_PARAMETER); + } + + if( pTargetFileObj == NULL) + { + + if ( pFileLinkInfo->RootDirectory) + { + + // + // The target directory is provided by HANDLE + // RootDirectory is only set when the target directory is not the same + // as the source directory. + // + // AFS only supports hard links within a single directory. + // + // The IOManager should translate any Handle to a FileObject for us. + // However, the failure to receive a FileObject is treated as a fatal + // error. + // + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Attempt to link %wZ to alternate directory by handle INVALID_PARAMETER\n", + &pSrcCcb->DirectoryCB->NameInformation.FileName); + + try_return( ntStatus = STATUS_INVALID_PARAMETER); + } + else + { + + uniFullTargetName.Length = (USHORT)pFileLinkInfo->FileNameLength; + + uniFullTargetName.Buffer = (PWSTR)&pFileLinkInfo->FileName; + + AFSRetrieveFinalComponent( &uniFullTargetName, + &uniTargetName); + + AFSRetrieveParentPath( &uniFullTargetName, + &uniTargetParentName); + + if ( uniTargetParentName.Length == 0) + { + + // + // This is a simple rename. Here the target directory is the same as the source parent directory + // and the name is retrieved from the system buffer information + // + + pTargetParentObject = pSrcParentObject; + } + else + { + // + // uniTargetParentName contains the directory the renamed object + // will be moved to. Must obtain the TargetParentObject. + // + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Attempt to link %wZ to alternate directory %wZ (NOT_SAME_DEVICE)\n", + &pSrcCcb->DirectoryCB->NameInformation.FileName, + &uniFullTargetName); + + try_return( ntStatus = STATUS_NOT_SAME_DEVICE); + } + } + + pTargetDcb = pTargetParentObject->Fcb; + } + else + { + + // + // So here we have the target directory taken from the targetfile object + // + + pTargetDcb = (AFSFcb *)pTargetFileObj->FsContext; + + pTargetDirCcb = (AFSCcb *)pTargetFileObj->FsContext2; + + pTargetParentObject = (AFSObjectInfoCB *)pTargetDcb->ObjectInformation; + + // + // Grab the target name which we setup in the IRP_MJ_CREATE handler. By how we set this up + // it is only the target component of the rename operation + // + + uniTargetName = *((PUNICODE_STRING)&pTargetFileObj->FileName); + } + + // + // The quick check to see if they are self linking. + // Do the names match? Only do this where the parent directories are + // the same + // + + if( pTargetParentObject == pSrcParentObject) + { + + if( FsRtlAreNamesEqual( &uniTargetName, + &uniSourceName, + FALSE, + NULL)) + { + try_return( ntStatus = STATUS_SUCCESS); + } + + bCommonParent = TRUE; + } + else + { + + // + // We do not allow cross-volume hard links + // + + if( pTargetParentObject->VolumeCB != pSrcObject->VolumeCB) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Attempt to link to different volume %wZ\n", + &pSrcCcb->DirectoryCB->NameInformation.FileName); + + try_return( ntStatus = STATUS_NOT_SAME_DEVICE); + } + } + + ulTargetCRC = AFSGenerateCRC( &uniTargetName, + FALSE); + + AFSAcquireExcl( pTargetParentObject->Specific.Directory.DirectoryNodeHdr.TreeLock, + TRUE); + + bReleaseTargetDirLock = TRUE; + + AFSLocateCaseSensitiveDirEntry( pTargetParentObject->Specific.Directory.DirectoryNodeHdr.CaseSensitiveTreeHead, + ulTargetCRC, + &pTargetDirEntry); + + if( pTargetDirEntry == NULL) + { + + // + // Missed so perform a case insensitive lookup + // + + ulTargetCRC = AFSGenerateCRC( &uniTargetName, + TRUE); + + AFSLocateCaseInsensitiveDirEntry( pTargetParentObject->Specific.Directory.DirectoryNodeHdr.CaseInsensitiveTreeHead, + ulTargetCRC, + &pTargetDirEntry); + } + + if ( !BooleanFlagOn( pDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_DISABLE_SHORTNAMES) && + pTargetDirEntry == NULL && RtlIsNameLegalDOS8Dot3( &uniTargetName, + NULL, + NULL)) + { + // + // Try the short name + // + AFSLocateShortNameDirEntry( pTargetParentObject->Specific.Directory.ShortNameTree, + ulTargetCRC, + &pTargetDirEntry); + } + + // + // Increment our ref count on the dir entry + // + + if( pTargetDirEntry != NULL) + { + + ASSERT( pTargetParentObject == pTargetDirEntry->ObjectInformation->ParentObjectInformation); + + lCount = InterlockedIncrement( &pTargetDirEntry->OpenReferenceCount); + + if( !pFileLinkInfo->ReplaceIfExists) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Attempt to link with target collision %wZ Target %wZ\n", + &pSrcCcb->DirectoryCB->NameInformation.FileName, + &pTargetDirEntry->NameInformation.FileName); + + try_return( ntStatus = STATUS_OBJECT_NAME_COLLISION); + } + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Target %wZ exists DE %p Count %08lX, performing delete of target\n", + &pTargetDirEntry->NameInformation.FileName, + pTargetDirEntry, + pTargetDirEntry->OpenReferenceCount); + + // + // Pull the directory entry from the parent + // + + AFSRemoveDirNodeFromParent( pTargetParentObject, + pTargetDirEntry, + FALSE); + + bTargetEntryExists = TRUE; + } + else + { + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_VERBOSE, + "AFSSetFileLinkInfo Target does NOT exist, normal linking\n"); + } + + // + // OK, this is a simple rename. Issue the rename + // request to the service. + // + + ntStatus = AFSNotifyHardLink( pSrcFcb->ObjectInformation, + &pSrcCcb->AuthGroup, + pSrcFcb->ObjectInformation->ParentObjectInformation, + pTargetDcb->ObjectInformation, + pSrcCcb->DirectoryCB, + &uniTargetName, + pFileLinkInfo->ReplaceIfExists, + &pNewTargetDirEntry); + + if( !NT_SUCCESS( ntStatus)) + { + + AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING, + AFS_TRACE_LEVEL_ERROR, + "AFSSetFileLinkInfo Failed link of %wZ to target %wZ Status %08lX\n", + &pSrcCcb->DirectoryCB->NameInformation.FileName, + &uniTargetName, + ntStatus); + + try_return( ntStatus); + } + + AFSInsertDirectoryNode( pTargetDcb->ObjectInformation, + pNewTargetDirEntry, + TRUE); + + // + // Send notification for the target link file + // + + if( bTargetEntryExists || pNewTargetDirEntry) + { + + ulNotificationAction = FILE_ACTION_MODIFIED; + } + else + { + + ulNotificationAction = FILE_ACTION_ADDED; + } + + AFSFsRtlNotifyFullReportChange( pTargetParentObject->ParentObjectInformation, + pSrcCcb, + (ULONG)ulNotifyFilter, + (ULONG)ulNotificationAction); + + try_exit: + + if( !NT_SUCCESS( ntStatus)) + { + + if( bTargetEntryExists) + { + + AFSInsertDirectoryNode( pTargetDirEntry->ObjectInformation->ParentObjectInformation, + pTargetDirEntry, + FALSE); + } + } + + if( pTargetDirEntry != NULL) + { + + lCount = InterlockedDecrement( &pTargetDirEntry->OpenReferenceCount); + } + + if( bReleaseTargetDirLock) + { + + AFSReleaseResource( pTargetParentObject->Specific.Directory.DirectoryNodeHdr.TreeLock); + } + } + + return ntStatus; +} + NTSTATUS AFSSetRenameInfo( IN PIRP Irp) { @@ -2236,6 +2592,7 @@ AFSSetRenameInfo( IN PIRP Irp) } } + // // Extract off the final component name from the Fcb // @@ -2774,7 +3131,6 @@ AFSSetRenameInfo( IN PIRP Irp) try_exit: - if( !NT_SUCCESS( ntStatus)) { diff --git a/src/WINNT/afsrdr/kernel/lib/AFSVolumeInfo.cpp b/src/WINNT/afsrdr/kernel/lib/AFSVolumeInfo.cpp index c61258339..45141be43 100644 --- a/src/WINNT/afsrdr/kernel/lib/AFSVolumeInfo.cpp +++ b/src/WINNT/afsrdr/kernel/lib/AFSVolumeInfo.cpp @@ -426,6 +426,7 @@ AFSQueryFsAttributeInfo( IN AFSVolumeInfoCB *VolumeInfo, Buffer->FileSystemAttributes = (FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | + FILE_SUPPORTS_HARD_LINKS | FILE_SUPPORTS_REPARSE_POINTS); Buffer->MaximumComponentNameLength = 255; diff --git a/src/WINNT/afsrdr/kernel/lib/Include/AFSCommon.h b/src/WINNT/afsrdr/kernel/lib/Include/AFSCommon.h index b3d885aa0..204b0adc6 100644 --- a/src/WINNT/afsrdr/kernel/lib/Include/AFSCommon.h +++ b/src/WINNT/afsrdr/kernel/lib/Include/AFSCommon.h @@ -202,6 +202,16 @@ AFSNotifyRename( IN AFSObjectInfoCB *ObjectInfo, IN UNICODE_STRING *TargetName, OUT AFSFileID *UpdatedFID); +NTSTATUS +AFSNotifyHardLink( IN AFSObjectInfoCB *ObjectInfo, + IN GUID *AuthGroup, + IN AFSObjectInfoCB *ParentObjectInfo, + IN AFSObjectInfoCB *TargetParentObjectInfo, + IN AFSDirectoryCB *SourceDirectoryCB, + IN UNICODE_STRING *TargetName, + IN BOOLEAN bReplaceIfExists, + OUT AFSDirectoryCB **TargetDirectoryCB); + NTSTATUS AFSEvaluateTargetByID( IN AFSObjectInfoCB *ObjectInfo, IN GUID *AuthGroup, @@ -816,6 +826,9 @@ AFSSetDispositionInfo( IN PIRP Irp, NTSTATUS AFSSetRenameInfo( IN PIRP Irp); +NTSTATUS +AFSSetFileLinkInfo( IN PIRP Irp); + NTSTATUS AFSSetPositionInfo( IN PIRP Irp, IN AFSDirectoryCB *DirectoryCB);