Subversion Repositories HelenOS

Rev

Rev 3343 | 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.     padding[1]                 /* reserved for NT */
  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.ctime_fine = 0 # FIXME
  221.     dir_entry.ctime = 0 # FIXME
  222.     dir_entry.cdate = 0 # FIXME
  223.     dir_entry.adate = 0 # FIXME
  224.     dir_entry.mtime = 0 # FIXME
  225.     dir_entry.mdate = 0 # FIXME
  226.     dir_entry.cluster = cluster
  227.    
  228.     if (directory):
  229.         dir_entry.size = 0
  230.     else:
  231.         dir_entry.size = size
  232.    
  233.     return dir_entry
  234.  
  235. def create_dot_dirent(empty_cluster):
  236.     dir_entry = xstruct.create(DOT_DIR_ENTRY)
  237.    
  238.     dir_entry.signature = 0x2e
  239.     dir_entry.name = '       '
  240.     dir_entry.ext = '   '
  241.     dir_entry.attr = 0x10
  242.    
  243.     dir_entry.ctime_fine = 0 # FIXME
  244.     dir_entry.ctime = 0 # FIXME
  245.     dir_entry.cdate = 0 # FIXME
  246.     dir_entry.adate = 0 # FIXME
  247.     dir_entry.mtime = 0 # FIXME
  248.     dir_entry.mdate = 0 # FIXME
  249.     dir_entry.cluster = empty_cluster
  250.     dir_entry.size = 0
  251.    
  252.     return dir_entry
  253.  
  254. def create_dotdot_dirent(parent_cluster):
  255.     dir_entry = xstruct.create(DOTDOT_DIR_ENTRY)
  256.    
  257.     dir_entry.signature = [0x2e, 0x2e]
  258.     dir_entry.name = '      '
  259.     dir_entry.ext = '   '
  260.     dir_entry.attr = 0x10
  261.    
  262.     dir_entry.ctime_fine = 0 # FIXME
  263.     dir_entry.ctime = 0 # FIXME
  264.     dir_entry.cdate = 0 # FIXME
  265.     dir_entry.adate = 0 # FIXME
  266.     dir_entry.mtime = 0 # FIXME
  267.     dir_entry.mdate = 0 # FIXME
  268.     dir_entry.cluster = parent_cluster
  269.     dir_entry.size = 0
  270.    
  271.     return dir_entry
  272.  
  273. def recursion(head, root, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, parent_cluster):
  274.     "Recursive directory walk"
  275.    
  276.     directory = []
  277.    
  278.     if (not head):
  279.         # Directory cluster preallocation
  280.         empty_cluster = fat.index(0)
  281.         fat[empty_cluster] = 0xffff
  282.        
  283.         directory.append(create_dot_dirent(empty_cluster))
  284.         directory.append(create_dotdot_dirent(parent_cluster))
  285.     else:
  286.         empty_cluster = 0
  287.    
  288.     for name in os.listdir(root):
  289.         canon = os.path.join(root, name)
  290.        
  291.         if (os.path.isfile(canon) and (not name in exclude_names)):
  292.             rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
  293.             directory.append(create_dirent(name, False, rv[0], rv[1]))
  294.        
  295.         if (os.path.isdir(canon) and (not name in exclude_names)):
  296.             rv = recursion(False, canon, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
  297.             directory.append(create_dirent(name, True, rv[0], rv[1]))
  298.    
  299.     if (head):
  300.         outf.seek(root_start)
  301.         for dir_entry in directory:
  302.             outf.write(dir_entry.pack())
  303.     else:
  304.         return write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
  305.  
  306. BOOT_SECTOR = """little:
  307.     uint8_t jmp[3]             /* jump instruction */
  308.     char oem[8]                /* OEM string */
  309.     uint16_t sector            /* bytes per sector */
  310.     uint8_t cluster            /* sectors per cluster */
  311.     uint16_t reserved          /* reserved sectors */
  312.     uint8_t fats               /* number of FATs */
  313.     uint16_t rootdir           /* root directory entries */
  314.     uint16_t sectors           /* total number of sectors */
  315.     uint8_t descriptor         /* media descriptor */
  316.     uint16_t fat_sectors       /* sectors per single FAT */
  317.     uint16_t track_sectors     /* sectors per track */
  318.     uint16_t heads             /* number of heads */
  319.     uint32_t hidden            /* hidden sectors */
  320.     uint32_t sectors_big       /* total number of sectors (if sectors == 0) */
  321.    
  322.     /* Extended BIOS Parameter Block */
  323.     uint8_t drive              /* physical drive number */
  324.     padding[1]                 /* reserved (current head) */
  325.     uint8_t extboot_signature  /* extended boot signature */
  326.     uint32_t serial            /* serial number */
  327.     char label[11]             /* volume label */
  328.     char fstype[8]             /* filesystem type */
  329.     padding[448]               /* boot code */
  330.     uint8_t boot_signature[2]  /* boot signature */
  331. """
  332.  
  333. EMPTY_SECTOR = """little:
  334.     padding[512]               /* empty sector data */
  335. """
  336.  
  337. FAT_ENTRY = """little:
  338.     uint16_t next              /* FAT16 entry */
  339. """
  340.  
  341. def usage(prname):
  342.     "Print usage syntax"
  343.     print prname + " <PATH> <IMAGE>"
  344.  
  345. def main():
  346.     if (len(sys.argv) < 3):
  347.         usage(sys.argv[0])
  348.         return
  349.    
  350.     path = os.path.abspath(sys.argv[1])
  351.     if (not os.path.isdir(path)):
  352.         print "<PATH> must be a directory"
  353.         return
  354.    
  355.     fat16_clusters = 4096
  356.    
  357.     sector_size = 512
  358.     cluster_size = 4096
  359.     dirent_size = 32
  360.     fatent_size = 2
  361.     fat_count = 2
  362.     reserved_clusters = 2
  363.    
  364.     # Make sure the filesystem is large enought for FAT16
  365.     size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
  366.     while (size / cluster_size < fat16_clusters):
  367.         if (cluster_size > sector_size):
  368.             cluster_size /= 2
  369.             size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
  370.         else:
  371.             size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
  372.    
  373.     root_size = align_up(root_entries(path) * dirent_size, cluster_size)
  374.    
  375.     fat_size = align_up(align_up(size, cluster_size) / cluster_size * fatent_size, sector_size)
  376.    
  377.     sectors = (cluster_size + fat_count * fat_size + root_size + size) / sector_size
  378.     root_start = cluster_size + fat_count * fat_size
  379.     data_start = root_start + root_size
  380.    
  381.     outf = file(sys.argv[2], "w")
  382.    
  383.     boot_sector = xstruct.create(BOOT_SECTOR)
  384.     boot_sector.jmp = [0xEB, 0x3C, 0x90]
  385.     boot_sector.oem = "MSDOS5.0"
  386.     boot_sector.sector = sector_size
  387.     boot_sector.cluster = cluster_size / sector_size
  388.     boot_sector.reserved = cluster_size / sector_size
  389.     boot_sector.fats = fat_count
  390.     boot_sector.rootdir = root_size / dirent_size
  391.     if (sectors <= 65535):
  392.         boot_sector.sectors = sectors
  393.     else:
  394.         boot_sector.sectors = 0
  395.     boot_sector.descriptor = 0xF8
  396.     boot_sector.fat_sectors = fat_size / sector_size
  397.     boot_sector.track_sectors = 63
  398.     boot_sector.heads = 6
  399.     boot_sector.hidden = 0
  400.     if (sectors > 65535):
  401.         boot_sector.sectors_big = sectors
  402.     else:
  403.         boot_sector.sectors_big = 0
  404.    
  405.     boot_sector.drive = 0x80
  406.     boot_sector.extboot_signature = 0x29
  407.     boot_sector.serial = random.randint(0, 0x7fffffff)
  408.     boot_sector.label = "HELENOS"
  409.     boot_sector.fstype = "FAT16   "
  410.     boot_sector.boot_signature = [0x55, 0xAA]
  411.    
  412.     outf.write(boot_sector.pack())
  413.    
  414.     empty_sector = xstruct.create(EMPTY_SECTOR)
  415.    
  416.     # Reserved sectors
  417.     for i in range(1, cluster_size / sector_size):
  418.         outf.write(empty_sector.pack())
  419.    
  420.     # FAT tables
  421.     for i in range(0, fat_count):
  422.         for j in range(0, fat_size / sector_size):
  423.             outf.write(empty_sector.pack())
  424.    
  425.     # Root directory
  426.     for i in range(0, root_size / sector_size):
  427.         outf.write(empty_sector.pack())
  428.    
  429.     # Data
  430.     for i in range(0, size / sector_size):
  431.         outf.write(empty_sector.pack())
  432.    
  433.     fat = array.array('L', [0] * (fat_size / fatent_size))
  434.     fat[0] = 0xfff8
  435.     fat[1] = 0xffff
  436.    
  437.     recursion(True, path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, 0)
  438.    
  439.     # Store FAT
  440.     fat_entry = xstruct.create(FAT_ENTRY)
  441.     for i in range(0, fat_count):
  442.         outf.seek(cluster_size + i * fat_size)
  443.         for j in range(0, fat_size / fatent_size):
  444.             fat_entry.next = fat[j]
  445.             outf.write(fat_entry.pack())
  446.    
  447.     outf.close()
  448.    
  449. if __name__ == '__main__':
  450.     main()
  451.