1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.IO; 10 using System.Linq; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Exceptions; 13 14 namespace Antmicro.Renode.Utilities 15 { 16 public class FilePath 17 { FilePath(string path, FileAccess fileAccess, bool validate = true)18 public FilePath(string path, FileAccess fileAccess, bool validate = true) 19 { 20 this.path = path; 21 this.fileAccess = fileAccess; 22 23 if(validate) 24 { 25 Validate(); 26 } 27 } 28 Validate()29 public virtual void Validate() 30 { 31 if(!File.Exists(path)) 32 { 33 throw new RecoverableException($"File does not exist: {path}"); 34 } 35 36 try 37 { 38 using(var fs = File.Open(path, FileMode.Open, fileAccess, FileShare.ReadWrite)) 39 { 40 if(!fs.CanRead && fileAccess == FileAccess.Read) 41 { 42 throw new RecoverableException($"File is not readable: {path}"); 43 } 44 if(!fs.CanWrite && fileAccess == FileAccess.Write) 45 { 46 throw new RecoverableException($"File is not writable: {path}"); 47 } 48 } 49 } 50 catch(UnauthorizedAccessException e) 51 { 52 throw new RecoverableException($"Error while accessing {path}: {e.Message}"); 53 } 54 } 55 ToString()56 public override string ToString() 57 { 58 return path; 59 } 60 operator string(FilePath fp)61 public static implicit operator string(FilePath fp) 62 { 63 return fp.path; 64 } 65 CanBeCreated()66 protected bool CanBeCreated() 67 { 68 if(File.Exists(path)) 69 { 70 return false; 71 } 72 73 try 74 { 75 using(File.Create(path)) 76 { 77 } 78 File.Delete(path); 79 } 80 catch 81 { 82 return false; 83 } 84 85 return true; 86 } 87 88 protected readonly string path; 89 protected readonly FileAccess fileAccess; 90 } 91 92 public class ReadFilePath : FilePath 93 { ReadFilePath(string path)94 public ReadFilePath(string path) : base(path, FileAccess.Read) {} 95 operator ReadFilePath(string path)96 public static implicit operator ReadFilePath(string path) 97 { 98 return new ReadFilePath(path); 99 } 100 } 101 102 public class OptionalReadFilePath : FilePath 103 { OptionalReadFilePath(string path)104 public OptionalReadFilePath(string path) : base(path, FileAccess.Read, false) 105 { 106 if(path != null) 107 { 108 Validate(); 109 } 110 } 111 operator string(OptionalReadFilePath fp)112 public static implicit operator string(OptionalReadFilePath fp) 113 { 114 return fp?.path; 115 } 116 operator OptionalReadFilePath(string path)117 public static implicit operator OptionalReadFilePath(string path) 118 { 119 return new OptionalReadFilePath(path); 120 } 121 operator OptionalReadFilePath(ReadFilePath fp)122 public static implicit operator OptionalReadFilePath(ReadFilePath fp) 123 { 124 return new OptionalReadFilePath(fp); 125 } 126 } 127 128 public class AppendFilePath : FilePath 129 { AppendFilePath(string path)130 public AppendFilePath(string path) : base(path, FileAccess.Write) {} 131 operator AppendFilePath(string path)132 public static implicit operator AppendFilePath(string path) 133 { 134 return new AppendFilePath(path); 135 } 136 } 137 138 public class WriteFilePath : FilePath 139 { WriteFilePath(string path)140 public WriteFilePath(string path) : base(path, FileAccess.Write) {} 141 Validate()142 public override void Validate() 143 { 144 if(!File.Exists(path)) 145 { 146 if(!CanBeCreated()) 147 { 148 throw new RecoverableException($"File {path} could not be created"); 149 } 150 return; 151 } 152 153 base.Validate(); 154 } 155 operator WriteFilePath(string path)156 public static implicit operator WriteFilePath(string path) 157 { 158 return new WriteFilePath(path); 159 } 160 } 161 162 public class SequencedFilePath : WriteFilePath 163 { SequencedFilePath(string path)164 public SequencedFilePath(string path) : base(path) {} 165 Validate()166 public override void Validate() 167 { 168 if(!File.Exists(path)) 169 { 170 if(!CanBeCreated()) 171 { 172 throw new RecoverableException($"File {path} could not be created"); 173 } 174 return; 175 } 176 177 var lastSplit = path.LastIndexOf(Path.DirectorySeparatorChar); 178 if(lastSplit == -1) 179 { 180 throw new RecoverableException($"{path} is an invalid path"); 181 } 182 183 var dirPath = path.Substring(0, lastSplit); 184 var fileName = path.Substring(lastSplit + 1); 185 var pathGlob = string.Concat(fileName, ".*"); 186 187 var lastIndex = Directory.EnumerateFiles(dirPath, pathGlob) 188 .Select(path => path.Substring(path.LastIndexOf('.') + 1)) 189 .Where(suffix => suffix.All(char.IsDigit)) 190 .Select(suffix => int.Parse(suffix)) 191 .Concat(new [] { 0 }) 192 .Max(); 193 194 var newPath = string.Format("{0}.{1}", path, lastIndex + 1); 195 196 try 197 { 198 File.Move(path, newPath); 199 } 200 catch(Exception e) 201 { 202 throw new RecoverableException($"Error occured while moving old file to {newPath}: {e.Message}"); 203 } 204 205 Logger.Log(LogLevel.Info, "Old file {0} moved to {1}", path, newPath); 206 207 if(!CanBeCreated()) 208 { 209 throw new RecoverableException($"File {newPath} could not be created"); 210 } 211 } 212 operator SequencedFilePath(string path)213 public static implicit operator SequencedFilePath(string path) 214 { 215 return new SequencedFilePath(path); 216 } 217 } 218 } 219