server: Introduce get_file_info request for server-side NtQueryInformationFile implem...
[wine.git] / dlls / ole32 / stg_stream.c
bloba3abf46f26a7cec6c55b63b71aa93693b607caae
1 /*
2 * Compound Storage (32 bit version)
3 * Stream implementation
5 * This file contains the implementation of the stream interface
6 * for streams contained in a compound storage.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Thuy Nguyen
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
32 #define COBJMACROS
33 #define NONAMELESSUNION
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winerror.h"
38 #include "winternl.h"
39 #include "wine/debug.h"
41 #include "storage32.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
45 /***
46 * This implements the IUnknown method QueryInterface for this
47 * class
49 static HRESULT WINAPI StgStreamImpl_QueryInterface(
50 IStream* iface,
51 REFIID riid, /* [in] */
52 void** ppvObject) /* [iid_is][out] */
54 StgStreamImpl* This = impl_from_IStream(iface);
56 if (ppvObject==0)
57 return E_INVALIDARG;
59 *ppvObject = 0;
61 if (IsEqualIID(&IID_IUnknown, riid) ||
62 IsEqualIID(&IID_ISequentialStream, riid) ||
63 IsEqualIID(&IID_IStream, riid))
65 *ppvObject = &This->IStream_iface;
67 else
68 return E_NOINTERFACE;
70 IStream_AddRef(iface);
72 return S_OK;
75 /***
76 * This implements the IUnknown method AddRef for this
77 * class
79 static ULONG WINAPI StgStreamImpl_AddRef(
80 IStream* iface)
82 StgStreamImpl* This = impl_from_IStream(iface);
83 return InterlockedIncrement(&This->ref);
86 /***
87 * This implements the IUnknown method Release for this
88 * class
90 static ULONG WINAPI StgStreamImpl_Release(
91 IStream* iface)
93 StgStreamImpl* This = impl_from_IStream(iface);
94 ULONG ref = InterlockedDecrement(&This->ref);
96 if (!ref)
98 TRACE("(%p)\n", This);
101 * Release the reference we are holding on the parent storage.
102 * IStorage_Release(&This->parentStorage->IStorage_iface);
104 * No, don't do this. Some apps call IStorage_Release without
105 * calling IStream_Release first. If we grab a reference the
106 * file is not closed, and the app fails when it tries to
107 * reopen the file (Easy-PC, for example). Just inform the
108 * storage that we have closed the stream
111 if (This->parentStorage)
112 StorageBaseImpl_RemoveStream(This->parentStorage, This);
113 This->parentStorage = 0;
114 HeapFree(GetProcessHeap(), 0, This);
117 return ref;
120 /***
121 * This method is part of the ISequentialStream interface.
123 * It reads a block of information from the stream at the current
124 * position. It then moves the current position at the end of the
125 * read block
127 * See the documentation of ISequentialStream for more info.
129 static HRESULT WINAPI StgStreamImpl_Read(
130 IStream* iface,
131 void* pv, /* [length_is][size_is][out] */
132 ULONG cb, /* [in] */
133 ULONG* pcbRead) /* [out] */
135 StgStreamImpl* This = impl_from_IStream(iface);
137 ULONG bytesReadBuffer;
138 HRESULT res;
140 TRACE("(%p, %p, %d, %p)\n",
141 iface, pv, cb, pcbRead);
143 if (!This->parentStorage)
145 WARN("storage reverted\n");
146 return STG_E_REVERTED;
150 * If the caller is not interested in the number of bytes read,
151 * we use another buffer to avoid "if" statements in the code.
153 if (pcbRead==0)
154 pcbRead = &bytesReadBuffer;
156 res = StorageBaseImpl_StreamReadAt(This->parentStorage,
157 This->dirEntry,
158 This->currentPosition,
161 pcbRead);
163 if (SUCCEEDED(res))
166 * Advance the pointer for the number of positions read.
168 This->currentPosition.QuadPart += *pcbRead;
171 TRACE("<-- %08x\n", res);
172 return res;
175 /***
176 * This method is part of the ISequentialStream interface.
178 * It writes a block of information to the stream at the current
179 * position. It then moves the current position at the end of the
180 * written block. If the stream is too small to fit the block,
181 * the stream is grown to fit.
183 * See the documentation of ISequentialStream for more info.
185 static HRESULT WINAPI StgStreamImpl_Write(
186 IStream* iface,
187 const void* pv, /* [size_is][in] */
188 ULONG cb, /* [in] */
189 ULONG* pcbWritten) /* [out] */
191 StgStreamImpl* This = impl_from_IStream(iface);
193 ULONG bytesWritten = 0;
194 HRESULT res;
196 TRACE("(%p, %p, %d, %p)\n",
197 iface, pv, cb, pcbWritten);
200 * Do we have permission to write to this stream?
202 switch(STGM_ACCESS_MODE(This->grfMode))
204 case STGM_WRITE:
205 case STGM_READWRITE:
206 break;
207 default:
208 WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This->grfMode));
209 return STG_E_ACCESSDENIED;
212 if (!pv)
213 return STG_E_INVALIDPOINTER;
215 if (!This->parentStorage)
217 WARN("storage reverted\n");
218 return STG_E_REVERTED;
222 * If the caller is not interested in the number of bytes written,
223 * we use another buffer to avoid "if" statements in the code.
225 if (pcbWritten == 0)
226 pcbWritten = &bytesWritten;
229 * Initialize the out parameter
231 *pcbWritten = 0;
233 if (cb == 0)
235 TRACE("<-- S_OK, written 0\n");
236 return S_OK;
239 res = StorageBaseImpl_StreamWriteAt(This->parentStorage,
240 This->dirEntry,
241 This->currentPosition,
244 pcbWritten);
247 * Advance the position pointer for the number of positions written.
249 This->currentPosition.QuadPart += *pcbWritten;
251 if (SUCCEEDED(res))
252 res = StorageBaseImpl_Flush(This->parentStorage);
254 TRACE("<-- %08x, written %u\n", res, *pcbWritten);
255 return res;
258 /***
259 * This method is part of the IStream interface.
261 * It will move the current stream pointer according to the parameters
262 * given.
264 * See the documentation of IStream for more info.
266 static HRESULT WINAPI StgStreamImpl_Seek(
267 IStream* iface,
268 LARGE_INTEGER dlibMove, /* [in] */
269 DWORD dwOrigin, /* [in] */
270 ULARGE_INTEGER* plibNewPosition) /* [out] */
272 StgStreamImpl* This = impl_from_IStream(iface);
274 ULARGE_INTEGER newPosition;
275 DirEntry currentEntry;
276 HRESULT hr;
278 TRACE("(%p, %d, %d, %p)\n",
279 iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
282 * fail if the stream has no parent (as does windows)
285 if (!This->parentStorage)
287 WARN("storage reverted\n");
288 return STG_E_REVERTED;
292 * The caller is allowed to pass in NULL as the new position return value.
293 * If it happens, we assign it to a dynamic variable to avoid special cases
294 * in the code below.
296 if (plibNewPosition == 0)
298 plibNewPosition = &newPosition;
302 * The file pointer is moved depending on the given "function"
303 * parameter.
305 switch (dwOrigin)
307 case STREAM_SEEK_SET:
308 plibNewPosition->u.HighPart = 0;
309 plibNewPosition->u.LowPart = 0;
310 break;
311 case STREAM_SEEK_CUR:
312 *plibNewPosition = This->currentPosition;
313 break;
314 case STREAM_SEEK_END:
315 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, &currentEntry);
316 if (FAILED(hr)) return hr;
317 *plibNewPosition = currentEntry.size;
318 break;
319 default:
320 WARN("invalid dwOrigin %d\n", dwOrigin);
321 return STG_E_INVALIDFUNCTION;
324 plibNewPosition->QuadPart += dlibMove.QuadPart;
327 * tell the caller what we calculated
329 This->currentPosition = *plibNewPosition;
331 return S_OK;
334 /***
335 * This method is part of the IStream interface.
337 * It will change the size of a stream.
339 * See the documentation of IStream for more info.
341 static HRESULT WINAPI StgStreamImpl_SetSize(
342 IStream* iface,
343 ULARGE_INTEGER libNewSize) /* [in] */
345 StgStreamImpl* This = impl_from_IStream(iface);
347 HRESULT hr;
349 TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
351 if(!This->parentStorage)
353 WARN("storage reverted\n");
354 return STG_E_REVERTED;
358 * As documented.
360 if (libNewSize.u.HighPart != 0)
362 WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize.u.HighPart);
363 return STG_E_INVALIDFUNCTION;
367 * Do we have permission?
369 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
371 WARN("access denied\n");
372 return STG_E_ACCESSDENIED;
375 hr = StorageBaseImpl_StreamSetSize(This->parentStorage, This->dirEntry, libNewSize);
377 if (SUCCEEDED(hr))
378 hr = StorageBaseImpl_Flush(This->parentStorage);
380 return hr;
383 /***
384 * This method is part of the IStream interface.
386 * It will copy the 'cb' Bytes to 'pstm' IStream.
388 * See the documentation of IStream for more info.
390 static HRESULT WINAPI StgStreamImpl_CopyTo(
391 IStream* iface,
392 IStream* pstm, /* [unique][in] */
393 ULARGE_INTEGER cb, /* [in] */
394 ULARGE_INTEGER* pcbRead, /* [out] */
395 ULARGE_INTEGER* pcbWritten) /* [out] */
397 StgStreamImpl* This = impl_from_IStream(iface);
398 HRESULT hr = S_OK;
399 BYTE tmpBuffer[128];
400 ULONG bytesRead, bytesWritten, copySize;
401 ULARGE_INTEGER totalBytesRead;
402 ULARGE_INTEGER totalBytesWritten;
404 TRACE("(%p, %p, %d, %p, %p)\n",
405 iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
408 * Sanity check
411 if (!This->parentStorage)
413 WARN("storage reverted\n");
414 return STG_E_REVERTED;
417 if ( pstm == 0 )
418 return STG_E_INVALIDPOINTER;
420 totalBytesRead.QuadPart = 0;
421 totalBytesWritten.QuadPart = 0;
423 while ( cb.QuadPart > 0 )
425 if ( cb.QuadPart >= sizeof(tmpBuffer) )
426 copySize = sizeof(tmpBuffer);
427 else
428 copySize = cb.u.LowPart;
430 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
432 totalBytesRead.QuadPart += bytesRead;
434 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
436 totalBytesWritten.QuadPart += bytesWritten;
439 * Check that read & write operations were successful
441 if (bytesRead != bytesWritten)
443 hr = STG_E_MEDIUMFULL;
444 WARN("medium full\n");
445 break;
448 if (bytesRead!=copySize)
449 cb.QuadPart = 0;
450 else
451 cb.QuadPart -= bytesRead;
454 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
455 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
457 return hr;
460 /***
461 * This method is part of the IStream interface.
463 * For streams contained in structured storages, this method
464 * does nothing. This is what the documentation tells us.
466 * See the documentation of IStream for more info.
468 static HRESULT WINAPI StgStreamImpl_Commit(
469 IStream* iface,
470 DWORD grfCommitFlags) /* [in] */
472 StgStreamImpl* This = impl_from_IStream(iface);
474 if (!This->parentStorage)
476 WARN("storage reverted\n");
477 return STG_E_REVERTED;
480 return StorageBaseImpl_Flush(This->parentStorage);
483 /***
484 * This method is part of the IStream interface.
486 * For streams contained in structured storages, this method
487 * does nothing. This is what the documentation tells us.
489 * See the documentation of IStream for more info.
491 static HRESULT WINAPI StgStreamImpl_Revert(
492 IStream* iface)
494 return S_OK;
497 static HRESULT WINAPI StgStreamImpl_LockRegion(
498 IStream* iface,
499 ULARGE_INTEGER libOffset, /* [in] */
500 ULARGE_INTEGER cb, /* [in] */
501 DWORD dwLockType) /* [in] */
503 StgStreamImpl* This = impl_from_IStream(iface);
505 if (!This->parentStorage)
507 WARN("storage reverted\n");
508 return STG_E_REVERTED;
511 FIXME("not implemented!\n");
512 return E_NOTIMPL;
515 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
516 IStream* iface,
517 ULARGE_INTEGER libOffset, /* [in] */
518 ULARGE_INTEGER cb, /* [in] */
519 DWORD dwLockType) /* [in] */
521 StgStreamImpl* This = impl_from_IStream(iface);
523 if (!This->parentStorage)
525 WARN("storage reverted\n");
526 return STG_E_REVERTED;
529 FIXME("not implemented!\n");
530 return E_NOTIMPL;
533 /***
534 * This method is part of the IStream interface.
536 * This method returns information about the current
537 * stream.
539 * See the documentation of IStream for more info.
541 static HRESULT WINAPI StgStreamImpl_Stat(
542 IStream* iface,
543 STATSTG* pstatstg, /* [out] */
544 DWORD grfStatFlag) /* [in] */
546 StgStreamImpl* This = impl_from_IStream(iface);
548 DirEntry currentEntry;
549 HRESULT hr;
551 TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
554 * if stream has no parent, return STG_E_REVERTED
557 if (!This->parentStorage)
559 WARN("storage reverted\n");
560 return STG_E_REVERTED;
564 * Read the information from the directory entry.
566 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
567 This->dirEntry,
568 &currentEntry);
570 if (SUCCEEDED(hr))
572 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
573 pstatstg,
574 &currentEntry,
575 grfStatFlag);
577 pstatstg->grfMode = This->grfMode;
579 /* In simple create mode cbSize is the current pos */
580 if((This->parentStorage->openFlags & STGM_SIMPLE) && This->parentStorage->create)
581 pstatstg->cbSize = This->currentPosition;
583 return S_OK;
586 WARN("failed to read entry\n");
587 return hr;
590 /***
591 * This method is part of the IStream interface.
593 * This method returns a clone of the interface that allows for
594 * another seek pointer
596 * See the documentation of IStream for more info.
598 * I am not totally sure what I am doing here but I presume that this
599 * should be basically as simple as creating a new stream with the same
600 * parent etc and positioning its seek cursor.
602 static HRESULT WINAPI StgStreamImpl_Clone(
603 IStream* iface,
604 IStream** ppstm) /* [out] */
606 StgStreamImpl* This = impl_from_IStream(iface);
607 HRESULT hres;
608 StgStreamImpl* new_stream;
609 LARGE_INTEGER seek_pos;
611 TRACE("%p %p\n", This, ppstm);
614 * Sanity check
617 if (!This->parentStorage)
618 return STG_E_REVERTED;
620 if ( ppstm == 0 )
621 return STG_E_INVALIDPOINTER;
623 new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry);
625 if (!new_stream)
626 return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
628 *ppstm = &new_stream->IStream_iface;
629 IStream_AddRef(*ppstm);
631 seek_pos.QuadPart = This->currentPosition.QuadPart;
633 hres = IStream_Seek(*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
635 assert (SUCCEEDED(hres));
637 return S_OK;
641 * Virtual function table for the StgStreamImpl class.
643 static const IStreamVtbl StgStreamVtbl =
645 StgStreamImpl_QueryInterface,
646 StgStreamImpl_AddRef,
647 StgStreamImpl_Release,
648 StgStreamImpl_Read,
649 StgStreamImpl_Write,
650 StgStreamImpl_Seek,
651 StgStreamImpl_SetSize,
652 StgStreamImpl_CopyTo,
653 StgStreamImpl_Commit,
654 StgStreamImpl_Revert,
655 StgStreamImpl_LockRegion,
656 StgStreamImpl_UnlockRegion,
657 StgStreamImpl_Stat,
658 StgStreamImpl_Clone
661 /******************************************************************************
662 ** StgStreamImpl implementation
665 /***
666 * This is the constructor for the StgStreamImpl class.
668 * Params:
669 * parentStorage - Pointer to the storage that contains the stream to open
670 * dirEntry - Index of the directory entry that points to this stream.
672 StgStreamImpl* StgStreamImpl_Construct(
673 StorageBaseImpl* parentStorage,
674 DWORD grfMode,
675 DirRef dirEntry)
677 StgStreamImpl* newStream;
679 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
681 if (newStream)
684 * Set-up the virtual function table and reference count.
686 newStream->IStream_iface.lpVtbl = &StgStreamVtbl;
687 newStream->ref = 0;
689 newStream->parentStorage = parentStorage;
692 * We want to nail-down the reference to the storage in case the
693 * stream out-lives the storage in the client application.
695 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
697 * No, don't do this. Some apps call IStorage_Release without
698 * calling IStream_Release first. If we grab a reference the
699 * file is not closed, and the app fails when it tries to
700 * reopen the file (Easy-PC, for example)
703 newStream->grfMode = grfMode;
704 newStream->dirEntry = dirEntry;
707 * Start the stream at the beginning.
709 newStream->currentPosition.u.HighPart = 0;
710 newStream->currentPosition.u.LowPart = 0;
712 /* add us to the storage's list of active streams */
713 StorageBaseImpl_AddStream(parentStorage, newStream);
716 return newStream;