Merge tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 20 Mar 2024 23:42:47 +0000 (16:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 20 Mar 2024 23:42:47 +0000 (16:42 -0700)
Pull smb server updates from Steve French:

 - add support for durable file handles (an important data integrity
   feature)

 - fixes for potential out of bounds issues

 - fix possible null dereference in close

 - getattr fixes

 - trivial typo fix and minor cleanup

* tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: remove module version
  ksmbd: fix potencial out-of-bounds when buffer offset is invalid
  ksmbd: fix slab-out-of-bounds in smb_strndup_from_utf16()
  ksmbd: Fix spelling mistake "connction" -> "connection"
  ksmbd: fix possible null-deref in smb_lazy_parent_lease_break_close
  ksmbd: add support for durable handles v1/v2
  ksmbd: mark SMB2_SESSION_EXPIRED to session when destroying previous session
  ksmbd: retrieve number of blocks using vfs_getattr in set_file_allocation_info
  ksmbd: replace generic_fillattr with vfs_getattr

1  2 
fs/smb/server/smb2pdu.c
fs/smb/server/vfs.c

diff --combined fs/smb/server/smb2pdu.c
index 089527a8b4ff42211c7bccdd4aa8adba9b984436,3f3408f086699fe9f150af0993e7069d0a743f3d..d478fa0c57abdbc7b8478624edf5133e202c85bf
@@@ -607,30 -607,6 +607,6 @@@ int smb2_check_user_session(struct ksmb
        return -ENOENT;
  }
  
- static void destroy_previous_session(struct ksmbd_conn *conn,
-                                    struct ksmbd_user *user, u64 id)
- {
-       struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id);
-       struct ksmbd_user *prev_user;
-       struct channel *chann;
-       long index;
-       if (!prev_sess)
-               return;
-       prev_user = prev_sess->user;
-       if (!prev_user ||
-           strcmp(user->name, prev_user->name) ||
-           user->passkey_sz != prev_user->passkey_sz ||
-           memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
-               return;
-       prev_sess->state = SMB2_SESSION_EXPIRED;
-       xa_for_each(&prev_sess->ksmbd_chann_list, index, chann)
-               ksmbd_conn_set_exiting(chann->conn);
- }
  /**
   * smb2_get_name() - get filename string from on the wire smb format
   * @src:      source buffer
@@@ -1951,7 -1927,7 +1927,7 @@@ int smb2_tree_connect(struct ksmbd_wor
  
        WORK_BUFFERS(work, req, rsp);
  
-       treename = smb_strndup_from_utf16(req->Buffer,
+       treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset),
                                          le16_to_cpu(req->PathLength), true,
                                          conn->local_nls);
        if (IS_ERR(treename)) {
@@@ -2642,6 -2618,165 +2618,165 @@@ static void ksmbd_acls_fattr(struct smb
        }
  }
  
+ enum {
+       DURABLE_RECONN_V2 = 1,
+       DURABLE_RECONN,
+       DURABLE_REQ_V2,
+       DURABLE_REQ,
+ };
+ struct durable_info {
+       struct ksmbd_file *fp;
+       unsigned short int type;
+       bool persistent;
+       bool reconnected;
+       unsigned int timeout;
+       char *CreateGuid;
+ };
+ static int parse_durable_handle_context(struct ksmbd_work *work,
+                                       struct smb2_create_req *req,
+                                       struct lease_ctx_info *lc,
+                                       struct durable_info *dh_info)
+ {
+       struct ksmbd_conn *conn = work->conn;
+       struct create_context *context;
+       int dh_idx, err = 0;
+       u64 persistent_id = 0;
+       int req_op_level;
+       static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ"};
+       req_op_level = req->RequestedOplockLevel;
+       for (dh_idx = DURABLE_RECONN_V2; dh_idx <= ARRAY_SIZE(durable_arr);
+            dh_idx++) {
+               context = smb2_find_context_vals(req, durable_arr[dh_idx - 1], 4);
+               if (IS_ERR(context)) {
+                       err = PTR_ERR(context);
+                       goto out;
+               }
+               if (!context)
+                       continue;
+               switch (dh_idx) {
+               case DURABLE_RECONN_V2:
+               {
+                       struct create_durable_reconn_v2_req *recon_v2;
+                       if (dh_info->type == DURABLE_RECONN ||
+                           dh_info->type == DURABLE_REQ_V2) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       recon_v2 = (struct create_durable_reconn_v2_req *)context;
+                       persistent_id = recon_v2->Fid.PersistentFileId;
+                       dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
+                       if (!dh_info->fp) {
+                               ksmbd_debug(SMB, "Failed to get durable handle state\n");
+                               err = -EBADF;
+                               goto out;
+                       }
+                       if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid,
+                                  SMB2_CREATE_GUID_SIZE)) {
+                               err = -EBADF;
+                               ksmbd_put_durable_fd(dh_info->fp);
+                               goto out;
+                       }
+                       dh_info->type = dh_idx;
+                       dh_info->reconnected = true;
+                       ksmbd_debug(SMB,
+                               "reconnect v2 Persistent-id from reconnect = %llu\n",
+                                       persistent_id);
+                       break;
+               }
+               case DURABLE_RECONN:
+               {
+                       struct create_durable_reconn_req *recon;
+                       if (dh_info->type == DURABLE_RECONN_V2 ||
+                           dh_info->type == DURABLE_REQ_V2) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       recon = (struct create_durable_reconn_req *)context;
+                       persistent_id = recon->Data.Fid.PersistentFileId;
+                       dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
+                       if (!dh_info->fp) {
+                               ksmbd_debug(SMB, "Failed to get durable handle state\n");
+                               err = -EBADF;
+                               goto out;
+                       }
+                       dh_info->type = dh_idx;
+                       dh_info->reconnected = true;
+                       ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n",
+                                   persistent_id);
+                       break;
+               }
+               case DURABLE_REQ_V2:
+               {
+                       struct create_durable_req_v2 *durable_v2_blob;
+                       if (dh_info->type == DURABLE_RECONN ||
+                           dh_info->type == DURABLE_RECONN_V2) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       durable_v2_blob =
+                               (struct create_durable_req_v2 *)context;
+                       ksmbd_debug(SMB, "Request for durable v2 open\n");
+                       dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid);
+                       if (dh_info->fp) {
+                               if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid,
+                                           SMB2_CLIENT_GUID_SIZE)) {
+                                       if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATION)) {
+                                               err = -ENOEXEC;
+                                               goto out;
+                                       }
+                                       dh_info->fp->conn = conn;
+                                       dh_info->reconnected = true;
+                                       goto out;
+                               }
+                       }
+                       if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
+                            req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
+                               dh_info->CreateGuid =
+                                       durable_v2_blob->CreateGuid;
+                               dh_info->persistent =
+                                       le32_to_cpu(durable_v2_blob->Flags);
+                               dh_info->timeout =
+                                       le32_to_cpu(durable_v2_blob->Timeout);
+                               dh_info->type = dh_idx;
+                       }
+                       break;
+               }
+               case DURABLE_REQ:
+                       if (dh_info->type == DURABLE_RECONN)
+                               goto out;
+                       if (dh_info->type == DURABLE_RECONN_V2 ||
+                           dh_info->type == DURABLE_REQ_V2) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
+                            req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
+                               ksmbd_debug(SMB, "Request for durable open\n");
+                               dh_info->type = dh_idx;
+                       }
+               }
+       }
+ out:
+       return err;
+ }
  /**
   * smb2_open() - handler for smb file open request
   * @work:     smb work containing request buffer
@@@ -2665,6 -2800,7 +2800,7 @@@ int smb2_open(struct ksmbd_work *work
        struct lease_ctx_info *lc = NULL;
        struct create_ea_buf_req *ea_buf = NULL;
        struct oplock_info *opinfo;
+       struct durable_info dh_info = {0};
        __le32 *next_ptr = NULL;
        int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
        int rc = 0;
                        goto err_out2;
                }
  
-               name = smb2_get_name(req->Buffer,
+               name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset),
                                     le16_to_cpu(req->NameLength),
                                     work->conn->local_nls);
                if (IS_ERR(name)) {
                }
        }
  
+       req_op_level = req->RequestedOplockLevel;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE &&
+           req->CreateContextsOffset) {
+               lc = parse_lease_state(req);
+               rc = parse_durable_handle_context(work, req, lc, &dh_info);
+               if (rc) {
+                       ksmbd_debug(SMB, "error parsing durable handle context\n");
+                       goto err_out2;
+               }
+               if (dh_info.reconnected == true) {
+                       rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name);
+                       if (rc) {
+                               ksmbd_put_durable_fd(dh_info.fp);
+                               goto err_out2;
+                       }
+                       rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
+                       if (rc) {
+                               ksmbd_put_durable_fd(dh_info.fp);
+                               goto err_out2;
+                       }
+                       if (ksmbd_override_fsids(work)) {
+                               rc = -ENOMEM;
+                               ksmbd_put_durable_fd(dh_info.fp);
+                               goto err_out2;
+                       }
+                       fp = dh_info.fp;
+                       file_info = FILE_OPENED;
+                       rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
+                       if (rc)
+                               goto err_out2;
+                       ksmbd_put_durable_fd(fp);
+                       goto reconnected_fp;
+               }
+       } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
+               lc = parse_lease_state(req);
        if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) {
                pr_err("Invalid impersonationlevel : 0x%x\n",
                       le32_to_cpu(req->ImpersonationLevel));
                need_truncate = 1;
        }
  
-       req_op_level = req->RequestedOplockLevel;
-       if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
-               lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode));
        share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
        if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
            (req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
                }
        } else {
                if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
+                       if (S_ISDIR(file_inode(filp)->i_mode)) {
+                               lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE;
+                               lc->is_dir = true;
+                       }
                        /*
                         * Compare parent lease using parent key. If there is no
                         * a lease that has same parent key, Send lease break
  
        memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
  
+       if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) {
+               if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent)
+                       fp->is_persistent = true;
+               else
+                       fp->is_durable = true;
+               if (dh_info.type == DURABLE_REQ_V2) {
+                       memcpy(fp->create_guid, dh_info.CreateGuid,
+                                       SMB2_CREATE_GUID_SIZE);
+                       if (dh_info.timeout)
+                               fp->durable_timeout = min(dh_info.timeout,
+                                               300000);
+                       else
+                               fp->durable_timeout = 60;
+               }
+       }
+ reconnected_fp:
        rsp->StructureSize = cpu_to_le16(89);
        rcu_read_lock();
        opinfo = rcu_dereference(fp->f_opinfo);
                next_off = conn->vals->create_disk_id_size;
        }
  
+       if (dh_info.type == DURABLE_REQ || dh_info.type == DURABLE_REQ_V2) {
+               struct create_context *durable_ccontext;
+               durable_ccontext = (struct create_context *)(rsp->Buffer +
+                               le32_to_cpu(rsp->CreateContextsLength));
+               contxt_cnt++;
+               if (dh_info.type == DURABLE_REQ) {
+                       create_durable_rsp_buf(rsp->Buffer +
+                                       le32_to_cpu(rsp->CreateContextsLength));
+                       le32_add_cpu(&rsp->CreateContextsLength,
+                                       conn->vals->create_durable_size);
+                       iov_len += conn->vals->create_durable_size;
+               } else {
+                       create_durable_v2_rsp_buf(rsp->Buffer +
+                                       le32_to_cpu(rsp->CreateContextsLength),
+                                       fp);
+                       le32_add_cpu(&rsp->CreateContextsLength,
+                                       conn->vals->create_durable_v2_size);
+                       iov_len += conn->vals->create_durable_v2_size;
+               }
+               if (next_ptr)
+                       *next_ptr = cpu_to_le32(next_off);
+               next_ptr = &durable_ccontext->Next;
+               next_off = conn->vals->create_durable_size;
+       }
        if (posix_ctxt) {
                contxt_cnt++;
                create_posix_rsp_buf(rsp->Buffer +
@@@ -3828,11 -4053,16 +4053,16 @@@ static int process_query_dir_entries(st
                }
  
                ksmbd_kstat.kstat = &kstat;
-               if (priv->info_level != FILE_NAMES_INFORMATION)
-                       ksmbd_vfs_fill_dentry_attrs(priv->work,
-                                                   idmap,
-                                                   dent,
-                                                   &ksmbd_kstat);
+               if (priv->info_level != FILE_NAMES_INFORMATION) {
+                       rc = ksmbd_vfs_fill_dentry_attrs(priv->work,
+                                                        idmap,
+                                                        dent,
+                                                        &ksmbd_kstat);
+                       if (rc) {
+                               dput(dent);
+                               continue;
+                       }
+               }
  
                rc = smb2_populate_readdir_entry(priv->work->conn,
                                                 priv->info_level,
@@@ -4075,7 -4305,7 +4305,7 @@@ int smb2_query_dir(struct ksmbd_work *w
        }
  
        srch_flag = req->Flags;
-       srch_ptr = smb_strndup_from_utf16(req->Buffer,
+       srch_ptr = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->FileNameOffset),
                                          le16_to_cpu(req->FileNameLength), 1,
                                          conn->local_nls);
        if (IS_ERR(srch_ptr)) {
@@@ -4335,7 -4565,8 +4565,8 @@@ static int smb2_get_ea(struct ksmbd_wor
                    sizeof(struct smb2_ea_info_req))
                        return -EINVAL;
  
-               ea_req = (struct smb2_ea_info_req *)req->Buffer;
+               ea_req = (struct smb2_ea_info_req *)((char *)req +
+                                                    le16_to_cpu(req->InputBufferOffset));
        } else {
                /* need to send all EAs, if no specific EA is requested*/
                if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY)
