1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
#!/usr/bin/env bash
#
# Example Bash plugin.
#
# This example can be freely used for any purpose.
#
# Run it from the build directory like this:
#
# ./nbdkit -f -v sh ./plugins/sh/examples/example.sh file=disk.img
#
# Or run it after installing nbdkit like this:
#
# nbdkit -f -v sh ./plugins/sh/examples/example.sh file=disk.img
#
# The file= prefix is optional: see nbdkit(1) section "Magic
# parameters" and the magic_config_key setting below.
#
# The -f -v arguments are optional. They cause the server to stay in
# the foreground and print debugging, which is useful when testing.
#
# You can connect to the server using guestfish or qemu, eg:
#
# guestfish --format=raw -a nbd://localhost
# ><fs> run
# ><fs> list-filesystems
# ><fs> mount /dev/sda1 /
# Note that the exit code of the script matters:
# 0 => OK
# 1 => Error
# 2 => Method is missing
# 3 => False
# For other values, see the nbdkit-sh-plugin(3) manual page.
# Check we're being run from nbdkit.
#
# Because the script has to be executable (for nbdkit to run it) there
# is a danger that someone could run the script standalone which won't
# work. Use two tests to try to make sure we are run from nbdkit:
#
# - $tmpdir is set to a random, empty directory by nbdkit. Note the
# contents are deleted when nbdkit exits.
#
# - $1 is set (to a method name).
if [ ! -d $tmpdir ] || [ "x$1" = "x" ]; then
echo "$0: this script must be run from nbdkit" >&2
echo "Use ‘nbdkit sh $0’" >&2
exit 1
fi
# We make a symlink to the file in the tmpdir directory.
f=$tmpdir/file
case "$1" in
dump_plugin)
# This is called from: nbdkit sh example.sh --dump-plugin
echo "example_sh=1"
;;
config)
# We expect a file=... parameter pointing to the file to serve.
if [ "$2" = "file" ]; then
if [ ! -r "$3" ]; then
echo "file $3 does not exist or is not readable" >&2
exit 1
fi
ln -sf "$(realpath "$3")" $f
else
echo "unknown parameter $2=$3" >&2
exit 1
fi
;;
config_complete)
# Check the file parameter was passed.
if [ ! -L $f ]; then
echo "file parameter missing" >&2
exit 1
fi
;;
thread_model)
# You must opt-in for parallel behavior; the default is
# serialize_all_requests.
echo parallel
;;
list_exports | default_export)
# The following lists the names of all files in the current
# directory that do not contain whitespace, backslash, or single
# quotes. No description accompanies the export names.
# The first file listed is used when a client requests export ''.
find . -type f \! -name "*['\\\\[:space:]]*"
;;
open)
# Open a new client connection.
# Create a directory to store per-connection state. The
# directory name is printed out by the mktemp command, that
# output is captured by nbdkit, and it is passed back as the
# handle parameter ($2) in subsequent calls below.
#
# You can use this directory to store per-connection state,
# and it along with everything in $tmpdir is cleaned up by
# nbdkit on exit.
#
# (This plugin does not actually use per-connection state,
# it's just an example.)
mktemp -d $tmpdir/handle-XXXXXX
;;
get_size)
# Print the disk size on stdout.
stat -L -c '%s' $f || exit 1
;;
pread)
# Read the requested part of the disk and write to stdout.
dd iflag=skip_bytes,count_bytes skip=$4 count=$3 if=$f || exit 1
;;
pwrite)
# Copy data from stdin and write it to the disk.
dd oflag=seek_bytes conv=notrunc seek=$4 of=$f || exit 1
;;
can_write)
# If we provide a pwrite method, we must provide this method
# (and similarly for flush and trim). See nbdkit-sh-plugin(3)
# for details. This will exit 0 (below) which means true.
# Use ‘exit 3’ if false.
;;
trim)
# Punch a hole in the backing file, if supported.
fallocate -p -o $4 -l $3 -n $f || exit 1
;;
can_trim)
# We can trim if the fallocate command exists.
fallocate --help >/dev/null 2>&1 || exit 3
;;
zero)
# Efficiently zero the backing file, if supported.
# Try punching a hole if flags includes may_trim, otherwise
# request to leave the zeroed range allocated.
# Attempt a fallback to write on any failure, but this requires
# specific prefix on stderr prior to any message from fallocate;
# exploit the fact that stderr is ignored on success.
echo ENOTSUP >&2
case ,$5, in
*,may_trim,*) fallocate -p -o $4 -l $3 -n $f || exit 1 ;;
*) fallocate -z -o $4 -l $3 -n $f || exit 1 ;;
esac
;;
can_zero)
# We can efficiently zero if the fallocate command exists.
fallocate --help >/dev/null 2>&1 || exit 3
;;
# cache)
# Implement an efficient prefetch, if desired.
# It is intentionally omitted from this example.
# dd iflag=skip_bytes,count_bytes skip=$4 count=$3 \
# if=$f of=/dev/null || exit 1
# ;;
can_cache)
# Caching is not advertised to the client unless can_cache prints
# a tri-state value. Here, we choose for caching to be a no-op,
# by omitting counterpart handling for 'cache'.
echo native
;;
extents)
# Report extent (block status) information as 'offset length [type]'.
# This example could omit the handler, since it just matches
# the default behavior of treating everything as data; but if
# your code can detect holes, this demonstrates the usage.
echo "$4 $(($3/2)) 0"
echo "$(($4+$3/2)) $(($3/2))"
# echo "$4 $3 hole,zero"
;;
can_extents)
# Similar to can_write
;;
magic_config_key)
# See nbdkit(1) section "Magic parameters" for what this does.
echo "file"
;;
*)
# Unknown methods must exit with code 2.
exit 2
esac
exit 0
|