Subversion Repositories HelenOS

Rev

Rev 3523 | Rev 3525 | 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. def align_up(size, alignment):
  40.     "Return size aligned up to alignment"
  41.    
  42.     if (size % alignment == 0):
  43.         return size
  44.    
  45.     return (((size / alignment) + 1) * alignment)
  46.  
  47. def subtree_size(root, cluster_size, dirent_size):
  48.     "Recursive directory walk and calculate size"
  49.    
  50.     size = 0
  51.     files = 2
  52.    
  53.     for name in os.listdir(root):
  54.         canon = os.path.join(root, name)
  55.        
  56.         if (os.path.isfile(canon)):
  57.             size += align_up(os.path.getsize(canon), cluster_size)
  58.             files += 1
  59.        
  60.         if (os.path.isdir(canon)):
  61.             size += subtree_size(canon, cluster_size, dirent_size)
  62.             files += 1
  63.    
  64.     return size + align_up(files * dirent_size, cluster_size)
  65.  
  66. def root_entries(root):
  67.     "Return number of root directory entries"
  68.    
  69.     return len(os.listdir(root))
  70.  
  71. def write_file(path, outf, cluster_size, data_start, fat, reserved_clusters):
  72.     "Store the contents of a file"
  73.    
  74.     size = os.path.getsize(path)
  75.     prev = -1
  76.     first = 0
  77.    
  78.     inf = file(path, "r")
  79.     rd = 0;
  80.     while (rd < size):
  81.         empty_cluster = fat.index(0)
  82.         fat[empty_cluster] = 0xffff
  83.        
  84.         if (prev != -1):
  85.             fat[prev] = empty_cluster
  86.         else:
  87.             first = empty_cluster
  88.        
  89.         prev = empty_cluster
  90.        
  91.         data = inf.read(cluster_size);
  92.         outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
  93.         outf.write(data)
  94.         rd += len(data)
  95.     inf.close()
  96.    
  97.     return first, size
  98.  
  99. def write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster):
  100.     "Store the contents of a directory"
  101.    
  102.     length = len(directory)
  103.     size = length * dirent_size
  104.     prev = -1
  105.     first = 0
  106.    
  107.     i = 0
  108.     rd = 0;
  109.     while (rd < size):
  110.         if (prev != -1):
  111.             empty_cluster = fat.index(0)
  112.             fat[empty_cluster] = 0xffff
  113.             fat[prev] = empty_cluster
  114.         else:
  115.             first = empty_cluster
  116.        
  117.         prev = empty_cluster
  118.        
  119.         data = ''
  120.         data_len = 0
  121.         while ((i < length) and (data_len < cluster_size)):
  122.             if (i == 0):
  123.                 directory[i].cluster = empty_cluster
  124.            
  125.             data += directory[i].pack()
  126.             data_len += dirent_size
  127.             i += 1
  128.        
  129.         outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
  130.         outf.write(data)
  131.         rd += len(data)
  132.    
  133.     return first, size
  134.  
  135. DIR_ENTRY = """little:
  136.     char name[8]               /* file name */
  137.     char ext[3]                /* file extension */
  138.     uint8_t attr               /* file attributes */
  139.     padding[1]                 /* reserved for NT */
  140.     uint8_t ctime_fine         /* create time (fine resolution) */
  141.     uint16_t ctime             /* create time */
  142.     uint16_t cdate             /* create date */
  143.     uint16_t adate             /* access date */
  144.     padding[2]                 /* EA-index */
  145.     uint16_t mtime             /* modification time */
  146.     uint16_t mdate             /* modification date */
  147.     uint16_t cluster           /* first cluster */
  148.     uint32_t size              /* file size */
  149. """
  150.  
  151. DOT_DIR_ENTRY = """little:
  152.     uint8_t signature          /* 0x2e signature */
  153.     char name[7]               /* empty */
  154.     char ext[3]                /* empty */
  155.     uint8_t attr               /* file attributes */
  156.     padding[1]                 /* reserved for NT */
  157.     uint8_t ctime_fine         /* create time (fine resolution) */
  158.     uint16_t ctime             /* create time */
  159.     uint16_t cdate             /* create date */
  160.     uint16_t adate             /* access date */
  161.     padding[2]                 /* EA-index */
  162.     uint16_t mtime             /* modification time */
  163.     uint16_t mdate             /* modification date */
  164.     uint16_t cluster           /* first cluster */
  165.     uint32_t size              /* file size */
  166. """
  167.  
  168. DOTDOT_DIR_ENTRY = """little:
  169.     uint8_t signature[2]       /* 0x2e signature */
  170.     char name[6]               /* empty */
  171.     char ext[3]                /* empty */
  172.     uint8_t attr               /* file attributes */
  173.     padding[1]                 /* reserved for NT */
  174.     uint8_t ctime_fine         /* create time (fine resolution) */
  175.     uint16_t ctime             /* create time */
  176.     uint16_t cdate             /* create date */
  177.     uint16_t adate             /* access date */
  178.     padding[2]                 /* EA-index */
  179.     uint16_t mtime             /* modification time */
  180.     uint16_t mdate             /* modification date */
  181.     uint16_t cluster           /* first cluster */
  182.     uint32_t size              /* file size */
  183. """
  184.  
  185. def mangle_fname(name):
  186.     # FIXME: filter illegal characters
  187.     parts = name.split('.')
  188.    
  189.     if (len(parts) > 0):
  190.         fname = parts[0]
  191.     else:
  192.         fname = ''
  193.        
  194.     return (fname + '          ').upper()[0:8]
  195.  
  196. def mangle_ext(name):
  197.     # FIXME: filter illegal characters
  198.     parts = name.split('.')
  199.    
  200.     if (len(parts) > 1):
  201.         ext = parts[1]
  202.     else:
  203.         ext = ''
  204.    
  205.     return (ext + '   ').upper()[0:3]
  206.  
  207. def create_dirent(name, directory, cluster, size):
  208.     dir_entry = xstruct.create(DIR_ENTRY)
  209.    
  210.     dir_entry.name = mangle_fname(name)
  211.     dir_entry.ext = mangle_ext(name)
  212.    
  213.     if (directory):
  214.         dir_entry.attr = 0x30
  215.     else:
  216.         dir_entry.attr = 0x20
  217.    
  218.     dir_entry.ctime_fine = 0 # FIXME
  219.     dir_entry.ctime = 0 # FIXME
  220.     dir_entry.cdate = 0 # FIXME
  221.     dir_entry.adate = 0 # FIXME
  222.     dir_entry.mtime = 0 # FIXME
  223.     dir_entry.mdate = 0 # FIXME
  224.     dir_entry.cluster = cluster
  225.    
  226.     if (directory):
  227.         dir_entry.size = 0
  228.     else:
  229.         dir_entry.size = size
  230.    
  231.     return dir_entry
  232.  
  233. def create_dot_dirent(empty_cluster):
  234.     dir_entry = xstruct.create(DOT_DIR_ENTRY)
  235.    
  236.     dir_entry.signature = 0x2e
  237.     dir_entry.name = '       '
  238.     dir_entry.ext = '   '
  239.     dir_entry.attr = 0x10
  240.    
  241.     dir_entry.ctime_fine = 0 # FIXME
  242.     dir_entry.ctime = 0 # FIXME
  243.     dir_entry.cdate = 0 # FIXME
  244.     dir_entry.adate = 0 # FIXME
  245.     dir_entry.mtime = 0 # FIXME
  246.     dir_entry.mdate = 0 # FIXME
  247.     dir_entry.cluster = empty_cluster
  248.     dir_entry.size = 0
  249.    
  250.     return dir_entry
  251.  
  252. def create_dotdot_dirent(parent_cluster):
  253.     dir_entry = xstruct.create(DOTDOT_DIR_ENTRY)
  254.    
  255.     dir_entry.signature = [0x2e, 0x2e]
  256.     dir_entry.name = '      '
  257.     dir_entry.ext = '   '
  258.     dir_entry.attr = 0x10
  259.    
  260.     dir_entry.ctime_fine = 0 # FIXME
  261.     dir_entry.ctime = 0 # FIXME
  262.     dir_entry.cdate = 0 # FIXME
  263.     dir_entry.adate = 0 # FIXME
  264.     dir_entry.mtime = 0 # FIXME
  265.     dir_entry.mdate = 0 # FIXME
  266.     dir_entry.cluster = parent_cluster
  267.     dir_entry.size = 0
  268.    
  269.     return dir_entry
  270.  
  271. def recursion(head, root, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, parent_cluster):
  272.     "Recursive directory walk"
  273.    
  274.     directory = []
  275.    
  276.     if (not head):
  277.         # Directory cluster preallocation
  278.         empty_cluster = fat.index(0)
  279.         fat[empty_cluster] = 0xffff
  280.        
  281.         directory.append(create_dot_dirent(empty_cluster))
  282.         directory.append(create_dotdot_dirent(parent_cluster))
  283.     else:
  284.         empty_cluster = 0
  285.    
  286.     for name in os.listdir(root):
  287.         canon = os.path.join(root, name)
  288.        
  289.         if (os.path.isfile(canon)):
  290.             rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
  291.             directory.append(create_dirent(name, False, rv[0], rv[1]))
  292.        
  293.         if (os.path.isdir(canon)):
  294.             rv = recursion(False, canon, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
  295.             directory.append(create_dirent(name, True, rv[0], rv[1]))
  296.    
  297.     if (head):
  298.         outf.seek(root_start)
  299.         for dir_entry in directory:
  300.             outf.write(dir_entry.pack())
  301.     else:
  302.         return write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
  303.  
  304. BOOT_SECTOR = """little:
  305.     uint8_t jmp[3]             /* jump instruction */
  306.     char oem[8]                /* OEM string */
  307.     uint16_t sector            /* bytes per sector */
  308.     uint8_t cluster            /* sectors per cluster */
  309.     uint16_t reserved          /* reserved sectors */
  310.     uint8_t fats               /* number of FATs */
  311.     uint16_t rootdir           /* root directory entries */
  312.     uint16_t sectors           /* total number of sectors */
  313.     uint8_t descriptor         /* media descriptor */
  314.     uint16_t fat_sectors       /* sectors per single FAT */
  315.     uint16_t track_sectors     /* sectors per track */
  316.     uint16_t heads             /* number of heads */
  317.     uint32_t hidden            /* hidden sectors */
  318.     uint32_t sectors_big       /* total number of sectors (if sectors == 0) */
  319.    
  320.     /* Extended BIOS Parameter Block */
  321.     uint8_t drive              /* physical drive number */
  322.     padding[1]                 /* reserved (current head) */
  323.     uint8_t extboot_signature  /* extended boot signature */
  324.     uint32_t serial            /* serial number */
  325.     char label[11]             /* volume label */
  326.     char fstype[8]             /* filesystem type */
  327.     padding[448]               /* boot code */
  328.     uint8_t boot_signature[2]  /* boot signature */
  329. """
  330.  
  331. EMPTY_SECTOR = """little:
  332.     padding[512]               /* empty sector data */
  333. """
  334.  
  335. FAT_ENTRY = """little:
  336.     uint16_t next              /* FAT16 entry */
  337. """
  338.  
  339. def usage(prname):
  340.     "Print usage syntax"
  341.     print prname + " <PATH> <IMAGE>"
  342.  
  343. def main():
  344.     if (len(sys.argv) < 3):
  345.         usage(sys.argv[0])
  346.         return
  347.    
  348.     path = os.path.abspath(sys.argv[1])
  349.     if (not os.path.isdir(path)):
  350.         print "<PATH> must be a directory"
  351.         return
  352.    
  353.     fat16_clusters = 4096
  354.     min_cluster_size = 1024
  355.    
  356.     sector_size = 512
  357.     cluster_size = 4096
  358.     dirent_size = 32
  359.     fatent_size = 2
  360.     fat_count = 2
  361.     reserved_clusters = 2
  362.    
  363.     # Make sure the filesystem is large enought for FAT16
  364.     size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
  365.     while (size / cluster_size < fat16_clusters):
  366.         if (cluster_size > min_cluster_size):
  367.             cluster_size /= 2
  368.             size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
  369.         else:
  370.             size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
  371.    
  372.     root_size = align_up(root_entries(path) * dirent_size, cluster_size)
  373.    
  374.     fat_size = align_up(align_up(size, cluster_size) / cluster_size * fatent_size, sector_size)
  375.    
  376.     sectors = (cluster_size + fat_count * fat_size + root_size + size) / sector_size
  377.     root_start = cluster_size + fat_count * fat_size
  378.     data_start = root_start + root_size
  379.    
  380.     outf = file(sys.argv[2], "w")
  381.    
  382.     boot_sector = xstruct.create(BOOT_SECTOR)
  383.     boot_sector.jmp = [0xEB, 0x3C, 0x90]
  384.     boot_sector.oem = "MSDOS5.0"
  385.     boot_sector.sector = sector_size
  386.     boot_sector.cluster = cluster_size / sector_size
  387.     boot_sector.reserved = cluster_size / sector_size
  388.     boot_sector.fats = fat_count
  389.     boot_sector.rootdir = root_size / dirent_size
  390.     if (sectors <= 65535):
  391.         boot_sector.sectors = sectors
  392.     else:
  393.         boot_sector.sectors = 0
  394.     boot_sector.descriptor = 0xF8
  395.     boot_sector.fat_sectors = fat_size / sector_size
  396.     boot_sector.track_sectors = 63
  397.     boot_sector.heads = 6
  398.     boot_sector.hidden = 0
  399.     if (sectors > 65535):
  400.         boot_sector.sectors_big = sectors
  401.     else:
  402.         boot_sector.sectors_big = 0
  403.    
  404.     boot_sector.drive = 0x80
  405.     boot_sector.extboot_signature = 0x29
  406.     boot_sector.serial = random.randint(0, 0xFFFFFFFF)
  407.     boot_sector.label = "HELENOS"
  408.     boot_sector.fstype = "FAT16   "
  409.     boot_sector.boot_signature = [0x55, 0xAA]
  410.    
  411.     outf.write(boot_sector.pack())
  412.    
  413.     empty_sector = xstruct.create(EMPTY_SECTOR)
  414.    
  415.     # Reserved sectors
  416.     for i in range(1, cluster_size / sector_size):
  417.         outf.write(empty_sector.pack())
  418.    
  419.     # FAT tables
  420.     for i in range(0, fat_count):
  421.         for j in range(0, fat_size / sector_size):
  422.             outf.write(empty_sector.pack())
  423.    
  424.     # Root directory
  425.     for i in range(0, root_size / sector_size):
  426.         outf.write(empty_sector.pack())
  427.    
  428.     # Data
  429.     for i in range(0, size / sector_size):
  430.         outf.write(empty_sector.pack())
  431.    
  432.     fat = array.array('L', [0] * (fat_size / fatent_size))
  433.     fat[0] = 0xfff8
  434.     fat[1] = 0xffff
  435.    
  436.     recursion(True, path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, 0)
  437.    
  438.     # Store FAT
  439.     fat_entry = xstruct.create(FAT_ENTRY)
  440.     for i in range(0, fat_count):
  441.         outf.seek(cluster_size + i * fat_size)
  442.         for j in range(0, fat_size / fatent_size):
  443.             fat_entry.next = fat[j]
  444.             outf.write(fat_entry.pack())
  445.    
  446.     outf.close()
  447.    
  448. if __name__ == '__main__':
  449.     main()
  450.