xref: /reactos/base/applications/cmdutils/reg/copy.c (revision 84344399)
1 /*
2  * Copyright 2021 Hugh McMaster
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdio.h>
20 #include "reg.h"
21 
22 struct key {
23     HKEY root;      /* system key */
24     WCHAR *subkey;  /* relative path to subkey */
25     HKEY hkey;      /* handle to opened or created key */
26     WCHAR *path;    /* full path to subkey */
27 };
28 
29 enum operation {
30     COPY_NO,
31     COPY_YES,
32     COPY_ALL
33 };
34 
35 static void output_error(LONG rc)
36 {
37     if (rc == ERROR_FILE_NOT_FOUND)
38         output_message(STRING_KEY_NONEXIST);
39     else
40         output_message(STRING_ACCESS_DENIED);
41 }
42 
43 static enum operation ask_overwrite_value(WCHAR *path, WCHAR *value)
44 {
45     HMODULE hmod;
46     static WCHAR Ybuffer[4];
47     static WCHAR Nbuffer[4];
48     static WCHAR Abuffer[4];
49     static WCHAR defval[32];
50     WCHAR answer[MAX_PATH];
51     WCHAR *str;
52     DWORD count;
53 
54     hmod = GetModuleHandleW(NULL);
55     LoadStringW(hmod, STRING_YES, Ybuffer, ARRAY_SIZE(Ybuffer));
56     LoadStringW(hmod, STRING_NO,  Nbuffer, ARRAY_SIZE(Nbuffer));
57     LoadStringW(hmod, STRING_ALL, Abuffer, ARRAY_SIZE(Abuffer));
58     LoadStringW(hmod, STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval));
59 
60     str = (value && *value) ? value : defval;
61 
62     while (1)
63     {
64         output_message(STRING_COPY_CONFIRM, path, str);
65         output_message(STRING_YESNOALL);
66 
67         ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count, NULL);
68 
69         *answer = towupper(*answer);
70 
71         if (*answer == *Ybuffer)
72             return COPY_YES;
73         if (*answer == *Nbuffer)
74             return COPY_NO;
75         if (*answer == *Abuffer)
76             return COPY_ALL;
77     }
78 }
79 
80 static int run_copy(struct key *src, struct key *dest, REGSAM sam, BOOL recurse, BOOL force)
81 {
82     LONG rc;
83     DWORD max_subkey_len;
84     DWORD max_name_len, name_len;
85     DWORD max_data_size, data_size;
86     DWORD type, dispos, i;
87     WCHAR *name = NULL;
88     BYTE *data = NULL;
89 
90     if ((rc = RegOpenKeyExW(src->root, src->subkey, 0, KEY_READ|sam, &src->hkey)))
91     {
92         output_error(rc);
93         return 1;
94     }
95 
96     if ((rc = RegCreateKeyExW(dest->root, dest->subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
97                               KEY_READ|KEY_WRITE|sam, NULL, &dest->hkey, &dispos)))
98     {
99         RegCloseKey(src->hkey);
100         output_error(rc);
101         return 1;
102     }
103 
104     rc = RegQueryInfoKeyW(src->hkey, NULL, NULL, NULL, NULL, &max_subkey_len, NULL,
105                           NULL, &max_name_len, &max_data_size, NULL, NULL);
106     if (rc) goto cleanup;
107 
108     max_name_len = max(max_subkey_len, max_name_len) + 1;
109 
110     if (!(name = malloc(max_name_len * sizeof(WCHAR))))
111     {
112         rc = ERROR_NOT_ENOUGH_MEMORY;
113         goto cleanup;
114     }
115 
116     if (!(data = malloc(max_data_size)))
117     {
118         rc = ERROR_NOT_ENOUGH_MEMORY;
119         goto cleanup;
120     }
121 
122     for (i = 0; ; i++)
123     {
124         name_len = max_name_len;
125         data_size = max_data_size;
126 
127         rc = RegEnumValueW(src->hkey, i, name, &name_len, NULL, &type, data, &data_size);
128         if (rc == ERROR_NO_MORE_ITEMS) break;
129         if (rc) goto cleanup;
130 
131         if (!force && dispos == REG_OPENED_EXISTING_KEY)
132         {
133             if (!RegQueryValueExW(dest->hkey, name, NULL, NULL, NULL, NULL))
134             {
135                 enum operation op;
136 
137                 op = ask_overwrite_value(src->path, name);
138                 if (op == COPY_NO) continue;
139                 if (op == COPY_ALL) force = TRUE;
140             }
141         }
142 
143         if ((rc = RegSetValueExW(dest->hkey, name, 0, type, data, data_size)))
144         {
145             output_error(rc);
146             goto cleanup;
147         }
148     }
149 
150     for (i = 0; recurse; i++)
151     {
152         struct key subkey_src, subkey_dest;
153         size_t path_len;
154 
155         name_len = max_name_len;
156 
157         rc = RegEnumKeyExW(src->hkey, i, name, &name_len, NULL, NULL, NULL, NULL);
158         if (rc) break;
159 
160         subkey_src.root = src->hkey;
161         subkey_src.subkey = name;
162 
163         subkey_dest.root = dest->hkey;
164         subkey_dest.subkey = name;
165 
166         path_len = lstrlenW(src->path) + name_len + 2;
167 
168         if (!(subkey_src.path = malloc(path_len * sizeof(WCHAR))))
169         {
170             rc = ERROR_NOT_ENOUGH_MEMORY;
171             goto cleanup;
172         }
173 
174         swprintf(subkey_src.path, L"%s\\%s", src->path, name);
175 
176         rc = run_copy(&subkey_src, &subkey_dest, sam, TRUE, force);
177 
178         free(subkey_src.path);
179 
180         if (rc) goto cleanup;
181     }
182 
183 cleanup:
184     free(name);
185     free(data);
186 
187     RegCloseKey(src->hkey);
188     RegCloseKey(dest->hkey);
189 
190     return rc != ERROR_NO_MORE_ITEMS;
191 }
192 
193 int reg_copy(int argc, WCHAR *argvW[])
194 {
195     struct key src, dest;
196     BOOL recurse = FALSE, force = FALSE;
197     REGSAM sam = 0;
198     int i;
199 
200     if (argc == 3)
201         goto invalid;
202 
203     if (!parse_registry_key(argvW[2], &src.root, &src.subkey))
204         return 1;
205 
206     if (!parse_registry_key(argvW[3], &dest.root, &dest.subkey))
207         return 1;
208 
209     for (i = 4; i < argc; i++)
210     {
211         WCHAR *str;
212 
213         if (argvW[i][0] != '/' && argvW[i][0] != '-')
214             goto invalid;
215 
216         str = &argvW[i][1];
217 
218         if (!lstrcmpiW(str, L"reg:32"))
219         {
220             if (sam & KEY_WOW64_32KEY) goto invalid;
221             sam |= KEY_WOW64_32KEY;
222             continue;
223         }
224         else if (!lstrcmpiW(str, L"reg:64"))
225         {
226             if (sam & KEY_WOW64_64KEY) goto invalid;
227             sam |= KEY_WOW64_64KEY;
228             continue;
229         }
230         else if (!str[0] || str[1])
231             goto invalid;
232 
233         switch (towlower(*str))
234         {
235         case 's':
236             if (recurse) goto invalid;
237             recurse = TRUE;
238             break;
239         case 'f':
240             if (force) goto invalid;
241             force = TRUE;
242             break;
243         default:
244             goto invalid;
245         }
246     }
247 
248     if (sam == (KEY_WOW64_32KEY|KEY_WOW64_64KEY))
249         goto invalid;
250 
251     if (src.root == dest.root && !lstrcmpiW(src.subkey, dest.subkey))
252     {
253         output_message(STRING_COPY_SRC_DEST_SAME);
254         return 1;
255     }
256 
257     src.path = src.subkey;
258 
259     return run_copy(&src, &dest, sam, recurse, force);
260 
261 invalid:
262     output_message(STRING_INVALID_SYNTAX);
263     output_message(STRING_FUNC_HELP, _wcsupr(argvW[1]));
264     return 1;
265 }
266