using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Security.AccessControl ;
using System.Security.Principal ;
using NLog ;
using NzbDrone.Common.EnsureThat ;
using NzbDrone.Common.EnvironmentInfo ;
using NzbDrone.Common.Extensions ;
using NzbDrone.Common.Instrumentation ;
using NzbDrone.Common.Instrumentation.Extensions ;
namespace NzbDrone.Common.Disk
{
public abstract class DiskProviderBase : IDiskProvider
{
private static readonly Logger Logger = NzbDroneLogger . GetLogger ( typeof ( DiskProviderBase ) ) ;
public abstract long? GetAvailableSpace ( string path ) ;
public abstract void InheritFolderPermissions ( string filename ) ;
public abstract void SetPermissions ( string path , string mask , string user , string group ) ;
public abstract long? GetTotalSize ( string path ) ;
public DateTime FolderGetCreationTime ( string path )
{
CheckFolderExists ( path ) ;
return new DirectoryInfo ( path ) . CreationTimeUtc ;
}
public DateTime FolderGetLastWrite ( string path )
{
CheckFolderExists ( path ) ;
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 ) ;
}
public DateTime FileGetLastWrite ( string path )
{
CheckFileExists ( path ) ;
return new FileInfo ( path ) . LastWriteTimeUtc ;
}
private void CheckFolderExists ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
if ( ! FolderExists ( path ) )
{
throw new DirectoryNotFoundException ( "Directory doesn't exist. " + path ) ;
}
}
private void CheckFileExists ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
if ( ! FileExists ( path ) )
{
throw new FileNotFoundException ( "File doesn't exist: " + path ) ;
}
}
public void EnsureFolder ( string path )
{
if ( ! FolderExists ( path ) )
{
CreateFolder ( path ) ;
}
}
public bool FolderExists ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
return Directory . Exists ( path ) ;
}
public bool FileExists ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
return FileExists ( path , OsInfo . PathStringComparison ) ;
}
public bool FileExists ( string path , StringComparison stringComparison )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
switch ( stringComparison )
{
case StringComparison . CurrentCulture :
case StringComparison . InvariantCulture :
case StringComparison . Ordinal :
{
return File . Exists ( path ) & & path = = path . GetActualCasing ( ) ;
}
default :
{
return File . Exists ( path ) ;
}
}
}
public bool FolderWritable ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
try
{
var testPath = Path . Combine ( path , "sonarr_write_test.txt" ) ;
var testContent = string . Format ( "This file was created to verify if '{0}' is writable. It should've been automatically deleted. Feel free to delete it." , path ) ;
File . WriteAllText ( testPath , testContent ) ;
File . Delete ( testPath ) ;
return true ;
}
catch ( Exception e )
{
Logger . Trace ( "Directory '{0}' isn't writable. {1}" , path , e . Message ) ;
return false ;
}
}
public string [ ] GetDirectories ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
return Directory . GetDirectories ( path ) ;
}
public string [ ] GetFiles ( string path , SearchOption searchOption )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
return Directory . GetFiles ( path , "*.*" , searchOption ) ;
}
public long GetFolderSize ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
return GetFiles ( path , SearchOption . AllDirectories ) . Sum ( e = > new FileInfo ( e ) . Length ) ;
}
public long GetFileSize ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
if ( ! FileExists ( path ) )
{
throw new FileNotFoundException ( "File doesn't exist: " + path ) ;
}
var fi = new FileInfo ( path ) ;
return fi . Length ;
}
public void CreateFolder ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
Directory . CreateDirectory ( path ) ;
}
public void DeleteFile ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
Logger . Trace ( "Deleting file: {0}" , path ) ;
RemoveReadOnly ( path ) ;
File . Delete ( path ) ;
}
public void CopyFile ( string source , string destination , bool overwrite = false )
{
Ensure . That ( source , ( ) = > source ) . IsValidPath ( ) ;
Ensure . That ( destination , ( ) = > destination ) . IsValidPath ( ) ;
if ( source . PathEquals ( destination ) )
{
throw new IOException ( string . Format ( "Source and destination can't be the same {0}" , source ) ) ;
}
File . Copy ( source , destination , overwrite ) ;
}
public void MoveFile ( string source , string destination , bool overwrite = false )
{
Ensure . That ( source , ( ) = > source ) . IsValidPath ( ) ;
Ensure . That ( destination , ( ) = > destination ) . IsValidPath ( ) ;
if ( source . PathEquals ( destination ) )
{
throw new IOException ( string . Format ( "Source and destination can't be the same {0}" , source ) ) ;
}
if ( FileExists ( destination ) & & overwrite )
{
DeleteFile ( destination ) ;
}
RemoveReadOnly ( source ) ;
File . Move ( source , destination ) ;
}
public abstract bool TryCreateHardLink ( string source , string destination ) ;
public void DeleteFolder ( string path , bool recursive )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
var files = Directory . GetFiles ( path , "*.*" , recursive ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly ) ;
Array . ForEach ( files , RemoveReadOnly ) ;
Directory . Delete ( path , recursive ) ;
}
public string ReadAllText ( string filePath )
{
Ensure . That ( filePath , ( ) = > filePath ) . IsValidPath ( ) ;
return File . ReadAllText ( filePath ) ;
}
public void WriteAllText ( string filename , string contents )
{
Ensure . That ( filename , ( ) = > filename ) . IsValidPath ( ) ;
RemoveReadOnly ( filename ) ;
File . WriteAllText ( filename , contents ) ;
}
public void FolderSetLastWriteTime ( string path , DateTime dateTime )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
Directory . SetLastWriteTimeUtc ( path , dateTime ) ;
}
public void FileSetLastWriteTime ( string path , DateTime dateTime )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
File . SetLastWriteTime ( path , dateTime ) ;
}
public bool IsFileLocked ( string file )
{
try
{
using ( File . Open ( file , FileMode . Open , FileAccess . Read , FileShare . None ) )
{
return false ;
}
}
catch ( IOException )
{
return true ;
}
}
public string GetPathRoot ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
return Path . GetPathRoot ( path ) ;
}
public string GetParentFolder ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
var parent = Directory . GetParent ( path . TrimEnd ( Path . DirectorySeparatorChar ) ) ;
if ( parent = = null )
{
return null ;
}
return parent . FullName ;
}
public void SetPermissions ( string filename , WellKnownSidType accountSid , FileSystemRights rights , AccessControlType controlType )
{
try
{
var sid = new SecurityIdentifier ( accountSid , null ) ;
var directoryInfo = new DirectoryInfo ( filename ) ;
var directorySecurity = directoryInfo . GetAccessControl ( ) ;
var accessRule = new FileSystemAccessRule ( sid , rights ,
InheritanceFlags . ContainerInherit | InheritanceFlags . ObjectInherit ,
PropagationFlags . None , controlType ) ;
directorySecurity . AddAccessRule ( accessRule ) ;
directoryInfo . SetAccessControl ( directorySecurity ) ;
}
catch ( Exception e )
{
Logger . WarnException ( string . Format ( "Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}" , filename , accountSid , rights , controlType ) , e ) ;
throw ;
}
}
private static void RemoveReadOnly ( string path )
{
if ( File . Exists ( path ) )
{
var attributes = File . GetAttributes ( path ) ;
if ( attributes . HasFlag ( FileAttributes . ReadOnly ) )
{
var newAttributes = attributes & ~ ( FileAttributes . ReadOnly ) ;
File . SetAttributes ( path , newAttributes ) ;
}
}
}
public FileAttributes GetFileAttributes ( string path )
{
return File . GetAttributes ( path ) ;
}
public void EmptyFolder ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
foreach ( var file in GetFiles ( path , SearchOption . TopDirectoryOnly ) )
{
DeleteFile ( file ) ;
}
foreach ( var directory in GetDirectories ( path ) )
{
DeleteFolder ( directory , true ) ;
}
}
public string [ ] GetFixedDrives ( )
{
return GetMounts ( ) . Where ( x = > x . DriveType = = DriveType . Fixed ) . Select ( x = > x . RootDirectory ) . ToArray ( ) ;
}
public string GetVolumeLabel ( string path )
{
var driveInfo = GetMounts ( ) . SingleOrDefault ( d = > d . RootDirectory . PathEquals ( path ) ) ;
if ( driveInfo = = null )
{
return null ;
}
return driveInfo . VolumeLabel ;
}
public FileStream OpenReadStream ( string path )
{
if ( ! FileExists ( path ) )
{
throw new FileNotFoundException ( "Unable to find file: " + path , path ) ;
}
return new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
}
public FileStream OpenWriteStream ( string path )
{
return new FileStream ( path , FileMode . Create ) ;
}
public virtual List < IMount > GetMounts ( )
{
return GetDriveInfoMounts ( ) ;
}
public virtual IMount GetMount ( string path )
{
var mounts = GetMounts ( ) ;
return mounts . Where ( drive = > drive . RootDirectory . PathEquals ( path ) | |
drive . RootDirectory . IsParentPath ( path ) )
. OrderByDescending ( drive = > drive . RootDirectory . Length )
. FirstOrDefault ( ) ;
}
protected List < IMount > GetDriveInfoMounts ( )
{
return DriveInfo . GetDrives ( )
. Where ( d = > d . DriveType = = DriveType . Fixed | | d . DriveType = = DriveType . Network | | d . DriveType = = DriveType . Removable )
. Where ( d = > d . IsReady )
. Select ( d = > new DriveInfoMount ( d ) )
. Cast < IMount > ( )
. ToList ( ) ;
}
public List < DirectoryInfo > GetDirectoryInfos ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
var di = new DirectoryInfo ( path ) ;
return di . GetDirectories ( ) . ToList ( ) ;
}
public List < FileInfo > GetFileInfos ( string path )
{
Ensure . That ( path , ( ) = > path ) . IsValidPath ( ) ;
var di = new DirectoryInfo ( path ) ;
return di . GetFiles ( ) . ToList ( ) ;
}
public void RemoveEmptySubfolders ( string path )
{
var subfolders = GetDirectories ( path ) ;
var files = GetFiles ( path , SearchOption . AllDirectories ) ;
foreach ( var subfolder in subfolders )
{
if ( files . None ( f = > subfolder . IsParentPath ( f ) ) )
{
DeleteFolder ( subfolder , false ) ;
}
}
}
}
}