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

« back to all changes in this revision

Viewing changes to grub-core/fs/ntfs.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
/* ntfs.c - NTFS filesystem */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
 
5
 *
 
6
 *  This program 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
 *  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 this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include <grub/file.h>
 
21
#include <grub/mm.h>
 
22
#include <grub/misc.h>
 
23
#include <grub/disk.h>
 
24
#include <grub/dl.h>
 
25
#include <grub/fshelp.h>
 
26
#include <grub/ntfs.h>
 
27
#include <grub/charset.h>
 
28
 
 
29
static grub_dl_t my_mod;
 
30
 
 
31
ntfscomp_func_t grub_ntfscomp_func;
 
32
 
 
33
static grub_err_t
 
34
fixup (struct grub_ntfs_data *data, char *buf, int len, char *magic)
 
35
{
 
36
  int ss;
 
37
  char *pu;
 
38
  grub_uint16_t us;
 
39
 
 
40
  if (grub_memcmp (buf, magic, 4))
 
41
    return grub_error (GRUB_ERR_BAD_FS, "%s label not found", magic);
 
42
 
 
43
  ss = u16at (buf, 6) - 1;
 
44
  if (ss * (int) data->blocksize != len * GRUB_DISK_SECTOR_SIZE)
 
45
    return grub_error (GRUB_ERR_BAD_FS, "size not match",
 
46
                       ss * (int) data->blocksize,
 
47
                       len * GRUB_DISK_SECTOR_SIZE);
 
48
  pu = buf + u16at (buf, 4);
 
49
  us = u16at (pu, 0);
 
50
  buf -= 2;
 
51
  while (ss > 0)
 
52
    {
 
53
      buf += data->blocksize;
 
54
      pu += 2;
 
55
      if (u16at (buf, 0) != us)
 
56
        return grub_error (GRUB_ERR_BAD_FS, "fixup signature not match");
 
57
      v16at (buf, 0) = v16at (pu, 0);
 
58
      ss--;
 
59
    }
 
60
 
 
61
  return 0;
 
62
}
 
63
 
 
64
static grub_err_t read_mft (struct grub_ntfs_data *data, char *buf,
 
65
                            grub_uint32_t mftno);
 
66
static grub_err_t read_attr (struct grub_ntfs_attr *at, char *dest,
 
67
                             grub_disk_addr_t ofs, grub_size_t len,
 
68
                             int cached,
 
69
                             void
 
70
                             NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t
 
71
                                                            sector,
 
72
                                                            unsigned offset,
 
73
                                                            unsigned length));
 
74
 
 
75
static grub_err_t read_data (struct grub_ntfs_attr *at, char *pa, char *dest,
 
76
                             grub_disk_addr_t ofs, grub_size_t len,
 
77
                             int cached,
 
78
                             void
 
79
                             NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t
 
80
                                                            sector,
 
81
                                                            unsigned offset,
 
82
                                                            unsigned length));
 
83
 
 
84
static void
 
85
init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft)
 
86
{
 
87
  at->mft = mft;
 
88
  at->flags = (mft == &mft->data->mmft) ? AF_MMFT : 0;
 
89
  at->attr_nxt = mft->buf + u16at (mft->buf, 0x14);
 
90
  at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL;
 
91
}
 
92
 
 
93
static void
 
94
free_attr (struct grub_ntfs_attr *at)
 
95
{
 
96
  grub_free (at->emft_buf);
 
97
  grub_free (at->edat_buf);
 
98
  grub_free (at->sbuf);
 
99
}
 
100
 
 
101
static char *
 
102
find_attr (struct grub_ntfs_attr *at, unsigned char attr)
 
