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 }; 406