/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. * Copyright 2022-2023 RackTop Systems, Inc. */ /* * Dispatch function for SMB2_QUERY_INFO * * [MS-FSCC 2.4] If a file system does not support ... * an Information Classs, NT_STATUS_INVALID_PARAMETER... */ #include #include #include static uint32_t smb2_qif_basic(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_standard(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_internal(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_ea_size(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_access(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_name(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_normalized_name(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_position(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_full_ea(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_mode(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_alignment(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_all(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_altname(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_stream(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_pipe(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_pipe_lcl(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_pipe_rem(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_compr(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_opens(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_tags(smb_request_t *, smb_queryinfo_t *); static uint32_t smb2_qif_id_info(smb_request_t *, smb_queryinfo_t *); /* * MS-SMB2 3.3.5.20.1 says (in a windows behavior note) that * 2012R2 and older fill in the FileNameInformation. * Default to the new behavior. */ boolean_t smb2_qif_all_get_name = B_FALSE; uint32_t smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi) { smb_ofile_t *of = sr->fid_ofile; uint_t mask = 0; boolean_t getstd = B_FALSE; boolean_t getname = B_FALSE; uint32_t status; /* * Which attributes do we need from the FS? */ switch (qi->qi_InfoClass) { case FileBasicInformation: mask = SMB_AT_BASIC; break; case FileStandardInformation: mask = SMB_AT_STANDARD; getstd = B_TRUE; break; case FileInternalInformation: mask = SMB_AT_NODEID; break; case FileAllInformation: mask = SMB_AT_ALL; getstd = B_TRUE; if (smb2_qif_all_get_name) getname = B_TRUE; break; case FileNameInformation: case FileNormalizedNameInformation: getname = B_TRUE; break; case FileAlternateNameInformation: mask = SMB_AT_NODEID; getname = B_TRUE; break; case FileStreamInformation: mask = SMB_AT_STANDARD; getstd = B_TRUE; break; case FileCompressionInformation: mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ; break; case FileNetworkOpenInformation: mask = SMB_AT_BASIC | SMB_AT_STANDARD; break; case FileIdInformation: mask = SMB_AT_NODEID; break; default: break; } qi->qi_attr.sa_mask = mask; qi->qi_node = of->f_node; if (mask & SMB_AT_ALL) { status = smb2_ofile_getattr(sr, of, &qi->qi_attr); if (status) return (status); } if (getstd) { status = smb2_ofile_getstd(of, qi); if (status) return (status); } if (getname) { status = smb2_ofile_getname(of, qi); if (status) return (status); } switch (qi->qi_InfoClass) { case FileBasicInformation: status = smb2_qif_basic(sr, qi); break; case FileStandardInformation: status = smb2_qif_standard(sr, qi); break; case FileInternalInformation: status = smb2_qif_internal(sr, qi); break; case FileEaInformation: status = smb2_qif_ea_size(sr, qi); break; case FileAccessInformation: status = smb2_qif_access(sr, qi); break; case FileNameInformation: status = smb2_qif_name(sr, qi); break; case FileNormalizedNameInformation: status = smb2_qif_normalized_name(sr, qi); break; case FilePositionInformation: status = smb2_qif_position(sr, qi); break; case FileFullEaInformation: status = smb2_qif_full_ea(sr, qi); break; case FileModeInformation: status = smb2_qif_mode(sr, qi); break; case FileAlignmentInformation: status = smb2_qif_alignment(sr, qi); break; case FileAllInformation: status = smb2_qif_all(sr, qi); break; case FileAlternateNameInformation: status = smb2_qif_altname(sr, qi); break; case FileStreamInformation: status = smb2_qif_stream(sr, qi); break; case FilePipeInformation: status = smb2_qif_pipe(sr, qi); break; case FilePipeLocalInformation: status = smb2_qif_pipe_lcl(sr, qi); break; case FilePipeRemoteInformation: status = smb2_qif_pipe_rem(sr, qi); break; case FileCompressionInformation: status = smb2_qif_compr(sr, qi); break; case FileNetworkOpenInformation: status = smb2_qif_opens(sr, qi); break; case FileAttributeTagInformation: status = smb2_qif_tags(sr, qi); break; case FileIdInformation: status = smb2_qif_id_info(sr, qi); break; default: status = NT_STATUS_INVALID_INFO_CLASS; break; } return (status); } /* * FileAllInformation * * This returns a concatenation of: * FileBasicInformation * FileStandardInformation * FileInternalInformation * FileEaInformation * FileAccessInformation * FilePositionInformation * FileModeInformation * FileAlignmentInformation * FileNameInformation * * Note: FileNameInformation is all zero on Win2016 and later. */ static uint32_t smb2_qif_all(smb_request_t *sr, smb_queryinfo_t *qi) { uint32_t status; status = smb2_qif_basic(sr, qi); if (status) return (status); status = smb2_qif_standard(sr, qi); if (status) return (status); status = smb2_qif_internal(sr, qi); if (status) return (status); status = smb2_qif_ea_size(sr, qi); if (status) return (status); status = smb2_qif_access(sr, qi); if (status) return (status); status = smb2_qif_position(sr, qi); if (status) return (status); status = smb2_qif_mode(sr, qi); if (status) return (status); status = smb2_qif_alignment(sr, qi); if (status) return (status); /* See smb2_qif_all_get_name */ if (qi->qi_namelen != 0) { /* Win2012r2 and earlier fill it in. */ status = smb2_qif_name(sr, qi); } else { /* Win2016 and later just put zeros. */ int rc = smb_mbc_encodef(&sr->raw_data, "6."); status = (rc == 0) ? 0 : NT_STATUS_BUFFER_OVERFLOW; } return (status); } /* * FileBasicInformation * See also: * case SMB_QUERY_FILE_BASIC_INFO: * case SMB_FILE_BASIC_INFORMATION: */ static uint32_t smb2_qif_basic(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; int rc; ASSERT((sa->sa_mask & SMB_AT_BASIC) == SMB_AT_BASIC); rc = smb_mbc_encodef( &sr->raw_data, "TTTTll", &sa->sa_crtime, /* T */ &sa->sa_vattr.va_atime, /* T */ &sa->sa_vattr.va_mtime, /* T */ &sa->sa_vattr.va_ctime, /* T */ sa->sa_dosattr, /* l */ 0); /* reserved */ /* l */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileStandardInformation * See also: * SMB_QUERY_FILE_STANDARD_INFO * SMB_FILE_STANDARD_INFORMATION */ static uint32_t smb2_qif_standard(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; int rc; ASSERT((sa->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD); rc = smb_mbc_encodef( &sr->raw_data, "qqlbbw", sa->sa_allocsz, /* q */ sa->sa_vattr.va_size, /* q */ sa->sa_vattr.va_nlink, /* l */ qi->qi_delete_on_close, /* b */ qi->qi_isdir, /* b */ 0); /* reserved */ /* w */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileInternalInformation * See also: * SMB_FILE_INTERNAL_INFORMATION */ static uint32_t smb2_qif_internal(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; u_longlong_t nodeid; int rc; ASSERT((sa->sa_mask & SMB_AT_NODEID) == SMB_AT_NODEID); nodeid = sa->sa_vattr.va_nodeid; if (smb2_aapl_use_file_ids == 0 && (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0) nodeid = 0; rc = smb_mbc_encodef( &sr->raw_data, "q", nodeid); /* q */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileEaInformation * See also: * SMB_QUERY_FILE_EA_INFO * SMB_FILE_EA_INFORMATION */ static uint32_t smb2_qif_ea_size(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) int rc; rc = smb_mbc_encodef( &sr->raw_data, "l", 0); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileFullEaInformation * We could put EAs in a named stream... */ /* ARGSUSED */ static uint32_t smb2_qif_full_ea(smb_request_t *sr, smb_queryinfo_t *qi) { return (NT_STATUS_NO_EAS_ON_FILE); } /* * FileAccessInformation */ static uint32_t smb2_qif_access(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; int rc; rc = smb_mbc_encodef( &sr->raw_data, "l", of->f_granted_access); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileNameInformation * See also: * SMB_QUERY_FILE_NAME_INFO * SMB_FILE_NAME_INFORMATION * MS-FSCC 2.1.7 FILE_NAME_INFORMATION */ static uint32_t smb2_qif_name(smb_request_t *sr, smb_queryinfo_t *qi) { char *name; uint32_t nlen; int rc; /* SMB2 leaves off the leading / */ nlen = qi->qi_namelen; name = qi->qi_name; if (qi->qi_name[0] == '\\') { name++; nlen -= 2; } rc = smb_mbc_encodef( &sr->raw_data, "lU", nlen, /* l */ name); /* U */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileNormalizedNameInformation */ static uint32_t smb2_qif_normalized_name(smb_request_t *sr, smb_queryinfo_t *qi) { char *name; uint32_t nlen; int rc; /* SMB2 leaves off the leading / */ nlen = qi->qi_namelen; name = qi->qi_name; if (qi->qi_name[0] == '\\') { name++; nlen -= 2; } rc = smb_mbc_encodef( &sr->raw_data, "lU", nlen, /* l */ name); /* U */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FilePositionInformation */ static uint32_t smb2_qif_position(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; uint64_t pos; int rc; mutex_enter(&of->f_mutex); pos = of->f_seek_pos; mutex_exit(&of->f_mutex); rc = smb_mbc_encodef( &sr->raw_data, "q", pos); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileModeInformation [MS-FSA 2.4.24] */ static uint32_t smb2_qif_mode(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; uint32_t mode; int rc; /* * See MS-FSA description of Open.Mode * For now, we have these in... */ mode = of->f_create_options & (FILE_WRITE_THROUGH | FILE_SEQUENTIAL_ONLY | FILE_NO_INTERMEDIATE_BUFFERING | FILE_DELETE_ON_CLOSE); /* * The ofile level DoC flag is currently in of->f_flags * (SMB_OFLAGS_SET_DELETE_ON_CLOSE) though probably it * should be in f_create_options (and perhaps rename * that field to f_mode or something closer to the * Open.Mode terminology used in MS-FSA). */ if (of->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE) mode |= FILE_DELETE_ON_CLOSE; rc = smb_mbc_encodef( &sr->raw_data, "l", mode); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileAlignmentInformation */ static uint32_t smb2_qif_alignment(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) int rc; rc = smb_mbc_encodef( &sr->raw_data, "l", 0); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileAlternateNameInformation * See also: * SMB_QUERY_FILE_ALT_NAME_INFO * SMB_FILE_ALT_NAME_INFORMATION */ static uint32_t smb2_qif_altname(smb_request_t *sr, smb_queryinfo_t *qi) { smb_ofile_t *of = sr->fid_ofile; int rc; ASSERT(qi->qi_namelen > 0); ASSERT(qi->qi_attr.sa_mask & SMB_AT_NODEID); if (of->f_ftype != SMB_FTYPE_DISK) return (NT_STATUS_OBJECT_NAME_NOT_FOUND); if ((of->f_tree->t_flags & SMB_TREE_SHORTNAMES) == 0) return (NT_STATUS_OBJECT_NAME_NOT_FOUND); /* fill in qi->qi_shortname */ smb_query_shortname(of->f_node, qi); rc = smb_mbc_encodef( &sr->raw_data, "%lU", sr, smb_wcequiv_strlen(qi->qi_shortname), qi->qi_shortname); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileStreamInformation */ static uint32_t smb2_qif_stream(smb_request_t *sr, smb_queryinfo_t *qi) { smb_ofile_t *of = sr->fid_ofile; smb_attr_t *attr = &qi->qi_attr; uint32_t status; ASSERT((attr->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD); if (of->f_ftype != SMB_FTYPE_DISK) { (void) smb_mbc_encodef( &sr->raw_data, "l", 0); return (0); } status = smb_query_stream_info(sr, &sr->raw_data, qi); return (status); } /* * FilePipeInformation */ static uint32_t smb2_qif_pipe(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; uint32_t pipe_mode; uint32_t nonblock; int rc; switch (of->f_ftype) { case SMB_FTYPE_BYTE_PIPE: pipe_mode = 0; /* FILE_PIPE_BYTE_STREAM_MODE */ break; case SMB_FTYPE_MESG_PIPE: pipe_mode = 1; /* FILE_PIPE_MESSAGE_MODE */ break; case SMB_FTYPE_DISK: case SMB_FTYPE_PRINTER: default: return (NT_STATUS_INVALID_PARAMETER); } nonblock = 0; /* XXX todo: Get this from the pipe handle. */ rc = smb_mbc_encodef( &sr->raw_data, "ll", pipe_mode, nonblock); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FilePipeLocalInformation */ /* ARGSUSED */ static uint32_t smb2_qif_pipe_lcl(smb_request_t *sr, smb_queryinfo_t *qi) { return (NT_STATUS_INVALID_PARAMETER); /* XXX todo */ } /* * FilePipeRemoteInformation */ /* ARGSUSED */ static uint32_t smb2_qif_pipe_rem(smb_request_t *sr, smb_queryinfo_t *qi) { return (NT_STATUS_INVALID_PARAMETER); /* XXX todo */ } /* * FileCompressionInformation * XXX: For now, just say "not compressed". */ static uint32_t smb2_qif_compr(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; uint16_t CompressionFormat = 0; /* COMPRESSION_FORMAT_NONE */ int rc; ASSERT(sa->sa_mask & SMB_AT_SIZE); rc = smb_mbc_encodef( &sr->raw_data, "qw6.", sa->sa_vattr.va_size, /* q */ CompressionFormat); /* w */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileNetworkOpenInformation */ static uint32_t smb2_qif_opens(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; int rc; rc = smb_mbc_encodef( &sr->raw_data, "TTTTqqll", &sa->sa_crtime, /* T */ &sa->sa_vattr.va_atime, /* T */ &sa->sa_vattr.va_mtime, /* T */ &sa->sa_vattr.va_ctime, /* T */ sa->sa_allocsz, /* q */ sa->sa_vattr.va_size, /* q */ sa->sa_dosattr, /* l */ 0); /* reserved */ /* l */ if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileAttributeTagInformation * * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the * second dword should be the reparse tag. Otherwise * the tag value should be set to zero. * We don't support reparse points, so we set the tag * to zero. */ static uint32_t smb2_qif_tags(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) int rc; rc = smb_mbc_encodef( &sr->raw_data, "ll", 0, 0); if (rc != 0) return (NT_STATUS_BUFFER_OVERFLOW); return (0); } /* * FileIdInformation * * Returns a A FILE_ID_INFORMATION * VolumeSerialNumber (8 bytes) * FileId (16 bytes) * * Take the volume serial from the share root, * and compose the FileId from the nodeid and fsid * of the file (in case we crossed mounts) */ static uint32_t smb2_qif_id_info(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; smb_ofile_t *of = sr->fid_ofile; smb_tree_t *tree = sr->tid_tree; vfs_t *f_vfs; // file vfs_t *s_vfs; // share uint64_t nodeid; int rc; ASSERT((sa->sa_mask & SMB_AT_NODEID) != 0); if (of->f_ftype != SMB_FTYPE_DISK) return (NT_STATUS_INVALID_INFO_CLASS); s_vfs = SMB_NODE_VFS(tree->t_snode); f_vfs = SMB_NODE_VFS(of->f_node); nodeid = (uint64_t)sa->sa_vattr.va_nodeid; rc = smb_mbc_encodef( &sr->raw_data, "llqll", s_vfs->vfs_fsid.val[0], /* l */ s_vfs->vfs_fsid.val[1], /* l */ nodeid, /* q */ f_vfs->vfs_fsid.val[0], /* l */ f_vfs->vfs_fsid.val[1]); /* l */ if (rc != 0) return (NT_STATUS_INFO_LENGTH_MISMATCH); return (0); }