2 // Mono.Unix/UnixStream.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004-2006 Jonathan Pryor
8 // (C) 2007 Novell, Inc.
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
33 using System
.Runtime
.InteropServices
;
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
);
60 long read
= Native
.Syscall
.read (fileDescriptor
, IntPtr
.Zero
, 0);
63 long write
= Native
.Syscall
.write (fileDescriptor
, IntPtr
.Zero
, 0);
68 private void AssertNotDisposed ()
70 if (fileDescriptor
== InvalidFileDescriptor
)
71 throw new ObjectDisposedException ("Invalid File Descriptor");
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");
100 public override long Position
{
102 AssertNotDisposed ();
104 throw new NotSupportedException ("The stream does not support seeking");
105 long pos
= Native
.Syscall
.lseek (fileDescriptor
, 0, Native
.SeekFlags
.SEEK_CUR
);
107 UnixMarshal
.ThrowExceptionForLastError ();
111 Seek (value, SeekOrigin
.Begin
);
115 [CLSCompliant (false)]
116 public Native
.FilePermissions Protection
{
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
{
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
{
139 int perms
= (int) Protection
;
140 return (FileAccessPermissions
) (perms
& (int) FileAccessPermissions
.AllPermissions
);
143 int perms
= (int) Protection
;
144 perms
&= (int) ~FileAccessPermissions
.AllPermissions
;
145 perms
|= (int) value;
146 Protection
= (Native
.FilePermissions
) perms
;
150 public FileSpecialAttributes FileSpecialAttributes
{
152 int attrs
= (int) Protection
;
153 return (FileSpecialAttributes
) (attrs
& (int) UnixFileSystemInfo
.AllSpecialAttributes
);
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
);
205 throw new NotSupportedException ("Stream does not support reading");
207 if (buffer
.Length
== 0)
211 fixed (byte* buf
= &buffer
[offset
]) {
213 r
= Native
.Syscall
.read (fileDescriptor
, buf
, (ulong) count
);
214 } while (UnixMarshal
.ShouldRetrySyscall ((int) r
));
217 UnixMarshal
.ThrowExceptionForLastError ();
221 private void AssertValidBuffer (byte[] buffer
, int offset
, int count
)
224 throw new ArgumentNullException ("buffer");
226 throw new ArgumentOutOfRangeException ("offset", "< 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
);
241 throw new NotSupportedException ("Stream does not support reading");
243 if (buffer
.Length
== 0)
247 fixed (byte* buf
= &buffer
[offset
]) {
249 r
= Native
.Syscall
.pread (fileDescriptor
, buf
, (ulong) count
, fileOffset
);
250 } while (UnixMarshal
.ShouldRetrySyscall ((int) r
));
253 UnixMarshal
.ThrowExceptionForLastError ();
257 public override long Seek (long offset
, SeekOrigin origin
)
259 AssertNotDisposed ();
261 throw new NotSupportedException ("The File Descriptor does not support seeking");
263 Native
.SeekFlags sf
= Native
.SeekFlags
.SEEK_CUR
;
265 case SeekOrigin
.Begin
: sf
= Native
.SeekFlags
.SEEK_SET
; break;
266 case SeekOrigin
.Current
: sf
= Native
.SeekFlags
.SEEK_CUR
; break;
267 case SeekOrigin
.End
: sf
= Native
.SeekFlags
.SEEK_END
; break;
270 long pos
= Native
.Syscall
.lseek (fileDescriptor
, offset
, sf
);
272 UnixMarshal
.ThrowExceptionForLastError ();
276 public override void SetLength (long value)
278 AssertNotDisposed ();
280 throw new ArgumentOutOfRangeException ("value", "< 0");
281 if (!CanSeek
&& !CanWrite
)
282 throw new NotSupportedException ("You can't truncating the current file descriptor");
286 r
= Native
.Syscall
.ftruncate (fileDescriptor
, value);
287 } while (UnixMarshal
.ShouldRetrySyscall (r
));
288 UnixMarshal
.ThrowExceptionForLastErrorIf (r
);
291 public override unsafe void Write (byte[] buffer
, int offset
, int count
)
293 AssertNotDisposed ();
294 AssertValidBuffer (buffer
, offset
, count
);
296 throw new NotSupportedException ("File Descriptor does not support writing");
298 if (buffer
.Length
== 0)
302 fixed (byte* buf
= &buffer
[offset
]) {
304 r
= Native
.Syscall
.write (fileDescriptor
, buf
, (ulong) count
);
305 } while (UnixMarshal
.ShouldRetrySyscall ((int) r
));
308 UnixMarshal
.ThrowExceptionForLastError ();
311 public unsafe void WriteAtOffset (byte[] buffer
,
312 int offset
, int count
, long fileOffset
)
314 AssertNotDisposed ();
315 AssertValidBuffer (buffer
, offset
, count
);
317 throw new NotSupportedException ("File Descriptor does not support writing");
319 if (buffer
.Length
== 0)
323 fixed (byte* buf
= &buffer
[offset
]) {
325 r
= Native
.Syscall
.pwrite (fileDescriptor
, buf
, (ulong) count
, fileOffset
);
326 } while (UnixMarshal
.ShouldRetrySyscall ((int) r
));
329 UnixMarshal
.ThrowExceptionForLastError ();
332 public void SendTo (UnixStream output
)
334 SendTo (output
, (ulong) output
.Length
);
337 [CLSCompliant (false)]
338 public void SendTo (UnixStream output
, ulong count
)
340 SendTo (output
.Handle
, count
);
343 [CLSCompliant (false)]
344 public void SendTo (int out_fd
, ulong count
)
347 throw new NotSupportedException ("Unable to write to the current file descriptor");
348 long offset
= Position
;
349 long r
= Native
.Syscall
.sendfile (out_fd
, fileDescriptor
, ref offset
, count
);
351 UnixMarshal
.ThrowExceptionForLastError ();
354 public void SetOwner (long user
, long group)
356 AssertNotDisposed ();
358 int r
= Native
.Syscall
.fchown (fileDescriptor
,
359 Convert
.ToInt32 (user
), Convert
.ToInt32 (group));
360 UnixMarshal
.ThrowExceptionForLastErrorIf (r
);
363 public void SetOwner (string user
, string group)
365 AssertNotDisposed ();
367 long uid
= new UnixUserInfo (user
).UserId
;
368 long gid
= new UnixGroupInfo (group).GroupId
;
372 public void SetOwner (string user
)
374 AssertNotDisposed ();
376 Native
.Passwd pw
= Native
.Syscall
.getpwnam (user
);
378 throw new ArgumentException (Locale
.GetText ("invalid username"), "user");
379 long uid
= pw
.pw_uid
;
380 long gid
= pw
.pw_gid
;
384 [CLSCompliant (false)]
385 public long GetConfigurationValue (Native
.PathconfName name
)
387 AssertNotDisposed ();
388 long r
= Native
.Syscall
.fpathconf (fileDescriptor
, name
);
389 if (r
== -1 && Native
.Syscall
.GetLastError() != (Native
.Errno
) 0)
390 UnixMarshal
.ThrowExceptionForLastError ();
399 public override void Close ()
401 if (fileDescriptor
== InvalidFileDescriptor
)
411 r
= Native
.Syscall
.close (fileDescriptor
);
412 } while (UnixMarshal
.ShouldRetrySyscall (r
));
413 UnixMarshal
.ThrowExceptionForLastErrorIf (r
);
414 fileDescriptor
= InvalidFileDescriptor
;
415 GC
.SuppressFinalize (this);
418 void IDisposable
.Dispose ()
420 if (fileDescriptor
!= InvalidFileDescriptor
&& owner
) {
423 GC
.SuppressFinalize (this);
426 private bool canSeek
= false;
427 private bool canRead
= false;
428 private bool canWrite
= false;
429 private bool owner
= true;
430 private int fileDescriptor
= InvalidFileDescriptor
;
431 private Native
.Stat stat
;