1 %%%-------------------------------------------------------------------
2 %%% File : file_process.erl
3 %%% Author : User Jlouis <jesper.louis.andersen@gmail.com>
4 %%% License : See COPYING
5 %%% Description : The file process implements an interface to a given
6 %%% file. It is possible to carry out the wished operations on the file
7 %%% in question for operating in a Torrent Client. The implementation has
8 %%% an automatic handler for file descriptors: If no request has been
9 %%% received in a given timeout, then the file is closed.
11 %%% Created : 18 Jun 2007 by User Jlouis <jesper.louis.andersen@gmail.com>
12 %%%-------------------------------------------------------------------
13 -module(etorrent_fs_process
).
15 -include("etorrent_mnesia_table.hrl").
18 -behaviour(gen_server
).
21 -export([start_link
/2, get_data
/3, put_data
/4, stop
/1]).
23 %% gen_server callbacks
24 -export([init
/1, handle_call
/3, handle_cast
/2, handle_info
/2,
25 terminate
/2, code_change
/3]).
27 -record(state
, {path
= none
,
30 % If no request has been received in this interval, close the server.
31 -define(REQUEST_TIMEOUT
, 60000).
33 %%====================================================================
35 %%====================================================================
36 %%--------------------------------------------------------------------
37 %% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
38 %% Description: Starts the server
39 %%--------------------------------------------------------------------
40 start_link(Path
, Id
) ->
41 gen_server:start_link(?MODULE
, [Path
, Id
], []).
43 get_data(Pid
, OffSet
, Size
) ->
44 gen_server:call(Pid
, {read_request
, OffSet
, Size
}).
46 put_data(Pid
, Chunk
, Offset
, _Size
) ->
47 gen_server:call(Pid
, {write_request
, Offset
, Chunk
}).
50 gen_server:cast(Pid
, stop
).
52 %%====================================================================
53 %% gen_server callbacks
54 %%====================================================================
55 init([Id
, TorrentId
]) ->
56 %% We'll clean up file descriptors gracefully on termination.
57 process_flag(trap_exit
, true
),
58 #path_map
{ path
= Path
} = etorrent_path_map:select(Id
, TorrentId
),
59 {ok
, Workdir
} = application:get_env(etorrent
, dir
),
60 FullPath
= filename:join([Workdir
, Path
]),
61 {ok
, IODev
} = file:open(FullPath
, [read
, write
, binary, raw
, read_ahead
]),
62 {ok
, #state
{iodev
= IODev
,
63 path
= FullPath
}, ?REQUEST_TIMEOUT
}.
65 %%--------------------------------------------------------------------
66 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
67 %% {reply, Reply, State, Timeout} |
69 %% {noreply, State, Timeout} |
70 %% {stop, Reason, Reply, State} |
71 %% {stop, Reason, State}
72 %% Description: Handling call messages
73 %%--------------------------------------------------------------------
74 handle_call({read_request
, Offset
, Size
}, _From
, State
) ->
75 Data
= read_request(Offset
, Size
, State
),
76 {reply
, Data
, State
, ?REQUEST_TIMEOUT
};
77 handle_call({write_request
, Offset
, Data
}, _From
, S
) ->
78 ok
= write_request(Offset
, Data
, S
),
79 {reply
, ok
, S
, ?REQUEST_TIMEOUT
}.
81 %%--------------------------------------------------------------------
82 %% Function: handle_cast(Msg, State) -> {noreply, State} |
83 %% {noreply, State, Timeout} |
84 %% {stop, Reason, State}
85 %% Description: Handling cast messages
86 %%--------------------------------------------------------------------
87 handle_cast(stop
, S
) ->
89 handle_cast(_Msg
, State
) ->
90 {noreply
, State
, ?REQUEST_TIMEOUT
}.
92 handle_info(timeout
, State
) ->
93 {stop
, normal
, State
};
94 handle_info(Info
, State
) ->
95 error_logger:warning_report([unknown_fs_process
, Info
]),
98 terminate(_Reason
, State
) ->
99 case file:close(State#state
.iodev
) of
101 E
-> ?
log([cant_close_file
, E
]), ok
104 %%--------------------------------------------------------------------
105 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
106 %% Description: Convert process state when code is changed
107 %%--------------------------------------------------------------------
108 code_change(_OldVsn
, State
, _Extra
) ->
111 %%--------------------------------------------------------------------
112 %%% Internal functions
113 %%--------------------------------------------------------------------
115 %%--------------------------------------------------------------------
116 %% Func: read_request(Offset, Size, State) -> {ok, Data}
117 %% | {read_error, posix()}
118 %% | {pos_error, posix()}
119 %% Description: Attempt to read at Offset; Size bytes. Either returns
120 %% ok or an error from the positioning or reading with a posix()
122 %%--------------------------------------------------------------------
123 read_request(Offset
, Size
, State
) ->
124 {ok
, NP
} = file:position(State#state
.iodev
, Offset
),
126 {ok
, Data
} = file:read(State#state
.iodev
, Size
),
129 %%--------------------------------------------------------------------
130 %% Func: write_request(Offset, Bytes, State) -> ok
131 %% | {pos_error, posix()}
132 %% | {write_error, posix()}
133 %% Description: Attempt to write Bytes at offset Offset. Either returns
134 %% ok, or an error from positioning or writing which is posix().
135 %%--------------------------------------------------------------------
136 write_request(Offset
, Bytes
, State
) ->
137 {ok
, NP
} = file:position(State#state
.iodev
, Offset
),
139 ok
= file:write(State#state
.iodev
, Bytes
).
141 %%--------------------------------------------------------------------
144 %%--------------------------------------------------------------------