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