ntfs: use a runlist for handling multiple data runs
authorPaulo Alcantara <pcacjr@gmail.com>
Wed, 7 Sep 2011 06:25:03 +0000 (06:25 +0000)
committerPaulo Alcantara <pcacjr@gmail.com>
Sun, 11 Sep 2011 04:09:59 +0000 (04:09 +0000)
NTFS files can contain multiple data runs, so use a runlist for handling
multiple data runs of a single file, and also modify ntfs_next_extent()
in order to support this new feature.

Signed-off-by: Paulo Alcantara <pcacjr@gmail.com>
core/fs/ntfs/ntfs.c
core/fs/ntfs/ntfs.h
core/fs/ntfs/runlist.h [new file with mode: 0644]

index 14c275b..95af54a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Paulo Alcantara <pcacjr@gmail.com>
+ * Copyright (C) Paulo Alcantara <pcacjr@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@
 
 #include "codepage.h"
 #include "ntfs.h"
+#include "runlist.h"
 
 /* Check if there are specific zero fields in an NTFS boot sector */
 static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
@@ -441,6 +442,7 @@ static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
             attr_len = (uint8_t *)attr + attr->len;
 
             stream = mapping_chunk_init(attr, &chunk, &offset);
+            NTFS_PVT(inode)->data.non_resident.rlist = NULL;
             for (;;) {
                 err = parse_data_run(stream, &offset, attr_len, &chunk);
                 if (err) {
@@ -450,17 +452,22 @@ static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
 
                 if (chunk.flags & MAP_UNALLOCATED)
                     continue;
-                if (chunk.flags & (MAP_ALLOCATED | MAP_END))
+                if (chunk.flags & MAP_END)
                     break;
+                if (chunk.flags &  MAP_ALLOCATED) {
+                    /* append new run to the runlist */
+                    runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
+                                    (struct runlist_element *)&chunk);
+                    /* update for next VCN */
+                    chunk.vcn += chunk.len;
+                }
             }
 
-            if (chunk.flags & MAP_END) {
-                dprintf("No mapping found\n");
+            if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
+                printf("No mapping found\n");
                 goto out;
             }
 
-            NTFS_PVT(inode)->data.non_resident.len = chunk.len;
-            NTFS_PVT(inode)->data.non_resident.lcn = chunk.lcn;
             inode->size = attr->data.non_resident.initialized_size;
         }
     }
@@ -689,26 +696,41 @@ static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
 {
     struct fs_info *fs = inode->fs;
     struct ntfs_sb_info *sbi = NTFS_SB(fs);
-    uint32_t mcluster = lstart >> sbi->clust_shift;
-    uint32_t tcluster;
-    const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift;
-    sector_t pstart;
+    sector_t pstart = 0;
+    struct runlist *rlist;
+    struct runlist *ret;
     const uint32_t sec_size = SECTOR_SIZE(fs);
     const uint32_t sec_shift = SECTOR_SHIFT(fs);
 
-    tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift;
-    if (mcluster >= tcluster)
-        goto out;       /* Requested cluster beyond end of file */
-
     if (!NTFS_PVT(inode)->non_resident) {
-        pstart = sbi->mft_blk + NTFS_PVT(inode)->here;
-        pstart <<= BLOCK_SHIFT(fs) >> sec_shift;
+        pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
+                sec_shift;
+        inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
     } else {
-        pstart = NTFS_PVT(inode)->data.non_resident.lcn << sbi->clust_shift;
+        rlist = NTFS_PVT(inode)->data.non_resident.rlist;
+
+        if (!lstart || lstart >= NTFS_PVT(inode)->here) {
+            if (runlist_is_empty(rlist))
+                goto out;   /* nothing to do ;-) */
+
+            ret = runlist_remove(&rlist);
+
+            NTFS_PVT(inode)->here =
+                ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
+
+            pstart = ret->run.lcn << sbi->clust_shift;
+            inode->next_extent.len =
+                ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
+                sec_shift;
+
+            NTFS_PVT(inode)->data.non_resident.rlist = rlist;
+
+            free(ret);
+            ret = NULL;
+        }
     }
 
     inode->next_extent.pstart = pstart;
-    inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
 
     return 0;
 
index 611ae3d..6bd78ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Paulo Alcantara <pcacjr@gmail.com>
+ * Copyright (C) Paulo Alcantara <pcacjr@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -82,8 +82,7 @@ struct ntfs_inode {
             uint32_t offset;    /* Data offset */
         } resident;
         struct {            /* Used only if non_resident is set */
-            uint64_t len;
-            int64_t lcn;        /* Logical Cluster Number offset */
+            struct runlist *rlist;
         } non_resident;
     } data;
     union {
@@ -111,9 +110,10 @@ enum {
 };
 
 struct mapping_chunk {
+    uint64_t vcn;
+    int64_t lcn;
     uint64_t len;
-    int64_t lcn;        /* Logical Cluster Number */
-    uint32_t flags;     /* Specific flags of this chunk */
+    uint32_t flags;
 };
 
 /* System defined attributes (32-bit)
diff --git a/core/fs/ntfs/runlist.h b/core/fs/ntfs/runlist.h
new file mode 100644 (file)
index 0000000..b74c7d8
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _RUNLIST_H_
+#define _RUNLIST_H_
+
+struct runlist_element {
+    uint64_t vcn;
+    int64_t lcn;
+    uint64_t len;
+};
+
+struct runlist {
+    struct runlist_element run;
+    struct runlist *next;
+};
+
+static struct runlist **tail;
+
+static inline bool runlist_is_empty(struct runlist *rlist)
+{
+    return !rlist;
+}
+
+static inline struct runlist *runlist_alloc(void)
+{
+    struct runlist *rlist;
+
+    rlist = malloc(sizeof *rlist);
+    if (!rlist)
+        malloc_error("runlist structure");
+
+    rlist->next = NULL;
+
+    return rlist;
+}
+
+static inline void runlist_append(struct runlist **rlist,
+                                struct runlist_element *elem)
+{
+    struct runlist *n = runlist_alloc();
+
+    n->run = *elem;
+
+    if (runlist_is_empty(*rlist)) {
+        *rlist = n;
+        *tail = n;
+    } else {
+        (*tail)->next = n;
+        *tail = n;
+    }
+}
+
+static inline struct runlist *runlist_remove(struct runlist **rlist)
+{
+    struct runlist *ret;
+
+    if (runlist_is_empty(*rlist))
+        return NULL;
+
+    ret = *rlist;
+    *rlist = ret->next;
+
+    return ret;
+}
+
+#endif /* _RUNLIST_H_ */