ntfs: and more speed improvements
[profile/ivi/syslinux.git] / core / fs / ntfs / ntfs.c
1 /*
2  * Copyright (c) Paulo Alcantara <pcacjr@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc.,
17  * 59 Temple Place - Suite 330, Boston, MA 02111EXIT_FAILURE307, USA.
18 */
19
20 /* Note: No support for compressed files */
21
22 #include <dprintf.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/dirent.h>
26 #include <cache.h>
27 #include <core.h>
28 #include <disk.h>
29 #include <fs.h>
30 #include <ilog2.h>
31 #include <klibc/compiler.h>
32
33 #include "codepage.h"
34 #include "ntfs.h"
35
36 /* Check if there are specific zero fields in an NTFS boot sector */
37 static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
38 {
39     return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
40             !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
41             !sb->zero_3;
42 }
43
44 static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
45 {
46     return ntfs_check_zero_fields(sb) &&
47             (!memcmp(sb->oem_name, "NTFS    ", 8) ||
48              !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
49              !memcmp(sb->oem_name, "MSWIN4.1", 8));
50 }
51
52 static inline struct inode *new_ntfs_inode(struct fs_info *fs)
53 {
54     struct inode *inode;
55
56     inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
57     if (!inode)
58         malloc_error("inode structure");
59
60     return inode;
61 }
62
63 static inline const void *get_right_block(struct fs_info *fs,
64                                                         block_t block)
65 {
66     return get_cache(fs->fs_dev, NTFS_SB(fs)->mft_block + block);
67 }
68
69 static int fixups_writeback(struct fs_info *fs, NTFS_RECORD *nrec)
70 {
71     uint16_t *usa;
72     uint16_t usa_no;
73     uint16_t usa_count;
74     uint16_t *block;
75
76     if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX) {
77         printf("Not a valid NTFS record\n");
78         goto out;
79     }
80
81     /* get the Update Sequence Array offset */
82     usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
83     /* get the Update Sequence Array Number and skip it */
84     usa_no = *usa++;
85     /* get the Update Sequene Array count */
86     usa_count = nrec->usa_count - 1;    /* exclude the USA number */
87     /* make it to point to the last two bytes of the RECORD's first sector */
88     block = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
89
90     while (usa_count--) {
91         if (*block != usa_no)
92             break;
93
94         *block = *usa++;
95         block = (uint16_t *)((uint8_t *)block + SECTOR_SIZE(fs));
96     }
97
98     return 0;
99
100 out:
101     return -1;
102 }
103
104 static MFT_RECORD *mft_record_lookup(uint32_t file, struct fs_info *fs,
105                                     block_t *block)
106 {
107     uint8_t *data;
108     int64_t offset = 0;
109     int err;
110     MFT_RECORD *mrec;
111
112     goto jump_in;
113
114     for (;;) {
115         err = fixups_writeback(fs, (NTFS_RECORD *)(data + offset));
116         if (err)
117             break;
118
119         mrec = (MFT_RECORD *)(data + offset);
120         if (mrec->mft_record_no == file)
121             return mrec;   /* MFT record found! */
122
123         offset += mrec->bytes_allocated;
124         if (offset >= BLOCK_SIZE(fs)) {
125             ++*block;
126             offset -= BLOCK_SIZE(fs);
127 jump_in:
128             data = (uint8_t *)get_right_block(fs, *block);
129             if (!data)
130                 break;
131         }
132     }
133
134     return NULL;
135 }
136
137 static ATTR_RECORD *attr_lookup(uint32_t type, const MFT_RECORD *mrec)
138 {
139     ATTR_RECORD *attr;
140
141     /* sanity check */
142     if (!mrec || type == NTFS_AT_END)
143         return NULL;
144
145     attr = (ATTR_RECORD *)((uint8_t *)mrec + mrec->attrs_offset);
146     /* walk through the file attribute records */
147     for (;; attr = (ATTR_RECORD *)((uint8_t *)attr + attr->len)) {
148         if (attr->type == NTFS_AT_END)
149             return NULL;
150
151         if (attr->type == type)
152             break;
153     }
154
155     return attr;
156 }
157
158 static bool ntfs_match_longname(const char *str, const uint16_t *match, int len)
159 {
160     unsigned char c = -1;       /* Nonzero: we have not yet seen NULL */
161     uint16_t cp;
162
163     dprintf("Matching: %s\n", str);
164
165     if (strlen(str) != len)
166         goto out;
167
168     while (len) {
169         cp = *match++;
170         len--;
171         if (!cp)
172             break;
173
174         c = *str++;
175         if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c])
176             goto out;
177     }
178
179     if (*str)
180         goto out;
181
182     while (len--)
183         if (*match++ != 0xFFFF)
184             goto out;
185
186     return true;
187
188 out:
189     return false;
190 }
191
192 static inline uint8_t *mapping_chunk_init(ATTR_RECORD *attr,
193                                     struct mapping_chunk *chunk,
194                                     uint32_t *offset)
195 {
196     memset(chunk, 0, sizeof *chunk);
197     *offset = 0U;
198
199     return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
200 }
201
202 /* Parse data runs.
203  *
204  * return 0 on success or -1 on failure.
205  */
206 static int parse_data_run(const void *stream, uint32_t *offset,
207                             uint8_t *attr_len, struct mapping_chunk *chunk)
208 {
209     uint8_t *buf;   /* Pointer to the zero-terminated byte stream */
210     uint8_t count;  /* The count byte */
211     uint8_t v, l;   /* v is the number of changed low-order VCN bytes;
212                      * l is the number of changed low-order LCN bytes
213                      */
214     uint8_t *byte;
215     int byte_shift = 8;
216     int mask;
217     uint8_t val;
218     int64_t res;
219
220     (void)attr_len;
221
222     chunk->flags &= ~MAP_MASK;
223
224     buf = (uint8_t *)stream + *offset;
225     if (buf > attr_len || !*buf) {
226         chunk->flags |= MAP_END;    /* we're done */
227         return 0;
228     }
229
230     if (!*offset)
231         chunk->flags |= MAP_START;  /* initial chunk */
232
233     count = *buf;
234     v = count & 0x0F;
235     l = count >> 4;
236
237     if (v > 8 || l > 8) /* more than 8 bytes ? */
238         goto out;
239
240     byte = (uint8_t *)buf + v;
241     count = v;
242
243     res = 0LL;
244     while (count--) {
245         val = *byte--;
246         mask = val >> (byte_shift - 1);
247         res = (res << byte_shift) | ((val + mask) ^ mask);
248     }
249
250     chunk->len = res;   /* get length data */
251
252     byte = (uint8_t *)buf + v + l;
253     count = l;
254
255     mask = 0xFFFFFFFF;
256     res = 0LL;
257     if (*byte & 0x80)
258         res |= (int64_t)mask;   /* sign-extend it */
259
260     while (count--)
261         res = (res << byte_shift) | *byte--;
262
263     chunk->lcn += res;
264     /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
265     if (!chunk->lcn)
266         chunk->flags |= MAP_UNALLOCATED;
267     else
268         chunk->flags |= MAP_ALLOCATED;
269
270     *offset += v + l + 1;
271
272     return 0;
273
274 out:
275     return -1;
276 }
277
278 static enum dirent_type get_inode_mode(MFT_RECORD *mrec)
279 {
280     ATTR_RECORD *attr;
281     FILE_NAME_ATTR *fn;
282     bool infile = false;
283     uint32_t dir_mask, root_mask, file_mask;
284     uint32_t dir, root, file;
285
286     attr = attr_lookup(NTFS_AT_FILENAME, mrec);
287     if (!attr) {
288         printf("No attribute found.\n");
289         return DT_UNKNOWN;
290     }
291
292     fn = (FILE_NAME_ATTR *)((uint8_t *)attr +
293                                 attr->data.resident.value_offset);
294     dprintf("File attributes:        0x%X\n", fn->file_attrs);
295
296     dir_mask = NTFS_FILE_ATTR_ARCHIVE |
297                 NTFS_FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT;
298     root_mask = NTFS_FILE_ATTR_READONLY | NTFS_FILE_ATTR_HIDDEN |
299                 NTFS_FILE_ATTR_SYSTEM |
300                 NTFS_FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT;
301     file_mask = NTFS_FILE_ATTR_ARCHIVE;
302
303     dir = fn->file_attrs & ~dir_mask;
304     root = fn->file_attrs & ~root_mask;
305     file = fn->file_attrs & ~file_mask;
306
307     dprintf("dir = 0x%X\n", dir);
308     dprintf("root= 0x%X\n", root);
309     dprintf("file = 0x%X\n", file);
310     if (((!dir && root) || (!dir && !root)) && !file)
311         infile = true;
312
313     return infile ? DT_REG : DT_DIR;
314 }
315
316 static int index_inode_setup(struct fs_info *fs, block_t start_block,
317                             unsigned long mft_no, struct inode *inode)
318 {
319     MFT_RECORD *mrec;
320     ATTR_RECORD *attr;
321     enum dirent_type d_type;
322     uint32_t len;
323     INDEX_ROOT *ir;
324     uint32_t clust_size;
325     uint8_t *attr_len;
326     struct mapping_chunk chunk;
327     int err;
328     uint8_t *stream;
329     uint32_t offset;
330
331     mrec = mft_record_lookup(mft_no, fs, &start_block);
332     if (!mrec) {
333         printf("No MFT record found.\n");
334         goto out;
335     }
336
337     NTFS_PVT(inode)->mft_no = mft_no;
338     NTFS_PVT(inode)->seq_no = mrec->seq_no;
339
340     NTFS_PVT(inode)->start_cluster = start_block >> NTFS_SB(fs)->clust_shift;
341     NTFS_PVT(inode)->here = start_block;
342
343     d_type = get_inode_mode(mrec);
344     if (d_type == DT_UNKNOWN) {
345         dprintf("Failed on determining inode's mode\n");
346         goto out;
347     }
348
349     if (d_type == DT_DIR) {    /* directory stuff */
350         dprintf("Got a directory.\n");
351         attr = attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
352         if (!attr) {
353             printf("No attribute found.\n");
354             goto out;
355         }
356
357         /* note: INDEX_ROOT is always resident */
358         ir = (INDEX_ROOT *)((uint8_t *)attr +
359                                     attr->data.resident.value_offset);
360         len = attr->data.resident.value_len;
361         if ((uint8_t *)ir + len > (uint8_t *)mrec +
362                         NTFS_SB(fs)->mft_record_size) {
363             dprintf("Corrupt index\n");
364             goto out;
365         }
366
367         NTFS_PVT(inode)->itype.index.collation_rule = ir->collation_rule;
368         NTFS_PVT(inode)->itype.index.block_size = ir->index_block_size;
369         NTFS_PVT(inode)->itype.index.block_size_shift =
370                             ilog2(NTFS_PVT(inode)->itype.index.block_size);
371
372         /* determine the size of a vcn in the index */
373         clust_size = NTFS_PVT(inode)->itype.index.block_size;
374         if (NTFS_SB(fs)->clust_size <= clust_size) {
375             NTFS_PVT(inode)->itype.index.vcn_size = NTFS_SB(fs)->clust_size;
376             NTFS_PVT(inode)->itype.index.vcn_size_shift =
377                                         NTFS_SB(fs)->clust_shift;
378         } else {
379             NTFS_PVT(inode)->itype.index.vcn_size = BLOCK_SIZE(fs);
380             NTFS_PVT(inode)->itype.index.vcn_size_shift = BLOCK_SHIFT(fs);
381         }
382     } else if (d_type == DT_REG) {        /* file stuff */
383         dprintf("Got a file.\n");
384         attr = attr_lookup(NTFS_AT_DATA, mrec);
385         if (!attr) {
386             printf("No attribute found.\n");
387             goto out;
388         }
389
390         NTFS_PVT(inode)->non_resident = attr->non_resident;
391         NTFS_PVT(inode)->type = attr->type;
392
393         if (!attr->non_resident) {
394             NTFS_PVT(inode)->data.resident.offset =
395                 (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
396             inode->size = attr->data.resident.value_len;
397         } else {
398             attr_len = (uint8_t *)attr + attr->len;
399
400             stream = mapping_chunk_init(attr, &chunk, &offset);
401             for (;;) {
402                 err = parse_data_run(stream, &offset, attr_len, &chunk);
403                 if (err) {
404                     printf("parse_data_run()\n");
405                     goto out;
406                 }
407
408                 if (chunk.flags & MAP_UNALLOCATED)
409                     continue;
410                 if (chunk.flags & (MAP_ALLOCATED | MAP_END))
411                     break;
412             }
413
414             if (chunk.flags & MAP_END) {
415                 dprintf("No mapping found\n");
416                 goto out;
417             }
418
419             NTFS_PVT(inode)->data.non_resident.len = chunk.len;
420             NTFS_PVT(inode)->data.non_resident.lcn = chunk.lcn;
421             inode->size = attr->data.non_resident.initialized_size;
422         }
423     }
424
425     inode->mode = d_type;
426
427     return 0;
428
429 out:
430     return -1;
431 }
432
433 static struct inode *index_lookup(const char *dname, struct inode *dir)
434 {
435     struct fs_info *fs = dir->fs;
436     MFT_RECORD *mrec;
437     block_t block;
438     ATTR_RECORD *attr;
439     INDEX_ROOT *ir;
440     uint32_t len;
441     INDEX_ENTRY *ie;
442     uint8_t *data;
443     INDEX_BLOCK *iblock;
444     int err;
445     uint8_t *stream;
446     uint8_t *attr_len;
447     struct mapping_chunk chunk;
448     uint32_t offset;
449     int64_t vcn;
450     int64_t lcn;
451     struct inode *inode;
452
453     block = NTFS_PVT(dir)->start;
454     dprintf("index_lookup() - mft record number: %d\n", NTFS_PVT(dir)->mft_no);
455     mrec = mft_record_lookup(NTFS_PVT(dir)->mft_no, fs, &block);
456     if (!mrec) {
457         printf("No MFT record found.\n");
458         goto out;
459     }
460
461     attr = attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
462     if (!attr) {
463         printf("No attribute found.\n");
464         goto out;
465     }
466
467     ir = (INDEX_ROOT *)((uint8_t *)attr +
468                             attr->data.resident.value_offset);
469     len = attr->data.resident.value_len;
470     /* sanity check */
471     if ((uint8_t *)ir + len > (uint8_t *)mrec + NTFS_SB(fs)->mft_record_size)
472         goto index_err;
473
474     ie = (INDEX_ENTRY *)((uint8_t *)&ir->index +
475                                 ir->index.entries_offset);
476     for (;; ie = (INDEX_ENTRY *)((uint8_t *)ie + ie->len)) {
477         /* bounds checks */
478         if ((uint8_t *)ie < (uint8_t *)mrec ||
479             (uint8_t *)ie + sizeof(INDEX_ENTRY_HEADER) >
480             (uint8_t *)&ir->index + ir->index.index_len ||
481             (uint8_t *)ie + ie->len >
482             (uint8_t *)&ir->index + ir->index.index_len)
483             goto index_err;
484
485         /* last entry cannot contain a key. it can however contain
486          * a pointer to a child node in the B+ tree so we just break out
487          */
488         dprintf("(0) ie->flags:          0x%X\n", ie->flags);
489         if (ie->flags & INDEX_ENTRY_END)
490             break;
491
492         if (ntfs_match_longname(dname, ie->key.file_name.file_name,
493                                 ie->key.file_name.file_name_len)) {
494             dprintf("Filename matches up!\n");
495             dprintf("MFT record number = %d\n", ie->data.dir.indexed_file);
496             goto found;
497         }
498     }
499
500     /* check for the presence of a child node */
501     if (!(ie->flags & INDEX_ENTRY_NODE)) {
502         printf("No child node, aborting...\n");
503         goto out;
504     }
505
506     /* then descend into child node */
507
508     attr = attr_lookup(NTFS_AT_INDEX_ALLOCATION, mrec);
509     if (!attr) {
510         printf("No attribute found.\n");
511         goto out;
512     }
513
514     if (!attr->non_resident) {
515         printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
516         goto out;
517     }
518
519     attr_len = (uint8_t *)attr + attr->len;
520     stream = mapping_chunk_init(attr, &chunk, &offset);
521     do {
522         err = parse_data_run(stream, &offset, attr_len, &chunk);
523         if (err)
524             break;
525
526         if (chunk.flags & MAP_UNALLOCATED)
527             continue;
528
529         if (chunk.flags & MAP_ALLOCATED) {
530             dprintf("%d cluster(s) starting at 0x%X\n", chunk.len, chunk.lcn);
531
532             vcn = 0;
533             lcn = chunk.lcn;
534             while (vcn < chunk.len) {
535                 block = ((lcn + vcn) << NTFS_SB(fs)->clust_shift) <<
536                         SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
537
538                 data = (uint8_t *)get_cache(fs->fs_dev, block);
539                 if (!data) {
540                     printf("get_cache() returned NULL.\n");
541                     goto not_found;
542                 }
543
544                 err = fixups_writeback(fs, (NTFS_RECORD *)data);
545                 if (err)
546                     goto not_found;
547
548                 iblock = (INDEX_BLOCK *)&data[0];
549                 if (iblock->magic != NTFS_MAGIC_INDX) {
550                     printf("Not a valid INDX record.\n");
551                     goto not_found;
552                 }
553
554                 ie = (INDEX_ENTRY *)((uint8_t *)&iblock->index +
555                                             iblock->index.entries_offset);
556                 for (;; ie = (INDEX_ENTRY *)((uint8_t *)ie + ie->len)) {
557                     /* bounds checks */
558                     if ((uint8_t *)ie < (uint8_t *)iblock || (uint8_t *)ie +
559                         sizeof(INDEX_ENTRY_HEADER) >
560                         (uint8_t *)&iblock->index + iblock->index.index_len ||
561                         (uint8_t *)ie + ie->len >
562                         (uint8_t *)&iblock->index + iblock->index.index_len)
563                         goto index_err;
564
565                     /* last entry cannot contain a key */
566                     if (ie->flags & INDEX_ENTRY_END)
567                         break;
568
569                     if (ntfs_match_longname(dname, ie->key.file_name.file_name,
570                                             ie->key.file_name.file_name_len)) {
571                         dprintf("Filename matches up!\n");
572                         goto found;
573                     }
574                 }
575
576                 vcn++;  /* go to the next VCN */
577             }
578         }
579     } while (!(chunk.flags & MAP_END));
580
581 not_found:
582     dprintf("Index not found\n");
583
584 out:
585     dprintf("%s not found!\n", dname);
586
587     return NULL;
588
589 found:
590     dprintf("Index found\n");
591     inode = new_ntfs_inode(fs);
592     err = index_inode_setup(fs, NTFS_PVT(dir)->here, ie->data.dir.indexed_file,
593                             inode);
594     if (err) {
595         free(inode);
596         goto out;
597     }
598
599     dprintf("%s found!\n", dname);
600
601     return inode;
602
603 index_err:
604     printf("Corrupt index. Aborting lookup...\n");
605     goto out;
606 }
607
608 /*
609  * Convert an UTF-16LE longname to the system codepage; return
610  * the length on success or -1 on failure.
611  */
612 static int ntfs_cvt_longname(char *entry_name, const uint16_t *long_name)
613 {
614     struct unicache {
615         uint16_t utf16;
616         uint8_t cp;
617     };
618     static struct unicache unicache[256];
619     struct unicache *uc;
620     uint16_t cp;
621     unsigned int c;
622     char *p = entry_name;
623
624     do {
625         cp = *long_name++;
626         uc = &unicache[cp % 256];
627
628         if (__likely(uc->utf16 == cp)) {
629             *p++ = uc->cp;
630         } else {
631             for (c = 0; c < 512; c++) {
632                 if (codepage.uni[0][c] == cp) {
633                     uc->utf16 = cp;
634                     *p++ = uc->cp = (uint8_t)c;
635                     goto found;
636                 }
637             }
638
639             return -1;
640             found:
641                 ;
642         }
643     } while (cp);
644
645     return (p - entry_name) - 1;
646 }
647
648 static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
649 {
650     struct fs_info *fs = inode->fs;
651     struct ntfs_sb_info *sbi = NTFS_SB(fs);
652     uint32_t mcluster = lstart >> sbi->clust_shift;
653     uint32_t tcluster;
654     const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift;
655     sector_t pstart;
656     const uint32_t sec_size = SECTOR_SIZE(fs);
657     const uint32_t sec_shift = SECTOR_SHIFT(fs);
658
659     tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift;
660     if (mcluster >= tcluster)
661         goto out;       /* Requested cluster beyond end of file */
662
663     if (!NTFS_PVT(inode)->non_resident) {
664         pstart = sbi->mft_block + NTFS_PVT(inode)->here;
665         pstart <<= BLOCK_SHIFT(fs) >> sec_shift;
666     } else {
667         pstart = NTFS_PVT(inode)->data.non_resident.lcn << sbi->clust_shift;
668     }
669
670     inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
671     inode->next_extent.pstart = pstart;
672
673     return 0;
674
675 out:
676     return -1;
677 }
678
679 static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
680                                 bool *have_more)
681 {
682     uint8_t non_resident;
683     uint32_t ret;
684     struct fs_info *fs = file->fs;
685     struct inode *inode = file->inode;
686     block_t block;
687     MFT_RECORD *mrec;
688     ATTR_RECORD *attr;
689     char *p;
690
691     non_resident = NTFS_PVT(inode)->non_resident;
692
693     ret = generic_getfssec(file, buf, sectors, have_more);
694     if (!ret)
695         return ret;
696
697     if (!non_resident) {
698         block = NTFS_PVT(inode)->here;
699         mrec = mft_record_lookup(NTFS_PVT(inode)->mft_no, fs, &block);
700         if (!mrec) {
701             printf("No MFT record found.\n");
702             goto out;
703         }
704
705         attr = attr_lookup(NTFS_AT_DATA, mrec);
706         if (!attr) {
707             printf("No attribute found.\n");
708             goto out;
709         }
710
711         p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
712
713         /* p now points to the data offset, so let's copy it into buf */
714         memcpy(buf, p, inode->size);
715
716         ret = inode->size;
717     }
718
719     return ret;
720
721 out:
722     return 0;
723 }
724
725 static int ntfs_readdir(struct file *file, struct dirent *dirent)
726 {
727     struct fs_info *fs = file->fs;
728     MFT_RECORD *mrec;
729     struct inode *inode = file->inode;
730     block_t block = 0;
731     ATTR_RECORD *attr;
732     FILE_NAME_ATTR *fn;
733     char filename[NTFS_MAX_FILE_NAME_LEN + 1];
734     int len;
735
736     mrec = mft_record_lookup(NTFS_PVT(inode)->mft_no, fs, &block);
737     if (!mrec) {
738         printf("No MFT record found.\n");
739         goto out;
740     }
741
742     attr = attr_lookup(NTFS_AT_FILENAME, mrec);
743     if (!attr) {
744         printf("No attribute found.\n");
745         goto out;
746     }
747
748     fn = (FILE_NAME_ATTR *)((uint8_t *)attr +
749                             attr->data.resident.value_offset);
750
751     len = ntfs_cvt_longname(filename, fn->file_name);
752     if (len < 0 || len != fn->file_name_len) {
753         printf("Failed on converting UTF-16LE LFN to OEM LFN\n");
754         goto out;
755     }
756
757     dirent->d_ino = NTFS_PVT(inode)->mft_no;
758     dirent->d_off = file->offset++;
759     dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
760     dirent->d_type = get_inode_mode(mrec);
761     memcpy(dirent->d_name, filename, len + 1);
762
763     return 0;
764
765 out:
766     return -1;
767 }
768
769 static struct inode *ntfs_iget(const char *dname, struct inode *parent)
770 {
771     return index_lookup(dname, parent);
772 }
773
774 static struct inode *ntfs_iget_root(struct fs_info *fs)
775 {
776     struct inode *inode = new_ntfs_inode(fs);
777     int err;
778
779     inode->fs = fs;
780
781     err = index_inode_setup(fs, 0, FILE_root, inode);
782     if (err)
783         goto free_out;
784
785     NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
786
787     return inode;
788
789 free_out:
790     free(inode);
791
792     return NULL;
793 }
794
795 /* Initialize the filesystem metadata and return block size in bits */
796 static int ntfs_fs_init(struct fs_info *fs)
797 {
798     struct ntfs_bpb ntfs;
799     struct ntfs_sb_info *sbi;
800     struct disk *disk = fs->fs_dev->disk;
801     uint8_t clust_per_mft_record;
802
803     disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
804
805     /* sanity check */
806     if (!ntfs_check_sb_fields(&ntfs))
807         return -1;
808
809     /* Note: clust_per_mft_record can be a negative number */
810     clust_per_mft_record = ntfs.clust_per_mft_record < 0 ?
811                     -ntfs.clust_per_mft_record : ntfs.clust_per_mft_record;
812
813     SECTOR_SHIFT(fs) = disk->sector_shift;
814
815     /* We need _at least_ 1 KiB to read the whole MFT record */
816     BLOCK_SHIFT(fs) = ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs);
817     if (BLOCK_SHIFT(fs) < clust_per_mft_record)
818         BLOCK_SHIFT(fs) = clust_per_mft_record;
819
820     SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
821     BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
822
823     sbi = malloc(sizeof *sbi);
824     if (!sbi)
825         malloc_error("ntfs_sb_info structure");
826
827     fs->fs_info = sbi;
828
829     sbi->clust_shift        = ilog2(ntfs.sec_per_clust);
830     sbi->clust_byte_shift   = sbi->clust_shift + SECTOR_SHIFT(fs);
831     sbi->clust_mask         = ntfs.sec_per_clust - 1;
832     sbi->clust_size         = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
833     sbi->mft_record_size    = 1 << clust_per_mft_record;
834
835     sbi->mft_block = ntfs.mft_lclust << sbi->clust_shift <<
836                     SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
837     /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
838     sbi->mft_size = (clust_per_mft_record << sbi->clust_shift) << 4;
839
840     sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
841     if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
842         sbi->clusters = 0xFFFFFFFFFFF4ULL;
843
844     /* Initialize the cache */
845     cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
846
847     return BLOCK_SHIFT(fs);
848 }
849
850 const struct fs_ops ntfs_fs_ops = {
851     .fs_name        = "ntfs",
852     .fs_flags       = FS_USEMEM | FS_THISIND,
853     .fs_init        = ntfs_fs_init,
854     .searchdir      = NULL,
855     .getfssec       = ntfs_getfssec,
856     .close_file     = generic_close_file,
857     .mangle_name    = generic_mangle_name,
858     .load_config    = generic_load_config,
859     .readdir        = ntfs_readdir,
860     .iget_root      = ntfs_iget_root,
861     .iget           = ntfs_iget,
862     .next_extent    = ntfs_next_extent,
863 };