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