SftpFileAttributesTest.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. using System;
  2. using System.Buffers.Binary;
  3. using Microsoft.VisualStudio.TestTools.UnitTesting;
  4. using Renci.SshNet.Common;
  5. using Renci.SshNet.Sftp;
  6. namespace Renci.SshNet.Tests.Classes.Sftp
  7. {
  8. [TestClass]
  9. public class SftpFileAttributesTest
  10. {
  11. [TestMethod]
  12. [DataRow(0xC000u, true, false, false, false, false, false, false)] // Socket
  13. [DataRow(0xA000u, false, true, false, false, false, false, false)] // Symbolic link
  14. [DataRow(0x8000u, false, false, true, false, false, false, false)] // Regular file
  15. [DataRow(0x6000u, false, false, false, true, false, false, false)] // Block device
  16. [DataRow(0x4000u, false, false, false, false, true, false, false)] // Directory
  17. [DataRow(0x2000u, false, false, false, false, false, true, false)] // Character device
  18. [DataRow(0x1000u, false, false, false, false, false, false, true)] // Named pipe
  19. public void FileTypePropertiesAreMutuallyExclusive(
  20. uint permissions,
  21. bool isSocket,
  22. bool isSymbolicLink,
  23. bool isRegularFile,
  24. bool isBlockDevice,
  25. bool isDirectory,
  26. bool isCharacterDevice,
  27. bool isNamedPipe)
  28. {
  29. var attributeBytes = new byte[8];
  30. attributeBytes[3] = 0x4; // SSH_FILEXFER_ATTR_PERMISSIONS
  31. BinaryPrimitives.WriteUInt32BigEndian(attributeBytes.AsSpan(4), permissions);
  32. var attributes = SftpFileAttributes.FromBytes(attributeBytes);
  33. Assert.AreEqual(isSocket, attributes.IsSocket);
  34. Assert.AreEqual(isSymbolicLink, attributes.IsSymbolicLink);
  35. Assert.AreEqual(isRegularFile, attributes.IsRegularFile);
  36. Assert.AreEqual(isBlockDevice, attributes.IsBlockDevice);
  37. Assert.AreEqual(isDirectory, attributes.IsDirectory);
  38. Assert.AreEqual(isCharacterDevice, attributes.IsCharacterDevice);
  39. Assert.AreEqual(isNamedPipe, attributes.IsNamedPipe);
  40. }
  41. [TestMethod]
  42. public void FromBytesGetBytes()
  43. {
  44. // 81a4 in hex = 100644 in octal
  45. var attributes = SftpFileAttributes.FromBytes([0, 0, 0, 0x4, 0, 0, 0x81, 0xa4]);
  46. Assert.IsTrue(attributes.IsRegularFile);
  47. Assert.IsFalse(attributes.IsUIDBitSet);
  48. Assert.IsFalse(attributes.IsGroupIDBitSet);
  49. Assert.IsFalse(attributes.IsStickyBitSet);
  50. Assert.IsTrue(attributes.OwnerCanRead);
  51. Assert.IsTrue(attributes.OwnerCanWrite);
  52. Assert.IsFalse(attributes.OwnerCanExecute);
  53. Assert.IsTrue(attributes.GroupCanRead);
  54. Assert.IsFalse(attributes.GroupCanWrite);
  55. Assert.IsFalse(attributes.GroupCanExecute);
  56. Assert.IsTrue(attributes.OthersCanRead);
  57. Assert.IsFalse(attributes.OthersCanWrite);
  58. Assert.IsFalse(attributes.OthersCanExecute);
  59. Assert.AreEqual(-1, attributes.Size); // Erm, OK?
  60. Assert.AreEqual(-1, attributes.UserId);
  61. Assert.AreEqual(-1, attributes.GroupId);
  62. Assert.AreEqual(default, attributes.LastAccessTimeUtc);
  63. Assert.AreEqual(DateTimeKind.Utc, attributes.LastAccessTimeUtc.Kind);
  64. Assert.AreEqual(default, attributes.LastWriteTimeUtc);
  65. Assert.AreEqual(DateTimeKind.Utc, attributes.LastWriteTimeUtc.Kind);
  66. Assert.AreEqual("-rw-r--r--", attributes.ToString());
  67. // No changes
  68. CollectionAssert.AreEqual(
  69. new byte[] { 0, 0, 0, 0 },
  70. attributes.GetBytes());
  71. // Permissions change
  72. attributes.IsUIDBitSet = true;
  73. attributes.OwnerCanExecute = true;
  74. CollectionAssert.AreEqual(
  75. new byte[] { 0, 0, 0, 0x4, 0, 0, 0x89, 0xe4 },
  76. attributes.GetBytes());
  77. Assert.AreEqual("-rwsr--r--", attributes.ToString());
  78. // Size change
  79. attributes.Size = 123;
  80. CollectionAssert.AreEqual(
  81. new byte[] {
  82. 0, 0, 0, 0x1 | 0x4,
  83. 0, 0, 0, 0, 0, 0, 0, 123,
  84. 0, 0, 0x89, 0xe4 },
  85. attributes.GetBytes());
  86. Assert.IsTrue(attributes.ToString().StartsWith("-rwsr--r-- Size: ", StringComparison.Ordinal));
  87. // Uid/gid change
  88. attributes.UserId = 99;
  89. attributes.GroupId = 66;
  90. CollectionAssert.AreEqual(
  91. new byte[] {
  92. 0, 0, 0, 0x1 | 0x2 | 0x4,
  93. 0, 0, 0, 0, 0, 0, 0, 123,
  94. 0, 0, 0, 99, 0, 0, 0, 66,
  95. 0, 0, 0x89, 0xe4 },
  96. attributes.GetBytes());
  97. // Access/mod time change
  98. attributes.LastAccessTimeUtc = new DateTime(2025, 08, 10, 17, 51, 37, DateTimeKind.Unspecified);
  99. attributes.LastWriteTime = new DateTimeOffset(2016, 12, 02, 13, 18, 20, TimeSpan.FromHours(3)).LocalDateTime;
  100. var expectedTimeBytes = new byte[8];
  101. BinaryPrimitives.WriteUInt32BigEndian(expectedTimeBytes, 1754848297);
  102. BinaryPrimitives.WriteUInt32BigEndian(expectedTimeBytes.AsSpan(4), 1480673900);
  103. CollectionAssert.AreEqual(
  104. new byte[] {
  105. 0, 0, 0, 0x1 | 0x2 | 0x4 | 0x8,
  106. 0, 0, 0, 0, 0, 0, 0, 123,
  107. 0, 0, 0, 99, 0, 0, 0, 66,
  108. 0, 0, 0x89, 0xe4
  109. }.Concat(expectedTimeBytes),
  110. attributes.GetBytes());
  111. Assert.AreEqual(new DateTime(2016, 12, 02, 10, 18, 20, DateTimeKind.Utc), attributes.LastWriteTimeUtc);
  112. Assert.AreEqual(DateTimeKind.Utc, attributes.LastWriteTimeUtc.Kind);
  113. var attributesString = attributes.ToString();
  114. Assert.IsTrue(attributesString.StartsWith("-rwsr--r-- Size: ", StringComparison.Ordinal));
  115. Assert.Contains(" LastWriteTime: ", attributesString, StringComparison.CurrentCulture);
  116. }
  117. [TestMethod]
  118. [DataRow((short)8888)]
  119. [DataRow((short)10000)]
  120. [DataRow((short)8000)]
  121. [DataRow((short)0080)]
  122. [DataRow((short)0008)]
  123. [DataRow((short)1797)]
  124. [DataRow((short)-1)]
  125. [DataRow(short.MaxValue)]
  126. public void SetPermissions_InvalidMode_ThrowsArgumentOutOfRangeException(short mode)
  127. {
  128. var attributes = SftpFileAttributes.FromBytes([0, 0, 0, 0]);
  129. var ex = Assert.Throws<ArgumentOutOfRangeException>(() => attributes.SetPermissions(mode));
  130. Assert.AreEqual("mode", ex.ParamName);
  131. }
  132. [TestMethod]
  133. [DataRow((short)0777, false, false, false, true, true, true, true, true, true, true, true, true)]
  134. [DataRow((short)0755, false, false, false, true, true, true, true, false, true, true, false, true)]
  135. [DataRow((short)0644, false, false, false, true, true, false, true, false, false, true, false, false)]
  136. [DataRow((short)0444, false, false, false, true, false, false, true, false, false, true, false, false)]
  137. [DataRow((short)0000, false, false, false, false, false, false, false, false, false, false, false, false)]
  138. [DataRow((short)4700, true, false, false, true, true, true, false, false, false, false, false, false)]
  139. [DataRow((short)3001, false, true, true, false, false, false, false, false, false, false, false, true)]
  140. [DataRow((short)7777, true, true, true, true, true, true, true, true, true, true, true, true)]
  141. public void SetPermissions_ValidMode(
  142. short mode,
  143. bool setUid, bool setGid, bool sticky,
  144. bool ownerRead, bool ownerWrite, bool ownerExec,
  145. bool groupRead, bool groupWrite, bool groupExec,
  146. bool othersRead, bool othersWrite, bool othersExec)
  147. {
  148. var attributes = SftpFileAttributes.FromBytes([0, 0, 0, 0]);
  149. attributes.SetPermissions(mode);
  150. Assert.AreEqual(setUid, attributes.IsUIDBitSet);
  151. Assert.AreEqual(setGid, attributes.IsGroupIDBitSet);
  152. Assert.AreEqual(sticky, attributes.IsStickyBitSet);
  153. Assert.AreEqual(ownerRead, attributes.OwnerCanRead);
  154. Assert.AreEqual(ownerWrite, attributes.OwnerCanWrite);
  155. Assert.AreEqual(ownerExec, attributes.OwnerCanExecute);
  156. Assert.AreEqual(groupRead, attributes.GroupCanRead);
  157. Assert.AreEqual(groupWrite, attributes.GroupCanWrite);
  158. Assert.AreEqual(groupExec, attributes.GroupCanExecute);
  159. Assert.AreEqual(othersRead, attributes.OthersCanRead);
  160. Assert.AreEqual(othersWrite, attributes.OthersCanWrite);
  161. Assert.AreEqual(othersExec, attributes.OthersCanExecute);
  162. }
  163. [TestMethod]
  164. [DataRow(0xC000u, (short)1770, "srwxrwx--T")] // Socket
  165. [DataRow(0xA000u, (short)2707, "lrwx--Srwx")] // Symbolic link
  166. [DataRow(0x8000u, (short)4755, "-rwsr-xr-x")] // Regular file
  167. [DataRow(0x8000u, (short)4644, "-rwSr--r--")] // Regular file
  168. [DataRow(0x6000u, (short)2711, "brwx--s--x")] // Block device
  169. [DataRow(0x4000u, (short)1777, "drwxrwxrwt")] // Directory
  170. [DataRow(0x4000u, (short)1776, "drwxrwxrwT")] // Directory
  171. [DataRow(0x2000u, (short)0660, "crw-rw----")] // Character device
  172. [DataRow(0x1000u, (short)0022, "p----w--w-")] // Named pipe
  173. public void ToStringWithPermissions(
  174. uint fileType,
  175. short permissions,
  176. string expected)
  177. {
  178. var attributeBytes = new byte[8];
  179. attributeBytes[3] = 0x4; // SSH_FILEXFER_ATTR_PERMISSIONS
  180. BinaryPrimitives.WriteUInt32BigEndian(attributeBytes.AsSpan(4), fileType);
  181. var attributes = SftpFileAttributes.FromBytes(attributeBytes);
  182. attributes.SetPermissions(permissions);
  183. Assert.AreEqual(expected, attributes.ToString());
  184. }
  185. }
  186. }