~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to grub-core/kern/disk.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  GRUB  --  GRand Unified Bootloader
 
3
 *  Copyright (C) 2002,2003,2004,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
 
4
 *
 
5
 *  GRUB is free software: you can redistribute it and/or modify
 
6
 *  it under the terms of the GNU General Public License as published by
 
7
 *  the Free Software Foundation, either version 3 of the License, or
 
8
 *  (at your option) any later version.
 
9
 *
 
10
 *  GRUB is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#include <grub/disk.h>
 
20
#include <grub/err.h>
 
21
#include <grub/mm.h>
 
22
#include <grub/types.h>
 
23
#include <grub/partition.h>
 
24
#include <grub/misc.h>
 
25
#include <grub/time.h>
 
26
#include <grub/file.h>
 
27
 
 
28
#define GRUB_CACHE_TIMEOUT      2
 
29
 
 
30
/* The last time the disk was used.  */
 
31
static grub_uint64_t grub_last_time = 0;
 
32
 
 
33
 
 
34
/* Disk cache.  */
 
35
struct grub_disk_cache
 
36
{
 
37
  enum grub_disk_dev_id dev_id;
 
38
  unsigned long disk_id;
 
39
  grub_disk_addr_t sector;
 
40
  char *data;
 
41
  int lock;
 
42
};
 
43
 
 
44
static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
 
45
 
 
46
void (*grub_disk_firmware_fini) (void);
 
47
int grub_disk_firmware_is_tainted;
 
48
 
 
49
grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
 
50
            struct grub_disk_ata_pass_through_parms *);
 
51
 
 
52
 
 
53
#if 0
 
54
static unsigned long grub_disk_cache_hits;
 
55
static unsigned long grub_disk_cache_misses;
 
56
 
 
57
void
 
58
grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
 
59
{
 
60
  *hits = grub_disk_cache_hits;
 
61
  *misses = grub_disk_cache_misses;
 
62
}
 
63
#endif
 
64
 
 
65
static unsigned
 
66
grub_disk_cache_get_index (unsigned long dev_id, unsigned long disk_id,
 
67
                           grub_disk_addr_t sector)
 
68
{
 
69
  return ((dev_id * 524287UL + disk_id * 2606459UL
 
70
           + ((unsigned) (sector >> GRUB_DISK_CACHE_BITS)))
 
71
          % GRUB_DISK_CACHE_NUM);
 
72
}
 
73
 
 
74
static void
 
75
grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id,
 
76
                            grub_disk_addr_t sector)
 
77
{
 
78
  unsigned index;
 
79
  struct grub_disk_cache *cache;
 
80
 
 
81
  sector &= ~(GRUB_DISK_CACHE_SIZE - 1);
 
82
  index = grub_disk_cache_get_index (dev_id, disk_id, sector);
 
83
  cache = grub_disk_cache_table + index;
 
84
 
 
85
  if (cache->dev_id == dev_id && cache->disk_id == disk_id
 
86
      && cache->sector == sector && cache->data)
 
87
    {
 
88
      cache->lock = 1;
 
89
      grub_free (cache->data);
 
90
      cache->data = 0;
 
91
      cache->lock = 0;
 
92
    }
 
93
}
 
94
 
 
95
void
 
96
grub_disk_cache_invalidate_all (void)
 
97
{
 
98
  unsigned i;
 
99
 
 
100
  for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
 
101
    {
 
102
      struct grub_disk_cache *cache = grub_disk_cache_table + i;
 
103
 
 
104
      if (cache->data && ! cache->lock)
 
105
        {
 
106
          grub_free (cache->data);
 
107
          cache->data = 0;
 
108
        }
 
109
    }
 
110
}
 
111
 
 
112
static char *
 
113
grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
 
114
                       grub_disk_addr_t sector)
 