@@@ -4480,6 -4711,7 +4711,7 @@@ static int get_file_basic_info(struct s
        struct smb2_file_basic_info *basic_info;
        struct kstat stat;
        u64 time;
+       int ret;
  
        if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
                pr_err("no right to read the attributes : 0x%x\n",
                return -EACCES;
        }
  
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
        basic_info = (struct smb2_file_basic_info *)rsp->Buffer;
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
-                        file_inode(fp->filp), &stat);
        basic_info->CreationTime = cpu_to_le64(fp->create_time);
        time = ksmbd_UnixTimeToNT(stat.atime);
        basic_info->LastAccessTime = cpu_to_le64(time);
        return 0;
  }
  
- static void get_file_standard_info(struct smb2_query_info_rsp *rsp,
-                                  struct ksmbd_file *fp, void *rsp_org)
+ static int get_file_standard_info(struct smb2_query_info_rsp *rsp,
+                                 struct ksmbd_file *fp, void *rsp_org)
  {
        struct smb2_file_standard_info *sinfo;
        unsigned int delete_pending;
-       struct inode *inode;
        struct kstat stat;
+       int ret;
  
-       inode = file_inode(fp->filp);
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat);
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
        sinfo = (struct smb2_file_standard_info *)rsp->Buffer;
        delete_pending = ksmbd_inode_pending_delete(fp);
  
