xref: /reactos/dll/win32/msi/storages.c (revision f4be6dc3)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2008 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 
23 #define COBJMACROS
24 
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "ole2.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "msipriv.h"
34 #include "query.h"
35 
36 #include "wine/debug.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
39 
40 #define NUM_STORAGES_COLS    2
41 #define MAX_STORAGES_NAME_LEN 62
42 
43 struct storage
44 {
45     UINT str_index;
46     IStorage *storage;
47 } STORAGE;
48 
49 struct storages_view
50 {
51     MSIVIEW view;
52     MSIDATABASE *db;
53     struct storage *storages;
54     UINT max_storages;
55     UINT num_rows;
56     UINT row_size;
57 };
58 
storages_set_table_size(struct storages_view * sv,UINT size)59 static BOOL storages_set_table_size(struct storages_view *sv, UINT size)
60 {
61     if (size >= sv->max_storages)
62     {
63         sv->max_storages *= 2;
64         sv->storages = realloc(sv->storages, sv->max_storages * sizeof(*sv->storages));
65         if (!sv->storages)
66             return FALSE;
67     }
68 
69     return TRUE;
70 }
71 
STORAGES_fetch_int(struct tagMSIVIEW * view,UINT row,UINT col,UINT * val)72 static UINT STORAGES_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
73 {
74     struct storages_view *sv = (struct storages_view *)view;
75 
76     TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
77 
78     if (col != 1)
79         return ERROR_INVALID_PARAMETER;
80 
81     if (row >= sv->num_rows)
82         return ERROR_NO_MORE_ITEMS;
83 
84     *val = sv->storages[row].str_index;
85 
86     return ERROR_SUCCESS;
87 }
88 
STORAGES_fetch_stream(struct tagMSIVIEW * view,UINT row,UINT col,IStream ** stm)89 static UINT STORAGES_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
90 {
91     struct storages_view *sv = (struct storages_view *)view;
92 
93     TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
94 
95     if (row >= sv->num_rows)
96         return ERROR_FUNCTION_FAILED;
97 
98     return ERROR_INVALID_DATA;
99 }
100 
STORAGES_set_string(struct tagMSIVIEW * view,UINT row,UINT col,const WCHAR * val,int len)101 static UINT STORAGES_set_string( struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
102 {
103     ERR("Cannot modify primary key.\n");
104     return ERROR_FUNCTION_FAILED;
105 }
106 
stream_to_storage(IStream * stm,IStorage ** stg)107 static HRESULT stream_to_storage(IStream *stm, IStorage **stg)
108 {
109     ILockBytes *lockbytes = NULL;
110     STATSTG stat;
111     LPVOID data;
112     HRESULT hr;
113     DWORD size, read;
114     ULARGE_INTEGER offset;
115 
116     hr = IStream_Stat(stm, &stat, STATFLAG_NONAME);
117     if (FAILED(hr))
118         return hr;
119 
120     if (stat.cbSize.QuadPart >> 32)
121     {
122         ERR("Storage is too large\n");
123         return E_FAIL;
124     }
125 
126     size = stat.cbSize.QuadPart;
127     data = malloc(size);
128     if (!data)
129         return E_OUTOFMEMORY;
130 
131     hr = IStream_Read(stm, data, size, &read);
132     if (FAILED(hr) || read != size)
133         goto done;
134 
135     hr = CreateILockBytesOnHGlobal(NULL, TRUE, &lockbytes);
136     if (FAILED(hr))
137         goto done;
138 
139     ZeroMemory(&offset, sizeof(ULARGE_INTEGER));
140     hr = ILockBytes_WriteAt(lockbytes, offset, data, size, &read);
141     if (FAILED(hr) || read != size)
142         goto done;
143 
144     hr = StgOpenStorageOnILockBytes(lockbytes, NULL,
145                                     STGM_READWRITE | STGM_SHARE_DENY_NONE,
146                                     NULL, 0, stg);
147     if (FAILED(hr))
148         goto done;
149 
150 done:
151     free(data);
152     if (lockbytes) ILockBytes_Release(lockbytes);
153     return hr;
154 }
155 
STORAGES_set_stream(MSIVIEW * view,UINT row,UINT col,IStream * stream)156 static UINT STORAGES_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream )
157 {
158     struct storages_view *sv = (struct storages_view *)view;
159     IStorage *stg, *substg, *prev;
160     const WCHAR *name;
161     HRESULT hr;
162     UINT r;
163 
164     TRACE("view %p, row %u, col %u, stream %p.\n", view, row, col, stream);
165 
166     if ((r = stream_to_storage(stream, &stg)))
167         return r;
168 
169     name = msi_string_lookup(sv->db->strings, sv->storages[row].str_index, NULL);
170 
171     hr = IStorage_CreateStorage(sv->db->storage, name,
172                                 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
173                                 0, 0, &substg);
174     if (FAILED(hr))
175     {
176         IStorage_Release(stg);
177         return ERROR_FUNCTION_FAILED;
178     }
179 
180     hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg);
181     if (FAILED(hr))
182     {
183         IStorage_Release(substg);
184         IStorage_Release(stg);
185         return ERROR_FUNCTION_FAILED;
186     }
187     IStorage_Release(substg);
188 
189     prev = sv->storages[row].storage;
190     sv->storages[row].storage = stg;
191     if (prev) IStorage_Release(prev);
192 
193     return ERROR_SUCCESS;
194 }
195 
STORAGES_set_row(struct tagMSIVIEW * view,UINT row,MSIRECORD * rec,UINT mask)196 static UINT STORAGES_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
197 {
198     struct storages_view *sv = (struct storages_view *)view;
199     IStorage *stg, *substg = NULL, *prev;
200     IStream *stm;
201     LPWSTR name = NULL;
202     HRESULT hr;
203     UINT r = ERROR_FUNCTION_FAILED;
204 
205     TRACE("(%p, %p)\n", view, rec);
206 
207     if (row >= sv->num_rows)
208         return ERROR_FUNCTION_FAILED;
209 
210     r = MSI_RecordGetIStream(rec, 2, &stm);
211     if (r != ERROR_SUCCESS)
212         return r;
213 
214     r = stream_to_storage(stm, &stg);
215     if (r != ERROR_SUCCESS)
216     {
217         IStream_Release(stm);
218         return r;
219     }
220 
221     name = wcsdup(MSI_RecordGetString(rec, 1));
222     if (!name)
223     {
224         r = ERROR_OUTOFMEMORY;
225         goto done;
226     }
227 
228     hr = IStorage_CreateStorage(sv->db->storage, name,
229                                 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
230                                 0, 0, &substg);
231     if (FAILED(hr))
232     {
233         r = ERROR_FUNCTION_FAILED;
234         goto done;
235     }
236 
237     hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg);
238     if (FAILED(hr))
239     {
240         r = ERROR_FUNCTION_FAILED;
241         goto done;
242     }
243 
244     prev = sv->storages[row].storage;
245     sv->storages[row].str_index = msi_add_string(sv->db->strings, name, -1, FALSE);
246     IStorage_AddRef(stg);
247     sv->storages[row].storage = stg;
248     if (prev) IStorage_Release(prev);
249 
250 done:
251     free(name);
252 
253     if (substg) IStorage_Release(substg);
254     IStorage_Release(stg);
255     IStream_Release(stm);
256 
257     return r;
258 }
259 
STORAGES_insert_row(struct tagMSIVIEW * view,MSIRECORD * rec,UINT row,BOOL temporary)260 static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
261 {
262     struct storages_view *sv = (struct storages_view *)view;
263 
264     if (!storages_set_table_size(sv, ++sv->num_rows))
265         return ERROR_FUNCTION_FAILED;
266 
267     if (row == -1)
268         row = sv->num_rows - 1;
269 
270     memset(&sv->storages[row], 0, sizeof(sv->storages[row]));
271 
272     /* FIXME have to readjust rows */
273 
274     return STORAGES_set_row(view, row, rec, 0);
275 }
276 
STORAGES_delete_row(struct tagMSIVIEW * view,UINT row)277 static UINT STORAGES_delete_row(struct tagMSIVIEW *view, UINT row)
278 {
279     FIXME("(%p %d): stub!\n", view, row);
280     return ERROR_SUCCESS;
281 }
282 
STORAGES_execute(struct tagMSIVIEW * view,MSIRECORD * record)283 static UINT STORAGES_execute(struct tagMSIVIEW *view, MSIRECORD *record)
284 {
285     TRACE("(%p, %p)\n", view, record);
286     return ERROR_SUCCESS;
287 }
288 
STORAGES_close(struct tagMSIVIEW * view)289 static UINT STORAGES_close(struct tagMSIVIEW *view)
290 {
291     TRACE("(%p)\n", view);
292     return ERROR_SUCCESS;
293 }
294 
STORAGES_get_dimensions(struct tagMSIVIEW * view,UINT * rows,UINT * cols)295 static UINT STORAGES_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
296 {
297     struct storages_view *sv = (struct storages_view *)view;
298 
299     TRACE("(%p, %p, %p)\n", view, rows, cols);
300 
301     if (cols) *cols = NUM_STORAGES_COLS;
302     if (rows) *rows = sv->num_rows;
303 
304     return ERROR_SUCCESS;
305 }
306 
STORAGES_get_column_info(struct tagMSIVIEW * view,UINT n,LPCWSTR * name,UINT * type,BOOL * temporary,LPCWSTR * table_name)307 static UINT STORAGES_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
308                                       UINT *type, BOOL *temporary, LPCWSTR *table_name )
309 {
310     TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary,
311           table_name);
312 
313     if (n == 0 || n > NUM_STORAGES_COLS)
314         return ERROR_INVALID_PARAMETER;
315 
316     switch (n)
317     {
318     case 1:
319         if (name) *name = L"Name";
320         if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STORAGES_NAME_LEN;
321         break;
322 
323     case 2:
324         if (name) *name = L"Data";
325         if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
326         break;
327     }
328     if (table_name) *table_name = L"_Storages";
329     if (temporary) *temporary = FALSE;
330     return ERROR_SUCCESS;
331 }
332 
storages_find_row(struct storages_view * sv,MSIRECORD * rec,UINT * row)333 static UINT storages_find_row(struct storages_view *sv, MSIRECORD *rec, UINT *row)
334 {
335     LPCWSTR str;
336     UINT r, i, id, data;
337 
338     str = MSI_RecordGetString(rec, 1);
339     r = msi_string2id(sv->db->strings, str, -1, &id);
340     if (r != ERROR_SUCCESS)
341         return r;
342 
343     for (i = 0; i < sv->num_rows; i++)
344     {
345         STORAGES_fetch_int(&sv->view, i, 1, &data);
346 
347         if (data == id)
348         {
349             *row = i;
350             return ERROR_SUCCESS;
351         }
352     }
353 
354     return ERROR_FUNCTION_FAILED;
355 }
356 
storages_modify_update(struct tagMSIVIEW * view,MSIRECORD * rec)357 static UINT storages_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
358 {
359     struct storages_view *sv = (struct storages_view *)view;
360     UINT r, row;
361 
362     r = storages_find_row(sv, rec, &row);
363     if (r != ERROR_SUCCESS)
364         return ERROR_FUNCTION_FAILED;
365 
366     return STORAGES_set_row(view, row, rec, 0);
367 }
368 
storages_modify_assign(struct tagMSIVIEW * view,MSIRECORD * rec)369 static UINT storages_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
370 {
371     struct storages_view *sv = (struct storages_view *)view;
372     UINT r, row;
373 
374     r = storages_find_row(sv, rec, &row);
375     if (r == ERROR_SUCCESS)
376         return storages_modify_update(view, rec);
377 
378     return STORAGES_insert_row(view, rec, -1, FALSE);
379 }
380 
STORAGES_modify(struct tagMSIVIEW * view,MSIMODIFY eModifyMode,MSIRECORD * rec,UINT row)381 static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
382 {
383     UINT r;
384 
385     TRACE("%p %d %p\n", view, eModifyMode, rec);
386 
387     switch (eModifyMode)
388     {
389     case MSIMODIFY_ASSIGN:
390         r = storages_modify_assign(view, rec);
391         break;
392 
393     case MSIMODIFY_INSERT:
394         r = STORAGES_insert_row(view, rec, -1, FALSE);
395         break;
396 
397     case MSIMODIFY_UPDATE:
398         r = storages_modify_update(view, rec);
399         break;
400 
401     case MSIMODIFY_VALIDATE_NEW:
402     case MSIMODIFY_INSERT_TEMPORARY:
403     case MSIMODIFY_REFRESH:
404     case MSIMODIFY_REPLACE:
405     case MSIMODIFY_MERGE:
406     case MSIMODIFY_DELETE:
407     case MSIMODIFY_VALIDATE:
408     case MSIMODIFY_VALIDATE_FIELD:
409     case MSIMODIFY_VALIDATE_DELETE:
410         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
411         r = ERROR_CALL_NOT_IMPLEMENTED;
412         break;
413 
414     default:
415         r = ERROR_INVALID_DATA;
416     }
417 
418     return r;
419 }
420 
STORAGES_delete(struct tagMSIVIEW * view)421 static UINT STORAGES_delete(struct tagMSIVIEW *view)
422 {
423     struct storages_view *sv = (struct storages_view *)view;
424     UINT i;
425 
426     TRACE("(%p)\n", view);
427 
428     for (i = 0; i < sv->num_rows; i++)
429     {
430         if (sv->storages[i].storage)
431             IStorage_Release(sv->storages[i].storage);
432     }
433 
434     free(sv->storages);
435     sv->storages = NULL;
436     free(sv);
437 
438     return ERROR_SUCCESS;
439 }
440 
441 static const MSIVIEWOPS storages_ops =
442 {
443     STORAGES_fetch_int,
444     STORAGES_fetch_stream,
445     NULL,
446     STORAGES_set_string,
447     STORAGES_set_stream,
448     STORAGES_set_row,
449     STORAGES_insert_row,
450     STORAGES_delete_row,
451     STORAGES_execute,
452     STORAGES_close,
453     STORAGES_get_dimensions,
454     STORAGES_get_column_info,
455     STORAGES_modify,
456     STORAGES_delete,
457     NULL,
458     NULL,
459     NULL,
460     NULL,
461     NULL,
462 };
463 
add_storages_to_table(struct storages_view * sv)464 static INT add_storages_to_table(struct storages_view *sv)
465 {
466     IEnumSTATSTG *stgenum = NULL;
467     STATSTG stat;
468     HRESULT hr;
469     UINT count = 0;
470     ULONG size;
471 
472     hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum);
473     if (FAILED(hr))
474         return -1;
475 
476     sv->max_storages = 1;
477     sv->storages = malloc(sizeof(*sv->storages));
478     if (!sv->storages)
479         return -1;
480 
481     while (TRUE)
482     {
483         size = 0;
484         hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size);
485         if (FAILED(hr) || !size)
486             break;
487 
488         if (stat.type != STGTY_STORAGE)
489         {
490             CoTaskMemFree(stat.pwcsName);
491             continue;
492         }
493 
494         TRACE("enumerated storage %s\n", debugstr_w(stat.pwcsName));
495 
496         if (!storages_set_table_size(sv, ++count))
497         {
498             count = -1;
499             break;
500         }
501 
502         sv->storages[count - 1].str_index = msi_add_string(sv->db->strings, stat.pwcsName, -1, FALSE);
503         sv->storages[count - 1].storage = NULL;
504 
505         IStorage_OpenStorage(sv->db->storage, stat.pwcsName, NULL,
506                              STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0,
507                              &sv->storages[count - 1].storage);
508         CoTaskMemFree(stat.pwcsName);
509     }
510 
511     IEnumSTATSTG_Release(stgenum);
512     return count;
513 }
514 
STORAGES_CreateView(MSIDATABASE * db,MSIVIEW ** view)515 UINT STORAGES_CreateView(MSIDATABASE *db, MSIVIEW **view)
516 {
517     struct storages_view *sv;
518     INT rows;
519 
520     TRACE("(%p, %p)\n", db, view);
521 
522     sv = calloc(1, sizeof(*sv));
523     if (!sv)
524         return ERROR_FUNCTION_FAILED;
525 
526     sv->view.ops = &storages_ops;
527     sv->db = db;
528 
529     rows = add_storages_to_table(sv);
530     if (rows < 0)
531     {
532         free(sv);
533         return ERROR_FUNCTION_FAILED;
534     }
535     sv->num_rows = rows;
536 
537     *view = (MSIVIEW *)sv;
538 
539     return ERROR_SUCCESS;
540 }
541