smb: client: fix OOB in receive_encrypted_standard()
[platform/kernel/linux-starfive.git] / fs / smb / client / smb2ops.c
index 9aeecee..258a591 100644 (file)
@@ -756,6 +756,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
        unsigned int ret_data_len = 0;
        struct network_interface_info_ioctl_rsp *out_buf = NULL;
        struct cifs_ses *ses = tcon->ses;
+       struct TCP_Server_Info *pserver;
 
        /* do not query too frequently */
        if (ses->iface_last_update &&
@@ -780,6 +781,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
        if (rc)
                goto out;
 
+       /* check if iface is still active */
+       pserver = ses->chans[0].server;
+       if (pserver && !cifs_chan_is_iface_active(ses, pserver))
+               cifs_chan_update_iface(ses, pserver);
+
 out:
        kfree(out_buf);
        return rc;
@@ -2828,6 +2834,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
                usleep_range(512, 2048);
        } while (++retry_count < 5);
 
+       if (!rc && !dfs_rsp)
+               rc = -EIO;
        if (rc) {
                if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
                        cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
@@ -2858,115 +2866,119 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
        return rc;
 }
 
-static int
-parse_reparse_posix(struct reparse_posix_data *symlink_buf,
-                     u32 plen, char **target_path,
-                     struct cifs_sb_info *cifs_sb)
+/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
+static int parse_reparse_posix(struct reparse_posix_data *buf,
+                              struct cifs_sb_info *cifs_sb,
+                              struct cifs_open_info_data *data)
 {
        unsigned int len;
-
-       /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
-       len = le16_to_cpu(symlink_buf->ReparseDataLength);
-
-       if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) {
-               cifs_dbg(VFS, "%lld not a supported symlink type\n",
-                       le64_to_cpu(symlink_buf->InodeType));
+       u64 type;
+
+       switch ((type = le64_to_cpu(buf->InodeType))) {
+       case NFS_SPECFILE_LNK:
+               len = le16_to_cpu(buf->ReparseDataLength);
+               data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
+                                                              len, true,
+                                                              cifs_sb->local_nls);
+               if (!data->symlink_target)
+                       return -ENOMEM;
+               convert_delimiter(data->symlink_target, '/');
+               cifs_dbg(FYI, "%s: target path: %s\n",
+                        __func__, data->symlink_target);
+               break;
+       case NFS_SPECFILE_CHR:
+       case NFS_SPECFILE_BLK:
+       case NFS_SPECFILE_FIFO:
+       case NFS_SPECFILE_SOCK:
+               break;
+       default:
+               cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
+                        __func__, type);
                return -EOPNOTSUPP;
        }
-
-       *target_path = cifs_strndup_from_utf16(
-                               symlink_buf->PathBuffer,
-                               len, true, cifs_sb->local_nls);
-       if (!(*target_path))
-               return -ENOMEM;
-
-       convert_delimiter(*target_path, '/');
-       cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
-
        return 0;
 }
 
-static int
-parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf,
-                     u32 plen, char **target_path,
-                     struct cifs_sb_info *cifs_sb)
+static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
+                                u32 plen, bool unicode,
+                                struct cifs_sb_info *cifs_sb,
+                                struct cifs_open_info_data *data)
 {
-       unsigned int sub_len;
-       unsigned int sub_offset;
+       unsigned int len;
+       unsigned int offs;
 
        /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
 
-       sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset);
-       sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength);
-       if (sub_offset + 20 > plen ||
-           sub_offset + sub_len + 20 > plen) {
+       offs = le16_to_cpu(sym->SubstituteNameOffset);
+       len = le16_to_cpu(sym->SubstituteNameLength);
+       if (offs + 20 > plen || offs + len + 20 > plen) {
                cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
                return -EIO;
        }
 
-       *target_path = cifs_strndup_from_utf16(
-                               symlink_buf->PathBuffer + sub_offset,
-                               sub_len, true, cifs_sb->local_nls);
-       if (!(*target_path))
+       data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
+                                                      len, unicode,
+                                                      cifs_sb->local_nls);
+       if (!data->symlink_target)
                return -ENOMEM;
 
-       convert_delimiter(*target_path, '/');
-       cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
+       convert_delimiter(data->symlink_target, '/');
+       cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
 
        return 0;
 }
 
