2011-12-03 02:38:52 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
"""\
|
|
|
|
|
==============================================
|
|
|
|
|
mksparse.py - sparse file / disk image creator
|
|
|
|
|
==============================================
|
|
|
|
|
|
2020-10-11 20:06:24 +03:00
|
|
|
Usage: mksparse.py <imagefile.img> <size>[k|m|g|t]
|
2013-11-20 02:14:04 +02:00
|
|
|
|
2011-12-03 02:38:52 +02:00
|
|
|
This will create <imagefile.img> with size <size>. If the suffix is
|
|
|
|
|
not given then the size is in bytes, 'k' stands for kilobytes (1024),
|
2020-10-11 20:06:24 +03:00
|
|
|
'm' for megabytes and 'g' for gigabyes and 't' for terabytes.
|
|
|
|
|
K/M/G/T are not implemented, one can type some 000-s easy enough.
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
Simple python script that creates sparse files on unix / Win2k NTFS5.
|
|
|
|
|
This script opens a file for writing, seeks at the desired position
|
|
|
|
|
(or file size) and truncates the file there. Can be handy while
|
2013-11-20 02:14:04 +02:00
|
|
|
playing with KVM / qemu / bochs / loopback images.
|
2011-12-03 02:38:52 +02:00
|
|
|
|
2020-10-11 15:06:57 +03:00
|
|
|
Tested on linux-2.6+ only.
|
|
|
|
|
|
2020-10-11 20:06:24 +03:00
|
|
|
NB: Check fallocate from util-linux, may work even better for you.
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
Author: Doncho N. Gunchev <gunchev at gmail dot com>
|
|
|
|
|
Based on Brad Watson's work mkimage.py from
|
|
|
|
|
http://lists.gnu.org/archive/html/qemu-devel/2004-07/msg00733.html
|
|
|
|
|
http://qemu.dad-answers.com/download/qemu/utilities/QEMU-HD-Create/
|
|
|
|
|
"""
|
|
|
|
|
|
2011-12-03 03:03:44 +02:00
|
|
|
import os.path
|
|
|
|
|
import re
|
|
|
|
|
import sys
|
|
|
|
|
|
2020-10-11 15:06:57 +03:00
|
|
|
__version__ = "0.3"
|
2011-12-03 02:38:52 +02:00
|
|
|
__author__ = "Doncho Gunchev <gunchev@gmail.com>, Brad Watson"
|
2020-10-11 15:06:57 +03:00
|
|
|
__depends__ = ['Python-3']
|
2020-10-11 20:06:24 +03:00
|
|
|
# __copyright__ = """Ask Brad Watson, I want nothing."""
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
|
2011-12-03 03:03:44 +02:00
|
|
|
class MkSparseError(Exception):
|
|
|
|
|
"""MkSpace errors"""
|
|
|
|
|
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
def mk_sparse(file_name, file_size):
|
2020-10-11 15:06:57 +03:00
|
|
|
"""Create a sparse file by truncating it at given position"""
|
2011-12-03 02:38:52 +02:00
|
|
|
try:
|
2020-10-11 20:06:24 +03:00
|
|
|
sparse_file = open(file_name, 'wb+')
|
|
|
|
|
except EnvironmentError as exc:
|
|
|
|
|
raise MkSparseError("Error: Can't create file {!r}:\n\t{}".format(file_name, exc))
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Note that I don't want (you too) to write() anything in the file
|
|
|
|
|
# because this will consume at least one sector/block.
|
|
|
|
|
sparse_file.truncate(int(file_size))
|
|
|
|
|
except EnvironmentError as exc:
|
2011-12-03 02:38:52 +02:00
|
|
|
try:
|
2020-10-11 20:06:24 +03:00
|
|
|
os.unlink(file_name) # clean the mess...
|
|
|
|
|
except EnvironmentError:
|
|
|
|
|
pass
|
|
|
|
|
try: # could close in finally, but would lose the option to report close error properly.
|
|
|
|
|
sparse_file.close()
|
|
|
|
|
except EnvironmentError:
|
|
|
|
|
pass
|
|
|
|
|
raise MkSparseError("Error: Can't truncate {!r}:\n\t{}".format(file_name, exc))
|
|
|
|
|
|
2011-12-03 03:03:44 +02:00
|
|
|
try:
|
|
|
|
|
sparse_file.close()
|
2020-10-11 20:06:24 +03:00
|
|
|
except EnvironmentError as exc:
|
|
|
|
|
raise MkSparseError("Error: Can't close {!r}:\n\t{}".format(file_name, exc))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_file_size(file_size):
|
|
|
|
|
'''file size validation and parsing'''
|
|
|
|
|
try:
|
|
|
|
|
size_str, dim = re.match('^(\\d+)([kmgt])?$', file_size).groups()
|
|
|
|
|
except AttributeError: # if it did not match we get None, which has no .groups...
|
|
|
|
|
raise ValueError('Bad image size given: {!r}'.format(file_size))
|
|
|
|
|
|
|
|
|
|
size = int(size_str) # can not raise...
|
|
|
|
|
if dim is None:
|
|
|
|
|
return size
|
|
|
|
|
if dim == 'k':
|
|
|
|
|
return size * 1024
|
|
|
|
|
if dim == 'm':
|
|
|
|
|
return size * 1024 * 1024
|
|
|
|
|
if dim == 'g':
|
|
|
|
|
return size * 1024 * 1024 * 1024
|
|
|
|
|
if dim == 't':
|
|
|
|
|
return size * 1024 * 1024 * 1024 * 1024
|
|
|
|
|
|
|
|
|
|
raise NotImplementedError('Size modifier {!r} not handled.'.format(dim))
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
|
2011-12-03 03:03:44 +02:00
|
|
|
def main():
|
|
|
|
|
"""The main function for a command line execution"""
|
2020-10-11 20:06:24 +03:00
|
|
|
my_name = os.path.basename(sys.argv[0])
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
if len(sys.argv) != 3:
|
2020-10-11 20:06:24 +03:00
|
|
|
# .pyo (docstrings stripped) workaround, no idea if needed any more or at all ;-) just having fun obv.
|
|
|
|
|
print((__doc__ and __doc__ or ("Usage: " + my_name + " <image-name> <size>[kmgt]")), file=sys.stderr)
|
2020-10-11 15:06:57 +03:00
|
|
|
print("Version:", __version__, file=sys.stderr)
|
2011-12-03 02:38:52 +02:00
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
# 'Process' command line parameters
|
|
|
|
|
file_name = sys.argv[1]
|
|
|
|
|
file_size = sys.argv[2]
|
|
|
|
|
|
2020-10-11 20:06:24 +03:00
|
|
|
# Check if the file exists, -f (force) would be a good parameter to add
|
2011-12-03 02:38:52 +02:00
|
|
|
if os.path.exists(file_name):
|
2020-10-11 20:06:24 +03:00
|
|
|
print("{}: Error: file (directory) {!r} already exists!".format(my_name, file_name), file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
2011-12-03 02:38:52 +02:00
|
|
|
|
|
|
|
|
try:
|
2020-10-11 20:06:24 +03:00
|
|
|
mk_sparse(file_name, parse_file_size(file_size))
|
2020-10-11 15:06:57 +03:00
|
|
|
except MkSparseError as exc:
|
2020-10-11 20:06:24 +03:00
|
|
|
print('{}: {}'.format(my_name, exc), file=sys.stderr)
|
|
|
|
|
sys.exit(2)
|
|
|
|
|
except ValueError as exc:
|
|
|
|
|
print('{}: {}'.format(my_name, exc), file=sys.stderr)
|
|
|
|
|
sys.exit(3)
|
|
|
|
|
except NotImplementedError as exc:
|
|
|
|
|
print('{}: {}'.format(my_name, exc), file=sys.stderr)
|
|
|
|
|
sys.exit(4)
|
2011-12-03 03:03:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|