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