public FileLock(Log log, params string[] filenames) { _locks = new LockFile[filenames.Length]; for (int i = 0; i < _locks.Length; i++) { Disk.CreateDirectory(log, Path.GetDirectoryName(filenames[i])); _locks[i] = new LockFile(filenames[i] + ".lock"); } for (int tries = 1; !TryAcquireAll(); tries++) { if (tries == 2000) { throw new TimeoutException("Failed to lock " + this + "; aborting after " + tries + " tries"); } if (tries % 50 == 0) { log.Warning("Failed to lock " + this + "; retrying..."); } // Spin wait while another process owns the file(s), // Some randomness helps with race conditions Thread.Sleep(Rand.Next(200, 500)); } }
public static string GetFile(Log log, string url) { var dst = GetFileName(url); // 1) Early out if the file exists locally if (File.Exists(dst)) { Disk.TouchFile(log, dst); return(dst); } Disk.CreateDirectory(log, StuffDirectory); try { // Use a set to avoid repeating errors var errors = new HashSet <string>(); for (int tries = 0;; tries++) { // 2) Download the file from URL log.WriteLine("Downloading " + url + ( tries > 0 ? " (retry " + tries + "...)" : null ), ConsoleColor.Blue); try { using (var client = new StuffWebClient()) client.DownloadFile(url, dst); return(dst); } catch (Exception e) { // Fail the 10th time, or unless WebException if (tries >= 10 || !(e is WebException)) { // Print previous errors, before rethrowing foreach (var s in errors) { log.Error(s); } throw; } errors.Add(e.Message); Thread.Sleep(500); } } } catch { Disk.DeleteFile(log, dst); throw; } }
void InstallItem(KeyValuePair <string, object> item) { var itemKey = item.Key.Replace('/', Path.DirectorySeparatorChar); var components = itemKey.Split(Path.DirectorySeparatorChar); if (components.Contains(".") || components.Contains("..")) { throw new FormatException(item.Key + ": '.' or '..' are not valid in directory names"); } var itemFile = Path.Combine(_parentDirectory, ".uno", "stuff", itemKey); var targetDir = Path.Combine(_parentDirectory, itemKey); var itemValue = item.Value?.ToString(); if (string.IsNullOrEmpty(itemValue) || IsItemUpToDate(Log, targetDir, itemFile, itemValue, _flags)) { return; } using (new FileLock(Log, itemFile, DownloadCache.GetFileName(itemValue))) { if (IsItemUpToDate(Log, targetDir, itemFile, itemValue, _flags)) { return; } for (int tries = 0;; tries++) { Log.Verbose("Extracting " + targetDir.ToRelativePath().Quote(), ConsoleColor.DarkGray); // Support local files (e.g. from .STUFF-UPLOAD files) var localFile = Path.Combine(_parentDirectory, itemValue); var isLocal = File.Exists(localFile); var file = isLocal ? localFile : DownloadCache.GetFile(Log, itemValue); Disk.DeleteDirectory(Log, targetDir); Disk.CreateDirectory(Log, targetDir); try { if (PlatformDetection.IsWindows) { LongPathZipFile.ExtractToDirectory(file, targetDir); } else { // Use system tar/unzip to preserve file permissions and links // (ZipFile doesn't handle this) if (Path.GetFileName(file).ToUpper().EndsWith(".TAR.GZ")) { Shell.Untar(Log, file, targetDir); } else { Shell.Unzip(Log, file, targetDir); } // Make sure files extracted are writable so we can touch them Shell.Chmod(Log, "+w", targetDir); Disk.TouchAllFiles(Log, targetDir); } Disk.CreateDirectory(Log, Path.GetDirectoryName(itemFile)); File.WriteAllText(itemFile, itemValue); break; } catch (UnauthorizedAccessException) { // Don't try more if we get this throw; } catch { // Delete any installed files Disk.DeleteFile(Log, itemFile); Disk.DeleteDirectory(Log, targetDir); if (isLocal) { throw; } // Delete the cached download too Disk.DeleteFile(Log, file); // Redownload, and try just one more time to be sure // This might fix the problem if the cached file was corrupt if (tries > 0) { throw; } Thread.Sleep(150); } } } }