shlwapi/tests: Enumerate the STGM sharing modes in the SHCreateStreamOnFile tests.
[wine] / dlls / shlwapi / istream.c
1 /*
2  * SHLWAPI IStream functions
3  *
4  * Copyright 2002 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include <stdarg.h>
21 #include <string.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "winnls.h"
31 #define NO_SHLWAPI_REG
32 #define NO_SHLWAPI_PATH
33 #include "shlwapi.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
37
38 #define STGM_ACCESS_MODE(stgm)   ((stgm)&0x0000f)
39 #define STGM_SHARE_MODE(stgm)    ((stgm)&0x000f0)
40 #define STGM_CREATE_MODE(stgm)   ((stgm)&0x0f000)
41
42 /* Layout of ISHFileStream object */
43 typedef struct
44 {
45   const IStreamVtbl *lpVtbl;
46   LONG     ref;
47   HANDLE   hFile;
48   DWORD    dwMode;
49   LPOLESTR lpszPath;
50   DWORD    type;
51   DWORD    grfStateBits;
52 } ISHFileStream;
53
54 static HRESULT WINAPI IStream_fnCommit(IStream*,DWORD);
55
56
57 /**************************************************************************
58 *  IStream_fnQueryInterface
59 */
60 static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj)
61 {
62   ISHFileStream *This = (ISHFileStream *)iface;
63
64   TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj);
65
66   *ppvObj = NULL;
67
68   if(IsEqualIID(riid, &IID_IUnknown) ||
69      IsEqualIID(riid, &IID_IStream))
70   {
71     *ppvObj = This;
72     IStream_AddRef(iface);
73     return S_OK;
74   }
75   return E_NOINTERFACE;
76 }
77
78 /**************************************************************************
79 *  IStream_fnAddRef
80 */
81 static ULONG WINAPI IStream_fnAddRef(IStream *iface)
82 {
83   ISHFileStream *This = (ISHFileStream *)iface;
84   ULONG refCount = InterlockedIncrement(&This->ref);
85   
86   TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
87
88   return refCount;
89 }
90
91 /**************************************************************************
92 *  IStream_fnRelease
93 */
94 static ULONG WINAPI IStream_fnRelease(IStream *iface)
95 {
96   ISHFileStream *This = (ISHFileStream *)iface;
97   ULONG refCount = InterlockedDecrement(&This->ref); 
98
99   TRACE("(%p)->(ref before=%u)\n",This, refCount + 1);
100   
101   if (!refCount)
102   {
103     IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
104     LocalFree((HLOCAL)This->lpszPath);
105     CloseHandle(This->hFile);
106     HeapFree(GetProcessHeap(), 0, This);
107   }
108   
109   return refCount;
110 }
111
112 /**************************************************************************
113  * IStream_fnRead
114  */
115 static HRESULT WINAPI IStream_fnRead(IStream *iface, void* pv, ULONG cb, ULONG* pcbRead)
116 {
117   ISHFileStream *This = (ISHFileStream *)iface;
118   DWORD dwRead = 0;
119
120   TRACE("(%p,%p,0x%08x,%p)\n", This, pv, cb, pcbRead);
121
122   if (!ReadFile(This->hFile, pv, cb, &dwRead, NULL))
123   {
124     WARN("error %d reading file\n", GetLastError());
125     return S_FALSE;
126   }
127   if (pcbRead)
128     *pcbRead = dwRead;
129   return S_OK;
130 }
131
132 /**************************************************************************
133  * IStream_fnWrite
134  */
135 static HRESULT WINAPI IStream_fnWrite(IStream *iface, const void* pv, ULONG cb, ULONG* pcbWritten)
136 {
137   ISHFileStream *This = (ISHFileStream *)iface;
138   DWORD dwWritten = 0;
139
140   TRACE("(%p,%p,0x%08x,%p)\n", This, pv, cb, pcbWritten);
141
142   switch (STGM_ACCESS_MODE(This->dwMode))
143   {
144   case STGM_WRITE:
145   case STGM_READWRITE:
146     break;
147   default:
148     return STG_E_ACCESSDENIED;
149   }
150
151   if (!WriteFile(This->hFile, pv, cb, &dwWritten, NULL))
152     return HRESULT_FROM_WIN32(GetLastError());
153
154   if (pcbWritten)
155     *pcbWritten = dwWritten;
156   return S_OK;
157 }
158
159 /**************************************************************************
160  *  IStream_fnSeek
161  */
162 static HRESULT WINAPI IStream_fnSeek(IStream *iface, LARGE_INTEGER dlibMove,
163                                      DWORD dwOrigin, ULARGE_INTEGER* pNewPos)
164 {
165   ISHFileStream *This = (ISHFileStream *)iface;
166   DWORD dwPos;
167
168   TRACE("(%p,%d,%d,%p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos);
169
170   IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
171   dwPos = SetFilePointer(This->hFile, dlibMove.u.LowPart, NULL, dwOrigin);
172   if( dwPos == INVALID_SET_FILE_POINTER )
173      return HRESULT_FROM_WIN32(GetLastError());
174
175   if (pNewPos)
176   {
177     pNewPos->u.HighPart = 0;
178     pNewPos->u.LowPart = dwPos;
179   }
180   return S_OK;
181 }
182
183 /**************************************************************************
184  * IStream_fnSetSize
185  */
186 static HRESULT WINAPI IStream_fnSetSize(IStream *iface, ULARGE_INTEGER libNewSize)
187 {
188   ISHFileStream *This = (ISHFileStream *)iface;
189
190   TRACE("(%p,%d)\n", This, libNewSize.u.LowPart);
191
192   IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
193   if( ! SetFilePointer( This->hFile, libNewSize.QuadPart, NULL, FILE_BEGIN ) )
194     return E_FAIL;
195
196   if( ! SetEndOfFile( This->hFile ) )
197     return E_FAIL;
198
199   return S_OK;
200 }
201
202 /**************************************************************************
203  * IStream_fnCopyTo
204  */
205 static HRESULT WINAPI IStream_fnCopyTo(IStream *iface, IStream* pstm, ULARGE_INTEGER cb,
206                                        ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten)
207 {
208   ISHFileStream *This = (ISHFileStream *)iface;
209   char copyBuff[1024];
210   ULONGLONG ulSize;
211   HRESULT hRet = S_OK;
212
213   TRACE("(%p,%p,%d,%p,%p)\n", This, pstm, cb.u.LowPart, pcbRead, pcbWritten);
214
215   if (pcbRead)
216     pcbRead->QuadPart = 0;
217   if (pcbWritten)
218     pcbWritten->QuadPart = 0;
219
220   if (!pstm)
221     return S_OK;
222
223   IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
224
225   /* Copy data */
226   ulSize = cb.QuadPart;
227   while (ulSize)
228   {
229     ULONG ulLeft, ulAmt;
230
231     ulLeft = ulSize > sizeof(copyBuff) ? sizeof(copyBuff) : ulSize;
232
233     /* Read */
234     hRet = IStream_fnRead(iface, copyBuff, ulLeft, &ulAmt);
235     if (pcbRead)
236       pcbRead->QuadPart += ulAmt;
237     if (FAILED(hRet) || ulAmt != ulLeft)
238       break;
239
240     /* Write */
241     hRet = IStream_fnWrite(pstm, copyBuff, ulLeft, &ulAmt);
242     if (pcbWritten)
243       pcbWritten->QuadPart += ulAmt;
244     if (FAILED(hRet) || ulAmt != ulLeft)
245       break;
246
247     ulSize -= ulLeft;
248   }
249   return hRet;
250 }
251
252 /**************************************************************************
253  * IStream_fnCommit
254  */
255 static HRESULT WINAPI IStream_fnCommit(IStream *iface, DWORD grfCommitFlags)
256 {
257   ISHFileStream *This = (ISHFileStream *)iface;
258
259   TRACE("(%p,%d)\n", This, grfCommitFlags);
260   /* Currently unbuffered: This function is not needed */
261   return S_OK;
262 }
263
264 /**************************************************************************
265  * IStream_fnRevert
266  */
267 static HRESULT WINAPI IStream_fnRevert(IStream *iface)
268 {
269   ISHFileStream *This = (ISHFileStream *)iface;
270
271   TRACE("(%p)\n", This);
272   return E_NOTIMPL;
273 }
274
275 /**************************************************************************
276  * IStream_fnLockUnlockRegion
277  */
278 static HRESULT WINAPI IStream_fnLockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
279                                                  ULARGE_INTEGER cb, DWORD dwLockType)
280 {
281   ISHFileStream *This = (ISHFileStream *)iface;
282   TRACE("(%p,%d,%d,%d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
283   return E_NOTIMPL;
284 }
285
286 /*************************************************************************
287  * IStream_fnStat
288  */
289 static HRESULT WINAPI IStream_fnStat(IStream *iface, STATSTG* lpStat,
290                                      DWORD grfStatFlag)
291 {
292   ISHFileStream *This = (ISHFileStream *)iface;
293   BY_HANDLE_FILE_INFORMATION fi;
294   HRESULT hRet = S_OK;
295
296   TRACE("(%p,%p,%d)\n", This, lpStat, grfStatFlag);
297
298   if (!grfStatFlag)
299     hRet = STG_E_INVALIDPOINTER;
300   else
301   {
302     memset(&fi, 0, sizeof(fi));
303     GetFileInformationByHandle(This->hFile, &fi);
304
305     if (grfStatFlag & STATFLAG_NONAME)
306       lpStat->pwcsName = NULL;
307     else
308       lpStat->pwcsName = StrDupW(This->lpszPath);
309     lpStat->type = This->type;
310     lpStat->cbSize.u.LowPart = fi.nFileSizeLow;
311     lpStat->cbSize.u.HighPart = fi.nFileSizeHigh;
312     lpStat->mtime = fi.ftLastWriteTime;
313     lpStat->ctime = fi.ftCreationTime;
314     lpStat->atime = fi.ftLastAccessTime;
315     lpStat->grfMode = This->dwMode;
316     lpStat->grfLocksSupported = 0;
317     memcpy(&lpStat->clsid, &IID_IStream, sizeof(CLSID));
318     lpStat->grfStateBits = This->grfStateBits;
319     lpStat->reserved = 0;
320   }
321   return hRet;
322 }
323
324 /*************************************************************************
325  * IStream_fnClone
326  */
327 static HRESULT WINAPI IStream_fnClone(IStream *iface, IStream** ppstm)
328 {
329   ISHFileStream *This = (ISHFileStream *)iface;
330
331   TRACE("(%p)\n",This);
332   if (ppstm)
333     *ppstm = NULL;
334   return E_NOTIMPL;
335 }
336
337 static const IStreamVtbl SHLWAPI_fsVTable =
338 {
339   IStream_fnQueryInterface,
340   IStream_fnAddRef,
341   IStream_fnRelease,
342   IStream_fnRead,
343   IStream_fnWrite,
344   IStream_fnSeek,
345   IStream_fnSetSize,
346   IStream_fnCopyTo,
347   IStream_fnCommit,
348   IStream_fnRevert,
349   IStream_fnLockUnlockRegion,
350   IStream_fnLockUnlockRegion,
351   IStream_fnStat,
352   IStream_fnClone
353 };
354
355 /**************************************************************************
356  * IStream_Create
357  *
358  * Internal helper: Create and initialise a new file stream object.
359  */
360 static IStream *IStream_Create(LPCWSTR lpszPath, HANDLE hFile, DWORD dwMode)
361 {
362  ISHFileStream* fileStream;
363
364  fileStream = HeapAlloc(GetProcessHeap(), 0, sizeof(ISHFileStream));
365
366  if (fileStream)
367  {
368    fileStream->lpVtbl = &SHLWAPI_fsVTable;
369    fileStream->ref = 1;
370    fileStream->hFile = hFile;
371    fileStream->dwMode = dwMode;
372    fileStream->lpszPath = StrDupW(lpszPath);
373    fileStream->type = 0; /* FIXME */
374    fileStream->grfStateBits = 0; /* FIXME */
375  }
376  TRACE ("Returning %p\n", fileStream);
377  return (IStream *)fileStream;
378 }
379
380 /*************************************************************************
381  * SHCreateStreamOnFileEx   [SHLWAPI.@]
382  *
383  * Create a stream on a file.
384  *
385  * PARAMS
386  *  lpszPath     [I] Path of file to create stream on
387  *  dwMode       [I] Mode to create stream in
388  *  dwAttributes [I] Attributes of the file
389  *  bCreate      [I] Whether to create the file if it doesn't exist
390  *  lpTemplate   [I] Reserved, must be NULL
391  *  lppStream    [O] Destination for created stream
392  *
393  * RETURNS
394  * Success: S_OK. lppStream contains the new stream object
395  * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
396  *
397  * NOTES
398  *  This function is available in Unicode only.
399  */
400 HRESULT WINAPI SHCreateStreamOnFileEx(LPCWSTR lpszPath, DWORD dwMode,
401                                       DWORD dwAttributes, BOOL bCreate,
402                                       IStream *lpTemplate, IStream **lppStream)
403 {
404   DWORD dwAccess, dwShare, dwCreate;
405   HANDLE hFile;
406
407   TRACE("(%s,%d,0x%08X,%d,%p,%p)\n", debugstr_w(lpszPath), dwMode,
408         dwAttributes, bCreate, lpTemplate, lppStream);
409
410   if (!lpszPath || !lppStream || lpTemplate)
411     return E_INVALIDARG;
412
413   *lppStream = NULL;
414
415   /* Access */
416   switch (STGM_ACCESS_MODE(dwMode))
417   {
418   case STGM_READWRITE:
419     dwAccess = GENERIC_READ|GENERIC_WRITE;
420     break;
421   case STGM_WRITE:
422     dwAccess = GENERIC_WRITE;
423     break;
424   case STGM_READ:
425     dwAccess = GENERIC_READ;
426     break;
427   default:
428     return E_INVALIDARG;
429   }
430
431   /* Sharing */
432   switch (STGM_SHARE_MODE(dwMode))
433   {
434   case 0:
435     dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE;
436     break;
437   case STGM_SHARE_DENY_READ:
438     dwShare = FILE_SHARE_WRITE;
439     break;
440   case STGM_SHARE_DENY_WRITE:
441     dwShare = FILE_SHARE_READ;
442     break;
443   case STGM_SHARE_EXCLUSIVE:
444     dwShare = 0;
445     break;
446   case STGM_SHARE_DENY_NONE:
447     dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE;
448     break;
449   default:
450     return E_INVALIDARG;
451   }
452
453   switch(STGM_CREATE_MODE(dwMode))
454   {
455   case STGM_FAILIFTHERE:
456     dwCreate = bCreate ? CREATE_NEW : OPEN_EXISTING;
457     break;
458   case STGM_CREATE:
459     dwCreate = CREATE_ALWAYS;
460     break;
461   default:
462     return E_INVALIDARG;
463   }
464
465   /* Open HANDLE to file */
466   hFile = CreateFileW(lpszPath, dwAccess, dwShare, NULL, dwCreate,
467                       dwAttributes, 0);
468
469   if(hFile == INVALID_HANDLE_VALUE)
470     return HRESULT_FROM_WIN32(GetLastError());
471
472   *lppStream = IStream_Create(lpszPath, hFile, dwMode);
473
474   if(!*lppStream)
475   {
476     CloseHandle(hFile);
477     return E_OUTOFMEMORY;
478   }
479   return S_OK;
480 }
481
482 /*************************************************************************
483  * SHCreateStreamOnFileW   [SHLWAPI.@]
484  *
485  * See SHCreateStreamOnFileA.
486  */
487 HRESULT WINAPI SHCreateStreamOnFileW(LPCWSTR lpszPath, DWORD dwMode,
488                                    IStream **lppStream)
489 {
490   TRACE("(%s,%d,%p)\n", debugstr_w(lpszPath), dwMode, lppStream);
491
492   if (!lpszPath || !lppStream)
493     return E_INVALIDARG;
494
495   if ((dwMode & (STGM_CONVERT|STGM_DELETEONRELEASE|STGM_TRANSACTED)) != 0)
496     return E_INVALIDARG;
497
498   return SHCreateStreamOnFileEx(lpszPath, dwMode, 0, FALSE, NULL, lppStream);
499 }
500
501 /*************************************************************************
502  * SHCreateStreamOnFileA   [SHLWAPI.@]
503  *
504  * Create a stream on a file.
505  *
506  * PARAMS
507  *  lpszPath  [I] Path of file to create stream on
508  *  dwMode    [I] Mode to create stream in
509  *  lppStream [O] Destination for created IStream object
510  *
511  * RETURNS
512  * Success: S_OK. lppStream contains the new IStream object
513  * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
514  */
515 HRESULT WINAPI SHCreateStreamOnFileA(LPCSTR lpszPath, DWORD dwMode,
516                                      IStream **lppStream)
517 {
518   WCHAR szPath[MAX_PATH];
519
520   TRACE("(%s,%d,%p)\n", debugstr_a(lpszPath), dwMode, lppStream);
521
522   if (!lpszPath)
523     return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
524
525   MultiByteToWideChar(0, 0, lpszPath, -1, szPath, MAX_PATH);
526   return SHCreateStreamOnFileW(szPath, dwMode, lppStream);
527 }
528
529 /*************************************************************************
530  * @       [SHLWAPI.184]
531  *
532  * Call IStream_Read() on a stream.
533  *
534  * PARAMS
535  *  lpStream [I] IStream object
536  *  lpvDest  [O] Destination for data read
537  *  ulSize   [I] Size of data to read
538  *
539  * RETURNS
540  *  Success: S_OK. ulSize bytes have been read from the stream into lpvDest.
541  *  Failure: An HRESULT error code, or E_FAIL if the read succeeded but the
542  *           number of bytes read does not match.
543  */
544 HRESULT WINAPI SHIStream_Read(IStream *lpStream, LPVOID lpvDest, ULONG ulSize)
545 {
546   ULONG ulRead;
547   HRESULT hRet;
548
549   TRACE("(%p,%p,%d)\n", lpStream, lpvDest, ulSize);
550
551   hRet = IStream_Read(lpStream, lpvDest, ulSize, &ulRead);
552
553   if (SUCCEEDED(hRet) && ulRead != ulSize)
554     hRet = E_FAIL;
555   return hRet;
556 }
557
558 /*************************************************************************
559  * @       [SHLWAPI.166]
560  *
561  * Determine if a stream has 0 length.
562  *
563  * PARAMS
564  *  lpStream [I] IStream object
565  *
566  * RETURNS
567  *  TRUE:  If the stream has 0 length
568  *  FALSE: Otherwise.
569  */
570 BOOL WINAPI SHIsEmptyStream(IStream *lpStream)
571 {
572   STATSTG statstg;
573   BOOL bRet = TRUE;
574
575   TRACE("(%p)\n", lpStream);
576
577   memset(&statstg, 0, sizeof(statstg));
578
579   if(SUCCEEDED(IStream_Stat(lpStream, &statstg, 1)))
580   {
581     if(statstg.cbSize.QuadPart)
582       bRet = FALSE; /* Non-Zero */
583   }
584   else
585   {
586     DWORD dwDummy;
587
588     /* Try to read from the stream */
589     if(SUCCEEDED(SHIStream_Read(lpStream, &dwDummy, sizeof(dwDummy))))
590     {
591       LARGE_INTEGER zero;
592       zero.QuadPart = 0;
593
594       IStream_Seek(lpStream, zero, 0, NULL);
595       bRet = FALSE; /* Non-Zero */
596     }
597   }
598   return bRet;
599 }
600
601 /*************************************************************************
602  * @       [SHLWAPI.212]
603  *
604  * Call IStream_Write() on a stream.
605  *
606  * PARAMS
607  *  lpStream [I] IStream object
608  *  lpvSrc   [I] Source for data to write
609  *  ulSize   [I] Size of data
610  *
611  * RETURNS
612  *  Success: S_OK. ulSize bytes have been written to the stream from lpvSrc.
613  *  Failure: An HRESULT error code, or E_FAIL if the write succeeded but the
614  *           number of bytes written does not match.
615  */
616 HRESULT WINAPI SHIStream_Write(IStream *lpStream, LPCVOID lpvSrc, ULONG ulSize)
617 {
618   ULONG ulWritten;
619   HRESULT hRet;
620
621   TRACE("(%p,%p,%d)\n", lpStream, lpvSrc, ulSize);
622
623   hRet = IStream_Write(lpStream, lpvSrc, ulSize, &ulWritten);
624
625   if (SUCCEEDED(hRet) && ulWritten != ulSize)
626     hRet = E_FAIL;
627
628   return hRet;
629 }
630
631 /*************************************************************************
632  * @       [SHLWAPI.213]
633  *
634  * Seek to the start of a stream.
635  *
636  * PARAMS
637  *  lpStream [I] IStream object
638  *
639  * RETURNS
640  *  Success: S_OK. The current position within the stream is updated
641  *  Failure: An HRESULT error code.
642  */
643 HRESULT WINAPI IStream_Reset(IStream *lpStream)
644 {
645   LARGE_INTEGER zero;
646   TRACE("(%p)\n", lpStream);
647   zero.QuadPart = 0;
648   return IStream_Seek(lpStream, zero, 0, NULL);
649 }
650
651 /*************************************************************************
652  * @       [SHLWAPI.214]
653  *
654  * Get the size of a stream.
655  *
656  * PARAMS
657  *  lpStream [I] IStream object
658  *  lpulSize [O] Destination for size
659  *
660  * RETURNS
661  *  Success: S_OK. lpulSize contains the size of the stream.
662  *  Failure: An HRESULT error code.
663  */
664 HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize)
665 {
666   STATSTG statstg;
667   HRESULT hRet;
668
669   TRACE("(%p,%p)\n", lpStream, lpulSize);
670
671   memset(&statstg, 0, sizeof(statstg));
672
673   hRet = IStream_Stat(lpStream, &statstg, 1);
674
675   if (SUCCEEDED(hRet) && lpulSize)
676     *lpulSize = statstg.cbSize;
677   return hRet;
678 }