blob: ac0263cadf59b446a1c13842891064eeb499cc5a [file] [log] [blame]
Vincent Povirk14f8f9d2010-07-17 11:56:17 -05001/******************************************************************************
2 *
3 * File-based ILockBytes implementation
4 *
5 * Copyright 1999 Thuy Nguyen
6 * Copyright 2010 Vincent Povirk for CodeWeavers
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 <assert.h>
24#include <stdlib.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <string.h>
28#include <limits.h>
29
30#define COBJMACROS
31#define NONAMELESSUNION
32#define NONAMELESSSTRUCT
33
34#include "windef.h"
35#include "winbase.h"
36#include "winuser.h"
37#include "winerror.h"
38#include "objbase.h"
39#include "ole2.h"
40
41#include "storage32.h"
42
43#include "wine/debug.h"
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -050044#include "wine/unicode.h"
Vincent Povirk14f8f9d2010-07-17 11:56:17 -050045
46WINE_DEFAULT_DEBUG_CHANNEL(storage);
47
48typedef struct FileLockBytesImpl
49{
Michael Stefaniuc2605b762010-12-08 22:59:46 +010050 ILockBytes ILockBytes_iface;
Vincent Povirk14f8f9d2010-07-17 11:56:17 -050051 LONG ref;
52 ULARGE_INTEGER filesize;
53 HANDLE hfile;
54 DWORD flProtect;
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -050055 LPWSTR pwcsName;
Vincent Povirk14f8f9d2010-07-17 11:56:17 -050056} FileLockBytesImpl;
57
58static const ILockBytesVtbl FileLockBytesImpl_Vtbl;
59
Michael Stefaniuc2605b762010-12-08 22:59:46 +010060static inline FileLockBytesImpl *impl_from_ILockBytes(ILockBytes *iface)
61{
62 return CONTAINING_RECORD(iface, FileLockBytesImpl, ILockBytes_iface);
63}
64
Vincent Povirk14f8f9d2010-07-17 11:56:17 -050065/***********************************************************
66 * Prototypes for private methods
67 */
68
69/* Note that this evaluates a and b multiple times, so don't
70 * pass expressions with side effects. */
71#define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b))
72
73/****************************************************************************
74 * GetProtectMode
75 *
76 * This function will return a protection mode flag for a file-mapping object
77 * from the open flags of a file.
78 */
79static DWORD GetProtectMode(DWORD openFlags)
80{
81 switch(STGM_ACCESS_MODE(openFlags))
82 {
83 case STGM_WRITE:
84 case STGM_READWRITE:
85 return PAGE_READWRITE;
86 }
87 return PAGE_READONLY;
88}
89
90/******************************************************************************
91 * FileLockBytesImpl_Construct
92 *
93 * Initialize a big block object supported by a file.
94 */
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -050095HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes)
Vincent Povirk14f8f9d2010-07-17 11:56:17 -050096{
97 FileLockBytesImpl *This;
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -050098 WCHAR fullpath[MAX_PATH];
Vincent Povirk14f8f9d2010-07-17 11:56:17 -050099
100 if (hFile == INVALID_HANDLE_VALUE)
101 return E_FAIL;
102
103 This = HeapAlloc(GetProcessHeap(), 0, sizeof(FileLockBytesImpl));
104
105 if (!This)
106 return E_OUTOFMEMORY;
107
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100108 This->ILockBytes_iface.lpVtbl = &FileLockBytesImpl_Vtbl;
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500109 This->ref = 1;
110 This->hfile = hFile;
111 This->filesize.u.LowPart = GetFileSize(This->hfile,
112 &This->filesize.u.HighPart);
113 This->flProtect = GetProtectMode(openFlags);
114
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -0500115 if(pwcsName) {
116 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
117 {
118 lstrcpynW(fullpath, pwcsName, MAX_PATH);
119 }
120 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
121 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
122 if (!This->pwcsName)
123 {
124 HeapFree(GetProcessHeap(), 0, This);
125 return E_OUTOFMEMORY;
126 }
127 strcpyW(This->pwcsName, fullpath);
128 }
129 else
130 This->pwcsName = NULL;
131
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500132 TRACE("file len %u\n", This->filesize.u.LowPart);
133
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100134 *pLockBytes = &This->ILockBytes_iface;
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500135
136 return S_OK;
137}
138
139/* ILockByte Interfaces */
140
141static HRESULT WINAPI FileLockBytesImpl_QueryInterface(ILockBytes *iface, REFIID riid,
142 void **ppvObject)
143{
144 if (IsEqualIID(riid, &IID_ILockBytes) || IsEqualIID(riid, &IID_ILockBytes))
145 *ppvObject = iface;
146 else
147 {
148 *ppvObject = NULL;
149 return E_NOINTERFACE;
150 }
151
152 IUnknown_AddRef((IUnknown*)*ppvObject);
153
154 return S_OK;
155}
156
157static ULONG WINAPI FileLockBytesImpl_AddRef(ILockBytes *iface)
158{
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100159 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500160 return InterlockedIncrement(&This->ref);
161}
162
163static ULONG WINAPI FileLockBytesImpl_Release(ILockBytes *iface)
164{
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100165 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500166 ULONG ref;
167
168 ref = InterlockedDecrement(&This->ref);
169
170 if (ref == 0)
171 {
172 CloseHandle(This->hfile);
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -0500173 HeapFree(GetProcessHeap(), 0, This->pwcsName);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500174 HeapFree(GetProcessHeap(), 0, This);
175 }
176
177 return ref;
178}
179
180/******************************************************************************
181 * This method is part of the ILockBytes interface.
182 *
183 * It reads a block of information from the byte array at the specified
184 * offset.
185 *
186 * See the documentation of ILockBytes for more info.
187 */
188static HRESULT WINAPI FileLockBytesImpl_ReadAt(
189 ILockBytes* iface,
190 ULARGE_INTEGER ulOffset, /* [in] */
191 void* pv, /* [length_is][size_is][out] */
192 ULONG cb, /* [in] */
193 ULONG* pcbRead) /* [out] */
194{
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100195 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500196 ULONG bytes_left = cb;
197 LPBYTE readPtr = pv;
198 BOOL ret;
199 LARGE_INTEGER offset;
200 ULONG cbRead;
201
202 TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbRead);
203
204 /* verify a sane environment */
205 if (!This) return E_FAIL;
206
207 if (pcbRead)
208 *pcbRead = 0;
209
210 offset.QuadPart = ulOffset.QuadPart;
211
212 ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
213
214 if (!ret)
215 return STG_E_READFAULT;
216
217 while (bytes_left)
218 {
219 ret = ReadFile(This->hfile, readPtr, bytes_left, &cbRead, NULL);
220
221 if (!ret || cbRead == 0)
222 return STG_E_READFAULT;
223
224 if (pcbRead)
225 *pcbRead += cbRead;
226
227 bytes_left -= cbRead;
228 readPtr += cbRead;
229 }
230
231 TRACE("finished\n");
232 return S_OK;
233}
234
235/******************************************************************************
236 * This method is part of the ILockBytes interface.
237 *
238 * It writes the specified bytes at the specified offset.
239 * position. If the file is too small, it will be resized.
240 *
241 * See the documentation of ILockBytes for more info.
242 */
243static HRESULT WINAPI FileLockBytesImpl_WriteAt(
244 ILockBytes* iface,
245 ULARGE_INTEGER ulOffset, /* [in] */
246 const void* pv, /* [size_is][in] */
247 ULONG cb, /* [in] */
248 ULONG* pcbWritten) /* [out] */
249{
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100250 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500251 ULONG size_needed = ulOffset.u.LowPart + cb;
252 ULONG bytes_left = cb;
253 const BYTE *writePtr = pv;
254 BOOL ret;
255 LARGE_INTEGER offset;
256 ULONG cbWritten;
257
258 TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbWritten);
259
260 /* verify a sane environment */
261 if (!This) return E_FAIL;
262
263 if (This->flProtect != PAGE_READWRITE)
264 return STG_E_ACCESSDENIED;
265
266 if (pcbWritten)
267 *pcbWritten = 0;
268
269 if (size_needed > This->filesize.u.LowPart)
270 {
271 ULARGE_INTEGER newSize;
272 newSize.u.HighPart = 0;
273 newSize.u.LowPart = size_needed;
274 ILockBytes_SetSize(iface, newSize);
275 }
276
277 offset.QuadPart = ulOffset.QuadPart;
278
279 ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
280
281 if (!ret)
282 return STG_E_READFAULT;
283
284 while (bytes_left)
285 {
286 ret = WriteFile(This->hfile, writePtr, bytes_left, &cbWritten, NULL);
287
288 if (!ret)
289 return STG_E_READFAULT;
290
291 if (pcbWritten)
292 *pcbWritten += cbWritten;
293
294 bytes_left -= cbWritten;
295 writePtr += cbWritten;
296 }
297
298 TRACE("finished\n");
299 return S_OK;
300}
301
302static HRESULT WINAPI FileLockBytesImpl_Flush(ILockBytes* iface)
303{
304 return S_OK;
305}
306
307/******************************************************************************
308 * ILockBytes_SetSize
309 *
310 * Sets the size of the file.
311 *
312 */
313static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGER newSize)
314{
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100315 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500316 HRESULT hr = S_OK;
317 LARGE_INTEGER newpos;
318
319 if (This->filesize.u.LowPart == newSize.u.LowPart)
320 return hr;
321
322 TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart);
323
324 newpos.QuadPart = newSize.QuadPart;
325 if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN))
326 {
327 SetEndOfFile(This->hfile);
328 }
329
330 This->filesize = newSize;
331 return hr;
332}
333
334static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface,
335 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
336{
337 FIXME("stub\n");
338 return E_NOTIMPL;
339}
340
341static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface,
342 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
343{
344 FIXME("stub\n");
345 return E_NOTIMPL;
346}
347
348static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface,
349 STATSTG *pstatstg, DWORD grfStatFlag)
350{
Michael Stefaniuc2605b762010-12-08 22:59:46 +0100351 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500352
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -0500353 if (!(STATFLAG_NONAME & grfStatFlag) && This->pwcsName)
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500354 {
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -0500355 pstatstg->pwcsName =
356 CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500357
Vincent Povirkd0e6e4a2010-07-17 13:00:17 -0500358 strcpyW(pstatstg->pwcsName, This->pwcsName);
359 }
360 else
361 pstatstg->pwcsName = NULL;
362
Vincent Povirk14f8f9d2010-07-17 11:56:17 -0500363 pstatstg->type = STGTY_LOCKBYTES;
364 pstatstg->cbSize = This->filesize;
365 /* FIXME: If the implementation is exported, we'll need to set other fields. */
366
367 return S_OK;
368}
369
370static const ILockBytesVtbl FileLockBytesImpl_Vtbl = {
371 FileLockBytesImpl_QueryInterface,
372 FileLockBytesImpl_AddRef,
373 FileLockBytesImpl_Release,
374 FileLockBytesImpl_ReadAt,
375 FileLockBytesImpl_WriteAt,
376 FileLockBytesImpl_Flush,
377 FileLockBytesImpl_SetSize,
378 FileLockBytesImpl_LockRegion,
379 FileLockBytesImpl_UnlockRegion,
380 FileLockBytesImpl_Stat
381};