1/* 2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <sys/mount.h> 25#include <sys/vm.h> 26#include <sys/kauth.h> 27#include <sys/smb_apple.h> 28#include <sys/msfscc.h> 29 30#include <netsmb/smb.h> 31#include <netsmb/smb_2.h> 32#include <smbfs/smbfs.h> 33#include <netsmb/smb_rq.h> 34#include <netsmb/smb_rq_2.h> 35#include <netsmb/smb_conn.h> 36#include <smbfs/smbfs_subr.h> 37#include <smbfs/smbfs_subr_2.h> 38#include <smbfs/smbfs_attrlist.h> 39 40#define SMBFS_AVERAGE_NAME_SIZE 22 41#define AVERAGE_SMBDIRENTRY_SIZE (8 + SMBFS_AVERAGE_NAME_SIZE + 4) 42 43/* Packing routines: */ 44static int attrblksize(struct attrlist *attrlist); 45 46static char* mountpointname(struct mount *mp); 47 48static void packattrblk(struct attrblock *abp, 49 struct smbmount *smp, 50 struct vnode *vp, 51 struct smbfs_fctx *ctx, 52 struct vfs_context *context); 53 54static void packcommonattr(struct attrblock *abp, 55 struct smbmount *smp, 56 struct smbfs_fctx *ctx, 57 struct vfs_context *context); 58 59static void packdirattr(struct attrblock *abp, 60 struct vnode *vp, 61 struct smbfs_fctx *ctx); 62 63static void packfileattr(struct attrblock *abp, 64 struct smbmount *smp, 65 struct smbfs_fctx *ctx, 66 struct vfs_context *context); 67 68static void packnameattr(struct attrblock *abp, struct smbfs_fctx *ctx, 69 const u_int8_t *name, size_t namelen); 70 71 72/*==================== Attribute list support routines ====================*/ 73 74/* 75 * Calculate the total size of an attribute block. 76 */ 77static int 78attrblksize(struct attrlist *attrlist) 79{ 80 int size; 81 attrgroup_t a; 82 int sizeof_timespec; 83 boolean_t is_64_bit = proc_is64bit(current_proc()); 84 85 if (is_64_bit) 86 sizeof_timespec = sizeof(struct user64_timespec); 87 else 88 sizeof_timespec = sizeof(struct user32_timespec); 89 90 DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0); 91 92 DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0); 93 94 DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0); 95 96 DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0); 97 98 DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0); 99 100 size = 0; 101 102 if ((a = attrlist->commonattr) != 0) { 103 if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference); 104 if (a & ATTR_CMN_DEVID) size += sizeof(dev_t); 105 if (a & ATTR_CMN_FSID) size += sizeof(fsid_t); 106 if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t); 107 if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t); 108 if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t); 109 if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t); 110 if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t); 111 if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t); 112 if (a & ATTR_CMN_CRTIME) size += sizeof_timespec; 113 if (a & ATTR_CMN_MODTIME) size += sizeof_timespec; 114 if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec; 115 if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec; 116 if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec; 117 if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t); 118 if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t); 119 if (a & ATTR_CMN_GRPID) size += sizeof(gid_t); 120 if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_int32_t); 121 if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t); 122 if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t); 123 if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t); 124 if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t); 125 } 126 if ((a = attrlist->dirattr) != 0) { 127 if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t); 128 if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t); 129 if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t); 130 } 131 if ((a = attrlist->fileattr) != 0) { 132 if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t); 133 if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t); 134 if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t); 135 if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(u_int32_t); 136 if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t); 137 if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t); 138 if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t); 139 if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t); 140 if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t); 141 if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t); 142 } 143 144 return (size); 145} 146 147static char* 148mountpointname(struct mount *mp) 149{ 150 size_t namelength = strlen(vfs_statfs(mp)->f_mntonname); 151 int foundchars = 0; 152 char *c; 153 154 if (namelength == 0) 155 return (NULL); 156 157 /* 158 * Look backwards through the name string, looking for 159 * the first slash encountered (which must precede the 160 * last part of the pathname). 161 */ 162 for (c = vfs_statfs(mp)->f_mntonname + namelength - 1; 163 namelength > 0; --c, --namelength) { 164 if (*c != '/') { 165 foundchars = 1; 166 } else if (foundchars) { 167 return (c + 1); 168 } 169 } 170 171 return (vfs_statfs(mp)->f_mntonname); 172} 173 174/* 175 * Pack cnode attributes into an attribute block. 176 */ 177static void 178packattrblk(struct attrblock *abp, 179 struct smbmount *smp, 180 struct vnode *vp, 181 struct smbfs_fctx *ctx, 182 struct vfs_context *context) 183{ 184 struct attrlist *attrlistp = abp->ab_attrlist; 185 uint32_t is_dir; 186 int error; 187 uint32_t stream_flags = 0; 188 attrgroup_t file_attr = attrlistp->fileattr; 189 attrgroup_t common_attr = attrlistp->commonattr; 190 struct smbnode *np = NULL; 191 uint32_t need_rsrc_fork = 0; 192 size_t afp_size = 0; 193 uint8_t afp_info[60] = {0}; 194 uint8_t zero_finfo[32] = {0}; 195 uio_t afp_uio = NULL; 196 SMBFID fid = 0; 197 struct timespec ts; 198 uint32_t rsrc_fork_from_cache = 0; 199 uint32_t finder_info_from_cache = 0; 200 uint32_t max_access_from_cache = 0; 201 202 /* Do we need Resource Fork info? */ 203 is_dir = (ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY) ? 1 : 0; 204 if ((!is_dir) && 205 ((ATTR_FILE_TOTALSIZE & file_attr) || 206 (ATTR_FILE_ALLOCSIZE & file_attr) || 207 (ATTR_FILE_RSRCLENGTH & file_attr) || 208 (ATTR_FILE_RSRCALLOCSIZE & file_attr))) { 209 need_rsrc_fork = 1; 210 } 211 212 /* 213 * We may already have all the meta data we need from Mac <-> Mac (not yet 214 * implemented) or dont need Resource Fork, Finder Info, or Max Access data. 215 * If we do have all the needed meta data, then just go update vnode caches 216 * if we have a vnode and then pack the return attribute block. 217 */ 218 if (((need_rsrc_fork) && (ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) && 219 ((ATTR_CMN_FNDRINFO & common_attr) && (ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) && 220 ((ATTR_CMN_USERACCESS & common_attr) && (ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID))) { 221 goto update_caches; 222 } 223 224 /* 225 * Figure out what attributes we are missing and then go get them from 226 * either the vnode or from the server. 227 */ 228 229 /* 230 * Do we have a vnode that already has the attributes we need? 231 */ 232 if (vp != NULL) { 233 np = VTOSMB(vp); 234 235 /* Check cached Resource Fork info */ 236 if ((need_rsrc_fork) && 237 !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) { 238 239 lck_mtx_lock(&np->rfrkMetaLock); 240 241 if (np->rfrk_cache_timer != 0) { 242 /* Resource fork data is valid in vnode so use it */ 243 rsrc_fork_from_cache = 1; 244 ctx->f_attr.fa_valid_mask |= FA_RSRC_FORK_VALID; 245 ctx->f_attr.fa_rsrc_size = np->rfrk_size; 246 ctx->f_attr.fa_rsrc_alloc = np->rfrk_alloc_size; 247 } 248 249 lck_mtx_unlock(&np->rfrkMetaLock); 250 } 251 252 /* Check cached Finder Info */ 253 if ((ATTR_CMN_FNDRINFO & common_attr) && 254 !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) { 255 256 if (np->finfo_cache != 0) { 257 /* Finder Info data is valid in vnode so use it */ 258 finder_info_from_cache = 1; 259 ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID; 260 bcopy(&np->finfo, ctx->f_attr.fa_finder_info, sizeof(u_int8_t) * 32); 261 } 262 } 263 264 /* Check cached Max Info */ 265 if ((ATTR_CMN_USERACCESS & common_attr) && 266 !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) { 267 if (timespeccmp(&np->maxAccessRightChTime, &np->n_chtime, ==)) { 268 269 /* Max Access data is valid in vnode so use it */ 270 max_access_from_cache = 1; 271 ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID; 272 ctx->f_attr.fa_max_access = np->maxAccessRights; 273 } 274 } 275 } 276 277 /* Are we still missing attribute information? */ 278 if (((need_rsrc_fork) && !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) || 279 ((ATTR_CMN_FNDRINFO & common_attr) && !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) || 280 ((ATTR_CMN_USERACCESS & common_attr) && !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID))) { 281 /* 282 * This will get us 283 * 1) Resource Fork sizes 284 * 2) Whether Finder Info exists or not on the item 285 * 3) For SMB 2.x, gets the max access on the item 286 * 287 * If we have to ask the server for the resource fork info or the 288 * user access, do it now as this will also tell us if there is any 289 * Finder Info on the file or not. For SMB 2.x, it will also get us the 290 * max access which is used for ATTR_CMN_USERACCESS. 291 * 292 * Best case (SMB 2.x) - Just this one call because no Finder Info found 293 * Worst case (SMB 2.x) - This call and another call to read Finder Info 294 * 295 * Best case (SMB 1.x) - This call and 2 calls (Create/Read + Close) to 296 * read Finder Info which will get the max access 297 * for SMB 1.x 298 * Worst case (SMB 1.x) - This call and 2 calls (Create + Close) to get 299 * max access because there is no Finder Info 300 */ 301 error = smbfs_smb_qstreaminfo(ctx->f_share, ctx->f_dnp, 302 ctx->f_LocalName, ctx->f_LocalNameLen, 303 SFM_RESOURCEFORK_NAME, 304 NULL, NULL, 305 &ctx->f_attr.fa_rsrc_size, &ctx->f_attr.fa_rsrc_alloc, 306 &stream_flags, &ctx->f_attr.fa_max_access, 307 context); 308 309 if ((!error) || (error == ENOATTR)) { 310 /* smbfs_smb_qstreaminfo worked */ 311 312 /* Did we need Resource Fork info? */ 313 if ((need_rsrc_fork) && 314 !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) { 315 /* 316 * Successfully got Resource Fork Info. Its either already in 317 * f_attr or no Resource Fork was found 318 */ 319 ctx->f_attr.fa_valid_mask |= FA_RSRC_FORK_VALID; 320 321 if (stream_flags & SMB_NO_RESOURCE_FORK) { 322 /* No Resource Fork, so set resource fork lengths to zero */ 323 ctx->f_attr.fa_rsrc_size = 0; 324 ctx->f_attr.fa_rsrc_alloc = 0; 325 } 326 else { 327 /* SMBDEBUG("%s rsrc fork from qstreaminfo\n", ctx->f_LocalName); */ 328 } 329 } 330 331 /* Did we need Finder Info? */ 332 if ((ATTR_CMN_FNDRINFO & common_attr) && 333 !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) { 334 /* 335 * Now we know if there is Finder Info or not on the item 336 */ 337 if (stream_flags & SMB_NO_FINDER_INFO) { 338 /* No Finder Info, so set Finder Info to all zeros */ 339 ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID; 340 bcopy(&zero_finfo, ctx->f_attr.fa_finder_info, 341 sizeof(u_int8_t) * 32); 342 } 343 } 344 345 /* Did we need Max Access? */ 346 if ((ATTR_CMN_USERACCESS & common_attr) && 347 !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) { 348 if (SSTOVC(ctx->f_share)->vc_flags & SMBV_SMB2) { 349 /* 350 * Only SMB 2.x can get max access from 351 * smbfs_smb_qstreaminfo call 352 */ 353 ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID; 354 } 355 } 356 } 357 else { 358 /* Got some sort of error. This shouldn't happen */ 359 SMBDEBUG("smbfs_smb_qstreaminfo failed %d for %s \n", 360 error, ctx->f_LocalName); 361 } 362 } 363 364 /* 365 * Do we still need to get the Finder Info? At this point we know 366 * (1) Either no vnode or the cached finder info is not present 367 * (2) smbfs_smb_qstreaminfo told us that there is Finder Info on the item 368 * 369 * <11615553> For SMB 1.x, we can NOT get both the Finder Info and max 370 * access at the same time. Windows based servers will often give 371 * "Unspecified error" when you do a CreateAndX with extended response (ie 372 * max access) combined with Read of a named stream. 373 */ 374 if ((ATTR_CMN_FNDRINFO & common_attr) && 375 !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) { 376 377 do { 378 afp_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); 379 if (afp_uio == NULL) { 380 SMBERROR("uio_create failed for %s \n", ctx->f_LocalName); 381 break; 382 } 383 384 error = uio_addiov(afp_uio, CAST_USER_ADDR_T(afp_info), 385 sizeof(afp_info)); 386 if (error) { 387 SMBERROR("uio_addiov failed for %s \n", ctx->f_LocalName); 388 break; 389 } 390 391 uio_setoffset(afp_uio, 0); 392 393 /* Open/Read/Close the Finder Info */ 394 error = smbfs_smb_cmpd_create_read_close(ctx->f_share, ctx->f_dnp, 395 ctx->f_LocalName, ctx->f_LocalNameLen, 396 SFM_FINDERINFO_NAME, strlen(SFM_FINDERINFO_NAME), 397 afp_uio, &afp_size, 398 NULL, 399 context); 400 if (!error) { 401 /* Successfully got Finder Info */ 402 ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID; 403 404 /* Verify returned size */ 405 if (afp_size != AFP_INFO_SIZE) { 406 /* Could be a 0 size returned meaning no Finder Info */ 407 if (afp_size != 0) { 408 /* SMBDEBUG("%s Finder Info size mismatch %ld != %d \n", 409 ctx->f_LocalName, afp_size, AFP_INFO_SIZE); */ 410 } 411 bcopy(&zero_finfo, ctx->f_attr.fa_finder_info, 412 sizeof(u_int8_t) * 32); 413 } 414 else { 415 /* Correct size, so just copy it in */ 416 bcopy(&afp_info[AFP_INFO_FINDER_OFFSET], 417 ctx->f_attr.fa_finder_info, 418 sizeof(u_int8_t) * 32); 419 /* SMBDEBUG("Finder Info 0x%x 0x%x 0x%x 0x%x for %s \n", 420 afp_info[AFP_INFO_FINDER_OFFSET], 421 afp_info[AFP_INFO_FINDER_OFFSET + 1], 422 afp_info[AFP_INFO_FINDER_OFFSET + 2], 423 afp_info[AFP_INFO_FINDER_OFFSET + 3], 424 ctx->f_LocalName); */ 425 } 426 } 427 else { 428 if (error != ENOENT) { 429 SMBDEBUG("smbfs_smb_cmpd_create_read_close failed %d for %s \n", 430 error, ctx->f_LocalName); 431 } 432 } 433 434 if (afp_uio) { 435 uio_free(afp_uio); 436 } 437 } while (0); 438 } 439 440 /* Do we still need Max Access and its SMB 1.x? */ 441 if ((ATTR_CMN_USERACCESS & common_attr) && 442 !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID) && 443 !(SSTOVC(ctx->f_share)->vc_flags & SMBV_SMB2)) 444 { 445 error = smb1fs_smb_open_maxaccess(ctx->f_share, ctx->f_dnp, 446 ctx->f_LocalName, ctx->f_LocalNameLen, 447 &fid, &ctx->f_attr.fa_max_access, 448 context); 449 if (!error) { 450 ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID; 451 } 452 453 if (fid) { 454 (void)smbfs_smb_close(ctx->f_share, fid, context); 455 } 456 } 457 458update_caches: 459 460 /* Update vnodes caches if the data did not come from the vnode caches */ 461 if (vp) { 462 np = VTOSMB(vp); 463 464 /* Do we have updated resource fork info? */ 465 if ((rsrc_fork_from_cache == 0) && 466 (need_rsrc_fork) && 467 (ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) { 468 469 lck_mtx_lock(&np->rfrkMetaLock); 470 np->rfrk_size = ctx->f_attr.fa_rsrc_size; 471 np->rfrk_alloc_size = ctx->f_attr.fa_rsrc_alloc; 472 nanouptime(&ts); 473 np->rfrk_cache_timer = ts.tv_sec; 474 lck_mtx_unlock(&np->rfrkMetaLock); 475 } 476 477 /* Do we have updated Finder Info? */ 478 if ((finder_info_from_cache == 0) && 479 (ATTR_CMN_FNDRINFO & common_attr) && 480 (ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) { 481 482 bcopy(ctx->f_attr.fa_finder_info, &np->finfo, 483 sizeof(u_int8_t) * 32); 484 nanouptime(&ts); 485 np->finfo_cache = ts.tv_sec; 486 } 487 488 /* Do we have updated Max Access? */ 489 if ((max_access_from_cache == 0) && 490 (ATTR_CMN_USERACCESS & common_attr) && 491 (ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) { 492 493 np->maxAccessRights = ctx->f_attr.fa_max_access; 494 np->maxAccessRightChTime = ctx->f_attr.fa_chtime; 495 } 496 } 497 498 /* 499 * Error Handling. At this point, we should have valid information and if 500 * we do not, then some earlier error must have occurred, so fill in with 501 * default values. 502 */ 503 if ((need_rsrc_fork) && !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) { 504 /* Assume resource fork lengths of zero */ 505 ctx->f_attr.fa_valid_mask |= FA_RSRC_FORK_VALID; 506 ctx->f_attr.fa_rsrc_size = 0; 507 ctx->f_attr.fa_rsrc_alloc = 0; 508 } 509 510 if ((ATTR_CMN_FNDRINFO & common_attr) && 511 !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) { 512 /* Assume zero Finder Info */ 513 ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID; 514 bcopy(&zero_finfo, ctx->f_attr.fa_finder_info, sizeof(u_int8_t) * 32); 515 } 516 517 if ((ATTR_CMN_USERACCESS & common_attr) && 518 !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) { 519 /* Assume full access */ 520 ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID; 521 ctx->f_attr.fa_max_access = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS; 522 } 523 524 /* 525 * At this point we should have all the meta data we need to fill out the 526 * return attribute block. 527 */ 528 if (attrlistp->commonattr) { 529 packcommonattr(abp, smp, ctx, context); 530 } 531 532 if (attrlistp->dirattr && is_dir) { 533 packdirattr(abp, vp, ctx); 534 } 535 536 if (attrlistp->fileattr && !is_dir) { 537 packfileattr(abp, smp, ctx, context); 538 } 539} 540 541static void 542packcommonattr(struct attrblock *abp, 543 struct smbmount *smp, 544 struct smbfs_fctx *ctx, 545 struct vfs_context *context) 546{ 547 attrgroup_t attr = abp->ab_attrlist->commonattr; 548 struct mount *mp = smp->sm_mp; 549 void *attrbufptr = *abp->ab_attrbufpp; 550 void *varbufptr = *abp->ab_varbufpp; 551 boolean_t is_64_bit = proc_is64bit(vfs_context_proc(context)); 552 uid_t cuid = 1; 553 int isroot = 0; 554 struct timespec temp_time; 555 uid_t uid = KAUTH_UID_NONE; 556 gid_t gid = KAUTH_GID_NONE; 557 mode_t mode = 0; 558 uint32_t flags = 0; 559 uint32_t cmn_user_rights = 0; 560 uint32_t ino; 561 562 /* Calculate uid, gid and mode from Query Dir results */ 563 if (ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY) { 564 flags |= SMBFS_GET_UGM_IS_DIR; 565 } 566 567 /* Get the uid, gid and mode */ 568 smb_get_uid_gid_mode(ctx->f_share, smp, 569 &ctx->f_attr, flags, 570 &uid, &gid, &mode); 571 572 /* This is used later in this function... */ 573 if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) { 574 cuid = kauth_cred_getuid(vfs_context_ucred(context)); 575 isroot = cuid == 0; 576 } 577 578 if (ATTR_CMN_NAME & attr) { 579 packnameattr(abp, ctx, (const u_int8_t *) ctx->f_LocalName, 580 ctx->f_LocalNameLen); 581 582 attrbufptr = *abp->ab_attrbufpp; 583 varbufptr = *abp->ab_varbufpp; 584 } 585 586 if (ATTR_CMN_DEVID & attr) { 587 /* Copy AFP Client behavior */ 588 *((dev_t *)attrbufptr) = vfs_statfs(mp)->f_fsid.val[0]; 589 attrbufptr = ((dev_t *)attrbufptr) + 1; 590 } 591 592 if (ATTR_CMN_FSID & attr) { 593 /* Copy AFP Client behavior */ 594 *((fsid_t *)attrbufptr) = vfs_statfs(mp)->f_fsid; 595 attrbufptr = ((fsid_t *)attrbufptr) + 1; 596 } 597 598 if (ATTR_CMN_OBJTYPE & attr) { 599 /* 600 * Because of the Steve/Conrad Symlinks we can never be completely 601 * sure that we have the correct vnode type if its a file. Since we 602 * don't support Steve/Conrad Symlinks with Darwin we can always count 603 * on the vtype being correct. For directories we always know the 604 * correct information. 605 */ 606 *((fsobj_type_t *)attrbufptr) = ctx->f_attr.fa_vtype; 607 608 attrbufptr = ((fsobj_type_t *)attrbufptr) + 1; 609 } 610 611 if (ATTR_CMN_OBJTAG & attr) { 612 *((fsobj_tag_t *)attrbufptr) = VT_CIFS; 613 attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1; 614 } 615 616 /* 617 * Exporting file IDs from HFS Plus: 618 * 619 * For "normal" files the c_fileid is the same value as the 620 * c_cnid. But for hard link files, they are different - the 621 * c_cnid belongs to the active directory entry (ie the link) 622 * and the c_fileid is for the actual inode (ie the data file). 623 * 624 * The stat call (getattr) will always return the c_fileid 625 * and Carbon APIs, which are hardlink-ignorant, will always 626 * receive the c_cnid (from getattrlist). 627 */ 628 if (ATTR_CMN_OBJID & attr) { 629 /* 630 * VOL_CAP_FMT_64BIT_OBJECT_IDS is set so this value is undefined 631 */ 632 ino = (uint32_t) smb2fs_smb_file_id_get(smp, ctx->f_attr.fa_ino, 633 ctx->f_LocalName); 634 635 ((fsobj_id_t *)attrbufptr)->fid_objno = ino; 636 ((fsobj_id_t *)attrbufptr)->fid_generation = 0; 637 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; 638 } 639 640 if (ATTR_CMN_OBJPERMANENTID & attr) { 641 /* 642 * VOL_CAP_FMT_64BIT_OBJECT_IDS is set so this value is undefined 643 */ 644 ino = (uint32_t) smb2fs_smb_file_id_get(smp, ctx->f_attr.fa_ino, 645 ctx->f_LocalName); 646 647 ((fsobj_id_t *)attrbufptr)->fid_objno = ino; 648 ((fsobj_id_t *)attrbufptr)->fid_generation = 0; 649 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; 650 } 651 652 if (ATTR_CMN_PAROBJID & attr) { 653 /* 654 * VOL_CAP_FMT_64BIT_OBJECT_IDS is set so this value is undefined 655 */ 656 ino = (uint32_t) smb2fs_smb_file_id_get(smp, ctx->f_dnp->n_ino, 657 ctx->f_dnp->n_name); 658 659 ((fsobj_id_t *)attrbufptr)->fid_objno = ino; 660 ((fsobj_id_t *)attrbufptr)->fid_generation = 0; 661 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; 662 } 663 664 if (ATTR_CMN_SCRIPT & attr) { 665 /* %%% TO DO: What should this be??? */ 666 *((text_encoding_t *)attrbufptr) = 0; /* kTextEncodingMacRoman */ 667 attrbufptr = ((text_encoding_t *)attrbufptr) + 1; 668 } 669 670 if (ATTR_CMN_CRTIME & attr) { 671 temp_time = ctx->f_attr.fa_crtime; 672 673 if (is_64_bit) { 674 ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec; 675 ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec; 676 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; 677 } 678 else { 679 /* Seems like getattr just slams in uint64_t into uint32_t fields */ 680 ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec; 681 ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec; 682 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; 683 } 684 } 685 686 if (ATTR_CMN_MODTIME & attr) { 687 temp_time = ctx->f_attr.fa_mtime; 688 689 if (is_64_bit) { 690 ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec; 691 ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec; 692 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; 693 } 694 else { 695 /* Seems like getattr just slams in uint64_t into uint32_t fields */ 696 ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec; 697 ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec; 698 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; 699 } 700 } 701 702 if (ATTR_CMN_CHGTIME & attr) { 703 temp_time = ctx->f_attr.fa_chtime; 704 705 if (is_64_bit) { 706 ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec; 707 ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec; 708 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; 709 } 710 else { 711 /* Seems like getattr just slams in uint64_t into uint32_t fields */ 712 ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec; 713 ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec; 714 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; 715 } 716 } 717 718 if (ATTR_CMN_ACCTIME & attr) { 719 temp_time = ctx->f_attr.fa_atime; 720 721 if (is_64_bit) { 722 ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec; 723 ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec; 724 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; 725 } 726 else { 727 /* Seems like getattr just slams in uint64_t into uint32_t fields */ 728 ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec; 729 ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec; 730 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; 731 } 732 } 733 734 if (ATTR_CMN_BKUPTIME & attr) { 735 /* Backup time not supported so return 0 */ 736 if (is_64_bit) { 737 ((struct user64_timespec *)attrbufptr)->tv_sec = 0; 738 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0; 739 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; 740 } 741 else { 742 ((struct user32_timespec *)attrbufptr)->tv_sec = 0; 743 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0; 744 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; 745 } 746 } 747 748 if (ATTR_CMN_FNDRINFO & attr) { 749 DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID); 750 bcopy(ctx->f_attr.fa_finder_info, attrbufptr, sizeof(u_int8_t) * 32); 751 attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32; 752 } 753 754 if (ATTR_CMN_OWNERID & attr) { 755 uid_t nuid; 756 757 if (SMBV_HAS_GUEST_ACCESS(SSTOVC(ctx->f_share))) { 758 nuid = UNKNOWNUID; 759 } 760 else { 761 /* 762 * For servers that support the UNIX extensions we know the uid/gid. 763 * For server that don't support ACLs then the node uid/gid will be 764 * set to the mounted user's uid/gid. For all other servers we need 765 * to get the ACL and translate the SID to a uid or gid. The uid/gid 766 * really is for display purpose only and means nothing to us. We will 767 * set the nodes ids if we get a request for the ACL, but otherwise 768 * we leave them unset for performance reasons. 769 */ 770 if (ctx->f_attr.fa_uid == KAUTH_UID_NONE) { 771 nuid = smp->sm_args.uid; 772 } 773 else { 774 nuid = uid; 775 } 776 } 777 778 if (!isroot) { 779 if (((unsigned int)vfs_flags(smp->sm_mp)) & MNT_UNKNOWNPERMISSIONS) 780 nuid = cuid; 781 else if (nuid == UNKNOWNUID) 782 nuid = cuid; 783 } 784 785 *((uid_t *)attrbufptr) = nuid; 786 attrbufptr = ((uid_t *)attrbufptr) + 1; 787 } 788 789 if (ATTR_CMN_GRPID & attr) { 790 gid_t ngid; 791 792 if (SMBV_HAS_GUEST_ACCESS(SSTOVC(ctx->f_share))) { 793 ngid = UNKNOWNGID; 794 } 795 else { 796 /* 797 * For servers that support the UNIX extensions we know the uid/gid. 798 * For server that don't support ACLs then the node uid/gid will be 799 * set to the mounted user's uid/gid. For all other servers we need 800 * to get the ACL and translate the SID to a uid or gid. The uid/gid 801 * really is for display purpose only and means nothing to us. We will 802 * set the nodes ids if we get a request for the ACL, but otherwise 803 * we leave them unset for performance reasons. 804 */ 805 if (ctx->f_attr.fa_gid == KAUTH_GID_NONE) { 806 ngid = smp->sm_args.gid; 807 } 808 else { 809 ngid = gid; 810 } 811 } 812 813 if (!isroot) { 814 gid_t cgid = kauth_cred_getgid(vfs_context_ucred(context)); 815 if (((unsigned int)vfs_flags(smp->sm_mp)) & MNT_UNKNOWNPERMISSIONS) 816 ngid = cgid; 817 else if (ngid == UNKNOWNUID) 818 ngid = cgid; 819 } 820 821 *((gid_t *)attrbufptr) = ngid; 822 attrbufptr = ((gid_t *)attrbufptr) + 1; 823 } 824 825 if (ATTR_CMN_ACCESSMASK & attr) { 826 if (ctx->f_attr.fa_vtype == VDIR) { 827 *((u_int32_t *)attrbufptr) = (S_IFDIR | mode); 828 } 829 830 if (ctx->f_attr.fa_vtype == VREG) { 831 *((u_int32_t *)attrbufptr) = (S_IFREG | mode); 832 } 833 834 if (ctx->f_attr.fa_vtype == VLNK) { 835 *((u_int32_t *)attrbufptr) = (S_IFLNK | mode); 836 } 837 838 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 839 } 840 841 if (ATTR_CMN_FLAGS & attr) { 842 uint32_t va_flags = 0; 843 844 if (ctx->f_attr.fa_attr & SMB_EFA_HIDDEN) { 845 /* 846 * Dont have to special case whether root vnode is hidden or not. 847 * root volume doesn't show up in a readdirattr, I think? 848 */ 849 va_flags |= UF_HIDDEN; 850 } 851 852 if (ctx->f_attr.fa_attr & SMB_EFA_ARCHIVE) { 853 va_flags |= SF_ARCHIVED; 854 } 855 856 if (ctx->f_attr.fa_attr & SMB_EFA_RDONLY) { 857 va_flags |= UF_IMMUTABLE; 858 } 859 860 *((u_int32_t *)attrbufptr) = va_flags; 861 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 862 } 863 864 if (ATTR_CMN_USERACCESS & attr) { 865 /* 866 * The effective permissions for the current user which we derive 867 * from the max access 868 */ 869 DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID); 870 871 if (ctx->f_attr.fa_max_access & SMB2_FILE_READ_DATA) { 872 cmn_user_rights |= R_OK; 873 } 874 875 if (ctx->f_attr.fa_max_access & SMB2_FILE_WRITE_DATA) { 876 cmn_user_rights |= W_OK; 877 } 878 879 if (ctx->f_attr.fa_max_access & SMB2_FILE_EXECUTE) { 880 cmn_user_rights |= X_OK; 881 } 882 883 *((u_int32_t *)attrbufptr) = cmn_user_rights; 884 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 885 } 886 887 if (ATTR_CMN_FILEID & attr) { 888 *((u_int64_t *)attrbufptr) = smb2fs_smb_file_id_get(smp, 889 ctx->f_attr.fa_ino, 890 ctx->f_LocalName); 891 attrbufptr = ((u_int64_t *)attrbufptr) + 1; 892 } 893 894 if (ATTR_CMN_PARENTID & attr) { 895 *((u_int64_t *)attrbufptr) = smb2fs_smb_file_id_get(smp, 896 ctx->f_dnp->n_ino, 897 ctx->f_dnp->n_name); 898 attrbufptr = ((u_int64_t *)attrbufptr) + 1; 899 } 900 901 *abp->ab_attrbufpp = attrbufptr; 902 *abp->ab_varbufpp = varbufptr; 903} 904 905static void 906packdirattr(struct attrblock *abp, 907 struct vnode *vp, 908 struct smbfs_fctx *ctx) 909{ 910 attrgroup_t attr = abp->ab_attrlist->dirattr; 911 void *attrbufptr = *abp->ab_attrbufpp; 912 u_int32_t entries; 913 uint32_t mnt_status; 914 915 /* 916 * The DIR_LINKCOUNT is the count of real directory hard links. 917 * (i.e. its not the sum of the implied "." and ".." references 918 * typically used in stat's st_nlink field) 919 */ 920 if (ATTR_DIR_LINKCOUNT & attr) { 921 /* There ARE no hard links, at least not yet... */ 922 *((u_int32_t *)attrbufptr) = 1; 923 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 924 } 925 926 if (ATTR_DIR_ENTRYCOUNT & attr) { 927 /* Apparently 0 is a fine answer to return for a filesystem */ 928 entries = 0; 929 930 *((u_int32_t *)attrbufptr) = entries; 931 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 932 } 933 934 if (ATTR_DIR_MOUNTSTATUS & attr) { 935 mnt_status = 0; 936 937 /* if its a DFS reparse point, then its a mnt trigger */ 938 if ((ctx->f_attr.fa_attr & SMB_EFA_REPARSE_POINT) && 939 (ctx->f_attr.fa_reparse_tag == IO_REPARSE_TAG_DFS)) { 940 mnt_status |= DIR_MNTSTATUS_TRIGGER; 941 } 942 943 /* If we have a vnode, check to see if its already mounted on */ 944 if ((vp != NULL) && vnode_mountedhere(vp)) { 945 mnt_status |= DIR_MNTSTATUS_MNTPOINT; 946 } 947 948 *((u_int32_t *)attrbufptr) = mnt_status; 949 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 950 } 951 952 *abp->ab_attrbufpp = attrbufptr; 953} 954 955static void 956packfileattr(struct attrblock *abp, 957 struct smbmount *smp, 958 struct smbfs_fctx *ctx, 959 struct vfs_context *context) 960{ 961#pragma unused(context) 962 attrgroup_t attr = abp->ab_attrlist->fileattr; 963 void *attrbufptr = *abp->ab_attrbufpp; 964 void *varbufptr = *abp->ab_varbufpp; 965 uint64_t data_fork_size, data_fork_alloc; 966 967 /* Use values from ctx */ 968 data_fork_size = ctx->f_attr.fa_size; 969 data_fork_alloc = ctx->f_attr.fa_data_alloc; 970 971 if (ATTR_FILE_LINKCOUNT & attr) { 972 /* There ARE no hard links, at least not yet... */ 973 *((u_int32_t *)attrbufptr) = 1; 974 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 975 } 976 977 if (ATTR_FILE_TOTALSIZE & attr) { 978 /* Return same value as smbfs_vnop_getattr */ 979 DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID); 980 981 *((off_t *)attrbufptr) = data_fork_size + ctx->f_attr.fa_rsrc_size; 982 attrbufptr = ((off_t *)attrbufptr) + 1; 983 } 984 985 if (ATTR_FILE_ALLOCSIZE & attr) { 986 DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID); 987 988 /* Should already be rounded up */ 989 *((off_t *)attrbufptr) = data_fork_alloc + ctx->f_attr.fa_rsrc_alloc; 990 attrbufptr = ((off_t *)attrbufptr) + 1; 991 } 992 993 if (ATTR_FILE_IOBLOCKSIZE & attr) { 994 *((u_int32_t *)attrbufptr) = smp->sm_statfsbuf.f_bsize; 995 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 996 } 997 998 if (ATTR_FILE_CLUMPSIZE & attr) { 999 /* This attribute is obsolete so return 0 */ 1000 *((u_int32_t *)attrbufptr) = 0; 1001 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 1002 } 1003 1004 if (ATTR_FILE_DEVTYPE & attr) { 1005 /* Copy AFP Client behavior */ 1006 *((u_int32_t *)attrbufptr) = 0; 1007 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 1008 } 1009 1010 if (ATTR_FILE_DATALENGTH & attr) { 1011 *((off_t *)attrbufptr) = data_fork_size; 1012 attrbufptr = ((off_t *)attrbufptr) + 1; 1013 } 1014 1015 if (ATTR_FILE_DATAALLOCSIZE & attr) { 1016 *((off_t *)attrbufptr) = data_fork_alloc; 1017 attrbufptr = ((off_t *)attrbufptr) + 1; 1018 } 1019 1020 if (ATTR_FILE_RSRCLENGTH & attr) { 1021 DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID); 1022 1023 *((off_t *)attrbufptr) = ctx->f_attr.fa_rsrc_size; 1024 attrbufptr = ((off_t *)attrbufptr) + 1; 1025 } 1026 1027 if (ATTR_FILE_RSRCALLOCSIZE & attr) { 1028 DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID); 1029 1030 *((off_t *)attrbufptr) = ctx->f_attr.fa_rsrc_alloc; 1031 attrbufptr = ((off_t *)attrbufptr) + 1; 1032 } 1033 1034 *abp->ab_attrbufpp = attrbufptr; 1035 *abp->ab_varbufpp = varbufptr; 1036} 1037 1038static void 1039packnameattr(struct attrblock *abp, struct smbfs_fctx *ctx, 1040 const u_int8_t *name, size_t namelen) 1041{ 1042 void *varbufptr; 1043 struct attrreference * attr_refptr; 1044 char *mpname; 1045 size_t mpnamelen; 1046 u_int32_t attrlength; 1047 u_int8_t empty = 0; 1048 struct smbmount *smp; 1049 1050 /* A cnode's name may be incorrect for the root of a mounted 1051 * filesystem (it can be mounted on a different directory name 1052 * than the name of the volume, such as "blah-1"). So for the 1053 * root directory, it's best to return the last element of the 1054 location where the volume's mounted: 1055 */ 1056 1057 smp = ctx->f_share->ss_mount; 1058 if ((ctx->f_attr.fa_ino == smp->sm_root_ino) && 1059 (mpname = mountpointname(vnode_mount(ctx->f_dnp->n_vnode)))) { 1060 mpnamelen = strlen(mpname); 1061 1062 /* Trim off any trailing slashes: */ 1063 while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) 1064 --mpnamelen; 1065 1066 /* If there's anything left, use it instead of the volume's name */ 1067 if (mpnamelen > 0) { 1068 name = (u_int8_t *)mpname; 1069 namelen = mpnamelen; 1070 } 1071 } 1072 if (name == NULL) { 1073 name = ∅ 1074 namelen = 0; 1075 } 1076 1077 varbufptr = *abp->ab_varbufpp; 1078 attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp); 1079 1080 attrlength = (uint32_t) (namelen + 1); 1081 attr_refptr->attr_dataoffset = (uint32_t) ((char *)varbufptr - (char *)attr_refptr); 1082 attr_refptr->attr_length = attrlength; 1083 (void) strncpy((char *)varbufptr, (const char *) name, attrlength); 1084 /* 1085 * Advance beyond the space just allocated and 1086 * round up to the next 4-byte boundary: 1087 */ 1088 varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3); 1089 ++attr_refptr; 1090 1091 *abp->ab_attrbufpp = attr_refptr; 1092 *abp->ab_varbufpp = varbufptr; 1093} 1094 1095/* 1096 * readdirattr operation will return attributes for the items in the 1097 * directory specified. 1098 * 1099 * It does not do . and .. entries. The problem is if you are at the root of the 1100 * smbfs directory and go to .. you could be crossing a mountpoint into a 1101 * different (ufs) file system. The attributes that apply for it may not 1102 * apply for the file system you are doing the readdirattr on. To make life 1103 * simpler, this call will only return entries in its directory, hfs like. 1104 */ 1105int 1106smbfs_vnop_readdirattr(ap) 1107struct vnop_readdirattr_args /* { 1108 struct vnode *a_vp; 1109 struct attrlist *a_alist; 1110 struct uio *a_uio; 1111 u_long a_maxcount; 1112 u_long a_options; 1113 u_long *a_newstate; 1114 int *a_eofflag; 1115 u_long *a_actualcount; 1116 vfs_context_t a_context; 1117 } */ *ap; 1118{ 1119 struct vnode *vp = NULL; 1120 struct vnode *dvp = ap->a_vp; 1121 struct attrlist *alist = ap->a_alist; 1122 uio_t uio = ap->a_uio; 1123 int maxcount = ap->a_maxcount; 1124 u_int32_t fixedblocksize; 1125 u_int32_t maxattrblocksize; 1126 u_int32_t currattrbufsize; 1127 void *attrbufptr = NULL; 1128 void *attrptr; 1129 void *varptr; 1130 struct attrblock attrblk; 1131 vfs_context_t context = ap->a_context; 1132 struct smbnode *dnp = NULL; 1133 struct smbfs_fctx *ctx; 1134 off_t offset; 1135 int error = 0; 1136 struct smb_share *share = NULL; 1137 struct smbmount *smp = NULL; 1138 1139 *(ap->a_actualcount) = 0; 1140 *(ap->a_eofflag) = 0; 1141 1142 /* Check for invalid options and buffer space. */ 1143 if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) || 1144 (uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1) || (maxcount <= 0)) { 1145 SMBDEBUG("Invalid options or buf size\n"); 1146 return (EINVAL); 1147 } 1148 1149 /* 1150 * Reject requests for unsupported attributes. 1151 */ 1152 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) || 1153 (alist->commonattr & ~SMBFS_ATTR_CMN_VALID) || 1154 (alist->volattr != 0) || 1155 (alist->dirattr & ~SMBFS_ATTR_DIR_VALID) || 1156 (alist->fileattr & ~SMBFS_ATTR_FILE_VALID) || 1157 (alist->forkattr != 0)) { 1158 SMBDEBUG("Unsupported attributes\n"); 1159 return (EINVAL); 1160 } 1161 1162 /* 1163 * Lock parent dir that we are enumerating 1164 */ 1165 if ((error = smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK))) 1166 return (error); 1167 VTOSMB(dvp)->n_lastvop = smbfs_vnop_readdirattr; 1168 1169 dnp = VTOSMB(dvp); 1170 smp = VTOSMBFS(dvp); 1171 1172 /* 1173 * Do we need to start or restart the directory listing 1174 * 1175 * The uio_offset is actually just used to store whatever we want. In HFS, 1176 * they store an index and a dir tag. For SMB, we will store just the offset 1177 */ 1178 offset = uio_offset(uio); 1179 1180 /* Get Share reference */ 1181 share = smb_get_share_with_reference(VTOSMBFS(dvp)); 1182 if (!dnp->d_fctx || (dnp->d_fctx->f_share != share) || (offset == 0) || 1183 (offset != dnp->d_offset)) { 1184 smbfs_closedirlookup(dnp, context); 1185 error = smbfs_smb_findopen(share, dnp, "*", 1, &dnp->d_fctx, TRUE, 1186 context); 1187 } 1188 1189 /* 1190 * The directory fctx keeps a reference on the share so we can release our 1191 * reference on the share now. 1192 */ 1193 smb_share_rele(share, context); 1194 1195 if (error) { 1196 SMBERROR("Can't open search for %s, error = %d", dnp->n_name, error); 1197 goto done; 1198 } 1199 ctx = dnp->d_fctx; 1200 1201 /* 1202 * Get a buffer to hold packed attributes. 1203 */ 1204 fixedblocksize = (sizeof(u_int32_t) + attrblksize(alist)); /* 4 bytes for length */ 1205 maxattrblocksize = fixedblocksize; 1206 if (alist->commonattr & ATTR_CMN_NAME) 1207 maxattrblocksize += (share->ss_maxfilenamelen * 3) + 1; 1208 1209 MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK); 1210 if (attrbufptr == NULL) { 1211 error = ENOMEM; 1212 goto done; 1213 } 1214 attrptr = attrbufptr; 1215 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */ 1216 1217 /* 1218 * They are continuing from some point ahead of us in the buffer. Skip all 1219 * entries until we reach their point in the buffer. 1220 */ 1221 while (dnp->d_offset < offset) { 1222 error = smbfs_findnext(ctx, context); 1223 if (error) { 1224 smbfs_closedirlookup(dnp, context); 1225 goto done; 1226 } 1227 dnp->d_offset++; 1228 } 1229 1230 /* Loop until we end the search or we don't have enough room for the max element */ 1231 while (uio_resid(uio)) { 1232 /* Get one entry out of the buffer and fill in ctx with its info */ 1233 error = smbfs_findnext(ctx, context); 1234 if (error) { 1235 break; 1236 } 1237 1238 /* 1239 * <14430881> If file IDs are supported by this server, skip any 1240 * child that has the same id as the current parent that we are 1241 * enumerating. Seems like snapshot dirs have the same id as the parent 1242 * and that will cause us to deadlock when we find the vnode with same 1243 * id and then try to lock it again (deadlock on parent id). 1244 */ 1245 if (SSTOVC(share)->vc_misc_flags & SMBV_HAS_FILEIDS) { 1246 if (ctx->f_attr.fa_ino == dnp->n_ino) { 1247 SMBDEBUG("Skipping <%s> as it has same ID as parent\n", 1248 ctx->f_LocalName); 1249 continue; 1250 } 1251 } 1252 1253 /* 1254 * Check to see if vnode already exists. If so, then can pull the data 1255 * from the inode instead of having to poll the server for missing info 1256 */ 1257 error = smbfs_nget(share, vnode_mount(dvp), 1258 dvp, ctx->f_LocalName, ctx->f_LocalNameLen, 1259 &ctx->f_attr, &vp, 1260 0, SMBFS_NGET_LOOKUP_ONLY, 1261 ap->a_context); 1262 /* dont care if we got an error or not, just whether vp == NULL or not */ 1263 if ((error == 0) && (vp != NULL)) { 1264 /* 1265 * Enumerates alway return the correct case of the name. 1266 * Update the name and parent if needed. 1267 */ 1268 smbfs_update_name_par(ctx->f_share, dvp, vp, 1269 &ctx->f_attr.fa_reqtime, 1270 ctx->f_LocalName, ctx->f_LocalNameLen); 1271 } 1272 1273 *((u_int32_t *)attrptr) = 0; 1274 attrptr = ((u_int32_t *)attrptr) + 1; 1275 attrblk.ab_attrlist = alist; 1276 attrblk.ab_attrbufpp = &attrptr; 1277 attrblk.ab_varbufpp = &varptr; 1278 attrblk.ab_flags = 0; 1279 attrblk.ab_blocksize = maxattrblocksize; 1280 attrblk.ab_context = ap->a_context; 1281 1282 /* 1283 * Pack catalog entries into attribute buffer. 1284 */ 1285 packattrblk(&attrblk, smp, vp, ctx, context); 1286 currattrbufsize = (uint32_t) ((char *)varptr - (char *)attrbufptr); 1287 1288 /* Done with the vp */ 1289 if (vp != NULL) { 1290 smbnode_unlock(VTOSMB(vp)); 1291 vnode_put(vp); 1292 } 1293 1294 /* 1295 * Make sure there's enough buffer space remaining. 1296 */ 1297 // LP64todo - fix this! 1298 if (uio_resid(uio) < 0 || (currattrbufsize > (u_int32_t)uio_resid(uio))) { 1299 break; 1300 } 1301 else { 1302 *((u_int32_t *)attrbufptr) = currattrbufsize; 1303 error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio); 1304 if (error) { 1305 break; 1306 } 1307 1308 attrptr = attrbufptr; 1309 /* Point to variable-length storage */ 1310 varptr = (char *)attrbufptr + fixedblocksize; 1311 1312 *ap->a_actualcount += 1; 1313 dnp->d_offset++; 1314 1315 /* Termination checks */ 1316 if ((--maxcount <= 0) || 1317 // LP64todo - fix this! 1318 uio_resid(uio) < 0 || 1319 ((u_int32_t)uio_resid(uio) < (fixedblocksize + SMBFS_AVERAGE_NAME_SIZE))) { 1320 break; 1321 } 1322 } 1323 } /* while loop */ 1324 1325 if (error == ENOENT) { 1326 *(ap->a_eofflag) = TRUE; 1327 error = 0; 1328 } 1329 1330 if (error) { 1331 goto done; 1332 } 1333 1334done: 1335 /* Last offset into uio_offset. */ 1336 uio_setoffset(uio, dnp->d_offset); 1337 1338 *ap->a_newstate = dnp->d_changecnt; 1339 1340 if (attrbufptr) { 1341 FREE(attrbufptr, M_TEMP); 1342 } 1343 1344 smbnode_unlock(VTOSMB(dvp)); 1345 1346 return (error); 1347} 1348 1349