Windows: RDR SymbolicLink create support
authorpete scott <pscott@kerneldrivers.com>
Tue, 5 Mar 2013 20:21:41 +0000 (13:21 -0700)
committerJeffrey Altman <jaltman@your-file-system.com>
Mon, 11 Mar 2013 13:52:27 +0000 (06:52 -0700)
Permit the redirector to handle Microsoft's IO_REPARSE_TAG_MOUNT_POINT
and IO_REPARSE_TAG_SYMLINK requests.   The IO_REPARSE_TAG_SYMLINK request
is issued as a result of a CreateSymbolicLink Win32 API.

Creating a symlink in Windows is not equivalent to the way a symlink is
created in AFS or UNIX.  Instead of creating a symlink object whose data
string represents the target and mode bits indicate that the stream should
be treated as a link, on Windows it is a two step process.

To create a symlink to a directory, create an empty directory and then
assign the reparse tag data to the directory object.  To create a symlink
to anything else, create an empty file and assign the reparse tag data to
the file.  Deleting a reparse point simply removes the reparse tag data
and not the underlying directory or file.

The way this will work for AFS is that assigning reparse data to an
existing directory or file will require that the object be deleted from
the directory and a new symlink object be created in its place.  This is
why upon successful completion of the upcall to the service the directory
object information has the AFS_OBJECT_FLAGS_DIRECTORY_ENUMERATED flag
cleared.

This patchset permits symlink creation but does not do anything to support
symlink removal.

Symlink target data is not validated.

Change-Id: Ie7019445a7c307dcb2cd47beee47d02e1a82145f
Reviewed-on: http://gerrit.openafs.org/9424
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Rod Widdowson <rdw@steadingsoftware.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/kernel/lib/AFSCommSupport.cpp
src/WINNT/afsrdr/kernel/lib/AFSFSControl.cpp
src/WINNT/afsrdr/kernel/lib/Include/AFSCommon.h

index 9c419fb..a5b08f2 100644 (file)
 #define AFS_PROCESS_AG_CB_TAG        'APFA'
 #define AFS_BYTERANGE_TAG            '_RBA'
 #define AFS_HARDLINK_REQUEST_TAG     'LFFA'
+#define AFS_SYMLINK_REQUEST_TAG      'YSFA'
 
 #define __Enter
 
index b6f729a..e37fe17 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
- * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Kernel Drivers, LLC.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Your File System, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * - Redistributions of source code must retain the above copyright notice,
  *   this list of conditions and the following disclaimer.
  * - Redistributions in binary form must reproduce the above copyright
- *   notice,
- *   this list of conditions and the following disclaimer in the
- *   documentation
- *   and/or other materials provided with the distribution.
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
  *   nor the names of their contributors may be used to endorse or promote
  *   products derived from this software without specific prior written
@@ -3515,3 +3513,148 @@ AFSIsExtentRequestQueued( IN AFSFileID *FileID,
 
     return bRequestQueued;
 }
