/src/Voron/Platform/Posix/PosixHelper.cs

https://github.com/fitzchak/ravendb · C# · 189 lines · 164 code · 19 blank · 6 comment · 39 complexity · 7d3a07ba8f830a9c23818adc6aebcd78 MD5 · raw file

  1. // -----------------------------------------------------------------------
  2. // <copyright file="NativeMethods.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using System.IO;
  9. using System.Runtime.InteropServices;
  10. using Sparrow;
  11. using Sparrow.Platform.Posix;
  12. using Voron.Exceptions;
  13. using Voron.Impl.FileHeaders;
  14. using Voron.Util.Settings;
  15. namespace Voron.Platform.Posix
  16. {
  17. public class PosixHelper
  18. {
  19. public static void AllocateFileSpace(StorageEnvironmentOptions options, int fd, long size, string file)
  20. {
  21. bool usingLseek;
  22. var result = Syscall.AllocateFileSpace(fd, size, file, out usingLseek);
  23. if (result == (int)Errno.ENOSPC)
  24. {
  25. int matchLen = 0;
  26. DriveInfo match = null;
  27. foreach (var drive in DriveInfo.GetDrives())
  28. {
  29. if (file.StartsWith(drive.RootDirectory.Name))
  30. {
  31. if (drive.RootDirectory.Name.Length > matchLen)
  32. match = drive;
  33. }
  34. }
  35. throw new DiskFullException(file, size, match?.AvailableFreeSpace);
  36. }
  37. if (result != 0)
  38. Syscall.ThrowLastError(result, $"posix_fallocate(\"{file}\", {size})");
  39. }
  40. public static unsafe void WriteFileHeader(FileHeader* header, VoronPathSetting path)
  41. {
  42. bool syncIsNeeded = false;
  43. var fd = Syscall.open(path.FullPath, OpenFlags.O_WRONLY | PerPlatformValues.OpenFlags.O_CREAT,
  44. FilePermissions.S_IWUSR | FilePermissions.S_IRUSR);
  45. try
  46. {
  47. if (fd == -1)
  48. {
  49. var err = Marshal.GetLastWin32Error();
  50. Syscall.ThrowLastError(err, "when opening " + path);
  51. }
  52. int remaining = sizeof(FileHeader);
  53. FileInfo fi = new FileInfo(path.FullPath);
  54. if (fi.Length != remaining)
  55. syncIsNeeded = true;
  56. var ptr = ((byte*) header);
  57. while (remaining > 0)
  58. {
  59. var written = Syscall.write(fd, ptr, (ulong) remaining);
  60. if (written == -1)
  61. {
  62. var err = Marshal.GetLastWin32Error();
  63. Syscall.ThrowLastError(err, "writing to " + path);
  64. }
  65. remaining -= (int) written;
  66. ptr += written;
  67. }
  68. if (Syscall.FSync(fd) == -1)
  69. {
  70. var err = Marshal.GetLastWin32Error();
  71. Syscall.ThrowLastError(err, "FSync " + path);
  72. }
  73. if (syncIsNeeded)
  74. Syscall.FsyncDirectoryFor(path.FullPath);
  75. }
  76. finally
  77. {
  78. if (fd != -1)
  79. {
  80. Syscall.close(fd);
  81. fd = -1;
  82. }
  83. }
  84. }
  85. public static string GetFileSystemOfPath(string path)
  86. {
  87. var allMounts = DriveInfo.GetDrives();
  88. string filesystem = "Unresolved";
  89. var matchSize = 0;
  90. foreach (var m in allMounts)
  91. {
  92. var mountNameSize = m.Name.Length;
  93. if (path.StartsWith(m.Name))
  94. {
  95. if (mountNameSize > matchSize)
  96. {
  97. matchSize = mountNameSize;
  98. filesystem = m.DriveType == DriveType.Unknown ? "Unknown" : m.DriveFormat;
  99. // do not break foreach statement to get longest substring path match
  100. }
  101. }
  102. }
  103. return filesystem;
  104. }
  105. public static unsafe bool TryReadFileHeader(FileHeader* header, VoronPathSetting path)
  106. {
  107. var fd = Syscall.open(path.FullPath, OpenFlags.O_RDONLY, FilePermissions.S_IRUSR);
  108. try
  109. {
  110. if (fd == -1)
  111. {
  112. var lastError = Marshal.GetLastWin32Error();
  113. if (((Errno) lastError) == Errno.EACCES)
  114. return false;
  115. Syscall.ThrowLastError(lastError);
  116. }
  117. int remaining = sizeof(FileHeader);
  118. var ptr = ((byte*) header);
  119. while (remaining > 0)
  120. {
  121. var read = Syscall.read(fd, ptr, (ulong) remaining);
  122. if (read == -1)
  123. {
  124. var err = Marshal.GetLastWin32Error();
  125. Syscall.ThrowLastError(err);
  126. }
  127. if (read == 0)
  128. return false; // truncated file?
  129. remaining -= (int) read;
  130. ptr += read;
  131. }
  132. return true;
  133. }
  134. finally
  135. {
  136. if (fd != -1)
  137. {
  138. Syscall.close(fd);
  139. fd = -1;
  140. }
  141. }
  142. }
  143. public static string FixLinuxPath(string path)
  144. {
  145. if (path != null)
  146. {
  147. var length = Path.GetPathRoot(path).Length;
  148. if (length > 0)
  149. path = "/" + path.Substring(length);
  150. path = path.Replace('\\', '/');
  151. path = path.Replace("/./", "/");
  152. path = path.Replace("//", "/");
  153. }
  154. return path;
  155. }
  156. public static void EnsurePathExists(string file)
  157. {
  158. var dirpath = Path.GetDirectoryName(file);
  159. List<string> dirsToCreate = new List<string>();
  160. while (Directory.Exists(dirpath) == false)
  161. {
  162. dirsToCreate.Add(dirpath);
  163. dirpath = Directory.GetParent(dirpath).ToString();
  164. if (dirpath == null)
  165. break;
  166. }
  167. dirsToCreate.ForEach(x => Directory.CreateDirectory(x));
  168. }
  169. }
  170. }