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");
262 if (offset
> int.MaxValue
)
263 throw new ArgumentOutOfRangeException ("offset", "too large");
265 Native
.SeekFlags sf
= Native
.SeekFlags
.SEEK_CUR
;
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
);
274 UnixMarshal
.ThrowExceptionForLastError ();
278 public override void SetLength (long value)
280 AssertNotDisposed ();
282 throw new ArgumentOutOfRangeException ("value", "< 0");
283 if (!CanSeek
&& !CanWrite
)
284 throw new NotSupportedException ("You can't truncating the current file descriptor");
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
);
298 throw new NotSupportedException ("File Descriptor does not support writing");
300 if (buffer
.Length
== 0)
304 fixed (byte* buf
= &buffer
[offset
]) {
306 r
= Native
.Syscall
.write (fileDescriptor
, buf
, (ulong) count
);
307 } while (UnixMarshal
.ShouldRetrySyscall ((int) r
));
310 UnixMarshal
.ThrowExceptionForLastError ();
313 public unsafe void WriteAtOffset (byte[] buffer
,
314 int offset
, int count
, long fileOffset
)
316 AssertNotDisposed ();
317 AssertValidBuffer (buffer
, offset
, count
);
319 throw new NotSupportedException ("File Descriptor does not support writing");
321 if (buffer
.Length
== 0)
325 fixed (byte* buf
= &buffer
[offset
]) {
327 r
= Native
.Syscall
.pwrite (fileDescriptor
, buf
, (ulong) count
, fileOffset
);
328 } while (UnixMarshal
.ShouldRetrySyscall ((int) r
));
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
)
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
);
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
;
374 public void SetOwner (string user
)
376 AssertNotDisposed ();
378 Native
.Passwd pw
= Native
.Syscall
.getpwnam (user
);
380 throw new ArgumentException (Locale
.GetText ("invalid username"), "user");
381 long uid
= pw
.pw_uid
;
382 long gid
= pw
.pw_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 ();
401 public override void Close ()
403 if (fileDescriptor
== InvalidFileDescriptor
)
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 ();
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
;