103
{
 
104
  if (at->flags & AF_ALST)
 
105
    {
 
106
    retry:
 
107
      while (at->attr_nxt < at->attr_end)
 
108
        {
 
109
          at->attr_cur = at->attr_nxt;
 
110
          at->attr_nxt += u16at (at->attr_cur, 4);
 
111
          if (((unsigned char) *at->attr_cur == attr) || (attr == 0))
 
112
            {
 
113
              char *new_pos;
 
114
 
 
115
              if (at->flags & AF_MMFT)
 
116
                {
 
117
                  if ((grub_disk_read
 
118
                       (at->mft->data->disk, v32at (at->attr_cur, 0x10), 0,
 
119
                        512, at->emft_buf))
 
120
                      ||
 
121
                      (grub_disk_read
 
122
                       (at->mft->data->disk, v32at (at->attr_cur, 0x14), 0,
 
123
                        512, at->emft_buf + 512)))
 
124
                    return NULL;
 
125
 
 
126
                  if (fixup
 
127
                      (at->mft->data, at->emft_buf, at->mft->data->mft_size,
 
128
                       "FILE"))
 
129
                    return NULL;
 
130
                }
 
131
              else
 
132
                {
 
133
                  if (read_mft (at->mft->data, at->emft_buf,
 
134
                                u32at (at->attr_cur, 0x10)))
 
135
                    return NULL;
 
136
                }
 
137
 
 
138
              new_pos = &at->emft_buf[u16at (at->emft_buf, 0x14)];
 
139
              while ((unsigned char) *new_pos != 0xFF)
 
140
                {
 
141
                  if (((unsigned char) *new_pos ==
 
142
                       (unsigned char) *at->attr_cur)
 
143
                      && (u16at (new_pos, 0xE) == u16at (at->attr_cur, 0x18)))
 
144
                    {
 
145
                      return new_pos;
 
146
                    }
 
147
                  new_pos += u16at (new_pos, 4);
 
148
                }
 
149
              grub_error (GRUB_ERR_BAD_FS,
 
150
                          "can\'t find 0x%X in attribute list",
 
151
                          (unsigned char) *at->attr_cur);
 
152
              return NULL;
 
153
            }
 
154
        }
 
155
      return NULL;
 
156
    }
 
157
  at->attr_cur = at->attr_nxt;
 
158
  while ((unsigned char) *at->attr_cur != 0xFF)
 
159
    {
 
160
      at->attr_nxt += u16at (at->attr_cur, 4);
 
161
      if ((unsigned char) *at->attr_cur == AT_ATTRIBUTE_LIST)
 
162
        at->attr_end = at->attr_cur;
 
163
      if (((unsigned char) *at->attr_cur == attr) || (attr == 0))
 
164
        return at->attr_cur;
 
165
      at->attr_cur = at->attr_nxt;
 
166
    }
 
167
  if (at->attr_end)
 
168
    {
 
169
      char *pa;
 
170
 
 
171
      at->emft_buf = grub_malloc (at->mft->data->mft_size << BLK_SHR);
 
172
      if (at->emft_buf == NULL)
 
173
        return NULL;
 
174
 
 
175
      pa = at->attr_end;
 
176
      if (pa[8])
 
177
        {
 
178
          int n;
 
179
 
 
180
          n = ((u32at (pa, 0x30) + GRUB_DISK_SECTOR_SIZE - 1)
 
181
               & (~(GRUB_DISK_SECTOR_SIZE - 1)));
 
182
          at->attr_cur = at->attr_end;
 
183
          at->edat_buf = grub_malloc (n);
 
184
          if (!at->edat_buf)
 
185
            return NULL;
 
186
          if (read_data (at, pa, at->edat_buf, 0, n, 0, 0))
 
187
            {
 
188
              grub_error (GRUB_ERR_BAD_FS,
 
189
                          "fail to read non-resident attribute list");
 
190
              return NULL;
 
191
            }
 
192
          at->attr_nxt = at->edat_buf;
 
193
          at->attr_end = at->edat_buf + u32at (pa, 0x30);
 
194
        }
 
195
      else
 
196
        {
 
197
          at->attr_nxt = at->attr_end + u16at (pa, 0x14);
 
198
          at->attr_end = at->attr_end + u32at (pa, 4);
 
199
        }
 
200
      at->flags |= AF_ALST;
 
201
      while (at->attr_nxt < at->attr_end)
 
202
        {
 
203
          if (((unsigned char) *at->attr_nxt == attr) || (attr == 0))
 
204
            break;
 
205
          at->attr_nxt += u16at (at->attr_nxt, 4);
 
206
        }
 
207
      if (at->attr_nxt >= at->attr_end)
 
208
        return NULL;
 
209
 
 
210
      if ((at->flags & AF_MMFT) && (attr == AT_DATA))
 
211
        {
 
212
          at->flags |= AF_GPOS;
 
213
          at->attr_cur = at->attr_nxt;
 
214
          pa = at->attr_cur;
 
215
          v32at (pa, 0x10) = at->mft->data->mft_start;
 
216
          v32at (pa, 0x14) = at->mft->data->mft_start + 1;
 
217
          pa = at->attr_nxt + u16at (pa, 4);
 
218
          while (pa < at->attr_end)
 
219
            {
 
220
              if ((unsigned char) *pa != attr)
 
221
                break;
 
222
              if (read_attr
 
223
                  (at, pa + 0x10,
 
224
                   u32at (pa, 0x10) * (at->mft->data->mft_size << BLK_SHR),
 
225
                   at->mft->data->mft_size << BLK_SHR, 0, 0))
 
226
                return NULL;
 
227
              pa += u16at (pa, 4);
 
228
            }
 
229
          at->attr_nxt = at->attr_cur;
 
230
          at->flags &= ~AF_GPOS;
 
231
        }
 
232
      goto retry;
 
233
    }
 
234
  return NULL;
 
235
}
 
236
 
 
237
static char *
 
238
locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft,
 
239
             unsigned char attr)
 
240
{
 
241
  char *pa;
 
242
 
 
243
  init_attr (at, mft);
 
244
  if ((pa = find_attr (at, attr)) == NULL)
 
245
    return NULL;
 
246
  if ((at->flags & AF_ALST) == 0)
 
247
    {
 
248
      while (1)
 
249
        {
 
250
          if ((pa = find_attr (at, attr)) == NULL)
 
251
            break;
 
252
          if (at->flags & AF_ALST)
 
253
            return pa;
 
254
        }
 
255
      grub_errno = GRUB_ERR_NONE;
 
256
      free_attr (at);
 
257
      init_attr (at, mft);
 
258
      pa = find_attr (at, attr);
 
259
    }
 
260
  return pa;
 
261
}
 
262
 
 
263
static char *
 
264
read_run_data (char *run, int nn, grub_disk_addr_t * val, int sig)
 
265
{
 
266
  grub_disk_addr_t r, v;
 
267
 
 
268
  r = 0;
 
269
  v = 1;
 
270
 
 
271
  while (nn--)
 
272
    {
 
273
      r += v * (*(unsigned char *) (run++));
 
274
      v <<= 8;
 
275
    }
 
276
 
 
277
  if ((sig) && (r & (v >> 1)))
 
278
    r -= v;
 
279
 
 
280
  *val = r;
 
281
  return run;
 
282
}
 
283
 
 
284
grub_err_t
 
