FileStream.cs 27 KB

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