FileStream.cs 28 KB

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