pathbased: Add FAT support to extlinux and let FAT be "true" pathbased
authorAlek Du <alek.du@intel.com>
Mon, 8 Feb 2010 02:54:31 +0000 (10:54 +0800)
committerH. Peter Anvin <hpa@zytor.com>
Thu, 11 Feb 2010 00:04:14 +0000 (16:04 -0800)
Now extlinux can install FAT partition now, and FAT honors patched
path area.

Signed-off-by: Alek Du <alek.du@intel.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
core/extlinux.asm
core/fs/fat/fat.c
extlinux/fat.h [new file with mode: 0644]
extlinux/main.c

index 1ac749b..9538576 100644 (file)
@@ -27,6 +27,8 @@ my_id         equ extlinux_id
                section .rodata
                alignz 4
 ROOT_FS_OPS:
+               extern vfat_fs_ops
+               dd vfat_fs_ops
                extern ext2_fs_ops
                dd ext2_fs_ops
                extern btrfs_fs_ops
index 13cf674..d723f28 100644 (file)
@@ -13,7 +13,7 @@ static struct inode * new_fat_inode(struct fs_info *fs)
 {
     struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode));
     if (!inode)
-       malloc_error("inode structure");                
+       malloc_error("inode structure");
 
     return inode;
 }
@@ -44,7 +44,7 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
     int lo, hi;
     struct cache_struct *cs;
     uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
-    
+
     switch(FAT_SB(fs)->fat_type) {
     case FAT12:
        offset = clust_num + (clust_num >> 1);
@@ -52,8 +52,8 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
        offset &= sector_mask;
        cs = get_fat_sector(fs, fat_sector);
        if (offset == sector_mask) {
-           /* 
-            * we got the end of the one fat sector, 
+           /*
+            * we got the end of the one fat sector,
             * but we have just one byte and we need two,
             * so store the low part, then read the next fat
             * sector, read the high part, then combine it.
@@ -65,13 +65,13 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
        } else {
            next_cluster = *(uint16_t *)(cs->data + offset);
        }
-       
+
        if (clust_num & 0x0001)
            next_cluster >>= 4;         /* cluster number is ODD */
        else
            next_cluster &= 0x0fff;     /* cluster number is EVEN */
        break;
-       
+
     case FAT16:
        offset = clust_num << 1;
        fat_sector = offset >> SECTOR_SHIFT(fs);
@@ -79,7 +79,7 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
        cs = get_fat_sector(fs, fat_sector);
        next_cluster = *(uint16_t *)(cs->data + offset);
        break;
-       
+
     case FAT32:
        offset = clust_num << 2;
        fat_sector = offset >> SECTOR_SHIFT(fs);
@@ -89,7 +89,7 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
        next_cluster &= 0x0fffffff;
        break;
     }
-    
+
     return next_cluster;
 }
 
@@ -101,7 +101,7 @@ static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
     sector_t data_sector;
     uint32_t cluster;
     int clust_shift = sbi->clust_shift;
-    
+
     if (sector < data_area) {
        /* Root directory sector... */
        sector++;
@@ -109,7 +109,7 @@ static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
            sector = 0; /* Ran out of root directory, return EOF */
        return sector;
     }
-    
+
     data_sector = sector - data_area;
     if ((data_sector + 1) & sbi->clust_mask)  /* Still in the same cluster */
        return sector + 1;                    /* Next sector inside cluster */
@@ -120,7 +120,7 @@ static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
 
     if (cluster >= sbi->clusters)
        return 0;
-    
+
     /* return the start of the new cluster */
     sector = (cluster << clust_shift) + data_area;
     return sector;
@@ -157,7 +157,7 @@ static sector_t get_the_right_sector(struct file *file)
 
     PVT(inode)->offset = sector_pos;
     PVT(inode)->here   = sector;
-    
+
     return sector;
 }
 
@@ -184,19 +184,19 @@ static sector_t next_sector(struct file *file)
  * 64K boundaries.
  *
  */
-static void __getfssec(struct fs_info *fs, char *buf, 
+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++;
@@ -204,14 +204,14 @@ static void __getfssec(struct fs_info *fs, char *buf,
             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;
     }
 }
