vfs: add the readdir stuff support
authorLiu Aleaxander <Aleaxander@gmail.com>
Tue, 15 Dec 2009 06:00:38 +0000 (14:00 +0800)
committerLiu Aleaxander <Aleaxander@gmail.com>
Tue, 15 Dec 2009 06:00:38 +0000 (14:00 +0800)
For now, the EXT, VFAT and ISO fs support readdir lib. So, the dir.c32
module can do the right work.

Signed-off-by: Liu Aleaxander <Aleaxander@gmail.com>
core/comboot.inc
core/dir.c
core/fs/ext2/bmap.c
core/fs/ext2/ext2.c
core/fs/fat/fat.c
core/fs/iso9660/iso9660.c
core/include/fs.h

index 72f642e..2540959 100644 (file)
@@ -899,7 +899,10 @@ comapi_getcwd:
 ;
 ; INT 22h AX=0020h     Open directory
 ;
-%if IS_SYSLINUX
+%if IS_PXELINUX
+comapi_opendir equ comapi_err
+
+%else
 comapi_opendir:
                mov es,P_ES
                mov si,P_SI
@@ -911,21 +914,20 @@ comapi_opendir:
                 mov P_EAX,eax
                clc
                ret
-%else
-comapi_opendir equ comapi_err
 %endif
 
 ;
 ; INT 22h AX=0021h     Read directory
 ;
-%if IS_SYSLINUX
+%if IS_PXELINUX
+comapi_readdir equ comapi_err
+
+%else
 comapi_readdir:
                mov esi,P_ESI       ; The address of DIR structure
                pm_call readdir
                mov P_EAX,eax       ; The address of newly read dirent structure
                ret
-%else
-comapi_readdir equ comapi_err
 %endif
 
 ;
index 42400c9..37d92ed 100644 (file)
@@ -10,8 +10,11 @@ extern struct fs_info *this_fs;
  * open dir, return the file structure pointer in _eax_, or NULL if failed 
  */
 void opendir(com32sys_t *regs)
-{      
-    this_fs->fs_ops->opendir(regs);
+{
+    char *src = MK_PTR(regs->es, regs->esi.w[0]);
+    char *dst = MK_PTR(regs->ds, regs->edi.w[0]);
+    strcpy(dst, src);
+    searchdir(regs);
     regs->eax.l = (uint32_t)handle_to_file(regs->esi.w[0]);    
 }
 
index 1ef3008..e38bcd2 100644 (file)
@@ -181,10 +181,5 @@ block_t bmap(struct fs_info *fs, struct inode * inode, int block)
     else
        ret = bmap_traditional(fs, inode, block);
     
-    if (!ret) {
-        printf("ERROR: something error happend at linsector..\n");
-        return 0;
-    }
-
     return ret;
 }
index b8ca5c3..536a7f6 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <string.h>
+#include <sys/dirent.h>
 #include <cache.h>
 #include <core.h>
 #include <disk.h>
@@ -352,6 +353,39 @@ static char * ext2_follow_symlink(struct inode *inode, const char *name_left)
     return p;
 }
 
+/*
+ * Read one directory entry at a time 
+ */
+static struct dirent * ext2_readdir(struct file *file)
+{
+    struct fs_info *fs = file->fs;
+    struct inode *inode = file->inode;
+    struct dirent *dirent;
+    struct ext2_dir_entry *de;
+    struct cache_struct *cs;
+    int index = file->offset >> fs->block_shift;
+    block_t block;
+    
+    if (!(block = bmap(fs, inode, index)))
+       return NULL;        
+    cs = get_cache_block(fs->fs_dev, block);
+    de = (struct ext2_dir_entry *)(cs->data + (file->offset & (BLOCK_SIZE(fs) - 1)));
+    
+    if (!(dirent = malloc(sizeof(*dirent)))) {
+       malloc_error("dirent structure in ext2_readdir");
+       return NULL;
+    }
+    dirent->d_ino = de->d_inode;
+    dirent->d_off = file->offset;
+    dirent->d_reclen = de->d_rec_len;
+    dirent->d_type = de->d_file_type;
+    memcpy(dirent->d_name, de->d_name, de->d_name_len);
+    dirent->d_name[de->d_name_len] = '\0';
+    
+    file->offset += de->d_rec_len;  /* Update for next reading */
+    
+    return dirent;
+}
 
 /* Load the config file, return 1 if failed, or 0 */
 static int ext2_load_config(void)
@@ -452,5 +486,6 @@ const struct fs_ops ext2_fs_ops = {
     .iget_root     = ext2_iget_root,
     .iget_current  = ext2_iget_current,
     .iget          = ext2_iget,
-    .follow_symlink = ext2_follow_symlink
+    .follow_symlink = ext2_follow_symlink,
+    .readdir       = ext2_readdir
 };
index 843c90c..d77fcb1 100644 (file)
@@ -529,27 +529,143 @@ static struct inode *vfat_iget(char *dname, struct inode *parent)
     return vfat_find_entry(dname, parent);
 }
 