+
+NTSTATUS
+AFSCreateSymlink( IN GUID *AuthGroup,
+                  IN AFSObjectInfoCB *ParentObjectInfo,
+                  IN UNICODE_STRING *FileName,
+                  IN AFSObjectInfoCB *ObjectInfo,
+                  IN UNICODE_STRING *TargetName)
+{
+
+    NTSTATUS                  ntStatus = STATUS_SUCCESS;
+    AFSCreateSymlinkCB       *pSymlinkCreate = NULL;
+    ULONG                     ulResultLen = 0;
+    AFSCreateSymlinkResultCB *pSymlinkResult = NULL;
+
+    __Enter
+    {
+
+        //
+        // Allocate our request and result structures
+        //
+
+        pSymlinkCreate = (AFSCreateSymlinkCB *)ExAllocatePoolWithTag( PagedPool,
+                                                                      sizeof( AFSCreateSymlinkCB) +
+                                                                          TargetName->Length,
+                                                                      AFS_SYMLINK_REQUEST_TAG);
+
+        if( pSymlinkCreate == NULL)
+        {
+
+            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
+        }
+
+        RtlZeroMemory( pSymlinkCreate,
+                       sizeof( AFSCreateSymlinkCB) +
+                             TargetName->Length);
+
+        pSymlinkResult = (AFSCreateSymlinkResultCB *)ExAllocatePoolWithTag( PagedPool,
+                                                                            PAGE_SIZE,
+                                                                            AFS_SYMLINK_REQUEST_TAG);
+
+        if( pSymlinkResult == NULL)
+        {
+
+            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
+        }
+
+        RtlZeroMemory( pSymlinkResult,
+                       PAGE_SIZE);
+
+        //
+        // Populate the request buffer
+        //
+
+        RtlCopyMemory( &pSymlinkCreate->ParentId,
+                       &ObjectInfo->ParentFileId,
+                       sizeof( AFSFileID));
+
+        pSymlinkCreate->TargetNameLength = TargetName->Length;
+
+        RtlCopyMemory( pSymlinkCreate->TargetName,
+                       TargetName->Buffer,
+                       TargetName->Length);
+
+        ulResultLen = PAGE_SIZE;
+
+        //
+        // Call the service to create the symlink entry
+        //
+
+        ntStatus = AFSProcessRequest( AFS_REQUEST_TYPE_CREATE_SYMLINK,
+                                      AFS_REQUEST_FLAG_SYNCHRONOUS,
+                                      AuthGroup,
+                                      FileName,
+                                      &ObjectInfo->FileId,
+                                      ObjectInfo->VolumeCB->VolumeInformation.Cell,
+                                      ObjectInfo->VolumeCB->VolumeInformation.CellLength,
+                                      pSymlinkCreate,
+                                      sizeof( AFSCreateSymlinkCB) +
+                                                TargetName->Length,
+                                      pSymlinkResult,
+                                      &ulResultLen);
+
+        if ( ntStatus == STATUS_FILE_DELETED )
+        {
+
+            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                          AFS_TRACE_LEVEL_ERROR,
+                          "AFSCreateSymlink failed FID %08lX-%08lX-%08lX-%08lX Status %08lX\n",
+                          ObjectInfo->FileId.Cell,
+                          ObjectInfo->FileId.Volume,
+                          ObjectInfo->FileId.Vnode,
+                          ObjectInfo->FileId.Unique,
+                          ntStatus);
+
+            SetFlag( ParentObjectInfo->Flags, AFS_OBJECT_FLAGS_VERIFY);
+
+            ClearFlag( ParentObjectInfo->Flags, AFS_OBJECT_FLAGS_DIRECTORY_ENUMERATED);
+
+            SetFlag( ObjectInfo->Flags, AFS_OBJECT_FLAGS_DELETED);
+
+            try_return( ntStatus = STATUS_ACCESS_DENIED);
+        }
+        else if( ntStatus != STATUS_SUCCESS)
+        {
+
+            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                          AFS_TRACE_LEVEL_ERROR,
+                          "AFSCreateSymlink 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);
+        }
+
+        //
+        // After successful creation the open object has been deleted and replaced by
+        // the actual symlink.
+        //
+
+        SetFlag( ParentObjectInfo->Flags, AFS_OBJECT_FLAGS_VERIFY);
+
+        ClearFlag( ParentObjectInfo->Flags, AFS_OBJECT_FLAGS_DIRECTORY_ENUMERATED);
+
+        SetFlag( ObjectInfo->Flags, AFS_OBJECT_FLAGS_DELETED);
+
+try_exit:
+
+        if( pSymlinkCreate != NULL)
+        {
+
+            AFSExFreePoolWithTag( pSymlinkCreate, AFS_SYMLINK_REQUEST_TAG);
+        }
+
+        if( pSymlinkResult != NULL)
+        {
+
+            AFSExFreePoolWithTag( pSymlinkResult, AFS_SYMLINK_REQUEST_TAG);
+        }
+    }
+
+    return ntStatus;
+}
index 337b7ad..77c691f 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
- * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Kernel Drivers, LLC.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Your File System, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * - Redistributions of source code must retain the above copyright notice,
  *   this list of conditions and the following disclaimer.
  * - Redistributions in binary form must reproduce the above copyright
- *   notice,
- *   this list of conditions and the following disclaimer in the
- *   documentation
- *   and/or other materials provided with the distribution.
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
  *   nor the names of their contributors may be used to endorse or promote
  *   products derived from this software without specific prior written
@@ -570,58 +568,231 @@ AFSProcessUserFsRequest( IN PIRP Irp)
             case FSCTL_SET_REPARSE_POINT:
             {
 
-                REPARSE_GUID_DATA_BUFFER *pReparseBuffer = (REPARSE_GUID_DATA_BUFFER *)Irp->AssociatedIrp.SystemBuffer;
+                REPARSE_GUID_DATA_BUFFER *pReparseGUIDBuffer = (REPARSE_GUID_DATA_BUFFER *)Irp->AssociatedIrp.SystemBuffer;
+                REPARSE_DATA_BUFFER *pReparseBuffer = NULL;
+                AFSReparseTagInfo *pReparseInfo = NULL;
+                AFSObjectInfoCB *pParentObjectInfo = NULL;
+                UNICODE_STRING uniTargetName;
+                ULONGLONG ullIndex = 0;
+                LONG lCount;
 
                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
-                              AFS_TRACE_LEVEL_VERBOSE_2,
+                              AFS_TRACE_LEVEL_VERBOSE,
                               "AFSProcessUserFsRequest Processing FSCTL_SET_REPARSE_POINT request %wZ Type 0x%x Attrib 0x%x\n",
                               &pCcb->DirectoryCB->NameInformation.FileName,
                               pCcb->DirectoryCB->ObjectInformation->FileType,
                               pCcb->DirectoryCB->ObjectInformation->FileAttributes);
 
