Windows: Add Hard Link support to AFS Redirector
authorJeffrey Altman <jaltman@your-file-system.com>
Sat, 17 Nov 2012 03:27:02 +0000 (22:27 -0500)
committerJeffrey Altman <jaltman@your-file-system.com>
Sat, 24 Nov 2012 03:23:44 +0000 (19:23 -0800)
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 <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@your-file-system.com>
Tested-by: Jeffrey Altman <jaltman@your-file-system.com>

src/WINNT/afsrdr/common/AFSRedirCommonDefines.h
src/WINNT/afsrdr/common/AFSUserDefines.h
src/WINNT/afsrdr/common/AFSUserStructs.h
src/WINNT/afsrdr/kernel/lib/AFSCommSupport.cpp
src/WINNT/afsrdr/kernel/lib/AFSFileInfo.cpp
src/WINNT/afsrdr/kernel/lib/AFSVolumeInfo.cpp
src/WINNT/afsrdr/kernel/lib/Include/AFSCommon.h

index d98c015..3493c09 100644 (file)
 #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; }
index b3037b1..b637148 100644 (file)
@@ -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
 #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
 
index 8911a63..ab550d4 100644 (file)
@@ -603,6 +603,39 @@ typedef struct _AFS_FILE_RENAME_RESULT_CB
 
 
 //
+// 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
 //
index 47d160f..90f9444 100644 (file)
@@ -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,
index 526144e..ab13757 100644 (file)
@@ -661,7 +661,7 @@ AFSSetFileInfo( IN PDEVICE_OBJECT LibDeviceObject,
             case FileLinkInformation:
             {
 
-                ntStatus = STATUS_INVALID_DEVICE_REQUEST;
+                ntStatus = AFSSetFileLinkInfo( Irp);
 
                 break;
             }
@@ -2049,6 +2049,13 @@ AFSSetDispositionInfo( IN PIRP Irp,
             {
 
                 //
+                // 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
                 //
 
@@ -2155,6 +2162,355 @@ try_exit:
 }
 
 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))
         {
 
index c612583..45141be 100644 (file)
@@ -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;
index b3d885a..204b0ad 100644 (file)
@@ -203,6 +203,16 @@ AFSNotifyRename( IN AFSObjectInfoCB *ObjectInfo,
                  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,
                        IN BOOLEAN FastCall,
@@ -817,6 +827,9 @@ NTSTATUS
 AFSSetRenameInfo( IN PIRP Irp);
 
 NTSTATUS
+AFSSetFileLinkInfo( IN PIRP Irp);
+
+NTSTATUS
 AFSSetPositionInfo( IN PIRP Irp,
                     IN AFSDirectoryCB *DirectoryCB);