summaryrefslogtreecommitdiff
path: root/core/fs/ntfs/ntfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/fs/ntfs/ntfs.c')
-rw-r--r--core/fs/ntfs/ntfs.c203
1 files changed, 170 insertions, 33 deletions
diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
index a60bf0fb..f64c8c82 100644
--- a/core/fs/ntfs/ntfs.c
+++ b/core/fs/ntfs/ntfs.c
@@ -291,28 +291,6 @@ static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
return NULL;
}
-static struct ntfs_attr_record *ntfs_attr_lookup(uint32_t type,
- const struct ntfs_mft_record *mrec)
-{
- struct ntfs_attr_record *attr;
-
- /* sanity check */
- if (!mrec || type == NTFS_AT_END)
- return NULL;
-
- attr = (struct ntfs_attr_record *)((uint8_t *)mrec + mrec->attrs_offset);
- /* walk through the file attribute records */
- for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
- if (attr->type == NTFS_AT_END)
- return NULL;
-
- if (attr->type == type)
- break;
- }
-
- return attr;
-}
-
static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)
{
const uint16_t *entry_fn;
@@ -425,6 +403,165 @@ out:
return -1;
}
+static const struct ntfs_mft_record *
+ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr,
+ uint32_t type)
+{
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ uint8_t *stream;
+ int err;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint8_t buf[blk_size];
+ uint64_t blk_offset;
+ int64_t vcn;
+ int64_t lcn;
+ int64_t last_lcn;
+ block_t blk;
+ struct ntfs_attr_list_entry *attr_entry;
+ struct ntfs_mft_record *mrec;
+ uint64_t start_blk = 0;
+
+ if (attr->non_resident)
+ goto handle_non_resident_attr;
+
+ attr_entry = (struct ntfs_attr_list_entry *)
+ ((uint8_t *)attr + attr->data.resident.value_offset);
+ for (;; attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry +
+ attr_entry->length)) {
+ if (attr_entry->type == type)
+ goto found;
+ }
+
+ /* You should never reach here! If you did, so you do have a bogus
+ * NTFS drive. Fix it.
+ */
+ goto fatal_error;
+
+handle_non_resident_attr:
+ attr_len = (uint8_t *)attr + attr->len;
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ do {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("parse_data_run()\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+ if (chunk.flags & MAP_END)
+ break;
+ if (chunk.flags & MAP_ALLOCATED) {
+ vcn = 0;
+ lcn = chunk.lcn;
+ while (vcn < chunk.len) {
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >>
+ BLOCK_SHIFT(fs);
+ blk_offset = 0;
+ last_lcn = lcn;
+ lcn += vcn;
+ err = ntfs_read(fs, buf, blk_size, blk_size, &blk,
+ &blk_offset, NULL, (uint64_t *)&lcn);
+ if (err) {
+ printf("Error on reading from cache.\n");
+ goto out;
+ }
+
+ attr_entry = (struct ntfs_attr_list_entry *)&buf;
+ for (;; attr_entry = (struct ntfs_attr_list_entry *)
+ ((uint8_t *)attr_entry + attr_entry->length)) {
+ if (attr_entry->type == type)
+ goto found; /* We got the attribute! :-) */
+ }
+
+ lcn = last_lcn; /* restore original LCN */
+ /* go to the next VCN */
+ vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
+ }
+ }
+ } while (!(chunk.flags & MAP_END));
+
+ /* You should never reach here! If you did, so you do have a bogus
+ * NTFS drive. Fix it.
+ */
+ goto fatal_error;
+
+out:
+ return NULL;
+
+found:
+ /* At this point we have the attribute we were looking for. Now we
+ * will look for the MFT record that stores information about this
+ * attribute.
+ */
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref, &start_blk);
+ if (!mrec) {
+ printf("No MFT record found!\n");
+ goto out;
+ }
+
+ /* return the found MFT record */
+ return mrec;
+
+fatal_error:
+ printf("(FATAL) You have a bogus NTFS drive installed in your system. "
+ "Fix it!\n");
+ goto out;
+}
+
+static struct ntfs_attr_record *
+ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
+ const struct ntfs_mft_record *mrec)
+{
+ struct ntfs_attr_record *attr;
+ struct ntfs_attr_record *attr_list_attr;
+
+ /* sanity check */
+ if (!mrec || type == NTFS_AT_END)
+ goto out;
+
+again:
+ attr_list_attr = NULL;
+
+ attr = (struct ntfs_attr_record *)((uint8_t *)mrec + mrec->attrs_offset);
+ /* walk through the file attribute records */
+ for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
+ if (attr->type == NTFS_AT_END)
+ break;
+
+ if (attr->type == NTFS_AT_ATTR_LIST) {
+ dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
+ mrec->mft_record_no);
+ attr_list_attr = attr;
+ continue;
+ }
+
+ if (attr->type == type)
+ break;
+ }
+
+ /* if the record has an $ATTRIBUTE_LIST attribute associated
+ * with it, then we need to look for the wanted attribute in
+ * it as well.
+ */
+ if (attr->type == NTFS_AT_END && attr_list_attr) {
+ mrec = ntfs_attr_list_lookup(fs, attr_list_attr, type);
+ if (!mrec)
+ goto out;
+
+ goto again;
+ } else if (attr->type == NTFS_AT_END && !attr_list_attr) {
+ attr = NULL;
+ }
+
+ return attr;
+
+out:
+ return NULL;
+}
+
static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec)
{
return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
@@ -460,7 +597,7 @@ static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
d_type = get_inode_mode(mrec);
if (d_type == DT_DIR) { /* directory stuff */
dprintf("Got a directory.\n");
- attr = ntfs_attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, mrec);
if (!attr) {
printf("No attribute found.\n");
goto out;
@@ -492,7 +629,7 @@ static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
readdir_state->in_idx_root = true;
} else if (d_type == DT_REG) { /* file stuff */
dprintf("Got a file.\n");
- attr = ntfs_attr_lookup(NTFS_AT_DATA, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, mrec);
if (!attr) {
printf("No attribute found.\n");
goto out;
@@ -580,7 +717,7 @@ static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
goto out;
}
- attr = ntfs_attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, mrec);
if (!attr) {
printf("No attribute found.\n");
goto out;
@@ -622,7 +759,7 @@ static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
/* then descend into child node */
- attr = ntfs_attr_lookup(NTFS_AT_INDEX_ALLOCATION, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, mrec);
if (!attr) {
printf("No attribute found.\n");
goto out;
@@ -813,13 +950,13 @@ static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
if (!non_resident) {
mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
- NULL);
+ NULL);
if (!mrec) {
printf("No MFT record found.\n");
goto out;
}
- attr = ntfs_attr_lookup(NTFS_AT_DATA, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, mrec);
if (!attr) {
printf("No attribute found.\n");
goto out;
@@ -878,7 +1015,7 @@ static int ntfs_readdir(struct file *file, struct dirent *dirent)
goto out;
}
- attr = ntfs_attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, mrec);
if (!attr) {
printf("No attribute found.\n");
goto out;
@@ -920,7 +1057,7 @@ descend_into_child_node:
if (!(ie->flags & INDEX_ENTRY_NODE))
goto out;
- attr = ntfs_attr_lookup(NTFS_AT_INDEX_ALLOCATION, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, mrec);
if (!attr)
goto out;
@@ -1046,7 +1183,7 @@ index_err:
goto out;
}
-static struct inode *ntfs_iget(const char *dname, struct inode *parent)
+static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
{
return ntfs_index_lookup(dname, parent);
}
@@ -1069,7 +1206,7 @@ static struct inode *ntfs_iget_root(struct fs_info *fs)
}
/* Fetch the volume information attribute */
- attr = ntfs_attr_lookup(NTFS_AT_VOL_INFO, mrec);
+ attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, mrec);
if (!attr) {
printf("Could not find volume info attribute!\n");
goto err_attr;
@@ -1122,7 +1259,7 @@ static int ntfs_fs_init(struct fs_info *fs)
read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
if (!read_count)
- return -1;
+ return -1;
/* sanity check */
if (!ntfs_check_sb_fields(&ntfs))