1a90cf9f2SGordon Ross /* 2a90cf9f2SGordon Ross * CDDL HEADER START 3a90cf9f2SGordon Ross * 4a90cf9f2SGordon Ross * The contents of this file are subject to the terms of the 5a90cf9f2SGordon Ross * Common Development and Distribution License (the "License"). 6a90cf9f2SGordon Ross * You may not use this file except in compliance with the License. 7a90cf9f2SGordon Ross * 8a90cf9f2SGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9a90cf9f2SGordon Ross * or http://www.opensolaris.org/os/licensing. 10a90cf9f2SGordon Ross * See the License for the specific language governing permissions 11a90cf9f2SGordon Ross * and limitations under the License. 12a90cf9f2SGordon Ross * 13a90cf9f2SGordon Ross * When distributing Covered Code, include this CDDL HEADER in each 14a90cf9f2SGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15a90cf9f2SGordon Ross * If applicable, add the following below this CDDL HEADER, with the 16a90cf9f2SGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying 17a90cf9f2SGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner] 18a90cf9f2SGordon Ross * 19a90cf9f2SGordon Ross * CDDL HEADER END 20a90cf9f2SGordon Ross */ 21a90cf9f2SGordon Ross /* 22a90cf9f2SGordon Ross * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23a90cf9f2SGordon Ross * Use is subject to license terms. 24a90cf9f2SGordon Ross * 25*93bc28dbSGordon Ross * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 26a90cf9f2SGordon Ross */ 27a90cf9f2SGordon Ross 28a90cf9f2SGordon Ross /* 29a90cf9f2SGordon Ross * Dispatch function for SMB2_QUERY_DIRECTORY 30a90cf9f2SGordon Ross * 31a90cf9f2SGordon Ross * Similar to smb_trans2_find.c (from SMB1) 32a90cf9f2SGordon Ross */ 33a90cf9f2SGordon Ross 34a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h> 35d082c877SGordon Ross #include <smbsrv/smb2_aapl.h> 36d082c877SGordon Ross 37d082c877SGordon Ross /* 38d082c877SGordon Ross * Internally defined info. level for MacOS support. 39d082c877SGordon Ross * Make sure this does not conflict with real values in 40d082c877SGordon Ross * FILE_INFORMATION_CLASS, and that it fits in 8-bits. 41d082c877SGordon Ross */ 42d082c877SGordon Ross #define FileIdMacOsDirectoryInformation (FileMaximumInformation + 10) 43a90cf9f2SGordon Ross 44a90cf9f2SGordon Ross /* 45a90cf9f2SGordon Ross * Args (and other state) that we carry around among the 46a90cf9f2SGordon Ross * various functions involved in SMB2 Query Directory. 47a90cf9f2SGordon Ross */ 48a90cf9f2SGordon Ross typedef struct smb2_find_args { 49a90cf9f2SGordon Ross uint32_t fa_maxdata; 50a90cf9f2SGordon Ross uint8_t fa_infoclass; 51a90cf9f2SGordon Ross uint8_t fa_fflags; 52a90cf9f2SGordon Ross uint16_t fa_maxcount; 53a90cf9f2SGordon Ross uint16_t fa_eos; /* End Of Search */ 54a90cf9f2SGordon Ross uint16_t fa_fixedsize; /* size of fixed part of a returned entry */ 55a90cf9f2SGordon Ross uint32_t fa_lastkey; /* Last resume key */ 56a90cf9f2SGordon Ross int fa_last_entry; /* offset of last entry */ 57d082c877SGordon Ross 58d082c877SGordon Ross /* Normal info, per dir. entry */ 59d082c877SGordon Ross smb_fileinfo_t fa_fi; 60d082c877SGordon Ross 61d082c877SGordon Ross /* MacOS AAPL extension stuff. */ 62d082c877SGordon Ross smb_macinfo_t fa_mi; 63a90cf9f2SGordon Ross } smb2_find_args_t; 64a90cf9f2SGordon Ross 65a90cf9f2SGordon Ross static uint32_t smb2_find_entries(smb_request_t *, 66a90cf9f2SGordon Ross smb_odir_t *, smb2_find_args_t *); 67d082c877SGordon Ross static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *); 68a90cf9f2SGordon Ross 69a90cf9f2SGordon Ross /* 70a90cf9f2SGordon Ross * Tunable parameter to limit the maximum 71a90cf9f2SGordon Ross * number of entries to be returned. 72a90cf9f2SGordon Ross */ 73a90cf9f2SGordon Ross uint16_t smb2_find_max = 128; 74a90cf9f2SGordon Ross 75a90cf9f2SGordon Ross smb_sdrc_t 76a90cf9f2SGordon Ross smb2_query_dir(smb_request_t *sr) 77a90cf9f2SGordon Ross { 78a90cf9f2SGordon Ross smb2_find_args_t args; 79a90cf9f2SGordon Ross smb_odir_resume_t odir_resume; 80a90cf9f2SGordon Ross smb_ofile_t *of = NULL; 81a90cf9f2SGordon Ross smb_odir_t *od = NULL; 82a90cf9f2SGordon Ross char *pattern = NULL; 83a90cf9f2SGordon Ross uint16_t StructSize; 84a90cf9f2SGordon Ross uint32_t FileIndex; 85a90cf9f2SGordon Ross uint16_t NameOffset; 86a90cf9f2SGordon Ross uint16_t NameLength; 87a90cf9f2SGordon Ross smb2fid_t smb2fid; 88a90cf9f2SGordon Ross uint16_t sattr = SMB_SEARCH_ATTRIBUTES; 89a90cf9f2SGordon Ross uint16_t DataOff; 90a90cf9f2SGordon Ross uint32_t DataLen; 91a90cf9f2SGordon Ross uint32_t status; 92a90cf9f2SGordon Ross int skip, rc = 0; 93a90cf9f2SGordon Ross 94a90cf9f2SGordon Ross bzero(&args, sizeof (args)); 95a90cf9f2SGordon Ross bzero(&odir_resume, sizeof (odir_resume)); 96a90cf9f2SGordon Ross 97a90cf9f2SGordon Ross /* 98a90cf9f2SGordon Ross * SMB2 Query Directory request 99a90cf9f2SGordon Ross */ 100a90cf9f2SGordon Ross rc = smb_mbc_decodef( 101a90cf9f2SGordon Ross &sr->smb_data, "wbblqqwwl", 102a90cf9f2SGordon Ross &StructSize, /* w */ 103a90cf9f2SGordon Ross &args.fa_infoclass, /* b */ 104a90cf9f2SGordon Ross &args.fa_fflags, /* b */ 105a90cf9f2SGordon Ross &FileIndex, /* l */ 106a90cf9f2SGordon Ross &smb2fid.persistent, /* q */ 107a90cf9f2SGordon Ross &smb2fid.temporal, /* q */ 108a90cf9f2SGordon Ross &NameOffset, /* w */ 109a90cf9f2SGordon Ross &NameLength, /* w */ 110a90cf9f2SGordon Ross &args.fa_maxdata); /* l */ 111a90cf9f2SGordon Ross if (rc || StructSize != 33) 112a90cf9f2SGordon Ross return (SDRC_ERROR); 113a90cf9f2SGordon Ross 114a90cf9f2SGordon Ross status = smb2sr_lookup_fid(sr, &smb2fid); 115*93bc28dbSGordon Ross of = sr->fid_ofile; 116*93bc28dbSGordon Ross 117*93bc28dbSGordon Ross DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr); 118*93bc28dbSGordon Ross 119a90cf9f2SGordon Ross if (status) 120a90cf9f2SGordon Ross goto errout; 121a90cf9f2SGordon Ross 122a90cf9f2SGordon Ross /* 123a90cf9f2SGordon Ross * If there's an input buffer (search pattern), decode it. 124a90cf9f2SGordon Ross * Two times MAXNAMELEN because it represents the UNICODE string 125a90cf9f2SGordon Ross * length in bytes. 126a90cf9f2SGordon Ross */ 127a90cf9f2SGordon Ross if (NameLength >= (2 * MAXNAMELEN)) { 128a90cf9f2SGordon Ross status = NT_STATUS_OBJECT_PATH_INVALID; 129a90cf9f2SGordon Ross goto errout; 130a90cf9f2SGordon Ross } 131a90cf9f2SGordon Ross if (NameLength != 0) { 132a90cf9f2SGordon Ross /* 133a90cf9f2SGordon Ross * We're normally positioned at the pattern now, 134a90cf9f2SGordon Ross * but there could be some padding before it. 135a90cf9f2SGordon Ross */ 136a90cf9f2SGordon Ross skip = (sr->smb2_cmd_hdr + NameOffset) - 137a90cf9f2SGordon Ross sr->smb_data.chain_offset; 138a90cf9f2SGordon Ross if (skip < 0) { 139a90cf9f2SGordon Ross status = NT_STATUS_OBJECT_PATH_INVALID; 140a90cf9f2SGordon Ross goto errout; 141a90cf9f2SGordon Ross } 142a90cf9f2SGordon Ross if (skip > 0) 143a90cf9f2SGordon Ross (void) smb_mbc_decodef(&sr->smb_data, "#.", skip); 144a90cf9f2SGordon Ross rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr, 145a90cf9f2SGordon Ross NameLength, &pattern); 146a90cf9f2SGordon Ross if (rc || pattern == NULL) { 147a90cf9f2SGordon Ross status = NT_STATUS_OBJECT_PATH_INVALID; 148a90cf9f2SGordon Ross goto errout; 149a90cf9f2SGordon Ross } 150a90cf9f2SGordon Ross } else 151a90cf9f2SGordon Ross pattern = "*"; 152a90cf9f2SGordon Ross 153a90cf9f2SGordon Ross /* 154a90cf9f2SGordon Ross * Setup the output buffer. 155a90cf9f2SGordon Ross */ 156a90cf9f2SGordon Ross if (args.fa_maxdata > smb2_max_trans) 157a90cf9f2SGordon Ross args.fa_maxdata = smb2_max_trans; 158a90cf9f2SGordon Ross sr->raw_data.max_bytes = args.fa_maxdata; 159a90cf9f2SGordon Ross 160a90cf9f2SGordon Ross /* 161d082c877SGordon Ross * Get the fixed size of entries we will return, which 162a90cf9f2SGordon Ross * lets us estimate the number of entries we'll need. 163a90cf9f2SGordon Ross * 164a90cf9f2SGordon Ross * Also use this opportunity to validate fa_infoclass. 165a90cf9f2SGordon Ross */ 166a90cf9f2SGordon Ross 167a90cf9f2SGordon Ross switch (args.fa_infoclass) { 168a90cf9f2SGordon Ross case FileDirectoryInformation: /* 1 */ 169a90cf9f2SGordon Ross args.fa_fixedsize = 64; 170a90cf9f2SGordon Ross break; 171a90cf9f2SGordon Ross case FileFullDirectoryInformation: /* 2 */ 172a90cf9f2SGordon Ross args.fa_fixedsize = 68; 173a90cf9f2SGordon Ross break; 174a90cf9f2SGordon Ross case FileBothDirectoryInformation: /* 3 */ 175a90cf9f2SGordon Ross args.fa_fixedsize = 94; 176a90cf9f2SGordon Ross break; 177a90cf9f2SGordon Ross case FileNamesInformation: /* 12 */ 178a90cf9f2SGordon Ross args.fa_fixedsize = 12; 179a90cf9f2SGordon Ross break; 180a90cf9f2SGordon Ross case FileIdBothDirectoryInformation: /* 37 */ 181a90cf9f2SGordon Ross args.fa_fixedsize = 96; 182a90cf9f2SGordon Ross break; 183a90cf9f2SGordon Ross case FileIdFullDirectoryInformation: /* 38 */ 184a90cf9f2SGordon Ross args.fa_fixedsize = 84; 185a90cf9f2SGordon Ross break; 186a90cf9f2SGordon Ross default: 187a90cf9f2SGordon Ross status = NT_STATUS_INVALID_INFO_CLASS; 188a90cf9f2SGordon Ross goto errout; 189a90cf9f2SGordon Ross } 190a90cf9f2SGordon Ross 191d082c877SGordon Ross /* 192d082c877SGordon Ross * MacOS, when using the AAPL CreateContext extensions 193d082c877SGordon Ross * and the "read dir attr" feature, uses a non-standard 194d082c877SGordon Ross * information format for directory entries. Internally 195d082c877SGordon Ross * we'll use a fake info level to represent this case. 196d082c877SGordon Ross * (Wish they had just defined a new info level.) 197d082c877SGordon Ross */ 198d082c877SGordon Ross if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 && 199d082c877SGordon Ross args.fa_infoclass == FileIdBothDirectoryInformation) { 200d082c877SGordon Ross args.fa_infoclass = FileIdMacOsDirectoryInformation; 201d082c877SGordon Ross args.fa_fixedsize = 96; /* yes, same size */ 202d082c877SGordon Ross } 203d082c877SGordon Ross 204a90cf9f2SGordon Ross args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4); 205a90cf9f2SGordon Ross if (args.fa_maxcount == 0) 206a90cf9f2SGordon Ross args.fa_maxcount = 1; 207a90cf9f2SGordon Ross if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max)) 208a90cf9f2SGordon Ross args.fa_maxcount = smb2_find_max; 209a90cf9f2SGordon Ross if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) 210a90cf9f2SGordon Ross args.fa_maxcount = 1; 211a90cf9f2SGordon Ross 212a90cf9f2SGordon Ross /* 213a90cf9f2SGordon Ross * If this ofile does not have an odir yet, get one. 214a90cf9f2SGordon Ross */ 215a90cf9f2SGordon Ross mutex_enter(&of->f_mutex); 216a90cf9f2SGordon Ross if ((od = of->f_odir) == NULL) { 217a90cf9f2SGordon Ross status = smb_odir_openfh(sr, pattern, sattr, &od); 218a90cf9f2SGordon Ross of->f_odir = od; 219a90cf9f2SGordon Ross } 220a90cf9f2SGordon Ross mutex_exit(&of->f_mutex); 221a90cf9f2SGordon Ross if (od == NULL) { 222a90cf9f2SGordon Ross if (status == 0) 223a90cf9f2SGordon Ross status = NT_STATUS_INTERNAL_ERROR; 224a90cf9f2SGordon Ross goto errout; 225a90cf9f2SGordon Ross } 226a90cf9f2SGordon Ross 227a90cf9f2SGordon Ross /* 228a90cf9f2SGordon Ross * "Reopen" sets a new pattern and restart. 229a90cf9f2SGordon Ross */ 230a90cf9f2SGordon Ross if (args.fa_fflags & SMB2_QDIR_FLAG_REOPEN) { 231a90cf9f2SGordon Ross smb_odir_reopen(od, pattern, sattr); 232a90cf9f2SGordon Ross } 233a90cf9f2SGordon Ross 234a90cf9f2SGordon Ross /* 235a90cf9f2SGordon Ross * Set the correct position in the directory. 236a90cf9f2SGordon Ross */ 237a90cf9f2SGordon Ross if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) { 238a90cf9f2SGordon Ross odir_resume.or_type = SMB_ODIR_RESUME_COOKIE; 239a90cf9f2SGordon Ross odir_resume.or_cookie = 0; 240a90cf9f2SGordon Ross } else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) { 241a90cf9f2SGordon Ross odir_resume.or_type = SMB_ODIR_RESUME_COOKIE; 242a90cf9f2SGordon Ross odir_resume.or_cookie = FileIndex; 243a90cf9f2SGordon Ross } else { 244a90cf9f2SGordon Ross odir_resume.or_type = SMB_ODIR_RESUME_CONT; 245a90cf9f2SGordon Ross } 246a90cf9f2SGordon Ross smb_odir_resume_at(od, &odir_resume); 247a90cf9f2SGordon Ross of->f_seek_pos = od->d_offset; 248a90cf9f2SGordon Ross 249a90cf9f2SGordon Ross /* 250a90cf9f2SGordon Ross * The real work of readdir and format conversion. 251a90cf9f2SGordon Ross */ 252a90cf9f2SGordon Ross status = smb2_find_entries(sr, od, &args); 253a90cf9f2SGordon Ross 254a90cf9f2SGordon Ross of->f_seek_pos = od->d_offset; 255a90cf9f2SGordon Ross 256*93bc28dbSGordon Ross if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) && 257*93bc28dbSGordon Ross status == NT_STATUS_NO_MORE_FILES) { 258a90cf9f2SGordon Ross status = NT_STATUS_NO_SUCH_FILE; 259a90cf9f2SGordon Ross } 260*93bc28dbSGordon Ross 261*93bc28dbSGordon Ross errout: 262*93bc28dbSGordon Ross sr->smb2_status = status; 263*93bc28dbSGordon Ross DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr); 264*93bc28dbSGordon Ross 265a90cf9f2SGordon Ross /* 266*93bc28dbSGordon Ross * Note: NT_STATUS_NO_MORE_FILES is a warning 267a90cf9f2SGordon Ross * used to tell the client that this data return 268a90cf9f2SGordon Ross * is the last of the enumeration. Returning this 269a90cf9f2SGordon Ross * warning now (with the data) saves the client a 270a90cf9f2SGordon Ross * round trip that would otherwise be needed to 271a90cf9f2SGordon Ross * find out it's at the end. 272a90cf9f2SGordon Ross */ 273*93bc28dbSGordon Ross if (status != 0 && 274*93bc28dbSGordon Ross status != NT_STATUS_NO_MORE_FILES) { 275*93bc28dbSGordon Ross smb2sr_put_error(sr, status); 276*93bc28dbSGordon Ross return (SDRC_SUCCESS); 277a90cf9f2SGordon Ross } 278a90cf9f2SGordon Ross 279a90cf9f2SGordon Ross /* 280a90cf9f2SGordon Ross * SMB2 Query Directory reply 281a90cf9f2SGordon Ross */ 282a90cf9f2SGordon Ross StructSize = 9; 283a90cf9f2SGordon Ross DataOff = SMB2_HDR_SIZE + 8; 284a90cf9f2SGordon Ross DataLen = MBC_LENGTH(&sr->raw_data); 285a90cf9f2SGordon Ross rc = smb_mbc_encodef( 286a90cf9f2SGordon Ross &sr->reply, "wwlC", 287a90cf9f2SGordon Ross StructSize, /* w */ 288a90cf9f2SGordon Ross DataOff, /* w */ 289a90cf9f2SGordon Ross DataLen, /* l */ 290a90cf9f2SGordon Ross &sr->raw_data); /* C */ 291a90cf9f2SGordon Ross if (DataLen == 0) 292a90cf9f2SGordon Ross (void) smb_mbc_encodef(&sr->reply, "."); 293a90cf9f2SGordon Ross 294*93bc28dbSGordon Ross if (rc) 295*93bc28dbSGordon Ross sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 296*93bc28dbSGordon Ross 297a90cf9f2SGordon Ross return (SDRC_SUCCESS); 298a90cf9f2SGordon Ross } 299a90cf9f2SGordon Ross 300a90cf9f2SGordon Ross /* 301a90cf9f2SGordon Ross * smb2_find_entries 302a90cf9f2SGordon Ross * 303a90cf9f2SGordon Ross * Find and encode up to args->fa_maxcount directory entries. 304a90cf9f2SGordon Ross * 305a90cf9f2SGordon Ross * Returns: 306a90cf9f2SGordon Ross * NT status 307a90cf9f2SGordon Ross */ 308a90cf9f2SGordon Ross static uint32_t 309a90cf9f2SGordon Ross smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args) 310a90cf9f2SGordon Ross { 311a90cf9f2SGordon Ross smb_odir_resume_t odir_resume; 312d082c877SGordon Ross char *tbuf = NULL; 313d082c877SGordon Ross size_t tbuflen = 0; 314a90cf9f2SGordon Ross uint16_t count; 315a90cf9f2SGordon Ross uint16_t minsize; 316a90cf9f2SGordon Ross uint32_t status = 0; 317a90cf9f2SGordon Ross int rc = -1; 318a90cf9f2SGordon Ross 319a90cf9f2SGordon Ross /* 320a90cf9f2SGordon Ross * Let's stop when the remaining space will not hold a 321a90cf9f2SGordon Ross * minimum size entry. That's the fixed part plus the 322a90cf9f2SGordon Ross * storage size of a 1 char unicode string. 323a90cf9f2SGordon Ross */ 324a90cf9f2SGordon Ross minsize = args->fa_fixedsize + 2; 325a90cf9f2SGordon Ross 326d082c877SGordon Ross /* 327d082c877SGordon Ross * FileIdMacOsDirectoryInformation needs some buffer space 328d082c877SGordon Ross * for composing directory entry + stream name for lookup. 329d082c877SGordon Ross * Get the buffer now to avoid alloc/free per entry. 330d082c877SGordon Ross */ 331d082c877SGordon Ross if (args->fa_infoclass == FileIdMacOsDirectoryInformation) { 332d082c877SGordon Ross tbuflen = 2 * MAXNAMELEN; 333d082c877SGordon Ross tbuf = kmem_alloc(tbuflen, KM_SLEEP); 334d082c877SGordon Ross } 335d082c877SGordon Ross 336a90cf9f2SGordon Ross count = 0; 337a90cf9f2SGordon Ross while (count < args->fa_maxcount) { 338a90cf9f2SGordon Ross 339a90cf9f2SGordon Ross if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) { 340a90cf9f2SGordon Ross status = NT_STATUS_BUFFER_OVERFLOW; 341a90cf9f2SGordon Ross break; 342a90cf9f2SGordon Ross } 343a90cf9f2SGordon Ross 344d082c877SGordon Ross rc = smb_odir_read_fileinfo(sr, od, 345d082c877SGordon Ross &args->fa_fi, &args->fa_eos); 346a90cf9f2SGordon Ross if (rc == ENOENT) { 347a90cf9f2SGordon Ross status = NT_STATUS_NO_MORE_FILES; 348a90cf9f2SGordon Ross break; 349a90cf9f2SGordon Ross } 350a90cf9f2SGordon Ross if (rc != 0) { 351a90cf9f2SGordon Ross status = smb_errno2status(rc); 352a90cf9f2SGordon Ross break; 353a90cf9f2SGordon Ross } 354a90cf9f2SGordon Ross if (args->fa_eos != 0) { 355a90cf9f2SGordon Ross /* The readdir call hit the end. */ 356a90cf9f2SGordon Ross status = NT_STATUS_NO_MORE_FILES; 357a90cf9f2SGordon Ross break; 358a90cf9f2SGordon Ross } 359a90cf9f2SGordon Ross 360d082c877SGordon Ross if (args->fa_infoclass == FileIdMacOsDirectoryInformation) 361d082c877SGordon Ross (void) smb2_aapl_get_macinfo(sr, od, 362d082c877SGordon Ross &args->fa_fi, &args->fa_mi, tbuf, tbuflen); 363d082c877SGordon Ross 364d082c877SGordon Ross if (smb2_aapl_use_file_ids == 0 && 365d082c877SGordon Ross (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0) 366d082c877SGordon Ross args->fa_fi.fi_nodeid = 0; 367d082c877SGordon Ross 368d082c877SGordon Ross status = smb2_find_mbc_encode(sr, args); 369a90cf9f2SGordon Ross if (status) { 370a90cf9f2SGordon Ross /* 371a90cf9f2SGordon Ross * We read a directory entry but failed to 372a90cf9f2SGordon Ross * copy it into the output buffer. Rewind 373a90cf9f2SGordon Ross * the directory pointer so this will be 374a90cf9f2SGordon Ross * the first entry read next time. 375a90cf9f2SGordon Ross */ 376a90cf9f2SGordon Ross bzero(&odir_resume, sizeof (odir_resume)); 377a90cf9f2SGordon Ross odir_resume.or_type = SMB_ODIR_RESUME_COOKIE; 378a90cf9f2SGordon Ross odir_resume.or_cookie = args->fa_lastkey; 379a90cf9f2SGordon Ross smb_odir_resume_at(od, &odir_resume); 380a90cf9f2SGordon Ross break; 381a90cf9f2SGordon Ross } 382a90cf9f2SGordon Ross 383a90cf9f2SGordon Ross /* 384a90cf9f2SGordon Ross * Save the offset of the next entry we'll read. 385a90cf9f2SGordon Ross * If we fail copying, we'll need this offset. 386a90cf9f2SGordon Ross */ 387d082c877SGordon Ross args->fa_lastkey = args->fa_fi.fi_cookie; 388a90cf9f2SGordon Ross ++count; 389a90cf9f2SGordon Ross } 390a90cf9f2SGordon Ross 391a90cf9f2SGordon Ross if (count == 0) { 392a90cf9f2SGordon Ross ASSERT(status != 0); 393a90cf9f2SGordon Ross } else { 394a90cf9f2SGordon Ross /* 395a90cf9f2SGordon Ross * We copied some directory entries, but stopped for 396a90cf9f2SGordon Ross * NT_STATUS_NO_MORE_FILES, or something. 397a90cf9f2SGordon Ross * 398a90cf9f2SGordon Ross * Per [MS-FSCC] sec. 2.4, the last entry in the 399a90cf9f2SGordon Ross * enumeration MUST have its NextEntryOffset value 400a90cf9f2SGordon Ross * set to zero. Overwrite that in the last entry. 401a90cf9f2SGordon Ross */ 402a90cf9f2SGordon Ross (void) smb_mbc_poke(&sr->raw_data, 403a90cf9f2SGordon Ross args->fa_last_entry, "l", 0); 404a90cf9f2SGordon Ross status = 0; 405a90cf9f2SGordon Ross } 406a90cf9f2SGordon Ross 407d082c877SGordon Ross if (tbuf != NULL) 408d082c877SGordon Ross kmem_free(tbuf, tbuflen); 409d082c877SGordon Ross 410a90cf9f2SGordon Ross return (status); 411a90cf9f2SGordon Ross } 412a90cf9f2SGordon Ross 413a90cf9f2SGordon Ross /* 414a90cf9f2SGordon Ross * smb2_mbc_encode 415a90cf9f2SGordon Ross * 416a90cf9f2SGordon Ross * This function encodes the mbc for one directory entry. 417a90cf9f2SGordon Ross * 418a90cf9f2SGordon Ross * The function returns -1 when the max data requested by client 419a90cf9f2SGordon Ross * is reached. If the entry is valid and successful encoded, 0 420a90cf9f2SGordon Ross * will be returned; otherwise, 1 will be returned. 421a90cf9f2SGordon Ross * 422a90cf9f2SGordon Ross * We always null terminate the filename. The space for the null 423a90cf9f2SGordon Ross * is included in the maxdata calculation and is therefore included 424a90cf9f2SGordon Ross * in the next_entry_offset. namelen is the unterminated length of 425a90cf9f2SGordon Ross * the filename. For levels except STANDARD and EA_SIZE, if the 426a90cf9f2SGordon Ross * filename is ascii the name length returned to the client should 427a90cf9f2SGordon Ross * include the null terminator. Otherwise the length returned to 428a90cf9f2SGordon Ross * the client should not include the terminator. 429a90cf9f2SGordon Ross * 430a90cf9f2SGordon Ross * Returns: 0 - data successfully encoded 431a90cf9f2SGordon Ross * NT status 432a90cf9f2SGordon Ross */ 433a90cf9f2SGordon Ross static uint32_t 434d082c877SGordon Ross smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args) 435a90cf9f2SGordon Ross { 436d082c877SGordon Ross smb_fileinfo_t *fileinfo = &args->fa_fi; 437d082c877SGordon Ross smb_macinfo_t *macinfo = &args->fa_mi; 438a90cf9f2SGordon Ross uint8_t buf83[26]; 439a90cf9f2SGordon Ross smb_msgbuf_t mb; 440a90cf9f2SGordon Ross int namelen, padsz; 441a90cf9f2SGordon Ross int shortlen = 0; 442a90cf9f2SGordon Ross int rc, starting_offset; 443a90cf9f2SGordon Ross uint32_t next_entry_offset; 444a90cf9f2SGordon Ross uint32_t mb_flags = SMB_MSGBUF_UNICODE; 445a90cf9f2SGordon Ross uint32_t resume_key; 446a90cf9f2SGordon Ross 447a90cf9f2SGordon Ross namelen = smb_wcequiv_strlen(fileinfo->fi_name); 448a90cf9f2SGordon Ross if (namelen == -1) 449a90cf9f2SGordon Ross return (NT_STATUS_INTERNAL_ERROR); 450a90cf9f2SGordon Ross 451a90cf9f2SGordon Ross /* 452a90cf9f2SGordon Ross * Keep track of where the last entry starts so we can 453a90cf9f2SGordon Ross * come back and poke the NextEntryOffset field. Also, 454a90cf9f2SGordon Ross * after enumeration finishes, the caller uses this to 455a90cf9f2SGordon Ross * poke the last entry again with zero to mark it as 456a90cf9f2SGordon Ross * the end of the enumeration. 457a90cf9f2SGordon Ross */ 458a90cf9f2SGordon Ross starting_offset = sr->raw_data.chain_offset; 459a90cf9f2SGordon Ross 460a90cf9f2SGordon Ross /* 461a90cf9f2SGordon Ross * Technically (per MS-SMB2) resume keys are optional. 462a90cf9f2SGordon Ross * Windows doesn't need them, but MacOS does. 463a90cf9f2SGordon Ross */ 464a90cf9f2SGordon Ross resume_key = fileinfo->fi_cookie; 465a90cf9f2SGordon Ross 466a90cf9f2SGordon Ross /* 467a90cf9f2SGordon Ross * This switch handles all the "information levels" (formats) 468a90cf9f2SGordon Ross * that we support. Note that all formats have the file name 469a90cf9f2SGordon Ross * placed after some fixed-size data, and the code to write 470a90cf9f2SGordon Ross * the file name is factored out at the end of this switch. 471a90cf9f2SGordon Ross */ 472a90cf9f2SGordon Ross switch (args->fa_infoclass) { 473a90cf9f2SGordon Ross 474a90cf9f2SGordon Ross /* See also: SMB_FIND_FILE_DIRECTORY_INFO */ 475a90cf9f2SGordon Ross case FileDirectoryInformation: /* 1 */ 476a90cf9f2SGordon Ross rc = smb_mbc_encodef( 477a90cf9f2SGordon Ross &sr->raw_data, "llTTTTqqll", 478a90cf9f2SGordon Ross 0, /* NextEntryOffset (set later) */ 479a90cf9f2SGordon Ross resume_key, 480a90cf9f2SGordon Ross &fileinfo->fi_crtime, 481a90cf9f2SGordon Ross &fileinfo->fi_atime, 482a90cf9f2SGordon Ross &fileinfo->fi_mtime, 483a90cf9f2SGordon Ross &fileinfo->fi_ctime, 484a90cf9f2SGordon Ross fileinfo->fi_size, 485a90cf9f2SGordon Ross fileinfo->fi_alloc_size, 486a90cf9f2SGordon Ross fileinfo->fi_dosattr, 487a90cf9f2SGordon Ross namelen); 488a90cf9f2SGordon Ross break; 489a90cf9f2SGordon Ross 490a90cf9f2SGordon Ross /* See also: SMB_FIND_FILE_FULL_DIRECTORY_INFO */ 491a90cf9f2SGordon Ross case FileFullDirectoryInformation: /* 2 */ 492a90cf9f2SGordon Ross rc = smb_mbc_encodef( 493a90cf9f2SGordon Ross &sr->raw_data, "llTTTTqqlll", 494a90cf9f2SGordon Ross 0, /* NextEntryOffset (set later) */ 495a90cf9f2SGordon Ross resume_key, 496a90cf9f2SGordon Ross &fileinfo->fi_crtime, 497a90cf9f2SGordon Ross &fileinfo->fi_atime, 498a90cf9f2SGordon Ross &fileinfo->fi_mtime, 499a90cf9f2SGordon Ross &fileinfo->fi_ctime, 500a90cf9f2SGordon Ross fileinfo->fi_size, 501a90cf9f2SGordon Ross fileinfo->fi_alloc_size, 502a90cf9f2SGordon Ross fileinfo->fi_dosattr, 503a90cf9f2SGordon Ross namelen, 504a90cf9f2SGordon Ross 0L); /* EaSize */ 505a90cf9f2SGordon Ross break; 506a90cf9f2SGordon Ross 507a90cf9f2SGordon Ross /* See also: SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO */ 508a90cf9f2SGordon Ross case FileIdFullDirectoryInformation: /* 38 */ 509a90cf9f2SGordon Ross rc = smb_mbc_encodef( 510a90cf9f2SGordon Ross &sr->raw_data, "llTTTTqqllllq", 511a90cf9f2SGordon Ross 0, /* NextEntryOffset (set later) */ 512a90cf9f2SGordon Ross resume_key, 513a90cf9f2SGordon Ross &fileinfo->fi_crtime, 514a90cf9f2SGordon Ross &fileinfo->fi_atime, 515a90cf9f2SGordon Ross &fileinfo->fi_mtime, 516a90cf9f2SGordon Ross &fileinfo->fi_ctime, 517a90cf9f2SGordon Ross fileinfo->fi_size, 518a90cf9f2SGordon Ross fileinfo->fi_alloc_size, 519a90cf9f2SGordon Ross fileinfo->fi_dosattr, 520a90cf9f2SGordon Ross namelen, 521a90cf9f2SGordon Ross 0L, /* EaSize */ 522a90cf9f2SGordon Ross 0L, /* reserved */ 523a90cf9f2SGordon Ross fileinfo->fi_nodeid); 524a90cf9f2SGordon Ross break; 525a90cf9f2SGordon Ross 526a90cf9f2SGordon Ross /* See also: SMB_FIND_FILE_BOTH_DIRECTORY_INFO */ 527a90cf9f2SGordon Ross case FileBothDirectoryInformation: /* 3 */ 528a90cf9f2SGordon Ross bzero(buf83, sizeof (buf83)); 529a90cf9f2SGordon Ross smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags); 530a90cf9f2SGordon Ross if (!smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname)) 531a90cf9f2SGordon Ross shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname); 532a90cf9f2SGordon Ross 533a90cf9f2SGordon Ross rc = smb_mbc_encodef( 534a90cf9f2SGordon Ross &sr->raw_data, "llTTTTqqlllb.24c", 535a90cf9f2SGordon Ross 0, /* NextEntryOffset (set later) */ 536a90cf9f2SGordon Ross resume_key, 537a90cf9f2SGordon Ross &fileinfo->fi_crtime, 538a90cf9f2SGordon Ross &fileinfo->fi_atime, 539a90cf9f2SGordon Ross &fileinfo->fi_mtime, 540a90cf9f2SGordon Ross &fileinfo->fi_ctime, 541a90cf9f2SGordon Ross fileinfo->fi_size, 542a90cf9f2SGordon Ross fileinfo->fi_alloc_size, 543a90cf9f2SGordon Ross fileinfo->fi_dosattr, 544a90cf9f2SGordon Ross namelen, 545a90cf9f2SGordon Ross 0L, /* EaSize */ 546a90cf9f2SGordon Ross shortlen, 547a90cf9f2SGordon Ross buf83); 548a90cf9f2SGordon Ross 549a90cf9f2SGordon Ross smb_msgbuf_term(&mb); 550a90cf9f2SGordon Ross break; 551a90cf9f2SGordon Ross 552a90cf9f2SGordon Ross /* See also: SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO */ 553a90cf9f2SGordon Ross case FileIdBothDirectoryInformation: /* 37 */ 554a90cf9f2SGordon Ross bzero(buf83, sizeof (buf83)); 555a90cf9f2SGordon Ross smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags); 556a90cf9f2SGordon Ross if (!smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname)) 557a90cf9f2SGordon Ross shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname); 558a90cf9f2SGordon Ross 559a90cf9f2SGordon Ross rc = smb_mbc_encodef( 560a90cf9f2SGordon Ross &sr->raw_data, "llTTTTqqlllb.24c..q", 561a90cf9f2SGordon Ross 0, /* NextEntryOffset (set later) */ 562a90cf9f2SGordon Ross resume_key, 563a90cf9f2SGordon Ross &fileinfo->fi_crtime, 564a90cf9f2SGordon Ross &fileinfo->fi_atime, 565a90cf9f2SGordon Ross &fileinfo->fi_mtime, 566a90cf9f2SGordon Ross &fileinfo->fi_ctime, 567a90cf9f2SGordon Ross fileinfo->fi_size, /* q */ 568a90cf9f2SGordon Ross fileinfo->fi_alloc_size, /* q */ 569a90cf9f2SGordon Ross fileinfo->fi_dosattr, /* l */ 570a90cf9f2SGordon Ross namelen, /* l */ 571a90cf9f2SGordon Ross 0L, /* EaSize l */ 572a90cf9f2SGordon Ross shortlen, /* b. */ 573a90cf9f2SGordon Ross buf83, /* 24c */ 574a90cf9f2SGordon Ross /* reserved .. */ 575a90cf9f2SGordon Ross fileinfo->fi_nodeid); /* q */ 576a90cf9f2SGordon Ross 577a90cf9f2SGordon Ross smb_msgbuf_term(&mb); 578a90cf9f2SGordon Ross break; 579a90cf9f2SGordon Ross 580d082c877SGordon Ross /* 581d082c877SGordon Ross * MacOS, when using the AAPL extensions (see smb2_create) 582d082c877SGordon Ross * uses modified directory listing responses where the 583d082c877SGordon Ross * "EA size" field is replaced with "maximum access". 584d082c877SGordon Ross * This avoids the need for MacOS Finder to come back 585d082c877SGordon Ross * N times to get the maximum access for every file. 586d082c877SGordon Ross */ 587d082c877SGordon Ross case FileIdMacOsDirectoryInformation: 588d082c877SGordon Ross rc = smb_mbc_encodef( 589d082c877SGordon Ross &sr->raw_data, "llTTTTqqll", 590d082c877SGordon Ross 0, /* NextEntryOffset (set later) */ 591d082c877SGordon Ross resume_key, /* a.k.a. file index */ 592d082c877SGordon Ross &fileinfo->fi_crtime, 593d082c877SGordon Ross &fileinfo->fi_atime, 594d082c877SGordon Ross &fileinfo->fi_mtime, 595d082c877SGordon Ross &fileinfo->fi_ctime, 596d082c877SGordon Ross fileinfo->fi_size, /* q */ 597d082c877SGordon Ross fileinfo->fi_alloc_size, /* q */ 598d082c877SGordon Ross fileinfo->fi_dosattr, /* l */ 599d082c877SGordon Ross namelen); /* l */ 600d082c877SGordon Ross if (rc != 0) 601d082c877SGordon Ross break; 602d082c877SGordon Ross /* 603d082c877SGordon Ross * This where FileIdMacOsDirectoryInformation 604d082c877SGordon Ross * differs from FileIdBothDirectoryInformation 605d082c877SGordon Ross * Instead of: EaSize, ShortNameLen, ShortName; 606d082c877SGordon Ross * MacOS wants: MaxAccess, ResourceForkSize, and 607d082c877SGordon Ross * 16 bytes of "compressed finder info". 608d082c877SGordon Ross * mi_rforksize + mi_finderinfo falls where 609d082c877SGordon Ross * the 24 byte shortname would normally be. 610d082c877SGordon Ross */ 611d082c877SGordon Ross rc = smb_mbc_encodef( 612d082c877SGordon Ross &sr->raw_data, "l..q16cwq", 613d082c877SGordon Ross macinfo->mi_maxaccess, /* l */ 614d082c877SGordon Ross /* short_name_len, reserved (..) */ 615d082c877SGordon Ross macinfo->mi_rforksize, /* q */ 616d082c877SGordon Ross macinfo->mi_finderinfo, /* 16c */ 617d082c877SGordon Ross macinfo->mi_unixmode, /* w */ 618d082c877SGordon Ross fileinfo->fi_nodeid); /* q */ 619d082c877SGordon Ross break; 620d082c877SGordon Ross 621a90cf9f2SGordon Ross /* See also: SMB_FIND_FILE_NAMES_INFO */ 622a90cf9f2SGordon Ross case FileNamesInformation: /* 12 */ 623a90cf9f2SGordon Ross rc = smb_mbc_encodef( 624a90cf9f2SGordon Ross &sr->raw_data, "lll", 625a90cf9f2SGordon Ross 0, /* NextEntryOffset (set later) */ 626a90cf9f2SGordon Ross resume_key, 627a90cf9f2SGordon Ross namelen); 628a90cf9f2SGordon Ross break; 629a90cf9f2SGordon Ross 630a90cf9f2SGordon Ross default: 631a90cf9f2SGordon Ross return (NT_STATUS_INVALID_INFO_CLASS); 632a90cf9f2SGordon Ross } 633a90cf9f2SGordon Ross if (rc) /* smb_mbc_encodef failed */ 634a90cf9f2SGordon Ross return (NT_STATUS_BUFFER_OVERFLOW); 635a90cf9f2SGordon Ross 636a90cf9f2SGordon Ross /* 637a90cf9f2SGordon Ross * At this point we have written all the fixed-size data 638a90cf9f2SGordon Ross * for the specified info. class. Now put the name and 639a90cf9f2SGordon Ross * alignment padding, and then patch the NextEntryOffset. 640a90cf9f2SGordon Ross * Also store this offset for the caller so they can 641a90cf9f2SGordon Ross * patch this (again) to zero on the very last entry. 642a90cf9f2SGordon Ross */ 643a90cf9f2SGordon Ross rc = smb_mbc_encodef( 644a90cf9f2SGordon Ross &sr->raw_data, "U", 645a90cf9f2SGordon Ross fileinfo->fi_name); 646a90cf9f2SGordon Ross if (rc) 647a90cf9f2SGordon Ross return (NT_STATUS_BUFFER_OVERFLOW); 648a90cf9f2SGordon Ross 649a90cf9f2SGordon Ross /* Next entry needs to be 8-byte aligned. */ 650a90cf9f2SGordon Ross padsz = sr->raw_data.chain_offset & 7; 651a90cf9f2SGordon Ross if (padsz) { 652a90cf9f2SGordon Ross padsz = 8 - padsz; 653a90cf9f2SGordon Ross (void) smb_mbc_encodef(&sr->raw_data, "#.", padsz); 654a90cf9f2SGordon Ross } 655a90cf9f2SGordon Ross next_entry_offset = sr->raw_data.chain_offset - starting_offset; 656a90cf9f2SGordon Ross (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l", 657a90cf9f2SGordon Ross next_entry_offset); 658a90cf9f2SGordon Ross args->fa_last_entry = starting_offset; 659a90cf9f2SGordon Ross 660a90cf9f2SGordon Ross return (0); 661a90cf9f2SGordon Ross } 662