~ubuntu-branches/debian/sid/grub2/sid-200907171837

« back to all changes in this revision

Viewing changes to util/i386/pc/biosdisk.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060105152040-1ab076d4n3y2o5yf
Tags: 1.92-1
* New upstream release.
  - Add support for GPT partition table format.
  - Add a new command "play" to play an audio file on PC.
  - Add support for Linux/ADFS partition table format.
  - Add support for BASH-like scripting.
  - Add support for Apple HFS+ filesystems.
* 01_fix_grub-install.patch: Added. Fix grub-install to use
  /bin/grub-mkimage instead of /sbin/grub-mkimage. Closes: #338824
* Do not use CDBS tarball mode anymore. Closes: #344272  

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* biosdisk.c - emulate biosdisk */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 1999,2000,2001,2002,2003,2004  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 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program 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, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
#include <grub/machine/biosdisk.h>
 
22
#include <grub/disk.h>
 
23
#include <grub/partition.h>
 
24
#include <grub/pc_partition.h>
 
25
#include <grub/types.h>
 
26
#include <grub/err.h>
 
27
#include <grub/util/misc.h>
 
28
 
 
29
#include <stdio.h>
 
30
#include <stdlib.h>
 
31
#include <string.h>
 
32
#include <ctype.h>
 
33
#include <assert.h>
 
34
#include <unistd.h>
 
35
#include <sys/types.h>
 
36
#include <sys/stat.h>
 
37
#include <fcntl.h>
 
38
#include <errno.h>
 
39
#include <limits.h>
 
40
 
 
41
#ifdef __linux__
 
42
# include <sys/ioctl.h>         /* ioctl */
 
43
# if !defined(__GLIBC__) || \
 
44
        ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
 
45
/* Maybe libc doesn't have large file support.  */
 
46
#  include <linux/unistd.h>     /* _llseek */
 
47
# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
 
48
# ifndef BLKFLSBUF
 
49
#  define BLKFLSBUF     _IO (0x12,97)   /* flush buffer cache */
 
50
# endif /* ! BLKFLSBUF */
 
51
# include <sys/ioctl.h>         /* ioctl */
 
52
# ifndef HDIO_GETGEO
 
53
#  define HDIO_GETGEO   0x0301  /* get device geometry */
 
54
/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
 
55
   defined.  */
 
56
struct hd_geometry
 
57
{
 
58
  unsigned char heads;
 
59
  unsigned char sectors;
 
60
  unsigned short cylinders;
 
61
  unsigned long start;
 
62
};
 
63
# endif /* ! HDIO_GETGEO */
 
64
# ifndef BLKGETSIZE
 
65
#  define BLKGETSIZE    _IO(0x12,96)    /* return device size */
 
66
# endif /* ! BLKGETSIZE */
 
67
# ifndef MAJOR
 
68
#  ifndef MINORBITS
 
69
#   define MINORBITS    8
 
70
#  endif /* ! MINORBITS */
 
71
#  define MAJOR(dev)    ((unsigned) ((dev) >> MINORBITS))
 
72
# endif /* ! MAJOR */
 
73
# ifndef FLOPPY_MAJOR
 
74
#  define FLOPPY_MAJOR  2
 
75
# endif /* ! FLOPPY_MAJOR */
 
76
# ifndef LOOP_MAJOR
 
77
#  define LOOP_MAJOR    7
 
78
# endif /* ! LOOP_MAJOR */
 
79
#endif /* __linux__ */
 
80
 
 
81
static char *map[256];
 
82
 
 
83
#ifdef __linux__
 
84
/* Check if we have devfs support.  */
 
85
static int
 
86
have_devfs (void)
 
87
{
 
88
  static int dev_devfsd_exists = -1;
 
89
 
 
90
  if (dev_devfsd_exists < 0)
 
91
    {
 
92
      struct stat st;
 
93
 
 
94
      dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
 
95
    }
 
96
 
 
97
  return dev_devfsd_exists;
 
98
}
 
99
#endif /* __linux__ */
 
100
 
 
101
static int
 
102
get_drive (const char *name)
 