@@ -219,7 +219,7 @@ static void __getfssec(struct fs_info *fs, char *buf,
 
 
 /**
- * get multiple sectors from a file 
+ * get multiple sectors from a file
  *
  * @param: buf, the buffer to store the read data
  * @param: file, the file structure pointer
@@ -236,26 +236,26 @@ static uint32_t vfat_getfssec(struct file *file, char *buf, int sectors,
     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; 
+ * Mangle a filename pointed to by src into a buffer pointed to by dst;
  * ends on encountering any whitespace.
  *
  */
@@ -264,7 +264,7 @@ static void vfat_mangle_name(char *dst, const char *src)
     char *p = dst;
     char c;
     int i = FILENAME_MAX -1;
-    
+
     /*
      * Copy the filename, converting backslash to slash and
      * collapsing duplicate separators.
@@ -272,14 +272,14 @@ static void vfat_mangle_name(char *dst, const char *src)
     while (not_whitespace(c = *src)) {
         if (c == '\\')
             c = '/';
-        
+
         if (c == '/') {
             if (src[1] == '/' || src[1] == '\\') {
                 src++;
                 i--;
                 continue;
             }
-        }        
+        }
         i--;
         *dst++ = *src++;
     }
@@ -292,7 +292,7 @@ static void vfat_mangle_name(char *dst, const char *src)
                        break;
         if ((*(dst-1) != '/') && (*(dst-1) != '.'))
             break;
-        
+
         dst--;
         i++;
     }
@@ -306,17 +306,17 @@ static void vfat_mangle_name(char *dst, const char *src)
  * Mangle a normal style string to DOS style string.
  */
 static void mangle_dos_name(char *mangle_buf, char *src)
-{       
+{
     int i;
-    unsigned char c;        
-    
+    unsigned char c;
+
     i = 0;
     while (i < 11) {
        c = *src++;
-       
-       if ((c <= ' ') || (c == '/')) 
+
+       if ((c <= ' ') || (c == '/'))
            break;
-       
+
        if (c == '.') {
            while (i < 8)
                mangle_buf[i++] = ' ';
@@ -327,7 +327,7 @@ static void mangle_dos_name(char *mangle_buf, char *src)
        c = codepage.upper[c];
        if (i == 0 && c == 0xe5)
            c = 0x05;           /* Special hack for the first byte only! */
-       
+
        mangle_buf[i++] = c;
     }
     while (i < 11)
@@ -422,7 +422,7 @@ static uint8_t get_checksum(char *dir_name)
 {
     int  i;
     uint8_t sum = 0;
-    
+
     for (i = 11; i; i--)
        sum = ((sum & 1) << 7) + (sum >> 1) + *dir_name++;
     return sum;
@@ -436,7 +436,7 @@ static inline sector_t first_sector(struct fs_info *fs,
     const struct fat_sb_info *sbi = FAT_SB(fs);
     sector_t first_clust;
     sector_t sector;
-    
+
     first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
     sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
 
@@ -451,7 +451,7 @@ static inline int get_inode_mode(uint8_t attr)
        return I_FILE;
 }
 
+
 static struct inode *vfat_find_entry(char *dname, struct inode *dir)
 {
     struct fs_info *fs = dir->fs;
@@ -459,7 +459,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
     struct fat_dir_entry *de;
     struct fat_long_name_entry *long_de;
     struct cache_struct *cs;
-    
+
     char mangled_name[12];
     uint16_t long_name[260];   /* == 20*13 */
     int long_len;
@@ -471,7 +471,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
     int entries;
     int checksum;
     int long_match = 0;
-    
+
     slots = (strlen(dname) + 12) / 13;
     if (slots > 20)
        return NULL;            /* Name too long */
@@ -479,7 +479,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
     slots |= 0x40;
     vfat_init = vfat_next = slots;
     long_len = slots*13;
-    
+
     /* Produce the shortname version, in case we need it. */
     mangle_dos_name(mangled_name, dname);
 
@@ -487,11 +487,11 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
        cs = get_cache_block(fs->fs_dev, dir_sector);
        de = (struct fat_dir_entry *)cs->data;
        entries = 1 << (fs->sector_shift - 5);
-       
+
        while (entries--) {
            if (de->name[0] == 0)
                return NULL;
-           
+
            if (de->attr == 0x0f) {
                /*
                 * It's a long name entry.
@@ -500,7 +500,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
                id = long_de->id;
                if (id != vfat_next)
                    goto not_match;
-               
+
                if (id & 0x40) {
                    /* get the initial checksum value */
                    vfat_csum = long_de->checksum;
@@ -513,13 +513,13 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
                    if (long_de->checksum != vfat_csum)
                        goto not_match;
                }
-               
+
                vfat_next = --id;
-               
+
                /* got the long entry name */
                copy_long_chunk(long_name + id*13, de);
-                               
-               /* 
+
+               /*
                 * If we got the last entry, check it.
                 * Or, go on with the next entry.
                 */
@@ -532,14 +532,14 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
                continue;     /* Try the next entry */
            } else {
                /*
-                * It's a short entry 
+                * It's a short entry
                 */
                if (de->attr & 0x08) /* ignore volume labels */
                    goto not_match;
-               
+
                if (long_match) {
-                   /* 
-                    * We already have a VFAT long name match. However, the 
+                   /*
+                    * We already have a VFAT long name match. However, the
                     * match is only valid if the checksum matches.
                     */
                    checksum = get_checksum(de->name);
@@ -550,25 +550,25 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
                        goto found;
                }
            }
-           
+
        not_match:
            vfat_next = vfat_init;
            long_match = 0;
-           
+
            de++;
        }
-       
+
        /* Try with the next sector */
        dir_sector = get_next_sector(fs, dir_sector);
     }
     return NULL;               /* Nothing found... */
-    
+
 found:
     inode = new_fat_inode(fs);
     inode->size = de->file_size;
     PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
     inode->mode = get_inode_mode(de->attr);
-    
+
     return inode;
 }
 
@@ -576,15 +576,15 @@ static struct inode *vfat_iget_root(struct fs_info *fs)
 {
     struct inode *inode = new_fat_inode(fs);
     int root_size = FAT_SB(fs)->root_size;
-    
-    /* 
+
+    /*
      * For FAT32, the only way to get the root directory size is to
      * follow the entire FAT chain to the end... which seems pointless.
      */
     inode->size = root_size ? root_size << fs->sector_shift : ~0;
     PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
     inode->mode = I_DIR;
-    
+
     return inode;
 }
 
@@ -600,9 +600,9 @@ static struct dirent * vfat_readdir(struct file *file)
     struct fat_dir_entry *de;
     struct fat_long_name_entry *long_de;
     struct cache_struct *cs;
-    
+
     sector_t sector = get_the_right_sector(file);
-    
+
     uint16_t long_name[261];   /* == 20*13 + 1 (to guarantee null) */
     char filename[261];
 
@@ -612,27 +612,27 @@ static struct dirent * vfat_readdir(struct file *file)
     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;
 
     vfat_next = vfat_csum = 0xff;
-    
+
     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;
@@ -641,7 +641,7 @@ static struct dirent * vfat_readdir(struct file *file)
                        goto invalid; /* Too long! */
 
                    vfat_init = id;
-                   
+
                    /* ZERO the long_name buffer */
                    memset(long_name, 0, sizeof long_name);
                } else {
@@ -649,9 +649,9 @@ static struct dirent * vfat_readdir(struct file *file)
                        id != vfat_next)
                        goto invalid;
                }
-               
+
                vfat_next = --id;
-               
+
                /* got the long entry name */
                copy_long_chunk(long_name + id*13, de);
 
@@ -661,17 +661,17 @@ static struct dirent * vfat_readdir(struct file *file)
                    if (longlen > 0 && longlen < sizeof(dirent->d_name))
                        long_entry = 1;
                }
