Subversion Repositories HelenOS

Rev

Rev 3597 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2008 Martin Decky
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # - Redistributions of source code must retain the above copyright
  11. #   notice, this list of conditions and the following disclaimer.
  12. # - Redistributions in binary form must reproduce the above copyright
  13. #   notice, this list of conditions and the following disclaimer in the
  14. #   documentation and/or other materials provided with the distribution.
  15. # - The name of the author may not be used to endorse or promote products
  16. #   derived from this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19. # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20. # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23. # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. #
  29. """
  30. FAT creator
  31. """
  32.  
  33. import sys
  34. import os
  35. import random
  36. import xstruct
  37. import array
  38.  
  39. exclude_names = set(['.svn'])
  40.  
  41. def align_up(size, alignment):
  42.     "Return size aligned up to alignment"
  43.    
  44.     if (size % alignment == 0):
  45.         return size
  46.    
  47.     return (((size / alignment) + 1) * alignment)
  48.  
  49. def subtree_size(root, cluster_size, dirent_size):
  50.     "Recursive directory walk and calculate size"
  51.    
  52.     size = 0
  53.     files = 2
  54.    
  55.     for name in os.listdir(root):
  56.         canon = os.path.join(root, name)
  57.        
  58.         if (os.path.isfile(canon) and (not name in exclude_names)):
  59.             size += align_up(os.path.getsize(canon), cluster_size)
  60.             files += 1
  61.        
  62.         if (os.path.isdir(canon) and (not name in exclude_names)):
  63.             size += subtree_size(canon, cluster_size, dirent_size)
  64.             files += 1
  65.    
  66.     return size + align_up(files * dirent_size, cluster_size)
  67.  
  68. def root_entries(root):
  69.     "Return number of root directory entries"
  70.    
  71.     return len(os.listdir(root))
  72.  
  73. def write_file(path, outf, cluster_size, data_start, fat, reserved_clusters):
  74.     "Store the contents of a file"
  75.    
  76.     size = os.path.getsize(path)
  77.     prev = -1
  78.     first = 0
  79.    
  80.     inf = file(path, "r")
  81.     rd = 0;
  82.     while (rd < size):
  83.         empty_cluster = fat.index(0)
  84.         fat[empty_cluster] = 0xffff
  85.        
  86.         if (prev != -1):
  87.             fat[prev] = empty_cluster
  88.         else:
  89.             first = empty_cluster
  90.        
  91.         prev = empty_cluster
  92.        
  93.         data = inf.read(cluster_size);
  94.         outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
  95.         outf.write(data)
  96.         rd += len(data)
  97.     inf.close()
  98.    
  99.     return first, size
  100.  
  101. def write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster):
  102.     "Store the contents of a directory"
  103.    
  104.     length = len(directory)
  105.     size = length * dirent_size
  106.     prev = -1
  107.     first = 0
  108.    
  109.     i = 0
  110.     rd = 0;
  111.     while (rd < size):
  112.         if (prev != -1):
  113.             empty_cluster = fat.index(0)
  114.             fat[empty_cluster] = 0xffff
  115.             fat[prev] = empty_cluster
  116.         else:
  117.             first = empty_cluster
  118.        
  119.         prev = empty_cluster
  120.        
  121.         data = ''
  122.         data_len = 0
  123.         while ((i < length) and (data_len < cluster_size)):
  124.             if (i == 0):
  125.                 directory[i].cluster = empty_cluster
  126.            
  127.             data += directory[i].pack()
  128.             data_len += dirent_size
  129.             i += 1
  130.        
  131.         outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
  132.         outf.write(data)
  133.         rd += len(data)
  134.    
  135.     return first, size
  136.  
  137. DIR_ENTRY = """little:
  138.     char name[8]               /* file name */
  139.     char ext[3]                /* file extension */
  140.     uint8_t attr               /* file attributes */
  141.     uint8_t lcase              /* file name case (NT extension) */
  142.     uint8_t ctime_fine         /* create time (fine resolution) */
  143.     uint16_t ctime             /* create time */
  144.     uint16_t cdate             /* create date */
  145.     uint16_t adate             /* access date */
  146.     padding[2]                 /* EA-index */
  147.     uint16_t mtime             /* modification time */
  148.     uint16_t mdate             /* modification date */
  149.     uint16_t cluster           /* first cluster */
  150.     uint32_t size              /* file size */
  151. """
  152.  
  153. DOT_DIR_ENTRY = """little:
  154.     uint8_t signature          /* 0x2e signature */
  155.     char name[7]               /* empty */
  156.     char ext[3]                /* empty */
  157.     uint8_t attr               /* file attributes */
  158.     padding[1]                 /* reserved for NT */
  159.     uint8_t ctime_fine         /* create time (fine resolution) */
  160.     uint16_t ctime             /* create time */
  161.     uint16_t cdate             /* create date */
  162.     uint16_t adate             /* access date */
  163.     padding[2]                 /* EA-index */
  164.     uint16_t mtime             /* modification time */
  165.     uint16_t mdate             /* modification date */
  166.     uint16_t cluster           /* first cluster */
  167.     uint32_t size              /* file size */
  168. """
  169.  
  170. DOTDOT_DIR_ENTRY = """little:
  171.     uint8_t signature[2]       /* 0x2e signature */
  172.     char name[6]               /* empty */
  173.     char ext[3]                /* empty */
  174.     uint8_t attr               /* file attributes */
  175.     padding[1]                 /* reserved for NT */
  176.     uint8_t ctime_fine         /* create time (fine resolution) */
  177.     uint16_t ctime             /* create time */
  178.     uint16_t cdate             /* create date */
  179.     uint16_t adate             /* access date */
  180.     padding[2]                 /* EA-index */
  181.     uint16_t mtime             /* modification time */
  182.     uint16_t mdate             /* modification date */
  183.     uint16_t cluster           /* first cluster */
  184.     uint32_t size              /* file size */
  185. """
  186.  
  187. def mangle_fname(name):
  188.     # FIXME: filter illegal characters
  189.     parts = name.split('.')
  190.    
  191.     if (len(parts) > 0):
  192.         fname = parts[0]
  193.     else:
  194.         fname = ''
  195.        
  196.     return (fname + '          ').upper()[0:8]
  197.  
  198. def mangle_ext(name):
  199.     # FIXME: filter illegal characters
  200.     parts = name.split('.')
  201.    
  202.     if (len(parts) > 1):
  203.         ext = parts[1]
  204.     else:
  205.         ext = ''
  206.    
  207.     return (ext + '   ').upper()[0:3]
  208.  
  209. def create_dirent(name, directory, cluster, size):
  210.     dir_entry = xstruct.create(DIR_ENTRY)
  211.    
  212.     dir_entry.name = mangle_fname(name)
  213.     dir_entry.ext = mangle_ext(name)
  214.    
  215.     if (directory):
  216.         dir_entry.attr = 0x30
  217.     else:
  218.         dir_entry.attr = 0x20
  219.    
  220.     dir_entry.lcase = 0x18
  221.     dir_entry.ctime_fine = 0 # FIXME
  222.     dir_entry.ctime = 0 # FIXME
  223.     dir_entry.cdate = 0 # FIXME
  224.     dir_entry.adate = 0 # FIXME
  225.     dir_entry.mtime = 0 # FIXME
  226.     dir_entry.mdate = 0 # FIXME
  227.     dir_entry.cluster = cluster
  228.    
  229.     if (directory):
  230.         dir_entry.size = 0
  231.     else:
  232.         dir_entry.size = size
  233.    
  234.     return dir_entry
  235.  
  236. def create_dot_dirent(empty_cluster):
  237.     dir_entry = xstruct.create(DOT_DIR_ENTRY)
  238.    
  239.     dir_entry.signature = 0x2e
  240.     dir_entry.name = '       '
  241.     dir_entry.ext = '   '
  242.     dir_entry.attr = 0x10
  243.    
  244.     dir_entry.ctime_fine = 0 # FIXME
  245.     dir_entry.ctime = 0 # FIXME
  246.     dir_entry.cdate = 0 # FIXME
  247.     dir_entry.adate = 0 # FIXME
  248.     dir_entry.mtime = 0 # FIXME
  249.     dir_entry.mdate = 0 # FIXME
  250.     dir_entry.cluster = empty_cluster
  251.     dir_entry.size = 0
  252.    
  253.     return dir_entry
  254.  
  255. def create_dotdot_dirent(parent_cluster):
  256.     dir_entry = xstruct.create(DOTDOT_DIR_ENTRY)
  257.    
  258.     dir_entry.signature = [0x2e, 0x2e]
  259.     dir_entry.name = '      '
  260.     dir_entry.ext = '   '
  261.     dir_entry.attr = 0x10
  262.    
  263.     dir_entry.ctime_fine = 0 # FIXME
  264.     dir_entry.ctime = 0 # FIXME
  265.     dir_entry.cdate = 0 # FIXME
  266.     dir_entry.adate = 0 # FIXME
  267.     dir_entry.mtime = 0 # FIXME
  268.     dir_entry.mdate = 0 # FIXME
  269.     dir_entry.cluster = parent_cluster
  270.     dir_entry.size = 0
  271.    
  272.     return dir_entry
  273.  
  274. def recursion(head, root, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, parent_cluster):
  275.     "Recursive directory walk"
  276.    
  277.     directory = []
  278.    
  279.     if (not head):
  280.         # Directory cluster preallocation
  281.         empty_cluster = fat.index(0)
  282.         fat[empty_cluster] = 0xffff
  283.        
  284.         directory.append(create_dot_dirent(empty_cluster))
  285.         directory.append(create_dotdot_dirent(parent_cluster))
  286.     else:
  287.         empty_cluster = 0
  288.    
  289.     for name in os.listdir(root):
  290.         canon = os.path.join(root, name)
  291.        
  292.         if (os.path.isfile(canon) and (not name in exclude_names)):
  293.             rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
  294.             directory.append(create_dirent(name, False, rv[0], rv[1]))
  295.        
  296.         if (os.path.isdir(canon) and (not name in exclude_names)):
  297.             rv = recursion(False, canon, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
  298.             directory.append(create_dirent(name, True, rv[0], rv[1]))
  299.    
  300.     if (head):
  301.         outf.seek(root_start)
  302.         for dir_entry in directory:
  303.             outf.write(dir_entry.pack())
  304.     else:
  305.         return write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
  306.  
  307. BOOT_SECTOR = """little:
  308.     uint8_t jmp[3]             /* jump instruction */
  309.     char oem[8]                /* OEM string */
  310.     uint16_t sector            /* bytes per sector */
  311.     uint8_t cluster            /* sectors per cluster */
  312.     uint16_t reserved          /* reserved sectors */
  313.     uint8_t fats               /* number of FATs */
  314.     uint16_t rootdir           /* root directory entries */
  315.     uint16_t sectors           /* total number of sectors */
  316.     uint8_t descriptor         /* media descriptor */
  317.     uint16_t fat_sectors       /* sectors per single FAT */
  318.     uint16_t track_sectors     /* sectors per track */
  319.     uint16_t heads             /* number of heads */
  320.     uint32_t hidden            /* hidden sectors */
  321.     uint32_t sectors_big       /* total number of sectors (if sectors == 0) */
  322.    
  323.     /* Extended BIOS Parameter Block */
  324.     uint8_t drive              /* physical drive number */
  325.     padding[1]                 /* reserved (current head) */
  326.     uint8_t extboot_signature  /* extended boot signature */
  327.     uint32_t serial            /* serial number */
  328.     char label[11]             /* volume label */
  329.     char fstype[8]             /* filesystem type */
  330.     padding[448]               /* boot code */
  331.     uint8_t boot_signature[2]  /* boot signature */
  332. """
  333.  
  334. EMPTY_SECTOR = """little:
  335.     padding[512]               /* empty sector data */
  336. """
  337.  
  338. FAT_ENTRY = """little:
  339.     uint16_t next              /* FAT16 entry */
  340. """
  341.  
  342. def usage(prname):
  343.     "Print usage syntax"
  344.     print prname + " <PATH> <IMAGE>"
  345.  
  346. def main():
  347.     if (len(sys.argv) < 3):
  348.         usage(sys.argv[0])
  349.         return
  350.    
  351.     path = os.path.abspath(sys.argv[1])
  352.     if (not os.path.isdir(path)):
  353.         print "<PATH> must be a directory"
  354.         return
  355.    
  356.     fat16_clusters = 4096
  357.    
  358.     sector_size = 512
  359.     cluster_size = 4096
  360.     dirent_size = 32
  361.     fatent_size = 2
  362.     fat_count = 2
  363.     reserved_clusters = 2
  364.    
  365.     # Make sure the filesystem is large enought for FAT16
  366.     size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
  367.     while (size / cluster_size < fat16_clusters):
  368.         if (cluster_size > sector_size):
  369.             cluster_size /= 2
  370.             size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
  371.         else:
  372.             size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
  373.    
  374.     root_size = align_up(root_entries(path) * dirent_size, cluster_size)
  375.    
  376.     fat_size = align_up(align_up(size, cluster_size) / cluster_size * fatent_size, sector_size)
  377.    
  378.     sectors = (cluster_size + fat_count * fat_size + root_size + size) / sector_size
  379.     root_start = cluster_size + fat_count * fat_size
  380.     data_start = root_start + root_size
  381.    
  382.     outf = file(sys.argv[2], "w")
  383.    
  384.     boot_sector = xstruct.create(BOOT_SECTOR)
  385.     boot_sector.jmp = [0xEB, 0x3C, 0x90]
  386.     boot_sector.oem = "MSDOS5.0"
  387.     boot_sector.sector = sector_size
  388.     boot_sector.cluster = cluster_size / sector_size
  389.     boot_sector.reserved = cluster_size / sector_size
  390.     boot_sector.fats = fat_count
  391.     boot_sector.rootdir = root_size / dirent_size
  392.     if (sectors <= 65535):
  393.         boot_sector.sectors = sectors
  394.     else:
  395.         boot_sector.sectors = 0
  396.     boot_sector.descriptor = 0xF8
  397.     boot_sector.fat_sectors = fat_size / sector_size
  398.     boot_sector.track_sectors = 63
  399.     boot_sector.heads = 6
  400.     boot_sector.hidden = 0
  401.     if (sectors > 65535):
  402.         boot_sector.sectors_big = sectors
  403.     else:
  404.         boot_sector.sectors_big = 0
  405.    
  406.     boot_sector.drive = 0x80
  407.     boot_sector.extboot_signature = 0x29
  408.     boot_sector.serial = random.randint(0, 0x7fffffff)
  409.     boot_sector.label = "HELENOS"
  410.     boot_sector.fstype = "FAT16   "
  411.     boot_sector.boot_signature = [0x55, 0xAA]
  412.    
  413.     outf.write(boot_sector.pack())
  414.    
  415.     empty_sector = xstruct.create(EMPTY_SECTOR)
  416.    
  417.     # Reserved sectors
  418.     for i in range(1, cluster_size / sector_size):
  419.         outf.write(empty_sector.pack())
  420.    
  421.     # FAT tables
  422.     for i in range(0, fat_count):
  423.         for j in range(0, fat_size / sector_size):
  424.             outf.write(empty_sector.pack())
  425.    
  426.     # Root directory
  427.     for i in range(0, root_size / sector_size):
  428.         outf.write(empty_sector.pack())
  429.    
  430.     # Data
  431.     for i in range(0, size / sector_size):
  432.         outf.write(empty_sector.pack())
  433.    
  434.     fat = array.array('L', [0] * (fat_size / fatent_size))
  435.     fat[0] = 0xfff8
  436.     fat[1] = 0xffff
  437.    
  438.     recursion(True, path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, 0)
  439.    
  440.     # Store FAT
  441.     fat_entry = xstruct.create(FAT_ENTRY)
  442.     for i in range(0, fat_count):
  443.         outf.seek(cluster_size + i * fat_size)
  444.         for j in range(0, fat_size / fatent_size):
  445.             fat_entry.next = fat[j]
  446.             outf.write(fat_entry.pack())
  447.    
  448.     outf.close()
  449.    
  450. if __name__ == '__main__':
  451.     main()
  452.