103
{
 
104
  unsigned long drive;
 
105
  char *p;
 
106
  
 
107
  if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
 
108
    goto fail;
 
109
 
 
110
  drive = strtoul (name + 2, &p, 10);
 
111
  if (p == name + 2)
 
112
    goto fail;
 
113
 
 
114
  if (name[0] == 'h')
 
115
    drive += 0x80;
 
116
 
 
117
  if (drive > sizeof (map) / sizeof (map[0]))
 
118
    goto fail;
 
119
  
 
120
  return (int) drive;
 
121
 
 
122
 fail:
 
123
  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
 
124
  return -1;
 
125
}
 
126
 
 
127
static int
 
128
call_hook (int (*hook) (const char *name), int drive)
 
129
{
 
130
  char name[10];
 
131
 
 
132
  sprintf (name, (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
 
133
  return hook (name);
 
134
}
 
135
 
 
136
static int
 
137
grub_util_biosdisk_iterate (int (*hook) (const char *name))
 
138
{
 
139
  unsigned i;
 
140
 
 
141
  for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
 
142
    if (map[i] && call_hook (hook, i))
 
143
      return 1;
 
144
 
 
145
  return 0;
 
146
}
 
147
 
 
148
static grub_err_t
 
149
grub_util_biosdisk_open (const char *name, grub_disk_t disk)
 
150
{
 
151
  int drive;
 
152
  struct stat st;
 
153
  
 
154
  drive = get_drive (name);
 
155
  if (drive < 0)
 
156
    return grub_errno;
 
157
 
 
158
  if (! map[drive])
 
159
    return grub_error (GRUB_ERR_BAD_DEVICE,
 
160
                       "no mapping exists for `%s'", name);
 
161
  
 
162
  disk->has_partitions = (drive & 0x80);
 
163
  disk->id = drive;
 
164
 
 
165
  /* Get the size.  */
 
166
#ifdef __linux__
 
167
  {
 
168
    unsigned long nr;
 
169
    int fd;
 
170
 
 
171
    fd = open (map[drive], O_RDONLY);
 
172
    if (! fd)
 
173
      return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[drive]);
 
174
 
 
175
    if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
 
176
      {
 
177
        close (fd);
 
178
        goto fail;
 
179
      }
 
180
    
 
181
    if (ioctl (fd, BLKGETSIZE, &nr))
 
182
      {
 
183
        close (fd);
 
184
        goto fail;
 
185
      }
 
186
 
 
187
    close (fd);
 
188
    disk->total_sectors = nr;
 
189
    
 
190
    grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
 
191
    
 
192
    return GRUB_ERR_NONE;
 
193
  }
 
194
 
 
195
 fail:
 
196
  /* In GNU/Hurd, stat() will return the right size.  */
 
197
#elif !defined (__GNU__)
 
198
# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
 
199
#endif
 
200
  if (stat (map[drive], &st) < 0)
 
201
    return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive]);
 
202
 
 
203
  disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
 
204
  
 
205
  grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
 
206
  
 
207
  return GRUB_ERR_NONE;
 
208
}
 
209
 
 
210
#ifdef __linux__
 
211
static int
 
212
linux_find_partition (char *dev, unsigned long sector)
 
