/* Copyright (C) 2014-2017 Tal Aloni . All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. */ using System; using System.Collections.Generic; using System.Text; using Utilities; namespace SMBLibrary { public partial class NTFileSystemAdapter { /// Expression as described in [MS-FSA] 2.1.4.4 public NTStatus QueryDirectory(out List result, object handle, string fileName, FileInformationClass informationClass) { result = null; FileHandle directoryHandle = (FileHandle)handle; if (!directoryHandle.IsDirectory) { return NTStatus.STATUS_INVALID_PARAMETER; } if (fileName == String.Empty) { return NTStatus.STATUS_INVALID_PARAMETER; } string path = directoryHandle.Path; bool findExactName = !ContainsWildcardCharacters(fileName); List entries; if (!findExactName) { try { entries = m_fileSystem.ListEntriesInDirectory(path); } catch (UnauthorizedAccessException) { return NTStatus.STATUS_ACCESS_DENIED; } entries = GetFiltered(entries, fileName); // Windows will return "." and ".." when enumerating directory files. // The SMB1 / SMB2 specifications mandate that when zero entries are found, the server SHOULD / MUST return STATUS_NO_SUCH_FILE. // For this reason, we MUST include the current directory and/or parent directory when enumerating a directory // in order to diffrentiate between a directory that does not exist and a directory with no entries. FileSystemEntry currentDirectory = m_fileSystem.GetEntry(path); currentDirectory.Name = "."; FileSystemEntry parentDirectory = m_fileSystem.GetEntry(FileSystem.GetParentDirectory(path)); parentDirectory.Name = ".."; entries.Insert(0, parentDirectory); entries.Insert(0, currentDirectory); } else { path = FileSystem.GetDirectoryPath(path); FileSystemEntry entry = m_fileSystem.GetEntry(path + fileName); if (entry == null) { return NTStatus.STATUS_NO_SUCH_FILE; } entries = new List(); entries.Add(entry); } try { result = FromFileSystemEntries(entries, informationClass); } catch (UnsupportedInformationLevelException) { return NTStatus.STATUS_INVALID_INFO_CLASS; } return NTStatus.STATUS_SUCCESS; } /// Expression as described in [MS-FSA] 2.1.4.4 private static List GetFiltered(List entries, string expression) { if (expression == "*") { return entries; } List result = new List(); foreach (FileSystemEntry entry in entries) { if (IsFileNameInExpression(entry.Name, expression)) { result.Add(entry); } } return result; } private static bool ContainsWildcardCharacters(string expression) { return (expression.Contains("?") || expression.Contains("*") || expression.Contains("\"") || expression.Contains(">") || expression.Contains("<")); } // [MS-FSA] 2.1.4.4 // The FileName is string compared with Expression using the following wildcard rules: // * (asterisk) Matches zero or more characters. // ? (question mark) Matches a single character. // DOS_DOT (" quotation mark) Matches either a period or zero characters beyond the name string. // DOS_QM (> greater than) Matches any single character or, upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs. // DOS_STAR (< less than) Matches zero or more characters until encountering and matching the final . in the name. private static bool IsFileNameInExpression(string fileName, string expression) { if (expression == "*") { return true; } else if (expression.EndsWith("*")) // expression.Length > 1 { string desiredFileNameStart = expression.Substring(0, expression.Length - 1); bool findExactNameWithoutExtension = false; if (desiredFileNameStart.EndsWith("\"")) { findExactNameWithoutExtension = true; desiredFileNameStart = desiredFileNameStart.Substring(0, desiredFileNameStart.Length - 1); } if (!findExactNameWithoutExtension) { if (fileName.StartsWith(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase)) { return true; } } else { if (fileName.StartsWith(desiredFileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) || fileName.Equals(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase)) { return true; } } } else if (expression.StartsWith("<")) { string desiredFileNameEnd = expression.Substring(1); if (fileName.EndsWith(desiredFileNameEnd, StringComparison.InvariantCultureIgnoreCase)) { return true; } } else if (String.Equals(fileName, expression, StringComparison.CurrentCultureIgnoreCase)) { return true; } return false; } private static List FromFileSystemEntries(List entries, FileInformationClass informationClass) { List result = new List(); foreach (FileSystemEntry entry in entries) { QueryDirectoryFileInformation information = FromFileSystemEntry(entry, informationClass); result.Add(information); } return result; } private static QueryDirectoryFileInformation FromFileSystemEntry(FileSystemEntry entry, FileInformationClass informationClass) { switch (informationClass) { case FileInformationClass.FileBothDirectoryInformation: { FileBothDirectoryInformation result = new FileBothDirectoryInformation(); result.CreationTime = entry.CreationTime; result.LastAccessTime = entry.LastAccessTime; result.LastWriteTime = entry.LastWriteTime; result.ChangeTime = entry.LastWriteTime; result.EndOfFile = (long)entry.Size; result.AllocationSize = (long)GetAllocationSize(entry.Size); result.FileAttributes = GetFileAttributes(entry); result.EaSize = 0; result.ShortName = GetShortName(entry.Name); result.FileName = entry.Name; return result; } case FileInformationClass.FileDirectoryInformation: { FileDirectoryInformation result = new FileDirectoryInformation(); result.CreationTime = entry.CreationTime; result.LastAccessTime = entry.LastAccessTime; result.LastWriteTime = entry.LastWriteTime; result.ChangeTime = entry.LastWriteTime; result.EndOfFile = (long)entry.Size; result.AllocationSize = (long)GetAllocationSize(entry.Size); result.FileAttributes = GetFileAttributes(entry); result.FileName = entry.Name; return result; } case FileInformationClass.FileFullDirectoryInformation: { FileFullDirectoryInformation result = new FileFullDirectoryInformation(); result.CreationTime = entry.CreationTime; result.LastAccessTime = entry.LastAccessTime; result.LastWriteTime = entry.LastWriteTime; result.ChangeTime = entry.LastWriteTime; result.EndOfFile = (long)entry.Size; result.AllocationSize = (long)GetAllocationSize(entry.Size); result.FileAttributes = GetFileAttributes(entry); result.EaSize = 0; result.FileName = entry.Name; return result; } case FileInformationClass.FileIdBothDirectoryInformation: { FileIdBothDirectoryInformation result = new FileIdBothDirectoryInformation(); result.CreationTime = entry.CreationTime; result.LastAccessTime = entry.LastAccessTime; result.LastWriteTime = entry.LastWriteTime; result.ChangeTime = entry.LastWriteTime; result.EndOfFile = (long)entry.Size; result.AllocationSize = (long)GetAllocationSize(entry.Size); result.FileAttributes = GetFileAttributes(entry); result.EaSize = 0; result.ShortName = GetShortName(entry.Name); result.FileId = 0; result.FileName = entry.Name; return result; } case FileInformationClass.FileIdFullDirectoryInformation: { FileIdFullDirectoryInformation result = new FileIdFullDirectoryInformation(); result.CreationTime = entry.CreationTime; result.LastAccessTime = entry.LastAccessTime; result.LastWriteTime = entry.LastWriteTime; result.ChangeTime = entry.LastWriteTime; result.EndOfFile = (long)entry.Size; result.AllocationSize = (long)GetAllocationSize(entry.Size); result.FileAttributes = GetFileAttributes(entry); result.EaSize = 0; result.FileId = 0; result.FileName = entry.Name; return result; } case FileInformationClass.FileNamesInformation: { FileNamesInformation result = new FileNamesInformation(); result.FileName = entry.Name; return result; } default: { throw new UnsupportedInformationLevelException(); } } } } }