-/*
- * The open dir function, just call the searchdir function  directly. 
- * I don't think we need call the mangle_name function first 
- */
-void vfat_opendir(com32sys_t *regs)
+static struct dirent * vfat_readdir(struct file *file)
 {
-    char *src = MK_PTR(regs->es, regs->esi.w[0]);
-    char *dst = MK_PTR(regs->ds, regs->edi.w[0]);
-    strcpy(dst, src);
-    searchdir(regs);   
+    struct fs_info *fs = file->fs;
+    struct dirent *dirent;
+    struct fat_dir_entry *de;
+    struct fat_long_name_entry *long_de;
+    struct cache_struct *cs;
+    
+    sector_t sector = get_the_right_sector(file);
+    
+    uint8_t vfat_init, vfat_next, vfat_csum;
+    uint8_t id;
+    int entries_left;
+    int checksum;
+    int long_entry = 0;
+    int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
+    
+    cs = get_cache_block(fs->fs_dev, sector);
+    de = (struct fat_dir_entry *)(cs->data + sec_off);
+    entries_left = ((1 << fs->sector_shift) - sec_off) >> 5;
+    
+    while (1) {
+       while(entries_left--) {
+           if (de->name[0] == 0)
+               return NULL;
+           if ((uint8_t)de->name[0] == 0xe5)
+               goto invalid;
+           
+           if (de->attr == 0x0f) {
+               /*
+                * It's a long name entry.
+                */
+               long_de = (struct fat_long_name_entry *)de;
+               id = long_de->id;
+               
+               if (id & 0x40) {
+                   /* init vfat_csum and vfat_init */
+                   vfat_csum = long_de->checksum;
+                   id &= 0x3f;
+                   vfat_init = id;
+                   
+                   /* ZERO the long_name buffer */
+                   memset(long_name, 0, sizeof long_name);
+               } else {
+                   if (long_de->checksum != vfat_csum ||
+                       id != vfat_next)
+                       goto invalid;
+               }
+               
+               vfat_next = --id;
+               
+               /* got the long entry name */
+               long_entry_name(long_de);
+               memcpy(long_name + id * 13, entry_name, 13);
+               
+               if (id == 0) 
+                   long_entry = 1;
+               
+               de++;
+               file->offset += sizeof(struct fat_dir_entry);
+               continue;     /* Try the next entry */
+           } else {
+               /*
+                * It's a short entry 
+                */
+               if (de->attr & 0x08) /* ignore volume labels */
+                   goto invalid;
+               
+               if (long_entry == 1) {
+                   /* Got a long entry */
+                   checksum = get_checksum(de->name);
+                   if (checksum == vfat_csum)
+                       goto got;
+               } else {
+                   /* Use the long_name buffer to store a short one. */
+                   int i;
+                   char *p = long_name;
+                   
+                   for (i = 0; i < 8; i++) {
+                       if (de->name[i] == ' ')
+                           break;
+                       *p++ = de->name[i];
+                   }
+                   *p++ = '.';
+                   if (de->name[8] == ' ') {
+                       *--p = '\0';
+                   } else {
+                       for (i = 8; i < 11; i++) {
+                           if (de->name[i] == ' ')
+                               break;
+                           *p++ = de->name[i];
+                       }
+                       *p = '\0';
+                   }
+                   
+                   goto got;
+               }
+           }
+           
+       invalid:
+           de++;
+           file->offset += sizeof(struct fat_dir_entry);
+       }
+       
+       /* Try with the next sector */
+       sector = get_next_sector(fs, sector);
+       if (!sector)
+           return NULL;
+       cs = get_cache_block(fs->fs_dev, sector);
+       de = (struct fat_dir_entry *)cs->data;
+       entries_left = 1 << (fs->sector_shift - 5);
+    }
+    
+got:
+    if (!(dirent = malloc(sizeof(*dirent)))) {
+       malloc_error("dirent structure in vfat_readdir");
+       return NULL;
+    }
+    dirent->d_ino = 0;           /* Inode number is invalid to FAT fs */
+    dirent->d_off = file->offset;
+    dirent->d_reclen = 0;
+    dirent->d_type = get_inode_mode(de->attr);
+    strcpy(dirent->d_name, long_name);
+    
+    file->offset += sizeof(*de);  /* Update for next reading */
+    
+    return dirent;
 }
 
-
 /* Load the config file, return 1 if failed, or 0 */
 static int vfat_load_config(void)
 {
-    static const char syslinux_cfg1[] = "/boot/syslinux/syslinux.cfg";
-    static const char syslinux_cfg2[] = "/syslinux/syslinux.cfg";
-    static const char syslinux_cfg3[] = "/syslinux.cfg";
-    const char * const syslinux_cfg[] =
-       { syslinux_cfg1, syslinux_cfg2, syslinux_cfg3 };
+    const char * const syslinux_cfg[] = {
+       "/boot/syslinux/syslinux.cfg",
+       "/syslinux/syslinux.cfg",
+       "/syslinux.cfg"
+    };
     com32sys_t regs;
     char *p;
     int i = 0;
@@ -576,7 +692,7 @@ static int vfat_load_config(void)
     strcpy(ConfigName, "syslinux.cfg");
     strcpy(CurrentDirName, syslinux_cfg[i]);
     p = strrchr(CurrentDirName, '/');
-    *p = '\0';
+    *(p + 1) = '\0';        /* In case we met '/syslinux.cfg' */
     
     return 0;
 }
