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

« back to all changes in this revision

Viewing changes to 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
 
      *uuid = grub_xasprintf ("%016llx", (unsigned long long) data->uuid);
1076
 
    }
1077
 
  else
1078
 
    *uuid = NULL;
1079
 
 
1080
 
  grub_dl_unref (my_mod);
1081
 
 
1082
 
  grub_free (data);
1083
 
 
1084
 
  return grub_errno;
1085
 
}
1086
 
 
1087
 
static struct grub_fs grub_ntfs_fs =
1088
 
  {
1089
 
    .name = "ntfs",
1090
 
    .dir = grub_ntfs_dir,
1091
 
    .open = grub_ntfs_open,
1092
 
    .read = grub_ntfs_read,
1093
 
    .close = grub_ntfs_close,
1094
 
    .label = grub_ntfs_label,
1095
 
    .uuid = grub_ntfs_uuid,
1096
 
#ifdef GRUB_UTIL
1097
 
    .reserved_first_sector = 1,
1098
 
#endif
1099
 
    .next = 0
1100
 
};
1101
 
 
1102
 
GRUB_MOD_INIT (ntfs)
1103
 
{
1104
 
  grub_fs_register (&grub_ntfs_fs);
1105
 
  my_mod = mod;
1106
 
}
1107
 
 
1108
 
GRUB_MOD_FINI (ntfs)
1109
 
{
1110
 
  grub_fs_unregister (&grub_ntfs_fs);
1111
 
}