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 (grub_uint8_t
*buf
, grub_size_t len
, const grub_uint8_t
*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
, grub_uint8_t
*buf
,
91 static grub_err_t
read_attr (struct grub_ntfs_attr
*at
, grub_uint8_t
*dest
,
92 grub_disk_addr_t ofs
, grub_size_t len
,
94 grub_disk_read_hook_t read_hook
,
95 void *read_hook_data
);
97 static grub_err_t
read_data (struct grub_ntfs_attr
*at
, grub_uint8_t
*pa
,
99 grub_disk_addr_t ofs
, grub_size_t len
,
101 grub_disk_read_hook_t read_hook
,
102 void *read_hook_data
);
105 init_attr (struct grub_ntfs_attr
*at
, struct grub_ntfs_file
*mft
)
108 at
->flags
= (mft
== &mft
->data
->mmft
) ? GRUB_NTFS_AF_MMFT
: 0;
109 at
->attr_nxt
= mft
->buf
+ u16at (mft
->buf
, 0x14);
110 at
->attr_end
= at
->emft_buf
= at
->edat_buf
= at
->sbuf
= NULL
;
114 free_attr (struct grub_ntfs_attr
*at
)
116 grub_free (at
->emft_buf
);
117 grub_free (at
->edat_buf
);
118 grub_free (at
->sbuf
);
121 static grub_uint8_t
*
122 find_attr (struct grub_ntfs_attr
*at
, grub_uint8_t attr
)
124 if (at
->flags
& GRUB_NTFS_AF_ALST
)
127 while (at
->attr_nxt
< at
->attr_end
)
129 at
->attr_cur
= at
->attr_nxt
;
130 at
->attr_nxt
+= u16at (at
->attr_cur
, 4);
131 if ((*at
->attr_cur
== attr
) || (attr
== 0))
133 grub_uint8_t
*new_pos
;
135 if (at
->flags
& GRUB_NTFS_AF_MMFT
)
138 (at
->mft
->data
->disk
, u32at (at
->attr_cur
, 0x10), 0,
142 (at
->mft
->data
->disk
, u32at (at
->attr_cur
, 0x14), 0,
143 512, at
->emft_buf
+ 512)))
146 if (fixup (at
->emft_buf
, at
->mft
->data
->mft_size
,
147 (const grub_uint8_t
*) "FILE"))
152 if (read_mft (at
->mft
->data
, at
->emft_buf
,
153 u32at (at
->attr_cur
, 0x10)))
157 new_pos
= &at
->emft_buf
[u16at (at
->emft_buf
, 0x14)];
158 while (*new_pos
!= 0xFF)
160 if ((*new_pos
== *at
->attr_cur
)
161 && (u16at (new_pos
, 0xE) == u16at (at
->attr_cur
, 0x18)))
165 new_pos
+= u16at (new_pos
, 4);
167 grub_error (GRUB_ERR_BAD_FS
,
168 "can\'t find 0x%X in attribute list",
169 (unsigned char) *at
->attr_cur
);
175 at
->attr_cur
= at
->attr_nxt
;
176 while (*at
->attr_cur
!= 0xFF)
178 at
->attr_nxt
+= u16at (at
->attr_cur
, 4);
179 if (*at
->attr_cur
== GRUB_NTFS_AT_ATTRIBUTE_LIST
)
180 at
->attr_end
= at
->attr_cur
;
181 if ((*at
->attr_cur
== attr
) || (attr
== 0))
183 at
->attr_cur
= at
->attr_nxt
;
189 at
->emft_buf
= grub_malloc (at
->mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
190 if (at
->emft_buf
== NULL
)
198 n
= ((u32at (pa
, 0x30) + GRUB_DISK_SECTOR_SIZE
- 1)
199 & (~(GRUB_DISK_SECTOR_SIZE
- 1)));
200 at
->attr_cur
= at
->attr_end
;
201 at
->edat_buf
= grub_malloc (n
);
204 if (read_data (at
, pa
, at
->edat_buf
, 0, n
, 0, 0, 0))
206 grub_error (GRUB_ERR_BAD_FS
,
207 "fail to read non-resident attribute list");
210 at
->attr_nxt
= at
->edat_buf
;
211 at
->attr_end
= at
->edat_buf
+ u32at (pa
, 0x30);
215 at
->attr_nxt
= at
->attr_end
+ u16at (pa
, 0x14);
216 at
->attr_end
= at
->attr_end
+ u32at (pa
, 4);
218 at
->flags
|= GRUB_NTFS_AF_ALST
;
219 while (at
->attr_nxt
< at
->attr_end
)
221 if ((*at
->attr_nxt
== attr
) || (attr
== 0))
223 at
->attr_nxt
+= u16at (at
->attr_nxt
, 4);
225 if (at
->attr_nxt
>= at
->attr_end
)
228 if ((at
->flags
& GRUB_NTFS_AF_MMFT
) && (attr
== GRUB_NTFS_AT_DATA
))
230 at
->flags
|= GRUB_NTFS_AF_GPOS
;
231 at
->attr_cur
= at
->attr_nxt
;
233 grub_set_unaligned32 ((char *) pa
+ 0x10,
234 grub_cpu_to_le32 (at
->mft
->data
->mft_start
));
235 grub_set_unaligned32 ((char *) pa
+ 0x14,
236 grub_cpu_to_le32 (at
->mft
->data
->mft_start
238 pa
= at
->attr_nxt
+ u16at (pa
, 4);
239 while (pa
< at
->attr_end
)
245 u32at (pa
, 0x10) * (at
->mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
),
246 at
->mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
, 0, 0, 0))
250 at
->attr_nxt
= at
->attr_cur
;
251 at
->flags
&= ~GRUB_NTFS_AF_GPOS
;
258 static grub_uint8_t
*
259 locate_attr (struct grub_ntfs_attr
*at
, struct grub_ntfs_file
*mft
,
265 pa
= find_attr (at
, attr
);
268 if ((at
->flags
& GRUB_NTFS_AF_ALST
) == 0)
272 pa
= find_attr (at
, attr
);
275 if (at
->flags
& GRUB_NTFS_AF_ALST
)
278 grub_errno
= GRUB_ERR_NONE
;
281 pa
= find_attr (at
, attr
);
286 static grub_disk_addr_t
287 read_run_data (const grub_uint8_t
*run
, int nn
, int sig
)
291 if (sig
&& nn
&& (run
[nn
- 1] & 0x80))
294 grub_memcpy (&r
, run
, nn
);
296 return grub_le_to_cpu64 (r
);
300 grub_ntfs_read_run_list (struct grub_ntfs_rlst
* ctx
)
303 grub_disk_addr_t val
;
309 c2
= ((*run
) >> 4) & 0x7;
313 if ((ctx
->attr
) && (ctx
->attr
->flags
& GRUB_NTFS_AF_ALST
))
315 grub_disk_read_hook_t save_hook
;
317 save_hook
= ctx
->comp
.disk
->read_hook
;
318 ctx
->comp
.disk
->read_hook
= 0;
319 run
= find_attr (ctx
->attr
, *ctx
->attr
->attr_cur
);
320 ctx
->comp
.disk
->read_hook
= save_hook
;
324 return grub_error (GRUB_ERR_BAD_FS
,
325 "$DATA should be non-resident");
327 run
+= u16at (run
, 0x20);
332 return grub_error (GRUB_ERR_BAD_FS
, "run list overflown");
334 ctx
->curr_vcn
= ctx
->next_vcn
;
335 ctx
->next_vcn
+= read_run_data (run
, c1
, 0); /* length of current VCN */
337 val
= read_run_data (run
, c2
, 1); /* offset to previous LCN */
339 ctx
->curr_lcn
+= val
;
341 ctx
->flags
|= GRUB_NTFS_RF_BLNK
;
343 ctx
->flags
&= ~GRUB_NTFS_RF_BLNK
;
348 static grub_disk_addr_t
349 grub_ntfs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t block
)
351 struct grub_ntfs_rlst
*ctx
;
353 ctx
= (struct grub_ntfs_rlst
*) node
;
354 if (block
>= ctx
->next_vcn
)
356 if (grub_ntfs_read_run_list (ctx
))
358 return ctx
->curr_lcn
;
361 return (ctx
->flags
& GRUB_NTFS_RF_BLNK
) ? 0 : (block
-
362 ctx
->curr_vcn
+ ctx
->curr_lcn
);
366 read_data (struct grub_ntfs_attr
*at
, grub_uint8_t
*pa
, grub_uint8_t
*dest
,
367 grub_disk_addr_t ofs
, grub_size_t len
, int cached
,
368 grub_disk_read_hook_t read_hook
, void *read_hook_data
)
370 struct grub_ntfs_rlst cc
, *ctx
;
375 grub_memset (&cc
, 0, sizeof (cc
));
378 ctx
->comp
.log_spc
= at
->mft
->data
->log_spc
;
379 ctx
->comp
.disk
= at
->mft
->data
->disk
;
381 if (read_hook
== grub_file_progress_hook
)
382 ctx
->file
= read_hook_data
;
386 if (ofs
+ len
> u32at (pa
, 0x10))
387 return grub_error (GRUB_ERR_BAD_FS
, "read out of range");
388 grub_memcpy (dest
, pa
+ u32at (pa
, 0x14) + ofs
, len
);
392 ctx
->cur_run
= pa
+ u16at (pa
, 0x20);
394 ctx
->next_vcn
= u32at (pa
, 0x10);
397 if ((pa
[0xC] & GRUB_NTFS_FLAG_COMPRESSED
)
398 && !(at
->flags
& GRUB_NTFS_AF_GPOS
))
401 return grub_error (GRUB_ERR_BAD_FS
, "attribute can\'t be compressed");
403 return (grub_ntfscomp_func
) ? grub_ntfscomp_func (dest
, ofs
, len
, ctx
)
404 : grub_error (GRUB_ERR_BAD_FS
, N_("module `%s' isn't loaded"),
408 ctx
->target_vcn
= ofs
>> (GRUB_NTFS_BLK_SHR
+ ctx
->comp
.log_spc
);
409 while (ctx
->next_vcn
<= ctx
->target_vcn
)
411 if (grub_ntfs_read_run_list (ctx
))
415 if (at
->flags
& GRUB_NTFS_AF_GPOS
)
417 grub_disk_addr_t st0
, st1
;
420 m
= (ofs
>> GRUB_NTFS_BLK_SHR
) & ((1 << ctx
->comp
.log_spc
) - 1);
423 ((ctx
->target_vcn
- ctx
->curr_vcn
+ ctx
->curr_lcn
) << ctx
->comp
.log_spc
) + m
;
426 (ctx
->next_vcn
- ctx
->curr_vcn
+ ctx
->curr_lcn
) << ctx
->comp
.log_spc
)
428 if (grub_ntfs_read_run_list (ctx
))
430 st1
= ctx
->curr_lcn
<< ctx
->comp
.log_spc
;
432 grub_set_unaligned32 (dest
, grub_cpu_to_le32 (st0
));
433 grub_set_unaligned32 (dest
+ 4, grub_cpu_to_le32 (st1
));
437 grub_fshelp_read_file (ctx
->comp
.disk
, (grub_fshelp_node_t
) ctx
,
438 read_hook
, read_hook_data
, ofs
, len
,
440 grub_ntfs_read_block
, ofs
+ len
,
441 ctx
->comp
.log_spc
, 0);
446 read_attr (struct grub_ntfs_attr
*at
, grub_uint8_t
*dest
, grub_disk_addr_t ofs
,
447 grub_size_t len
, int cached
,
448 grub_disk_read_hook_t read_hook
, void *read_hook_data
)
450 grub_uint8_t
*save_cur
;
455 save_cur
= at
->attr_cur
;
456 at
->attr_nxt
= at
->attr_cur
;
457 attr
= *at
->attr_nxt
;
458 if (at
->flags
& GRUB_NTFS_AF_ALST
)
461 grub_disk_addr_t vcn
;
463 /* If compression is possible make sure that we include possible
464 compressed block size. */
465 if (GRUB_NTFS_LOG_COM_SEC
>= at
->mft
->data
->log_spc
)
466 vcn
= ((ofs
>> GRUB_NTFS_COM_LOG_LEN
)
467 << (GRUB_NTFS_LOG_COM_SEC
- at
->mft
->data
->log_spc
)) & ~0xFULL
;
469 vcn
= ofs
>> (at
->mft
->data
->log_spc
+ GRUB_NTFS_BLK_SHR
);
470 pa
= at
->attr_nxt
+ u16at (at
->attr_nxt
, 4);
471 while (pa
< at
->attr_end
)
475 if (u32at (pa
, 8) > vcn
)
481 pp
= find_attr (at
, attr
);
483 ret
= read_data (at
, pp
, dest
, ofs
, len
, cached
,
484 read_hook
, read_hook_data
);
487 (grub_errno
) ? grub_errno
: grub_error (GRUB_ERR_BAD_FS
,
488 "attribute not found");
489 at
->attr_cur
= save_cur
;
494 read_mft (struct grub_ntfs_data
*data
, grub_uint8_t
*buf
, grub_uint64_t mftno
)
497 (&data
->mmft
.attr
, buf
, mftno
* ((grub_disk_addr_t
) data
->mft_size
<< GRUB_NTFS_BLK_SHR
),
498 data
->mft_size
<< GRUB_NTFS_BLK_SHR
, 0, 0, 0))
499 return grub_error (GRUB_ERR_BAD_FS
, "read MFT 0x%llx fails", (unsigned long long) mftno
);
500 return fixup (buf
, data
->mft_size
, (const grub_uint8_t
*) "FILE");
504 init_file (struct grub_ntfs_file
*mft
, grub_uint64_t mftno
)
510 mft
->buf
= grub_malloc (mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
511 if (mft
->buf
== NULL
)
514 if (read_mft (mft
->data
, mft
->buf
, mftno
))
517 flag
= u16at (mft
->buf
, 0x16);
519 return grub_error (GRUB_ERR_BAD_FS
, "MFT 0x%llx is not in use",
520 (unsigned long long) mftno
);
526 pa
= locate_attr (&mft
->attr
, mft
, GRUB_NTFS_AT_DATA
);
528 return grub_error (GRUB_ERR_BAD_FS
, "no $DATA in MFT 0x%llx",
529 (unsigned long long) mftno
);
532 mft
->size
= u32at (pa
, 0x10);
534 mft
->size
= u64at (pa
, 0x30);
536 if ((mft
->attr
.flags
& GRUB_NTFS_AF_ALST
) == 0)
537 mft
->attr
.attr_end
= 0; /* Don't jump to attribute list */
540 init_attr (&mft
->attr
, mft
);
546 free_file (struct grub_ntfs_file
*mft
)
548 free_attr (&mft
->attr
);
549 grub_free (mft
->buf
);
553 get_utf8 (grub_uint8_t
*in
, grub_size_t len
)
559 buf
= grub_malloc (len
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
560 tmp
= grub_malloc (len
* sizeof (tmp
[0]));
567 for (i
= 0; i
< len
; i
++)
568 tmp
[i
] = grub_le_to_cpu16 (grub_get_unaligned16 (in
+ 2 * i
));
569 *grub_utf16_to_utf8 (buf
, tmp
, len
) = '\0';
575 list_file (struct grub_ntfs_file
*diro
, grub_uint8_t
*pos
,
576 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
583 grub_uint8_t
namespace;
586 if (pos
[0xC] & 2) /* end signature */
594 * Ignore files in DOS namespace, as they will reappear as Win32
597 if ((ns
) && (namespace != 2))
599 enum grub_fshelp_filetype type
;
600 struct grub_ntfs_file
*fdiro
;
603 attr
= u32at (pos
, 0x48);
604 if (attr
& GRUB_NTFS_ATTR_REPARSE
)
605 type
= GRUB_FSHELP_SYMLINK
;
606 else if (attr
& GRUB_NTFS_ATTR_DIRECTORY
)
607 type
= GRUB_FSHELP_DIR
;
609 type
= GRUB_FSHELP_REG
;
611 fdiro
= grub_zalloc (sizeof (struct grub_ntfs_file
));
615 fdiro
->data
= diro
->data
;
616 fdiro
->ino
= u64at (pos
, 0) & 0xffffffffffffULL
;
617 fdiro
->mtime
= u64at (pos
, 0x20);
619 ustr
= get_utf8 (np
, ns
);
626 type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
628 if (hook (ustr
, type
, fdiro
, hook_data
))
636 pos
+= u16at (pos
, 8);
641 struct symlink_descriptor
644 grub_uint32_t total_len
;
652 grub_ntfs_read_symlink (grub_fshelp_node_t node
)
654 struct grub_ntfs_file
*mft
;
655 struct symlink_descriptor symdesc
;
663 mft
= (struct grub_ntfs_file
*) node
;
665 mft
->buf
= grub_malloc (mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
666 if (mft
->buf
== NULL
)
669 if (read_mft (mft
->data
, mft
->buf
, mft
->ino
))
672 pa
= locate_attr (&mft
->attr
, mft
, GRUB_NTFS_AT_SYMLINK
);
675 grub_error (GRUB_ERR_BAD_FS
, "no $SYMLINK in MFT 0x%llx",
676 (unsigned long long) mft
->ino
);
680 err
= read_attr (&mft
->attr
, (grub_uint8_t
*) &symdesc
, 0,
681 sizeof (struct symlink_descriptor
), 1, 0, 0);
685 switch (grub_cpu_to_le32 (symdesc
.type
))
688 off
= (sizeof (struct symlink_descriptor
) + 4
689 + grub_cpu_to_le32 (symdesc
.off1
));
690 len
= grub_cpu_to_le32 (symdesc
.len1
);
693 off
= (sizeof (struct symlink_descriptor
)
694 + grub_cpu_to_le32 (symdesc
.off1
));
695 len
= grub_cpu_to_le32 (symdesc
.len1
);
698 grub_error (GRUB_ERR_BAD_FS
, "symlink type invalid (%x)",
699 grub_cpu_to_le32 (symdesc
.type
));
703 buf16
= grub_malloc (len
);
707 err
= read_attr (&mft
->attr
, buf16
, off
, len
, 1, 0, 0);
711 buf
= get_utf8 (buf16
, len
/ 2);
719 for (end
= buf
; *end
; end
++)
723 /* Split the sequence to avoid GCC thinking that this is a trigraph. */
724 if (grub_memcmp (buf
, "/?" "?/", 4) == 0 && buf
[5] == ':' && buf
[6] == '/'
725 && grub_isalpha (buf
[4]))
727 grub_memmove (buf
, buf
+ 6, end
- buf
+ 1 - 6);
734 grub_ntfs_iterate_dir (grub_fshelp_node_t dir
,
735 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
737 grub_uint8_t
*bitmap
;
738 struct grub_ntfs_attr attr
, *at
;
739 grub_uint8_t
*cur_pos
, *indx
, *bmp
;
741 grub_size_t bitmap_len
;
742 struct grub_ntfs_file
*mft
;
744 mft
= (struct grub_ntfs_file
*) dir
;
746 if (!mft
->inode_read
)
748 if (init_file (mft
, mft
->ino
))
759 cur_pos
= find_attr (at
, GRUB_NTFS_AT_INDEX_ROOT
);
762 grub_error (GRUB_ERR_BAD_FS
, "no $INDEX_ROOT");
766 /* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */
767 if ((u32at (cur_pos
, 8) != 0x180400) ||
768 (u32at (cur_pos
, 0x18) != 0x490024) ||
769 (u32at (cur_pos
, 0x1C) != 0x300033))
771 cur_pos
+= u16at (cur_pos
, 0x14);
772 if (*cur_pos
!= 0x30) /* Not filename index */
777 cur_pos
+= 0x10; /* Skip index root */
778 ret
= list_file (mft
, cur_pos
+ u16at (cur_pos
, 0), hook
, hook_data
);
786 while ((cur_pos
= find_attr (at
, GRUB_NTFS_AT_BITMAP
)) != NULL
)
791 /* Namelen=4, Name="$I30" */
792 if ((cur_pos
[9] == 4) &&
793 (u32at (cur_pos
, ofs
) == 0x490024) &&
794 (u32at (cur_pos
, ofs
+ 4) == 0x300033))
796 int is_resident
= (cur_pos
[8] == 0);
798 bitmap_len
= ((is_resident
) ? u32at (cur_pos
, 0x10) :
799 u32at (cur_pos
, 0x28));
801 bmp
= grub_malloc (bitmap_len
);
807 grub_memcpy (bmp
, cur_pos
+ u16at (cur_pos
, 0x14),
812 if (read_data (at
, cur_pos
, bmp
, 0, bitmap_len
, 0, 0, 0))
814 grub_error (GRUB_ERR_BAD_FS
,
815 "fails to read non-resident $BITMAP");
818 bitmap_len
= u32at (cur_pos
, 0x30);
827 cur_pos
= locate_attr (at
, mft
, GRUB_NTFS_AT_INDEX_ALLOCATION
);
828 while (cur_pos
!= NULL
)
830 /* Non-resident, Namelen=4, Offset=0x40, Flags=0, Name="$I30" */
831 if ((u32at (cur_pos
, 8) == 0x400401) &&
832 (u32at (cur_pos
, 0x40) == 0x490024) &&
833 (u32at (cur_pos
, 0x44) == 0x300033))
835 cur_pos
= find_attr (at
, GRUB_NTFS_AT_INDEX_ALLOCATION
);
838 if ((!cur_pos
) && (bitmap
))
840 grub_error (GRUB_ERR_BAD_FS
, "$BITMAP without $INDEX_ALLOCATION");
849 indx
= grub_malloc (mft
->data
->idx_size
<< GRUB_NTFS_BLK_SHR
);
854 for (i
= 0; i
< (grub_disk_addr_t
)bitmap_len
* 8; i
++)
859 (at
, indx
, i
* (mft
->data
->idx_size
<< GRUB_NTFS_BLK_SHR
),
860 (mft
->data
->idx_size
<< GRUB_NTFS_BLK_SHR
), 0, 0, 0))
861 || (fixup (indx
, mft
->data
->idx_size
,
862 (const grub_uint8_t
*) "INDX")))
864 ret
= list_file (mft
, &indx
[0x18 + u16at (indx
, 0x18)],
886 static struct grub_ntfs_data
*
887 grub_ntfs_mount (grub_disk_t disk
)
889 struct grub_ntfs_bpb bpb
;
890 struct grub_ntfs_data
*data
= 0;
896 data
= (struct grub_ntfs_data
*) grub_zalloc (sizeof (*data
));
903 if (grub_disk_read (disk
, 0, 0, sizeof (bpb
), &bpb
))
906 if (grub_memcmp ((char *) &bpb
.oem_name
, "NTFS", 4) != 0
907 || bpb
.sectors_per_cluster
== 0
908 || (bpb
.sectors_per_cluster
& (bpb
.sectors_per_cluster
- 1)) != 0
909 || bpb
.bytes_per_sector
== 0
910 || (bpb
.bytes_per_sector
& (bpb
.bytes_per_sector
- 1)) != 0)
913 spc
= (((grub_uint32_t
) bpb
.sectors_per_cluster
914 * (grub_uint32_t
) grub_le_to_cpu16 (bpb
.bytes_per_sector
))
915 >> GRUB_NTFS_BLK_SHR
);
919 for (data
->log_spc
= 0; (1U << data
->log_spc
) < spc
; data
->log_spc
++);
921 if (bpb
.clusters_per_mft
> 0)
922 data
->mft_size
= ((grub_disk_addr_t
) bpb
.clusters_per_mft
) << data
->log_spc
;
923 else if (-bpb
.clusters_per_mft
< GRUB_NTFS_BLK_SHR
|| -bpb
.clusters_per_mft
>= 31)
926 data
->mft_size
= 1ULL << (-bpb
.clusters_per_mft
- GRUB_NTFS_BLK_SHR
);
928 if (bpb
.clusters_per_index
> 0)
929 data
->idx_size
= (((grub_disk_addr_t
) bpb
.clusters_per_index
)
931 else if (-bpb
.clusters_per_index
< GRUB_NTFS_BLK_SHR
|| -bpb
.clusters_per_index
>= 31)
934 data
->idx_size
= 1ULL << (-bpb
.clusters_per_index
- GRUB_NTFS_BLK_SHR
);
936 data
->mft_start
= grub_le_to_cpu64 (bpb
.mft_lcn
) << data
->log_spc
;
938 if ((data
->mft_size
> GRUB_NTFS_MAX_MFT
) || (data
->idx_size
> GRUB_NTFS_MAX_IDX
))
941 data
->mmft
.data
= data
;
942 data
->cmft
.data
= data
;
944 data
->mmft
.buf
= grub_malloc (data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
949 (disk
, data
->mft_start
, 0, data
->mft_size
<< GRUB_NTFS_BLK_SHR
, data
->mmft
.buf
))
952 data
->uuid
= grub_le_to_cpu64 (bpb
.num_serial
);
954 if (fixup (data
->mmft
.buf
, data
->mft_size
, (const grub_uint8_t
*) "FILE"))
957 if (!locate_attr (&data
->mmft
.attr
, &data
->mmft
, GRUB_NTFS_AT_DATA
))
960 if (init_file (&data
->cmft
, GRUB_NTFS_FILE_ROOT
))
966 grub_error (GRUB_ERR_BAD_FS
, "not an ntfs filesystem");
970 free_file (&data
->mmft
);
971 free_file (&data
->cmft
);
977 /* Context for grub_ntfs_dir. */
978 struct grub_ntfs_dir_ctx
980 grub_fs_dir_hook_t hook
;
984 /* Helper for grub_ntfs_dir. */
986 grub_ntfs_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
987 grub_fshelp_node_t node
, void *data
)
989 struct grub_ntfs_dir_ctx
*ctx
= data
;
990 struct grub_dirhook_info info
;
992 grub_memset (&info
, 0, sizeof (info
));
993 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
995 info
.mtime
= grub_divmod64 (node
->mtime
, 10000000, 0)
996 - 86400ULL * 365 * (1970 - 1601)
997 - 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100);
999 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
1003 grub_ntfs_dir (grub_device_t device
, const char *path
,
1004 grub_fs_dir_hook_t hook
, void *hook_data
)
1006 struct grub_ntfs_dir_ctx ctx
= { hook
, hook_data
};
1007 struct grub_ntfs_data
*data
= 0;
1008 struct grub_fshelp_node
*fdiro
= 0;
1010 grub_dl_ref (my_mod
);
1012 data
= grub_ntfs_mount (device
->disk
);
1016 grub_fshelp_find_file (path
, &data
->cmft
, &fdiro
, grub_ntfs_iterate_dir
,
1017 grub_ntfs_read_symlink
, GRUB_FSHELP_DIR
);
1022 grub_ntfs_iterate_dir (fdiro
, grub_ntfs_dir_iter
, &ctx
);
1025 if ((fdiro
) && (fdiro
!= &data
->cmft
))
1032 free_file (&data
->mmft
);
1033 free_file (&data
->cmft
);
1037 grub_dl_unref (my_mod
);
1043 grub_ntfs_open (grub_file_t file
, const char *name
)
1045 struct grub_ntfs_data
*data
= 0;
1046 struct grub_fshelp_node
*mft
= 0;
1048 grub_dl_ref (my_mod
);
1050 data
= grub_ntfs_mount (file
->device
->disk
);
1054 grub_fshelp_find_file (name
, &data
->cmft
, &mft
, grub_ntfs_iterate_dir
,
1055 grub_ntfs_read_symlink
, GRUB_FSHELP_REG
);
1060 if (mft
!= &data
->cmft
)
1062 free_file (&data
->cmft
);
1063 grub_memcpy (&data
->cmft
, mft
, sizeof (*mft
));
1065 if (!data
->cmft
.inode_read
)
1067 if (init_file (&data
->cmft
, data
->cmft
.ino
))
1072 file
->size
= data
->cmft
.size
;
1081 free_file (&data
->mmft
);
1082 free_file (&data
->cmft
);
1086 grub_dl_unref (my_mod
);
1092 grub_ntfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1094 struct grub_ntfs_file
*mft
;
1096 mft
= &((struct grub_ntfs_data
*) file
->data
)->cmft
;
1097 if (file
->read_hook
)
1098 mft
->attr
.save_pos
= 1;
1100 read_attr (&mft
->attr
, (grub_uint8_t
*) buf
, file
->offset
, len
, 1,
1101 file
->read_hook
, file
->read_hook_data
);
1102 return (grub_errno
) ? -1 : (grub_ssize_t
) len
;
1106 grub_ntfs_close (grub_file_t file
)
1108 struct grub_ntfs_data
*data
;
1114 free_file (&data
->mmft
);
1115 free_file (&data
->cmft
);
1119 grub_dl_unref (my_mod
);
1125 grub_ntfs_label (grub_device_t device
, char **label
)
1127 struct grub_ntfs_data
*data
= 0;
1128 struct grub_fshelp_node
*mft
= 0;
1131 grub_dl_ref (my_mod
);
1135 data
= grub_ntfs_mount (device
->disk
);
1139 grub_fshelp_find_file ("/$Volume", &data
->cmft
, &mft
, grub_ntfs_iterate_dir
,
1140 0, GRUB_FSHELP_REG
);
1145 if (!mft
->inode_read
)
1147 mft
->buf
= grub_malloc (mft
->data
->mft_size
<< GRUB_NTFS_BLK_SHR
);
1148 if (mft
->buf
== NULL
)
1151 if (read_mft (mft
->data
, mft
->buf
, mft
->ino
))
1155 init_attr (&mft
->attr
, mft
);
1156 pa
= find_attr (&mft
->attr
, GRUB_NTFS_AT_VOLUME_NAME
);
1157 if ((pa
) && (pa
[8] == 0) && (u32at (pa
, 0x10)))
1161 len
= u32at (pa
, 0x10) / 2;
1162 pa
+= u16at (pa
, 0x14);
1163 *label
= get_utf8 (pa
, len
);
1167 if ((mft
) && (mft
!= &data
->cmft
))
1174 free_file (&data
->mmft
);
1175 free_file (&data
->cmft
);
1179 grub_dl_unref (my_mod
);
1185 grub_ntfs_uuid (grub_device_t device
, char **uuid
)
1187 struct grub_ntfs_data
*data
;
1188 grub_disk_t disk
= device
->disk
;
1190 grub_dl_ref (my_mod
);
1192 data
= grub_ntfs_mount (disk
);
1196 *uuid
= grub_xasprintf ("%016llx", (unsigned long long) data
->uuid
);
1198 for (ptr
= *uuid
; *ptr
; ptr
++)
1199 *ptr
= grub_toupper (*ptr
);
1200 free_file (&data
->mmft
);
1201 free_file (&data
->cmft
);
1207 grub_dl_unref (my_mod
);
1212 static struct grub_fs grub_ntfs_fs
=
1215 .dir
= grub_ntfs_dir
,
1216 .open
= grub_ntfs_open
,
1217 .read
= grub_ntfs_read
,
1218 .close
= grub_ntfs_close
,
1219 .label
= grub_ntfs_label
,
1220 .uuid
= grub_ntfs_uuid
,
1222 .reserved_first_sector
= 1,
1223 .blocklist_install
= 1,
1228 GRUB_MOD_INIT (ntfs
)
1230 grub_fs_register (&grub_ntfs_fs
);
1234 GRUB_MOD_FINI (ntfs
)
1236 grub_fs_unregister (&grub_ntfs_fs
);