FileStream.cs 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. //
  2. // System.IO.FileStream.cs
  3. //
  4. // Authors:
  5. // Dietmar Maurer ([email protected])
  6. // Dan Lewis ([email protected])
  7. // Gonzalo Paniagua Javier ([email protected])
  8. // Marek Safar ([email protected])
  9. //
  10. // (C) 2001-2003 Ximian, Inc. http://www.ximian.com
  11. // Copyright (C) 2004-2005, 2008, 2010 Novell, Inc (http://www.novell.com)
  12. // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining
  15. // a copy of this software and associated documentation files (the
  16. // "Software"), to deal in the Software without restriction, including
  17. // without limitation the rights to use, copy, modify, merge, publish,
  18. // distribute, sublicense, and/or sell copies of the Software, and to
  19. // permit persons to whom the Software is furnished to do so, subject to
  20. // the following conditions:
  21. //
  22. // The above copyright notice and this permission notice shall be
  23. // included in all copies or substantial portions of the Software.
  24. //
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. //
  33. using System.Collections;
  34. using System.Globalization;
  35. using System.Runtime.CompilerServices;
  36. using System.Runtime.InteropServices;
  37. using System.Runtime.Remoting.Messaging;
  38. using System.Security;
  39. using System.Security.Permissions;
  40. using System.Threading;
  41. using Microsoft.Win32.SafeHandles;
  42. #if NET_2_1
  43. using System.IO.IsolatedStorage;
  44. #else
  45. using System.Security.AccessControl;
  46. #endif
  47. using System.Threading.Tasks;
  48. namespace System.IO
  49. {
  50. [ComVisible (true)]
  51. public class FileStream : Stream
  52. {
  53. // construct from handle
  54. [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
  55. public FileStream (IntPtr handle, FileAccess access)
  56. : this (handle, access, true, DefaultBufferSize, false, false) {}
  57. [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
  58. public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
  59. : this (handle, access, ownsHandle, DefaultBufferSize, false, false) {}
  60. [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
  61. public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
  62. : this (handle, access, ownsHandle, bufferSize, false, false) {}
  63. [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
  64. public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
  65. : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
  66. [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
  67. internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isConsoleWrapper)
  68. {
  69. if (handle == MonoIO.InvalidHandle)
  70. throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
  71. Init (new SafeFileHandle (handle, false), access, ownsHandle, bufferSize, isAsync, isConsoleWrapper);
  72. }
  73. // construct from filename
  74. public FileStream (string path, FileMode mode)
  75. : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
  76. {
  77. }
  78. public FileStream (string path, FileMode mode, FileAccess access)
  79. : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
  80. {
  81. }
  82. public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
  83. : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
  84. {
  85. }
  86. public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
  87. : this (path, mode, access, share, bufferSize, false, FileOptions.None)
  88. {
  89. }
  90. public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
  91. : this (path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
  92. {
  93. }
  94. public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
  95. : this (path, mode, access, share, bufferSize, false, options)
  96. {
  97. }
  98. public FileStream (SafeFileHandle handle, FileAccess access)
  99. :this(handle, access, DefaultBufferSize, false)
  100. {
  101. }
  102. public FileStream (SafeFileHandle handle, FileAccess access,
  103. int bufferSize)
  104. :this(handle, access, bufferSize, false)
  105. {
  106. }
  107. public FileStream (SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
  108. {
  109. Init (handle, access, false, bufferSize, isAsync, false);
  110. }
  111. #if !MOBILE
  112. [MonoLimitation ("This ignores the rights parameter")]
  113. public FileStream (string path, FileMode mode,
  114. FileSystemRights rights, FileShare share,
  115. int bufferSize, FileOptions options)
  116. : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
  117. {
  118. }
  119. [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
  120. public FileStream (string path, FileMode mode,
  121. FileSystemRights rights, FileShare share,
  122. int bufferSize, FileOptions options,
  123. FileSecurity fileSecurity)
  124. : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
  125. {
  126. }
  127. #endif
  128. internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, string msgPath, bool bFromProxy, bool useLongPath = false, bool checkHost = false)
  129. : this (path, mode, access, share, bufferSize, false, options)
  130. {
  131. }
  132. internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
  133. : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
  134. {
  135. }
  136. internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
  137. {
  138. if (path == null) {
  139. throw new ArgumentNullException ("path");
  140. }
  141. if (path.Length == 0) {
  142. throw new ArgumentException ("Path is empty");
  143. }
  144. this.anonymous = anonymous;
  145. // ignore the Inheritable flag
  146. share &= ~FileShare.Inheritable;
  147. if (bufferSize <= 0)
  148. throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
  149. if (mode < FileMode.CreateNew || mode > FileMode.Append) {
  150. #if NET_2_1
  151. if (anonymous)
  152. throw new ArgumentException ("mode", "Enum value was out of legal range.");
  153. else
  154. #endif
  155. throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
  156. }
  157. if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
  158. throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
  159. }
  160. if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
  161. throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
  162. }
  163. if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
  164. throw new ArgumentException ("Name has invalid chars");
  165. }
  166. if (Directory.Exists (path)) {
  167. // don't leak the path information for isolated storage
  168. string msg = Locale.GetText ("Access to the path '{0}' is denied.");
  169. throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false)));
  170. }
  171. /* Append streams can't be read (see FileMode
  172. * docs)
  173. */
  174. if (mode==FileMode.Append &&
  175. (access&FileAccess.Read)==FileAccess.Read) {
  176. throw new ArgumentException("Append access can be requested only in write-only mode.");
  177. }
  178. if ((access & FileAccess.Write) == 0 &&
  179. (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
  180. string msg = Locale.GetText ("Combining FileMode: {0} with " +
  181. "FileAccess: {1} is invalid.");
  182. throw new ArgumentException (string.Format (msg, access, mode));
  183. }
  184. SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
  185. string dname;
  186. if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
  187. dname = Path.GetDirectoryName (Path.GetFullPath (path));
  188. else
  189. dname = Path.GetDirectoryName (path);
  190. if (dname.Length > 0) {
  191. string fp = Path.GetFullPath (dname);
  192. if (!Directory.Exists (fp)) {
  193. // don't leak the path information for isolated storage
  194. string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
  195. string fname = (anonymous) ? dname : Path.GetFullPath (path);
  196. throw new DirectoryNotFoundException (String.Format (msg, fname));
  197. }
  198. }
  199. if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
  200. mode != FileMode.CreateNew && !File.Exists (path)) {
  201. // don't leak the path information for isolated storage
  202. string msg = Locale.GetText ("Could not find file \"{0}\".");
  203. string fname = GetSecureFileName (path);
  204. throw new FileNotFoundException (String.Format (msg, fname), fname);
  205. }
  206. // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
  207. if (!anonymous)
  208. this.name = path;
  209. // TODO: demand permissions
  210. MonoIOError error;
  211. var nativeHandle = MonoIO.Open (path, mode, access, share, options, out error);
  212. if (nativeHandle == MonoIO.InvalidHandle) {
  213. // don't leak the path information for isolated storage
  214. throw MonoIO.GetException (GetSecureFileName (path), error);
  215. }
  216. this.safeHandle = new SafeFileHandle (nativeHandle, false);
  217. this.access = access;
  218. this.owner = true;
  219. /* Can we open non-files by name? */
  220. if (MonoIO.GetFileType (safeHandle, out error) == MonoFileType.Disk) {
  221. this.canseek = true;
  222. this.async = (options & FileOptions.Asynchronous) != 0;
  223. } else {
  224. this.canseek = false;
  225. this.async = false;
  226. }
  227. if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
  228. /* Avoid allocating a large buffer for small files */
  229. long len = Length;
  230. if (bufferSize > len) {
  231. bufferSize = (int)(len < 1000 ? 1000 : len);
  232. }
  233. }
  234. InitBuffer (bufferSize, false);
  235. if (mode==FileMode.Append) {
  236. this.Seek (0, SeekOrigin.End);
  237. this.append_startpos=this.Position;
  238. } else {
  239. this.append_startpos=0;
  240. }
  241. }
  242. private void Init (SafeFileHandle safeHandle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isConsoleWrapper)
  243. {
  244. if (!isConsoleWrapper && safeHandle.IsInvalid)
  245. throw new ArgumentException(Environment.GetResourceString("Arg_InvalidHandle"), "handle");
  246. if (access < FileAccess.Read || access > FileAccess.ReadWrite)
  247. throw new ArgumentOutOfRangeException ("access");
  248. if (!isConsoleWrapper && bufferSize <= 0)
  249. throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
  250. MonoIOError error;
  251. MonoFileType ftype = MonoIO.GetFileType (safeHandle, out error);
  252. if (error != MonoIOError.ERROR_SUCCESS) {
  253. throw MonoIO.GetException (name, error);
  254. }
  255. if (ftype == MonoFileType.Unknown) {
  256. throw new IOException ("Invalid handle.");
  257. } else if (ftype == MonoFileType.Disk) {
  258. this.canseek = true;
  259. } else {
  260. this.canseek = false;
  261. }
  262. this.safeHandle = safeHandle;
  263. ExposeHandle ();
  264. this.access = access;
  265. this.owner = ownsHandle;
  266. this.async = isAsync;
  267. this.anonymous = false;
  268. if (canseek) {
  269. buf_start = MonoIO.Seek (safeHandle, 0, SeekOrigin.Current, out error);
  270. if (error != MonoIOError.ERROR_SUCCESS) {
  271. throw MonoIO.GetException (name, error);
  272. }
  273. }
  274. /* Can't set append mode */
  275. this.append_startpos=0;
  276. }
  277. // properties
  278. public override bool CanRead {
  279. get {
  280. return access == FileAccess.Read ||
  281. access == FileAccess.ReadWrite;
  282. }
  283. }
  284. public override bool CanWrite {
  285. get {
  286. return access == FileAccess.Write ||
  287. access == FileAccess.ReadWrite;
  288. }
  289. }
  290. public override bool CanSeek {
  291. get {
  292. return(canseek);
  293. }
  294. }
  295. public virtual bool IsAsync {
  296. get {
  297. return (async);
  298. }
  299. }
  300. public string Name {
  301. get {
  302. return name;
  303. }
  304. }
  305. public override long Length {
  306. get {
  307. if (safeHandle.IsClosed)
  308. throw new ObjectDisposedException ("Stream has been closed");
  309. if (!CanSeek)
  310. throw new NotSupportedException ("The stream does not support seeking");
  311. // Buffered data might change the length of the stream
  312. FlushBufferIfDirty ();
  313. MonoIOError error;
  314. long length = MonoIO.GetLength (safeHandle, out error);
  315. if (error != MonoIOError.ERROR_SUCCESS) {
  316. // don't leak the path information for isolated storage
  317. throw MonoIO.GetException (GetSecureFileName (name), error);
  318. }
  319. return(length);
  320. }
  321. }
  322. public override long Position {
  323. get {
  324. if (safeHandle.IsClosed)
  325. throw new ObjectDisposedException ("Stream has been closed");
  326. if (CanSeek == false)
  327. throw new NotSupportedException("The stream does not support seeking");
  328. if (!isExposed)
  329. return(buf_start + buf_offset);
  330. // If the handle was leaked outside we always ask the real handle
  331. MonoIOError error;
  332. long ret = MonoIO.Seek (safeHandle, 0, SeekOrigin.Current, out error);
  333. if (error != MonoIOError.ERROR_SUCCESS) {
  334. // don't leak the path information for isolated storage
  335. throw MonoIO.GetException (GetSecureFileName (name), error);
  336. }
  337. return ret;
  338. }
  339. set {
  340. if (value < 0) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  341. Seek (value, SeekOrigin.Begin);
  342. }
  343. }
  344. [Obsolete ("Use SafeFileHandle instead")]
  345. public virtual IntPtr Handle {
  346. [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
  347. [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
  348. get {
  349. var handle = safeHandle.DangerousGetHandle ();
  350. if (!isExposed)
  351. ExposeHandle ();
  352. return handle;
  353. }
  354. }
  355. public virtual SafeFileHandle SafeFileHandle {
  356. [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
  357. [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
  358. get {
  359. if (!isExposed)
  360. ExposeHandle ();
  361. return safeHandle;
  362. }
  363. }
  364. void ExposeHandle ()
  365. {
  366. isExposed = true;
  367. FlushBuffer ();
  368. InitBuffer (0, true);
  369. }
  370. // methods
  371. public override int ReadByte ()
  372. {
  373. if (safeHandle.IsClosed)
  374. throw new ObjectDisposedException ("Stream has been closed");
  375. if (!CanRead)
  376. throw new NotSupportedException ("Stream does not support reading");
  377. if (buf_size == 0) {
  378. int n = ReadData (safeHandle, buf, 0, 1);
  379. if (n == 0) return -1;
  380. else return buf[0];
  381. }
  382. else if (buf_offset >= buf_length) {
  383. RefillBuffer ();
  384. if (buf_length == 0)
  385. return -1;
  386. }
  387. return buf [buf_offset ++];
  388. }
  389. public override void WriteByte (byte value)
  390. {
  391. if (safeHandle.IsClosed)
  392. throw new ObjectDisposedException ("Stream has been closed");
  393. if (!CanWrite)
  394. throw new NotSupportedException ("Stream does not support writing");
  395. if (buf_offset == buf_size)
  396. FlushBuffer ();
  397. if (buf_size == 0) { // No buffering
  398. buf [0] = value;
  399. buf_dirty = true;
  400. buf_length = 1;
  401. FlushBuffer ();
  402. return;
  403. }
  404. buf [buf_offset ++] = value;
  405. if (buf_offset > buf_length)
  406. buf_length = buf_offset;
  407. buf_dirty = true;
  408. }
  409. public override int Read ([In,Out] byte[] array, int offset, int count)
  410. {
  411. if (safeHandle.IsClosed)
  412. throw new ObjectDisposedException ("Stream has been closed");
  413. if (array == null)
  414. throw new ArgumentNullException ("array");
  415. if (!CanRead)
  416. throw new NotSupportedException ("Stream does not support reading");
  417. int len = array.Length;
  418. if (offset < 0)
  419. throw new ArgumentOutOfRangeException ("offset", "< 0");
  420. if (count < 0)
  421. throw new ArgumentOutOfRangeException ("count", "< 0");
  422. if (offset > len)
  423. throw new ArgumentException ("destination offset is beyond array size");
  424. // reordered to avoid possible integer overflow
  425. if (offset > len - count)
  426. throw new ArgumentException ("Reading would overrun buffer");
  427. if (async) {
  428. IAsyncResult ares = BeginRead (array, offset, count, null, null);
  429. return EndRead (ares);
  430. }
  431. return ReadInternal (array, offset, count);
  432. }
  433. int ReadInternal (byte [] dest, int offset, int count)
  434. {
  435. int n = ReadSegment (dest, offset, count);
  436. if (n == count) {
  437. return count;
  438. }
  439. int copied = n;
  440. count -= n;
  441. if (count > buf_size) {
  442. /* Read as much as we can, up
  443. * to count bytes
  444. */
  445. FlushBuffer();
  446. n = ReadData (safeHandle, dest, offset+n, count);
  447. /* Make the next buffer read
  448. * start from the right place
  449. */
  450. buf_start += n;
  451. } else {
  452. RefillBuffer ();
  453. n = ReadSegment (dest, offset+copied, count);
  454. }
  455. return copied + n;
  456. }
  457. delegate int ReadDelegate (byte [] buffer, int offset, int count);
  458. public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
  459. AsyncCallback userCallback, object stateObject)
  460. {
  461. if (safeHandle.IsClosed)
  462. throw new ObjectDisposedException ("Stream has been closed");
  463. if (!CanRead)
  464. throw new NotSupportedException ("This stream does not support reading");
  465. if (array == null)
  466. throw new ArgumentNullException ("array");
  467. if (numBytes < 0)
  468. throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
  469. if (offset < 0)
  470. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  471. // reordered to avoid possible integer overflow
  472. if (numBytes > array.Length - offset)
  473. throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
  474. if (!async)
  475. return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
  476. ReadDelegate r = new ReadDelegate (ReadInternal);
  477. return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
  478. }
  479. public override int EndRead (IAsyncResult asyncResult)
  480. {
  481. if (asyncResult == null)
  482. throw new ArgumentNullException ("asyncResult");
  483. if (!async)
  484. return base.EndRead (asyncResult);
  485. AsyncResult ares = asyncResult as AsyncResult;
  486. if (ares == null)
  487. throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
  488. ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
  489. if (r == null)
  490. throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
  491. return r.EndInvoke (asyncResult);
  492. }
  493. public override void Write (byte[] array, int offset, int count)
  494. {
  495. if (safeHandle.IsClosed)
  496. throw new ObjectDisposedException ("Stream has been closed");
  497. if (array == null)
  498. throw new ArgumentNullException ("array");
  499. if (offset < 0)
  500. throw new ArgumentOutOfRangeException ("offset", "< 0");
  501. if (count < 0)
  502. throw new ArgumentOutOfRangeException ("count", "< 0");
  503. // ordered to avoid possible integer overflow
  504. if (offset > array.Length - count)
  505. throw new ArgumentException ("Reading would overrun buffer");
  506. if (!CanWrite)
  507. throw new NotSupportedException ("Stream does not support writing");
  508. if (async) {
  509. IAsyncResult ares = BeginWrite (array, offset, count, null, null);
  510. EndWrite (ares);
  511. return;
  512. }
  513. WriteInternal (array, offset, count);
  514. }
  515. void WriteInternal (byte [] src, int offset, int count)
  516. {
  517. if (count > buf_size) {
  518. // shortcut for long writes
  519. MonoIOError error;
  520. FlushBuffer ();
  521. if (CanSeek && !isExposed) {
  522. MonoIO.Seek (safeHandle, buf_start, SeekOrigin.Begin, out error);
  523. if (error != MonoIOError.ERROR_SUCCESS)
  524. throw MonoIO.GetException (GetSecureFileName (name), error);
  525. }
  526. int wcount = count;
  527. while (wcount > 0){
  528. int n = MonoIO.Write (safeHandle, src, offset, wcount, out error);
  529. if (error != MonoIOError.ERROR_SUCCESS)
  530. throw MonoIO.GetException (GetSecureFileName (name), error);
  531. wcount -= n;
  532. offset += n;
  533. }
  534. buf_start += count;
  535. } else {
  536. int copied = 0;
  537. while (count > 0) {
  538. int n = WriteSegment (src, offset + copied, count);
  539. copied += n;
  540. count -= n;
  541. if (count == 0) {
  542. break;
  543. }
  544. FlushBuffer ();
  545. }
  546. }
  547. }
  548. delegate void WriteDelegate (byte [] buffer, int offset, int count);
  549. public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
  550. AsyncCallback userCallback, object stateObject)
  551. {
  552. if (safeHandle.IsClosed)
  553. throw new ObjectDisposedException ("Stream has been closed");
  554. if (!CanWrite)
  555. throw new NotSupportedException ("This stream does not support writing");
  556. if (array == null)
  557. throw new ArgumentNullException ("array");
  558. if (numBytes < 0)
  559. throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
  560. if (offset < 0)
  561. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  562. // reordered to avoid possible integer overflow
  563. if (numBytes > array.Length - offset)
  564. throw new ArgumentException ("array too small. numBytes/offset wrong.");
  565. if (!async)
  566. return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
  567. FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
  568. result.BytesRead = -1;
  569. result.Count = numBytes;
  570. result.OriginalCount = numBytes;
  571. /*
  572. if (buf_dirty) {
  573. MemoryStream ms = new MemoryStream ();
  574. FlushBuffer (ms);
  575. ms.Write (array, offset, numBytes);
  576. // Set arguments to new compounded buffer
  577. offset = 0;
  578. array = ms.ToArray ();
  579. numBytes = array.Length;
  580. }
  581. */
  582. WriteDelegate w = WriteInternal;
  583. return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
  584. }
  585. public override void EndWrite (IAsyncResult asyncResult)
  586. {
  587. if (asyncResult == null)
  588. throw new ArgumentNullException ("asyncResult");
  589. if (!async) {
  590. base.EndWrite (asyncResult);
  591. return;
  592. }
  593. AsyncResult ares = asyncResult as AsyncResult;
  594. if (ares == null)
  595. throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
  596. WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
  597. if (w == null)
  598. throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
  599. w.EndInvoke (asyncResult);
  600. return;
  601. }
  602. public override long Seek (long offset, SeekOrigin origin)
  603. {
  604. long pos;
  605. if (safeHandle.IsClosed)
  606. throw new ObjectDisposedException ("Stream has been closed");
  607. // make absolute
  608. if(CanSeek == false) {
  609. throw new NotSupportedException("The stream does not support seeking");
  610. }
  611. switch (origin) {
  612. case SeekOrigin.End:
  613. pos = Length + offset;
  614. break;
  615. case SeekOrigin.Current:
  616. pos = Position + offset;
  617. break;
  618. case SeekOrigin.Begin:
  619. pos = offset;
  620. break;
  621. default:
  622. throw new ArgumentException ("origin", "Invalid SeekOrigin");
  623. }
  624. if (pos < 0) {
  625. /* LAMESPEC: shouldn't this be
  626. * ArgumentOutOfRangeException?
  627. */
  628. throw new IOException("Attempted to Seek before the beginning of the stream");
  629. }
  630. if(pos < this.append_startpos) {
  631. /* More undocumented crap */
  632. throw new IOException("Can't seek back over pre-existing data in append mode");
  633. }
  634. FlushBuffer ();
  635. MonoIOError error;
  636. buf_start = MonoIO.Seek (safeHandle, pos, SeekOrigin.Begin, out error);
  637. if (error != MonoIOError.ERROR_SUCCESS) {
  638. // don't leak the path information for isolated storage
  639. throw MonoIO.GetException (GetSecureFileName (name), error);
  640. }
  641. return(buf_start);
  642. }
  643. public override void SetLength (long value)
  644. {
  645. if (safeHandle.IsClosed)
  646. throw new ObjectDisposedException ("Stream has been closed");
  647. if(CanSeek == false)
  648. throw new NotSupportedException("The stream does not support seeking");
  649. if(CanWrite == false)
  650. throw new NotSupportedException("The stream does not support writing");
  651. if(value < 0)
  652. throw new ArgumentOutOfRangeException("value is less than 0");
  653. FlushBuffer ();
  654. MonoIOError error;
  655. MonoIO.SetLength (safeHandle, value, out error);
  656. if (error != MonoIOError.ERROR_SUCCESS) {
  657. // don't leak the path information for isolated storage
  658. throw MonoIO.GetException (GetSecureFileName (name), error);
  659. }
  660. if (Position > value)
  661. Position = value;
  662. }
  663. public override void Flush ()
  664. {
  665. if (safeHandle.IsClosed)
  666. throw new ObjectDisposedException ("Stream has been closed");
  667. FlushBuffer ();
  668. }
  669. public virtual void Flush (bool flushToDisk)
  670. {
  671. if (safeHandle.IsClosed)
  672. throw new ObjectDisposedException ("Stream has been closed");
  673. FlushBuffer ();
  674. // This does the fsync
  675. if (flushToDisk){
  676. MonoIOError error;
  677. MonoIO.Flush (safeHandle, out error);
  678. }
  679. }
  680. public virtual void Lock (long position, long length)
  681. {
  682. if (safeHandle.IsClosed)
  683. throw new ObjectDisposedException ("Stream has been closed");
  684. if (position < 0) {
  685. throw new ArgumentOutOfRangeException ("position must not be negative");
  686. }
  687. if (length < 0) {
  688. throw new ArgumentOutOfRangeException ("length must not be negative");
  689. }
  690. MonoIOError error;
  691. MonoIO.Lock (safeHandle, position, length, out error);
  692. if (error != MonoIOError.ERROR_SUCCESS) {
  693. // don't leak the path information for isolated storage
  694. throw MonoIO.GetException (GetSecureFileName (name), error);
  695. }
  696. }
  697. public virtual void Unlock (long position, long length)
  698. {
  699. if (safeHandle.IsClosed)
  700. throw new ObjectDisposedException ("Stream has been closed");
  701. if (position < 0) {
  702. throw new ArgumentOutOfRangeException ("position must not be negative");
  703. }
  704. if (length < 0) {
  705. throw new ArgumentOutOfRangeException ("length must not be negative");
  706. }
  707. MonoIOError error;
  708. MonoIO.Unlock (safeHandle, position, length, out error);
  709. if (error != MonoIOError.ERROR_SUCCESS) {
  710. // don't leak the path information for isolated storage
  711. throw MonoIO.GetException (GetSecureFileName (name), error);
  712. }
  713. }
  714. // protected
  715. ~FileStream ()
  716. {
  717. Dispose (false);
  718. }
  719. protected override void Dispose (bool disposing)
  720. {
  721. Exception exc = null;
  722. if (safeHandle != null && !safeHandle.IsClosed) {
  723. try {
  724. // If the FileStream is in "exposed" status
  725. // it means that we do not have a buffer(we write the data without buffering)
  726. // therefor we don't and can't flush the buffer becouse we don't have one.
  727. FlushBuffer ();
  728. } catch (Exception e) {
  729. exc = e;
  730. }
  731. if (owner) {
  732. MonoIOError error;
  733. MonoIO.Close (safeHandle.DangerousGetHandle (), out error);
  734. if (error != MonoIOError.ERROR_SUCCESS) {
  735. // don't leak the path information for isolated storage
  736. throw MonoIO.GetException (GetSecureFileName (name), error);
  737. }
  738. safeHandle.DangerousRelease ();
  739. }
  740. }
  741. canseek = false;
  742. access = 0;
  743. if (disposing && buf != null) {
  744. if (buf.Length == DefaultBufferSize && buf_recycle == null) {
  745. lock (buf_recycle_lock) {
  746. if (buf_recycle == null) {
  747. buf_recycle = buf;
  748. }
  749. }
  750. }
  751. buf = null;
  752. GC.SuppressFinalize (this);
  753. }
  754. if (exc != null)
  755. throw exc;
  756. }
  757. #if !NET_2_1
  758. public FileSecurity GetAccessControl ()
  759. {
  760. if (safeHandle.IsClosed)
  761. throw new ObjectDisposedException ("Stream has been closed");
  762. return new FileSecurity (SafeFileHandle,
  763. AccessControlSections.Owner |
  764. AccessControlSections.Group |
  765. AccessControlSections.Access);
  766. }
  767. public void SetAccessControl (FileSecurity fileSecurity)
  768. {
  769. if (safeHandle.IsClosed)
  770. throw new ObjectDisposedException ("Stream has been closed");
  771. if (null == fileSecurity)
  772. throw new ArgumentNullException ("fileSecurity");
  773. fileSecurity.PersistModifications (SafeFileHandle);
  774. }
  775. #endif
  776. public override Task FlushAsync (CancellationToken cancellationToken)
  777. {
  778. if (safeHandle.IsClosed)
  779. throw new ObjectDisposedException ("Stream has been closed");
  780. return base.FlushAsync (cancellationToken);
  781. }
  782. public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  783. {
  784. return base.ReadAsync (buffer, offset, count, cancellationToken);
  785. }
  786. public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  787. {
  788. return base.WriteAsync (buffer, offset, count, cancellationToken);
  789. }
  790. // private.
  791. // ReadSegment, WriteSegment, FlushBuffer,
  792. // RefillBuffer and ReadData should only be called
  793. // when the Monitor lock is held, but these methods
  794. // grab it again just to be safe.
  795. private int ReadSegment (byte [] dest, int dest_offset, int count)
  796. {
  797. count = Math.Min (count, buf_length - buf_offset);
  798. if (count > 0) {
  799. // Use the fastest method, all range checks has been done
  800. Buffer.InternalBlockCopy (buf, buf_offset, dest, dest_offset, count);
  801. buf_offset += count;
  802. }
  803. return count;
  804. }
  805. private int WriteSegment (byte [] src, int src_offset,
  806. int count)
  807. {
  808. if (count > buf_size - buf_offset) {
  809. count = buf_size - buf_offset;
  810. }
  811. if (count > 0) {
  812. Buffer.BlockCopy (src, src_offset,
  813. buf, buf_offset,
  814. count);
  815. buf_offset += count;
  816. if (buf_offset > buf_length) {
  817. buf_length = buf_offset;
  818. }
  819. buf_dirty = true;
  820. }
  821. return(count);
  822. }
  823. void FlushBuffer ()
  824. {
  825. if (buf_dirty) {
  826. // if (st == null) {
  827. MonoIOError error;
  828. if (CanSeek == true && !isExposed) {
  829. MonoIO.Seek (safeHandle, buf_start, SeekOrigin.Begin, out error);
  830. if (error != MonoIOError.ERROR_SUCCESS) {
  831. // don't leak the path information for isolated storage
  832. throw MonoIO.GetException (GetSecureFileName (name), error);
  833. }
  834. }
  835. int wcount = buf_length;
  836. int offset = 0;
  837. while (wcount > 0){
  838. int n = MonoIO.Write (safeHandle, buf, offset, buf_length, out error);
  839. if (error != MonoIOError.ERROR_SUCCESS) {
  840. // don't leak the path information for isolated storage
  841. throw MonoIO.GetException (GetSecureFileName (name), error);
  842. }
  843. wcount -= n;
  844. offset += n;
  845. }
  846. // } else {
  847. // st.Write (buf, 0, buf_length);
  848. // }
  849. }
  850. buf_start += buf_offset;
  851. buf_offset = buf_length = 0;
  852. buf_dirty = false;
  853. }
  854. private void FlushBufferIfDirty ()
  855. {
  856. if (buf_dirty)
  857. FlushBuffer ();
  858. }
  859. private void RefillBuffer ()
  860. {
  861. FlushBuffer ();
  862. buf_length = ReadData (safeHandle, buf, 0, buf_size);
  863. }
  864. private int ReadData (SafeHandle safeHandle, byte[] buf, int offset,
  865. int count)
  866. {
  867. MonoIOError error;
  868. int amount = 0;
  869. /* when async == true, if we get here we don't suport AIO or it's disabled
  870. * and we're using the threadpool */
  871. amount = MonoIO.Read (safeHandle, buf, offset, count, out error);
  872. if (error == MonoIOError.ERROR_BROKEN_PIPE) {
  873. amount = 0; // might not be needed, but well...
  874. } else if (error != MonoIOError.ERROR_SUCCESS) {
  875. // don't leak the path information for isolated storage
  876. throw MonoIO.GetException (GetSecureFileName (name), error);
  877. }
  878. /* Check for read error */
  879. if(amount == -1) {
  880. throw new IOException ();
  881. }
  882. return(amount);
  883. }
  884. void InitBuffer (int size, bool isZeroSize)
  885. {
  886. if (isZeroSize) {
  887. size = 0;
  888. buf = new byte[1];
  889. } else {
  890. if (size <= 0)
  891. throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
  892. size = Math.Max (size, 8);
  893. //
  894. // Instead of allocating a new default buffer use the
  895. // last one if there is any available
  896. //
  897. if (size <= DefaultBufferSize && buf_recycle != null) {
  898. lock (buf_recycle_lock) {
  899. if (buf_recycle != null) {
  900. buf = buf_recycle;
  901. buf_recycle = null;
  902. }
  903. }
  904. }
  905. if (buf == null)
  906. buf = new byte [size];
  907. else
  908. Array.Clear (buf, 0, size);
  909. }
  910. buf_size = size;
  911. // buf_start = 0;
  912. // buf_offset = buf_length = 0;
  913. // buf_dirty = false;
  914. }
  915. private string GetSecureFileName (string filename)
  916. {
  917. return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
  918. }
  919. private string GetSecureFileName (string filename, bool full)
  920. {
  921. return (anonymous) ? Path.GetFileName (filename) :
  922. (full) ? Path.GetFullPath (filename) : filename;
  923. }
  924. // fields
  925. internal const int DefaultBufferSize = 4096;
  926. // Input buffer ready for recycling
  927. static byte[] buf_recycle;
  928. static readonly object buf_recycle_lock = new object ();
  929. private byte [] buf; // the buffer
  930. private string name = "[Unknown]"; // name of file.
  931. private SafeFileHandle safeHandle;
  932. private bool isExposed;
  933. private long append_startpos;
  934. private FileAccess access;
  935. private bool owner;
  936. private bool async;
  937. private bool canseek;
  938. private bool anonymous;
  939. private bool buf_dirty; // true if buffer has been written to
  940. private int buf_size; // capacity in bytes
  941. private int buf_length; // number of valid bytes in buffer
  942. private int buf_offset; // position of next byte
  943. private long buf_start; // location of buffer in file
  944. }
  945. }