* attribute.cs (GetMarshal): Work even if "DefineCustom" is
[mono.git] / mcs / class / Mono.Posix / Mono.Unix / UnixStream.cs
1 //
2 // Mono.Unix/UnixStream.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.
27 //
28
29 using System;
30 using System.IO;
31 using System.Runtime.InteropServices;
32 using System.Text;
33 using Mono.Unix;
34
35 namespace Mono.Unix {
36
37         public sealed class UnixStream : Stream, IDisposable
38         {
39                 public const int InvalidFileDescriptor = -1;
40                 public const int StandardInputFileDescriptor = 0;
41                 public const int StandardOutputFileDescriptor = 1;
42                 public const int StandardErrorFileDescriptor = 2;
43
44                 public UnixStream (int fileDescriptor)
45                         : this (fileDescriptor, true) {}
46
47                 public UnixStream (int fileDescriptor, bool ownsHandle)
48                 {
49                         if (InvalidFileDescriptor == fileDescriptor)
50                                 throw new ArgumentException (Locale.GetText ("Invalid file descriptor"), "fileDescriptor");
51                         
52                         this.fileDescriptor = fileDescriptor;
53                         this.owner = ownsHandle;
54                         
55                         long offset = Syscall.lseek (fileDescriptor, 0, SeekFlags.SEEK_CUR);
56                         if (offset != -1)
57                                 canSeek = true;
58                         long read = Syscall.read (fileDescriptor, IntPtr.Zero, 0);
59                         if (read != -1)
60                                 canRead = true;
61                         long write = Syscall.write (fileDescriptor, IntPtr.Zero, 0);
62                         if (write != -1)
63                                 canWrite = true;  
64                 }
65
66                 private void AssertNotDisposed ()
67                 {
68                         if (fileDescriptor == InvalidFileDescriptor)
69                                 throw new ObjectDisposedException ("Invalid File Descriptor");
70                 }
71
72                 public int Handle {
73                         get {return fileDescriptor;}
74                 }
75
76                 public override bool CanRead {
77                         get {return canRead;}
78                 }
79
80                 public override bool CanSeek {
81                         get {return canSeek;}
82                 }
83
84                 public override bool CanWrite {
85                         get {return canWrite;}
86                 }
87
88                 public override long Length {
89                         get {
90                                 AssertNotDisposed ();
91                                 if (!CanSeek)
92                                         throw new NotSupportedException ("File descriptor doesn't support seeking");
93                                 Stat stat;
94                                 int r = Syscall.fstat (fileDescriptor, out stat);
95                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
96                                 return (long) stat.st_size;
97                         }
98                 }
99
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 = Syscall.lseek (fileDescriptor, 0, SeekFlags.SEEK_CUR);
106                                 if (pos == -1)
107                                         UnixMarshal.ThrowExceptionForLastError ();
108                                 return (long) pos;
109                         }
110                         set {
111                                 Seek (value, SeekOrigin.Begin);
112                         }
113                 }
114
115                 public FilePermissions Permissions {
116                         get {
117                                 Stat stat;
118                                 int r = Syscall.fstat (fileDescriptor, out stat);
119                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
120                                 return stat.st_mode;
121                         }
122                         set {
123                                 int r = Syscall.fchmod (fileDescriptor, value);
124                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
125                         }
126                 }
127
128                 public void AdviseNormalAccess (long offset, long len)
129                 {
130                         UnixFile.AdviseNormalAccess (fileDescriptor, offset, len);
131                 }
132
133                 public void AdviseNormalAccess ()
134                 {
135                         UnixFile.AdviseNormalAccess (fileDescriptor);
136                 }
137
138                 public void AdviseSequentialAccess (long offset, long len)
139                 {
140                         UnixFile.AdviseSequentialAccess (fileDescriptor, offset, len);
141                 }
142
143                 public void AdviseSequentialAccess ()
144                 {
145                         UnixFile.AdviseSequentialAccess (fileDescriptor);
146                 }
147
148                 public void AdviseRandomAccess (long offset, long len)
149                 {
150                         UnixFile.AdviseRandomAccess (fileDescriptor, offset, len);
151                 }
152
153                 public void AdviseRandomAccess ()
154                 {
155                         UnixFile.AdviseRandomAccess (fileDescriptor);
156                 }
157
158                 public void AdviseNeedAccess (long offset, long len)
159                 {
160                         UnixFile.AdviseNeedAccess (fileDescriptor, offset, len);
161                 }
162
163                 public void AdviseNeedAccess ()
164                 {
165                         UnixFile.AdviseNeedAccess (fileDescriptor);
166                 }
167
168                 public void AdviseNoAccess (long offset, long len)
169                 {
170                         UnixFile.AdviseNoAccess (fileDescriptor, offset, len);
171                 }
172
173                 public void AdviseNoAccess ()
174                 {
175                         UnixFile.AdviseNoAccess (fileDescriptor);
176                 }
177
178                 public void AdviseOnceAccess (long offset, long len)
179                 {
180                         UnixFile.AdviseOnceAccess (fileDescriptor, offset, len);
181                 }
182
183                 public void AdviseOnceAccess ()
184                 {
185                         UnixFile.AdviseOnceAccess (fileDescriptor);
186                 }
187
188                 public override void Flush ()
189                 {
190                         int r = Syscall.fsync (fileDescriptor);
191                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
192                 }
193
194                 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
195                 {
196                         AssertNotDisposed ();
197                         AssertValidBuffer (buffer, offset, count);
198                         if (!CanRead)
199                                 throw new NotSupportedException ("Stream does not support reading");
200                                  
201                         long r = 0;
202                         fixed (byte* buf = &buffer[offset]) {
203                                 do {
204                                         r = Syscall.read (fileDescriptor, buf, (ulong) count);
205                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
206                         }
207                         if (r == -1)
208                                 UnixMarshal.ThrowExceptionForLastError ();
209                         return (int) r;
210                 }
211
212                 private void AssertValidBuffer (byte[] buffer, int offset, int count)
213                 {
214                         if (buffer == null)
215                                 throw new ArgumentNullException ("buffer");
216                         if (offset < 0)
217                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
218                         if (count < 0)
219                                 throw new ArgumentOutOfRangeException ("count", "< 0");
220                         if (offset > buffer.Length)
221                                 throw new ArgumentException ("destination offset is beyond array size");
222                         if (offset > (buffer.Length - count))
223                                 throw new ArgumentException ("would overrun buffer");
224                 }
225
226                 public unsafe int ReadAtOffset ([In, Out] byte[] buffer, 
227                         int offset, int count, long fileOffset)
228                 {
229                         AssertNotDisposed ();
230                         AssertValidBuffer (buffer, offset, count);
231                         if (!CanRead)
232                                 throw new NotSupportedException ("Stream does not support reading");
233                                  
234                         long r = 0;
235                         fixed (byte* buf = &buffer[offset]) {
236                                 do {
237                                         r = Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
238                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
239                         }
240                         if (r == -1)
241                                 UnixMarshal.ThrowExceptionForLastError ();
242                         return (int) r;
243                 }
244
245                 public override long Seek (long offset, SeekOrigin origin)
246                 {
247                         AssertNotDisposed ();
248                         if (!CanSeek)
249                                 throw new NotSupportedException ("The File Descriptor does not support seeking");
250                         if (offset > int.MaxValue)
251                                 throw new ArgumentOutOfRangeException ("offset", "too large");
252                                         
253                         SeekFlags sf = SeekFlags.SEEK_CUR;
254                         switch (origin) {
255                                 case SeekOrigin.Begin:   sf = SeekFlags.SEEK_SET; break;
256                                 case SeekOrigin.Current: sf = SeekFlags.SEEK_CUR; break;
257                                 case SeekOrigin.End:     sf = SeekFlags.SEEK_END; break;
258                         }
259
260                         long pos = Syscall.lseek (fileDescriptor, offset, sf);
261                         if (pos == -1)
262                                 UnixMarshal.ThrowExceptionForLastError ();
263                         return (long) pos;
264                 }
265
266                 public override void SetLength (long value)
267                 {
268                         AssertNotDisposed ();
269                         if (value < 0)
270                                 throw new ArgumentOutOfRangeException ("value", "< 0");
271                         if (!CanSeek && !CanWrite)
272                                 throw new NotSupportedException ("You can't truncating the current file descriptor");
273                         
274                         int r;
275                         do {
276                                 r = Syscall.ftruncate (fileDescriptor, value);
277                         } while (UnixMarshal.ShouldRetrySyscall (r));
278                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
279                 }
280
281                 public override unsafe void Write (byte[] buffer, int offset, int count)
282                 {
283                         AssertNotDisposed ();
284                         AssertValidBuffer (buffer, offset, count);
285                         if (!CanWrite)
286                                 throw new NotSupportedException ("File Descriptor does not support writing");
287
288                         long r = 0;
289                         fixed (byte* buf = &buffer[offset]) {
290                                 do {
291                                         r = Syscall.write (fileDescriptor, buf, (ulong) count);
292                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
293                         }
294                         if (r == -1)
295                                 UnixMarshal.ThrowExceptionForLastError ();
296                 }
297                 
298                 public unsafe void WriteAtOffset (byte[] buffer, 
299                         int offset, int count, long fileOffset)
300                 {
301                         AssertNotDisposed ();
302                         AssertValidBuffer (buffer, offset, count);
303                         if (!CanWrite)
304                                 throw new NotSupportedException ("File Descriptor does not support writing");
305
306                         long r = 0;
307                         fixed (byte* buf = &buffer[offset]) {
308                                 do {
309                                         r = Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
310                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
311                         }
312                         if (r == -1)
313                                 UnixMarshal.ThrowExceptionForLastError ();
314                 }
315
316                 public void SendTo (UnixStream output)
317                 {
318                         SendTo (output, (ulong) output.Length);
319                 }
320
321                 public void SendTo (UnixStream output, ulong count)
322                 {
323                         SendTo (output.Handle, count);
324                 }
325
326                 public void SendTo (int out_fd, ulong count)
327                 {
328                         if (!CanWrite)
329                                 throw new NotSupportedException ("Unable to write to the current file descriptor");
330                         long offset = Position;
331                         long r = Syscall.sendfile (out_fd, fileDescriptor, ref offset, count);
332                         if (r == -1)
333                                 UnixMarshal.ThrowExceptionForLastError ();
334                 }
335                 
336                 public void SetOwner (uint user, uint group)
337                 {
338                         AssertNotDisposed ();
339
340                         int r = Syscall.fchown (fileDescriptor, user, group);
341                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
342                 }
343
344                 public void SetOwner (string user, string group)
345                 {
346                         AssertNotDisposed ();
347
348                         uint uid = UnixUser.GetUserId (user);
349                         uint gid = UnixGroup.GetGroupId (group);
350                         SetOwner (uid, gid);
351                 }
352
353                 public void SetOwner (string user)
354                 {
355                         AssertNotDisposed ();
356
357                         Passwd pw = Syscall.getpwnam (user);
358                         if (pw == null)
359                                 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
360                         uint uid = pw.pw_uid;
361                         uint gid = pw.pw_gid;
362                         SetOwner (uid, gid);
363                 }
364
365                 public long GetConfigurationValue (PathConf name)
366                 {
367                         AssertNotDisposed ();
368                         Syscall.SetLastError ((Error) 0);
369                         long r = Syscall.fpathconf (fileDescriptor, name);
370                         if (r == -1 && Syscall.GetLastError() != (Error) 0)
371                                 UnixMarshal.ThrowExceptionForLastError ();
372                         return r;
373                 }
374
375                 ~UnixStream ()
376                 {
377                         Close ();
378                 }
379
380                 public override void Close ()
381                 {
382                         if (fileDescriptor == InvalidFileDescriptor)
383                                 return;
384                                 
385                         Flush ();
386                         int r;
387                         do {
388                                 r = Syscall.close (fileDescriptor);
389                         } while (UnixMarshal.ShouldRetrySyscall (r));
390                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
391                         fileDescriptor = InvalidFileDescriptor;
392                         GC.SuppressFinalize (this);
393                 }
394                 
395                 void IDisposable.Dispose ()
396                 {
397                         AssertNotDisposed ();
398                         if (owner) {
399                                 Close ();
400                         }
401                         GC.SuppressFinalize (this);
402                 }
403
404                 private bool canSeek = false;
405                 private bool canRead = false;
406                 private bool canWrite = false;
407                 private bool owner = true;
408                 private int fileDescriptor = InvalidFileDescriptor;
409         }
410 }
411
412 // vim: noexpandtab