• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
38 static struct ntfs_readdir_state *readdir_state;
39 
40 /*** Function declarations */
41 static f_mft_record_lookup ntfs_mft_record_lookup_3_0;
42 static f_mft_record_lookup ntfs_mft_record_lookup_3_1;
43 static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec);
44 static 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);
45 static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,struct mapping_chunk *chunk,uint32_t *offset);
46 static 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 */
ntfs_check_zero_fields(const struct ntfs_bpb * sb)51 static 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 
ntfs_check_sb_fields(const struct ntfs_bpb * sb)58 static 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 
new_ntfs_inode(struct fs_info * fs)66 static 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 
ntfs_fixups_writeback(struct fs_info * fs,struct ntfs_record * nrec)77 static 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 */
ntfs_read(struct fs_info * fs,void * buf,size_t len,uint64_t count,block_t * blk,uint64_t * blk_offset,uint64_t * blk_next_offset,uint64_t * lcn)108 static 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 
179 out:
180     return -1;
181 }
182 
183 /* AndyAlex: read and validate single MFT record. Keep in mind that MFT itself can be fragmented */
ntfs_mft_record_lookup_any(struct fs_info * fs,uint32_t file,block_t * out_blk,bool is_v31)184 static 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 
ntfs_mft_record_lookup_3_0(struct fs_info * fs,uint32_t file,block_t * blk)283 static 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 
ntfs_mft_record_lookup_3_1(struct fs_info * fs,uint32_t file,block_t * blk)289 static 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 
ntfs_filename_cmp(const char * dname,struct ntfs_idx_entry * ie)295 static 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 
mapping_chunk_init(struct ntfs_attr_record * attr,struct mapping_chunk * chunk,uint32_t * offset)323 static 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  */
parse_data_run(const void * stream,uint32_t * offset,uint8_t * attr_len,struct mapping_chunk * chunk)337 static 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 
403 out:
404     return -1;
405 }
406 
407 static struct ntfs_mft_record *
ntfs_attr_list_lookup(struct fs_info * fs,struct ntfs_attr_record * attr,uint32_t type,struct ntfs_mft_record * mrec)408 ntfs_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 
448 handle_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 
498 out:
499     return NULL;
500 
501 found:
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 
524 static struct ntfs_attr_record *
__ntfs_attr_lookup(struct fs_info * fs,uint32_t type,struct ntfs_mft_record ** mrec)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 
537 again:
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 
576 out:
577     return NULL;
578 }
579 
580 static 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)581 ntfs_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 
get_inode_mode(struct ntfs_mft_record * mrec)605 static 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 
index_inode_setup(struct fs_info * fs,unsigned long mft_no,struct inode * inode)610 static 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 
717 out:
718     free(mrec);
719 
720     return -1;
721 }
722 
ntfs_index_lookup(const char * dname,struct inode * dir)723 static 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 
867 not_found:
868     dprintf("Index not found\n");
869 
870 out:
871     free(mrec);
872 
873     return NULL;
874 
875 found:
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 
889 index_err:
890     printf("Corrupt index. Aborting lookup...\n");
891     goto out;
892 }
893 
894 /* Convert an UTF-16LE LFN to OEM LFN */
ntfs_cvt_filename(char * filename,const struct ntfs_idx_entry * ie)895 static 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 
ntfs_next_extent(struct inode * inode,uint32_t lstart)913 static 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 
957 out:
958     return -1;
959 }
960 
ntfs_getfssec(struct file * file,char * buf,int sectors,bool * have_more)961 static 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 
1007 out:
1008     free(mrec);
1009 
1010     return 0;
1011 }
1012 
is_filename_printable(const char * s)1013 static inline bool is_filename_printable(const char *s)
1014 {
1015     return s && (*s != '.' && *s != '$');
1016 }
1017 
ntfs_readdir(struct file * file,struct dirent * dirent)1018 static 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 
1063 idx_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 
1083 descend_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 
1098 next_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 
1119 next_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 
1147 idx_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 
1185 out:
1186     readdir_state->in_idx_root = true;
1187 
1188     free(mrec);
1189 
1190     return -1;
1191 
1192 done:
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 
1212 not_found:
1213     printf("Index not found\n");
1214     goto out;
1215 
1216 index_err:
1217     printf("Corrupt index. Aborting lookup...\n");
1218     goto out;
1219 }
1220 
ntfs_iget(const char * dname,struct inode * parent)1221 static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
1222 {
1223     return ntfs_index_lookup(dname, parent);
1224 }
1225 
ntfs_iget_root(struct fs_info * fs)1226 static 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 
1279 err_setup:
1280 
1281     free(inode);
1282 err_attr:
1283 
1284     free(mrec);
1285 err_mrec:
1286 
1287     return NULL;
1288 }
1289 
1290 /* Initialize the filesystem metadata and return blk size in bits */
ntfs_fs_init(struct fs_info * fs)1291 static 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 
1362 const 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 };
1377