-                //
-                // Check if we have the reparse entry set on the entry
-                //
-
-                if( !BooleanFlagOn( pCcb->DirectoryCB->ObjectInformation->FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT))
+                if( ulInputBufferLen < FIELD_OFFSET( REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer.DataBuffer))
                 {
 
-                    ntStatus = STATUS_NOT_A_REPARSE_POINT;
+                    ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
 
                     break;
                 }
 
-                if( ulInputBufferLen < FIELD_OFFSET( REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer.DataBuffer))
+                if( (pReparseGUIDBuffer->ReparseTag & 0x0000FFFF) == IO_REPARSE_TAG_OPENAFS_DFS)
                 {
 
-                    ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+                    if( RtlCompareMemory( &pReparseGUIDBuffer->ReparseGuid,
+                                          &GUID_AFS_REPARSE_GUID,
+                                          sizeof( GUID)) != sizeof( GUID))
+                    {
 
-                    break;
-                }
+                        ntStatus = STATUS_REPARSE_ATTRIBUTE_CONFLICT;
 
-                if( (pReparseBuffer->ReparseTag & 0x0000FFFF) != IO_REPARSE_TAG_OPENAFS_DFS)
+                        break;
+                    }
+
+                    if( ulInputBufferLen < FIELD_OFFSET( REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer.DataBuffer) +
+                                                                        sizeof( AFSReparseTagInfo))
+                    {
+
+                        ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+
+                        break;
+                    }
+
+                    pReparseInfo = (AFSReparseTagInfo *)pReparseGUIDBuffer->GenericReparseBuffer.DataBuffer;
+
+                    switch( pReparseInfo->SubTag)
+                    {
+
+                        case OPENAFS_SUBTAG_SYMLINK:
+                        {
+
+                            if( ulInputBufferLen < (ULONG)(FIELD_OFFSET( REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer.DataBuffer) +
+                                                           FIELD_OFFSET( AFSReparseTagInfo, AFSSymLink.Buffer) +
+                                                           pReparseInfo->AFSSymLink.SymLinkTargetLength))
+                            {
+
+                                ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+
+                                break;
+                            }
+
+                            uniTargetName.Length = pReparseInfo->AFSSymLink.SymLinkTargetLength;
+                            uniTargetName.MaximumLength = uniTargetName.Length;
+
+                            uniTargetName.Buffer = (WCHAR *)pReparseInfo->AFSSymLink.Buffer;
+
+                            break;
+                        }
+
+                        case OPENAFS_SUBTAG_UNC:
+                        {
+
+                            if( ulInputBufferLen < (ULONG)(FIELD_OFFSET( REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer.DataBuffer) +
+                                                           FIELD_OFFSET( AFSReparseTagInfo, UNCReferral.Buffer) +
+                                                           pReparseInfo->UNCReferral.UNCTargetLength))
+                            {
+
+                                ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+
+                                break;
+                            }
+
+                            uniTargetName.Length = pReparseInfo->UNCReferral.UNCTargetLength;
+                            uniTargetName.MaximumLength = uniTargetName.Length;
+
+                            uniTargetName.Buffer = (WCHAR *)pReparseInfo->UNCReferral.Buffer;
+
+                            break;
+                        }
+
+                        case OPENAFS_SUBTAG_MOUNTPOINT:
+                            //
+                            // Not yet handled
+                            //
+                        default:
+                        {
+
+                            ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+
+                            break;
+                        }
+                    }
+                }
+                else
                 {
+                    //
+                    // Handle Microsoft Reparse Tags
+                    //
 
-                    ntStatus = STATUS_IO_REPARSE_TAG_MISMATCH;
+                    switch( pReparseGUIDBuffer->ReparseTag)
+                    {
+
+                        case IO_REPARSE_TAG_MOUNT_POINT:
+                        {
+
+                            pReparseBuffer = (REPARSE_DATA_BUFFER *)Irp->AssociatedIrp.SystemBuffer;
+
+                            uniTargetName.Length = pReparseBuffer->MountPointReparseBuffer.PrintNameLength;
+                            uniTargetName.MaximumLength = uniTargetName.Length;
+
+                            uniTargetName.Buffer = (WCHAR *)((char *)pReparseBuffer->MountPointReparseBuffer.PathBuffer +
+                                                              pReparseBuffer->MountPointReparseBuffer.PrintNameOffset);
+
+                            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                                          AFS_TRACE_LEVEL_VERBOSE_2,
+                                          "AFSProcessUserFsRequest IO_REPARSE_TAG_MOUNT_POINT request %wZ\n",
+                                          &uniTargetName);
+
+                            ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+
+                            break;
+                        }
+
+                        case IO_REPARSE_TAG_SYMLINK:
+                        {
+
+                            pReparseBuffer = (REPARSE_DATA_BUFFER *)Irp->AssociatedIrp.SystemBuffer;
+
+                            uniTargetName.Length = pReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
+                            uniTargetName.MaximumLength = uniTargetName.Length;
+
+                            uniTargetName.Buffer = (WCHAR *)((char *)pReparseBuffer->SymbolicLinkReparseBuffer.PathBuffer +
+                                                              pReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset);
+
+                            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                                          AFS_TRACE_LEVEL_VERBOSE_2,
+                                          "AFSProcessUserFsRequest IO_REPARSE_TAG_SYMLINK request %wZ\n",
+                                          &uniTargetName);
+                            break;
+                        }
+
+                        default:
+                        {
+
+                            ntStatus = STATUS_IO_REPARSE_DATA_INVALID;
+
+                            break;
+                        }
+                    }
+                }
+
+                if( !NT_SUCCESS( ntStatus))
+                {
 
                     break;
                 }
 