-static int
-parse_reparse_point(struct reparse_data_buffer *buf,
-                   u32 plen, char **target_path,
-                   struct cifs_sb_info *cifs_sb)
+int parse_reparse_point(struct reparse_data_buffer *buf,
+                       u32 plen, struct cifs_sb_info *cifs_sb,
+                       bool unicode, struct cifs_open_info_data *data)
 {
-       if (plen < sizeof(struct reparse_data_buffer)) {
-               cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n",
-                        plen);
+       if (plen < sizeof(*buf)) {
+               cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n",
+                        __func__, plen);
                return -EIO;
        }
 
-       if (plen < le16_to_cpu(buf->ReparseDataLength) +
-           sizeof(struct reparse_data_buffer)) {
-               cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n",
-                        plen);
+       if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) {
+               cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n",
+                        __func__, plen);
                return -EIO;
        }
 
+       data->reparse.buf = buf;
+
        /* See MS-FSCC 2.1.2 */
        switch (le32_to_cpu(buf->ReparseTag)) {
        case IO_REPARSE_TAG_NFS:
-               return parse_reparse_posix(
-                       (struct reparse_posix_data *)buf,
-                       plen, target_path, cifs_sb);
+               return parse_reparse_posix((struct reparse_posix_data *)buf,
+                                          cifs_sb, data);
        case IO_REPARSE_TAG_SYMLINK:
                return parse_reparse_symlink(
                        (struct reparse_symlink_data_buffer *)buf,
-                       plen, target_path, cifs_sb);
+                       plen, unicode, cifs_sb, data);
+       case IO_REPARSE_TAG_LX_SYMLINK:
+       case IO_REPARSE_TAG_AF_UNIX:
+       case IO_REPARSE_TAG_LX_FIFO:
+       case IO_REPARSE_TAG_LX_CHR:
+       case IO_REPARSE_TAG_LX_BLK:
+               return 0;
        default:
-               cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n",
-                        le32_to_cpu(buf->ReparseTag));
+               cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
+                        __func__, le32_to_cpu(buf->ReparseTag));
                return -EOPNOTSUPP;
        }
 }
 
-static int smb2_query_symlink(const unsigned int xid,
-                             struct cifs_tcon *tcon,
-                             struct cifs_sb_info *cifs_sb,
-                             const char *full_path,
-                             char **target_path,
-                             struct kvec *rsp_iov)
+static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
+                                   struct kvec *rsp_iov,
+                                   struct cifs_open_info_data *data)
 {
        struct reparse_data_buffer *buf;
        struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
        u32 plen = le32_to_cpu(io->OutputCount);
 
-       cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
-
        buf = (struct reparse_data_buffer *)((u8 *)io +
                                             le32_to_cpu(io->OutputOffset));
-       return parse_reparse_point(buf, plen, target_path, cifs_sb);
+       return parse_reparse_point(buf, plen, cifs_sb, true, data);
 }
 
 static int smb2_query_reparse_point(const unsigned int xid,
@@ -3299,6 +3311,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
        struct inode *inode = file_inode(file);
        struct cifsInodeInfo *cifsi = CIFS_I(inode);
        struct cifsFileInfo *cfile = file->private_data;
+       unsigned long long new_size;
        long rc;
        unsigned int xid;
        __le64 eof;
@@ -3329,10 +3342,15 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
        /*
         * do we also need to change the size of the file?
         */
-       if (keep_size == false && i_size_read(inode) < offset + len) {
-               eof = cpu_to_le64(offset + len);
+       new_size = offset + len;
+       if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
+               eof = cpu_to_le64(new_size);
                rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
                                  cfile->fid.volatile_fid, cfile->pid, &eof);
+               if (rc >= 0) {
+                       truncate_setsize(inode, new_size);
+                       fscache_resize_cookie(cifs_inode_cookie(inode), new_size);
+               }
        }
 
  zero_range_exit:
@@ -3727,6 +3745,9 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
        if (rc < 0)
                goto out_2;
 
+       truncate_setsize(inode, old_eof + len);
+       fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
+
        rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
        if (rc < 0)
                goto out_2;
@@ -4920,6 +4941,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
        struct smb2_hdr *shdr;
        unsigned int pdu_length = server->pdu_size;
        unsigned int buf_size;
