sparseutils

utilities for interacting with sparse files
git clone git://git.vx21.xyz/sparseutils
Log | Files | Refs | README | LICENSE

mksparse.py (2854B)


      1 # mksparse
      2 #
      3 # Copyright 2013-2014  Lars Wirzenius
      4 #
      5 # Copyright 2017 Richard Ipsum
      6 #
      7 # This program is free software: you can redistribute it and/or modify
      8 # it under the terms of the GNU General Public License as published by
      9 # the Free Software Foundation, either version 3 of the License, or
     10 # (at your option) any later version.
     11 #
     12 # This program is distributed in the hope that it will be useful,
     13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 # GNU General Public License for more details.
     16 #
     17 # You should have received a copy of the GNU General Public License
     18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     19 #
     20 # =*= License: GPL-3+ =*=
     21 
     22 DESCRIPTION = '''Create a sparse file.
     23 
     24 This tool reads a specification for how the file is to be made sparse
     25 from stdin: a sequence of "data" and "hole" words, which may be
     26 interspersed with spaces, commas, or the word "a", all of which are
     27 ignored, except that the "data" and "hole" words must have something
     28 in between them.
     29 
     30 For example, to create a file `foo' with 4096 bytes of data followed by
     31 an 8192 byte hole followed by 4096 bytes of data:
     32 
     33 echo data,hole,data | mksparse --hole-size 8192 --data-size 4096 foo
     34 
     35 '''
     36 
     37 import argparse
     38 import os
     39 import sys
     40 
     41 DEFAULT_DATA_SIZE = 4096
     42 DEFAULT_HOLE_SIZE = 1024 ** 2
     43 
     44 def parse_spec():
     45     text = sys.stdin.read()
     46     # Remove commas.
     47     text = ' '.join(text.split(','))
     48 
     49     # Split into words.
     50     words = text.split()
     51 
     52     # Remove any words that are not "data" or "hole".
     53     spec = [x for x in words if x in ('data', 'hole')]
     54 
     55     return spec
     56 
     57 def append_data(f, data_size):
     58     f.write('x' * data_size)
     59     f.flush()
     60 
     61 def append_hole(f, hole_size):
     62     fd = f.fileno()
     63     pos = os.lseek(fd, hole_size, os.SEEK_CUR)
     64     os.ftruncate(fd, pos)
     65 
     66 def main():
     67     parser = argparse.ArgumentParser(
     68                     description=DESCRIPTION,
     69                     formatter_class=argparse.RawDescriptionHelpFormatter)
     70 
     71     parser.add_argument('OUTPUT_FILE')
     72 
     73     parser.add_argument('--hole-size',
     74                         type=int,
     75                         metavar='SIZE',
     76                         default=DEFAULT_HOLE_SIZE,
     77                         help='hole size in bytes')
     78 
     79     parser.add_argument('--data-size',
     80                         type=int,
     81                         metavar='SIZE',
     82                         default=DEFAULT_DATA_SIZE,
     83                         help='data size in bytes')
     84 
     85     args = vars(parser.parse_args())
     86     spec = parse_spec()
     87 
     88     with open(args['OUTPUT_FILE'], 'w') as f:
     89         for word in spec:
     90             if word == 'hole':
     91                 append_hole(f, args['hole_size'])
     92             else:
     93                 assert word == 'data'
     94                 append_data(f, args['data_size'])
     95 
     96 if __name__ == '__main__':
     97     main()