/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * This module provides functions for TRANS2_FIND_FIRST2 and * TRANS2_FIND_NEXT2 requests. The requests allow the client to search * for the file(s) which match the file specification. The search is * started with TRANS2_FIND_FIRST2 and can be continued if necessary with * TRANS2_FIND_NEXT2. There are numerous levels of information which may be * obtained for the returned files, the desired level is specified in the * InformationLevel field of the requests. * * InformationLevel Name Value * ================================= ================ * * SMB_INFO_STANDARD 1 * SMB_INFO_QUERY_EA_SIZE 2 * SMB_INFO_QUERY_EAS_FROM_LIST 3 * SMB_FIND_FILE_DIRECTORY_INFO 0x101 * SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 * SMB_FIND_FILE_NAMES_INFO 0x103 * SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 * SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO 0x105 * SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO 0x106 * * The following sections detail the data returned for each * InformationLevel. The requested information is placed in the Data * portion of the transaction response. Note: a client which does not * support long names can only request SMB_INFO_STANDARD. * * A four-byte resume key precedes each data item (described below) if bit * 2 in the Flags field is set, i.e. if the request indicates the server * should return resume keys. Note: it is not always the case. If the * data item already includes the resume key, the resume key should not be * added again. * * 4.3.4.1 SMB_INFO_STANDARD * * Response Field Description * ================================ ================================== * * SMB_DATE CreationDate; Date when file was created * SMB_TIME CreationTime; Time when file was created * SMB_DATE LastAccessDate; Date of last file access * SMB_TIME LastAccessTime; Time of last file access * SMB_DATE LastWriteDate; Date of last write to the file * SMB_TIME LastWriteTime; Time of last write to the file * ULONG DataSize; File Size * ULONG AllocationSize; Size of filesystem allocation unit * USHORT Attributes; File Attributes * UCHAR FileNameLength; Length of filename in bytes * STRING FileName; Name of found file * * 4.3.4.2 SMB_INFO_QUERY_EA_SIZE * * Response Field Description * ================================= ================================== * * SMB_DATE CreationDate; Date when file was created * SMB_TIME CreationTime; Time when file was created * SMB_DATE LastAccessDate; Date of last file access * SMB_TIME LastAccessTime; Time of last file access * SMB_DATE LastWriteDate; Date of last write to the file * SMB_TIME LastWriteTime; Time of last write to the file * ULONG DataSize; File Size * ULONG AllocationSize; Size of filesystem allocation unit * USHORT Attributes; File Attributes * ULONG EaSize; Size of file's EA information * UCHAR FileNameLength; Length of filename in bytes * STRING FileName; Name of found file * * 4.3.4.3 SMB_INFO_QUERY_EAS_FROM_LIST * * This request returns the same information as SMB_INFO_QUERY_EA_SIZE, but * only for files which have an EA list which match the EA information in * the Data part of the request. * * 4.3.4.4 SMB_FIND_FILE_DIRECTORY_INFO * * Response Field Description * ================================= ================================== * * ULONG NextEntryOffset; Offset from this structure to * beginning of next one * ULONG FileIndex; * LARGE_INTEGER CreationTime; file creation time * LARGE_INTEGER LastAccessTime; last access time * LARGE_INTEGER LastWriteTime; last write time * LARGE_INTEGER ChangeTime; last attribute change time * LARGE_INTEGER EndOfFile; file size * LARGE_INTEGER AllocationSize; size of filesystem allocation information * ULONG ExtFileAttributes; Extended file attributes * (see section 3.11) * ULONG FileNameLength; Length of filename in bytes * STRING FileName; Name of the file * * 4.3.4.5 SMB_FIND_FILE_FULL_DIRECTORY_INFO * * Response Field Description * ================================= ================================== * * ULONG NextEntryOffset; Offset from this structure to * beginning of next one * ULONG FileIndex; * LARGE_INTEGER CreationTime; file creation time * LARGE_INTEGER LastAccessTime; last access time * LARGE_INTEGER LastWriteTime; last write time * LARGE_INTEGER ChangeTime; last attribute change time * LARGE_INTEGER EndOfFile; file size * LARGE_INTEGER AllocationSize; size of filesystem allocation information * ULONG ExtFileAttributes; Extended file attributes * (see section 3.11) * ULONG FileNameLength; Length of filename in bytes * ULONG EaSize; Size of file's extended attributes * STRING FileName; Name of the file * * * SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO * * This is the same as SMB_FIND_FILE_FULL_DIRECTORY_INFO but with * FileId inserted after EaSize. FileId is preceded by a 4 byte * alignment padding. * * Response Field Description * ================================= ================================== * ... * ULONG EaSize; Size of file's extended attributes * UCHAR Reserved[4] * LARGE_INTEGER FileId Internal file system unique id. * STRING FileName; Name of the file * * 4.3.4.6 SMB_FIND_FILE_BOTH_DIRECTORY_INFO * * Response Field Description * ================================= ================================== * * ULONG NextEntryOffset; Offset from this structure to * beginning of next one * ULONG FileIndex; * LARGE_INTEGER CreationTime; file creation time * LARGE_INTEGER LastAccessTime; last access time * LARGE_INTEGER LastWriteTime; last write time * LARGE_INTEGER ChangeTime; last attribute change time * LARGE_INTEGER EndOfFile; file size * LARGE_INTEGER AllocationSize; size of filesystem allocation information * ULONG ExtFileAttributes; Extended file attributes * (see section 3.11) * ULONG FileNameLength; Length of FileName in bytes * ULONG EaSize; Size of file's extended attributes * UCHAR ShortNameLength; Length of file's short name in bytes * UCHAR Reserved * WCHAR ShortName[12]; File's 8.3 conformant name in Unicode * STRING FileName; Files full length name * * * SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO * * This is the same as SMB_FIND_FILE_BOTH_DIRECTORY_INFO but with * FileId inserted after ShortName. FileId is preceded by a 2 byte * alignment pad. * * Response Field Description * ================================= ================================== * ... * WCHAR ShortName[12]; File's 8.3 conformant name in Unicode * UCHAR Reserved[2] * LARGE_INTEGER FileId Internal file system unique id. * STRING FileName; Files full length name * * 4.3.4.7 SMB_FIND_FILE_NAMES_INFO * * Response Field Description * ================================= ================================== * * ULONG NextEntryOffset; Offset from this structure to * beginning of next one * ULONG FileIndex; * ULONG FileNameLength; Length of FileName in bytes * STRING FileName; Files full length name */ #include #include #include /* * Args (and other state) that we carry around among the * various functions involved in FindFirst, FindNext. */ typedef struct smb_find_args { uint32_t fa_fixedsize; uint16_t fa_infolev; uint16_t fa_maxcount; uint16_t fa_fflag; uint16_t fa_eos; /* End Of Search */ uint16_t fa_lno; /* Last Name Offset */ uint32_t fa_lastkey; /* Last resume key */ char fa_lastname[MAXNAMELEN]; /* and name */ } smb_find_args_t; static int smb_trans2_find_entries(smb_request_t *, smb_xa_t *, smb_odir_t *, smb_find_args_t *); static int smb_trans2_find_get_fixedsize(smb_request_t *, uint16_t, uint16_t); static int smb_trans2_find_mbc_encode(smb_request_t *, smb_xa_t *, smb_fileinfo_t *, smb_find_args_t *); /* * Tunable parameter to limit the maximum * number of entries to be returned. */ uint16_t smb_trans2_find_max = 128; /* * smb_com_trans2_find_first2 * * Client Request Value * ============================ ================================== * * UCHAR WordCount 15 * UCHAR TotalDataCount Total size of extended attribute list * UCHAR SetupCount 1 * UCHAR Setup[0] TRANS2_FIND_FIRST2 * * Parameter Block Encoding Description * ============================ ================================== * USHORT SearchAttributes; * USHORT SearchCount; Maximum number of entries to return * USHORT Flags; Additional information: * Bit 0 - close search after this request * Bit 1 - close search if end of search * reached * Bit 2 - return resume keys for each * entry found * Bit 3 - continue search from previous * ending place * Bit 4 - find with backup intent * USHORT InformationLevel; See below * ULONG SearchStorageType; * STRING FileName; Pattern for the search * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is * QUERY_EAS_FROM_LIST * * Response Parameter Block Description * ============================ ================================== * * USHORT Sid; Search handle * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of last * entry, if server needs it to resume * search; else 0 * UCHAR Data[ TotalDataCount ] Level dependent info about the matches * found in the search */ smb_sdrc_t smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t sattr; smb_pathname_t *pn; smb_odir_t *od; smb_find_args_t args; uint32_t status; uint32_t odir_flags = 0; bzero(&args, sizeof (smb_find_args_t)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } pn = &sr->arg.dirop.fqi.fq_path; if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr, &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev, &pn->pn_path) != 0) { return (SDRC_ERROR); } smb_pathname_init(sr, pn, pn->pn_path); if (!smb_pathname_validate(sr, pn)) return (-1); if (smb_is_stream_name(pn->pn_path)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) { sr->user_cr = smb_user_getprivcred(sr->uid_user); odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT; } args.fa_fixedsize = smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag); if (args.fa_fixedsize == 0) return (SDRC_ERROR); status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od); if (status != 0) { smbsr_error(sr, status, 0, 0); return (SDRC_ERROR); } if (od == NULL) return (SDRC_ERROR); count = smb_trans2_find_entries(sr, xa, od, &args); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if (count == 0) { smb_odir_close(od); smb_odir_release(od); smbsr_status(sr, NT_STATUS_NO_SUCH_FILE, ERRDOS, ERROR_FILE_NOT_FOUND); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww", od->d_odid, /* Search ID */ count, /* Search Count */ args.fa_eos, /* End Of Search */ 0, /* EA Error Offset */ args.fa_lno); /* Last Name Offset */ smb_odir_release(od); return (SDRC_SUCCESS); } /* * smb_com_trans2_find_next2 * * Client Request Value * ================================== ================================= * * WordCount 15 * SetupCount 1 * Setup[0] TRANS2_FIND_NEXT2 * * Parameter Block Encoding Description * ================================== ================================= * * USHORT Sid; Search handle * USHORT SearchCount; Maximum number of entries to * return * USHORT InformationLevel; Levels described in * TRANS2_FIND_FIRST2 request * ULONG ResumeKey; Value returned by previous find2 * call * USHORT Flags; Additional information: bit set- * 0 - close search after this * request * 1 - close search if end of search * reached * 2 - return resume keys for each * entry found * 3 - resume/continue from previous * ending place * 4 - find with backup intent * STRING FileName; Resume file name * * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2 * call. If Bit3 of Flags is set, then FileName may be the NULL string, * since the search is continued from the previous TRANS2_FIND request. * Otherwise, FileName must not be more than 256 characters long. * * Response Field Description * ================================== ================================= * * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of * last entry, if server needs it to * resume search; else 0 * UCHAR Data[TotalDataCount] Level dependent info about the * matches found in the search * * * The last parameter in the request is a filename, which is a * null-terminated unicode string. * * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr, * &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname) * * The filename parameter is not currently decoded because we * expect a 2-byte null but Mac OS 10 clients send a 1-byte null, * which leads to a decode error. * Thus, we do not support resume by filename. We treat a request * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST. */ smb_sdrc_t smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t odid; smb_odir_t *od; smb_find_args_t args; smb_odir_resume_t odir_resume; bzero(&args, sizeof (args)); bzero(&odir_resume, sizeof (odir_resume)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr, &odid, &args.fa_maxcount, &args.fa_infolev, &odir_resume.or_cookie, &args.fa_fflag, &odir_resume.or_fname) != 0) { return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); args.fa_fixedsize = smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag); if (args.fa_fixedsize == 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr, odid); if (od == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERROR_INVALID_HANDLE); return (SDRC_ERROR); } /* * Set the correct position in the directory. * * "Continue from last" is easy, but due to a history of * buggy server implementations, most clients don't use * that method. The most widely used (and reliable) is * resume by file name. Unfortunately, that can't really * be fully supported unless your file system stores all * directory entries in some sorted order (like NTFS). * We can partially support resume by name, where the only * name we're ever asked to resume on is the same as the * most recent we returned. That's always what the client * gives us as the resume name, so we can simply remember * the last name/offset pair and use that to position on * the following FindNext call. In the unlikely event * that the client asks to resume somewhere else, we'll * use the numeric resume key, and hope the client gives * correctly uses one of the resume keys we provided. */ if (args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) { odir_resume.or_type = SMB_ODIR_RESUME_CONT; } else { odir_resume.or_type = SMB_ODIR_RESUME_FNAME; } smb_odir_resume_at(od, &odir_resume); count = smb_trans2_find_entries(sr, xa, od, &args); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", count, /* Search Count */ args.fa_eos, /* End Of Search */ 0, /* EA Error Offset */ args.fa_lno); /* Last Name Offset */ return (SDRC_SUCCESS); } /* * smb_trans2_find_entries * * Find and encode up to args->fa_maxcount directory entries. * For compatibilty with Windows, if args->fa_maxcount is zero treat it as 1. * * Returns: * count - count of entries encoded * *eos = B_TRUE if no more directory entries * -1 - error */ static int smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od, smb_find_args_t *args) { smb_fileinfo_t fileinfo; smb_odir_resume_t odir_resume; uint16_t count, maxcount; int rc = -1; int LastEntryOffset = 0; boolean_t need_rewind = B_FALSE; /* * EAs are not current supported, so a search for level * SMB_INFO_QUERY_EAS_FROM_LIST should always return an * empty list. Returning zero for this case gives the * client an empty response, which is better than an * NT_STATUS_INVALID_LEVEL return (and test failures). * * If and when we do support EAs, this level will modify * the search here, and then return results just like * SMB_INFO_QUERY_EA_SIZE, but only including files * that have an EA in the provided list. */ if (args->fa_infolev == SMB_INFO_QUERY_EAS_FROM_LIST) return (0); if ((maxcount = args->fa_maxcount) == 0) maxcount = 1; if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max)) maxcount = smb_trans2_find_max; count = 0; while (count < maxcount) { rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos); if (rc != 0 || args->fa_eos != 0) break; LastEntryOffset = xa->rep_data_mb.chain_offset; rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args); if (rc == -1) return (-1); /* fatal encoding error */ if (rc == 1) { need_rewind = B_TRUE; break; /* output space exhausted */ } /* * Save the info about the last file returned. */ args->fa_lastkey = fileinfo.fi_cookie; bcopy(fileinfo.fi_name, args->fa_lastname, MAXNAMELEN); ++count; } if (args->fa_eos != 0 && rc == ENOENT) rc = 0; /* * All but the ancient info levels start with NextEntryOffset. * That's supposed to be zero in the last entry returned. */ if (args->fa_infolev >= SMB_FIND_FILE_DIRECTORY_INFO) { (void) smb_mbc_poke(&xa->rep_data_mb, LastEntryOffset, "l", 0); } /* save the last cookie returned to client */ if (count != 0) smb_odir_save_fname(od, args->fa_lastkey, args->fa_lastname); /* * If all retrieved entries have been successfully encoded * and eos has not already been detected, check if there are * any more entries. eos will be set if there are no more. */ if ((rc == 0) && (args->fa_eos == 0)) { rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos); /* * If rc == ENOENT, we did not read any additional data. * if rc != 0, there's no need to rewind. */ if (rc == 0) need_rewind = B_TRUE; } /* * When the last entry we read from the directory did not * fit in the return buffer, we will have read one entry * that will not be returned in this call. That, and the * check for EOS just above both can leave the directory * position incorrect for the next call. Fix that now. */ if (need_rewind) { bzero(&odir_resume, sizeof (odir_resume)); odir_resume.or_type = SMB_ODIR_RESUME_COOKIE; odir_resume.or_cookie = args->fa_lastkey; smb_odir_resume_at(od, &odir_resume); } return (count); } /* * smb_trans2_find_get_fixedsize * * Calculate the sizeof the fixed part of the response for the * specified information level. * * A non-zero return value provides the fixed size. * A return value of zero indicates an unknown information level. */ static int smb_trans2_find_get_fixedsize(smb_request_t *sr, uint16_t infolev, uint16_t fflag) { int maxdata = 0; switch (infolev) { case SMB_INFO_STANDARD : if (fflag & SMB_FIND_RETURN_RESUME_KEYS) maxdata += sizeof (int32_t); maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1; break; case SMB_INFO_QUERY_EA_SIZE: case SMB_INFO_QUERY_EAS_FROM_LIST: if (fflag & SMB_FIND_RETURN_RESUME_KEYS) maxdata += sizeof (int32_t); maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1; break; case SMB_FIND_FILE_DIRECTORY_INFO: maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4; break; case SMB_FIND_FILE_FULL_DIRECTORY_INFO: maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4; break; case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO: maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 8; break; case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24; break; case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO: maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24 + 2 + 8; break; case SMB_FIND_FILE_NAMES_INFO: maxdata += 4 + 4 + 4; break; case SMB_MAC_FIND_BOTH_HFS_INFO: maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1 + 2 + 4 + 32 + 4 + 1 + 1 + 24 + 4; break; default: maxdata = 0; smbsr_error(sr, NT_STATUS_INVALID_LEVEL, ERRDOS, ERROR_INVALID_LEVEL); } return (maxdata); } /* * This is an experimental feature that allows us to return zero * for all numeric resume keys, to match Windows behavior with an * NTFS share. Setting this variable to zero does that. * * It's possible we could remove this variable and always set * numeric resume keys to zero, but that would leave us unable * to handle a FindNext call with an arbitrary start position. * In practice we never see these, but in theory we could. * * See the long comment above smb_com_trans2_find_next2() for * more details about resume key / resume name handling. */ int smbd_use_resume_keys = 1; /* * smb_trans2_mbc_encode * * This function encodes the mbc for one directory entry. * * The function returns -1 when the max data requested by client * is reached. If the entry is valid and successful encoded, 0 * will be returned; otherwise, 1 will be returned. * * We always null terminate the filename. The space for the null * is included in the maxdata calculation and is therefore included * in the next_entry_offset. namelen is the unterminated length of * the filename. For levels except STANDARD and EA_SIZE, if the * filename is ascii the name length returned to the client should * include the null terminator. Otherwise the length returned to * the client should not include the terminator. * * Returns: 0 - data successfully encoded * 1 - client request's maxdata limit reached * -1 - error */ static int smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa, smb_fileinfo_t *fileinfo, smb_find_args_t *args) { int namelen, shortlen; uint32_t next_entry_offset; uint32_t dsize32, asize32; uint32_t mb_flags = 0; uint32_t resume_key; char buf83[26]; smb_msgbuf_t mb; int pad = 0; namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name); if (namelen == -1) return (-1); if (args->fa_infolev < SMB_FIND_FILE_DIRECTORY_INFO) { /* * Ancient info levels don't have a NextEntryOffset * field, so there's no padding for alignment. * The client expects a null after the file name, * and then the next entry. The namelength field * never includes the null for these old levels. * Using the pad value to write the null because * we don't want to add that to namelen. * [MS-CIFS] sec. 2.8.1.{1-3} */ if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0) pad = 2; /* Unicode null */ else pad = 1; /* ascii null */ next_entry_offset = args->fa_fixedsize + namelen + pad; if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset)) return (1); } else { /* * Later info levels: The file name is written WITH * null termination, and the size of that null _is_ * included in the namelen field. There may also * be padding, and we pad to align(4) like Windows. * Don't include the padding in the "room for" test * because we want to ignore any error writing the * pad bytes after the last element. */ if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0) namelen += 2; else namelen += 1; next_entry_offset = args->fa_fixedsize + namelen; if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset)) return (1); if ((next_entry_offset & 3) != 0) { pad = 4 - (next_entry_offset & 3); next_entry_offset += pad; } } mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0; dsize32 = (fileinfo->fi_size > UINT_MAX) ? UINT_MAX : (uint32_t)fileinfo->fi_size; asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ? UINT_MAX : (uint32_t)fileinfo->fi_alloc_size; resume_key = fileinfo->fi_cookie; if (smbd_use_resume_keys == 0) resume_key = 0; /* * This switch handles all the "information levels" (formats) * that we support. Note that all formats have the file name * placed after some fixed-size data, and the code to write * the file name is factored out at the end of this switch. */ switch (args->fa_infolev) { case SMB_INFO_STANDARD: if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS) (void) smb_mbc_encodef(&xa->rep_data_mb, "l", resume_key); (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwb", sr, smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, namelen); break; case SMB_INFO_QUERY_EA_SIZE: case SMB_INFO_QUERY_EAS_FROM_LIST: if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS) (void) smb_mbc_encodef(&xa->rep_data_mb, "l", resume_key); (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr, smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, 0L, /* EA Size */ namelen); break; case SMB_FIND_FILE_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqll", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen); break; case SMB_FIND_FILE_FULL_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L); break; case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.q", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, fileinfo->fi_nodeid); break; case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: bzero(buf83, sizeof (buf83)); smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83), mb_flags); if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) { smb_msgbuf_term(&mb); return (-1); } shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname); (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, shortlen, buf83); smb_msgbuf_term(&mb); break; case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO: bzero(buf83, sizeof (buf83)); smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83), mb_flags); if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) { smb_msgbuf_term(&mb); return (-1); } shortlen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_shortname); (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c2.q", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, shortlen, buf83, fileinfo->fi_nodeid); smb_msgbuf_term(&mb); break; case SMB_FIND_FILE_NAMES_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%lll", sr, next_entry_offset, resume_key, namelen); break; default: /* invalid info. level */ return (-1); } /* * At this point we have written all the fixed-size data * for the specified info. level, and we're about to put * the file name string in the message. We may later * need the offset in the trans2 data where this string * is placed, so save the message position now. Note: * We also need to account for the alignment padding * that may precede the unicode string. */ args->fa_lno = xa->rep_data_mb.chain_offset; if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 && (args->fa_lno & 1) != 0) args->fa_lno++; (void) smb_mbc_encodef(&xa->rep_data_mb, "%#u", sr, namelen, fileinfo->fi_name); if (pad) (void) smb_mbc_encodef(&xa->rep_data_mb, "#.", pad); return (0); } /* * Close a search started by a Trans2FindFirst2 request. */ smb_sdrc_t smb_pre_find_close2(smb_request_t *sr) { DTRACE_SMB_START(op__FindClose2, smb_request_t *, sr); return (SDRC_SUCCESS); } void smb_post_find_close2(smb_request_t *sr) { DTRACE_SMB_DONE(op__FindClose2, smb_request_t *, sr); } smb_sdrc_t smb_com_find_close2(smb_request_t *sr) { uint16_t odid; smb_odir_t *od; if (smbsr_decode_vwv(sr, "w", &odid) != 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr, odid); if (od == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERROR_INVALID_HANDLE); return (SDRC_ERROR); } smb_odir_close(od); smb_odir_release(od); if (smbsr_encode_empty_result(sr)) return (SDRC_ERROR); return (SDRC_SUCCESS); }