+       unsigned int next_cmd;
        struct mid_q_entry *mid_entry;
        int next_is_large;
        char *next_buffer = NULL;
@@ -4948,14 +4970,15 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
        next_is_large = server->large_buf;
 one_more:
        shdr = (struct smb2_hdr *)buf;
-       if (shdr->NextCommand) {
+       next_cmd = le32_to_cpu(shdr->NextCommand);
+       if (next_cmd) {
+               if (WARN_ON_ONCE(next_cmd > pdu_length))
+                       return -1;
                if (next_is_large)
                        next_buffer = (char *)cifs_buf_get();
                else
                        next_buffer = (char *)cifs_small_buf_get();
-               memcpy(next_buffer,
-                      buf + le32_to_cpu(shdr->NextCommand),
-                      pdu_length - le32_to_cpu(shdr->NextCommand));
+               memcpy(next_buffer, buf + next_cmd, pdu_length - next_cmd);
        }
 
        mid_entry = smb2_find_mid(server, buf);
@@ -4979,8 +5002,8 @@ one_more:
        else
                ret = cifs_handle_standard(server, mid_entry);
 
-       if (ret == 0 && shdr->NextCommand) {
-               pdu_length -= le32_to_cpu(shdr->NextCommand);
+       if (ret == 0 && next_cmd) {
+               pdu_length -= next_cmd;
                server->large_buf = next_is_large;
                if (next_is_large)
                        server->bigbuf = buf = next_buffer;
@@ -5087,7 +5110,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
         * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
         */
 
-       if (!S_ISCHR(mode) && !S_ISBLK(mode))
+       if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode))
                return rc;
 
        cifs_dbg(FYI, "sfu compat create special file\n");
@@ -5135,6 +5158,12 @@ smb2_make_node(unsigned int xid, struct inode *inode,
                pdev->minor = cpu_to_le64(MINOR(dev));
                rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
                                                        &bytes_written, iov, 1);
+       } else if (S_ISFIFO(mode)) {
+               memcpy(pdev->type, "LnxFIFO", 8);
+               pdev->major = 0;
+               pdev->minor = 0;
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
        }
        tcon->ses->server->ops->close(xid, tcon, &fid);
        d_drop(dentry);
@@ -5195,7 +5224,7 @@ struct smb_version_operations smb20_operations = {
        .unlink = smb2_unlink,
        .rename = smb2_rename_path,
        .create_hardlink = smb2_create_hardlink,
-       .query_symlink = smb2_query_symlink,
+       .parse_reparse_point = smb2_parse_reparse_point,
        .query_mf_symlink = smb3_query_mf_symlink,
        .create_mf_symlink = smb3_create_mf_symlink,
        .open = smb2_open_file,
@@ -5297,7 +5326,7 @@ struct smb_version_operations smb21_operations = {
        .unlink = smb2_unlink,
        .rename = smb2_rename_path,
        .create_hardlink = smb2_create_hardlink,
-       .query_symlink = smb2_query_symlink,
+       .parse_reparse_point = smb2_parse_reparse_point,
        .query_mf_symlink = smb3_query_mf_symlink,
        .create_mf_symlink = smb3_create_mf_symlink,
        .open = smb2_open_file,
@@ -5402,7 +5431,7 @@ struct smb_version_operations smb30_operations = {
        .unlink = smb2_unlink,
        .rename = smb2_rename_path,
        .create_hardlink = smb2_create_hardlink,
-       .query_symlink = smb2_query_symlink,
+       .parse_reparse_point = smb2_parse_reparse_point,
        .query_mf_symlink = smb3_query_mf_symlink,
        .create_mf_symlink = smb3_create_mf_symlink,
        .open = smb2_open_file,
@@ -5516,7 +5545,7 @@ struct smb_version_operations smb311_operations = {
        .unlink = smb2_unlink,
        .rename = smb2_rename_path,
        .create_hardlink = smb2_create_hardlink,
-       .query_symlink = smb2_query_symlink,
+       .parse_reparse_point = smb2_parse_reparse_point,
        .query_mf_symlink = smb3_query_mf_symlink,
        .create_mf_symlink = smb3_create_mf_symlink,
        .open = smb2_open_file,