213
{
 
214
  size_t len = strlen (dev);
 
215
  const char *format;
 
216
  char *p;
 
217
  int i;
 
218
  char *real_dev;
 
219
 
 
220
  real_dev = xstrdup (dev);
 
221
  
 
222
  if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
 
223
    {
 
224
      p = real_dev + len - 4;
 
225
      format = "part%d";
 
226
    }
 
227
  else if ((strncmp (real_dev + 5, "hd", 2) == 0
 
228
            || strncmp (real_dev + 5, "sd", 2) == 0)
 
229
           && real_dev[7] >= 'a' && real_dev[7] <= 'z')
 
230
    {
 
231
      p = real_dev + 8;
 
232
      format = "%d";
 
233
    }
 
234
  else if (strncmp (real_dev + 5, "rd/c", 4) == 0)
 
235
    {
 
236
      p = strchr (real_dev + 9, 'd');
 
237
      if (! p)
 
238
        return 0;
 
239
 
 
240
      p++;
 
241
      while (*p && isdigit (*p))
 
242
        p++;
 
243
 
 
244
      format = "p%d";
 
245
    }
 
246
  else
 
247
    {
 
248
      free (real_dev);
 
249
      return 0;
 
250
    }
 
251
 
 
252
  for (i = 1; i < 10000; i++)
 
253
    {
 
254
      int fd;
 
255
      struct hd_geometry hdg;
 
256
      
 
257
      sprintf (p, format, i);
 
258
      fd = open (real_dev, O_RDONLY);
 
259
      if (! fd)
 
260
        {
 
261
          free (real_dev);
 
262
          return 0;
 
263
        }
 
264
 
 
265
      if (ioctl (fd, HDIO_GETGEO, &hdg))
 
266
        {
 
267
          close (fd);
 
268
          free (real_dev);
 
269
          return 0;
 
270
        }
 
271
 
 
272
      close (fd);
 
273
      
 
274
      if (hdg.start == sector)
 
275
        {
 
276
          strcpy (dev, real_dev);
 
277
          free (real_dev);
 
278
          return 1;
 
279
        }
 
280
    }
 
281
 
 
282
  free (real_dev);
 
283
  return 0;
 
284
}
 
285
#endif /* __linux__ */
 
286
 
 
287
static int
 
288
open_device (const grub_disk_t disk, unsigned long sector, int flags)
 
289
{
 
290
  int fd;
 
291
 
 
292
#ifdef O_LARGEFILE
 
293
  flags |= O_LARGEFILE;
 
294
#endif
 
295
#ifdef O_SYNC
 
296
  flags |= O_SYNC;
 
297
#endif
 
298
#ifdef O_FSYNC
 
299
  flags |= O_FSYNC;
 
300
#endif
 
301
  
 
302
#ifdef __linux__
 
303
  /* Linux has a bug that the disk cache for a whole disk is not consistent
 
304
     with the one for a partition of the disk.  */
 
305
  {
 
306
    int is_partition = 0;
 
307
    char dev[PATH_MAX];
 
308
    
 
309
    strcpy (dev, map[disk->id]);
 
310
    if (disk->partition && strncmp (map[disk->id], "/dev/", 5) == 0)
 
311
      is_partition = linux_find_partition (dev, disk->partition->start);
 
312
    
 
313
    /* Open the partition.  */
 
314
    grub_util_info ("opening the device `%s'", dev);
 
315
    fd = open (dev, flags);
 
316
    if (fd < 0)
 
317
      {
 
318
        grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
 
319
        return -1;
 
320
      }
 
321
 
 
322
    /* Make the buffer cache consistent with the physical disk.  */
 
323
    ioctl (fd, BLKFLSBUF, 0);
 
324
    
 
325
    if (is_partition)
 
326
      sector -= disk->partition->start;
 
327
  }
 
328
#else /* ! __linux__ */
 
329
  fd = open (map[disk->id], flags);
 
330
  if (fd < 0)
 
331
    {
 
332
      grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[disk->id]);
 
333
      return -1;
 
334
    }
 
335
#endif /* ! __linux__ */
 
336
 
 
337
#if defined(__linux__) && (!defined(__GLIBC__) || \
 
338
        ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
 
339
  /* Maybe libc doesn't have large file support.  */
 
340
  {
 
341
    loff_t offset, result;
 
342
    static int _llseek (uint filedes, ulong hi, ulong lo,
 
343
                        loff_t *res, uint wh);
 
344
    _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
 
345
               loff_t *, res, uint, wh);
 
346
 
 
347
    offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
 
348
    if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
 
349
      {
 
350
        grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id]);
 
351
        close (fd);
 
352
        return -1;
 
353
      }
 
354
  }
 
355
#else
 
356
  {
 
357
    off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
 
358
 
 
359
    if (lseek (fd, offset, SEEK_SET) != offset)
 
360
      {
 
361
        grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id]);
 
362
        close (fd);
 
