* Stdlib.cs: sys_* functions shouldn't be public.
[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                         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;  
65                         }
66                 }
67
68                 private void AssertNotDisposed ()
69                 {
70                         if (fileDescriptor == InvalidFileDescriptor)
71                                 throw new ObjectDisposedException ("Invalid File Descriptor");
72                 }
73
74                 public int FileDescriptor {
75                         get {return fileDescriptor;}
76                 }
77
78                 public override bool CanRead {
79                         get {return canRead;}
80                 }
81
82                 public override bool CanSeek {
83                         get {return canSeek;}
84                 }
85
86                 public override bool CanWrite {
87                         get {return canWrite;}
88                 }
89
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                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
98                                 return (long) stat.st_size;
99                         }
100                 }
101
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                                         UnixMarshal.ThrowExceptionForLastError ();
110                                 return (long) pos;
111                         }
112                         set {
113                                 Seek (value, SeekOrigin.Begin);
114                         }
115                 }
116
117                 public FilePermissions Permissions {
118                         get {
119                                 Stat stat;
120                                 int r = Syscall.fstat (fileDescriptor, out stat);
121                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
122                                 return stat.st_mode;
123                         }
124                         set {
125                                 int r = Syscall.fchmod (fileDescriptor, value);
126                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
127                         }
128                 }
129
130                 public void AdviseNormalAccess (long offset, long len)
131                 {
132                         UnixFile.AdviseNormalAccess (fileDescriptor, offset, len);
133                 }
134
135                 public void AdviseNormalAccess ()
136                 {
137                         UnixFile.AdviseNormalAccess (fileDescriptor);
138                 }
139
140                 public void AdviseSequentialAccess (long offset, long len)
141                 {
142                         UnixFile.AdviseSequentialAccess (fileDescriptor, offset, len);
143                 }
144
145                 public void AdviseSequentialAccess ()
146                 {
147                         UnixFile.AdviseSequentialAccess (fileDescriptor);
148                 }
149
150                 public void AdviseRandomAccess (long offset, long len)
151                 {
152                         UnixFile.AdviseRandomAccess (fileDescriptor, offset, len);
153                 }
154
155                 public void AdviseRandomAccess ()
156                 {
157                         UnixFile.AdviseRandomAccess (fileDescriptor);
158                 }
159
160                 public void AdviseNeedAccess (long offset, long len)
161                 {
162                         UnixFile.AdviseNeedAccess (fileDescriptor, offset, len);
163                 }
164
165                 public void AdviseNeedAccess ()
166                 {
167                         UnixFile.AdviseNeedAccess (fileDescriptor);
168                 }
169
170                 public void AdviseNoAccess (long offset, long len)
171                 {
172                         UnixFile.AdviseNoAccess (fileDescriptor, offset, len);
173                 }
174
175                 public void AdviseNoAccess ()
176                 {
177                         UnixFile.AdviseNoAccess (fileDescriptor);
178                 }
179
180                 public void AdviseOnceAccess (long offset, long len)
181                 {
182                         UnixFile.AdviseOnceAccess (fileDescriptor, offset, len);
183                 }
184
185                 public void AdviseOnceAccess ()
186                 {
187                         UnixFile.AdviseOnceAccess (fileDescriptor);
188                 }
189
190                 public override void Flush ()
191                 {
192                         int r = Syscall.fsync (fileDescriptor);
193                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
194                 }
195
196                 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
197                 {
198                         AssertNotDisposed ();
199                         AssertValidBuffer (buffer, offset, count);
200                         if (!CanRead)
201                                 throw new NotSupportedException ("Stream does not support reading");
202                                  
203                         long r = 0;
204                         fixed (byte* buf = &buffer[offset]) {
205                                 do {
206                                         r = Syscall.read (fileDescriptor, buf, (ulong) count);
207                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
208                         }
209                         if (r == -1)
210                                 UnixMarshal.ThrowExceptionForLastError ();
211                         return (int) r;
212                 }
213
214                 private void AssertValidBuffer (byte[] buffer, int offset, int count)
215                 {
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");
226                 }
227
228                 public unsafe int ReadAtOffset ([In, Out] byte[] buffer, 
229                         int offset, int count, long fileOffset)
230                 {
231                         AssertNotDisposed ();
232                         AssertValidBuffer (buffer, offset, count);
233                         if (!CanRead)
234                                 throw new NotSupportedException ("Stream does not support reading");
235                                  
236                         long r = 0;
237                         fixed (byte* buf = &buffer[offset]) {
238                                 do {
239                                         r = Syscall.pread (fileDescriptor, buf, (ulong) count, fileOffset);
240                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
241                         }
242                         if (r == -1)
243                                 UnixMarshal.ThrowExceptionForLastError ();
244                         return (int) r;
245                 }
246
247                 public override long Seek (long offset, SeekOrigin origin)
248                 {
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");
254                                         
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;
260                         }
261
262                         long pos = Syscall.lseek (fileDescriptor, offset, sf);
263                         if (pos == -1)
264                                 UnixMarshal.ThrowExceptionForLastError ();
265                         return (long) pos;
266                 }
267
268                 public override void SetLength (long value)
269                 {
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");
275                         
276                         int r;
277                         do {
278                                 r = Syscall.ftruncate (fileDescriptor, value);
279                         } while (UnixMarshal.ShouldRetrySyscall (r));
280                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
281                 }
282
283                 public override unsafe void Write (byte[] buffer, int offset, int count)
284                 {
285                         AssertNotDisposed ();
286                         AssertValidBuffer (buffer, offset, count);
287                         if (!CanWrite)
288                                 throw new NotSupportedException ("File Descriptor does not support writing");
289
290                         long r = 0;
291                         fixed (byte* buf = &buffer[offset]) {
292                                 do {
293                                         r = Syscall.write (fileDescriptor, buf, (ulong) count);
294                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
295                         }
296                         if (r == -1)
297                                 UnixMarshal.ThrowExceptionForLastError ();
298                 }
299                 
300                 public unsafe void WriteAtOffset (byte[] buffer, 
301                         int offset, int count, long fileOffset)
302                 {
303                         AssertNotDisposed ();
304                         AssertValidBuffer (buffer, offset, count);
305                         if (!CanWrite)
306                                 throw new NotSupportedException ("File Descriptor does not support writing");
307
308                         long r = 0;
309                         fixed (byte* buf = &buffer[offset]) {
310                                 do {
311                                         r = Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
312                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
313                         }
314                         if (r == -1)
315                                 UnixMarshal.ThrowExceptionForLastError ();
316                 }
317
318                 public void SendTo (UnixStream output)
319                 {
320                         SendTo (output, (ulong) output.Length);
321                 }
322
323                 public void SendTo (UnixStream output, ulong count)
324                 {
325                         SendTo (output.FileDescriptor, count);
326                 }
327
328                 public void SendTo (int out_fd, ulong count)
329                 {
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                                 UnixMarshal.ThrowExceptionForLastError ();
336                 }
337                 
338                 public void SetOwner (uint user, uint group)
339                 {
340                         AssertNotDisposed ();
341
342                         int r = Syscall.fchown (fileDescriptor, user, group);
343                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
344                 }
345
346                 public void SetOwner (string user, string group)
347                 {
348                         AssertNotDisposed ();
349
350                         uint uid = UnixUser.GetUserId (user);
351                         uint gid = UnixGroup.GetGroupId (group);
352                         SetOwner (uid, gid);
353                 }
354
355                 public void SetOwner (string user)
356                 {
357                         AssertNotDisposed ();
358
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);
365                 }
366
367                 public long GetConfigurationValue (PathConf name)
368                 {
369                         AssertNotDisposed ();
370                         Syscall.SetLastError ((Error) 0);
371                         long r = Syscall.fpathconf (fileDescriptor, name);
372                         if (r == -1 && Syscall.GetLastError() != (Error) 0)
373                                 UnixMarshal.ThrowExceptionForLastError ();
374                         return r;
375                 }
376
377                 ~UnixStream ()
378                 {
379                         Close ();
380                 }
381
382                 public override void Close ()
383                 {
384                         if (fileDescriptor == InvalidFileDescriptor)
385                                 return;
386                                 
387                         Flush ();
388                         int r;
389                         do {
390                                 r = Syscall.close (fileDescriptor);
391                         } while (UnixMarshal.ShouldRetrySyscall (r));
392                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
393                         fileDescriptor = InvalidFileDescriptor;
394                 }
395                 
396                 void IDisposable.Dispose ()
397                 {
398                         AssertNotDisposed ();
399                         if (owner) {
400                                 Close ();
401                         }
402                         GC.SuppressFinalize (this);
403                 }
404
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;
410         }
411 }
412
413 // vim: noexpandtab