285
grub_ntfs_read_run_list (struct grub_ntfs_rlst * ctx)
 
286
{
 
287
  int c1, c2;
 
288
  grub_disk_addr_t val;
 
289
  char *run;
 
290
 
 
291
  run = ctx->cur_run;
 
292
retry:
 
293
  c1 = ((unsigned char) (*run) & 0xF);
 
294
  c2 = ((unsigned char) (*run) >> 4);
 
295
  if (!c1)
 
296
    {
 
297
      if ((ctx->attr) && (ctx->attr->flags & AF_ALST))
 
298
        {
 
299
          void NESTED_FUNC_ATTR (*save_hook) (grub_disk_addr_t sector,
 
300
                                              unsigned offset,
 
301
                                              unsigned length);
 
302
 
 
303
          save_hook = ctx->comp.disk->read_hook;
 
304
          ctx->comp.disk->read_hook = 0;
 
305
          run = find_attr (ctx->attr, (unsigned char) *ctx->attr->attr_cur);
 
306
          ctx->comp.disk->read_hook = save_hook;
 
307
          if (run)
 
308
            {
 
309
              if (run[8] == 0)
 
310
                return grub_error (GRUB_ERR_BAD_FS,
 
311
                                   "$DATA should be non-resident");
 
312
 
 
313
              run += u16at (run, 0x20);
 
314
              ctx->curr_lcn = 0;
 
315
              goto retry;
 
316
            }
 
317
        }
 
318
      return grub_error (GRUB_ERR_BAD_FS, "run list overflown");
 
319
    }
 
320
  run = read_run_data (run + 1, c1, &val, 0);   /* length of current VCN */
 
321
  ctx->curr_vcn = ctx->next_vcn;
 
322
  ctx->next_vcn += val;
 
323
  run = read_run_data (run, c2, &val, 1);       /* offset to previous LCN */
 
324
  ctx->curr_lcn += val;
 
325
  if (val == 0)
 
326
    ctx->flags |= RF_BLNK;
 
327
  else
 
328
    ctx->flags &= ~RF_BLNK;
 
329
  ctx->cur_run = run;
 
330
  return 0;
 
331
}
 
332
 
 
333
static grub_disk_addr_t
 
334
grub_ntfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t block)
 
335
{
 
336
  struct grub_ntfs_rlst *ctx;
 
337
 
 
338
  ctx = (struct grub_ntfs_rlst *) node;
 
339
  if (block >= ctx->next_vcn)
 
340
    {
 
341
      if (grub_ntfs_read_run_list (ctx))
 
342
        return -1;
 
343
      return ctx->curr_lcn;
 
344
    }
 
345
  else
 
346
    return (ctx->flags & RF_BLNK) ? 0 : (block -
 
347
                                         ctx->curr_vcn + ctx->curr_lcn);
 
348
}
 
349
 
 
350
static grub_err_t
 
351
read_data (struct grub_ntfs_attr *at, char *pa, char *dest,
 
352
           grub_disk_addr_t ofs, grub_size_t len, int cached,
 
353
           void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
 
354
                                               unsigned offset,
 
355
                                               unsigned length))
 
356
{
 
357
  grub_disk_addr_t vcn;
 
358
  struct grub_ntfs_rlst cc, *ctx;
 
359
 
 
360
  if (len == 0)
 
361
    return 0;
 
362
 
 
363
  grub_memset (&cc, 0, sizeof (cc));
 
364
  ctx = &cc;
 
365
  ctx->attr = at;
 
366
  ctx->comp.spc = at->mft->data->spc;
 
367
  ctx->comp.disk = at->mft->data->disk;
 
368
 
 
369
  if (pa[8] == 0)
 
370
    {
 
371
      if (ofs + len > u32at (pa, 0x10))
 
372
        return grub_error (GRUB_ERR_BAD_FS, "read out of range");
 
373
      grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len);
 
374
      return 0;
 
375
    }
 
376
 
 
377
  if (u16at (pa, 0xC) & FLAG_COMPRESSED)
 
378
    ctx->flags |= RF_COMP;
 
379
  else
 
380
    ctx->flags &= ~RF_COMP;
 
381
  ctx->cur_run = pa + u16at (pa, 0x20);
 
382
 
 
383
  if (ctx->flags & RF_COMP)
 
384
    {
 
385
      if (!cached)
 
386
        return grub_error (GRUB_ERR_BAD_FS, "attribute can\'t be compressed");
 
387
 
 
388
      if (at->sbuf)
 
389
        {
 
390
          if ((ofs & (~(COM_LEN - 1))) == at->save_pos)
 
391
            {
 
392
              grub_disk_addr_t n;
 
393
 
 
394
              n = COM_LEN - (ofs - at->save_pos);
 
395
              if (n > len)
 
396
                n = len;
 
397
 
 
398
              grub_memcpy (dest, at->sbuf + ofs - at->save_pos, n);
 
399
              if (n == len)
 
400
                return 0;
 
401
 
 
402
              dest += n;
 
403
              len -= n;
 
404
              ofs += n;
 
405
            }
 
406
        }
 
407
      else
 
408
        {
 
409
          at->sbuf = grub_malloc (COM_LEN);
 
410
          if (at->sbuf == NULL)
 
411
            return grub_errno;
 
412
          at->save_pos = 1;
 
413
        }
 
414
 
 
415
      vcn = ctx->target_vcn = (ofs >> COM_LOG_LEN) * (COM_SEC / ctx->comp.spc);
 
416
      ctx->target_vcn &= ~0xF;
 
417
    }
 
