xml_windows.c: use correct integer type (no change in behavior)
[wimlib] / src / xml_windows.c
1 /*
2  * xml_windows.c
3  *
4  * Set Windows-specific metadata in a WIM file's XML document based on the image
5  * contents.
6  */
7
8 /*
9  * Copyright (C) 2016 Eric Biggers
10  *
11  * This file is free software; you can redistribute it and/or modify it under
12  * the terms of the GNU Lesser General Public License as published by the Free
13  * Software Foundation; either version 3 of the License, or (at your option) any
14  * later version.
15  *
16  * This file is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this file; if not, see http://www.gnu.org/licenses/.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28
29 #include <stdlib.h>
30
31 #include "wimlib.h"
32 #include "wimlib/blob_table.h"
33 #include "wimlib/dentry.h"
34 #include "wimlib/encoding.h"
35 #include "wimlib/endianness.h"
36 #include "wimlib/error.h"
37 #include "wimlib/metadata.h"
38 #include "wimlib/registry.h"
39 #include "wimlib/wim.h"
40 #include "wimlib/xml_windows.h"
41
42 /* Context for a call to set_windows_specific_info()  */
43 struct windows_info_ctx {
44         WIMStruct *wim;
45         int image;
46         bool oom_encountered;
47         bool debug_enabled;
48 };
49
50 /* For debugging purposes, the environmental variable WIMLIB_DEBUG_XML_INFO can
51  * be set to enable messages about certain things not being as expected in the
52  * registry or other files used as information sources.  */
53
54 #define XML_WARN(format, ...)                   \
55         if (ctx->debug_enabled)                 \
56                 WARNING(format, ##__VA_ARGS__)
57
58 /* Set a property in the XML document, with error checking.  */
59 static void
60 set_string_property(struct windows_info_ctx *ctx,
61                     const tchar *name, const tchar *value)
62 {
63         int ret = wimlib_set_image_property(ctx->wim, ctx->image, name, value);
64         if (likely(!ret))
65                 return;
66
67         ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM);
68         WARNING("Failed to set image property \"%"TS"\" to value "
69                 "\"%"TS"\": %"TS, name, value, wimlib_get_error_string(ret));
70 }
71
72 /* Set a property in the XML document, with error checking.  */
73 static void
74 set_number_property(struct windows_info_ctx *ctx, const tchar *name, s64 value)
75 {
76         tchar buffer[32];
77         tsprintf(buffer, T("%"PRIi64""), value);
78         set_string_property(ctx, name, buffer);
79 }
80
81 /* Check the result of a registry hive operation.  If unsuccessful, possibly
82  * print debugging information.  Return true iff successful.  */
83 static bool
84 check_hive_status(struct windows_info_ctx *ctx, enum hive_status status,
85                   const tchar *key, const tchar *value)
86 {
87         if (likely(status == HIVE_OK))
88                 return true;
89
90         ctx->oom_encountered |= (status == HIVE_OUT_OF_MEMORY);
91         XML_WARN("%s; key=%"TS" value=%"TS, hive_status_to_string(status),
92                  (key ? key : T("(null)")), (value ? value : T("(null)")));
93         return false;
94 }
95
96 static bool
97 is_registry_valid(struct windows_info_ctx *ctx, const void *hive_mem,
98                   size_t hive_size)
99 {
100         enum hive_status status;
101
102         status = hive_validate(hive_mem, hive_size);
103         return check_hive_status(ctx, status, NULL, NULL);
104 }
105
106 static bool
107 get_string_from_registry(struct windows_info_ctx *ctx, const struct regf *regf,
108                          const tchar *key_name, const tchar *value_name,
109                          tchar **value_ret)
110 {
111         enum hive_status status;
112
113         status = hive_get_string(regf, key_name, value_name, value_ret);
114         return check_hive_status(ctx, status, key_name, value_name);
115 }
116
117 static bool
118 get_number_from_registry(struct windows_info_ctx *ctx, const struct regf *regf,
119                          const tchar *key_name, const tchar *value_name,
120                          s64 *value_ret)
121 {
122         enum hive_status status;
123
124         status = hive_get_number(regf, key_name, value_name, value_ret);
125         return check_hive_status(ctx, status, key_name, value_name);
126 }
127
128 static bool
129 list_subkeys_in_registry(struct windows_info_ctx *ctx, const struct regf *regf,
130                          const tchar *key_name, tchar ***subkeys_ret)
131 {
132         enum hive_status status;
133
134         status = hive_list_subkeys(regf, key_name, subkeys_ret);
135         return check_hive_status(ctx, status, key_name, NULL);
136 }
137
138 /* Copy a string value from a registry hive to the XML document.  */
139 static void
140 copy_registry_string(struct windows_info_ctx *ctx, const struct regf *regf,
141                      const tchar *key_name, const tchar *value_name,
142                      const tchar *property_name)
143 {
144         tchar *string;
145
146         if (get_string_from_registry(ctx, regf, key_name, value_name, &string)) {
147                 set_string_property(ctx, property_name, string);
148                 FREE(string);
149         }
150 }
151
152 /* A table that map Windows language IDs, sorted numerically, to their language
153  * names.  It was generated by tools/generate_language_id_map.c.  */
154 static const struct {
155         u16 id;
156         u16 name_start_offset;
157 } language_id_map[452] = {
158         {0x0000,    0}, {0x0001,    6}, {0x0002,   12}, {0x0003,   18},
159         {0x0004,   24}, {0x0005,   30}, {0x0006,   36}, {0x0007,   42},
160         {0x0008,   48}, {0x0009,   54}, {0x000a,   60}, {0x000b,   66},
161         {0x000c,   72}, {0x000d,   78}, {0x000e,   84}, {0x000f,   90},
162         {0x0010,   96}, {0x0011,  102}, {0x0012,  108}, {0x0013,  114},
163         {0x0014,  120}, {0x0015,  126}, {0x0016,  132}, {0x0017,  138},
164         {0x0018,  144}, {0x0019,  150}, {0x001a,  156}, {0x001b,  162},
165         {0x001c,  168}, {0x001d,  174}, {0x001e,  180}, {0x001f,  186},
166         {0x0020,  192}, {0x0021,  198}, {0x0022,  204}, {0x0023,  210},
167         {0x0024,  216}, {0x0025,  222}, {0x0026,  228}, {0x0027,  234},
168         {0x0028,  240}, {0x0029,  251}, {0x002a,  257}, {0x002b,  263},
169         {0x002c,  269}, {0x002d,  280}, {0x002e,  286}, {0x002f,  293},
170         {0x0030,  299}, {0x0031,  305}, {0x0032,  311}, {0x0033,  317},
171         {0x0034,  323}, {0x0035,  329}, {0x0036,  335}, {0x0037,  341},
172         {0x0038,  347}, {0x0039,  353}, {0x003a,  359}, {0x003b,  365},
173         {0x003c,  371}, {0x003d,  377}, {0x003e,  384}, {0x003f,  390},
174         {0x0040,  396}, {0x0041,  402}, {0x0042,  408}, {0x0043,  414},
175         {0x0044,  425}, {0x0045,  431}, {0x0046,  437}, {0x0047,  443},
176         {0x0048,  449}, {0x0049,  455}, {0x004a,  461}, {0x004b,  467},
177         {0x004c,  473}, {0x004d,  479}, {0x004e,  485}, {0x004f,  491},
178         {0x0050,  497}, {0x0051,  503}, {0x0052,  509}, {0x0053,  515},
179         {0x0054,  521}, {0x0055,  527}, {0x0056,  533}, {0x0057,  539},
180         {0x0058,  546}, {0x0059,  553}, {0x005a,  564}, {0x005b,  571},
181         {0x005c,  577}, {0x005d,  589}, {0x005e,  600}, {0x005f,  606},
182         {0x0060,  618}, {0x0061,  629}, {0x0062,  635}, {0x0063,  641},
183         {0x0064,  647}, {0x0065,  654}, {0x0066,  660}, {0x0067,  667},
184         {0x0068,  678}, {0x0069,  689}, {0x006a,  696}, {0x006b,  702},
185         {0x006c,  709}, {0x006d,  716}, {0x006e,  722}, {0x006f,  728},
186         {0x0070,  734}, {0x0071,  740}, {0x0072,  746}, {0x0073,  752},
187         {0x0074,  758}, {0x0075,  764}, {0x0076,  771}, {0x0077,  778},
188         {0x0078,  784}, {0x0079,  790}, {0x007a,  798}, {0x007c,  805},
189         {0x007e,  812}, {0x007f,  818}, {0x0080,  819}, {0x0081,  825},
190         {0x0082,  831}, {0x0083,  837}, {0x0084,  843}, {0x0085,  850},
191         {0x0086,  857}, {0x0087,  869}, {0x0088,  875}, {0x008c,  881},
192         {0x0091,  888}, {0x0092,  894}, {0x0400,  905}, {0x0401,  911},
193         {0x0402,  917}, {0x0403,  923}, {0x0404,  929}, {0x0405,  935},
194         {0x0406,  941}, {0x0407,  947}, {0x0408,  953}, {0x0409,  959},
195         {0x040a,  965}, {0x040b,  978}, {0x040c,  984}, {0x040d,  990},
196         {0x040e,  996}, {0x040f, 1002}, {0x0410, 1008}, {0x0411, 1014},
197         {0x0412, 1020}, {0x0413, 1026}, {0x0414, 1032}, {0x0415, 1038},
198         {0x0416, 1044}, {0x0417, 1050}, {0x0418, 1056}, {0x0419, 1062},
199         {0x041a, 1068}, {0x041b, 1074}, {0x041c, 1080}, {0x041d, 1086},
200         {0x041e, 1092}, {0x041f, 1098}, {0x0420, 1104}, {0x0421, 1110},
201         {0x0422, 1116}, {0x0423, 1122}, {0x0424, 1128}, {0x0425, 1134},
202         {0x0426, 1140}, {0x0427, 1146}, {0x0428, 1152}, {0x0429, 1163},
203         {0x042a, 1169}, {0x042b, 1175}, {0x042c, 1181}, {0x042d, 1192},
204         {0x042e, 1198}, {0x042f, 1205}, {0x0430, 1211}, {0x0431, 1217},
205         {0x0432, 1223}, {0x0433, 1229}, {0x0434, 1235}, {0x0435, 1241},
206         {0x0436, 1247}, {0x0437, 1253}, {0x0438, 1259}, {0x0439, 1265},
207         {0x043a, 1271}, {0x043b, 1277}, {0x043d, 1283}, {0x043e, 1290},
208         {0x043f, 1296}, {0x0440, 1302}, {0x0441, 1308}, {0x0442, 1314},
209         {0x0443, 1320}, {0x0444, 1331}, {0x0445, 1337}, {0x0446, 1343},
210         {0x0447, 1349}, {0x0448, 1355}, {0x0449, 1361}, {0x044a, 1367},
211         {0x044b, 1373}, {0x044c, 1379}, {0x044d, 1385}, {0x044e, 1391},
212         {0x044f, 1397}, {0x0450, 1403}, {0x0451, 1409}, {0x0452, 1415},
213         {0x0453, 1421}, {0x0454, 1427}, {0x0455, 1433}, {0x0456, 1439},
214         {0x0457, 1445}, {0x0458, 1452}, {0x0459, 1459}, {0x045a, 1470},
215         {0x045b, 1477}, {0x045c, 1483}, {0x045d, 1495}, {0x045e, 1506},
216         {0x045f, 1512}, {0x0460, 1524}, {0x0461, 1535}, {0x0462, 1541},
217         {0x0463, 1547}, {0x0464, 1553}, {0x0465, 1560}, {0x0466, 1566},
218         {0x0467, 1573}, {0x0468, 1579}, {0x0469, 1590}, {0x046a, 1597},
219         {0x046b, 1603}, {0x046c, 1610}, {0x046d, 1617}, {0x046e, 1623},
220         {0x046f, 1629}, {0x0470, 1635}, {0x0471, 1641}, {0x0472, 1647},
221         {0x0473, 1653}, {0x0474, 1659}, {0x0475, 1665}, {0x0476, 1672},
222         {0x0477, 1679}, {0x0478, 1685}, {0x0479, 1691}, {0x047a, 1699},
223         {0x047c, 1706}, {0x047e, 1713}, {0x0480, 1719}, {0x0481, 1725},
224         {0x0482, 1731}, {0x0483, 1737}, {0x0484, 1743}, {0x0485, 1750},
225         {0x0486, 1757}, {0x0487, 1769}, {0x0488, 1775}, {0x048c, 1781},
226         {0x0491, 1788}, {0x0492, 1794}, {0x0501, 1805}, {0x05fe, 1814},
227         {0x0800, 1824}, {0x0801, 1830}, {0x0803, 1836}, {0x0804, 1851},
228         {0x0807, 1857}, {0x0809, 1863}, {0x080a, 1869}, {0x080c, 1875},
229         {0x0810, 1881}, {0x0813, 1887}, {0x0814, 1893}, {0x0816, 1899},
230         {0x0818, 1905}, {0x0819, 1911}, {0x081a, 1917}, {0x081d, 1928},
231         {0x0820, 1934}, {0x082c, 1940}, {0x082e, 1951}, {0x0832, 1958},
232         {0x083b, 1964}, {0x083c, 1970}, {0x083e, 1976}, {0x0843, 1982},
233         {0x0845, 1993}, {0x0846, 1999}, {0x0849, 2010}, {0x0850, 2016},
234         {0x0859, 2027}, {0x085d, 2038}, {0x085f, 2049}, {0x0860, 2061},
235         {0x0861, 2072}, {0x0867, 2078}, {0x086b, 2089}, {0x0873, 2096},
236         {0x0901, 2102}, {0x09ff, 2116}, {0x0c00, 2126}, {0x0c01, 2132},
237         {0x0c04, 2138}, {0x0c07, 2144}, {0x0c09, 2150}, {0x0c0a, 2156},
238         {0x0c0c, 2162}, {0x0c1a, 2168}, {0x0c3b, 2179}, {0x0c50, 2185},
239         {0x0c51, 2196}, {0x0c6b, 2202}, {0x1000, 2209}, {0x1001, 2220},
240         {0x1004, 2226}, {0x1007, 2232}, {0x1009, 2238}, {0x100a, 2244},
241         {0x100c, 2250}, {0x101a, 2256}, {0x103b, 2262}, {0x105f, 2269},
242         {0x1401, 2281}, {0x1404, 2287}, {0x1407, 2293}, {0x1409, 2299},
243         {0x140a, 2305}, {0x140c, 2311}, {0x141a, 2317}, {0x143b, 2328},
244         {0x1801, 2335}, {0x1809, 2341}, {0x180a, 2347}, {0x180c, 2353},
245         {0x181a, 2359}, {0x183b, 2370}, {0x1c01, 2377}, {0x1c09, 2383},
246         {0x1c0a, 2389}, {0x1c0c, 2395}, {0x1c1a, 2402}, {0x1c3b, 2413},
247         {0x2000, 2420}, {0x2001, 2426}, {0x2009, 2432}, {0x200a, 2438},
248         {0x200c, 2444}, {0x201a, 2450}, {0x203b, 2461}, {0x2400, 2468},
249         {0x2401, 2474}, {0x2409, 2480}, {0x240a, 2487}, {0x240c, 2493},
250         {0x241a, 2499}, {0x243b, 2510}, {0x2800, 2517}, {0x2801, 2523},
251         {0x2809, 2529}, {0x280a, 2535}, {0x280c, 2541}, {0x281a, 2547},
252         {0x2c00, 2558}, {0x2c01, 2564}, {0x2c09, 2570}, {0x2c0a, 2576},
253         {0x2c0c, 2582}, {0x2c1a, 2588}, {0x3000, 2599}, {0x3001, 2605},
254         {0x3009, 2611}, {0x300a, 2617}, {0x300c, 2623}, {0x301a, 2629},
255         {0x3400, 2640}, {0x3401, 2646}, {0x3409, 2652}, {0x340a, 2658},
256         {0x340c, 2664}, {0x3800, 2670}, {0x3801, 2676}, {0x3809, 2682},
257         {0x380a, 2688}, {0x380c, 2694}, {0x3c00, 2700}, {0x3c01, 2706},
258         {0x3c09, 2712}, {0x3c0a, 2718}, {0x3c0c, 2724}, {0x4000, 2730},
259         {0x4001, 2736}, {0x4009, 2742}, {0x400a, 2748}, {0x4400, 2754},
260         {0x4409, 2760}, {0x440a, 2766}, {0x4800, 2772}, {0x4809, 2778},
261         {0x480a, 2784}, {0x4c00, 2790}, {0x4c0a, 2796}, {0x500a, 2802},
262         {0x540a, 2808}, {0x580a, 2814}, {0x5c0a, 2821}, {0x641a, 2827},
263         {0x681a, 2838}, {0x6c1a, 2849}, {0x701a, 2860}, {0x703b, 2871},
264         {0x742c, 2878}, {0x743b, 2889}, {0x7804, 2896}, {0x7814, 2902},
265         {0x781a, 2908}, {0x782c, 2919}, {0x783b, 2930}, {0x7843, 2937},
266         {0x7850, 2948}, {0x785d, 2954}, {0x785f, 2965}, {0x7c04, 2977},
267         {0x7c14, 2983}, {0x7c1a, 2989}, {0x7c28, 3000}, {0x7c2e, 3011},
268         {0x7c3b, 3018}, {0x7c43, 3025}, {0x7c46, 3036}, {0x7c50, 3047},
269         {0x7c59, 3058}, {0x7c5c, 3069}, {0x7c5d, 3081}, {0x7c5f, 3092},
270         {0x7c67, 3104}, {0x7c68, 3115}, {0x7c86, 3126}, {0x7c92, 3138},
271 };
272
273 /* All the language names; generated by tools/generate_language_id_map.c.
274  * For compactness, this is a 'char' string rather than a 'tchar' string.  */
275 static const char language_names[3149] =
276         "en-US\0ar-SA\0bg-BG\0ca-ES\0zh-CN\0cs-CZ\0da-DK\0de-DE\0el-GR\0en-US\0"
277         "es-ES\0fi-FI\0fr-FR\0he-IL\0hu-HU\0is-IS\0it-IT\0ja-JP\0ko-KR\0nl-NL\0"
278         "nb-NO\0pl-PL\0pt-BR\0rm-CH\0ro-RO\0ru-RU\0hr-HR\0sk-SK\0sq-AL\0sv-SE\0"
279         "th-TH\0tr-TR\0ur-PK\0id-ID\0uk-UA\0be-BY\0sl-SI\0et-EE\0lv-LV\0lt-LT\0"
280         "tg-Cyrl-TJ\0fa-IR\0vi-VN\0hy-AM\0az-Latn-AZ\0eu-ES\0hsb-DE\0mk-MK\0"
281         "st-ZA\0ts-ZA\0tn-ZA\0ve-ZA\0xh-ZA\0zu-ZA\0af-ZA\0ka-GE\0fo-FO\0hi-IN\0"
282         "mt-MT\0se-NO\0ga-IE\0yi-001\0ms-MY\0kk-KZ\0ky-KG\0sw-KE\0tk-TM\0"
283         "uz-Latn-UZ\0tt-RU\0bn-IN\0pa-IN\0gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0"
284         "ml-IN\0as-IN\0mr-IN\0sa-IN\0mn-MN\0bo-CN\0cy-GB\0km-KH\0lo-LA\0my-MM\0"
285         "gl-ES\0kok-IN\0mni-IN\0sd-Arab-PK\0syr-SY\0si-LK\0chr-Cher-US\0"
286         "iu-Latn-CA\0am-ET\0tzm-Latn-DZ\0ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0"
287         "fil-PH\0dv-MV\0bin-NG\0ff-Latn-SN\0ha-Latn-NG\0ibb-NG\0yo-NG\0quz-BO\0"
288         "nso-ZA\0ba-RU\0lb-LU\0kl-GL\0ig-NG\0kr-NG\0om-ET\0ti-ER\0gn-PY\0"
289         "haw-US\0la-001\0so-SO\0ii-CN\0pap-029\0arn-CL\0moh-CA\0br-FR\0\0"
290         "ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-FR\0sah-RU\0quc-Latn-GT\0rw-RW\0"
291         "wo-SN\0prs-AF\0gd-GB\0ku-Arab-IQ\0en-US\0ar-SA\0bg-BG\0ca-ES\0zh-TW\0"
292         "cs-CZ\0da-DK\0de-DE\0el-GR\0en-US\0es-ES_tradnl\0fi-FI\0fr-FR\0he-IL\0"
293         "hu-HU\0is-IS\0it-IT\0ja-JP\0ko-KR\0nl-NL\0nb-NO\0pl-PL\0pt-BR\0rm-CH\0"
294         "ro-RO\0ru-RU\0hr-HR\0sk-SK\0sq-AL\0sv-SE\0th-TH\0tr-TR\0ur-PK\0id-ID\0"
295         "uk-UA\0be-BY\0sl-SI\0et-EE\0lv-LV\0lt-LT\0tg-Cyrl-TJ\0fa-IR\0vi-VN\0"
296         "hy-AM\0az-Latn-AZ\0eu-ES\0hsb-DE\0mk-MK\0st-ZA\0ts-ZA\0tn-ZA\0ve-ZA\0"
297         "xh-ZA\0zu-ZA\0af-ZA\0ka-GE\0fo-FO\0hi-IN\0mt-MT\0se-NO\0yi-001\0"
298         "ms-MY\0kk-KZ\0ky-KG\0sw-KE\0tk-TM\0uz-Latn-UZ\0tt-RU\0bn-IN\0pa-IN\0"
299         "gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0ml-IN\0as-IN\0mr-IN\0sa-IN\0mn-MN\0"
300         "bo-CN\0cy-GB\0km-KH\0lo-LA\0my-MM\0gl-ES\0kok-IN\0mni-IN\0sd-Deva-IN\0"
301         "syr-SY\0si-LK\0chr-Cher-US\0iu-Cans-CA\0am-ET\0tzm-Arab-MA\0"
302         "ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0fil-PH\0dv-MV\0bin-NG\0ff-NG\0"
303         "ha-Latn-NG\0ibb-NG\0yo-NG\0quz-BO\0nso-ZA\0ba-RU\0lb-LU\0kl-GL\0"
304         "ig-NG\0kr-NG\0om-ET\0ti-ET\0gn-PY\0haw-US\0la-001\0so-SO\0ii-CN\0"
305         "pap-029\0arn-CL\0moh-CA\0br-FR\0ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-FR\0"
306         "sah-RU\0quc-Latn-GT\0rw-RW\0wo-SN\0prs-AF\0gd-GB\0ku-Arab-IQ\0"
307         "qps-ploc\0qps-ploca\0en-US\0ar-IQ\0ca-ES-valencia\0zh-CN\0de-CH\0"
308         "en-GB\0es-MX\0fr-BE\0it-CH\0nl-BE\0nn-NO\0pt-PT\0ro-MD\0ru-MD\0"
309         "sr-Latn-CS\0sv-FI\0ur-IN\0az-Cyrl-AZ\0dsb-DE\0tn-BW\0se-SE\0ga-IE\0"
310         "ms-BN\0uz-Cyrl-UZ\0bn-BD\0pa-Arab-PK\0ta-LK\0mn-Mong-CN\0sd-Arab-PK\0"
311         "iu-Latn-CA\0tzm-Latn-DZ\0ks-Deva-IN\0ne-IN\0ff-Latn-SN\0quz-EC\0"
312         "ti-ER\0qps-Latn-x-sh\0qps-plocm\0en-US\0ar-EG\0zh-HK\0de-AT\0en-AU\0"
313         "es-ES\0fr-CA\0sr-Cyrl-CS\0se-FI\0mn-Mong-MN\0dz-BT\0quz-PE\0"
314         "ks-Arab-IN\0ar-LY\0zh-SG\0de-LU\0en-CA\0es-GT\0fr-CH\0hr-BA\0smj-NO\0"
315         "tzm-Tfng-MA\0ar-DZ\0zh-MO\0de-LI\0en-NZ\0es-CR\0fr-LU\0bs-Latn-BA\0"
316         "smj-SE\0ar-MA\0en-IE\0es-PA\0fr-MC\0sr-Latn-BA\0sma-NO\0ar-TN\0en-ZA\0"
317         "es-DO\0fr-029\0sr-Cyrl-BA\0sma-SE\0en-US\0ar-OM\0en-JM\0es-VE\0fr-RE\0"
318         "bs-Cyrl-BA\0sms-FI\0en-US\0ar-YE\0en-029\0es-CO\0fr-CD\0sr-Latn-RS\0"
319         "smn-FI\0en-US\0ar-SY\0en-BZ\0es-PE\0fr-SN\0sr-Cyrl-RS\0en-US\0ar-JO\0"
320         "en-TT\0es-AR\0fr-CM\0sr-Latn-ME\0en-US\0ar-LB\0en-ZW\0es-EC\0fr-CI\0"
321         "sr-Cyrl-ME\0en-US\0ar-KW\0en-PH\0es-CL\0fr-ML\0en-US\0ar-AE\0en-ID\0"
322         "es-UY\0fr-MA\0en-US\0ar-BH\0en-HK\0es-PY\0fr-HT\0en-US\0ar-QA\0en-IN\0"
323         "es-BO\0en-US\0en-MY\0es-SV\0en-US\0en-SG\0es-HN\0en-US\0es-NI\0es-PR\0"
324         "es-US\0es-419\0es-CU\0bs-Cyrl-BA\0bs-Latn-BA\0sr-Cyrl-RS\0sr-Latn-RS\0"
325         "smn-FI\0az-Cyrl-AZ\0sms-FI\0zh-CN\0nn-NO\0bs-Latn-BA\0az-Latn-AZ\0"
326         "sma-SE\0uz-Cyrl-UZ\0mn-MN\0iu-Cans-CA\0tzm-Tfng-MA\0zh-HK\0nb-NO\0"
327         "sr-Latn-RS\0tg-Cyrl-TJ\0dsb-DE\0smj-SE\0uz-Latn-UZ\0pa-Arab-PK\0"
328         "mn-Mong-CN\0sd-Arab-PK\0chr-Cher-US\0iu-Latn-CA\0tzm-Latn-DZ\0"
329         "ff-Latn-SN\0ha-Latn-NG\0quc-Latn-GT\0ku-Arab-IQ\0";
330
331 /* Translate a Windows language ID to its name.  Returns NULL if the ID is not
332  * recognized.  */
333 static const char *
334 language_id_to_name(u16 id)
335 {
336         int l = 0;
337         int r = ARRAY_LEN(language_id_map) - 1;
338         do {
339                 int m = (l + r) / 2;
340                 if (id < language_id_map[m].id)
341                         r = m - 1;
342                 else if (id > language_id_map[m].id)
343                         l = m + 1;
344                 else
345                         return &language_names[language_id_map[m].name_start_offset];
346         } while (l <= r);
347         return NULL;
348 }
349
350 /* PE binary processor architecture codes (common ones only)  */
351 #define IMAGE_FILE_MACHINE_I386         0x014C
352 #define IMAGE_FILE_MACHINE_ARM          0x01C0
353 #define IMAGE_FILE_MACHINE_ARMV7        0x01C4
354 #define IMAGE_FILE_MACHINE_THUMB        0x01C2
355 #define IMAGE_FILE_MACHINE_IA64         0x0200
356 #define IMAGE_FILE_MACHINE_AMD64        0x8664
357 #define IMAGE_FILE_MACHINE_ARM64        0xAA64
358
359 /* Windows API processor architecture codes (common ones only)  */
360 #define PROCESSOR_ARCHITECTURE_INTEL    0
361 #define PROCESSOR_ARCHITECTURE_ARM      5
362 #define PROCESSOR_ARCHITECTURE_IA64     6
363 #define PROCESSOR_ARCHITECTURE_AMD64    9
364 #define PROCESSOR_ARCHITECTURE_ARM64    12
365
366 /* Translate a processor architecture code as given in a PE binary to the code
367  * used by the Windows API.  Returns -1 if the code is not recognized.  */
368 static int
369 pe_arch_to_windows_arch(unsigned pe_arch)
370 {
371         switch (pe_arch) {
372         case IMAGE_FILE_MACHINE_I386:
373                 return PROCESSOR_ARCHITECTURE_INTEL;
374         case IMAGE_FILE_MACHINE_ARM:
375         case IMAGE_FILE_MACHINE_ARMV7:
376         case IMAGE_FILE_MACHINE_THUMB:
377                 return PROCESSOR_ARCHITECTURE_ARM;
378         case IMAGE_FILE_MACHINE_IA64:
379                 return PROCESSOR_ARCHITECTURE_IA64;
380         case IMAGE_FILE_MACHINE_AMD64:
381                 return PROCESSOR_ARCHITECTURE_AMD64;
382         case IMAGE_FILE_MACHINE_ARM64:
383                 return PROCESSOR_ARCHITECTURE_ARM64;
384         }
385         return -1;
386 }
387
388 /* Gather information from kernel32.dll.  */
389 static void
390 set_info_from_kernel32(struct windows_info_ctx *ctx,
391                        const void *contents, size_t size)
392 {
393         u32 e_lfanew;
394         const u8 *pe_hdr;
395         unsigned pe_arch;
396         int arch;
397
398         /* Read the processor architecture from the executable header.  */
399
400         if (size < 0x40)
401                 goto invalid;
402
403         e_lfanew = le32_to_cpu(*(le32 *)((u8 *)contents + 0x3C));
404         if (e_lfanew > size || size - e_lfanew < 6 || (e_lfanew & 3))
405                 goto invalid;
406
407         pe_hdr = (u8 *)contents + e_lfanew;
408         if (*(le32 *)pe_hdr != cpu_to_le32(0x00004550)) /* "PE\0\0"  */
409                 goto invalid;
410
411         pe_arch = le16_to_cpu(*(le16 *)(pe_hdr + 4));
412         arch = pe_arch_to_windows_arch(pe_arch);
413         if (arch >= 0) {
414                 /* Save the processor architecture in the XML document.  */
415                 set_number_property(ctx, T("WINDOWS/ARCH"), arch);
416         } else {
417                 XML_WARN("Architecture value %x from kernel32.dll "
418                          "header not recognized", pe_arch);
419         }
420         return;
421
422 invalid:
423         XML_WARN("kernel32.dll is not a valid PE binary.");
424 }
425
426 /* Gather information from the SOFTWARE registry hive.  */
427 static void
428 set_info_from_software_hive(struct windows_info_ctx *ctx,
429                             const struct regf *regf)
430 {
431         const tchar *version_key = T("Microsoft\\Windows NT\\CurrentVersion");
432         s64 major_version = -1;
433         s64 minor_version = -1;
434         tchar *version_string;
435         tchar *build_string;
436
437         /* Image flags  */
438         copy_registry_string(ctx, regf, version_key, T("EditionID"),
439                              T("FLAGS"));
440
441         /* Image display name  */
442         copy_registry_string(ctx, regf, version_key, T("ProductName"),
443                              T("DISPLAYNAME"));
444
445         /* Image display description  */
446         copy_registry_string(ctx, regf, version_key, T("ProductName"),
447                              T("DISPLAYDESCRIPTION"));
448
449         /* Edition ID  */
450         copy_registry_string(ctx, regf, version_key, T("EditionID"),
451                              T("WINDOWS/EDITIONID"));
452
453         /* Installation type  */
454         copy_registry_string(ctx, regf, version_key, T("InstallationType"),
455                              T("WINDOWS/INSTALLATIONTYPE"));
456
457         /* Product name  */
458         copy_registry_string(ctx, regf, version_key, T("ProductName"),
459                              T("WINDOWS/PRODUCTNAME"));
460
461         /* Major and minor version number  */
462
463         /* Note: in Windows 10, CurrentVersion was apparently fixed at 6.3.
464          * Instead, the new values CurrentMajorVersionNumber and
465          * CurrentMinorVersionNumber should be used.  */
466
467         get_number_from_registry(ctx, regf, version_key,
468                                  T("CurrentMajorVersionNumber"), &major_version);
469
470         get_number_from_registry(ctx, regf, version_key,
471                                  T("CurrentMinorVersionNumber"), &minor_version);
472
473         if (major_version < 0 || minor_version < 0) {
474                 if (get_string_from_registry(ctx, regf, version_key,
475                                              T("CurrentVersion"),
476                                              &version_string))
477                 {
478                         if (2 != tscanf(version_string, T("%"PRIi64".%"PRIi64),
479                                         &major_version, &minor_version))
480                         {
481                                 XML_WARN("Unrecognized CurrentVersion: %"TS,
482                                          version_string);
483                         }
484                         FREE(version_string);
485                 }
486         }
487
488         if (major_version >= 0) {
489                 set_number_property(ctx, T("WINDOWS/VERSION/MAJOR"),
490                                     major_version);
491                 if (minor_version >= 0) {
492                         set_number_property(ctx, T("WINDOWS/VERSION/MINOR"),
493                                             minor_version);
494                 }
495         }
496
497         /* Build number  */
498
499         /* Note: "CurrentBuild" is marked as obsolete in Windows XP registries
500          * (example value:  "1.511.1 () (Obsolete data - do not use)"), and
501          * "CurrentBuildNumber" contains the correct value.  But oddly enough,
502          * it is "CurrentBuild" that contains the correct value on *later*
503          * versions of Windows.  */
504         if (get_string_from_registry(ctx, regf, version_key, T("CurrentBuild"),
505                                      &build_string))
506         {
507                 if (tstrchr(build_string, T('.'))) {
508                         FREE(build_string);
509                         build_string = NULL;
510                         get_string_from_registry(ctx, regf, version_key,
511                                                  T("CurrentBuildNumber"),
512                                                  &build_string);
513                 }
514                 if (build_string) {
515                         set_string_property(ctx, T("WINDOWS/VERSION/BUILD"),
516                                             build_string);
517                         FREE(build_string);
518                 }
519         }
520 }
521
522 /* Gather the default language from the SYSTEM registry hive.  */
523 static void
524 set_default_language(struct windows_info_ctx *ctx, const struct regf *regf)
525 {
526         tchar *string;
527         unsigned language_id;
528
529         if (!get_string_from_registry(ctx, regf,
530                                       T("ControlSet001\\Control\\Nls\\Language"),
531                                       T("InstallLanguage"), &string))
532                 return;
533
534         if (1 == tscanf(string, T("%x"), &language_id)) {
535                 const char *language_name = language_id_to_name(language_id);
536                 if (language_name) {
537                         size_t len = strlen(language_name);
538                         tchar tstr[len + 1];
539                         for (size_t i = 0; i <= len; i++)
540                                 tstr[i] = language_name[i];
541                         set_string_property(ctx, T("WINDOWS/LANGUAGES/DEFAULT"),
542                                             tstr);
543                         FREE(string);
544                         return;
545                 }
546         }
547         XML_WARN("Unrecognized InstallLanguage: %"TS, string);
548         FREE(string);
549 }
550
551 /* Gather information from the SYSTEM registry hive.  */
552 static void
553 set_info_from_system_hive(struct windows_info_ctx *ctx, const struct regf *regf)
554 {
555         const tchar *windows_key = T("ControlSet001\\Control\\Windows");
556         const tchar *uilanguages_key = T("ControlSet001\\Control\\MUI\\UILanguages");
557         const tchar *productoptions_key = T("ControlSet001\\Control\\ProductOptions");
558         s64 spbuild;
559         s64 splevel;
560         tchar **subkeys;
561
562         /* Service pack build  */
563         if (get_number_from_registry(ctx, regf, windows_key,
564                                      T("CSDBuildNumber"), &spbuild))
565                 set_number_property(ctx, T("WINDOWS/VERSION/SPBUILD"), spbuild);
566
567         /* Service pack level  */
568         if (get_number_from_registry(ctx, regf, windows_key,
569                                      T("CSDVersion"), &splevel))
570                 set_number_property(ctx, T("WINDOWS/VERSION/SPLEVEL"), splevel >> 8);
571
572         /* Product type  */
573         copy_registry_string(ctx, regf, productoptions_key, T("ProductType"),
574                              T("WINDOWS/PRODUCTTYPE"));
575
576         /* Product suite  */
577         copy_registry_string(ctx, regf, productoptions_key, T("ProductSuite"),
578                              T("WINDOWS/PRODUCTSUITE"));
579
580         /* Hardware abstraction layer  */
581         copy_registry_string(ctx, regf,
582                              T("ControlSet001\\Control\\Class\\{4D36E966-E325-11CE-BFC1-08002BE10318}\\0000"),
583                              T("MatchingDeviceId"),
584                              T("WINDOWS/HAL"));
585
586         /* Languages  */
587         if (list_subkeys_in_registry(ctx, regf, uilanguages_key, &subkeys)) {
588                 tchar property_name[64];
589                 for (tchar **p = subkeys; *p; p++) {
590                         tsprintf(property_name,
591                                  T("WINDOWS/LANGUAGES/LANGUAGE[%zu]"), p - subkeys + 1);
592                         set_string_property(ctx, property_name, *p);
593                 }
594                 hive_free_subkeys_list(subkeys);
595         }
596
597         /* Default language  */
598         set_default_language(ctx, regf);
599 }
600
601 /* Load the contents of a file in the currently selected WIM image into memory.
602  */
603 static void *
604 load_file_contents(struct windows_info_ctx *ctx,
605                    const struct wim_dentry *dentry, const char *filename,
606                    size_t *size_ret)
607 {
608         const struct blob_descriptor *blob;
609         void *contents;
610         int ret;
611
612         if (!dentry) {
613                 XML_WARN("%s does not exist", filename);
614                 return NULL;
615         }
616
617         blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode,
618                                                       ctx->wim->blob_table);
619         if (!blob) {
620                 XML_WARN("%s has no contents", filename);
621                 return NULL;
622         }
623
624         ret = read_blob_into_alloc_buf(blob, &contents);
625         if (ret) {
626                 XML_WARN("Error loading %s (size=%"PRIu64"): %"TS,
627                          filename, blob->size, wimlib_get_error_string(ret));
628                 ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM &&
629                                          blob->size < 100000000);
630                 return NULL;
631         }
632
633         *size_ret = blob->size;
634         return contents;
635 }
636
637 /* Load and validate a registry hive file.  */
638 static void *
639 load_hive(struct windows_info_ctx *ctx, const struct wim_dentry *dentry,
640           const char *filename)
641 {
642         void *hive_mem;
643         size_t hive_size;
644
645         hive_mem = load_file_contents(ctx, dentry, filename, &hive_size);
646         if (hive_mem && !is_registry_valid(ctx, hive_mem, hive_size)) {
647                 XML_WARN("%s is not a valid registry hive!", filename);
648                 FREE(hive_mem);
649                 hive_mem = NULL;
650         }
651         return hive_mem;
652 }
653
654 /* Set the WINDOWS/SYSTEMROOT property to the name of the directory specified by
655  * 'systemroot'.  */
656 static void
657 set_systemroot_property(struct windows_info_ctx *ctx,
658                         const struct wim_dentry *systemroot)
659 {
660         utf16lechar *uname;
661         const tchar *name;
662         size_t name_nbytes;
663         int ret;
664
665         /* to uppercase ...  */
666         uname = utf16le_dupz(systemroot->d_name, systemroot->d_name_nbytes);
667         if (!uname) {
668                 ctx->oom_encountered = true;
669                 goto out;
670         }
671         for (size_t i = 0; i < systemroot->d_name_nbytes / 2; i++)
672                 uname[i] = cpu_to_le16(upcase[le16_to_cpu(uname[i])]);
673
674         /* to tstring ...  */
675         ret = utf16le_get_tstr(uname, systemroot->d_name_nbytes,
676                                &name, &name_nbytes);
677         if (ret) {
678                 ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM);
679                 XML_WARN("Failed to get systemroot name: %"TS,
680                          wimlib_get_error_string(ret));
681                 goto out;
682         }
683         set_string_property(ctx, T("WINDOWS/SYSTEMROOT"), name);
684         utf16le_put_tstr(name);
685 out:
686         FREE(uname);
687 }
688
689 static int
690 do_set_windows_specific_info(WIMStruct *wim,
691                              const struct wim_dentry *systemroot,
692                              const struct wim_dentry *kernel32,
693                              const struct wim_dentry *software,
694                              const struct wim_dentry *system)
695 {
696         void *contents;
697         size_t size;
698         struct windows_info_ctx _ctx = {
699                 .wim = wim,
700                 .image = wim->current_image,
701                 .oom_encountered = false,
702                 .debug_enabled = (tgetenv(T("WIMLIB_DEBUG_XML_INFO")) != NULL),
703         }, *ctx = &_ctx;
704
705         set_systemroot_property(ctx, systemroot);
706
707         if ((contents = load_file_contents(ctx, kernel32, "kernel32.dll", &size))) {
708                 set_info_from_kernel32(ctx, contents, size);
709                 FREE(contents);
710         }
711
712         if ((contents = load_hive(ctx, software, "SOFTWARE"))) {
713                 set_info_from_software_hive(ctx, contents);
714                 FREE(contents);
715         }
716
717         if ((contents = load_hive(ctx, system, "SYSTEM"))) {
718                 set_info_from_system_hive(ctx, contents);
719                 FREE(contents);
720         }
721
722         if (ctx->oom_encountered) {
723                 ERROR("Ran out of memory while setting Windows-specific "
724                       "metadata in the WIM file's XML document.");
725                 return WIMLIB_ERR_NOMEM;
726         }
727
728         return 0;
729 }
730
731 /* Windows */
732 static const utf16lechar windows_name[] = {
733         cpu_to_le16('W'), cpu_to_le16('i'), cpu_to_le16('n'),
734         cpu_to_le16('d'), cpu_to_le16('o'), cpu_to_le16('w'),
735         cpu_to_le16('s'),
736 };
737
738 /* System32 */
739 static const utf16lechar system32_name[] = {
740         cpu_to_le16('S'), cpu_to_le16('y'), cpu_to_le16('s'),
741         cpu_to_le16('t'), cpu_to_le16('e'), cpu_to_le16('m'),
742         cpu_to_le16('3'), cpu_to_le16('2'),
743 };
744
745 /* kernel32.dll */
746 static const utf16lechar kernel32_name[] = {
747         cpu_to_le16('k'), cpu_to_le16('e'), cpu_to_le16('r'),
748         cpu_to_le16('n'), cpu_to_le16('e'), cpu_to_le16('l'),
749         cpu_to_le16('3'), cpu_to_le16('2'), cpu_to_le16('.'),
750         cpu_to_le16('d'), cpu_to_le16('l'), cpu_to_le16('l'),
751 };
752
753 /* config */
754 static const utf16lechar config_name[] = {
755         cpu_to_le16('c'), cpu_to_le16('o'), cpu_to_le16('n'),
756         cpu_to_le16('f'), cpu_to_le16('i'), cpu_to_le16('g'),
757 };
758
759 /* SOFTWARE */
760 static const utf16lechar software_name[] = {
761         cpu_to_le16('S'), cpu_to_le16('O'), cpu_to_le16('F'),
762         cpu_to_le16('T'), cpu_to_le16('W'), cpu_to_le16('A'),
763         cpu_to_le16('R'), cpu_to_le16('E'),
764 };
765
766 /* SYSTEM */
767 static const utf16lechar system_name[] = {
768         cpu_to_le16('S'), cpu_to_le16('Y'), cpu_to_le16('S'),
769         cpu_to_le16('T'), cpu_to_le16('E'), cpu_to_le16('M'),
770 };
771
772 #define GET_CHILD(parent, child_name)                           \
773         get_dentry_child_with_utf16le_name(parent,              \
774                                            child_name,          \
775                                            sizeof(child_name),  \
776                                            WIMLIB_CASE_INSENSITIVE)
777
778 static bool
779 is_default_systemroot(const struct wim_dentry *potential_systemroot)
780 {
781         return !cmp_utf16le_strings(potential_systemroot->d_name,
782                                     potential_systemroot->d_name_nbytes / 2,
783                                     windows_name,
784                                     ARRAY_LEN(windows_name),
785                                     true);
786 }
787
788 /*
789  * Set Windows-specific XML information for the currently selected WIM image.
790  *
791  * This process is heavily based on heuristics and hard-coded logic related to
792  * where Windows stores certain types of information.  Therefore, it simply
793  * tries to set as much information as possible.  If there's a problem, it skips
794  * the affected information and proceeds to the next part.  It only returns an
795  * error code if there was a severe problem such as out-of-memory.
796  */
797 int
798 set_windows_specific_info(WIMStruct *wim)
799 {
800         const struct wim_dentry *root, *potential_systemroot,
801                                 *best_systemroot = NULL,
802                                 *best_kernel32 = NULL,
803                                 *best_software = NULL,
804                                 *best_system = NULL;
805         int best_score = 0;
806
807         root = wim_get_current_root_dentry(wim);
808         if (!root)
809                 return 0;
810
811         /* Find the system root.  This is usually the toplevel directory
812          * "Windows", but it might be a different toplevel directory.  Choose
813          * the directory that contains the greatest number of the files we want:
814          * System32/kernel32.dll, System32/config/SOFTWARE, and
815          * System32/config/SYSTEM.  Compare all names case insensitively.  */
816         for_dentry_child(potential_systemroot, root) {
817                 const struct wim_dentry *system32, *kernel32, *config,
818                                         *software = NULL, *system = NULL;
819                 int score;
820
821                 if (!dentry_is_directory(potential_systemroot))
822                         continue;
823                 system32 = GET_CHILD(potential_systemroot, system32_name);
824                 if (!system32)
825                         continue;
826                 kernel32 = GET_CHILD(system32, kernel32_name);
827                 config = GET_CHILD(system32, config_name);
828                 if (config) {
829                         software = GET_CHILD(config, software_name);
830                         system = GET_CHILD(config, system_name);
831                 }
832
833                 score = !!kernel32 + !!software + !!system;
834                 if (score >= best_score) {
835                         /* If there's a tie, prefer the "Windows" directory.  */
836                         if (score > best_score ||
837                             is_default_systemroot(potential_systemroot))
838                         {
839                                 best_score = score;
840                                 best_systemroot = potential_systemroot;
841                                 best_kernel32 = kernel32;
842                                 best_software = software;
843                                 best_system = system;
844                         }
845                 }
846         }
847
848         if (likely(best_score == 0))
849                 return 0;  /* No Windows system root found.  */
850
851         /* Found the Windows system root.  */
852         return do_set_windows_specific_info(wim, best_systemroot, best_kernel32,
853                                             best_software, best_system);
854 }