-       sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9);
+       sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9);
        sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
        sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending);
        sinfo->DeletePending = delete_pending;
        sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0;
        rsp->OutputBufferLength =
                cpu_to_le32(sizeof(struct smb2_file_standard_info));
+       return 0;
  }
  
  static void get_file_alignment_info(struct smb2_query_info_rsp *rsp,
@@@ -4546,11 -4785,11 +4785,11 @@@ static int get_file_all_info(struct ksm
        struct ksmbd_conn *conn = work->conn;
        struct smb2_file_all_info *file_info;
        unsigned int delete_pending;
-       struct inode *inode;
        struct kstat stat;
        int conv_len;
        char *filename;
        u64 time;
+       int ret;
  
        if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
                ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
        if (IS_ERR(filename))
                return PTR_ERR(filename);
  
-       inode = file_inode(fp->filp);
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat);
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
        ksmbd_debug(SMB, "filename = %s\n", filename);
        delete_pending = ksmbd_inode_pending_delete(fp);
        file_info->Attributes = fp->f_ci->m_fattr;
        file_info->Pad1 = 0;
        file_info->AllocationSize =
-               cpu_to_le64(inode->i_blocks << 9);
+               cpu_to_le64(stat.blocks << 9);
        file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
        file_info->NumberOfLinks =
                        cpu_to_le32(get_nlink(&stat) - delete_pending);
@@@ -4623,10 -4864,10 +4864,10 @@@ static void get_file_alternate_info(str
                cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len);
  }
  
