2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 /* @(#)hfs_readwrite.c 1.0
25 * (c) 1998-2001 Apple Computer, Inc. All Rights Reserved
27 * hfs_readwrite.c -- vnode operations to deal with reading and writing files.
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/resourcevar.h>
34 #include <sys/kernel.h>
35 #include <sys/fcntl.h>
36 #include <sys/filedesc.h>
40 #include <sys/kauth.h>
41 #include <sys/vnode.h>
43 #include <sys/vfs_context.h>
45 #include <miscfs/specfs/specdev.h>
48 #include <vm/vm_pageout.h>
49 #include <vm/vm_kern.h>
51 #include <sys/kdebug.h>
54 #include "hfs_endian.h"
55 #include "hfs_fsctl.h"
56 #include "hfs_quota.h"
57 #include "hfscommon/headers/FileMgrInternal.h"
58 #include "hfscommon/headers/BTreesInternal.h"
59 #include "hfs_cnode.h"
62 extern int overflow_extents(struct filefork
*fp
);
64 #define can_cluster(size) ((((size & (4096-1))) == 0) && (size <= (MAXPHYSIO/2)))
67 MAXHFSFILESIZE
= 0x7FFFFFFF /* this needs to go in the mount structure */
70 extern u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
72 extern int hfs_setextendedsecurity(struct hfsmount
*, int);
75 static int hfs_clonelink(struct vnode
*, int, kauth_cred_t
, struct proc
*);
76 static int hfs_clonefile(struct vnode
*, int, int, int);
77 static int hfs_clonesysfile(struct vnode
*, int, int, int, kauth_cred_t
, struct proc
*);
80 /*****************************************************************************
82 * I/O Operations on vnodes
84 *****************************************************************************/
85 int hfs_vnop_read(struct vnop_read_args
*);
86 int hfs_vnop_write(struct vnop_write_args
*);
87 int hfs_vnop_ioctl(struct vnop_ioctl_args
*);
88 int hfs_vnop_select(struct vnop_select_args
*);
89 int hfs_vnop_blktooff(struct vnop_blktooff_args
*);
90 int hfs_vnop_offtoblk(struct vnop_offtoblk_args
*);
91 int hfs_vnop_blockmap(struct vnop_blockmap_args
*);
92 int hfs_vnop_strategy(struct vnop_strategy_args
*);
93 int hfs_vnop_allocate(struct vnop_allocate_args
*);
94 int hfs_vnop_pagein(struct vnop_pagein_args
*);
95 int hfs_vnop_pageout(struct vnop_pageout_args
*);
96 int hfs_vnop_bwrite(struct vnop_bwrite_args
*);
100 * Read data from a file.
103 hfs_vnop_read(struct vnop_read_args
*ap
)
105 uio_t uio
= ap
->a_uio
;
106 struct vnode
*vp
= ap
->a_vp
;
109 struct hfsmount
*hfsmp
;
112 off_t start_resid
= uio_resid(uio
);
113 off_t offset
= uio_offset(uio
);
117 /* Preflight checks */
118 if (!vnode_isreg(vp
)) {
119 /* can only read regular files */
125 if (start_resid
== 0)
126 return (0); /* Nothing left to do */
128 return (EINVAL
); /* cant read from a negative offset */
134 /* Protect against a size change. */
135 hfs_lock_truncate(cp
, 0);
137 filesize
= fp
->ff_size
;
138 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
139 if (offset
> filesize
) {
140 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) &&
141 (offset
> (off_t
)MAXHFSFILESIZE
)) {
147 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 12)) | DBG_FUNC_START
,
148 (int)uio_offset(uio
), uio_resid(uio
), (int)filesize
, (int)filebytes
, 0);
150 retval
= cluster_read(vp
, uio
, filesize
, 0);
152 cp
->c_touch_acctime
= TRUE
;
154 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 12)) | DBG_FUNC_END
,
155 (int)uio_offset(uio
), uio_resid(uio
), (int)filesize
, (int)filebytes
, 0);
158 * Keep track blocks read
160 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
&& retval
== 0) {
161 int took_cnode_lock
= 0;
164 bytesread
= start_resid
- uio_resid(uio
);
166 /* When ff_bytesread exceeds 32-bits, update it behind the cnode lock. */
167 if ((fp
->ff_bytesread
+ bytesread
) > 0x00000000ffffffff) {
168 hfs_lock(cp
, HFS_FORCE_LOCK
);
172 * If this file hasn't been seen since the start of
173 * the current sampling period then start over.
175 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
) {
178 fp
->ff_bytesread
= bytesread
;
180 cp
->c_atime
= tv
.tv_sec
;
182 fp
->ff_bytesread
+= bytesread
;
188 hfs_unlock_truncate(cp
);
193 * Write data to a file.
196 hfs_vnop_write(struct vnop_write_args
*ap
)
198 uio_t uio
= ap
->a_uio
;
199 struct vnode
*vp
= ap
->a_vp
;
202 struct hfsmount
*hfsmp
;
203 kauth_cred_t cred
= NULL
;
207 off_t actualBytesAdded
;
212 int ioflag
= ap
->a_ioflag
;
215 int cnode_locked
= 0;
217 // LP64todo - fix this! uio_resid may be 64-bit value
218 resid
= uio_resid(uio
);
219 offset
= uio_offset(uio
);
225 if (!vnode_isreg(vp
))
226 return (EPERM
); /* Can only write regular files */
228 /* Protect against a size change. */
229 hfs_lock_truncate(VTOC(vp
), TRUE
);
231 if ( (retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
232 hfs_unlock_truncate(VTOC(vp
));
239 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
241 if (ioflag
& IO_APPEND
) {
242 uio_setoffset(uio
, fp
->ff_size
);
243 offset
= fp
->ff_size
;
245 if ((cp
->c_flags
& APPEND
) && offset
!= fp
->ff_size
) {
250 origFileSize
= fp
->ff_size
;
251 eflags
= kEFDeferMask
; /* defer file block allocations */
253 #ifdef HFS_SPARSE_DEV
255 * When the underlying device is sparse and space
256 * is low (< 8MB), stop doing delayed allocations
257 * and begin doing synchronous I/O.
259 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) &&
260 (hfs_freeblks(hfsmp
, 0) < 2048)) {
261 eflags
&= ~kEFDeferMask
;
264 #endif /* HFS_SPARSE_DEV */
266 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_START
,
267 (int)offset
, uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
269 /* Now test if we need to extend the file */
270 /* Doing so will adjust the filebytes for us */
272 writelimit
= offset
+ resid
;
273 if (writelimit
<= filebytes
)
276 cred
= vfs_context_ucred(ap
->a_context
);
278 bytesToAdd
= writelimit
- filebytes
;
279 retval
= hfs_chkdq(cp
, (int64_t)(roundup(bytesToAdd
, hfsmp
->blockSize
)),
285 if (hfs_start_transaction(hfsmp
) != 0) {
290 while (writelimit
> filebytes
) {
291 bytesToAdd
= writelimit
- filebytes
;
292 if (cred
&& suser(cred
, NULL
) != 0)
293 eflags
|= kEFReserveMask
;
295 /* Protect extents b-tree and allocation bitmap */
296 lockflags
= SFL_BITMAP
;
297 if (overflow_extents(fp
))
298 lockflags
|= SFL_EXTENTS
;
299 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
301 /* Files that are changing size are not hot file candidates. */
302 if (hfsmp
->hfc_stage
== HFC_RECORDING
) {
303 fp
->ff_bytesread
= 0;
305 retval
= MacToVFSError(ExtendFileC (hfsmp
, (FCB
*)fp
, bytesToAdd
,
306 0, eflags
, &actualBytesAdded
));
308 hfs_systemfile_unlock(hfsmp
, lockflags
);
310 if ((actualBytesAdded
== 0) && (retval
== E_NONE
))
312 if (retval
!= E_NONE
)
314 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
315 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_NONE
,
316 (int)offset
, uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
318 (void) hfs_update(vp
, TRUE
);
319 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
320 (void) hfs_end_transaction(hfsmp
);
323 if (retval
== E_NONE
) {
331 struct rl_entry
*invalid_range
;
333 if (writelimit
> fp
->ff_size
)
334 filesize
= writelimit
;
336 filesize
= fp
->ff_size
;
338 lflag
= (ioflag
& IO_SYNC
);
340 if (offset
<= fp
->ff_size
) {
341 zero_off
= offset
& ~PAGE_MASK_64
;
343 /* Check to see whether the area between the zero_offset and the start
344 of the transfer to see whether is invalid and should be zero-filled
345 as part of the transfer:
347 if (offset
> zero_off
) {
348 if (rl_scan(&fp
->ff_invalidranges
, zero_off
, offset
- 1, &invalid_range
) != RL_NOOVERLAP
)
349 lflag
|= IO_HEADZEROFILL
;
352 off_t eof_page_base
= fp
->ff_size
& ~PAGE_MASK_64
;
354 /* The bytes between fp->ff_size and uio->uio_offset must never be
355 read without being zeroed. The current last block is filled with zeroes
356 if it holds valid data but in all cases merely do a little bookkeeping
357 to track the area from the end of the current last page to the start of
358 the area actually written. For the same reason only the bytes up to the
359 start of the page where this write will start is invalidated; any remainder
360 before uio->uio_offset is explicitly zeroed as part of the cluster_write.
362 Note that inval_start, the start of the page after the current EOF,
363 may be past the start of the write, in which case the zeroing
364 will be handled by the cluser_write of the actual data.
366 inval_start
= (fp
->ff_size
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
367 inval_end
= offset
& ~PAGE_MASK_64
;
368 zero_off
= fp
->ff_size
;
370 if ((fp
->ff_size
& PAGE_MASK_64
) &&
371 (rl_scan(&fp
->ff_invalidranges
,
374 &invalid_range
) != RL_NOOVERLAP
)) {
375 /* The page containing the EOF is not valid, so the
376 entire page must be made inaccessible now. If the write
377 starts on a page beyond the page containing the eof
378 (inval_end > eof_page_base), add the
379 whole page to the range to be invalidated. Otherwise
380 (i.e. if the write starts on the same page), zero-fill
381 the entire page explicitly now:
383 if (inval_end
> eof_page_base
) {
384 inval_start
= eof_page_base
;
386 zero_off
= eof_page_base
;
390 if (inval_start
< inval_end
) {
392 /* There's some range of data that's going to be marked invalid */
394 if (zero_off
< inval_start
) {
395 /* The pages between inval_start and inval_end are going to be invalidated,
396 and the actual write will start on a page past inval_end. Now's the last
397 chance to zero-fill the page containing the EOF:
401 retval
= cluster_write(vp
, (uio_t
) 0,
402 fp
->ff_size
, inval_start
,
404 lflag
| IO_HEADZEROFILL
| IO_NOZERODIRTY
);
405 hfs_lock(cp
, HFS_FORCE_LOCK
);
407 if (retval
) goto ioerr_exit
;
408 offset
= uio_offset(uio
);
411 /* Mark the remaining area of the newly allocated space as invalid: */
412 rl_add(inval_start
, inval_end
- 1 , &fp
->ff_invalidranges
);
414 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
415 zero_off
= fp
->ff_size
= inval_end
;
418 if (offset
> zero_off
) lflag
|= IO_HEADZEROFILL
;
421 /* Check to see whether the area between the end of the write and the end of
422 the page it falls in is invalid and should be zero-filled as part of the transfer:
424 tail_off
= (writelimit
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
425 if (tail_off
> filesize
) tail_off
= filesize
;
426 if (tail_off
> writelimit
) {
427 if (rl_scan(&fp
->ff_invalidranges
, writelimit
, tail_off
- 1, &invalid_range
) != RL_NOOVERLAP
) {
428 lflag
|= IO_TAILZEROFILL
;
433 * if the write starts beyond the current EOF (possibly advanced in the
434 * zeroing of the last block, above), then we'll zero fill from the current EOF
435 * to where the write begins:
437 * NOTE: If (and ONLY if) the portion of the file about to be written is
438 * before the current EOF it might be marked as invalid now and must be
439 * made readable (removed from the invalid ranges) before cluster_write
442 io_start
= (lflag
& IO_HEADZEROFILL
) ? zero_off
: offset
;
443 if (io_start
< fp
->ff_size
) {
446 io_end
= (lflag
& IO_TAILZEROFILL
) ? tail_off
: writelimit
;
447 rl_remove(io_start
, io_end
- 1, &fp
->ff_invalidranges
);
452 retval
= cluster_write(vp
, uio
, fp
->ff_size
, filesize
, zero_off
,
453 tail_off
, lflag
| IO_NOZERODIRTY
);
454 offset
= uio_offset(uio
);
455 if (offset
> fp
->ff_size
) {
456 fp
->ff_size
= offset
;
458 ubc_setsize(vp
, fp
->ff_size
); /* XXX check errors */
459 /* Files that are changing size are not hot file candidates. */
460 if (hfsmp
->hfc_stage
== HFC_RECORDING
)
461 fp
->ff_bytesread
= 0;
463 if (resid
> uio_resid(uio
)) {
464 cp
->c_touch_chgtime
= TRUE
;
465 cp
->c_touch_modtime
= TRUE
;
468 HFS_KNOTE(vp
, NOTE_WRITE
);
472 * If we successfully wrote any data, and we are not the superuser
473 * we clear the setuid and setgid bits as a precaution against
476 if (cp
->c_mode
& (S_ISUID
| S_ISGID
)) {
477 cred
= vfs_context_ucred(ap
->a_context
);
478 if (resid
> uio_resid(uio
) && cred
&& suser(cred
, NULL
)) {
480 hfs_lock(cp
, HFS_FORCE_LOCK
);
483 cp
->c_mode
&= ~(S_ISUID
| S_ISGID
);
487 if (ioflag
& IO_UNIT
) {
489 hfs_lock(cp
, HFS_FORCE_LOCK
);
492 (void)hfs_truncate(vp
, origFileSize
, ioflag
& IO_SYNC
,
494 // LP64todo - fix this! resid needs to by user_ssize_t
495 uio_setoffset(uio
, (uio_offset(uio
) - (resid
- uio_resid(uio
))));
496 uio_setresid(uio
, resid
);
497 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
499 } else if ((ioflag
& IO_SYNC
) && (resid
> uio_resid(uio
))) {
501 hfs_lock(cp
, HFS_FORCE_LOCK
);
504 retval
= hfs_update(vp
, TRUE
);
506 /* Updating vcbWrCnt doesn't need to be atomic. */
509 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_END
,
510 (int)uio_offset(uio
), uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
514 hfs_unlock_truncate(cp
);
518 /* support for the "bulk-access" fcntl */
520 #define CACHE_ELEMS 64
521 #define CACHE_LEVELS 16
522 #define PARENT_IDS_FLAG 0x100
524 /* from hfs_attrlist.c */
525 extern unsigned long DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
,
526 mode_t obj_mode
, struct mount
*mp
,
527 kauth_cred_t cred
, struct proc
*p
);
529 /* from vfs/vfs_fsevents.c */
530 extern char *get_pathbuff(void);
531 extern void release_pathbuff(char *buff
);
533 struct access_cache
{
535 int cachehits
; /* these two for statistics gathering */
537 unsigned int *acache
;
542 uid_t uid
; /* IN: effective user id */
543 short flags
; /* IN: access requested (i.e. R_OK) */
544 short num_groups
; /* IN: number of groups user belongs to */
545 int num_files
; /* IN: number of files to process */
546 int *file_ids
; /* IN: array of file ids */
547 gid_t
*groups
; /* IN: array of groups */
548 short *access
; /* OUT: access info for each file (0 for 'has access') */
551 struct user_access_t
{
552 uid_t uid
; /* IN: effective user id */
553 short flags
; /* IN: access requested (i.e. R_OK) */
554 short num_groups
; /* IN: number of groups user belongs to */
555 int num_files
; /* IN: number of files to process */
556 user_addr_t file_ids
; /* IN: array of file ids */
557 user_addr_t groups
; /* IN: array of groups */
558 user_addr_t access
; /* OUT: access info for each file (0 for 'has access') */
562 * Perform a binary search for the given parent_id. Return value is
563 * found/not found boolean, and indexp will be the index of the item
564 * or the index at which to insert the item if it's not found.
567 lookup_bucket(struct access_cache
*cache
, int *indexp
, cnid_t parent_id
)
570 int index
, matches
= 0;
572 if (cache
->numcached
== 0) {
574 return 0; // table is empty, so insert at index=0 and report no match
577 if (cache
->numcached
> CACHE_ELEMS
) {
578 /*printf("EGAD! numcached is %d... cut our losses and trim to %d\n",
579 cache->numcached, CACHE_ELEMS);*/
580 cache
->numcached
= CACHE_ELEMS
;
584 hi
= cache
->numcached
- 1;
587 /* perform binary search for parent_id */
589 unsigned int mid
= (hi
- lo
)/2 + lo
;
590 unsigned int this_id
= cache
->acache
[mid
];
592 if (parent_id
== this_id
) {
597 if (parent_id
< this_id
) {
602 if (parent_id
> this_id
) {
608 /* check if lo and hi converged on the match */
609 if (parent_id
== cache
->acache
[hi
]) {
613 /* if no existing entry found, find index for new one */
615 index
= (parent_id
< cache
->acache
[hi
]) ? hi
: hi
+ 1;
626 * Add a node to the access_cache at the given index (or do a lookup first
627 * to find the index if -1 is passed in). We currently do a replace rather
628 * than an insert if the cache is full.
631 add_node(struct access_cache
*cache
, int index
, cnid_t nodeID
, int access
)
633 int lookup_index
= -1;
635 /* need to do a lookup first if -1 passed for index */
637 if (lookup_bucket(cache
, &lookup_index
, nodeID
)) {
638 if (cache
->haveaccess
[lookup_index
] != access
) {
639 /* change access info for existing entry... should never happen */
640 cache
->haveaccess
[lookup_index
] = access
;
643 /* mission accomplished */
646 index
= lookup_index
;
651 /* if the cache is full, do a replace rather than an insert */
652 if (cache
->numcached
>= CACHE_ELEMS
) {
653 //printf("cache is full (%d). replace at index %d\n", cache->numcached, index);
654 cache
->numcached
= CACHE_ELEMS
-1;
656 if (index
> cache
->numcached
) {
657 // printf("index %d pinned to %d\n", index, cache->numcached);
658 index
= cache
->numcached
;
660 } else if (index
>= 0 && index
< cache
->numcached
) {
661 /* only do bcopy if we're inserting */
662 bcopy( cache
->acache
+index
, cache
->acache
+(index
+1), (cache
->numcached
- index
)*sizeof(int) );
663 bcopy( cache
->haveaccess
+index
, cache
->haveaccess
+(index
+1), (cache
->numcached
- index
)*sizeof(Boolean
) );
666 cache
->acache
[index
] = nodeID
;
667 cache
->haveaccess
[index
] = access
;
680 snoop_callback(const struct cat_desc
*descp
, const struct cat_attr
*attrp
, void * arg
)
682 struct cinfo
*cip
= (struct cinfo
*)arg
;
684 cip
->uid
= attrp
->ca_uid
;
685 cip
->gid
= attrp
->ca_gid
;
686 cip
->mode
= attrp
->ca_mode
;
687 cip
->parentcnid
= descp
->cd_parentcnid
;
693 * Lookup the cnid's attr info (uid, gid, and mode) as well as its parent id. If the item
694 * isn't incore, then go to the catalog.
697 do_attr_lookup(struct hfsmount
*hfsmp
, struct access_cache
*cache
, dev_t dev
, cnid_t cnid
,
698 struct cnode
*skip_cp
, CatalogKey
*keyp
, struct cat_attr
*cnattrp
, struct proc
*p
)
702 /* if this id matches the one the fsctl was called with, skip the lookup */
703 if (cnid
== skip_cp
->c_cnid
) {
704 cnattrp
->ca_uid
= skip_cp
->c_uid
;
705 cnattrp
->ca_gid
= skip_cp
->c_gid
;
706 cnattrp
->ca_mode
= skip_cp
->c_mode
;
707 keyp
->hfsPlus
.parentID
= skip_cp
->c_parentcnid
;
711 /* otherwise, check the cnode hash incase the file/dir is incore */
712 if (hfs_chash_snoop(dev
, cnid
, snoop_callback
, &c_info
) == 0) {
713 cnattrp
->ca_uid
= c_info
.uid
;
714 cnattrp
->ca_gid
= c_info
.gid
;
715 cnattrp
->ca_mode
= c_info
.mode
;
716 keyp
->hfsPlus
.parentID
= c_info
.parentcnid
;
720 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
722 /* lookup this cnid in the catalog */
723 error
= cat_getkeyplusattr(hfsmp
, cnid
, keyp
, cnattrp
);
725 hfs_systemfile_unlock(hfsmp
, lockflags
);
735 * Compute whether we have access to the given directory (nodeID) and all its parents. Cache
736 * up to CACHE_LEVELS as we progress towards the root.
739 do_access_check(struct hfsmount
*hfsmp
, int *err
, struct access_cache
*cache
, HFSCatalogNodeID nodeID
,
740 struct cnode
*skip_cp
, struct proc
*theProcPtr
, kauth_cred_t myp_ucred
, dev_t dev
)
744 HFSCatalogNodeID thisNodeID
;
745 unsigned long myPerms
;
746 struct cat_attr cnattr
;
747 int cache_index
= -1;
750 int i
= 0, ids_to_cache
= 0;
751 int parent_ids
[CACHE_LEVELS
];
753 /* root always has access */
754 if (!suser(myp_ucred
, NULL
)) {
759 while (thisNodeID
>= kRootDirID
) {
760 myResult
= 0; /* default to "no access" */
762 /* check the cache before resorting to hitting the catalog */
764 /* ASSUMPTION: access info of cached entries is "final"... i.e. no need
765 * to look any further after hitting cached dir */
767 if (lookup_bucket(cache
, &cache_index
, thisNodeID
)) {
769 myResult
= cache
->haveaccess
[cache_index
];
770 goto ExitThisRoutine
;
773 /* remember which parents we want to cache */
774 if (ids_to_cache
< CACHE_LEVELS
) {
775 parent_ids
[ids_to_cache
] = thisNodeID
;
779 /* do the lookup (checks the cnode hash, then the catalog) */
780 myErr
= do_attr_lookup(hfsmp
, cache
, dev
, thisNodeID
, skip_cp
, &catkey
, &cnattr
, theProcPtr
);
782 goto ExitThisRoutine
; /* no access */
785 myPerms
= DerivePermissionSummary(cnattr
.ca_uid
, cnattr
.ca_gid
,
786 cnattr
.ca_mode
, hfsmp
->hfs_mp
,
787 myp_ucred
, theProcPtr
);
789 if ( (myPerms
& X_OK
) == 0 ) {
791 goto ExitThisRoutine
; /* no access */
794 /* up the hierarchy we go */
795 thisNodeID
= catkey
.hfsPlus
.parentID
;
798 /* if here, we have access to this node */
803 //printf("*** error %d from catalog looking up parent %d/%d!\n", myErr, dev, thisNodeID);
808 /* cache the parent directory(ies) */
809 for (i
= 0; i
< ids_to_cache
; i
++) {
810 /* small optimization: get rid of double-lookup for all these */
811 // printf("adding %d to cache with result: %d\n", parent_ids[i], myResult);
812 add_node(cache
, -1, parent_ids
[i
], myResult
);
817 /* end "bulk-access" support */
822 * Callback for use with freeze ioctl.
825 hfs_freezewrite_callback(struct vnode
*vp
, void *cargs
)
827 vnode_waitforwrites(vp
, 0, 0, 0, "hfs freeze");
833 * Control filesystem operating characteristics.
836 hfs_vnop_ioctl( struct vnop_ioctl_args
/* {
841 vfs_context_t a_context;
844 struct vnode
* vp
= ap
->a_vp
;
845 struct hfsmount
*hfsmp
= VTOHFS(vp
);
846 vfs_context_t context
= ap
->a_context
;
847 kauth_cred_t cred
= vfs_context_ucred(context
);
848 proc_t p
= vfs_context_proc(context
);
849 struct vfsstatfs
*vfsp
;
852 is64bit
= proc_is64bit(p
);
854 switch (ap
->a_command
) {
856 case HFS_RESIZE_VOLUME
: {
860 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
861 if (suser(cred
, NULL
) &&
862 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
863 return (EACCES
); /* must be owner of file system */
865 if (!vnode_isvroot(vp
)) {
868 newsize
= *(u_int64_t
*)ap
->a_data
;
869 cursize
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
871 if (newsize
> cursize
) {
872 return hfs_extendfs(hfsmp
, *(u_int64_t
*)ap
->a_data
, context
);
873 } else if (newsize
< cursize
) {
874 return hfs_truncatefs(hfsmp
, *(u_int64_t
*)ap
->a_data
, context
);
879 case HFS_CHANGE_NEXT_ALLOCATION
: {
882 if (vnode_vfsisrdonly(vp
)) {
885 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
886 if (suser(cred
, NULL
) &&
887 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
888 return (EACCES
); /* must be owner of file system */
890 if (!vnode_isvroot(vp
)) {
893 location
= *(u_int32_t
*)ap
->a_data
;
894 if (location
> hfsmp
->totalBlocks
- 1) {
897 /* Return previous value. */
898 *(u_int32_t
*)ap
->a_data
= hfsmp
->nextAllocation
;
899 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
900 hfsmp
->nextAllocation
= location
;
901 hfsmp
->vcbFlags
|= 0xFF00;
902 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
906 #ifdef HFS_SPARSE_DEV
907 case HFS_SETBACKINGSTOREINFO
: {
908 struct vnode
* bsfs_rootvp
;
909 struct vnode
* di_vp
;
910 struct hfs_backingstoreinfo
*bsdata
;
913 if (hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) {
916 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
917 if (suser(cred
, NULL
) &&
918 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
919 return (EACCES
); /* must be owner of file system */
921 bsdata
= (struct hfs_backingstoreinfo
*)ap
->a_data
;
922 if (bsdata
== NULL
) {
925 if ((error
= file_vnode(bsdata
->backingfd
, &di_vp
))) {
928 if ((error
= vnode_getwithref(di_vp
))) {
929 file_drop(bsdata
->backingfd
);
933 if (vnode_mount(vp
) == vnode_mount(di_vp
)) {
934 (void)vnode_put(di_vp
);
935 file_drop(bsdata
->backingfd
);
940 * Obtain the backing fs root vnode and keep a reference
941 * on it. This reference will be dropped in hfs_unmount.
943 error
= VFS_ROOT(vnode_mount(di_vp
), &bsfs_rootvp
, NULL
); /* XXX use context! */
945 (void)vnode_put(di_vp
);
946 file_drop(bsdata
->backingfd
);
949 vnode_ref(bsfs_rootvp
);
950 vnode_put(bsfs_rootvp
);
952 hfsmp
->hfs_backingfs_rootvp
= bsfs_rootvp
;
953 hfsmp
->hfs_flags
|= HFS_HAS_SPARSE_DEVICE
;
954 hfsmp
->hfs_sparsebandblks
= bsdata
->bandsize
/ HFSTOVCB(hfsmp
)->blockSize
;
955 hfsmp
->hfs_sparsebandblks
*= 4;
957 (void)vnode_put(di_vp
);
958 file_drop(bsdata
->backingfd
);
961 case HFS_CLRBACKINGSTOREINFO
: {
962 struct vnode
* tmpvp
;
964 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
965 if (suser(cred
, NULL
) &&
966 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
967 return (EACCES
); /* must be owner of file system */
969 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) &&
970 hfsmp
->hfs_backingfs_rootvp
) {
972 hfsmp
->hfs_flags
&= ~HFS_HAS_SPARSE_DEVICE
;
973 tmpvp
= hfsmp
->hfs_backingfs_rootvp
;
974 hfsmp
->hfs_backingfs_rootvp
= NULLVP
;
975 hfsmp
->hfs_sparsebandblks
= 0;
980 #endif /* HFS_SPARSE_DEV */
989 mp
= vnode_mount(vp
);
990 hfsmp
= VFSTOHFS(mp
);
995 lck_rw_lock_exclusive(&hfsmp
->hfs_insync
);
997 task
= current_task();
998 task_working_set_disable(task
);
1000 // flush things before we get started to try and prevent
1001 // dirty data from being paged out while we're frozen.
1002 // note: can't do this after taking the lock as it will
1003 // deadlock against ourselves.
1004 vnode_iterate(mp
, 0, hfs_freezewrite_callback
, NULL
);
1005 hfs_global_exclusive_lock_acquire(hfsmp
);
1006 journal_flush(hfsmp
->jnl
);
1008 // don't need to iterate on all vnodes, we just need to
1009 // wait for writes to the system files and the device vnode
1010 if (HFSTOVCB(hfsmp
)->extentsRefNum
)
1011 vnode_waitforwrites(HFSTOVCB(hfsmp
)->extentsRefNum
, 0, 0, 0, "hfs freeze");
1012 if (HFSTOVCB(hfsmp
)->catalogRefNum
)
1013 vnode_waitforwrites(HFSTOVCB(hfsmp
)->catalogRefNum
, 0, 0, 0, "hfs freeze");
1014 if (HFSTOVCB(hfsmp
)->allocationsRefNum
)
1015 vnode_waitforwrites(HFSTOVCB(hfsmp
)->allocationsRefNum
, 0, 0, 0, "hfs freeze");
1016 if (hfsmp
->hfs_attribute_vp
)
1017 vnode_waitforwrites(hfsmp
->hfs_attribute_vp
, 0, 0, 0, "hfs freeze");
1018 vnode_waitforwrites(hfsmp
->hfs_devvp
, 0, 0, 0, "hfs freeze");
1020 hfsmp
->hfs_freezing_proc
= current_proc();
1029 // if we're not the one who froze the fs then we
1031 if (hfsmp
->hfs_freezing_proc
!= current_proc()) {
1035 // NOTE: if you add code here, also go check the
1036 // code that "thaws" the fs in hfs_vnop_close()
1038 hfsmp
->hfs_freezing_proc
= NULL
;
1039 hfs_global_exclusive_lock_release(hfsmp
);
1040 lck_rw_unlock_exclusive(&hfsmp
->hfs_insync
);
1045 #define HFSIOC_BULKACCESS _IOW('h', 9, struct access_t)
1046 #define HFS_BULKACCESS_FSCTL IOCBASECMD(HFSIOC_BULKACCESS)
1048 case HFS_BULKACCESS_FSCTL
:
1049 case HFS_BULKACCESS
: {
1051 * NOTE: on entry, the vnode is locked. Incase this vnode
1052 * happens to be in our list of file_ids, we'll note it
1053 * avoid calling hfs_chashget_nowait() on that id as that
1054 * will cause a "locking against myself" panic.
1056 Boolean check_leaf
= true;
1058 struct user_access_t
*user_access_structp
;
1059 struct user_access_t tmp_user_access_t
;
1060 struct access_cache cache
;
1064 dev_t dev
= VTOC(vp
)->c_dev
;
1067 struct ucred myucred
; /* XXX ILLEGAL */
1069 int *file_ids
= NULL
;
1070 short *access
= NULL
;
1073 cnid_t prevParent_cnid
= 0;
1074 unsigned long myPerms
;
1076 struct cat_attr cnattr
;
1078 struct cnode
*skip_cp
= VTOC(vp
);
1079 struct vfs_context my_context
;
1081 /* first, return error if not run as root */
1082 if (cred
->cr_ruid
!= 0) {
1086 /* initialize the local cache and buffers */
1087 cache
.numcached
= 0;
1088 cache
.cachehits
= 0;
1091 file_ids
= (int *) get_pathbuff();
1092 access
= (short *) get_pathbuff();
1093 cache
.acache
= (int *) get_pathbuff();
1094 cache
.haveaccess
= (Boolean
*) get_pathbuff();
1096 if (file_ids
== NULL
|| access
== NULL
|| cache
.acache
== NULL
|| cache
.haveaccess
== NULL
) {
1097 release_pathbuff((char *) file_ids
);
1098 release_pathbuff((char *) access
);
1099 release_pathbuff((char *) cache
.acache
);
1100 release_pathbuff((char *) cache
.haveaccess
);
1105 /* struct copyin done during dispatch... need to copy file_id array separately */
1106 if (ap
->a_data
== NULL
) {
1108 goto err_exit_bulk_access
;
1112 user_access_structp
= (struct user_access_t
*)ap
->a_data
;
1115 struct access_t
* accessp
= (struct access_t
*)ap
->a_data
;
1116 tmp_user_access_t
.uid
= accessp
->uid
;
1117 tmp_user_access_t
.flags
= accessp
->flags
;
1118 tmp_user_access_t
.num_groups
= accessp
->num_groups
;
1119 tmp_user_access_t
.num_files
= accessp
->num_files
;
1120 tmp_user_access_t
.file_ids
= CAST_USER_ADDR_T(accessp
->file_ids
);
1121 tmp_user_access_t
.groups
= CAST_USER_ADDR_T(accessp
->groups
);
1122 tmp_user_access_t
.access
= CAST_USER_ADDR_T(accessp
->access
);
1123 user_access_structp
= &tmp_user_access_t
;
1126 num_files
= user_access_structp
->num_files
;
1127 if (num_files
< 1) {
1128 goto err_exit_bulk_access
;
1130 if (num_files
> 256) {
1132 goto err_exit_bulk_access
;
1135 if ((error
= copyin(user_access_structp
->file_ids
, (caddr_t
)file_ids
,
1136 num_files
* sizeof(int)))) {
1137 goto err_exit_bulk_access
;
1140 /* fill in the ucred structure */
1141 flags
= user_access_structp
->flags
;
1142 if ((flags
& (F_OK
| R_OK
| W_OK
| X_OK
)) == 0) {
1146 /* check if we've been passed leaf node ids or parent ids */
1147 if (flags
& PARENT_IDS_FLAG
) {
1151 memset(&myucred
, 0, sizeof(myucred
));
1153 myucred
.cr_uid
= myucred
.cr_ruid
= myucred
.cr_svuid
= user_access_structp
->uid
;
1154 myucred
.cr_ngroups
= user_access_structp
->num_groups
;
1155 if (myucred
.cr_ngroups
< 1 || myucred
.cr_ngroups
> 16) {
1156 myucred
.cr_ngroups
= 0;
1157 } else if ((error
= copyin(user_access_structp
->groups
, (caddr_t
)myucred
.cr_groups
,
1158 myucred
.cr_ngroups
* sizeof(gid_t
)))) {
1159 goto err_exit_bulk_access
;
1161 myucred
.cr_rgid
= myucred
.cr_svgid
= myucred
.cr_groups
[0];
1163 my_context
.vc_proc
= p
;
1164 my_context
.vc_ucred
= &myucred
;
1166 /* Check access to each file_id passed in */
1167 for (i
= 0; i
< num_files
; i
++) {
1169 cnid
= (cnid_t
) file_ids
[i
];
1171 /* root always has access */
1172 if (!suser(&myucred
, NULL
)) {
1179 /* do the lookup (checks the cnode hash, then the catalog) */
1180 error
= do_attr_lookup(hfsmp
, &cache
, dev
, cnid
, skip_cp
, &catkey
, &cnattr
, p
);
1182 access
[i
] = (short) error
;
1186 /* before calling CheckAccess(), check the target file for read access */
1187 myPerms
= DerivePermissionSummary(cnattr
.ca_uid
, cnattr
.ca_gid
,
1188 cnattr
.ca_mode
, hfsmp
->hfs_mp
, &myucred
, p
);
1191 /* fail fast if no access */
1192 if ((myPerms
& flags
) == 0) {
1197 /* we were passed an array of parent ids */
1198 catkey
.hfsPlus
.parentID
= cnid
;
1201 /* if the last guy had the same parent and had access, we're done */
1202 if (i
> 0 && catkey
.hfsPlus
.parentID
== prevParent_cnid
&& access
[i
-1] == 0) {
1208 myaccess
= do_access_check(hfsmp
, &error
, &cache
, catkey
.hfsPlus
.parentID
,
1209 skip_cp
, p
, &myucred
, dev
);
1212 access
[i
] = 0; // have access.. no errors to report
1214 access
[i
] = (error
!= 0 ? (short) error
: EACCES
);
1217 prevParent_cnid
= catkey
.hfsPlus
.parentID
;
1221 cnid
= (cnid_t
)file_ids
[i
];
1223 while (cnid
>= kRootDirID
) {
1224 /* get the vnode for this cnid */
1225 myErr
= hfs_vget(hfsmp
, cnid
, &vp
, 0);
1231 cnid
= VTOC(vp
)->c_parentcnid
;
1233 hfs_unlock(VTOC(vp
));
1234 if (vnode_vtype(vp
) == VDIR
) {
1235 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
| KAUTH_VNODE_LIST_DIRECTORY
), &my_context
);
1237 myErr
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, &my_context
);
1248 /* copyout the access array */
1249 if ((error
= copyout((caddr_t
)access
, user_access_structp
->access
,
1250 num_files
* sizeof (short)))) {
1251 goto err_exit_bulk_access
;
1254 err_exit_bulk_access
:
1256 //printf("on exit (err %d), numfiles/numcached/cachehits/lookups is %d/%d/%d/%d\n", error, num_files, cache.numcached, cache.cachehits, cache.lookups);
1258 release_pathbuff((char *) cache
.acache
);
1259 release_pathbuff((char *) cache
.haveaccess
);
1260 release_pathbuff((char *) file_ids
);
1261 release_pathbuff((char *) access
);
1264 } /* HFS_BULKACCESS */
1266 case HFS_SETACLSTATE
: {
1269 if (ap
->a_data
== NULL
) {
1273 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
1274 state
= *(int *)ap
->a_data
;
1276 // super-user can enable or disable acl's on a volume.
1277 // the volume owner can only enable acl's
1278 if (!is_suser() && (state
== 0 || kauth_cred_getuid(cred
) != vfsp
->f_owner
)) {
1281 if (state
== 0 || state
== 1)
1282 return hfs_setextendedsecurity(hfsmp
, state
);
1290 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1292 error
= hfs_fsync(vp
, MNT_NOWAIT
, TRUE
, p
);
1293 hfs_unlock(VTOC(vp
));
1300 register struct cnode
*cp
;
1303 if (!vnode_isreg(vp
))
1306 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1310 * used by regression test to determine if
1311 * all the dirty pages (via write) have been cleaned
1312 * after a call to 'fsysnc'.
1314 error
= is_file_clean(vp
, VTOF(vp
)->ff_size
);
1321 register struct radvisory
*ra
;
1322 struct filefork
*fp
;
1325 if (!vnode_isreg(vp
))
1328 ra
= (struct radvisory
*)(ap
->a_data
);
1331 /* Protect against a size change. */
1332 hfs_lock_truncate(VTOC(vp
), TRUE
);
1334 if (ra
->ra_offset
>= fp
->ff_size
) {
1337 error
= advisory_read(vp
, fp
->ff_size
, ra
->ra_offset
, ra
->ra_count
);
1340 hfs_unlock_truncate(VTOC(vp
));
1344 case F_READBOOTSTRAP
:
1345 case F_WRITEBOOTSTRAP
:
1347 struct vnode
*devvp
= NULL
;
1348 user_fbootstraptransfer_t
*user_bootstrapp
;
1352 daddr64_t blockNumber
;
1356 user_fbootstraptransfer_t user_bootstrap
;
1358 if (!vnode_isvroot(vp
))
1360 /* LP64 - when caller is a 64 bit process then we are passed a pointer
1361 * to a user_fbootstraptransfer_t else we get a pointer to a
1362 * fbootstraptransfer_t which we munge into a user_fbootstraptransfer_t
1365 user_bootstrapp
= (user_fbootstraptransfer_t
*)ap
->a_data
;
1368 fbootstraptransfer_t
*bootstrapp
= (fbootstraptransfer_t
*)ap
->a_data
;
1369 user_bootstrapp
= &user_bootstrap
;
1370 user_bootstrap
.fbt_offset
= bootstrapp
->fbt_offset
;
1371 user_bootstrap
.fbt_length
= bootstrapp
->fbt_length
;
1372 user_bootstrap
.fbt_buffer
= CAST_USER_ADDR_T(bootstrapp
->fbt_buffer
);
1374 if (user_bootstrapp
->fbt_offset
+ user_bootstrapp
->fbt_length
> 1024)
1377 devvp
= VTOHFS(vp
)->hfs_devvp
;
1378 auio
= uio_create(1, user_bootstrapp
->fbt_offset
,
1379 is64bit
? UIO_USERSPACE64
: UIO_USERSPACE32
,
1380 (ap
->a_command
== F_WRITEBOOTSTRAP
) ? UIO_WRITE
: UIO_READ
);
1381 uio_addiov(auio
, user_bootstrapp
->fbt_buffer
, user_bootstrapp
->fbt_length
);
1383 devBlockSize
= vfs_devblocksize(vnode_mount(vp
));
1385 while (uio_resid(auio
) > 0) {
1386 blockNumber
= uio_offset(auio
) / devBlockSize
;
1387 error
= (int)buf_bread(devvp
, blockNumber
, devBlockSize
, cred
, &bp
);
1389 if (bp
) buf_brelse(bp
);
1394 blockOffset
= uio_offset(auio
) % devBlockSize
;
1395 xfersize
= devBlockSize
- blockOffset
;
1396 error
= uiomove((caddr_t
)buf_dataptr(bp
) + blockOffset
, (int)xfersize
, auio
);
1402 if (uio_rw(auio
) == UIO_WRITE
) {
1403 error
= VNOP_BWRITE(bp
);
1416 case _IOC(IOC_OUT
,'h', 4, 0): /* Create date in local time */
1419 *(user_time_t
*)(ap
->a_data
) = (user_time_t
) (to_bsd_time(VTOVCB(vp
)->localCreateDate
));
1422 *(time_t *)(ap
->a_data
) = to_bsd_time(VTOVCB(vp
)->localCreateDate
);
1427 case HFS_GET_MOUNT_TIME
:
1428 return copyout(&hfsmp
->hfs_mount_time
, CAST_USER_ADDR_T(ap
->a_data
), sizeof(hfsmp
->hfs_mount_time
));
1431 case HFS_GET_LAST_MTIME
:
1432 return copyout(&hfsmp
->hfs_last_mounted_mtime
, CAST_USER_ADDR_T(ap
->a_data
), sizeof(hfsmp
->hfs_last_mounted_mtime
));
1435 case HFS_SET_BOOT_INFO
:
1436 if (!vnode_isvroot(vp
))
1438 if (!kauth_cred_issuser(cred
) && (kauth_cred_getuid(cred
) != vfs_statfs(HFSTOVFS(hfsmp
))->f_owner
))
1439 return(EACCES
); /* must be superuser or owner of filesystem */
1440 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1441 bcopy(ap
->a_data
, &hfsmp
->vcbFndrInfo
, sizeof(hfsmp
->vcbFndrInfo
));
1442 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1443 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
1446 case HFS_GET_BOOT_INFO
:
1447 if (!vnode_isvroot(vp
))
1449 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1450 bcopy(&hfsmp
->vcbFndrInfo
, ap
->a_data
, sizeof(hfsmp
->vcbFndrInfo
));
1451 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1458 /* Should never get here */
1466 hfs_vnop_select(__unused
struct vnop_select_args
*ap
)
1468 struct vnop_select_args {
1473 vfs_context_t a_context;
1478 * We should really check to see if I/O is possible.
1484 * Converts a logical block number to a physical block, and optionally returns
1485 * the amount of remaining blocks in a run. The logical block is based on hfsNode.logBlockSize.
1486 * The physical block number is based on the device block size, currently its 512.
1487 * The block run is returned in logical blocks, and is the REMAINING amount of blocks
1490 hfs_bmap(struct vnode
*vp
, daddr_t bn
, struct vnode
**vpp
, daddr64_t
*bnp
, int *runp
)
1492 struct cnode
*cp
= VTOC(vp
);
1493 struct filefork
*fp
= VTOF(vp
);
1494 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1495 int retval
= E_NONE
;
1496 daddr_t logBlockSize
;
1497 size_t bytesContAvail
= 0;
1498 off_t blockposition
;
1503 * Check for underlying vnode requests and ensure that logical
1504 * to physical mapping is requested.
1511 logBlockSize
= GetLogicalBlockSize(vp
);
1512 blockposition
= (off_t
)bn
* (off_t
)logBlockSize
;
1514 lockExtBtree
= overflow_extents(fp
);
1517 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_SHARED_LOCK
);
1519 retval
= MacToVFSError(
1520 MapFileBlockC (HFSTOVCB(hfsmp
),
1528 hfs_systemfile_unlock(hfsmp
, lockflags
);
1530 if (retval
== E_NONE
) {
1531 /* Figure out how many read ahead blocks there are */
1533 if (can_cluster(logBlockSize
)) {
1534 /* Make sure this result never goes negative: */
1535 *runp
= (bytesContAvail
< logBlockSize
) ? 0 : (bytesContAvail
/ logBlockSize
) - 1;
1545 * Convert logical block number to file offset.
1548 hfs_vnop_blktooff(struct vnop_blktooff_args
*ap
)
1550 struct vnop_blktooff_args {
1557 if (ap
->a_vp
== NULL
)
1559 *ap
->a_offset
= (off_t
)ap
->a_lblkno
* (off_t
)GetLogicalBlockSize(ap
->a_vp
);
1565 * Convert file offset to logical block number.
1568 hfs_vnop_offtoblk(struct vnop_offtoblk_args
*ap
)
1570 struct vnop_offtoblk_args {
1573 daddr64_t *a_lblkno;
1577 if (ap
->a_vp
== NULL
)
1579 *ap
->a_lblkno
= (daddr64_t
)(ap
->a_offset
/ (off_t
)GetLogicalBlockSize(ap
->a_vp
));
1585 * Map file offset to physical block number.
1587 * System file cnodes are expected to be locked (shared or exclusive).
1590 hfs_vnop_blockmap(struct vnop_blockmap_args
*ap
)
1592 struct vnop_blockmap_args {
1600 vfs_context_t a_context;
1604 struct vnode
*vp
= ap
->a_vp
;
1606 struct filefork
*fp
;
1607 struct hfsmount
*hfsmp
;
1608 size_t bytesContAvail
= 0;
1609 int retval
= E_NONE
;
1612 struct rl_entry
*invalid_range
;
1613 enum rl_overlaptype overlaptype
;
1617 /* Do not allow blockmap operation on a directory */
1618 if (vnode_isdir(vp
)) {
1623 * Check for underlying vnode requests and ensure that logical
1624 * to physical mapping is requested.
1626 if (ap
->a_bpn
== NULL
)
1629 if ( !vnode_issystem(vp
) && !vnode_islnk(vp
)) {
1630 if (VTOC(vp
)->c_lockowner
!= current_thread()) {
1631 hfs_lock(VTOC(vp
), HFS_FORCE_LOCK
);
1635 panic("blockmap: %s cnode lock already held!\n",
1636 cp
->c_desc
.cd_nameptr
? cp
->c_desc
.cd_nameptr
: "");
1644 if (fp
->ff_unallocblocks
) {
1645 if (hfs_start_transaction(hfsmp
) != 0) {
1651 syslocks
= SFL_EXTENTS
| SFL_BITMAP
;
1653 } else if (overflow_extents(fp
)) {
1654 syslocks
= SFL_EXTENTS
;
1658 lockflags
= hfs_systemfile_lock(hfsmp
, syslocks
, HFS_EXCLUSIVE_LOCK
);
1661 * Check for any delayed allocations.
1663 if (fp
->ff_unallocblocks
) {
1665 u_int32_t loanedBlocks
;
1668 // Make sure we have a transaction. It's possible
1669 // that we came in and fp->ff_unallocblocks was zero
1670 // but during the time we blocked acquiring the extents
1671 // btree, ff_unallocblocks became non-zero and so we
1672 // will need to start a transaction.
1674 if (started_tr
== 0) {
1676 hfs_systemfile_unlock(hfsmp
, lockflags
);
1683 * Note: ExtendFileC will Release any blocks on loan and
1684 * aquire real blocks. So we ask to extend by zero bytes
1685 * since ExtendFileC will account for the virtual blocks.
1688 loanedBlocks
= fp
->ff_unallocblocks
;
1689 retval
= ExtendFileC(hfsmp
, (FCB
*)fp
, 0, 0,
1690 kEFAllMask
| kEFNoClumpMask
, &actbytes
);
1693 fp
->ff_unallocblocks
= loanedBlocks
;
1694 cp
->c_blocks
+= loanedBlocks
;
1695 fp
->ff_blocks
+= loanedBlocks
;
1697 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1698 hfsmp
->loanedBlocks
+= loanedBlocks
;
1699 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1703 hfs_systemfile_unlock(hfsmp
, lockflags
);
1704 cp
->c_flag
|= C_MODIFIED
;
1706 (void) hfs_update(vp
, TRUE
);
1707 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1709 hfs_end_transaction(hfsmp
);
1715 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, ap
->a_size
, ap
->a_foffset
,
1716 ap
->a_bpn
, &bytesContAvail
);
1718 hfs_systemfile_unlock(hfsmp
, lockflags
);
1723 (void) hfs_update(vp
, TRUE
);
1724 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1725 hfs_end_transaction(hfsmp
);
1732 /* Adjust the mapping information for invalid file ranges: */
1733 overlaptype
= rl_scan(&fp
->ff_invalidranges
, ap
->a_foffset
,
1734 ap
->a_foffset
+ (off_t
)bytesContAvail
- 1,
1736 if (overlaptype
!= RL_NOOVERLAP
) {
1737 switch(overlaptype
) {
1738 case RL_MATCHINGOVERLAP
:
1739 case RL_OVERLAPCONTAINSRANGE
:
1740 case RL_OVERLAPSTARTSBEFORE
:
1741 /* There's no valid block for this byte offset: */
1742 *ap
->a_bpn
= (daddr64_t
)-1;
1743 /* There's no point limiting the amount to be returned
1744 * if the invalid range that was hit extends all the way
1745 * to the EOF (i.e. there's no valid bytes between the
1746 * end of this range and the file's EOF):
1748 if (((off_t
)fp
->ff_size
> (invalid_range
->rl_end
+ 1)) &&
1749 (invalid_range
->rl_end
+ 1 - ap
->a_foffset
< bytesContAvail
)) {
1750 bytesContAvail
= invalid_range
->rl_end
+ 1 - ap
->a_foffset
;
1754 case RL_OVERLAPISCONTAINED
:
1755 case RL_OVERLAPENDSAFTER
:
1756 /* The range of interest hits an invalid block before the end: */
1757 if (invalid_range
->rl_start
== ap
->a_foffset
) {
1758 /* There's actually no valid information to be had starting here: */
1759 *ap
->a_bpn
= (daddr64_t
)-1;
1760 if (((off_t
)fp
->ff_size
> (invalid_range
->rl_end
+ 1)) &&
1761 (invalid_range
->rl_end
+ 1 - ap
->a_foffset
< bytesContAvail
)) {
1762 bytesContAvail
= invalid_range
->rl_end
+ 1 - ap
->a_foffset
;
1765 bytesContAvail
= invalid_range
->rl_start
- ap
->a_foffset
;
1772 if (bytesContAvail
> ap
->a_size
)
1773 bytesContAvail
= ap
->a_size
;
1776 *ap
->a_run
= bytesContAvail
;
1779 *(int *)ap
->a_poff
= 0;
1784 return (MacToVFSError(retval
));
1789 * prepare and issue the I/O
1790 * buf_strategy knows how to deal
1791 * with requests that require
1795 hfs_vnop_strategy(struct vnop_strategy_args
*ap
)
1797 buf_t bp
= ap
->a_bp
;
1798 vnode_t vp
= buf_vnode(bp
);
1799 struct cnode
*cp
= VTOC(vp
);
1801 return (buf_strategy(cp
->c_devvp
, ap
));
1806 do_hfs_truncate(struct vnode
*vp
, off_t length
, int flags
, int skipsetsize
, vfs_context_t context
)
1808 register struct cnode
*cp
= VTOC(vp
);
1809 struct filefork
*fp
= VTOF(vp
);
1810 struct proc
*p
= vfs_context_proc(context
);;
1811 kauth_cred_t cred
= vfs_context_ucred(context
);
1814 off_t actualBytesAdded
;
1816 u_int64_t old_filesize
;
1819 struct hfsmount
*hfsmp
;
1822 blksize
= VTOVCB(vp
)->blockSize
;
1823 fileblocks
= fp
->ff_blocks
;
1824 filebytes
= (off_t
)fileblocks
* (off_t
)blksize
;
1825 old_filesize
= fp
->ff_size
;
1827 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_START
,
1828 (int)length
, (int)fp
->ff_size
, (int)filebytes
, 0, 0);
1833 if ((!ISHFSPLUS(VTOVCB(vp
))) && (length
> (off_t
)MAXHFSFILESIZE
))
1840 /* Files that are changing size are not hot file candidates. */
1841 if (hfsmp
->hfc_stage
== HFC_RECORDING
) {
1842 fp
->ff_bytesread
= 0;
1846 * We cannot just check if fp->ff_size == length (as an optimization)
1847 * since there may be extra physical blocks that also need truncation.
1850 if ((retval
= hfs_getinoquota(cp
)))
1855 * Lengthen the size of the file. We must ensure that the
1856 * last byte of the file is allocated. Since the smallest
1857 * value of ff_size is 0, length will be at least 1.
1859 if (length
> (off_t
)fp
->ff_size
) {
1861 retval
= hfs_chkdq(cp
, (int64_t)(roundup(length
- filebytes
, blksize
)),
1867 * If we don't have enough physical space then
1868 * we need to extend the physical size.
1870 if (length
> filebytes
) {
1872 u_long blockHint
= 0;
1874 /* All or nothing and don't round up to clumpsize. */
1875 eflags
= kEFAllMask
| kEFNoClumpMask
;
1877 if (cred
&& suser(cred
, NULL
) != 0)
1878 eflags
|= kEFReserveMask
; /* keep a reserve */
1881 * Allocate Journal and Quota files in metadata zone.
1883 if (filebytes
== 0 &&
1884 hfsmp
->hfs_flags
& HFS_METADATA_ZONE
&&
1885 hfs_virtualmetafile(cp
)) {
1886 eflags
|= kEFMetadataMask
;
1887 blockHint
= hfsmp
->hfs_metazone_start
;
1889 if (hfs_start_transaction(hfsmp
) != 0) {
1894 /* Protect extents b-tree and allocation bitmap */
1895 lockflags
= SFL_BITMAP
;
1896 if (overflow_extents(fp
))
1897 lockflags
|= SFL_EXTENTS
;
1898 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
1900 while ((length
> filebytes
) && (retval
== E_NONE
)) {
1901 bytesToAdd
= length
- filebytes
;
1902 retval
= MacToVFSError(ExtendFileC(VTOVCB(vp
),
1907 &actualBytesAdded
));
1909 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)blksize
;
1910 if (actualBytesAdded
== 0 && retval
== E_NONE
) {
1911 if (length
> filebytes
)
1917 hfs_systemfile_unlock(hfsmp
, lockflags
);
1920 (void) hfs_update(vp
, TRUE
);
1921 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1924 hfs_end_transaction(hfsmp
);
1929 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_NONE
,
1930 (int)length
, (int)fp
->ff_size
, (int)filebytes
, 0, 0);
1933 if (!(flags
& IO_NOZEROFILL
)) {
1934 if (UBCINFOEXISTS(vp
) && retval
== E_NONE
) {
1935 struct rl_entry
*invalid_range
;
1938 zero_limit
= (fp
->ff_size
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
1939 if (length
< zero_limit
) zero_limit
= length
;
1941 if (length
> (off_t
)fp
->ff_size
) {
1944 /* Extending the file: time to fill out the current last page w. zeroes? */
1945 if ((fp
->ff_size
& PAGE_MASK_64
) &&
1946 (rl_scan(&fp
->ff_invalidranges
, fp
->ff_size
& ~PAGE_MASK_64
,
1947 fp
->ff_size
- 1, &invalid_range
) == RL_NOOVERLAP
)) {
1949 /* There's some valid data at the start of the (current) last page
1950 of the file, so zero out the remainder of that page to ensure the
1951 entire page contains valid data. Since there is no invalid range
1952 possible past the (current) eof, there's no need to remove anything
1953 from the invalid range list before calling cluster_write(): */
1955 retval
= cluster_write(vp
, (struct uio
*) 0, fp
->ff_size
, zero_limit
,
1956 fp
->ff_size
, (off_t
)0,
1957 (flags
& IO_SYNC
) | IO_HEADZEROFILL
| IO_NOZERODIRTY
);
1958 hfs_lock(cp
, HFS_FORCE_LOCK
);
1959 if (retval
) goto Err_Exit
;
1961 /* Merely invalidate the remaining area, if necessary: */
1962 if (length
> zero_limit
) {
1964 rl_add(zero_limit
, length
- 1, &fp
->ff_invalidranges
);
1965 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
1968 /* The page containing the (current) eof is invalid: just add the
1969 remainder of the page to the invalid list, along with the area
1970 being newly allocated:
1973 rl_add(fp
->ff_size
, length
- 1, &fp
->ff_invalidranges
);
1974 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
1978 panic("hfs_truncate: invoked on non-UBC object?!");
1981 cp
->c_touch_modtime
= TRUE
;
1982 fp
->ff_size
= length
;
1984 /* Nested transactions will do their own ubc_setsize. */
1987 * ubc_setsize can cause a pagein here
1988 * so we need to drop cnode lock.
1991 ubc_setsize(vp
, length
);
1992 hfs_lock(cp
, HFS_FORCE_LOCK
);
1995 } else { /* Shorten the size of the file */
1997 if ((off_t
)fp
->ff_size
> length
) {
1999 * Any buffers that are past the truncation point need to be
2000 * invalidated (to maintain buffer cache consistency).
2003 /* Nested transactions will do their own ubc_setsize. */
2006 * ubc_setsize can cause a pageout here
2007 * so we need to drop cnode lock.
2010 ubc_setsize(vp
, length
);
2011 hfs_lock(cp
, HFS_FORCE_LOCK
);
2014 /* Any space previously marked as invalid is now irrelevant: */
2015 rl_remove(length
, fp
->ff_size
- 1, &fp
->ff_invalidranges
);
2019 * Account for any unmapped blocks. Note that the new
2020 * file length can still end up with unmapped blocks.
2022 if (fp
->ff_unallocblocks
> 0) {
2023 u_int32_t finalblks
;
2024 u_int32_t loanedBlocks
;
2026 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
2028 loanedBlocks
= fp
->ff_unallocblocks
;
2029 cp
->c_blocks
-= loanedBlocks
;
2030 fp
->ff_blocks
-= loanedBlocks
;
2031 fp
->ff_unallocblocks
= 0;
2033 hfsmp
->loanedBlocks
-= loanedBlocks
;
2035 finalblks
= (length
+ blksize
- 1) / blksize
;
2036 if (finalblks
> fp
->ff_blocks
) {
2037 /* calculate required unmapped blocks */
2038 loanedBlocks
= finalblks
- fp
->ff_blocks
;
2039 hfsmp
->loanedBlocks
+= loanedBlocks
;
2041 fp
->ff_unallocblocks
= loanedBlocks
;
2042 cp
->c_blocks
+= loanedBlocks
;
2043 fp
->ff_blocks
+= loanedBlocks
;
2045 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2049 * For a TBE process the deallocation of the file blocks is
2050 * delayed until the file is closed. And hfs_close calls
2051 * truncate with the IO_NDELAY flag set. So when IO_NDELAY
2052 * isn't set, we make sure this isn't a TBE process.
2054 if ((flags
& IO_NDELAY
) || (proc_tbe(p
) == 0)) {
2056 off_t savedbytes
= ((off_t
)fp
->ff_blocks
* (off_t
)blksize
);
2058 if (hfs_start_transaction(hfsmp
) != 0) {
2063 if (fp
->ff_unallocblocks
== 0) {
2064 /* Protect extents b-tree and allocation bitmap */
2065 lockflags
= SFL_BITMAP
;
2066 if (overflow_extents(fp
))
2067 lockflags
|= SFL_EXTENTS
;
2068 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2070 retval
= MacToVFSError(TruncateFileC(VTOVCB(vp
),
2071 (FCB
*)fp
, length
, false));
2073 hfs_systemfile_unlock(hfsmp
, lockflags
);
2077 fp
->ff_size
= length
;
2079 (void) hfs_update(vp
, TRUE
);
2080 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2083 hfs_end_transaction(hfsmp
);
2085 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)blksize
;
2089 /* These are bytesreleased */
2090 (void) hfs_chkdq(cp
, (int64_t)-(savedbytes
- filebytes
), NOCRED
, 0);
2093 /* Only set update flag if the logical length changes */
2094 if (old_filesize
!= length
)
2095 cp
->c_touch_modtime
= TRUE
;
2096 fp
->ff_size
= length
;
2098 cp
->c_touch_chgtime
= TRUE
;
2099 retval
= hfs_update(vp
, MNT_WAIT
);
2101 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_NONE
,
2102 -1, -1, -1, retval
, 0);
2107 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_END
,
2108 (int)length
, (int)fp
->ff_size
, (int)filebytes
, retval
, 0);
2116 * Truncate a cnode to at most length size, freeing (or adding) the
2121 hfs_truncate(struct vnode
*vp
, off_t length
, int flags
, int skipsetsize
,
2122 vfs_context_t context
)
2124 struct filefork
*fp
= VTOF(vp
);
2127 int blksize
, error
= 0;
2128 struct cnode
*cp
= VTOC(vp
);
2130 if (vnode_isdir(vp
))
2131 return (EISDIR
); /* cannot truncate an HFS directory! */
2133 blksize
= VTOVCB(vp
)->blockSize
;
2134 fileblocks
= fp
->ff_blocks
;
2135 filebytes
= (off_t
)fileblocks
* (off_t
)blksize
;
2137 // have to loop truncating or growing files that are
2138 // really big because otherwise transactions can get
2139 // enormous and consume too many kernel resources.
2141 if (length
< filebytes
) {
2142 while (filebytes
> length
) {
2143 if ((filebytes
- length
) > HFS_BIGFILE_SIZE
) {
2144 filebytes
-= HFS_BIGFILE_SIZE
;
2148 cp
->c_flag
|= C_FORCEUPDATE
;
2149 error
= do_hfs_truncate(vp
, filebytes
, flags
, skipsetsize
, context
);
2153 } else if (length
> filebytes
) {
2154 while (filebytes
< length
) {
2155 if ((length
- filebytes
) > HFS_BIGFILE_SIZE
) {
2156 filebytes
+= HFS_BIGFILE_SIZE
;
2160 cp
->c_flag
|= C_FORCEUPDATE
;
2161 error
= do_hfs_truncate(vp
, filebytes
, flags
, skipsetsize
, context
);
2165 } else /* Same logical size */ {
2167 error
= do_hfs_truncate(vp
, length
, flags
, skipsetsize
, context
);
2169 /* Files that are changing size are not hot file candidates. */
2170 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) {
2171 fp
->ff_bytesread
= 0;
2180 * Preallocate file storage space.
2183 hfs_vnop_allocate(struct vnop_allocate_args
/* {
2187 off_t *a_bytesallocated;
2189 vfs_context_t a_context;
2192 struct vnode
*vp
= ap
->a_vp
;
2194 struct filefork
*fp
;
2196 off_t length
= ap
->a_length
;
2198 off_t moreBytesRequested
;
2199 off_t actualBytesAdded
;
2202 int retval
, retval2
;
2204 UInt32 extendFlags
; /* For call to ExtendFileC */
2205 struct hfsmount
*hfsmp
;
2206 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
2209 *(ap
->a_bytesallocated
) = 0;
2211 if (!vnode_isreg(vp
))
2213 if (length
< (off_t
)0)
2216 if ((retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2223 fileblocks
= fp
->ff_blocks
;
2224 filebytes
= (off_t
)fileblocks
* (off_t
)vcb
->blockSize
;
2226 if ((ap
->a_flags
& ALLOCATEFROMVOL
) && (length
< filebytes
)) {
2231 /* Fill in the flags word for the call to Extend the file */
2233 extendFlags
= kEFNoClumpMask
;
2234 if (ap
->a_flags
& ALLOCATECONTIG
)
2235 extendFlags
|= kEFContigMask
;
2236 if (ap
->a_flags
& ALLOCATEALL
)
2237 extendFlags
|= kEFAllMask
;
2238 if (cred
&& suser(cred
, NULL
) != 0)
2239 extendFlags
|= kEFReserveMask
;
2243 startingPEOF
= filebytes
;
2245 if (ap
->a_flags
& ALLOCATEFROMPEOF
)
2246 length
+= filebytes
;
2247 else if (ap
->a_flags
& ALLOCATEFROMVOL
)
2248 blockHint
= ap
->a_offset
/ VTOVCB(vp
)->blockSize
;
2250 /* If no changes are necesary, then we're done */
2251 if (filebytes
== length
)
2255 * Lengthen the size of the file. We must ensure that the
2256 * last byte of the file is allocated. Since the smallest
2257 * value of filebytes is 0, length will be at least 1.
2259 if (length
> filebytes
) {
2260 moreBytesRequested
= length
- filebytes
;
2263 retval
= hfs_chkdq(cp
,
2264 (int64_t)(roundup(moreBytesRequested
, vcb
->blockSize
)),
2271 * Metadata zone checks.
2273 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) {
2275 * Allocate Journal and Quota files in metadata zone.
2277 if (hfs_virtualmetafile(cp
)) {
2278 extendFlags
|= kEFMetadataMask
;
2279 blockHint
= hfsmp
->hfs_metazone_start
;
2280 } else if ((blockHint
>= hfsmp
->hfs_metazone_start
) &&
2281 (blockHint
<= hfsmp
->hfs_metazone_end
)) {
2283 * Move blockHint outside metadata zone.
2285 blockHint
= hfsmp
->hfs_metazone_end
+ 1;
2289 if (hfs_start_transaction(hfsmp
) != 0) {
2294 /* Protect extents b-tree and allocation bitmap */
2295 lockflags
= SFL_BITMAP
;
2296 if (overflow_extents(fp
))
2297 lockflags
|= SFL_EXTENTS
;
2298 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2300 retval
= MacToVFSError(ExtendFileC(vcb
,
2305 &actualBytesAdded
));
2307 *(ap
->a_bytesallocated
) = actualBytesAdded
;
2308 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)vcb
->blockSize
;
2310 hfs_systemfile_unlock(hfsmp
, lockflags
);
2313 (void) hfs_update(vp
, TRUE
);
2314 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2317 hfs_end_transaction(hfsmp
);
2320 * if we get an error and no changes were made then exit
2321 * otherwise we must do the hfs_update to reflect the changes
2323 if (retval
&& (startingPEOF
== filebytes
))
2327 * Adjust actualBytesAdded to be allocation block aligned, not
2328 * clump size aligned.
2329 * NOTE: So what we are reporting does not affect reality
2330 * until the file is closed, when we truncate the file to allocation
2333 if ((actualBytesAdded
!= 0) && (moreBytesRequested
< actualBytesAdded
))
2334 *(ap
->a_bytesallocated
) =
2335 roundup(moreBytesRequested
, (off_t
)vcb
->blockSize
);
2337 } else { /* Shorten the size of the file */
2339 if (fp
->ff_size
> length
) {
2341 * Any buffers that are past the truncation point need to be
2342 * invalidated (to maintain buffer cache consistency).
2346 if (hfs_start_transaction(hfsmp
) != 0) {
2351 /* Protect extents b-tree and allocation bitmap */
2352 lockflags
= SFL_BITMAP
;
2353 if (overflow_extents(fp
))
2354 lockflags
|= SFL_EXTENTS
;
2355 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2357 retval
= MacToVFSError(TruncateFileC(vcb
, (FCB
*)fp
, length
, false));
2359 hfs_systemfile_unlock(hfsmp
, lockflags
);
2361 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)vcb
->blockSize
;
2364 (void) hfs_update(vp
, TRUE
);
2365 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2368 hfs_end_transaction(hfsmp
);
2372 * if we get an error and no changes were made then exit
2373 * otherwise we must do the hfs_update to reflect the changes
2375 if (retval
&& (startingPEOF
== filebytes
)) goto Err_Exit
;
2377 /* These are bytesreleased */
2378 (void) hfs_chkdq(cp
, (int64_t)-((startingPEOF
- filebytes
)), NOCRED
,0);
2381 if (fp
->ff_size
> filebytes
) {
2382 fp
->ff_size
= filebytes
;
2385 ubc_setsize(vp
, fp
->ff_size
);
2386 hfs_lock(cp
, HFS_FORCE_LOCK
);
2391 cp
->c_touch_chgtime
= TRUE
;
2392 cp
->c_touch_modtime
= TRUE
;
2393 retval2
= hfs_update(vp
, MNT_WAIT
);
2404 * Pagein for HFS filesystem
2407 hfs_vnop_pagein(struct vnop_pagein_args
*ap
)
2409 struct vnop_pagein_args {
2412 vm_offset_t a_pl_offset,
2416 vfs_context_t a_context;
2420 vnode_t vp
= ap
->a_vp
;
2423 error
= cluster_pagein(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
2424 ap
->a_size
, (off_t
)VTOF(vp
)->ff_size
, ap
->a_flags
);
2426 * Keep track of blocks read.
2428 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
&& error
== 0) {
2430 struct filefork
*fp
;
2432 int took_cnode_lock
= 0;
2437 if (ap
->a_f_offset
== 0 && fp
->ff_size
< PAGE_SIZE
)
2438 bytesread
= fp
->ff_size
;
2440 bytesread
= ap
->a_size
;
2442 /* When ff_bytesread exceeds 32-bits, update it behind the cnode lock. */
2443 if ((fp
->ff_bytesread
+ bytesread
) > 0x00000000ffffffff) {
2444 hfs_lock(cp
, HFS_FORCE_LOCK
);
2445 took_cnode_lock
= 1;
2448 * If this file hasn't been seen since the start of
2449 * the current sampling period then start over.
2451 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
) {
2454 fp
->ff_bytesread
= bytesread
;
2456 cp
->c_atime
= tv
.tv_sec
;
2458 fp
->ff_bytesread
+= bytesread
;
2460 cp
->c_touch_acctime
= TRUE
;
2461 if (took_cnode_lock
)
2468 * Pageout for HFS filesystem.
2471 hfs_vnop_pageout(struct vnop_pageout_args
*ap
)
2473 struct vnop_pageout_args {
2476 vm_offset_t a_pl_offset,
2480 vfs_context_t a_context;
2484 vnode_t vp
= ap
->a_vp
;
2486 struct filefork
*fp
;
2492 if (cp
->c_lockowner
== current_thread()) {
2493 panic("pageout: %s cnode lock already held!\n",
2494 cp
->c_desc
.cd_nameptr
? cp
->c_desc
.cd_nameptr
: "");
2496 if ( (retval
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
2497 if (!(ap
->a_flags
& UPL_NOCOMMIT
)) {
2498 ubc_upl_abort_range(ap
->a_pl
,
2501 UPL_ABORT_FREE_ON_EMPTY
);
2507 filesize
= fp
->ff_size
;
2508 end_of_range
= ap
->a_f_offset
+ ap
->a_size
- 1;
2510 if (end_of_range
>= filesize
) {
2511 end_of_range
= (off_t
)(filesize
- 1);
2513 if (ap
->a_f_offset
< filesize
) {
2514 rl_remove(ap
->a_f_offset
, end_of_range
, &fp
->ff_invalidranges
);
2515 cp
->c_flag
|= C_MODIFIED
; /* leof is dirty */
2519 retval
= cluster_pageout(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
2520 ap
->a_size
, filesize
, ap
->a_flags
);
2523 * If data was written, and setuid or setgid bits are set and
2524 * this process is not the superuser then clear the setuid and
2525 * setgid bits as a precaution against tampering.
2527 if ((retval
== 0) &&
2528 (cp
->c_mode
& (S_ISUID
| S_ISGID
)) &&
2529 (vfs_context_suser(ap
->a_context
) != 0)) {
2530 hfs_lock(cp
, HFS_FORCE_LOCK
);
2531 cp
->c_mode
&= ~(S_ISUID
| S_ISGID
);
2532 cp
->c_touch_chgtime
= TRUE
;
2539 * Intercept B-Tree node writes to unswap them if necessary.
2542 hfs_vnop_bwrite(struct vnop_bwrite_args
*ap
)
2545 register struct buf
*bp
= ap
->a_bp
;
2546 register struct vnode
*vp
= buf_vnode(bp
);
2547 BlockDescriptor block
;
2549 /* Trap B-Tree writes */
2550 if ((VTOC(vp
)->c_fileid
== kHFSExtentsFileID
) ||
2551 (VTOC(vp
)->c_fileid
== kHFSCatalogFileID
) ||
2552 (VTOC(vp
)->c_fileid
== kHFSAttributesFileID
)) {
2555 * Swap and validate the node if it is in native byte order.
2556 * This is always be true on big endian, so we always validate
2557 * before writing here. On little endian, the node typically has
2558 * been swapped and validatated when it was written to the journal,
2559 * so we won't do anything here.
2561 if (((UInt16
*)((char *)buf_dataptr(bp
) + buf_count(bp
) - 2))[0] == 0x000e) {
2562 /* Prepare the block pointer */
2563 block
.blockHeader
= bp
;
2564 block
.buffer
= (char *)buf_dataptr(bp
);
2565 block
.blockNum
= buf_lblkno(bp
);
2566 /* not found in cache ==> came from disk */
2567 block
.blockReadFromDisk
= (buf_fromcache(bp
) == 0);
2568 block
.blockSize
= buf_count(bp
);
2570 /* Endian un-swap B-Tree node */
2571 retval
= hfs_swap_BTNode (&block
, vp
, kSwapBTNodeHostToBig
);
2573 panic("hfs_vnop_bwrite: about to write corrupt node!\n");
2577 /* This buffer shouldn't be locked anymore but if it is clear it */
2578 if ((buf_flags(bp
) & B_LOCKED
)) {
2580 if (VTOHFS(vp
)->jnl
) {
2581 panic("hfs: CLEARING the lock bit on bp 0x%x\n", bp
);
2583 buf_clearflags(bp
, B_LOCKED
);
2585 retval
= vn_bwrite (ap
);
2591 * Relocate a file to a new location on disk
2592 * cnode must be locked on entry
2594 * Relocation occurs by cloning the file's data from its
2595 * current set of blocks to a new set of blocks. During
2596 * the relocation all of the blocks (old and new) are
2597 * owned by the file.
2604 * ----------------- -----------------
2605 * |///////////////| | | STEP 1 (aquire new blocks)
2606 * ----------------- -----------------
2609 * ----------------- -----------------
2610 * |///////////////| |///////////////| STEP 2 (clone data)
2611 * ----------------- -----------------
2615 * |///////////////| STEP 3 (head truncate blocks)
2619 * During steps 2 and 3 page-outs to file offsets less
2620 * than or equal to N are suspended.
2622 * During step 3 page-ins to the file get supended.
2626 hfs_relocate(struct vnode
*vp
, u_int32_t blockHint
, kauth_cred_t cred
,
2630 struct filefork
*fp
;
2631 struct hfsmount
*hfsmp
;
2636 u_int32_t nextallocsave
;
2637 daddr64_t sector_a
, sector_b
;
2638 int disabled_caching
= 0;
2643 int took_trunc_lock
= 0;
2645 enum vtype vnodetype
;
2647 vnodetype
= vnode_vtype(vp
);
2648 if (vnodetype
!= VREG
&& vnodetype
!= VLNK
) {
2653 if (hfsmp
->hfs_flags
& HFS_FRAGMENTED_FREESPACE
) {
2659 if (fp
->ff_unallocblocks
)
2661 blksize
= hfsmp
->blockSize
;
2663 blockHint
= hfsmp
->nextAllocation
;
2665 if ((fp
->ff_size
> (u_int64_t
)0x7fffffff) ||
2666 ((fp
->ff_size
> blksize
) && vnodetype
== VLNK
)) {
2671 // We do not believe that this call to hfs_fsync() is
2672 // necessary and it causes a journal transaction
2673 // deadlock so we are removing it.
2675 //if (vnodetype == VREG && !vnode_issystem(vp)) {
2676 // retval = hfs_fsync(vp, MNT_WAIT, 0, p);
2681 if (!vnode_issystem(vp
) && (vnodetype
!= VLNK
)) {
2683 hfs_lock_truncate(cp
, TRUE
);
2684 if ((retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
2685 hfs_unlock_truncate(cp
);
2688 took_trunc_lock
= 1;
2690 headblks
= fp
->ff_blocks
;
2691 datablks
= howmany(fp
->ff_size
, blksize
);
2692 growsize
= datablks
* blksize
;
2693 eflags
= kEFContigMask
| kEFAllMask
| kEFNoClumpMask
;
2694 if (blockHint
>= hfsmp
->hfs_metazone_start
&&
2695 blockHint
<= hfsmp
->hfs_metazone_end
)
2696 eflags
|= kEFMetadataMask
;
2698 if (hfs_start_transaction(hfsmp
) != 0) {
2699 if (took_trunc_lock
)
2700 hfs_unlock_truncate(cp
);
2705 * Protect the extents b-tree and the allocation bitmap
2706 * during MapFileBlockC and ExtendFileC operations.
2708 lockflags
= SFL_BITMAP
;
2709 if (overflow_extents(fp
))
2710 lockflags
|= SFL_EXTENTS
;
2711 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2713 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, 1, growsize
- 1, §or_a
, NULL
);
2715 retval
= MacToVFSError(retval
);
2720 * STEP 1 - aquire new allocation blocks.
2722 if (!vnode_isnocache(vp
)) {
2723 vnode_setnocache(vp
);
2724 disabled_caching
= 1;
2727 nextallocsave
= hfsmp
->nextAllocation
;
2728 retval
= ExtendFileC(hfsmp
, (FCB
*)fp
, growsize
, blockHint
, eflags
, &newbytes
);
2729 if (eflags
& kEFMetadataMask
) {
2730 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
2731 hfsmp
->nextAllocation
= nextallocsave
;
2732 hfsmp
->vcbFlags
|= 0xFF00;
2733 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2736 retval
= MacToVFSError(retval
);
2738 cp
->c_flag
|= C_MODIFIED
;
2739 if (newbytes
< growsize
) {
2742 } else if (fp
->ff_blocks
< (headblks
+ datablks
)) {
2743 printf("hfs_relocate: allocation failed");
2748 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, 1, growsize
, §or_b
, NULL
);
2750 retval
= MacToVFSError(retval
);
2751 } else if ((sector_a
+ 1) == sector_b
) {
2754 } else if ((eflags
& kEFMetadataMask
) &&
2755 ((((u_int64_t
)sector_b
* hfsmp
->hfs_phys_block_size
) / blksize
) >
2756 hfsmp
->hfs_metazone_end
)) {
2757 printf("hfs_relocate: didn't move into metadata zone\n");
2762 /* Done with system locks and journal for now. */
2763 hfs_systemfile_unlock(hfsmp
, lockflags
);
2765 hfs_end_transaction(hfsmp
);
2770 * Check to see if failure is due to excessive fragmentation.
2772 if ((retval
== ENOSPC
) &&
2773 (hfs_freeblks(hfsmp
, 0) > (datablks
* 2))) {
2774 hfsmp
->hfs_flags
|= HFS_FRAGMENTED_FREESPACE
;
2779 * STEP 2 - clone file data into the new allocation blocks.
2782 if (vnodetype
== VLNK
)
2783 retval
= hfs_clonelink(vp
, blksize
, cred
, p
);
2784 else if (vnode_issystem(vp
))
2785 retval
= hfs_clonesysfile(vp
, headblks
, datablks
, blksize
, cred
, p
);
2787 retval
= hfs_clonefile(vp
, headblks
, datablks
, blksize
);
2789 /* Start transaction for step 3 or for a restore. */
2790 if (hfs_start_transaction(hfsmp
) != 0) {
2799 * STEP 3 - switch to cloned data and remove old blocks.
2801 lockflags
= SFL_BITMAP
;
2802 if (overflow_extents(fp
))
2803 lockflags
|= SFL_EXTENTS
;
2804 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2806 retval
= HeadTruncateFile(hfsmp
, (FCB
*)fp
, headblks
);
2808 hfs_systemfile_unlock(hfsmp
, lockflags
);
2813 if (took_trunc_lock
)
2814 hfs_unlock_truncate(cp
);
2817 hfs_systemfile_unlock(hfsmp
, lockflags
);
2821 // See comment up above about calls to hfs_fsync()
2824 // retval = hfs_fsync(vp, MNT_WAIT, 0, p);
2827 if (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)
2828 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
2830 (void) hfs_flushvolumeheader(hfsmp
, MNT_NOWAIT
, 0);
2833 if (disabled_caching
) {
2834 vnode_clearnocache(vp
);
2837 hfs_end_transaction(hfsmp
);
2842 if (fp
->ff_blocks
== headblks
)
2845 * Give back any newly allocated space.
2847 if (lockflags
== 0) {
2848 lockflags
= SFL_BITMAP
;
2849 if (overflow_extents(fp
))
2850 lockflags
|= SFL_EXTENTS
;
2851 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2854 (void) TruncateFileC(hfsmp
, (FCB
*)fp
, fp
->ff_size
, false);
2856 hfs_systemfile_unlock(hfsmp
, lockflags
);
2859 if (took_trunc_lock
)
2860 hfs_unlock_truncate(cp
);
2870 hfs_clonelink(struct vnode
*vp
, int blksize
, kauth_cred_t cred
, struct proc
*p
)
2872 struct buf
*head_bp
= NULL
;
2873 struct buf
*tail_bp
= NULL
;
2877 error
= (int)buf_meta_bread(vp
, (daddr64_t
)0, blksize
, cred
, &head_bp
);
2881 tail_bp
= buf_getblk(vp
, (daddr64_t
)1, blksize
, 0, 0, BLK_META
);
2882 if (tail_bp
== NULL
) {
2886 bcopy((char *)buf_dataptr(head_bp
), (char *)buf_dataptr(tail_bp
), blksize
);
2887 error
= (int)buf_bwrite(tail_bp
);
2890 buf_markinvalid(head_bp
);
2891 buf_brelse(head_bp
);
2893 (void) buf_invalidateblks(vp
, BUF_WRITE_DATA
, 0, 0);
2899 * Clone a file's data within the file.
2903 hfs_clonefile(struct vnode
*vp
, int blkstart
, int blkcnt
, int blksize
)
2915 filesize
= VTOF(vp
)->ff_blocks
* blksize
; /* virtual file size */
2916 writebase
= blkstart
* blksize
;
2917 copysize
= blkcnt
* blksize
;
2918 iosize
= bufsize
= MIN(copysize
, 4096 * 16);
2921 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufp
, bufsize
)) {
2924 hfs_unlock(VTOC(vp
));
2926 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2928 while (offset
< copysize
) {
2929 iosize
= MIN(copysize
- offset
, iosize
);
2931 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
2932 uio_addiov(auio
, (uintptr_t)bufp
, iosize
);
2934 error
= cluster_read(vp
, auio
, copysize
, 0);
2936 printf("hfs_clonefile: cluster_read failed - %d\n", error
);
2939 if (uio_resid(auio
) != 0) {
2940 printf("clonedata: cluster_read: uio_resid = %lld\n", uio_resid(auio
));
2945 uio_reset(auio
, writebase
+ offset
, UIO_SYSSPACE32
, UIO_WRITE
);
2946 uio_addiov(auio
, (uintptr_t)bufp
, iosize
);
2948 error
= cluster_write(vp
, auio
, filesize
+ offset
,
2949 filesize
+ offset
+ iosize
,
2950 uio_offset(auio
), 0, IO_NOCACHE
| IO_SYNC
);
2952 printf("hfs_clonefile: cluster_write failed - %d\n", error
);
2955 if (uio_resid(auio
) != 0) {
2956 printf("hfs_clonefile: cluster_write failed - uio_resid not zero\n");
2965 * No need to call ubc_sync_range or hfs_invalbuf
2966 * since the file was copied using IO_NOCACHE.
2969 kmem_free(kernel_map
, (vm_offset_t
)bufp
, bufsize
);
2971 hfs_lock(VTOC(vp
), HFS_FORCE_LOCK
);
2976 * Clone a system (metadata) file.
2980 hfs_clonesysfile(struct vnode
*vp
, int blkstart
, int blkcnt
, int blksize
,
2981 kauth_cred_t cred
, struct proc
*p
)
2987 struct buf
*bp
= NULL
;
2990 daddr64_t start_blk
;
2997 iosize
= GetLogicalBlockSize(vp
);
2998 bufsize
= MIN(blkcnt
* blksize
, 1024 * 1024) & ~(iosize
- 1);
2999 breadcnt
= bufsize
/ iosize
;
3001 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufp
, bufsize
)) {
3004 start_blk
= ((daddr64_t
)blkstart
* blksize
) / iosize
;
3005 last_blk
= ((daddr64_t
)blkcnt
* blksize
) / iosize
;
3008 while (blkno
< last_blk
) {
3010 * Read up to a megabyte
3013 for (i
= 0, blk
= blkno
; (i
< breadcnt
) && (blk
< last_blk
); ++i
, ++blk
) {
3014 error
= (int)buf_meta_bread(vp
, blk
, iosize
, cred
, &bp
);
3016 printf("hfs_clonesysfile: meta_bread error %d\n", error
);
3019 if (buf_count(bp
) != iosize
) {
3020 printf("hfs_clonesysfile: b_bcount is only %d\n", buf_count(bp
));
3023 bcopy((char *)buf_dataptr(bp
), offset
, iosize
);
3025 buf_markinvalid(bp
);
3033 * Write up to a megabyte
3036 for (i
= 0; (i
< breadcnt
) && (blkno
< last_blk
); ++i
, ++blkno
) {
3037 bp
= buf_getblk(vp
, start_blk
+ blkno
, iosize
, 0, 0, BLK_META
);
3039 printf("hfs_clonesysfile: getblk failed on blk %qd\n", start_blk
+ blkno
);
3043 bcopy(offset
, (char *)buf_dataptr(bp
), iosize
);
3044 error
= (int)buf_bwrite(bp
);
3056 kmem_free(kernel_map
, (vm_offset_t
)bufp
, bufsize
);
3058 error
= hfs_fsync(vp
, MNT_WAIT
, 0, p
);