xref: /reactos/dll/win32/ole32/filelockbytes.c (revision 682f85ad)
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