2 // Mono.Unix/UnixStream.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004-2005 Jonathan Pryor
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Runtime.InteropServices;
37 public sealed class UnixStream : Stream, IDisposable
39 public const int InvalidFileDescriptor = -1;
40 public const int StandardInputFileDescriptor = 0;
41 public const int StandardOutputFileDescriptor = 1;
42 public const int StandardErrorFileDescriptor = 2;
44 public UnixStream (int fileDescriptor)
45 : this (fileDescriptor, true) {}
47 public UnixStream (int fileDescriptor, bool ownsHandle)
49 if (InvalidFileDescriptor == fileDescriptor)
50 throw new ArgumentException (Locale.GetText ("Invalid file descriptor"), "fileDescriptor");
52 this.fileDescriptor = fileDescriptor;
53 this.owner = ownsHandle;
55 long offset = Syscall.lseek (fileDescriptor, 0, SeekFlags.SEEK_CUR);
58 long read = Syscall.read (fileDescriptor, IntPtr.Zero, 0);
61 long write = Syscall.write (fileDescriptor, IntPtr.Zero, 0);
66 private void AssertNotDisposed ()
68 if (fileDescriptor == InvalidFileDescriptor)
69 throw new ObjectDisposedException ("Invalid File Descriptor");
73 get {return fileDescriptor;}
76 public override bool CanRead {
80 public override bool CanSeek {
84 public override bool CanWrite {
85 get {return canWrite;}
88 public override long Length {
92 throw new NotSupportedException ("File descriptor doesn't support seeking");
98 public override long Position {
100 AssertNotDisposed ();
102 throw new NotSupportedException ("The stream does not support seeking");
103 long pos = Syscall.lseek (fileDescriptor, 0, SeekFlags.SEEK_CUR);
105 UnixMarshal.ThrowExceptionForLastError ();
109 Seek (value, SeekOrigin.Begin);
113 [CLSCompliant (false)]
114 [Obsolete ("Use Protection")]
115 public FilePermissions Permissions {
118 return (FilePermissions) stat.st_mode;
121 int r = Syscall.fchmod (fileDescriptor, value);
122 UnixMarshal.ThrowExceptionForLastErrorIf (r);
126 [CLSCompliant (false)]
127 public Native.FilePermissions Protection {
133 // we can't change file type with fchmod, so clear out that portion
134 value &= ~Native.FilePermissions.S_IFMT;
135 int r = Native.Syscall.fchmod (fileDescriptor, value);
136 UnixMarshal.ThrowExceptionForLastErrorIf (r);
140 public FileTypes FileType {
142 int type = (int) Protection;
143 return (FileTypes) (type & (int) FileTypes.AllTypes);
145 // no set as fchmod(2) won't accept changing the file type.
148 public FileAccessPermissions FileAccessPermissions {
150 int perms = (int) Protection;
151 return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions);
154 int perms = (int) Protection;
155 perms &= (int) ~FileAccessPermissions.AllPermissions;
156 perms |= (int) value;
157 Protection = (Native.FilePermissions) perms;
161 public FileSpecialAttributes FileSpecialAttributes {
163 int attrs = (int) Protection;
164 return (FileSpecialAttributes) (attrs & (int) FileSpecialAttributes.AllAttributes);
167 int perms = (int) Protection;
168 perms &= (int) ~FileSpecialAttributes.AllAttributes;
169 perms |= (int) value;
170 Protection = (Native.FilePermissions) perms;
174 public UnixUserInfo OwnerUser {
175 get {RefreshStat (); return new UnixUserInfo (stat.st_uid);}
178 public long OwnerUserId {
179 get {RefreshStat (); return stat.st_uid;}
182 public UnixGroupInfo OwnerGroup {
183 get {RefreshStat (); return new UnixGroupInfo (stat.st_gid);}
186 public long OwnerGroupId {
187 get {RefreshStat (); return stat.st_gid;}
190 private void RefreshStat ()
192 int r = Native.Syscall.fstat (fileDescriptor, out stat);
193 UnixMarshal.ThrowExceptionForLastErrorIf (r);
196 public void AdviseFileAccessPattern (FileAccessPattern pattern, long offset, long len)
198 FileHandleOperations.AdviseFileAccessPattern (fileDescriptor, pattern, offset, len);
201 public void AdviseFileAccessPattern (FileAccessPattern pattern)
203 AdviseFileAccessPattern (pattern, 0, 0);
206 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.Normal, offset, len)")]
207 public void AdviseNormalAccess (long offset, long len)
209 UnixFile.AdviseNormalAccess (fileDescriptor, offset, len);
212 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.Normal)")]
213 public void AdviseNormalAccess ()
215 UnixFile.AdviseNormalAccess (fileDescriptor);
218 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.Sequential, offset, len)")]
219 public void AdviseSequentialAccess (long offset, long len)
221 UnixFile.AdviseSequentialAccess (fileDescriptor, offset, len);
224 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.Sequential)")]
225 public void AdviseSequentialAccess ()
227 UnixFile.AdviseSequentialAccess (fileDescriptor);
230 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.Random, offset, len)")]
231 public void AdviseRandomAccess (long offset, long len)
233 UnixFile.AdviseRandomAccess (fileDescriptor, offset, len);
236 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.Random)")]
237 public void AdviseRandomAccess ()
239 UnixFile.AdviseRandomAccess (fileDescriptor);
242 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.UseSoon, offset, len)")]
243 public void AdviseNeedAccess (long offset, long len)
245 UnixFile.AdviseNeedAccess (fileDescriptor, offset, len);
248 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.UseSoon)")]
249 public void AdviseNeedAccess ()
251 UnixFile.AdviseNeedAccess (fileDescriptor);
254 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.WillNotUse, offset, len)")]
255 public void AdviseNoAccess (long offset, long len)
257 UnixFile.AdviseNoAccess (fileDescriptor, offset, len);
260 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.WillNotUse)")]
261 public void AdviseNoAccess ()
263 UnixFile.AdviseNoAccess (fileDescriptor);
266 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.UseOnce, offset, len)")]
267 public void AdviseOnceAccess (long offset, long len)
269 UnixFile.AdviseOnceAccess (fileDescriptor, offset, len);
272 [Obsolete ("Use AdviseFileAccessPattern (FileAccessPattern.UseOnce)")]
273 public void AdviseOnceAccess ()
275 UnixFile.AdviseOnceAccess (fileDescriptor);
278 public override void Flush ()
280 int r = Native.Syscall.fsync (fileDescriptor);
283 Native.Errno e = Native.Stdlib.GetLastError ();
285 // From the man page:
287 // fd is bound to a special file which does not support
289 // Sockets are such a file, and since Close() calls Flush(), and we
290 // want to support manually opened sockets, we shouldn't generate an
291 // exception for these errors.
292 if (e == Native.Errno.EROFS || e == Native.Errno.EINVAL) {
296 UnixMarshal.ThrowExceptionForError (e);
300 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
302 AssertNotDisposed ();
303 AssertValidBuffer (buffer, offset, count);
305 throw new NotSupportedException ("Stream does not support reading");
308 fixed (byte* buf = &buffer[offset]) {
310 r = Syscall.read (fileDescriptor, buf, (ulong) count);
311 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
314 UnixMarshal.ThrowExceptionForLastError ();
318 private void AssertValidBuffer (byte[] buffer, int offset, int count)
321 throw new ArgumentNullException ("buffer");
323 throw new ArgumentOutOfRangeException ("offset", "< 0");
325 throw new ArgumentOutOfRangeException ("count", "< 0");
326 if (offset > buffer.Length)
327 throw new ArgumentException ("destination offset is beyond array size");
328 if (offset > (buffer.Length - count))
329 throw new ArgumentException ("would overrun buffer");
332 public unsafe int ReadAtOffset ([In, Out] byte[] buffer,
333 int offset, int count, long fileOffset)
335 AssertNotDisposed ();
336 AssertValidBuffer (buffer, offset, count);
338 throw new NotSupportedException ("Stream does not support reading");
341 fixed (byte* buf = &buffer[offset]) {
343 r = Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
344 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
347 UnixMarshal.ThrowExceptionForLastError ();
351 public override long Seek (long offset, SeekOrigin origin)
353 AssertNotDisposed ();
355 throw new NotSupportedException ("The File Descriptor does not support seeking");
356 if (offset > int.MaxValue)
357 throw new ArgumentOutOfRangeException ("offset", "too large");
359 SeekFlags sf = SeekFlags.SEEK_CUR;
361 case SeekOrigin.Begin: sf = SeekFlags.SEEK_SET; break;
362 case SeekOrigin.Current: sf = SeekFlags.SEEK_CUR; break;
363 case SeekOrigin.End: sf = SeekFlags.SEEK_END; break;
366 long pos = Syscall.lseek (fileDescriptor, offset, sf);
368 UnixMarshal.ThrowExceptionForLastError ();
372 public override void SetLength (long value)
374 AssertNotDisposed ();
376 throw new ArgumentOutOfRangeException ("value", "< 0");
377 if (!CanSeek && !CanWrite)
378 throw new NotSupportedException ("You can't truncating the current file descriptor");
382 r = Syscall.ftruncate (fileDescriptor, value);
383 } while (UnixMarshal.ShouldRetrySyscall (r));
384 UnixMarshal.ThrowExceptionForLastErrorIf (r);
387 public override unsafe void Write (byte[] buffer, int offset, int count)
389 AssertNotDisposed ();
390 AssertValidBuffer (buffer, offset, count);
392 throw new NotSupportedException ("File Descriptor does not support writing");
395 fixed (byte* buf = &buffer[offset]) {
397 r = Syscall.write (fileDescriptor, buf, (ulong) count);
398 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
401 UnixMarshal.ThrowExceptionForLastError ();
404 public unsafe void WriteAtOffset (byte[] buffer,
405 int offset, int count, long fileOffset)
407 AssertNotDisposed ();
408 AssertValidBuffer (buffer, offset, count);
410 throw new NotSupportedException ("File Descriptor does not support writing");
413 fixed (byte* buf = &buffer[offset]) {
415 r = Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
416 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
419 UnixMarshal.ThrowExceptionForLastError ();
422 public void SendTo (UnixStream output)
424 SendTo (output, (ulong) output.Length);
427 [CLSCompliant (false)]
428 public void SendTo (UnixStream output, ulong count)
430 SendTo (output.Handle, count);
433 [CLSCompliant (false)]
434 public void SendTo (int out_fd, ulong count)
437 throw new NotSupportedException ("Unable to write to the current file descriptor");
438 long offset = Position;
439 long r = Syscall.sendfile (out_fd, fileDescriptor, ref offset, count);
441 UnixMarshal.ThrowExceptionForLastError ();
444 [CLSCompliant (false)]
445 [Obsolete ("Use SetOwner (long, long)")]
446 public void SetOwner (uint user, uint group)
448 AssertNotDisposed ();
450 int r = Syscall.fchown (fileDescriptor, user, group);
451 UnixMarshal.ThrowExceptionForLastErrorIf (r);
454 public void SetOwner (long user, long group)
456 AssertNotDisposed ();
458 int r = Syscall.fchown (fileDescriptor,
459 Convert.ToUInt32 (user), Convert.ToUInt32 (group));
460 UnixMarshal.ThrowExceptionForLastErrorIf (r);
463 public void SetOwner (string user, string group)
465 AssertNotDisposed ();
467 uint uid = UnixUser.GetUserId (user);
468 uint gid = UnixGroup.GetGroupId (group);
472 public void SetOwner (string user)
474 AssertNotDisposed ();
476 Passwd pw = Syscall.getpwnam (user);
478 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
479 uint uid = pw.pw_uid;
480 uint gid = pw.pw_gid;
484 [CLSCompliant (false)]
485 [Obsolete ("Use GetConfigurationValue (Mono.Unix.Native.PathconfName")]
486 public long GetConfigurationValue (PathConf name)
488 AssertNotDisposed ();
489 long r = Syscall.fpathconf (fileDescriptor, name);
490 if (r == -1 && Syscall.GetLastError() != (Error) 0)
491 UnixMarshal.ThrowExceptionForLastError ();
495 [CLSCompliant (false)]
496 public long GetConfigurationValue (Native.PathconfName name)
498 AssertNotDisposed ();
499 long r = Native.Syscall.fpathconf (fileDescriptor, name);
500 if (r == -1 && Syscall.GetLastError() != (Error) 0)
501 UnixMarshal.ThrowExceptionForLastError ();
510 public override void Close ()
512 if (fileDescriptor == InvalidFileDescriptor)
518 r = Syscall.close (fileDescriptor);
519 } while (UnixMarshal.ShouldRetrySyscall (r));
520 UnixMarshal.ThrowExceptionForLastErrorIf (r);
521 fileDescriptor = InvalidFileDescriptor;
522 GC.SuppressFinalize (this);
525 void IDisposable.Dispose ()
527 AssertNotDisposed ();
531 GC.SuppressFinalize (this);
534 private bool canSeek = false;
535 private bool canRead = false;
536 private bool canWrite = false;
537 private bool owner = true;
538 private int fileDescriptor = InvalidFileDescriptor;
539 private Native.Stat stat;