1 /* ntfs.c - NTFS filesystem */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #define grub_fshelp_node grub_ntfs_file
22 #include <grub/file.h>
24 #include <grub/misc.h>
25 #include <grub/disk.h>
27 #include <grub/fshelp.h>
28 #include <grub/ntfs.h>
29 #include <grub/charset.h>
31 GRUB_MOD_LICENSE ("GPLv3+");
33 static grub_dl_t my_mod
;
35 #define grub_fshelp_node grub_ntfs_file
37 static inline grub_uint16_t
38 u16at (void *ptr
, grub_size_t ofs
)
40 return grub_le_to_cpu16 (grub_get_unaligned16 ((char *) ptr
+ ofs
));
43 static inline grub_uint32_t
44 u32at (void *ptr
, grub_size_t ofs
)
46 return grub_le_to_cpu32 (grub_get_unaligned32 ((char *) ptr
+ ofs
));
49 static inline grub_uint64_t
50 u64at (void *ptr
, grub_size_t ofs
)
52 return grub_le_to_cpu64 (grub_get_unaligned64 ((char *) ptr
+ ofs
));
55 grub_ntfscomp_func_t grub_ntfscomp_func
;
58 fixup (char *buf
, int len
, const char *magic
)
64 COMPILE_TIME_ASSERT ((1 << GRUB_NTFS_BLK_SHR
) == GRUB_DISK_SECTOR_SIZE
);
66 if (grub_memcmp (buf
, magic
, 4))
67 return grub_error (GRUB_ERR_BAD_FS
, "%s label not found", magic
);
69 ss
= u16at (buf
, 6) - 1;
71 return grub_error (GRUB_ERR_BAD_FS
, "size not match");
72 pu
= buf
+ u16at (buf
, 4);
77 buf
+= GRUB_DISK_SECTOR_SIZE
;
79 if (u16at (buf
, 0) != us
)
80 return grub_error (GRUB_ERR_BAD_FS
, "fixup signature not match");
89 static grub_err_t
read_mft (struct grub_ntfs_data
*data
, char *buf
,
91 static grub_err_t
read_attr (struct grub_ntfs_attr
*at
, char *dest
,
92 grub_disk_addr_t ofs
, grub_size_t len
,
95 NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t
100 static grub_err_t
read_data (struct grub_ntfs_attr
*at
, char *pa
, char *dest
,
101 grub_disk_addr_t ofs
, grub_size_t len
,
104 NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t
110 init_attr (struct grub_ntfs_attr
*at
, struct grub_ntfs_file
*mft
)
113 at
->flags
= (mft
== &mft
->data
->mmft
) ? GRUB_NTFS_AF_MMFT
: 0;
114 at
->attr_nxt
= mft
->buf
+ u16at (mft
->buf
, 0x14);
115 at
->attr_end
= at
->emft_buf
= at
->edat_buf
= at
->sbuf
= NULL
;
119 free_attr (struct grub_ntfs_attr
*at
)
121 grub_free (at
->emft_buf
);
122 grub_free (at
->edat_buf
);
123 grub_free (at
->sbuf
);
127 find_attr (struct grub_ntfs_attr
*at
, unsigned char attr
)
129 if (at
->flags
& GRUB_NTFS_AF_ALST
)
132 while (at
->attr_nxt
< at
->attr_end
)
134 at
->attr_cur
= at
->attr_nxt
;
135 at
->attr_nxt
+= u16at (at
->attr_cur
, 4);
136 if (((unsigned char) *at
->attr_cur
== attr
) || (attr
== 0))
140 if (at
->flags
& GRUB_NTFS_AF_MMFT
)
143 (at
->mft
->data
->disk
, u32at (at
->attr_cur
, 0x10), 0,
147 (at
->mft
->data
->disk
, u32at (at
->attr_cur
, 0x14), 0,
148 512, at
->emft_buf
+ 512)))
151 if (fixup (at
->emft_buf
, at
->mft
->data
->mft_size
, "FILE"))
156 if (read_mft (at
->mft
->data
, at
->emft_buf
,
157 u32at (at
->attr_cur
, 0x10)))
161 new_pos
= &at
->emft_buf
[u16at (at
->emft_buf
, 0x14)];
162 while ((unsigned char) *new_pos
!= 0xFF)
164 if (((unsigned char) *new_pos
==
165 (unsigned char) *at
->attr_cur
)
166 && (u16at (new_pos
, 0xE) == u16at (at
->attr_cur
, 0x18)))
170 new_pos
+= u16at (new_pos
, 4);
172 grub_error (GRUB_ERR_BAD_FS
,
173 "can\'t find 0x%X in attribute list",
174 (unsigned char) *at
->attr_cur
);
180 at
->attr_cur
= at
->attr_nxt
;
181 while ((unsigned char) *at
->attr_cur
!= 0xFF)
183 at
->attr_nxt
+= u16at (at
->attr_cur
, 4);
184 if ((unsigned char) *at
->attr_cur
== GRUB_NTFS_AT_ATTRIBUTE_LIST
)
185 at
->attr_end
= at
->attr_cur
;
186 if (((unsigned char) *at
->attr_cur
== attr
) || (attr
== 0))
188 at
->attr_cur
= at
->attr_nxt
;
194 at
->emft_buf
= grub_malloc (at
->mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
195 if (at
->emft_buf
== NULL
)
203 n
= ((u32at (pa
, 0x30) + GRUB_DISK_SECTOR_SIZE
- 1)
204 & (~(GRUB_DISK_SECTOR_SIZE
- 1)));
205 at
->attr_cur
= at
->attr_end
;
206 at
->edat_buf
= grub_malloc (n
);
209 if (read_data (at
, pa
, at
->edat_buf
, 0, n
, 0, 0))
211 grub_error (GRUB_ERR_BAD_FS
,
212 "fail to read non-resident attribute list");
215 at
->attr_nxt
= at
->edat_buf
;
216 at
->attr_end
= at
->edat_buf
+ u32at (pa
, 0x30);
220 at
->attr_nxt
= at
->attr_end
+ u16at (pa
, 0x14);
221 at
->attr_end
= at
->attr_end
+ u32at (pa
, 4);
223 at
->flags
|= GRUB_NTFS_AF_ALST
;
224 while (at
->attr_nxt
< at
->attr_end
)
226 if (((unsigned char) *at
->attr_nxt
== attr
) || (attr
== 0))
228 at
->attr_nxt
+= u16at (at
->attr_nxt
, 4);
230 if (at
->attr_nxt
>= at
->attr_end
)
233 if ((at
->flags
& GRUB_NTFS_AF_MMFT
) && (attr
== GRUB_NTFS_AT_DATA
))
235 at
->flags
|= GRUB_NTFS_AF_GPOS
;
236 at
->attr_cur
= at
->attr_nxt
;
238 grub_set_unaligned32 ((char *) pa
+ 0x10,
239 grub_cpu_to_le32 (at
->mft
->data
->mft_start
));
240 grub_set_unaligned32 ((char *) pa
+ 0x14,
241 grub_cpu_to_le32 (at
->mft
->data
->mft_start
243 pa
= at
->attr_nxt
+ u16at (pa
, 4);
244 while (pa
< at
->attr_end
)
246 if ((unsigned char) *pa
!= attr
)
250 u32at (pa
, 0x10) * (at
->mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
),
251 at
->mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
, 0, 0))
255 at
->attr_nxt
= at
->attr_cur
;
256 at
->flags
&= ~GRUB_NTFS_AF_GPOS
;
264 locate_attr (struct grub_ntfs_attr
*at
, struct grub_ntfs_file
*mft
,
270 pa
= find_attr (at
, attr
);
273 if ((at
->flags
& GRUB_NTFS_AF_ALST
) == 0)
277 pa
= find_attr (at
, attr
);
280 if (at
->flags
& GRUB_NTFS_AF_ALST
)
283 grub_errno
= GRUB_ERR_NONE
;
286 pa
= find_attr (at
, attr
);
292 read_run_data (char *run
, int nn
, grub_disk_addr_t
* val
, int sig
)
294 grub_disk_addr_t r
, v
;
301 r
+= v
* (*(unsigned char *) (run
++));
305 if ((sig
) && (r
& (v
>> 1)))
313 grub_ntfs_read_run_list (struct grub_ntfs_rlst
* ctx
)
316 grub_disk_addr_t val
;
321 c1
= ((unsigned char) (*run
) & 0xF);
322 c2
= ((unsigned char) (*run
) >> 4);
325 if ((ctx
->attr
) && (ctx
->attr
->flags
& GRUB_NTFS_AF_ALST
))
327 void NESTED_FUNC_ATTR (*save_hook
) (grub_disk_addr_t sector
,
331 save_hook
= ctx
->comp
.disk
->read_hook
;
332 ctx
->comp
.disk
->read_hook
= 0;
333 run
= find_attr (ctx
->attr
, (unsigned char) *ctx
->attr
->attr_cur
);
334 ctx
->comp
.disk
->read_hook
= save_hook
;
338 return grub_error (GRUB_ERR_BAD_FS
,
339 "$DATA should be non-resident");
341 run
+= u16at (run
, 0x20);
346 return grub_error (GRUB_ERR_BAD_FS
, "run list overflown");
348 run
= read_run_data (run
+ 1, c1
, &val
, 0); /* length of current VCN */
349 ctx
->curr_vcn
= ctx
->next_vcn
;
350 ctx
->next_vcn
+= val
;
351 run
= read_run_data (run
, c2
, &val
, 1); /* offset to previous LCN */
352 ctx
->curr_lcn
+= val
;
354 ctx
->flags
|= GRUB_NTFS_RF_BLNK
;
356 ctx
->flags
&= ~GRUB_NTFS_RF_BLNK
;
361 static grub_disk_addr_t
362 grub_ntfs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t block
)
364 struct grub_ntfs_rlst
*ctx
;
366 ctx
= (struct grub_ntfs_rlst
*) node
;
367 if (block
>= ctx
->next_vcn
)
369 if (grub_ntfs_read_run_list (ctx
))
371 return ctx
->curr_lcn
;
374 return (ctx
->flags
& GRUB_NTFS_RF_BLNK
) ? 0 : (block
-
375 ctx
->curr_vcn
+ ctx
->curr_lcn
);
379 read_data (struct grub_ntfs_attr
*at
, char *pa
, char *dest
,
380 grub_disk_addr_t ofs
, grub_size_t len
, int cached
,
381 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
385 grub_disk_addr_t vcn
;
386 struct grub_ntfs_rlst cc
, *ctx
;
391 grub_memset (&cc
, 0, sizeof (cc
));
394 ctx
->comp
.spc
= at
->mft
->data
->spc
;
395 ctx
->comp
.disk
= at
->mft
->data
->disk
;
399 if (ofs
+ len
> u32at (pa
, 0x10))
400 return grub_error (GRUB_ERR_BAD_FS
, "read out of range");
401 grub_memcpy (dest
, pa
+ u32at (pa
, 0x14) + ofs
, len
);
405 if (u16at (pa
, 0xC) & GRUB_NTFS_FLAG_COMPRESSED
)
406 ctx
->flags
|= GRUB_NTFS_RF_COMP
;
408 ctx
->flags
&= ~GRUB_NTFS_RF_COMP
;
409 ctx
->cur_run
= pa
+ u16at (pa
, 0x20);
411 if (ctx
->flags
& GRUB_NTFS_RF_COMP
)
414 return grub_error (GRUB_ERR_BAD_FS
, "attribute can\'t be compressed");
418 if ((ofs
& (~(GRUB_NTFS_COM_LEN
- 1))) == at
->save_pos
)
422 n
= GRUB_NTFS_COM_LEN
- (ofs
- at
->save_pos
);
426 grub_memcpy (dest
, at
->sbuf
+ ofs
- at
->save_pos
, n
);
437 at
->sbuf
= grub_malloc (GRUB_NTFS_COM_LEN
);
438 if (at
->sbuf
== NULL
)
443 vcn
= ctx
->target_vcn
= (ofs
>> GRUB_NTFS_COM_LOG_LEN
) * (GRUB_NTFS_COM_SEC
/ ctx
->comp
.spc
);
444 ctx
->target_vcn
&= ~0xFULL
;
447 vcn
= ctx
->target_vcn
= grub_divmod64 (ofs
>> GRUB_NTFS_BLK_SHR
, ctx
->comp
.spc
, 0);
449 ctx
->next_vcn
= u32at (pa
, 0x10);
451 while (ctx
->next_vcn
<= ctx
->target_vcn
)
453 if (grub_ntfs_read_run_list (ctx
))
457 if (at
->flags
& GRUB_NTFS_AF_GPOS
)
459 grub_disk_addr_t st0
, st1
;
462 grub_divmod64 (ofs
>> GRUB_NTFS_BLK_SHR
, ctx
->comp
.spc
, &m
);
465 (ctx
->target_vcn
- ctx
->curr_vcn
+ ctx
->curr_lcn
) * ctx
->comp
.spc
+ m
;
468 (ctx
->next_vcn
- ctx
->curr_vcn
+ ctx
->curr_lcn
) * ctx
->comp
.spc
)
470 if (grub_ntfs_read_run_list (ctx
))
472 st1
= ctx
->curr_lcn
* ctx
->comp
.spc
;
474 grub_set_unaligned32 (dest
, grub_cpu_to_le32 (st0
));
475 grub_set_unaligned32 (dest
+ 4, grub_cpu_to_le32 (st1
));
479 if (!(ctx
->flags
& GRUB_NTFS_RF_COMP
))
483 if (!grub_fshelp_log2blksize (ctx
->comp
.spc
, &pow
))
484 grub_fshelp_read_file (ctx
->comp
.disk
, (grub_fshelp_node_t
) ctx
,
485 read_hook
, ofs
, len
, dest
,
486 grub_ntfs_read_block
, ofs
+ len
, pow
, 0);
490 return (grub_ntfscomp_func
) ? grub_ntfscomp_func (at
, dest
, ofs
, len
, ctx
,
492 grub_error (GRUB_ERR_BAD_FS
, N_("module `%s' isn't loaded"),
497 read_attr (struct grub_ntfs_attr
*at
, char *dest
, grub_disk_addr_t ofs
,
498 grub_size_t len
, int cached
,
499 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
508 save_cur
= at
->attr_cur
;
509 at
->attr_nxt
= at
->attr_cur
;
510 attr
= (unsigned char) *at
->attr_nxt
;
511 if (at
->flags
& GRUB_NTFS_AF_ALST
)
514 grub_disk_addr_t vcn
;
516 /* If compression is possible make sure that we include possible
517 compressed block size. */
518 if (GRUB_NTFS_COM_SEC
>= at
->mft
->data
->spc
)
519 vcn
= ((ofs
>> GRUB_NTFS_COM_LOG_LEN
)
520 * (GRUB_NTFS_COM_SEC
/ at
->mft
->data
->spc
)) & ~0xFULL
;
522 vcn
= grub_divmod64 (ofs
, at
->mft
->data
->spc
<< GRUB_NTFS_BLK_SHR
, 0);
523 pa
= at
->attr_nxt
+ u16at (at
->attr_nxt
, 4);
524 while (pa
< at
->attr_end
)
526 if ((unsigned char) *pa
!= attr
)
528 if (u32at (pa
, 8) > vcn
)
534 pp
= find_attr (at
, attr
);
536 ret
= read_data (at
, pp
, dest
, ofs
, len
, cached
, read_hook
);
539 (grub_errno
) ? grub_errno
: grub_error (GRUB_ERR_BAD_FS
,
540 "attribute not found");
541 at
->attr_cur
= save_cur
;
546 read_mft (struct grub_ntfs_data
*data
, char *buf
, grub_uint32_t mftno
)
549 (&data
->mmft
.attr
, buf
, mftno
* ((grub_disk_addr_t
) data
->mft_size
<< GRUB_NTFS_BLK_SHR
),
550 data
->mft_size
<< GRUB_NTFS_BLK_SHR
, 0, 0))
551 return grub_error (GRUB_ERR_BAD_FS
, "read MFT 0x%X fails", mftno
);
552 return fixup (buf
, data
->mft_size
, "FILE");
556 init_file (struct grub_ntfs_file
*mft
, grub_uint32_t mftno
)
562 mft
->buf
= grub_malloc (mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
563 if (mft
->buf
== NULL
)
566 if (read_mft (mft
->data
, mft
->buf
, mftno
))
569 flag
= u16at (mft
->buf
, 0x16);
571 return grub_error (GRUB_ERR_BAD_FS
, "MFT 0x%X is not in use", mftno
);
577 pa
= locate_attr (&mft
->attr
, mft
, GRUB_NTFS_AT_DATA
);
579 return grub_error (GRUB_ERR_BAD_FS
, "no $DATA in MFT 0x%X", mftno
);
582 mft
->size
= u32at (pa
, 0x10);
584 mft
->size
= u64at (pa
, 0x30);
586 if ((mft
->attr
.flags
& GRUB_NTFS_AF_ALST
) == 0)
587 mft
->attr
.attr_end
= 0; /* Don't jump to attribute list */
590 init_attr (&mft
->attr
, mft
);
596 free_file (struct grub_ntfs_file
*mft
)
598 free_attr (&mft
->attr
);
599 grub_free (mft
->buf
);
603 list_file (struct grub_ntfs_file
*diro
, char *pos
,
605 (*hook
) (const char *filename
,
606 enum grub_fshelp_filetype filetype
,
607 grub_fshelp_node_t node
))
614 char *ustr
, namespace;
616 if (pos
[0xC] & 2) /* end signature */
620 ns
= (unsigned char) *(np
++);
624 * Ignore files in DOS namespace, as they will reappear as Win32
627 if ((ns
) && (namespace != 2))
629 enum grub_fshelp_filetype type
;
630 struct grub_ntfs_file
*fdiro
;
635 grub_error (GRUB_ERR_BAD_FS
, "64-bit MFT number");
639 attr
= u32at (pos
, 0x48);
640 if (attr
& GRUB_NTFS_ATTR_REPARSE
)
641 type
= GRUB_FSHELP_SYMLINK
;
642 else if (attr
& GRUB_NTFS_ATTR_DIRECTORY
)
643 type
= GRUB_FSHELP_DIR
;
645 type
= GRUB_FSHELP_REG
;
647 fdiro
= grub_zalloc (sizeof (struct grub_ntfs_file
));
651 fdiro
->data
= diro
->data
;
652 fdiro
->ino
= u32at (pos
, 0);
653 fdiro
->mtime
= u64at (pos
, 0x20);
655 ustr
= grub_malloc (ns
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
659 grub_uint16_t tmp
[ns
];
661 for (i
= 0; i
< ns
; i
++)
662 tmp
[i
] = grub_le_to_cpu16 (grub_get_unaligned16 ((char *) np
665 *grub_utf16_to_utf8 ((grub_uint8_t
*) ustr
, tmp
, ns
) = '\0';
668 type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
670 if (hook (ustr
, type
, fdiro
))
678 pos
+= u16at (pos
, 8);
683 struct symlink_descriptor
686 grub_uint32_t total_len
;
691 } __attribute__ ((packed
));
694 grub_ntfs_read_symlink (grub_fshelp_node_t node
)
696 struct grub_ntfs_file
*mft
;
697 struct symlink_descriptor symdesc
;
699 grub_uint16_t
*buf16
;
706 mft
= (struct grub_ntfs_file
*) node
;
708 mft
->buf
= grub_malloc (mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
709 if (mft
->buf
== NULL
)
712 if (read_mft (mft
->data
, mft
->buf
, mft
->ino
))
715 pa
= locate_attr (&mft
->attr
, mft
, GRUB_NTFS_AT_SYMLINK
);
718 grub_error (GRUB_ERR_BAD_FS
, "no $SYMLINK in MFT 0x%X", mft
->ino
);
722 err
= read_attr (&mft
->attr
, (char *) &symdesc
, 0,
723 sizeof (struct symlink_descriptor
), 1, 0);
727 switch (grub_cpu_to_le32 (symdesc
.type
))
730 off
= (sizeof (struct symlink_descriptor
) + 4
731 + grub_cpu_to_le32 (symdesc
.off1
));
732 len
= grub_cpu_to_le32 (symdesc
.len1
);
735 off
= (sizeof (struct symlink_descriptor
)
736 + grub_cpu_to_le32 (symdesc
.off1
));
737 len
= grub_cpu_to_le32 (symdesc
.len1
);
740 grub_error (GRUB_ERR_BAD_FS
, "symlink type invalid (%x)",
741 grub_cpu_to_le32 (symdesc
.type
));
745 buf16
= grub_malloc (len
);
749 err
= read_attr (&mft
->attr
, (char *) buf16
, off
, len
, 1, 0);
753 buf
= grub_malloc (len
* 2 + 1);
760 for (i
= 0; i
< len
/ 2; i
++)
762 buf16
[i
] = grub_le_to_cpu16 (buf16
[i
]);
763 if (buf16
[i
] == '\\')
767 end
= (char *) grub_utf16_to_utf8 ((grub_uint8_t
*) buf
, buf16
, len
/ 2);
769 /* Split the sequence to avoid GCC thinking that this is a trigraph. */
770 if (grub_memcmp (buf
, "/?" "?/", 4) == 0 && buf
[5] == ':' && buf
[6] == '/'
771 && grub_isalpha (buf
[4]))
773 grub_memmove (buf
, buf
+ 6, end
- buf
+ 1 - 6);
780 grub_ntfs_iterate_dir (grub_fshelp_node_t dir
,
782 (*hook
) (const char *filename
,
783 enum grub_fshelp_filetype filetype
,
784 grub_fshelp_node_t node
))
786 unsigned char *bitmap
;
787 struct grub_ntfs_attr attr
, *at
;
788 char *cur_pos
, *indx
, *bmp
;
790 grub_size_t bitmap_len
;
791 struct grub_ntfs_file
*mft
;
793 mft
= (struct grub_ntfs_file
*) dir
;
795 if (!mft
->inode_read
)
797 if (init_file (mft
, mft
->ino
))
808 cur_pos
= find_attr (at
, GRUB_NTFS_AT_INDEX_ROOT
);
811 grub_error (GRUB_ERR_BAD_FS
, "no $INDEX_ROOT");
815 /* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */
816 if ((u32at (cur_pos
, 8) != 0x180400) ||
817 (u32at (cur_pos
, 0x18) != 0x490024) ||
818 (u32at (cur_pos
, 0x1C) != 0x300033))
820 cur_pos
+= u16at (cur_pos
, 0x14);
821 if (*cur_pos
!= 0x30) /* Not filename index */
826 cur_pos
+= 0x10; /* Skip index root */
827 ret
= list_file (mft
, cur_pos
+ u16at (cur_pos
, 0), hook
);
835 while ((cur_pos
= find_attr (at
, GRUB_NTFS_AT_BITMAP
)) != NULL
)
839 ofs
= (unsigned char) cur_pos
[0xA];
840 /* Namelen=4, Name="$I30" */
841 if ((cur_pos
[9] == 4) &&
842 (u32at (cur_pos
, ofs
) == 0x490024) &&
843 (u32at (cur_pos
, ofs
+ 4) == 0x300033))
845 int is_resident
= (cur_pos
[8] == 0);
847 bitmap_len
= ((is_resident
) ? u32at (cur_pos
, 0x10) :
848 u32at (cur_pos
, 0x28));
850 bmp
= grub_malloc (bitmap_len
);
856 grub_memcpy (bmp
, (char *) (cur_pos
+ u16at (cur_pos
, 0x14)),
861 if (read_data (at
, cur_pos
, bmp
, 0, bitmap_len
, 0, 0))
863 grub_error (GRUB_ERR_BAD_FS
,
864 "fails to read non-resident $BITMAP");
867 bitmap_len
= u32at (cur_pos
, 0x30);
870 bitmap
= (unsigned char *) bmp
;
876 cur_pos
= locate_attr (at
, mft
, GRUB_NTFS_AT_INDEX_ALLOCATION
);
877 while (cur_pos
!= NULL
)
879 /* Non-resident, Namelen=4, Offset=0x40, Flags=0, Name="$I30" */
880 if ((u32at (cur_pos
, 8) == 0x400401) &&
881 (u32at (cur_pos
, 0x40) == 0x490024) &&
882 (u32at (cur_pos
, 0x44) == 0x300033))
884 cur_pos
= find_attr (at
, GRUB_NTFS_AT_INDEX_ALLOCATION
);
887 if ((!cur_pos
) && (bitmap
))
889 grub_error (GRUB_ERR_BAD_FS
, "$BITMAP without $INDEX_ALLOCATION");
895 grub_disk_addr_t v
, i
;
897 indx
= grub_malloc (mft
->data
->idx_size
<< GRUB_NTFS_BLK_SHR
);
902 for (i
= 0; i
< (grub_disk_addr_t
)bitmap_len
* 8; i
++)
907 (at
, indx
, i
* (mft
->data
->idx_size
<< GRUB_NTFS_BLK_SHR
),
908 (mft
->data
->idx_size
<< GRUB_NTFS_BLK_SHR
), 0, 0))
909 || (fixup (indx
, mft
->data
->idx_size
, "INDX")))
911 ret
= list_file (mft
, &indx
[0x18 + u16at (indx
, 0x18)], hook
);
932 static struct grub_ntfs_data
*
933 grub_ntfs_mount (grub_disk_t disk
)
935 struct grub_ntfs_bpb bpb
;
936 struct grub_ntfs_data
*data
= 0;
941 data
= (struct grub_ntfs_data
*) grub_zalloc (sizeof (*data
));
948 if (grub_disk_read (disk
, 0, 0, sizeof (bpb
), &bpb
))
951 if (grub_memcmp ((char *) &bpb
.oem_name
, "NTFS", 4) != 0
952 || bpb
.sectors_per_cluster
== 0
953 || (bpb
.sectors_per_cluster
& (bpb
.sectors_per_cluster
- 1)) != 0
954 || bpb
.bytes_per_sector
== 0
955 || (bpb
.bytes_per_sector
& (bpb
.bytes_per_sector
- 1)) != 0)
958 data
->spc
= (((grub_uint32_t
) bpb
.sectors_per_cluster
959 * (grub_uint32_t
) grub_le_to_cpu16 (bpb
.bytes_per_sector
))
960 >> GRUB_NTFS_BLK_SHR
);
964 if (bpb
.clusters_per_mft
> 0)
965 data
->mft_size
= data
->spc
* bpb
.clusters_per_mft
;
967 data
->mft_size
= 1 << (-bpb
.clusters_per_mft
- GRUB_NTFS_BLK_SHR
);
969 if (bpb
.clusters_per_index
> 0)
970 data
->idx_size
= data
->spc
* bpb
.clusters_per_index
;
972 data
->idx_size
= 1 << (-bpb
.clusters_per_index
- GRUB_NTFS_BLK_SHR
);
974 data
->mft_start
= grub_le_to_cpu64 (bpb
.mft_lcn
) * data
->spc
;
976 if ((data
->mft_size
> GRUB_NTFS_MAX_MFT
) || (data
->idx_size
> GRUB_NTFS_MAX_IDX
))
979 data
->mmft
.data
= data
;
980 data
->cmft
.data
= data
;
982 data
->mmft
.buf
= grub_malloc (data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
987 (disk
, data
->mft_start
, 0, data
->mft_size
<< GRUB_NTFS_BLK_SHR
, data
->mmft
.buf
))
990 data
->uuid
= grub_le_to_cpu64 (bpb
.num_serial
);
992 if (fixup (data
->mmft
.buf
, data
->mft_size
, "FILE"))
995 if (!locate_attr (&data
->mmft
.attr
, &data
->mmft
, GRUB_NTFS_AT_DATA
))
998 if (init_file (&data
->cmft
, GRUB_NTFS_FILE_ROOT
))
1004 grub_error (GRUB_ERR_BAD_FS
, "not an ntfs filesystem");
1008 free_file (&data
->mmft
);
1009 free_file (&data
->cmft
);
1016 grub_ntfs_dir (grub_device_t device
, const char *path
,
1017 int (*hook
) (const char *filename
,
1018 const struct grub_dirhook_info
*info
))
1020 struct grub_ntfs_data
*data
= 0;
1021 struct grub_fshelp_node
*fdiro
= 0;
1023 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
1024 enum grub_fshelp_filetype filetype
,
1025 grub_fshelp_node_t node
);
1027 int NESTED_FUNC_ATTR
iterate (const char *filename
,
1028 enum grub_fshelp_filetype filetype
,
1029 grub_fshelp_node_t node
)
1031 struct grub_dirhook_info info
;
1032 grub_memset (&info
, 0, sizeof (info
));
1033 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
1035 info
.mtime
= grub_divmod64 (node
->mtime
, 10000000, 0)
1036 - 86400ULL * 365 * (1970 - 1601)
1037 - 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100);
1039 return hook (filename
, &info
);
1042 grub_dl_ref (my_mod
);
1044 data
= grub_ntfs_mount (device
->disk
);
1048 grub_fshelp_find_file (path
, &data
->cmft
, &fdiro
, grub_ntfs_iterate_dir
,
1049 grub_ntfs_read_symlink
, GRUB_FSHELP_DIR
);
1054 grub_ntfs_iterate_dir (fdiro
, iterate
);
1057 if ((fdiro
) && (fdiro
!= &data
->cmft
))
1064 free_file (&data
->mmft
);
1065 free_file (&data
->cmft
);
1069 grub_dl_unref (my_mod
);
1075 grub_ntfs_open (grub_file_t file
, const char *name
)
1077 struct grub_ntfs_data
*data
= 0;
1078 struct grub_fshelp_node
*mft
= 0;
1080 grub_dl_ref (my_mod
);
1082 data
= grub_ntfs_mount (file
->device
->disk
);
1086 grub_fshelp_find_file (name
, &data
->cmft
, &mft
, grub_ntfs_iterate_dir
,
1087 grub_ntfs_read_symlink
, GRUB_FSHELP_REG
);
1092 if (mft
!= &data
->cmft
)
1094 free_file (&data
->cmft
);
1095 grub_memcpy (&data
->cmft
, mft
, sizeof (*mft
));
1097 if (!data
->cmft
.inode_read
)
1099 if (init_file (&data
->cmft
, data
->cmft
.ino
))
1104 file
->size
= data
->cmft
.size
;
1113 free_file (&data
->mmft
);
1114 free_file (&data
->cmft
);
1118 grub_dl_unref (my_mod
);
1124 grub_ntfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1126 struct grub_ntfs_file
*mft
;
1128 mft
= &((struct grub_ntfs_data
*) file
->data
)->cmft
;
1129 if (file
->read_hook
)
1130 mft
->attr
.save_pos
= 1;
1132 read_attr (&mft
->attr
, buf
, file
->offset
, len
, 1, file
->read_hook
);
1133 return (grub_errno
) ? -1 : (grub_ssize_t
) len
;
1137 grub_ntfs_close (grub_file_t file
)
1139 struct grub_ntfs_data
*data
;
1145 free_file (&data
->mmft
);
1146 free_file (&data
->cmft
);
1150 grub_dl_unref (my_mod
);
1156 grub_ntfs_label (grub_device_t device
, char **label
)
1158 struct grub_ntfs_data
*data
= 0;
1159 struct grub_fshelp_node
*mft
= 0;
1162 grub_dl_ref (my_mod
);
1166 data
= grub_ntfs_mount (device
->disk
);
1170 grub_fshelp_find_file ("/$Volume", &data
->cmft
, &mft
, grub_ntfs_iterate_dir
,
1171 0, GRUB_FSHELP_REG
);
1176 if (!mft
->inode_read
)
1178 mft
->buf
= grub_malloc (mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
1179 if (mft
->buf
== NULL
)
1182 if (read_mft (mft
->data
, mft
->buf
, mft
->ino
))
1186 init_attr (&mft
->attr
, mft
);
1187 pa
= find_attr (&mft
->attr
, GRUB_NTFS_AT_VOLUME_NAME
);
1188 if ((pa
) && (pa
[8] == 0) && (u32at (pa
, 0x10)))
1193 len
= u32at (pa
, 0x10) / 2;
1194 buf
= grub_malloc (len
* 4 + 1);
1195 pa
+= u16at (pa
, 0x14);
1197 grub_uint16_t tmp
[len
];
1199 for (i
= 0; i
< len
; i
++)
1200 tmp
[i
] = grub_le_to_cpu16 (grub_get_unaligned16 (pa
+ 2 * i
));
1201 *grub_utf16_to_utf8 ((grub_uint8_t
*) buf
, tmp
, len
) =
1208 if ((mft
) && (mft
!= &data
->cmft
))
1215 free_file (&data
->mmft
);
1216 free_file (&data
->cmft
);
1220 grub_dl_unref (my_mod
);
1226 grub_ntfs_uuid (grub_device_t device
, char **uuid
)
1228 struct grub_ntfs_data
*data
;
1229 grub_disk_t disk
= device
->disk
;
1231 grub_dl_ref (my_mod
);
1233 data
= grub_ntfs_mount (disk
);
1237 *uuid
= grub_xasprintf ("%016llx", (unsigned long long) data
->uuid
);
1239 for (ptr
= *uuid
; *ptr
; ptr
++)
1240 *ptr
= grub_toupper (*ptr
);
1241 free_file (&data
->mmft
);
1242 free_file (&data
->cmft
);
1248 grub_dl_unref (my_mod
);
1253 static struct grub_fs grub_ntfs_fs
=
1256 .dir
= grub_ntfs_dir
,
1257 .open
= grub_ntfs_open
,
1258 .read
= grub_ntfs_read
,
1259 .close
= grub_ntfs_close
,
1260 .label
= grub_ntfs_label
,
1261 .uuid
= grub_ntfs_uuid
,
1263 .reserved_first_sector
= 1,
1264 .blocklist_install
= 1,
1269 GRUB_MOD_INIT (ntfs
)
1271 grub_fs_register (&grub_ntfs_fs
);
1275 GRUB_MOD_FINI (ntfs
)
1277 grub_fs_unregister (&grub_ntfs_fs
);