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