-               
+
                de++;
                file->offset += sizeof(struct fat_dir_entry);
                continue;     /* Try the next entry */
            } else {
                /*
-                * It's a short entry 
+                * 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);
@@ -682,7 +682,7 @@ static struct dirent * vfat_readdir(struct file *file)
                    int i;
                    uint8_t c;
                    char *p = filename;
-                   
+
                    for (i = 0; i < 8; i++) {
                        c = de->name[i];
                        if (c == ' ')
@@ -703,16 +703,16 @@ static struct dirent * vfat_readdir(struct file *file)
                        }
                    }
                    *p = '\0';
-                   
+
                    goto got;
                }
            }
-           
+
        invalid:
            de++;
            file->offset += sizeof(struct fat_dir_entry);
        }
-       
+
        /* Try with the next sector */
        sector = next_sector(file);
        if (!sector)
@@ -721,7 +721,7 @@ static struct dirent * vfat_readdir(struct file *file)
        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");
@@ -734,7 +734,7 @@ got:
     strcpy(dirent->d_name, filename);
 
     file->offset += sizeof(*de);  /* Update for next reading */
-    
+
     return dirent;
 }
 
@@ -750,12 +750,19 @@ static int vfat_load_config(void)
     char *p;
     int i = 0;
 
-    /* 
+    /*
      * we use the ConfigName to pass the config path because
      * it is under the address 0xffff
      */
     memset(&regs, 0, sizeof regs);
     regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+    if (*CurrentDirName) { /* installed by extlinux not syslinux */
+       sprintf(ConfigName, "%s/extlinux.conf", CurrentDirName);
+       call16(core_open, &regs, &regs);
+       strcpy(ConfigName, "extlinux.conf");
+       return regs.eflags.l & EFLAGS_ZF;
+    }
+    /* installed by syslinux */
     for (; i < 3; i++) {
         strcpy(ConfigName, syslinux_cfg[i]);
         call16(core_open, &regs, &regs);
@@ -768,15 +775,15 @@ static int vfat_load_config(void)
         printf("no config file found\n");
         return 1;  /* no config file */
     }
