NTFileSystemHelper.Find.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using Utilities;
  11. namespace SMBLibrary.Server
  12. {
  13. public partial class NTFileSystemHelper
  14. {
  15. /// <param name="fileName">Expression as described in [MS-FSA] 2.1.4.4</param>
  16. public static NTStatus FindEntries(out List<FileSystemEntry> entries, IFileSystem fileSystem, string path, string fileName)
  17. {
  18. entries = null;
  19. FileSystemEntry entry = fileSystem.GetEntry(path);
  20. if (entry == null)
  21. {
  22. return NTStatus.STATUS_NO_SUCH_FILE;
  23. }
  24. if (!entry.IsDirectory)
  25. {
  26. return NTStatus.STATUS_INVALID_PARAMETER;
  27. }
  28. if (fileName == String.Empty)
  29. {
  30. return NTStatus.STATUS_INVALID_PARAMETER;
  31. }
  32. bool findExactName = !ContainsWildcardCharacters(fileName);
  33. if (!findExactName)
  34. {
  35. try
  36. {
  37. entries = fileSystem.ListEntriesInDirectory(path);
  38. }
  39. catch (Exception ex)
  40. {
  41. NTStatus status = ToNTStatus(ex);
  42. return status; ;
  43. }
  44. entries = GetFiltered(entries, fileName);
  45. // Windows will return "." and ".." when enumerating directory files.
  46. // The SMB1 / SMB2 specifications mandate that when zero entries are found, the server SHOULD / MUST return STATUS_NO_SUCH_FILE.
  47. // For this reason, we MUST include the current directory and/or parent directory when enumerating a directory
  48. // in order to diffrentiate between a directory that does not exist and a directory with no entries.
  49. FileSystemEntry currentDirectory = fileSystem.GetEntry(path);
  50. currentDirectory.Name = ".";
  51. FileSystemEntry parentDirectory = fileSystem.GetEntry(FileSystem.GetParentDirectory(path));
  52. parentDirectory.Name = "..";
  53. entries.Insert(0, parentDirectory);
  54. entries.Insert(0, currentDirectory);
  55. }
  56. else
  57. {
  58. path = FileSystem.GetDirectoryPath(path);
  59. entry = fileSystem.GetEntry(path + fileName);
  60. if (entry == null)
  61. {
  62. return NTStatus.STATUS_NO_SUCH_FILE;
  63. }
  64. entries = new List<FileSystemEntry>();
  65. entries.Add(entry);
  66. }
  67. return NTStatus.STATUS_SUCCESS;
  68. }
  69. /// <param name="expression">Expression as described in [MS-FSA] 2.1.4.4</param>
  70. private static List<FileSystemEntry> GetFiltered(List<FileSystemEntry> entries, string expression)
  71. {
  72. if (expression == "*")
  73. {
  74. return entries;
  75. }
  76. List<FileSystemEntry> result = new List<FileSystemEntry>();
  77. foreach (FileSystemEntry entry in entries)
  78. {
  79. if (IsFileNameInExpression(entry.Name, expression))
  80. {
  81. result.Add(entry);
  82. }
  83. }
  84. return result;
  85. }
  86. private static bool ContainsWildcardCharacters(string expression)
  87. {
  88. return (expression.Contains("?") || expression.Contains("*") || expression.Contains("\"") || expression.Contains(">") || expression.Contains("<"));
  89. }
  90. // [MS-FSA] 2.1.4.4
  91. // The FileName is string compared with Expression using the following wildcard rules:
  92. // * (asterisk) Matches zero or more characters.
  93. // ? (question mark) Matches a single character.
  94. // DOS_DOT (" quotation mark) Matches either a period or zero characters beyond the name string.
  95. // 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.
  96. // DOS_STAR (< less than) Matches zero or more characters until encountering and matching the final . in the name.
  97. private static bool IsFileNameInExpression(string fileName, string expression)
  98. {
  99. if (expression == "*")
  100. {
  101. return true;
  102. }
  103. else if (expression.EndsWith("*")) // expression.Length > 1
  104. {
  105. string desiredFileNameStart = expression.Substring(0, expression.Length - 1);
  106. bool findExactNameWithoutExtension = false;
  107. if (desiredFileNameStart.EndsWith("\""))
  108. {
  109. findExactNameWithoutExtension = true;
  110. desiredFileNameStart = desiredFileNameStart.Substring(0, desiredFileNameStart.Length - 1);
  111. }
  112. if (!findExactNameWithoutExtension)
  113. {
  114. if (fileName.StartsWith(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase))
  115. {
  116. return true;
  117. }
  118. }
  119. else
  120. {
  121. if (fileName.StartsWith(desiredFileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) ||
  122. fileName.Equals(desiredFileNameStart, StringComparison.InvariantCultureIgnoreCase))
  123. {
  124. return true;
  125. }
  126. }
  127. }
  128. else if (expression.StartsWith("<"))
  129. {
  130. string desiredFileNameEnd = expression.Substring(1);
  131. if (fileName.EndsWith(desiredFileNameEnd, StringComparison.InvariantCultureIgnoreCase))
  132. {
  133. return true;
  134. }
  135. }
  136. else if (String.Equals(fileName, expression, StringComparison.CurrentCultureIgnoreCase))
  137. {
  138. return true;
  139. }
  140. return false;
  141. }
  142. }
  143. }