363
        return -1;
 
364
      }
 
365
  }
 
366
#endif
 
367
 
 
368
  return fd;
 
369
}
 
370
  
 
371
/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
 
372
   error occurs, otherwise return LEN.  */
 
373
static ssize_t
 
374
nread (int fd, char *buf, size_t len)
 
375
{
 
376
  ssize_t size = len;
 
377
  
 
378
  while (len)
 
379
    {
 
380
      ssize_t ret = read (fd, buf, len);
 
381
      
 
382
      if (ret <= 0)
 
383
        {
 
384
          if (errno == EINTR)
 
385
            continue;
 
386
          else
 
387
            return ret;
 
388
        }
 
389
      
 
390
      len -= ret;
 
391
      buf += ret;
 
392
    }
 
393
  
 
394
  return size;
 
395
}
 
396
 
 
397
/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
 
398
   error occurs, otherwise return LEN.  */
 
399
static ssize_t
 
400
nwrite (int fd, const char *buf, size_t len)
 
401
{
 
402
  ssize_t size = len;
 
403
  
 
404
  while (len)
 
405
    {
 
406
      ssize_t ret = write (fd, buf, len);
 
407
      
 
408
      if (ret <= 0)
 
409
        {
 
410
          if (errno == EINTR)
 
411
            continue;
 
412
          else
 
413
            return ret;
 
414
        }
 
415
      
 
416
      len -= ret;
 
417
      buf += ret;
 
418
    }
 
419
  
 
420
  return size;
 
421
}
 
422
 
 
423
static grub_err_t
 
424
grub_util_biosdisk_read (grub_disk_t disk, unsigned long sector,
 
425
                         unsigned long size, char *buf)
 
426
{
 
427
  int fd;
 
428
 
 
429
  fd = open_device (disk, sector, O_RDONLY);
 
430
  if (fd < 0)
 
431
    return grub_errno;
 
432
  
 
433
#ifdef __linux__
 
434
  if (sector == 0 && size > 1)
 
435
    {
 
436
      /* Work around a bug in linux's ez remapping.  Linux remaps all
 
437
         sectors that are read together with the MBR in one read.  It
 
438
         should only remap the MBR, so we split the read in two 
 
439
         parts. -jochen  */
 
440
      if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
 
441
        {
 
442
          grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id]);
 
443
          close (fd);
 
444
          return grub_errno;
 
445
        }
 
446
      
 
447
      buf += GRUB_DISK_SECTOR_SIZE;
 
448
      size--;
 
449
    }
 
450
#endif /* __linux__ */
 
451
  
 
452
  if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
 
453
      != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
 
454
    grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id]);
 
455
 
 
456
  close (fd);
 
457
  return grub_errno;
 
458
}
 
459
 
 
460
static grub_err_t
 
461
grub_util_biosdisk_write (grub_disk_t disk, unsigned long sector,
 
462
                          unsigned long size, const char *buf)
 
463
{
 
464
  int fd;
 
465
 
 
466
  fd = open_device (disk, sector, O_WRONLY);
 
467
  if (fd < 0)
 
468
    return grub_errno;
 
469
  
 
470
  if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
 
471
      != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
 
472
    grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id]);
 
473
 
 
474
  close (fd);
 
475
  return grub_errno;
 
476
}
 
477
 
 
478
static struct grub_disk_dev grub_util_biosdisk_dev =
 
479
  {
 
480
    .name = "biosdisk",
 
481
    .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
 
482
    .iterate = grub_util_biosdisk_iterate,
 
483
    .open = grub_util_biosdisk_open,
 
484
    .close = 0,
 
485
    .read = grub_util_biosdisk_read,
 
486
    .write = grub_util_biosdisk_write,
 
487
    .next = 0
 
488
  };
 
489
 
 
490
static void
 
491
read_device_map (const char *dev_map)
 
