NTFileSystemAdapter.QueryDirectory.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /* Copyright (C) 2014-2018 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
  12. {
  13. public partial class NTFileSystemAdapter
  14. {
  15. /// <param name="fileName">Expression as described in [MS-FSA] 2.1.4.4</param>
  16. public NTStatus QueryDirectory(out List<QueryDirectoryFileInformation> result, object handle, string fileName, FileInformationClass informationClass)
  17. {
  18. result = null;
  19. FileHandle directoryHandle = (FileHandle)handle;
  20. if (!directoryHandle.IsDirectory)
  21. {
  22. return NTStatus.STATUS_INVALID_PARAMETER;
  23. }
  24. if (fileName == String.Empty)
  25. {
  26. return NTStatus.STATUS_INVALID_PARAMETER;
  27. }
  28. string path = directoryHandle.Path;
  29. bool findExactName = !ContainsWildcardCharacters(fileName);
  30. List<FileSystemEntry> entries;
  31. if (!findExactName)
  32. {
  33. try
  34. {
  35. entries = m_fileSystem.ListEntriesInDirectory(path);
  36. }
  37. catch (UnauthorizedAccessException)
  38. {
  39. return NTStatus.STATUS_ACCESS_DENIED;
  40. }
  41. entries = GetFiltered(entries, fileName);
  42. // Windows will return "." and ".." when enumerating directory files.
  43. // The SMB1 / SMB2 specifications mandate that when zero entries are found, the server SHOULD / MUST return STATUS_NO_SUCH_FILE.
  44. // For this reason, we MUST include the current directory and/or parent directory when enumerating a directory
  45. // in order to diffrentiate between a directory that does not exist and a directory with no entries.
  46. FileSystemEntry currentDirectory = m_fileSystem.GetEntry(path);
  47. currentDirectory.Name = ".";
  48. FileSystemEntry parentDirectory = m_fileSystem.GetEntry(FileSystem.GetParentDirectory(path));
  49. parentDirectory.Name = "..";
  50. entries.Insert(0, parentDirectory);
  51. entries.Insert(0, currentDirectory);
  52. }
  53. else
  54. {
  55. path = FileSystem.GetDirectoryPath(path);
  56. FileSystemEntry entry;
  57. try
  58. {
  59. entry = m_fileSystem.GetEntry(path + fileName);
  60. }
  61. catch (Exception ex)
  62. {
  63. if (ex is IOException || ex is UnauthorizedAccessException)
  64. {
  65. NTStatus status = ToNTStatus(ex);
  66. Log(Severity.Verbose, "QueryDirectory: Error querying '{0}'. {1}.", path, status);
  67. return status;
  68. }
  69. else
  70. {
  71. throw;
  72. }
  73. }
  74. entries = new List<FileSystemEntry>();
  75. entries.Add(entry);
  76. }
  77. try
  78. {
  79. result = FromFileSystemEntries(entries, informationClass);
  80. }
  81. catch (UnsupportedInformationLevelException)
  82. {
  83. return NTStatus.STATUS_INVALID_INFO_CLASS;
  84. }
  85. return NTStatus.STATUS_SUCCESS;
  86. }
  87. /// <param name="expression">Expression as described in [MS-FSA] 2.1.4.4</param>
  88. private static List<FileSystemEntry> GetFiltered(List<FileSystemEntry> entries, string expression)
  89. {
  90. if (expression == "*")
  91. {
  92. return entries;
  93. }
  94. List<FileSystemEntry> result = new List<FileSystemEntry>();
  95. foreach (FileSystemEntry entry in entries)
  96. {
  97. if (IsFileNameInExpression(entry.Name, expression))
  98. {
  99. result.Add(entry);
  100. }
  101. }
  102. return result;
  103. }
  104. private static bool ContainsWildcardCharacters(string expression)
  105. {
  106. return (expression.Contains("?") || expression.Contains("*") || expression.Contains("\"") || expression.Contains(">") || expression.Contains("<"));
  107. }
  108. // [MS-FSA] 2.1.4.4
  109. // The FileName is string compared with Expression using the following wildcard rules:
  110. // * (asterisk) Matches zero or more characters.
  111. // ? (question mark) Matches a single character.
  112. // DOS_DOT (" quotation mark) Matches either a period or zero characters beyond the name string.
  113. // 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.
  114. // DOS_STAR (< less than) Matches zero or more characters until encountering and matching the final . in the name.
  115. private static bool IsFileNameInExpression(string fileName, string expression)
  116. {
  117. if (expression == "*")
  118. {
  119. return true;
  120. }
  121. else if (expression.EndsWith("*")) // expression.Length > 1
  122. {
  123. string desiredFileNameStart = expression.Substring(0, expression.Length - 1);
  124. bool findExactNameWithoutExtension = false;
  125. if (desiredFileNameStart.EndsWith("\""))
  126. {
  127. findExactNameWithoutExtension = true;
  128. desiredFileNameStart = desiredFileNameStart.Substring(0, desiredFileNameStart.Length - 1);
  129. }
  130. if (!findExactNameWithoutExtension)
  131. {
  132. if (fileName.StartsWith(desiredFileNameStart, StringComparison.OrdinalIgnoreCase))
  133. {
  134. return true;
  135. }
  136. }
  137. else
  138. {
  139. if (fileName.StartsWith(desiredFileNameStart + ".", StringComparison.OrdinalIgnoreCase) ||
  140. fileName.Equals(desiredFileNameStart, StringComparison.OrdinalIgnoreCase))
  141. {
  142. return true;
  143. }
  144. }
  145. }
  146. else if (expression.StartsWith("<"))
  147. {
  148. string desiredFileNameEnd = expression.Substring(1);
  149. if (fileName.EndsWith(desiredFileNameEnd, StringComparison.OrdinalIgnoreCase))
  150. {
  151. return true;
  152. }
  153. }
  154. else if (String.Equals(fileName, expression, StringComparison.OrdinalIgnoreCase))
  155. {
  156. return true;
  157. }
  158. return false;
  159. }
  160. private static List<QueryDirectoryFileInformation> FromFileSystemEntries(List<FileSystemEntry> entries, FileInformationClass informationClass)
  161. {
  162. List<QueryDirectoryFileInformation> result = new List<QueryDirectoryFileInformation>();
  163. foreach (FileSystemEntry entry in entries)
  164. {
  165. QueryDirectoryFileInformation information = FromFileSystemEntry(entry, informationClass);
  166. result.Add(information);
  167. }
  168. return result;
  169. }
  170. private static QueryDirectoryFileInformation FromFileSystemEntry(FileSystemEntry entry, FileInformationClass informationClass)
  171. {
  172. switch (informationClass)
  173. {
  174. case FileInformationClass.FileBothDirectoryInformation:
  175. {
  176. FileBothDirectoryInformation result = new FileBothDirectoryInformation();
  177. result.CreationTime = entry.CreationTime;
  178. result.LastAccessTime = entry.LastAccessTime;
  179. result.LastWriteTime = entry.LastWriteTime;
  180. result.ChangeTime = entry.LastWriteTime;
  181. result.EndOfFile = (long)entry.Size;
  182. result.AllocationSize = (long)GetAllocationSize(entry.Size);
  183. result.FileAttributes = GetFileAttributes(entry);
  184. result.EaSize = 0;
  185. result.FileName = entry.Name;
  186. return result;
  187. }
  188. case FileInformationClass.FileDirectoryInformation:
  189. {
  190. FileDirectoryInformation result = new FileDirectoryInformation();
  191. result.CreationTime = entry.CreationTime;
  192. result.LastAccessTime = entry.LastAccessTime;
  193. result.LastWriteTime = entry.LastWriteTime;
  194. result.ChangeTime = entry.LastWriteTime;
  195. result.EndOfFile = (long)entry.Size;
  196. result.AllocationSize = (long)GetAllocationSize(entry.Size);
  197. result.FileAttributes = GetFileAttributes(entry);
  198. result.FileName = entry.Name;
  199. return result;
  200. }
  201. case FileInformationClass.FileFullDirectoryInformation:
  202. {
  203. FileFullDirectoryInformation result = new FileFullDirectoryInformation();
  204. result.CreationTime = entry.CreationTime;
  205. result.LastAccessTime = entry.LastAccessTime;
  206. result.LastWriteTime = entry.LastWriteTime;
  207. result.ChangeTime = entry.LastWriteTime;
  208. result.EndOfFile = (long)entry.Size;
  209. result.AllocationSize = (long)GetAllocationSize(entry.Size);
  210. result.FileAttributes = GetFileAttributes(entry);
  211. result.EaSize = 0;
  212. result.FileName = entry.Name;
  213. return result;
  214. }
  215. case FileInformationClass.FileIdBothDirectoryInformation:
  216. {
  217. FileIdBothDirectoryInformation result = new FileIdBothDirectoryInformation();
  218. result.CreationTime = entry.CreationTime;
  219. result.LastAccessTime = entry.LastAccessTime;
  220. result.LastWriteTime = entry.LastWriteTime;
  221. result.ChangeTime = entry.LastWriteTime;
  222. result.EndOfFile = (long)entry.Size;
  223. result.AllocationSize = (long)GetAllocationSize(entry.Size);
  224. result.FileAttributes = GetFileAttributes(entry);
  225. result.EaSize = 0;
  226. result.FileId = 0;
  227. result.FileName = entry.Name;
  228. return result;
  229. }
  230. case FileInformationClass.FileIdFullDirectoryInformation:
  231. {
  232. FileIdFullDirectoryInformation result = new FileIdFullDirectoryInformation();
  233. result.CreationTime = entry.CreationTime;
  234. result.LastAccessTime = entry.LastAccessTime;
  235. result.LastWriteTime = entry.LastWriteTime;
  236. result.ChangeTime = entry.LastWriteTime;
  237. result.EndOfFile = (long)entry.Size;
  238. result.AllocationSize = (long)GetAllocationSize(entry.Size);
  239. result.FileAttributes = GetFileAttributes(entry);
  240. result.EaSize = 0;
  241. result.FileId = 0;
  242. result.FileName = entry.Name;
  243. return result;
  244. }
  245. case FileInformationClass.FileNamesInformation:
  246. {
  247. FileNamesInformation result = new FileNamesInformation();
  248. result.FileName = entry.Name;
  249. return result;
  250. }
  251. default:
  252. {
  253. throw new UnsupportedInformationLevelException();
  254. }
  255. }
  256. }
  257. }
  258. }