RE: [PATCH v3 1/3] CIFS: make IPC a regular tcon

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2018-01-24 4:46 GMT-08:00 Aurelien Aptel <aaptel@xxxxxxxx>:
> * Remove ses->ipc_tid.
> * Make IPC$ regular tcon.
> * Add a direct pointer to it in ses->tcon_ipc.
> * Distinguish PIPE tcon from IPC tcon by adding a tcon->pipe flag. All
>   IPC tcons are pipes but not all pipes are IPC.
> * All TreeConnect functions now cannot take a NULL tcon object.
>
> The IPC tcon has the same lifetime as the session it belongs to. It is
> created when the session is created and destroyed when the session is
> destroyed.
>
> Since no mounts directly refer to the IPC tcon, its refcount should
> always be set to initialisation value (1). Thus we make sure
> cifs_put_tcon() skips it.
>
> If the mount request resulting in a new session being created requires
> encryption, try to require it too for IPC.
>
> * set SERVER_NAME_LENGTH to serverName actual size
>
> The maximum length of an ipv6 string representation is defined in
> INET6_ADDRSTRLEN as 45+1 for null but lets keep what we know works.
>
> Signed-off-by: Aurelien Aptel <aaptel@xxxxxxxx>
> ---
>  fs/cifs/cifsglob.h |  14 ++---
>  fs/cifs/cifssmb.c  |   7 +--
>  fs/cifs/connect.c  | 150 ++++++++++++++++++++++++++++++++++++++++-------------
>  fs/cifs/inode.c    |   2 +-
>  fs/cifs/smb2pdu.c  |  36 +++----------
>  5 files changed, 133 insertions(+), 76 deletions(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 678e638c1e69..48f7c197cd2d 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -64,8 +64,8 @@
>  #define RFC1001_NAME_LEN 15
>  #define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1)
>
> -/* currently length of NIP6_FMT */
> -#define SERVER_NAME_LENGTH 40
> +/* maximum length of ip addr as a string (including ipv6 and sctp) */
> +#define SERVER_NAME_LENGTH 80
>  #define SERVER_NAME_LEN_WITH_NULL     (SERVER_NAME_LENGTH + 1)
>
>  /* echo interval in seconds */
> @@ -833,12 +833,12 @@ static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
>  struct cifs_ses {
>         struct list_head smb_ses_list;
>         struct list_head tcon_list;
> +       struct cifs_tcon *tcon_ipc;
>         struct mutex session_mutex;
>         struct TCP_Server_Info *server; /* pointer to server info */
>         int ses_count;          /* reference counter */
>         enum statusEnum status;
>         unsigned overrideSecFlg;  /* if non-zero override global sec flags */
> -       __u32 ipc_tid;          /* special tid for connection to IPC share */
>         char *serverOS;         /* name of operating system underlying server */
>         char *serverNOS;        /* name of network operating system of server */
>         char *serverDomain;     /* security realm of server */
> @@ -846,8 +846,7 @@ struct cifs_ses {
>         kuid_t linux_uid;       /* overriding owner of files on the mount */
>         kuid_t cred_uid;        /* owner of credentials */
>         unsigned int capabilities;
> -       char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for
> -                               TCP names - will ipv6 and sctp addresses fit? */
> +       char serverName[SERVER_NAME_LEN_WITH_NULL];
>         char *user_name;        /* must not be null except during init of sess
>                                    and after mount option parsing we fill it */
>         char *domainName;
> @@ -942,7 +941,9 @@ struct cifs_tcon {
>         FILE_SYSTEM_DEVICE_INFO fsDevInfo;
>         FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
>         FILE_SYSTEM_UNIX_INFO fsUnixInfo;
> -       bool ipc:1;             /* set if connection to IPC$ eg for RPC/PIPES */
> +       bool ipc:1;   /* set if connection to IPC$ share (always also pipe) */
> +       bool pipe:1;  /* set if connection to pipe share */
> +       bool print:1; /* set if connection to printer share */
>         bool retry:1;
>         bool nocase:1;
>         bool seal:1;      /* transport encryption for this mounted share */
> @@ -955,7 +956,6 @@ struct cifs_tcon {
>         bool need_reopen_files:1; /* need to reopen tcon file handles */
>         bool use_resilient:1; /* use resilient instead of durable handles */
>         bool use_persistent:1; /* use persistent instead of durable handles */
> -       bool print:1;           /* set if connection to printer share */
>         __le32 capabilities;
>         __u32 share_flags;
>         __u32 maximal_access;
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index 49cf999f3d46..4e0922d24eb2 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -4833,10 +4833,11 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
>         *target_nodes = NULL;
>
>         cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name);
> -       if (ses == NULL)
> +       if (ses == NULL || ses->tcon_ipc == NULL)
>                 return -ENODEV;
> +
>  getDFSRetry:
> -       rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
> +       rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, (void **) &pSMB,
>                       (void **) &pSMBr);
>         if (rc)
>                 return rc;
> @@ -4844,7 +4845,7 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
>         /* server pointer checked in called function,
>         but should never be null here anyway */
>         pSMB->hdr.Mid = get_next_mid(ses->server);
> -       pSMB->hdr.Tid = ses->ipc_tid;
> +       pSMB->hdr.Tid = ses->tcon_ipc->tid;
>         pSMB->hdr.Uid = ses->Suid;
>         if (ses->capabilities & CAP_STATUS32)
>                 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 63c5d85fe25e..8b5e401f547a 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -354,11 +354,12 @@ cifs_reconnect(struct TCP_Server_Info *server)
>         list_for_each(tmp, &server->smb_ses_list) {
>                 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
>                 ses->need_reconnect = true;
> -               ses->ipc_tid = 0;
>                 list_for_each(tmp2, &ses->tcon_list) {
>                         tcon = list_entry(tmp2, struct cifs_tcon, tcon_list);
>                         tcon->need_reconnect = true;
>                 }
> +               if (ses->tcon_ipc)
> +                       ses->tcon_ipc->need_reconnect = true;
>         }
>         spin_unlock(&cifs_tcp_ses_lock);
>
> @@ -2426,6 +2427,93 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
>         return 1;
>  }
>
> +/**
> + * cifs_setup_ipc - helper to setup the IPC tcon for the session
> + *
> + * A new IPC connection is made and stored in the session
> + * tcon_ipc. The IPC tcon has the same lifetime as the session.
> + */
> +static int
> +cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
> +{
> +       int rc = 0, xid;
> +       struct cifs_tcon *tcon;
> +       struct nls_table *nls_codepage;
> +       char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
> +       bool seal = false;
> +
> +       /*
> +        * If the mount request that resulted in the creation of the
> +        * session requires encryption, force IPC to be encrypted too.
> +        */
> +       if (volume_info->seal) {
> +               if (ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
> +                       seal = true;
> +               else {
> +                       cifs_dbg(VFS,
> +                                "IPC: server doesn't support encryption\n");
> +                       return -EOPNOTSUPP;
> +               }
> +       }
> +
> +       tcon = tconInfoAlloc();
> +       if (tcon == NULL)
> +               return -ENOMEM;
> +
> +       snprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->serverName);
> +
> +       /* cannot fail */
> +       nls_codepage = load_nls_default();
> +
> +       xid = get_xid();
> +       tcon->ses = ses;
> +       tcon->ipc = true;
> +       tcon->seal = seal;
> +       rc = ses->server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage);
> +       free_xid(xid);
> +
> +       if (rc) {
> +               cifs_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
> +               tconInfoFree(tcon);
> +               goto out;
> +       }
> +
> +       cifs_dbg(FYI, "IPC tcon rc = %d ipc tid = %d\n", rc, tcon->tid);
> +
> +       ses->tcon_ipc = tcon;
> +out:
> +       unload_nls(nls_codepage);
> +       return rc;
> +}
> +
> +/**
> + * cifs_free_ipc - helper to release the session IPC tcon
> + *
> + * Needs to be called everytime a session is destroyed
> + */
> +static int
> +cifs_free_ipc(struct cifs_ses *ses)
> +{
> +       int rc = 0, xid;
> +       struct cifs_tcon *tcon = ses->tcon_ipc;
> +
> +       if (tcon == NULL)
> +               return 0;
> +
> +       if (ses->server->ops->tree_disconnect) {
> +               xid = get_xid();
> +               rc = ses->server->ops->tree_disconnect(xid, tcon);
> +               free_xid(xid);
> +       }
> +
> +       if (rc)
> +               cifs_dbg(FYI, "failed to disconnect IPC tcon (rc=%d)\n", rc);
> +
> +       tconInfoFree(tcon);
> +       ses->tcon_ipc = NULL;
> +       return rc;
> +}
> +
>  static struct cifs_ses *
>  cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
>  {
> @@ -2466,6 +2554,8 @@ cifs_put_smb_ses(struct cifs_ses *ses)
>                 ses->status = CifsExiting;
>         spin_unlock(&cifs_tcp_ses_lock);
>
> +       cifs_free_ipc(ses);
> +
>         if (ses->status == CifsExiting && server->ops->logoff) {
>                 xid = get_xid();
>                 rc = server->ops->logoff(xid, ses);
> @@ -2710,6 +2800,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
>         spin_unlock(&cifs_tcp_ses_lock);
>
>         free_xid(xid);
> +
> +       cifs_setup_ipc(ses, volume_info);
> +
>         return ses;
>
>  get_ses_fail:
> @@ -2754,8 +2847,16 @@ void
>  cifs_put_tcon(struct cifs_tcon *tcon)
>  {
>         unsigned int xid;
> -       struct cifs_ses *ses = tcon->ses;
> +       struct cifs_ses *ses;
>
> +       /*
> +        * IPC tcon share the lifetime of their session and are
> +        * destroyed in the session put function
> +        */
> +       if (tcon == NULL || tcon->ipc)
> +               return;
> +
> +       ses = tcon->ses;
>         cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
>         spin_lock(&cifs_tcp_ses_lock);
>         if (--tcon->tc_count > 0) {
> @@ -3031,39 +3132,17 @@ get_dfs_path(const unsigned int xid, struct cifs_ses *ses, const char *old_path,
>              const struct nls_table *nls_codepage, unsigned int *num_referrals,
>              struct dfs_info3_param **referrals, int remap)
>  {
> -       char *temp_unc;
>         int rc = 0;
>
> -       if (!ses->server->ops->tree_connect || !ses->server->ops->get_dfs_refer)
> +       if (!ses->server->ops->get_dfs_refer)
>                 return -ENOSYS;
>
>         *num_referrals = 0;
>         *referrals = NULL;
>
> -       if (ses->ipc_tid == 0) {
> -               temp_unc = kmalloc(2 /* for slashes */ +
> -                       strnlen(ses->serverName, SERVER_NAME_LEN_WITH_NULL * 2)
> -                               + 1 + 4 /* slash IPC$ */ + 2, GFP_KERNEL);
> -               if (temp_unc == NULL)
> -                       return -ENOMEM;
> -               temp_unc[0] = '\\';
> -               temp_unc[1] = '\\';
> -               strcpy(temp_unc + 2, ses->serverName);
> -               strcpy(temp_unc + 2 + strlen(ses->serverName), "\\IPC$");
> -               rc = ses->server->ops->tree_connect(xid, ses, temp_unc, NULL,
> -                                                   nls_codepage);
> -               cifs_dbg(FYI, "Tcon rc = %d ipc_tid = %d\n", rc, ses->ipc_tid);
> -               kfree(temp_unc);
> -       }
> -       if (rc == 0)
> -               rc = ses->server->ops->get_dfs_refer(xid, ses, old_path,
> -                                                    referrals, num_referrals,
> -                                                    nls_codepage, remap);
> -       /*
> -        * BB - map targetUNCs to dfs_info3 structures, here or in
> -        * ses->server->ops->get_dfs_refer.
> -        */
> -
> +       rc = ses->server->ops->get_dfs_refer(xid, ses, old_path,
> +                                            referrals, num_referrals,
> +                                            nls_codepage, remap);
>         return rc;
>  }
>
> @@ -3828,7 +3907,7 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
>                 tcon->unix_ext = 0; /* server does not support them */
>
>         /* do not care if a following call succeed - informational */
> -       if (!tcon->ipc && server->ops->qfs_tcon)
> +       if (!tcon->pipe && server->ops->qfs_tcon)
>                 server->ops->qfs_tcon(xid, tcon);
>
>         cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info);
> @@ -3958,8 +4037,7 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
>  }
>
>  /*
> - * Issue a TREE_CONNECT request. Note that for IPC$ shares, that the tcon
> - * pointer may be NULL.
> + * Issue a TREE_CONNECT request.
>   */
>  int
>  CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
> @@ -3995,7 +4073,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
>         pSMB->AndXCommand = 0xFF;
>         pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
>         bcc_ptr = &pSMB->Password[0];
> -       if (!tcon || (ses->server->sec_mode & SECMODE_USER)) {
> +       if (tcon->pipe || (ses->server->sec_mode & SECMODE_USER)) {
>                 pSMB->PasswordLength = cpu_to_le16(1);  /* minimum */
>                 *bcc_ptr = 0; /* password is null byte */
>                 bcc_ptr++;              /* skip password */
> @@ -4067,7 +4145,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
>                          0);
>
>         /* above now done in SendReceive */
> -       if ((rc == 0) && (tcon != NULL)) {
> +       if (rc == 0) {
>                 bool is_unicode;
>
>                 tcon->tidStatus = CifsGood;
> @@ -4087,7 +4165,8 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
>                         if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
>                             (bcc_ptr[2] == 'C')) {
>                                 cifs_dbg(FYI, "IPC connection\n");
> -                               tcon->ipc = 1;
> +                               tcon->ipc = true;
> +                               tcon->pipe = true;
>                         }
>                 } else if (length == 2) {
>                         if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
> @@ -4114,9 +4193,6 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
>                 else
>                         tcon->Flags = 0;
>                 cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags);
> -       } else if ((rc == 0) && tcon == NULL) {
> -               /* all we need to save for IPC$ connection */
> -               ses->ipc_tid = smb_buffer_response->Tid;
>         }
>
>         cifs_buf_release(smb_buffer);
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index ecb99079363a..8f9a8cc7cc62 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -1049,7 +1049,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
>         tcon->resource_id = CIFS_I(inode)->uniqueid;
>  #endif
>
> -       if (rc && tcon->ipc) {
> +       if (rc && tcon->pipe) {
>                 cifs_dbg(FYI, "ipc connection - fake read inode\n");
>                 spin_lock(&inode->i_lock);
>                 inode->i_mode |= S_IFDIR;
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 7900aec7f92f..2943adc754e4 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -1258,8 +1258,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
>         }
>
>         /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
> -       if (tcon)
> -               tcon->tid = 0;
> +       tcon->tid = 0;
>
>         rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, (void **) &req,
>                              &total_len);
> @@ -1268,15 +1267,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
>                 return rc;
>         }
>
> -       if (tcon == NULL) {
> -               if ((ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA))
> -                       flags |= CIFS_TRANSFORM_REQ;
> -
> -               /* since no tcon, smb2_init can not do this, so do here */
> -               req->sync_hdr.SessionId = ses->Suid;
> -               if (ses->server->sign)
> -                       req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
> -       } else if (encryption_required(tcon))
> +       if (encryption_required(tcon))
>                 flags |= CIFS_TRANSFORM_REQ;
>
>         iov[0].iov_base = (char *)req;
> @@ -1302,21 +1293,16 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
>                 goto tcon_error_exit;
>         }
>
> -       if (tcon == NULL) {
> -               ses->ipc_tid = rsp->hdr.sync_hdr.TreeId;
> -               goto tcon_exit;
> -       }
> -
>         switch (rsp->ShareType) {
>         case SMB2_SHARE_TYPE_DISK:
>                 cifs_dbg(FYI, "connection to disk share\n");
>                 break;
>         case SMB2_SHARE_TYPE_PIPE:
> -               tcon->ipc = true;
> +               tcon->pipe = true;
>                 cifs_dbg(FYI, "connection to pipe share\n");
>                 break;
>         case SMB2_SHARE_TYPE_PRINT:
> -               tcon->ipc = true;
> +               tcon->print = true;
>                 cifs_dbg(FYI, "connection to printer\n");
>                 break;
>         default:
> @@ -1892,16 +1878,6 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
>         if (rc)
>                 return rc;
>
> -       if (use_ipc) {
> -               if (ses->ipc_tid == 0) {
> -                       cifs_small_buf_release(req);
> -                       return -ENOTCONN;
> -               }
> -
> -               cifs_dbg(FYI, "replacing tid 0x%x with IPC tid 0x%x\n",
> -                        req->sync_hdr.TreeId, ses->ipc_tid);
> -               req->sync_hdr.TreeId = ses->ipc_tid;
> -       }
>         if (encryption_required(tcon))
>                 flags |= CIFS_TRANSFORM_REQ;
>
> @@ -2317,6 +2293,10 @@ void smb2_reconnect_server(struct work_struct *work)
>                                 tcon_exist = true;
>                         }
>                 }
> +               if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
> +                       list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
> +                       tcon_exist = true;
> +               }
>         }
>         /*
>          * Get the reference to server struct to be sure that the last call of
> --
> 2.12.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reviewed-by: Pavel Shilovsky <pshilov@xxxxxxxxxxxxx>

--
Best regards,
Pavel Shilovsky

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux