1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2017. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(raw_file_io_deflate). 21 22-behavior(gen_statem). 23 24-export([init/1, callback_mode/0, terminate/3]). 25-export([opening/3, opened/3]). 26 27-include("file_int.hrl"). 28 29-define(GZIP_WBITS, 16 + 15). 30 31callback_mode() -> state_functions. 32 33init({Owner, Secret, [compressed]}) -> 34 Monitor = monitor(process, Owner), 35 Z = zlib:open(), 36 ok = zlib:deflateInit(Z, default, deflated, ?GZIP_WBITS, 8, default), 37 Data = 38 #{ owner => Owner, 39 monitor => Monitor, 40 secret => Secret, 41 position => 0, 42 zlib => Z }, 43 {ok, opening, Data}. 44 45opening({call, From}, {'$open', Secret, Filename, Modes}, #{ secret := Secret } = Data) -> 46 case raw_file_io:open(Filename, Modes) of 47 {ok, PrivateFd} -> 48 NewData = Data#{ handle => PrivateFd }, 49 {next_state, opened, NewData, [{reply, From, ok}]}; 50 Other -> 51 {stop_and_reply, normal, [{reply, From, Other}]} 52 end; 53opening(_Event, _Contents, _Data) -> 54 {keep_state_and_data, [postpone]}. 55 56%% 57 58opened(info, {'DOWN', Monitor, process, _Owner, Reason}, #{ monitor := Monitor } = Data) -> 59 if 60 Reason =/= kill -> flush_deflate_state(Data); 61 Reason =:= kill -> ignored 62 end, 63 {stop, shutdown}; 64 65opened(info, _Message, _Data) -> 66 keep_state_and_data; 67 68opened({call, {Owner, _Tag} = From}, [close], #{ owner := Owner } = Data) -> 69 #{ handle := PrivateFd } = Data, 70 Response = 71 case flush_deflate_state(Data) of 72 ok -> ?CALL_FD(PrivateFd, close, []); 73 Other -> Other 74 end, 75 {stop_and_reply, normal, [{reply, From, Response}]}; 76 77opened({call, {Owner, _Tag} = From}, [position, Mark], #{ owner := Owner } = Data) -> 78 case position(Data, Mark) of 79 {ok, NewData, Result} -> 80 Response = {ok, Result}, 81 {keep_state, NewData, [{reply, From, Response}]}; 82 Other -> 83 {keep_state_and_data, [{reply, From, Other}]} 84 end; 85 86opened({call, {Owner, _Tag} = From}, [write, IOVec], #{ owner := Owner } = Data) -> 87 case write(Data, IOVec) of 88 {ok, NewData} -> {keep_state, NewData, [{reply, From, ok}]}; 89 Other -> {keep_state_and_data, [{reply, From, Other}]} 90 end; 91 92opened({call, {Owner, _Tag} = From}, [read, _Size], #{ owner := Owner }) -> 93 Response = {error, ebadf}, 94 {keep_state_and_data, [{reply, From, Response}]}; 95 96opened({call, {Owner, _Tag} = From}, [read_line], #{ owner := Owner }) -> 97 Response = {error, ebadf}, 98 {keep_state_and_data, [{reply, From, Response}]}; 99 100opened({call, {Owner, _Tag} = From}, _Command, #{ owner := Owner }) -> 101 Response = {error, enotsup}, 102 {keep_state_and_data, [{reply, From, Response}]}; 103 104opened({call, _From}, _Command, _Data) -> 105 %% The client functions filter this out, so we'll crash if the user does 106 %% anything stupid on purpose. 107 {shutdown, protocol_violation}; 108 109opened(_Event, _Request, _Data) -> 110 keep_state_and_data. 111 112write(Data, IOVec) -> 113 #{ handle := PrivateFd, position := Position, zlib := Z } = Data, 114 UncompressedSize = iolist_size(IOVec), 115 case ?CALL_FD(PrivateFd, write, [zlib:deflate(Z, IOVec)]) of 116 ok -> {ok, Data#{ position := (Position + UncompressedSize) }}; 117 Other -> Other 118 end. 119 120%% 121%% We support "seeking" forward as long as it isn't relative to EOF. 122%% 123%% Seeking is a bit of a misnomer as it's really just compressing zeroes until 124%% we reach the desired point, but it has always behaved like this. 125%% 126 127position(Data, Mark) when is_atom(Mark) -> 128 position(Data, {Mark, 0}); 129position(Data, Offset) when is_integer(Offset) -> 130 position(Data, {bof, Offset}); 131position(Data, {bof, Offset}) when is_integer(Offset) -> 132 position_1(Data, Offset); 133position(Data, {cur, Offset}) when is_integer(Offset) -> 134 #{ position := Position } = Data, 135 position_1(Data, Position + Offset); 136position(_Data, {eof, Offset}) when is_integer(Offset) -> 137 {error, einval}; 138position(_Data, _Any) -> 139 {error, badarg}. 140 141position_1(#{ position := Desired } = Data, Desired) -> 142 {ok, Data, Desired}; 143position_1(#{ position := Current } = Data, Desired) when Current < Desired -> 144 BytesToWrite = min(Desired - Current, 4 bsl 20), 145 case write(Data, <<0:(BytesToWrite)/unit:8>>) of 146 {ok, NewData} -> position_1(NewData, Desired); 147 Other -> Other 148 end; 149position_1(#{ position := Current }, Desired) when Current > Desired -> 150 {error, einval}. 151 152flush_deflate_state(#{ handle := PrivateFd, zlib := Z }) -> 153 case ?CALL_FD(PrivateFd, write, [zlib:deflate(Z, [], finish)]) of 154 ok -> ok; 155 Other -> Other 156 end. 157 158terminate(_Reason, _State, _Data) -> 159 ok. 160