/// <summary>
        /// Copies the <paramref name="sourceFileInfo"/> to the <paramref name="targetFileInfo"/>.
        /// </summary>
        /// <param name="sourceFileInfo">Source file to be copied.</param>
        /// <param name="targetFileInfo">Target file.</param>
        /// <returns><see langword="true"/>, if the file was created or overwritten; <see langword="false"/>, if the file was skipped.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="sourceFileInfo"/> or <paramref name="targetFileInfo"/> is <see langword="null"/>.</exception>
        /// <exception cref="FileNotFoundException"><paramref name="sourceFileInfo"/> does not exist.</exception>
        public static bool CopyFile(LongPathFileInfo sourceFileInfo, LongPathFileInfo targetFileInfo)
        {
            if (sourceFileInfo == null)
            {
                throw new ArgumentNullException(nameof(sourceFileInfo));
            }

            if (targetFileInfo == null)
            {
                throw new ArgumentNullException(nameof(targetFileInfo));
            }

            // Will throw an exception, if the source file does not exist.
            if (sourceFileInfo.Match(targetFileInfo))
            {
                return(false);
            }

            LongPathDirectory.CreateDirectory(targetFileInfo.DirectoryName);
            sourceFileInfo.CopyTo(targetFileInfo.FullName, true);

            LongPathCommon.SetAttributes(targetFileInfo.FullName, FileAttributes.Normal);
            LongPathCommon.SetTimestamps(targetFileInfo.FullName, sourceFileInfo.CreationTime, sourceFileInfo.LastAccessTime, sourceFileInfo.LastWriteTime);

            return(true);
        }