115
{
 
116
  struct grub_disk_cache *cache;
 
117
  unsigned index;
 
118
 
 
119
  index = grub_disk_cache_get_index (dev_id, disk_id, sector);
 
120
  cache = grub_disk_cache_table + index;
 
121
 
 
122
  if (cache->dev_id == dev_id && cache->disk_id == disk_id
 
123
      && cache->sector == sector)
 
124
    {
 
125
      cache->lock = 1;
 
126
#if 0
 
127
      grub_disk_cache_hits++;
 
128
#endif
 
129
      return cache->data;
 
130
    }
 
131
 
 
132
#if 0
 
133
  grub_disk_cache_misses++;
 
134
#endif
 
135
 
 
136
  return 0;
 
137
}
 
138
 
 
139
static void
 
140
grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
 
141
                        grub_disk_addr_t sector)
 
142
{
 
143
  struct grub_disk_cache *cache;
 
144
  unsigned index;
 
145
 
 
146
  index = grub_disk_cache_get_index (dev_id, disk_id, sector);
 
147
  cache = grub_disk_cache_table + index;
 
148
 
 
149
  if (cache->dev_id == dev_id && cache->disk_id == disk_id
 
150
      && cache->sector == sector)
 
151
    cache->lock = 0;
 
152
}
 
153
 
 
154
static grub_err_t
 
155
grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
 
156
                       grub_disk_addr_t sector, const char *data)
 
