[LoongArch64] Part-5:add loongarch support in some files for LoongArch64. (#21769)
[mono-project.git] / mcs / class / referencesource / System.Core / System / IO / LogStream.cs
blob37b0623d37d0ce3c06c00c48b52ed7550ca856f2
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class: LogStream
9 **
10 ===========================================================*/
11 using System;
12 using Microsoft.Win32;
13 using Microsoft.Win32.SafeHandles;
14 using System.Security;
15 using System.Security.Permissions;
16 using System.Threading;
17 using System.Runtime.InteropServices;
18 using System.Runtime.Remoting.Messaging;
19 using System.Runtime.CompilerServices;
20 using System.Globalization;
21 using System.Runtime.Versioning;
23 using System.Diagnostics;
24 using System.Diagnostics.Contracts;
26 namespace System.IO {
28 // This stream has very limited support to enable EventSchemaTraceListener
29 // Eventually we might want to add more functionality and expose this type
30 internal class LogStream : BufferedStream2
32 internal const long DefaultFileSize = 10*1000*1024;
33 internal const int DefaultNumberOfFiles = 2;
34 internal const LogRetentionOption DefaultRetention = LogRetentionOption.SingleFileUnboundedSize;
36 // Retention policy
37 private const int _retentionRetryThreshold = 2;
38 private LogRetentionOption _retention;
39 private long _maxFileSize = DefaultFileSize;
40 private int _maxNumberOfFiles = DefaultNumberOfFiles;
41 private int _currentFileNum = 1;
42 bool _disableLogging;
43 int _retentionRetryCount;
45 private bool _canRead;
46 private bool _canWrite;
47 private bool _canSeek;
48 [SecurityCritical]
49 private SafeFileHandle _handle;
51 private String _fileName; // Fully qualified file name.
52 string _fileNameWithoutExt;
53 string _fileExt;
55 // Save input for retention
56 string _pathSav;
57 int _fAccessSav;
58 FileShare _shareSav;
59 UnsafeNativeMethods.SECURITY_ATTRIBUTES _secAttrsSav;
60 FileIOPermissionAccess _secAccessSav;
61 FileMode _modeSav;
62 int _flagsAndAttributesSav;
63 bool _seekToEndSav;
65 private readonly object m_lockObject = new Object();
67 //Limited to immediate internal need from EventSchemaTraceListener
68 //Not param validation done!!
69 [ResourceExposure(ResourceScope.Machine)]
70 [ResourceConsumption(ResourceScope.Machine)]
71 [System.Security.SecurityCritical]
72 internal LogStream(String path, int bufferSize, LogRetentionOption retention, long maxFileSize, int maxNumOfFiles)
74 Debug.Assert(!String.IsNullOrEmpty(path));
76 // Get absolute path - Security needs this to prevent something
77 // like trying to create a file in c:\tmp with the name
78 // "..\WinNT\System32\ntoskrnl.exe". Store it for user convenience.
79 //String filePath = Path.GetFullPathInternal(path);
80 String filePath = Path.GetFullPath(path);
81 _fileName = filePath;
83 // Prevent access to your disk drives as raw block devices.
84 if (filePath.StartsWith("\\\\.\\", StringComparison.Ordinal))
85 throw new NotSupportedException(SR.GetString(SR.NotSupported_IONonFileDevices));
87 UnsafeNativeMethods.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(FileShare.Read);
89 // For mitigating local elevation of privilege attack through named pipes
90 // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
91 // named pipe server can't impersonate a high privileged client security context
92 int flagsAndAttributes = (int)FileOptions.None | (UnsafeNativeMethods.SECURITY_SQOS_PRESENT | UnsafeNativeMethods.SECURITY_ANONYMOUS);
94 // Only write is enabled
95 //_canRead = false;
96 //_canSeek = false;
97 _canWrite = true;
99 _pathSav = filePath;
100 _fAccessSav = UnsafeNativeMethods.GENERIC_WRITE;
101 _shareSav = FileShare.Read;
102 _secAttrsSav = secAttrs;
103 _secAccessSav = FileIOPermissionAccess.Write;
104 _modeSav = (retention != LogRetentionOption.SingleFileUnboundedSize)? FileMode.Create : FileMode.OpenOrCreate;
105 _flagsAndAttributesSav = flagsAndAttributes;
106 _seekToEndSav = (retention != LogRetentionOption.SingleFileUnboundedSize)? false : true;
108 this.bufferSize = bufferSize;
109 _retention = retention;
110 _maxFileSize = maxFileSize;
111 _maxNumberOfFiles = maxNumOfFiles;
113 _Init(filePath, _fAccessSav, _shareSav, _secAttrsSav, _secAccessSav, _modeSav, _flagsAndAttributesSav, _seekToEndSav);
116 [System.Security.SecurityCritical]
117 internal void _Init(String path, int fAccess, FileShare share, UnsafeNativeMethods.SECURITY_ATTRIBUTES secAttrs, FileIOPermissionAccess secAccess,
118 FileMode mode, int flagsAndAttributes, bool seekToEnd)
120 String filePath = Path.GetFullPath(path);
121 _fileName = filePath;
123 new FileIOPermission(secAccess, new String[] { filePath }).Demand();
125 // Don't pop up a dialog for reading from an emtpy floppy drive
126 int oldMode = UnsafeNativeMethods.SetErrorMode(UnsafeNativeMethods.SEM_FAILCRITICALERRORS);
127 try {
128 _handle = UnsafeNativeMethods.SafeCreateFile(filePath, fAccess, share, secAttrs, mode, flagsAndAttributes, UnsafeNativeMethods.NULL);
129 int errorCode = Marshal.GetLastWin32Error();
131 if (_handle.IsInvalid) {
132 // Return a meaningful exception, using the RELATIVE path to
133 // the file to avoid returning extra information to the caller
134 // unless they have path discovery permission, in which case
135 // the full path is fine & useful.
137 // We need to give an exception, and preferably it would include
138 // the fully qualified path name. Do security check here. If
139 // we fail, give back the msgPath, which should not reveal much.
140 // While this logic is largely duplicated in
141 // __Error.WinIOError, we need this for
142 // IsolatedStorageLogFileStream.
143 bool canGiveFullPath = false;
145 try {
146 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }).Demand();
147 canGiveFullPath = true;
149 catch(SecurityException) {}
151 if (canGiveFullPath)
152 __Error.WinIOError(errorCode, _fileName);
153 else
154 __Error.WinIOError(errorCode, Path.GetFileName(_fileName));
157 finally {
158 UnsafeNativeMethods.SetErrorMode(oldMode);
160 Debug.Assert(UnsafeNativeMethods.GetFileType(_handle) == UnsafeNativeMethods.FILE_TYPE_DISK, "did someone accidentally removed the device type check from SafeCreateFile P/Invoke wrapper?");
162 pos = 0;
164 // For Append mode...
165 if (seekToEnd) {
166 SeekCore(0, SeekOrigin.End);
170 public override bool CanRead {
171 [Pure]
172 get { return _canRead; }
175 public override bool CanWrite {
176 [Pure]
177 get { return _canWrite; }
180 public override bool CanSeek {
181 [Pure]
182 get { return _canSeek; }
185 public override long Length {
186 get {
187 throw new NotSupportedException();
191 public override long Position {
192 get {
193 throw new NotSupportedException();
195 set {
196 throw new NotSupportedException();
200 public override void SetLength(long value)
202 throw new NotSupportedException();
205 public override long Seek(long offset, SeekOrigin origin)
207 throw new NotSupportedException();
210 public override int Read(byte[] array, int offset, int count)
212 throw new NotSupportedException();
215 [System.Security.SecurityCritical]
216 protected override unsafe void WriteCore(byte[] buffer, int offset, int count, bool blockForWrite, out long streamPos) {
217 Debug.Assert(CanWrite, "CanWrite");
218 Debug.Assert(buffer != null, "buffer != null");
219 Debug.Assert(offset >= 0, "offset is negative");
220 Debug.Assert(count >= 0, "count is negative");
222 int hr = 0;
223 int r = WriteFileNative(buffer, offset, count, null, out hr);
224 if (r == -1) {
225 // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
226 if (hr == UnsafeNativeMethods.ERROR_NO_DATA) {
227 r = 0;
229 else {
230 // ERROR_INVALID_PARAMETER may be returned for writes
231 // where the position is too large (ie, writing at Int64.MaxValue
232 // on Win9x) OR for synchronous writes to a handle opened
233 // asynchronously.
234 if (hr == UnsafeNativeMethods.ERROR_INVALID_PARAMETER)
235 throw new IOException(SR.GetString(SR.IO_FileTooLongOrHandleNotSync));
236 __Error.WinIOError(hr, String.Empty);
239 Debug.Assert(r >= 0, "WriteCore is likely broken.");
240 // update cached position
241 streamPos = AddUnderlyingStreamPosition((long)r);
242 EnforceRetentionPolicy(_handle, streamPos);
243 streamPos = pos;
244 return;
247 [System.Security.SecurityCritical]
248 unsafe private int WriteFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) {
249 if (_handle.IsClosed) __Error.FileNotOpen();
251 if (_disableLogging) {
252 hr = 0;
253 return 0;
256 Debug.Assert(offset >= 0, "offset >= 0");
257 Debug.Assert(count >= 0, "count >= 0");
258 Debug.Assert(bytes != null, "bytes != null");
260 // Don't corrupt memory when multiple threads are erroneously writing
261 // to this stream simultaneously. (the OS is reading from
262 // the array we pass to WriteFile, but if we read beyond the end and
263 // that memory isn't allocated, we could get an AV.)
264 if (bytes.Length - offset < count)
265 throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
267 // You can't use the fixed statement on an array of length 0.
268 if (bytes.Length==0) {
269 hr = 0;
270 return 0;
273 int numBytesWritten = 0;
274 int r = 0;
276 fixed(byte* p = bytes) {
277 r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, out numBytesWritten, overlapped);
280 if (r == 0) {
281 // We should never silently swallow an error here without some
282 // extra work. We must make sure that BeginWriteCore won't return an
283 // IAsyncResult that will cause EndWrite to block, since the OS won't
284 // call AsyncFSCallback for us.
285 hr = Marshal.GetLastWin32Error();
287 // For invalid handles, detect the error and mark our handle
288 // as closed to give slightly better error messages. Also
289 // help ensure we avoid handle recycling bugs.
290 if (hr == UnsafeNativeMethods.ERROR_INVALID_HANDLE)
291 _handle.SetHandleAsInvalid();
293 return -1;
295 else
296 hr = 0;
297 return numBytesWritten;
300 // This doesn't do argument checking. Necessary for SetLength, which must
301 // set the file pointer beyond the end of the file. This will update the
302 // internal position
303 [System.Security.SecurityCritical]
304 private long SeekCore(long offset, SeekOrigin origin)
306 Debug.Assert(!_handle.IsClosed, "!_handle.IsClosed");
307 Debug.Assert(origin>=SeekOrigin.Begin && origin<=SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End");
308 int hr = 0;
309 long ret = 0;
311 ret = UnsafeNativeMethods.SetFilePointer(_handle, offset, origin, out hr);
312 if (ret == -1) {
313 // For invalid handles, detect the error and mark our handle
314 // as closed to give slightly better error messages. Also
315 // help ensure we avoid handle recycling bugs.
316 if (hr == UnsafeNativeMethods.ERROR_INVALID_HANDLE)
317 _handle.SetHandleAsInvalid();
318 __Error.WinIOError(hr, String.Empty);
321 UnderlyingStreamPosition = ret;
322 return ret;
325 [System.Security.SecurityCritical]
326 protected override void Dispose(bool disposing)
328 // Nothing will be done differently based on whether we are
329 // disposing vs. finalizing. This is taking advantage of the
330 // weak ordering between normal finalizable objects & critical
331 // finalizable objects, which I included in the SafeHandle
332 // design for LogStream, which would often "just work" when
333 // finalized.
334 try {
335 if (_handle == null || _handle.IsClosed) {
336 // Make sure BufferedStream doesn't try to flush data on a closed handle
337 DiscardBuffer();
340 finally {
341 try {
342 // Cleanup base streams
343 base.Dispose(disposing);
345 finally {
346 if (_handle != null && !_handle.IsClosed)
347 _handle.Dispose();
349 _handle = null;
350 _canRead = false;
351 _canWrite = false;
352 _canSeek = false;
357 [System.Security.SecurityCritical]
358 ~LogStream()
360 if (_handle != null) {
361 Dispose(false);
365 [System.Security.SecurityCritical]
366 private void EnforceRetentionPolicy(SafeFileHandle handle, long lastPos)
368 switch (_retention) {
369 case LogRetentionOption.LimitedSequentialFiles:
370 case LogRetentionOption.UnlimitedSequentialFiles:
371 case LogRetentionOption.LimitedCircularFiles:
372 if ((lastPos >= _maxFileSize) && (handle == _handle)){
373 lock (m_lockObject) {
374 if ((handle != _handle) || (lastPos < _maxFileSize))
375 return;
377 _currentFileNum++;
378 if ((_retention == LogRetentionOption.LimitedCircularFiles) && (_currentFileNum > _maxNumberOfFiles)) {
379 _currentFileNum = 1;
381 else if ((_retention == LogRetentionOption.LimitedSequentialFiles) && (_currentFileNum > _maxNumberOfFiles)) {
382 _DisableLogging();
383 return;
386 if (_fileNameWithoutExt == null) {
387 _fileNameWithoutExt = Path.Combine(Path.GetDirectoryName(_pathSav), Path.GetFileNameWithoutExtension(_pathSav));
388 _fileExt = Path.GetExtension(_pathSav);
391 string path = (_currentFileNum == 1)?_pathSav: _fileNameWithoutExt + _currentFileNum.ToString(CultureInfo.InvariantCulture) + _fileExt;
392 try {
393 _Init(path, _fAccessSav, _shareSav, _secAttrsSav, _secAccessSav, _modeSav, _flagsAndAttributesSav, _seekToEndSav);
395 // Dispose the old handle and release the file write lock
396 // No need to flush the buffer as we just came off a write
397 if (handle != null && !handle.IsClosed) {
398 handle.Dispose();
401 catch (IOException ) {
402 // Should we do this only for ERROR_SHARING_VIOLATION?
403 //if (UnsafeNativeMethods.MakeErrorCodeFromHR(Marshal.GetHRForException(ioexc)) != InternalResources.ERROR_SHARING_VIOLATION) break;
405 // Possible sharing violation - ----? Let the next iteration try again
406 // For now revert the handle to the original one
407 _handle = handle;
409 _retentionRetryCount++;
410 if (_retentionRetryCount >= _retentionRetryThreshold) {
411 _DisableLogging();
413 #if DEBUG
414 throw;
415 #endif
417 catch (UnauthorizedAccessException ) {
418 // Indicative of ACL issues
419 _DisableLogging();
420 #if DEBUG
421 throw;
422 #endif
424 catch (Exception ) {
425 _DisableLogging();
426 #if DEBUG
427 throw;
428 #endif
432 break;
434 case LogRetentionOption.SingleFileBoundedSize:
435 if (lastPos >= _maxFileSize)
436 _DisableLogging();
437 break;
439 case LogRetentionOption.SingleFileUnboundedSize:
440 break;
444 // When we enable this class widely, we need to raise an
445 // event when we disable logging due to rention policy or
446 // error such as ACL that is preventing retention
447 [MethodImplAttribute(MethodImplOptions.Synchronized)]
448 private void _DisableLogging()
450 // Discard write buffer?
451 _disableLogging = true;
454 [System.Security.SecurityCritical]
455 private static UnsafeNativeMethods.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share)
457 UnsafeNativeMethods.SECURITY_ATTRIBUTES secAttrs = null;
458 if ((share & FileShare.Inheritable) != 0) {
459 secAttrs = new UnsafeNativeMethods.SECURITY_ATTRIBUTES();
460 secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
462 secAttrs.bInheritHandle = 1;
464 return secAttrs;