DirectoryFileSystem.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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 System.Text;
  11. using Utilities;
  12. using Microsoft.Win32.SafeHandles;
  13. namespace SMBServer
  14. {
  15. public class DirectoryFileSystem : FileSystem
  16. {
  17. private DirectoryInfo m_directory;
  18. // If a FileStream is already opened, calling File.SetCreationTime(path, ..) will result in ERROR_SHARING_VIOLATION,
  19. // So we must keep track of the opened file handles and use SetFileTime(handle, ..)
  20. private Dictionary<string, SafeFileHandle> m_openHandles = new Dictionary<string, SafeFileHandle>();
  21. public DirectoryFileSystem(string path) : this(new DirectoryInfo(path))
  22. {
  23. }
  24. public DirectoryFileSystem(DirectoryInfo directory)
  25. {
  26. m_directory = directory;
  27. }
  28. public override FileSystemEntry GetEntry(string path)
  29. {
  30. ValidatePath(path);
  31. string fullPath = m_directory.FullName + path;
  32. if (File.Exists(fullPath))
  33. {
  34. FileInfo file = new FileInfo(fullPath);
  35. bool isHidden = (file.Attributes & FileAttributes.Hidden) > 0;
  36. bool isReadonly = (file.Attributes & FileAttributes.ReadOnly) > 0;
  37. bool isArchived = (file.Attributes & FileAttributes.Archive) > 0;
  38. return new FileSystemEntry(path, file.Name, false, (ulong)file.Length, file.CreationTime, file.LastWriteTime, file.LastAccessTime, isHidden, isReadonly, isArchived);
  39. }
  40. else if (Directory.Exists(fullPath))
  41. {
  42. DirectoryInfo directory = new DirectoryInfo(fullPath);
  43. string fullName = FileSystem.GetDirectoryPath(path);
  44. bool isHidden = (directory.Attributes & FileAttributes.Hidden) > 0;
  45. bool isReadonly = (directory.Attributes & FileAttributes.ReadOnly) > 0;
  46. bool isArchived = (directory.Attributes & FileAttributes.Archive) > 0;
  47. return new FileSystemEntry(fullName, directory.Name, true, 0, directory.CreationTime, directory.LastWriteTime, directory.LastAccessTime, isHidden, isReadonly, isArchived);
  48. }
  49. else
  50. {
  51. return null;
  52. }
  53. }
  54. public override FileSystemEntry CreateFile(string path)
  55. {
  56. ValidatePath(path);
  57. string fullPath = m_directory.FullName + path;
  58. FileStream stream = File.Create(fullPath);
  59. stream.Close();
  60. return GetEntry(path);
  61. }
  62. public override FileSystemEntry CreateDirectory(string path)
  63. {
  64. ValidatePath(path);
  65. string fullPath = m_directory.FullName + path;
  66. Directory.CreateDirectory(fullPath);
  67. return GetEntry(path);
  68. }
  69. public override void Move(string source, string destination)
  70. {
  71. ValidatePath(source);
  72. ValidatePath(destination);
  73. string sourcePath = m_directory.FullName + source;
  74. string destinationPath = m_directory.FullName + destination;
  75. if (File.Exists(sourcePath))
  76. {
  77. File.Move(sourcePath, destinationPath);
  78. }
  79. else // Entry is a directory
  80. {
  81. Directory.Move(sourcePath, destinationPath);
  82. }
  83. }
  84. public override void Delete(string path)
  85. {
  86. ValidatePath(path);
  87. string fullPath = m_directory.FullName + path;
  88. if (File.Exists(fullPath))
  89. {
  90. File.Delete(fullPath);
  91. }
  92. else if (Directory.Exists(fullPath))
  93. {
  94. Directory.Delete(fullPath, false);
  95. }
  96. else
  97. {
  98. throw new FileNotFoundException();
  99. }
  100. }
  101. public override List<FileSystemEntry> ListEntriesInDirectory(string path)
  102. {
  103. ValidatePath(path);
  104. if (path == String.Empty)
  105. {
  106. path = @"\";
  107. }
  108. string fullPath = m_directory.FullName + path;
  109. DirectoryInfo directory = new DirectoryInfo(fullPath);
  110. List<FileSystemEntry> result = new List<FileSystemEntry>();
  111. foreach (DirectoryInfo subDirectory in directory.GetDirectories())
  112. {
  113. string fullName = GetRelativeDirectoryPath(subDirectory.FullName);
  114. bool isHidden = (subDirectory.Attributes & FileAttributes.Hidden) > 0;
  115. bool isReadonly = (subDirectory.Attributes & FileAttributes.ReadOnly) > 0;
  116. bool isArchived = (subDirectory.Attributes & FileAttributes.Archive) > 0;
  117. result.Add(new FileSystemEntry(fullName, subDirectory.Name, true, 0, subDirectory.CreationTime, subDirectory.LastWriteTime, subDirectory.LastAccessTime, isHidden, isReadonly, isArchived));
  118. }
  119. foreach (FileInfo file in directory.GetFiles())
  120. {
  121. string fullName = GetRelativePath(file.FullName);
  122. bool isHidden = (file.Attributes & FileAttributes.Hidden) > 0;
  123. bool isReadonly = (file.Attributes & FileAttributes.ReadOnly) > 0;
  124. bool isArchived = (file.Attributes & FileAttributes.Archive) > 0;
  125. result.Add(new FileSystemEntry(fullName, file.Name, false, (ulong)file.Length, file.CreationTime, file.LastWriteTime, file.LastAccessTime, isHidden, isReadonly, isArchived));
  126. }
  127. return result;
  128. }
  129. public override Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share)
  130. {
  131. ValidatePath(path);
  132. string fullPath = m_directory.FullName + path;
  133. FileStream fileStream = File.Open(fullPath, mode, access, share);
  134. if (!m_openHandles.ContainsKey(fullPath.ToLower()))
  135. {
  136. m_openHandles.Add(fullPath.ToLower(), fileStream.SafeFileHandle);
  137. }
  138. StreamWatcher watcher = new StreamWatcher(fileStream);
  139. watcher.Closed += new EventHandler(Stream_Closed);
  140. return watcher;
  141. }
  142. private void Stream_Closed(object sender, EventArgs e)
  143. {
  144. StreamWatcher watcher = (StreamWatcher)sender;
  145. FileStream fileStream = (FileStream)watcher.Stream;
  146. m_openHandles.Remove(fileStream.Name.ToLower());
  147. }
  148. public override void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived)
  149. {
  150. ValidatePath(path);
  151. string fullPath = m_directory.FullName + path;
  152. if (File.Exists(fullPath) || Directory.Exists(fullPath))
  153. {
  154. FileAttributes attributes = File.GetAttributes(fullPath);
  155. if (isHidden.HasValue)
  156. {
  157. if (isHidden.Value)
  158. {
  159. attributes |= FileAttributes.Hidden;
  160. }
  161. else
  162. {
  163. attributes &= ~FileAttributes.Hidden;
  164. }
  165. }
  166. if (isReadonly.HasValue)
  167. {
  168. if (isReadonly.Value)
  169. {
  170. attributes |= FileAttributes.ReadOnly;
  171. }
  172. else
  173. {
  174. attributes &= ~FileAttributes.ReadOnly;
  175. }
  176. }
  177. if (isArchived.HasValue)
  178. {
  179. if (isArchived.Value)
  180. {
  181. attributes |= FileAttributes.Archive;
  182. }
  183. else
  184. {
  185. attributes &= ~FileAttributes.Archive;
  186. }
  187. }
  188. File.SetAttributes(fullPath, attributes);
  189. }
  190. else
  191. {
  192. throw new FileNotFoundException();
  193. }
  194. }
  195. public override void SetDates(string path, DateTime? creationDT, DateTime? lastWriteDT, DateTime? lastAccessDT)
  196. {
  197. ValidatePath(path);
  198. string fullPath = m_directory.FullName + path;
  199. if (File.Exists(fullPath))
  200. {
  201. SafeFileHandle openHandle;
  202. if (m_openHandles.TryGetValue(fullPath.ToLower(), out openHandle))
  203. {
  204. if (creationDT.HasValue)
  205. {
  206. Win32Native.SetCreationTime(openHandle, creationDT.Value);
  207. }
  208. if (lastWriteDT.HasValue)
  209. {
  210. Win32Native.SetLastWriteTime(openHandle, lastWriteDT.Value);
  211. }
  212. if (lastAccessDT.HasValue)
  213. {
  214. Win32Native.SetLastAccessTime(openHandle, lastAccessDT.Value);
  215. }
  216. }
  217. else
  218. {
  219. if (creationDT.HasValue)
  220. {
  221. File.SetCreationTime(fullPath, creationDT.Value);
  222. }
  223. if (lastWriteDT.HasValue)
  224. {
  225. File.SetLastWriteTime(fullPath, lastWriteDT.Value);
  226. }
  227. if (lastAccessDT.HasValue)
  228. {
  229. File.SetLastAccessTime(fullPath, lastAccessDT.Value);
  230. }
  231. }
  232. }
  233. else if (Directory.Exists(fullPath))
  234. {
  235. if (creationDT.HasValue)
  236. {
  237. Directory.SetCreationTime(fullPath, creationDT.Value);
  238. }
  239. if (lastWriteDT.HasValue)
  240. {
  241. Directory.SetLastWriteTime(fullPath, lastWriteDT.Value);
  242. }
  243. if (lastAccessDT.HasValue)
  244. {
  245. Directory.SetLastAccessTime(fullPath, lastAccessDT.Value);
  246. }
  247. }
  248. else
  249. {
  250. throw new FileNotFoundException();
  251. }
  252. }
  253. private void ValidatePath(string path)
  254. {
  255. if (path != String.Empty && !path.StartsWith(@"\"))
  256. {
  257. throw new ArgumentException("Path must start with a backslash");
  258. }
  259. if (path.Contains(@"\..\"))
  260. {
  261. throw new ArgumentException("Given path is not allowed");
  262. }
  263. }
  264. public override string Name
  265. {
  266. get
  267. {
  268. DriveInfo drive = new DriveInfo(m_directory.FullName.Substring(0, 2));
  269. return drive.DriveFormat;
  270. }
  271. }
  272. public override long Size
  273. {
  274. get
  275. {
  276. DriveInfo drive = new DriveInfo(m_directory.FullName.Substring(0, 2));
  277. return drive.TotalSize;
  278. }
  279. }
  280. public override long FreeSpace
  281. {
  282. get
  283. {
  284. DriveInfo drive = new DriveInfo(m_directory.FullName.Substring(0, 2));
  285. return drive.AvailableFreeSpace;
  286. }
  287. }
  288. private string GetRelativePath(string fullPath)
  289. {
  290. return fullPath.Substring(m_directory.FullName.Length);
  291. }
  292. private string GetRelativeDirectoryPath(string fullPath)
  293. {
  294. return GetRelativePath(GetDirectoryPath(fullPath));
  295. }
  296. }
  297. }