SMB2FileStore.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /* Copyright (C) 2017-2020 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 SMBLibrary.SMB2;
  10. using Utilities;
  11. namespace SMBLibrary.Client
  12. {
  13. public class SMB2FileStore : ISMBFileStore
  14. {
  15. private const int BytesPerCredit = 65536;
  16. private SMB2Client m_client;
  17. private uint m_treeID;
  18. public SMB2FileStore(SMB2Client client, uint treeID)
  19. {
  20. m_client = client;
  21. m_treeID = treeID;
  22. }
  23. public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, SecurityContext securityContext)
  24. {
  25. handle = null;
  26. fileStatus = FileStatus.FILE_DOES_NOT_EXIST;
  27. CreateRequest request = new CreateRequest();
  28. request.Name = path;
  29. request.DesiredAccess = desiredAccess;
  30. request.FileAttributes = fileAttributes;
  31. request.ShareAccess = shareAccess;
  32. request.CreateDisposition = createDisposition;
  33. request.CreateOptions = createOptions;
  34. request.ImpersonationLevel = ImpersonationLevel.Impersonation;
  35. TrySendCommand(request);
  36. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Create);
  37. if (response != null)
  38. {
  39. if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is CreateResponse)
  40. {
  41. CreateResponse createResponse = ((CreateResponse)response);
  42. handle = createResponse.FileId;
  43. fileStatus = ToFileStatus(createResponse.CreateAction);
  44. }
  45. return response.Header.Status;
  46. }
  47. return NTStatus.STATUS_INVALID_SMB;
  48. }
  49. public NTStatus CloseFile(object handle)
  50. {
  51. CloseRequest request = new CloseRequest();
  52. request.FileId = (FileID)handle;
  53. TrySendCommand(request);
  54. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Close);
  55. if (response != null)
  56. {
  57. return response.Header.Status;
  58. }
  59. return NTStatus.STATUS_INVALID_SMB;
  60. }
  61. public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount)
  62. {
  63. data = null;
  64. ReadRequest request = new ReadRequest();
  65. request.Header.CreditCharge = (ushort)Math.Ceiling((double)maxCount / BytesPerCredit);
  66. request.FileId = (FileID)handle;
  67. request.Offset = (ulong)offset;
  68. request.ReadLength = (uint)maxCount;
  69. TrySendCommand(request);
  70. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Read);
  71. if (response != null)
  72. {
  73. if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is ReadResponse)
  74. {
  75. data = ((ReadResponse)response).Data;
  76. }
  77. return response.Header.Status;
  78. }
  79. return NTStatus.STATUS_INVALID_SMB;
  80. }
  81. public NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data)
  82. {
  83. numberOfBytesWritten = 0;
  84. WriteRequest request = new WriteRequest();
  85. request.Header.CreditCharge = (ushort)Math.Ceiling((double)data.Length / BytesPerCredit);
  86. request.FileId = (FileID)handle;
  87. request.Offset = (ulong)offset;
  88. request.Data = data;
  89. TrySendCommand(request);
  90. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Write);
  91. if (response != null)
  92. {
  93. if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is WriteResponse)
  94. {
  95. numberOfBytesWritten = (int)((WriteResponse)response).Count;
  96. }
  97. return response.Header.Status;
  98. }
  99. return NTStatus.STATUS_INVALID_SMB;
  100. }
  101. public NTStatus FlushFileBuffers(object handle)
  102. {
  103. throw new NotImplementedException();
  104. }
  105. public NTStatus LockFile(object handle, long byteOffset, long length, bool exclusiveLock)
  106. {
  107. throw new NotImplementedException();
  108. }
  109. public NTStatus UnlockFile(object handle, long byteOffset, long length)
  110. {
  111. throw new NotImplementedException();
  112. }
  113. public NTStatus QueryDirectory(out List<QueryDirectoryFileInformation> result, object handle, string fileName, FileInformationClass informationClass)
  114. {
  115. result = new List<QueryDirectoryFileInformation>();
  116. QueryDirectoryRequest request = new QueryDirectoryRequest();
  117. request.Header.CreditCharge = (ushort)Math.Ceiling((double)m_client.MaxTransactSize / BytesPerCredit);
  118. request.FileInformationClass = informationClass;
  119. request.Reopen = true;
  120. request.FileId = (FileID)handle;
  121. request.OutputBufferLength = m_client.MaxTransactSize;
  122. request.FileName = fileName;
  123. TrySendCommand(request);
  124. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.QueryDirectory);
  125. if (response != null)
  126. {
  127. while (response.Header.Status == NTStatus.STATUS_SUCCESS && response is QueryDirectoryResponse)
  128. {
  129. List<QueryDirectoryFileInformation> page = ((QueryDirectoryResponse)response).GetFileInformationList(informationClass);
  130. result.AddRange(page);
  131. request.Reopen = false;
  132. TrySendCommand(request);
  133. response = m_client.WaitForCommand(SMB2CommandName.QueryDirectory);
  134. }
  135. return response.Header.Status;
  136. }
  137. return NTStatus.STATUS_INVALID_SMB;
  138. }
  139. public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass)
  140. {
  141. result = null;
  142. QueryInfoRequest request = new QueryInfoRequest();
  143. request.InfoType = InfoType.File;
  144. request.FileInformationClass = informationClass;
  145. request.OutputBufferLength = 4096;
  146. request.FileId = (FileID)handle;
  147. TrySendCommand(request);
  148. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.QueryInfo);
  149. if (response != null)
  150. {
  151. if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is QueryInfoResponse)
  152. {
  153. result = ((QueryInfoResponse)response).GetFileInformation(informationClass);
  154. }
  155. return response.Header.Status;
  156. }
  157. return NTStatus.STATUS_INVALID_SMB;
  158. }
  159. public NTStatus SetFileInformation(object handle, FileInformation information)
  160. {
  161. SetInfoRequest request = new SetInfoRequest();
  162. request.InfoType = InfoType.File;
  163. request.FileInformationClass = information.FileInformationClass;
  164. request.FileId = (FileID)handle;
  165. request.SetFileInformation(information);
  166. TrySendCommand(request);
  167. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.SetInfo);
  168. if (response != null)
  169. {
  170. return response.Header.Status;
  171. }
  172. return NTStatus.STATUS_INVALID_SMB;
  173. }
  174. public NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass)
  175. {
  176. result = null;
  177. object fileHandle;
  178. FileStatus fileStatus;
  179. NTStatus status = CreateFile(out fileHandle, out fileStatus, String.Empty, (AccessMask)DirectoryAccessMask.FILE_LIST_DIRECTORY | (AccessMask)DirectoryAccessMask.FILE_READ_ATTRIBUTES | AccessMask.SYNCHRONIZE, 0, ShareAccess.Read | ShareAccess.Write | ShareAccess.Delete, CreateDisposition.FILE_OPEN, CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT | CreateOptions.FILE_DIRECTORY_FILE, null);
  180. if (status != NTStatus.STATUS_SUCCESS)
  181. {
  182. return status;
  183. }
  184. status = GetFileSystemInformation(out result, fileHandle, informationClass);
  185. CloseFile(fileHandle);
  186. return status;
  187. }
  188. public NTStatus GetFileSystemInformation(out FileSystemInformation result, object handle, FileSystemInformationClass informationClass)
  189. {
  190. result = null;
  191. QueryInfoRequest request = new QueryInfoRequest();
  192. request.InfoType = InfoType.FileSystem;
  193. request.FileSystemInformationClass = informationClass;
  194. request.OutputBufferLength = 4096;
  195. request.FileId = (FileID)handle;
  196. TrySendCommand(request);
  197. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.QueryInfo);
  198. if (response != null)
  199. {
  200. if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is QueryInfoResponse)
  201. {
  202. result = ((QueryInfoResponse)response).GetFileSystemInformation(informationClass);
  203. }
  204. return response.Header.Status;
  205. }
  206. return NTStatus.STATUS_INVALID_SMB;
  207. }
  208. public NTStatus SetFileSystemInformation(FileSystemInformation information)
  209. {
  210. throw new NotImplementedException();
  211. }
  212. public NTStatus GetSecurityInformation(out SecurityDescriptor result, object handle, SecurityInformation securityInformation)
  213. {
  214. result = null;
  215. QueryInfoRequest request = new QueryInfoRequest();
  216. request.InfoType = InfoType.Security;
  217. request.SecurityInformation = securityInformation;
  218. request.OutputBufferLength = 4096;
  219. request.FileId = (FileID)handle;
  220. TrySendCommand(request);
  221. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.QueryInfo);
  222. if (response != null)
  223. {
  224. if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is QueryInfoResponse)
  225. {
  226. result = ((QueryInfoResponse)response).GetSecurityInformation();
  227. }
  228. return response.Header.Status;
  229. }
  230. return NTStatus.STATUS_INVALID_SMB;
  231. }
  232. public NTStatus SetSecurityInformation(object handle, SecurityInformation securityInformation, SecurityDescriptor securityDescriptor)
  233. {
  234. return NTStatus.STATUS_NOT_SUPPORTED;
  235. }
  236. public NTStatus NotifyChange(out object ioRequest, object handle, NotifyChangeFilter completionFilter, bool watchTree, int outputBufferSize, OnNotifyChangeCompleted onNotifyChangeCompleted, object context)
  237. {
  238. throw new NotImplementedException();
  239. }
  240. public NTStatus Cancel(object ioRequest)
  241. {
  242. throw new NotImplementedException();
  243. }
  244. public NTStatus DeviceIOControl(object handle, uint ctlCode, byte[] input, out byte[] output, int maxOutputLength)
  245. {
  246. output = null;
  247. IOCtlRequest request = new IOCtlRequest();
  248. request.Header.CreditCharge = (ushort)Math.Ceiling((double)maxOutputLength / BytesPerCredit);
  249. request.CtlCode = ctlCode;
  250. request.IsFSCtl = true;
  251. request.FileId = (FileID)handle;
  252. request.Input = input;
  253. request.MaxOutputResponse = (uint)maxOutputLength;
  254. TrySendCommand(request);
  255. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.IOCtl);
  256. if (response != null)
  257. {
  258. if ((response.Header.Status == NTStatus.STATUS_SUCCESS || response.Header.Status == NTStatus.STATUS_BUFFER_OVERFLOW) && response is IOCtlResponse)
  259. {
  260. output = ((IOCtlResponse)response).Output;
  261. }
  262. return response.Header.Status;
  263. }
  264. return NTStatus.STATUS_INVALID_SMB;
  265. }
  266. public NTStatus Disconnect()
  267. {
  268. TreeDisconnectRequest request = new TreeDisconnectRequest();
  269. TrySendCommand(request);
  270. SMB2Command response = m_client.WaitForCommand(SMB2CommandName.TreeDisconnect);
  271. if (response != null)
  272. {
  273. return response.Header.Status;
  274. }
  275. return NTStatus.STATUS_INVALID_SMB;
  276. }
  277. private void TrySendCommand(SMB2Command request)
  278. {
  279. request.Header.TreeID = m_treeID;
  280. m_client.TrySendCommand(request);
  281. }
  282. public uint MaxReadSize
  283. {
  284. get
  285. {
  286. return m_client.MaxReadSize;
  287. }
  288. }
  289. public uint MaxWriteSize
  290. {
  291. get
  292. {
  293. return m_client.MaxWriteSize;
  294. }
  295. }
  296. private static FileStatus ToFileStatus(CreateAction createAction)
  297. {
  298. switch (createAction)
  299. {
  300. case CreateAction.FILE_SUPERSEDED:
  301. return FileStatus.FILE_SUPERSEDED;
  302. case CreateAction.FILE_OPENED:
  303. return FileStatus.FILE_OPENED;
  304. case CreateAction.FILE_CREATED:
  305. return FileStatus.FILE_CREATED;
  306. case CreateAction.FILE_OVERWRITTEN:
  307. return FileStatus.FILE_OVERWRITTEN;
  308. default:
  309. return FileStatus.FILE_OPENED;
  310. }
  311. }
  312. }
  313. }