-    
+
     strcpy(ConfigName, "syslinux.cfg");
     strcpy(CurrentDirName, syslinux_cfg[i]);
     p = strrchr(CurrentDirName, '/');
     *(p + 1) = '\0';        /* In case we met '/syslinux.cfg' */
-    
+
     return 0;
 }
+
 static inline __constfunc uint32_t bsr(uint32_t num)
 {
     asm("bsrl %1,%0" : "=r" (num) : "rm" (num));
@@ -792,31 +799,34 @@ static int vfat_fs_init(struct fs_info *fs)
     int sectors_per_fat;
     uint32_t clusters;
     sector_t total_sectors;
-    
+
     fs->sector_shift = fs->block_shift = disk->sector_shift;
     fs->sector_size  = 1 << fs->sector_shift;
     fs->block_size   = 1 << fs->block_shift;
 
     disk->rdwr_sectors(disk, &fat, 0, 1, 0);
-    
+
+    /* XXX: Find better sanity checks... */
+    if (!fat.bxResSectors || !fat.bxFATs)
+       return -1;
     sbi = malloc(sizeof(*sbi));
     if (!sbi)
        malloc_error("fat_sb_info structure");
     fs->fs_info = sbi;
-    
+
     sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32;
     total_sectors   = fat.bxSectors ? : fat.bsHugeSectors;
-    
-    sbi->fat       = fat.bxResSectors; 
+
+    sbi->fat       = fat.bxResSectors;
     sbi->root      = sbi->fat + sectors_per_fat * fat.bxFATs;
     sbi->root_size = root_dir_size(fs, &fat);
     sbi->data      = sbi->root + sbi->root_size;
-    
+
     sbi->clust_shift      = bsr(fat.bxSecPerClust);
     sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
     sbi->clust_mask       = fat.bxSecPerClust - 1;
     sbi->clust_size       = fat.bxSecPerClust << fs->sector_shift;
-    
+
     clusters = (total_sectors - sbi->data) >> sbi->clust_shift;
     if (clusters <= 0xff4) {
        sbi->fat_type = FAT12;
@@ -838,11 +848,11 @@ static int vfat_fs_init(struct fs_info *fs)
            + ((fat.fat32.root_cluster-2) << sbi->clust_shift);
     }
     sbi->clusters = clusters;
-    
+
     /* for SYSLINUX, the cache is based on sector size */
     return fs->sector_shift;
 }
