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/device.h>
  26 #include <grub/file.h>
  27 #include <grub/env.h>
  28 #include <grub/command.h>
  29 #include <grub/search.h>
  30 #include <grub/i18n.h>
  31 #include <grub/disk.h>
  32 #include <grub/partition.h>
  33 
  34 GRUB_MOD_LICENSE ("GPLv3+");
  35 
  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 
  45 void
  46 FUNC_NAME (const char *key, const char *var, int no_floppy,
  47            char **hints, unsigned nhints)
  48 {
  49   int count = 0;
  50   int is_cache = 0;
  51   grub_fs_autoload_hook_t saved_autoload;
  52 
  53   auto int iterate_device (const char *name);
  54   int iterate_device (const char *name)
  55   {
  56     int found = 0;
  57 
  58     /* Skip floppy drives when requested.  */
  59     if (no_floppy &&
  60         name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9')
  61       return 0;
  62 
  63 #ifdef DO_SEARCH_FS_UUID
  64 #define compare_fn grub_strcasecmp
  65 #else
  66 #define compare_fn grub_strcmp
  67 #endif
  68 
  69 #ifdef DO_SEARCH_FILE
  70       {
  71         char *buf;
  72         grub_file_t file;
  73 
  74         buf = grub_xasprintf ("(%s)%s", name, key);
  75         if (! buf)
  76           return 1;
  77 
  78         grub_file_filter_disable_compression ();
  79         file = grub_file_open (buf);
  80         if (file)
  81           {
  82             found = 1;
  83             grub_file_close (file);
  84           }
  85         grub_free (buf);
  86       }
  87 #else
  88       {
  89         /* SEARCH_FS_UUID or SEARCH_LABEL */
  90         grub_device_t dev;
  91         grub_fs_t fs;
  92         char *quid;
  93 
  94         dev = grub_device_open (name);
  95         if (dev)
  96           {
  97             fs = grub_fs_probe (dev);
  98 
  99 #ifdef DO_SEARCH_FS_UUID
 100 #define read_fn uuid
 101 #else
 102 #define read_fn label
 103 #endif
 104 
 105             if (fs && fs->read_fn)
 106               {
 107                 fs->read_fn (dev, &quid);
 108 
 109                 if (grub_errno == GRUB_ERR_NONE && quid)
 110                   {
 111                     if (compare_fn (quid, key) == 0)
 112                       found = 1;
 113 
 114                     grub_free (quid);
 115                   }
 116               }
 117 
 118             grub_device_close (dev);
 119           }
 120       }
 121 #endif
 122 
 123     if (!is_cache && found && count == 0)
 124       {
 125         struct cache_entry *cache_ent;
 126         cache_ent = grub_malloc (sizeof (*cache_ent));
 127         if (cache_ent)
 128           {
 129             cache_ent->key = grub_strdup (key);
 130             cache_ent->value = grub_strdup (name);
 131             if (cache_ent->value && cache_ent->key)
 132               {
 133                 cache_ent->next = cache;
 134                 cache = cache_ent;
 135               }
 136             else
 137               {
 138                 grub_free (cache_ent->value);
 139                 grub_free (cache_ent->key);
 140                 grub_free (cache_ent);
 141                 grub_errno = GRUB_ERR_NONE;
 142               }
 143           }
 144         else
 145           grub_errno = GRUB_ERR_NONE;
 146       }
 147 
 148     if (found)
 149       {
 150         count++;
 151         if (var)
 152           grub_env_set (var, name);
 153         else
 154           grub_printf (" %s", name);
 155       }
 156 
 157     grub_errno = GRUB_ERR_NONE;
 158     return (found && var);
 159   }
 160 
 161   auto int part_hook (grub_disk_t disk, const grub_partition_t partition);
 162   int part_hook (grub_disk_t disk, const grub_partition_t partition)
 163   {
 164     char *partition_name, *devname;
 165     int ret;
 166 
 167     partition_name = grub_partition_get_name (partition);
 168     if (! partition_name)
 169       return 1;
 170 
 171     devname = grub_xasprintf ("%s,%s", disk->name, partition_name);
 172     grub_free (partition_name);
 173     if (!devname)
 174       return 1;
 175     ret = iterate_device (devname);
 176     grub_free (devname);    
 177 
 178     return ret;
 179   }
 180 
 181   auto void try (void);
 182   void try (void)    
 183   {
 184     unsigned i;
 185     struct cache_entry **prev;
 186     struct cache_entry *cache_ent;
 187 
 188     for (prev = &cache, cache_ent = *prev; cache_ent;
 189          prev = &cache_ent->next, cache_ent = *prev)
 190       if (compare_fn (cache_ent->key, key) == 0)
 191         break;
 192     if (cache_ent)
 193       {
 194         is_cache = 1;
 195         if (iterate_device (cache_ent->value))
 196           {
 197             is_cache = 0;
 198             return;
 199           }
 200         is_cache = 0;
 201         /* Cache entry was outdated. Remove it.  */
 202         if (!count)
 203           {
 204             grub_free (cache_ent->key);
 205             grub_free (cache_ent->value);
 206             grub_free (cache_ent);
 207             *prev = cache_ent->next;
 208           }
 209       }
 210 
 211     for (i = 0; i < nhints; i++)
 212       {
 213         char *end;
 214         if (!hints[i][0])
 215           continue;
 216         end = hints[i] + grub_strlen (hints[i]) - 1;
 217         if (*end == ',')
 218           *end = 0;
 219         if (iterate_device (hints[i]))
 220           {
 221             if (!*end)
 222               *end = ',';
 223             return;
 224           }
 225         if (!*end)
 226           {
 227             grub_device_t dev;
 228             int ret;
 229             dev = grub_device_open (hints[i]);
 230             if (!dev)
 231               {
 232                 if (!*end)
 233                   *end = ',';
 234                 continue;
 235               }
 236             if (!dev->disk)
 237               {
 238                 grub_device_close (dev);
 239                 if (!*end)
 240                   *end = ',';
 241                 continue;
 242               }
 243             ret = grub_partition_iterate (dev->disk, part_hook);
 244             if (!*end)
 245               *end = ',';
 246             grub_device_close (dev);
 247             if (ret)
 248               return;
 249           }
 250       }
 251     grub_device_iterate (iterate_device);
 252   }
 253 
 254   /* First try without autoloading if we're setting variable. */
 255   if (var)
 256     {
 257       saved_autoload = grub_fs_autoload_hook;
 258       grub_fs_autoload_hook = 0;
 259       try ();
 260 
 261       /* Restore autoload hook.  */
 262       grub_fs_autoload_hook = saved_autoload;
 263 
 264       /* Retry with autoload if nothing found.  */
 265       if (grub_errno == GRUB_ERR_NONE && count == 0)
 266         try ();
 267     }
 268   else
 269     try ();
 270 
 271   if (grub_errno == GRUB_ERR_NONE && count == 0)
 272     grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key);
 273 }
 274 
 275 static grub_err_t
 276 grub_cmd_do_search (grub_command_t cmd __attribute__ ((unused)), int argc,
 277                     char **args)
 278 {
 279   if (argc == 0)
 280     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
 281 
 282   FUNC_NAME (args[0], argc == 1 ? 0 : args[1], 0, (args + 2),
 283              argc > 2 ? argc - 2 : 0);
 284 
 285   return grub_errno;
 286 }
 287 
 288 static grub_command_t cmd;
 289 
 290 #ifdef DO_SEARCH_FILE
 291 GRUB_MOD_INIT(search_fs_file)
 292 #elif defined (DO_SEARCH_FS_UUID)
 293 GRUB_MOD_INIT(search_fs_uuid)
 294 #else
 295 GRUB_MOD_INIT(search_label)
 296 #endif
 297 {
 298   cmd =
 299     grub_register_command (COMMAND_NAME, grub_cmd_do_search,
 300                            N_("NAME [VARIABLE] [HINTS]"),
 301                            HELP_MESSAGE);
 302 }
 303 
 304 #ifdef DO_SEARCH_FILE
 305 GRUB_MOD_FINI(search_fs_file)
 306 #elif defined (DO_SEARCH_FS_UUID)
 307 GRUB_MOD_FINI(search_fs_uuid)
 308 #else
 309 GRUB_MOD_FINI(search_label)
 310 #endif
 311 {
 312   grub_unregister_command (cmd);
 313 }