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