Monday, October 08, 2018

Samba: Triggering oplock breaks

I have been investigating the Samba code as part of my task to implement oplock break retry code for a multichannel setup. This is a small extract from my notes which looks into how the oplock break is triggered on the samba server.

Registering the oplock break handler

The smbd server forks a new smbd process to handle a new incoming request by a SMB client.

As part of initialisation of the new smbd process an oplock break handler is initialised.



void smbd_process(struct tevent_context *ev_ctx,
                  struct messaging_context *msg_ctx,
                  int sock_fd,
                  bool interactive)
{
..
        status = smbXsrv_client_create(ev_ctx, ev_ctx, msg_ctx, now, &client);
..
        status = smbd_add_connection(client, sock_fd, &xconn);
..
        /* Setup oplocks */
        if (!init_oplocks(sconn))
                exit_server("Failed to init oplocks");
..
}

The oplock message queue is registered along with the necessary call backs.


bool init_oplocks(struct smbd_server_connection *sconn)

{

        DEBUG(3,("init_oplocks: initializing messages.\n"));



        messaging_register(sconn->msg_ctx, sconn, MSG_SMB_BREAK_REQUEST,

                           process_oplock_break_message);

        messaging_register(sconn->msg_ctx, sconn, MSG_SMB_KERNEL_BREAK,

                           process_kernel_oplock_break);

        return true;

}


This is the oplock break handler which handles oplock break requests coming in for files opened by this smbd process.

Triggering an oplock break


For another smbd process attempting to open the file, we end up calling the handler open_file_ntcreate().

In this case, we are not looking at the scenario where kernel oplocks are enabled. Kernel oplocks are useful when you other processes(eg: NFS) using the filesystem exported by samba at the same time. Without kernel oplocks, other processes cannot safely use the filesystem since the locking info is stored by samba in its own databases.


tatic NTSTATUS open_file_ntcreate(connection_struct *conn,

                            struct smb_request *req,

                            uint32_t access_mask,               /* access bits (FILE_READ_DATA etc.) */

                            uint32_t share_access,      /* share constants (FILE_SHARE_READ etc) */

                            uint32_t create_disposition,        /* FILE_OPEN_IF etc. */

                            uint32_t create_options,    /* options such as delete on close. */

                            uint32_t new_dos_attributes,        /* attributes used for new file. */

                            int oplock_request,         /* internal Samba oplock codes. */

                            struct smb2_lease *lease,

                                                        /* Information (FILE_EXISTS etc.) */

                            uint32_t private_flags,     /* Samba specific flags. */

                            int *pinfo,

                            files_struct *fsp)

{

..

        /* ignore any oplock requests if oplocks are disabled */

        if (!lp_oplocks(SNUM(conn)) ||

            IS_VETO_OPLOCK_PATH(conn, smb_fname->base_name)) {

                /* Mask off everything except the private Samba bits. */

                oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;

        }

..

 //First open the file

        fsp_open = open_file(fsp, conn, req, parent_dir,

                             flags|flags2, unx_mode, access_mask,

                             open_access_mask, &new_file_created);

..

 //Fetch the share mode from the database or allocate a fresh one if record doesn't exist.

        lck = get_share_mode_lock(talloc_tos(), id,

                                  conn->connectpath,

                                  smb_fname, &old_write_time);

..

 //Check to see if oplocks are set and if they violate the share mode

        status = open_mode_check(conn, lck,

                                 access_mask, share_access);

..

 //If there is a sharing violation, delay for oplock.

        if (req != NULL) {

                /*

                 * Handle oplocks, deferring the request if delay_for_oplock()

                 * triggered a break message and we have to wait for the break

                 * response.

                 */

                bool delay;

                bool sharing_violation = NT_STATUS_EQUAL(

                        status, NT_STATUS_SHARING_VIOLATION);



  //Here we end up calling send_break_message() to the smbd pid which opened the file 1st.

                delay = delay_for_oplock(fsp, oplock_request, lease, lck,

                                         sharing_violation,

                                         create_disposition,

                                         first_open_attempt);

                if (delay) {

                        schedule_defer_open(lck, fsp->file_id,

                                            request_time, req);

                        TALLOC_FREE(lck);

                        fd_close(fsp);

                        return NT_STATUS_SHARING_VIOLATION;

                }

        }

..

 //And finally set the new oplock for the file.

        /*

         * Setup the oplock info in both the shared memory and

         * file structs.

         */

        status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);

        if (!NT_STATUS_IS_OK(status)) {

                TALLOC_FREE(lck);

                fd_close(fsp);

                return status;

        }



}


Thus the oplock break is trigerred.

Setting a frost alarm on Home assistant

One of the issues with winter is the possibility of ice covering your windscreen which needs to be cleared before you can drive. Clearing ou...