418
  else
 
419
    vcn = ctx->target_vcn = grub_divmod64 (ofs >> BLK_SHR, ctx->comp.spc, 0);
 
420
 
 
421
  ctx->next_vcn = u32at (pa, 0x10);
 
422
  ctx->curr_lcn = 0;
 
423
  while (ctx->next_vcn <= ctx->target_vcn)
 
424
    {
 
425
      if (grub_ntfs_read_run_list (ctx))
 
426
        return grub_errno;
 
427
    }
 
428
 
 
429
  if (at->flags & AF_GPOS)
 
430
    {
 
431
      grub_disk_addr_t st0, st1;
 
432
      grub_uint32_t m;
 
433
 
 
434
      grub_divmod64 (ofs >> BLK_SHR, ctx->comp.spc, &m);
 
435
 
 
436
      st0 =
 
437
        (ctx->target_vcn - ctx->curr_vcn + ctx->curr_lcn) * ctx->comp.spc + m;
 
438
      st1 = st0 + 1;
 
439
      if (st1 ==
 
440
          (ctx->next_vcn - ctx->curr_vcn + ctx->curr_lcn) * ctx->comp.spc)
 
441
        {
 
442
          if (grub_ntfs_read_run_list (ctx))
 
443
            return grub_errno;
 
444
          st1 = ctx->curr_lcn * ctx->comp.spc;
 
445
        }
 
446
      v32at (dest, 0) = st0;
 
447
      v32at (dest, 4) = st1;
 
448
      return 0;
 
449
    }
 
450
 
 
451
  if (!(ctx->flags & RF_COMP))
 
452
    {
 
453
      unsigned int pow;
 
454
 
 
455
      if (!grub_fshelp_log2blksize (ctx->comp.spc, &pow))
 
456
        grub_fshelp_read_file (ctx->comp.disk, (grub_fshelp_node_t) ctx,
 
457
                               read_hook, ofs, len, dest,
 
458
                               grub_ntfs_read_block, ofs + len, pow);
 
459
      return grub_errno;
 
460
    }
 
461
 
 
462
  return (grub_ntfscomp_func) ? grub_ntfscomp_func (at, dest, ofs, len, ctx,
 
463
                                                    vcn) :
 
464
    grub_error (GRUB_ERR_BAD_FS, "ntfscomp module not loaded");
 
465
}
 
466
 
 
467
static grub_err_t
 
468
read_attr (struct grub_ntfs_attr *at, char *dest, grub_disk_addr_t ofs,
 
469
           grub_size_t len, int cached,
 
470
           void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
 
471
                                               unsigned offset,
 
472
                                               unsigned length))
 
473
{
 
474
  char *save_cur;
 
475
  unsigned char attr;
 
476
  char *pp;
 
477
  grub_err_t ret;
 
478
 
 
479
  save_cur = at->attr_cur;
 
480
  at->attr_nxt = at->attr_cur;
 
481
  attr = (unsigned char) *at->attr_nxt;
 
482
  if (at->flags & AF_ALST)
 
483
    {
 
484
      char *pa;
 
485
      grub_disk_addr_t vcn;
 
486
 
 
487
      vcn = grub_divmod64 (ofs, at->mft->data->spc << BLK_SHR, 0);
 
488
      pa = at->attr_nxt + u16at (at->attr_nxt, 4);
 
489
      while (pa < at->attr_end)
 
490
        {
 
491
          if ((unsigned char) *pa != attr)
 
492
            break;
 
493
          if (u32at (pa, 8) > vcn)
 
494
            break;
 
495
          at->attr_nxt = pa;
 
496
          pa += u16at (pa, 4);
 
497
        }
 
498
    }
 
499
  pp = find_attr (at, attr);
 
500
  if (pp)
 
501
    ret = read_data (at, pp, dest, ofs, len, cached, read_hook);
 
502
  else
 
503
    ret =
 
504
      (grub_errno) ? grub_errno : grub_error (GRUB_ERR_BAD_FS,
 
505
                                              "attribute not found");
 
506
  at->attr_cur = save_cur;
 
507
  return ret;
 
508
}
 
509
 
 
510
static grub_err_t
 
511
read_mft (struct grub_ntfs_data *data, char *buf, grub_uint32_t mftno)
 
512
{
 
513
  if (read_attr
 
514
      (&data->mmft.attr, buf, mftno * ((grub_disk_addr_t) data->mft_size << BLK_SHR),
 
515
       data->mft_size << BLK_SHR, 0, 0))
 
516
    return grub_error (GRUB_ERR_BAD_FS, "read MFT 0x%X fails", mftno);
 
517
  return fixup (data, buf, data->mft_size, "FILE");
 
518
}
 
519
 
 
520
static grub_err_t
 
521
init_file (struct grub_ntfs_file *mft, grub_uint32_t mftno)
 
