source: bootcd/isolinux/syslinux-6.03/core/fs/ntfs/ntfs.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 40.4 KB
Line 
1/*
2 * Copyright (C) 2011-2012 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 02111-1307, 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#include <ctype.h>
33
34#include "codepage.h"
35#include "ntfs.h"
36#include "runlist.h"
37
38static struct ntfs_readdir_state *readdir_state;
39
40/*** Function declarations */
41static f_mft_record_lookup ntfs_mft_record_lookup_3_0;
42static f_mft_record_lookup ntfs_mft_record_lookup_3_1;
43static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec);
44static inline struct ntfs_attr_record * ntfs_attr_lookup(struct fs_info *fs, uint32_t type, struct ntfs_mft_record **mmrec, struct ntfs_mft_record *mrec);
45static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,struct mapping_chunk *chunk,uint32_t *offset);
46static int parse_data_run(const void *stream, uint32_t *offset, uint8_t *attr_len, struct mapping_chunk *chunk);
47
48/*** Function definitions */
49
50/* Check if there are specific zero fields in an NTFS boot sector */
51static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
52{
53    return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
54            !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
55            !sb->zero_3;
56}
57
58static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
59{
60    return ntfs_check_zero_fields(sb) &&
61            (!memcmp(sb->oem_name, "NTFS    ", 8) ||
62             !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
63             !memcmp(sb->oem_name, "MSWIN4.1", 8));
64}
65
66static inline struct inode *new_ntfs_inode(struct fs_info *fs)
67{
68    struct inode *inode;
69
70    inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
71    if (!inode)
72        malloc_error("inode structure");
73
74    return inode;
75}
76
77static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec)
78{
79    uint16_t *usa;
80    uint16_t usa_no;
81    uint16_t usa_count;
82    uint16_t *blk;
83
84    dprintf("in %s()\n", __func__);
85
86    if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX)
87        return;
88
89    /* get the Update Sequence Array offset */
90    usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
91    /* get the Update Sequence Array Number and skip it */
92    usa_no = *usa++;
93    /* get the Update Sequene Array count */
94    usa_count = nrec->usa_count - 1;    /* exclude the USA number */
95    /* make it to point to the last two bytes of the RECORD's first sector */
96    blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
97
98    while (usa_count--) {
99        if (*blk != usa_no)
100            break;
101
102        *blk = *usa++;
103        blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs));
104    }
105}
106
107/* read content from cache */
108static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count,
109                    block_t *blk, uint64_t *blk_offset,
110                    uint64_t *blk_next_offset, uint64_t *lcn)
111{
112    uint8_t *data;
113    uint64_t offset = *blk_offset;
114    const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
115    const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
116    uint64_t bytes;
117    uint64_t lbytes;
118    uint64_t loffset;
119    uint64_t k;
120
121    dprintf("in %s()\n", __func__);
122
123    if (count > len)
124        goto out;
125
126    data = (uint8_t *)get_cache(fs->fs_dev, *blk);
127    if (!data)
128        goto out;
129
130    if (!offset)
131        offset = (*lcn << clust_byte_shift) % blk_size;
132
133    dprintf("LCN:            0x%X\n", *lcn);
134    dprintf("offset:         0x%X\n", offset);
135
136    bytes = count;              /* bytes to copy */
137    lbytes = blk_size - offset; /* bytes left to copy */
138    if (lbytes >= bytes) {
139        /* so there's room enough, then copy the whole content */
140        memcpy(buf, data + offset, bytes);
141        loffset = offset;
142        offset += count;
143    } else {
144        dprintf("bytes:             %u\n", bytes);
145        dprintf("bytes left:        %u\n", lbytes);
146        /* otherwise, let's copy it partially... */
147        k = 0;
148        while (bytes) {
149            memcpy(buf + k, data + offset, lbytes);
150            bytes -= lbytes;
151            loffset = offset;
152            offset += lbytes;
153            k += lbytes;
154            if (offset >= blk_size) {
155                /* then fetch a new FS block */
156                data = (uint8_t *)get_cache(fs->fs_dev, ++*blk);
157                if (!data)
158                    goto out;
159
160                lbytes = bytes;
161                loffset = offset;
162                offset = 0;
163            }
164        }
165    }
166
167    if (loffset >= blk_size)
168        loffset = 0;    /* it must be aligned on a block boundary */
169
170    *blk_offset = loffset;
171
172    if (blk_next_offset)
173        *blk_next_offset = offset;
174
175    *lcn += blk_size / count;   /* update LCN */
176
177    return 0;
178
179out:
180    return -1;
181}
182
183/* AndyAlex: read and validate single MFT record. Keep in mind that MFT itself can be fragmented */
184static struct ntfs_mft_record *ntfs_mft_record_lookup_any(struct fs_info *fs,
185                                                uint32_t file, block_t *out_blk, bool is_v31)
186{
187    const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
188    uint8_t *buf = NULL;
189    const uint32_t mft_record_shift = ilog2(mft_record_size);
190    const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
191    uint64_t next_offset = 0;
192    uint64_t lcn = 0;
193    block_t blk = 0;
194    uint64_t offset = 0;
195
196    struct ntfs_mft_record *mrec = NULL, *lmrec = NULL;
197    uint64_t start_blk = 0;
198    struct ntfs_attr_record *attr = NULL;
199    uint8_t *stream = NULL;
200    uint32_t attr_offset = 0;
201    uint8_t *attr_len = NULL;
202    struct mapping_chunk chunk;
203
204    int err = 0;
205
206    /* determine MFT record's LCN */
207    uint64_t vcn = (file << mft_record_shift >> clust_byte_shift);
208    dprintf("in %s(%s)\n", __func__,(is_v31?"v3.1":"v3.0"));
209    if (0==vcn) {
210      lcn = NTFS_SB(fs)->mft_lcn;
211    } else do {
212      dprintf("%s: looking for VCN %u for MFT record %u\n", __func__,(unsigned)vcn,(unsigned)file);
213      mrec = NTFS_SB(fs)->mft_record_lookup(fs, 0, &start_blk);
214      if (!mrec) {dprintf("%s: read MFT(0) failed\n", __func__); break;}
215      lmrec = mrec;
216      if (get_inode_mode(mrec) != DT_REG) {dprintf("%s: $MFT is not a file\n", __func__); break;}
217      attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
218      if (!attr) {dprintf("%s: $MFT have no data attr\n", __func__); break;}
219      if (!attr->non_resident) {dprintf("%s: $MFT data attr is resident\n", __func__); break;}
220      attr_len = (uint8_t *)attr + attr->len;
221      stream = mapping_chunk_init(attr, &chunk, &attr_offset);
222      while (true) {
223        err = parse_data_run(stream, &attr_offset, attr_len, &chunk);
224        if (err) {dprintf("%s: $MFT data run parse failed with error %d\n", __func__,err); break;}
225        if (chunk.flags & MAP_UNALLOCATED) continue;
226        if (chunk.flags & MAP_END) break;
227        if (chunk.flags & MAP_ALLOCATED) {
228          dprintf("%s: Chunk: VCN=%u, LCN=%u, len=%u\n", __func__,(unsigned)chunk.vcn,(unsigned)chunk.lcn,(unsigned)chunk.len);
229          if ((vcn>=chunk.vcn)&&(vcn<chunk.vcn+chunk.len)) {
230            lcn=vcn-chunk.vcn+chunk.lcn;
231            dprintf("%s: VCN %u for MFT record %u maps to lcn %u\n", __func__,(unsigned)vcn,(unsigned)file,(unsigned)lcn);
232            break;
233          }
234          chunk.vcn += chunk.len;
235        }
236      }
237    } while(false);
238    if (mrec!=NULL) free(mrec);
239    mrec = NULL;
240    if (0==lcn) {
241      dprintf("%s: unable to map VCN %u for MFT record %u\n", __func__,(unsigned)vcn,(unsigned)file);
242      return NULL;
243    }
244
245    /* determine MFT record's block number */
246    blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs));
247    offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
248
249    /* Allocate buffer */
250    buf = (uint8_t *)malloc(mft_record_size);
251    if (!buf) {malloc_error("uint8_t *");return 0;}
252
253    /* Read block */
254    err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &blk,
255                    &offset, &next_offset, &lcn);
256    if (err) {
257      dprintf("%s: error read block %u from cache\n", __func__, blk);
258      printf("Error while reading from cache.\n");
259      free(buf);
260      return NULL;
261    }
262
263    /* Process fixups and make structure pointer */
264    ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
265    mrec = (struct ntfs_mft_record *)buf;
266
267    /* check if it has a valid magic number and record number */
268    if (mrec->magic != NTFS_MAGIC_FILE) mrec = NULL;
269    if (mrec && is_v31) if (mrec->mft_record_no != file) mrec = NULL;
270    if (mrec!=NULL) {
271      if (out_blk) {
272        *out_blk = (file << mft_record_shift >> BLOCK_SHIFT(fs));   /* update record starting block */
273      }
274      return mrec;          /* found MFT record */
275    }
276
277    /* Invalid record */
278    dprintf("%s: MFT record %u is invalid\n", __func__, (unsigned)file);
279    free(buf);
280    return NULL;
281}
282
283static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs,
284                                                uint32_t file, block_t *blk)
285{
286    return ntfs_mft_record_lookup_any(fs,file,blk,false);
287}
288
289static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
290                                                uint32_t file, block_t *blk)
291{
292    return ntfs_mft_record_lookup_any(fs,file,blk,true);
293}
294
295static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)
296{
297    const uint16_t *entry_fn;
298    uint8_t entry_fn_len;
299    unsigned i;
300
301    dprintf("in %s()\n", __func__);
302
303    entry_fn = ie->key.file_name.file_name;
304    entry_fn_len = ie->key.file_name.file_name_len;
305
306    if (strlen(dname) != entry_fn_len)
307        return false;
308
309    /* Do case-sensitive compares for Posix file names */
310    if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
311        for (i = 0; i < entry_fn_len; i++)
312            if (entry_fn[i] != dname[i])
313                return false;
314    } else {
315        for (i = 0; i < entry_fn_len; i++)
316            if (tolower(entry_fn[i]) != tolower(dname[i]))
317                return false;
318    }
319
320    return true;
321}
322
323static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,
324                                        struct mapping_chunk *chunk,
325                                        uint32_t *offset)
326{
327    memset(chunk, 0, sizeof *chunk);
328    *offset = 0U;
329
330    return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
331}
332
333/* Parse data runs.
334 *
335 * return 0 on success or -1 on failure.
336 */
337static int parse_data_run(const void *stream, uint32_t *offset,
338                            uint8_t *attr_len, struct mapping_chunk *chunk)
339{
340    uint8_t *buf;   /* Pointer to the zero-terminated byte stream */
341    uint8_t count;  /* The count byte */
342    uint8_t v, l;   /* v is the number of changed low-order VCN bytes;
343                     * l is the number of changed low-order LCN bytes
344                     */
345    uint8_t *byte;
346    const int byte_shift = 8;
347    int mask;
348    int64_t res;
349
350    (void)attr_len;
351
352    dprintf("in %s()\n", __func__);
353
354    chunk->flags &= ~MAP_MASK;
355
356    buf = (uint8_t *)stream + *offset;
357    if (buf > attr_len || !*buf) {
358        chunk->flags |= MAP_END;    /* we're done */
359        return 0;
360    }
361
362    if (!*offset)
363        chunk->flags |= MAP_START;  /* initial chunk */
364
365    count = *buf;
366    v = count & 0x0F;
367    l = count >> 4;
368
369    if (v > 8 || l > 8) /* more than 8 bytes ? */
370        goto out;
371
372    byte = (uint8_t *)buf + v;
373    count = v;
374
375    res = 0LL;
376    while (count--)
377        res = (res << byte_shift) | *byte--;
378
379    chunk->len = res;   /* get length data */
380
381    byte = (uint8_t *)buf + v + l;
382    count = l;
383
384    mask = 0xFFFFFFFF;
385    res = 0LL;
386    if (*byte & 0x80)
387        res |= (int64_t)mask;   /* sign-extend it */
388
389    while (count--)
390        res = (res << byte_shift) | *byte--;
391
392    chunk->lcn += res;
393    /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
394    if (!chunk->lcn)
395        chunk->flags |= MAP_UNALLOCATED;
396    else
397        chunk->flags |= MAP_ALLOCATED;
398
399    *offset += v + l + 1;
400
401    return 0;
402
403out:
404    return -1;
405}
406
407static struct ntfs_mft_record *
408ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr,
409                      uint32_t type, struct ntfs_mft_record *mrec)
410{
411    uint8_t *attr_len;
412    struct mapping_chunk chunk;
413    uint32_t offset;
414    uint8_t *stream;
415    int err;
416    const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
417    uint8_t buf[blk_size];
418    uint64_t blk_offset;
419    int64_t vcn;
420    int64_t lcn;
421    int64_t last_lcn;
422    block_t blk;
423    struct ntfs_attr_list_entry *attr_entry;
424    uint32_t len = 0;
425    struct ntfs_mft_record *retval;
426    uint64_t start_blk = 0;
427
428    dprintf("in %s()\n", __func__);
429
430    if (attr->non_resident)
431        goto handle_non_resident_attr;
432
433    attr_entry = (struct ntfs_attr_list_entry *)
434        ((uint8_t *)attr + attr->data.resident.value_offset);
435    len = attr->data.resident.value_len;
436    for (; (uint8_t *)attr_entry < (uint8_t *)attr + len;
437         attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry +
438                                                      attr_entry->length)) {
439        dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n",
440                attr_entry->type);
441        if (attr_entry->type == type)
442            goto found; /* We got the attribute! :-) */
443    }
444
445    printf("No attribute found.\n");
446    goto out;
447
448handle_non_resident_attr:
449    attr_len = (uint8_t *)attr + attr->len;
450    stream = mapping_chunk_init(attr, &chunk, &offset);
451    do {
452        err = parse_data_run(stream, &offset, attr_len, &chunk);
453        if (err) {
454            printf("parse_data_run()\n");
455            goto out;
456        }
457
458        if (chunk.flags & MAP_UNALLOCATED)
459            continue;
460        if (chunk.flags & MAP_END)
461            break;
462        if (chunk.flags & MAP_ALLOCATED) {
463            vcn = 0;
464            lcn = chunk.lcn;
465            while (vcn < chunk.len) {
466                blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >>
467                    BLOCK_SHIFT(fs);
468                blk_offset = 0;
469                last_lcn = lcn;
470                lcn += vcn;
471                err = ntfs_read(fs, buf, blk_size, blk_size, &blk,
472                                &blk_offset, NULL, (uint64_t *)&lcn);
473                if (err) {
474                    printf("Error while reading from cache.\n");
475                    goto out;
476                }
477
478                attr_entry = (struct ntfs_attr_list_entry *)&buf;
479                len = attr->data.non_resident.data_size;
480                for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len;
481                     attr_entry = (struct ntfs_attr_list_entry *)
482                         ((uint8_t *)attr_entry + attr_entry->length)) {
483                    dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n",
484                            attr_entry->type);
485                    if (attr_entry->type == type)
486                        goto found; /* We got the attribute! :-) */
487                }
488
489                lcn = last_lcn; /* restore original LCN */
490                /* go to the next VCN */
491                vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
492            }
493        }
494    } while (!(chunk.flags & MAP_END));
495
496    printf("No attribute found.\n");
497
498out:
499    return NULL;
500
501found:
502    /* At this point we have the attribute we were looking for. Now we
503     * will look for the MFT record that stores information about this
504     * attribute.
505     */
506
507    /* Check if the attribute type we're looking for is in the same
508     * MFT record. If so, we do not need to look it up again - return it.
509     */
510    if (mrec->mft_record_no == attr_entry->mft_ref)
511        return mrec;
512
513    retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref,
514                                            &start_blk);
515    if (!retval) {
516        printf("No MFT record found!\n");
517        goto out;
518    }
519
520    /* return the found MFT record */
521    return retval;
522}
523
524static struct ntfs_attr_record *
525__ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
526                   struct ntfs_mft_record **mrec)
527{
528    struct ntfs_mft_record *_mrec = *mrec;
529    struct ntfs_attr_record *attr;
530    struct ntfs_attr_record *attr_list_attr;
531
532    dprintf("in %s()\n", __func__);
533
534    if (!_mrec || type == NTFS_AT_END)
535        goto out;
536
537again:
538    attr_list_attr = NULL;
539
540    attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset);
541    /* walk through the file attribute records */
542    for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
543        if (attr->type == NTFS_AT_END)
544            break;
545
546        if (attr->type == NTFS_AT_ATTR_LIST) {
547            dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
548                    _mrec->mft_record_no);
549            attr_list_attr = attr;
550            continue;
551        }
552
553        if (attr->type == type)
554            break;
555    }
556
557    /* if the record has an $ATTRIBUTE_LIST attribute associated
558     * with it, then we need to look for the wanted attribute in
559     * it as well.
560     */
561    if (attr->type == NTFS_AT_END && attr_list_attr) {
562        struct ntfs_mft_record *retval;
563
564        retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec);
565        if (!retval)
566            goto out;
567
568        _mrec = retval;
569        goto again;
570    } else if (attr->type == NTFS_AT_END && !attr_list_attr) {
571        attr = NULL;
572    }
573
574    return attr;
575
576out:
577    return NULL;
578}
579
580static inline struct ntfs_attr_record *
581ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
582                 struct ntfs_mft_record **mmrec,
583                 struct ntfs_mft_record *mrec)
584{
585    struct ntfs_mft_record *_mrec = mrec;
586    struct ntfs_mft_record *other = *mmrec;
587    struct ntfs_attr_record *retval = NULL;
588
589    if (mrec == other)
590        return __ntfs_attr_lookup(fs, type, &other);
591
592    retval = __ntfs_attr_lookup(fs, type, &_mrec);
593    if (!retval) {
594        _mrec = other;
595        retval = __ntfs_attr_lookup(fs, type, &other);
596        if (!retval)
597            other = _mrec;
598    } else if (retval && (_mrec != mrec)) {
599        other = _mrec;
600    }
601
602    return retval;
603}
604
605static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec)
606{
607    return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
608}
609
610static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
611                            struct inode *inode)
612{
613    uint64_t start_blk = 0;
614    struct ntfs_mft_record *mrec, *lmrec;
615    struct ntfs_attr_record *attr;
616    enum dirent_type d_type;
617    uint8_t *attr_len;
618    struct mapping_chunk chunk;
619    int err;
620    uint8_t *stream;
621    uint32_t offset;
622
623    dprintf("in %s()\n", __func__);
624
625    mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk);
626    if (!mrec) {
627        printf("No MFT record found.\n");
628        goto out;
629    }
630
631    lmrec = mrec;
632
633    NTFS_PVT(inode)->mft_no = mft_no;
634    NTFS_PVT(inode)->seq_no = mrec->seq_no;
635
636    NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift;
637    NTFS_PVT(inode)->here = start_blk;
638
639    d_type = get_inode_mode(mrec);
640    if (d_type == DT_DIR) {    /* directory stuff */
641        dprintf("Got a directory.\n");
642        attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
643        if (!attr) {
644            printf("No attribute found.\n");
645            goto out;
646        }
647
648        /* check if we have a previous allocated state structure */
649        if (readdir_state) {
650            free(readdir_state);
651            readdir_state = NULL;
652        }
653
654        /* allocate our state structure */
655        readdir_state = malloc(sizeof *readdir_state);
656        if (!readdir_state)
657            malloc_error("ntfs_readdir_state structure");
658
659        readdir_state->mft_no = mft_no;
660        /* obviously, the ntfs_readdir() caller will start from INDEX root */
661        readdir_state->in_idx_root = true;
662    } else if (d_type == DT_REG) {        /* file stuff */
663        dprintf("Got a file.\n");
664        attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
665        if (!attr) {
666            printf("No attribute found.\n");
667            goto out;
668        }
669
670        NTFS_PVT(inode)->non_resident = attr->non_resident;
671        NTFS_PVT(inode)->type = attr->type;
672
673        if (!attr->non_resident) {
674            NTFS_PVT(inode)->data.resident.offset =
675                (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
676            inode->size = attr->data.resident.value_len;
677        } else {
678            attr_len = (uint8_t *)attr + attr->len;
679
680            stream = mapping_chunk_init(attr, &chunk, &offset);
681            NTFS_PVT(inode)->data.non_resident.rlist = NULL;
682            for (;;) {
683                err = parse_data_run(stream, &offset, attr_len, &chunk);
684                if (err) {
685                    printf("parse_data_run()\n");
686                    goto out;
687                }
688
689                if (chunk.flags & MAP_UNALLOCATED)
690                    continue;
691                if (chunk.flags & MAP_END)
692                    break;
693                if (chunk.flags &  MAP_ALLOCATED) {
694                    /* append new run to the runlist */
695                    runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
696                                    (struct runlist_element *)&chunk);
697                    /* update for next VCN */
698                    chunk.vcn += chunk.len;
699                }
700            }
701
702            if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
703                printf("No mapping found\n");
704                goto out;
705            }
706
707            inode->size = attr->data.non_resident.initialized_size;
708        }
709    }
710
711    inode->mode = d_type;
712
713    free(mrec);
714
715    return 0;
716
717out:
718    free(mrec);
719
720    return -1;
721}
722
723static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
724{
725    struct fs_info *fs = dir->fs;
726    struct ntfs_mft_record *mrec, *lmrec;
727    block_t blk;
728    uint64_t blk_offset;
729    struct ntfs_attr_record *attr;
730    struct ntfs_idx_root *ir;
731    struct ntfs_idx_entry *ie;
732    const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
733    uint8_t buf[blk_size];
734    struct ntfs_idx_allocation *iblk;
735    int err;
736    uint8_t *stream;
737    uint8_t *attr_len;
738    struct mapping_chunk chunk;
739    uint32_t offset;
740    int64_t vcn;
741    int64_t lcn;
742    int64_t last_lcn;
743    struct inode *inode;
744
745    dprintf("in %s()\n", __func__);
746
747    mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
748    if (!mrec) {
749        printf("No MFT record found.\n");
750        goto out;
751    }
752
753    lmrec = mrec;
754    attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
755    if (!attr) {
756        printf("No attribute found.\n");
757        goto out;
758    }
759
760    ir = (struct ntfs_idx_root *)((uint8_t *)attr +
761                            attr->data.resident.value_offset);
762    ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index +
763                                ir->index.entries_offset);
764    for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
765        /* bounds checks */
766        if ((uint8_t *)ie < (uint8_t *)mrec ||
767            (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) >
768            (uint8_t *)&ir->index + ir->index.index_len ||
769            (uint8_t *)ie + ie->len >
770            (uint8_t *)&ir->index + ir->index.index_len)
771            goto index_err;
772
773        /* last entry cannot contain a key. it can however contain
774         * a pointer to a child node in the B+ tree so we just break out
775         */
776        if (ie->flags & INDEX_ENTRY_END)
777            break;
778
779        if (ntfs_filename_cmp(dname, ie))
780            goto found;
781    }
782
783    /* check for the presence of a child node */
784    if (!(ie->flags & INDEX_ENTRY_NODE)) {
785        printf("No child node, aborting...\n");
786        goto out;
787    }
788
789    /* then descend into child node */
790
791    attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
792    if (!attr) {
793        printf("No attribute found.\n");
794        goto out;
795    }
796
797    if (!attr->non_resident) {
798        printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
799        goto out;
800    }
801
802    attr_len = (uint8_t *)attr + attr->len;
803    stream = mapping_chunk_init(attr, &chunk, &offset);
804    do {
805        err = parse_data_run(stream, &offset, attr_len, &chunk);
806        if (err)
807            break;
808
809        if (chunk.flags & MAP_UNALLOCATED)
810            continue;
811
812        if (chunk.flags & MAP_ALLOCATED) {
813            dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len,
814                    chunk.lcn);
815
816            vcn = 0;
817            lcn = chunk.lcn;
818            while (vcn < chunk.len) {
819                blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift <<
820                    SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
821
822                blk_offset = 0;
823                last_lcn = lcn;
824                lcn += vcn;
825                err = ntfs_read(fs, &buf, blk_size, blk_size, &blk,
826                                &blk_offset, NULL, (uint64_t *)&lcn);
827                if (err) {
828                    printf("Error while reading from cache.\n");
829                    goto not_found;
830                }
831
832                ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
833
834                iblk = (struct ntfs_idx_allocation *)&buf;
835                if (iblk->magic != NTFS_MAGIC_INDX) {
836                    printf("Not a valid INDX record.\n");
837                    goto not_found;
838                }
839
840                ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
841                                            iblk->index.entries_offset);
842                for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie +
843                        ie->len)) {
844                    /* bounds checks */
845                    if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
846                        sizeof(struct ntfs_idx_entry_header) >
847                        (uint8_t *)&iblk->index + iblk->index.index_len ||
848                        (uint8_t *)ie + ie->len >
849                        (uint8_t *)&iblk->index + iblk->index.index_len)
850                        goto index_err;
851
852                    /* last entry cannot contain a key */
853                    if (ie->flags & INDEX_ENTRY_END)
854                        break;
855
856                    if (ntfs_filename_cmp(dname, ie))
857                        goto found;
858                }
859
860                lcn = last_lcn; /* restore the original LCN */
861                /* go to the next VCN */
862                vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
863            }
864        }
865    } while (!(chunk.flags & MAP_END));
866
867not_found:
868    dprintf("Index not found\n");
869
870out:
871    free(mrec);
872
873    return NULL;
874
875found:
876    dprintf("Index found\n");
877    inode = new_ntfs_inode(fs);
878    err = index_inode_setup(fs, ie->data.dir.indexed_file, inode);
879    if (err) {
880        printf("Error in index_inode_setup()\n");
881        free(inode);
882        goto out;
883    }
884
885    free(mrec);
886
887    return inode;
888
889index_err:
890    printf("Corrupt index. Aborting lookup...\n");
891    goto out;
892}
893
894/* Convert an UTF-16LE LFN to OEM LFN */
895static uint8_t ntfs_cvt_filename(char *filename,
896                                const struct ntfs_idx_entry *ie)
897{
898    const uint16_t *entry_fn;
899    uint8_t entry_fn_len;
900    unsigned i;
901
902    entry_fn = ie->key.file_name.file_name;
903    entry_fn_len = ie->key.file_name.file_name_len;
904
905    for (i = 0; i < entry_fn_len; i++)
906        filename[i] = (char)entry_fn[i];
907
908    filename[i] = '\0';
909
910    return entry_fn_len;
911}
912
913static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
914{
915    struct fs_info *fs = inode->fs;
916    struct ntfs_sb_info *sbi = NTFS_SB(fs);
917    sector_t pstart = 0;
918    struct runlist *rlist;
919    struct runlist *ret;
920    const uint32_t sec_size = SECTOR_SIZE(fs);
921    const uint32_t sec_shift = SECTOR_SHIFT(fs);
922
923    dprintf("in %s()\n", __func__);
924
925    if (!NTFS_PVT(inode)->non_resident) {
926        pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
927                sec_shift;
928        inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
929    } else {
930        rlist = NTFS_PVT(inode)->data.non_resident.rlist;
931
932        if (!lstart || lstart >= NTFS_PVT(inode)->here) {
933            if (runlist_is_empty(rlist))
934                goto out;   /* nothing to do ;-) */
935
936            ret = runlist_remove(&rlist);
937
938            NTFS_PVT(inode)->here =
939                ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
940
941            pstart = ret->run.lcn << sbi->clust_shift;
942            inode->next_extent.len =
943                ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
944                sec_shift;
945
946            NTFS_PVT(inode)->data.non_resident.rlist = rlist;
947
948            free(ret);
949            ret = NULL;
950        }
951    }
952
953    inode->next_extent.pstart = pstart;
954
955    return 0;
956
957out:
958    return -1;
959}
960
961static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
962                                bool *have_more)
963{
964    uint8_t non_resident;
965    uint32_t ret;
966    struct fs_info *fs = file->fs;
967    struct inode *inode = file->inode;
968    struct ntfs_mft_record *mrec, *lmrec;
969    struct ntfs_attr_record *attr;
970    char *p;
971
972    dprintf("in %s()\n", __func__);
973
974    non_resident = NTFS_PVT(inode)->non_resident;
975
976    ret = generic_getfssec(file, buf, sectors, have_more);
977    if (!ret)
978        return ret;
979
980    if (!non_resident) {
981        mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
982                                              NULL);
983        if (!mrec) {
984            printf("No MFT record found.\n");
985            goto out;
986        }
987
988        lmrec = mrec;
989        attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
990        if (!attr) {
991            printf("No attribute found.\n");
992            goto out;
993        }
994
995        p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
996
997        /* p now points to the data offset, so let's copy it into buf */
998        memcpy(buf, p, inode->size);
999
1000        ret = inode->size;
1001
1002        free(mrec);
1003    }
1004
1005    return ret;
1006
1007out:
1008    free(mrec);
1009
1010    return 0;
1011}
1012
1013static inline bool is_filename_printable(const char *s)
1014{
1015    return s && (*s != '.' && *s != '$');
1016}
1017
1018static int ntfs_readdir(struct file *file, struct dirent *dirent)
1019{
1020    struct fs_info *fs = file->fs;
1021    struct inode *inode = file->inode;
1022    struct ntfs_mft_record *mrec, *lmrec;
1023    block_t blk;
1024    uint64_t blk_offset;
1025    const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
1026    struct ntfs_attr_record *attr;
1027    struct ntfs_idx_root *ir;
1028    uint32_t count;
1029    int len;
1030    struct ntfs_idx_entry *ie = NULL;
1031    uint8_t buf[BLOCK_SIZE(fs)];
1032    struct ntfs_idx_allocation *iblk;
1033    int err;
1034    uint8_t *stream;
1035    uint8_t *attr_len;
1036    struct mapping_chunk chunk;
1037    uint32_t offset;
1038    int64_t vcn;
1039    int64_t lcn;
1040    char filename[NTFS_MAX_FILE_NAME_LEN + 1];
1041
1042    dprintf("in %s()\n", __func__);
1043
1044    mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
1045    if (!mrec) {
1046        printf("No MFT record found.\n");
1047        goto out;
1048    }
1049
1050    lmrec = mrec;
1051    attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
1052    if (!attr) {
1053        printf("No attribute found.\n");
1054        goto out;
1055    }
1056
1057    ir = (struct ntfs_idx_root *)((uint8_t *)attr +
1058                            attr->data.resident.value_offset);
1059
1060    if (!file->offset && readdir_state->in_idx_root)
1061        file->offset = ir->index.entries_offset;
1062
1063idx_root_next_entry:
1064    if (readdir_state->in_idx_root) {
1065        ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index + file->offset);
1066        if (ie->flags & INDEX_ENTRY_END) {
1067            file->offset = 0;
1068            readdir_state->in_idx_root = false;
1069            readdir_state->idx_blks_count = 1;
1070            readdir_state->entries_count = 0;
1071            readdir_state->last_vcn = 0;
1072            goto descend_into_child_node;
1073        }
1074
1075        file->offset += ie->len;
1076        len = ntfs_cvt_filename(filename, ie);
1077        if (!is_filename_printable(filename))
1078            goto idx_root_next_entry;
1079
1080        goto done;
1081    }
1082
1083descend_into_child_node:
1084    if (!(ie->flags & INDEX_ENTRY_NODE))
1085        goto out;
1086
1087    attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
1088    if (!attr)
1089        goto out;
1090
1091    if (!attr->non_resident) {
1092        printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
1093        goto out;
1094    }
1095
1096    attr_len = (uint8_t *)attr + attr->len;
1097
1098next_run:
1099    stream = mapping_chunk_init(attr, &chunk, &offset);
1100    count = readdir_state->idx_blks_count;
1101    while (count--) {
1102        err = parse_data_run(stream, &offset, attr_len, &chunk);
1103        if (err) {
1104            printf("Error while parsing data runs.\n");
1105            goto out;
1106        }
1107
1108        if (chunk.flags & MAP_UNALLOCATED)
1109            break;
1110        if (chunk.flags & MAP_END)
1111            goto out;
1112    }
1113
1114    if (chunk.flags & MAP_UNALLOCATED) {
1115       readdir_state->idx_blks_count++;
1116       goto next_run;
1117    }
1118
1119next_vcn:
1120    vcn = readdir_state->last_vcn;
1121    if (vcn >= chunk.len) {
1122        readdir_state->last_vcn = 0;
1123        readdir_state->idx_blks_count++;
1124        goto next_run;
1125    }
1126
1127    lcn = chunk.lcn;
1128    blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >>
1129            BLOCK_SHIFT(fs);
1130
1131    blk_offset = 0;
1132    err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL,
1133                    (uint64_t *)&lcn);
1134    if (err) {
1135        printf("Error while reading from cache.\n");
1136        goto not_found;
1137    }
1138
1139    ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
1140
1141    iblk = (struct ntfs_idx_allocation *)&buf;
1142    if (iblk->magic != NTFS_MAGIC_INDX) {
1143        printf("Not a valid INDX record.\n");
1144        goto not_found;
1145    }
1146
1147idx_block_next_entry:
1148    ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
1149                        iblk->index.entries_offset);
1150    count = readdir_state->entries_count;
1151    for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
1152        /* bounds checks */
1153        if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
1154            sizeof(struct ntfs_idx_entry_header) >
1155            (uint8_t *)&iblk->index + iblk->index.index_len ||
1156            (uint8_t *)ie + ie->len >
1157            (uint8_t *)&iblk->index + iblk->index.index_len)
1158            goto index_err;
1159
1160        /* last entry cannot contain a key */
1161        if (ie->flags & INDEX_ENTRY_END) {
1162            /* go to the next VCN */
1163            readdir_state->last_vcn += (blk_size / (1 <<
1164                                NTFS_SB(fs)->clust_byte_shift));
1165            readdir_state->entries_count = 0;
1166            goto next_vcn;
1167        }
1168    }
1169
1170    readdir_state->entries_count++;
1171
1172    /* Need to check if this entry has INDEX_ENTRY_END flag set. If
1173     * so, then it won't contain a indexed_file file, so continue the
1174     * lookup on the next VCN/LCN (if any).
1175     */
1176    if (ie->flags & INDEX_ENTRY_END)
1177        goto next_vcn;
1178
1179    len = ntfs_cvt_filename(filename, ie);
1180    if (!is_filename_printable(filename))
1181        goto idx_block_next_entry;
1182
1183    goto done;
1184
1185out:
1186    readdir_state->in_idx_root = true;
1187
1188    free(mrec);
1189
1190    return -1;
1191
1192done:
1193    dirent->d_ino = ie->data.dir.indexed_file;
1194    dirent->d_off = file->offset;
1195    dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
1196
1197    free(mrec);
1198
1199    mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
1200    if (!mrec) {
1201        printf("No MFT record found.\n");
1202        goto out;
1203    }
1204
1205    dirent->d_type = get_inode_mode(mrec);
1206    memcpy(dirent->d_name, filename, len + 1);
1207
1208    free(mrec);
1209
1210    return 0;
1211
1212not_found:
1213    printf("Index not found\n");
1214    goto out;
1215
1216index_err:
1217    printf("Corrupt index. Aborting lookup...\n");
1218    goto out;
1219}
1220
1221static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
1222{
1223    return ntfs_index_lookup(dname, parent);
1224}
1225
1226static struct inode *ntfs_iget_root(struct fs_info *fs)
1227{
1228    uint64_t start_blk;
1229    struct ntfs_mft_record *mrec, *lmrec;
1230    struct ntfs_attr_record *attr;
1231    struct ntfs_vol_info *vol_info;
1232    struct inode *inode;
1233    int err;
1234
1235    dprintf("in %s()\n", __func__);
1236
1237    /* Fetch the $Volume MFT record */
1238    start_blk = 0;
1239    mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk);
1240    if (!mrec) {
1241        printf("Could not fetch $Volume MFT record!\n");
1242        goto err_mrec;
1243    }
1244
1245    lmrec = mrec;
1246
1247    /* Fetch the volume information attribute */
1248    attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec);
1249    if (!attr) {
1250        printf("Could not find volume info attribute!\n");
1251        goto err_attr;
1252    }
1253
1254    /* Note NTFS version and choose version-dependent functions */
1255    vol_info = (void *)((char *)attr + attr->data.resident.value_offset);
1256    NTFS_SB(fs)->major_ver = vol_info->major_ver;
1257    NTFS_SB(fs)->minor_ver = vol_info->minor_ver;
1258    if (vol_info->major_ver == 3 && vol_info->minor_ver == 0)
1259        NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0;
1260    else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 &&
1261            mrec->mft_record_no == FILE_Volume)
1262        NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1;
1263
1264    /* Free MFT record */
1265    free(mrec);
1266    mrec = NULL;
1267
1268    inode = new_ntfs_inode(fs);
1269    inode->fs = fs;
1270
1271    err = index_inode_setup(fs, FILE_root, inode);
1272    if (err)
1273        goto err_setup;
1274
1275    NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
1276
1277    return inode;
1278
1279err_setup:
1280
1281    free(inode);
1282err_attr:
1283
1284    free(mrec);
1285err_mrec:
1286
1287    return NULL;
1288}
1289
1290/* Initialize the filesystem metadata and return blk size in bits */
1291static int ntfs_fs_init(struct fs_info *fs)
1292{
1293    int read_count;
1294    struct ntfs_bpb ntfs;
1295    struct ntfs_sb_info *sbi;
1296    struct disk *disk = fs->fs_dev->disk;
1297    uint8_t mft_record_shift;
1298
1299    dprintf("in %s()\n", __func__);
1300
1301    read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
1302    if (!read_count)
1303        return -1;
1304
1305    if (!ntfs_check_sb_fields(&ntfs))
1306        return -1;
1307
1308    SECTOR_SHIFT(fs) = disk->sector_shift;
1309
1310    /* Note: ntfs.clust_per_mft_record can be a negative number.
1311     * If negative, it represents a shift count, else it represents
1312     * a multiplier for the cluster size.
1313     */
1314    mft_record_shift = ntfs.clust_per_mft_record < 0 ?
1315                    -ntfs.clust_per_mft_record :
1316                    ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) +
1317                    ilog2(ntfs.clust_per_mft_record);
1318
1319    SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
1320
1321    sbi = malloc(sizeof *sbi);
1322    if (!sbi)
1323        malloc_error("ntfs_sb_info structure");
1324
1325    fs->fs_info = sbi;
1326
1327    sbi->clust_shift            = ilog2(ntfs.sec_per_clust);
1328    sbi->clust_byte_shift       = sbi->clust_shift + SECTOR_SHIFT(fs);
1329    sbi->clust_mask             = ntfs.sec_per_clust - 1;
1330    sbi->clust_size             = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
1331    sbi->mft_record_size        = 1 << mft_record_shift;
1332    sbi->clust_per_idx_record   = ntfs.clust_per_idx_record;
1333
1334    BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift;
1335    BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
1336
1337    sbi->mft_lcn = ntfs.mft_lclust;
1338    sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >>
1339                BLOCK_SHIFT(fs);
1340    /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
1341    sbi->mft_size = mft_record_shift << sbi->clust_shift << 4;
1342
1343    sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
1344    if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
1345        sbi->clusters = 0xFFFFFFFFFFF4ULL;
1346
1347    /*
1348     * Assume NTFS version 3.0 to begin with. If we find that the
1349     * volume is a different version later on, we will adjust at
1350     * that time.
1351     */
1352    sbi->major_ver = 3;
1353    sbi->minor_ver = 0;
1354    sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0;
1355
1356    /* Initialize the cache */
1357    cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
1358
1359    return BLOCK_SHIFT(fs);
1360}
1361
1362const struct fs_ops ntfs_fs_ops = {
1363    .fs_name        = "ntfs",
1364    .fs_flags       = FS_USEMEM | FS_THISIND,
1365    .fs_init        = ntfs_fs_init,
1366    .searchdir      = NULL,
1367    .getfssec       = ntfs_getfssec,
1368    .close_file     = generic_close_file,
1369    .mangle_name    = generic_mangle_name,
1370    .open_config    = generic_open_config,
1371    .readdir        = ntfs_readdir,
1372    .iget_root      = ntfs_iget_root,
1373    .iget           = ntfs_iget,
1374    .next_extent    = ntfs_next_extent,
1375    .fs_uuid        = NULL,
1376};
Note: See TracBrowser for help on using the repository browser.