itss: Ignore trailing slash in ITSProtocol::Start.
[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     if(*--p == '/')
355         *p = 0;
356
357     TRACE("Resolving %s\n", debugstr_w(path));
358
359     r = chm_resolve_object(This->chmfile, path, &ui);
360     HeapFree( GetProcessHeap(), 0, path );
361
362     if( r != CHM_RESOLVE_SUCCESS ) {
363         WARN("Could not resolve object\n");
364         return STG_E_FILENOTFOUND;
365     }
366
367     stm = ITSS_create_stream( This, &ui );
368     if( !stm )
369         return E_FAIL;
370
371     *ppstm = (IStream*) stm;
372
373     return S_OK;
374 }
375
376 static HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
377     IStorage* iface,
378     LPCOLESTR pwcsName,
379     DWORD grfMode,
380     DWORD dwStgFmt,
381     DWORD reserved2,
382     IStorage** ppstg)
383 {
384     FIXME("\n");
385     return E_NOTIMPL;
386 }
387
388 static HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
389     IStorage* iface,
390     LPCOLESTR pwcsName,
391     IStorage* pstgPriority,
392     DWORD grfMode,
393     SNB snbExclude,
394     DWORD reserved,
395     IStorage** ppstg)
396 {
397     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
398
399     FIXME("%p %s %p %u %p %u %p\n", This, debugstr_w(pwcsName),
400           pstgPriority, grfMode, snbExclude, reserved, ppstg);
401     return E_NOTIMPL;
402 }
403
404 static HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
405     IStorage* iface,
406     DWORD ciidExclude,
407     const IID* rgiidExclude,
408     SNB snbExclude,
409     IStorage* pstgDest)
410 {
411     FIXME("\n");
412     return E_NOTIMPL;
413 }
414
415 static HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
416     IStorage* iface,
417     LPCOLESTR pwcsName,
418     IStorage* pstgDest,
419     LPCOLESTR pwcsNewName,
420     DWORD grfFlags)
421 {
422     FIXME("\n");
423     return E_NOTIMPL;
424 }
425
426 static HRESULT WINAPI ITSS_IStorageImpl_Commit(
427     IStorage* iface,
428     DWORD grfCommitFlags)
429 {
430     FIXME("\n");
431     return E_NOTIMPL;
432 }
433
434 static HRESULT WINAPI ITSS_IStorageImpl_Revert(
435     IStorage* iface)
436 {
437     FIXME("\n");
438     return E_NOTIMPL;
439 }
440
441 static int ITSS_chm_enumerator(
442     struct chmFile *h,
443     struct chmUnitInfo *ui,
444     void *context)
445 {
446     struct enum_info *info;
447     IEnumSTATSTG_Impl* stgenum = context;
448
449     TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
450
451     info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
452     memcpy( &info->ui, ui, sizeof info->ui );
453
454     info->next = NULL;
455     info->prev = stgenum->last;
456     if( stgenum->last )
457         stgenum->last->next = info;
458     else
459         stgenum->first = info;
460     stgenum->last = info;
461     
462     return CHM_ENUMERATOR_CONTINUE;
463 }
464
465 static HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
466     IStorage* iface,
467     DWORD reserved1,
468     void* reserved2,
469     DWORD reserved3,
470     IEnumSTATSTG** ppenum)
471 {
472     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
473     IEnumSTATSTG_Impl* stgenum;
474
475     TRACE("%p %d %p %d %p\n", This, reserved1, reserved2, reserved3, ppenum );
476
477     stgenum = ITSS_create_enum();
478     if( !stgenum )
479         return E_FAIL;
480
481     chm_enumerate_dir(This->chmfile,
482                   This->dir,
483                   CHM_ENUMERATE_ALL,
484                   ITSS_chm_enumerator,
485                   stgenum );
486
487     stgenum->current = stgenum->first;
488
489     *ppenum = (IEnumSTATSTG*) stgenum;
490
491     return S_OK;
492 }
493
494 static HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
495     IStorage* iface,
496     LPCOLESTR pwcsName)
497 {
498     FIXME("\n");
499     return E_NOTIMPL;
500 }
501
502 static HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
503     IStorage* iface,
504     LPCOLESTR pwcsOldName,
505     LPCOLESTR pwcsNewName)
506 {
507     FIXME("\n");
508     return E_NOTIMPL;
509 }
510
511 static HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
512     IStorage* iface,
513     LPCOLESTR pwcsName,
514     const FILETIME* pctime,
515     const FILETIME* patime,
516     const FILETIME* pmtime)
517 {
518     FIXME("\n");
519     return E_NOTIMPL;
520 }
521
522 static HRESULT WINAPI ITSS_IStorageImpl_SetClass(
523     IStorage* iface,
524     REFCLSID clsid)
525 {
526     FIXME("\n");
527     return E_NOTIMPL;
528 }
529
530 static HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
531     IStorage* iface,
532     DWORD grfStateBits,
533     DWORD grfMask)
534 {
535     FIXME("\n");
536     return E_NOTIMPL;
537 }
538
539 static HRESULT WINAPI ITSS_IStorageImpl_Stat(
540     IStorage* iface,
541     STATSTG* pstatstg,
542     DWORD grfStatFlag)
543 {
544     FIXME("\n");
545     return E_NOTIMPL;
546 }
547
548 static const IStorageVtbl ITSS_IStorageImpl_Vtbl =
549 {
550     ITSS_IStorageImpl_QueryInterface,
551     ITSS_IStorageImpl_AddRef,
552     ITSS_IStorageImpl_Release,
553     ITSS_IStorageImpl_CreateStream,
554     ITSS_IStorageImpl_OpenStream,
555     ITSS_IStorageImpl_CreateStorage,
556     ITSS_IStorageImpl_OpenStorage,
557     ITSS_IStorageImpl_CopyTo,
558     ITSS_IStorageImpl_MoveElementTo,
559     ITSS_IStorageImpl_Commit,
560     ITSS_IStorageImpl_Revert,
561     ITSS_IStorageImpl_EnumElements,
562     ITSS_IStorageImpl_DestroyElement,
563     ITSS_IStorageImpl_RenameElement,
564     ITSS_IStorageImpl_SetElementTimes,
565     ITSS_IStorageImpl_SetClass,
566     ITSS_IStorageImpl_SetStateBits,
567     ITSS_IStorageImpl_Stat,
568 };
569
570 static HRESULT ITSS_create_chm_storage(
571       struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
572 {
573     ITSS_IStorageImpl *stg;
574     DWORD len;
575
576     TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
577
578     len = strlenW( dir ) + 1;
579     stg = HeapAlloc( GetProcessHeap(), 0, 
580                      sizeof (ITSS_IStorageImpl) + len*sizeof(WCHAR) );
581     stg->vtbl_IStorage = &ITSS_IStorageImpl_Vtbl;
582     stg->ref = 1;
583     stg->chmfile = chmfile;
584     strcpyW( stg->dir, dir );
585
586     *ppstgOpen = (IStorage*) stg;
587
588     ITSS_LockModule();
589     return S_OK;
590 }
591
592 HRESULT ITSS_StgOpenStorage( 
593     const WCHAR* pwcsName,
594     IStorage* pstgPriority,
595     DWORD grfMode,
596     SNB snbExclude,
597     DWORD reserved,
598     IStorage** ppstgOpen)
599 {
600     struct chmFile *chmfile;
601     static const WCHAR szRoot[] = { '/', 0 };
602
603     TRACE("%s\n", debugstr_w(pwcsName) );
604
605     chmfile = chm_openW( pwcsName );
606     if( !chmfile )
607         return E_FAIL;
608
609     return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
610 }
611
612 /************************************************************************/
613
614 static HRESULT WINAPI ITSS_IStream_QueryInterface(
615     IStream* iface,
616     REFIID riid,
617     void** ppvObject)
618 {
619     IStream_Impl *This = (IStream_Impl *)iface;
620
621     if (IsEqualGUID(riid, &IID_IUnknown)
622         || IsEqualGUID(riid, &IID_ISequentialStream)
623         || IsEqualGUID(riid, &IID_IStream))
624     {
625         IStream_AddRef(iface);
626         *ppvObject = This;
627         return S_OK;
628     }
629
630     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
631     return E_NOINTERFACE;
632 }
633
634 static ULONG WINAPI ITSS_IStream_AddRef(
635     IStream* iface)
636 {
637     IStream_Impl *This = (IStream_Impl *)iface;
638     return InterlockedIncrement(&This->ref);
639 }
640
641 static ULONG WINAPI ITSS_IStream_Release(
642     IStream* iface)
643 {
644     IStream_Impl *This = (IStream_Impl *)iface;
645
646     ULONG ref = InterlockedDecrement(&This->ref);
647
648     if (ref == 0)
649     {
650         IStorage_Release( (IStorage*) This->stg );
651         HeapFree(GetProcessHeap(), 0, This);
652         ITSS_UnlockModule();
653     }
654
655     return ref;
656 }
657
658 static HRESULT WINAPI ITSS_IStream_Read(
659         IStream* iface,
660         void* pv,
661         ULONG cb,
662         ULONG* pcbRead)
663 {
664     IStream_Impl *This = (IStream_Impl *)iface;
665     ULONG count;
666
667     TRACE("%p %p %u %p\n", This, pv, cb, pcbRead);
668
669     count = chm_retrieve_object(This->stg->chmfile, 
670                           &This->ui, pv, This->addr, cb);
671     This->addr += count;
672     if( pcbRead )
673         *pcbRead = count;
674
675     return count ? S_OK : S_FALSE;
676 }
677
678 static HRESULT WINAPI ITSS_IStream_Write(
679         IStream* iface,
680         const void* pv,
681         ULONG cb,
682         ULONG* pcbWritten)
683 {
684     FIXME("\n");
685     return E_NOTIMPL;
686 }
687
688 static HRESULT WINAPI ITSS_IStream_Seek(
689         IStream* iface,
690         LARGE_INTEGER dlibMove,
691         DWORD dwOrigin,
692         ULARGE_INTEGER* plibNewPosition)
693 {
694     IStream_Impl *This = (IStream_Impl *)iface;
695     LONGLONG newpos;
696
697     TRACE("%p %s %u %p\n", This,
698           wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
699
700     newpos = This->addr;
701     switch( dwOrigin )
702     {
703     case STREAM_SEEK_CUR:
704         newpos = This->addr + dlibMove.QuadPart;
705         break;
706     case STREAM_SEEK_SET:
707         newpos = dlibMove.QuadPart;
708         break;
709     case STREAM_SEEK_END:
710         newpos = This->ui.length + dlibMove.QuadPart;
711         break;
712     }
713
714     if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
715         return STG_E_INVALIDPOINTER;
716
717     This->addr = newpos;
718     if( plibNewPosition )
719         plibNewPosition->QuadPart = This->addr;
720
721     return S_OK;
722 }
723
724 static HRESULT WINAPI ITSS_IStream_SetSize(
725         IStream* iface,
726         ULARGE_INTEGER libNewSize)
727 {
728     FIXME("\n");
729     return E_NOTIMPL;
730 }
731
732 static HRESULT WINAPI ITSS_IStream_CopyTo(
733         IStream* iface,
734         IStream* pstm,
735         ULARGE_INTEGER cb,
736         ULARGE_INTEGER* pcbRead,
737         ULARGE_INTEGER* pcbWritten)
738 {
739     FIXME("\n");
740     return E_NOTIMPL;
741 }
742
743 static HRESULT WINAPI ITSS_IStream_Commit(
744         IStream* iface,
745         DWORD grfCommitFlags)
746 {
747     FIXME("\n");
748     return E_NOTIMPL;
749 }
750
751 static HRESULT WINAPI ITSS_IStream_Revert(
752         IStream* iface)
753 {
754     FIXME("\n");
755     return E_NOTIMPL;
756 }
757
758 static HRESULT WINAPI ITSS_IStream_LockRegion(
759         IStream* iface,
760         ULARGE_INTEGER libOffset,
761         ULARGE_INTEGER cb,
762         DWORD dwLockType)
763 {
764     FIXME("\n");
765     return E_NOTIMPL;
766 }
767
768 static HRESULT WINAPI ITSS_IStream_UnlockRegion(
769         IStream* iface,
770         ULARGE_INTEGER libOffset,
771         ULARGE_INTEGER cb,
772         DWORD dwLockType)
773 {
774     FIXME("\n");
775     return E_NOTIMPL;
776 }
777
778 static HRESULT WINAPI ITSS_IStream_Stat(
779         IStream* iface,
780         STATSTG* pstatstg,
781         DWORD grfStatFlag)
782 {
783     IStream_Impl *This = (IStream_Impl *)iface;
784
785     TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
786
787     memset( pstatstg, 0, sizeof *pstatstg );
788     if( !( grfStatFlag & STATFLAG_NONAME ) )
789     {
790         FIXME("copy the name\n");
791     }
792     pstatstg->type = STGTY_STREAM;
793     pstatstg->cbSize.QuadPart = This->ui.length;
794     pstatstg->grfMode = STGM_READ;
795     memcpy( &pstatstg->clsid, &CLSID_ITStorage, sizeof (CLSID) );
796
797     return S_OK;
798 }
799
800 static HRESULT WINAPI ITSS_IStream_Clone(
801         IStream* iface,
802         IStream** ppstm)
803 {
804     FIXME("\n");
805     return E_NOTIMPL;
806 }
807
808 static const IStreamVtbl ITSS_IStream_vtbl =
809 {
810     ITSS_IStream_QueryInterface,
811     ITSS_IStream_AddRef,
812     ITSS_IStream_Release,
813     ITSS_IStream_Read,
814     ITSS_IStream_Write,
815     ITSS_IStream_Seek,
816     ITSS_IStream_SetSize,
817     ITSS_IStream_CopyTo,
818     ITSS_IStream_Commit,
819     ITSS_IStream_Revert,
820     ITSS_IStream_LockRegion,
821     ITSS_IStream_UnlockRegion,
822     ITSS_IStream_Stat,
823     ITSS_IStream_Clone,
824 };
825
826 static IStream_Impl *ITSS_create_stream(
827            ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
828 {
829     IStream_Impl *stm;
830
831     stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
832     stm->vtbl_IStream = &ITSS_IStream_vtbl;
833     stm->ref = 1;
834     stm->addr = 0;
835     memcpy( &stm->ui, ui, sizeof stm->ui );
836     stm->stg = stg;
837     IStorage_AddRef( (IStorage*) stg );
838
839     ITSS_LockModule();
840
841     TRACE(" -> %p\n", stm );
842
843     return stm;
844 }