522
{
 
523
  unsigned short flag;
 
524
 
 
525
  mft->inode_read = 1;
 
526
 
 
527
  mft->buf = grub_malloc (mft->data->mft_size << BLK_SHR);
 
528
  if (mft->buf == NULL)
 
529
    return grub_errno;
 
530
 
 
531
  if (read_mft (mft->data, mft->buf, mftno))
 
532
    return grub_errno;
 
533
 
 
534
  flag = u16at (mft->buf, 0x16);
 
535
  if ((flag & 1) == 0)
 
536
    return grub_error (GRUB_ERR_BAD_FS, "MFT 0x%X is not in use", mftno);
 
537
 
 
538
  if ((flag & 2) == 0)
 
539
    {
 
540
      char *pa;
 
541
 
 
542
      pa = locate_attr (&mft->attr, mft, AT_DATA);
 
543
      if (pa == NULL)
 
544
        return grub_error (GRUB_ERR_BAD_FS, "no $DATA in MFT 0x%X", mftno);
 
545
 
 
546
      if (!pa[8])
 
547
        mft->size = u32at (pa, 0x10);
 
548
      else
 
549
        mft->size = u64at (pa, 0x30);
 
550
 
 
551
      if ((mft->attr.flags & AF_ALST) == 0)
 
552
        mft->attr.attr_end = 0; /*  Don't jump to attribute list */
 
553
    }
 
554
  else
 
555
    init_attr (&mft->attr, mft);
 
556
 
 
557
  return 0;
 
558
}
 
559
 
 
560
static void
 
561
free_file (struct grub_ntfs_file *mft)
 
562
{
 
563
  free_attr (&mft->attr);
 
564
  grub_free (mft->buf);
 
565
}
 
566
 
 
567
static int
 
568
list_file (struct grub_ntfs_file *diro, char *pos,
 
569
           int NESTED_FUNC_ATTR
 
570
           (*hook) (const char *filename,
 
571
                    enum grub_fshelp_filetype filetype,
 
572
                    grub_fshelp_node_t node))
 
573
{
 
574
  char *np;
 
575
  int ns;
 
576
 
 
577
  while (1)
 
578
    {
 
579
      char *ustr, namespace;
 
580
 
 
581
      if (pos[0xC] & 2)         /* end signature */
 
582
        break;
 
583
 
 
584
      np = pos + 0x50;
 
585
      ns = (unsigned char) *(np++);
 
586
      namespace = *(np++);
 
587
 
 
588
      /*
 
589
       *  Ignore files in DOS namespace, as they will reappear as Win32
 
590
       *  names.
 
591
       */
 
592
      if ((ns) && (namespace != 2))
 
593
        {
 
594
          enum grub_fshelp_filetype type;
 
595
          struct grub_ntfs_file *fdiro;
 
596
 
 
597
          if (u16at (pos, 4))
 
598
            {
 
599
              grub_error (GRUB_ERR_BAD_FS, "64-bit MFT number");
 
600
              return 0;
 
601
            }
 
602
 
 
603
          type =
 
604
            (u32at (pos, 0x48) & ATTR_DIRECTORY) ? GRUB_FSHELP_DIR :
 
605
            GRUB_FSHELP_REG;
 
606
 
 
607
          fdiro = grub_zalloc (sizeof (struct grub_ntfs_file));
 
608
          if (!fdiro)
 
609
            return 0;
 
610
 
 
611
          fdiro->data = diro->data;
 
612
          fdiro->ino = u32at (pos, 0);
 
613
 
 
614
          ustr = grub_malloc (ns * 4 + 1);
 
615
          if (ustr == NULL)
 
616
            return 0;
 
617
          *grub_utf16_to_utf8 ((grub_uint8_t *) ustr, (grub_uint16_t *) np,
 
618
                               ns) = '\0';
 
619
 
 
620
          if (namespace)
 
621
            type |= GRUB_FSHELP_CASE_INSENSITIVE;
 
622
 
 
623
          if (hook (ustr, type, fdiro))
 
624
            {
 
625
              grub_free (ustr);
 
626
              return 1;
 
627
            }
 
628
 
 
629
          grub_free (ustr);
 
630
        }
 
631
      pos += u16at (pos, 8);
 
632
    }
 
633
  return 0;
 
634
}
 
635
 
 
636
static int
 
637
grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
 
638
                       int NESTED_FUNC_ATTR
 
639
                       (*hook) (const char *filename,
 
640
                                enum grub_fshelp_filetype filetype,
 
641
                                grub_fshelp_node_t node))
 
642
{
 
643
  unsigned char *bitmap;
 
644
  struct grub_ntfs_attr attr, *at;
 
645
  char *cur_pos, *indx, *bmp;
 
646
  int ret = 0;
 
647
  grub_size_t bitmap_len;
 
648
  struct grub_ntfs_file *mft;
 
649
 
 
650
  mft = (struct grub_ntfs_file *) dir;
 
651
 
 
652
  if (!mft->inode_read)
 
653
    {
 
654
      if (init_file (mft, mft->ino))
 
655
        return 0;
 
656
    }
 
657
 
 
658
  indx = NULL;
 
659
  bmp = NULL;
 
660
 
 
661
  at = &attr;
 
662
  init_attr (at, mft);
 
663
  while (1)
 
664
    {
 
665
      if ((cur_pos = find_attr (at, AT_INDEX_ROOT)) == NULL)
 
666
        {
 
667
          grub_error (GRUB_ERR_BAD_FS, "no $INDEX_ROOT");
 
668
          goto done;
 
669
        }
 
670
 
 
671
      /* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */
 
672
      if ((u32at (cur_pos, 8) != 0x180400) ||
 
673
          (u32at (cur_pos, 0x18) != 0x490024) ||
 
674
          (u32at (cur_pos, 0x1C) != 0x300033))
 
675
        continue;
 
676
      cur_pos += u16at (cur_pos, 0x14);
 
677
      if (*cur_pos != 0x30)     /* Not filename index */
 
678
        continue;
 
679
      break;
 
680
    }
 
681
 
 
682
  cur_pos += 0x10;              /* Skip index root */
 
683
  ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook);
 
684
  if (ret)
 
685
    goto done;
 
