1/* 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * hfs_attrlist.c - HFS attribute list processing 31 * 32 * Copyright (c) 1998-2002, Apple Computer, Inc. All Rights Reserved. 33 */ 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/kernel.h> 38#include <sys/malloc.h> 39#include <sys/attr.h> 40#include <sys/stat.h> 41#include <sys/unistd.h> 42#include <sys/mount_internal.h> 43#include <sys/kauth.h> 44 45#include <kern/locks.h> 46 47#include "hfs.h" 48#include "hfs_cnode.h" 49#include "hfs_mount.h" 50#include "hfs_dbg.h" 51#include "hfs_attrlist.h" 52#include "hfs_btreeio.h" 53 54/* Packing routines: */ 55 56static void packnameattr(struct attrblock *abp, struct vnode *vp, 57 const u_int8_t *name, int namelen); 58 59static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp, 60 struct vnode *vp, struct cat_desc * cdp, 61 struct cat_attr * cap, struct proc *p); 62 63static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp, 64 struct cat_attr *cattrp, struct cat_fork *datafork, 65 struct cat_fork *rsrcfork); 66 67static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp, 68 struct vnode *vp, struct cat_desc * descp, 69 struct cat_attr * cattrp); 70 71static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx); 72 73/* 74 * readdirattr operation will return attributes for the items in the 75 * directory specified. 76 * 77 * It does not do . and .. entries. The problem is if you are at the root of the 78 * hfs directory and go to .. you could be crossing a mountpoint into a 79 * different (ufs) file system. The attributes that apply for it may not 80 * apply for the file system you are doing the readdirattr on. To make life 81 * simpler, this call will only return entries in its directory, hfs like. 82 */ 83__private_extern__ 84int 85hfs_vnop_readdirattr(ap) 86 struct vnop_readdirattr_args /* { 87 struct vnode *a_vp; 88 struct attrlist *a_alist; 89 struct uio *a_uio; 90 u_long a_maxcount; 91 u_long a_options; 92 u_long *a_newstate; 93 int *a_eofflag; 94 u_long *a_actualcount; 95 vfs_context_t a_context; 96 } */ *ap; 97{ 98 struct vnode *dvp = ap->a_vp; 99 struct cnode *dcp; 100 struct hfsmount * hfsmp; 101 struct attrlist *alist = ap->a_alist; 102 uio_t uio = ap->a_uio; 103 int maxcount = ap->a_maxcount; 104 struct proc *p = vfs_context_proc(ap->a_context); 105 u_int32_t fixedblocksize; 106 u_int32_t maxattrblocksize; 107 u_int32_t currattrbufsize; 108 void *attrbufptr = NULL; 109 void *attrptr; 110 void *varptr; 111 struct attrblock attrblk; 112 int error = 0; 113 int index = 0; 114 int i, dir_entries = 0; 115 struct cat_desc *lastdescp = NULL; 116 struct cat_entrylist *ce_list = NULL; 117 directoryhint_t *dirhint = NULL; 118 unsigned int tag; 119 int maxentries; 120 int lockflags; 121 122 *(ap->a_actualcount) = 0; 123 *(ap->a_eofflag) = 0; 124 125 /* Check for invalid options and buffer space. */ 126 if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) || 127 (uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1) || (maxcount <= 0)) { 128 return (EINVAL); 129 } 130 /* 131 * Reject requests for unsupported attributes. 132 */ 133 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) || 134 (alist->commonattr & ~HFS_ATTR_CMN_VALID) || 135 (alist->volattr != 0) || 136 (alist->dirattr & ~HFS_ATTR_DIR_VALID) || 137 (alist->fileattr & ~HFS_ATTR_FILE_VALID) || 138 (alist->forkattr != 0)) { 139 return (EINVAL); 140 } 141 /* 142 * Take an exclusive directory lock since we manipulate the directory hints 143 */ 144 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK))) { 145 return (error); 146 } 147 dcp = VTOC(dvp); 148 hfsmp = VTOHFS(dvp); 149 150 dir_entries = dcp->c_entries; 151 152 /* Extract directory index and tag (sequence number) from uio_offset */ 153 index = uio_offset(uio) & HFS_INDEX_MASK; 154 tag = uio_offset(uio) & ~HFS_INDEX_MASK; 155 if ((index + 1) > dir_entries) { 156 *(ap->a_eofflag) = 1; 157 error = 0; 158 goto exit2; 159 } 160 161 /* Get a buffer to hold packed attributes. */ 162 fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */ 163 maxattrblocksize = fixedblocksize; 164 if (alist->commonattr & ATTR_CMN_NAME) 165 maxattrblocksize += kHFSPlusMaxFileNameBytes + 1; 166 MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK); 167 attrptr = attrbufptr; 168 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */ 169 170 /* Get a detached directory hint (cnode must be locked exclusive) */ 171 dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE); 172 173 /* Hide tag from catalog layer. */ 174 dirhint->dh_index &= HFS_INDEX_MASK; 175 if (dirhint->dh_index == HFS_INDEX_MASK) { 176 dirhint->dh_index = -1; 177 } 178 179 /* 180 * Obtain a list of catalog entries and pack their attributes until 181 * the output buffer is full or maxcount entries have been packed. 182 */ 183 184 /* 185 * Constrain our list size. 186 */ 187 maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE); 188 maxentries = min(maxentries, dcp->c_entries - index); 189 maxentries = min(maxentries, maxcount); 190 maxentries = min(maxentries, MAXCATENTRIES); 191 if (maxentries < 1) { 192 error = EINVAL; 193 goto exit2; 194 } 195 196 /* Initialize a catalog entry list. */ 197 MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK); 198 bzero(ce_list, CE_LIST_SIZE(maxentries)); 199 ce_list->maxentries = maxentries; 200 201 /* 202 * Populate the ce_list from the catalog file. 203 */ 204 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); 205 206 error = cat_getentriesattr(hfsmp, dirhint, ce_list); 207 /* Don't forget to release the descriptors later! */ 208 209 hfs_systemfile_unlock(hfsmp, lockflags); 210 211 if (error == ENOENT) { 212 *(ap->a_eofflag) = TRUE; 213 error = 0; 214 } 215 if (error) { 216 goto exit1; 217 } 218 219 /* 220 * Drop the directory lock so we don't deadlock when we: 221 * - acquire a child cnode lock 222 * - make calls to vnode_authorize() 223 * - make calls to kauth_cred_ismember_gid() 224 */ 225 hfs_unlock(dcp); 226 dcp = NULL; 227 228 /* Process the catalog entries. */ 229 for (i = 0; i < (int)ce_list->realentries; ++i) { 230 struct cnode *cp = NULL; 231 struct vnode *vp = NULL; 232 struct cat_desc * cdescp; 233 struct cat_attr * cattrp; 234 struct cat_fork c_datafork; 235 struct cat_fork c_rsrcfork; 236 237 bzero(&c_datafork, sizeof(c_datafork)); 238 bzero(&c_rsrcfork, sizeof(c_rsrcfork)); 239 cdescp = &ce_list->entry[i].ce_desc; 240 cattrp = &ce_list->entry[i].ce_attr; 241 c_datafork.cf_size = ce_list->entry[i].ce_datasize; 242 c_datafork.cf_blocks = ce_list->entry[i].ce_datablks; 243 c_rsrcfork.cf_size = ce_list->entry[i].ce_rsrcsize; 244 c_rsrcfork.cf_blocks = ce_list->entry[i].ce_rsrcblks; 245 246 if ((alist->commonattr & ATTR_CMN_USERACCESS) && 247 (cattrp->ca_recflags & kHFSHasSecurityMask)) { 248 /* 249 * Obtain vnode for our vnode_authorize() calls. 250 */ 251 if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0) != 0) { 252 vp = NULL; 253 } 254 } else if (!(ap->a_options & FSOPT_NOINMEMUPDATE)) { 255 /* Get in-memory cnode data (if any). */ 256 vp = hfs_chash_getvnode(hfsmp->hfs_raw_dev, cattrp->ca_fileid, 0, 0); 257 } 258 if (vp != NULL) { 259 cp = VTOC(vp); 260 /* Only use cnode's decriptor for non-hardlinks */ 261 if (!(cp->c_flag & C_HARDLINK)) 262 cdescp = &cp->c_desc; 263 cattrp = &cp->c_attr; 264 if (cp->c_datafork) { 265 c_datafork.cf_size = cp->c_datafork->ff_size; 266 c_datafork.cf_blocks = cp->c_datafork->ff_blocks; 267 } 268 if (cp->c_rsrcfork) { 269 c_rsrcfork.cf_size = cp->c_rsrcfork->ff_size; 270 c_rsrcfork.cf_blocks = cp->c_rsrcfork->ff_blocks; 271 } 272 /* All done with cnode. */ 273 hfs_unlock(cp); 274 cp = NULL; 275 } 276 277 *((u_int32_t *)attrptr) = 0; 278 attrptr = ((u_int32_t *)attrptr) + 1; 279 attrblk.ab_attrlist = alist; 280 attrblk.ab_attrbufpp = &attrptr; 281 attrblk.ab_varbufpp = &varptr; 282 attrblk.ab_flags = 0; 283 attrblk.ab_blocksize = maxattrblocksize; 284 attrblk.ab_context = ap->a_context; 285 286 /* Pack catalog entries into attribute buffer. */ 287 hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, p); 288 currattrbufsize = ((char *)varptr - (char *)attrbufptr); 289 290 /* All done with vnode. */ 291 if (vp != NULL) { 292 vnode_put(vp); 293 vp = NULL; 294 } 295 296 /* Make sure there's enough buffer space remaining. */ 297 // LP64todo - fix this! 298 if (uio_resid(uio) < 0 || currattrbufsize > (u_int32_t)uio_resid(uio)) { 299 break; 300 } else { 301 *((u_int32_t *)attrbufptr) = currattrbufsize; 302 error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio); 303 if (error != E_NONE) { 304 break; 305 } 306 attrptr = attrbufptr; 307 /* Point to variable-length storage */ 308 varptr = (char *)attrbufptr + fixedblocksize; 309 /* Save the last valid catalog entry */ 310 lastdescp = &ce_list->entry[i].ce_desc; 311 index++; 312 *ap->a_actualcount += 1; 313 314 /* Termination checks */ 315 if ((--maxcount <= 0) || 316 // LP64todo - fix this! 317 uio_resid(uio) < 0 || 318 ((u_int32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE)) || 319 (index >= dir_entries)) { 320 break; 321 } 322 } 323 } /* for each catalog entry */ 324 325 /* For small directories, check if we're all done. */ 326 if (*ap->a_actualcount == (u_long)dir_entries) { 327 *(ap->a_eofflag) = TRUE; 328 } 329 330 /* If we skipped catalog entries for reserved files that should 331 * not be listed in namespace, update the index accordingly. 332 */ 333 if (ce_list->skipentries) { 334 index += ce_list->skipentries; 335 ce_list->skipentries = 0; 336 } 337 338 /* If there are more entries then save the last name. */ 339 if (index < dir_entries 340 && !(*(ap->a_eofflag)) 341 && lastdescp != NULL) { 342 343 /* Remember last entry */ 344 if ((dirhint->dh_desc.cd_flags & CD_HASBUF) && 345 (dirhint->dh_desc.cd_nameptr != NULL)) { 346 dirhint->dh_desc.cd_flags &= ~CD_HASBUF; 347 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr); 348 } 349 dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen; 350 dirhint->dh_desc.cd_nameptr = (const u_int8_t *) 351 vfs_addname((const char *)lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0); 352 dirhint->dh_desc.cd_flags |= CD_HASBUF; 353 dirhint->dh_index = index - 1; 354 dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid; 355 dirhint->dh_desc.cd_hint = lastdescp->cd_hint; 356 dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding; 357 } else { 358 /* No more entries. */ 359 *(ap->a_eofflag) = TRUE; 360 } 361 362 /* All done with the catalog descriptors. */ 363 for (i = 0; i < (int)ce_list->realentries; ++i) 364 cat_releasedesc(&ce_list->entry[i].ce_desc); 365 ce_list->realentries = 0; 366 367 (void) hfs_lock(VTOC(dvp), HFS_FORCE_LOCK); 368 dcp = VTOC(dvp); 369 370exit1: 371 /* Pack directory index and tag into uio_offset. */ 372 while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS; 373 uio_setoffset(uio, index | tag); 374 dirhint->dh_index |= tag; 375 376exit2: 377 *ap->a_newstate = dcp->c_dirchangecnt; 378 379 /* Drop directory hint on error or if there are no more entries */ 380 if (dirhint) { 381 if ((error != 0) || (index >= dir_entries) || *(ap->a_eofflag)) 382 hfs_reldirhint(dcp, dirhint); 383 else 384 hfs_insertdirhint(dcp, dirhint); 385 } 386 if (attrbufptr) 387 FREE(attrbufptr, M_TEMP); 388 if (ce_list) 389 FREE(ce_list, M_TEMP); 390 391 hfs_unlock(dcp); 392 return (error); 393} 394 395 396/*==================== Attribute list support routines ====================*/ 397 398/* 399 * Pack cnode attributes into an attribute block. 400 */ 401 __private_extern__ 402void 403hfs_packattrblk(struct attrblock *abp, 404 struct hfsmount *hfsmp, 405 struct vnode *vp, 406 struct cat_desc *descp, 407 struct cat_attr *attrp, 408 struct cat_fork *datafork, 409 struct cat_fork *rsrcfork, 410 struct proc *p) 411{ 412 struct attrlist *attrlistp = abp->ab_attrlist; 413 414 if (attrlistp->commonattr) 415 packcommonattr(abp, hfsmp, vp, descp, attrp, p); 416 417 if (attrlistp->dirattr && S_ISDIR(attrp->ca_mode)) 418 packdirattr(abp, hfsmp, vp, descp,attrp); 419 420 if (attrlistp->fileattr && !S_ISDIR(attrp->ca_mode)) 421 packfileattr(abp, hfsmp, attrp, datafork, rsrcfork); 422} 423 424 425static char* 426mountpointname(struct mount *mp) 427{ 428 size_t namelength = strlen(mp->mnt_vfsstat.f_mntonname); 429 int foundchars = 0; 430 char *c; 431 432 if (namelength == 0) 433 return (NULL); 434 435 /* 436 * Look backwards through the name string, looking for 437 * the first slash encountered (which must precede the 438 * last part of the pathname). 439 */ 440 for (c = mp->mnt_vfsstat.f_mntonname + namelength - 1; 441 namelength > 0; --c, --namelength) { 442 if (*c != '/') { 443 foundchars = 1; 444 } else if (foundchars) { 445 return (c + 1); 446 } 447 } 448 449 return (mp->mnt_vfsstat.f_mntonname); 450} 451 452 453static void 454packnameattr( 455 struct attrblock *abp, 456 struct vnode *vp, 457 const u_int8_t *name, 458 int namelen) 459{ 460 void *varbufptr; 461 struct attrreference * attr_refptr; 462 char *mpname; 463 size_t mpnamelen; 464 u_int32_t attrlength; 465 u_int8_t empty = 0; 466 467 /* A cnode's name may be incorrect for the root of a mounted 468 * filesystem (it can be mounted on a different directory name 469 * than the name of the volume, such as "blah-1"). So for the 470 * root directory, it's best to return the last element of the 471 location where the volume's mounted: 472 */ 473 if ((vp != NULL) && vnode_isvroot(vp) && 474 (mpname = mountpointname(vnode_mount(vp)))) { 475 mpnamelen = strlen(mpname); 476 477 /* Trim off any trailing slashes: */ 478 while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) 479 --mpnamelen; 480 481 /* If there's anything left, use it instead of the volume's name */ 482 if (mpnamelen > 0) { 483 name = (u_int8_t *)mpname; 484 namelen = mpnamelen; 485 } 486 } 487 if (name == NULL) { 488 name = ∅ 489 namelen = 0; 490 } 491 492 varbufptr = *abp->ab_varbufpp; 493 attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp); 494 495 attrlength = namelen + 1; 496 attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr; 497 attr_refptr->attr_length = attrlength; 498 (void) strncpy((char *)varbufptr, (const char *) name, attrlength); 499 /* 500 * Advance beyond the space just allocated and 501 * round up to the next 4-byte boundary: 502 */ 503 varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3); 504 ++attr_refptr; 505 506 *abp->ab_attrbufpp = attr_refptr; 507 *abp->ab_varbufpp = varbufptr; 508} 509 510 511static void 512packcommonattr( 513 struct attrblock *abp, 514 struct hfsmount *hfsmp, 515 struct vnode *vp, 516 struct cat_desc * cdp, 517 struct cat_attr * cap, 518 struct proc *p) 519{ 520 attrgroup_t attr = abp->ab_attrlist->commonattr; 521 struct mount *mp = HFSTOVFS(hfsmp); 522 void *attrbufptr = *abp->ab_attrbufpp; 523 void *varbufptr = *abp->ab_varbufpp; 524 boolean_t is_64_bit = proc_is64bit(p); 525 uid_t cuid = 1; 526 int isroot = 0; 527 528 if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) { 529 cuid = kauth_cred_getuid(proc_ucred(p)); 530 isroot = cuid == 0; 531 } 532 533 if (ATTR_CMN_NAME & attr) { 534 packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen); 535 attrbufptr = *abp->ab_attrbufpp; 536 varbufptr = *abp->ab_varbufpp; 537 } 538 if (ATTR_CMN_DEVID & attr) { 539 *((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev; 540 attrbufptr = ((dev_t *)attrbufptr) + 1; 541 } 542 if (ATTR_CMN_FSID & attr) { 543 fsid_t fsid; 544 545 fsid.val[0] = (long)hfsmp->hfs_raw_dev; 546 fsid.val[1] = (long)vfs_typenum(mp); 547 *((fsid_t *)attrbufptr) = fsid; 548 attrbufptr = ((fsid_t *)attrbufptr) + 1; 549 } 550 if (ATTR_CMN_OBJTYPE & attr) { 551 *((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode); 552 attrbufptr = ((fsobj_type_t *)attrbufptr) + 1; 553 } 554 if (ATTR_CMN_OBJTAG & attr) { 555 *((fsobj_tag_t *)attrbufptr) = VT_HFS; 556 attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1; 557 } 558 /* 559 * Exporting file IDs from HFS Plus: 560 * 561 * For "normal" files the c_fileid is the same value as the 562 * c_cnid. But for hard link files, they are different - the 563 * c_cnid belongs to the active directory entry (ie the link) 564 * and the c_fileid is for the actual inode (ie the data file). 565 * 566 * The stat call (getattr) will always return the c_fileid 567 * and Carbon APIs, which are hardlink-ignorant, will always 568 * receive the c_cnid (from getattrlist). 569 */ 570 if (ATTR_CMN_OBJID & attr) { 571 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; 572 ((fsobj_id_t *)attrbufptr)->fid_generation = 0; 573 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; 574 } 575 if (ATTR_CMN_OBJPERMANENTID & attr) { 576 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; 577 ((fsobj_id_t *)attrbufptr)->fid_generation = 0; 578 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; 579 } 580 if (ATTR_CMN_PAROBJID & attr) { 581 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid; 582 ((fsobj_id_t *)attrbufptr)->fid_generation = 0; 583 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; 584 } 585 if (ATTR_CMN_SCRIPT & attr) { 586 *((text_encoding_t *)attrbufptr) = cdp->cd_encoding; 587 attrbufptr = ((text_encoding_t *)attrbufptr) + 1; 588 } 589 if (ATTR_CMN_CRTIME & attr) { 590 if (is_64_bit) { 591 ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_itime; 592 ((struct user_timespec *)attrbufptr)->tv_nsec = 0; 593 attrbufptr = ((struct user_timespec *)attrbufptr) + 1; 594 } 595 else { 596 ((struct timespec *)attrbufptr)->tv_sec = cap->ca_itime; 597 ((struct timespec *)attrbufptr)->tv_nsec = 0; 598 attrbufptr = ((struct timespec *)attrbufptr) + 1; 599 } 600 } 601 if (ATTR_CMN_MODTIME & attr) { 602 if (is_64_bit) { 603 ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_mtime; 604 ((struct user_timespec *)attrbufptr)->tv_nsec = 0; 605 attrbufptr = ((struct user_timespec *)attrbufptr) + 1; 606 } 607 else { 608 ((struct timespec *)attrbufptr)->tv_sec = cap->ca_mtime; 609 ((struct timespec *)attrbufptr)->tv_nsec = 0; 610 attrbufptr = ((struct timespec *)attrbufptr) + 1; 611 } 612 } 613 if (ATTR_CMN_CHGTIME & attr) { 614 if (is_64_bit) { 615 ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_ctime; 616 ((struct user_timespec *)attrbufptr)->tv_nsec = 0; 617 attrbufptr = ((struct user_timespec *)attrbufptr) + 1; 618 } 619 else { 620 ((struct timespec *)attrbufptr)->tv_sec = cap->ca_ctime; 621 ((struct timespec *)attrbufptr)->tv_nsec = 0; 622 attrbufptr = ((struct timespec *)attrbufptr) + 1; 623 } 624 } 625 if (ATTR_CMN_ACCTIME & attr) { 626 if (is_64_bit) { 627 ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_atime; 628 ((struct user_timespec *)attrbufptr)->tv_nsec = 0; 629 attrbufptr = ((struct user_timespec *)attrbufptr) + 1; 630 } 631 else { 632 ((struct timespec *)attrbufptr)->tv_sec = cap->ca_atime; 633 ((struct timespec *)attrbufptr)->tv_nsec = 0; 634 attrbufptr = ((struct timespec *)attrbufptr) + 1; 635 } 636 } 637 if (ATTR_CMN_BKUPTIME & attr) { 638 if (is_64_bit) { 639 ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_btime; 640 ((struct user_timespec *)attrbufptr)->tv_nsec = 0; 641 attrbufptr = ((struct user_timespec *)attrbufptr) + 1; 642 } 643 else { 644 ((struct timespec *)attrbufptr)->tv_sec = cap->ca_btime; 645 ((struct timespec *)attrbufptr)->tv_nsec = 0; 646 attrbufptr = ((struct timespec *)attrbufptr) + 1; 647 } 648 } 649 if (ATTR_CMN_FNDRINFO & attr) { 650 bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32); 651 /* Don't expose a symlink's private type/creator. */ 652 if (S_ISLNK(cap->ca_mode)) { 653 struct FndrFileInfo *fip; 654 655 fip = (struct FndrFileInfo *)attrbufptr; 656 fip->fdType = 0; 657 fip->fdCreator = 0; 658 } 659 attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32; 660 } 661 if (ATTR_CMN_OWNERID & attr) { 662 uid_t nuid = cap->ca_uid; 663 664 if (!isroot) { 665 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) 666 nuid = cuid; 667 else if (nuid == UNKNOWNUID) 668 nuid = cuid; 669 } 670 671 *((uid_t *)attrbufptr) = nuid; 672 attrbufptr = ((uid_t *)attrbufptr) + 1; 673 } 674 if (ATTR_CMN_GRPID & attr) { 675 gid_t ngid = cap->ca_gid; 676 677 if (!isroot) { 678 gid_t cgid = kauth_cred_getgid(proc_ucred(p)); 679 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) 680 ngid = cgid; 681 else if (ngid == UNKNOWNUID) 682 ngid = cgid; 683 } 684 685 *((gid_t *)attrbufptr) = ngid; 686 attrbufptr = ((gid_t *)attrbufptr) + 1; 687 } 688 if (ATTR_CMN_ACCESSMASK & attr) { 689 /* 690 * [2856576] Since we are dynamically changing the owner, also 691 * effectively turn off the set-user-id and set-group-id bits, 692 * just like chmod(2) would when changing ownership. This prevents 693 * a security hole where set-user-id programs run as whoever is 694 * logged on (or root if nobody is logged in yet!) 695 */ 696 *((u_int32_t *)attrbufptr) = (cap->ca_uid == UNKNOWNUID) ? 697 cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode; 698 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 699 } 700 if (ATTR_CMN_FLAGS & attr) { 701 *((u_int32_t *)attrbufptr) = cap->ca_flags; 702 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 703 } 704 if (ATTR_CMN_USERACCESS & attr) { 705 u_int32_t user_access; 706 707 /* Take the long path when we have an ACL */ 708 if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) { 709 user_access = hfs_real_user_access(vp, abp->ab_context); 710 } else { 711 user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid, 712 cap->ca_mode, mp, proc_ucred(current_proc()), 0); 713 } 714 /* Also consider READ-ONLY file system. */ 715 if (vfs_flags(mp) & MNT_RDONLY) { 716 user_access &= ~W_OK; 717 } 718 /* Locked objects are not writable either */ 719 if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) != 0)) 720 user_access &= ~W_OK; 721 if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) == 0)) 722 user_access &= ~W_OK; 723 724 *((u_int32_t *)attrbufptr) = user_access; 725 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 726 } 727 if (ATTR_CMN_FILEID & attr) { 728 *((u_int64_t *)attrbufptr) = cap->ca_fileid; 729 attrbufptr = ((u_int64_t *)attrbufptr) + 1; 730 } 731 if (ATTR_CMN_PARENTID & attr) { 732 *((u_int64_t *)attrbufptr) = cdp->cd_parentcnid; 733 attrbufptr = ((u_int64_t *)attrbufptr) + 1; 734 } 735 736 *abp->ab_attrbufpp = attrbufptr; 737 *abp->ab_varbufpp = varbufptr; 738} 739 740static void 741packdirattr( 742 struct attrblock *abp, 743 struct hfsmount *hfsmp, 744 struct vnode *vp, 745 struct cat_desc * descp, 746 struct cat_attr * cattrp) 747{ 748 attrgroup_t attr = abp->ab_attrlist->dirattr; 749 void *attrbufptr = *abp->ab_attrbufpp; 750 u_int32_t entries; 751 752 /* 753 * The DIR_LINKCOUNT is the count of real directory hard links. 754 * (i.e. its not the sum of the implied "." and ".." references 755 * typically used in stat's st_nlink field) 756 */ 757 if (ATTR_DIR_LINKCOUNT & attr) { 758 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount; 759 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 760 } 761 if (ATTR_DIR_ENTRYCOUNT & attr) { 762 entries = cattrp->ca_entries; 763 764 if (descp->cd_parentcnid == kHFSRootParentID) { 765 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) 766 --entries; /* hide private dir */ 767 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) 768 --entries; /* hide private dir */ 769 if (hfsmp->jnl || 770 ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && 771 (hfsmp->hfs_flags & HFS_READ_ONLY))) 772 entries -= 2; /* hide the journal files */ 773 } 774 775 *((u_int32_t *)attrbufptr) = entries; 776 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 777 } 778 if (ATTR_DIR_MOUNTSTATUS & attr) { 779 if (vp != NULL && vnode_mountedhere(vp) != NULL) 780 *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT; 781 else 782 *((u_int32_t *)attrbufptr) = 0; 783 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 784 } 785 *abp->ab_attrbufpp = attrbufptr; 786} 787 788static void 789packfileattr( 790 struct attrblock *abp, 791 struct hfsmount *hfsmp, 792 struct cat_attr *cattrp, 793 struct cat_fork *datafork, 794 struct cat_fork *rsrcfork) 795{ 796 attrgroup_t attr = abp->ab_attrlist->fileattr; 797 void *attrbufptr = *abp->ab_attrbufpp; 798 void *varbufptr = *abp->ab_varbufpp; 799 u_int32_t allocblksize; 800 801 allocblksize = HFSTOVCB(hfsmp)->blockSize; 802 803 if (ATTR_FILE_LINKCOUNT & attr) { 804 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount; 805 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 806 } 807 if (ATTR_FILE_TOTALSIZE & attr) { 808 *((off_t *)attrbufptr) = datafork->cf_size + rsrcfork->cf_size; 809 attrbufptr = ((off_t *)attrbufptr) + 1; 810 } 811 if (ATTR_FILE_ALLOCSIZE & attr) { 812 *((off_t *)attrbufptr) = 813 (off_t)cattrp->ca_blocks * (off_t)allocblksize; 814 attrbufptr = ((off_t *)attrbufptr) + 1; 815 } 816 if (ATTR_FILE_IOBLOCKSIZE & attr) { 817 *((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize; 818 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 819 } 820 if (ATTR_FILE_CLUMPSIZE & attr) { 821 *((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz; 822 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 823 } 824 if (ATTR_FILE_DEVTYPE & attr) { 825 if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode)) 826 *((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev; 827 else 828 *((u_int32_t *)attrbufptr) = 0; 829 attrbufptr = ((u_int32_t *)attrbufptr) + 1; 830 } 831 if (ATTR_FILE_DATALENGTH & attr) { 832 *((off_t *)attrbufptr) = datafork->cf_size; 833 attrbufptr = ((off_t *)attrbufptr) + 1; 834 } 835 if (ATTR_FILE_DATAALLOCSIZE & attr) { 836 *((off_t *)attrbufptr) = 837 (off_t)datafork->cf_blocks * (off_t)allocblksize; 838 attrbufptr = ((off_t *)attrbufptr) + 1; 839 } 840 if (ATTR_FILE_RSRCLENGTH & attr) { 841 *((off_t *)attrbufptr) = rsrcfork->cf_size; 842 attrbufptr = ((off_t *)attrbufptr) + 1; 843 } 844 if (ATTR_FILE_RSRCALLOCSIZE & attr) { 845 *((off_t *)attrbufptr) = 846 (off_t)rsrcfork->cf_blocks * (off_t)allocblksize; 847 attrbufptr = ((off_t *)attrbufptr) + 1; 848 } 849 *abp->ab_attrbufpp = attrbufptr; 850 *abp->ab_varbufpp = varbufptr; 851} 852 853/* 854 * Calculate the total size of an attribute block. 855 */ 856 __private_extern__ 857int 858hfs_attrblksize(struct attrlist *attrlist) 859{ 860 int size; 861 attrgroup_t a; 862 int sizeof_timespec; 863 boolean_t is_64_bit = proc_is64bit(current_proc()); 864 865 if (is_64_bit) 866 sizeof_timespec = sizeof(struct user_timespec); 867 else 868 sizeof_timespec = sizeof(struct timespec); 869 870 DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0); 871 872 DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0); 873 874 DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0); 875 876 DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0); 877 878 DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0); 879 880 size = 0; 881 882 if ((a = attrlist->commonattr) != 0) { 883 if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference); 884 if (a & ATTR_CMN_DEVID) size += sizeof(dev_t); 885 if (a & ATTR_CMN_FSID) size += sizeof(fsid_t); 886 if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t); 887 if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t); 888 if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t); 889 if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t); 890 if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t); 891 if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t); 892 if (a & ATTR_CMN_CRTIME) size += sizeof_timespec; 893 if (a & ATTR_CMN_MODTIME) size += sizeof_timespec; 894 if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec; 895 if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec; 896 if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec; 897 if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t); 898 if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t); 899 if (a & ATTR_CMN_GRPID) size += sizeof(gid_t); 900 if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_int32_t); 901 if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t); 902 if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t); 903 if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t); 904 if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t); 905 } 906 if ((a = attrlist->dirattr) != 0) { 907 if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t); 908 if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t); 909 if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t); 910 } 911 if ((a = attrlist->fileattr) != 0) { 912 if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t); 913 if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t); 914 if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t); 915 if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(u_int32_t); 916 if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t); 917 if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t); 918 if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t); 919 if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t); 920 if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t); 921 if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t); 922 } 923 924 return (size); 925} 926 927#define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \ 928 KAUTH_VNODE_ADD_SUBDIRECTORY | \ 929 KAUTH_VNODE_DELETE_CHILD) 930 931#define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY) 932 933#define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH) 934 935#define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA) 936 937#define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA) 938 939#define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE) 940 941 942/* 943 * Compute the same [expensive] user_access value as getattrlist does 944 */ 945static u_int32_t 946hfs_real_user_access(vnode_t vp, vfs_context_t ctx) 947{ 948 u_int32_t user_access = 0; 949 950 if (vnode_isdir(vp)) { 951 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_WRITE_RIGHTS, ctx) == 0) 952 user_access |= W_OK; 953 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_READ_RIGHTS, ctx) == 0) 954 user_access |= R_OK; 955 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_EXECUTE_RIGHTS, ctx) == 0) 956 user_access |= X_OK; 957 } else { 958 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_WRITE_RIGHTS, ctx) == 0) 959 user_access |= W_OK; 960 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_READRIGHTS, ctx) == 0) 961 user_access |= R_OK; 962 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_EXECUTE_RIGHTS, ctx) == 0) 963 user_access |= X_OK; 964 } 965 return (user_access); 966} 967 968 969__private_extern__ 970unsigned long 971DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, 972 struct mount *mp, kauth_cred_t cred, __unused struct proc *p) 973{ 974 unsigned long permissions; 975 976 if (obj_uid == UNKNOWNUID) 977 obj_uid = kauth_cred_getuid(cred); 978 979 /* User id 0 (root) always gets access. */ 980 if (!suser(cred, NULL)) { 981 permissions = R_OK | W_OK | X_OK; 982 goto Exit; 983 }; 984 985 /* Otherwise, check the owner. */ 986 if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) { 987 permissions = ((unsigned long)obj_mode & S_IRWXU) >> 6; 988 goto Exit; 989 } 990 991 /* Otherwise, check the groups. */ 992 if (! (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)) { 993 int is_member; 994 995 if (kauth_cred_ismember_gid(cred, obj_gid, &is_member) == 0 && is_member) { 996 permissions = ((unsigned long)obj_mode & S_IRWXG) >> 3; 997 goto Exit; 998 } 999 } 1000 1001 /* Otherwise, settle for 'others' access. */ 1002 permissions = (unsigned long)obj_mode & S_IRWXO; 1003 1004Exit: 1005 return (permissions); 1006} 1007 1008