Przeglądaj źródła

Added NTFileSystemHelper.Find partial class

Tal Aloni 8 lat temu
rodzic
commit
67976270ca

+ 1 - 0
SMBLibrary/SMBLibrary.csproj

@@ -115,6 +115,7 @@
     <Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
     <Compile Include="Server\Helpers\IOExceptionHelper.cs" />
     <Compile Include="Server\Helpers\NTFileSystemHelper.cs" />
+    <Compile Include="Server\Helpers\NTFileSystemHelper.Find.cs" />
     <Compile Include="Server\Helpers\ServerPathUtils.cs" />
     <Compile Include="Server\IndependentUserCollection.cs" />
     <Compile Include="Server\INTLMAuthenticationProvider.cs" />

+ 139 - 0
SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs

@@ -0,0 +1,139 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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.IO;
+using Utilities;
+
+namespace SMBLibrary.Server
+{
+    public partial class NTFileSystemHelper
+    {
+        // Windows servers will return "." and ".." when enumerating directory files, Windows clients do not require it.
+        // It seems that Ubuntu 10.04.4 and 13.10 expect at least one entry in the response (so empty directory listing cause a problem when omitting both).
+        private const bool IncludeCurrentDirectoryInResults = true;
+        private const bool IncludeParentDirectoryInResults = true;
+
+        // Filename pattern examples:
+        // '\Directory' - Get the directory entry
+        // '\Directory\*' - List the directory files
+        // '\Directory\s*' - List the directory files starting with s (cmd.exe will use this syntax when entering 's' and hitting tab for autocomplete)
+        // '\Directory\<.inf' (Update driver will use this syntax)
+        // '\Directory\exefile"*' (cmd.exe will use this syntax when entering an exe without its extension, explorer will use this opening a directory from the run menu)
+        /// <param name="fileNamePattern">The filename pattern to search for. This field MAY contain wildcard characters</param>
+        /// <returns>null if the path does not exist</returns>
+        /// <exception cref="System.UnauthorizedAccessException"></exception>
+        public static List<FileSystemEntry> FindEntries(IFileSystem fileSystem, string path)
+        {
+            bool isDirectoryEnumeration = false;
+            string searchPattern = String.Empty;
+            if (path.Contains("*") || path.Contains("<"))
+            {
+                isDirectoryEnumeration = true;
+                int separatorIndex = path.LastIndexOf('\\');
+                searchPattern = path.Substring(separatorIndex + 1);
+                path = path.Substring(0, separatorIndex + 1);
+            }
+            bool exactNameWithoutExtension = searchPattern.Contains("\"");
+
+            FileSystemEntry entry = fileSystem.GetEntry(path);
+            if (entry == null)
+            {
+                return null;
+            }
+
+            List<FileSystemEntry> entries;
+            if (isDirectoryEnumeration)
+            {
+                entries = fileSystem.ListEntriesInDirectory(path);
+
+                if (searchPattern != String.Empty)
+                {
+                    entries = GetFiltered(entries, searchPattern);
+                }
+
+                if (!exactNameWithoutExtension)
+                {
+                    if (IncludeParentDirectoryInResults)
+                    {
+                        entries.Insert(0, fileSystem.GetEntry(FileSystem.GetParentDirectory(path)));
+                        entries[0].Name = "..";
+                    }
+                    if (IncludeCurrentDirectoryInResults)
+                    {
+                        entries.Insert(0, fileSystem.GetEntry(path));
+                        entries[0].Name = ".";
+                    }
+                }
+            }
+            else
+            {
+                entries = new List<FileSystemEntry>();
+                entries.Add(entry);
+            }
+            return entries;
+        }
+
+        // [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 List<FileSystemEntry> GetFiltered(List<FileSystemEntry> entries, string searchPattern)
+        {
+            if (searchPattern == String.Empty || searchPattern == "*")
+            {
+                return entries;
+            }
+
+            List<FileSystemEntry> result = new List<FileSystemEntry>();
+            if (searchPattern.EndsWith("*") && searchPattern.Length > 1)
+            {
+                string fileNameStart = searchPattern.Substring(0, searchPattern.Length - 1);
+                bool exactNameWithoutExtensionMatch = false;
+                if (fileNameStart.EndsWith("\""))
+                {
+                    exactNameWithoutExtensionMatch = true;
+                    fileNameStart = fileNameStart.Substring(0, fileNameStart.Length - 1);
+                }
+
+                foreach (FileSystemEntry entry in entries)
+                {
+                    if (!exactNameWithoutExtensionMatch)
+                    {
+                        if (entry.Name.StartsWith(fileNameStart, StringComparison.InvariantCultureIgnoreCase))
+                        {
+                            result.Add(entry);
+                        }
+                    }
+                    else
+                    {
+                        if (entry.Name.StartsWith(fileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) ||
+                            entry.Name.Equals(fileNameStart, StringComparison.InvariantCultureIgnoreCase))
+                        {
+                            result.Add(entry);
+                        }
+                    }
+                }
+            }
+            else if (searchPattern.StartsWith("<"))
+            {
+                string fileNameEnd = searchPattern.Substring(1);
+                foreach (FileSystemEntry entry in entries)
+                {
+                    if (entry.Name.EndsWith(fileNameEnd, StringComparison.InvariantCultureIgnoreCase))
+                    {
+                        result.Add(entry);
+                    }
+                }
+            }
+            return result;
+        }
+    }
+}