686
 
 
687
  bitmap = NULL;
 
688
  bitmap_len = 0;
 
689
  free_attr (at);
 
690
  init_attr (at, mft);
 
691
  while ((cur_pos = find_attr (at, AT_BITMAP)) != NULL)
 
692
    {
 
693
      int ofs;
 
694
 
 
695
      ofs = (unsigned char) cur_pos[0xA];
 
696
      /* Namelen=4, Name="$I30" */
 
697
      if ((cur_pos[9] == 4) &&
 
698
          (u32at (cur_pos, ofs) == 0x490024) &&
 
699
          (u32at (cur_pos, ofs + 4) == 0x300033))
 
700
        {
 
701
          int is_resident = (cur_pos[8] == 0);
 
702
 
 
703
          bitmap_len = ((is_resident) ? u32at (cur_pos, 0x10) :
 
704
                        u32at (cur_pos, 0x28));
 
705
 
 
706
          bmp = grub_malloc (bitmap_len);
 
707
          if (bmp == NULL)
 
708
            goto done;
 
709
 
 
710
          if (is_resident)
 
711
            {
 
712
              grub_memcpy (bmp, (char *) (cur_pos + u16at (cur_pos, 0x14)),
 
713
                           bitmap_len);
 
714
            }
 
715
          else
 
716
            {
 
717
              if (read_data (at, cur_pos, bmp, 0, bitmap_len, 0, 0))
 
718
                {
 
719
                  grub_error (GRUB_ERR_BAD_FS,
 
720
                              "fails to read non-resident $BITMAP");
 
721
                  goto done;
 
722
                }
 
723
              bitmap_len = u32at (cur_pos, 0x30);
 
724
            }
 
725
 
 
726
          bitmap = (unsigned char *) bmp;
 
727
          break;
 
728
        }
 
729
    }
 
730
 
 
731
  free_attr (at);
 
732
  cur_pos = locate_attr (at, mft, AT_INDEX_ALLOCATION);
 
733
  while (cur_pos != NULL)
 
734
    {
 
735
      /* Non-resident, Namelen=4, Offset=0x40, Flags=0, Name="$I30" */
 
736
      if ((u32at (cur_pos, 8) == 0x400401) &&
 
737
          (u32at (cur_pos, 0x40) == 0x490024) &&
 
738
          (u32at (cur_pos, 0x44) == 0x300033))
 
739
        break;
 
740
      cur_pos = find_attr (at, AT_INDEX_ALLOCATION);
 
741
    }
 
742
 
 
743
  if ((!cur_pos) && (bitmap))
 
744
    {
 
745
      grub_error (GRUB_ERR_BAD_FS, "$BITMAP without $INDEX_ALLOCATION");
 
746
      goto done;
 
747
    }
 
748
 
 
749
  if (bitmap)
 
750
    {
 
751
      grub_disk_addr_t v, i;
 
752
 
 
753
      indx = grub_malloc (mft->data->idx_size << BLK_SHR);
 
754
      if (indx == NULL)
 
755
        goto done;
 
756
 
 
757
      v = 1;
 
758
      for (i = 0; i < (grub_disk_addr_t)bitmap_len * 8; i++)
 
759
        {
 
760
          if (*bitmap & v)
 
761
            {
 
762
              if ((read_attr
 
763
                   (at, indx, i * (mft->data->idx_size << BLK_SHR),
 
764
                    (mft->data->idx_size << BLK_SHR), 0, 0))
 
765
                  || (fixup (mft->data, indx, mft->data->idx_size, "INDX")))
 
766
                goto done;
 
767
              ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], hook);
 
768
              if (ret)
 
769
                goto done;
 
770
            }
 
771
          v <<= 1;
 
772
          if (v >= 0x100)
 
773
            {
 
774
              v = 1;
 
775
              bitmap++;
 
776
            }
 
777
        }
 
778
    }
 
779
 
 
780
done:
 
781
  free_attr (at);
 
782
  grub_free (indx);
 
783
  grub_free (bmp);
 
784
 
 
785
  return ret;
 
786
}
 
787
 
 
788
static struct grub_ntfs_data *
 
789
grub_ntfs_mount (grub_disk_t disk)
 
790
{
 
791
  struct grub_ntfs_bpb bpb;
 
792
  struct grub_ntfs_data *data = 0;
 
793
 
 
794
  if (!disk)
 
795
    goto fail;
 
796
 
 
797
  data = (struct grub_ntfs_data *) grub_zalloc (sizeof (*data));
 
798
  if (!data)
 
799
    goto fail;
 
800
 
 
801
  data->disk = disk;
 
802
 
 
803
  /* Read the BPB.  */
 
804
  if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb))
 
805
    goto fail;
 
806
 
 
807
  if (grub_memcmp ((char *) &bpb.oem_name, "NTFS", 4))
 
808
    goto fail;
 
809
 
 
810
  data->blocksize = grub_le_to_cpu16 (bpb.bytes_per_sector);
 
811
  data->spc = bpb.sectors_per_cluster * (data->blocksize >> BLK_SHR);
 
812
 
 
813
  if (bpb.clusters_per_mft > 0)
 
814
    data->mft_size = data->spc * bpb.clusters_per_mft;
 
815
  else
 
816
    data->mft_size = 1 << (-bpb.clusters_per_mft - BLK_SHR);
 
817
 
 
818
  if (bpb.clusters_per_index > 0)
 
819
    data->idx_size = data->spc * bpb.clusters_per_index;
 
820
  else
 