-                if( RtlCompareMemory( &pReparseBuffer->ReparseGuid,
-                                      &GUID_AFS_REPARSE_GUID,
-                                      sizeof( GUID)) != sizeof( GUID))
+                //
+                // First thing is to locate/create our object information block
+                // for this entry
+                //
+
+                AFSAcquireExcl( pCcb->DirectoryCB->ObjectInformation->VolumeCB->ObjectInfoTree.TreeLock,
+                                TRUE);
+
+                ullIndex = AFSCreateLowIndex( &pCcb->DirectoryCB->ObjectInformation->ParentFileId);
+
+                ntStatus = AFSLocateHashEntry( pCcb->DirectoryCB->ObjectInformation->VolumeCB->ObjectInfoTree.TreeHead,
+                                               ullIndex,
+                                               (AFSBTreeEntry **)&pParentObjectInfo);
+
+                if ( NT_SUCCESS( ntStatus) &&
+                     pParentObjectInfo)
                 {
 
-                    ntStatus = STATUS_REPARSE_ATTRIBUTE_CONFLICT;
+                    lCount = AFSObjectInfoIncrement( pParentObjectInfo,
+                                                     AFS_OBJECT_REFERENCE_DIRENTRY);
 
-                    break;
+                    AFSDbgLogMsg( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                                  AFS_TRACE_LEVEL_VERBOSE,
+                                  "AFSProcessUserFsRequest Increment count on object %p Cnt %d\n",
+                                  pParentObjectInfo,
+                                  lCount);
                 }
 
+                AFSReleaseResource( pCcb->DirectoryCB->ObjectInformation->VolumeCB->ObjectInfoTree.TreeLock);
+
                 //
-                // For now deny access on this call
+                // Extract out the information to the call to the service
                 //
 
-                ntStatus = STATUS_INVALID_PARAMETER;
+                ntStatus = AFSCreateSymlink( &pCcb->AuthGroup,
+                                             pParentObjectInfo,
+                                             &pCcb->DirectoryCB->NameInformation.FileName,
+                                             pCcb->DirectoryCB->ObjectInformation,
+                                             &uniTargetName);
+
+                AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                              AFS_TRACE_LEVEL_VERBOSE_2,
+                              "AFSProcessUserFsRequest Processed FSCTL_SET_REPARSE_POINT request %wZ Type 0x%x Attrib 0x%x Status %08lX\n",
+                              &pCcb->DirectoryCB->NameInformation.FileName,
+                              pCcb->DirectoryCB->ObjectInformation->FileType,
+                              pCcb->DirectoryCB->ObjectInformation->FileAttributes,
+                              ntStatus);
+
+                lCount = AFSObjectInfoDecrement( pParentObjectInfo,
+                                                 AFS_OBJECT_REFERENCE_DIRENTRY);
+
+                AFSDbgLogMsg( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                              AFS_TRACE_LEVEL_VERBOSE,
+                              "AFSProcessUserFsRequest Decrement count on object %p Cnt %d\n",
+                              pParentObjectInfo,
+                              lCount);
 
                 break;
             }
index 2c137fa..fdbdc2f 100644 (file)
@@ -264,6 +264,13 @@ AFSIsExtentRequestQueued( IN AFSFileID *FileID,
                           IN LARGE_INTEGER *ExtentOffset,
                           IN ULONG Length);
 
+NTSTATUS
+AFSCreateSymlink( IN GUID *AuthGroup,
+                  IN AFSObjectInfoCB *ParentObjectInfo,
+                  IN UNICODE_STRING *FileName,
+                  IN AFSObjectInfoCB *ObjectInfo,
+                  IN UNICODE_STRING *TargetName);
+
 //
 // AFSCreate.cpp Prototypes
 //