<commit_before>%%%--------------------------------------------------------------------
%%% @author <NAME>
%%% @copyright (C) 2016 ACK CYFRONET AGH
%%% This software is released under the MIT license
%%% cited in 'LICENSE.txt'.
%%% @end
%%%--------------------------------------------------------------------
%%% @doc
%%% This module is responsible for handing requests operating on file attributes.
%%% @end
%%%--------------------------------------------------------------------
-module(file_req).
-author("<NAME>").

-include("proto/oneclient/fuse_messages.hrl").
-include("modules/fslogic/fslogic_common.hrl").
-include_lib("ctool/include/posix/acl.hrl").
-include_lib("ctool/include/logging.hrl").

%% API
-export([create_file/5, make_file/4, get_file_location/2, open_file/3,
    open_file_insecure/3, open_file_insecure/4, fsync/4, release/3]).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @equiv create_file_insecure/5 with permission checks
%% @end
%%--------------------------------------------------------------------
-spec create_file(user_ctx:ctx(), ParentFileCtx :: file_ctx:ctx(), Name :: file_meta:name(),
    Mode :: file_meta:posix_permissions(), Flags :: fslogic_worker:open_flag()) ->
    fslogic_worker:fuse_response().
create_file(UserCtx, ParentFileCtx, Name, Mode, _Flag) ->
    check_permissions:execute(
        [traverse_ancestors, ?traverse_container, ?add_object],
        [UserCtx, ParentFileCtx, Name, Mode, _Flag],
        fun create_file_insecure/5).

%%--------------------------------------------------------------------
%% @equiv make_file_insecure/4 with permission checks
%% @end
%%--------------------------------------------------------------------
-spec make_file(user_ctx:ctx(), ParentFileCtx :: file_ctx:ctx(), Name :: file_meta:name(),
    Mode :: file_meta:posix_permissions()) -> fslogic_worker:fuse_response().
make_file(UserCtx, ParentFileCtx, Name, Mode) ->
    check_permissions:execute(
        [traverse_ancestors, ?traverse_container, ?add_object],
        [UserCtx, ParentFileCtx, Name, Mode],
        fun make_file_insecure/4).

%%--------------------------------------------------------------------
%% @equiv get_file_location_insecure/2 with permission checks
%% @end
%%--------------------------------------------------------------------
-spec get_file_location(user_ctx:ctx(), file_ctx:ctx()) ->
    fslogic_worker:fuse_response().
get_file_location(_UserCtx, FileCtx) ->
    check_permissions:execute(
        [traverse_ancestors],
        [_UserCtx, FileCtx],
        fun get_file_location_insecure/2).

%%--------------------------------------------------------------------
%% @equiv open_file(UserCtx, FileCtx) with permission check
%% depending on the open flag.
%% @end
%%--------------------------------------------------------------------
-spec open_file(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    OpenFlag :: fslogic_worker:open_flag()) -> no_return() | #fuse_response{}.
open_file(UserCtx, FileCtx, read) ->
    open_file_for_read(UserCtx, FileCtx);
open_file(UserCtx, FileCtx, write) ->
    open_file_for_write(UserCtx, FileCtx);
open_file(UserCtx, FileCtx, rdwr) ->
    open_file_for_rdwr(UserCtx, FileCtx).

%%--------------------------------------------------------------------
%% @equiv open_file_insecure(UserCtx, FileCtx, Flag, undefined).
%% @end
%%--------------------------------------------------------------------
-spec open_file_insecure(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    fslogic_worker:open_flag()) -> no_return() | #fuse_response{}.
open_file_insecure(UserCtx, FileCtx, Flag) ->
    open_file_insecure(UserCtx, FileCtx, Flag, undefined).

%%--------------------------------------------------------------------
%% @doc
%% Opens a file and returns a handle to it.
%% @end
%%--------------------------------------------------------------------
-spec open_file_insecure(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    fslogic_worker:open_flag(), storage_file_manager:handle_id() | undefined) ->
    no_return() | #fuse_response{}.
