Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/fs/smb/client/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 14 kB image not shown  

Quelle  cifstransport.c   Sprache: C

 
// SPDX-License-Identifier: LGPL-2.1
/*
 *
 *   Copyright (C) International Business Machines  Corp., 2002,2008
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *   Jeremy Allison (jra@samba.org) 2006.
 *
 */


#include <linux/fs.h>
#include <linux/list.h>
#include <linux/gfp.h>
#include <linux/wait.h>
#include <linux/net.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/tcp.h>
#include <linux/bvec.h>
#include <linux/highmem.h>
#include <linux/uaccess.h>
#include <linux/processor.h>
#include <linux/mempool.h>
#include <linux/sched/signal.h>
#include <linux/task_io_accounting_ops.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smb2proto.h"
#include "smbdirect.h"
#include "compress.h"

/* Max number of iovectors we can use off the stack when sending requests. */
#define CIFS_MAX_IOV_SIZE 8

static struct mid_q_entry *
alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
{
 struct mid_q_entry *temp;

 if (server == NULL) {
  cifs_dbg(VFS, "%s: null TCP session\n", __func__);
  return NULL;
 }

 temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
 memset(temp, 0, sizeof(struct mid_q_entry));
 kref_init(&temp->refcount);
 spin_lock_init(&temp->mid_lock);
 temp->mid = get_mid(smb_buffer);
 temp->pid = current->pid;
 temp->command = cpu_to_le16(smb_buffer->Command);
 cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
 /* easier to use jiffies */
 /* when mid allocated can be before when sent */
 temp->when_alloc = jiffies;
 temp->server = server;

 /*
 * The default is for the mid to be synchronous, so the
 * default callback just wakes up the current task.
 */

 get_task_struct(current);
 temp->creator = current;
 temp->callback = cifs_wake_up_task;
 temp->callback_data = current;

 atomic_inc(&mid_count);
 temp->mid_state = MID_REQUEST_ALLOCATED;
 return temp;
}

int
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
  unsigned int smb_buf_length)
{
 struct kvec iov[2];
 struct smb_rqst rqst = { .rq_iov = iov,
     .rq_nvec = 2 };

 iov[0].iov_base = smb_buffer;
 iov[0].iov_len = 4;
 iov[1].iov_base = (char *)smb_buffer + 4;
 iov[1].iov_len = smb_buf_length;

 return __smb_send_rqst(server, 1, &rqst);
}

static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
   struct mid_q_entry **ppmidQ)
{
 spin_lock(&ses->ses_lock);
 if (ses->ses_status == SES_NEW) {
  if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
   (in_buf->Command != SMB_COM_NEGOTIATE)) {
   spin_unlock(&ses->ses_lock);
   return -EAGAIN;
  }
  /* else ok - we are setting up session */
 }

 if (ses->ses_status == SES_EXITING) {
  /* check if SMB session is bad because we are setting it up */
  if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
   spin_unlock(&ses->ses_lock);
   return -EAGAIN;
  }
  /* else ok - we are shutting down session */
 }
 spin_unlock(&ses->ses_lock);

 *ppmidQ = alloc_mid(in_buf, ses->server);
 if (*ppmidQ == NULL)
  return -ENOMEM;
 spin_lock(&ses->server->mid_queue_lock);
 list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
 spin_unlock(&ses->server->mid_queue_lock);
 return 0;
}

struct mid_q_entry *
cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
 int rc;
 struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
 struct mid_q_entry *mid;

 if (rqst->rq_iov[0].iov_len != 4 ||
     rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
  return ERR_PTR(-EIO);

 /* enable signing if server requires it */
 if (server->sign)
  hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;

 mid = alloc_mid(hdr, server);
 if (mid == NULL)
  return ERR_PTR(-ENOMEM);

 rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
 if (rc) {
  release_mid(mid);
  return ERR_PTR(rc);
 }

 return mid;
}

/*
 *
 * Send an SMB Request.  No response info (other than return code)
 * needs to be parsed.
 *
 * flags indicate the type of request buffer and how long to wait
 * and whether to log NT STATUS code (error) before mapping it to POSIX error
 *
 */

int
SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
   char *in_buf, int flags)
{
 int rc;
 struct kvec iov[1];
 struct kvec rsp_iov;
 int resp_buf_type;

 iov[0].iov_base = in_buf;
 iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
 flags |= CIFS_NO_RSP_BUF;
 rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
 cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);

 return rc;
}

int
cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
     bool log_error)
{
 unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;

 dump_smb(mid->resp_buf, min_t(u32, 92, len));

 /* convert the length into a more usable form */
 if (server->sign) {
  struct kvec iov[2];
  int rc = 0;
  struct smb_rqst rqst = { .rq_iov = iov,
      .rq_nvec = 2 };

  iov[0].iov_base = mid->resp_buf;
  iov[0].iov_len = 4;
  iov[1].iov_base = (char *)mid->resp_buf + 4;
  iov[1].iov_len = len - 4;
  /* FIXME: add code to kill session */
  rc = cifs_verify_signature(&rqst, server,
        mid->sequence_number);
  if (rc)
   cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
     rc);
 }

 /* BB special case reconnect tid and uid here? */
 return map_and_check_smb_error(mid, log_error);
}

