ManagedFileSystem.cs 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using MediaBrowser.Model.IO;
  8. using MediaBrowser.Model.Logging;
  9. using MediaBrowser.Model.System;
  10. namespace Emby.Server.Implementations.IO
  11. {
  12. /// <summary>
  13. /// Class ManagedFileSystem
  14. /// </summary>
  15. public class ManagedFileSystem : IFileSystem
  16. {
  17. protected ILogger Logger;
  18. private readonly bool _supportsAsyncFileStreams;
  19. private char[] _invalidFileNameChars;
  20. private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
  21. private bool EnableFileSystemRequestConcat;
  22. private string _tempPath;
  23. private SharpCifsFileSystem _sharpCifsFileSystem;
  24. private IEnvironmentInfo _environmentInfo;
  25. public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string tempPath)
  26. {
  27. Logger = logger;
  28. _supportsAsyncFileStreams = true;
  29. _tempPath = tempPath;
  30. _environmentInfo = environmentInfo;
  31. // On Linux, this needs to be true or symbolic links are ignored
  32. EnableFileSystemRequestConcat = environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows &&
  33. environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX;
  34. SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
  35. _sharpCifsFileSystem = new SharpCifsFileSystem(environmentInfo.OperatingSystem);
  36. }
  37. public void AddShortcutHandler(IShortcutHandler handler)
  38. {
  39. _shortcutHandlers.Add(handler);
  40. }
  41. protected void SetInvalidFileNameChars(bool enableManagedInvalidFileNameChars)
  42. {
  43. if (enableManagedInvalidFileNameChars)
  44. {
  45. _invalidFileNameChars = Path.GetInvalidFileNameChars();
  46. }
  47. else
  48. {
  49. // GetInvalidFileNameChars is less restrictive in Linux/Mac than Windows, this mimic Windows behavior for mono under Linux/Mac.
  50. _invalidFileNameChars = new char[41] { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
  51. '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12',
  52. '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D',
  53. '\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' };
  54. }
  55. }
  56. public char DirectorySeparatorChar
  57. {
  58. get
  59. {
  60. return Path.DirectorySeparatorChar;
  61. }
  62. }
  63. public string GetFullPath(string path)
  64. {
  65. return Path.GetFullPath(path);
  66. }
  67. /// <summary>
  68. /// Determines whether the specified filename is shortcut.
  69. /// </summary>
  70. /// <param name="filename">The filename.</param>
  71. /// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
  72. /// <exception cref="System.ArgumentNullException">filename</exception>
  73. public virtual bool IsShortcut(string filename)
  74. {
  75. if (string.IsNullOrEmpty(filename))
  76. {
  77. throw new ArgumentNullException("filename");
  78. }
  79. var extension = Path.GetExtension(filename);
  80. return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
  81. }
  82. /// <summary>
  83. /// Resolves the shortcut.
  84. /// </summary>
  85. /// <param name="filename">The filename.</param>
  86. /// <returns>System.String.</returns>
  87. /// <exception cref="System.ArgumentNullException">filename</exception>
  88. public virtual string ResolveShortcut(string filename)
  89. {
  90. if (string.IsNullOrEmpty(filename))
  91. {
  92. throw new ArgumentNullException("filename");
  93. }
  94. var extension = Path.GetExtension(filename);
  95. var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
  96. if (handler != null)
  97. {
  98. return handler.Resolve(filename);
  99. }
  100. return null;
  101. }
  102. /// <summary>
  103. /// Creates the shortcut.
  104. /// </summary>
  105. /// <param name="shortcutPath">The shortcut path.</param>
  106. /// <param name="target">The target.</param>
  107. /// <exception cref="System.ArgumentNullException">
  108. /// shortcutPath
  109. /// or
  110. /// target
  111. /// </exception>
  112. public void CreateShortcut(string shortcutPath, string target)
  113. {
  114. if (string.IsNullOrEmpty(shortcutPath))
  115. {
  116. throw new ArgumentNullException("shortcutPath");
  117. }
  118. if (string.IsNullOrEmpty(target))
  119. {
  120. throw new ArgumentNullException("target");
  121. }
  122. var extension = Path.GetExtension(shortcutPath);
  123. var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
  124. if (handler != null)
  125. {
  126. handler.Create(shortcutPath, target);
  127. }
  128. else
  129. {
  130. throw new NotImplementedException();
  131. }
  132. }
  133. /// <summary>
  134. /// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path.
  135. /// </summary>
  136. /// <param name="path">A path to a file or directory.</param>
  137. /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
  138. /// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
  139. /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks>
  140. public FileSystemMetadata GetFileSystemInfo(string path)
  141. {
  142. if (string.IsNullOrEmpty(path))
  143. {
  144. throw new ArgumentNullException("path");
  145. }
  146. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  147. {
  148. return _sharpCifsFileSystem.GetFileSystemInfo(path);
  149. }
  150. // Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
  151. if (Path.HasExtension(path))
  152. {
  153. var fileInfo = new FileInfo(path);
  154. if (fileInfo.Exists)
  155. {
  156. return GetFileSystemMetadata(fileInfo);
  157. }
  158. return GetFileSystemMetadata(new DirectoryInfo(path));
  159. }
  160. else
  161. {
  162. var fileInfo = new DirectoryInfo(path);
  163. if (fileInfo.Exists)
  164. {
  165. return GetFileSystemMetadata(fileInfo);
  166. }
  167. return GetFileSystemMetadata(new FileInfo(path));
  168. }
  169. }
  170. /// <summary>
  171. /// Returns a <see cref="FileSystemMetadata"/> object for the specified file path.
  172. /// </summary>
  173. /// <param name="path">A path to a file.</param>
  174. /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
  175. /// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
  176. /// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> property will both be set to false.</para>
  177. /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
  178. public FileSystemMetadata GetFileInfo(string path)
  179. {
  180. if (string.IsNullOrEmpty(path))
  181. {
  182. throw new ArgumentNullException("path");
  183. }
  184. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  185. {
  186. return _sharpCifsFileSystem.GetFileInfo(path);
  187. }
  188. var fileInfo = new FileInfo(path);
  189. return GetFileSystemMetadata(fileInfo);
  190. }
  191. /// <summary>
  192. /// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path.
  193. /// </summary>
  194. /// <param name="path">A path to a directory.</param>
  195. /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
  196. /// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object's
  197. /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetadata.Exists"/> property will be set to false.</para>
  198. /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
  199. public FileSystemMetadata GetDirectoryInfo(string path)
  200. {
  201. if (string.IsNullOrEmpty(path))
  202. {
  203. throw new ArgumentNullException("path");
  204. }
  205. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  206. {
  207. return _sharpCifsFileSystem.GetDirectoryInfo(path);
  208. }
  209. var fileInfo = new DirectoryInfo(path);
  210. return GetFileSystemMetadata(fileInfo);
  211. }
  212. private FileSystemMetadata GetFileSystemMetadata(FileSystemInfo info)
  213. {
  214. var result = new FileSystemMetadata();
  215. result.Exists = info.Exists;
  216. result.FullName = info.FullName;
  217. result.Extension = info.Extension;
  218. result.Name = info.Name;
  219. if (result.Exists)
  220. {
  221. var attributes = info.Attributes;
  222. result.IsDirectory = info is DirectoryInfo || (attributes & FileAttributes.Directory) == FileAttributes.Directory;
  223. result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
  224. result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
  225. var fileInfo = info as FileInfo;
  226. if (fileInfo != null)
  227. {
  228. result.Length = fileInfo.Length;
  229. result.DirectoryName = fileInfo.DirectoryName;
  230. }
  231. result.CreationTimeUtc = GetCreationTimeUtc(info);
  232. result.LastWriteTimeUtc = GetLastWriteTimeUtc(info);
  233. }
  234. else
  235. {
  236. result.IsDirectory = info is DirectoryInfo;
  237. }
  238. return result;
  239. }
  240. /// <summary>
  241. /// The space char
  242. /// </summary>
  243. private const char SpaceChar = ' ';
  244. /// <summary>
  245. /// Takes a filename and removes invalid characters
  246. /// </summary>
  247. /// <param name="filename">The filename.</param>
  248. /// <returns>System.String.</returns>
  249. /// <exception cref="System.ArgumentNullException">filename</exception>
  250. public string GetValidFilename(string filename)
  251. {
  252. if (string.IsNullOrEmpty(filename))
  253. {
  254. throw new ArgumentNullException("filename");
  255. }
  256. var builder = new StringBuilder(filename);
  257. foreach (var c in _invalidFileNameChars)
  258. {
  259. builder = builder.Replace(c, SpaceChar);
  260. }
  261. return builder.ToString();
  262. }
  263. /// <summary>
  264. /// Gets the creation time UTC.
  265. /// </summary>
  266. /// <param name="info">The info.</param>
  267. /// <returns>DateTime.</returns>
  268. public DateTime GetCreationTimeUtc(FileSystemInfo info)
  269. {
  270. // This could throw an error on some file systems that have dates out of range
  271. try
  272. {
  273. return info.CreationTimeUtc;
  274. }
  275. catch (Exception ex)
  276. {
  277. Logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName);
  278. return DateTime.MinValue;
  279. }
  280. }
  281. /// <summary>
  282. /// Gets the creation time UTC.
  283. /// </summary>
  284. /// <param name="path">The path.</param>
  285. /// <returns>DateTime.</returns>
  286. public DateTime GetCreationTimeUtc(string path)
  287. {
  288. return GetCreationTimeUtc(GetFileSystemInfo(path));
  289. }
  290. public DateTime GetCreationTimeUtc(FileSystemMetadata info)
  291. {
  292. return info.CreationTimeUtc;
  293. }
  294. public DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
  295. {
  296. return info.LastWriteTimeUtc;
  297. }
  298. /// <summary>
  299. /// Gets the creation time UTC.
  300. /// </summary>
  301. /// <param name="info">The info.</param>
  302. /// <returns>DateTime.</returns>
  303. public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
  304. {
  305. // This could throw an error on some file systems that have dates out of range
  306. try
  307. {
  308. return info.LastWriteTimeUtc;
  309. }
  310. catch (Exception ex)
  311. {
  312. Logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName);
  313. return DateTime.MinValue;
  314. }
  315. }
  316. /// <summary>
  317. /// Gets the last write time UTC.
  318. /// </summary>
  319. /// <param name="path">The path.</param>
  320. /// <returns>DateTime.</returns>
  321. public DateTime GetLastWriteTimeUtc(string path)
  322. {
  323. return GetLastWriteTimeUtc(GetFileSystemInfo(path));
  324. }
  325. /// <summary>
  326. /// Gets the file stream.
  327. /// </summary>
  328. /// <param name="path">The path.</param>
  329. /// <param name="mode">The mode.</param>
  330. /// <param name="access">The access.</param>
  331. /// <param name="share">The share.</param>
  332. /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
  333. /// <returns>FileStream.</returns>
  334. public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false)
  335. {
  336. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  337. {
  338. return _sharpCifsFileSystem.GetFileStream(path, mode, access, share);
  339. }
  340. if (_supportsAsyncFileStreams && isAsync)
  341. {
  342. return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous);
  343. }
  344. return GetFileStream(path, mode, access, share, FileOpenOptions.None);
  345. }
  346. public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions)
  347. {
  348. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  349. {
  350. return _sharpCifsFileSystem.GetFileStream(path, mode, access, share);
  351. }
  352. var defaultBufferSize = 4096;
  353. return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), defaultBufferSize, GetFileOptions(fileOpenOptions));
  354. }
  355. private FileOptions GetFileOptions(FileOpenOptions mode)
  356. {
  357. var val = (int)mode;
  358. return (FileOptions)val;
  359. }
  360. private FileMode GetFileMode(FileOpenMode mode)
  361. {
  362. switch (mode)
  363. {
  364. //case FileOpenMode.Append:
  365. // return FileMode.Append;
  366. case FileOpenMode.Create:
  367. return FileMode.Create;
  368. case FileOpenMode.CreateNew:
  369. return FileMode.CreateNew;
  370. case FileOpenMode.Open:
  371. return FileMode.Open;
  372. case FileOpenMode.OpenOrCreate:
  373. return FileMode.OpenOrCreate;
  374. //case FileOpenMode.Truncate:
  375. // return FileMode.Truncate;
  376. default:
  377. throw new Exception("Unrecognized FileOpenMode");
  378. }
  379. }
  380. private FileAccess GetFileAccess(FileAccessMode mode)
  381. {
  382. switch (mode)
  383. {
  384. //case FileAccessMode.ReadWrite:
  385. // return FileAccess.ReadWrite;
  386. case FileAccessMode.Write:
  387. return FileAccess.Write;
  388. case FileAccessMode.Read:
  389. return FileAccess.Read;
  390. default:
  391. throw new Exception("Unrecognized FileAccessMode");
  392. }
  393. }
  394. private FileShare GetFileShare(FileShareMode mode)
  395. {
  396. switch (mode)
  397. {
  398. case FileShareMode.ReadWrite:
  399. return FileShare.ReadWrite;
  400. case FileShareMode.Write:
  401. return FileShare.Write;
  402. case FileShareMode.Read:
  403. return FileShare.Read;
  404. case FileShareMode.None:
  405. return FileShare.None;
  406. default:
  407. throw new Exception("Unrecognized FileShareMode");
  408. }
  409. }
  410. public void SetHidden(string path, bool isHidden)
  411. {
  412. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  413. {
  414. _sharpCifsFileSystem.SetHidden(path, isHidden);
  415. return;
  416. }
  417. var info = GetFileInfo(path);
  418. if (info.Exists && info.IsHidden != isHidden)
  419. {
  420. if (isHidden)
  421. {
  422. File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
  423. }
  424. else
  425. {
  426. FileAttributes attributes = File.GetAttributes(path);
  427. attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
  428. File.SetAttributes(path, attributes);
  429. }
  430. }
  431. }
  432. public void SetReadOnly(string path, bool isReadOnly)
  433. {
  434. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  435. {
  436. _sharpCifsFileSystem.SetReadOnly(path, isReadOnly);
  437. return;
  438. }
  439. var info = GetFileInfo(path);
  440. if (info.Exists && info.IsReadOnly != isReadOnly)
  441. {
  442. if (isReadOnly)
  443. {
  444. File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.ReadOnly);
  445. }
  446. else
  447. {
  448. FileAttributes attributes = File.GetAttributes(path);
  449. attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
  450. File.SetAttributes(path, attributes);
  451. }
  452. }
  453. }
  454. public void SetAttributes(string path, bool isHidden, bool isReadOnly)
  455. {
  456. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  457. {
  458. _sharpCifsFileSystem.SetAttributes(path, isHidden, isReadOnly);
  459. return;
  460. }
  461. var info = GetFileInfo(path);
  462. if (!info.Exists)
  463. {
  464. return;
  465. }
  466. if (info.IsReadOnly == isReadOnly && info.IsHidden == isHidden)
  467. {
  468. return;
  469. }
  470. var attributes = File.GetAttributes(path);
  471. if (isReadOnly)
  472. {
  473. attributes = attributes | FileAttributes.ReadOnly;
  474. }
  475. else
  476. {
  477. attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
  478. }
  479. if (isHidden)
  480. {
  481. attributes = attributes | FileAttributes.Hidden;
  482. }
  483. else
  484. {
  485. attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
  486. }
  487. File.SetAttributes(path, attributes);
  488. }
  489. private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
  490. {
  491. return attributes & ~attributesToRemove;
  492. }
  493. /// <summary>
  494. /// Swaps the files.
  495. /// </summary>
  496. /// <param name="file1">The file1.</param>
  497. /// <param name="file2">The file2.</param>
  498. public void SwapFiles(string file1, string file2)
  499. {
  500. if (string.IsNullOrEmpty(file1))
  501. {
  502. throw new ArgumentNullException("file1");
  503. }
  504. if (string.IsNullOrEmpty(file2))
  505. {
  506. throw new ArgumentNullException("file2");
  507. }
  508. var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N"));
  509. // Copying over will fail against hidden files
  510. SetHidden(file1, false);
  511. SetHidden(file2, false);
  512. Directory.CreateDirectory(_tempPath);
  513. CopyFile(file1, temp1, true);
  514. CopyFile(file2, file1, true);
  515. CopyFile(temp1, file2, true);
  516. }
  517. private char GetDirectorySeparatorChar(string path)
  518. {
  519. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  520. {
  521. return _sharpCifsFileSystem.GetDirectorySeparatorChar(path);
  522. }
  523. return Path.DirectorySeparatorChar;
  524. }
  525. public bool ContainsSubPath(string parentPath, string path)
  526. {
  527. if (string.IsNullOrEmpty(parentPath))
  528. {
  529. throw new ArgumentNullException("parentPath");
  530. }
  531. if (string.IsNullOrEmpty(path))
  532. {
  533. throw new ArgumentNullException("path");
  534. }
  535. var separatorChar = GetDirectorySeparatorChar(parentPath);
  536. return path.IndexOf(parentPath.TrimEnd(separatorChar) + separatorChar, StringComparison.OrdinalIgnoreCase) != -1;
  537. }
  538. public bool IsRootPath(string path)
  539. {
  540. if (string.IsNullOrEmpty(path))
  541. {
  542. throw new ArgumentNullException("path");
  543. }
  544. var parent = GetDirectoryName(path);
  545. if (!string.IsNullOrEmpty(parent))
  546. {
  547. return false;
  548. }
  549. return true;
  550. }
  551. public string GetDirectoryName(string path)
  552. {
  553. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  554. {
  555. return _sharpCifsFileSystem.GetDirectoryName(path);
  556. }
  557. return Path.GetDirectoryName(path);
  558. }
  559. public string NormalizePath(string path)
  560. {
  561. if (string.IsNullOrEmpty(path))
  562. {
  563. throw new ArgumentNullException("path");
  564. }
  565. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  566. {
  567. return _sharpCifsFileSystem.NormalizePath(path);
  568. }
  569. if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
  570. {
  571. return path;
  572. }
  573. return path.TrimEnd(GetDirectorySeparatorChar(path));
  574. }
  575. public bool AreEqual(string path1, string path2)
  576. {
  577. if (path1 == null && path2 == null)
  578. {
  579. return true;
  580. }
  581. if (path1 == null || path2 == null)
  582. {
  583. return false;
  584. }
  585. return string.Equals(NormalizePath(path1), NormalizePath(path2), StringComparison.OrdinalIgnoreCase);
  586. }
  587. public string GetFileNameWithoutExtension(FileSystemMetadata info)
  588. {
  589. if (info.IsDirectory)
  590. {
  591. return info.Name;
  592. }
  593. return Path.GetFileNameWithoutExtension(info.FullName);
  594. }
  595. public string GetFileNameWithoutExtension(string path)
  596. {
  597. return Path.GetFileNameWithoutExtension(path);
  598. }
  599. public bool IsPathFile(string path)
  600. {
  601. if (string.IsNullOrWhiteSpace(path))
  602. {
  603. throw new ArgumentNullException("path");
  604. }
  605. // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
  606. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  607. {
  608. return true;
  609. }
  610. if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
  611. !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
  612. {
  613. return false;
  614. }
  615. return true;
  616. //return Path.IsPathRooted(path);
  617. }
  618. public void DeleteFile(string path)
  619. {
  620. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  621. {
  622. _sharpCifsFileSystem.DeleteFile(path);
  623. return;
  624. }
  625. SetAttributes(path, false, false);
  626. File.Delete(path);
  627. }
  628. public void DeleteDirectory(string path, bool recursive)
  629. {
  630. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  631. {
  632. _sharpCifsFileSystem.DeleteDirectory(path, recursive);
  633. return;
  634. }
  635. Directory.Delete(path, recursive);
  636. }
  637. public void CreateDirectory(string path)
  638. {
  639. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  640. {
  641. _sharpCifsFileSystem.CreateDirectory(path);
  642. return;
  643. }
  644. Directory.CreateDirectory(path);
  645. }
  646. public List<FileSystemMetadata> GetDrives()
  647. {
  648. // Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
  649. return DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => new FileSystemMetadata
  650. {
  651. Name = GetName(d),
  652. FullName = d.RootDirectory.FullName,
  653. IsDirectory = true
  654. }).ToList();
  655. }
  656. private string GetName(DriveInfo drive)
  657. {
  658. return drive.Name;
  659. }
  660. public IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
  661. {
  662. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  663. {
  664. return _sharpCifsFileSystem.GetDirectories(path, recursive);
  665. }
  666. var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
  667. return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", searchOption));
  668. }
  669. public IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
  670. {
  671. return GetFiles(path, null, false, recursive);
  672. }
  673. public IEnumerable<FileSystemMetadata> GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
  674. {
  675. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  676. {
  677. return _sharpCifsFileSystem.GetFiles(path, extensions, enableCaseSensitiveExtensions, recursive);
  678. }
  679. var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
  680. // On linux and osx the search pattern is case sensitive
  681. // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
  682. if (enableCaseSensitiveExtensions && extensions != null && extensions.Length == 1)
  683. {
  684. return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], searchOption));
  685. }
  686. var files = new DirectoryInfo(path).EnumerateFiles("*", searchOption);
  687. if (extensions != null && extensions.Length > 0)
  688. {
  689. files = files.Where(i =>
  690. {
  691. var ext = i.Extension;
  692. if (ext == null)
  693. {
  694. return false;
  695. }
  696. return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
  697. });
  698. }
  699. return ToMetadata(files);
  700. }
  701. public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
  702. {
  703. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  704. {
  705. return _sharpCifsFileSystem.GetFileSystemEntries(path, recursive);
  706. }
  707. var directoryInfo = new DirectoryInfo(path);
  708. var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
  709. if (EnableFileSystemRequestConcat)
  710. {
  711. return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
  712. .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
  713. }
  714. return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", searchOption));
  715. }
  716. private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
  717. {
  718. return infos.Select(GetFileSystemMetadata);
  719. }
  720. public string[] ReadAllLines(string path)
  721. {
  722. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  723. {
  724. return _sharpCifsFileSystem.ReadAllLines(path);
  725. }
  726. return File.ReadAllLines(path);
  727. }
  728. public void WriteAllLines(string path, IEnumerable<string> lines)
  729. {
  730. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  731. {
  732. _sharpCifsFileSystem.WriteAllLines(path, lines);
  733. return;
  734. }
  735. File.WriteAllLines(path, lines);
  736. }
  737. public Stream OpenRead(string path)
  738. {
  739. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  740. {
  741. return _sharpCifsFileSystem.OpenRead(path);
  742. }
  743. return File.OpenRead(path);
  744. }
  745. public void CopyFile(string source, string target, bool overwrite)
  746. {
  747. if (_sharpCifsFileSystem.IsEnabledForPath(source))
  748. {
  749. _sharpCifsFileSystem.CopyFile(source, target, overwrite);
  750. return;
  751. }
  752. File.Copy(source, target, overwrite);
  753. }
  754. public void MoveFile(string source, string target)
  755. {
  756. if (_sharpCifsFileSystem.IsEnabledForPath(source))
  757. {
  758. _sharpCifsFileSystem.MoveFile(source, target);
  759. return;
  760. }
  761. File.Move(source, target);
  762. }
  763. public void MoveDirectory(string source, string target)
  764. {
  765. if (_sharpCifsFileSystem.IsEnabledForPath(source))
  766. {
  767. _sharpCifsFileSystem.MoveDirectory(source, target);
  768. return;
  769. }
  770. Directory.Move(source, target);
  771. }
  772. public bool DirectoryExists(string path)
  773. {
  774. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  775. {
  776. return _sharpCifsFileSystem.DirectoryExists(path);
  777. }
  778. return Directory.Exists(path);
  779. }
  780. public bool FileExists(string path)
  781. {
  782. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  783. {
  784. return _sharpCifsFileSystem.FileExists(path);
  785. }
  786. return File.Exists(path);
  787. }
  788. public string ReadAllText(string path)
  789. {
  790. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  791. {
  792. return _sharpCifsFileSystem.ReadAllText(path);
  793. }
  794. return File.ReadAllText(path);
  795. }
  796. public byte[] ReadAllBytes(string path)
  797. {
  798. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  799. {
  800. return _sharpCifsFileSystem.ReadAllBytes(path);
  801. }
  802. return File.ReadAllBytes(path);
  803. }
  804. public void WriteAllText(string path, string text, Encoding encoding)
  805. {
  806. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  807. {
  808. _sharpCifsFileSystem.WriteAllText(path, text, encoding);
  809. return;
  810. }
  811. File.WriteAllText(path, text, encoding);
  812. }
  813. public void WriteAllText(string path, string text)
  814. {
  815. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  816. {
  817. _sharpCifsFileSystem.WriteAllText(path, text);
  818. return;
  819. }
  820. File.WriteAllText(path, text);
  821. }
  822. public void WriteAllBytes(string path, byte[] bytes)
  823. {
  824. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  825. {
  826. _sharpCifsFileSystem.WriteAllBytes(path, bytes);
  827. return;
  828. }
  829. File.WriteAllBytes(path, bytes);
  830. }
  831. public string ReadAllText(string path, Encoding encoding)
  832. {
  833. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  834. {
  835. return _sharpCifsFileSystem.ReadAllText(path, encoding);
  836. }
  837. return File.ReadAllText(path, encoding);
  838. }
  839. public IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
  840. {
  841. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  842. {
  843. return _sharpCifsFileSystem.GetDirectoryPaths(path, recursive);
  844. }
  845. var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
  846. return Directory.EnumerateDirectories(path, "*", searchOption);
  847. }
  848. public IEnumerable<string> GetFilePaths(string path, bool recursive = false)
  849. {
  850. return GetFilePaths(path, null, false, recursive);
  851. }
  852. public IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
  853. {
  854. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  855. {
  856. return _sharpCifsFileSystem.GetFilePaths(path, extensions, enableCaseSensitiveExtensions, recursive);
  857. }
  858. var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
  859. // On linux and osx the search pattern is case sensitive
  860. // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
  861. if (enableCaseSensitiveExtensions && extensions != null && extensions.Length == 1)
  862. {
  863. return Directory.EnumerateFiles(path, "*" + extensions[0], searchOption);
  864. }
  865. var files = Directory.EnumerateFiles(path, "*", searchOption);
  866. if (extensions != null && extensions.Length > 0)
  867. {
  868. files = files.Where(i =>
  869. {
  870. var ext = Path.GetExtension(i);
  871. if (ext == null)
  872. {
  873. return false;
  874. }
  875. return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
  876. });
  877. }
  878. return files;
  879. }
  880. public IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
  881. {
  882. if (_sharpCifsFileSystem.IsEnabledForPath(path))
  883. {
  884. return _sharpCifsFileSystem.GetFileSystemEntryPaths(path, recursive);
  885. }
  886. var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
  887. return Directory.EnumerateFileSystemEntries(path, "*", searchOption);
  888. }
  889. public virtual void SetExecutable(string path)
  890. {
  891. if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
  892. {
  893. RunProcess("chmod", "+x \"" + path + "\"", GetDirectoryName(path));
  894. }
  895. }
  896. private void RunProcess(string path, string args, string workingDirectory)
  897. {
  898. using (var process = Process.Start(new ProcessStartInfo
  899. {
  900. Arguments = args,
  901. FileName = path,
  902. CreateNoWindow = true,
  903. WorkingDirectory = workingDirectory,
  904. WindowStyle = ProcessWindowStyle.Normal
  905. }))
  906. {
  907. process.WaitForExit();
  908. }
  909. }
  910. }
  911. }