- static void get_file_stream_info(struct ksmbd_work *work,
-                                struct smb2_query_info_rsp *rsp,
-                                struct ksmbd_file *fp,
-                                void *rsp_org)
+ static int get_file_stream_info(struct ksmbd_work *work,
+                               struct smb2_query_info_rsp *rsp,
+                               struct ksmbd_file *fp,
+                               void *rsp_org)
  {
        struct ksmbd_conn *conn = work->conn;
        struct smb2_file_stream_info *file_info;
        int nbytes = 0, streamlen, stream_name_len, next, idx = 0;
        int buf_free_len;
        struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
+       int ret;
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
-                        file_inode(fp->filp), &stat);
        file_info = (struct smb2_file_stream_info *)rsp->Buffer;
  
        buf_free_len =
        kvfree(xattr_list);
  
        rsp->OutputBufferLength = cpu_to_le32(nbytes);
+       return 0;
  }
  
- static void get_file_internal_info(struct smb2_query_info_rsp *rsp,
-                                  struct ksmbd_file *fp, void *rsp_org)
+ static int get_file_internal_info(struct smb2_query_info_rsp *rsp,
+                                 struct ksmbd_file *fp, void *rsp_org)
  {
        struct smb2_file_internal_info *file_info;
        struct kstat stat;
+       int ret;
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
-                        file_inode(fp->filp), &stat);
        file_info = (struct smb2_file_internal_info *)rsp->Buffer;
        file_info->IndexNumber = cpu_to_le64(stat.ino);
        rsp->OutputBufferLength =
                cpu_to_le32(sizeof(struct smb2_file_internal_info));
+       return 0;
  }
  
  static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
                                      struct ksmbd_file *fp, void *rsp_org)
  {
        struct smb2_file_ntwrk_info *file_info;
-       struct inode *inode;
        struct kstat stat;
        u64 time;
+       int ret;
  
        if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
                pr_err("no right to read the attributes : 0x%x\n",
                return -EACCES;
        }
  
-       file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer;
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
-       inode = file_inode(fp->filp);
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat);
+       file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer;
  
        file_info->CreationTime = cpu_to_le64(fp->create_time);
        time = ksmbd_UnixTimeToNT(stat.atime);
        time = ksmbd_UnixTimeToNT(stat.ctime);
        file_info->ChangeTime = cpu_to_le64(time);
        file_info->Attributes = fp->f_ci->m_fattr;
-       file_info->AllocationSize =
-               cpu_to_le64(inode->i_blocks << 9);
+       file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
        file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
        file_info->Reserved = cpu_to_le32(0);
        rsp->OutputBufferLength =
@@@ -4804,14 -5058,17 +5058,17 @@@ static void get_file_mode_info(struct s
                cpu_to_le32(sizeof(struct smb2_file_mode_info));
  }
  
- static void get_file_compression_info(struct smb2_query_info_rsp *rsp,
-                                     struct ksmbd_file *fp, void *rsp_org)
+ static int get_file_compression_info(struct smb2_query_info_rsp *rsp,
+                                    struct ksmbd_file *fp, void *rsp_org)
  {
        struct smb2_file_comp_info *file_info;
        struct kstat stat;
+       int ret;
  
-       generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
-                        file_inode(fp->filp), &stat);
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
        file_info = (struct smb2_file_comp_info *)rsp->Buffer;
        file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9);
  
        rsp->OutputBufferLength =
                cpu_to_le32(sizeof(struct smb2_file_comp_info));
+       return 0;
  }
  
  static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp,
        return 0;
  }
  