492
{
 
493
  FILE *fp;
 
494
  char buf[1024];       /* XXX */
 
495
  int lineno = 0;
 
496
  auto void show_error (const char *msg);
 
497
 
 
498
  void show_error (const char *msg)
 
499
    {
 
500
      grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
 
501
    }
 
502
  
 
503
  fp = fopen (dev_map, "r");
 
504
  if (! fp)
 
505
    grub_util_error ("Cannot open `%s'", dev_map);
 
506
 
 
507
  while (fgets (buf, sizeof (buf), fp))
 
508
    {
 
509
      char *p = buf;
 
510
      char *e;
 
511
      int drive;
 
512
      
 
513
      lineno++;
 
514
      
 
515
      /* Skip leading spaces.  */
 
516
      while (*p && isspace (*p))
 
517
        p++;
 
518
 
 
519
      /* If the first character is `#' or NUL, skip this line.  */
 
520
      if (*p == '\0' || *p == '#')
 
521
        continue;
 
522
 
 
523
      if (*p != '(')
 
524
        show_error ("No open parenthesis found");
 
525
 
 
526
      p++;
 
527
      drive = get_drive (p);
 
528
      if (drive < 0 || drive >= (int) (sizeof (map) / sizeof (map[0])))
 
529
        show_error ("Bad device name");
 
530
 
 
531
      p = strchr (p, ')');
 
532
      if (! p)
 
533
        show_error ("No close parenthesis found");
 
534
 
 
535
      p++;
 
536
      /* Skip leading spaces.  */
 
537
      while (*p && isspace (*p))
 
538
        p++;
 
539
 
 
540
      if (*p == '\0')
 
541
        show_error ("No filename found");
 
542
 
 
543
      /* NUL-terminate the filename.  */
 
544
      e = p;
 
545
      while (*e && ! isspace (*e))
 
546
        e++;
 
547
      *e = '\0';
 
548
 
 
549
      /* Multiple entries for a given drive is not allowed.  */
 
550
      if (map[drive])
 
551
        show_error ("Duplicated entry found");
 
552
 
 
553
#ifdef __linux__
 
554
      /* On Linux, the devfs uses symbolic links horribly, and that
 
555
         confuses the interface very much, so use realpath to expand
 
556
         symbolic links.  */
 
557
      map[drive] = xmalloc (PATH_MAX);
 
558
      if (! realpath (p, map[drive]))
 
559
        grub_util_error ("Cannot get the real path of `%s'", p);
 
560
#else
 
561
      map[drive] = xstrdup (p);
 
562
#endif
 
563
    }
 
564
 
 
565
  fclose (fp);
 
566
}
 
567
 
 
568
void
 
569
grub_util_biosdisk_init (const char *dev_map)
 
570
{
 
571
  read_device_map (dev_map);
 
572
  grub_disk_dev_register (&grub_util_biosdisk_dev);
 
573
}
 
574
 
 
575
void
 
576
grub_util_biosdisk_fini (void)
 
577
{
 
578
  unsigned i;
 
579
  
 
580
  for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
 
581
    free (map[i]);
 
582
  
 
583
  grub_disk_dev_unregister (&grub_util_biosdisk_dev);
 
584
}
 
585
 
 
586
static char *
 
587
make_device_name (int drive, int dos_part, int bsd_part)
 
588
{
 
589
  char *p;
 
590
 
 
591
  p = xmalloc (30);
 
592
  sprintf (p, (drive & 0x80) ? "hd%d" : "fd%d", drive & ~0x80);
 
593
  
 
594
  if (dos_part >= 0)
 
595
    sprintf (p + strlen (p), ",%d", dos_part);
 
596
  
 
597
  if (bsd_part >= 0)
 
598
    sprintf (p + strlen (p), ",%c", bsd_part + 'a');
 
599
  
 
600
  return p;
 
601
}
 
602
 
 
603
static char *
 
604
get_os_disk (const char *os_dev)
 