821
    data->idx_size = 1 << (-bpb.clusters_per_index - BLK_SHR);
 
822
 
 
823
  data->mft_start = grub_le_to_cpu64 (bpb.mft_lcn) * data->spc;
 
824
 
 
825
  if ((data->mft_size > MAX_MFT) || (data->idx_size > MAX_IDX))
 
826
    goto fail;
 
827
 
 
828
  data->mmft.data = data;
 
829
  data->cmft.data = data;
 
830
 
 
831
  data->mmft.buf = grub_malloc (data->mft_size << BLK_SHR);
 
832
  if (!data->mmft.buf)
 
833
    goto fail;
 
834
 
 
835
  if (grub_disk_read
 
836
      (disk, data->mft_start, 0, data->mft_size << BLK_SHR, data->mmft.buf))
 
837
    goto fail;
 
838
 
 
839
  data->uuid = grub_le_to_cpu64 (bpb.num_serial);
 
840
 
 
841
  if (fixup (data, data->mmft.buf, data->mft_size, "FILE"))
 
842
    goto fail;
 
843
 
 
844
  if (!locate_attr (&data->mmft.attr, &data->mmft, AT_DATA))
 
845
    goto fail;
 
846
 
 
847
  if (init_file (&data->cmft, FILE_ROOT))
 
848
    goto fail;
 
849
 
 
850
  return data;
 
851
 
 
852
fail:
 
853
  grub_error (GRUB_ERR_BAD_FS, "not an ntfs filesystem");
 
854
 
 
855
  if (data)
 
856
    {
 
857
      free_file (&data->mmft);
 
858
      free_file (&data->cmft);
 
859
      grub_free (data);
 
860
    }
 
861
  return 0;
 
862
}
 
863
 
 
864
static grub_err_t
 
865
grub_ntfs_dir (grub_device_t device, const char *path,
 
866
               int (*hook) (const char *filename,
 
867
                            const struct grub_dirhook_info *info))
 
868
{
 
869
  struct grub_ntfs_data *data = 0;
 
870
  struct grub_fshelp_node *fdiro = 0;
 
871
 
 
872
  auto int NESTED_FUNC_ATTR iterate (const char *filename,
 
873
                                     enum grub_fshelp_filetype filetype,
 
874
                                     grub_fshelp_node_t node);
 
875
 
 
876
  int NESTED_FUNC_ATTR iterate (const char *filename,
 
877
                                enum grub_fshelp_filetype filetype,
 
878
                                grub_fshelp_node_t node)
 
879
  {
 
880
      struct grub_dirhook_info info;
 
881
      grub_memset (&info, 0, sizeof (info));
 
882
      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
 
883
      grub_free (node);
 
884
      return hook (filename, &info);
 
885
  }
 
886
 
 
887
  grub_dl_ref (my_mod);
 
888
 
 
889
  data = grub_ntfs_mount (device->disk);
 
890
  if (!data)
 
891
    goto fail;
 
892
 
 
893
  grub_fshelp_find_file (path, &data->cmft, &fdiro, grub_ntfs_iterate_dir,
 
894
                         0, GRUB_FSHELP_DIR);
 
895
 
 
896
  if (grub_errno)
 
897
    goto fail;
 
898
 
 
899
  grub_ntfs_iterate_dir (fdiro, iterate);
 
900
 
 
901
fail:
 
902
  if ((fdiro) && (fdiro != &data->cmft))
 
903
    {
 
904
      free_file (fdiro);
 
905
      grub_free (fdiro);
 
906
    }
 
907
  if (data)
 
908
    {
 
909
      free_file (&data->mmft);
 
910
      free_file (&data->cmft);
 
911
      grub_free (data);
 
912
    }
 
913
 
 
914
  grub_dl_unref (my_mod);
 
915
 
 
916
  return grub_errno;
 
917
}
 
918
 
 
919
static grub_err_t
 
920
grub_ntfs_open (grub_file_t file, const char *name)
 
921
{
 
922
  struct grub_ntfs_data *data = 0;
 
923
  struct grub_fshelp_node *mft = 0;
 
924
 
 
925
  grub_dl_ref (my_mod);
 
926
 
 
927
  data = grub_ntfs_mount (file->device->disk);
 
928
  if (!data)
 
929
    goto fail;
 
930
 
 
931
  grub_fshelp_find_file (name, &data->cmft, &mft, grub_ntfs_iterate_dir,
 
932
                         0, GRUB_FSHELP_REG);
 
933
 
 
934
  if (grub_errno)
 
935
    goto fail;
 
936
 
 
937
  if (mft != &data->cmft)
 
938
    {
 
939
      free_file (&data->cmft);
 
940
      grub_memcpy (&data->cmft, mft, sizeof (*mft));
 
941
      grub_free (mft);
 
942
      if (!data->cmft.inode_read)
 
943
        {
 
944
          if (init_file (&data->cmft, data->cmft.ino))
 
945
            goto fail;
 
946
        }
 
947
    }
 
948
 
 
949
  file->size = data->cmft.size;
 
950
  file->data = data;
 
951
  file->offset = 0;
 
952
 
 
953
  return 0;
 
954
 
 
955
fail:
 
956
  if (data)
 
957
    {
 
958
      free_file (&data->mmft);
 
959
      free_file (&data->cmft);
 
960
      grub_free (data);
 
961
    }
 
962
 
 
963
  grub_dl_unref (my_mod);
 
964
 
 
965
  return grub_errno;
 
966
}
 
967
 
 
968
static grub_ssize_t
 
969
grub_ntfs_read (grub_file_t file, char *buf, grub_size_t len)
 