open_file_insecure(UserCtx, FileCtx, Flag, HandleId0) ->
    SessId = user_ctx:get_session_id(UserCtx),
    FileCtx2 = sfm_utils:create_delayed_storage_file(FileCtx),
    {SFMHandle, FileCtx3} = storage_file_manager:new_handle(SessId, FileCtx2),
    SFMHandle2 = storage_file_manager:add_size(SFMHandle),
    {ok, Handle} = storage_file_manager:open(SFMHandle2, Flag),
    {ok, HandleId} = save_handle(SessId, Handle, HandleId0),
    ok = file_handles:register_open(FileCtx3, SessId, 1),
    #fuse_response{
        status = #status{code = ?OK},
        fuse_response = #file_opened{handle_id = HandleId}
    }.

%%--------------------------------------------------------------------
%% @equiv fsync_insecure(UserCtx, FileCtx, DataOnly) with permission check
%% @end
%%--------------------------------------------------------------------
-spec fsync(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    boolean(), binary()) -> no_return() | #fuse_response{}.
fsync(UserCtx, FileCtx, DataOnly, HandleId) ->
    check_permissions:execute(
        [traverse_ancestors],
        [UserCtx, FileCtx, DataOnly, HandleId],
        fun fsync_insecure/4).

%%--------------------------------------------------------------------
%% @doc
%% Removes file handle saved in session.
%% @end
%%--------------------------------------------------------------------
-spec release(user_ctx:ctx(), file_ctx:ctx(), HandleId :: binary()) ->
    fslogic_worker:fuse_response().
release(UserCtx, FileCtx, HandleId) ->
    SessId = user_ctx:get_session_id(UserCtx),
    ok = case session:get_handle(SessId, HandleId) of
        {ok, SfmHandle} ->
            ok = session:remove_handle(SessId, HandleId),
            ok = file_handles:register_release(FileCtx, SessId, 1),
            ok = storage_file_manager:release(SfmHandle);
        {error, link_not_found} ->
            ok;
        {error, {not_found, _}} ->
            ok;
        Other ->
            Other
    end,
    ok = file_popularity:increment_open(FileCtx),
    #fuse_response{status = #status{code = ?OK}}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Creates and open file. Returns handle to the file, its attributes
%% and location.
%% @end
%%--------------------------------------------------------------------
-spec create_file_insecure(user_ctx:ctx(), ParentFileCtx :: file_ctx:ctx(), Name :: file_meta:name(),
    Mode :: file_meta:posix_permissions(), Flags :: fslogic_worker:open_flag()) ->
    fslogic_worker:fuse_response().
create_file_insecure(UserCtx, ParentFileCtx, Name, Mode, _Flag) ->
    FileCtx = create_file_doc(UserCtx, ParentFileCtx, Name, Mode),
    FileCtx2 = sfm_utils:create_storage_file(UserCtx, FileCtx),
    {FileLocation, FileCtx3} = sfm_utils:create_storage_file_location(FileCtx2, true),
    fslogic_times:update_mtime_ctime(ParentFileCtx),

    HandleId = case user_ctx:is_direct_io(UserCtx) of
        true ->
            base64:encode(crypto:strong_rand_bytes(20));
        _ ->
            % open file on adequate node
            FileGuid = file_ctx:get_guid_const(FileCtx3),
            Node = consistent_hasing:get_node(FileGuid),
            #fuse_response{
                status = #status{code = ?OK},
                fuse_response = #file_opened{handle_id = HId}
            } = rpc:call(Node, file_req, open_file_insecure,
                [UserCtx, FileCtx3, rdwr]),
            HId
    end,

    #fuse_response{fuse_response = #file_attr{} = FileAttr} =
        attr_req:get_file_attr_insecure(UserCtx, FileCtx3, false, false),
    #fuse_response{
        status = #status{code = ?OK},
        fuse_response = #file_created{
            handle_id = HandleId,
            file_attr = FileAttr,<commit_msg>VFS-3500 Return size 0 instead of undefined for new file<commit_after>%%%--------------------------------------------------------------------