struct mid_q_entry *
cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
     struct smb_rqst *rqst)
{
 int rc;
 struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
 struct mid_q_entry *mid;

 if (rqst->rq_iov[0].iov_len != 4 ||
     rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
  return ERR_PTR(-EIO);

 rc = allocate_mid(ses, hdr, &mid);
 if (rc)
  return ERR_PTR(rc);
 rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
 if (rc) {
  delete_mid(mid);
  return ERR_PTR(rc);
 }
 return mid;
}

int
SendReceive2(const unsigned int xid, struct cifs_ses *ses,
      struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
      const int flags, struct kvec *resp_iov)
{
 struct smb_rqst rqst;
 struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
 int rc;

 if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
  new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
     GFP_KERNEL);
  if (!new_iov) {
   /* otherwise cifs_send_recv below sets resp_buf_type */
   *resp_buf_type = CIFS_NO_BUFFER;
   return -ENOMEM;
  }
 } else
  new_iov = s_iov;

 /* 1st iov is a RFC1001 length followed by the rest of the packet */
 memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));

 new_iov[0].iov_base = new_iov[1].iov_base;
 new_iov[0].iov_len = 4;
 new_iov[1].iov_base += 4;
 new_iov[1].iov_len -= 4;

 memset(&rqst, 0, sizeof(struct smb_rqst));
 rqst.rq_iov = new_iov;
 rqst.rq_nvec = n_vec + 1;

 rc = cifs_send_recv(xid, ses, ses->server,
       &rqst, resp_buf_type, flags, resp_iov);
 if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
  kfree(new_iov);
 return rc;
}

int
SendReceive(const unsigned int xid, struct cifs_ses *ses,
     struct smb_hdr *in_buf, struct smb_hdr *out_buf,
     int *pbytes_returned, const int flags)
{
 int rc = 0;
 struct mid_q_entry *midQ;
 unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
 struct kvec iov = { .iov_base = in_buf, .iov_len = len };
 struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
 struct cifs_credits credits = { .value = 1, .instance = 0 };
 struct TCP_Server_Info *server;

 if (ses == NULL) {
  cifs_dbg(VFS, "Null smb session\n");
  return -EIO;
 }
 server = ses->server;
 if (server == NULL) {
  cifs_dbg(VFS, "Null tcp session\n");
  return -EIO;
 }

 spin_lock(&server->srv_lock);
 if (server->tcpStatus == CifsExiting) {
  spin_unlock(&server->srv_lock);
  return -ENOENT;
 }
 spin_unlock(&server->srv_lock);

 /* Ensure that we do not send more than 50 overlapping requests
   to the same server. We may make this configurable later or
   use ses->maxReq */


 if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
  cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
    len);
  return -EIO;
 }

 rc = wait_for_free_request(server, flags, &credits.instance);
 if (rc)
  return rc;

 /* make sure that we sign in the same order that we send on this socket
   and avoid races inside tcp sendmsg code that could cause corruption
   of smb data */


 cifs_server_lock(server);

 rc = allocate_mid(ses, in_buf, &midQ);
 if (rc) {
  cifs_server_unlock(server);
  /* Update # of requests on wire to server */
  add_credits(server, &credits, 0);
  return rc;
 }

 rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
 if (rc) {
  cifs_server_unlock(server);
  goto out;
 }

 midQ->mid_state = MID_REQUEST_SUBMITTED;

 rc = smb_send(server, in_buf, len);
 cifs_save_when_sent(midQ);

 if (rc < 0)
  server->sequence_number -= 2;

 cifs_server_unlock(server);

 if (rc < 0)
  goto out;

 rc = wait_for_response(server, midQ);
 if (rc != 0) {
  send_cancel(server, &rqst, midQ);
  spin_lock(&midQ->mid_lock);
  if (midQ->callback) {
   /* no longer considered to be "in-flight" */
   midQ->callback = release_mid;
   spin_unlock(&midQ->mid_lock);
   add_credits(server, &credits, 0);
   return rc;
  }
  spin_unlock(&midQ->mid_lock);
 }

 rc = cifs_sync_mid_result(midQ, server);
 if (rc != 0) {
  add_credits(server, &credits, 0);
  return rc;
 }

 if (!midQ->resp_buf || !out_buf ||
     midQ->mid_state != MID_RESPONSE_READY) {
  rc = -EIO;
  cifs_server_dbg(VFS, "Bad MID state?\n");
  goto out;
 }

 *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
 memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
 rc = cifs_check_receive(midQ, server, 0);
out:
 delete_mid(midQ);
 add_credits(server, &credits, 0);

 return rc;
}

/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
   blocking lock to return. */


