Add ffs() to porting library, needed by MinGW.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "itss.h"
40 #include "chm_lib.h"
41 #include "itsstor.h"
42
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     IStorageVtbl *vtbl_IStorage;
53     DWORD 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     IEnumSTATSTGVtbl *vtbl_IEnumSTATSTG;
67     DWORD ref;
68     struct enum_info *first, *last, *current;
69 } IEnumSTATSTG_Impl;
70
71 typedef struct _IStream_Impl
72 {
73     IStreamVtbl *vtbl_IStream;
74     DWORD 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 ++(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 = --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     }
130
131     return ref;
132 }
133
134 static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
135         IEnumSTATSTG* iface,
136         ULONG celt,
137         STATSTG* rgelt,
138         ULONG* pceltFetched)
139 {
140     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
141     DWORD len, n;
142     struct enum_info *cur;
143
144     TRACE("%p %lu %p %p\n", This, celt, rgelt, pceltFetched );
145
146     cur = This->current;
147     n = 0;
148     while( (n<celt) && cur) 
149     {
150         WCHAR *str;
151
152         memset( rgelt, 0, sizeof *rgelt );
153
154         /* copy the name */
155         str = cur->ui.path;
156         if( *str == '/' )
157             str++;
158         len = strlenW( str ) + 1;
159         rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
160         strcpyW( rgelt->pwcsName, str );
161
162         /* determine the type */
163         if( rgelt->pwcsName[len-2] == '/' )
164         {
165             rgelt->pwcsName[len-2] = 0;
166             rgelt->type = STGTY_STORAGE;
167         }
168         else
169             rgelt->type = STGTY_STREAM;
170
171         /* copy the size */
172         rgelt->cbSize.QuadPart = cur->ui.length;
173
174         /* advance to the next item if it exists */
175         n++;
176         cur = cur->next;
177     }
178
179     This->current = cur;
180     *pceltFetched = n;
181
182     if( n < celt )
183         return S_FALSE;
184
185     return S_OK;
186 }
187
188 static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
189         IEnumSTATSTG* iface,
190         ULONG celt)
191 {
192     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
193     DWORD n;
194     struct enum_info *cur;
195
196     TRACE("%p %lu\n", This, celt );
197
198     cur = This->current;
199     n = 0;
200     while( (n<celt) && cur) 
201     {
202         n++;
203         cur = cur->next;
204     }
205     This->current = cur;
206
207     if( n < celt )
208         return S_FALSE;
209
210     return S_OK;
211 }
212
213 static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
214         IEnumSTATSTG* iface)
215 {
216     IEnumSTATSTG_Impl *This = (IEnumSTATSTG_Impl *)iface;
217
218     TRACE("%p\n", This );
219
220     This->current = This->first;
221
222     return S_OK;
223 }
224
225 static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
226         IEnumSTATSTG* iface,
227         IEnumSTATSTG** ppenum)
228 {
229     FIXME("\n");
230     return E_NOTIMPL;
231 }
232
233 struct IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
234 {
235     ITSS_IEnumSTATSTG_QueryInterface,
236     ITSS_IEnumSTATSTG_AddRef,
237     ITSS_IEnumSTATSTG_Release,
238     ITSS_IEnumSTATSTG_Next,
239     ITSS_IEnumSTATSTG_Skip,
240     ITSS_IEnumSTATSTG_Reset,
241     ITSS_IEnumSTATSTG_Clone
242 };
243
244 static IEnumSTATSTG_Impl *ITSS_create_enum( void )
245 {
246     IEnumSTATSTG_Impl *stgenum;
247
248     stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
249     stgenum->vtbl_IEnumSTATSTG = &IEnumSTATSTG_vtbl;
250     stgenum->ref = 1;
251     stgenum->first = NULL;
252     stgenum->last = NULL;
253     stgenum->current = NULL;
254
255     TRACE(" -> %p\n", stgenum );
256
257     return stgenum;
258 }
259
260 /************************************************************************/
261
262 HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
263     IStorage* iface,
264     REFIID riid,
265     void** ppvObject)
266 {
267     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
268
269     if (IsEqualGUID(riid, &IID_IUnknown)
270         || IsEqualGUID(riid, &IID_IStorage))
271     {
272         IStorage_AddRef(iface);
273         *ppvObject = This;
274         return S_OK;
275     }
276
277     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
278     return E_NOINTERFACE;
279 }
280
281 ULONG WINAPI ITSS_IStorageImpl_AddRef(
282     IStorage* iface)
283 {
284     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
285     return ++(This->ref);
286 }
287
288 ULONG WINAPI ITSS_IStorageImpl_Release(
289     IStorage* iface)
290 {
291     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
292
293     ULONG ref = --This->ref;
294
295     if (ref == 0)
296     {
297         HeapFree(GetProcessHeap(), 0, This);
298     }
299
300     return ref;
301 }
302
303 HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
304     IStorage* iface,
305     LPCOLESTR pwcsName,
306     DWORD grfMode,
307     DWORD reserved1,
308     DWORD reserved2,
309     IStream** ppstm)
310 {
311     FIXME("\n");
312     return E_NOTIMPL;
313 }
314
315 HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
316     IStorage* iface,
317     LPCOLESTR pwcsName,
318     void* reserved1,
319     DWORD grfMode,
320     DWORD reserved2,
321     IStream** ppstm)
322 {
323     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
324     IStream_Impl *stm;
325     DWORD len;
326     struct chmUnitInfo ui;
327     int r;
328     WCHAR *path;
329
330     TRACE("%p %s %p %lu %lu %p\n", This, debugstr_w(pwcsName),
331           reserved1, grfMode, reserved2, ppstm );
332
333     len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
334     path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
335     strcpyW( path, This->dir );
336     if( pwcsName[0] == '/' )
337     {
338         WCHAR *p = &path[strlenW( path ) - 1];
339         while( ( path <= p ) && ( *p = '/' ) )
340             *p-- = 0;
341     }
342     strcatW( path, pwcsName );
343
344     TRACE("Resolving %s\n", debugstr_w(path));
345
346     r = chm_resolve_object(This->chmfile, path, &ui);
347     HeapFree( GetProcessHeap(), 0, path );
348
349     if( r != CHM_RESOLVE_SUCCESS )
350         return STG_E_FILENOTFOUND;
351
352     stm = ITSS_create_stream( This, &ui );
353     if( !stm )
354         return E_FAIL;
355
356     *ppstm = (IStream*) stm;
357
358     return S_OK;
359 }
360
361 HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
362     IStorage* iface,
363     LPCOLESTR pwcsName,
364     DWORD grfMode,
365     DWORD dwStgFmt,
366     DWORD reserved2,
367     IStorage** ppstg)
368 {
369     FIXME("\n");
370     return E_NOTIMPL;
371 }
372
373 HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
374     IStorage* iface,
375     LPCOLESTR pwcsName,
376     IStorage* pstgPriority,
377     DWORD grfMode,
378     SNB snbExclude,
379     DWORD reserved,
380     IStorage** ppstg)
381 {
382     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
383
384     FIXME("%p %s %p %lu %p %lu %p\n", This, debugstr_w(pwcsName),
385           pstgPriority, grfMode, snbExclude, reserved, ppstg);
386     return E_NOTIMPL;
387 }
388
389 HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
390     IStorage* iface,
391     DWORD ciidExclude,
392     const IID* rgiidExclude,
393     SNB snbExclude,
394     IStorage* pstgDest)
395 {
396     FIXME("\n");
397     return E_NOTIMPL;
398 }
399
400 HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
401     IStorage* iface,
402     LPCOLESTR pwcsName,
403     IStorage* pstgDest,
404     LPCOLESTR pwcsNewName,
405     DWORD grfFlags)
406 {
407     FIXME("\n");
408     return E_NOTIMPL;
409 }
410
411 HRESULT WINAPI ITSS_IStorageImpl_Commit(
412     IStorage* iface,
413     DWORD grfCommitFlags)
414 {
415     FIXME("\n");
416     return E_NOTIMPL;
417 }
418
419 HRESULT WINAPI ITSS_IStorageImpl_Revert(
420     IStorage* iface)
421 {
422     FIXME("\n");
423     return E_NOTIMPL;
424 }
425
426 static int ITSS_chm_enumerator(
427     struct chmFile *h,
428     struct chmUnitInfo *ui,
429     void *context)
430 {
431     struct enum_info *info;
432     IEnumSTATSTG_Impl* stgenum = context;
433
434     TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
435
436     info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
437     memcpy( &info->ui, ui, sizeof info->ui );
438
439     info->next = NULL;
440     info->prev = stgenum->last;
441     if( stgenum->last )
442         stgenum->last->next = info;
443     else
444         stgenum->first = info;
445     stgenum->last = info;
446     
447     return CHM_ENUMERATOR_CONTINUE;
448 }
449
450 HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
451     IStorage* iface,
452     DWORD reserved1,
453     void* reserved2,
454     DWORD reserved3,
455     IEnumSTATSTG** ppenum)
456 {
457     ITSS_IStorageImpl *This = (ITSS_IStorageImpl *)iface;
458     IEnumSTATSTG_Impl* stgenum;
459
460     TRACE("%p %ld %p %ld %p\n", This, reserved1, reserved2, reserved3, ppenum );
461
462     stgenum = ITSS_create_enum();
463     if( !stgenum )
464         return E_FAIL;
465
466     chm_enumerate_dir(This->chmfile,
467                   This->dir,
468                   CHM_ENUMERATE_ALL,
469                   ITSS_chm_enumerator,
470                   stgenum );
471
472     stgenum->current = stgenum->first;
473
474     *ppenum = (IEnumSTATSTG*) stgenum;
475
476     return S_OK;
477 }
478
479 HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
480     IStorage* iface,
481     LPCOLESTR pwcsName)
482 {
483     FIXME("\n");
484     return E_NOTIMPL;
485 }
486
487 HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
488     IStorage* iface,
489     LPCOLESTR pwcsOldName,
490     LPCOLESTR pwcsNewName)
491 {
492     FIXME("\n");
493     return E_NOTIMPL;
494 }
495
496 HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
497     IStorage* iface,
498     LPCOLESTR pwcsName,
499     const FILETIME* pctime,
500     const FILETIME* patime,
501     const FILETIME* pmtime)
502 {
503     FIXME("\n");
504     return E_NOTIMPL;
505 }
506
507 HRESULT WINAPI ITSS_IStorageImpl_SetClass(
508     IStorage* iface,
509     REFCLSID clsid)
510 {
511     FIXME("\n");
512     return E_NOTIMPL;
513 }
514
515 HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
516     IStorage* iface,
517     DWORD grfStateBits,
518     DWORD grfMask)
519 {
520     FIXME("\n");
521     return E_NOTIMPL;
522 }
523
524 HRESULT WINAPI ITSS_IStorageImpl_Stat(
525     IStorage* iface,
526     STATSTG* pstatstg,
527     DWORD grfStatFlag)
528 {
529     FIXME("\n");
530     return E_NOTIMPL;
531 }
532
533 static IStorageVtbl ITSS_IStorageImpl_Vtbl =
534 {
535     ITSS_IStorageImpl_QueryInterface,
536     ITSS_IStorageImpl_AddRef,
537     ITSS_IStorageImpl_Release,
538     ITSS_IStorageImpl_CreateStream,
539     ITSS_IStorageImpl_OpenStream,
540     ITSS_IStorageImpl_CreateStorage,
541     ITSS_IStorageImpl_OpenStorage,
542     ITSS_IStorageImpl_CopyTo,
543     ITSS_IStorageImpl_MoveElementTo,
544     ITSS_IStorageImpl_Commit,
545     ITSS_IStorageImpl_Revert,
546     ITSS_IStorageImpl_EnumElements,
547     ITSS_IStorageImpl_DestroyElement,
548     ITSS_IStorageImpl_RenameElement,
549     ITSS_IStorageImpl_SetElementTimes,
550     ITSS_IStorageImpl_SetClass,
551     ITSS_IStorageImpl_SetStateBits,
552     ITSS_IStorageImpl_Stat,
553 };
554
555 static HRESULT ITSS_create_chm_storage(
556       struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
557 {
558     ITSS_IStorageImpl *stg;
559     DWORD len;
560
561     TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
562
563     len = strlenW( dir ) + 1;
564     stg = HeapAlloc( GetProcessHeap(), 0, 
565                      sizeof (ITSS_IStorageImpl) + len*sizeof(WCHAR) );
566     stg->vtbl_IStorage = &ITSS_IStorageImpl_Vtbl;
567     stg->ref = 1;
568     stg->chmfile = chmfile;
569     strcpyW( stg->dir, dir );
570
571     *ppstgOpen = (IStorage*) stg;
572
573     return S_OK;
574 }
575
576 HRESULT ITSS_StgOpenStorage( 
577     const WCHAR* pwcsName,
578     IStorage* pstgPriority,
579     DWORD grfMode,
580     SNB snbExclude,
581     DWORD reserved,
582     IStorage** ppstgOpen)
583 {
584     struct chmFile *chmfile;
585     static const WCHAR szRoot[] = { '/', 0 };
586
587     TRACE("%s\n", debugstr_w(pwcsName) );
588
589     chmfile = chm_openW( pwcsName );
590     if( !chmfile )
591         return E_FAIL;
592
593     return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
594 }
595
596 /************************************************************************/
597
598 static HRESULT WINAPI ITSS_IStream_QueryInterface(
599     IStream* iface,
600     REFIID riid,
601     void** ppvObject)
602 {
603     IStream_Impl *This = (IStream_Impl *)iface;
604
605     if (IsEqualGUID(riid, &IID_IUnknown)
606         || IsEqualGUID(riid, &IID_ISequentialStream)
607         || IsEqualGUID(riid, &IID_IStream))
608     {
609         IStream_AddRef(iface);
610         *ppvObject = This;
611         return S_OK;
612     }
613
614     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
615     return E_NOINTERFACE;
616 }
617
618 static ULONG WINAPI ITSS_IStream_AddRef(
619     IStream* iface)
620 {
621     IStream_Impl *This = (IStream_Impl *)iface;
622     return ++(This->ref);
623 }
624
625 static ULONG WINAPI ITSS_IStream_Release(
626     IStream* iface)
627 {
628     IStream_Impl *This = (IStream_Impl *)iface;
629
630     ULONG ref = --This->ref;
631
632     if (ref == 0)
633     {
634         IStorage_Release( (IStorage*) This->stg );
635         HeapFree(GetProcessHeap(), 0, This);
636     }
637
638     return ref;
639 }
640
641 static HRESULT WINAPI ITSS_IStream_Read(
642         IStream* iface,
643         void* pv,
644         ULONG cb,
645         ULONG* pcbRead)
646 {
647     IStream_Impl *This = (IStream_Impl *)iface;
648     ULONG count;
649
650     TRACE("%p %p %lu %p\n", This, pv, cb, pcbRead);
651
652     count = chm_retrieve_object(This->stg->chmfile, 
653                           &This->ui, pv, This->addr, cb);
654     This->addr += count;
655     if( pcbRead )
656         *pcbRead = count;
657     
658     return S_OK;
659 }
660
661 static HRESULT WINAPI ITSS_IStream_Write(
662         IStream* iface,
663         const void* pv,
664         ULONG cb,
665         ULONG* pcbWritten)
666 {
667     FIXME("\n");
668     return E_NOTIMPL;
669 }
670
671 static HRESULT WINAPI ITSS_IStream_Seek(
672         IStream* iface,
673         LARGE_INTEGER dlibMove,
674         DWORD dwOrigin,
675         ULARGE_INTEGER* plibNewPosition)
676 {
677     IStream_Impl *This = (IStream_Impl *)iface;
678     LONGLONG newpos;
679
680     TRACE("%p %s %lu %p\n", This,
681           wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
682
683     newpos = This->addr;
684     switch( dwOrigin )
685     {
686     case STREAM_SEEK_CUR:
687         newpos = This->addr + dlibMove.QuadPart;
688         break;
689     case STREAM_SEEK_SET:
690         newpos = dlibMove.QuadPart;
691         break;
692     case STREAM_SEEK_END:
693         newpos = This->ui.length + dlibMove.QuadPart;
694         break;
695     }
696
697     if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
698         return STG_E_INVALIDPOINTER;
699
700     This->addr = newpos;
701     if( plibNewPosition )
702         plibNewPosition->QuadPart = This->addr;
703
704     return S_OK;
705 }
706
707 static HRESULT WINAPI ITSS_IStream_SetSize(
708         IStream* iface,
709         ULARGE_INTEGER libNewSize)
710 {
711     FIXME("\n");
712     return E_NOTIMPL;
713 }
714
715 static HRESULT WINAPI ITSS_IStream_CopyTo(
716         IStream* iface,
717         IStream* pstm,
718         ULARGE_INTEGER cb,
719         ULARGE_INTEGER* pcbRead,
720         ULARGE_INTEGER* pcbWritten)
721 {
722     FIXME("\n");
723     return E_NOTIMPL;
724 }
725
726 static HRESULT WINAPI ITSS_IStream_Commit(
727         IStream* iface,
728         DWORD grfCommitFlags)
729 {
730     FIXME("\n");
731     return E_NOTIMPL;
732 }
733
734 static HRESULT WINAPI ITSS_IStream_Revert(
735         IStream* iface)
736 {
737     FIXME("\n");
738     return E_NOTIMPL;
739 }
740
741 static HRESULT WINAPI ITSS_IStream_LockRegion(
742         IStream* iface,
743         ULARGE_INTEGER libOffset,
744         ULARGE_INTEGER cb,
745         DWORD dwLockType)
746 {
747     FIXME("\n");
748     return E_NOTIMPL;
749 }
750
751 static HRESULT WINAPI ITSS_IStream_UnlockRegion(
752         IStream* iface,
753         ULARGE_INTEGER libOffset,
754         ULARGE_INTEGER cb,
755         DWORD dwLockType)
756 {
757     FIXME("\n");
758     return E_NOTIMPL;
759 }
760
761 static HRESULT WINAPI ITSS_IStream_Stat(
762         IStream* iface,
763         STATSTG* pstatstg,
764         DWORD grfStatFlag)
765 {
766     IStream_Impl *This = (IStream_Impl *)iface;
767
768     TRACE("%p %p %ld\n", This, pstatstg, grfStatFlag);
769
770     memset( pstatstg, 0, sizeof *pstatstg );
771     if( !( grfStatFlag & STATFLAG_NONAME ) )
772     {
773         FIXME("copy the name\n");
774     }
775     pstatstg->type = STGTY_STREAM;
776     pstatstg->cbSize.QuadPart = This->ui.length;
777     pstatstg->grfMode = STGM_READ;
778     memcpy( &pstatstg->clsid, &CLSID_ITStorage, sizeof (CLSID) );
779
780     return S_OK;
781 }
782
783 static HRESULT WINAPI ITSS_IStream_Clone(
784         IStream* iface,
785         IStream** ppstm)
786 {
787     FIXME("\n");
788     return E_NOTIMPL;
789 }
790
791 struct IStreamVtbl ITSS_IStream_vtbl =
792 {
793     ITSS_IStream_QueryInterface,
794     ITSS_IStream_AddRef,
795     ITSS_IStream_Release,
796     ITSS_IStream_Read,
797     ITSS_IStream_Write,
798     ITSS_IStream_Seek,
799     ITSS_IStream_SetSize,
800     ITSS_IStream_CopyTo,
801     ITSS_IStream_Commit,
802     ITSS_IStream_Revert,
803     ITSS_IStream_LockRegion,
804     ITSS_IStream_UnlockRegion,
805     ITSS_IStream_Stat,
806     ITSS_IStream_Clone,
807 };
808
809 static IStream_Impl *ITSS_create_stream(
810            ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
811 {
812     IStream_Impl *stm;
813
814     stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
815     stm->vtbl_IStream = &ITSS_IStream_vtbl;
816     stm->ref = 1;
817     stm->addr = 0;
818     memcpy( &stm->ui, ui, sizeof stm->ui );
819     stm->stg = stg;
820     IStorage_AddRef( (IStorage*) stg );
821
822     TRACE(" -> %p\n", stm );
823
824     return stm;
825 }