Moved all the content of the DDK files ntdef.h and ntddk.h to
[wine] / dlls / setupapi / install.c
1 /*
2  * Setupapi install routines
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winternl.h"
24 #include "winerror.h"
25 #include "setupapi.h"
26 #include "wine/unicode.h"
27 #include "setupapi_private.h"
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
31
32 /* info passed to callback functions dealing with files */
33 struct files_callback_info
34 {
35     HSPFILEQ queue;
36     PCWSTR   src_root;
37     UINT     copy_flags;
38     HINF     layout;
39 };
40
41 /* info passed to callback functions dealing with the registry */
42 struct registry_callback_info
43 {
44     HKEY default_root;
45     BOOL delete;
46 };
47
48 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
49
50 /* Unicode constants */
51 static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
52 static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
53 static const WCHAR RenFiles[]   = {'R','e','n','F','i','l','e','s',0};
54 static const WCHAR Ini2Reg[]    = {'I','n','i','2','R','e','g',0};
55 static const WCHAR LogConf[]    = {'L','o','g','C','o','n','f',0};
56 static const WCHAR AddReg[]     = {'A','d','d','R','e','g',0};
57 static const WCHAR DelReg[]     = {'D','e','l','R','e','g',0};
58 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
59 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
60
61
62 /***********************************************************************
63  *            get_field_string
64  *
65  * Retrieve the contents of a field, dynamically growing the buffer if necessary.
66  */
67 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
68                                 WCHAR *static_buffer, DWORD *size )
69 {
70     DWORD required;
71
72     if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
73     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
74     {
75         /* now grow the buffer */
76         if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
77         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
78         *size = required;
79         if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
80     }
81     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
82     return NULL;
83 }
84
85
86 /***********************************************************************
87  *            copy_files_callback
88  *
89  * Called once for each CopyFiles entry in a given section.
90  */
91 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
92 {
93     struct files_callback_info *info = arg;
94
95     if (field[0] == '@')  /* special case: copy single file */
96         SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags );
97     else
98         SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags );
99     return TRUE;
100 }
101
102
103 /***********************************************************************
104  *            delete_files_callback
105  *
106  * Called once for each DelFiles entry in a given section.
107  */
108 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
109 {
110     struct files_callback_info *info = arg;
111     SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
112     return TRUE;
113 }
114
115
116 /***********************************************************************
117  *            rename_files_callback
118  *
119  * Called once for each RenFiles entry in a given section.
120  */
121 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
122 {
123     struct files_callback_info *info = arg;
124     SetupQueueRenameSectionW( info->queue, hinf, 0, field );
125     return TRUE;
126 }
127
128
129 /***********************************************************************
130  *            get_root_key
131  *
132  * Retrieve the registry root key from its name.
133  */
134 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
135 {
136     static const WCHAR HKCR[] = {'H','K','C','R',0};
137     static const WCHAR HKCU[] = {'H','K','C','U',0};
138     static const WCHAR HKLM[] = {'H','K','L','M',0};
139     static const WCHAR HKU[]  = {'H','K','U',0};
140     static const WCHAR HKR[]  = {'H','K','R',0};
141
142     if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
143     if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
144     if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
145     if (!strcmpiW( name, HKU )) return HKEY_USERS;
146     if (!strcmpiW( name, HKR )) return def_root;
147     return 0;
148 }
149
150
151 /***********************************************************************
152  *            append_multi_sz_value
153  *
154  * Append a multisz string to a multisz registry value.
155  */
156 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
157                                    DWORD str_size )
158 {
159     DWORD size, type, total;
160     WCHAR *buffer, *p;
161
162     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
163     if (type != REG_MULTI_SZ) return;
164
165     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
166     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
167
168     /* compare each string against all the existing ones */
169     total = size;
170     while (*strings)
171     {
172         int len = strlenW(strings) + 1;
173
174         for (p = buffer; *p; p += strlenW(p) + 1)
175             if (!strcmpiW( p, strings )) break;
176
177         if (!*p)  /* not found, need to append it */
178         {
179             memcpy( p, strings, len * sizeof(WCHAR) );
180             p[len] = 0;
181             total += len;
182         }
183         strings += len;
184     }
185     if (total != size)
186     {
187         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
188         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
189     }
190  done:
191     HeapFree( GetProcessHeap(), 0, buffer );
192 }
193
194
195 /***********************************************************************
196  *            delete_multi_sz_value
197  *
198  * Remove a string from a multisz registry value.
199  */
200 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
201 {
202     DWORD size, type;
203     WCHAR *buffer, *src, *dst;
204
205     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
206     if (type != REG_MULTI_SZ) return;
207     /* allocate double the size, one for value before and one for after */
208     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
209     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
210     src = buffer;
211     dst = buffer + size;
212     while (*src)
213     {
214         int len = strlenW(src) + 1;
215         if (strcmpiW( src, string ))
216         {
217             memcpy( dst, src, len * sizeof(WCHAR) );
218             dst += len;
219         }
220         src += len;
221     }
222     *dst++ = 0;
223     if (dst != buffer + 2*size)  /* did we remove something? */
224     {
225         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
226         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
227                         (BYTE *)(buffer + size), dst - (buffer + size) );
228     }
229  done:
230     HeapFree( GetProcessHeap(), 0, buffer );
231 }
232
233
234 /***********************************************************************
235  *            do_reg_operation
236  *
237  * Perform an add/delete registry operation depending on the flags.
238  */
239 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
240 {
241     DWORD type, size;
242
243     if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
244     {
245         if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
246         {
247             if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
248             {
249                 WCHAR *str;
250
251                 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
252                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
253                 SetupGetStringFieldW( context, 5, str, size, NULL );
254                 delete_multi_sz_value( hkey, value, str );
255                 HeapFree( GetProcessHeap(), 0, str );
256             }
257             else RegDeleteValueW( hkey, value );
258         }
259         else RegDeleteKeyW( hkey, NULL );
260         return TRUE;
261     }
262
263     if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
264
265     if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
266     {
267         BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
268         if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
269         if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
270     }
271
272     switch(flags & FLG_ADDREG_TYPE_MASK)
273     {
274     case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
275     case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
276     case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
277     case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
278     case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
279     case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
280     default:                        type = flags >> 16; break;
281     }
282
283     if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
284         (type == REG_DWORD && SetupGetFieldCount(context) == 5))
285     {
286         static const WCHAR empty;
287         WCHAR *str = NULL;
288
289         if (type == REG_MULTI_SZ)
290         {
291             if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
292             if (size)
293             {
294                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
295                 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
296             }
297             if (flags & FLG_ADDREG_APPEND)
298             {
299                 if (!str) return TRUE;
300                 append_multi_sz_value( hkey, value, str, size );
301                 HeapFree( GetProcessHeap(), 0, str );
302                 return TRUE;
303             }
304             /* else fall through to normal string handling */
305         }
306         else
307         {
308             if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
309             if (size)
310             {
311                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
312                 SetupGetStringFieldW( context, 5, str, size, NULL );
313             }
314         }
315
316         if (type == REG_DWORD)
317         {
318             DWORD dw = str ? strtolW( str, NULL, 16 ) : 0;
319             TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw );
320             RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
321         }
322         else
323         {
324             TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
325             if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
326             else RegSetValueExW( hkey, value, 0, type, (BYTE *)&empty, sizeof(WCHAR) );
327         }
328         HeapFree( GetProcessHeap(), 0, str );
329         return TRUE;
330     }
331     else  /* get the binary data */
332     {
333         BYTE *data = NULL;
334
335         if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
336         if (size)
337         {
338             if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
339             TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size );
340             SetupGetBinaryField( context, 5, data, size, NULL );
341         }
342         RegSetValueExW( hkey, value, 0, type, data, size );
343         HeapFree( GetProcessHeap(), 0, data );
344         return TRUE;
345     }
346 }
347
348
349 /***********************************************************************
350  *            registry_callback
351  *
352  * Called once for each AddReg and DelReg entry in a given section.
353  */
354 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
355 {
356     struct registry_callback_info *info = arg;
357     INFCONTEXT context;
358     HKEY root_key, hkey;
359
360     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
361
362     for (; ok; ok = SetupFindNextLine( &context, &context ))
363     {
364         WCHAR buffer[MAX_INF_STRING_LENGTH];
365         INT flags;
366
367         /* get root */
368         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
369             continue;
370         if (!(root_key = get_root_key( buffer, info->default_root )))
371             continue;
372
373         /* get key */
374         if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
375             *buffer = 0;
376
377         /* get flags */
378         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
379
380         if (!info->delete)
381         {
382             if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
383         }
384         else
385         {
386             if (!flags) flags = FLG_ADDREG_DELREG_BIT;
387             else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
388         }
389
390         if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
391         {
392             if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
393         }
394         else if (RegCreateKeyW( root_key, buffer, &hkey ))
395         {
396             ERR( "could not create key %08x %s\n", root_key, debugstr_w(buffer) );
397             continue;
398         }
399         TRACE( "key %08x %s\n", root_key, debugstr_w(buffer) );
400
401         /* get value name */
402         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
403             *buffer = 0;
404
405         /* and now do it */
406         if (!do_reg_operation( hkey, buffer, &context, flags ))
407         {
408             RegCloseKey( hkey );
409             return FALSE;
410         }
411         RegCloseKey( hkey );
412     }
413     return TRUE;
414 }
415
416
417 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
418 {
419     INFCONTEXT context;
420
421     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
422
423     for (; ok; ok = SetupFindNextLine( &context, &context ))
424     {
425         WCHAR buffer[MAX_INF_STRING_LENGTH];
426         WCHAR  filename[MAX_INF_STRING_LENGTH];
427         WCHAR  section[MAX_INF_STRING_LENGTH];
428         WCHAR  entry[MAX_INF_STRING_LENGTH];
429         WCHAR  string[MAX_INF_STRING_LENGTH];
430         LPWSTR divider;
431
432         if (!SetupGetStringFieldW( &context, 1, filename,
433                                    sizeof(filename)/sizeof(WCHAR), NULL ))
434             continue;
435
436         if (!SetupGetStringFieldW( &context, 2, section,
437                                    sizeof(section)/sizeof(WCHAR), NULL ))
438             continue;
439
440         if (!SetupGetStringFieldW( &context, 4, buffer,
441                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
442             continue;
443
444         divider = strchrW(buffer,'=');
445         if (divider)
446         {
447             *divider = 0;
448             strcpyW(entry,buffer);
449             divider++;
450             strcpyW(string,divider);
451         }
452         else
453         {
454             strcpyW(entry,buffer);
455             string[0]=0;
456         }
457
458         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
459                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
460         WritePrivateProfileStringW(section,entry,string,filename);
461
462     }
463     return TRUE;
464 }
465
466 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
467 {
468     FIXME( "should update ini fields %s\n", debugstr_w(field) );
469     return TRUE;
470 }
471
472 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
473 {
474     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
475     return TRUE;
476 }
477
478 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
479 {
480     FIXME( "should do logconf %s\n", debugstr_w(field) );
481     return TRUE;
482 }
483
484
485 /***********************************************************************
486  *            iterate_section_fields
487  *
488  * Iterate over all fields of a certain key of a certain section
489  */
490 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
491                                     iterate_fields_func callback, void *arg )
492 {
493     WCHAR static_buffer[200];
494     WCHAR *buffer = static_buffer;
495     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
496     INFCONTEXT context;
497     BOOL ret = FALSE;
498
499     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
500     while (ok)
501     {
502         UINT i, count = SetupGetFieldCount( &context );
503         for (i = 1; i <= count; i++)
504         {
505             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
506                 goto done;
507             if (!callback( hinf, buffer, arg ))
508             {
509                 ERR("callback failed for %s %s\n", debugstr_w(section), debugstr_w(buffer) );
510                 goto done;
511             }
512         }
513         ok = SetupFindNextMatchLineW( &context, key, &context );
514     }
515     ret = TRUE;
516  done:
517     if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
518     return ret;
519 }
520
521
522 /***********************************************************************
523  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
524  */
525 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
526                                               PCSTR section, PCSTR src_root, UINT flags )
527 {
528     UNICODE_STRING sectionW;
529     BOOL ret = FALSE;
530
531     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
532     {
533         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
534         return FALSE;
535     }
536     if (!src_root)
537         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
538                                                 NULL, flags );
539     else
540     {
541         UNICODE_STRING srcW;
542         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
543         {
544             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
545                                                     srcW.Buffer, flags );
546             RtlFreeUnicodeString( &srcW );
547         }
548         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
549     }
550     RtlFreeUnicodeString( &sectionW );
551     return ret;
552 }
553
554
555 /***********************************************************************
556  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
557  */
558 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
559                                               PCWSTR section, PCWSTR src_root, UINT flags )
560 {
561     struct files_callback_info info;
562
563     info.queue      = queue;
564     info.src_root   = src_root;
565     info.copy_flags = flags;
566     info.layout     = hlayout;
567     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
568 }
569
570
571 /***********************************************************************
572  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
573  */
574 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
575                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
576                                          PSP_FILE_CALLBACK_A callback, PVOID context,
577                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
578 {
579     UNICODE_STRING sectionW, src_rootW;
580     struct callback_WtoA_context ctx;
581     BOOL ret = FALSE;
582
583     src_rootW.Buffer = NULL;
584     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
585     {
586         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
587         return FALSE;
588     }
589
590     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
591     {
592         ctx.orig_context = context;
593         ctx.orig_handler = callback;
594         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
595                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
596                                            &ctx, devinfo, devinfo_data );
597         RtlFreeUnicodeString( &sectionW );
598     }
599     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
600
601     RtlFreeUnicodeString( &src_rootW );
602     return ret;
603 }
604
605
606 /***********************************************************************
607  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
608  */
609 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
610                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
611                                          PSP_FILE_CALLBACK_W callback, PVOID context,
612                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
613 {
614     if (flags & SPINST_FILES)
615     {
616         struct files_callback_info info;
617         HSPFILEQ queue;
618         BOOL ret;
619
620         if (!(queue = SetupOpenFileQueue())) return FALSE;
621         info.queue      = queue;
622         info.src_root   = src_root;
623         info.copy_flags = copy_flags;
624         info.layout     = hinf;
625         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
626                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
627                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
628                SetupCommitFileQueueW( owner, queue, callback, context ));
629         SetupCloseFileQueue( queue );
630         if (!ret) return FALSE;
631     }
632     if (flags & SPINST_INIFILES)
633     {
634         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
635             !iterate_section_fields( hinf, section, UpdateIniFields,
636                                      update_ini_fields_callback, NULL ))
637             return FALSE;
638     }
639     if (flags & SPINST_INI2REG)
640     {
641         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
642             return FALSE;
643     }
644
645     if (flags & SPINST_LOGCONFIG)
646     {
647         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
648             return FALSE;
649     }
650
651     if (flags & SPINST_REGISTRY)
652     {
653         struct registry_callback_info info;
654
655         info.default_root = key_root;
656         info.delete = TRUE;
657         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
658             return FALSE;
659         info.delete = FALSE;
660         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
661             return FALSE;
662     }
663     if (flags & (SPINST_BITREG|SPINST_REGSVR|SPINST_UNREGSVR|SPINST_PROFILEITEMS|SPINST_COPYINF))
664         FIXME( "unsupported flags %x\n", flags );
665     return TRUE;
666 }