2008-09-30 Michael Hutchinson <mhutchinson@novell.com>
[mono-project.git] / mcs / class / Mono.Posix / Mono.Unix / UnixStream.cs
blobd5d16676b8deb45fae11a1b79b8cbd899c91e446
1 //
2 // Mono.Unix/UnixStream.cs
3 //
4 // Authors:
5 // Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2004-2006 Jonathan Pryor
8 // (C) 2007 Novell, Inc.
9 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using System.IO;
33 using System.Runtime.InteropServices;
34 using System.Text;
35 using Mono.Unix;
37 namespace Mono.Unix {
39 public sealed class UnixStream : Stream, IDisposable
41 public const int InvalidFileDescriptor = -1;
42 public const int StandardInputFileDescriptor = 0;
43 public const int StandardOutputFileDescriptor = 1;
44 public const int StandardErrorFileDescriptor = 2;
46 public UnixStream (int fileDescriptor)
47 : this (fileDescriptor, true) {}
49 public UnixStream (int fileDescriptor, bool ownsHandle)
51 if (InvalidFileDescriptor == fileDescriptor)
52 throw new ArgumentException (Locale.GetText ("Invalid file descriptor"), "fileDescriptor");
54 this.fileDescriptor = fileDescriptor;
55 this.owner = ownsHandle;
57 long offset = Native.Syscall.lseek (fileDescriptor, 0, Native.SeekFlags.SEEK_CUR);
58 if (offset != -1)
59 canSeek = true;
60 long read = Native.Syscall.read (fileDescriptor, IntPtr.Zero, 0);
61 if (read != -1)
62 canRead = true;
63 long write = Native.Syscall.write (fileDescriptor, IntPtr.Zero, 0);
64 if (write != -1)
65 canWrite = true;
68 private void AssertNotDisposed ()
70 if (fileDescriptor == InvalidFileDescriptor)
71 throw new ObjectDisposedException ("Invalid File Descriptor");
74 public int Handle {
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 RefreshStat ();
96 return stat.st_size;
100 public override long Position {
101 get {
102 AssertNotDisposed ();
103 if (!CanSeek)
104 throw new NotSupportedException ("The stream does not support seeking");
105 long pos = Native.Syscall.lseek (fileDescriptor, 0, Native.SeekFlags.SEEK_CUR);
106 if (pos == -1)
107 UnixMarshal.ThrowExceptionForLastError ();
108 return (long) pos;
110 set {
111 Seek (value, SeekOrigin.Begin);
115 [CLSCompliant (false)]
116 public Native.FilePermissions Protection {
117 get {
118 RefreshStat ();
119 return stat.st_mode;
121 set {
122 // we can't change file type with fchmod, so clear out that portion
123 value &= ~Native.FilePermissions.S_IFMT;
124 int r = Native.Syscall.fchmod (fileDescriptor, value);
125 UnixMarshal.ThrowExceptionForLastErrorIf (r);
129 public FileTypes FileType {
130 get {
131 int type = (int) Protection;
132 return (FileTypes) (type & (int) UnixFileSystemInfo.AllFileTypes);
134 // no set as fchmod(2) won't accept changing the file type.
137 public FileAccessPermissions FileAccessPermissions {
138 get {
139 int perms = (int) Protection;
140 return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions);
142 set {
143 int perms = (int) Protection;
144 perms &= (int) ~FileAccessPermissions.AllPermissions;
145 perms |= (int) value;
146 Protection = (Native.FilePermissions) perms;
150 public FileSpecialAttributes FileSpecialAttributes {
151 get {
152 int attrs = (int) Protection;
153 return (FileSpecialAttributes) (attrs & (int) UnixFileSystemInfo.AllSpecialAttributes);
155 set {
156 int perms = (int) Protection;
157 perms &= (int) ~UnixFileSystemInfo.AllSpecialAttributes;
158 perms |= (int) value;
159 Protection = (Native.FilePermissions) perms;
163 public UnixUserInfo OwnerUser {
164 get {RefreshStat (); return new UnixUserInfo (stat.st_uid);}
167 public long OwnerUserId {
168 get {RefreshStat (); return stat.st_uid;}
171 public UnixGroupInfo OwnerGroup {
172 get {RefreshStat (); return new UnixGroupInfo (stat.st_gid);}
175 public long OwnerGroupId {
176 get {RefreshStat (); return stat.st_gid;}
179 private void RefreshStat ()
181 AssertNotDisposed ();
182 int r = Native.Syscall.fstat (fileDescriptor, out stat);
183 UnixMarshal.ThrowExceptionForLastErrorIf (r);
186 public void AdviseFileAccessPattern (FileAccessPattern pattern, long offset, long len)
188 FileHandleOperations.AdviseFileAccessPattern (fileDescriptor, pattern, offset, len);
191 public void AdviseFileAccessPattern (FileAccessPattern pattern)
193 AdviseFileAccessPattern (pattern, 0, 0);
196 public override void Flush ()
200 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
202 AssertNotDisposed ();
203 AssertValidBuffer (buffer, offset, count);
204 if (!CanRead)
205 throw new NotSupportedException ("Stream does not support reading");
207 if (buffer.Length == 0)
208 return 0;
210 long r = 0;
211 fixed (byte* buf = &buffer[offset]) {
212 do {
213 r = Native.Syscall.read (fileDescriptor, buf, (ulong) count);
214 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
216 if (r == -1)
217 UnixMarshal.ThrowExceptionForLastError ();
218 return (int) r;
221 private void AssertValidBuffer (byte[] buffer, int offset, int count)
223 if (buffer == null)
224 throw new ArgumentNullException ("buffer");
225 if (offset < 0)
226 throw new ArgumentOutOfRangeException ("offset", "< 0");
227 if (count < 0)
228 throw new ArgumentOutOfRangeException ("count", "< 0");
229 if (offset > buffer.Length)
230 throw new ArgumentException ("destination offset is beyond array size");
231 if (offset > (buffer.Length - count))
232 throw new ArgumentException ("would overrun buffer");
235 public unsafe int ReadAtOffset ([In, Out] byte[] buffer,
236 int offset, int count, long fileOffset)
238 AssertNotDisposed ();
239 AssertValidBuffer (buffer, offset, count);
240 if (!CanRead)
241 throw new NotSupportedException ("Stream does not support reading");
243 if (buffer.Length == 0)
244 return 0;
246 long r = 0;
247 fixed (byte* buf = &buffer[offset]) {
248 do {
249 r = Native.Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
250 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
252 if (r == -1)
253 UnixMarshal.ThrowExceptionForLastError ();
254 return (int) r;
257 public override long Seek (long offset, SeekOrigin origin)
259 AssertNotDisposed ();
260 if (!CanSeek)
261 throw new NotSupportedException ("The File Descriptor does not support seeking");
262 if (offset > int.MaxValue)
263 throw new ArgumentOutOfRangeException ("offset", "too large");
265 Native.SeekFlags sf = Native.SeekFlags.SEEK_CUR;
266 switch (origin) {
267 case SeekOrigin.Begin: sf = Native.SeekFlags.SEEK_SET; break;
268 case SeekOrigin.Current: sf = Native.SeekFlags.SEEK_CUR; break;
269 case SeekOrigin.End: sf = Native.SeekFlags.SEEK_END; break;
272 long pos = Native.Syscall.lseek (fileDescriptor, offset, sf);
273 if (pos == -1)
274 UnixMarshal.ThrowExceptionForLastError ();
275 return (long) pos;
278 public override void SetLength (long value)
280 AssertNotDisposed ();
281 if (value < 0)
282 throw new ArgumentOutOfRangeException ("value", "< 0");
283 if (!CanSeek && !CanWrite)
284 throw new NotSupportedException ("You can't truncating the current file descriptor");
286 int r;
287 do {
288 r = Native.Syscall.ftruncate (fileDescriptor, value);
289 } while (UnixMarshal.ShouldRetrySyscall (r));
290 UnixMarshal.ThrowExceptionForLastErrorIf (r);
293 public override unsafe void Write (byte[] buffer, int offset, int count)
295 AssertNotDisposed ();
296 AssertValidBuffer (buffer, offset, count);
297 if (!CanWrite)
298 throw new NotSupportedException ("File Descriptor does not support writing");
300 if (buffer.Length == 0)
301 return;
303 long r = 0;
304 fixed (byte* buf = &buffer[offset]) {
305 do {
306 r = Native.Syscall.write (fileDescriptor, buf, (ulong) count);
307 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
309 if (r == -1)
310 UnixMarshal.ThrowExceptionForLastError ();
313 public unsafe void WriteAtOffset (byte[] buffer,
314 int offset, int count, long fileOffset)
316 AssertNotDisposed ();
317 AssertValidBuffer (buffer, offset, count);
318 if (!CanWrite)
319 throw new NotSupportedException ("File Descriptor does not support writing");
321 if (buffer.Length == 0)
322 return;
324 long r = 0;
325 fixed (byte* buf = &buffer[offset]) {
326 do {
327 r = Native.Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
328 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
330 if (r == -1)
331 UnixMarshal.ThrowExceptionForLastError ();
334 public void SendTo (UnixStream output)
336 SendTo (output, (ulong) output.Length);
339 [CLSCompliant (false)]
340 public void SendTo (UnixStream output, ulong count)
342 SendTo (output.Handle, count);
345 [CLSCompliant (false)]
346 public void SendTo (int out_fd, ulong count)
348 if (!CanWrite)
349 throw new NotSupportedException ("Unable to write to the current file descriptor");
350 long offset = Position;
351 long r = Native.Syscall.sendfile (out_fd, fileDescriptor, ref offset, count);
352 if (r == -1)
353 UnixMarshal.ThrowExceptionForLastError ();
356 public void SetOwner (long user, long group)
358 AssertNotDisposed ();
360 int r = Native.Syscall.fchown (fileDescriptor,
361 Convert.ToUInt32 (user), Convert.ToUInt32 (group));
362 UnixMarshal.ThrowExceptionForLastErrorIf (r);
365 public void SetOwner (string user, string group)
367 AssertNotDisposed ();
369 long uid = new UnixUserInfo (user).UserId;
370 long gid = new UnixGroupInfo (group).GroupId;
371 SetOwner (uid, gid);
374 public void SetOwner (string user)
376 AssertNotDisposed ();
378 Native.Passwd pw = Native.Syscall.getpwnam (user);
379 if (pw == null)
380 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
381 long uid = pw.pw_uid;
382 long gid = pw.pw_gid;
383 SetOwner (uid, gid);
386 [CLSCompliant (false)]
387 public long GetConfigurationValue (Native.PathconfName name)
389 AssertNotDisposed ();
390 long r = Native.Syscall.fpathconf (fileDescriptor, name);
391 if (r == -1 && Native.Syscall.GetLastError() != (Native.Errno) 0)
392 UnixMarshal.ThrowExceptionForLastError ();
393 return r;
396 ~UnixStream ()
398 Close ();
401 public override void Close ()
403 if (fileDescriptor == InvalidFileDescriptor)
404 return;
406 Flush ();
408 if (!owner)
409 return;
411 int r;
412 do {
413 r = Native.Syscall.close (fileDescriptor);
414 } while (UnixMarshal.ShouldRetrySyscall (r));
415 UnixMarshal.ThrowExceptionForLastErrorIf (r);
416 fileDescriptor = InvalidFileDescriptor;
417 GC.SuppressFinalize (this);
420 void IDisposable.Dispose ()
422 AssertNotDisposed ();
423 if (owner) {
424 Close ();
426 GC.SuppressFinalize (this);
429 private bool canSeek = false;
430 private bool canRead = false;
431 private bool canWrite = false;
432 private bool owner = true;
433 private int fileDescriptor = InvalidFileDescriptor;
434 private Native.Stat stat;
438 // vim: noexpandtab