/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Security; using System.Security.AccessControl; namespace Alphaleonis.Win32.Filesystem { partial class Directory { /// [AlphaFS] Retrieves information about files in the directory specified by in mode. /// An enumeration of records for each file system entry in the specified diretory. /// A path to a directory from which to retrieve information. [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfo(string path) { return EnumerateFileIdBothDirectoryInfoCore(null, null, path, FileShare.ReadWrite, false, PathFormat.RelativePath); } /// [AlphaFS] Retrieves information about files in the directory specified by in mode. /// An enumeration of records for each file system entry in the specified diretory. /// A path to a directory from which to retrieve information. /// Indicates the format of the path parameter(s). [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfo(string path, PathFormat pathFormat) { return EnumerateFileIdBothDirectoryInfoCore(null, null, path, FileShare.ReadWrite, false, pathFormat); } /// [AlphaFS] Retrieves information about files in the directory specified by in specified mode. /// An enumeration of records for each file system entry in the specified diretory. /// A path to a directory from which to retrieve information. /// The mode with which to open a handle to the directory. [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfo(string path, FileShare shareMode) { return EnumerateFileIdBothDirectoryInfoCore(null, null, path, shareMode, false, PathFormat.RelativePath); } /// [AlphaFS] Retrieves information about files in the directory specified by in specified mode. /// An enumeration of records for each file system entry in the specified diretory. /// A path to a directory from which to retrieve information. /// The mode with which to open a handle to the directory. /// Indicates the format of the path parameter(s). [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfo(string path, FileShare shareMode, PathFormat pathFormat) { return EnumerateFileIdBothDirectoryInfoCore(null, null, path, shareMode, false, pathFormat); } /// [AlphaFS] Retrieves information about files in the directory handle specified. /// An IEnumerable of records for each file system entry in the specified diretory. /// An open handle to the directory from which to retrieve information. [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfo(SafeFileHandle handle) { // FileShare has no effect since a handle is already opened. return EnumerateFileIdBothDirectoryInfoCore(null, handle, null, FileShare.ReadWrite, false, PathFormat.RelativePath); } #region Transactional /// [AlphaFS] Retrieves information about files in the directory specified by in mode. /// An enumeration of records for each file system entry in the specified diretory. /// The transaction. /// A path to a directory from which to retrieve information. [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path) { return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, FileShare.ReadWrite, false, PathFormat.RelativePath); } /// [AlphaFS] Retrieves information about files in the directory specified by in mode. /// An enumeration of records for each file system entry in the specified diretory. /// The transaction. /// A path to a directory from which to retrieve information. /// Indicates the format of the path parameter(s). [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path, PathFormat pathFormat) { return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, FileShare.ReadWrite, false, pathFormat); } /// [AlphaFS] Retrieves information about files in the directory specified by in specified mode. /// An enumeration of records for each file system entry in the specified diretory. /// The transaction. /// A path to a directory from which to retrieve information. /// The mode with which to open a handle to the directory. [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path, FileShare shareMode) { return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, shareMode, false, PathFormat.RelativePath); } /// [AlphaFS] Retrieves information about files in the directory specified by in specified mode. /// An enumeration of records for each file system entry in the specified diretory. /// The transaction. /// A path to a directory from which to retrieve information. /// The mode with which to open a handle to the directory. /// Indicates the format of the path parameter(s). [SecurityCritical] public static IEnumerable EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path, FileShare shareMode, PathFormat pathFormat) { return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, shareMode, false, pathFormat); } #endregion // Transactional #region Internal Methods /// Returns an enumerable collection of information about files in the directory handle specified. /// An IEnumerable of records for each file system entry in the specified diretory. /// /// Either use or , not both. /// /// The number of files that are returned for each call to GetFileInformationByHandleEx depends on the size of the buffer that is passed to the function. /// Any subsequent calls to GetFileInformationByHandleEx on the same handle will resume the enumeration operation after the last file is returned. /// /// /// The transaction. /// An open handle to the directory from which to retrieve information. /// A path to the directory. /// The mode with which to open a handle to the directory. /// suppress any Exception that might be thrown as a result from a failure, such as ACLs protected directories or non-accessible reparse points. /// Indicates the format of the path parameter(s). [SecurityCritical] internal static IEnumerable EnumerateFileIdBothDirectoryInfoCore(KernelTransaction transaction, SafeFileHandle safeHandle, string path, FileShare shareMode, bool continueOnException, PathFormat pathFormat) { if (!NativeMethods.IsAtLeastWindowsVista) throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher); bool callerHandle = safeHandle != null; if (!callerHandle) { if (Utils.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path"); string pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); safeHandle = File.CreateFileCore(transaction, pathLp, ExtendedFileAttributes.BackupSemantics, null, FileMode.Open, FileSystemRights.ReadData, shareMode, true, PathFormat.LongFullPath); } try { if (!NativeMethods.IsValidHandle(safeHandle, Marshal.GetLastWin32Error(), !continueOnException)) yield break; var fileNameOffset = (int)Marshal.OffsetOf(typeof(NativeMethods.FILE_ID_BOTH_DIR_INFO), "FileName"); using (var safeBuffer = new SafeGlobalMemoryBufferHandle(NativeMethods.DefaultFileBufferSize)) { while (true) { if (!NativeMethods.GetFileInformationByHandleEx(safeHandle, NativeMethods.FileInfoByHandleClass.FileIdBothDirectoryInfo, safeBuffer, (uint)safeBuffer.Capacity)) { uint lastError = (uint)Marshal.GetLastWin32Error(); switch (lastError) { case Win32Errors.ERROR_SUCCESS: case Win32Errors.ERROR_NO_MORE_FILES: case Win32Errors.ERROR_HANDLE_EOF: yield break; case Win32Errors.ERROR_MORE_DATA: continue; default: NativeError.ThrowException(lastError, path); yield break; // we should never get to this yield break. } } int offset = 0; NativeMethods.FILE_ID_BOTH_DIR_INFO fibdi; do { fibdi = safeBuffer.PtrToStructure(offset); string fileName = safeBuffer.PtrToStringUni(offset + fileNameOffset, (int)(fibdi.FileNameLength / 2)); if (!fileName.Equals(Path.CurrentDirectoryPrefix, StringComparison.OrdinalIgnoreCase) && !fileName.Equals(Path.ParentDirectoryPrefix, StringComparison.OrdinalIgnoreCase)) yield return new FileIdBothDirectoryInfo(fibdi, fileName); offset += fibdi.NextEntryOffset; } while (fibdi.NextEntryOffset != 0); } } } finally { // Handle is ours, dispose. if (!callerHandle && safeHandle != null) safeHandle.Close(); } } #endregion // Internal Methods } }