157
{
 
158
  unsigned index;
 
159
  struct grub_disk_cache *cache;
 
160
 
 
161
  index = grub_disk_cache_get_index (dev_id, disk_id, sector);
 
162
  cache = grub_disk_cache_table + index;
 
163
 
 
164
  cache->lock = 1;
 
165
  grub_free (cache->data);
 
166
  cache->data = 0;
 
167
  cache->lock = 0;
 
168
 
 
169
  cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
 
170
  if (! cache->data)
 
171
    return grub_errno;
 
172
 
 
173
  grub_memcpy (cache->data, data,
 
174
               GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
 
175
  cache->dev_id = dev_id;
 
176
  cache->disk_id = disk_id;
 
177
  cache->sector = sector;
 
178
 
 
179
  return GRUB_ERR_NONE;
 
180
}
 
181
 
 
182
 
 
183
 
 
184
static grub_disk_dev_t grub_disk_dev_list;
 
185
 
 
186
void
 
187
grub_disk_dev_register (grub_disk_dev_t dev)
 
188
{
 
189
  dev->next = grub_disk_dev_list;
 
190
  grub_disk_dev_list = dev;
 
191
}
 
192
 
 
193
void
 
194
grub_disk_dev_unregister (grub_disk_dev_t dev)
 
195
{
 
196
  grub_disk_dev_t *p, q;
 
197
 
 
198
  for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
 
199
    if (q == dev)
 
200
      {
 
201
        *p = q->next;
 
202
        break;
 
203
      }
 
204
}
 
205
 
 
206
int
 
207
grub_disk_dev_iterate (int (*hook) (const char *name))
 
208
{
 
209
  grub_disk_dev_t p;
 
210
 
 
211
  for (p = grub_disk_dev_list; p; p = p->next)
 
212
    if (p->iterate && (p->iterate) (hook))
 
213
      return 1;
 
214
 
 
215
  return 0;
 
216
}
 
217
 
 
218
/* Return the location of the first ',', if any, which is not
 
219
   escaped by a '\'.  */
 
220
static const char *
 
221
find_part_sep (const char *name)
 
222
{
 
223
  const char *p = name;
 
224
  char c;
 
225
 
 
226
  while ((c = *p++) != '\0')
 
227
    {
 
228
      if (c == '\\' && *p == ',')
 
229
        p++;
 
230
      else if (c == ',')
 
231
        return p - 1;
 
232
    }
 
233
  return NULL;
 
234
}
 
235
 
 
236
grub_disk_t
 
237
grub_disk_open (const char *name)
 
238
{
 
239
  const char *p;
 
240
  grub_disk_t disk;
 
241
  grub_disk_dev_t dev;
 
242
  char *raw = (char *) name;
 
243
  grub_uint64_t current_time;
 
244
 
 
245
  grub_dprintf ("disk", "Opening `%s'...\n", name);
 
246
 
 
247
  disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
 
248
  if (! disk)
 
249
    return 0;
 
250
 
 
251
  p = find_part_sep (name);
 
252
  if (p)
 
253
    {
 
254
      grub_size_t len = p - name;
 
255
 
 
256
      raw = grub_malloc (len + 1);
 
257
      if (! raw)
 
258
        goto fail;
 
259
 
 
260
      grub_memcpy (raw, name, len);
 
261
      raw[len] = '\0';
 
262
      disk->name = grub_strdup (raw);
 
263
    }
 
264
  else
 
265
    disk->name = grub_strdup (name);
 
266
  if (! disk->name)
 
267
    goto fail;
 
268
 
 
269
 
 
270
  for (dev = grub_disk_dev_list; dev; dev = dev->next)
 
271
    {
 
272
      if ((dev->open) (raw, disk) == GRUB_ERR_NONE)
 
273
        break;
 
274
      else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
 
275
        grub_errno = GRUB_ERR_NONE;
 
276
      else
 
277
        goto fail;
 
278
    }
 
279
 
 
280
  if (! dev)
 
281
    {
 
282
      grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such disk");
 
283
      goto fail;
 
284
    }
 
285
 
 
286
  disk->dev = dev;
 
287
 
 
288
  if (p)
 
289
    {
 
290
      disk->partition = grub_partition_probe (disk, p + 1);
 
291
      if (! disk->partition)
 
292
        {
 
293
          grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition");
 
294
          goto fail;
 
295
        }
 
296
    }
 
297
 
 
298
  /* The cache will be invalidated about 2 seconds after a device was
 
299
     closed.  */
 
300
  current_time = grub_get_time_ms ();
 
301
 
 
302
  if (current_time > (grub_last_time
 
303
                      + GRUB_CACHE_TIMEOUT * 1000))
 
304
    grub_disk_cache_invalidate_all ();
 
305
 
 
306
  grub_last_time = current_time;
 
307
 
 
308
 fail:
 
309
 
 
310
  if (raw && raw != name)
 
311
    grub_free (raw);
 
312
 
 
313
  if (grub_errno != GRUB_ERR_NONE)
 
314
    {
 
315
      grub_error_push ();
 
316
      grub_dprintf ("disk", "Opening `%s' failed.\n", name);
 
317
      grub_error_pop ();
 
318
 
 
319
      grub_disk_close (disk);
 
320
      return 0;
 
321
    }
 
322
 
 
323
  return disk;
 
324
}
 
325
 
 
326
void
 
327
grub_disk_close (grub_disk_t disk)
 
328
{
 
329
  grub_partition_t part;
 
330
  grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
 
331
 
 
332
  if (disk->dev && disk->dev->close)
 
333
    (disk->dev->close) (disk);
 
334
 
 
335
  /* Reset the timer.  */
 
336
  grub_last_time = grub_get_time_ms ();
 
337
 
 
338
  while (disk->partition)
 
339
    {
 
340
      part = disk->partition->parent;
 
341
      grub_free (disk->partition);
 
342
      disk->partition = part;
 
343
    }
 
344
  grub_free ((void *) disk->name);
 
345
  grub_free (disk);
 
346
}
 
347
 
 
348
/* This function performs three tasks:
 
349
   - Make sectors disk relative from partition relative.
 
350
   - Normalize offset to be less than the sector size.
 
351
   - Verify that the range is inside the partition.  */
 
352
static grub_err_t
 
353
grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector,
 
354
                        grub_off_t *offset, grub_size_t size)
 
355
{
 
356
  grub_partition_t part;
 
357
  *sector += *offset >> GRUB_DISK_SECTOR_BITS;
 
358
  *offset &= GRUB_DISK_SECTOR_SIZE - 1;
 
359
 
 
360
  for (part = disk->partition; part; part = part->parent)
 
361
    {
 
362
      grub_disk_addr_t start;
 
363
      grub_uint64_t len;
 
364
 
 
365
      start = part->start;
 
366
      len = part->len;
 
367
 
 
368
      if (*sector >= len
 
369
          || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
 
370
                              >> GRUB_DISK_SECTOR_BITS))
 
371
        return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of partition");
 
372
 
 
373
      *sector += start;
 
374
    }
 