Esempio n. 2
0
        /// <summary>
        /// Gets the LazyCopy reparse data from the <paramref name="path"/> given.
        /// </summary>
        /// <param name="path">Path to the file to get the reparse data from.</param>
        /// <returns>
        /// Reparse data found or <see langword="null"/>, if it's not set.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="IOException">File cannot be opened.</exception>
        /// <exception cref="InvalidOperationException">Reparse point data cannot be retrieved.</exception>
        public static LazyCopyFileData GetReparseData(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            string           normalizedPath = LongPathCommon.NormalizePath(path);
            LongPathFileInfo fileInfo       = new LongPathFileInfo(normalizedPath);

            if (!fileInfo.Exists || !fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
            {
                return(null);
            }

            try
            {
                LazyCopyReparseData data = ReparsePointHelper.GetReparsePointData <LazyCopyReparseData>(path, LazyCopyFileHelper.LazyCopyReparseTag, LazyCopyFileHelper.LazyCopyReparseGuid);
                bool useCustomHandler    = data.UseCustomHandler > 0;

                return(new LazyCopyFileData
                {
                    UseCustomHandler = useCustomHandler,
                    FileSize = data.FileSize,
                    RemotePath = useCustomHandler ? data.RemotePath : PathHelper.ChangeDeviceNameToDriveLetter(data.RemotePath)
                });
            }
            catch (InvalidOperationException)
            {
                return(null);
            }
        }
Esempio n. 3
0
        string IDirectoryAdapter.GetFullPath(string path)
        {
            AssertAllowed(path);
#if !MONO
            var mTx = CurrentTransaction();
            if (mTx.HasValue)
            {
                return(((IDirectoryAdapter)mTx.Value).GetFullPath(path));
            }
#endif
            return(LongPathCommon.NormalizeLongPath(path));
        }
Esempio n. 4
0
        public static string GetFullPath(string path)
        {
            Contract.Requires(!string.IsNullOrEmpty(path));
            Contract.Ensures(Contract.Result <string>() != null);

            if (path.StartsWith("file:///"))
            {
                return(new Uri(path).LocalPath);
            }

            return(LongPathCommon.NormalizeLongPath(path));
        }
Esempio n. 5
0
        /// <summary>
        /// Changes the DOS device name for the <paramref name="path"/> given to the according drive letter.
        /// </summary>
        /// <param name="path">Path to replace DOS device name for.</param>
        /// <returns>New path string.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="ArgumentException"><paramref name="path"/> does not start with a DOS device name.</exception>
        /// <exception cref="InvalidOperationException">Drive letter could not be found for the <paramref name="path"/> given.</exception>
        /// <remarks>
        /// This method also supports UNC paths.
        /// </remarks>
        public static string ChangeDeviceNameToDriveLetter(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            path = LongPathCommon.RemoveLongPathPrefix(path);

            // Path is already converted.
            if (Regex.IsMatch(path, @"^(?:\\\\|[a-z]:)", RegexOptions.IgnoreCase))
            {
                return(path);
            }

            if (!path.StartsWith(@"\Device\", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Path given does not start with a device name: {0}", path), nameof(path));
            }

            // Convert Network Redirector path to UNC.
            if (path.StartsWith(@"\Device\Mup\", StringComparison.OrdinalIgnoreCase))
            {
                return(@"\\" + path.Substring(12));
            }

            string driveLetter;
            string deviceName = null;

            // Find the proper device name.
            lock (PathHelper.DriveLetterToDeviceName)
            {
                Func <string> findDeviceName = () => deviceName = PathHelper.DriveLetterToDeviceName.Values.FirstOrDefault(dn => path.StartsWith(dn, StringComparison.OrdinalIgnoreCase));
                if (findDeviceName() == null)
                {
                    PathHelper.UpdateDeviceMappings();

                    if (findDeviceName() == null)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to find root path for {0}", path));
                    }
                }

                // Find the root path for the device name given.
                driveLetter = PathHelper.DriveLetterToDeviceName.Keys.First(rp => string.Equals(PathHelper.DriveLetterToDeviceName[rp], deviceName));
            }

            // Change parent.
            return(driveLetter + path.Substring(deviceName.Length));
        }
Esempio n. 6
0
        /// <summary>
        /// Changes the drive letter for the <paramref name="path"/> given to the according DOS device name.
        /// </summary>
        /// <param name="path">Path to replace the drive letter for.</param>
        /// <returns>New path string.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="ArgumentException"><paramref name="path"/> is too short.</exception>
        /// <exception cref="InvalidOperationException">The according DOS device name could not be found.</exception>
        /// <remarks>
        /// This method also supports UNC paths.
        /// </remarks>
        public static string ChangeDriveLetterToDeviceName(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (path.Length < 2)
            {
                throw new ArgumentException("Path is too short.", nameof(path));
            }

            // If the path is already converted, skip.
            if (path.StartsWith(@"\Device\", StringComparison.OrdinalIgnoreCase))
            {
                return(path);
            }

            path = LongPathCommon.RemoveLongPathPrefix(path);

            // Convert UNC path to a Network Redirector path.
            if (path.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase) || path.StartsWith(@"//", StringComparison.OrdinalIgnoreCase))
            {
                return(@"\Device\Mup\" + path.Substring(2));
            }

            // C:\, for example. Or will contain invalid string and will fail later.
            string driveLetter = path.Substring(0, 2) + "\\";
            string deviceName;

            lock (PathHelper.DriveLetterToDeviceName)
            {
                // Update the device-root caches, if the root path is not there.
                if (!PathHelper.DriveLetterToDeviceName.ContainsKey(driveLetter))
                {
                    PathHelper.UpdateDeviceMappings();

                    if (!PathHelper.DriveLetterToDeviceName.ContainsKey(driveLetter))
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to find DOS device name for path: {0}", path));
                    }
                }

                deviceName = PathHelper.DriveLetterToDeviceName[driveLetter];
            }

            // Change parent.
            return(deviceName + path.Substring(driveLetter.Length));
        }
Esempio n. 7
0
        // http://stackoverflow.com/questions/8991192/check-filesize-without-opening-file-in-c
        public override long GetSize()
        {
            using (var fh = LongPathFile.GetFileHandle(Path,
                                                       FileMode.Open, FileAccess.Read, FileShare.ReadWrite, FileOptions.None))
            {
                if (fh.IsInvalid)
                {
                    throw LongPathCommon.GetExceptionFromLastWin32Error();
                }

                long size;
                if (NativeMethods.GetFileSizeEx(fh, out size))
                {
                    return(size);
                }

                throw LongPathCommon.GetExceptionFromLastWin32Error();
            }
        }
Esempio n. 8
0
        public override DateTimeOffset?GetLastModifiedTimeUtc()
        {
            if (!Exists())
            {
                return(null);
            }

            WIN32_FILE_ATTRIBUTE_DATA data;

            if (!NativeMethods.GetFileAttributesEx(
                    Path.LongFullPath,
                    GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard,
                    out data))
            {
                throw LongPathCommon.GetExceptionFromLastWin32Error();
            }

            var ft = data.ftLastWriteTime;

            return(DateTime.FromFileTime((((long)ft.dwHighDateTime) << 32) + ft.dwLowDateTime));
        }
        /// <summary>
        /// Creates the symbolic link.
        /// </summary>
        /// <param name="linkPath">Link path.</param>
        /// <param name="targetPath">Target path.</param>
        /// <param name="linkFlag">Type of the symbolic link.</param>
        /// <exception cref="ArgumentNullException"><paramref name="targetPath"/> or <paramref name="linkPath"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="ArgumentException"><paramref name="targetPath"/> is equal to the <paramref name="linkPath"/>.</exception>
        /// <exception cref="FileNotFoundException"><paramref name="targetPath"/> was not found and <paramref name="linkFlag"/> is <see cref="SymbolicLinkFlag.File"/>.</exception>
        /// <exception cref="DirectoryNotFoundException"><paramref name="targetPath"/> was not found and <paramref name="linkFlag"/> is <see cref="SymbolicLinkFlag.Directory"/>.</exception>
        /// <exception cref="InvalidOperationException">Symbolic link was not created.</exception>
        /// <remarks>
        /// The <c>SE_CREATE_SYMBOLIC_LINK_NAME</c> privilege is required to create symbolic links.
        /// </remarks>
        private static void CreateSymbolicLink(string linkPath, string targetPath, SymbolicLinkFlag linkFlag)
        {
            if (string.IsNullOrEmpty(targetPath))
            {
                throw new ArgumentNullException(nameof(targetPath));
            }

            if (string.IsNullOrEmpty(linkPath))
            {
                throw new ArgumentNullException(nameof(linkPath));
            }

            if (string.Equals(targetPath, linkPath, StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Target path is equal to the link path: {0}", targetPath));
            }

            string normalizedLinkPath   = LongPathCommon.NormalizePath(linkPath);
            string normalizedTargetPath = LongPathCommon.NormalizePath(targetPath);

            if (!LongPathCommon.Exists(targetPath))
            {
                if (linkFlag == SymbolicLinkFlag.Directory)
                {
                    throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, "Target directory not found: {0}", targetPath));
                }

                throw new FileNotFoundException("Target file not found.", targetPath);
            }

            if (!NativeMethods.CreateSymbolicLink(normalizedLinkPath, normalizedTargetPath, linkFlag))
            {
                Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create symbolic link: {0} -> {1}", linkPath, targetPath), nativeException);
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Creates a new <c>LazyCopy</c> file.
        /// </summary>
        /// <param name="path">Path to the reparse point to get data from.</param>
        /// <param name="fileData">Reparse file data to be set for the <paramref name="path"/>.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="path"/> is <see langword="null"/> or empty.
        ///     <para>-or-</para>
        /// <paramref name="fileData"/> is <see langword="null"/>.
        ///     <para>-or-</para>
        /// <paramref name="fileData"/> contains <see langword="null"/> or empty file path.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="fileData"/> contains negative file size.</exception>
        /// <exception cref="IOException">File cannot be created.</exception>
        /// <exception cref="InvalidOperationException">Reparse point data cannot be set.</exception>
        public static void CreateLazyCopyFile(string path, LazyCopyFileData fileData)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (fileData == null)
            {
                throw new ArgumentNullException(nameof(fileData));
            }

            if (string.IsNullOrEmpty(fileData.RemotePath))
            {
                throw new ArgumentNullException(nameof(fileData));
            }

            if (fileData.FileSize < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(fileData), fileData.FileSize, "File size is negative.");
            }

            string           normalizedPath = LongPathCommon.NormalizePath(path);
            LongPathFileInfo fileInfo       = new LongPathFileInfo(normalizedPath);

            bool shouldCreateFile = false;

            if (!fileInfo.Exists || fileInfo.Length > 0)
            {
                LongPathDirectory.CreateDirectory(fileInfo.DirectoryName);
                shouldCreateFile = true;
            }
            else if (!fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
            {
                shouldCreateFile = true;
            }
            else if (fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly))
            {
                fileInfo.Attributes &= ~FileAttributes.ReadOnly;
            }

            if (shouldCreateFile)
            {
                using (fileInfo.Create())
                {
                    // Do nothing.
                }
            }

            // If the original file is empty, we don't need to set a reparse point for it.
            if (fileData.FileSize == 0)
            {
                return;
            }

            ReparsePointHelper.SetReparsePointData(
                path,
                new object[] // Custom serialization layout for the LazyCopyFileData object.
            {
                (long)(fileData.UseCustomHandler ? 1L : 0L),
                (long)fileData.FileSize,
                // Add the prefix, if the custom handling is needed for the file.
                Encoding.Unicode.GetBytes(
                    (fileData.UseCustomHandler ? fileData.RemotePath : PathHelper.ChangeDriveLetterToDeviceName(fileData.RemotePath))
                    + '\0')
            },
                LazyCopyFileHelper.LazyCopyReparseTag,
                LazyCopyFileHelper.LazyCopyReparseGuid);

            // Set the proper file attributes.
            LongPathCommon.SetAttributes(path, FileAttributes.ReparsePoint | FileAttributes.NotContentIndexed | FileAttributes.Offline);
        }
        /// <summary>
        /// Sets the reparse point data for the <paramref name="path"/> given.
        /// </summary>
        /// <param name="path">File or directory to set the reparse point data for.</param>
        /// <param name="data">
        /// Reparse point data to be set.
        /// It should be a value type, or an <see cref="IEnumerable"/>, and it should not contain
        /// reparse point data header information, because this function handles it separately.
        /// </param>
        /// <param name="reparseTag">Reparse point tag.</param>
        /// <param name="reparseGuid">Reparse point <see cref="Guid"/>. Must be specified, if the <paramref name="reparseTag"/> is a non-Microsoft tag.</param>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="data"/> is not a collection or a value type.
        ///     <para>-or-</para>
        /// <paramref name="reparseTag"/> is a Microsoft tag, but the <paramref name="reparseGuid"/> is not <see langword="null"/>.
        ///     <para>-or-</para>
        /// <paramref name="reparseTag"/> is a non-Microsoft tag, but the <paramref name="reparseGuid"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Reparse point <paramref name="data"/> cannot be set for the <paramref name="path"/>.
        /// </exception>
        /// <exception cref="IOException">
        /// <paramref name="path"/> cannot be accessed.
        /// </exception>
        /// <remarks>
        /// This method will <i>NOT</i> update file attributes.<br/>
        /// For example, the <see cref="FileAttributes.ReparsePoint"/> will not be set.
        /// </remarks>
        public static void SetReparsePointData(string path, object data, int reparseTag, Guid?reparseGuid)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            ReparsePointHelper.ValidateTagAndGuid(reparseTag, reparseGuid);

            bool   isMicrosoftTag = ReparsePointHelper.IsMicrosoftTag(reparseTag);
            string normalizedPath = LongPathCommon.NormalizePath(path);

            if (!LongPathCommon.Exists(normalizedPath))
            {
                throw new IOException(string.Format(CultureInfo.InvariantCulture, "{0} cannot be found.", path));
            }

            using (SafeFileHandle handle = NativeMethods.CreateFile(
                       normalizedPath,
                       AccessRights.GenericWrite,
                       FileShare.None,
                       IntPtr.Zero,
                       FileMode.Open,
                       EFileAttributes.OpenReparsePoint | EFileAttributes.BackupSemantics,
                       IntPtr.Zero))
            {
                if (handle.IsInvalid)
                {
                    Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
                    throw new IOException(string.Format(CultureInfo.InvariantCulture, "Unable to open: {0}", path), nativeException);
                }

                int dataSize = MarshalingHelper.GetObjectSize(data);

                object header = isMicrosoftTag
                                ? (object)new ReparseDataBufferHeader {
                    ReparseDataLength = unchecked ((ushort)dataSize), ReparseTag = reparseTag
                }
                                : new ReparseGuidDataBufferHeader     {
                    ReparseDataLength = unchecked ((ushort)dataSize), ReparseTag = reparseTag, ReparseGuid = reparseGuid.Value
                };

                int headerSize    = Marshal.SizeOf(header);
                int tagDataLength = headerSize + dataSize;

                using (ResizableBuffer buffer = new ResizableBuffer(Math.Max(ReparsePointHelper.BufferSize, tagDataLength)))
                {
                    MarshalingHelper.MarshalObjectToPointer(new[] { header, data }, buffer.DangerousGetPointer());

                    // Set the reparse point data.
                    int  bytesReturned;
                    bool success = NativeMethods.DeviceIoControl(
                        handle,
                        ReparsePointHelper.SetReparsePointControlCode,
                        buffer.DangerousGetPointer(),
                        tagDataLength,
                        IntPtr.Zero,
                        0,
                        out bytesReturned,
                        IntPtr.Zero);

                    if (!success)
                    {
                        Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to set the reparse point data: {0}", path), nativeException);
                    }
                }
            }
        }
        /// <summary>
        /// Gets the reparse point data from the <paramref name="path"/> given.
        /// </summary>
        /// <typeparam name="T">
        /// Reparse point buffer data type.
        /// It should not contain reparse point data header information, because this function handles it separately.
        /// </typeparam>
        /// <param name="path">Path to the reparse point to get data from.</param>
        /// <param name="reparseTag">Reparse point tag.</param>
        /// <param name="reparseGuid">Reparse point <see cref="Guid"/>. Must be specified, if the <paramref name="reparseTag"/> is a non-Microsoft tag.</param>
        /// <returns>Reparse point data found.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="reparseTag"/> is a Microsoft tag, but the <paramref name="reparseGuid"/> is not <see langword="null"/>.
        ///     <para>-or-</para>
        /// <paramref name="reparseTag"/> is a non-Microsoft tag, but the <paramref name="reparseGuid"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// <paramref name="path"/> cannot be found.
        ///     <para>-or-</para>
        /// <paramref name="path"/> is not a reparse point.
        ///     <para>-or-</para>
        /// <paramref name="path"/> reparse point cannot be opened.
        ///     <para>-or-</para>
        /// <paramref name="path"/> reparse point data cannot be retrieved.
        ///     <para>-or-</para>
        /// <paramref name="path"/> reparse point tag or GUID is invalid.
        /// </exception>
        /// <remarks>
        /// See <c>http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx</c> for more details about reparse point tags.
        /// </remarks>
        public static T GetReparsePointData <T>(string path, int reparseTag, Guid?reparseGuid)
            where T : struct
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            ReparsePointHelper.ValidateTagAndGuid(reparseTag, reparseGuid);

            bool   isMicrosoftTag = ReparsePointHelper.IsMicrosoftTag(reparseTag);
            string normalizedPath = LongPathCommon.NormalizePath(path);

            bool isDirectory;

            if (!LongPathCommon.Exists(normalizedPath, out isDirectory))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Path does not exist: {0}", path));
            }

            // Check, whether the path given is a reparse point.
            FileAttributes attributes = isDirectory ? new LongPathDirectoryInfo(normalizedPath).Attributes : LongPathFile.GetAttributes(normalizedPath);

            if (!attributes.HasFlag(FileAttributes.ReparsePoint))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Path given is not a reparse point: {0}", path));
            }

            using (SafeFileHandle handle = NativeMethods.CreateFile(
                       normalizedPath,
                       AccessRights.GenericRead,
                       FileShare.Read,
                       IntPtr.Zero,
                       FileMode.Open,
                       EFileAttributes.OpenReparsePoint | EFileAttributes.BackupSemantics,
                       IntPtr.Zero))
            {
                if (handle.IsInvalid)
                {
                    Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to open reparse point: {0}", path), nativeException);
                }

                // Make sure that the buffer will be large enough to hold reparse point data.
                int initialBufferSize = Math.Max(ReparsePointHelper.BufferSize, Marshal.SizeOf(typeof(ReparseGuidDataBufferHeader)) + Marshal.SizeOf(typeof(T)));

                using (ResizableBuffer buffer = new ResizableBuffer(initialBufferSize))
                {
                    // Query the reparse point data.
                    int  bytesReturned;
                    bool success = NativeMethods.DeviceIoControl(
                        handle,
                        ReparsePointHelper.GetReparsePointControlCode,
                        IntPtr.Zero,
                        0,
                        buffer.DangerousGetPointer(),
                        buffer.ByteLength,
                        out bytesReturned,
                        IntPtr.Zero);

                    int headerSize = isMicrosoftTag ? Marshal.SizeOf(typeof(ReparseDataBufferHeader)) : Marshal.SizeOf(typeof(ReparseGuidDataBufferHeader));

                    if (!success)
                    {
                        int hr = Marshal.GetHRForLastWin32Error();
                        if (hr != NativeMethods.ErrorMoreData)
                        {
                            Exception nativeException = Marshal.GetExceptionForHR(hr);
                            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to get the reparse point data: {0}", path), nativeException);
                        }

                        // Read the ReparseDataLength value, and resize buffer to fit the data.
                        int dataSize = headerSize + Marshal.ReadInt16(buffer.DangerousGetPointer(), 4 /* sizeof(uint) */);

                        buffer.Resize(dataSize);

                        success = NativeMethods.DeviceIoControl(
                            handle,
                            ReparsePointHelper.GetReparsePointControlCode,
                            IntPtr.Zero,
                            0,
                            buffer.DangerousGetPointer(),
                            buffer.ByteLength,
                            out bytesReturned,
                            IntPtr.Zero);

                        if (!success)
                        {
                            Exception nativeException = Marshal.GetExceptionForHR(hr);
                            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to get the reparse point data: {0}", path), nativeException);
                        }
                    }

                    // Make sure that the reparse tag is correct.
                    uint tag = unchecked ((uint)Marshal.ReadInt32(buffer.DangerousGetPointer()));
                    if (tag != unchecked ((uint)reparseTag))
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Reparse point tag is invalid. Path has 0x{0:X8}, but 0x{1:X8} is specified.", tag, reparseTag));
                    }

                    // Make sure that the reparse point GUID is correct, if needed.
                    if (!isMicrosoftTag)
                    {
                        ReparseGuidDataBufferHeader header = (ReparseGuidDataBufferHeader)Marshal.PtrToStructure(buffer.DangerousGetPointer(), typeof(ReparseGuidDataBufferHeader));
                        if (header.ReparseGuid != reparseGuid)
                        {
                            throw new InvalidOperationException(string.Format(
                                                                    CultureInfo.InvariantCulture,
                                                                    "Reparse point GUID is invalid. Path has {0}, but {1} is specified.",
                                                                    header.ReparseGuid.ToString("N"),
                                                                    reparseGuid.Value.ToString("N")));
                        }
                    }

                    return((T)Marshal.PtrToStructure(buffer.DangerousGetPointer() + headerSize, typeof(T)));
                }
            }
        }