@@ -641,8 +757,7 @@ const struct fs_ops vfat_fs_ops = {
     .mangle_name   = vfat_mangle_name,
     .unmangle_name = generic_unmangle_name,
     .load_config   = vfat_load_config,
-    .opendir       = vfat_opendir,
-    .readdir       = NULL,
+    .readdir       = vfat_readdir,
     .iget_root     = vfat_iget_root,
     .iget_current  = NULL,
     .iget          = vfat_iget,
index 19bd314..1670e60 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <string.h>
+#include <sys/dirent.h>
 #include <core.h>
 #include <cache.h>
 #include <disk.h>
@@ -298,6 +299,97 @@ static struct inode *iso_iget(char *dname, struct inode *parent)
     return iso_get_inode(de);
 }
 
+/* Convert to lower case string */
+static void tolower_str(char *str)
+{
+       while (*str) {
+               if (*str >= 'A' && *str <= 'Z')
+                       *str = *str + 0x20;
+               str++;
+       }
+}
+
+static struct dirent *iso_readdir(struct file *file)
+{
+    struct fs_info *fs = file->fs;
+    struct inode *inode = file->inode;
+    struct iso_dir_entry *de, tmpde;
+    struct dirent *dirent;
+    struct cache_struct *cs = NULL;
+    block_t block =  *file->inode->data + (file->offset >> fs->block_shift);
+    int offset = file->offset & (BLOCK_SIZE(fs) - 1);
+    int i = 0;
+    int de_len, de_name_len;
+    char *de_name;
+    
+    while (1) {
+       if (!cs) {
+           if (++i > inode->blocks)
+               return NULL;
+           cs = get_cache_block(fs->fs_dev, block++);
+       }
+       de = (struct iso_dir_entry *)(cs->data + offset);
+       
+       de_len = de->length;
+       if (de_len == 0) {    /* move on to the next block */
+           cs = NULL;
+           file->offset = (file->offset + BLOCK_SIZE(fs) - 1)
+               >> fs->block_shift;
+           continue;
+       }
+       offset += de_len;
+       
+       /* Make sure we have a full directory entry */
+       if (offset >= BLOCK_SIZE(fs)) {
+           int slop = de_len + BLOCK_SIZE(fs) - offset;
+           
+           memcpy(&tmpde, de, slop);
+           offset &= BLOCK_SIZE(fs) - 1;
+           if (offset) {
+               if (++i > inode->blocks)
+                   return NULL;
+               cs = get_cache_block(fs->fs_dev, block++);
+               memcpy((void *)&tmpde + slop, cs->data, offset);
+           }
+           de = &tmpde;
+       }
+       
+       if (de_len < 33) {
+           printf("Corrupted directory entry in sector %u\n", 
+                  (uint32_t)(block - 1));
+           return NULL;
+       }
+       
+       de_name_len = de->name_len;
+       de_name = de->name;
+       /* Handling the special case ".' and '..' here */
+       if((de_name_len == 1) && (*de_name == 0)) {
+           de_name = ".";
+       } else if ((de_name_len == 1) && (*de_name == 1)) {
+           de_name ="..";
+           de_name_len = 2;
+       }
+       
+       break;
+    }
+    
+    if (!(dirent = malloc(sizeof(*dirent)))) {
+       malloc_error("dirent structure in iso_readdir");
+       return NULL;
+    }
+    
+    dirent->d_ino = 0;           /* Inode number is invalid to ISO fs */
+    dirent->d_off = file->offset;
+    dirent->d_reclen = de_len;
+    dirent->d_type = get_inode_mode(de->flags);
+    iso_convert_name(dirent->d_name, de_name, de_name_len);
+    tolower_str(dirent->d_name);
+    
+    file->offset += de_len;  /* Update for next reading */
+    
+    return dirent;
+}
+
 /* Load the config file, return 1 if failed, or 0 */
 static int iso_load_config(void)
 {
@@ -364,5 +456,6 @@ const struct fs_ops iso_fs_ops = {
     .load_config   = iso_load_config,
     .iget_root     = iso_iget_root,
     .iget_current  = NULL,
-    .iget          = iso_iget,    
+    .iget          = iso_iget,
+    .readdir       = iso_readdir
 };
index 04eb21f..c5cea6f 100644 (file)
@@ -65,7 +65,6 @@ struct fs_ops {
     char * (*follow_symlink)(struct inode *, const char *);
 
     /* the _dir_ stuff */
-    void     (*opendir)(com32sys_t *);
     struct dirent * (*readdir)(struct file *);
 };