]>
Commit | Line | Data |
---|---|---|
6a85ce79 | 1 | /* search.c - search devices based on a file or a filesystem label */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
2bf5885a | 4 | * Copyright (C) 2005,2007,2008,2009 Free Software Foundation, Inc. |
6a85ce79 | 5 | * |
5a79f472 | 6 | * GRUB is free software: you can redistribute it and/or modify |
6a85ce79 | 7 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 8 | * the Free Software Foundation, either version 3 of the License, or |
6a85ce79 | 9 | * (at your option) any later version. |
10 | * | |
5a79f472 | 11 | * GRUB is distributed in the hope that it will be useful, |
6a85ce79 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
5a79f472 | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
6a85ce79 | 18 | */ |
19 | ||
20 | #include <grub/types.h> | |
21 | #include <grub/misc.h> | |
22 | #include <grub/mm.h> | |
23 | #include <grub/err.h> | |
24 | #include <grub/dl.h> | |
6a85ce79 | 25 | #include <grub/device.h> |
26 | #include <grub/file.h> | |
27 | #include <grub/env.h> | |
937d332d | 28 | #include <grub/command.h> |
49968932 | 29 | #include <grub/search.h> |
ec5f98ab | 30 | #include <grub/i18n.h> |
1f1dd48a VS |
31 | #include <grub/disk.h> |
32 | #include <grub/partition.h> | |
6a85ce79 | 33 | |
e745cf0c VS |
34 | GRUB_MOD_LICENSE ("GPLv3+"); |
35 | ||
f4d5a8ce VS |
36 | struct cache_entry |
37 | { | |
38 | struct cache_entry *next; | |
39 | char *key; | |
40 | char *value; | |
41 | }; | |
42 | ||
43 | static struct cache_entry *cache; | |
44 | ||
25239370 CW |
45 | /* Context for FUNC_NAME. */ |
46 | struct search_ctx | |
6a85ce79 | 47 | { |
25239370 CW |
48 | const char *key; |
49 | const char *var; | |
50 | int no_floppy; | |
51 | char **hints; | |
52 | unsigned nhints; | |
53 | int count; | |
54 | int is_cache; | |
55 | }; | |
62191274 | 56 | |
25239370 CW |
57 | /* Helper for FUNC_NAME. */ |
58 | static int | |
59 | iterate_device (const char *name, void *data) | |
60 | { | |
61 | struct search_ctx *ctx = data; | |
62 | int found = 0; | |
7d8a52d3 | 63 | |
25239370 CW |
64 | /* Skip floppy drives when requested. */ |
65 | if (ctx->no_floppy && | |
66 | name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9') | |
1b782e90 | 67 | return 1; |
7d8a52d3 | 68 | |
f4d5a8ce VS |
69 | #ifdef DO_SEARCH_FS_UUID |
70 | #define compare_fn grub_strcasecmp | |
71 | #else | |
72 | #define compare_fn grub_strcmp | |
73 | #endif | |
74 | ||
063e925f | 75 | #ifdef DO_SEARCH_FILE |
25239370 CW |
76 | { |
77 | char *buf; | |
78 | grub_file_t file; | |
79 | ||
80 | buf = grub_xasprintf ("(%s)%s", name, ctx->key); | |
81 | if (! buf) | |
82 | return 1; | |
83 | ||
ca0a4f68 VS |
84 | file = grub_file_open (buf, GRUB_FILE_TYPE_FS_SEARCH |
85 | | GRUB_FILE_TYPE_NO_DECOMPRESS); | |
25239370 CW |
86 | if (file) |
87 | { | |
88 | found = 1; | |
89 | grub_file_close (file); | |
90 | } | |
91 | grub_free (buf); | |
92 | } | |
063e925f | 93 | #else |
25239370 CW |
94 | { |
95 | /* SEARCH_FS_UUID or SEARCH_LABEL */ | |
96 | grub_device_t dev; | |
97 | grub_fs_t fs; | |
98 | char *quid; | |
7d8a52d3 | 99 | |
25239370 CW |
100 | dev = grub_device_open (name); |
101 | if (dev) | |
102 | { | |
103 | fs = grub_fs_probe (dev); | |
7d8a52d3 | 104 | |
063e925f | 105 | #ifdef DO_SEARCH_FS_UUID |
063e925f | 106 | #define read_fn uuid |
107 | #else | |
063e925f | 108 | #define read_fn label |
109 | #endif | |
110 | ||
25239370 CW |
111 | if (fs && fs->read_fn) |
112 | { | |
113 | fs->read_fn (dev, &quid); | |
7d8a52d3 | 114 | |
25239370 CW |
115 | if (grub_errno == GRUB_ERR_NONE && quid) |
116 | { | |
117 | if (compare_fn (quid, ctx->key) == 0) | |
118 | found = 1; | |
7d8a52d3 | 119 | |
25239370 CW |
120 | grub_free (quid); |
121 | } | |
122 | } | |
7d8a52d3 | 123 | |
25239370 CW |
124 | grub_device_close (dev); |
125 | } | |
126 | } | |
063e925f | 127 | #endif |
7d8a52d3 | 128 | |
25239370 CW |
129 | if (!ctx->is_cache && found && ctx->count == 0) |
130 | { | |
131 | struct cache_entry *cache_ent; | |
132 | cache_ent = grub_malloc (sizeof (*cache_ent)); | |
133 | if (cache_ent) | |
134 | { | |
135 | cache_ent->key = grub_strdup (ctx->key); | |
136 | cache_ent->value = grub_strdup (name); | |
137 | if (cache_ent->value && cache_ent->key) | |
138 | { | |
139 | cache_ent->next = cache; | |
140 | cache = cache_ent; | |
141 | } | |
142 | else | |
143 | { | |
144 | grub_free (cache_ent->value); | |
145 | grub_free (cache_ent->key); | |
146 | grub_free (cache_ent); | |
147 | grub_errno = GRUB_ERR_NONE; | |
148 | } | |
149 | } | |
150 | else | |
151 | grub_errno = GRUB_ERR_NONE; | |
152 | } | |
153 | ||
154 | if (found) | |
155 | { | |
156 | ctx->count++; | |
157 | if (ctx->var) | |
158 | grub_env_set (ctx->var, name); | |
159 | else | |
160 | grub_printf (" %s", name); | |
161 | } | |
162 | ||
163 | grub_errno = GRUB_ERR_NONE; | |
164 | return (found && ctx->var); | |
165 | } | |
166 | ||
167 | /* Helper for FUNC_NAME. */ | |
168 | static int | |
169 | part_hook (grub_disk_t disk, const grub_partition_t partition, void *data) | |
170 | { | |
171 | struct search_ctx *ctx = data; | |
172 | char *partition_name, *devname; | |
173 | int ret; | |
174 | ||
175 | partition_name = grub_partition_get_name (partition); | |
176 | if (! partition_name) | |
177 | return 1; | |
178 | ||
179 | devname = grub_xasprintf ("%s,%s", disk->name, partition_name); | |
180 | grub_free (partition_name); | |
181 | if (!devname) | |
182 | return 1; | |
183 | ret = iterate_device (devname, ctx); | |
184 | grub_free (devname); | |
185 | ||
186 | return ret; | |
187 | } | |
188 | ||
189 | /* Helper for FUNC_NAME. */ | |
190 | static void | |
191 | try (struct search_ctx *ctx) | |
192 | { | |
193 | unsigned i; | |
194 | struct cache_entry **prev; | |
195 | struct cache_entry *cache_ent; | |
196 | ||
197 | for (prev = &cache, cache_ent = *prev; cache_ent; | |
198 | prev = &cache_ent->next, cache_ent = *prev) | |
199 | if (compare_fn (cache_ent->key, ctx->key) == 0) | |
200 | break; | |
201 | if (cache_ent) | |
202 | { | |
203 | ctx->is_cache = 1; | |
204 | if (iterate_device (cache_ent->value, ctx)) | |
205 | { | |
206 | ctx->is_cache = 0; | |
207 | return; | |
208 | } | |
209 | ctx->is_cache = 0; | |
210 | /* Cache entry was outdated. Remove it. */ | |
211 | if (!ctx->count) | |
212 | { | |
0fb886cd | 213 | *prev = cache_ent->next; |
25239370 CW |
214 | grub_free (cache_ent->key); |
215 | grub_free (cache_ent->value); | |
216 | grub_free (cache_ent); | |
25239370 CW |
217 | } |
218 | } | |
219 | ||
220 | for (i = 0; i < ctx->nhints; i++) | |
221 | { | |
222 | char *end; | |
223 | if (!ctx->hints[i][0]) | |
224 | continue; | |
225 | end = ctx->hints[i] + grub_strlen (ctx->hints[i]) - 1; | |
226 | if (*end == ',') | |
227 | *end = 0; | |
228 | if (iterate_device (ctx->hints[i], ctx)) | |
229 | { | |
230 | if (!*end) | |
231 | *end = ','; | |
232 | return; | |
233 | } | |
234 | if (!*end) | |
235 | { | |
236 | grub_device_t dev; | |
237 | int ret; | |
238 | dev = grub_device_open (ctx->hints[i]); | |
239 | if (!dev) | |
240 | { | |
241 | if (!*end) | |
242 | *end = ','; | |
243 | continue; | |
244 | } | |
245 | if (!dev->disk) | |
246 | { | |
247 | grub_device_close (dev); | |
248 | if (!*end) | |
249 | *end = ','; | |
250 | continue; | |
251 | } | |
252 | ret = grub_partition_iterate (dev->disk, part_hook, ctx); | |
253 | if (!*end) | |
254 | *end = ','; | |
255 | grub_device_close (dev); | |
256 | if (ret) | |
1f1dd48a | 257 | return; |
25239370 CW |
258 | } |
259 | } | |
260 | grub_device_iterate (iterate_device, ctx); | |
261 | } | |
262 | ||
263 | void | |
264 | FUNC_NAME (const char *key, const char *var, int no_floppy, | |
265 | char **hints, unsigned nhints) | |
266 | { | |
267 | struct search_ctx ctx = { | |
268 | .key = key, | |
269 | .var = var, | |
270 | .no_floppy = no_floppy, | |
271 | .hints = hints, | |
272 | .nhints = nhints, | |
273 | .count = 0, | |
274 | .is_cache = 0 | |
275 | }; | |
276 | grub_fs_autoload_hook_t saved_autoload; | |
7b748576 | 277 | |
e9a925da | 278 | /* First try without autoloading if we're setting variable. */ |
279 | if (var) | |
280 | { | |
281 | saved_autoload = grub_fs_autoload_hook; | |
282 | grub_fs_autoload_hook = 0; | |
25239370 | 283 | try (&ctx); |
e9a925da | 284 | |
285 | /* Restore autoload hook. */ | |
286 | grub_fs_autoload_hook = saved_autoload; | |
287 | ||
288 | /* Retry with autoload if nothing found. */ | |
25239370 CW |
289 | if (grub_errno == GRUB_ERR_NONE && ctx.count == 0) |
290 | try (&ctx); | |
e9a925da | 291 | } |
292 | else | |
25239370 | 293 | try (&ctx); |
b39f9d20 | 294 | |
25239370 | 295 | if (grub_errno == GRUB_ERR_NONE && ctx.count == 0) |
7d8a52d3 | 296 | grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key); |
6a85ce79 | 297 | } |
298 | ||
299 | static grub_err_t | |
063e925f | 300 | grub_cmd_do_search (grub_command_t cmd __attribute__ ((unused)), int argc, |
301 | char **args) | |
6a85ce79 | 302 | { |
6a85ce79 | 303 | if (argc == 0) |
9c4b5c13 | 304 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
b39f9d20 | 305 | |
1f1dd48a | 306 | FUNC_NAME (args[0], argc == 1 ? 0 : args[1], 0, (args + 2), |
7b748576 | 307 | argc > 2 ? argc - 2 : 0); |
6a85ce79 | 308 | |
309 | return grub_errno; | |
310 | } | |
311 | ||
063e925f | 312 | static grub_command_t cmd; |
b1b797cb | 313 | |
49968932 | 314 | #ifdef DO_SEARCH_FILE |
19a9fb83 | 315 | GRUB_MOD_INIT(search_fs_file) |
49968932 | 316 | #elif defined (DO_SEARCH_FS_UUID) |
063e925f | 317 | GRUB_MOD_INIT(search_fs_uuid) |
318 | #else | |
19a9fb83 | 319 | GRUB_MOD_INIT(search_label) |
063e925f | 320 | #endif |
6a85ce79 | 321 | { |
b1b797cb | 322 | cmd = |
063e925f | 323 | grub_register_command (COMMAND_NAME, grub_cmd_do_search, |
a848c54e | 324 | N_("NAME [VARIABLE] [HINTS]"), |
40e3a41f | 325 | HELP_MESSAGE); |
6a85ce79 | 326 | } |
327 | ||
49968932 | 328 | #ifdef DO_SEARCH_FILE |
19a9fb83 | 329 | GRUB_MOD_FINI(search_fs_file) |
49968932 | 330 | #elif defined (DO_SEARCH_FS_UUID) |
063e925f | 331 | GRUB_MOD_FINI(search_fs_uuid) |
332 | #else | |
19a9fb83 | 333 | GRUB_MOD_FINI(search_label) |
063e925f | 334 | #endif |
6a85ce79 | 335 | { |
063e925f | 336 | grub_unregister_command (cmd); |
6a85ce79 | 337 | } |