605
{
 
606
  char *path, *p;
 
607
  
 
608
#if defined(__linux__)
 
609
  path = xmalloc (PATH_MAX);
 
610
  if (! realpath (os_dev, path))
 
611
    return 0;
 
612
  
 
613
  if (strncmp ("/dev/", path, 5) == 0)
 
614
    {
 
615
      p = path + 5;
 
616
 
 
617
      if (have_devfs ())
 
618
        {
 
619
          /* If this is an IDE disk.  */
 
620
          if (strncmp ("/dev/ide/", p, 9) == 0)
 
621
            {
 
622
              p = strstr (p, "part");
 
623
              if (p)
 
624
                strcpy (p, "disc");
 
625
 
 
626
              return path;
 
627
            }
 
628
 
 
629
          /* If this is a SCSI disk.  */
 
630
          if (strncmp ("/dev/scsi/", p, 10) == 0)
 
631
            {
 
632
              p = strstr (p, "part");
 
633
              if (p)
 
634
                strcpy (p, "disc");
 
635
 
 
636
              return path;
 
637
            }
 
638
        }
 
639
      
 
640
      /* If this is a DAC960 disk.  */
 
641
      if (strncmp ("rd/c", p, 4) == 0)
 
642
        {
 
643
          /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
 
644
          p = strchr (p, 'p');
 
645
          if (p)
 
646
            *p = '\0';
 
647
 
 
648
          return path;
 
649
        }
 
650
      
 
651
      /* If this is an IDE disk or a SCSI disk.  */
 
652
      if ((strncmp ("hd", p, 2) == 0
 
653
           || strncmp ("sd", p, 2) == 0)
 
654
          && p[2] >= 'a' && p[2] <= 'z')
 
655
        {
 
656
          /* /dev/[hs]d[a-z][0-9]* */
 
657
          p[3] = '\0';
 
658
          return path;
 
659
        }
 
660
    }
 
661
 
 
662
  return path;
 
663
  
 
664
#elif defined(__GNU__)
 
665
  path = xstrdup (os_dev);
 
666
  if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
 
667
    {
 
668
      p = strchr (path, 's');
 
669
      if (p)
 
670
        *p = '\0';
 
671
    }
 
672
  return path;
 
673
 
 
674
#else
 
675
# warning "The function `get_os_disk' might not work on your OS correctly."
 
676
  return xstrdup (os_dev);
 
677
#endif
 
678
}
 
679
 
 
680
static int
 
681
find_drive (const char *os_dev)
 
682
{
 
683
  int i;
 
684
  char *os_disk;
 
685
 
 
686
  os_disk = get_os_disk (os_dev);
 
687
  if (! os_disk)
 
688
    return -1;
 
689
  
 
690
  for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
 
691
    if (map[i] && strcmp (map[i], os_disk) == 0)
 
692
      {
 
693
        free (os_disk);
 
694
        return i;
 
695
      }
 
696
 
 
697
  free (os_disk);
 
698
  return -1;
 
699
}
 
700
 
 
701
char *
 
702
grub_util_biosdisk_get_grub_dev (const char *os_dev)
 
