**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.Posix / Mono.Posix / PosixStream.cs
bloba96234018dd2733db9a007c1ccc6dcc0881927a3
1 //
2 // Mono.Posix/PosixStream.cs
3 //
4 // Authors:
5 // Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2004 Jonathan Pryor
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
29 using System;
30 using System.IO;
31 using System.Runtime.InteropServices;
32 using System.Text;
33 using Mono.Posix;
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);
56 if (offset != -1)
57 canSeek = true;
58 unsafe {
59 long read = Syscall.read (fileDescriptor, null, 0);
60 if (read != -1)
61 canRead = true;
62 long write = Syscall.write (fileDescriptor, null, 0);
63 if (write != -1)
64 canWrite = true;
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 {
79 get {return canRead;}
82 public override bool CanSeek {
83 get {return canSeek;}
86 public override bool CanWrite {
87 get {return canWrite;}
90 public override long Length {
91 get {
92 AssertNotDisposed ();
93 if (!CanSeek)
94 throw new NotSupportedException ("File descriptor doesn't support seeking");
95 Stat stat;
96 int r = Syscall.fstat (fileDescriptor, out stat);
97 PosixMarshal.ThrowExceptionForLastErrorIf (r);
98 return (long) stat.st_size;
102 public override long Position {
103 get {
104 AssertNotDisposed ();
105 if (!CanSeek)
106 throw new NotSupportedException ("The stream does not support seeking");
107 long pos = Syscall.lseek (fileDescriptor, 0, SeekFlags.SEEK_CUR);
108 if (pos == -1)
109 PosixMarshal.ThrowExceptionForLastError ();
110 return (long) pos;
112 set {
113 Seek (value, SeekOrigin.Begin);
117 public FilePermissions Permissions {
118 get {
119 Stat stat;
120 int r = Syscall.fstat (fileDescriptor, out stat);
121 PosixMarshal.ThrowExceptionForLastErrorIf (r);
122 return stat.st_mode;
124 set {
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);
200 if (!CanRead)
201 throw new NotSupportedException ("Stream does not support reading");
203 long r = 0;
204 fixed (byte* buf = &buffer[offset]) {
205 do {
206 r = Syscall.read (fileDescriptor, buf, (ulong) count);
207 } while (PosixMarshal.ShouldRetrySyscall ((int) r));
209 if (r == -1)
210 PosixMarshal.ThrowExceptionForLastError ();
211 return (int) r;
214 private void AssertValidBuffer (byte[] buffer, int offset, int count)
216 if (buffer == null)
217 throw new ArgumentNullException ("buffer");
218 if (offset < 0)
219 throw new ArgumentOutOfRangeException ("offset", "< 0");
220 if (count < 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);
233 if (!CanRead)
234 throw new NotSupportedException ("Stream does not support reading");
236 long r = 0;
237 fixed (byte* buf = &buffer[offset]) {
238 do {
239 r = Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
240 } while (PosixMarshal.ShouldRetrySyscall ((int) r));
242 if (r == -1)
243 PosixMarshal.ThrowExceptionForLastError ();
244 return (int) r;
247 public override long Seek (long offset, SeekOrigin origin)
249 AssertNotDisposed ();
250 if (!CanSeek)
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;
256 switch (origin) {
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);
263 if (pos == -1)
264 PosixMarshal.ThrowExceptionForLastError ();
265 return (long) pos;
268 public override void SetLength (long value)
270 AssertNotDisposed ();
271 if (value < 0)
272 throw new ArgumentOutOfRangeException ("value", "< 0");
273 if (!CanSeek && !CanWrite)
274 throw new NotSupportedException ("You can't truncating the current file descriptor");
276 int r;
277 do {
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);
287 if (!CanWrite)
288 throw new NotSupportedException ("File Descriptor does not support writing");
290 long r = 0;
291 fixed (byte* buf = &buffer[offset]) {
292 do {
293 r = Syscall.write (fileDescriptor, buf, (ulong) count);
294 } while (PosixMarshal.ShouldRetrySyscall ((int) r));
296 if (r == -1)
297 PosixMarshal.ThrowExceptionForLastError ();
300 public unsafe void WriteAtOffset (byte[] buffer,
301 int offset, int count, long fileOffset)
303 AssertNotDisposed ();
304 AssertValidBuffer (buffer, offset, count);
305 if (!CanWrite)
306 throw new NotSupportedException ("File Descriptor does not support writing");
308 long r = 0;
309 fixed (byte* buf = &buffer[offset]) {
310 do {
311 r = Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
312 } while (PosixMarshal.ShouldRetrySyscall ((int) r));
314 if (r == -1)
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)
330 if (!CanWrite)
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);
334 if (r == -1)
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);
352 SetOwner (uid, gid);
355 public void SetOwner (string user)
357 AssertNotDisposed ();
359 Passwd pw = Syscall.getpwnam (user);
360 if (pw == null)
361 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
362 uint uid = pw.pw_uid;
363 uint gid = pw.pw_gid;
364 SetOwner (uid, 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 ();
374 return r;
377 ~PosixStream ()
379 Close ();
382 public override void Close ()
384 if (fileDescriptor == InvalidFileDescriptor)
385 return;
387 Flush ();
388 int r;
389 do {
390 r = Syscall.close (fileDescriptor);
391 } while (PosixMarshal.ShouldRetrySyscall (r));
392 PosixMarshal.ThrowExceptionForLastErrorIf (r);
393 fileDescriptor = InvalidFileDescriptor;
396 void IDisposable.Dispose ()
398 AssertNotDisposed ();
399 if (owner) {
400 Close ();
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;
413 // vim: noexpandtab