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