-        
+
 const struct fs_ops vfat_fs_ops = {
     .fs_name       = "vfat",
     .fs_flags      = FS_USEMEM | FS_THISIND,
diff --git a/extlinux/fat.h b/extlinux/fat.h
new file mode 100644 (file)
index 0000000..dd5a362
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _H_FAT_
+#define _H_FAT_
+
+#define MSDOS_SUPER_MAGIC       0x4d44          /* MD */
+#if 0
+/* FAT bootsector format, also used by other disk-based derivatives */
+struct boot_sector {
+    uint8_t bsJump[3];
+    char bsOemName[8];
+    uint16_t bsBytesPerSec;
+    uint8_t bsSecPerClust;
+    uint16_t bsResSectors;
+    uint8_t bsFATs;
+    uint16_t bsRootDirEnts;
+    uint16_t bsSectors;
+    uint8_t bsMedia;
+    uint16_t bsFATsecs;
+    uint16_t bsSecPerTrack;
+    uint16_t bsHeads;
+    uint32_t bsHiddenSecs;
+    uint32_t bsHugeSectors;
+
+    union {
+        struct {
+            uint8_t DriveNumber;
+            uint8_t Reserved1;
+            uint8_t BootSignature;
+            uint32_t VolumeID;
+            char VolumeLabel[11];
+            char FileSysType[8];
+            uint8_t Code[442];
+        } __attribute__ ((packed)) bs16;
+        struct {
+            uint32_t FATSz32; 
+            uint16_t ExtFlags;
+            uint16_t FSVer;
+            uint32_t RootClus;
+            uint16_t FSInfo;
+            uint16_t BkBootSec;
+            uint8_t Reserved0[12];
+            uint8_t DriveNumber;
+            uint8_t Reserved1;
+            uint8_t BootSignature;
+            uint32_t VolumeID;
+            char VolumeLabel[11];
+            char FileSysType[8];
+            uint8_t Code[414];
+        } __attribute__ ((packed)) bs32;
+    } __attribute__ ((packed));
+    
+    uint32_t NextSector;        /* Pointer to the first unused sector */
+    uint16_t MaxTransfer;       /* Max sectors per transfer */
+    uint16_t bsSignature;
+} __attribute__ ((packed));
+
+#define bsHead      bsJump
+#define bsHeadLen   offsetof(struct boot_sector, bsOemName)
+#define bsCode      bs32.Code   /* The common safe choice */
+#define bsCodeLen   (offsetof(struct boot_sector, bsSignature) - \
+                     offsetof(struct boot_sector, bsCode))
+#endif
+#endif
index c28f190..aed265f 100644 (file)
@@ -14,7 +14,7 @@
 /*
  * extlinux.c
  *
- * Install the extlinux boot block on an ext2/3/4 and btrfs filesystem
+ * Install the extlinux boot block on an fat, ext2/3/4 and btrfs filesystem
  */
 
 #define  _GNU_SOURCE           /* Enable everything */
@@ -30,6 +30,7 @@ typedef uint64_t u64;
 #include <mntent.h>
 #endif
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <getopt.h>
@@ -48,6 +49,7 @@ typedef uint64_t u64;
 
 #include "ext2_fs.h"
 #include "btrfs.h"
+#include "fat.h"
 #include "../version.h"
 #include "syslxint.h"
 
@@ -58,9 +60,10 @@ typedef uint64_t u64;
 #endif
 
 /* Global option handling */
-/* Global fs_type for handling ext2/3/4 vs btrfs */
+/* Global fs_type for handling fat, ext2/3/4 and btrfs */
 #define EXT2 1
 #define BTRFS 2
+#define VFAT 3
 int fs_type;
 
 const char *program;
@@ -444,7 +447,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
     nsect += 2;                        /* Two sectors for the ADV */
     sectp = alloca(sizeof(uint32_t) * nsect);