%%% @author <NAME>
%%% @copyright (C) 2016 ACK CYFRONET AGH
%%% This software is released under the MIT license
%%% cited in 'LICENSE.txt'.
%%% @end
%%%--------------------------------------------------------------------
%%% @doc
%%% This module is responsible for handing requests operating on file attributes.
%%% @end
%%%--------------------------------------------------------------------
-module(file_req).
-author("<NAME>").

-include("proto/oneclient/fuse_messages.hrl").
-include("modules/fslogic/fslogic_common.hrl").
-include_lib("ctool/include/posix/acl.hrl").
-include_lib("ctool/include/logging.hrl").

%% API
-export([create_file/5, make_file/4, get_file_location/2, open_file/3,
    open_file_insecure/3, open_file_insecure/4, fsync/4, release/3]).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @equiv create_file_insecure/5 with permission checks
%% @end
%%--------------------------------------------------------------------
-spec create_file(user_ctx:ctx(), ParentFileCtx :: file_ctx:ctx(), Name :: file_meta:name(),
    Mode :: file_meta:posix_permissions(), Flags :: fslogic_worker:open_flag()) ->
    fslogic_worker:fuse_response().
create_file(UserCtx, ParentFileCtx, Name, Mode, _Flag) ->
    check_permissions:execute(
        [traverse_ancestors, ?traverse_container, ?add_object],
        [UserCtx, ParentFileCtx, Name, Mode, _Flag],
        fun create_file_insecure/5).

%%--------------------------------------------------------------------
%% @equiv make_file_insecure/4 with permission checks
%% @end
%%--------------------------------------------------------------------
-spec make_file(user_ctx:ctx(), ParentFileCtx :: file_ctx:ctx(), Name :: file_meta:name(),
    Mode :: file_meta:posix_permissions()) -> fslogic_worker:fuse_response().
make_file(UserCtx, ParentFileCtx, Name, Mode) ->
    check_permissions:execute(
        [traverse_ancestors, ?traverse_container, ?add_object],
        [UserCtx, ParentFileCtx, Name, Mode],
        fun make_file_insecure/4).

%%--------------------------------------------------------------------
%% @equiv get_file_location_insecure/2 with permission checks
%% @end
%%--------------------------------------------------------------------
-spec get_file_location(user_ctx:ctx(), file_ctx:ctx()) ->
    fslogic_worker:fuse_response().
get_file_location(_UserCtx, FileCtx) ->
    check_permissions:execute(
        [traverse_ancestors],
        [_UserCtx, FileCtx],
        fun get_file_location_insecure/2).

%%--------------------------------------------------------------------
%% @equiv open_file(UserCtx, FileCtx) with permission check
%% depending on the open flag.
%% @end
%%--------------------------------------------------------------------
-spec open_file(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    OpenFlag :: fslogic_worker:open_flag()) -> no_return() | #fuse_response{}.
open_file(UserCtx, FileCtx, read) ->
    open_file_for_read(UserCtx, FileCtx);
open_file(UserCtx, FileCtx, write) ->
    open_file_for_write(UserCtx, FileCtx);
open_file(UserCtx, FileCtx, rdwr) ->
    open_file_for_rdwr(UserCtx, FileCtx).

%%--------------------------------------------------------------------
%% @equiv open_file_insecure(UserCtx, FileCtx, Flag, undefined).
%% @end
%%--------------------------------------------------------------------
-spec open_file_insecure(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    fslogic_worker:open_flag()) -> no_return() | #fuse_response{}.
open_file_insecure(UserCtx, FileCtx, Flag) ->
    open_file_insecure(UserCtx, FileCtx, Flag, undefined).

%%--------------------------------------------------------------------
%% @doc
%% Opens a file and returns a handle to it.
%% @end
%%--------------------------------------------------------------------
-spec open_file_insecure(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    fslogic_worker:open_flag(), storage_file_manager:handle_id() | undefined) ->
    no_return() | #fuse_response{}.
