2 // Mono.Unix/UnixStream.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004 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");
94 int r = Syscall.fstat (fileDescriptor, out stat);
95 UnixMarshal.ThrowExceptionForLastErrorIf (r);
96 return (long) stat.st_size;
100 public override long Position {
102 AssertNotDisposed ();
104 throw new NotSupportedException ("The stream does not support seeking");
105 long pos = Syscall.lseek (fileDescriptor, 0, SeekFlags.SEEK_CUR);
107 UnixMarshal.ThrowExceptionForLastError ();
111 Seek (value, SeekOrigin.Begin);
115 public FilePermissions Permissions {
118 int r = Syscall.fstat (fileDescriptor, out stat);
119 UnixMarshal.ThrowExceptionForLastErrorIf (r);
123 int r = Syscall.fchmod (fileDescriptor, value);
124 UnixMarshal.ThrowExceptionForLastErrorIf (r);
128 public void AdviseNormalAccess (long offset, long len)
130 UnixFile.AdviseNormalAccess (fileDescriptor, offset, len);
133 public void AdviseNormalAccess ()
135 UnixFile.AdviseNormalAccess (fileDescriptor);
138 public void AdviseSequentialAccess (long offset, long len)
140 UnixFile.AdviseSequentialAccess (fileDescriptor, offset, len);
143 public void AdviseSequentialAccess ()
145 UnixFile.AdviseSequentialAccess (fileDescriptor);
148 public void AdviseRandomAccess (long offset, long len)
150 UnixFile.AdviseRandomAccess (fileDescriptor, offset, len);
153 public void AdviseRandomAccess ()
155 UnixFile.AdviseRandomAccess (fileDescriptor);
158 public void AdviseNeedAccess (long offset, long len)
160 UnixFile.AdviseNeedAccess (fileDescriptor, offset, len);
163 public void AdviseNeedAccess ()
165 UnixFile.AdviseNeedAccess (fileDescriptor);
168 public void AdviseNoAccess (long offset, long len)
170 UnixFile.AdviseNoAccess (fileDescriptor, offset, len);
173 public void AdviseNoAccess ()
175 UnixFile.AdviseNoAccess (fileDescriptor);
178 public void AdviseOnceAccess (long offset, long len)
180 UnixFile.AdviseOnceAccess (fileDescriptor, offset, len);
183 public void AdviseOnceAccess ()
185 UnixFile.AdviseOnceAccess (fileDescriptor);
188 public override void Flush ()
190 int r = Syscall.fsync (fileDescriptor);
191 UnixMarshal.ThrowExceptionForLastErrorIf (r);
194 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
196 AssertNotDisposed ();
197 AssertValidBuffer (buffer, offset, count);
199 throw new NotSupportedException ("Stream does not support reading");
202 fixed (byte* buf = &buffer[offset]) {
204 r = Syscall.read (fileDescriptor, buf, (ulong) count);
205 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
208 UnixMarshal.ThrowExceptionForLastError ();
212 private void AssertValidBuffer (byte[] buffer, int offset, int count)
215 throw new ArgumentNullException ("buffer");
217 throw new ArgumentOutOfRangeException ("offset", "< 0");
219 throw new ArgumentOutOfRangeException ("count", "< 0");
220 if (offset > buffer.Length)
221 throw new ArgumentException ("destination offset is beyond array size");
222 if (offset > (buffer.Length - count))
223 throw new ArgumentException ("would overrun buffer");
226 public unsafe int ReadAtOffset ([In, Out] byte[] buffer,
227 int offset, int count, long fileOffset)
229 AssertNotDisposed ();
230 AssertValidBuffer (buffer, offset, count);
232 throw new NotSupportedException ("Stream does not support reading");
235 fixed (byte* buf = &buffer[offset]) {
237 r = Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
238 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
241 UnixMarshal.ThrowExceptionForLastError ();
245 public override long Seek (long offset, SeekOrigin origin)
247 AssertNotDisposed ();
249 throw new NotSupportedException ("The File Descriptor does not support seeking");
250 if (offset > int.MaxValue)
251 throw new ArgumentOutOfRangeException ("offset", "too large");
253 SeekFlags sf = SeekFlags.SEEK_CUR;
255 case SeekOrigin.Begin: sf = SeekFlags.SEEK_SET; break;
256 case SeekOrigin.Current: sf = SeekFlags.SEEK_CUR; break;
257 case SeekOrigin.End: sf = SeekFlags.SEEK_END; break;
260 long pos = Syscall.lseek (fileDescriptor, offset, sf);
262 UnixMarshal.ThrowExceptionForLastError ();
266 public override void SetLength (long value)
268 AssertNotDisposed ();
270 throw new ArgumentOutOfRangeException ("value", "< 0");
271 if (!CanSeek && !CanWrite)
272 throw new NotSupportedException ("You can't truncating the current file descriptor");
276 r = Syscall.ftruncate (fileDescriptor, value);
277 } while (UnixMarshal.ShouldRetrySyscall (r));
278 UnixMarshal.ThrowExceptionForLastErrorIf (r);
281 public override unsafe void Write (byte[] buffer, int offset, int count)
283 AssertNotDisposed ();
284 AssertValidBuffer (buffer, offset, count);
286 throw new NotSupportedException ("File Descriptor does not support writing");
289 fixed (byte* buf = &buffer[offset]) {
291 r = Syscall.write (fileDescriptor, buf, (ulong) count);
292 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
295 UnixMarshal.ThrowExceptionForLastError ();
298 public unsafe void WriteAtOffset (byte[] buffer,
299 int offset, int count, long fileOffset)
301 AssertNotDisposed ();
302 AssertValidBuffer (buffer, offset, count);
304 throw new NotSupportedException ("File Descriptor does not support writing");
307 fixed (byte* buf = &buffer[offset]) {
309 r = Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
310 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
313 UnixMarshal.ThrowExceptionForLastError ();
316 public void SendTo (UnixStream output)
318 SendTo (output, (ulong) output.Length);
321 public void SendTo (UnixStream output, ulong count)
323 SendTo (output.Handle, count);
326 public void SendTo (int out_fd, ulong count)
329 throw new NotSupportedException ("Unable to write to the current file descriptor");
330 long offset = Position;
331 long r = Syscall.sendfile (out_fd, fileDescriptor, ref offset, count);
333 UnixMarshal.ThrowExceptionForLastError ();
336 public void SetOwner (uint user, uint group)
338 AssertNotDisposed ();
340 int r = Syscall.fchown (fileDescriptor, user, group);
341 UnixMarshal.ThrowExceptionForLastErrorIf (r);
344 public void SetOwner (string user, string group)
346 AssertNotDisposed ();
348 uint uid = UnixUser.GetUserId (user);
349 uint gid = UnixGroup.GetGroupId (group);
353 public void SetOwner (string user)
355 AssertNotDisposed ();
357 Passwd pw = Syscall.getpwnam (user);
359 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
360 uint uid = pw.pw_uid;
361 uint gid = pw.pw_gid;
365 public long GetConfigurationValue (PathConf name)
367 AssertNotDisposed ();
368 Syscall.SetLastError ((Error) 0);
369 long r = Syscall.fpathconf (fileDescriptor, name);
370 if (r == -1 && Syscall.GetLastError() != (Error) 0)
371 UnixMarshal.ThrowExceptionForLastError ();
380 public override void Close ()
382 if (fileDescriptor == InvalidFileDescriptor)
388 r = Syscall.close (fileDescriptor);
389 } while (UnixMarshal.ShouldRetrySyscall (r));
390 UnixMarshal.ThrowExceptionForLastErrorIf (r);
391 fileDescriptor = InvalidFileDescriptor;
392 GC.SuppressFinalize (this);
395 void IDisposable.Dispose ()
397 AssertNotDisposed ();
401 GC.SuppressFinalize (this);
404 private bool canSeek = false;
405 private bool canRead = false;
406 private bool canWrite = false;
407 private bool owner = true;
408 private int fileDescriptor = InvalidFileDescriptor;