From aa7ad950590dffef2a7dc37135c763e9890a5b9e Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Tue, 20 May 2014 14:35:59 +0100 Subject: Initial commit --- mk-test-data | 32 ++++++++++++++++++ recv-hole | 41 +++++++++++++++++++++++ test.sh | 12 +++++++ xfer-hole.py | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100755 mk-test-data create mode 100755 recv-hole create mode 100755 test.sh create mode 100755 xfer-hole.py diff --git a/mk-test-data b/mk-test-data new file mode 100755 index 0000000..57a3213 --- /dev/null +++ b/mk-test-data @@ -0,0 +1,32 @@ +#!/bin/sh + +data() +{ + echo -n "foobar" >> "$1" +} + +hole() +{ + truncate --size +10240 "$1" +} + + +touch test.empty + +data test.data + +hole test.hole + +data test.data-hole +hole test.data-hole + +hole test.hole-data +data test.hole-data + +data test.data-hole-data +hole test.data-hole-data +data test.data-hole-data + +hole test.hole-data-hole +data test.hole-data-hole +hole test.hole-data-hole diff --git a/recv-hole b/recv-hole new file mode 100755 index 0000000..cd54233 --- /dev/null +++ b/recv-hole @@ -0,0 +1,41 @@ +#!/bin/sh + +set -eu + + +die() +{ + echo "$@" 1>&2 + exit 1 +} + + +recv_hole() +{ + local n + + read n + truncate --size "+$n" "$1" +} + + +recv_data() +{ + local n + + read n + dd of="$1" conv=notrunc oflag=append bs=1 count="$n" status=noxfer 2>&1 | grep -Ev ' records (in|out)$' || true +} + + +output="$1" + +touch "$output" +while read what +do + case "$what" in + DATA) recv_data "$output" ;; + HOLE) recv_hole "$output" ;; + *) die "Unknown instruction: $what" ;; + esac +done diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..04f2d3f --- /dev/null +++ b/test.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -eu + +rm -rf result +mkdir result +for x in test.* +do + echo "============== $x =================" + ./xfer-hole.py "$x" | ./recv-hole "result/$x" + cmp "$x" "result/$x" +done diff --git a/xfer-hole.py b/xfer-hole.py new file mode 100755 index 0000000..0822232 --- /dev/null +++ b/xfer-hole.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + + +import errno +import os +import sys + + +SEEK_DATA = 3 +SEEK_HOLE = 4 + + +filename = sys.argv[1] +fd = os.open(filename, os.O_RDONLY) +pos = 0 + + +DATA = 'data' +HOLE = 'hole' +EOF = 'eof' + + +def safe_lseek(fd, pos, whence): + try: + return os.lseek(fd, pos, whence) + except OSError as e: + if e.errno == errno.ENXIO: + return -1 + raise + + +def current_data_or_pos(fd, pos): + length = safe_lseek(fd, 0, os.SEEK_END) + next_data = safe_lseek(fd, pos, SEEK_DATA) + next_hole = safe_lseek(fd, pos, SEEK_HOLE) + + if pos == length: + return EOF, pos + elif pos == next_data: + return DATA, pos + elif pos == next_hole: + return HOLE, pos + else: + assert False, \ + ("Do not understand: pos=%d next_data=%d next_hole=%d" % + (pos, next_data, next_hole)) + + +def next_data_or_hole(fd, pos): + length = safe_lseek(fd, 0, os.SEEK_END) + next_data = safe_lseek(fd, pos, SEEK_DATA) + next_hole = safe_lseek(fd, pos, SEEK_HOLE) + + if pos == length: + return EOF, pos + elif pos == next_data: + # We are at data. + if next_hole == -1 or next_hole == length: + return EOF, length + else: + return HOLE, next_hole + elif pos == next_hole: + # We are at a hole. + if next_data == -1 or next_data == length: + return EOF, length + else: + return DATA, next_data + else: + assert False, \ + ("Do not understand: pos=%d next_data=%d next_hole=%d" % + (pos, next_data, next_hole)) + + +def find_data_and_holes(fd): + pos = safe_lseek(fd, 0, os.SEEK_CUR) + + kind, pos = current_data_or_pos(fd, pos) + while kind != EOF: + yield kind, pos + kind, pos = next_data_or_hole(fd, pos) + yield kind, pos + + +def make_xfer_instructions(fd): + prev_kind = None + prev_pos = None + for kind, pos in find_data_and_holes(fd): + if prev_kind == DATA: + yield (DATA, prev_pos, pos) + elif prev_kind == HOLE: + yield (HOLE, prev_pos, pos) + prev_kind = kind + prev_pos = pos + + +def copy_slice_from_file(to, fd, start, end): + safe_lseek(fd, start, os.SEEK_SET) + data = os.read(fd, end - start) + to.write(data) + + +for kind, start, end in make_xfer_instructions(fd): + if kind == HOLE: + sys.stdout.write('HOLE\n%d\n' % (end - start)) + elif kind == DATA: + sys.stdout.write('DATA\n%d\n' % (end - start)) + copy_slice_from_file(sys.stdout, fd, start, end) -- cgit v1.2.1