open_file_insecure(UserCtx, FileCtx, Flag, HandleId0) ->
    SessId = user_ctx:get_session_id(UserCtx),
    FileCtx2 = sfm_utils:create_delayed_storage_file(FileCtx),
    {SFMHandle, FileCtx3} = storage_file_manager:new_handle(SessId, FileCtx2),
    SFMHandle2 = storage_file_manager:add_size(SFMHandle),
    {ok, Handle} = storage_file_manager:open(SFMHandle2, Flag),
    {ok, HandleId} = save_handle(SessId, Handle, HandleId0),
    ok = file_handles:register_open(FileCtx3, SessId, 1),
    #fuse_response{
        status = #status{code = ?OK},
        fuse_response = #file_opened{handle_id = HandleId}
    }.

%%--------------------------------------------------------------------
%% @equiv fsync_insecure(UserCtx, FileCtx, DataOnly) with permission check
%% @end
%%--------------------------------------------------------------------
-spec fsync(user_ctx:ctx(), FileCtx :: file_ctx:ctx(),
    boolean(), binary()) -> no_return() | #fuse_response{}.
fsync(UserCtx, FileCtx, DataOnly, HandleId) ->
    check_permissions:execute(
        [traverse_ancestors],
        [UserCtx, FileCtx, DataOnly, HandleId],
        fun fsync_insecure/4).

%%--------------------------------------------------------------------
%% @doc
%% Removes file handle saved in session.
%% @end
%%--------------------------------------------------------------------
-spec release(user_ctx:ctx(), file_ctx:ctx(), HandleId :: binary()) ->
    fslogic_worker:fuse_response().
release(UserCtx, FileCtx, HandleId) ->
    SessId = user_ctx:get_session_id(UserCtx),
    ok = case session:get_handle(SessId, HandleId) of
        {ok, SfmHandle} ->
            ok = session:remove_handle(SessId, HandleId),
            ok = file_handles:register_release(FileCtx, SessId, 1),
            ok = storage_file_manager:release(SfmHandle);
        {error, link_not_found} ->
            ok;
        {error, {not_found, _}} ->
            ok;
        Other ->
            Other
    end,
    ok = file_popularity:increment_open(FileCtx),
    #fuse_response{status = #status{code = ?OK}}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Creates and open file. Returns handle to the file, its attributes
%% and location.
%% @end
%%--------------------------------------------------------------------
-spec create_file_insecure(user_ctx:ctx(), ParentFileCtx :: file_ctx:ctx(), Name :: file_meta:name(),
    Mode :: file_meta:posix_permissions(), Flags :: fslogic_worker:open_flag()) ->
    fslogic_worker:fuse_response().
create_file_insecure(UserCtx, ParentFileCtx, Name, Mode, _Flag) ->
    FileCtx = create_file_doc(UserCtx, ParentFileCtx, Name, Mode),
    FileCtx2 = sfm_utils:create_storage_file(UserCtx, FileCtx),
    {FileLocation, FileCtx3} = sfm_utils:create_storage_file_location(FileCtx2, true),
    fslogic_times:update_mtime_ctime(ParentFileCtx),

    HandleId = case user_ctx:is_direct_io(UserCtx) of
        true ->
            base64:encode(crypto:strong_rand_bytes(20));
        _ ->
            % open file on adequate node
            FileGuid = file_ctx:get_guid_const(FileCtx3),
            Node = consistent_hasing:get_node(FileGuid),
            #fuse_response{
                status = #status{code = ?OK},
                fuse_response = #file_opened{handle_id = HId}
            } = rpc:call(Node, file_req, open_file_insecure,
                [UserCtx, FileCtx3, rdwr]),
            HId
    end,

    #fuse_response{fuse_response = #file_attr{size = Size} = FileAttr} =
        attr_req:get_file_attr_insecure(UserCtx, FileCtx3, false, false),
    FileAttr2 = case Size of
        undefined ->
            FileAttr#file_attr{size = 0};
        _ ->
            FileAttr
    end,
    #fuse_response{
        status = #status{code = ?OK},
        fuse_response = #file_created{
            handle_id = HandleId,
            file_attr = FileAttr2,
{'fraction_non_alphanumeric': 0.31832033154558753, 'fraction_numerical': 0.005241345685031692, 'mean_word_length': 2.008065994500458, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2224698', 'n_tokens_mistral': 4599, 'n_tokens_neox': 4642, 'n_words': 838}