Loading fs/cifs/smb2ops.c +155 −15 Original line number Diff line number Diff line Loading @@ -294,34 +294,176 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } #ifdef CONFIG_CIFS_STATS2 static int parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, size_t buf_len, struct cifs_server_iface **iface_list, size_t *iface_count) { struct network_interface_info_ioctl_rsp *p; struct sockaddr_in *addr4; struct sockaddr_in6 *addr6; struct iface_info_ipv4 *p4; struct iface_info_ipv6 *p6; struct cifs_server_iface *info; ssize_t bytes_left; size_t next = 0; int nb_iface = 0; int rc = 0; *iface_list = NULL; *iface_count = 0; /* * Fist pass: count and sanity check */ bytes_left = buf_len; p = buf; while (bytes_left >= sizeof(*p)) { nb_iface++; next = le32_to_cpu(p->Next); if (!next) { bytes_left -= sizeof(*p); break; } p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); bytes_left -= next; } if (!nb_iface) { cifs_dbg(VFS, "%s: malformed interface info\n", __func__); rc = -EINVAL; goto out; } if (bytes_left || p->Next) cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); /* * Second pass: extract info to internal structure */ *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL); if (!*iface_list) { rc = -ENOMEM; goto out; } info = *iface_list; bytes_left = buf_len; p = buf; while (bytes_left >= sizeof(*p)) { info->speed = le64_to_cpu(p->LinkSpeed); info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE); info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE); cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count); cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, le32_to_cpu(p->Capability)); switch (p->Family) { /* * The kernel and wire socket structures have the same * layout and use network byte order but make the * conversion explicit in case either one changes. */ case INTERNETWORK: addr4 = (struct sockaddr_in *)&info->sockaddr; p4 = (struct iface_info_ipv4 *)p->Buffer; addr4->sin_family = AF_INET; memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ addr4->sin_port = cpu_to_be16(CIFS_PORT); cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, &addr4->sin_addr); break; case INTERNETWORKV6: addr6 = (struct sockaddr_in6 *)&info->sockaddr; p6 = (struct iface_info_ipv6 *)p->Buffer; addr6->sin6_family = AF_INET6; memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ addr6->sin6_flowinfo = 0; addr6->sin6_scope_id = 0; addr6->sin6_port = cpu_to_be16(CIFS_PORT); cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, &addr6->sin6_addr); break; default: cifs_dbg(VFS, "%s: skipping unsupported socket family\n", __func__); goto next_iface; } (*iface_count)++; info++; next_iface: next = le32_to_cpu(p->Next); if (!next) break; p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); bytes_left -= next; } if (!*iface_count) { rc = -EINVAL; goto out; } out: if (rc) { kfree(*iface_list); *iface_count = 0; *iface_list = NULL; } return rc; } static int SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) { int rc; unsigned int ret_data_len = 0; struct network_interface_info_ioctl_rsp *out_buf; struct network_interface_info_ioctl_rsp *out_buf = NULL; struct cifs_server_iface *iface_list; size_t iface_count; struct cifs_ses *ses = tcon->ses; rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, NULL /* no data input */, 0 /* no data input */, (char **)&out_buf, &ret_data_len); if (rc != 0) if (rc != 0) { cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) { cifs_dbg(VFS, "server returned bad net interface info buf\n"); rc = -EINVAL; } else { /* Dump info on first interface */ cifs_dbg(FYI, "Adapter Capability 0x%x\t", le32_to_cpu(out_buf->Capability)); cifs_dbg(FYI, "Link Speed %lld\n", le64_to_cpu(out_buf->LinkSpeed)); goto out; } rc = parse_server_interfaces(out_buf, ret_data_len, &iface_list, &iface_count); if (rc) goto out; spin_lock(&ses->iface_lock); kfree(ses->iface_list); ses->iface_list = iface_list; ses->iface_count = iface_count; ses->iface_last_update = jiffies; spin_unlock(&ses->iface_lock); out: kfree(out_buf); return rc; } #endif /* STATS2 */ void smb2_cached_lease_break(struct work_struct *work) Loading Loading @@ -399,9 +541,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) if (rc) return; #ifdef CONFIG_CIFS_STATS2 SMB3_request_interfaces(xid, tcon); #endif /* STATS2 */ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, FS_ATTRIBUTE_INFORMATION); Loading Loading
fs/cifs/smb2ops.c +155 −15 Original line number Diff line number Diff line Loading @@ -294,34 +294,176 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } #ifdef CONFIG_CIFS_STATS2 static int parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, size_t buf_len, struct cifs_server_iface **iface_list, size_t *iface_count) { struct network_interface_info_ioctl_rsp *p; struct sockaddr_in *addr4; struct sockaddr_in6 *addr6; struct iface_info_ipv4 *p4; struct iface_info_ipv6 *p6; struct cifs_server_iface *info; ssize_t bytes_left; size_t next = 0; int nb_iface = 0; int rc = 0; *iface_list = NULL; *iface_count = 0; /* * Fist pass: count and sanity check */ bytes_left = buf_len; p = buf; while (bytes_left >= sizeof(*p)) { nb_iface++; next = le32_to_cpu(p->Next); if (!next) { bytes_left -= sizeof(*p); break; } p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); bytes_left -= next; } if (!nb_iface) { cifs_dbg(VFS, "%s: malformed interface info\n", __func__); rc = -EINVAL; goto out; } if (bytes_left || p->Next) cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); /* * Second pass: extract info to internal structure */ *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL); if (!*iface_list) { rc = -ENOMEM; goto out; } info = *iface_list; bytes_left = buf_len; p = buf; while (bytes_left >= sizeof(*p)) { info->speed = le64_to_cpu(p->LinkSpeed); info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE); info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE); cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count); cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, le32_to_cpu(p->Capability)); switch (p->Family) { /* * The kernel and wire socket structures have the same * layout and use network byte order but make the * conversion explicit in case either one changes. */ case INTERNETWORK: addr4 = (struct sockaddr_in *)&info->sockaddr; p4 = (struct iface_info_ipv4 *)p->Buffer; addr4->sin_family = AF_INET; memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ addr4->sin_port = cpu_to_be16(CIFS_PORT); cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, &addr4->sin_addr); break; case INTERNETWORKV6: addr6 = (struct sockaddr_in6 *)&info->sockaddr; p6 = (struct iface_info_ipv6 *)p->Buffer; addr6->sin6_family = AF_INET6; memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ addr6->sin6_flowinfo = 0; addr6->sin6_scope_id = 0; addr6->sin6_port = cpu_to_be16(CIFS_PORT); cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, &addr6->sin6_addr); break; default: cifs_dbg(VFS, "%s: skipping unsupported socket family\n", __func__); goto next_iface; } (*iface_count)++; info++; next_iface: next = le32_to_cpu(p->Next); if (!next) break; p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); bytes_left -= next; } if (!*iface_count) { rc = -EINVAL; goto out; } out: if (rc) { kfree(*iface_list); *iface_count = 0; *iface_list = NULL; } return rc; } static int SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) { int rc; unsigned int ret_data_len = 0; struct network_interface_info_ioctl_rsp *out_buf; struct network_interface_info_ioctl_rsp *out_buf = NULL; struct cifs_server_iface *iface_list; size_t iface_count; struct cifs_ses *ses = tcon->ses; rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, NULL /* no data input */, 0 /* no data input */, (char **)&out_buf, &ret_data_len); if (rc != 0) if (rc != 0) { cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) { cifs_dbg(VFS, "server returned bad net interface info buf\n"); rc = -EINVAL; } else { /* Dump info on first interface */ cifs_dbg(FYI, "Adapter Capability 0x%x\t", le32_to_cpu(out_buf->Capability)); cifs_dbg(FYI, "Link Speed %lld\n", le64_to_cpu(out_buf->LinkSpeed)); goto out; } rc = parse_server_interfaces(out_buf, ret_data_len, &iface_list, &iface_count); if (rc) goto out; spin_lock(&ses->iface_lock); kfree(ses->iface_list); ses->iface_list = iface_list; ses->iface_count = iface_count; ses->iface_last_update = jiffies; spin_unlock(&ses->iface_lock); out: kfree(out_buf); return rc; } #endif /* STATS2 */ void smb2_cached_lease_break(struct work_struct *work) Loading Loading @@ -399,9 +541,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) if (rc) return; #ifdef CONFIG_CIFS_STATS2 SMB3_request_interfaces(xid, tcon); #endif /* STATS2 */ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, FS_ATTRIBUTE_INFORMATION); Loading