Browse Source

Improved NTFileSystemHelper's FindEntries implementation

Tal Aloni 8 years ago
parent
commit
dd4839e2f1

+ 53 - 33
SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs

@@ -13,11 +13,6 @@ 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
@@ -27,55 +22,75 @@ namespace SMBLibrary.Server
         /// <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)
+        public static NTStatus FindEntries(out List<FileSystemEntry> entries, IFileSystem fileSystem, string fileNamePattern)
         {
-            bool isDirectoryEnumeration = false;
-            string searchPattern = String.Empty;
-            if (path.Contains("*") || path.Contains("<"))
+            int separatorIndex = fileNamePattern.LastIndexOf('\\');
+            if (separatorIndex >= 0)
+            {
+                string path = fileNamePattern.Substring(0, separatorIndex + 1);
+                string expression = fileNamePattern.Substring(separatorIndex + 1);
+                return FindEntries(out entries, fileSystem, path, expression);
+            }
+            else
             {
-                isDirectoryEnumeration = true;
-                int separatorIndex = path.LastIndexOf('\\');
-                searchPattern = path.Substring(separatorIndex + 1);
-                path = path.Substring(0, separatorIndex + 1);
+                entries = null;
+                return NTStatus.STATUS_INVALID_PARAMETER;
             }
-            bool exactNameWithoutExtension = searchPattern.Contains("\"");
+        }
 
+        /// <param name="expression">Expression as described in [MS-FSA] 2.1.4.4</param>
+        /// <returns>null if the path does not exist</returns>
+        public static NTStatus FindEntries(out List<FileSystemEntry> entries, IFileSystem fileSystem, string path, string expression)
+        {
+            entries = null;
             FileSystemEntry entry = fileSystem.GetEntry(path);
             if (entry == null)
             {
-                return null;
+                return NTStatus.STATUS_NO_SUCH_FILE;
             }
 
-            List<FileSystemEntry> entries;
-            if (isDirectoryEnumeration)
+            if (expression == String.Empty)
             {
-                entries = fileSystem.ListEntriesInDirectory(path);
+                return NTStatus.STATUS_INVALID_PARAMETER;
+            }
 
-                if (searchPattern != String.Empty)
+            bool findExactName = !ContainsWildcardCharacters(expression);
+
+            if (!findExactName)
+            {
+                try
                 {
-                    entries = GetFiltered(entries, searchPattern);
+                    entries = fileSystem.ListEntriesInDirectory(path);
                 }
-
-                if (!exactNameWithoutExtension)
+                catch (UnauthorizedAccessException)
                 {
-                    if (IncludeParentDirectoryInResults)
-                    {
-                        entries.Insert(0, fileSystem.GetEntry(FileSystem.GetParentDirectory(path)));
-                        entries[0].Name = "..";
-                    }
-                    if (IncludeCurrentDirectoryInResults)
-                    {
-                        entries.Insert(0, fileSystem.GetEntry(path));
-                        entries[0].Name = ".";
-                    }
+                    return NTStatus.STATUS_ACCESS_DENIED;
                 }
+
+                entries = GetFiltered(entries, expression);
+
+                // 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 = fileSystem.GetEntry(path);
+                currentDirectory.Name = ".";
+                FileSystemEntry parentDirectory = fileSystem.GetEntry(FileSystem.GetParentDirectory(path));
+                parentDirectory.Name = "..";
+                entries.Insert(0, parentDirectory);
+                entries.Insert(0, currentDirectory);
             }
             else
             {
+                entry = fileSystem.GetEntry(path + expression);
+                if (entry == null)
+                {
+                    return NTStatus.STATUS_NO_SUCH_FILE;
+                }
                 entries = new List<FileSystemEntry>();
                 entries.Add(entry);
             }
-            return entries;
+            return NTStatus.STATUS_SUCCESS;
         }
 
         /// <param name="expression">Expression as described in [MS-FSA] 2.1.4.4</param>
@@ -97,6 +112,11 @@ namespace SMBLibrary.Server
             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.

+ 6 - 8
SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs

@@ -22,19 +22,17 @@ namespace SMBLibrary.Server.SMB1
             string fileNamePattern = subcommand.FileName;
 
             List<FileSystemEntry> entries;
-            try
+            NTStatus searchStatus = NTFileSystemHelper.FindEntries(out entries, fileSystem, fileNamePattern);
+            if (searchStatus != NTStatus.STATUS_SUCCESS)
             {
-                entries = NTFileSystemHelper.FindEntries(fileSystem, fileNamePattern);
-            }
-            catch (UnauthorizedAccessException)
-            {
-                header.Status = NTStatus.STATUS_ACCESS_DENIED;
+                state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', NTStatus: {1}", fileNamePattern, searchStatus.ToString());
+                header.Status = searchStatus;
                 return null;
             }
-            state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', found {1} matching entries", fileNamePattern, entries != null ? entries.Count.ToString() : "no");
+            state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', found {1} matching entries", fileNamePattern, entries.Count);
 
             // [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)
+            if (entries.Count == 0)
             {
                 header.Status = NTStatus.STATUS_NO_SUCH_FILE;
                 return null;