2011-10-23 07:26:43 +02:00
using System ;
using System.IO ;
using System.Linq ;
2011-11-13 05:07:06 +01:00
using System.Runtime.InteropServices ;
2013-07-05 08:30:45 +02:00
using System.Security.AccessControl ;
2013-08-13 02:22:35 +02:00
using System.Security.Principal ;
2011-10-23 07:26:43 +02:00
using NLog ;
2013-05-25 00:18:37 +02:00
using NzbDrone.Common.EnsureThat ;
2013-06-28 02:04:52 +02:00
using NzbDrone.Common.EnvironmentInfo ;
2013-08-31 03:42:30 +02:00
using NzbDrone.Common.Instrumentation ;
2011-10-23 07:26:43 +02:00
2011-10-24 00:44:37 +02:00
namespace NzbDrone.Common
2011-10-23 07:26:43 +02:00
{
2013-05-11 01:53:50 +02:00
public interface IDiskProvider
{
DateTime GetLastFolderWrite ( string path ) ;
DateTime GetLastFileWrite ( string path ) ;
void EnsureFolder ( string path ) ;
bool FolderExists ( string path ) ;
bool FileExists ( string path ) ;
2013-07-24 08:26:10 +02:00
bool FileExists ( string path , bool caseSensitive ) ;
2013-05-11 01:53:50 +02:00
string [ ] GetDirectories ( string path ) ;
string [ ] GetFiles ( string path , SearchOption searchOption ) ;
2013-07-05 06:43:28 +02:00
long GetFolderSize ( string path ) ;
2013-05-29 06:10:23 +02:00
long GetFileSize ( string path ) ;
2013-09-12 08:52:37 +02:00
void CreateFolder ( string path ) ;
void CopyFolder ( string source , string destination ) ;
2013-07-05 06:43:28 +02:00
void MoveFolder ( string source , string destination ) ;
2013-05-11 01:53:50 +02:00
void DeleteFile ( string path ) ;
void MoveFile ( string source , string destination ) ;
void DeleteFolder ( string path , bool recursive ) ;
void InheritFolderPermissions ( string filename ) ;
2013-09-01 06:38:06 +02:00
long? GetAvailableSpace ( string path ) ;
2013-05-11 01:53:50 +02:00
string ReadAllText ( string filePath ) ;
void WriteAllText ( string filename , string contents ) ;
void FileSetLastWriteTimeUtc ( string path , DateTime dateTime ) ;
2013-07-05 06:43:28 +02:00
void FolderSetLastWriteTimeUtc ( string path , DateTime dateTime ) ;
2013-09-12 08:52:37 +02:00
bool IsFileLocked ( string path ) ;
2013-05-11 01:53:50 +02:00
string GetPathRoot ( string path ) ;
2013-08-13 02:22:35 +02:00
void SetPermissions ( string filename , WellKnownSidType accountSid , FileSystemRights rights , AccessControlType controlType ) ;
2013-08-12 00:55:26 +02:00
bool IsParent ( string parentPath , string childPath ) ;
2013-08-20 21:28:46 +02:00
FileAttributes GetFileAttributes ( string path ) ;
2013-09-08 19:13:46 +02:00
void EmptyFolder ( string path ) ;
2013-05-11 01:53:50 +02:00
}
public class DiskProvider : IDiskProvider
2011-10-23 07:26:43 +02:00
{
2013-08-20 21:28:46 +02:00
enum TransferAction
2011-11-18 07:52:50 +01:00
{
Copy ,
Move
}
2011-11-13 05:07:06 +01:00
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetDiskFreeSpaceEx ( string lpDirectoryName ,
out ulong lpFreeBytesAvailable ,
out ulong lpTotalNumberOfBytes ,
out ulong lpTotalNumberOfFreeBytes ) ;
2013-09-12 08:52:37 +02:00
private static readonly Logger Logger = NzbDroneLogger . GetLogger ( ) ;
2011-10-23 07:26:43 +02:00
2013-08-03 05:01:16 +02:00
public DateTime GetLastFolderWrite ( string path )
2012-01-23 05:34:30 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-01-23 05:34:30 +01:00
if ( ! FolderExists ( path ) )
{
throw new DirectoryNotFoundException ( "Directory doesn't exist. " + path ) ;
}
2012-01-23 05:59:23 +01:00
var dirFiles = GetFiles ( path , SearchOption . AllDirectories ) . ToList ( ) ;
if ( ! dirFiles . Any ( ) )
{
return new DirectoryInfo ( path ) . LastWriteTimeUtc ;
}
return dirFiles . Select ( f = > new FileInfo ( f ) )
. Max ( c = > c . LastWriteTimeUtc ) ;
2012-01-23 05:34:30 +01:00
}
2013-08-03 05:01:16 +02:00
public DateTime GetLastFileWrite ( string path )
2012-08-29 17:34:51 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-08-29 17:34:51 +02:00
if ( ! FileExists ( path ) )
2013-09-13 06:59:29 +02:00
{
2012-08-29 17:34:51 +02:00
throw new FileNotFoundException ( "File doesn't exist: " + path ) ;
2013-09-13 06:59:29 +02:00
}
2012-08-29 17:34:51 +02:00
return new FileInfo ( path ) . LastWriteTimeUtc ;
}
2013-08-03 05:01:16 +02:00
public void EnsureFolder ( string path )
2013-04-15 03:41:39 +02:00
{
if ( ! FolderExists ( path ) )
{
CreateFolder ( path ) ;
}
}
2013-08-03 05:01:16 +02:00
public bool FolderExists ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2011-10-23 07:26:43 +02:00
return Directory . Exists ( path ) ;
}
2013-07-26 07:55:19 +02:00
2013-08-03 05:01:16 +02:00
public bool FileExists ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2011-10-23 07:26:43 +02:00
return File . Exists ( path ) ;
}
2013-08-03 05:01:16 +02:00
public bool FileExists ( string path , bool caseSensitive )
2013-07-24 08:26:10 +02:00
{
if ( caseSensitive )
{
return FileExists ( path ) & & path = = path . GetActualCasing ( ) ;
}
return FileExists ( path ) ;
}
2013-08-03 05:01:16 +02:00
public string [ ] GetDirectories ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-01-23 07:43:11 +01:00
return Directory . GetDirectories ( path ) ;
2011-10-23 07:26:43 +02:00
}
2013-08-03 05:01:16 +02:00
public string [ ] GetFiles ( string path , SearchOption searchOption )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-01-23 07:43:11 +01:00
return Directory . GetFiles ( path , "*.*" , searchOption ) ;
2011-10-23 07:26:43 +02:00
}
2013-08-03 05:01:16 +02:00
public long GetFolderSize ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2011-10-23 07:26:43 +02:00
return GetFiles ( path , SearchOption . AllDirectories ) . Sum ( e = > new FileInfo ( e ) . Length ) ;
}
2013-08-03 05:01:16 +02:00
public long GetFileSize ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-10-20 03:42:42 +02:00
if ( ! FileExists ( path ) )
2013-09-13 06:59:29 +02:00
{
2012-10-20 03:42:42 +02:00
throw new FileNotFoundException ( "File doesn't exist: " + path ) ;
2013-09-13 06:59:29 +02:00
}
2012-10-20 03:42:42 +02:00
2011-10-23 07:26:43 +02:00
var fi = new FileInfo ( path ) ;
return fi . Length ;
}
2013-09-12 08:52:37 +02:00
public void CreateFolder ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2013-09-12 08:52:37 +02:00
Directory . CreateDirectory ( path ) ;
2011-10-23 07:26:43 +02:00
}
2013-09-12 08:52:37 +02:00
public void CopyFolder ( string source , string destination )
2011-11-13 05:07:06 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > source ) . IsValidPath ( ) ;
2013-09-12 08:52:37 +02:00
Ensure . That ( ( ) = > destination ) . IsValidPath ( ) ;
2013-05-25 00:18:37 +02:00
2013-09-12 08:52:37 +02:00
TransferFolder ( source , destination , TransferAction . Copy ) ;
2011-11-18 07:52:50 +01:00
}
2013-08-03 05:01:16 +02:00
public void MoveFolder ( string source , string destination )
2011-11-18 07:52:50 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > source ) . IsValidPath ( ) ;
Ensure . That ( ( ) = > destination ) . IsValidPath ( ) ;
2011-11-18 07:52:50 +01:00
try
{
2013-07-05 06:43:28 +02:00
TransferFolder ( source , destination , TransferAction . Move ) ;
2013-09-13 06:59:29 +02:00
DeleteFolder ( source , true ) ;
2011-11-18 07:52:50 +01:00
}
catch ( Exception e )
{
e . Data . Add ( "Source" , source ) ;
e . Data . Add ( "Destination" , destination ) ;
throw ;
}
}
2013-07-05 06:43:28 +02:00
private void TransferFolder ( string source , string target , TransferAction transferAction )
2011-11-18 07:52:50 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > source ) . IsValidPath ( ) ;
Ensure . That ( ( ) = > target ) . IsValidPath ( ) ;
2011-11-18 07:52:50 +01:00
Logger . Trace ( "{0} {1} -> {2}" , transferAction , source , target ) ;
2011-11-13 05:07:06 +01:00
var sourceFolder = new DirectoryInfo ( source ) ;
var targetFolder = new DirectoryInfo ( target ) ;
if ( ! targetFolder . Exists )
{
targetFolder . Create ( ) ;
}
2011-11-14 05:05:33 +01:00
foreach ( var subDir in sourceFolder . GetDirectories ( ) )
{
2013-07-05 06:43:28 +02:00
TransferFolder ( subDir . FullName , Path . Combine ( target , subDir . Name ) , transferAction ) ;
2011-11-14 05:05:33 +01:00
}
2011-11-18 08:16:05 +01:00
foreach ( var sourceFile in sourceFolder . GetFiles ( "*.*" , SearchOption . TopDirectoryOnly ) )
2011-11-13 05:07:06 +01:00
{
2011-11-18 08:16:05 +01:00
var destFile = Path . Combine ( target , sourceFile . Name ) ;
2011-11-18 07:52:50 +01:00
2013-07-19 01:41:11 +02:00
Logger . Trace ( "{0} {1} -> {2}" , transferAction , sourceFile , destFile ) ;
2011-11-18 07:52:50 +01:00
switch ( transferAction )
{
case TransferAction . Copy :
{
2011-11-18 08:16:05 +01:00
sourceFile . CopyTo ( destFile , true ) ;
2011-11-18 07:52:50 +01:00
break ;
}
case TransferAction . Move :
{
2011-11-18 08:16:05 +01:00
MoveFile ( sourceFile . FullName , destFile ) ;
2011-11-18 07:52:50 +01:00
break ;
}
}
2011-11-13 05:07:06 +01:00
}
}
2013-08-03 05:01:16 +02:00
public void DeleteFile ( string path )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2011-11-20 06:35:44 +01:00
Logger . Trace ( "Deleting file: {0}" , path ) ;
2013-09-13 06:59:29 +02:00
RemoveReadOnly ( path ) ;
2011-10-23 07:26:43 +02:00
File . Delete ( path ) ;
}
2013-08-03 05:01:16 +02:00
public void MoveFile ( string source , string destination )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > source ) . IsValidPath ( ) ;
Ensure . That ( ( ) = > destination ) . IsValidPath ( ) ;
2013-08-20 21:28:46 +02:00
if ( source . PathEquals ( destination ) )
2011-11-18 08:16:05 +01:00
{
2011-12-14 09:06:54 +01:00
Logger . Warn ( "Source and destination can't be the same {0}" , source ) ;
return ;
2011-11-18 08:16:05 +01:00
}
2011-12-14 09:06:54 +01:00
if ( FileExists ( destination ) )
{
DeleteFile ( destination ) ;
}
2013-09-13 06:59:29 +02:00
RemoveReadOnly ( source ) ;
2011-12-14 09:06:54 +01:00
File . Move ( source , destination ) ;
2011-10-23 07:26:43 +02:00
}
2013-08-03 05:01:16 +02:00
public void DeleteFolder ( string path , bool recursive )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2011-10-23 07:26:43 +02:00
Directory . Delete ( path , recursive ) ;
}
2013-08-03 05:01:16 +02:00
public void InheritFolderPermissions ( string filename )
2011-10-23 07:26:43 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > filename ) . IsValidPath ( ) ;
2013-09-15 02:08:16 +02:00
try
{
var fs = File . GetAccessControl ( filename ) ;
fs . SetAccessRuleProtection ( false , false ) ;
File . SetAccessControl ( filename , fs ) ;
}
catch ( NotImplementedException )
{
if ( ! OsInfo . IsLinux )
{
throw ;
}
}
2011-10-23 07:26:43 +02:00
}
2011-11-13 05:07:06 +01:00
2013-09-01 06:38:06 +02:00
public long? GetAvailableSpace ( string path )
2011-11-13 05:07:06 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2013-09-01 06:38:06 +02:00
var root = GetPathRoot ( path ) ;
if ( ! FolderExists ( root ) )
throw new DirectoryNotFoundException ( root ) ;
2013-07-12 02:07:21 +02:00
if ( OsInfo . IsLinux )
2013-04-30 08:11:49 +02:00
{
2013-09-01 06:38:06 +02:00
var drives = DriveInfo . GetDrives ( ) ;
2013-07-12 02:07:21 +02:00
2013-09-01 06:38:06 +02:00
foreach ( var drive in drives )
2013-04-30 08:11:49 +02:00
{
2013-09-01 06:38:06 +02:00
try
{
if ( drive . IsReady & & path . StartsWith ( drive . Name , StringComparison . CurrentCultureIgnoreCase ) )
{
return drive . AvailableFreeSpace ;
}
}
catch ( InvalidOperationException e )
{
Logger . ErrorException ( "Couldn't get free space for " + path , e ) ;
}
2013-04-30 08:11:49 +02:00
}
2013-05-11 01:53:50 +02:00
2013-09-01 06:38:06 +02:00
return null ;
2013-04-30 08:11:49 +02:00
}
2013-08-09 06:44:49 +02:00
2013-08-09 03:40:24 +02:00
return DriveFreeSpaceEx ( root ) ;
2013-08-20 21:28:46 +02:00
}
2013-04-30 08:11:49 +02:00
private static long DriveFreeSpaceEx ( string folderName )
{
if ( string . IsNullOrEmpty ( folderName ) )
{
throw new ArgumentNullException ( "folderName" ) ;
}
if ( ! folderName . EndsWith ( "\\" ) )
{
folderName + = '\\' ;
}
ulong free = 0 ;
ulong dummy1 = 0 ;
ulong dummy2 = 0 ;
if ( GetDiskFreeSpaceEx ( folderName , out free , out dummy1 , out dummy2 ) )
{
return ( long ) free ;
}
return 0 ;
2011-11-13 05:07:06 +01:00
}
2011-11-22 07:55:09 +01:00
2013-08-03 05:01:16 +02:00
public string ReadAllText ( string filePath )
2011-11-22 07:55:09 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > filePath ) . IsValidPath ( ) ;
2011-11-22 07:55:09 +01:00
return File . ReadAllText ( filePath ) ;
}
2011-12-10 20:22:47 +01:00
2013-08-03 05:01:16 +02:00
public void WriteAllText ( string filename , string contents )
2012-07-10 06:37:24 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > filename ) . IsValidPath ( ) ;
2013-09-13 06:59:29 +02:00
RemoveReadOnly ( filename ) ;
2012-07-10 06:37:24 +02:00
File . WriteAllText ( filename , contents ) ;
}
2011-12-10 20:22:47 +01:00
2013-08-03 05:01:16 +02:00
public void FileSetLastWriteTimeUtc ( string path , DateTime dateTime )
2012-09-04 08:49:04 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-09-04 08:49:04 +02:00
File . SetLastWriteTimeUtc ( path , dateTime ) ;
}
2013-08-03 05:01:16 +02:00
public void FolderSetLastWriteTimeUtc ( string path , DateTime dateTime )
2012-09-04 08:49:04 +02:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-09-04 08:49:04 +02:00
Directory . SetLastWriteTimeUtc ( path , dateTime ) ;
}
2012-10-20 10:01:47 +02:00
2013-09-12 08:52:37 +02:00
public bool IsFileLocked ( string file )
2012-10-20 10:01:47 +02:00
{
try
{
2013-09-13 06:59:29 +02:00
using ( File . Open ( file , FileMode . Open , FileAccess . Read , FileShare . None ) )
{
return false ;
}
2012-10-20 10:01:47 +02:00
}
catch ( IOException )
{
return true ;
}
}
2013-08-03 05:01:16 +02:00
public string GetPathRoot ( string path )
2012-12-24 08:16:43 +01:00
{
2013-05-25 00:18:37 +02:00
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
2012-12-24 08:16:43 +01:00
return Path . GetPathRoot ( path ) ;
}
2013-07-05 08:30:45 +02:00
2013-08-13 02:22:35 +02:00
public void SetPermissions ( string filename , WellKnownSidType accountSid , FileSystemRights rights , AccessControlType controlType )
2013-07-05 08:30:45 +02:00
{
2013-07-26 07:55:19 +02:00
try
{
2013-08-13 02:22:35 +02:00
var sid = new SecurityIdentifier ( accountSid , null ) ;
2013-07-05 08:30:45 +02:00
2013-07-26 07:55:19 +02:00
var directoryInfo = new DirectoryInfo ( filename ) ;
var directorySecurity = directoryInfo . GetAccessControl ( ) ;
2013-08-13 02:22:35 +02:00
var accessRule = new FileSystemAccessRule ( sid , rights ,
2013-07-26 07:55:19 +02:00
InheritanceFlags . ContainerInherit | InheritanceFlags . ObjectInherit ,
PropagationFlags . None , controlType ) ;
directorySecurity . AddAccessRule ( accessRule ) ;
directoryInfo . SetAccessControl ( directorySecurity ) ;
}
catch ( Exception e )
{
2013-08-13 02:22:35 +02:00
Logger . WarnException ( string . Format ( "Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}" , filename , accountSid , rights , controlType ) , e ) ;
2013-07-26 07:55:19 +02:00
throw ;
}
2013-07-05 08:30:45 +02:00
}
2013-07-23 02:50:37 +02:00
2013-08-12 00:55:26 +02:00
public bool IsParent ( string parentPath , string childPath )
2013-07-23 02:50:37 +02:00
{
2013-08-12 00:55:26 +02:00
parentPath = parentPath . TrimEnd ( Path . DirectorySeparatorChar ) ;
childPath = childPath . TrimEnd ( Path . DirectorySeparatorChar ) ;
2013-07-23 02:50:37 +02:00
2013-08-12 00:55:26 +02:00
var parent = new DirectoryInfo ( parentPath ) ;
var child = new DirectoryInfo ( childPath ) ;
2013-07-23 02:50:37 +02:00
2013-08-12 00:55:26 +02:00
while ( child . Parent ! = null )
2013-07-23 02:50:37 +02:00
{
2013-08-12 00:55:26 +02:00
if ( child . Parent . FullName = = parent . FullName )
2013-07-23 02:50:37 +02:00
{
return true ;
}
2013-07-26 07:55:19 +02:00
2013-08-12 00:55:26 +02:00
child = child . Parent ;
2013-07-23 02:50:37 +02:00
}
return false ;
}
2013-07-30 02:09:48 +02:00
2013-09-13 06:59:29 +02:00
private static void RemoveReadOnly ( string path )
{
if ( File . Exists ( path ) )
{
var newAttributes = File . GetAttributes ( path ) & ~ ( FileAttributes . ReadOnly ) ;
File . SetAttributes ( path , newAttributes ) ;
}
}
2013-07-30 02:09:48 +02:00
public FileAttributes GetFileAttributes ( string path )
{
return File . GetAttributes ( path ) ;
}
2013-09-08 19:13:46 +02:00
public void EmptyFolder ( string path )
{
Ensure . That ( ( ) = > path ) . IsValidPath ( ) ;
foreach ( var file in GetFiles ( path , SearchOption . TopDirectoryOnly ) )
{
DeleteFile ( file ) ;
}
foreach ( var directory in GetDirectories ( path ) )
{
DeleteFolder ( directory , true ) ;
}
}
2011-10-23 07:26:43 +02:00
}
2011-11-13 05:07:06 +01:00
}