fat: use generic_getfssec(), fix generic_getfssec(), add dprintf
authorH. Peter Anvin <hpa@zytor.com>
Sat, 27 Feb 2010 01:15:35 +0000 (17:15 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Sat, 27 Feb 2010 01:15:35 +0000 (17:15 -0800)
Use generic_getfssec() for the FAT filesystem.  Do a bunch of
calculations based on clusters rather than on sectors, so we don't
have to do the same thing N times for N sectors per cluster.

Fix boundary conditions in generic_getfssec().  Adjust iso9660 to
match the resulting interface change.

Add dprintf's to generic_getfssec() and a few other routines.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
core/Makefile
core/fs/fat/fat.c
core/fs/fat/fat_fs.h
core/fs/fs.c
core/fs/getfssec.c
core/fs/iso9660/iso9660.c
core/include/fs.h

index a5d0fdd..b496433 100644 (file)
@@ -58,7 +58,7 @@ NASMOPT  += $(NASMDEBUG)
 
 PREPCORE = ../lzo/prepcore
 
-# CFLAGS       += -DDEBUG=1
+CFLAGS += -DDEBUG=1
 
 # The DATE is set on the make command line when building binaries for
 # official release.  Otherwise, substitute a hex string that is pretty much
index 03c6f05..3ea253d 100644 (file)
@@ -29,7 +29,7 @@ static const void *get_fat_sector(struct fs_info *fs, sector_t sector)
 
 static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
 {
-    uint32_t next_cluster;
+    uint32_t next_cluster = 0;
     sector_t fat_sector;
     uint32_t offset;
     int lo, hi;
@@ -84,6 +84,70 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
     return next_cluster;
 }
 
+static void fat_next_extent(struct inode *inode)
+{
+    struct fs_info *fs = inode->fs;
+    struct fat_sb_info *sbi = FAT_SB(fs);
+    uint32_t mcluster = inode->next_extent.lstart >> sbi->clust_shift;
+    uint32_t lcluster;
+    uint32_t pcluster;
+    uint32_t tcluster;
+    uint32_t cluster_size = UINT32_C(1) << sbi->clust_shift;
+    sector_t data_area = sbi->data;
+
+    tcluster = (inode->size + cluster_size - 1) >> sbi->clust_shift;
+    if (mcluster >= tcluster)
+       goto err;               /* Requested cluster beyond end of file */
+
+    if (inode->prev_extent.len) {
+       if (inode->prev_extent.pstart < data_area)
+           goto err;           /* Root directory has only one extent */
+       lcluster = (inode->prev_extent.lstart + inode->prev_extent.len)
+           >> sbi->clust_shift;
+       pcluster = ((inode->prev_extent.pstart + inode->prev_extent.len
+                    - data_area) >> sbi->clust_shift) + 2;
+
+       if (lcluster > mcluster) {
+           lcluster = 0;
+           pcluster = PVT(inode)->start_cluster;
+       }
+    } else {
+       lcluster = 0;
+       pcluster = PVT(inode)->start_cluster;
+    }
+
+    for (;;) {
+       if (pcluster-2 >= sbi->clusters) {
+           inode->size = lcluster << sbi->clust_shift;
+           goto err;
+       }
+
+       if (lcluster >= mcluster)
+           break;
+
+       lcluster++;
+       pcluster = get_next_cluster(fs, pcluster);
+    }
+
+    inode->next_extent.pstart =
+       ((sector_t)(pcluster-2) << sbi->clust_shift) + data_area;
+    inode->next_extent.len = cluster_size;
+    lcluster++;
+
+    while (lcluster < tcluster) {
+       uint32_t xcluster;
+       xcluster = get_next_cluster(fs, pcluster);
+       if (xcluster != pcluster+1)
+           break;              /* Not contiguous */
+       pcluster = xcluster;
+       inode->next_extent.len += cluster_size;
+       lcluster++;
+    }
+
+    /* In the case of error, the caller has already set next_extent.len = 0 */
+err:
+    return;
+}
 
 static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
 {
@@ -165,86 +229,6 @@ static sector_t next_sector(struct file *file)
     return sector;
 }
 
-/**
- * __getfssec:
- *
- * get multiple sectors from a file
- *
- * This routine makes sure the subransfers do not cross a 64K boundary
- * and will correct the situation if it does, UNLESS *sectos* cross
- * 64K boundaries.
- *
- */
-static void __getfssec(struct fs_info *fs, char *buf,
-                       struct file *file, uint32_t sectors)
-{
-    sector_t curr_sector = get_the_right_sector(file);
-    sector_t frag_start , next_sector;
-    uint32_t con_sec_cnt;
-    struct disk *disk = fs->fs_dev->disk;
-
-    while (sectors) {
-        /* get fragment */
-        con_sec_cnt = 0;
-        frag_start = curr_sector;
-
-        do {
-            /* get consective sector  count */
-            con_sec_cnt++;
-            sectors--;
-            next_sector = get_next_sector(fs, curr_sector);
-           curr_sector++;
-        } while (sectors && next_sector == curr_sector);
-
-       PVT(file->inode)->offset += con_sec_cnt;
-       PVT(file->inode)->here    = next_sector;
-
-        /* do read */
-        disk->rdwr_sectors(disk, buf, frag_start, con_sec_cnt, 0);
-        buf += con_sec_cnt << SECTOR_SHIFT(fs);/* adjust buffer pointer */
-
-        curr_sector = next_sector;
-    }
-}
-
-
-
-/**
- * get multiple sectors from a file
- *
- * @param: buf, the buffer to store the read data
- * @param: file, the file structure pointer
- * @param: sectors, number of sectors wanna read
- * @param: have_more, set one if has more
- *
- * @return: number of bytes read
- *
- */
-static uint32_t vfat_getfssec(struct file *file, char *buf, int sectors,
-                             bool *have_more)
-{
-    struct fs_info *fs = file->fs;
-    uint32_t bytes_left = file->inode->size - file->offset;
-    uint32_t bytes_read = sectors << fs->sector_shift;
-    int sector_left;
-
-    sector_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> fs->sector_shift;
-    if (sectors > sector_left)
-        sectors = sector_left;
-
-    __getfssec(fs, buf, file, sectors);
-
-    if (bytes_read >= bytes_left) {
-        bytes_read = bytes_left;
-        *have_more = 0;
-    } else {
-        *have_more = 1;
-    }
-    file->offset += bytes_read;
-
-    return bytes_read;
-}
-
 /*
  * Mangle a filename pointed to by src into a buffer pointed to by dst;
  * ends on encountering any whitespace.
@@ -555,6 +539,8 @@ static struct inode *vfat_find_entry(const char *dname, struct inode *dir)
 found:
     inode = new_fat_inode(fs);
     inode->size = de->file_size;
+    PVT(inode)->start_cluster = 
+       (de->first_cluster_high << 16) + de->first_cluster_low;
     PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
     inode->mode = get_inode_mode(de->attr);
 
@@ -570,6 +556,7 @@ static struct inode *vfat_iget_root(struct fs_info *fs)
      * For FAT32, the only way to get the root directory size is to
      * follow the entire FAT chain to the end... which seems pointless.
      */
+    PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
     inode->size = root_size ? root_size << fs->sector_shift : ~0;
     PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
     inode->mode = I_DIR;
@@ -835,7 +822,7 @@ const struct fs_ops vfat_fs_ops = {
     .fs_flags      = FS_USEMEM | FS_THISIND,
     .fs_init       = vfat_fs_init,
     .searchdir     = NULL,
-    .getfssec      = vfat_getfssec,
+    .getfssec      = generic_getfssec,
     .close_file    = generic_close_file,
     .mangle_name   = vfat_mangle_name,
     .unmangle_name = generic_unmangle_name,
@@ -843,4 +830,5 @@ const struct fs_ops vfat_fs_ops = {
     .readdir       = vfat_readdir,
     .iget_root     = vfat_iget_root,
     .iget          = vfat_iget,
+    .next_extent   = fat_next_extent,
 };
index 60b5aee..7ea3db8 100644 (file)
@@ -89,11 +89,12 @@ struct fat_sb_info {
        sector_t data;            /* The data region */
 
        uint32_t clusters;        /* Total number of clusters */
-       int      root_size;       /* The root dir size in sectores */
+       uint32_t root_cluster;    /* Cluster number for (FAT32) root dir */
+       int      root_size;       /* The root dir size in sectors */
        
        int      clust_shift;      /* based on sectors */
        int      clust_byte_shift; /* based on bytes   */
-       int      clust_mask;
+       int      clust_mask;       /* sectors per cluster mask */
        int      clust_size;
 
        int      fat_type;
@@ -146,6 +147,7 @@ static inline int root_dir_size(struct fs_info *fs, struct fat_bpb *fat)
  * FAT private inode information
  */
 struct fat_pvt_inode {
+    uint32_t start_cluster;    /* Starting cluster address */
     sector_t start;            /* Starting sector */
     sector_t offset;           /* Current sector offset */
     sector_t here;             /* Sector corresponding to offset */
index fc7948d..94ee0c9 100644 (file)
@@ -198,7 +198,6 @@ int searchdir(const char *name)
            goto err;
     }
 
-
     /* else, try the generic-path-lookup method */
 
     parent = get_inode(this_fs->cwd);
@@ -297,6 +296,9 @@ int searchdir(const char *name)
     file->offset = 0;
     file->file_len  = inode->size;
 
+    dprintf("File %s -> %p (inode %p) len %u\n", name, file,
+           inode, inode->size);
+
     return file_to_handle(file);
 
 err:
index e8ae62f..80923c1 100644 (file)
@@ -29,9 +29,9 @@
  * getfssec.c
  *
  * Generic getfssec implementation for disk-based filesystems, which
- * support the populate_next_extent method.
+ * support the next_extent() method.
  *
- * The expected semantics of populate_next_extent are as follows:
+ * The expected semantics of next_extent are as follows:
  *
  * inode->next_extent.lstart will contain the initial sector number to
  * be mapped.  The routine is expected to populate inode->next_extent.pstart
  *
  * If the filesystem can map the entire file as a single extent
  * (e.g. iso9660), then the filesystem can simply insert the extent
- * information into inode->prev_extent at searchdir/iget time, and leave
- * populate_next_extent as NULL.
+ * information into inode->next_extent at searchdir/iget time, and leave
+ * next_extent() as NULL.
  *
  * Note: the filesystem driver is not required to do extent coalescing,
  * if that is difficult to do; this routine will perform extent lookahead
  * and coalescing.
  */
 
+#include <dprintf.h>
 #include <minmax.h>
 #include "fs.h"
 
@@ -65,6 +66,28 @@ static inline sector_t next_pstart(const struct extent *e)
     return next_psector(e->pstart, e->len);
 }
 
+
+static void get_next_extent(struct inode *inode)
+{
+    /* The logical start address that we care about... */
+
+    inode->next_extent.lstart =
+       inode->this_extent.lstart + inode->this_extent.len;
+    dprintf("next_extent.lstart = %u\n", inode->next_extent.lstart);
+
+    /* Whatever we had before... */
+    inode->prev_extent = inode->next_extent;
+
+    /* Dummy information to make failure returns easier */
+    inode->next_extent.len = 0;
+    
+    inode->fs->fs_ops->next_extent(inode);
+
+    dprintf("Extent: inode %p @ %u start %llu len %u\n",
+           inode, inode->next_extent.lstart,
+           inode->next_extent.pstart, inode->next_extent.len);
+}
+
 uint32_t generic_getfssec(struct file *file, char *buf,
                          int sectors, bool *have_more)
 {
@@ -80,19 +103,23 @@ uint32_t generic_getfssec(struct file *file, char *buf,
     if (sectors > sectors_left)
        sectors = sectors_left;
 
+    if (!sectors)
+       return 0;
+
     lsector = file->offset >> SECTOR_SHIFT(fs);
+    dprintf("Offset: %u  lsector: %u\n", file->offset, lsector);
 
     if (lsector < inode->this_extent.lstart ||
        lsector >= inode->this_extent.lstart + inode->this_extent.len) {
-       /* inode->this_extent unusable, maybe prev_extent is... */
-       inode->this_extent = inode->prev_extent;
+       /* inode->this_extent unusable, maybe next_extent is... */
+       inode->this_extent = inode->next_extent;
     }
 
     if (lsector < inode->this_extent.lstart ||
        lsector >= inode->this_extent.lstart + inode->this_extent.len) {
        /* Still nothing useful... */
        inode->this_extent.lstart = lsector;
-       inode->this_extent.len    = 0;
+       inode->this_extent.len = 0;
     } else {
        /* We have some usable information */
        uint32_t delta = lsector - inode->this_extent.lstart;
@@ -102,22 +129,30 @@ uint32_t generic_getfssec(struct file *file, char *buf,
            = next_psector(inode->this_extent.pstart, delta);
     }
 
+    dprintf("this_extent: lstart %u pstart %llu len %u\n",
+           inode->this_extent.lstart,
+           inode->this_extent.pstart,
+           inode->this_extent.len);
+
+    if (inode->this_extent.len < sectors &&
+       (!inode->next_extent.len ||
+        inode->next_extent.lstart !=
+        inode->this_extent.lstart + inode->this_extent.len)) {
+       get_next_extent(inode);
+    }
+
     while (sectors) {
        uint32_t chunk;
        size_t len;
 
-       if (!inode->this_extent.len && inode->next_extent.len &&
-           inode->this_extent.lstart == inode->next_extent.lstart)
+       if (!inode->this_extent.len) {
            inode->this_extent = inode->next_extent;
+           if (!inode->next_extent.len)
+               break;          /* Can't read anything more */
+       }
 
        while (sectors > inode->this_extent.len) {
-           /* Whatever we had before... */
-           inode->prev_extent = inode->next_extent;
-
-           /* Dummy information to make failure returns easier */
-           inode->next_extent.len = 0;
-
-           fs->fs_ops->populate_next_extent(inode);
+           get_next_extent(inode);
 
            if (inode->next_extent.len &&
                inode->next_extent.pstart == next_pstart(&inode->this_extent)) {
@@ -129,9 +164,18 @@ uint32_t generic_getfssec(struct file *file, char *buf,
            }
        }
 
+       dprintf("this_extent: lstart %u pstart %llu len %u\n",
+               inode->this_extent.lstart,
+               inode->this_extent.pstart,
+               inode->this_extent.len);
+
        chunk = min(sectors, inode->this_extent.len);
        len = chunk << SECTOR_SHIFT(fs);
 
+       dprintf("   I/O: inode %p @ %u start %llu len %u\n",
+               inode, inode->this_extent.lstart,
+               inode->this_extent.pstart, chunk);
+
        if (inode->this_extent.pstart == EXTENT_ZERO) {
            memset(buf, 0, len);
        } else {
index 995cd21..41c27ec 100644 (file)
@@ -209,8 +209,8 @@ static struct inode *iso_get_inode(struct fs_info *fs,
     inode->blocks = (inode->size + BLOCK_SIZE(fs) - 1) >> BLOCK_SHIFT(fs);
 
     /* We have a single extent for all data */
-    inode->prev_extent.pstart = de->extent_le << blktosec;
-    inode->prev_extent.len = inode->blocks << blktosec;
+    inode->next_extent.pstart = de->extent_le << blktosec;
+    inode->next_extent.len = inode->blocks << blktosec;
 
     return inode;
 }
index 1870de7..79499d8 100644 (file)
@@ -70,7 +70,7 @@ struct fs_ops {
     /* the _dir_ stuff */
     struct dirent * (*readdir)(struct file *);
 
-    void     (*populate_next_extent)(struct inode *);
+    void     (*next_extent)(struct inode *);
 };
 
 enum inode_mode {I_FILE, I_DIR, I_SYMLINK};