quartz: Use proper alloc/free functions for COM objects.
[wine] / dlls / itss / storage.c
1 /*
2  *    ITSS Storage implementation
3  *
4  * Copyright 2004 Mike McCormack
5  *
6  *  see http://bonedaddy.net/pabs3/hhm/#chmspec
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #define COBJMACROS
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "winreg.h"
35 #include "ole2.h"
36
37 #include "uuids.h"
38
39 #include "chm_lib.h"
40 #include "itsstor.h"
41
42 #include "wine/itss.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(itss);
47
48 /************************************************************************/
49
50 typedef struct _ITSS_IStorageImpl
51 {
52     const IStorageVtbl *vtbl_IStorage;
53     LONG ref;
54     struct chmFile *chmfile;
55     WCHAR dir[1];
56 } ITSS_IStorageImpl;
57
58 struct enum_info
59 {
60     struct enum_info *next, *prev;
61     struct chmUnitInfo ui;
62 };
63
64 typedef struct _IEnumSTATSTG_Impl
65 {
66     const IEnumSTATSTGVtbl *vtbl_IEnumSTATSTG;
67     LONG ref;
68     struct enum_info *first, *last, *current;
69 } IEnumSTATSTG_Impl;
70
71 typedef struct _IStream_Impl
72 {
73     const IStreamVtbl *vtbl_IStream;
74     LONG ref;
75     ITSS_IStorageImpl *stg;
76     ULONGLONG addr;
77     struct chmUnitInfo ui;
78 } IStream_Impl;
79
80 static HRESULT ITSS_create_chm_storage(
81            struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen );
82 static IStream_Impl* ITSS_create_stream( 
83            ITSS_IStorageImpl *stg, struct chmUnitInfo *ui );
84
85 /************************************************************************/
86
87 static HRESULT WINAPI ITSS_IEnumSTATSTG_QueryInterface(
88     IEnumSTATSTG* iface,
89     REFIID riid,
90     void** ppvObject)
91 {
92     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
93
94     if (IsEqualGUID(riid, &IID_IUnknown)
95         || IsEqualGUID(riid, &IID_IEnumSTATSTG))
96     {
97         IEnumSTATSTG_AddRef(iface);
98         *ppvObject = This;
99         return S_OK;
100     }
101
102     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
103     return E_NOINTERFACE;
104 }
105
106 static ULONG WINAPI ITSS_IEnumSTATSTG_AddRef(
107     IEnumSTATSTG* iface)
108 {
109     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
110     return InterlockedIncrement(&This->ref);
111 }
112
113 static ULONG WINAPI ITSS_IEnumSTATSTG_Release(
114     IEnumSTATSTG* iface)
115 {
116     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
117
118     ULONG ref = InterlockedDecrement(&This->ref);
119
120     if (ref == 0)
121     {
122         while( This->first )
123         {
124             struct enum_info *t = This->first->next;
125             HeapFree( GetProcessHeap(), 0, This->first );
126             This->first = t;
127         }
128         HeapFree(GetProcessHeap(), 0, This);
129         ITSS_UnlockModule();
130     }
131
132     return ref;
133 }
134
135 static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
136         IEnumSTATSTG* iface,
137         ULONG celt,
138         STATSTG* rgelt,
139         ULONG* pceltFetched)
140 {
141     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
142     DWORD len, n;
143     struct enum_info *cur;
144
145     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched );
146
147     cur = This->current;
148     n = 0;
149     while( (n<celt) && cur) 
150     {
151         WCHAR *str;
152
153         memset( rgelt, 0, sizeof *rgelt );
154
155         /* copy the name */
156         str = cur->ui.path;
157         if( *str == '/' )
158             str++;
159         len = strlenW( str ) + 1;
160         rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
161         strcpyW( rgelt->pwcsName, str );
162
163         /* determine the type */
164         if( rgelt->pwcsName[len-2] == '/' )
165         {
166             rgelt->pwcsName[len-2] = 0;
167             rgelt->type = STGTY_STORAGE;
168         }
169         else
170             rgelt->type = STGTY_STREAM;
171
172         /* copy the size */
173         rgelt->cbSize.QuadPart = cur->ui.length;
174
175         /* advance to the next item if it exists */
176         n++;
177         cur = cur->next;
178     }
179
180     This->current = cur;
181     *pceltFetched = n;
182
183     if( n < celt )
184         return S_FALSE;
185
186     return S_OK;
187 }
188
189 static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
190         IEnumSTATSTG* iface,
191         ULONG celt)
192 {
193     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
194     DWORD n;
195     struct enum_info *cur;
196
197     TRACE("%p %u\n", This, celt );
198
199     cur = This->current;
200     n = 0;
201     while( (n<celt) && cur) 
202     {
203         n++;
204         cur = cur->next;
205     }
206     This->current = cur;
207
208     if( n < celt )
209         return S_FALSE;
210
211     return S_OK;
212 }
213
214 static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
215         IEnumSTATSTG* iface)
216 {
217     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
218
219     TRACE("%p\n", This );
220
221     This->current = This->first;
222
223     return S_OK;
224 }
225
226 static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
227         IEnumSTATSTG* iface,
228         IEnumSTATSTG** ppenum)
229 {
230     FIXME("\n");
231     return E_NOTIMPL;
232 }
233
234 static const IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
235 {
236     ITSS_IEnumSTATSTG_QueryInterface,
237     ITSS_IEnumSTATSTG_AddRef,
238     ITSS_IEnumSTATSTG_Release,
239     ITSS_IEnumSTATSTG_Next,
240     ITSS_IEnumSTATSTG_Skip,
241     ITSS_IEnumSTATSTG_Reset,
242     ITSS_IEnumSTATSTG_Clone
243 };
244
245 static IEnumSTATSTG_Impl *ITSS_create_enum( void )
246 {
247     IEnumSTATSTG_Impl *stgenum;
248
249     stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
250     stgenum->vtbl_IEnumSTATSTG = &IEnumSTATSTG_vtbl;
251     stgenum->ref = 1;
252     stgenum->first = NULL;
253     stgenum->last = NULL;
254     stgenum->current = NULL;
255
256     ITSS_LockModule();
257     TRACE(" -> %p\n", stgenum );
258
259     return stgenum;
260 }
261
262 /************************************************************************/
263
264 static HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
265     IStorage* iface,
266     REFIID riid,
267     void** ppvObject)
268 {
269     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
270
271     if (IsEqualGUID(riid, &IID_IUnknown)
272         || IsEqualGUID(riid, &IID_IStorage))
273     {
274         IStorage_AddRef(iface);
275         *ppvObject = This;
276         return S_OK;
277     }
278
279     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
280     return E_NOINTERFACE;
281 }
282
283 static ULONG WINAPI ITSS_IStorageImpl_AddRef(
284     IStorage* iface)
285 {
286     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
287     return InterlockedIncrement(&This->ref);
288 }
289
290 static ULONG WINAPI ITSS_IStorageImpl_Release(
291     IStorage* iface)
292 {
293     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
294
295     ULONG ref = InterlockedDecrement(&This->ref);
296
297     if (ref == 0)
298     {
299         chm_close(This->chmfile);
300         HeapFree(GetProcessHeap(), 0, This);
301         ITSS_UnlockModule();
302     }
303
304     return ref;
305 }
306
307 static HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
308     IStorage* iface,
309     LPCOLESTR pwcsName,
310     DWORD grfMode,
311     DWORD reserved1,
312     DWORD reserved2,
313     IStream** ppstm)
314 {
315     FIXME("\n");
316     return E_NOTIMPL;
317 }
318
319 static HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
320     IStorage* iface,
321     LPCOLESTR pwcsName,
322     void* reserved1,
323     DWORD grfMode,
324     DWORD reserved2,
325     IStream** ppstm)
326 {
327     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
328     IStream_Impl *stm;
329     DWORD len;
330     struct chmUnitInfo ui;
331     int r;
332     WCHAR *path, *p;
333
334     TRACE("%p %s %p %u %u %p\n", This, debugstr_w(pwcsName),
335           reserved1, grfMode, reserved2, ppstm );
336
337     len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
338     path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
339     strcpyW( path, This->dir );
340
341     if( pwcsName[0] == '/' || pwcsName[0] == '\\' )
342     {
343         p = &path[strlenW( path ) - 1];
344         while( ( path <= p ) && ( *p == '/' ) )
345             *p-- = 0;
346     }
347     strcatW( path, pwcsName );
348
349     for(p=path; *p; p++) {
350         if(*p == '\\')
351             *p = '/';
352     }
353
354     TRACE("Resolving %s\n", debugstr_w(path));
355
356     r = chm_resolve_object(This->chmfile, path, &ui);
357     HeapFree( GetProcessHeap(), 0, path );
358
359     if( r != CHM_RESOLVE_SUCCESS ) {
360         WARN("Could not resolve object\n");
361         return STG_E_FILENOTFOUND;
362     }
363
364     stm = ITSS_create_stream( This, &ui );
365     if( !stm )
366         return E_FAIL;
367
368     *ppstm = (IStream*) stm;
369
370     return S_OK;
371 }
372
373 static HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
374     IStorage* iface,
375     LPCOLESTR pwcsName,
376     DWORD grfMode,
377     DWORD dwStgFmt,
378     DWORD reserved2,
379     IStorage** ppstg)
380 {
381     FIXME("\n");
382     return E_NOTIMPL;
383 }
384
385 static HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
386     IStorage* iface,
387     LPCOLESTR pwcsName,
388     IStorage* pstgPriority,
389     DWORD grfMode,
390     SNB snbExclude,
391     DWORD reserved,
392     IStorage** ppstg)
393 {
394     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
395
396     FIXME("%p %s %p %u %p %u %p\n", This, debugstr_w(pwcsName),
397           pstgPriority, grfMode, snbExclude, reserved, ppstg);
398     return E_NOTIMPL;
399 }
400
401 static HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
402     IStorage* iface,
403     DWORD ciidExclude,
404     const IID* rgiidExclude,
405     SNB snbExclude,
406     IStorage* pstgDest)
407 {
408     FIXME("\n");
409     return E_NOTIMPL;
410 }
411
412 static HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
413     IStorage* iface,
414     LPCOLESTR pwcsName,
415     IStorage* pstgDest,
416     LPCOLESTR pwcsNewName,
417     DWORD grfFlags)
418 {
419     FIXME("\n");
420     return E_NOTIMPL;
421 }
422
423 static HRESULT WINAPI ITSS_IStorageImpl_Commit(
424     IStorage* iface,
425     DWORD grfCommitFlags)
426 {
427     FIXME("\n");
428     return E_NOTIMPL;
429 }
430
431 static HRESULT WINAPI ITSS_IStorageImpl_Revert(
432     IStorage* iface)
433 {
434     FIXME("\n");
435     return E_NOTIMPL;
436 }
437
438 static int ITSS_chm_enumerator(
439     struct chmFile *h,
440     struct chmUnitInfo *ui,
441     void *context)
442 {
443     struct enum_info *info;
444     IEnumSTATSTG_Impl* stgenum = context;
445
446     TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
447
448     info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
449     memcpy( &info->ui, ui, sizeof info->ui );
450
451     info->next = NULL;
452     info->prev = stgenum->last;
453     if( stgenum->last )
454         stgenum->last->next = info;
455     else
456         stgenum->first = info;
457     stgenum->last = info;
458     
459     return CHM_ENUMERATOR_CONTINUE;
460 }
461
462 static HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
463     IStorage* iface,
464     DWORD reserved1,
465     void* reserved2,
466     DWORD reserved3,
467     IEnumSTATSTG** ppenum)
468 {
469     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
470     IEnumSTATSTG_Impl* stgenum;
471
472     TRACE("%p %d %p %d %p\n", This, reserved1, reserved2, reserved3, ppenum );
473
474     stgenum = ITSS_create_enum();
475     if( !stgenum )
476         return E_FAIL;
477
478     chm_enumerate_dir(This->chmfile,
479                   This->dir,
480                   CHM_ENUMERATE_ALL,
481                   ITSS_chm_enumerator,
482                   stgenum );
483
484     stgenum->current = stgenum->first;
485
486     *ppenum = (IEnumSTATSTG*) stgenum;
487
488     return S_OK;
489 }
490
491 static HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
492     IStorage* iface,
493     LPCOLESTR pwcsName)
494 {
495     FIXME("\n");
496     return E_NOTIMPL;
497 }
498
499 static HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
500     IStorage* iface,
501     LPCOLESTR pwcsOldName,
502     LPCOLESTR pwcsNewName)
503 {
504     FIXME("\n");
505     return E_NOTIMPL;
506 }
507
508 static HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
509     IStorage* iface,
510     LPCOLESTR pwcsName,
511     const FILETIME* pctime,
512     const FILETIME* patime,
513     const FILETIME* pmtime)
514 {
515     FIXME("\n");
516     return E_NOTIMPL;
517 }
518
519 static HRESULT WINAPI ITSS_IStorageImpl_SetClass(
520     IStorage* iface,
521     REFCLSID clsid)
522 {
523     FIXME("\n");
524     return E_NOTIMPL;
525 }
526
527 static HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
528     IStorage* iface,
529     DWORD grfStateBits,
530     DWORD grfMask)
531 {
532     FIXME("\n");
533     return E_NOTIMPL;
534 }
535
536 static HRESULT WINAPI ITSS_IStorageImpl_Stat(
537     IStorage* iface,
538     STATSTG* pstatstg,
539     DWORD grfStatFlag)
540 {
541     FIXME("\n");
542     return E_NOTIMPL;
543 }
544
545 static const IStorageVtbl ITSS_IStorageImpl_Vtbl =
546 {
547     ITSS_IStorageImpl_QueryInterface,
548     ITSS_IStorageImpl_AddRef,
549     ITSS_IStorageImpl_Release,
550     ITSS_IStorageImpl_CreateStream,
551     ITSS_IStorageImpl_OpenStream,
552     ITSS_IStorageImpl_CreateStorage,
553     ITSS_IStorageImpl_OpenStorage,
554     ITSS_IStorageImpl_CopyTo,
555     ITSS_IStorageImpl_MoveElementTo,
556     ITSS_IStorageImpl_Commit,
557     ITSS_IStorageImpl_Revert,
558     ITSS_IStorageImpl_EnumElements,
559     ITSS_IStorageImpl_DestroyElement,
560     ITSS_IStorageImpl_RenameElement,
561     ITSS_IStorageImpl_SetElementTimes,
562     ITSS_IStorageImpl_SetClass,
563     ITSS_IStorageImpl_SetStateBits,
564     ITSS_IStorageImpl_Stat,
565 };
566
567 static HRESULT ITSS_create_chm_storage(
568       struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
569 {
570     ITSS_IStorageImpl *stg;
571     DWORD len;
572
573     TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
574
575     len = strlenW( dir ) + 1;
576     stg = HeapAlloc( GetProcessHeap(), 0, 
577                      sizeof (ITSS_IStorageImpl) + len*sizeof(WCHAR) );
578     stg->vtbl_IStorage = &ITSS_IStorageImpl_Vtbl;
579     stg->ref = 1;
580     stg->chmfile = chmfile;
581     strcpyW( stg->dir, dir );
582
583     *ppstgOpen = (IStorage*) stg;
584
585     ITSS_LockModule();
586     return S_OK;
587 }
588
589 HRESULT ITSS_StgOpenStorage( 
590     const WCHAR* pwcsName,
591     IStorage* pstgPriority,
592     DWORD grfMode,
593     SNB snbExclude,
594     DWORD reserved,
595     IStorage** ppstgOpen)
596 {
597     struct chmFile *chmfile;
598     static const WCHAR szRoot[] = { '/', 0 };
599
600     TRACE("%s\n", debugstr_w(pwcsName) );
601
602     chmfile = chm_openW( pwcsName );
603     if( !chmfile )
604         return E_FAIL;
605
606     return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
607 }
608
609 /************************************************************************/
610
611 static HRESULT WINAPI ITSS_IStream_QueryInterface(
612     IStream* iface,
613     REFIID riid,
614     void** ppvObject)
615 {
616     IStream_Impl *This = (IStream_Impl *)iface;
617
618     if (IsEqualGUID(riid, &IID_IUnknown)
619         || IsEqualGUID(riid, &IID_ISequentialStream)
620         || IsEqualGUID(riid, &IID_IStream))
621     {
622         IStream_AddRef(iface);
623         *ppvObject = This;
624         return S_OK;
625     }
626
627     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
628     return E_NOINTERFACE;
629 }
630
631 static ULONG WINAPI ITSS_IStream_AddRef(
632     IStream* iface)
633 {
634     IStream_Impl *This = (IStream_Impl *)iface;
635     return InterlockedIncrement(&This->ref);
636 }
637
638 static ULONG WINAPI ITSS_IStream_Release(
639     IStream* iface)
640 {
641     IStream_Impl *This = (IStream_Impl *)iface;
642
643     ULONG ref = InterlockedDecrement(&This->ref);
644
645     if (ref == 0)
646     {
647         IStorage_Release( (IStorage*) This->stg );
648         HeapFree(GetProcessHeap(), 0, This);
649         ITSS_UnlockModule();
650     }
651
652     return ref;
653 }
654
655 static HRESULT WINAPI ITSS_IStream_Read(
656         IStream* iface,
657         void* pv,
658         ULONG cb,
659         ULONG* pcbRead)
660 {
661     IStream_Impl *This = (IStream_Impl *)iface;
662     ULONG count;
663
664     TRACE("%p %p %u %p\n", This, pv, cb, pcbRead);
665
666     count = chm_retrieve_object(This->stg->chmfile, 
667                           &This->ui, pv, This->addr, cb);
668     This->addr += count;
669     if( pcbRead )
670         *pcbRead = count;
671
672     return count ? S_OK : S_FALSE;
673 }
674
675 static HRESULT WINAPI ITSS_IStream_Write(
676         IStream* iface,
677         const void* pv,
678         ULONG cb,
679         ULONG* pcbWritten)
680 {
681     FIXME("\n");
682     return E_NOTIMPL;
683 }
684
685 static HRESULT WINAPI ITSS_IStream_Seek(
686         IStream* iface,
687         LARGE_INTEGER dlibMove,
688         DWORD dwOrigin,
689         ULARGE_INTEGER* plibNewPosition)
690 {
691     IStream_Impl *This = (IStream_Impl *)iface;
692     LONGLONG newpos;
693
694     TRACE("%p %s %u %p\n", This,
695           wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
696
697     newpos = This->addr;
698     switch( dwOrigin )
699     {
700     case STREAM_SEEK_CUR:
701         newpos = This->addr + dlibMove.QuadPart;
702         break;
703     case STREAM_SEEK_SET:
704         newpos = dlibMove.QuadPart;
705         break;
706     case STREAM_SEEK_END:
707         newpos = This->ui.length + dlibMove.QuadPart;
708         break;
709     }
710
711     if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
712         return STG_E_INVALIDPOINTER;
713
714     This->addr = newpos;
715     if( plibNewPosition )
716         plibNewPosition->QuadPart = This->addr;
717
718     return S_OK;
719 }
720
721 static HRESULT WINAPI ITSS_IStream_SetSize(
722         IStream* iface,
723         ULARGE_INTEGER libNewSize)
724 {
725     FIXME("\n");
726     return E_NOTIMPL;
727 }
728
729 static HRESULT WINAPI ITSS_IStream_CopyTo(
730         IStream* iface,
731         IStream* pstm,
732         ULARGE_INTEGER cb,
733         ULARGE_INTEGER* pcbRead,
734         ULARGE_INTEGER* pcbWritten)
735 {
736     FIXME("\n");
737     return E_NOTIMPL;
738 }
739
740 static HRESULT WINAPI ITSS_IStream_Commit(
741         IStream* iface,
742         DWORD grfCommitFlags)
743 {
744     FIXME("\n");
745     return E_NOTIMPL;
746 }
747
748 static HRESULT WINAPI ITSS_IStream_Revert(
749         IStream* iface)
750 {
751     FIXME("\n");
752     return E_NOTIMPL;
753 }
754
755 static HRESULT WINAPI ITSS_IStream_LockRegion(
756         IStream* iface,
757         ULARGE_INTEGER libOffset,
758         ULARGE_INTEGER cb,
759         DWORD dwLockType)
760 {
761     FIXME("\n");
762     return E_NOTIMPL;
763 }
764
765 static HRESULT WINAPI ITSS_IStream_UnlockRegion(
766         IStream* iface,
767         ULARGE_INTEGER libOffset,
768         ULARGE_INTEGER cb,
769         DWORD dwLockType)
770 {
771     FIXME("\n");
772     return E_NOTIMPL;
773 }
774
775 static HRESULT WINAPI ITSS_IStream_Stat(
776         IStream* iface,
777         STATSTG* pstatstg,
778         DWORD grfStatFlag)
779 {
780     IStream_Impl *This = (IStream_Impl *)iface;
781
782     TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
783
784     memset( pstatstg, 0, sizeof *pstatstg );
785     if( !( grfStatFlag & STATFLAG_NONAME ) )
786     {
787         FIXME("copy the name\n");
788     }
789     pstatstg->type = STGTY_STREAM;
790     pstatstg->cbSize.QuadPart = This->ui.length;
791     pstatstg->grfMode = STGM_READ;
792     memcpy( &pstatstg->clsid, &CLSID_ITStorage, sizeof (CLSID) );
793
794     return S_OK;
795 }
796
797 static HRESULT WINAPI ITSS_IStream_Clone(
798         IStream* iface,
799         IStream** ppstm)
800 {
801     FIXME("\n");
802     return E_NOTIMPL;
803 }
804
805 static const IStreamVtbl ITSS_IStream_vtbl =
806 {
807     ITSS_IStream_QueryInterface,
808     ITSS_IStream_AddRef,
809     ITSS_IStream_Release,
810     ITSS_IStream_Read,
811     ITSS_IStream_Write,
812     ITSS_IStream_Seek,
813     ITSS_IStream_SetSize,
814     ITSS_IStream_CopyTo,
815     ITSS_IStream_Commit,
816     ITSS_IStream_Revert,
817     ITSS_IStream_LockRegion,
818     ITSS_IStream_UnlockRegion,
819     ITSS_IStream_Stat,
820     ITSS_IStream_Clone,
821 };
822
823 static IStream_Impl *ITSS_create_stream(
824            ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
825 {
826     IStream_Impl *stm;
827
828     stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
829     stm->vtbl_IStream = &ITSS_IStream_vtbl;
830     stm->ref = 1;
831     stm->addr = 0;
832     memcpy( &stm->ui, ui, sizeof stm->ui );
833     stm->stg = stg;
834     IStorage_AddRef( (IStorage*) stg );
835
836     ITSS_LockModule();
837
838     TRACE(" -> %p\n", stm );
839
840     return stm;
841 }