-    if (fs_type == EXT2) {
+    if (fs_type == EXT2 || fs_type == VFAT) {
        if (sectmap(fd, sectp, nsect)) {
                perror("bmap");
                exit(1);
@@ -490,7 +493,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     }
     strncpy((char *)boot_image + diroffset, subpath, dirlen);
     free(dirpath);
-  
+
     /* write subvol info if we have */
     subvoloffset = get_16(&patcharea->subvoloffset);
     subvollen = get_16(&patcharea->subvollen);
@@ -502,7 +505,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
 
     /* Now produce a checksum */
     set_32(&patcharea->checksum, 0);
-    
+
     csum = LDLINUX_MAGIC;
     for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
        csum -= get_32(wp);     /* Negative checksum */
@@ -694,6 +697,7 @@ int install_bootblock(int fd, const char *device)
 {
     struct ext2_super_block sb;
     struct btrfs_super_block sb2;
+    struct boot_sector sb3;
     bool ok = false;
 
     if (fs_type == EXT2) {
@@ -711,13 +715,29 @@ int install_bootblock(int fd, const char *device)
        }
        if (sb2.magic == *(u64 *)BTRFS_MAGIC)
                ok = true;
+    } else if (fs_type == VFAT) {
+       if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
+               perror("reading fat superblock");
+               return 1;
+       }
+       if (sb3.bsResSectors && sb3.bsFATs &&
+           (strstr(sb3.bs16.FileSysType, "FAT") ||
+            strstr(sb3.bs32.FileSysType, "FAT")))
+               ok = true;
     }
     if (!ok) {
-       fprintf(stderr, "no ext2/3/4 or btrfs superblock found on %s\n",
+       fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
                        device);
        return 1;
     }
-    if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
+    if (fs_type == VFAT) {
+       struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
+        if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
+           xpwrite(fd, &bs->bsCode, bsCodeLen,
+               offsetof(struct boot_sector, bsCode)) != bsCodeLen)
+               perror("writing fat bootblock");
+               return 1;
+    } else if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
        perror("writing bootblock");
        return 1;
     }
@@ -725,7 +745,7 @@ int install_bootblock(int fd, const char *device)
     return 0;
 }
 
-int ext2_install_file(const char *path, int devfd, struct stat *rst)
+int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
 {
     char *file;
     int fd = -1, dirfd = -1, flags;
@@ -751,7 +771,7 @@ int ext2_install_file(const char *path, int devfd, struct stat *rst)
            perror(file);
            goto bail;
        }
-    } else {
+    } else if (fs_type == EXT2) {
        /* If file exist, remove the immutable flag and set u+w mode */
        if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
            flags &= ~EXT2_IMMUTABLE_FL;
@@ -843,8 +863,8 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst)
 
 int install_file(const char *path, int devfd, struct stat *rst)
 {
-       if (fs_type == EXT2)
-               return ext2_install_file(path, devfd, rst);
+       if (fs_type == EXT2 || fs_type == VFAT)
+               return ext2_fat_install_file(path, devfd, rst);
        else if (fs_type == BTRFS)
                return btrfs_install_file(path, devfd, rst);
        return 1;
@@ -930,6 +950,13 @@ static const char *find_device(const char *mtab_file, dev_t dev)
                    done = true;
                    break;
                }
+       case VFAT:
+               if ((!strcmp(mnt->mnt_type, "vfat")) &&
+                   !stat(mnt->mnt_fsname, &dst) &&
+                   dst.st_rdev == dev) {
+                   done = true;
+                   break;
+               }
        }
        if (done) {
                devname = strdup(mnt->mnt_fsname);
@@ -1022,9 +1049,11 @@ static int open_device(const char *path, struct stat *st, const char **_devname)
        fs_type = EXT2;
     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
        fs_type = BTRFS;
+    else if (sfs.f_type == MSDOS_SUPER_MAGIC)
+       fs_type = VFAT;
 
     if (!fs_type) {
-       fprintf(stderr, "%s: not an ext2/3/4 or btrfs filesystem: %s\n",
+       fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
                program, path);
        return -1;
     }