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()