970
{
 
971
  struct grub_ntfs_file *mft;
 
972
 
 
973
  mft = &((struct grub_ntfs_data *) file->data)->cmft;
 
974
  if (file->read_hook)
 
975
    mft->attr.save_pos = 1;
 
976
 
 
977
  read_attr (&mft->attr, buf, file->offset, len, 1, file->read_hook);
 
978
  return (grub_errno) ? 0 : len;
 
979
}
 
980
 
 
981
static grub_err_t
 
982
grub_ntfs_close (grub_file_t file)
 
983
{
 
984
  struct grub_ntfs_data *data;
 
985
 
 
986
  data = file->data;
 
987
 
 
988
  if (data)
 
989
    {
 
990
      free_file (&data->mmft);
 
991
      free_file (&data->cmft);
 
992
      grub_free (data);
 
993
    }
 
994
 
 
995
  grub_dl_unref (my_mod);
 
996
 
 
997
  return grub_errno;
 
998
}
 
999
 
 
1000
static grub_err_t
 
1001
grub_ntfs_label (grub_device_t device, char **label)
 
1002
{
 
1003
  struct grub_ntfs_data *data = 0;
 
1004
  struct grub_fshelp_node *mft = 0;
 
1005
  char *pa;
 
1006
 
 
1007
  grub_dl_ref (my_mod);
 
1008
 
 
1009
  *label = 0;
 
1010
 
 
1011
  data = grub_ntfs_mount (device->disk);
 
1012
  if (!data)
 
1013
    goto fail;
 
1014
 
 
1015
  grub_fshelp_find_file ("/$Volume", &data->cmft, &mft, grub_ntfs_iterate_dir,
 
1016
                         0, GRUB_FSHELP_REG);
 
1017
 
 
1018
  if (grub_errno)
 
1019
    goto fail;
 
1020
 
 
1021
  if (!mft->inode_read)
 
1022
    {
 
1023
      mft->buf = grub_malloc (mft->data->mft_size << BLK_SHR);
 
1024
      if (mft->buf == NULL)
 
1025
        goto fail;
 
1026
 
 
1027
      if (read_mft (mft->data, mft->buf, mft->ino))
 
1028
        goto fail;
 
1029
    }
 
1030
 
 
1031
  init_attr (&mft->attr, mft);
 
1032
  pa = find_attr (&mft->attr, AT_VOLUME_NAME);
 
1033
  if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10)))
 
1034
    {
 
1035
      char *buf;
 
1036
      int len;
 
1037
 
 
1038
      len = u32at (pa, 0x10) / 2;
 
1039
      buf = grub_malloc (len * 4 + 1);
 
1040
      pa += u16at (pa, 0x14);
 
1041
      *grub_utf16_to_utf8 ((grub_uint8_t *) buf, (grub_uint16_t *) pa, len) =
 
1042
        '\0';
 
1043
      *label = buf;
 
1044
    }
 
1045
 
 
1046
fail:
 
1047
  if ((mft) && (mft != &data->cmft))
 
1048
    {
 
1049
      free_file (mft);
 
1050
      grub_free (mft);
 
1051
    }
 
1052
  if (data)
 
1053
    {
 
1054
      free_file (&data->mmft);
 
1055
      free_file (&data->cmft);
 
1056
      grub_free (data);
 
1057
    }
 
1058
 
 
1059
  grub_dl_unref (my_mod);
 
1060
 
 
1061
  return grub_errno;
 
1062
}
 
1063
 
 
1064
static grub_err_t
 
1065
grub_ntfs_uuid (grub_device_t device, char **uuid)
 
1066
{
 
1067
  struct grub_ntfs_data *data;
 
1068
  grub_disk_t disk = device->disk;
 
1069
 
 
1070
  grub_dl_ref (my_mod);
 
1071
 
 
1072
  data = grub_ntfs_mount (disk);
 
1073
  if (data)
 
1074
    {
 
1075
      char *ptr;
 
1076
      *uuid = grub_xasprintf ("%016llx", (unsigned long long) data->uuid);
 
1077
      if (*uuid)
 
1078
        for (ptr = *uuid; *ptr; ptr++)
 
1079
          *ptr = grub_toupper (*ptr);
 
1080
    }
 
1081
  else
 
1082
    *uuid = NULL;
 
1083
 
 
1084
  grub_dl_unref (my_mod);
 
1085
 
 
1086
  grub_free (data);
 
1087
 
 
1088
  return grub_errno;
 
1089
}
 
1090
 
 
1091
static struct grub_fs grub_ntfs_fs =
 
1092
  {
 
1093
    .name = "ntfs",
 
1094
    .dir = grub_ntfs_dir,
 
1095
    .open = grub_ntfs_open,
 
1096
    .read = grub_ntfs_read,
 
1097
    .close = grub_ntfs_close,
 
1098
    .label = grub_ntfs_label,
 
1099
    .uuid = grub_ntfs_uuid,
 
1100
#ifdef GRUB_UTIL
 
1101
    .reserved_first_sector = 1,
 
1102
#endif
 
1103
    .next = 0
 
1104
};
 
1105
 
 
1106
GRUB_MOD_INIT (ntfs)
 
1107
{
 
1108
  grub_fs_register (&grub_ntfs_fs);
 
1109
  my_mod = mod;
 
1110
}
 
1111
 
 
1112
GRUB_MOD_FINI (ntfs)
 
1113
{
 
1114
  grub_fs_unregister (&grub_ntfs_fs);
 
1115
}