Merge PR #283 "[USBPORT] Transaction Translator (TT) support bringup"
[reactos.git] / dll / win32 / ole32 / filelockbytes.c
1 /******************************************************************************
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"
44 #include "wine/unicode.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(storage);
47
48 typedef struct FileLockBytesImpl
49 {
50 ILockBytes ILockBytes_iface;
51 LONG ref;
52 HANDLE hfile;
53 DWORD flProtect;
54 LPWSTR pwcsName;
55 } FileLockBytesImpl;
56
57 static const ILockBytesVtbl FileLockBytesImpl_Vtbl;
58
59 static inline FileLockBytesImpl *impl_from_ILockBytes(ILockBytes *iface)
60 {
61 return CONTAINING_RECORD(iface, FileLockBytesImpl, ILockBytes_iface);
62 }
63
64 /***********************************************************
65 * Prototypes for private methods
66 */
67
68 /****************************************************************************
69 * GetProtectMode
70 *
71 * This function will return a protection mode flag for a file-mapping object
72 * from the open flags of a file.
73 */
74 static DWORD GetProtectMode(DWORD openFlags)
75 {
76 switch(STGM_ACCESS_MODE(openFlags))
77 {
78 case STGM_WRITE:
79 case STGM_READWRITE:
80 return PAGE_READWRITE;
81 }
82 return PAGE_READONLY;
83 }
84
85 /******************************************************************************
86 * FileLockBytesImpl_Construct
87 *
88 * Initialize a big block object supported by a file.
89 */
90 HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes)
91 {
92 FileLockBytesImpl *This;
93 WCHAR fullpath[MAX_PATH];
94
95 if (hFile == INVALID_HANDLE_VALUE)
96 return E_FAIL;
97
98 This = HeapAlloc(GetProcessHeap(), 0, sizeof(FileLockBytesImpl));
99
100 if (!This)
101 return E_OUTOFMEMORY;
102
103 This->ILockBytes_iface.lpVtbl = &FileLockBytesImpl_Vtbl;
104 This->ref = 1;
105 This->hfile = hFile;
106 This->flProtect = GetProtectMode(openFlags);
107
108 if(pwcsName) {
109 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
110 {
111 lstrcpynW(fullpath, pwcsName, MAX_PATH);
112 }
113 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
114 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
115 if (!This->pwcsName)
116 {
117 HeapFree(GetProcessHeap(), 0, This);
118 return E_OUTOFMEMORY;
119 }
120 strcpyW(This->pwcsName, fullpath);
121 }
122 else
123 This->pwcsName = NULL;
124
125 *pLockBytes = &This->ILockBytes_iface;
126
127 return S_OK;
128 }
129
130 /* ILockByte Interfaces */
131
132 static HRESULT WINAPI FileLockBytesImpl_QueryInterface(ILockBytes *iface, REFIID riid,
133 void **ppvObject)
134 {
135 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ILockBytes))
136 *ppvObject = iface;
137 else
138 {
139 *ppvObject = NULL;
140 return E_NOINTERFACE;
141 }
142
143 IUnknown_AddRef((IUnknown*)*ppvObject);
144
145 return S_OK;
146 }
147
148 static ULONG WINAPI FileLockBytesImpl_AddRef(ILockBytes *iface)
149 {
150 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
151 return InterlockedIncrement(&This->ref);
152 }
153
154 static ULONG WINAPI FileLockBytesImpl_Release(ILockBytes *iface)
155 {
156 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
157 ULONG ref;
158
159 ref = InterlockedDecrement(&This->ref);
160
161 if (ref == 0)
162 {
163 CloseHandle(This->hfile);
164 HeapFree(GetProcessHeap(), 0, This->pwcsName);
165 HeapFree(GetProcessHeap(), 0, This);
166 }
167
168 return ref;
169 }
170
171 /******************************************************************************
172 * This method is part of the ILockBytes interface.
173 *
174 * It reads a block of information from the byte array at the specified
175 * offset.
176 *
177 * See the documentation of ILockBytes for more info.
178 */
179 static HRESULT WINAPI FileLockBytesImpl_ReadAt(
180 ILockBytes* iface,
181 ULARGE_INTEGER ulOffset, /* [in] */
182 void* pv, /* [length_is][size_is][out] */
183 ULONG cb, /* [in] */
184 ULONG* pcbRead) /* [out] */
185 {
186 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
187 ULONG bytes_left = cb;
188 LPBYTE readPtr = pv;
189 BOOL ret;
190 LARGE_INTEGER offset;
191 ULONG cbRead;
192
193 TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbRead);
194
195 /* verify a sane environment */
196 if (!This) return E_FAIL;
197
198 if (pcbRead)
199 *pcbRead = 0;
200
201 offset.QuadPart = ulOffset.QuadPart;
202
203 ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
204
205 if (!ret)
206 return STG_E_READFAULT;
207
208 while (bytes_left)
209 {
210 ret = ReadFile(This->hfile, readPtr, bytes_left, &cbRead, NULL);
211
212 if (!ret || cbRead == 0)
213 return STG_E_READFAULT;
214
215 if (pcbRead)
216 *pcbRead += cbRead;
217
218 bytes_left -= cbRead;
219 readPtr += cbRead;
220 }
221
222 TRACE("finished\n");
223 return S_OK;
224 }
225
226 /******************************************************************************
227 * This method is part of the ILockBytes interface.
228 *
229 * It writes the specified bytes at the specified offset.
230 * position. If the file is too small, it will be resized.
231 *
232 * See the documentation of ILockBytes for more info.
233 */
234 static HRESULT WINAPI FileLockBytesImpl_WriteAt(
235 ILockBytes* iface,
236 ULARGE_INTEGER ulOffset, /* [in] */
237 const void* pv, /* [size_is][in] */
238 ULONG cb, /* [in] */
239 ULONG* pcbWritten) /* [out] */
240 {
241 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
242 ULONG bytes_left = cb;
243 const BYTE *writePtr = pv;
244 BOOL ret;
245 LARGE_INTEGER offset;
246 ULONG cbWritten;
247
248 TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbWritten);
249
250 /* verify a sane environment */
251 if (!This) return E_FAIL;
252
253 if (This->flProtect != PAGE_READWRITE)
254 return STG_E_ACCESSDENIED;
255
256 if (pcbWritten)
257 *pcbWritten = 0;
258
259 offset.QuadPart = ulOffset.QuadPart;
260
261 ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
262
263 if (!ret)
264 return STG_E_WRITEFAULT;
265
266 while (bytes_left)
267 {
268 ret = WriteFile(This->hfile, writePtr, bytes_left, &cbWritten, NULL);
269
270 if (!ret)
271 return STG_E_WRITEFAULT;
272
273 if (pcbWritten)
274 *pcbWritten += cbWritten;
275
276 bytes_left -= cbWritten;
277 writePtr += cbWritten;
278 }
279
280 TRACE("finished\n");
281 return S_OK;
282 }
283
284 static HRESULT WINAPI FileLockBytesImpl_Flush(ILockBytes* iface)
285 {
286 return S_OK;
287 }
288
289 /******************************************************************************
290 * ILockBytes_SetSize
291 *
292 * Sets the size of the file.
293 *
294 */
295 static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGER newSize)
296 {
297 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
298 HRESULT hr = S_OK;
299 LARGE_INTEGER newpos;
300
301 TRACE("new size %u\n", newSize.u.LowPart);
302
303 newpos.QuadPart = newSize.QuadPart;
304 if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN))
305 {
306 SetEndOfFile(This->hfile);
307 }
308
309 return hr;
310 }
311
312 static HRESULT get_lock_error(void)
313 {
314 switch (GetLastError())
315 {
316 case ERROR_LOCK_VIOLATION: return STG_E_LOCKVIOLATION; break;
317 case ERROR_ACCESS_DENIED: return STG_E_ACCESSDENIED; break;
318 case ERROR_NOT_SUPPORTED: return STG_E_INVALIDFUNCTION; break;
319 default:
320 FIXME("no mapping for error %d\n", GetLastError());
321 return STG_E_INVALIDFUNCTION;
322 }
323 }
324
325 static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface,
326 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
327 {
328 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
329 OVERLAPPED ol;
330 DWORD lock_flags = LOCKFILE_FAIL_IMMEDIATELY;
331
332 TRACE("ofs %u count %u flags %x\n", libOffset.u.LowPart, cb.u.LowPart, dwLockType);
333
334 if (dwLockType & LOCK_WRITE)
335 return STG_E_INVALIDFUNCTION;
336
337 if (dwLockType & (LOCK_EXCLUSIVE|LOCK_ONLYONCE))
338 lock_flags |= LOCKFILE_EXCLUSIVE_LOCK;
339
340 ol.hEvent = 0;
341 ol.u.s.Offset = libOffset.u.LowPart;
342 ol.u.s.OffsetHigh = libOffset.u.HighPart;
343
344 if (LockFileEx(This->hfile, lock_flags, 0, cb.u.LowPart, cb.u.HighPart, &ol))
345 return S_OK;
346 return get_lock_error();
347 }
348
349 static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface,
350 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
351 {
352 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
353 OVERLAPPED ol;
354
355 TRACE("ofs %u count %u flags %x\n", libOffset.u.LowPart, cb.u.LowPart, dwLockType);
356
357 if (dwLockType & LOCK_WRITE)
358 return STG_E_INVALIDFUNCTION;
359
360 ol.hEvent = 0;
361 ol.u.s.Offset = libOffset.u.LowPart;
362 ol.u.s.OffsetHigh = libOffset.u.HighPart;
363
364 if (UnlockFileEx(This->hfile, 0, cb.u.LowPart, cb.u.HighPart, &ol))
365 return S_OK;
366 return get_lock_error();
367 }
368
369 static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface,
370 STATSTG *pstatstg, DWORD grfStatFlag)
371 {
372 FileLockBytesImpl* This = impl_from_ILockBytes(iface);
373
374 if (!(STATFLAG_NONAME & grfStatFlag) && This->pwcsName)
375 {
376 pstatstg->pwcsName =
377 CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
378
379 strcpyW(pstatstg->pwcsName, This->pwcsName);
380 }
381 else
382 pstatstg->pwcsName = NULL;
383
384 pstatstg->type = STGTY_LOCKBYTES;
385
386 pstatstg->cbSize.u.LowPart = GetFileSize(This->hfile, &pstatstg->cbSize.u.HighPart);
387 /* FIXME: If the implementation is exported, we'll need to set other fields. */
388
389 pstatstg->grfLocksSupported = LOCK_EXCLUSIVE|LOCK_ONLYONCE|WINE_LOCK_READ;
390
391 return S_OK;
392 }
393
394 static const ILockBytesVtbl FileLockBytesImpl_Vtbl = {
395 FileLockBytesImpl_QueryInterface,
396 FileLockBytesImpl_AddRef,
397 FileLockBytesImpl_Release,
398 FileLockBytesImpl_ReadAt,
399 FileLockBytesImpl_WriteAt,
400 FileLockBytesImpl_Flush,
401 FileLockBytesImpl_SetSize,
402 FileLockBytesImpl_LockRegion,
403 FileLockBytesImpl_UnlockRegion,
404 FileLockBytesImpl_Stat
405 };