static int
send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
   struct smb_hdr *in_buf,
   struct smb_hdr *out_buf)
{
 int bytes_returned;
 struct cifs_ses *ses = tcon->ses;
 LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;

 /* We just modify the current in_buf to change
   the type of lock from LOCKING_ANDX_SHARED_LOCK
   or LOCKING_ANDX_EXCLUSIVE_LOCK to
   LOCKING_ANDX_CANCEL_LOCK. */


 pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
 pSMB->Timeout = 0;
 pSMB->hdr.Mid = get_next_mid(ses->server);

 return SendReceive(xid, ses, in_buf, out_buf,
   &bytes_returned, 0);
}

int
SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
     struct smb_hdr *in_buf, struct smb_hdr *out_buf,
     int *pbytes_returned)
{
 int rc = 0;
 int rstart = 0;
 struct mid_q_entry *midQ;
 struct cifs_ses *ses;
 unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
 struct kvec iov = { .iov_base = in_buf, .iov_len = len };
 struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
 unsigned int instance;
 struct TCP_Server_Info *server;

 if (tcon == NULL || tcon->ses == NULL) {
  cifs_dbg(VFS, "Null smb session\n");
  return -EIO;
 }
 ses = tcon->ses;
 server = ses->server;

 if (server == NULL) {
  cifs_dbg(VFS, "Null tcp session\n");
  return -EIO;
 }

 spin_lock(&server->srv_lock);
 if (server->tcpStatus == CifsExiting) {
  spin_unlock(&server->srv_lock);
  return -ENOENT;
 }
 spin_unlock(&server->srv_lock);

 /* Ensure that we do not send more than 50 overlapping requests
   to the same server. We may make this configurable later or
   use ses->maxReq */


 if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
  cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
         len);
  return -EIO;
 }

 rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
 if (rc)
  return rc;

 /* make sure that we sign in the same order that we send on this socket
   and avoid races inside tcp sendmsg code that could cause corruption
   of smb data */


 cifs_server_lock(server);

 rc = allocate_mid(ses, in_buf, &midQ);
 if (rc) {
  cifs_server_unlock(server);
  return rc;
 }

 rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
 if (rc) {
  delete_mid(midQ);
  cifs_server_unlock(server);
  return rc;
 }

 midQ->mid_state = MID_REQUEST_SUBMITTED;
 rc = smb_send(server, in_buf, len);
 cifs_save_when_sent(midQ);

 if (rc < 0)
  server->sequence_number -= 2;

 cifs_server_unlock(server);

 if (rc < 0) {
  delete_mid(midQ);
  return rc;
 }

 /* Wait for a reply - allow signals to interrupt. */
 rc = wait_event_interruptible(server->response_q,
  (!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
     midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
  ((server->tcpStatus != CifsGood) &&
   (server->tcpStatus != CifsNew)));

 /* Were we interrupted by a signal ? */
 spin_lock(&server->srv_lock);
 if ((rc == -ERESTARTSYS) &&
  (midQ->mid_state == MID_REQUEST_SUBMITTED ||
   midQ->mid_state == MID_RESPONSE_RECEIVED) &&
  ((server->tcpStatus == CifsGood) ||
   (server->tcpStatus == CifsNew))) {
  spin_unlock(&server->srv_lock);

  if (in_buf->Command == SMB_COM_TRANSACTION2) {
   /* POSIX lock. We send a NT_CANCEL SMB to cause the
   blocking lock to return. */

   rc = send_cancel(server, &rqst, midQ);
   if (rc) {
    delete_mid(midQ);
    return rc;
   }
  } else {
   /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
   to cause the blocking lock to return. */


   rc = send_lock_cancel(xid, tcon, in_buf, out_buf);

   /* If we get -ENOLCK back the lock may have
   already been removed. Don't exit in this case. */

   if (rc && rc != -ENOLCK) {
    delete_mid(midQ);
    return rc;
   }
  }

  rc = wait_for_response(server, midQ);
  if (rc) {
   send_cancel(server, &rqst, midQ);
   spin_lock(&midQ->mid_lock);
   if (midQ->callback) {
    /* no longer considered to be "in-flight" */
    midQ->callback = release_mid;
    spin_unlock(&midQ->mid_lock);
    return rc;
   }
   spin_unlock(&midQ->mid_lock);
  }

  /* We got the response - restart system call. */
  rstart = 1;
  spin_lock(&server->srv_lock);
 }
 spin_unlock(&server->srv_lock);

 rc = cifs_sync_mid_result(midQ, server);
 if (rc != 0)
  return rc;

 /* rcvd frame is ok */
 if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
  rc = -EIO;
  cifs_tcon_dbg(VFS, "Bad MID state?\n");
  goto out;
 }

 *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
 memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
 rc = cifs_check_receive(midQ, server, 0);
out:
 delete_mid(midQ);
 if (rstart && rc == -EACCES)
  return -ERESTARTSYS;
 return rc;
}

Messung V0.5
C=97 H=96 G=96

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.