2 // Mono.Unix/UnixStream.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004-2006 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 = Native.Syscall.lseek (fileDescriptor, 0, Native.SeekFlags.SEEK_CUR);
58 long read = Native.Syscall.read (fileDescriptor, IntPtr.Zero, 0);
61 long write = Native.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 = Native.Syscall.lseek (fileDescriptor, 0, Native.SeekFlags.SEEK_CUR);
105 UnixMarshal.ThrowExceptionForLastError ();
109 Seek (value, SeekOrigin.Begin);
113 [CLSCompliant (false)]
114 public Native.FilePermissions Protection {
120 // we can't change file type with fchmod, so clear out that portion
121 value &= ~Native.FilePermissions.S_IFMT;
122 int r = Native.Syscall.fchmod (fileDescriptor, value);
123 UnixMarshal.ThrowExceptionForLastErrorIf (r);
127 public FileTypes FileType {
129 int type = (int) Protection;
130 return (FileTypes) (type & (int) UnixFileSystemInfo.AllFileTypes);
132 // no set as fchmod(2) won't accept changing the file type.
135 public FileAccessPermissions FileAccessPermissions {
137 int perms = (int) Protection;
138 return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions);
141 int perms = (int) Protection;
142 perms &= (int) ~FileAccessPermissions.AllPermissions;
143 perms |= (int) value;
144 Protection = (Native.FilePermissions) perms;
148 public FileSpecialAttributes FileSpecialAttributes {
150 int attrs = (int) Protection;
151 return (FileSpecialAttributes) (attrs & (int) UnixFileSystemInfo.AllSpecialAttributes);
154 int perms = (int) Protection;
155 perms &= (int) ~UnixFileSystemInfo.AllSpecialAttributes;
156 perms |= (int) value;
157 Protection = (Native.FilePermissions) perms;
161 public UnixUserInfo OwnerUser {
162 get {RefreshStat (); return new UnixUserInfo (stat.st_uid);}
165 public long OwnerUserId {
166 get {RefreshStat (); return stat.st_uid;}
169 public UnixGroupInfo OwnerGroup {
170 get {RefreshStat (); return new UnixGroupInfo (stat.st_gid);}
173 public long OwnerGroupId {
174 get {RefreshStat (); return stat.st_gid;}
177 private void RefreshStat ()
179 int r = Native.Syscall.fstat (fileDescriptor, out stat);
180 UnixMarshal.ThrowExceptionForLastErrorIf (r);
183 public void AdviseFileAccessPattern (FileAccessPattern pattern, long offset, long len)
185 FileHandleOperations.AdviseFileAccessPattern (fileDescriptor, pattern, offset, len);
188 public void AdviseFileAccessPattern (FileAccessPattern pattern)
190 AdviseFileAccessPattern (pattern, 0, 0);
193 public override void Flush ()
195 int r = Native.Syscall.fsync (fileDescriptor);
198 Native.Errno e = Native.Stdlib.GetLastError ();
200 // From the man page:
202 // fd is bound to a special file which does not support
204 // Sockets are such a file, and since Close() calls Flush(), and we
205 // want to support manually opened sockets, we shouldn't generate an
206 // exception for these errors.
207 if (e == Native.Errno.EROFS || e == Native.Errno.EINVAL) {
211 UnixMarshal.ThrowExceptionForError (e);
215 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
217 AssertNotDisposed ();
218 AssertValidBuffer (buffer, offset, count);
220 throw new NotSupportedException ("Stream does not support reading");
223 fixed (byte* buf = &buffer[offset]) {
225 r = Native.Syscall.read (fileDescriptor, buf, (ulong) count);
226 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
229 UnixMarshal.ThrowExceptionForLastError ();
233 private void AssertValidBuffer (byte[] buffer, int offset, int count)
236 throw new ArgumentNullException ("buffer");
238 throw new ArgumentOutOfRangeException ("offset", "< 0");
240 throw new ArgumentOutOfRangeException ("count", "< 0");
241 if (offset > buffer.Length)
242 throw new ArgumentException ("destination offset is beyond array size");
243 if (offset > (buffer.Length - count))
244 throw new ArgumentException ("would overrun buffer");
247 public unsafe int ReadAtOffset ([In, Out] byte[] buffer,
248 int offset, int count, long fileOffset)
250 AssertNotDisposed ();
251 AssertValidBuffer (buffer, offset, count);
253 throw new NotSupportedException ("Stream does not support reading");
256 fixed (byte* buf = &buffer[offset]) {
258 r = Native.Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
259 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
262 UnixMarshal.ThrowExceptionForLastError ();
266 public override long Seek (long offset, SeekOrigin origin)
268 AssertNotDisposed ();
270 throw new NotSupportedException ("The File Descriptor does not support seeking");
271 if (offset > int.MaxValue)
272 throw new ArgumentOutOfRangeException ("offset", "too large");
274 Native.SeekFlags sf = Native.SeekFlags.SEEK_CUR;
276 case SeekOrigin.Begin: sf = Native.SeekFlags.SEEK_SET; break;
277 case SeekOrigin.Current: sf = Native.SeekFlags.SEEK_CUR; break;
278 case SeekOrigin.End: sf = Native.SeekFlags.SEEK_END; break;
281 long pos = Native.Syscall.lseek (fileDescriptor, offset, sf);
283 UnixMarshal.ThrowExceptionForLastError ();
287 public override void SetLength (long value)
289 AssertNotDisposed ();
291 throw new ArgumentOutOfRangeException ("value", "< 0");
292 if (!CanSeek && !CanWrite)
293 throw new NotSupportedException ("You can't truncating the current file descriptor");
297 r = Native.Syscall.ftruncate (fileDescriptor, value);
298 } while (UnixMarshal.ShouldRetrySyscall (r));
299 UnixMarshal.ThrowExceptionForLastErrorIf (r);
302 public override unsafe void Write (byte[] buffer, int offset, int count)
304 AssertNotDisposed ();
305 AssertValidBuffer (buffer, offset, count);
307 throw new NotSupportedException ("File Descriptor does not support writing");
310 fixed (byte* buf = &buffer[offset]) {
312 r = Native.Syscall.write (fileDescriptor, buf, (ulong) count);
313 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
316 UnixMarshal.ThrowExceptionForLastError ();
319 public unsafe void WriteAtOffset (byte[] buffer,
320 int offset, int count, long fileOffset)
322 AssertNotDisposed ();
323 AssertValidBuffer (buffer, offset, count);
325 throw new NotSupportedException ("File Descriptor does not support writing");
328 fixed (byte* buf = &buffer[offset]) {
330 r = Native.Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
331 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
334 UnixMarshal.ThrowExceptionForLastError ();
337 public void SendTo (UnixStream output)
339 SendTo (output, (ulong) output.Length);
342 [CLSCompliant (false)]
343 public void SendTo (UnixStream output, ulong count)
345 SendTo (output.Handle, count);
348 [CLSCompliant (false)]
349 public void SendTo (int out_fd, ulong count)
352 throw new NotSupportedException ("Unable to write to the current file descriptor");
353 long offset = Position;
354 long r = Native.Syscall.sendfile (out_fd, fileDescriptor, ref offset, count);
356 UnixMarshal.ThrowExceptionForLastError ();
359 public void SetOwner (long user, long group)
361 AssertNotDisposed ();
363 int r = Native.Syscall.fchown (fileDescriptor,
364 Convert.ToUInt32 (user), Convert.ToUInt32 (group));
365 UnixMarshal.ThrowExceptionForLastErrorIf (r);
368 public void SetOwner (string user, string group)
370 AssertNotDisposed ();
372 long uid = new UnixUserInfo (user).UserId;
373 long gid = new UnixGroupInfo (group).GroupId;
377 public void SetOwner (string user)
379 AssertNotDisposed ();
381 Native.Passwd pw = Native.Syscall.getpwnam (user);
383 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
384 long uid = pw.pw_uid;
385 long gid = pw.pw_gid;
389 [CLSCompliant (false)]
390 public long GetConfigurationValue (Native.PathconfName name)
392 AssertNotDisposed ();
393 long r = Native.Syscall.fpathconf (fileDescriptor, name);
394 if (r == -1 && Native.Syscall.GetLastError() != (Native.Errno) 0)
395 UnixMarshal.ThrowExceptionForLastError ();
404 public override void Close ()
406 if (fileDescriptor == InvalidFileDescriptor)
412 r = Native.Syscall.close (fileDescriptor);
413 } while (UnixMarshal.ShouldRetrySyscall (r));
414 UnixMarshal.ThrowExceptionForLastErrorIf (r);
415 fileDescriptor = InvalidFileDescriptor;
416 GC.SuppressFinalize (this);
419 void IDisposable.Dispose ()
421 AssertNotDisposed ();
425 GC.SuppressFinalize (this);
428 private bool canSeek = false;
429 private bool canRead = false;
430 private bool canWrite = false;
431 private bool owner = true;
432 private int fileDescriptor = InvalidFileDescriptor;
433 private Native.Stat stat;