FileStream.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  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. #if !NET_2_1
  99. public FileStream (SafeFileHandle handle, FileAccess access)
  100. :this(handle, access, DefaultBufferSize, false)
  101. {
  102. }
  103. public FileStream (SafeFileHandle handle, FileAccess access,
  104. int bufferSize)
  105. :this(handle, access, bufferSize, false)
  106. {
  107. }
  108. public FileStream (SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
  109. {
  110. Init (handle, access, false, bufferSize, isAsync, false);
  111. }
  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. int wcount = count;
  522. while (wcount > 0){
  523. int n = MonoIO.Write (safeHandle, src, offset, wcount, out error);
  524. if (error != MonoIOError.ERROR_SUCCESS)
  525. throw MonoIO.GetException (GetSecureFileName (name), error);
  526. wcount -= n;
  527. offset += n;
  528. }
  529. buf_start += count;
  530. } else {
  531. int copied = 0;
  532. while (count > 0) {
  533. int n = WriteSegment (src, offset + copied, count);
  534. copied += n;
  535. count -= n;
  536. if (count == 0) {
  537. break;
  538. }
  539. FlushBuffer ();
  540. }
  541. }
  542. }
  543. delegate void WriteDelegate (byte [] buffer, int offset, int count);
  544. public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
  545. AsyncCallback userCallback, object stateObject)
  546. {
  547. if (safeHandle.IsClosed)
  548. throw new ObjectDisposedException ("Stream has been closed");
  549. if (!CanWrite)
  550. throw new NotSupportedException ("This stream does not support writing");
  551. if (array == null)
  552. throw new ArgumentNullException ("array");
  553. if (numBytes < 0)
  554. throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
  555. if (offset < 0)
  556. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  557. // reordered to avoid possible integer overflow
  558. if (numBytes > array.Length - offset)
  559. throw new ArgumentException ("array too small. numBytes/offset wrong.");
  560. if (!async)
  561. return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
  562. FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
  563. result.BytesRead = -1;
  564. result.Count = numBytes;
  565. result.OriginalCount = numBytes;
  566. /*
  567. if (buf_dirty) {
  568. MemoryStream ms = new MemoryStream ();
  569. FlushBuffer (ms);
  570. ms.Write (array, offset, numBytes);
  571. // Set arguments to new compounded buffer
  572. offset = 0;
  573. array = ms.ToArray ();
  574. numBytes = array.Length;
  575. }
  576. */
  577. WriteDelegate w = WriteInternal;
  578. return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
  579. }
  580. public override void EndWrite (IAsyncResult asyncResult)
  581. {
  582. if (asyncResult == null)
  583. throw new ArgumentNullException ("asyncResult");
  584. if (!async) {
  585. base.EndWrite (asyncResult);
  586. return;
  587. }
  588. AsyncResult ares = asyncResult as AsyncResult;
  589. if (ares == null)
  590. throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
  591. WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
  592. if (w == null)
  593. throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
  594. w.EndInvoke (asyncResult);
  595. return;
  596. }
  597. public override long Seek (long offset, SeekOrigin origin)
  598. {
  599. long pos;
  600. if (safeHandle.IsClosed)
  601. throw new ObjectDisposedException ("Stream has been closed");
  602. // make absolute
  603. if(CanSeek == false) {
  604. throw new NotSupportedException("The stream does not support seeking");
  605. }
  606. switch (origin) {
  607. case SeekOrigin.End:
  608. pos = Length + offset;
  609. break;
  610. case SeekOrigin.Current:
  611. pos = Position + offset;
  612. break;
  613. case SeekOrigin.Begin:
  614. pos = offset;
  615. break;
  616. default:
  617. throw new ArgumentException ("origin", "Invalid SeekOrigin");
  618. }
  619. if (pos < 0) {
  620. /* LAMESPEC: shouldn't this be
  621. * ArgumentOutOfRangeException?
  622. */
  623. throw new IOException("Attempted to Seek before the beginning of the stream");
  624. }
  625. if(pos < this.append_startpos) {
  626. /* More undocumented crap */
  627. throw new IOException("Can't seek back over pre-existing data in append mode");
  628. }
  629. FlushBuffer ();
  630. MonoIOError error;
  631. buf_start = MonoIO.Seek (safeHandle, pos, SeekOrigin.Begin, out error);
  632. if (error != MonoIOError.ERROR_SUCCESS) {
  633. // don't leak the path information for isolated storage
  634. throw MonoIO.GetException (GetSecureFileName (name), error);
  635. }
  636. return(buf_start);
  637. }
  638. public override void SetLength (long value)
  639. {
  640. if (safeHandle.IsClosed)
  641. throw new ObjectDisposedException ("Stream has been closed");
  642. if(CanSeek == false)
  643. throw new NotSupportedException("The stream does not support seeking");
  644. if(CanWrite == false)
  645. throw new NotSupportedException("The stream does not support writing");
  646. if(value < 0)
  647. throw new ArgumentOutOfRangeException("value is less than 0");
  648. FlushBuffer ();
  649. MonoIOError error;
  650. MonoIO.SetLength (safeHandle, value, out error);
  651. if (error != MonoIOError.ERROR_SUCCESS) {
  652. // don't leak the path information for isolated storage
  653. throw MonoIO.GetException (GetSecureFileName (name), error);
  654. }
  655. if (Position > value)
  656. Position = value;
  657. }
  658. public override void Flush ()
  659. {
  660. if (safeHandle.IsClosed)
  661. throw new ObjectDisposedException ("Stream has been closed");
  662. FlushBuffer ();
  663. }
  664. public virtual void Flush (bool flushToDisk)
  665. {
  666. if (safeHandle.IsClosed)
  667. throw new ObjectDisposedException ("Stream has been closed");
  668. FlushBuffer ();
  669. // This does the fsync
  670. if (flushToDisk){
  671. MonoIOError error;
  672. MonoIO.Flush (safeHandle, out error);
  673. }
  674. }
  675. public virtual void Lock (long position, long length)
  676. {
  677. if (safeHandle.IsClosed)
  678. throw new ObjectDisposedException ("Stream has been closed");
  679. if (position < 0) {
  680. throw new ArgumentOutOfRangeException ("position must not be negative");
  681. }
  682. if (length < 0) {
  683. throw new ArgumentOutOfRangeException ("length must not be negative");
  684. }
  685. MonoIOError error;
  686. MonoIO.Lock (safeHandle, position, length, out error);
  687. if (error != MonoIOError.ERROR_SUCCESS) {
  688. // don't leak the path information for isolated storage
  689. throw MonoIO.GetException (GetSecureFileName (name), error);
  690. }
  691. }
  692. public virtual void Unlock (long position, long length)
  693. {
  694. if (safeHandle.IsClosed)
  695. throw new ObjectDisposedException ("Stream has been closed");
  696. if (position < 0) {
  697. throw new ArgumentOutOfRangeException ("position must not be negative");
  698. }
  699. if (length < 0) {
  700. throw new ArgumentOutOfRangeException ("length must not be negative");
  701. }
  702. MonoIOError error;
  703. MonoIO.Unlock (safeHandle, position, length, out error);
  704. if (error != MonoIOError.ERROR_SUCCESS) {
  705. // don't leak the path information for isolated storage
  706. throw MonoIO.GetException (GetSecureFileName (name), error);
  707. }
  708. }
  709. // protected
  710. ~FileStream ()
  711. {
  712. Dispose (false);
  713. }
  714. protected override void Dispose (bool disposing)
  715. {
  716. Exception exc = null;
  717. if (safeHandle != null && !safeHandle.IsClosed) {
  718. try {
  719. // If the FileStream is in "exposed" status
  720. // it means that we do not have a buffer(we write the data without buffering)
  721. // therefor we don't and can't flush the buffer becouse we don't have one.
  722. FlushBuffer ();
  723. } catch (Exception e) {
  724. exc = e;
  725. }
  726. if (owner) {
  727. MonoIOError error;
  728. MonoIO.Close (safeHandle.DangerousGetHandle (), out error);
  729. if (error != MonoIOError.ERROR_SUCCESS) {
  730. // don't leak the path information for isolated storage
  731. throw MonoIO.GetException (GetSecureFileName (name), error);
  732. }
  733. safeHandle.DangerousRelease ();
  734. }
  735. }
  736. canseek = false;
  737. access = 0;
  738. if (disposing && buf != null) {
  739. if (buf.Length == DefaultBufferSize && buf_recycle == null) {
  740. lock (buf_recycle_lock) {
  741. if (buf_recycle == null) {
  742. buf_recycle = buf;
  743. }
  744. }
  745. }
  746. buf = null;
  747. GC.SuppressFinalize (this);
  748. }
  749. if (exc != null)
  750. throw exc;
  751. }
  752. #if !NET_2_1
  753. public FileSecurity GetAccessControl ()
  754. {
  755. if (safeHandle.IsClosed)
  756. throw new ObjectDisposedException ("Stream has been closed");
  757. return new FileSecurity (SafeFileHandle,
  758. AccessControlSections.Owner |
  759. AccessControlSections.Group |
  760. AccessControlSections.Access);
  761. }
  762. public void SetAccessControl (FileSecurity fileSecurity)
  763. {
  764. if (safeHandle.IsClosed)
  765. throw new ObjectDisposedException ("Stream has been closed");
  766. if (null == fileSecurity)
  767. throw new ArgumentNullException ("fileSecurity");
  768. fileSecurity.PersistModifications (SafeFileHandle);
  769. }
  770. #endif
  771. public override Task FlushAsync (CancellationToken cancellationToken)
  772. {
  773. if (safeHandle.IsClosed)
  774. throw new ObjectDisposedException ("Stream has been closed");
  775. return base.FlushAsync (cancellationToken);
  776. }
  777. public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  778. {
  779. return base.ReadAsync (buffer, offset, count, cancellationToken);
  780. }
  781. public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  782. {
  783. return base.WriteAsync (buffer, offset, count, cancellationToken);
  784. }
  785. // private.
  786. // ReadSegment, WriteSegment, FlushBuffer,
  787. // RefillBuffer and ReadData should only be called
  788. // when the Monitor lock is held, but these methods
  789. // grab it again just to be safe.
  790. private int ReadSegment (byte [] dest, int dest_offset, int count)
  791. {
  792. count = Math.Min (count, buf_length - buf_offset);
  793. if (count > 0) {
  794. // Use the fastest method, all range checks has been done
  795. Buffer.InternalBlockCopy (buf, buf_offset, dest, dest_offset, count);
  796. buf_offset += count;
  797. }
  798. return count;
  799. }
  800. private int WriteSegment (byte [] src, int src_offset,
  801. int count)
  802. {
  803. if (count > buf_size - buf_offset) {
  804. count = buf_size - buf_offset;
  805. }
  806. if (count > 0) {
  807. Buffer.BlockCopy (src, src_offset,
  808. buf, buf_offset,
  809. count);
  810. buf_offset += count;
  811. if (buf_offset > buf_length) {
  812. buf_length = buf_offset;
  813. }
  814. buf_dirty = true;
  815. }
  816. return(count);
  817. }
  818. void FlushBuffer ()
  819. {
  820. if (buf_dirty) {
  821. // if (st == null) {
  822. MonoIOError error;
  823. if (CanSeek == true && !isExposed) {
  824. MonoIO.Seek (safeHandle, buf_start, SeekOrigin.Begin, out error);
  825. if (error != MonoIOError.ERROR_SUCCESS) {
  826. // don't leak the path information for isolated storage
  827. throw MonoIO.GetException (GetSecureFileName (name), error);
  828. }
  829. }
  830. int wcount = buf_length;
  831. int offset = 0;
  832. while (wcount > 0){
  833. int n = MonoIO.Write (safeHandle, buf, 0, buf_length, out error);
  834. if (error != MonoIOError.ERROR_SUCCESS) {
  835. // don't leak the path information for isolated storage
  836. throw MonoIO.GetException (GetSecureFileName (name), error);
  837. }
  838. wcount -= n;
  839. offset += n;
  840. }
  841. // } else {
  842. // st.Write (buf, 0, buf_length);
  843. // }
  844. }
  845. buf_start += buf_offset;
  846. buf_offset = buf_length = 0;
  847. buf_dirty = false;
  848. }
  849. private void FlushBufferIfDirty ()
  850. {
  851. if (buf_dirty)
  852. FlushBuffer ();
  853. }
  854. private void RefillBuffer ()
  855. {
  856. FlushBuffer ();
  857. buf_length = ReadData (safeHandle, buf, 0, buf_size);
  858. }
  859. private int ReadData (SafeHandle safeHandle, byte[] buf, int offset,
  860. int count)
  861. {
  862. MonoIOError error;
  863. int amount = 0;
  864. /* when async == true, if we get here we don't suport AIO or it's disabled
  865. * and we're using the threadpool */
  866. amount = MonoIO.Read (safeHandle, buf, offset, count, out error);
  867. if (error == MonoIOError.ERROR_BROKEN_PIPE) {
  868. amount = 0; // might not be needed, but well...
  869. } else if (error != MonoIOError.ERROR_SUCCESS) {
  870. // don't leak the path information for isolated storage
  871. throw MonoIO.GetException (GetSecureFileName (name), error);
  872. }
  873. /* Check for read error */
  874. if(amount == -1) {
  875. throw new IOException ();
  876. }
  877. return(amount);
  878. }
  879. void InitBuffer (int size, bool isZeroSize)
  880. {
  881. if (isZeroSize) {
  882. size = 0;
  883. buf = new byte[1];
  884. } else {
  885. if (size <= 0)
  886. throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
  887. size = Math.Max (size, 8);
  888. //
  889. // Instead of allocating a new default buffer use the
  890. // last one if there is any available
  891. //
  892. if (size <= DefaultBufferSize && buf_recycle != null) {
  893. lock (buf_recycle_lock) {
  894. if (buf_recycle != null) {
  895. buf = buf_recycle;
  896. buf_recycle = null;
  897. }
  898. }
  899. }
  900. if (buf == null)
  901. buf = new byte [size];
  902. else
  903. Array.Clear (buf, 0, size);
  904. }
  905. buf_size = size;
  906. // buf_start = 0;
  907. // buf_offset = buf_length = 0;
  908. // buf_dirty = false;
  909. }
  910. private string GetSecureFileName (string filename)
  911. {
  912. return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
  913. }
  914. private string GetSecureFileName (string filename, bool full)
  915. {
  916. return (anonymous) ? Path.GetFileName (filename) :
  917. (full) ? Path.GetFullPath (filename) : filename;
  918. }
  919. // fields
  920. internal const int DefaultBufferSize = 4096;
  921. // Input buffer ready for recycling
  922. static byte[] buf_recycle;
  923. static readonly object buf_recycle_lock = new object ();
  924. private byte [] buf; // the buffer
  925. private string name = "[Unknown]"; // name of file.
  926. private SafeFileHandle safeHandle;
  927. private bool isExposed;
  928. private long append_startpos;
  929. private FileAccess access;
  930. private bool owner;
  931. private bool async;
  932. private bool canseek;
  933. private bool anonymous;
  934. private bool buf_dirty; // true if buffer has been written to
  935. private int buf_size; // capacity in bytes
  936. private int buf_length; // number of valid bytes in buffer
  937. private int buf_offset; // position of next byte
  938. private long buf_start; // location of buffer in file
  939. }
  940. }