- static void find_file_posix_info(struct smb2_query_info_rsp *rsp,
+ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
                                struct ksmbd_file *fp, void *rsp_org)
  {
        struct smb311_posix_qinfo *file_info;
        struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
        vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);
        vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
+       struct kstat stat;
        u64 time;
        int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32;
+       int ret;
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
+               return ret;
  
        file_info = (struct smb311_posix_qinfo *)rsp->Buffer;
        file_info->CreationTime = cpu_to_le64(fp->create_time);
-       time = ksmbd_UnixTimeToNT(inode_get_atime(inode));
+       time = ksmbd_UnixTimeToNT(stat.atime);
        file_info->LastAccessTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(inode_get_mtime(inode));
+       time = ksmbd_UnixTimeToNT(stat.mtime);
        file_info->LastWriteTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(inode_get_ctime(inode));
+       time = ksmbd_UnixTimeToNT(stat.ctime);
        file_info->ChangeTime = cpu_to_le64(time);
        file_info->DosAttributes = fp->f_ci->m_fattr;
-       file_info->Inode = cpu_to_le64(inode->i_ino);
-       file_info->EndOfFile = cpu_to_le64(inode->i_size);
-       file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9);
-       file_info->HardLinks = cpu_to_le32(inode->i_nlink);
-       file_info->Mode = cpu_to_le32(inode->i_mode & 0777);
-       file_info->DeviceId = cpu_to_le32(inode->i_rdev);
+       file_info->Inode = cpu_to_le64(stat.ino);
+       file_info->EndOfFile = cpu_to_le64(stat.size);
+       file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
+       file_info->HardLinks = cpu_to_le32(stat.nlink);
+       file_info->Mode = cpu_to_le32(stat.mode & 0777);
+       file_info->DeviceId = cpu_to_le32(stat.rdev);
  
        /*
         * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)).
                  SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]);
  
        rsp->OutputBufferLength = cpu_to_le32(out_buf_len);
+       return 0;
  }
  
  static int smb2_get_info_file(struct ksmbd_work *work,
                break;
  
        case FILE_STANDARD_INFORMATION:
-               get_file_standard_info(rsp, fp, work->response_buf);
+               rc = get_file_standard_info(rsp, fp, work->response_buf);
                break;
  
        case FILE_ALIGNMENT_INFORMATION:
                break;
  
        case FILE_STREAM_INFORMATION:
-               get_file_stream_info(work, rsp, fp, work->response_buf);
+               rc = get_file_stream_info(work, rsp, fp, work->response_buf);
                break;
  
        case FILE_INTERNAL_INFORMATION:
-               get_file_internal_info(rsp, fp, work->response_buf);
+               rc = get_file_internal_info(rsp, fp, work->response_buf);
                break;
  
        case FILE_NETWORK_OPEN_INFORMATION:
                break;
  
        case FILE_COMPRESSION_INFORMATION:
-               get_file_compression_info(rsp, fp, work->response_buf);
+               rc = get_file_compression_info(rsp, fp, work->response_buf);
                break;
  
        case FILE_ATTRIBUTE_TAG_INFORMATION:
                        pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n");
                        rc = -EOPNOTSUPP;
                } else {
-                       find_file_posix_info(rsp, fp, work->response_buf);
+                       rc = find_file_posix_info(rsp, fp, work->response_buf);
                }
                break;
        default:
@@@ -5398,7 -5666,6 +5666,6 @@@ int smb2_close(struct ksmbd_work *work
        struct smb2_close_rsp *rsp;
        struct ksmbd_conn *conn = work->conn;
        struct ksmbd_file *fp;
-       struct inode *inode;
        u64 time;
        int err = 0;
  
        rsp->Reserved = 0;
  
        if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) {
+               struct kstat stat;
+               int ret;
                fp = ksmbd_lookup_fd_fast(work, volatile_id);
                if (!fp) {
                        err = -ENOENT;
                        goto out;
                }
  
-               inode = file_inode(fp->filp);
+               ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                                 AT_STATX_SYNC_AS_STAT);
+               if (ret) {
+                       ksmbd_fd_put(work, fp);
+                       goto out;
+               }
                rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB;
-               rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 :
-                       cpu_to_le64(inode->i_blocks << 9);
-               rsp->EndOfFile = cpu_to_le64(inode->i_size);
+               rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 :
+                       cpu_to_le64(stat.blocks << 9);
+               rsp->EndOfFile = cpu_to_le64(stat.size);
                rsp->Attributes = fp->f_ci->m_fattr;
                rsp->CreationTime = cpu_to_le64(fp->create_time);
-               time = ksmbd_UnixTimeToNT(inode_get_atime(inode));
+               time = ksmbd_UnixTimeToNT(stat.atime);
                rsp->LastAccessTime = cpu_to_le64(time);
-               time = ksmbd_UnixTimeToNT(inode_get_mtime(inode));
+               time = ksmbd_UnixTimeToNT(stat.mtime);
                rsp->LastWriteTime = cpu_to_le64(time);
-               time = ksmbd_UnixTimeToNT(inode_get_ctime(inode));
+               time = ksmbd_UnixTimeToNT(stat.ctime);
                rsp->ChangeTime = cpu_to_le64(time);
                ksmbd_fd_put(work, fp);
        } else {
@@@ -5759,15 -6035,21 +6035,21 @@@ static int set_file_allocation_info(str
  
        loff_t alloc_blks;
        struct inode *inode;
+       struct kstat stat;
        int rc;
  
        if (!(fp->daccess & FILE_WRITE_DATA_LE))
                return -EACCES;
  
+       rc = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                        AT_STATX_SYNC_AS_STAT);
+       if (rc)
+               return rc;
        alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
        inode = file_inode(fp->filp);
  
-       if (alloc_blks > inode->i_blocks) {
+       if (alloc_blks > stat.blocks) {
                smb_break_all_levII_oplock(work, fp, 1);
                rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0,
                                   alloc_blks * 512);
                        pr_err("vfs_fallocate is failed : %d\n", rc);
                        return rc;
                }
-       } else if (alloc_blks < inode->i_blocks) {
+       } else if (alloc_blks < stat.blocks) {
                loff_t size;
  
                /*
@@@ -5930,6 -6212,7 +6212,7 @@@ static int smb2_set_info_file(struct ks
                              struct ksmbd_share_config *share)
  {
        unsigned int buf_len = le32_to_cpu(req->BufferLength);
+       char *buffer = (char *)req + le16_to_cpu(req->BufferOffset);
  
        switch (req->FileInfoClass) {
        case FILE_BASIC_INFORMATION:
                if (buf_len < sizeof(struct smb2_file_basic_info))
                        return -EINVAL;
  
-               return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
+               return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share);
        }
        case FILE_ALLOCATION_INFORMATION:
        {
                        return -EINVAL;
  
                return set_file_allocation_info(work, fp,
-                                               (struct smb2_file_alloc_info *)req->Buffer);
+                                               (struct smb2_file_alloc_info *)buffer);
        }
        case FILE_END_OF_FILE_INFORMATION:
        {
                        return -EINVAL;
  
                return set_end_of_file_info(work, fp,
-                                           (struct smb2_file_eof_info *)req->Buffer);
+                                           (struct smb2_file_eof_info *)buffer);
        }
        case FILE_RENAME_INFORMATION:
        {
                        return -EINVAL;
  
                return set_rename_info(work, fp,
-                                      (struct smb2_file_rename_info *)req->Buffer,
+                                      (struct smb2_file_rename_info *)buffer,
                                       buf_len);
        }
        case FILE_LINK_INFORMATION:
                        return -EINVAL;
  
                return smb2_create_link(work, work->tcon->share_conf,
-                                       (struct smb2_file_link_info *)req->Buffer,
+                                       (struct smb2_file_link_info *)buffer,
                                        buf_len, fp->filp,
                                        work->conn->local_nls);
        }
                        return -EINVAL;
  
                return set_file_disposition_info(fp,
-                                                (struct smb2_file_disposition_info *)req->Buffer);
+                                                (struct smb2_file_disposition_info *)buffer);
        }
        case FILE_FULL_EA_INFORMATION:
        {
                if (buf_len < sizeof(struct smb2_ea_info))
                        return -EINVAL;
  
-               return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
+               return smb2_set_ea((struct smb2_ea_info *)buffer,
                                   buf_len, &fp->filp->f_path, true);
        }
        case FILE_POSITION_INFORMATION:
                if (buf_len < sizeof(struct smb2_file_pos_info))
                        return -EINVAL;
  
-               return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
+               return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer);
        }
        case FILE_MODE_INFORMATION:
        {
                if (buf_len < sizeof(struct smb2_file_mode_info))
                        return -EINVAL;
  
-               return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
+               return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer);
        }
        }
  
@@@ -6089,7 -6372,7 +6372,7 @@@ int smb2_set_info(struct ksmbd_work *wo
                }
                rc = smb2_set_info_sec(fp,
                                       le32_to_cpu(req->AdditionalInformation),
-                                      req->Buffer,
+                                      (char *)req + le16_to_cpu(req->BufferOffset),
                                       le32_to_cpu(req->BufferLength));
                ksmbd_revert_fsids(work);
                break;
@@@ -6764,10 -7047,10 +7047,10 @@@ struct file_lock *smb_flock_init(struc
  
        locks_init_lock(fl);
  
 -      fl->fl_owner = f;
 -      fl->fl_pid = current->tgid;
 -      fl->fl_file = f;
 -      fl->fl_flags = FL_POSIX;
 +      fl->c.flc_owner = f;
 +      fl->c.flc_pid = current->tgid;
 +      fl->c.flc_file = f;
 +      fl->c.flc_flags = FL_POSIX;
        fl->fl_ops = NULL;
        fl->fl_lmops = NULL;
  
@@@ -6784,30 -7067,30 +7067,30 @@@ static int smb2_set_flock_flags(struct 
        case SMB2_LOCKFLAG_SHARED:
                ksmbd_debug(SMB, "received shared request\n");
                cmd = F_SETLKW;
 -              flock->fl_type = F_RDLCK;
 -              flock->fl_flags |= FL_SLEEP;
 +              flock->c.flc_type = F_RDLCK;
 +              flock->c.flc_flags |= FL_SLEEP;
                break;
        case SMB2_LOCKFLAG_EXCLUSIVE:
                ksmbd_debug(SMB, "received exclusive request\n");
                cmd = F_SETLKW;
 -              flock->fl_type = F_WRLCK;
 -              flock->fl_flags |= FL_SLEEP;
 +              flock->c.flc_type = F_WRLCK;
 +              flock->c.flc_flags |= FL_SLEEP;
                break;
        case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
                ksmbd_debug(SMB,
                            "received shared & fail immediately request\n");
                cmd = F_SETLK;
 -              flock->fl_type = F_RDLCK;
 +              flock->c.flc_type = F_RDLCK;
                break;
        case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
                ksmbd_debug(SMB,
                            "received exclusive & fail immediately request\n");
                cmd = F_SETLK;
 -              flock->fl_type = F_WRLCK;
 +              flock->c.flc_type = F_WRLCK;
                break;
        case SMB2_LOCKFLAG_UNLOCK:
                ksmbd_debug(SMB, "received unlock request\n");
 -              flock->fl_type = F_UNLCK;
 +              flock->c.flc_type = F_UNLCK;
                cmd = F_SETLK;
                break;
        }
@@@ -6845,13 -7128,13 +7128,13 @@@ static void smb2_remove_blocked_lock(vo
        struct file_lock *flock = (struct file_lock *)argv[0];
  
        ksmbd_vfs_posix_lock_unblock(flock);
 -      wake_up(&flock->fl_wait);
 +      locks_wake_up(flock);
  }
  
  static inline bool lock_defer_pending(struct file_lock *fl)
  {
        /* check pending lock waiters */
 -      return waitqueue_active(&fl->fl_wait);
 +      return waitqueue_active(&fl->c.flc_wait);
  }
  
  /**
@@@ -6942,8 -7225,8 +7225,8 @@@ int smb2_lock(struct ksmbd_work *work
                list_for_each_entry(cmp_lock, &lock_list, llist) {
                        if (cmp_lock->fl->fl_start <= flock->fl_start &&
                            cmp_lock->fl->fl_end >= flock->fl_end) {
 -                              if (cmp_lock->fl->fl_type != F_UNLCK &&
 -                                  flock->fl_type != F_UNLCK) {
 +                              if (cmp_lock->fl->c.flc_type != F_UNLCK &&
 +                                  flock->c.flc_type != F_UNLCK) {
                                        pr_err("conflict two locks in one request\n");
                                        err = -EINVAL;
                                        locks_free_lock(flock);
                list_for_each_entry(conn, &conn_list, conns_list) {
                        spin_lock(&conn->llist_lock);
                        list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) {
 -                              if (file_inode(cmp_lock->fl->fl_file) !=
 -                                  file_inode(smb_lock->fl->fl_file))
 +                              if (file_inode(cmp_lock->fl->c.flc_file) !=
 +                                  file_inode(smb_lock->fl->c.flc_file))
                                        continue;
  
 -                              if (smb_lock->fl->fl_type == F_UNLCK) {
 -                                      if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file &&
 +                              if (lock_is_unlock(smb_lock->fl)) {
 +                                      if (cmp_lock->fl->c.flc_file == smb_lock->fl->c.flc_file &&
                                            cmp_lock->start == smb_lock->start &&
                                            cmp_lock->end == smb_lock->end &&
                                            !lock_defer_pending(cmp_lock->fl)) {
                                        continue;
                                }
  
 -                              if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) {
 +                              if (cmp_lock->fl->c.flc_file == smb_lock->fl->c.flc_file) {
                                        if (smb_lock->flags & SMB2_LOCKFLAG_SHARED)
                                                continue;
                                } else {
                }
                up_read(&conn_list_lock);
  out_check_cl:
 -              if (smb_lock->fl->fl_type == F_UNLCK && nolock) {
 +              if (lock_is_unlock(smb_lock->fl) && nolock) {
                        pr_err("Try to unlock nolocked range\n");
                        rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED;
                        goto out;
@@@ -7179,7 -7462,7 +7462,7 @@@ out
                struct file_lock *rlock = NULL;
  
                rlock = smb_flock_init(filp);
 -              rlock->fl_type = F_UNLCK;
 +              rlock->c.flc_type = F_UNLCK;
                rlock->fl_start = smb_lock->start;
                rlock->fl_end = smb_lock->end;
  
@@@ -7535,7 -7818,7 +7818,7 @@@ static int fsctl_pipe_transceive(struc
                                 struct smb2_ioctl_rsp *rsp)
  {
        struct ksmbd_rpc_command *rpc_resp;
-       char *data_buf = (char *)&req->Buffer[0];
+       char *data_buf = (char *)req + le32_to_cpu(req->InputOffset);
        int nbytes = 0;
  
        rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf,
@@@ -7648,6 -7931,7 +7931,7 @@@ int smb2_ioctl(struct ksmbd_work *work
        u64 id = KSMBD_NO_FID;
        struct ksmbd_conn *conn = work->conn;
        int ret = 0;
+       char *buffer;
  
        if (work->next_smb2_rcv_hdr_off) {
                req = ksmbd_req_buf_next(work);
                goto out;
        }
  
+       buffer = (char *)req + le32_to_cpu(req->InputOffset);
        cnt_code = le32_to_cpu(req->CtlCode);
        ret = smb2_calc_max_out_buf_len(work, 48,
                                        le32_to_cpu(req->MaxOutputResponse));
                }
  
                ret = fsctl_validate_negotiate_info(conn,
-                       (struct validate_negotiate_info_req *)&req->Buffer[0],
+                       (struct validate_negotiate_info_req *)buffer,
                        (struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
                        in_buf_len);
                if (ret < 0)
                rsp->VolatileFileId = req->VolatileFileId;
                rsp->PersistentFileId = req->PersistentFileId;
                fsctl_copychunk(work,
-                               (struct copychunk_ioctl_req *)&req->Buffer[0],
+                               (struct copychunk_ioctl_req *)buffer,
                                le32_to_cpu(req->CtlCode),
                                le32_to_cpu(req->InputCount),
                                req->VolatileFileId,
                        goto out;
                }
  
-               ret = fsctl_set_sparse(work, id,
-                                      (struct file_sparse *)&req->Buffer[0]);
+               ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer);
                if (ret < 0)
                        goto out;
                break;
                }
  
                zero_data =
-                       (struct file_zero_data_information *)&req->Buffer[0];
+                       (struct file_zero_data_information *)buffer;
  
                off = le64_to_cpu(zero_data->FileOffset);
                bfz = le64_to_cpu(zero_data->BeyondFinalZero);
                }
  
                ret = fsctl_query_allocated_ranges(work, id,
-                       (struct file_allocated_range_buffer *)&req->Buffer[0],
+                       (struct file_allocated_range_buffer *)buffer,
                        (struct file_allocated_range_buffer *)&rsp->Buffer[0],
                        out_buf_len /
                        sizeof(struct file_allocated_range_buffer), &nbytes);
                        goto out;
                }
  
-               dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
+               dup_ext = (struct duplicate_extents_to_file *)buffer;
  
                fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
                                             dup_ext->PersistentFileHandle);
diff --combined fs/smb/server/vfs.c
index c487e834331aa61962b22d94c677289ec6e72067,2e992fadeaa7df3ee439c381ff09c5fecae3e326..22f0f3db3ac92df2447e6b62646447d5ca1895a0
@@@ -337,18 -337,18 +337,18 @@@ static int check_lock_range(struct fil
                return 0;
  
        spin_lock(&ctx->flc_lock);
 -      list_for_each_entry(flock, &ctx->flc_posix, fl_list) {
 +      for_each_file_lock(flock, &ctx->flc_posix) {
                /* check conflict locks */
                if (flock->fl_end >= start && end >= flock->fl_start) {
 -                      if (flock->fl_type == F_RDLCK) {
 +                      if (lock_is_read(flock)) {
                                if (type == WRITE) {
                                        pr_err("not allow write by shared lock\n");
                                        error = 1;
                                        goto out;
                                }
 -                      } else if (flock->fl_type == F_WRLCK) {
 +                      } else if (lock_is_write(flock)) {
                                /* check owner in lock */
 -                              if (flock->fl_file != filp) {
 +                              if (flock->c.flc_file != filp) {
                                        error = 1;
                                        pr_err("not allow rw access by exclusive lock from other opens\n");
                                        goto out;
@@@ -1682,11 -1682,19 +1682,19 @@@ int ksmbd_vfs_fill_dentry_attrs(struct 
                                struct dentry *dentry,
                                struct ksmbd_kstat *ksmbd_kstat)
  {
+       struct ksmbd_share_config *share_conf = work->tcon->share_conf;
        u64 time;
        int rc;
+       struct path path = {
+               .mnt = share_conf->vfs_path.mnt,
+               .dentry = dentry,
+       };
  
-       generic_fillattr(idmap, STATX_BASIC_STATS, d_inode(dentry),
-                        ksmbd_kstat->kstat);
+       rc = vfs_getattr(&path, ksmbd_kstat->kstat,
+                        STATX_BASIC_STATS | STATX_BTIME,
+                        AT_STATX_SYNC_AS_STAT);
+       if (rc)
+               return rc;
  
        time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime);
        ksmbd_kstat->create_time = time;
@@@ -1837,13 -1845,13 +1845,13 @@@ int ksmbd_vfs_copy_file_ranges(struct k
  
  void ksmbd_vfs_posix_lock_wait(struct file_lock *flock)
  {
 -      wait_event(flock->fl_wait, !flock->fl_blocker);
 +      wait_event(flock->c.flc_wait, !flock->c.flc_blocker);
  }
  
  int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout)
  {
 -      return wait_event_interruptible_timeout(flock->fl_wait,
 -                                              !flock->fl_blocker,
 +      return wait_event_interruptible_timeout(flock->c.flc_wait,
 +                                              !flock->c.flc_blocker,
                                                timeout);
  }