1 // 2 // Mono.Unix/UnixStream.cs 3 // 4 // Authors: 5 // Jonathan Pryor (jonpryor@vt.edu) 6 // 7 // (C) 2004-2006 Jonathan Pryor 8 // (C) 2007 Novell, Inc. 9 // 10 // 11 // Permission is hereby granted, free of charge, to any person obtaining 12 // a copy of this software and associated documentation files (the 13 // "Software"), to deal in the Software without restriction, including 14 // without limitation the rights to use, copy, modify, merge, publish, 15 // distribute, sublicense, and/or sell copies of the Software, and to 16 // permit persons to whom the Software is furnished to do so, subject to 17 // the following conditions: 18 // 19 // The above copyright notice and this permission notice shall be 20 // included in all copies or substantial portions of the Software. 21 // 22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 // 30 31 using System; 32 using System.IO; 33 using System.Runtime.InteropServices; 34 using System.Text; 35 using Mono.Unix; 36 37 namespace Mono.Unix { 38 39 public sealed class UnixStream : Stream, IDisposable 40 { 41 public const int InvalidFileDescriptor = -1; 42 public const int StandardInputFileDescriptor = 0; 43 public const int StandardOutputFileDescriptor = 1; 44 public const int StandardErrorFileDescriptor = 2; 45 UnixStream(int fileDescriptor)46 public UnixStream (int fileDescriptor) 47 : this (fileDescriptor, true) {} 48 UnixStream(int fileDescriptor, bool ownsHandle)49 public UnixStream (int fileDescriptor, bool ownsHandle) 50 { 51 if (InvalidFileDescriptor == fileDescriptor) 52 throw new ArgumentException (Locale.GetText ("Invalid file descriptor"), "fileDescriptor"); 53 54 this.fileDescriptor = fileDescriptor; 55 this.owner = ownsHandle; 56 57 long offset = Native.Syscall.lseek (fileDescriptor, 0, Native.SeekFlags.SEEK_CUR); 58 if (offset != -1) 59 canSeek = true; 60 long read = Native.Syscall.read (fileDescriptor, IntPtr.Zero, 0); 61 if (read != -1) 62 canRead = true; 63 long write = Native.Syscall.write (fileDescriptor, IntPtr.Zero, 0); 64 if (write != -1) 65 canWrite = true; 66 } 67 AssertNotDisposed()68 private void AssertNotDisposed () 69 { 70 if (fileDescriptor == InvalidFileDescriptor) 71 throw new ObjectDisposedException ("Invalid File Descriptor"); 72 } 73 74 public int Handle { 75 get {return fileDescriptor;} 76 } 77 78 public override bool CanRead { 79 get {return canRead;} 80 } 81 82 public override bool CanSeek { 83 get {return canSeek;} 84 } 85 86 public override bool CanWrite { 87 get {return canWrite;} 88 } 89 90 public override long Length { 91 get { 92 AssertNotDisposed (); 93 if (!CanSeek) 94 throw new NotSupportedException ("File descriptor doesn't support seeking"); 95 RefreshStat (); 96 return stat.st_size; 97 } 98 } 99 100 public override long Position { 101 get { 102 AssertNotDisposed (); 103 if (!CanSeek) 104 throw new NotSupportedException ("The stream does not support seeking"); 105 long pos = Native.Syscall.lseek (fileDescriptor, 0, Native.SeekFlags.SEEK_CUR); 106 if (pos == -1) 107 UnixMarshal.ThrowExceptionForLastError (); 108 return (long) pos; 109 } 110 set { 111 Seek (value, SeekOrigin.Begin); 112 } 113 } 114 115 [CLSCompliant (false)] 116 public Native.FilePermissions Protection { 117 get { 118 RefreshStat (); 119 return stat.st_mode; 120 } 121 set { 122 // we can't change file type with fchmod, so clear out that portion 123 value &= ~Native.FilePermissions.S_IFMT; 124 int r = Native.Syscall.fchmod (fileDescriptor, value); 125 UnixMarshal.ThrowExceptionForLastErrorIf (r); 126 } 127 } 128 129 public FileTypes FileType { 130 get { 131 int type = (int) Protection; 132 return (FileTypes) (type & (int) UnixFileSystemInfo.AllFileTypes); 133 } 134 // no set as fchmod(2) won't accept changing the file type. 135 } 136 137 public FileAccessPermissions FileAccessPermissions { 138 get { 139 int perms = (int) Protection; 140 return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions); 141 } 142 set { 143 int perms = (int) Protection; 144 perms &= (int) ~FileAccessPermissions.AllPermissions; 145 perms |= (int) value; 146 Protection = (Native.FilePermissions) perms; 147 } 148 } 149 150 public FileSpecialAttributes FileSpecialAttributes { 151 get { 152 int attrs = (int) Protection; 153 return (FileSpecialAttributes) (attrs & (int) UnixFileSystemInfo.AllSpecialAttributes); 154 } 155 set { 156 int perms = (int) Protection; 157 perms &= (int) ~UnixFileSystemInfo.AllSpecialAttributes; 158 perms |= (int) value; 159 Protection = (Native.FilePermissions) perms; 160 } 161 } 162 163 public UnixUserInfo OwnerUser { 164 get {RefreshStat (); return new UnixUserInfo (stat.st_uid);} 165 } 166 167 public long OwnerUserId { 168 get {RefreshStat (); return stat.st_uid;} 169 } 170 171 public UnixGroupInfo OwnerGroup { 172 get {RefreshStat (); return new UnixGroupInfo (stat.st_gid);} 173 } 174 175 public long OwnerGroupId { 176 get {RefreshStat (); return stat.st_gid;} 177 } 178 RefreshStat()179 private void RefreshStat () 180 { 181 AssertNotDisposed (); 182 int r = Native.Syscall.fstat (fileDescriptor, out stat); 183 UnixMarshal.ThrowExceptionForLastErrorIf (r); 184 } 185 AdviseFileAccessPattern(FileAccessPattern pattern, long offset, long len)186 public void AdviseFileAccessPattern (FileAccessPattern pattern, long offset, long len) 187 { 188 FileHandleOperations.AdviseFileAccessPattern (fileDescriptor, pattern, offset, len); 189 } 190 AdviseFileAccessPattern(FileAccessPattern pattern)191 public void AdviseFileAccessPattern (FileAccessPattern pattern) 192 { 193 AdviseFileAccessPattern (pattern, 0, 0); 194 } 195 Flush()196 public override void Flush () 197 { 198 } 199 Read([In, Out] byte[] buffer, int offset, int count)200 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count) 201 { 202 AssertNotDisposed (); 203 AssertValidBuffer (buffer, offset, count); 204 if (!CanRead) 205 throw new NotSupportedException ("Stream does not support reading"); 206 207 if (buffer.Length == 0) 208 return 0; 209 210 long r = 0; 211 fixed (byte* buf = &buffer[offset]) { 212 do { 213 r = Native.Syscall.read (fileDescriptor, buf, (ulong) count); 214 } while (UnixMarshal.ShouldRetrySyscall ((int) r)); 215 } 216 if (r == -1) 217 UnixMarshal.ThrowExceptionForLastError (); 218 return (int) r; 219 } 220 AssertValidBuffer(byte[] buffer, int offset, int count)221 private void AssertValidBuffer (byte[] buffer, int offset, int count) 222 { 223 if (buffer == null) 224 throw new ArgumentNullException ("buffer"); 225 if (offset < 0) 226 throw new ArgumentOutOfRangeException ("offset", "< 0"); 227 if (count < 0) 228 throw new ArgumentOutOfRangeException ("count", "< 0"); 229 if (offset > buffer.Length) 230 throw new ArgumentException ("destination offset is beyond array size"); 231 if (offset > (buffer.Length - count)) 232 throw new ArgumentException ("would overrun buffer"); 233 } 234 ReadAtOffset([In, Out] byte[] buffer, int offset, int count, long fileOffset)235 public unsafe int ReadAtOffset ([In, Out] byte[] buffer, 236 int offset, int count, long fileOffset) 237 { 238 AssertNotDisposed (); 239 AssertValidBuffer (buffer, offset, count); 240 if (!CanRead) 241 throw new NotSupportedException ("Stream does not support reading"); 242 243 if (buffer.Length == 0) 244 return 0; 245 246 long r = 0; 247 fixed (byte* buf = &buffer[offset]) { 248 do { 249 r = Native.Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset); 250 } while (UnixMarshal.ShouldRetrySyscall ((int) r)); 251 } 252 if (r == -1) 253 UnixMarshal.ThrowExceptionForLastError (); 254 return (int) r; 255 } 256 Seek(long offset, SeekOrigin origin)257 public override long Seek (long offset, SeekOrigin origin) 258 { 259 AssertNotDisposed (); 260 if (!CanSeek) 261 throw new NotSupportedException ("The File Descriptor does not support seeking"); 262 263 Native.SeekFlags sf = Native.SeekFlags.SEEK_CUR; 264 switch (origin) { 265 case SeekOrigin.Begin: sf = Native.SeekFlags.SEEK_SET; break; 266 case SeekOrigin.Current: sf = Native.SeekFlags.SEEK_CUR; break; 267 case SeekOrigin.End: sf = Native.SeekFlags.SEEK_END; break; 268 } 269 270 long pos = Native.Syscall.lseek (fileDescriptor, offset, sf); 271 if (pos == -1) 272 UnixMarshal.ThrowExceptionForLastError (); 273 return (long) pos; 274 } 275 SetLength(long value)276 public override void SetLength (long value) 277 { 278 AssertNotDisposed (); 279 if (value < 0) 280 throw new ArgumentOutOfRangeException ("value", "< 0"); 281 if (!CanSeek && !CanWrite) 282 throw new NotSupportedException ("You can't truncating the current file descriptor"); 283 284 int r; 285 do { 286 r = Native.Syscall.ftruncate (fileDescriptor, value); 287 } while (UnixMarshal.ShouldRetrySyscall (r)); 288 UnixMarshal.ThrowExceptionForLastErrorIf (r); 289 } 290 Write(byte[] buffer, int offset, int count)291 public override unsafe void Write (byte[] buffer, int offset, int count) 292 { 293 AssertNotDisposed (); 294 AssertValidBuffer (buffer, offset, count); 295 if (!CanWrite) 296 throw new NotSupportedException ("File Descriptor does not support writing"); 297 298 if (buffer.Length == 0) 299 return; 300 301 long r = 0; 302 fixed (byte* buf = &buffer[offset]) { 303 do { 304 r = Native.Syscall.write (fileDescriptor, buf, (ulong) count); 305 } while (UnixMarshal.ShouldRetrySyscall ((int) r)); 306 } 307 if (r == -1) 308 UnixMarshal.ThrowExceptionForLastError (); 309 } 310 WriteAtOffset(byte[] buffer, int offset, int count, long fileOffset)311 public unsafe void WriteAtOffset (byte[] buffer, 312 int offset, int count, long fileOffset) 313 { 314 AssertNotDisposed (); 315 AssertValidBuffer (buffer, offset, count); 316 if (!CanWrite) 317 throw new NotSupportedException ("File Descriptor does not support writing"); 318 319 if (buffer.Length == 0) 320 return; 321 322 long r = 0; 323 fixed (byte* buf = &buffer[offset]) { 324 do { 325 r = Native.Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset); 326 } while (UnixMarshal.ShouldRetrySyscall ((int) r)); 327 } 328 if (r == -1) 329 UnixMarshal.ThrowExceptionForLastError (); 330 } 331 SendTo(UnixStream output)332 public void SendTo (UnixStream output) 333 { 334 SendTo (output, (ulong) output.Length); 335 } 336 337 [CLSCompliant (false)] SendTo(UnixStream output, ulong count)338 public void SendTo (UnixStream output, ulong count) 339 { 340 SendTo (output.Handle, count); 341 } 342 343 [CLSCompliant (false)] SendTo(int out_fd, ulong count)344 public void SendTo (int out_fd, ulong count) 345 { 346 if (!CanWrite) 347 throw new NotSupportedException ("Unable to write to the current file descriptor"); 348 long offset = Position; 349 long r = Native.Syscall.sendfile (out_fd, fileDescriptor, ref offset, count); 350 if (r == -1) 351 UnixMarshal.ThrowExceptionForLastError (); 352 } 353 SetOwner(long user, long group)354 public void SetOwner (long user, long group) 355 { 356 AssertNotDisposed (); 357 358 int r = Native.Syscall.fchown (fileDescriptor, 359 Convert.ToUInt32 (user), Convert.ToUInt32 (group)); 360 UnixMarshal.ThrowExceptionForLastErrorIf (r); 361 } 362 SetOwner(string user, string group)363 public void SetOwner (string user, string group) 364 { 365 AssertNotDisposed (); 366 367 long uid = new UnixUserInfo (user).UserId; 368 long gid = new UnixGroupInfo (group).GroupId; 369 SetOwner (uid, gid); 370 } 371 SetOwner(string user)372 public void SetOwner (string user) 373 { 374 AssertNotDisposed (); 375 376 Native.Passwd pw = Native.Syscall.getpwnam (user); 377 if (pw == null) 378 throw new ArgumentException (Locale.GetText ("invalid username"), "user"); 379 long uid = pw.pw_uid; 380 long gid = pw.pw_gid; 381 SetOwner (uid, gid); 382 } 383 384 [CLSCompliant (false)] GetConfigurationValue(Native.PathconfName name)385 public long GetConfigurationValue (Native.PathconfName name) 386 { 387 AssertNotDisposed (); 388 long r = Native.Syscall.fpathconf (fileDescriptor, name); 389 if (r == -1 && Native.Syscall.GetLastError() != (Native.Errno) 0) 390 UnixMarshal.ThrowExceptionForLastError (); 391 return r; 392 } 393 ~UnixStream()394 ~UnixStream () 395 { 396 Close (); 397 } 398 Close()399 public override void Close () 400 { 401 if (fileDescriptor == InvalidFileDescriptor) 402 return; 403 404 Flush (); 405 406 if (!owner) 407 return; 408 409 int r; 410 do { 411 r = Native.Syscall.close (fileDescriptor); 412 } while (UnixMarshal.ShouldRetrySyscall (r)); 413 UnixMarshal.ThrowExceptionForLastErrorIf (r); 414 fileDescriptor = InvalidFileDescriptor; 415 GC.SuppressFinalize (this); 416 } 417 IDisposable.Dispose()418 void IDisposable.Dispose () 419 { 420 if (fileDescriptor != InvalidFileDescriptor && owner) { 421 Close (); 422 } 423 GC.SuppressFinalize (this); 424 } 425 426 private bool canSeek = false; 427 private bool canRead = false; 428 private bool canWrite = false; 429 private bool owner = true; 430 private int fileDescriptor = InvalidFileDescriptor; 431 private Native.Stat stat; 432 } 433 } 434 435 // vim: noexpandtab 436