375
 
 
376
  if (disk->total_sectors <= *sector
 
377
      || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
 
378
          >> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector)
 
379
    return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk");
 
380
 
 
381
  return GRUB_ERR_NONE;
 
382
}
 
383
 
 
384
/* Read data from the disk.  */
 
385
grub_err_t
 
386
grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
 
387
                grub_off_t offset, grub_size_t size, void *buf)
 
388
{
 
389
  char *tmp_buf;
 
390
  unsigned real_offset;
 
391
 
 
392
  /* First of all, check if the region is within the disk.  */
 
393
  if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
 
394
    {
 
395
      grub_error_push ();
 
396
      grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
 
397
                    (unsigned long long) sector, grub_errmsg);
 
398
      grub_error_pop ();
 
399
      return grub_errno;
 
400
    }
 
401
 
 
402
  real_offset = offset;
 
403
 
 
404
  /* Allocate a temporary buffer.  */
 
405
  tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
 
406
  if (! tmp_buf)
 
407
    return grub_errno;
 
408
 
 
409
  /* Until SIZE is zero...  */
 
410
  while (size)
 
411
    {
 
412
      char *data;
 
413
      grub_disk_addr_t start_sector;
 
414
      grub_size_t len;
 
415
      grub_size_t pos;
 
416
 
 
417
      /* For reading bulk data.  */
 
418
      start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
 
419
      pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
 
420
      len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
 
421
             - pos - real_offset);
 
422
      if (len > size)
 
423
        len = size;
 
424
 
 
425
      /* Fetch the cache.  */
 
426
      data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector);
 
427
      if (data)
 
428
        {
 
429
          /* Just copy it!  */
 
430
          grub_memcpy (buf, data + pos + real_offset, len);
 
431
          grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector);
 
432
        }
 
433
      else
 
434
        {
 
435
          /* Otherwise read data from the disk actually.  */
 
436
          if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors
 
437
              || (disk->dev->read) (disk, start_sector,
 
438
                                    GRUB_DISK_CACHE_SIZE, tmp_buf)
 
439
              != GRUB_ERR_NONE)
 
440
            {
 
441
              /* Uggh... Failed. Instead, just read necessary data.  */
 
442
              unsigned num;
 
443
              char *p;
 
444
 
 
445
              grub_errno = GRUB_ERR_NONE;
 
446
 
 
447
              num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1)
 
448
                     >> GRUB_DISK_SECTOR_BITS);
 
449
 
 
450
              p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS);
 
451
              if (!p)
 
452
                goto finish;
 
453
 
 
454
              tmp_buf = p;
 
455
 
 
456
              if ((disk->dev->read) (disk, sector, num, tmp_buf))
 
457
                {
 
458
                  grub_error_push ();
 
459
                  grub_dprintf ("disk", "%s read failed\n", disk->name);
 
460
                  grub_error_pop ();
 
461
                  goto finish;
 
462
                }
 
463
 
 
464
              grub_memcpy (buf, tmp_buf + real_offset, size);
 
465
 
 
466
              /* Call the read hook, if any.  */
 
467
              if (disk->read_hook)
 
468
                while (size)
 
469
                  {
 
470
                    grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size;
 
471
                    (disk->read_hook) (sector, real_offset,
 
472
                                       to_read);
 
473
                    if (grub_errno != GRUB_ERR_NONE)
 
474
                      goto finish;
 
475
 
 
476
                    sector++;
 
477
                    size -= to_read - real_offset;
 
478
                    real_offset = 0;
 
479
                  }
 
480
 
 
481
              /* This must be the end.  */
 
482
              goto finish;
 
483
            }
 
484
 
 
485
          /* Copy it and store it in the disk cache.  */
 
486
          grub_memcpy (buf, tmp_buf + pos + real_offset, len);
 
487
          grub_disk_cache_store (disk->dev->id, disk->id,
 
488
                                 start_sector, tmp_buf);
 
