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