703
{
 
704
  struct stat st;
 
705
  int drive;
 
706
 
 
707
  if (stat (os_dev, &st) < 0)
 
708
    {
 
709
      grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
 
710
      return 0;
 
711
    }
 
712
 
 
713
  drive = find_drive (os_dev);
 
714
  if (drive < 0)
 
715
    {
 
716
      grub_error (GRUB_ERR_BAD_DEVICE,
 
717
                  "no mapping exists for `%s'", os_dev);
 
718
      return 0;
 
719
    }
 
720
  
 
721
  if (! S_ISBLK (st.st_mode))
 
722
    return make_device_name (drive, -1, -1);
 
723
  
 
724
#if defined(__linux__)
 
725
  /* Linux counts partitions uniformly, whether a BSD partition or a DOS
 
726
     partition, so mapping them to GRUB devices is not trivial.
 
727
     Here, get the start sector of a partition by HDIO_GETGEO, and
 
728
     compare it with each partition GRUB recognizes.  */
 
729
  {
 
730
    char *name;
 
731
    grub_disk_t disk;
 
732
    int fd;
 
733
    struct hd_geometry hdg;
 
734
    int dos_part = -1;
 
735
    int bsd_part = -1;
 
736
    auto int find_partition (grub_disk_t disk,
 
737
                             const grub_partition_t partition);
 
738
    
 
739
    int find_partition (grub_disk_t disk __attribute__ ((unused)),
 
740
                        const grub_partition_t partition)
 
741
      {
 
742
        struct grub_pc_partition *pcdata = 0;
 
743
        
 
744
        if (strcmp (partition->partmap->name, "pc_partition_map") == 0)
 
745
          pcdata = partition->data;
 
746
          
 
747
        if (pcdata)
 
748
          {
 
749
            if (pcdata->bsd_part < 0)
 
750
              grub_util_info ("DOS partition %d starts from %lu",
 
751
                              pcdata->dos_part, partition->start);
 
752
            else
 
753
              grub_util_info ("BSD partition %d,%c starts from %lu",
 
754
                              pcdata->dos_part, pcdata->bsd_part + 'a',
 
755
                              partition->start);
 
756
          }
 
757
        
 
758
        if (hdg.start == partition->start)
 
759
          {
 
760
            if (pcdata)
 
761
              {
 
762
                dos_part = pcdata->dos_part;
 
763
                bsd_part = pcdata->bsd_part;
 
764
              }
 
765
            else
 
766
              {
 
767
                dos_part = 0;
 
768
                bsd_part = 0;
 
769
              }
 
770
            return 1;
 
771
          }
 
772
        
 
773
        return 0;
 
774
      }
 
775
    
 
776
    name = make_device_name (drive, -1, -1);
 
777
    
 
778
    if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
 
779
      return name;
 
780
    
 
781
    fd = open (os_dev, O_RDONLY);
 
782
    if (! fd)
 
783
      {
 
784
        grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", os_dev);
 
785
        free (name);
 
786
        return 0;
 
787
      }
 
788
    
 
789
    if (ioctl (fd, HDIO_GETGEO, &hdg))
 
790
      {
 
791
        grub_error (GRUB_ERR_BAD_DEVICE,
 
792
                    "cannot get geometry of `%s'", os_dev);
 
793
        close (fd);
 
794
        free (name);
 
795
        return 0;
 
796
      }
 
797
    
 
798
    close (fd);
 
799
 
 
800
    grub_util_info ("%s starts from %lu", os_dev, hdg.start);
 
801
    
 
802
    if (hdg.start == 0)
 
803
      return name;
 
804
 
 
805
    grub_util_info ("opening the device %s", name);
 
806
    disk = grub_disk_open (name);
 
807
    free (name);
 
808
    
 
809
    if (! disk)
 
810
      return 0;
 
811
    
 
812
    if (grub_partition_iterate (disk, find_partition) != GRUB_ERR_NONE)
 
813
      {
 
814
        grub_disk_close (disk);
 
815
        return 0;
 
816
      }
 
817
    
 
818
    if (dos_part < 0)
 
819
      {
 
820
        grub_disk_close (disk);
 
821
        grub_error (GRUB_ERR_BAD_DEVICE,
 
822
                    "cannot find the partition of `%s'", os_dev);
 
823
        return 0;
 
824
      }
 
825
    
 
826
    return make_device_name (drive, dos_part, bsd_part);
 
827
  }
 
828
  
 
829
#elif defined(__GNU__)
 
830
  /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?".  */
 
831
  {
 
832
    char *p;
 
833
    int dos_part = -1;
 
834
    int bsd_part = -1;
 
835
    
 
836
    p = strrchr (os_dev, 's');
 
837
    if (p)
 
838
      {
 
839
        long int n;
 
840
        char *q;
 
841
        
 
842
        p++;
 
843
        n = strtol (p, &q, 10);
 
844
        if (p != q && n != LONG_MIN && n != LONG_MAX)
 
845
          {
 
846
            dos_part = (int) n;
 
847
            
 
848
            if (*q >= 'a' && *q <= 'g')
 
849
              bsd_part = *q - 'a';
 
850
          }
 
851
      }
 
852
    
 
853
    return make_device_name (drive, dos_part, bsd_part);
 
854
  }
 
855
  
 
856
#else
 
857
# warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
 
858
  return make_device_name (drive, -1, -1);
 
859
#endif
 
860
}