489
        }
 
490
 
 
491
      /* Call the read hook, if any.  */
 
492
      if (disk->read_hook)
 
493
        {
 
494
          grub_disk_addr_t s = sector;
 
495
          grub_size_t l = len;
 
496
 
 
497
          while (l)
 
498
            {
 
499
              (disk->read_hook) (s, real_offset,
 
500
                                 ((l > GRUB_DISK_SECTOR_SIZE)
 
501
                                  ? GRUB_DISK_SECTOR_SIZE
 
502
                                  : l));
 
503
 
 
504
              if (l < GRUB_DISK_SECTOR_SIZE - real_offset)
 
505
                break;
 
506
 
 
507
              s++;
 
508
              l -= GRUB_DISK_SECTOR_SIZE - real_offset;
 
509
              real_offset = 0;
 
510
            }
 
511
        }
 
512
 
 
513
      sector = start_sector + GRUB_DISK_CACHE_SIZE;
 
514
      buf = (char *) buf + len;
 
515
      size -= len;
 
516
      real_offset = 0;
 
517
    }
 
518
 
 
519
 finish:
 
520
 
 
521
  grub_free (tmp_buf);
 
522
 
 
523
  return grub_errno;
 
524
}
 
525
 
 
526
grub_err_t
 
527
grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
 
528
                 grub_off_t offset, grub_size_t size, const void *buf)
 
529
{
 
530
  unsigned real_offset;
 
531
 
 
532
  grub_dprintf ("disk", "Writing `%s'...\n", disk->name);
 
533
 
 
534
  if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
 
535
    return -1;
 
536
 
 
537
  real_offset = offset;
 
538
 
 
539
  while (size)
 
540
    {
 
541
      if (real_offset != 0 || (size < GRUB_DISK_SECTOR_SIZE && size != 0))
 
542
        {
 
543
          char tmp_buf[GRUB_DISK_SECTOR_SIZE];
 
544
          grub_size_t len;
 
545
          grub_partition_t part;
 
546
 
 
547
          part = disk->partition;
 
548
          disk->partition = 0;
 
549
          if (grub_disk_read (disk, sector, 0, GRUB_DISK_SECTOR_SIZE, tmp_buf)
 
550
              != GRUB_ERR_NONE)
 
551
            {
 
552
              disk->partition = part;
 
553
              goto finish;
 
554
            }
 
555
          disk->partition = part;
 
556
 
 
557
          len = GRUB_DISK_SECTOR_SIZE - real_offset;
 
558
          if (len > size)
 
559
            len = size;
 
560
 
 
561
          grub_memcpy (tmp_buf + real_offset, buf, len);
 
562
 
 
563
          grub_disk_cache_invalidate (disk->dev->id, disk->id, sector);
 
564
 
 
565
          if ((disk->dev->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE)
 
566
            goto finish;
 
567
 
 
568
          sector++;
 
569
          buf = (char *) buf + len;
 
570
          size -= len;
 
571
          real_offset = 0;
 
572
        }
 
573
      else
 
574
        {
 
575
          grub_size_t len;
 
576
          grub_size_t n;
 
577
 
 
578
          len = size & ~(GRUB_DISK_SECTOR_SIZE - 1);
 
579
          n = size >> GRUB_DISK_SECTOR_BITS;
 
580
 
 
581
          if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE)
 
582
            goto finish;
 
583
 
 
584
          while (n--)
 
585
            grub_disk_cache_invalidate (disk->dev->id, disk->id, sector++);
 
586
 
 
587
          buf = (char *) buf + len;
 
588
          size -= len;
 
589
        }
 
590
    }
 
591
 
 
592
 finish:
 
593
 
 
594
  return grub_errno;
 
595
}
 
596
 
 
597
grub_uint64_t
 
598
grub_disk_get_size (grub_disk_t disk)
 
599
{
 
600
  if (disk->partition)
 
601
    return grub_partition_get_len (disk->partition);
 
602
  else
 
603
    return disk->total_sectors;
 
604
}