Delete end local endpoint when stopping the server
[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-2006 Jonathan Pryor
8 // (C) 2007 Novell, Inc.
9 //
10 //
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.
29 //
30
31 using System;
32 using System.IO;
33 using System.Runtime.InteropServices;
34 using System.Text;
35 using Mono.Unix;
36
37 namespace Mono.Unix {
38
39         public sealed class UnixStream : Stream, IDisposable
40         {
41                 public const int InvalidFileDescriptor = -1;
42                 public const int StandardInputFileDescriptor = 0;
43                 public const int StandardOutputFileDescriptor = 1;
44                 public const int StandardErrorFileDescriptor = 2;
45
46                 public UnixStream (int fileDescriptor)
47                         : this (fileDescriptor, true) {}
48
49                 public UnixStream (int fileDescriptor, bool ownsHandle)
50                 {
51                         if (InvalidFileDescriptor == fileDescriptor)
52                                 throw new ArgumentException (Locale.GetText ("Invalid file descriptor"), "fileDescriptor");
53                         
54                         this.fileDescriptor = fileDescriptor;
55                         this.owner = ownsHandle;
56                         
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;  
66                 }
67
68                 private void AssertNotDisposed ()
69                 {
70                         if (fileDescriptor == InvalidFileDescriptor)
71                                 throw new ObjectDisposedException ("Invalid File Descriptor");
72                 }
73
74                 public int Handle {
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                                 RefreshStat ();
96                                 return 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 = Native.Syscall.lseek (fileDescriptor, 0, Native.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                 [CLSCompliant (false)]
116                 public Native.FilePermissions Protection {
117                         get {
118                                 RefreshStat ();
119                                 return stat.st_mode;
120                         }
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);
126                         }
127                 }
128
129                 public FileTypes FileType {
130                         get {
131                                 int type = (int) Protection;
132                                 return (FileTypes) (type & (int) UnixFileSystemInfo.AllFileTypes);
133                         }
134                         // no set as fchmod(2) won't accept changing the file type.
135                 }
136
137                 public FileAccessPermissions FileAccessPermissions {
138                         get {
139                                 int perms = (int) Protection;
140                                 return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions);
141                         }
142                         set {
143                                 int perms = (int) Protection;
144                                 perms &= (int) ~FileAccessPermissions.AllPermissions;
145                                 perms |= (int) value;
146                                 Protection = (Native.FilePermissions) perms;
147                         }
148                 }
149
150                 public FileSpecialAttributes FileSpecialAttributes {
151                         get {
152                                 int attrs = (int) Protection;
153                                 return (FileSpecialAttributes) (attrs & (int) UnixFileSystemInfo.AllSpecialAttributes);
154                         }
155                         set {
156                                 int perms = (int) Protection;
157                                 perms &= (int) ~UnixFileSystemInfo.AllSpecialAttributes;
158                                 perms |= (int) value;
159                                 Protection = (Native.FilePermissions) perms;
160                         }
161                 }
162
163                 public UnixUserInfo OwnerUser {
164                         get {RefreshStat (); return new UnixUserInfo (stat.st_uid);}
165                 }
166                                                                                                 
167                 public long OwnerUserId {
168                         get {RefreshStat (); return stat.st_uid;}
169                 }
170                                                                                                 
171                 public UnixGroupInfo OwnerGroup {
172                         get {RefreshStat (); return new UnixGroupInfo (stat.st_gid);}
173                 }
174                                                                                                 
175                 public long OwnerGroupId {
176                         get {RefreshStat (); return stat.st_gid;}
177                 }
178
179                 private void RefreshStat ()
180                 {
181                         AssertNotDisposed ();
182                         int r = Native.Syscall.fstat (fileDescriptor, out stat);
183                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
184                 }
185
186                 public void AdviseFileAccessPattern (FileAccessPattern pattern, long offset, long len)
187                 {
188                         FileHandleOperations.AdviseFileAccessPattern (fileDescriptor, pattern, offset, len);
189                 }
190
191                 public void AdviseFileAccessPattern (FileAccessPattern pattern)
192                 {
193                         AdviseFileAccessPattern (pattern, 0, 0);
194                 }
195
196                 public override void Flush ()
197                 {
198                 }
199
200                 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
201                 {
202                         AssertNotDisposed ();
203                         AssertValidBuffer (buffer, offset, count);
204                         if (!CanRead)
205                                 throw new NotSupportedException ("Stream does not support reading");
206                                  
207                         if (buffer.Length == 0)
208                                 return 0;
209
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));
215                         }
216                         if (r == -1)
217                                 UnixMarshal.ThrowExceptionForLastError ();
218                         return (int) r;
219                 }
220
221                 private void AssertValidBuffer (byte[] buffer, int offset, int count)
222                 {
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");
233                 }
234
235                 public unsafe int ReadAtOffset ([In, Out] byte[] buffer, 
236                         int offset, int count, long fileOffset)
237                 {
238                         AssertNotDisposed ();
239                         AssertValidBuffer (buffer, offset, count);
240                         if (!CanRead)
241                                 throw new NotSupportedException ("Stream does not support reading");
242                                  
243                         if (buffer.Length == 0)
244                                 return 0;
245
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));
251                         }
252                         if (r == -1)
253                                 UnixMarshal.ThrowExceptionForLastError ();
254                         return (int) r;
255                 }
256
257                 public override long Seek (long offset, SeekOrigin origin)
258                 {
259                         AssertNotDisposed ();
260                         if (!CanSeek)
261                                 throw new NotSupportedException ("The File Descriptor does not support seeking");
262                                         
263                         Native.SeekFlags sf = Native.SeekFlags.SEEK_CUR;
264                         switch (origin) {
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;
268                         }
269
270                         long pos = Native.Syscall.lseek (fileDescriptor, offset, sf);
271                         if (pos == -1)
272                                 UnixMarshal.ThrowExceptionForLastError ();
273                         return (long) pos;
274                 }
275
276                 public override void SetLength (long value)
277                 {
278                         AssertNotDisposed ();
279                         if (value < 0)
280                                 throw new ArgumentOutOfRangeException ("value", "< 0");
281                         if (!CanSeek && !CanWrite)
282                                 throw new NotSupportedException ("You can't truncating the current file descriptor");
283                         
284                         int r;
285                         do {
286                                 r = Native.Syscall.ftruncate (fileDescriptor, value);
287                         } while (UnixMarshal.ShouldRetrySyscall (r));
288                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
289                 }
290
291                 public override unsafe void Write (byte[] buffer, int offset, int count)
292                 {
293                         AssertNotDisposed ();
294                         AssertValidBuffer (buffer, offset, count);
295                         if (!CanWrite)
296                                 throw new NotSupportedException ("File Descriptor does not support writing");
297
298                         if (buffer.Length == 0)
299                                 return;
300
301                         long r = 0;
302                         fixed (byte* buf = &buffer[offset]) {
303                                 do {
304                                         r = Native.Syscall.write (fileDescriptor, buf, (ulong) count);
305                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
306                         }
307                         if (r == -1)
308                                 UnixMarshal.ThrowExceptionForLastError ();
309                 }
310                 
311                 public unsafe void WriteAtOffset (byte[] buffer, 
312                         int offset, int count, long fileOffset)
313                 {
314                         AssertNotDisposed ();
315                         AssertValidBuffer (buffer, offset, count);
316                         if (!CanWrite)
317                                 throw new NotSupportedException ("File Descriptor does not support writing");
318
319                         if (buffer.Length == 0)
320                                 return;
321
322                         long r = 0;
323                         fixed (byte* buf = &buffer[offset]) {
324                                 do {
325                                         r = Native.Syscall.pwrite (fileDescriptor, buf, (ulong) count, fileOffset);
326                                 } while (UnixMarshal.ShouldRetrySyscall ((int) r));
327                         }
328                         if (r == -1)
329                                 UnixMarshal.ThrowExceptionForLastError ();
330                 }
331
332                 public void SendTo (UnixStream output)
333                 {
334                         SendTo (output, (ulong) output.Length);
335                 }
336
337                 [CLSCompliant (false)]
338                 public void SendTo (UnixStream output, ulong count)
339                 {
340                         SendTo (output.Handle, count);
341                 }
342
343                 [CLSCompliant (false)]
344                 public void SendTo (int out_fd, ulong count)
345                 {
346                         if (!CanWrite)
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);
350                         if (r == -1)
351                                 UnixMarshal.ThrowExceptionForLastError ();
352                 }
353                 
354                 public void SetOwner (long user, long group)
355                 {
356                         AssertNotDisposed ();
357
358                         int r = Native.Syscall.fchown (fileDescriptor, 
359                                         Convert.ToUInt32 (user), Convert.ToUInt32 (group));
360                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
361                 }
362
363                 public void SetOwner (string user, string group)
364                 {
365                         AssertNotDisposed ();
366
367                         long uid = new UnixUserInfo (user).UserId;
368                         long gid = new UnixGroupInfo (group).GroupId;
369                         SetOwner (uid, gid);
370                 }
371
372                 public void SetOwner (string user)
373                 {
374                         AssertNotDisposed ();
375
376                         Native.Passwd pw = Native.Syscall.getpwnam (user);
377                         if (pw == null)
378                                 throw new ArgumentException (Locale.GetText ("invalid username"), "user");
379                         long uid = pw.pw_uid;
380                         long gid = pw.pw_gid;
381                         SetOwner (uid, gid);
382                 }
383
384                 [CLSCompliant (false)]
385                 public long GetConfigurationValue (Native.PathconfName name)
386                 {
387                         AssertNotDisposed ();
388                         long r = Native.Syscall.fpathconf (fileDescriptor, name);
389                         if (r == -1 && Native.Syscall.GetLastError() != (Native.Errno) 0)
390                                 UnixMarshal.ThrowExceptionForLastError ();
391                         return r;
392                 }
393
394                 ~UnixStream ()
395                 {
396                         Close ();
397                 }
398
399                 public override void Close ()
400                 {
401                         if (fileDescriptor == InvalidFileDescriptor)
402                                 return;
403                                 
404                         Flush ();
405
406                         if (!owner)
407                                 return;
408
409                         int r;
410                         do {
411                                 r = Native.Syscall.close (fileDescriptor);
412                         } while (UnixMarshal.ShouldRetrySyscall (r));
413                         UnixMarshal.ThrowExceptionForLastErrorIf (r);
414                         fileDescriptor = InvalidFileDescriptor;
415                         GC.SuppressFinalize (this);
416                 }
417                 
418                 void IDisposable.Dispose ()
419                 {
420                         AssertNotDisposed ();
421                         if (owner) {
422                                 Close ();
423                         }
424                         GC.SuppressFinalize (this);
425                 }
426
427                 private bool canSeek = false;
428                 private bool canRead = false;
429                 private bool canWrite = false;
430                 private bool owner = true;
431                 private int fileDescriptor = InvalidFileDescriptor;
432                 private Native.Stat stat;
433         }
434 }
435
436 // vim: noexpandtab