//----------------------------------------------------------------------- // Gets the target path for a reparse point private static void GetTarget(ref ReparsePoint rp) { string wPath = rp.Path; if (!wPath.StartsWith(@"\\?\")) { wPath = @"\\?\" + wPath; } if (((uint)rp.TagType & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) != (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) { rp.Target = ""; return; } try { // We need a handle to the reparse point to pass to DeviceIocontrol down below. // CreateFile is how we get that handle. // We want to play nice with others, so we note that we're only reading it, and we want to // allow others to be able to access (but not delete) it as well while we have the handle (the // GENERIC_READ and FILE_SHARE_READ | FILE_SHARE_WRITE values) // // Biggest key is the flag FILE_FLAG_OPEN_REPARSE_POINT, which tells CreateFile that we want // a handle to the reparse point itself and not to the target of the reparse point // // CreateFile will return INVALID_HANDLE_VALUE with a last error of 5 - Access Denied // if the FILE_FLAG_BACKUP_SEMANTICS is not specified when opening a directory/reparse point // It's noted in the directory section of the CreateFile MSDN page IntPtr pathHndl = CreateFileW(wPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, IntPtr.Zero); if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE) { rp.Err = Marshal.GetLastWin32Error(); rp.ErrMsg = "Invalid Handle returned by CreateFile"; rp.Target = ""; return; } uint lenDataReturned = 0; REPARSE_DATA_BUFFER rpDataBuf = new REPARSE_DATA_BUFFER(); //Allocate a buffer to get the "user defined data" out of the reaprse point. //MSDN page on FSCTL_GET_REPARSE_POINT discusses size calculation IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize); //DeviceIocontrol takes a handle to a file/directory/device etc, that's obtained via CreateFile // In our case, it's a handle to the directory that's marked a reparse point //We pass in the FSCTL_GET_REPARSE_POINT flag to getll DeviceIoControl that we want to get data about a reparse point //There is no In buffer. pMem is our out buffer that will hold the returned REPARSE_DATA_BUFFER //lenDataReturned is of course how much data was copied into our buffer pMem. //We're doing a simple call.. no asyncronous stuff going on, so Overlapped is a null pointer if (!DeviceIoControl(pathHndl, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, pMem, (uint)Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize, out lenDataReturned, IntPtr.Zero)) { rp.ErrMsg = "Call to DeviceIoControl failed"; rp.Err = Marshal.GetLastWin32Error(); rp.Target = ""; } else { rpDataBuf = (REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rpDataBuf.GetType()); rp.Target = rpDataBuf.PathBuffer; } Marshal.FreeHGlobal(pMem); CloseHandle(pathHndl); } catch (Exception e) { rp.Err = -1; rp.ErrMsg = e.Message; rp.Target = ""; } return; }
//----------------------------------------------------------------------- // Gets the target path for a reparse point private static void GetTarget(ref ReparsePoint rp) { string wPath = rp.Path; if (!wPath.StartsWith(@"\\?\")) wPath = @"\\?\" + wPath; if (((uint)rp.TagType & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) != (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) { rp.Target = ""; return; } try { // We need a handle to the reparse point to pass to DeviceIocontrol down below. // CreateFile is how we get that handle. // We want to play nice with others, so we note that we're only reading it, and we want to // allow others to be able to access (but not delete) it as well while we have the handle (the // GENERIC_READ and FILE_SHARE_READ | FILE_SHARE_WRITE values) // // Biggest key is the flag FILE_FLAG_OPEN_REPARSE_POINT, which tells CreateFile that we want // a handle to the reparse point itself and not to the target of the reparse point // // CreateFile will return INVALID_HANDLE_VALUE with a last error of 5 - Access Denied // if the FILE_FLAG_BACKUP_SEMANTICS is not specified when opening a directory/reparse point // It's noted in the directory section of the CreateFile MSDN page IntPtr pathHndl = CreateFileW(wPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, IntPtr.Zero); if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE) { rp.Err = Marshal.GetLastWin32Error(); rp.ErrMsg = "Invalid Handle returned by CreateFile"; rp.Target = ""; return; } uint lenDataReturned = 0; REPARSE_DATA_BUFFER rpDataBuf = new REPARSE_DATA_BUFFER(); //Allocate a buffer to get the "user defined data" out of the reaprse point. //MSDN page on FSCTL_GET_REPARSE_POINT discusses size calculation IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize); //DeviceIocontrol takes a handle to a file/directory/device etc, that's obtained via CreateFile // In our case, it's a handle to the directory that's marked a reparse point //We pass in the FSCTL_GET_REPARSE_POINT flag to getll DeviceIoControl that we want to get data about a reparse point //There is no In buffer. pMem is our out buffer that will hold the returned REPARSE_DATA_BUFFER //lenDataReturned is of course how much data was copied into our buffer pMem. //We're doing a simple call.. no asyncronous stuff going on, so Overlapped is a null pointer if (!DeviceIoControl(pathHndl, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, pMem, (uint)Marshal.SizeOf(rpDataBuf) + ReparseHeaderSize, out lenDataReturned, IntPtr.Zero)) { rp.ErrMsg = "Call to DeviceIoControl failed"; rp.Err = Marshal.GetLastWin32Error(); rp.Target = ""; } else { rpDataBuf = (REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rpDataBuf.GetType()); rp.Target = rpDataBuf.PathBuffer; } Marshal.FreeHGlobal(pMem); CloseHandle(pathHndl); } catch (Exception e) { rp.Err = -1; rp.ErrMsg = e.Message; rp.Target = ""; } return; }
//----------------------------------------------------------------------- public static List GetReparsePoints(string path, bool recursive) { List lRps = new List(); path = path.Trim(); //skip the "." directories . and .. if (path.EndsWith(".")) { return(lRps); } //make sure the path does not end in a "\" or "\*". //We will make sure our converted path right below does however if (path.EndsWith(@"\*")) { path = path.Substring(0, path.Length - 2); } if (path.EndsWith(@"\")) { path = path.Substring(0, path.Length - 1); } //convert the path to a unicode path to ensure we don't hit length and character restrictions //make sure the converted path ends with "\*" for using with the FindFirstFile/FindNextFile calls //The managed classes don't support this, so we have to use Win32 API directly for all our processes string wPath = path; if (!wPath.StartsWith(@"\\?\")) { wPath = @"\\?\" + wPath; } wPath = wPath + @"\*"; //the find data holds the attributes of the file/folder found, including if it's a reparse point WIN32_FIND_DATA findData; //In theory, the FINDEX_SEARCH_OPS.LimitToDirectories will cause FindFirstFileExW to only return //directories... Unfortunatly, it's a great theory that doesn't work most of the time. //So we have to test every object returned to determine if it is indeed a directory IntPtr pathHndl = FindFirstFileExW(wPath, FINDEX_INFO_LEVELS.FindExInfoStandard, out findData, FINDEX_SEARCH_OPS.LimitToDirectories, IntPtr.Zero, 0); if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE) { return(lRps); } bool isDir = false; bool isMountPoint = false; if ((findData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) { isDir = true; } if ((findData.dwFileAttributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { if ((findData.dwReserved0 & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) == (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) { isMountPoint = true; ReparsePoint rp = new ReparsePoint(); rp.Path = path + "\\" + findData.cFileName; rp.TagType = ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT; rp.IsReparsePoint = true; rp.Err = 0; rp.ErrMsg = ""; GetTarget(ref rp); lRps.Add(rp); } } //Recurse //We only want to recurse down reparse points if we're told to, to avoid looping if ((isDir && !isMountPoint) || (isMountPoint && recursive)) { List tlRps = GetReparsePoints(path + "\\" + findData.cFileName, recursive); if (tlRps != null) { lRps.AddRange(tlRps); } } //Get all the rest of the files/directories while (true) { if (FindNextFileW(pathHndl, out findData)) { if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE) { return(lRps); } isDir = false; isMountPoint = false; if ((findData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) { isDir = true; } if ((findData.dwFileAttributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { if ((findData.dwReserved0 & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) == (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) { isMountPoint = true; ReparsePoint rp = new ReparsePoint(); rp.Path = path + "\\" + findData.cFileName; rp.TagType = ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT; rp.IsReparsePoint = true; rp.Err = 0; rp.ErrMsg = ""; GetTarget(ref rp); lRps.Add(rp); } } //Recurse //We only want to recurse down reparse points if we're told to, to avoid looping if ((isDir && !isMountPoint) || (isMountPoint && recursive)) { List tlRps = GetReparsePoints(path + "\\" + findData.cFileName, recursive); if (tlRps != null) { lRps.AddRange(tlRps); } } } else //findNextFile failed { break; } } FindClose(pathHndl); return(lRps); }
//----------------------------------------------------------------------- public static List GetReparsePoints(string path, bool recursive) { List lRps = new List(); path = path.Trim(); //skip the "." directories . and .. if (path.EndsWith(".")) return lRps; //make sure the path does not end in a "\" or "\*". //We will make sure our converted path right below does however if (path.EndsWith(@"\*")) path = path.Substring(0, path.Length - 2); if (path.EndsWith(@"\")) path = path.Substring(0, path.Length - 1); //convert the path to a unicode path to ensure we don't hit length and character restrictions //make sure the converted path ends with "\*" for using with the FindFirstFile/FindNextFile calls //The managed classes don't support this, so we have to use Win32 API directly for all our processes string wPath = path; if (!wPath.StartsWith(@"\\?\")) wPath = @"\\?\" + wPath; wPath = wPath + @"\*"; //the find data holds the attributes of the file/folder found, including if it's a reparse point WIN32_FIND_DATA findData; //In theory, the FINDEX_SEARCH_OPS.LimitToDirectories will cause FindFirstFileExW to only return //directories... Unfortunatly, it's a great theory that doesn't work most of the time. //So we have to test every object returned to determine if it is indeed a directory IntPtr pathHndl = FindFirstFileExW(wPath, FINDEX_INFO_LEVELS.FindExInfoStandard, out findData, FINDEX_SEARCH_OPS.LimitToDirectories, IntPtr.Zero, 0); if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE) return lRps; bool isDir = false; bool isMountPoint = false; if ((findData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) isDir = true; if ((findData.dwFileAttributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { if ((findData.dwReserved0 & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) == (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) { isMountPoint = true; ReparsePoint rp = new ReparsePoint(); rp.Path = path + "\\" + findData.cFileName; rp.TagType = ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT; rp.IsReparsePoint = true; rp.Err = 0; rp.ErrMsg = ""; GetTarget(ref rp); lRps.Add(rp); } } //Recurse //We only want to recurse down reparse points if we're told to, to avoid looping if ((isDir && !isMountPoint) || (isMountPoint && recursive)) { List tlRps = GetReparsePoints(path + "\\" + findData.cFileName, recursive); if (tlRps != null) lRps.AddRange(tlRps); } //Get all the rest of the files/directories while (true) { if (FindNextFileW(pathHndl, out findData)) { if (pathHndl.ToInt32() == INVALID_HANDLE_VALUE) return lRps; isDir = false; isMountPoint = false; if ((findData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) isDir = true; if ((findData.dwFileAttributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { if ((findData.dwReserved0 & (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) == (uint)ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT) { isMountPoint = true; ReparsePoint rp = new ReparsePoint(); rp.Path = path + "\\" + findData.cFileName; rp.TagType = ReparseTagType.IO_REPARSE_TAG_MOUNT_POINT; rp.IsReparsePoint = true; rp.Err = 0; rp.ErrMsg = ""; GetTarget(ref rp); lRps.Add(rp); } } //Recurse //We only want to recurse down reparse points if we're told to, to avoid looping if ((isDir && !isMountPoint) || (isMountPoint && recursive)) { List tlRps = GetReparsePoints(path + "\\" + findData.cFileName, recursive); if (tlRps != null) lRps.AddRange(tlRps); } } else //findNextFile failed { break; } } FindClose(pathHndl); return lRps; }