+ 12 - 123
SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs

@@ -15,82 +15,29 @@ namespace SMBLibrary.Server.SMB1
 {
     public class Transaction2SubcommandHelper
     {
-        // Windows servers will return "." and ".." when enumerating directory files, Windows clients do not require it.
-        // It seems that Ubuntu 10.04.4 and 13.10 expect at least one entry in the response (so empty directory listing cause a problem when omitting both).
-        public const bool IncludeCurrentDirectoryInResults = true;
-        public const bool IncludeParentDirectoryInResults = true;
-
         internal static Transaction2FindFirst2Response GetSubcommandResponse(SMB1Header header, Transaction2FindFirst2Request subcommand, FileSystemShare share, SMB1ConnectionState state)
         {
             SMB1Session session = state.GetSession(header.UID);
             IFileSystem fileSystem = share.FileSystem;
-            string path = subcommand.FileName;
-            // '\Directory' - Get the directory info
-            // '\Directory\*' - List the directory files
-            // '\Directory\s*' - List the directory files starting with s (cmd.exe will use this syntax when entering 's' and hitting tab for autocomplete)
-            // '\Directory\<.inf' (Update driver will use this syntax)
-            // '\Directory\exefile"*' (cmd.exe will use this syntax when entering an exe without its extension, explorer will use this opening a directory from the run menu)
-            bool isDirectoryEnumeration = false;
-            string searchPattern = String.Empty;
-            if (path.Contains("*") || path.Contains("<"))
+            string fileNamePattern = subcommand.FileName;
+
+            List<FileSystemEntry> entries;
+            try
             {
-                isDirectoryEnumeration = true;
-                int separatorIndex = path.LastIndexOf('\\');
-                searchPattern = path.Substring(separatorIndex + 1);
-                path = path.Substring(0, separatorIndex + 1);
+                entries = NTFileSystemHelper.FindEntries(fileSystem, fileNamePattern);
             }
-            bool exactNameWithoutExtension = searchPattern.Contains("\"");
-
-            FileSystemEntry entry = fileSystem.GetEntry(path);
-            if (entry == null)
+            catch (UnauthorizedAccessException)
             {
-                header.Status = NTStatus.STATUS_NO_SUCH_FILE;
+                header.Status = NTStatus.STATUS_ACCESS_DENIED;
                 return null;
             }
+            state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', found {1} matching entries", fileNamePattern, entries != null ? entries.Count.ToString() : "no");
 
-            List<FileSystemEntry> entries;
-            if (isDirectoryEnumeration)
-            {
-                try
-                {
-                    entries = fileSystem.ListEntriesInDirectory(path);
-                }
-                catch (UnauthorizedAccessException)
-                {
-                    header.Status = NTStatus.STATUS_ACCESS_DENIED;
-                    return null;
-                }
-
-                if (searchPattern != String.Empty)
-                {
-                    entries = GetFiltered(entries, searchPattern);
-                }
-
-                if (!exactNameWithoutExtension)
-                {
-                    if (IncludeParentDirectoryInResults)
-                    {
-                        entries.Insert(0, fileSystem.GetEntry(FileSystem.GetParentDirectory(path)));
-                        entries[0].Name = "..";
-                    }
-                    if (IncludeCurrentDirectoryInResults)
-                    {
-                        entries.Insert(0, fileSystem.GetEntry(path));
-                        entries[0].Name = ".";
-                    }
-                }
-
-                // If no matching entries are found, the server SHOULD fail the request with STATUS_NO_SUCH_FILE.
-                if (entries.Count == 0)
-                {
-                    header.Status = NTStatus.STATUS_NO_SUCH_FILE;
-                    return null;
-                }
-            }
-            else
+            // [MS-CIFS] If no matching entries are found, the server SHOULD fail the request with STATUS_NO_SUCH_FILE.
+            if (entries == null || entries.Count == 0)
             {
-                entries = new List<FileSystemEntry>();
-                entries.Add(entry);
+                header.Status = NTStatus.STATUS_NO_SUCH_FILE;
+                return null;
             }
 
             bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0;
@@ -132,64 +79,6 @@ namespace SMBLibrary.Server.SMB1
             return response;
         }
 
-        // [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.
-        internal static List<FileSystemEntry> GetFiltered(List<FileSystemEntry> entries, string searchPattern)
-        {
-            if (searchPattern == String.Empty || searchPattern == "*")
-            {
-                return entries;
-            }
-
-            List<FileSystemEntry> result = new List<FileSystemEntry>();
-            if (searchPattern.EndsWith("*") && searchPattern.Length > 1)
-            {
-                string fileNameStart = searchPattern.Substring(0, searchPattern.Length - 1);
-                bool exactNameWithoutExtensionMatch = false;
-                if (fileNameStart.EndsWith("\""))
-                {
-                    exactNameWithoutExtensionMatch = true;
-                    fileNameStart = fileNameStart.Substring(0, fileNameStart.Length - 1);
-                }
-
-                foreach (FileSystemEntry entry in entries)
-                {
-                    if (!exactNameWithoutExtensionMatch)
-                    {
-                        if (entry.Name.StartsWith(fileNameStart, StringComparison.InvariantCultureIgnoreCase))
-                        {
-                            result.Add(entry);
-                        }
-                    }
-                    else
-                    {
-                        if (entry.Name.StartsWith(fileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) ||
-                            entry.Name.Equals(fileNameStart, StringComparison.InvariantCultureIgnoreCase))
-                        {
-                            result.Add(entry);
-                        }
-                    }
-                }
-            }
-            else if (searchPattern.StartsWith("<"))
-            {
-                string fileNameEnd = searchPattern.Substring(1);
-                foreach (FileSystemEntry entry in entries)
-                {
-                    if (entry.Name.EndsWith(fileNameEnd, StringComparison.InvariantCultureIgnoreCase))
-                    {
-                        result.Add(entry);
-                    }
-                }
-            }
-            return result;
-        }
-
         internal static Transaction2FindNext2Response GetSubcommandResponse(SMB1Header header, Transaction2FindNext2Request subcommand, FileSystemShare share, SMB1ConnectionState state)
         {
             SMB1Session session = state.GetSession(header.UID);