2 // Mono.Posix/PosixStream.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
;
35 namespace Mono
.Posix
{
37 public sealed class PosixStream
: 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 PosixStream (int fileDescriptor
)
45 : this (fileDescriptor
, true) {}
47 public PosixStream (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
);
59 long read
= Syscall
.read (fileDescriptor
, null, 0);
62 long write
= Syscall
.write (fileDescriptor
, null, 0);
68 private void AssertNotDisposed ()
70 if (fileDescriptor
== InvalidFileDescriptor
)
71 throw new ObjectDisposedException ("Invalid File Descriptor");
74 public int FileDescriptor
{
75 get {return fileDescriptor;}
78 public override bool CanRead
{
82 public override bool CanSeek
{
86 public override bool CanWrite
{
87 get {return canWrite;}
90 public override long Length
{
94 throw new NotSupportedException ("File descriptor doesn't support seeking");
96 int r
= Syscall
.fstat (fileDescriptor
, out stat
);
97 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
98 return (long) stat
.st_size
;
102 public override long Position
{
104 AssertNotDisposed ();
106 throw new NotSupportedException ("The stream does not support seeking");
107 long pos
= Syscall
.lseek (fileDescriptor
, 0, SeekFlags
.SEEK_CUR
);
109 PosixMarshal
.ThrowExceptionForLastError ();
113 Seek (value, SeekOrigin
.Begin
);
117 public FilePermissions Permissions
{
120 int r
= Syscall
.fstat (fileDescriptor
, out stat
);
121 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
125 int r
= Syscall
.fchmod (fileDescriptor
, value);
126 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
130 public void AdviseNormalAccess (long offset
, long len
)
132 PosixFile
.AdviseNormalAccess (fileDescriptor
, offset
, len
);
135 public void AdviseNormalAccess ()
137 PosixFile
.AdviseNormalAccess (fileDescriptor
);
140 public void AdviseSequentialAccess (long offset
, long len
)
142 PosixFile
.AdviseSequentialAccess (fileDescriptor
, offset
, len
);
145 public void AdviseSequentialAccess ()
147 PosixFile
.AdviseSequentialAccess (fileDescriptor
);
150 public void AdviseRandomAccess (long offset
, long len
)
152 PosixFile
.AdviseRandomAccess (fileDescriptor
, offset
, len
);
155 public void AdviseRandomAccess ()
157 PosixFile
.AdviseRandomAccess (fileDescriptor
);
160 public void AdviseNeedAccess (long offset
, long len
)
162 PosixFile
.AdviseNeedAccess (fileDescriptor
, offset
, len
);
165 public void AdviseNeedAccess ()
167 PosixFile
.AdviseNeedAccess (fileDescriptor
);
170 public void AdviseNoAccess (long offset
, long len
)
172 PosixFile
.AdviseNoAccess (fileDescriptor
, offset
, len
);
175 public void AdviseNoAccess ()
177 PosixFile
.AdviseNoAccess (fileDescriptor
);
180 public void AdviseOnceAccess (long offset
, long len
)
182 PosixFile
.AdviseOnceAccess (fileDescriptor
, offset
, len
);
185 public void AdviseOnceAccess ()
187 PosixFile
.AdviseOnceAccess (fileDescriptor
);
190 public override void Flush ()
192 int r
= Syscall
.fsync (fileDescriptor
);
193 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
196 public override unsafe int Read ([In
, Out
] byte[] buffer
, int offset
, int count
)
198 AssertNotDisposed ();
199 AssertValidBuffer (buffer
, offset
, count
);
201 throw new NotSupportedException ("Stream does not support reading");
204 fixed (byte* buf
= &buffer
[offset
]) {
206 r
= Syscall
.read (fileDescriptor
, buf
, (ulong) count
);
207 } while (PosixMarshal
.ShouldRetrySyscall ((int) r
));
210 PosixMarshal
.ThrowExceptionForLastError ();
214 private void AssertValidBuffer (byte[] buffer
, int offset
, int count
)
217 throw new ArgumentNullException ("buffer");
219 throw new ArgumentOutOfRangeException ("offset", "< 0");
221 throw new ArgumentOutOfRangeException ("count", "< 0");
222 if (offset
> buffer
.Length
)
223 throw new ArgumentException ("destination offset is beyond array size");
224 if (offset
> (buffer
.Length
- count
))
225 throw new ArgumentException ("would overrun buffer");
228 public unsafe int ReadAtOffset ([In
, Out
] byte[] buffer
,
229 int offset
, int count
, long fileOffset
)
231 AssertNotDisposed ();
232 AssertValidBuffer (buffer
, offset
, count
);
234 throw new NotSupportedException ("Stream does not support reading");
237 fixed (byte* buf
= &buffer
[offset
]) {
239 r
= Syscall
.pread (fileDescriptor
, buf
, (ulong) count
, fileOffset
);
240 } while (PosixMarshal
.ShouldRetrySyscall ((int) r
));
243 PosixMarshal
.ThrowExceptionForLastError ();
247 public override long Seek (long offset
, SeekOrigin origin
)
249 AssertNotDisposed ();
251 throw new NotSupportedException ("The File Descriptor does not support seeking");
252 if (offset
> int.MaxValue
)
253 throw new ArgumentOutOfRangeException ("offset", "too large");
255 SeekFlags sf
= SeekFlags
.SEEK_CUR
;
257 case SeekOrigin
.Begin
: sf
= SeekFlags
.SEEK_SET
; break;
258 case SeekOrigin
.Current
: sf
= SeekFlags
.SEEK_CUR
; break;
259 case SeekOrigin
.End
: sf
= SeekFlags
.SEEK_END
; break;
262 long pos
= Syscall
.lseek (fileDescriptor
, offset
, sf
);
264 PosixMarshal
.ThrowExceptionForLastError ();
268 public override void SetLength (long value)
270 AssertNotDisposed ();
272 throw new ArgumentOutOfRangeException ("value", "< 0");
273 if (!CanSeek
&& !CanWrite
)
274 throw new NotSupportedException ("You can't truncating the current file descriptor");
278 r
= Syscall
.ftruncate (fileDescriptor
, value);
279 } while (PosixMarshal
.ShouldRetrySyscall (r
));
280 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
283 public override unsafe void Write (byte[] buffer
, int offset
, int count
)
285 AssertNotDisposed ();
286 AssertValidBuffer (buffer
, offset
, count
);
288 throw new NotSupportedException ("File Descriptor does not support writing");
291 fixed (byte* buf
= &buffer
[offset
]) {
293 r
= Syscall
.write (fileDescriptor
, buf
, (ulong) count
);
294 } while (PosixMarshal
.ShouldRetrySyscall ((int) r
));
297 PosixMarshal
.ThrowExceptionForLastError ();
300 public unsafe void WriteAtOffset (byte[] buffer
,
301 int offset
, int count
, long fileOffset
)
303 AssertNotDisposed ();
304 AssertValidBuffer (buffer
, offset
, count
);
306 throw new NotSupportedException ("File Descriptor does not support writing");
309 fixed (byte* buf
= &buffer
[offset
]) {
311 r
= Syscall
.pwrite (fileDescriptor
, buf
, (ulong) count
, fileOffset
);
312 } while (PosixMarshal
.ShouldRetrySyscall ((int) r
));
315 PosixMarshal
.ThrowExceptionForLastError ();
318 public void SendTo (PosixStream output
)
320 SendTo (output
, (ulong) output
.Length
);
323 public void SendTo (PosixStream output
, ulong count
)
325 SendTo (output
.FileDescriptor
, count
);
328 public void SendTo (int out_fd
, ulong count
)
331 throw new NotSupportedException ("Unable to write to the current file descriptor");
332 long offset
= Position
;
333 long r
= Syscall
.sendfile (out_fd
, fileDescriptor
, ref offset
, count
);
335 PosixMarshal
.ThrowExceptionForLastError ();
338 public void SetOwner (uint user
, uint group)
340 AssertNotDisposed ();
342 int r
= Syscall
.fchown (fileDescriptor
, user
, group);
343 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
346 public void SetOwner (string user
, string group)
348 AssertNotDisposed ();
350 uint uid
= PosixUser
.GetUserId (user
);
351 uint gid
= PosixGroup
.GetGroupId (group);
355 public void SetOwner (string user
)
357 AssertNotDisposed ();
359 Passwd pw
= Syscall
.getpwnam (user
);
361 throw new ArgumentException (Locale
.GetText ("invalid username"), "user");
362 uint uid
= pw
.pw_uid
;
363 uint gid
= pw
.pw_gid
;
367 public long GetConfigurationValue (PathConf name
)
369 AssertNotDisposed ();
370 Syscall
.SetLastError ((Error
) 0);
371 long r
= Syscall
.fpathconf (fileDescriptor
, name
);
372 if (r
== -1 && Syscall
.GetLastError() != (Error
) 0)
373 PosixMarshal
.ThrowExceptionForLastError ();
382 public override void Close ()
384 if (fileDescriptor
== InvalidFileDescriptor
)
390 r
= Syscall
.close (fileDescriptor
);
391 } while (PosixMarshal
.ShouldRetrySyscall (r
));
392 PosixMarshal
.ThrowExceptionForLastErrorIf (r
);
393 fileDescriptor
= InvalidFileDescriptor
;
396 void IDisposable
.Dispose ()
398 AssertNotDisposed ();
402 GC
.SuppressFinalize (this);
405 private bool canSeek
= false;
406 private bool canRead
= false;
407 private bool canWrite
= false;
408 private bool owner
= true;
409 private int fileDescriptor
= InvalidFileDescriptor
;