first commit
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,3 +8,7 @@ ebin/*.beam
|
||||
rel/example_project
|
||||
.concrete/DEV_MODE
|
||||
.rebar
|
||||
*.fossil
|
||||
*~
|
||||
_build
|
||||
.rebar.lock
|
||||
|
||||
39
LICENSE
39
LICENSE
@@ -1,29 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018,
|
||||
All rights reserved.
|
||||
Copyright (c) 2017 Mathieu Kerjouan <contact@steepath.eu>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
40
README.md
Normal file
40
README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# zfs
|
||||
|
||||
Erlang ZFS High and Low Level implementation.
|
||||
|
||||
## Usage
|
||||
|
||||
This is an Erlang Library, you can use it on any project
|
||||
based on Licensing.
|
||||
|
||||
## Documentation
|
||||
|
||||
This project is born from one idea, reverse engineering ZFS
|
||||
as gray box, from the hard way. We want to document every
|
||||
ZFS aspect and offer a high level implementation to check if
|
||||
specification and current existing documentation fit well.
|
||||
|
||||
You can find more documentation in doc directory at the root
|
||||
of this project.
|
||||
|
||||
## Build
|
||||
|
||||
$ rebar3 compile
|
||||
|
||||
## Test
|
||||
|
||||
Erlang ZFS implementation is also a test battery for ZFS, you
|
||||
can run different test to check if our implementation is well
|
||||
supported on your hardware, but also directly on your OS.
|
||||
|
||||
For running unit test:
|
||||
|
||||
$ rebar3 eunit
|
||||
|
||||
For running common_test:
|
||||
|
||||
$ rebar3 ct
|
||||
|
||||
For running local low level test:
|
||||
|
||||
$ cd c_src && make test
|
||||
0
c_src/Makefile
Normal file
0
c_src/Makefile
Normal file
26
doc/README.md
Normal file
26
doc/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Documentation
|
||||
|
||||
## Why implement ZFS in Erlang?
|
||||
|
||||
Erlang is natively distributed, high level and stable language.
|
||||
ZFS is complexe file-system requiring lot of knowledge. This
|
||||
file-system is also an old-one, developped in the 2000's, it
|
||||
wasn't developped with security in mind. ZFS stream for example
|
||||
should be trusted.
|
||||
|
||||
Erlang is particularly efficient to parse binary string, ZFS
|
||||
is a binary data-structure...
|
||||
|
||||
## What can we do with this library?
|
||||
|
||||
Currently, this library is only used to parse and check
|
||||
ZFS stream send from `zfs send` command.
|
||||
|
||||
## Some idea of concrete work?
|
||||
|
||||
* authenticated flow from external customers
|
||||
* authenticated flow from internal servers
|
||||
* zfs stream split
|
||||
* zfs stream fuzzing
|
||||
* zfs stream distributed check
|
||||
* and much more...
|
||||
126
doc/notes.md
Normal file
126
doc/notes.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Notes
|
||||
|
||||
This is a kind of specification documentation
|
||||
base on what I need and how to use it.
|
||||
|
||||
## ZFS Stream Parsing
|
||||
|
||||
First thing first. ZFS library for Erlang was made
|
||||
to easily parse ZFS send/receive stream and split
|
||||
it in small piece.
|
||||
|
||||
Parsing a zfs stream should be simple and return
|
||||
standard and comprehensible data-structure.
|
||||
|
||||
zfs_stream:parse(Stream).
|
||||
|
||||
Sometime, our ZFS stream is a bit larger than expected,
|
||||
so, we can filter it. This filtering will be done with
|
||||
Erlang Match syntax.
|
||||
|
||||
zfs_stream:parse(Stream, [{'object', '_'}]).
|
||||
|
||||
### Specific Stream Parsing Feature
|
||||
|
||||
zfs_stream:parse(Stream, []).
|
||||
|
||||
ZFS Stream is a complex data-structure containing
|
||||
different type of objects. We can parse it independantly
|
||||
of each others.
|
||||
|
||||
% dmu replay record is a full
|
||||
% zfs stream sent from zfs send command
|
||||
dmu_replay_record:parse(Stream).
|
||||
dmu_replay_record:parse(Stream, _Opts).
|
||||
|
||||
% drr_begin is the first data structure
|
||||
% used in ZFS Stream and contain common
|
||||
% information about this stream
|
||||
drr_begin:parse(Stream).
|
||||
drr_begin:parse(Stream; _Opts).
|
||||
|
||||
% drr_end
|
||||
drr_end:parse(Stream).
|
||||
drr_end:parse(Stream, _Opts).
|
||||
|
||||
% drr_object
|
||||
drr_object:parse(Stream).
|
||||
drr_object:parse(Stream, _Opts).
|
||||
|
||||
% drr_freeobjects
|
||||
drr_freeobjects:parse(Stream).
|
||||
drr_freeobjects:parse(Stream, _Opts).
|
||||
|
||||
% drr_write
|
||||
drr_write:parse(Stream).
|
||||
drr_write:parse(Stream, _Opts).
|
||||
|
||||
% drr_write_byref
|
||||
drr_write_byref:parse(Stream).
|
||||
drr_write_byref:parse(Stream, _Opts).
|
||||
|
||||
% drr_free
|
||||
drr_free:parse(Stream).
|
||||
drr_free:parse(Stream, _Opts).
|
||||
|
||||
% drr_spill
|
||||
drr_spill:parse(Stream).
|
||||
drr_spill:parse(Stream, _Opts).
|
||||
|
||||
Those functions return erlang map data-structure only
|
||||
if the stream or part of the stream is the right object.
|
||||
dmu_replay_record:parse/1 parse an entire ZFS stream and is
|
||||
equivalent with zfs_stream:parse/1 function.
|
||||
|
||||
## ZFS Stream Splitting
|
||||
|
||||
A ZFS Stream is a range of ordered transactional ZFS objects.
|
||||
This library is made to split stream in small pieces.
|
||||
|
||||
zfs_stream:split(Stream).
|
||||
zfs_stream:split(Stream, _Opts).
|
||||
|
||||
You can split it in different way, just split all logical part
|
||||
in bitstring or parse each part with erlang abstraction.
|
||||
|
||||
zfs_stream:split(Stream, [bitstring]).
|
||||
zfs_stream:split(Stream, [map]).
|
||||
|
||||
## ZFS Stream Analysing
|
||||
|
||||
All ZFS data-structure are delivered with checksum, analyzing it
|
||||
give us the change to validate every piece of a stream based
|
||||
on checksum.
|
||||
|
||||
zfs_stream:analyze(Stream).
|
||||
zfs_stream:analyze(Stream, _Opts).
|
||||
|
||||
## ZFS Stream Crafting
|
||||
|
||||
If we have the possibility to analyze or read a stream, we have
|
||||
also the possibility to craft any kind of ZFS stream from scratch.
|
||||
|
||||
zfs_stream:craft(Type, Content).
|
||||
|
||||
## ZFS Stream Fuzzing
|
||||
|
||||
ZFS is executed in Kernel Land, if something goes wrong, in the
|
||||
best case, system crash, in the worst case, an attacker can
|
||||
install a rootkit or gain privilege access to our server. Fuzzing
|
||||
is a technique used widely on security to check program inputs.
|
||||
|
||||
This high level library give us the possibility to randomly craft
|
||||
ZFS stream and check them but also alterate an existing one.
|
||||
|
||||
% create a totally random zfs stream from scratch
|
||||
zfs_stream:fuzz().
|
||||
|
||||
% generete an object stream based on Type
|
||||
zfs_stream:fuzz(Type).
|
||||
|
||||
% extra options
|
||||
zfs_stream:fuzz(Type, _Opts).
|
||||
zfs_stream:fuzz(Type, [{destination, Destination}
|
||||
,{source, Source}
|
||||
,{alter, []}
|
||||
,{format, Format}]).
|
||||
25
doc/resources.md
Normal file
25
doc/resources.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Resources
|
||||
|
||||
== Video
|
||||
|
||||
* https://www.youtube.com/channel/UC0IK6Y4Go2KtRueHDiQcxow
|
||||
|
||||
== Website
|
||||
|
||||
* http://www.open-zfs.org/
|
||||
|
||||
== Papers
|
||||
|
||||
* http://www.giis.co.in/Zfs_ondiskformat.pdf
|
||||
* https://www.usenix.org/legacy/events/fast10/tech/full_papers/zhang.pdf
|
||||
* https://www.usenix.org/legacy/event/lisa07/htgr_files/bonwick_htgr.pdf
|
||||
* https://www.usenix.org/system/files/conference/inflow14/inflow14-zuck.pdf
|
||||
* http://supertech.csail.mit.edu/papers/YuanZhJa16.pdf
|
||||
* https://cs.uwaterloo.ca/~mashti/cs854-f17/papers/zfs.pdf
|
||||
* http://www.usenix.net/legacy/events/fast10/tech/full_papers/zhang.pdf
|
||||
|
||||
== Source code
|
||||
|
||||
* http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
|
||||
* http://bxr.su/FreeBSD/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
|
||||
* http://bxr.su/FreeBSD/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
|
||||
2
rebar.config
Normal file
2
rebar.config
Normal file
@@ -0,0 +1,2 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
{deps, []}.
|
||||
48
src/dmu.erl
Normal file
48
src/dmu.erl
Normal file
@@ -0,0 +1,48 @@
|
||||
%%%===================================================================
|
||||
%%%
|
||||
%%%===================================================================
|
||||
-module(dmu).
|
||||
-export([replay_record/0, replay_record/1, replay_record/2]).
|
||||
-include("zfs.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-ifdef(debug).
|
||||
-compile(export_all).
|
||||
-endif.
|
||||
|
||||
%%%===================================================================
|
||||
%%% API SECTION - EXPORTED FUNCTION
|
||||
%%%===================================================================
|
||||
-spec replay_record() -> list().
|
||||
replay_record() ->
|
||||
[drr_type, drr_payloadlen, drr_begin].
|
||||
|
||||
-spec replay_record(bitstring()) -> {ok, map(), bitstring()}.
|
||||
replay_record(Bitstring) ->
|
||||
replay_record(Bitstring, []).
|
||||
|
||||
-spec replay_record(bitstring(), list()) -> {ok, map(), bitstring()}.
|
||||
replay_record(<<0:32, PayloadLen:32, Rest/bitstring>>, _Opts) ->
|
||||
<< Begin:(304*8)/bitstring, _/bitstring>> = Rest,
|
||||
{ok, Struct, <<>>} = drr:struct_begin(Begin),
|
||||
Init = #{ drr_type => 'begin'
|
||||
, drr_payloadlen => PayloadLen
|
||||
, content => Struct },
|
||||
{ok, Init, Rest};
|
||||
replay_record(<<1:32, PayloadLen:32, Rest/bitstring>>, _Opts) ->
|
||||
ok.
|
||||
|
||||
%%%===================================================================
|
||||
%%% PRIVATE SECTION
|
||||
%%%===================================================================
|
||||
-spec drr_payloadlen(bitstring()) -> {ok, map(), bitstring()}.
|
||||
drr_payloadlen(<<PayloadLen:32/little, Rest/bitstring>>) ->
|
||||
{ok, #{ payloadlen => PayloadLen }, Rest}.
|
||||
|
||||
drr_payloadlen_test() ->
|
||||
[ drr_payloadlen_test(X) || X <- lists:seq(1,1) ].
|
||||
|
||||
drr_payloadlen_test(1) ->
|
||||
IN = <<1,0,0,0>>,
|
||||
OUT = {ok, #{ payloadlen => 1 }, <<>>},
|
||||
?assertEqual(OUT, drr_payloadlen(IN)).
|
||||
|
||||
812
src/drr.erl
Normal file
812
src/drr.erl
Normal file
@@ -0,0 +1,812 @@
|
||||
%%%===================================================================
|
||||
%%% @author Mathieu Kerjouan
|
||||
%%% @copyright 2017
|
||||
%%% @version 0.1.0
|
||||
%%% @title drr data structure implementation
|
||||
%%% @doc
|
||||
%%% @end
|
||||
%%%===================================================================
|
||||
-module(drr).
|
||||
-export([struct_begin/0, struct_begin/1, struct_begin/2]).
|
||||
-export([struct_end/0, struct_end/1, struct_end/2]).
|
||||
-export([struct_object/0]).
|
||||
-export([struct_freeobjects/0]).
|
||||
-export([struct_write/0]).
|
||||
-export([struct_free/0]).
|
||||
-export([struct_write_byref/0]).
|
||||
-export([struct_spill/0]).
|
||||
-include("zfs.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-ifdef(debug).
|
||||
-compile(export_all).
|
||||
-endif.
|
||||
|
||||
%%%===================================================================
|
||||
%%% API SECTION - EXPORTED FUNCTION
|
||||
%%%===================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_begin() -> list().
|
||||
struct_begin() ->
|
||||
[ drr_magic, drr_versioninfo, drr_creation_time, drr_type
|
||||
, drr_flags, drr_toguid, drr_fromguid, drr_toname ].
|
||||
|
||||
-spec struct_begin(bitstring())
|
||||
-> {ok, map()} |
|
||||
{ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
struct_begin(Bitstring) ->
|
||||
struct_begin(Bitstring, []).
|
||||
|
||||
-spec struct_begin(bitstring(), list())
|
||||
-> {ok, map()} |
|
||||
{ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
struct_begin(Bitstring, _Opts) ->
|
||||
Return = #{},
|
||||
|
||||
{ok, Magic, MagicRest} = drr_magic(Bitstring),
|
||||
Return1 = maps:merge(Return, Magic),
|
||||
|
||||
{ok, VersionInfo, VersionInfoRest} = drr_versioninfo(MagicRest),
|
||||
Return2 = maps:merge(Return1, VersionInfo),
|
||||
|
||||
{ok, CreationTime, CreationTimeRest} = drr_creation_time(VersionInfoRest),
|
||||
Return3 = maps:merge(Return2, CreationTime),
|
||||
|
||||
{ok, Type, TypeRest} = objset_type(CreationTimeRest),
|
||||
Return4 = maps:merge(Return3, Type),
|
||||
|
||||
{ok, Flags, FlagsRest} = drr_flags(TypeRest),
|
||||
Return5 = maps:merge(Return4, Flags),
|
||||
|
||||
{ok, ToGuid, ToGuidRest} = drr_toguid(FlagsRest),
|
||||
Return6 = maps:merge(Return5, ToGuid),
|
||||
|
||||
{ok, FromGuid, FromGuidRest} = drr_fromguid(ToGuidRest),
|
||||
Return7 = maps:merge(Return6, FromGuid),
|
||||
|
||||
{ok, ToName, ToNameRest} = drr_toname(FromGuidRest),
|
||||
Return8 = maps:merge(Return7, ToName),
|
||||
|
||||
{ok, Return8, ToNameRest}.
|
||||
|
||||
struct_begin_test() ->
|
||||
[ struct_begin_test(X) || X <- [1] ].
|
||||
struct_begin_test(1) ->
|
||||
IN = <<172, 203, 186, 245, 2, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 102,
|
||||
47, 41, 90, 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 54, 80, 15,
|
||||
33, 249, 114, 233, 135, 0, 0, 0, 0, 0, 0, 0, 0, 112, 111,
|
||||
111, 108, 64, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >>,
|
||||
OUT = {ok
|
||||
, #{ magic => <<16#2F5bacbac:64/little>>
|
||||
, creation_time => 16#5a292f66
|
||||
, type => zfs
|
||||
, featuresflags => 4
|
||||
, flags => <<4,0,0,0>>
|
||||
, hdrtype => substream
|
||||
, toguid => <<54,80,15,33,249,114,233,135>>
|
||||
, fromguid => <<0,0,0,0,0,0,0,0>>
|
||||
, toname => <<"pool@test">>
|
||||
% , versioninfo => <<17,0,0,0,0,0,0,0>>
|
||||
}
|
||||
, <<>>
|
||||
},
|
||||
?assertEqual(OUT, struct_begin(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_end() -> list().
|
||||
struct_end() ->
|
||||
[ drr_checksum, drr_toguid ].
|
||||
|
||||
-spec struct_end(bitstring())
|
||||
-> {ok, map()} |
|
||||
{ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
struct_end(Bitstring) ->
|
||||
struct_end(Bitstring, []).
|
||||
|
||||
-spec struct_end(bitstring(), list())
|
||||
-> {ok, map()} |
|
||||
{ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
struct_end(Bitstring, _Opts) ->
|
||||
Init = #{},
|
||||
|
||||
{ok, Checksum, ChecksumRest} = drr_checksum(Bitstring),
|
||||
Return1 = maps:merge(Checksum, Init),
|
||||
|
||||
{ok, ToGuid, ToGuidRest} = drr_toguid(ChecksumRest),
|
||||
Return2 = maps:merge(ToGuid, Return1),
|
||||
|
||||
{ok, Return2, ToGuidRest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_object() -> list().
|
||||
struct_object() ->
|
||||
[ drr_object, drr_type, drr_bonustype, drr_blksz, drr_bonuslen
|
||||
, drr_checksumtype, drr_compress, {drr_pad, 6}, drr_toguid ].
|
||||
|
||||
% -spec object(bitstring())
|
||||
% -> {ok, drr_object()} |
|
||||
% {ok, drr_object(), bitstring()} |
|
||||
% {error, term()}.
|
||||
% -spec object(bitstring(), list())
|
||||
% -> {ok, drr_object()} |
|
||||
% {ok, drr_object(), bitstring()} |
|
||||
% {error, term()}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_freeobjects() -> list().
|
||||
struct_freeobjects() ->
|
||||
[ drr_firstobj, drr_numobjs, drr_toguid ].
|
||||
|
||||
-spec struct_freeobjects(bitstring())
|
||||
-> {ok, map()} |
|
||||
{ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
struct_freeobjects(Bitstring) ->
|
||||
struct_freeobjects(Bitstring, []).
|
||||
|
||||
-spec struct_freeobjects(bitstring(), list())
|
||||
-> {ok, map()} |
|
||||
{ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
struct_freeobjects(Bitstring, _Opts) ->
|
||||
{wip, Bitstring}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_write() -> list().
|
||||
struct_write() ->
|
||||
[ drr_object, drr_type, {drr_pad, 4}, drr_offset, drr_length
|
||||
, drr_toguid, drr_checksumtype, drr_checksumflags, {drr_pad, 2}
|
||||
, drr_key ].
|
||||
|
||||
% -spec write(bitstring())
|
||||
% -> {ok, drr_write()} |
|
||||
% {ok, drr_write(), bitstring()} |
|
||||
% {error, term()}.
|
||||
% -spec write(bitstring(), list())
|
||||
% -> {ok, drr_write()} |
|
||||
% {ok, drr_write(), bitstring()} |
|
||||
% {error, term()}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_free() -> list().
|
||||
struct_free() ->
|
||||
[ drr_object, drr_offset, drr_length, drr_toguid ].
|
||||
|
||||
% -spec free(bitstring())
|
||||
% -> {ok, drr_free()} |
|
||||
% {ok, drr_free(), bitstring()} |
|
||||
% {error, term()}.
|
||||
% -spec free(bitstring(), list())
|
||||
% -> {ok, drr_free()} |
|
||||
% {ok, drr_free(), bitstring()} |
|
||||
% {error, term()}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_write_byref() -> list().
|
||||
struct_write_byref() ->
|
||||
[ drr_object, drr_offset, drr_length, drr_toguid, drr_refguid
|
||||
, drr_refobject, drr_refoffset, drr_checksumtype, drr_checksumflags
|
||||
, {drr_pad, 6}, drr_key ].
|
||||
|
||||
% -spec byref(bitstring())
|
||||
% -> {ok, drr_byref()} |
|
||||
% {ok, drr_byref(), bitstring()} |
|
||||
% {error, term()}.
|
||||
% -spec byref(bitstring(), list())
|
||||
% -> {ok, drr_byref()} |
|
||||
% {ok, drr_byref(), bitstring()} |
|
||||
% {error, term()}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec struct_spill() -> list().
|
||||
struct_spill() ->
|
||||
[ drr_object, drr_length, drr_toguid, {drr_pad, 4} ].
|
||||
|
||||
% -spec struct_spill(bitstring())
|
||||
% -> {ok, map()} |
|
||||
% {ok, map(), bitstring()} |
|
||||
% {error, term()}.
|
||||
% -spec struct_spill(bitstring(), list())
|
||||
% -> {ok, map()} |
|
||||
% {ok, map(), bitstring()} |
|
||||
% {error, term()}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% INTERNAL SECTION - PRIVATE FUNCTION
|
||||
%%%===================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_blksz(bitstring())
|
||||
-> {ok, map(), bitstring()}.
|
||||
drr_blksz(<<BlkSz:32/little, Rest/bitstring>>) ->
|
||||
{ok, #{ blksz => BlkSz }, Rest}.
|
||||
|
||||
drr_blksz_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_bonuslen(bitstring())
|
||||
-> {ok, map(), bitstring()}.
|
||||
drr_bonuslen(<<BonusLen:32/little, Rest/bitstring>>) ->
|
||||
{ok, #{bonuslen => BonusLen}, Rest}.
|
||||
|
||||
drr_bonuslen_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_bonustype(bitstring())
|
||||
-> {ok, atom(), bitstring()}.
|
||||
drr_bonustype(<<BonusType:64/little, Rest/bitstring>>) ->
|
||||
{ok, bonustype(BonusType), Rest/bitstring}.
|
||||
|
||||
drr_bonustype_test() ->
|
||||
[ drr_bonustype_test(X) || X <- lists:seq(1,1) ].
|
||||
drr_bonustype_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
% http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h#126
|
||||
-define(BONUSTYPE(X,A), bonustype(X) when is_integer(X) -> A;
|
||||
bonustype(A) when is_atom(A) -> X).
|
||||
?BONUSTYPE(0, none);
|
||||
?BONUSTYPE(1, object_directory);
|
||||
?BONUSTYPE(2, object_array);
|
||||
?BONUSTYPE(3, packed_nvlist);
|
||||
?BONUSTYPE(4, packed_nvlist_size);
|
||||
?BONUSTYPE(5, pbobj);
|
||||
?BONUSTYPE(6, pbobj_hdr);
|
||||
?BONUSTYPE(7, space_map_header);
|
||||
?BONUSTYPE(8, space_map);
|
||||
?BONUSTYPE(9, intent_log);
|
||||
?BONUSTYPE(10, dnode);
|
||||
?BONUSTYPE(11, objset);
|
||||
?BONUSTYPE(12, dsl_dir);
|
||||
?BONUSTYPE(13, dsl_dir_child_map);
|
||||
?BONUSTYPE(14, dsl_ds_snap_map);
|
||||
?BONUSTYPE(15, dsl_ds_props);
|
||||
?BONUSTYPE(16, dsl_dataset);
|
||||
?BONUSTYPE(17, znode);
|
||||
?BONUSTYPE(18, oldacl);
|
||||
?BONUSTYPE(19, plain_file_contents);
|
||||
?BONUSTYPE(20, directory_contents);
|
||||
?BONUSTYPE(21, master_node);
|
||||
?BONUSTYPE(22, unlinked_set);
|
||||
?BONUSTYPE(23, zvol);
|
||||
?BONUSTYPE(25, zvol_prop);
|
||||
?BONUSTYPE(26, plain_other);
|
||||
?BONUSTYPE(27, uint64_other);
|
||||
?BONUSTYPE(28, zap_other);
|
||||
?BONUSTYPE(29, error_log);
|
||||
?BONUSTYPE(30, spa_history);
|
||||
?BONUSTYPE(31, spa_history_offsets);
|
||||
?BONUSTYPE(32, pool_props);
|
||||
?BONUSTYPE(33, dsl_perms);
|
||||
?BONUSTYPE(34, acl);
|
||||
?BONUSTYPE(35, sysacl);
|
||||
?BONUSTYPE(36, fuid);
|
||||
?BONUSTYPE(37, fuid_size);
|
||||
?BONUSTYPE(38, next_clones);
|
||||
?BONUSTYPE(39, scan_queue);
|
||||
?BONUSTYPE(40, usergroup_used);
|
||||
?BONUSTYPE(41, usergroup_quota);
|
||||
?BONUSTYPE(42, userrefs);
|
||||
?BONUSTYPE(43, ddt_zap);
|
||||
?BONUSTYPE(44, ddt_stats);
|
||||
?BONUSTYPE(45, sa);
|
||||
?BONUSTYPE(46, sa_master_node);
|
||||
?BONUSTYPE(47, sa_attr_registration);
|
||||
?BONUSTYPE(48, sa_attr_layouts);
|
||||
?BONUSTYPE(49, scan_xlate);
|
||||
?BONUSTYPE(50, dedup);
|
||||
?BONUSTYPE(51, deadlist);
|
||||
?BONUSTYPE(52, deadlist_hdr);
|
||||
?BONUSTYPE(53, dsl_clones);
|
||||
?BONUSTYPE(54, bpobj_subobj);
|
||||
?BONUSTYPE(55, numtypes).
|
||||
% DMU_OTN_UINT8_DATA = DMU_OT(DMU_BSWAP_UINT8, B_FALSE),
|
||||
% DMU_OTN_UINT8_METADATA = DMU_OT(DMU_BSWAP_UINT8, B_TRUE),
|
||||
% DMU_OTN_UINT16_DATA = DMU_OT(DMU_BSWAP_UINT16, B_FALSE),
|
||||
% DMU_OTN_UINT16_METADATA = DMU_OT(DMU_BSWAP_UINT16, B_TRUE),
|
||||
% DMU_OTN_UINT32_DATA = DMU_OT(DMU_BSWAP_UINT32, B_FALSE),
|
||||
% DMU_OTN_UINT32_METADATA = DMU_OT(DMU_BSWAP_UINT32, B_TRUE),
|
||||
% DMU_OTN_UINT64_DATA = DMU_OT(DMU_BSWAP_UINT64, B_FALSE),
|
||||
% DMU_OTN_UINT64_METADATA = DMU_OT(DMU_BSWAP_UINT64, B_TRUE),
|
||||
% DMU_OTN_ZAP_DATA = DMU_OT(DMU_BSWAP_ZAP, B_FALSE),
|
||||
% DMU_OTN_ZAP_METADATA = DMU_OT(DMU_BSWAP_ZAP, B_TRUE),
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%% @todo check data checksum
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_checksum(bitstring)
|
||||
-> {ok, bitstring(), bitstring}.
|
||||
drr_checksum(<<Checksum:256/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_checksum: ~p", [Checksum]),
|
||||
{ok, Checksum, Rest}.
|
||||
|
||||
drr_checksum_test() ->
|
||||
[ drr_checksum_test(X) || X <- [1] ].
|
||||
|
||||
drr_checksum_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_checksumflags(bitstring()) -> {ok, bitstring(), bitstring}.
|
||||
drr_checksumflags(<<ChecksumFlags:8, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_checksumflags: ~p", [ChecksumFlags]),
|
||||
{ok, ChecksumFlags, Rest}.
|
||||
|
||||
drr_checksumflags_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_checksumtype(bitstring()) -> {ok, atom(), bitstring()}.
|
||||
drr_checksumtype(<<ChecksumType:8/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_checksumtype: ~p", [ChecksumType]),
|
||||
{ok, ChecksumType, Rest}.
|
||||
|
||||
drr_checksumtype_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_compress(bitstring()) -> {ok, atom(), bitstring()}.
|
||||
drr_compress(<<Compress:8/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_compress: ~p", [Compress]),
|
||||
{ok, Compress, Rest}.
|
||||
|
||||
drr_compress_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_creation_time(bitstring())
|
||||
-> {ok, map(), bitstring()}.
|
||||
drr_creation_time(<<CreationTime:64/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_creation_time: ~p", [CreationTime]),
|
||||
{ok, #{ creation_time => CreationTime }, Rest}.
|
||||
|
||||
drr_creation_time_test() ->
|
||||
[drr_creation_time_test(X) || X <- [1] ].
|
||||
drr_creation_time_test(1) ->
|
||||
IN = <<16#66, 16#2f, 16#29, 16#5a,0,0,0,0>>,
|
||||
OUT = {ok, #{ creation_time => 16#5a292f66 }, <<>>},
|
||||
?assertEqual(OUT, drr_creation_time(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_firstobj(bitstring())
|
||||
-> {ok, bitstring(), bitstring()}.
|
||||
drr_firstobj(<<FirstObj:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_firstobj: ~p", [FirstObj]),
|
||||
{ok, FirstObj, Rest}.
|
||||
|
||||
drr_firstobj_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_flags(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_flags(<<Flags:32/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_flags: ~p", [Flags]),
|
||||
{ok, #{ flags => Flags }, Rest}.
|
||||
|
||||
drr_flags_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_fromguid(bitstring()) -> {ok, map(), bitstring()}.
|
||||
drr_fromguid(<<FromGuid:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_fromguid: ~p", [FromGuid]),
|
||||
{ok, #{ fromguid => FromGuid }, Rest}.
|
||||
|
||||
drr_fromguid_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_key(bitstring()) -> {ok, map(), bitstring()}.
|
||||
drr_key(<<CkSum:256/bitstring, Prop:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_key: ~p, ~p", [CkSum, Prop]),
|
||||
{ok, #{cksum => CkSum, prop => Prop}, Rest}.
|
||||
|
||||
drr_key_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_length(bitstring()) -> {ok, map(), bitstring()}.
|
||||
drr_length(<<Length:64/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_length: ~p", [Length]),
|
||||
{ok, #{ length => Length }, Rest}.
|
||||
|
||||
drr_length_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_magic(bitstring())
|
||||
-> {ok, map(), bitstring()} |
|
||||
{error, term()}.
|
||||
drr_magic(<<16#2F5bacbac:64/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_magic: ok", []),
|
||||
{ok, #{ magic => <<16#2F5bacbac:64/little>> }, Rest};
|
||||
drr_magic(<<_:64, _/bitstring>>) ->
|
||||
{error, magic}.
|
||||
|
||||
drr_magic_test() ->
|
||||
[ drr_magic_test(X) || X <- lists:seq(1,2) ].
|
||||
|
||||
drr_magic_test(1) ->
|
||||
IN = <<172,203,186,245,2,0,0,0>>,
|
||||
OUT = {ok, #{magic => <<16#2F5bacbac:64/little>>}, <<>>},
|
||||
?assertEqual(OUT, drr_magic(IN));
|
||||
drr_magic_test(2) ->
|
||||
IN = <<1,2,3,4, 5,6,7,8>>,
|
||||
OUT = {error, magic},
|
||||
?assertEqual(OUT, drr_magic(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_numobjs(bitstring()) -> {ok, non_neg_integer(), bitstring()}.
|
||||
drr_numobjs(<<NumObjs:64/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_numobjs: ~p", [NumObjs]),
|
||||
{ok, NumObjs, Rest}.
|
||||
|
||||
drr_numobjs_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_object(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_object(<<Object:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, Object, Rest}.
|
||||
|
||||
drr_object_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_offset(bitstring()) -> {ok, non_neg_integer(), bitstring()}.
|
||||
drr_offset(<<Offset:64/little, Rest/bitstring>>) ->
|
||||
{ok, Offset, Rest}.
|
||||
|
||||
drr_offset_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_pad(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_pad(<<Pad:32, Rest/bitstring>>) ->
|
||||
{ok, Pad, Rest}.
|
||||
|
||||
drr_pad_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
-spec drr_pad2(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_pad2(<<Pad:8, Rest/bitstring>>) ->
|
||||
{ok, Pad, Rest}.
|
||||
|
||||
drr_pad2_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_refguid(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_refguid(<<RefGuid:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, RefGuid, Rest}.
|
||||
|
||||
drr_refguid_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_refobject(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_refobject(<<RefObject:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, RefObject, Rest}.
|
||||
|
||||
drr_refobject_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_refoffset(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_refoffset(<<RefOffset:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, RefOffset, Rest}.
|
||||
|
||||
drr_refoffset_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_toguid(bitstring())
|
||||
-> {ok, map(), bitstring()}.
|
||||
drr_toguid(<<ToGuid:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, #{ toguid => ToGuid }, Rest}.
|
||||
|
||||
drr_toguid_test() ->
|
||||
[ drr_toguid_test(X) || X <- [1] ].
|
||||
|
||||
drr_toguid_test(1) ->
|
||||
IN = <<16#36, 16#50, 16#0f, 16#21, 16#f9, 16#72, 16#e9, 16#87>>,
|
||||
OUT = {ok, #{ toguid => <<54,80,15,33,249,114,233,135>>}, <<>>},
|
||||
?assertEqual(OUT, drr_toguid(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%% @todo check snapshot name.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_toname(bitstring())
|
||||
-> {ok, map(), bitstring()}.
|
||||
drr_toname(<<ToName:(256*8)/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_toname: ~p", [ToName]),
|
||||
{ok, #{ toname => zfs_lib:null_clean(ToName) }, Rest}.
|
||||
|
||||
drr_toname_test() ->
|
||||
[drr_toname_test(X) || X <- lists:seq(1, 2) ].
|
||||
|
||||
drr_toname_test(1) ->
|
||||
IN = <<>>,
|
||||
?assertException(error, _, drr_toname(IN));
|
||||
drr_toname_test(2) ->
|
||||
IN = <<"pool@test", 0:(256*8-72)>>,
|
||||
OUT = {ok, #{ toname => <<"pool@test">> }, <<>>},
|
||||
?assertEqual(OUT, drr_toname(IN));
|
||||
drr_toname_test(3) ->
|
||||
IN = <<"zpooltest", 0:(256*8-72)>>,
|
||||
OUT = {error, not_snapshot},
|
||||
?assertEqual(OUT, drr_toname(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_type(bitstring())
|
||||
-> {ok, map(), bitstring()}.
|
||||
drr_type(<<Type:32/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_type: ~p", [Type]),
|
||||
{ok, #{ type => type(Type) }, Rest}.
|
||||
|
||||
drr_type_test() ->
|
||||
[ drr_type_test(X) || X <- lists:seq(1,9) ].
|
||||
|
||||
drr_type_test(1) ->
|
||||
IN = <<0,0,0,0 >>,
|
||||
OUT = {ok, #{ type => drr_begin }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(2) ->
|
||||
IN = <<1,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_object }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(3) ->
|
||||
IN = <<2,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_freeobjects }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(4) ->
|
||||
IN = <<3,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_write}, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(5) ->
|
||||
IN = <<4,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_free }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(6) ->
|
||||
IN = <<5,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_end }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(7) ->
|
||||
IN = <<6,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_write_byref }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(8) ->
|
||||
IN = <<7,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_spill }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN));
|
||||
drr_type_test(9) ->
|
||||
IN = <<8,0,0,0>>,
|
||||
OUT = {ok, #{ type => drr_umtypes }, <<>>},
|
||||
?assertEqual(OUT, drr_type(IN)).
|
||||
|
||||
-define(TYPE(X,A), type(X) when is_integer(X) -> A;
|
||||
type(A) when is_atom(A) -> X ).
|
||||
?TYPE(0, drr_begin);
|
||||
?TYPE(1, drr_object);
|
||||
?TYPE(2, drr_freeobjects);
|
||||
?TYPE(3, drr_write);
|
||||
?TYPE(4, drr_free);
|
||||
?TYPE(5, drr_end);
|
||||
?TYPE(6, drr_write_byref);
|
||||
?TYPE(7, drr_spill);
|
||||
?TYPE(8, drr_umtypes).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_versioninfo(bitstring()) -> {ok, map(), bitstring()}.
|
||||
drr_versioninfo(<<VersionInfo:64/little, Rest/bitstring>>) ->
|
||||
?debugFmt("drr_versioninfo: ~p", [<<VersionInfo:64>>]),
|
||||
<<_:32, Features:30, HdrType:2>> = <<VersionInfo:64>>,
|
||||
{ok, #{ hdrtype => hdrtype(HdrType)
|
||||
, featuresflags => Features
|
||||
}, Rest}.
|
||||
|
||||
drr_versioninfo_test() ->
|
||||
drr_versioninfo_test(1),
|
||||
drr_versioninfo_test(2).
|
||||
drr_versioninfo_test(1) ->
|
||||
IN = <<17,0,0,0,0,0,0,0>>,
|
||||
OUT = {ok, #{ hdrtype => substream
|
||||
, featuresflags => 4 }, <<>>},
|
||||
?assertEqual(OUT, drr_versioninfo(IN));
|
||||
drr_versioninfo_test(2) ->
|
||||
IN = <<16,0,0,0,0,0,0,0>>,
|
||||
OUT = {ok, #{ hdrtype => compoundstream
|
||||
, featuresflags => 4 }, <<>>}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-define(HDRTYPE(X,A), hdrtype(X) -> A; hdrtype(A) -> X).
|
||||
-spec hdrtype(integer()) -> atom().
|
||||
?HDRTYPE(1, substream);
|
||||
?HDRTYPE(2, compoundstream).
|
||||
|
||||
hdrtype_test() ->
|
||||
[ hdrtype_test(X) || X <- [1,2] ].
|
||||
|
||||
hdrtype_test(1) ->
|
||||
IN = 1,
|
||||
OUT = substream,
|
||||
?assertEqual(OUT, hdrtype(1));
|
||||
hdrtype_test(2) ->
|
||||
IN = 2,
|
||||
OUT = compoundstream,
|
||||
?assertEqual(OUT, hdrtype(2)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%% @see http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h#58
|
||||
%%--------------------------------------------------------------------
|
||||
-define(OBJSET_TYPE(X,A), dmu_objset_type(X) -> A;
|
||||
dmu_objset_type(A) -> X).
|
||||
?OBJSET_TYPE(0, none);
|
||||
?OBJSET_TYPE(1, meta);
|
||||
?OBJSET_TYPE(2, zfs);
|
||||
?OBJSET_TYPE(3, zvol);
|
||||
?OBJSET_TYPE(4, other);
|
||||
?OBJSET_TYPE(5, any);
|
||||
?OBJSET_TYPE(6, numtypes).
|
||||
|
||||
%%
|
||||
%%
|
||||
%%
|
||||
-spec objset_type(bitstring()) -> {ok, atom(), bitstring()}.
|
||||
objset_type(<<ObjsetType:32/little, Rest/bitstring>>) ->
|
||||
{ok, #{ type => dmu_objset_type(ObjsetType) }, Rest}.
|
||||
|
||||
objset_type_test() ->
|
||||
[ objset_type_test(X) || X <- lists:seq(1,1) ].
|
||||
objset_type_test(1) ->
|
||||
IN = <<0,0,0,0>>,
|
||||
OUT = {ok, #{ type => none }, <<>>},
|
||||
?assertEqual(OUT, objset_type(IN));
|
||||
objset_type_test(2) ->
|
||||
IN = <<1,0,0,0>>,
|
||||
OUT = {ok, #{ type => meta }, <<>>},
|
||||
?assertEqual(OUT, objset_type(IN));
|
||||
objset_type_test(3) ->
|
||||
IN = <<2,0,0,0>>,
|
||||
OUT = {ok, #{ type => zfs}, <<>>},
|
||||
?assertEqual(OUT, objset_type(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%
|
||||
%%--------------------------------------------------------------------
|
||||
-define(DMU_BSWAP_UINT8, 0).
|
||||
-define(DMU_BSWAP_UINT16, 1).
|
||||
-define(DMU_BSWAP_UINT32, 2).
|
||||
-define(DMU_BSWAP_UINT64, 3).
|
||||
-define(DMU_BSWAP_ZAP, 4).
|
||||
-define(DMU_BSWAP_DNODE, 5).
|
||||
-define(DMU_BSWAP_OBJSET, 6).
|
||||
-define(DMU_BSWAP_ZNODE, 7).
|
||||
-define(DMU_BSWAP_OLDACL, 8).
|
||||
-define(DMU_BSWAP_ACL, 9).
|
||||
-define(DMU_BSWAP_NUMFUNCS, 10).
|
||||
154
src/fletcher.erl
Normal file
154
src/fletcher.erl
Normal file
@@ -0,0 +1,154 @@
|
||||
%%%===================================================================
|
||||
%%% @author Mathieu Kerjouan
|
||||
%%% @copyright 2017
|
||||
%%% @title Fletcher Checksums Implementation
|
||||
%%% @doc fletcher checksum naive implementation. All this code
|
||||
%%% is not optimized and was made only for testing purpose.
|
||||
%%% @end
|
||||
%%% @see sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.c
|
||||
%%%===================================================================
|
||||
-module(fletcher).
|
||||
-export([fletcher16/1]).
|
||||
-export([fletcher32/1]).
|
||||
-export([fletcher64/1]).
|
||||
-export([adler32/1]).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-define(ENDIANESS, little).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc naive fletcher16 implementation
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec fletcher16(bitstring()) -> integer().
|
||||
fletcher16(Bitstring) ->
|
||||
fletcher16(Bitstring, {0, 0}).
|
||||
|
||||
-spec fletcher16(bitstring(), {integer(), integer()}) -> integer().
|
||||
fletcher16(<<>>, {Sum1, Sum2}) ->
|
||||
(Sum2 bsl 8) bor Sum1;
|
||||
fletcher16(<<Char:8, Rest/bitstring>>, {Sum1, Sum2}) ->
|
||||
S1 = (Sum1 + Char) rem 255,
|
||||
S2 = (Sum2 + S1) rem 255,
|
||||
fletcher16(Rest, {S1, S2}).
|
||||
|
||||
fletcher16_test() ->
|
||||
[ fletcher16_test(X) || X <- lists:seq(1,3) ].
|
||||
|
||||
fletcher16_test(1) ->
|
||||
IN = <<"abcde">>,
|
||||
OUT = 51440,
|
||||
?assertEqual(OUT, fletcher16(IN));
|
||||
fletcher16_test(2) ->
|
||||
IN = <<"abcdef">>,
|
||||
OUT = 8279,
|
||||
?assertEqual(OUT, fletcher16(IN));
|
||||
fletcher16_test(3) ->
|
||||
IN = <<"abcdefgh">>,
|
||||
OUT = 1575,
|
||||
?assertEqual(OUT, fletcher16(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc naive fletcher32 implementation
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
fletcher32(Bitstring) ->
|
||||
fletcher32(Bitstring, {0, 0}).
|
||||
|
||||
fletcher32(<<>>, {Sum1, Sum2}) ->
|
||||
S1 = Sum1 rem 65535,
|
||||
S2 = Sum2 rem 65535,
|
||||
(S2 bsl 16) bor S1;
|
||||
fletcher32(<<Char:16/?ENDIANESS, Rest/bitstring>>, {Sum1, Sum2}) ->
|
||||
S1 = (Sum1 + Char),
|
||||
S2 = (Sum2 + S1),
|
||||
fletcher32(Rest, {S1, S2});
|
||||
fletcher32(<<Char:8/?ENDIANESS, Rest/bitstring>>, {Sum1, Sum2}) ->
|
||||
S1 = (Sum1 + Char),
|
||||
S2 = (Sum2 + S1),
|
||||
fletcher32(Rest, {S1, S2}).
|
||||
|
||||
fletcher32_test() ->
|
||||
[ fletcher32_test(X) || X <- lists:seq(1,3) ].
|
||||
|
||||
fletcher32_test(1) ->
|
||||
IN = <<"abcde">>,
|
||||
OUT = 4031760169,
|
||||
?assertEqual(OUT, fletcher32(IN));
|
||||
fletcher32_test(2) ->
|
||||
IN = <<"abcdef">>,
|
||||
OUT = 1448095018,
|
||||
?assertEqual(OUT, fletcher32(IN));
|
||||
fletcher32_test(3) ->
|
||||
IN = <<"abcdefgh">>,
|
||||
OUT = 3957429649,
|
||||
?assertEqual(OUT, fletcher32(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
fletcher64(Bitstring) ->
|
||||
fletcher64(Bitstring, {0, 0}).
|
||||
|
||||
fletcher64(<<>>, {Sum1, Sum2}) ->
|
||||
S1 = Sum1 rem 4294967295,
|
||||
S2 = Sum2 rem 4294967295,
|
||||
(S2 bsl 32) bor S1;
|
||||
fletcher64(<<Char:32/?ENDIANESS, Rest/bitstring>>, {Sum1, Sum2}) ->
|
||||
S1 = Sum1 + Char,
|
||||
S2 = Sum2 + S1,
|
||||
fletcher64(Rest, {S1, S2});
|
||||
fletcher64(<<Char:16/?ENDIANESS, Rest/bitstring>>, {Sum1, Sum2}) ->
|
||||
S1 = Sum1 + Char,
|
||||
S2 = Sum2 + S1,
|
||||
fletcher64(Rest, {S1, S2});
|
||||
fletcher64(<<Char:8/?ENDIANESS, Rest/bitstring>>, {Sum1, Sum2}) ->
|
||||
S1 = Sum1 + Char,
|
||||
S2 = Sum2 + S1,
|
||||
fletcher64(Rest, {S1, S2}).
|
||||
|
||||
fletcher64_test() ->
|
||||
[ fletcher64_test(X) || X <- lists:seq(1,3) ].
|
||||
|
||||
fletcher64_test(1) ->
|
||||
IN = <<"abcde">>,
|
||||
OUT = 14467467625952928454,
|
||||
?assertEqual(OUT, fletcher64(IN));
|
||||
fletcher64_test(2) ->
|
||||
IN = <<"abcdef">>,
|
||||
OUT = 14467579776138987718,
|
||||
?assertEqual(OUT, fletcher64(IN));
|
||||
fletcher64_test(3) ->
|
||||
IN = <<"abcdefgh">>,
|
||||
OUT = 3543817411021686982,
|
||||
?assertEqual(OUT, fletcher64(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc naive adler32 implementation. Erlang is delivered with its
|
||||
%% own adler implementation, see erlang:adler32/1 function.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
adler32(Bitstring) ->
|
||||
adler32(Bitstring, {1, 0}).
|
||||
|
||||
adler32(<<>>, {C1, C2}) ->
|
||||
(C2 bsl 16) bor C1;
|
||||
adler32(<<Char:8, Rest/bitstring>>, {C1, C2}) ->
|
||||
S1 = C1 + Char rem 65521,
|
||||
S2 = C2 + S1 rem 65521,
|
||||
adler32(Rest, {S1, S2}).
|
||||
|
||||
adler32_test() ->
|
||||
[ adler32_test(X) || X <- lists:seq(1,3) ].
|
||||
adler32_test(1) ->
|
||||
IN = <<"abcde">>,
|
||||
OUT = 16#05C801F0,
|
||||
?assertEqual(OUT, adler32(IN));
|
||||
adler32_test(2) ->
|
||||
IN = <<"abcdef">>,
|
||||
OUT = 16#081E0256,
|
||||
?assertEqual(OUT, adler32(IN));
|
||||
adler32_test(3) ->
|
||||
IN = <<"abcdefgh">>,
|
||||
OUT = 16#0E000325,
|
||||
?assertEqual(OUT, adler32(IN)).
|
||||
14
src/zfs.app.src
Normal file
14
src/zfs.app.src
Normal file
@@ -0,0 +1,14 @@
|
||||
{application, zfs,
|
||||
[{description, "Erlang ZFS Implementation"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib
|
||||
]},
|
||||
{env,[]},
|
||||
{modules, []},
|
||||
{maintainers, ["contact@steepath.eu"]},
|
||||
{licenses, ["ISC/OpenBSD"]},
|
||||
{links, []}
|
||||
]}.
|
||||
512
src/zfs.erl
Normal file
512
src/zfs.erl
Normal file
@@ -0,0 +1,512 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @title Erlang ZFS High Level Implementation
|
||||
%%% @author Mathieu Kerjouan
|
||||
%%% @copyright 2017
|
||||
%%% @version 0.1.0
|
||||
%%% @doc This module is the first step of Erlang ZFS Implementation
|
||||
%%% and contain all required functions for parsing ZFS streams.
|
||||
%%% @end
|
||||
%%% @see http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(zfs).
|
||||
-compile(export_all).
|
||||
-include("zfs.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc clean function remove null char from bitstring.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
clean(Bitstring) ->
|
||||
clean(Bitstring, <<>>).
|
||||
clean(<<A, Rest/bitstring>>, Buf)
|
||||
when A =/= 0 ->
|
||||
clean(Rest, <<Buf/bitstring, A>>);
|
||||
clean(_, Buf) ->
|
||||
Buf.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc generic automaton handler function, take a list of action in
|
||||
%% multiple form. Each action should take at least one argument
|
||||
%% wich is next action to do after executing the first one.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec next(list(), list()) -> {ok, list()} | term().
|
||||
next([], Args) ->
|
||||
{ok, Args};
|
||||
next([Function|T], Args)
|
||||
when is_atom(Function) ->
|
||||
?debugFmt("Function: ~p", [Function]),
|
||||
?debugFmt("Rest: ~p", [T]),
|
||||
?debugFmt("Args: ~p", [Args]),
|
||||
erlang:apply(?MODULE, Function, [T|Args]);
|
||||
next([Function|T], Args)
|
||||
when is_function(Function) ->
|
||||
?debugFmt("Function: ~p", [Function]),
|
||||
?debugFmt("Rest: ~p", [T]),
|
||||
?debugFmt("Args: ~p", [Args]),
|
||||
erlang:apply(Function, [T|Args]);
|
||||
next([{Module, Function}|T], Args)
|
||||
when is_atom(Module), is_atom(Function) ->
|
||||
?debugFmt("Module: ~p", [Module]),
|
||||
?debugFmt("Function: ~p", [Function]),
|
||||
?debugFmt("Rest: ~p", [T]),
|
||||
?debugFmt("Args: ~p", [Args]),
|
||||
erlang:apply(Module, Function, [T|Args]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc simple function to put value in different structure like
|
||||
%% map and proplist.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
put(Key, Value, Proplist)
|
||||
when is_list(Proplist) ->
|
||||
[{Key, Value}|Proplist];
|
||||
put(Key, Value, Map)
|
||||
when is_map(Map) ->
|
||||
maps:put(Key, Value, Map).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc main function to parse zfs stream. This function accept list
|
||||
%% or bitstring.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec dmu(bitstring() | list()) -> tuple().
|
||||
dmu(List)
|
||||
when is_list(List) ->
|
||||
dmu(erlang:list_to_bitstring(List));
|
||||
dmu(Bitstring)
|
||||
when is_bitstring(Bitstring) ->
|
||||
drr_header(Bitstring, #{}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_end/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_end() -> [atom(), ...].
|
||||
drr_end() ->
|
||||
[ drr_checksum, drr_toguid, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_object/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_object() -> [atom(), ...].
|
||||
drr_object() ->
|
||||
[ drr_object, drr_type, drr_bonustype, drr_blksz
|
||||
, drr_bonuslen, drr_checksumtype, drr_compress
|
||||
, drr_pad, drr_toguid, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_freeobjects/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_freeobjects() -> [atom(), ...].
|
||||
drr_freeobjects() ->
|
||||
[ drr_firstobj, drr_numobjs, drr_toguid, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_write/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_write() -> [atom(), ...].
|
||||
drr_write() ->
|
||||
[ drr_object, drr_type, drr_pad, drr_offset, drr_length
|
||||
, drr_toguid, drr_checksumtype, drr_checksumflags
|
||||
, drr_pad2, drr_key, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_free/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_free() -> [atom(), ...].
|
||||
drr_free() ->
|
||||
[ drr_object, drr_offset, drr_length, drr_toguid ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_write_byref/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_write_byref() -> [atom(), ...].
|
||||
drr_write_byref() ->
|
||||
[ drr_object, drr_offset, drr_length, drr_toguid
|
||||
, drr_refguid, drr_refobject, drr_refoffset
|
||||
, drr_checksumtype, drr_checksumflags, drr_pad2
|
||||
, drr_key, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_spill/0.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_spill() -> [atom(), ...].
|
||||
drr_spill() ->
|
||||
[ drr_object, drr_length, drr_toguid, drr_pad, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_header function will route type based on first 64bits of
|
||||
%% ZFS stream from send function. First 32bits define ZFS DRR,
|
||||
%% following 32bits define DRR payload length.
|
||||
%% Objects has defined size: 312bytes. This is the size of
|
||||
%% the dmu_replay_record structure from zfs_ioctl header. So,
|
||||
%% we can retrieve all information with recursive call, but
|
||||
%% if our structure isn't well sized, we'll need to ensure
|
||||
%% to remove remaining data. Another method, more imperative,
|
||||
%% is to extract directly 312bytes from current stream and
|
||||
%% parse it after. Current implementation use pattern
|
||||
%% matching method.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
drr_header(<<DrrType:32/little, DrrPayloadLen:32, Rest/bitstring>>, DmuHeader) ->
|
||||
?debugFmt("type: ~p", [DrrType]),
|
||||
?debugFmt("payload: ~p", [DrrPayloadLen]),
|
||||
DmuUpdate1 = maps:put(type, DrrType, DmuHeader),
|
||||
DmuUpdate2 = maps:put(payloadlen, DrrPayloadLen, DmuUpdate1),
|
||||
case DrrType of
|
||||
?DRR_BEGIN ->
|
||||
drr_begin(Rest, drr_begin(), #{}, DmuUpdate2);
|
||||
?DRR_OBJECT ->
|
||||
wip; % drr_object(Rest, drr_object(), #{}, DmuHeader);
|
||||
?DRR_FREEOBJECTS ->
|
||||
drr_freeobjects(Rest, drr_freeobjects(), #{});
|
||||
?DRR_WRITE ->
|
||||
wip; % drr_write(Rest, drr_write(), #{}, DmuHeader);
|
||||
?DRR_FREE ->
|
||||
wip; % drr_free(Rest, drr_free(), #{}, DmuHeader);
|
||||
?DRR_END ->
|
||||
wip; % drr_end(Rest, drr_end(), #{}, DmuHeader);
|
||||
?DRR_WRITE_BYREF ->
|
||||
wip; % drr_write_byref(Rest, drr_write_byref(), #{}, DmuHeader);
|
||||
?DRR_SPILL ->
|
||||
wip; % drr_spill(Rest, drr_spill(), #{}, DmuHeader);
|
||||
?DRR_NUMTYPES ->
|
||||
wip; % drr_numtypes(Rest, drr_numtypes(), #{}, DmuHeader)
|
||||
_ ->
|
||||
{error, "unsupported type"}
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_begin/0 function return a list of all function to parse
|
||||
%% a begin ZFS stream.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_begin() -> [atom(), ...].
|
||||
drr_begin() ->
|
||||
[ drr_magic, drr_versioninfo, drr_creation_time, drr_type
|
||||
, drr_flags, drr_toguid, drr_fromguid, drr_toname, return ].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
drr_begin(Bitstring, Order, Buf, DmuHeader) ->
|
||||
?debugMsg("drr_begin function"),
|
||||
next(Order, [Bitstring, Buf, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_magic function extract magic ZFS value following and
|
||||
%% check if this value is well configured.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_magic(bitstring()) -> {ok, list(), bitstring()}.
|
||||
drr_magic(<<Magic:64/little, Rest/bitstring>>) ->
|
||||
?debugFmt("magic: ~p", [Magic]),
|
||||
M = erlang:integer_to_binary(Magic, 16),
|
||||
case M =:= <<"2F5BACBAC">> of
|
||||
true ->
|
||||
?debugFmt("magic: ~p", [M]),
|
||||
{ok, M, Rest}
|
||||
end.
|
||||
|
||||
-spec drr_magic(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_magic(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, Magic, Rest} = drr_magic(Bitstring),
|
||||
Return = maps:put(magic, Magic, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_versioninfo extract a 64bits from current bitstring and
|
||||
%% split it in 2 value: hdrtype and features flags.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_versioninfo(bitstring) -> {ok, bitstring(), bitstring()}.
|
||||
drr_versioninfo(<<VersionInfo:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("versioninfo: ~p", [VersionInfo]),
|
||||
{ok, VersionInfo, Rest}.
|
||||
|
||||
-spec drr_versioninfo(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_versioninfo(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
% we need to extract feature and hdrtype
|
||||
% but this thing currently doesn't work as expected...
|
||||
% <<Features:6/little, HdrType:2, _/bitstring>> = VersionInfo,
|
||||
% <<HdrType:16, Features/bitstring>> = VersionInfo,
|
||||
% ?debugFmt("feature: ~p:~p", [HdrType, Features]),
|
||||
% Return1 = maps:put(features, Features, Buf),
|
||||
% Return2 = maps:put(hdrtype, undef, Return1),
|
||||
{ok, VersionInfo, Rest} = drr_versioninfo(Bitstring),
|
||||
Return = maps:put(versioninfo, VersionInfo, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_creation_time extract a 64bits value from current
|
||||
%% bitstring.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_creation_time(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_creation_time(<<CreationTime:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("creation_time: ~p", [CreationTime]),
|
||||
{ok, CreationTime, Rest}.
|
||||
|
||||
-spec drr_creation_time(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_creation_time(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, CreationTime, Rest} = drr_creation_time(Bitstring),
|
||||
Return = maps:put(creation_time, CreationTime, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_type extract 32bits value from current bitstring.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_type(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_type(<<Type:32/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("type: ~p", [Type]),
|
||||
{ok, Type, Rest}.
|
||||
|
||||
-spec drr_type(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_type(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, Type, Rest} = drr_type(Bitstring),
|
||||
Return = maps:put(type, Type, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_flags function extract flags from current bitstring.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_flags(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_flags(<<Flags:32/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("flags: ~p", [Flags]),
|
||||
{ok, Flags, Rest}.
|
||||
|
||||
-spec drr_flags(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_flags(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, Flags, Rest} = drr_flags(Bitstring),
|
||||
Return = maps:put(flags, Flags, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc drr_toguid extract 64bits as bitstring from current
|
||||
%% bitstring.
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_toguid(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_toguid(<<ToGuid:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("toguid: ~p", [ToGuid]),
|
||||
{ok, ToGuid, Rest}.
|
||||
|
||||
drr_toguid(Order, Bitstring, Buf)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, ToGuid, Rest} = drr_toguid(Bitstring),
|
||||
Return = maps:put(toguid, ToGuid, Buf),
|
||||
next(Order, [Rest, Return]).
|
||||
|
||||
-spec drr_toguid(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_toguid(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, ToGuid, Rest} = drr_toguid(Bitstring),
|
||||
Return = maps:put(toguid, ToGuid, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_fromguid(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_fromguid(<<FromGuid:64/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("fromguid: ~p", [FromGuid]),
|
||||
{ok, FromGuid, Rest}.
|
||||
|
||||
drr_fromguid(Order,Bitstring, Buf)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, FromGuid, Rest} = drr_fromguid(Bitstring),
|
||||
Return = maps:put(fromguid, FromGuid, Buf),
|
||||
next(Order, [Rest, Return]).
|
||||
|
||||
-spec drr_fromguid(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_fromguid(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, FromGuid, Rest} = drr_fromguid(Bitstring),
|
||||
Return = maps:put(fromguid, FromGuid, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_toname(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_toname(<<ToName:(256*8)/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("toname: ~p", [clean(ToName)]),
|
||||
{ok, ToName, Rest}.
|
||||
|
||||
-spec drr_toname(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_toname(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, ToName, Rest} = drr_toname(Bitstring),
|
||||
Return = maps:put(toname, clean(ToName), Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
return([], Rest, Buf ) ->
|
||||
{ok, Buf, Rest}.
|
||||
return([], Rest, Buf, DmuHeader) ->
|
||||
{ok, maps:put(drr, Buf, DmuHeader), Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
drr_end(Bitstring, Order, DmuHeader) ->
|
||||
next(Order, [Bitstring, #{}, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_checksum(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_checksum(<<Checksum:256/bitstring, Rest/bitstring>>) ->
|
||||
?debugFmt("Checksum: ~p", [Checksum]),
|
||||
{ok, Checksum, Rest}.
|
||||
|
||||
-spec drr_checksum(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_checksum(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, Checksum, Rest} = drr_checksum(Bitstring),
|
||||
Return = maps:put(checksum, Checksum, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_object(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_object(<<Object:64, Rest/bitstring>>) ->
|
||||
?debugFmt("Object: ~p", [Object]),
|
||||
{ok, Object, Rest}.
|
||||
|
||||
-spec drr_object(list(), bitstring(), map(), map()) -> tuple().
|
||||
drr_object(Order, Bitstring, Buf, DmuHeader)
|
||||
when is_bitstring(Bitstring) ->
|
||||
{ok, Object, Rest} = drr_object(Bitstring),
|
||||
Return = maps:put(object, Object, Buf),
|
||||
next(Order, [Rest, Return, DmuHeader]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_bonustype(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_bonustype(<<BonusType:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, BonusType, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_blksz(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_blksz(<<BlkSz:32, Rest/bitstring>>) ->
|
||||
{ok, BlkSz, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_bonuslen(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_bonuslen(<<BonusLen:32/bitstring, Rest/bitstring>>) ->
|
||||
{ok, BonusLen, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_checksumtype(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_checksumtype(<<ChecksumType:8, Rest/bitstring>>) ->
|
||||
{ok, ChecksumType, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_compress(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_compress(<<Compress:8, Rest/bitstring>>) ->
|
||||
{ok, Compress, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_pad(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_pad(<<Pad:8, Rest>>) ->
|
||||
{ok, Pad, Rest}.
|
||||
|
||||
-spec drr_pad(bitstring(), non_neg_integer())
|
||||
-> {ok, bitstring(), bitstring()}.
|
||||
drr_pad(Bitstring, Length) ->
|
||||
L = Length*8,
|
||||
<<Pad:L, Rest/bitstring>> = Bitstring,
|
||||
{ok, Pad, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
drr_freeobjects(Bitstring, Order, Buf) ->
|
||||
?debugMsg("drr_freeobjects function"),
|
||||
next(Order, [Bitstring, Buf]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_firstobj(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_firstobj(<<FirstObj:64/little, Rest/bitstring>>) ->
|
||||
{ok, FirstObj, Rest}.
|
||||
|
||||
drr_firstobj(Order, Bitstring, Buf) ->
|
||||
{ok, FirstObj, Rest} = drr_firstobj(Bitstring),
|
||||
Return = maps:put(firstobj, FirstObj, Buf),
|
||||
next(Order, [Rest, Return]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_numobjs(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_numobjs(<<NumObjs:64/little, Rest/bitstring>>) ->
|
||||
{ok, NumObjs, Rest}.
|
||||
|
||||
drr_numobjs(Order, Bitstring, Buf) ->
|
||||
{ok, NumObjs, Rest} = drr_numobjs(Bitstring),
|
||||
Return = maps:put(numobjs, NumObjs, Buf),
|
||||
next(Order, [Rest, Return]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_offset(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_offset(<<Offset:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, Offset, Rest}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec drr_length(bitstring()) -> {ok, bitstring(), bitstring()}.
|
||||
drr_length(<<Length:64/bitstring, Rest/bitstring>>) ->
|
||||
{ok, Length, Rest}.
|
||||
63
src/zfs.hrl
Normal file
63
src/zfs.hrl
Normal file
@@ -0,0 +1,63 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-define(MAXNAMELEN, 256).
|
||||
|
||||
-define(DRR_BEGIN, 0).
|
||||
-define(DRR_OBJECT, 1).
|
||||
-define(DRR_FREEOBJECTS, 2).
|
||||
-define(DRR_WRITE, 3).
|
||||
-define(DRR_FREE, 4).
|
||||
-define(DRR_END, 5).
|
||||
-define(DRR_WRITE_BYREF, 6).
|
||||
-define(DRR_SPILL, 7).
|
||||
-define(DRR_NUMTYPES, 8).
|
||||
|
||||
% drr_begin
|
||||
% http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h#128
|
||||
-define(DRR_MAGIC_SIZE, 64).
|
||||
-define(DRR_MAGIC, ?DRR_MAGIC_SIZE/bitstring).
|
||||
|
||||
-define(DRR_VERSIONINFO_SIZE, 64).
|
||||
-define(DRR_VERSIONINFO, ?DRR_VERSIONINFO_SIZE/bitstring).
|
||||
|
||||
-define(DRR_CREATIONTIME_SIZE, 64).
|
||||
-define(DRR_CREATIONTIME, ?DRR_CREATIONTIME_SIZE/bitstring).
|
||||
|
||||
-define(DRR_TYPE_SIZE, 32).
|
||||
-define(DRR_TYPE, ?DRR_TYPE_SIZE/bitstring).
|
||||
|
||||
-define(DRR_FLAGS_SIZE, 32).
|
||||
-define(DRR_FLAGS, ?DRR_FLAGS_SIZE/bitstring).
|
||||
|
||||
-define(DRR_TOGUID_SIZE, 64).
|
||||
-define(DRR_TOGUID, ?DRR_TOGUID_SIZE/bitstring).
|
||||
|
||||
-define(DRR_FROMGUID_SIZE, 64).
|
||||
-define(DRR_FROMGUID, ?DRR_FROMGUID_SIZE/bitstring).
|
||||
|
||||
-define(DRR_TONAME_SIZE, ?MAXNAMELEN).
|
||||
-define(DRR_TONAME, ?MAXNAMELEN/bitstring).
|
||||
|
||||
|
||||
% drr_end
|
||||
% http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h#139
|
||||
-define(DRR_CHECKSUM_SIZE, 64).
|
||||
|
||||
% drr_object
|
||||
% http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h#144
|
||||
-define(DRR_OBJECT_SIZE, 64).
|
||||
-define(DRR_BLKSZ_SIZE, 32).
|
||||
-define(DRR_BONUSLEN_SIZE, 32).
|
||||
-define(DRR_CHECKSUMTYPE_SIZE, 8).
|
||||
-define(DRR_COMPRESS_SIZE, 8).
|
||||
-define(DRR_PAD_SIZE, 8).
|
||||
|
||||
% drr_freeobjects
|
||||
% http://bxr.su/FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h#157
|
||||
|
||||
% drr_write
|
||||
|
||||
% drr_free
|
||||
|
||||
% drr_write_byref
|
||||
298
src/zfs_lib.erl
Normal file
298
src/zfs_lib.erl
Normal file
@@ -0,0 +1,298 @@
|
||||
%%%===================================================================
|
||||
%%% @doc
|
||||
%%% @end
|
||||
%%% @see sys/cddl/contrib/opensolaris/common/zfs
|
||||
%%%===================================================================
|
||||
-module(zfs_lib).
|
||||
-include("zfs.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-compile(export_all).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec valid_char(integer()) -> integer().
|
||||
valid_char(Char)
|
||||
when (Char >= $0 andalso Char =< $9) orelse
|
||||
(Char >= $a andalso Char =< $z) orelse
|
||||
(Char >= $A andalso Char =< $Z) orelse
|
||||
Char =:= $_ orelse Char =:= $- orelse
|
||||
Char =:= $: orelse Char =:= $. orelse Char =:= $ ->
|
||||
Char.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec zfs_name(list() | bitstring())
|
||||
-> {ok, atom(), bitstring()} |
|
||||
{error, term()}.
|
||||
zfs_name(List) when is_list(List) ->
|
||||
zfs_name(erlang:list_to_bitstring(List));
|
||||
zfs_name(Bitstring) ->
|
||||
zfs_name(Bitstring, <<>>).
|
||||
|
||||
-spec zfs_name(bitstring(), bitstring(), atom())
|
||||
-> {ok, atom(), bitstring()} |
|
||||
{error, term()}.
|
||||
zfs_name(Bitstring, Buf) ->
|
||||
zfs_name(Bitstring, <<>>, pool).
|
||||
|
||||
% pool
|
||||
zfs_name(<<>>, PoolName, pool) ->
|
||||
{ok, pool, PoolName};
|
||||
zfs_name(<<"/", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"@", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"_", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"c", Char:8, Rest/bitstring>>, <<>>, pool)
|
||||
when Char >= $0 andalso Char =< $9 ->
|
||||
{error, badname};
|
||||
zfs_name(<<"log", Rest/bitstring>>, Buf, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"mirror", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"raidz", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"raidz1", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"raidz2", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"raidz3", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"spare", Rest/bitstring>>, <<>>, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"@", Rest/bitstring>>, Buf, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"%", Rest/bitstring>>, Buf, pool) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"/", Rest/bitstring>>, Buf, pool) ->
|
||||
zfs_name(Rest, <<Buf/bitstring, "/">>, dataset);
|
||||
zfs_name(<<Char:8, Rest/bitstring>>, Buf, pool)
|
||||
when (Char >= $0 andalso Char =< $9) orelse
|
||||
(Char >= $a andalso Char =< $z) orelse
|
||||
(Char >= $A andalso Char =< $Z) orelse
|
||||
Char =:= $_ orelse Char =:= $- orelse
|
||||
Char =:= $: orelse Char =:= $. ->
|
||||
zfs_name(Rest, <<Buf/bitstring, Char>>, pool);
|
||||
|
||||
% dataset
|
||||
zfs_name(<<>>, DatasetName, dataset) ->
|
||||
{ok, dataset, DatasetName};
|
||||
zfs_name(<<"%", Rest/bitstring>>, Buf, dataset) ->
|
||||
{error, badname};
|
||||
zfs_name(<<"@", Rest/bitstring>>, Buf, dataset) ->
|
||||
zfs_name(Rest, <<Buf/bitstring, "@">>, snapshot);
|
||||
zfs_name(<<Char:8, Rest/bitstring>>, Buf, dataset)
|
||||
when (Char >= $0 andalso Char =< $9) orelse
|
||||
(Char >= $a andalso Char =< $z) orelse
|
||||
(Char >= $A andalso Char =< $Z) orelse
|
||||
Char =:= $_ orelse Char =:= $- orelse
|
||||
Char =:= $: orelse Char =:= $. orelse
|
||||
Char =:= $/ ->
|
||||
zfs_name(Rest, <<Buf/bitstring, Char>>, dataset);
|
||||
|
||||
% snapshot
|
||||
zfs_name(<<>>, Buf, snapshot) ->
|
||||
{ok, snapshot, Buf};
|
||||
zfs_name(<<Char:8, Rest/bitstring>>, Buf, snapshot)
|
||||
when (Char >= $0 andalso Char =< $9) orelse
|
||||
(Char >= $a andalso Char =< $z) orelse
|
||||
(Char >= $A andalso Char =< $Z) orelse
|
||||
Char =:= $_ orelse Char =:= $- orelse
|
||||
Char =:= $: orelse Char =:= $. orelse
|
||||
Char =:= $/ ->
|
||||
zfs_name(Rest, <<Buf/bitstring, Char>>, snapshot);
|
||||
|
||||
zfs_name(_, _, _) ->
|
||||
{error, badname}.
|
||||
|
||||
zfs_name_test() ->
|
||||
[ zfs_name_test(X) || X <- lists:seq(1,14) ].
|
||||
|
||||
zfs_name_test(1) ->
|
||||
IN = <<"pool">>,
|
||||
OUT = {ok, pool, IN},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(2) ->
|
||||
IN = <<"pool/test/toto">>,
|
||||
OUT = {ok, dataset, IN},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(3) ->
|
||||
IN = <<"pool/test/toto@snap1">>,
|
||||
OUT = {ok, snapshot, IN},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(4) ->
|
||||
IN = <<"/pool/test">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(5) ->
|
||||
IN = <<"@pool/test">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(6) ->
|
||||
IN = <<"pool@test@1">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(7) ->
|
||||
IN = <<"_pool/test">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(8) ->
|
||||
IN = <<"%OFDATA">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(9) ->
|
||||
IN = <<"ROOT">>,
|
||||
OUT = {ok, pool, IN},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(10) ->
|
||||
IN = <<"R_:0/level4-4">>,
|
||||
OUT = {ok, dataset, IN},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(11) ->
|
||||
IN = <<" test">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN));
|
||||
zfs_name_test(12) ->
|
||||
IN = [ <<"c012test">>, <<"c1">>, <<"c2">>
|
||||
, <<"c3">>, <<"c4">>, <<"c5">>, <<"c6">>
|
||||
, <<"c7">>, <<"c8">>, <<"c9">>
|
||||
],
|
||||
OUT = {error, badname},
|
||||
[ ?assertEqual(OUT, zfs_name(X)) || X <- IN ];
|
||||
zfs_name_test(13) ->
|
||||
IN = [ <<"log">>, <<"mirror">>, <<"raidz">>
|
||||
, <<"raidz1">>, <<"raidz2">>, <<"raidz3">>
|
||||
, <<"spare">>
|
||||
],
|
||||
OUT = {error, badname},
|
||||
[ ?assertEqual(OUT, zfs_name(X)) || X <- IN ];
|
||||
zfs_name_test(14) ->
|
||||
IN = <<"mypool/test%/1">>,
|
||||
OUT = {error, badname},
|
||||
?assertEqual(OUT, zfs_name(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec is_pool(bitstring()) -> true | false.
|
||||
is_pool(Bitstring) ->
|
||||
is_pool(Bitstring, <<>>).
|
||||
|
||||
-spec is_pool(bitstring(), bitstring()) -> true | false.
|
||||
is_pool(<<>>, Buf)
|
||||
when Buf =/= <<>> ->
|
||||
true;
|
||||
is_pool(<<Char:8, Rest/bitstring>>, Buf)
|
||||
when Char >= $0 andalso Char =< $9 orelse
|
||||
Char >= $A andalso Char =< $Z orelse
|
||||
Char >= $a andalso Char =< $z ->
|
||||
is_pool(Rest, <<Buf/bitstring, Char>>);
|
||||
is_pool(_, _) ->
|
||||
false.
|
||||
|
||||
is_pool_test() ->
|
||||
[ is_pool_test(X) || X <- [1] ].
|
||||
is_pool_test(1) ->
|
||||
IN = <<"test">>,
|
||||
OUT = true,
|
||||
?assertEqual(OUT, is_pool(IN));
|
||||
is_pool_test(2) ->
|
||||
IN = <<"/test">>,
|
||||
OUT = false,
|
||||
?assertEqual(OUT, is_pool(IN));
|
||||
is_pool_test(3) ->
|
||||
IN = <<"@test">>,
|
||||
OUT = false,
|
||||
?assertEqual(OUT, is_pool(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec is_dataset(bitstring()) -> true | false.
|
||||
is_dataset(Bitstring) ->
|
||||
is_dataset(Bitstring, <<>>).
|
||||
|
||||
-spec is_dataset(bitstring(), bitstring()) -> true | false.
|
||||
is_dataset(<<$/, _/bitstring>>, <<>>) ->
|
||||
false;
|
||||
is_dataset(<<>>, Buf)
|
||||
when Buf =/= <<>> ->
|
||||
true;
|
||||
is_dataset(<<$/, $/, Rest/bitstring>>, _) ->
|
||||
false;
|
||||
is_dataset(<<Char:8, Rest/bitstring>>, Buf)
|
||||
when Char >= $0 andalso Char =< $9 orelse
|
||||
Char >= $A andalso Char =< $Z orelse
|
||||
Char >= $a andalso Char =< $z orelse
|
||||
Char =:= $/ ->
|
||||
is_dataset(Rest, <<Buf/bitstring, Char>>);
|
||||
is_dataset(_, _) ->
|
||||
false.
|
||||
|
||||
is_dataset_test() ->
|
||||
[ is_dataset_test(X) || X <- [1] ].
|
||||
|
||||
is_dataset_test(1) ->
|
||||
IN = <<"test/test">>,
|
||||
OUT = true,
|
||||
?assertEqual(OUT, is_dataset(IN));
|
||||
is_dataset_test(2) ->
|
||||
IN = <<"/test/test">>,
|
||||
OUT = false,
|
||||
?assertEqual(OUT, is_dataset(IN));
|
||||
is_dataset_test(3) ->
|
||||
IN = <<"test//test">>,
|
||||
OUT = false,
|
||||
?assertEqual(OUT, is_dataset(IN)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec is_snapshot(bitstring()) -> true | false.
|
||||
is_snapshot(Bitstring) ->
|
||||
is_snapshot(Bitstring, <<>>).
|
||||
|
||||
-spec is_snapshot(bitstring(), bitstring()) -> true | false.
|
||||
is_snapshot(<<>>, Buf)
|
||||
when Buf =/= <<>> ->
|
||||
true;
|
||||
is_snapshot(<<Char:8, Rest/bitstring>>, Buf) ->
|
||||
ok.
|
||||
|
||||
is_snapshot_test() ->
|
||||
[ is_snapshot_test(X) || X <- [1] ].
|
||||
|
||||
is_snapshot_test(1) ->
|
||||
?assertEqual(ok, ok).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
null_clean(Bitstring) ->
|
||||
null_clean(Bitstring, <<>>).
|
||||
null_clean(<<A, Rest/bitstring>>, Buf)
|
||||
when A =/= 0 ->
|
||||
null_clean(Rest, <<Buf/bitstring, A>>);
|
||||
null_clean(_, Buf) ->
|
||||
Buf.
|
||||
|
||||
null_clean_test() ->
|
||||
[ null_clean_test(X) || X <- lists:seq(1,2) ].
|
||||
|
||||
null_clean_test(1) ->
|
||||
IN = <<"thisisatest",0,0,0,0>>,
|
||||
OUT = <<"thisisatest">>,
|
||||
?assertEqual(OUT, null_clean(IN));
|
||||
null_clean_test(2) ->
|
||||
IN = <<"thisis",0,"atest">>,
|
||||
OUT = <<"thisis">>,
|
||||
?assertEqual(OUT, null_clean(IN)).
|
||||
1
src/zpool.erl
Normal file
1
src/zpool.erl
Normal file
@@ -0,0 +1 @@
|
||||
-module(zpool).
|
||||
0
src/zpool.hrl
Normal file
0
src/zpool.hrl
Normal file
1
src/zpool_test.erl
Normal file
1
src/zpool_test.erl
Normal file
@@ -0,0 +1 @@
|
||||
-module(zpool_test).
|
||||
1
test/README.md
Normal file
1
test/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Test
|
||||
BIN
test/pool@test.zfs
Normal file
BIN
test/pool@test.zfs
Normal file
Binary file not shown.
2454
test/zstreamdump.return
Normal file
2454
test/zstreamdump.return
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user