2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
30 /* @(#)hfs_readwrite.c 1.0
32 * (c) 1998-2001 Apple Computer, Inc. All Rights Reserved
34 * hfs_readwrite.c -- vnode operations to deal with reading and writing files.
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/resourcevar.h>
41 #include <sys/kernel.h>
42 #include <sys/fcntl.h>
43 #include <sys/filedesc.h>
47 #include <sys/kauth.h>
48 #include <sys/vnode.h>
50 #include <sys/vfs_context.h>
52 #include <miscfs/specfs/specdev.h>
55 #include <vm/vm_pageout.h>
56 #include <vm/vm_kern.h>
58 #include <sys/kdebug.h>
61 #include "hfs_endian.h"
62 #include "hfs_fsctl.h"
63 #include "hfs_quota.h"
64 #include "hfscommon/headers/FileMgrInternal.h"
65 #include "hfscommon/headers/BTreesInternal.h"
66 #include "hfs_cnode.h"
69 extern int overflow_extents(struct filefork
*fp
);
71 #define can_cluster(size) ((((size & (4096-1))) == 0) && (size <= (MAXPHYSIO/2)))
74 MAXHFSFILESIZE
= 0x7FFFFFFF /* this needs to go in the mount structure */
77 extern u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
79 extern int hfs_setextendedsecurity(struct hfsmount
*, int);
82 static int hfs_clonelink(struct vnode
*, int, kauth_cred_t
, struct proc
*);
83 static int hfs_clonefile(struct vnode
*, int, int, int);
84 static int hfs_clonesysfile(struct vnode
*, int, int, int, kauth_cred_t
, struct proc
*);
87 /*****************************************************************************
89 * I/O Operations on vnodes
91 *****************************************************************************/
92 int hfs_vnop_read(struct vnop_read_args
*);
93 int hfs_vnop_write(struct vnop_write_args
*);
94 int hfs_vnop_ioctl(struct vnop_ioctl_args
*);
95 int hfs_vnop_select(struct vnop_select_args
*);
96 int hfs_vnop_blktooff(struct vnop_blktooff_args
*);
97 int hfs_vnop_offtoblk(struct vnop_offtoblk_args
*);
98 int hfs_vnop_blockmap(struct vnop_blockmap_args
*);
99 int hfs_vnop_strategy(struct vnop_strategy_args
*);
100 int hfs_vnop_allocate(struct vnop_allocate_args
*);
101 int hfs_vnop_pagein(struct vnop_pagein_args
*);
102 int hfs_vnop_pageout(struct vnop_pageout_args
*);
103 int hfs_vnop_bwrite(struct vnop_bwrite_args
*);
107 * Read data from a file.
110 hfs_vnop_read(struct vnop_read_args
*ap
)
112 uio_t uio
= ap
->a_uio
;
113 struct vnode
*vp
= ap
->a_vp
;
116 struct hfsmount
*hfsmp
;
119 off_t start_resid
= uio_resid(uio
);
120 off_t offset
= uio_offset(uio
);
124 /* Preflight checks */
125 if (!vnode_isreg(vp
)) {
126 /* can only read regular files */
132 if (start_resid
== 0)
133 return (0); /* Nothing left to do */
135 return (EINVAL
); /* cant read from a negative offset */
141 /* Protect against a size change. */
142 hfs_lock_truncate(cp
, 0);
144 filesize
= fp
->ff_size
;
145 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
146 if (offset
> filesize
) {
147 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) &&
148 (offset
> (off_t
)MAXHFSFILESIZE
)) {
154 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 12)) | DBG_FUNC_START
,
155 (int)uio_offset(uio
), uio_resid(uio
), (int)filesize
, (int)filebytes
, 0);
157 retval
= cluster_read(vp
, uio
, filesize
, 0);
159 cp
->c_touch_acctime
= TRUE
;
161 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 12)) | DBG_FUNC_END
,
162 (int)uio_offset(uio
), uio_resid(uio
), (int)filesize
, (int)filebytes
, 0);
165 * Keep track blocks read
167 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
&& retval
== 0) {
168 int took_cnode_lock
= 0;
171 bytesread
= start_resid
- uio_resid(uio
);
173 /* When ff_bytesread exceeds 32-bits, update it behind the cnode lock. */
174 if ((fp
->ff_bytesread
+ bytesread
) > 0x00000000ffffffff) {
175 hfs_lock(cp
, HFS_FORCE_LOCK
);
179 * If this file hasn't been seen since the start of
180 * the current sampling period then start over.
182 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
) {
185 fp
->ff_bytesread
= bytesread
;
187 cp
->c_atime
= tv
.tv_sec
;
189 fp
->ff_bytesread
+= bytesread
;
195 hfs_unlock_truncate(cp
);
200 * Write data to a file.
203 hfs_vnop_write(struct vnop_write_args
*ap
)
205 uio_t uio
= ap
->a_uio
;
206 struct vnode
*vp
= ap
->a_vp
;
209 struct hfsmount
*hfsmp
;
210 kauth_cred_t cred
= NULL
;
214 off_t actualBytesAdded
;
219 int ioflag
= ap
->a_ioflag
;
222 int cnode_locked
= 0;
224 // LP64todo - fix this! uio_resid may be 64-bit value
225 resid
= uio_resid(uio
);
226 offset
= uio_offset(uio
);
232 if (!vnode_isreg(vp
))
233 return (EPERM
); /* Can only write regular files */
235 /* Protect against a size change. */
236 hfs_lock_truncate(VTOC(vp
), TRUE
);
238 if ( (retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
239 hfs_unlock_truncate(VTOC(vp
));
246 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
248 if (ioflag
& IO_APPEND
) {
249 uio_setoffset(uio
, fp
->ff_size
);
250 offset
= fp
->ff_size
;
252 if ((cp
->c_flags
& APPEND
) && offset
!= fp
->ff_size
) {
257 origFileSize
= fp
->ff_size
;
258 eflags
= kEFDeferMask
; /* defer file block allocations */
260 #ifdef HFS_SPARSE_DEV
262 * When the underlying device is sparse and space
263 * is low (< 8MB), stop doing delayed allocations
264 * and begin doing synchronous I/O.
266 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) &&
267 (hfs_freeblks(hfsmp
, 0) < 2048)) {
268 eflags
&= ~kEFDeferMask
;
271 #endif /* HFS_SPARSE_DEV */
273 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_START
,
274 (int)offset
, uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
276 /* Now test if we need to extend the file */
277 /* Doing so will adjust the filebytes for us */
279 writelimit
= offset
+ resid
;
280 if (writelimit
<= filebytes
)
283 cred
= vfs_context_ucred(ap
->a_context
);
285 bytesToAdd
= writelimit
- filebytes
;
286 retval
= hfs_chkdq(cp
, (int64_t)(roundup(bytesToAdd
, hfsmp
->blockSize
)),
292 if (hfs_start_transaction(hfsmp
) != 0) {
297 while (writelimit
> filebytes
) {
298 bytesToAdd
= writelimit
- filebytes
;
299 if (cred
&& suser(cred
, NULL
) != 0)
300 eflags
|= kEFReserveMask
;
302 /* Protect extents b-tree and allocation bitmap */
303 lockflags
= SFL_BITMAP
;
304 if (overflow_extents(fp
))
305 lockflags
|= SFL_EXTENTS
;
306 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
308 /* Files that are changing size are not hot file candidates. */
309 if (hfsmp
->hfc_stage
== HFC_RECORDING
) {
310 fp
->ff_bytesread
= 0;
312 retval
= MacToVFSError(ExtendFileC (hfsmp
, (FCB
*)fp
, bytesToAdd
,
313 0, eflags
, &actualBytesAdded
));
315 hfs_systemfile_unlock(hfsmp
, lockflags
);
317 if ((actualBytesAdded
== 0) && (retval
== E_NONE
))
319 if (retval
!= E_NONE
)
321 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
322 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_NONE
,
323 (int)offset
, uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
325 (void) hfs_update(vp
, TRUE
);
326 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
327 (void) hfs_end_transaction(hfsmp
);
330 if (retval
== E_NONE
) {
338 struct rl_entry
*invalid_range
;
340 if (writelimit
> fp
->ff_size
)
341 filesize
= writelimit
;
343 filesize
= fp
->ff_size
;
345 lflag
= (ioflag
& IO_SYNC
);
347 if (offset
<= fp
->ff_size
) {
348 zero_off
= offset
& ~PAGE_MASK_64
;
350 /* Check to see whether the area between the zero_offset and the start
351 of the transfer to see whether is invalid and should be zero-filled
352 as part of the transfer:
354 if (offset
> zero_off
) {
355 if (rl_scan(&fp
->ff_invalidranges
, zero_off
, offset
- 1, &invalid_range
) != RL_NOOVERLAP
)
356 lflag
|= IO_HEADZEROFILL
;
359 off_t eof_page_base
= fp
->ff_size
& ~PAGE_MASK_64
;
361 /* The bytes between fp->ff_size and uio->uio_offset must never be
362 read without being zeroed. The current last block is filled with zeroes
363 if it holds valid data but in all cases merely do a little bookkeeping
364 to track the area from the end of the current last page to the start of
365 the area actually written. For the same reason only the bytes up to the
366 start of the page where this write will start is invalidated; any remainder
367 before uio->uio_offset is explicitly zeroed as part of the cluster_write.
369 Note that inval_start, the start of the page after the current EOF,
370 may be past the start of the write, in which case the zeroing
371 will be handled by the cluser_write of the actual data.
373 inval_start
= (fp
->ff_size
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
374 inval_end
= offset
& ~PAGE_MASK_64
;
375 zero_off
= fp
->ff_size
;
377 if ((fp
->ff_size
& PAGE_MASK_64
) &&
378 (rl_scan(&fp
->ff_invalidranges
,
381 &invalid_range
) != RL_NOOVERLAP
)) {
382 /* The page containing the EOF is not valid, so the
383 entire page must be made inaccessible now. If the write
384 starts on a page beyond the page containing the eof
385 (inval_end > eof_page_base), add the
386 whole page to the range to be invalidated. Otherwise
387 (i.e. if the write starts on the same page), zero-fill
388 the entire page explicitly now:
390 if (inval_end
> eof_page_base
) {
391 inval_start
= eof_page_base
;
393 zero_off
= eof_page_base
;
397 if (inval_start
< inval_end
) {
399 /* There's some range of data that's going to be marked invalid */
401 if (zero_off
< inval_start
) {
402 /* The pages between inval_start and inval_end are going to be invalidated,
403 and the actual write will start on a page past inval_end. Now's the last
404 chance to zero-fill the page containing the EOF:
408 retval
= cluster_write(vp
, (uio_t
) 0,
409 fp
->ff_size
, inval_start
,
411 lflag
| IO_HEADZEROFILL
| IO_NOZERODIRTY
);
412 hfs_lock(cp
, HFS_FORCE_LOCK
);
414 if (retval
) goto ioerr_exit
;
415 offset
= uio_offset(uio
);
418 /* Mark the remaining area of the newly allocated space as invalid: */
419 rl_add(inval_start
, inval_end
- 1 , &fp
->ff_invalidranges
);
421 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
422 zero_off
= fp
->ff_size
= inval_end
;
425 if (offset
> zero_off
) lflag
|= IO_HEADZEROFILL
;
428 /* Check to see whether the area between the end of the write and the end of
429 the page it falls in is invalid and should be zero-filled as part of the transfer:
431 tail_off
= (writelimit
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
432 if (tail_off
> filesize
) tail_off
= filesize
;
433 if (tail_off
> writelimit
) {
434 if (rl_scan(&fp
->ff_invalidranges
, writelimit
, tail_off
- 1, &invalid_range
) != RL_NOOVERLAP
) {
435 lflag
|= IO_TAILZEROFILL
;
440 * if the write starts beyond the current EOF (possibly advanced in the
441 * zeroing of the last block, above), then we'll zero fill from the current EOF
442 * to where the write begins:
444 * NOTE: If (and ONLY if) the portion of the file about to be written is
445 * before the current EOF it might be marked as invalid now and must be
446 * made readable (removed from the invalid ranges) before cluster_write
449 io_start
= (lflag
& IO_HEADZEROFILL
) ? zero_off
: offset
;
450 if (io_start
< fp
->ff_size
) {
453 io_end
= (lflag
& IO_TAILZEROFILL
) ? tail_off
: writelimit
;
454 rl_remove(io_start
, io_end
- 1, &fp
->ff_invalidranges
);
459 retval
= cluster_write(vp
, uio
, fp
->ff_size
, filesize
, zero_off
,
460 tail_off
, lflag
| IO_NOZERODIRTY
);
461 offset
= uio_offset(uio
);
462 if (offset
> fp
->ff_size
) {
463 fp
->ff_size
= offset
;
465 ubc_setsize(vp
, fp
->ff_size
); /* XXX check errors */
466 /* Files that are changing size are not hot file candidates. */
467 if (hfsmp
->hfc_stage
== HFC_RECORDING
)
468 fp
->ff_bytesread
= 0;
470 if (resid
> uio_resid(uio
)) {
471 cp
->c_touch_chgtime
= TRUE
;
472 cp
->c_touch_modtime
= TRUE
;
475 HFS_KNOTE(vp
, NOTE_WRITE
);
479 * If we successfully wrote any data, and we are not the superuser
480 * we clear the setuid and setgid bits as a precaution against
483 if (cp
->c_mode
& (S_ISUID
| S_ISGID
)) {
484 cred
= vfs_context_ucred(ap
->a_context
);
485 if (resid
> uio_resid(uio
) && cred
&& suser(cred
, NULL
)) {
487 hfs_lock(cp
, HFS_FORCE_LOCK
);
490 cp
->c_mode
&= ~(S_ISUID
| S_ISGID
);
494 if (ioflag
& IO_UNIT
) {
496 hfs_lock(cp
, HFS_FORCE_LOCK
);
499 (void)hfs_truncate(vp
, origFileSize
, ioflag
& IO_SYNC
,
501 // LP64todo - fix this! resid needs to by user_ssize_t
502 uio_setoffset(uio
, (uio_offset(uio
) - (resid
- uio_resid(uio
))));
503 uio_setresid(uio
, resid
);
504 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
506 } else if ((ioflag
& IO_SYNC
) && (resid
> uio_resid(uio
))) {
508 hfs_lock(cp
, HFS_FORCE_LOCK
);
511 retval
= hfs_update(vp
, TRUE
);
513 /* Updating vcbWrCnt doesn't need to be atomic. */
516 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_END
,
517 (int)uio_offset(uio
), uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
521 hfs_unlock_truncate(cp
);
525 /* support for the "bulk-access" fcntl */
527 #define CACHE_ELEMS 64
528 #define CACHE_LEVELS 16
529 #define PARENT_IDS_FLAG 0x100
531 /* from hfs_attrlist.c */
532 extern unsigned long DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
,
533 mode_t obj_mode
, struct mount
*mp
,
534 kauth_cred_t cred
, struct proc
*p
);
536 /* from vfs/vfs_fsevents.c */
537 extern char *get_pathbuff(void);
538 extern void release_pathbuff(char *buff
);
540 struct access_cache
{
542 int cachehits
; /* these two for statistics gathering */
544 unsigned int *acache
;
549 uid_t uid
; /* IN: effective user id */
550 short flags
; /* IN: access requested (i.e. R_OK) */
551 short num_groups
; /* IN: number of groups user belongs to */
552 int num_files
; /* IN: number of files to process */
553 int *file_ids
; /* IN: array of file ids */
554 gid_t
*groups
; /* IN: array of groups */
555 short *access
; /* OUT: access info for each file (0 for 'has access') */
558 struct user_access_t
{
559 uid_t uid
; /* IN: effective user id */
560 short flags
; /* IN: access requested (i.e. R_OK) */
561 short num_groups
; /* IN: number of groups user belongs to */
562 int num_files
; /* IN: number of files to process */
563 user_addr_t file_ids
; /* IN: array of file ids */
564 user_addr_t groups
; /* IN: array of groups */
565 user_addr_t access
; /* OUT: access info for each file (0 for 'has access') */
569 * Perform a binary search for the given parent_id. Return value is
570 * found/not found boolean, and indexp will be the index of the item
571 * or the index at which to insert the item if it's not found.
574 lookup_bucket(struct access_cache
*cache
, int *indexp
, cnid_t parent_id
)
577 int index
, matches
= 0;
579 if (cache
->numcached
== 0) {
581 return 0; // table is empty, so insert at index=0 and report no match
584 if (cache
->numcached
> CACHE_ELEMS
) {
585 /*printf("EGAD! numcached is %d... cut our losses and trim to %d\n",
586 cache->numcached, CACHE_ELEMS);*/
587 cache
->numcached
= CACHE_ELEMS
;
591 hi
= cache
->numcached
- 1;
594 /* perform binary search for parent_id */
596 unsigned int mid
= (hi
- lo
)/2 + lo
;
597 unsigned int this_id
= cache
->acache
[mid
];
599 if (parent_id
== this_id
) {
604 if (parent_id
< this_id
) {
609 if (parent_id
> this_id
) {
615 /* check if lo and hi converged on the match */
616 if (parent_id
== cache
->acache
[hi
]) {
620 /* if no existing entry found, find index for new one */
622 index
= (parent_id
< cache
->acache
[hi
]) ? hi
: hi
+ 1;
633 * Add a node to the access_cache at the given index (or do a lookup first
634 * to find the index if -1 is passed in). We currently do a replace rather
635 * than an insert if the cache is full.
638 add_node(struct access_cache
*cache
, int index
, cnid_t nodeID
, int access
)
640 int lookup_index
= -1;
642 /* need to do a lookup first if -1 passed for index */
644 if (lookup_bucket(cache
, &lookup_index
, nodeID
)) {
645 if (cache
->haveaccess
[lookup_index
] != access
) {
646 /* change access info for existing entry... should never happen */
647 cache
->haveaccess
[lookup_index
] = access
;
650 /* mission accomplished */
653 index
= lookup_index
;
658 /* if the cache is full, do a replace rather than an insert */
659 if (cache
->numcached
>= CACHE_ELEMS
) {
660 //printf("cache is full (%d). replace at index %d\n", cache->numcached, index);
661 cache
->numcached
= CACHE_ELEMS
-1;
663 if (index
> cache
->numcached
) {
664 // printf("index %d pinned to %d\n", index, cache->numcached);
665 index
= cache
->numcached
;
667 } else if (index
>= 0 && index
< cache
->numcached
) {
668 /* only do bcopy if we're inserting */
669 bcopy( cache
->acache
+index
, cache
->acache
+(index
+1), (cache
->numcached
- index
)*sizeof(int) );
670 bcopy( cache
->haveaccess
+index
, cache
->haveaccess
+(index
+1), (cache
->numcached
- index
)*sizeof(Boolean
) );
673 cache
->acache
[index
] = nodeID
;
674 cache
->haveaccess
[index
] = access
;
687 snoop_callback(const struct cat_desc
*descp
, const struct cat_attr
*attrp
, void * arg
)
689 struct cinfo
*cip
= (struct cinfo
*)arg
;
691 cip
->uid
= attrp
->ca_uid
;
692 cip
->gid
= attrp
->ca_gid
;
693 cip
->mode
= attrp
->ca_mode
;
694 cip
->parentcnid
= descp
->cd_parentcnid
;
700 * Lookup the cnid's attr info (uid, gid, and mode) as well as its parent id. If the item
701 * isn't incore, then go to the catalog.
704 do_attr_lookup(struct hfsmount
*hfsmp
, struct access_cache
*cache
, dev_t dev
, cnid_t cnid
,
705 struct cnode
*skip_cp
, CatalogKey
*keyp
, struct cat_attr
*cnattrp
, struct proc
*p
)
709 /* if this id matches the one the fsctl was called with, skip the lookup */
710 if (cnid
== skip_cp
->c_cnid
) {
711 cnattrp
->ca_uid
= skip_cp
->c_uid
;
712 cnattrp
->ca_gid
= skip_cp
->c_gid
;
713 cnattrp
->ca_mode
= skip_cp
->c_mode
;
714 keyp
->hfsPlus
.parentID
= skip_cp
->c_parentcnid
;
718 /* otherwise, check the cnode hash incase the file/dir is incore */
719 if (hfs_chash_snoop(dev
, cnid
, snoop_callback
, &c_info
) == 0) {
720 cnattrp
->ca_uid
= c_info
.uid
;
721 cnattrp
->ca_gid
= c_info
.gid
;
722 cnattrp
->ca_mode
= c_info
.mode
;
723 keyp
->hfsPlus
.parentID
= c_info
.parentcnid
;
727 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
729 /* lookup this cnid in the catalog */
730 error
= cat_getkeyplusattr(hfsmp
, cnid
, keyp
, cnattrp
);
732 hfs_systemfile_unlock(hfsmp
, lockflags
);
742 * Compute whether we have access to the given directory (nodeID) and all its parents. Cache
743 * up to CACHE_LEVELS as we progress towards the root.
746 do_access_check(struct hfsmount
*hfsmp
, int *err
, struct access_cache
*cache
, HFSCatalogNodeID nodeID
,
747 struct cnode
*skip_cp
, struct proc
*theProcPtr
, kauth_cred_t myp_ucred
, dev_t dev
)
751 HFSCatalogNodeID thisNodeID
;
752 unsigned long myPerms
;
753 struct cat_attr cnattr
;
754 int cache_index
= -1;
757 int i
= 0, ids_to_cache
= 0;
758 int parent_ids
[CACHE_LEVELS
];
760 /* root always has access */
761 if (!suser(myp_ucred
, NULL
)) {
766 while (thisNodeID
>= kRootDirID
) {
767 myResult
= 0; /* default to "no access" */
769 /* check the cache before resorting to hitting the catalog */
771 /* ASSUMPTION: access info of cached entries is "final"... i.e. no need
772 * to look any further after hitting cached dir */
774 if (lookup_bucket(cache
, &cache_index
, thisNodeID
)) {
776 myResult
= cache
->haveaccess
[cache_index
];
777 goto ExitThisRoutine
;
780 /* remember which parents we want to cache */
781 if (ids_to_cache
< CACHE_LEVELS
) {
782 parent_ids
[ids_to_cache
] = thisNodeID
;
786 /* do the lookup (checks the cnode hash, then the catalog) */
787 myErr
= do_attr_lookup(hfsmp
, cache
, dev
, thisNodeID
, skip_cp
, &catkey
, &cnattr
, theProcPtr
);
789 goto ExitThisRoutine
; /* no access */
792 myPerms
= DerivePermissionSummary(cnattr
.ca_uid
, cnattr
.ca_gid
,
793 cnattr
.ca_mode
, hfsmp
->hfs_mp
,
794 myp_ucred
, theProcPtr
);
796 if ( (myPerms
& X_OK
) == 0 ) {
798 goto ExitThisRoutine
; /* no access */
801 /* up the hierarchy we go */
802 thisNodeID
= catkey
.hfsPlus
.parentID
;
805 /* if here, we have access to this node */
810 //printf("*** error %d from catalog looking up parent %d/%d!\n", myErr, dev, thisNodeID);
815 /* cache the parent directory(ies) */
816 for (i
= 0; i
< ids_to_cache
; i
++) {
817 /* small optimization: get rid of double-lookup for all these */
818 // printf("adding %d to cache with result: %d\n", parent_ids[i], myResult);
819 add_node(cache
, -1, parent_ids
[i
], myResult
);
824 /* end "bulk-access" support */
829 * Callback for use with freeze ioctl.
832 hfs_freezewrite_callback(struct vnode
*vp
, void *cargs
)
834 vnode_waitforwrites(vp
, 0, 0, 0, "hfs freeze");
840 * Control filesystem operating characteristics.
843 hfs_vnop_ioctl( struct vnop_ioctl_args
/* {
848 vfs_context_t a_context;
851 struct vnode
* vp
= ap
->a_vp
;
852 struct hfsmount
*hfsmp
= VTOHFS(vp
);
853 vfs_context_t context
= ap
->a_context
;
854 kauth_cred_t cred
= vfs_context_ucred(context
);
855 proc_t p
= vfs_context_proc(context
);
856 struct vfsstatfs
*vfsp
;
859 is64bit
= proc_is64bit(p
);
861 switch (ap
->a_command
) {
863 case HFS_RESIZE_VOLUME
: {
867 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
868 if (suser(cred
, NULL
) &&
869 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
870 return (EACCES
); /* must be owner of file system */
872 if (!vnode_isvroot(vp
)) {
875 newsize
= *(u_int64_t
*)ap
->a_data
;
876 cursize
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
878 if (newsize
> cursize
) {
879 return hfs_extendfs(hfsmp
, *(u_int64_t
*)ap
->a_data
, context
);
880 } else if (newsize
< cursize
) {
881 return hfs_truncatefs(hfsmp
, *(u_int64_t
*)ap
->a_data
, context
);
886 case HFS_CHANGE_NEXT_ALLOCATION
: {
889 if (vnode_vfsisrdonly(vp
)) {
892 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
893 if (suser(cred
, NULL
) &&
894 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
895 return (EACCES
); /* must be owner of file system */
897 if (!vnode_isvroot(vp
)) {
900 location
= *(u_int32_t
*)ap
->a_data
;
901 if (location
> hfsmp
->totalBlocks
- 1) {
904 /* Return previous value. */
905 *(u_int32_t
*)ap
->a_data
= hfsmp
->nextAllocation
;
906 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
907 hfsmp
->nextAllocation
= location
;
908 hfsmp
->vcbFlags
|= 0xFF00;
909 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
913 #ifdef HFS_SPARSE_DEV
914 case HFS_SETBACKINGSTOREINFO
: {
915 struct vnode
* bsfs_rootvp
;
916 struct vnode
* di_vp
;
917 struct hfs_backingstoreinfo
*bsdata
;
920 if (hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) {
923 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
924 if (suser(cred
, NULL
) &&
925 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
926 return (EACCES
); /* must be owner of file system */
928 bsdata
= (struct hfs_backingstoreinfo
*)ap
->a_data
;
929 if (bsdata
== NULL
) {
932 if ((error
= file_vnode(bsdata
->backingfd
, &di_vp
))) {
935 if ((error
= vnode_getwithref(di_vp
))) {
936 file_drop(bsdata
->backingfd
);
940 if (vnode_mount(vp
) == vnode_mount(di_vp
)) {
941 (void)vnode_put(di_vp
);
942 file_drop(bsdata
->backingfd
);
947 * Obtain the backing fs root vnode and keep a reference
948 * on it. This reference will be dropped in hfs_unmount.
950 error
= VFS_ROOT(vnode_mount(di_vp
), &bsfs_rootvp
, NULL
); /* XXX use context! */
952 (void)vnode_put(di_vp
);
953 file_drop(bsdata
->backingfd
);
956 vnode_ref(bsfs_rootvp
);
957 vnode_put(bsfs_rootvp
);
959 hfsmp
->hfs_backingfs_rootvp
= bsfs_rootvp
;
960 hfsmp
->hfs_flags
|= HFS_HAS_SPARSE_DEVICE
;
961 hfsmp
->hfs_sparsebandblks
= bsdata
->bandsize
/ HFSTOVCB(hfsmp
)->blockSize
;
962 hfsmp
->hfs_sparsebandblks
*= 4;
964 (void)vnode_put(di_vp
);
965 file_drop(bsdata
->backingfd
);
968 case HFS_CLRBACKINGSTOREINFO
: {
969 struct vnode
* tmpvp
;
971 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
972 if (suser(cred
, NULL
) &&
973 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
974 return (EACCES
); /* must be owner of file system */
976 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) &&
977 hfsmp
->hfs_backingfs_rootvp
) {
979 hfsmp
->hfs_flags
&= ~HFS_HAS_SPARSE_DEVICE
;
980 tmpvp
= hfsmp
->hfs_backingfs_rootvp
;
981 hfsmp
->hfs_backingfs_rootvp
= NULLVP
;
982 hfsmp
->hfs_sparsebandblks
= 0;
987 #endif /* HFS_SPARSE_DEV */
996 mp
= vnode_mount(vp
);
997 hfsmp
= VFSTOHFS(mp
);
1002 lck_rw_lock_exclusive(&hfsmp
->hfs_insync
);
1004 task
= current_task();
1005 task_working_set_disable(task
);
1007 // flush things before we get started to try and prevent
1008 // dirty data from being paged out while we're frozen.
1009 // note: can't do this after taking the lock as it will
1010 // deadlock against ourselves.
1011 vnode_iterate(mp
, 0, hfs_freezewrite_callback
, NULL
);
1012 hfs_global_exclusive_lock_acquire(hfsmp
);
1013 journal_flush(hfsmp
->jnl
);
1015 // don't need to iterate on all vnodes, we just need to
1016 // wait for writes to the system files and the device vnode
1017 if (HFSTOVCB(hfsmp
)->extentsRefNum
)
1018 vnode_waitforwrites(HFSTOVCB(hfsmp
)->extentsRefNum
, 0, 0, 0, "hfs freeze");
1019 if (HFSTOVCB(hfsmp
)->catalogRefNum
)
1020 vnode_waitforwrites(HFSTOVCB(hfsmp
)->catalogRefNum
, 0, 0, 0, "hfs freeze");
1021 if (HFSTOVCB(hfsmp
)->allocationsRefNum
)
1022 vnode_waitforwrites(HFSTOVCB(hfsmp
)->allocationsRefNum
, 0, 0, 0, "hfs freeze");
1023 if (hfsmp
->hfs_attribute_vp
)
1024 vnode_waitforwrites(hfsmp
->hfs_attribute_vp
, 0, 0, 0, "hfs freeze");
1025 vnode_waitforwrites(hfsmp
->hfs_devvp
, 0, 0, 0, "hfs freeze");
1027 hfsmp
->hfs_freezing_proc
= current_proc();
1036 // if we're not the one who froze the fs then we
1038 if (hfsmp
->hfs_freezing_proc
!= current_proc()) {
1042 // NOTE: if you add code here, also go check the
1043 // code that "thaws" the fs in hfs_vnop_close()
1045 hfsmp
->hfs_freezing_proc
= NULL
;
1046 hfs_global_exclusive_lock_release(hfsmp
);
1047 lck_rw_unlock_exclusive(&hfsmp
->hfs_insync
);
1052 #define HFSIOC_BULKACCESS _IOW('h', 9, struct access_t)
1053 #define HFS_BULKACCESS_FSCTL IOCBASECMD(HFSIOC_BULKACCESS)
1055 case HFS_BULKACCESS_FSCTL
:
1056 case HFS_BULKACCESS
: {
1058 * NOTE: on entry, the vnode is locked. Incase this vnode
1059 * happens to be in our list of file_ids, we'll note it
1060 * avoid calling hfs_chashget_nowait() on that id as that
1061 * will cause a "locking against myself" panic.
1063 Boolean check_leaf
= true;
1065 struct user_access_t
*user_access_structp
;
1066 struct user_access_t tmp_user_access_t
;
1067 struct access_cache cache
;
1071 dev_t dev
= VTOC(vp
)->c_dev
;
1074 struct ucred myucred
; /* XXX ILLEGAL */
1076 int *file_ids
= NULL
;
1077 short *access
= NULL
;
1080 cnid_t prevParent_cnid
= 0;
1081 unsigned long myPerms
;
1083 struct cat_attr cnattr
;
1085 struct cnode
*skip_cp
= VTOC(vp
);
1086 struct vfs_context my_context
;
1088 /* first, return error if not run as root */
1089 if (cred
->cr_ruid
!= 0) {
1093 /* initialize the local cache and buffers */
1094 cache
.numcached
= 0;
1095 cache
.cachehits
= 0;
1098 file_ids
= (int *) get_pathbuff();
1099 access
= (short *) get_pathbuff();
1100 cache
.acache
= (int *) get_pathbuff();
1101 cache
.haveaccess
= (Boolean
*) get_pathbuff();
1103 if (file_ids
== NULL
|| access
== NULL
|| cache
.acache
== NULL
|| cache
.haveaccess
== NULL
) {
1104 release_pathbuff((char *) file_ids
);
1105 release_pathbuff((char *) access
);
1106 release_pathbuff((char *) cache
.acache
);
1107 release_pathbuff((char *) cache
.haveaccess
);
1112 /* struct copyin done during dispatch... need to copy file_id array separately */
1113 if (ap
->a_data
== NULL
) {
1115 goto err_exit_bulk_access
;
1119 user_access_structp
= (struct user_access_t
*)ap
->a_data
;
1122 struct access_t
* accessp
= (struct access_t
*)ap
->a_data
;
1123 tmp_user_access_t
.uid
= accessp
->uid
;
1124 tmp_user_access_t
.flags
= accessp
->flags
;
1125 tmp_user_access_t
.num_groups
= accessp
->num_groups
;
1126 tmp_user_access_t
.num_files
= accessp
->num_files
;
1127 tmp_user_access_t
.file_ids
= CAST_USER_ADDR_T(accessp
->file_ids
);
1128 tmp_user_access_t
.groups
= CAST_USER_ADDR_T(accessp
->groups
);
1129 tmp_user_access_t
.access
= CAST_USER_ADDR_T(accessp
->access
);
1130 user_access_structp
= &tmp_user_access_t
;
1133 num_files
= user_access_structp
->num_files
;
1134 if (num_files
< 1) {
1135 goto err_exit_bulk_access
;
1137 if (num_files
> 256) {
1139 goto err_exit_bulk_access
;
1142 if ((error
= copyin(user_access_structp
->file_ids
, (caddr_t
)file_ids
,
1143 num_files
* sizeof(int)))) {
1144 goto err_exit_bulk_access
;
1147 /* fill in the ucred structure */
1148 flags
= user_access_structp
->flags
;
1149 if ((flags
& (F_OK
| R_OK
| W_OK
| X_OK
)) == 0) {
1153 /* check if we've been passed leaf node ids or parent ids */
1154 if (flags
& PARENT_IDS_FLAG
) {
1158 memset(&myucred
, 0, sizeof(myucred
));
1160 myucred
.cr_uid
= myucred
.cr_ruid
= myucred
.cr_svuid
= user_access_structp
->uid
;
1161 myucred
.cr_ngroups
= user_access_structp
->num_groups
;
1162 if (myucred
.cr_ngroups
< 1 || myucred
.cr_ngroups
> 16) {
1163 myucred
.cr_ngroups
= 0;
1164 } else if ((error
= copyin(user_access_structp
->groups
, (caddr_t
)myucred
.cr_groups
,
1165 myucred
.cr_ngroups
* sizeof(gid_t
)))) {
1166 goto err_exit_bulk_access
;
1168 myucred
.cr_rgid
= myucred
.cr_svgid
= myucred
.cr_groups
[0];
1169 myucred
.cr_gmuid
= myucred
.cr_uid
;
1171 my_context
.vc_proc
= p
;
1172 my_context
.vc_ucred
= &myucred
;
1174 /* Check access to each file_id passed in */
1175 for (i
= 0; i
< num_files
; i
++) {
1177 cnid
= (cnid_t
) file_ids
[i
];
1179 /* root always has access */
1180 if (!suser(&myucred
, NULL
)) {
1187 /* do the lookup (checks the cnode hash, then the catalog) */
1188 error
= do_attr_lookup(hfsmp
, &cache
, dev
, cnid
, skip_cp
, &catkey
, &cnattr
, p
);
1190 access
[i
] = (short) error
;
1194 /* before calling CheckAccess(), check the target file for read access */
1195 myPerms
= DerivePermissionSummary(cnattr
.ca_uid
, cnattr
.ca_gid
,
1196 cnattr
.ca_mode
, hfsmp
->hfs_mp
, &myucred
, p
);
1199 /* fail fast if no access */
1200 if ((myPerms
& flags
) == 0) {
1205 /* we were passed an array of parent ids */
1206 catkey
.hfsPlus
.parentID
= cnid
;
1209 /* if the last guy had the same parent and had access, we're done */
1210 if (i
> 0 && catkey
.hfsPlus
.parentID
== prevParent_cnid
&& access
[i
-1] == 0) {
1216 myaccess
= do_access_check(hfsmp
, &error
, &cache
, catkey
.hfsPlus
.parentID
,
1217 skip_cp
, p
, &myucred
, dev
);
1220 access
[i
] = 0; // have access.. no errors to report
1222 access
[i
] = (error
!= 0 ? (short) error
: EACCES
);
1225 prevParent_cnid
= catkey
.hfsPlus
.parentID
;
1229 cnid
= (cnid_t
)file_ids
[i
];
1231 while (cnid
>= kRootDirID
) {
1232 /* get the vnode for this cnid */
1233 myErr
= hfs_vget(hfsmp
, cnid
, &vp
, 0);
1239 cnid
= VTOC(vp
)->c_parentcnid
;
1241 hfs_unlock(VTOC(vp
));
1242 if (vnode_vtype(vp
) == VDIR
) {
1243 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
| KAUTH_VNODE_LIST_DIRECTORY
), &my_context
);
1245 myErr
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, &my_context
);
1256 /* copyout the access array */
1257 if ((error
= copyout((caddr_t
)access
, user_access_structp
->access
,
1258 num_files
* sizeof (short)))) {
1259 goto err_exit_bulk_access
;
1262 err_exit_bulk_access
:
1264 //printf("on exit (err %d), numfiles/numcached/cachehits/lookups is %d/%d/%d/%d\n", error, num_files, cache.numcached, cache.cachehits, cache.lookups);
1266 release_pathbuff((char *) cache
.acache
);
1267 release_pathbuff((char *) cache
.haveaccess
);
1268 release_pathbuff((char *) file_ids
);
1269 release_pathbuff((char *) access
);
1272 } /* HFS_BULKACCESS */
1274 case HFS_SETACLSTATE
: {
1277 if (ap
->a_data
== NULL
) {
1281 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
1282 state
= *(int *)ap
->a_data
;
1284 // super-user can enable or disable acl's on a volume.
1285 // the volume owner can only enable acl's
1286 if (!is_suser() && (state
== 0 || kauth_cred_getuid(cred
) != vfsp
->f_owner
)) {
1289 if (state
== 0 || state
== 1)
1290 return hfs_setextendedsecurity(hfsmp
, state
);
1298 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1300 error
= hfs_fsync(vp
, MNT_NOWAIT
, TRUE
, p
);
1301 hfs_unlock(VTOC(vp
));
1308 register struct cnode
*cp
;
1311 if (!vnode_isreg(vp
))
1314 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1318 * used by regression test to determine if
1319 * all the dirty pages (via write) have been cleaned
1320 * after a call to 'fsysnc'.
1322 error
= is_file_clean(vp
, VTOF(vp
)->ff_size
);
1329 register struct radvisory
*ra
;
1330 struct filefork
*fp
;
1333 if (!vnode_isreg(vp
))
1336 ra
= (struct radvisory
*)(ap
->a_data
);
1339 /* Protect against a size change. */
1340 hfs_lock_truncate(VTOC(vp
), TRUE
);
1342 if (ra
->ra_offset
>= fp
->ff_size
) {
1345 error
= advisory_read(vp
, fp
->ff_size
, ra
->ra_offset
, ra
->ra_count
);
1348 hfs_unlock_truncate(VTOC(vp
));
1352 case F_READBOOTSTRAP
:
1353 case F_WRITEBOOTSTRAP
:
1355 struct vnode
*devvp
= NULL
;
1356 user_fbootstraptransfer_t
*user_bootstrapp
;
1360 daddr64_t blockNumber
;
1364 user_fbootstraptransfer_t user_bootstrap
;
1366 if (!vnode_isvroot(vp
))
1368 /* LP64 - when caller is a 64 bit process then we are passed a pointer
1369 * to a user_fbootstraptransfer_t else we get a pointer to a
1370 * fbootstraptransfer_t which we munge into a user_fbootstraptransfer_t
1373 user_bootstrapp
= (user_fbootstraptransfer_t
*)ap
->a_data
;
1376 fbootstraptransfer_t
*bootstrapp
= (fbootstraptransfer_t
*)ap
->a_data
;
1377 user_bootstrapp
= &user_bootstrap
;
1378 user_bootstrap
.fbt_offset
= bootstrapp
->fbt_offset
;
1379 user_bootstrap
.fbt_length
= bootstrapp
->fbt_length
;
1380 user_bootstrap
.fbt_buffer
= CAST_USER_ADDR_T(bootstrapp
->fbt_buffer
);
1382 if (user_bootstrapp
->fbt_offset
+ user_bootstrapp
->fbt_length
> 1024)
1385 devvp
= VTOHFS(vp
)->hfs_devvp
;
1386 auio
= uio_create(1, user_bootstrapp
->fbt_offset
,
1387 is64bit
? UIO_USERSPACE64
: UIO_USERSPACE32
,
1388 (ap
->a_command
== F_WRITEBOOTSTRAP
) ? UIO_WRITE
: UIO_READ
);
1389 uio_addiov(auio
, user_bootstrapp
->fbt_buffer
, user_bootstrapp
->fbt_length
);
1391 devBlockSize
= vfs_devblocksize(vnode_mount(vp
));
1393 while (uio_resid(auio
) > 0) {
1394 blockNumber
= uio_offset(auio
) / devBlockSize
;
1395 error
= (int)buf_bread(devvp
, blockNumber
, devBlockSize
, cred
, &bp
);
1397 if (bp
) buf_brelse(bp
);
1402 blockOffset
= uio_offset(auio
) % devBlockSize
;
1403 xfersize
= devBlockSize
- blockOffset
;
1404 error
= uiomove((caddr_t
)buf_dataptr(bp
) + blockOffset
, (int)xfersize
, auio
);
1410 if (uio_rw(auio
) == UIO_WRITE
) {
1411 error
= VNOP_BWRITE(bp
);
1424 case _IOC(IOC_OUT
,'h', 4, 0): /* Create date in local time */
1427 *(user_time_t
*)(ap
->a_data
) = (user_time_t
) (to_bsd_time(VTOVCB(vp
)->localCreateDate
));
1430 *(time_t *)(ap
->a_data
) = to_bsd_time(VTOVCB(vp
)->localCreateDate
);
1435 case HFS_GET_MOUNT_TIME
:
1436 return copyout(&hfsmp
->hfs_mount_time
, CAST_USER_ADDR_T(ap
->a_data
), sizeof(hfsmp
->hfs_mount_time
));
1439 case HFS_GET_LAST_MTIME
:
1440 return copyout(&hfsmp
->hfs_last_mounted_mtime
, CAST_USER_ADDR_T(ap
->a_data
), sizeof(hfsmp
->hfs_last_mounted_mtime
));
1443 case HFS_SET_BOOT_INFO
:
1444 if (!vnode_isvroot(vp
))
1446 if (!kauth_cred_issuser(cred
) && (kauth_cred_getuid(cred
) != vfs_statfs(HFSTOVFS(hfsmp
))->f_owner
))
1447 return(EACCES
); /* must be superuser or owner of filesystem */
1448 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1449 bcopy(ap
->a_data
, &hfsmp
->vcbFndrInfo
, sizeof(hfsmp
->vcbFndrInfo
));
1450 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1451 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
1454 case HFS_GET_BOOT_INFO
:
1455 if (!vnode_isvroot(vp
))
1457 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1458 bcopy(&hfsmp
->vcbFndrInfo
, ap
->a_data
, sizeof(hfsmp
->vcbFndrInfo
));
1459 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1466 /* Should never get here */
1474 hfs_vnop_select(__unused
struct vnop_select_args
*ap
)
1476 struct vnop_select_args {
1481 vfs_context_t a_context;
1486 * We should really check to see if I/O is possible.
1492 * Converts a logical block number to a physical block, and optionally returns
1493 * the amount of remaining blocks in a run. The logical block is based on hfsNode.logBlockSize.
1494 * The physical block number is based on the device block size, currently its 512.
1495 * The block run is returned in logical blocks, and is the REMAINING amount of blocks
1498 hfs_bmap(struct vnode
*vp
, daddr_t bn
, struct vnode
**vpp
, daddr64_t
*bnp
, int *runp
)
1500 struct cnode
*cp
= VTOC(vp
);
1501 struct filefork
*fp
= VTOF(vp
);
1502 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1503 int retval
= E_NONE
;
1504 daddr_t logBlockSize
;
1505 size_t bytesContAvail
= 0;
1506 off_t blockposition
;
1511 * Check for underlying vnode requests and ensure that logical
1512 * to physical mapping is requested.
1519 logBlockSize
= GetLogicalBlockSize(vp
);
1520 blockposition
= (off_t
)bn
* (off_t
)logBlockSize
;
1522 lockExtBtree
= overflow_extents(fp
);
1525 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_SHARED_LOCK
);
1527 retval
= MacToVFSError(
1528 MapFileBlockC (HFSTOVCB(hfsmp
),
1536 hfs_systemfile_unlock(hfsmp
, lockflags
);
1538 if (retval
== E_NONE
) {
1539 /* Figure out how many read ahead blocks there are */
1541 if (can_cluster(logBlockSize
)) {
1542 /* Make sure this result never goes negative: */
1543 *runp
= (bytesContAvail
< logBlockSize
) ? 0 : (bytesContAvail
/ logBlockSize
) - 1;
1553 * Convert logical block number to file offset.
1556 hfs_vnop_blktooff(struct vnop_blktooff_args
*ap
)
1558 struct vnop_blktooff_args {
1565 if (ap
->a_vp
== NULL
)
1567 *ap
->a_offset
= (off_t
)ap
->a_lblkno
* (off_t
)GetLogicalBlockSize(ap
->a_vp
);
1573 * Convert file offset to logical block number.
1576 hfs_vnop_offtoblk(struct vnop_offtoblk_args
*ap
)
1578 struct vnop_offtoblk_args {
1581 daddr64_t *a_lblkno;
1585 if (ap
->a_vp
== NULL
)
1587 *ap
->a_lblkno
= (daddr64_t
)(ap
->a_offset
/ (off_t
)GetLogicalBlockSize(ap
->a_vp
));
1593 * Map file offset to physical block number.
1595 * System file cnodes are expected to be locked (shared or exclusive).
1598 hfs_vnop_blockmap(struct vnop_blockmap_args
*ap
)
1600 struct vnop_blockmap_args {
1608 vfs_context_t a_context;
1612 struct vnode
*vp
= ap
->a_vp
;
1614 struct filefork
*fp
;
1615 struct hfsmount
*hfsmp
;
1616 size_t bytesContAvail
= 0;
1617 int retval
= E_NONE
;
1620 struct rl_entry
*invalid_range
;
1621 enum rl_overlaptype overlaptype
;
1625 /* Do not allow blockmap operation on a directory */
1626 if (vnode_isdir(vp
)) {
1631 * Check for underlying vnode requests and ensure that logical
1632 * to physical mapping is requested.
1634 if (ap
->a_bpn
== NULL
)
1637 if ( !vnode_issystem(vp
) && !vnode_islnk(vp
)) {
1638 if (VTOC(vp
)->c_lockowner
!= current_thread()) {
1639 hfs_lock(VTOC(vp
), HFS_FORCE_LOCK
);
1643 panic("blockmap: %s cnode lock already held!\n",
1644 cp
->c_desc
.cd_nameptr
? cp
->c_desc
.cd_nameptr
: "");
1652 if (fp
->ff_unallocblocks
) {
1653 if (hfs_start_transaction(hfsmp
) != 0) {
1659 syslocks
= SFL_EXTENTS
| SFL_BITMAP
;
1661 } else if (overflow_extents(fp
)) {
1662 syslocks
= SFL_EXTENTS
;
1666 lockflags
= hfs_systemfile_lock(hfsmp
, syslocks
, HFS_EXCLUSIVE_LOCK
);
1669 * Check for any delayed allocations.
1671 if (fp
->ff_unallocblocks
) {
1673 u_int32_t loanedBlocks
;
1676 // Make sure we have a transaction. It's possible
1677 // that we came in and fp->ff_unallocblocks was zero
1678 // but during the time we blocked acquiring the extents
1679 // btree, ff_unallocblocks became non-zero and so we
1680 // will need to start a transaction.
1682 if (started_tr
== 0) {
1684 hfs_systemfile_unlock(hfsmp
, lockflags
);
1691 * Note: ExtendFileC will Release any blocks on loan and
1692 * aquire real blocks. So we ask to extend by zero bytes
1693 * since ExtendFileC will account for the virtual blocks.
1696 loanedBlocks
= fp
->ff_unallocblocks
;
1697 retval
= ExtendFileC(hfsmp
, (FCB
*)fp
, 0, 0,
1698 kEFAllMask
| kEFNoClumpMask
, &actbytes
);
1701 fp
->ff_unallocblocks
= loanedBlocks
;
1702 cp
->c_blocks
+= loanedBlocks
;
1703 fp
->ff_blocks
+= loanedBlocks
;
1705 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1706 hfsmp
->loanedBlocks
+= loanedBlocks
;
1707 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1711 hfs_systemfile_unlock(hfsmp
, lockflags
);
1712 cp
->c_flag
|= C_MODIFIED
;
1714 (void) hfs_update(vp
, TRUE
);
1715 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1717 hfs_end_transaction(hfsmp
);
1723 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, ap
->a_size
, ap
->a_foffset
,
1724 ap
->a_bpn
, &bytesContAvail
);
1726 hfs_systemfile_unlock(hfsmp
, lockflags
);
1731 (void) hfs_update(vp
, TRUE
);
1732 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1733 hfs_end_transaction(hfsmp
);
1740 /* Adjust the mapping information for invalid file ranges: */
1741 overlaptype
= rl_scan(&fp
->ff_invalidranges
, ap
->a_foffset
,
1742 ap
->a_foffset
+ (off_t
)bytesContAvail
- 1,
1744 if (overlaptype
!= RL_NOOVERLAP
) {
1745 switch(overlaptype
) {
1746 case RL_MATCHINGOVERLAP
:
1747 case RL_OVERLAPCONTAINSRANGE
:
1748 case RL_OVERLAPSTARTSBEFORE
:
1749 /* There's no valid block for this byte offset: */
1750 *ap
->a_bpn
= (daddr64_t
)-1;
1751 /* There's no point limiting the amount to be returned
1752 * if the invalid range that was hit extends all the way
1753 * to the EOF (i.e. there's no valid bytes between the
1754 * end of this range and the file's EOF):
1756 if (((off_t
)fp
->ff_size
> (invalid_range
->rl_end
+ 1)) &&
1757 (invalid_range
->rl_end
+ 1 - ap
->a_foffset
< bytesContAvail
)) {
1758 bytesContAvail
= invalid_range
->rl_end
+ 1 - ap
->a_foffset
;
1762 case RL_OVERLAPISCONTAINED
:
1763 case RL_OVERLAPENDSAFTER
:
1764 /* The range of interest hits an invalid block before the end: */
1765 if (invalid_range
->rl_start
== ap
->a_foffset
) {
1766 /* There's actually no valid information to be had starting here: */
1767 *ap
->a_bpn
= (daddr64_t
)-1;
1768 if (((off_t
)fp
->ff_size
> (invalid_range
->rl_end
+ 1)) &&
1769 (invalid_range
->rl_end
+ 1 - ap
->a_foffset
< bytesContAvail
)) {
1770 bytesContAvail
= invalid_range
->rl_end
+ 1 - ap
->a_foffset
;
1773 bytesContAvail
= invalid_range
->rl_start
- ap
->a_foffset
;
1780 if (bytesContAvail
> ap
->a_size
)
1781 bytesContAvail
= ap
->a_size
;
1784 *ap
->a_run
= bytesContAvail
;
1787 *(int *)ap
->a_poff
= 0;
1792 return (MacToVFSError(retval
));
1797 * prepare and issue the I/O
1798 * buf_strategy knows how to deal
1799 * with requests that require
1803 hfs_vnop_strategy(struct vnop_strategy_args
*ap
)
1805 buf_t bp
= ap
->a_bp
;
1806 vnode_t vp
= buf_vnode(bp
);
1807 struct cnode
*cp
= VTOC(vp
);
1809 return (buf_strategy(cp
->c_devvp
, ap
));
1814 do_hfs_truncate(struct vnode
*vp
, off_t length
, int flags
, int skipsetsize
, vfs_context_t context
)
1816 register struct cnode
*cp
= VTOC(vp
);
1817 struct filefork
*fp
= VTOF(vp
);
1818 struct proc
*p
= vfs_context_proc(context
);;
1819 kauth_cred_t cred
= vfs_context_ucred(context
);
1822 off_t actualBytesAdded
;
1824 u_int64_t old_filesize
;
1827 struct hfsmount
*hfsmp
;
1830 blksize
= VTOVCB(vp
)->blockSize
;
1831 fileblocks
= fp
->ff_blocks
;
1832 filebytes
= (off_t
)fileblocks
* (off_t
)blksize
;
1833 old_filesize
= fp
->ff_size
;
1835 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_START
,
1836 (int)length
, (int)fp
->ff_size
, (int)filebytes
, 0, 0);
1841 if ((!ISHFSPLUS(VTOVCB(vp
))) && (length
> (off_t
)MAXHFSFILESIZE
))
1848 /* Files that are changing size are not hot file candidates. */
1849 if (hfsmp
->hfc_stage
== HFC_RECORDING
) {
1850 fp
->ff_bytesread
= 0;
1854 * We cannot just check if fp->ff_size == length (as an optimization)
1855 * since there may be extra physical blocks that also need truncation.
1858 if ((retval
= hfs_getinoquota(cp
)))
1863 * Lengthen the size of the file. We must ensure that the
1864 * last byte of the file is allocated. Since the smallest
1865 * value of ff_size is 0, length will be at least 1.
1867 if (length
> (off_t
)fp
->ff_size
) {
1869 retval
= hfs_chkdq(cp
, (int64_t)(roundup(length
- filebytes
, blksize
)),
1875 * If we don't have enough physical space then
1876 * we need to extend the physical size.
1878 if (length
> filebytes
) {
1880 u_long blockHint
= 0;
1882 /* All or nothing and don't round up to clumpsize. */
1883 eflags
= kEFAllMask
| kEFNoClumpMask
;
1885 if (cred
&& suser(cred
, NULL
) != 0)
1886 eflags
|= kEFReserveMask
; /* keep a reserve */
1889 * Allocate Journal and Quota files in metadata zone.
1891 if (filebytes
== 0 &&
1892 hfsmp
->hfs_flags
& HFS_METADATA_ZONE
&&
1893 hfs_virtualmetafile(cp
)) {
1894 eflags
|= kEFMetadataMask
;
1895 blockHint
= hfsmp
->hfs_metazone_start
;
1897 if (hfs_start_transaction(hfsmp
) != 0) {
1902 /* Protect extents b-tree and allocation bitmap */
1903 lockflags
= SFL_BITMAP
;
1904 if (overflow_extents(fp
))
1905 lockflags
|= SFL_EXTENTS
;
1906 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
1908 while ((length
> filebytes
) && (retval
== E_NONE
)) {
1909 bytesToAdd
= length
- filebytes
;
1910 retval
= MacToVFSError(ExtendFileC(VTOVCB(vp
),
1915 &actualBytesAdded
));
1917 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)blksize
;
1918 if (actualBytesAdded
== 0 && retval
== E_NONE
) {
1919 if (length
> filebytes
)
1925 hfs_systemfile_unlock(hfsmp
, lockflags
);
1928 (void) hfs_update(vp
, TRUE
);
1929 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1932 hfs_end_transaction(hfsmp
);
1937 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_NONE
,
1938 (int)length
, (int)fp
->ff_size
, (int)filebytes
, 0, 0);
1941 if (!(flags
& IO_NOZEROFILL
)) {
1942 if (UBCINFOEXISTS(vp
) && retval
== E_NONE
) {
1943 struct rl_entry
*invalid_range
;
1946 zero_limit
= (fp
->ff_size
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
1947 if (length
< zero_limit
) zero_limit
= length
;
1949 if (length
> (off_t
)fp
->ff_size
) {
1952 /* Extending the file: time to fill out the current last page w. zeroes? */
1953 if ((fp
->ff_size
& PAGE_MASK_64
) &&
1954 (rl_scan(&fp
->ff_invalidranges
, fp
->ff_size
& ~PAGE_MASK_64
,
1955 fp
->ff_size
- 1, &invalid_range
) == RL_NOOVERLAP
)) {
1957 /* There's some valid data at the start of the (current) last page
1958 of the file, so zero out the remainder of that page to ensure the
1959 entire page contains valid data. Since there is no invalid range
1960 possible past the (current) eof, there's no need to remove anything
1961 from the invalid range list before calling cluster_write(): */
1963 retval
= cluster_write(vp
, (struct uio
*) 0, fp
->ff_size
, zero_limit
,
1964 fp
->ff_size
, (off_t
)0,
1965 (flags
& IO_SYNC
) | IO_HEADZEROFILL
| IO_NOZERODIRTY
);
1966 hfs_lock(cp
, HFS_FORCE_LOCK
);
1967 if (retval
) goto Err_Exit
;
1969 /* Merely invalidate the remaining area, if necessary: */
1970 if (length
> zero_limit
) {
1972 rl_add(zero_limit
, length
- 1, &fp
->ff_invalidranges
);
1973 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
1976 /* The page containing the (current) eof is invalid: just add the
1977 remainder of the page to the invalid list, along with the area
1978 being newly allocated:
1981 rl_add(fp
->ff_size
, length
- 1, &fp
->ff_invalidranges
);
1982 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
1986 panic("hfs_truncate: invoked on non-UBC object?!");
1989 cp
->c_touch_modtime
= TRUE
;
1990 fp
->ff_size
= length
;
1992 /* Nested transactions will do their own ubc_setsize. */
1995 * ubc_setsize can cause a pagein here
1996 * so we need to drop cnode lock.
1999 ubc_setsize(vp
, length
);
2000 hfs_lock(cp
, HFS_FORCE_LOCK
);
2003 } else { /* Shorten the size of the file */
2005 if ((off_t
)fp
->ff_size
> length
) {
2007 * Any buffers that are past the truncation point need to be
2008 * invalidated (to maintain buffer cache consistency).
2011 /* Nested transactions will do their own ubc_setsize. */
2014 * ubc_setsize can cause a pageout here
2015 * so we need to drop cnode lock.
2018 ubc_setsize(vp
, length
);
2019 hfs_lock(cp
, HFS_FORCE_LOCK
);
2022 /* Any space previously marked as invalid is now irrelevant: */
2023 rl_remove(length
, fp
->ff_size
- 1, &fp
->ff_invalidranges
);
2027 * Account for any unmapped blocks. Note that the new
2028 * file length can still end up with unmapped blocks.
2030 if (fp
->ff_unallocblocks
> 0) {
2031 u_int32_t finalblks
;
2032 u_int32_t loanedBlocks
;
2034 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
2036 loanedBlocks
= fp
->ff_unallocblocks
;
2037 cp
->c_blocks
-= loanedBlocks
;
2038 fp
->ff_blocks
-= loanedBlocks
;
2039 fp
->ff_unallocblocks
= 0;
2041 hfsmp
->loanedBlocks
-= loanedBlocks
;
2043 finalblks
= (length
+ blksize
- 1) / blksize
;
2044 if (finalblks
> fp
->ff_blocks
) {
2045 /* calculate required unmapped blocks */
2046 loanedBlocks
= finalblks
- fp
->ff_blocks
;
2047 hfsmp
->loanedBlocks
+= loanedBlocks
;
2049 fp
->ff_unallocblocks
= loanedBlocks
;
2050 cp
->c_blocks
+= loanedBlocks
;
2051 fp
->ff_blocks
+= loanedBlocks
;
2053 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2057 * For a TBE process the deallocation of the file blocks is
2058 * delayed until the file is closed. And hfs_close calls
2059 * truncate with the IO_NDELAY flag set. So when IO_NDELAY
2060 * isn't set, we make sure this isn't a TBE process.
2062 if ((flags
& IO_NDELAY
) || (proc_tbe(p
) == 0)) {
2064 off_t savedbytes
= ((off_t
)fp
->ff_blocks
* (off_t
)blksize
);
2066 if (hfs_start_transaction(hfsmp
) != 0) {
2071 if (fp
->ff_unallocblocks
== 0) {
2072 /* Protect extents b-tree and allocation bitmap */
2073 lockflags
= SFL_BITMAP
;
2074 if (overflow_extents(fp
))
2075 lockflags
|= SFL_EXTENTS
;
2076 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2078 retval
= MacToVFSError(TruncateFileC(VTOVCB(vp
),
2079 (FCB
*)fp
, length
, false));
2081 hfs_systemfile_unlock(hfsmp
, lockflags
);
2085 fp
->ff_size
= length
;
2087 (void) hfs_update(vp
, TRUE
);
2088 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2091 hfs_end_transaction(hfsmp
);
2093 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)blksize
;
2097 /* These are bytesreleased */
2098 (void) hfs_chkdq(cp
, (int64_t)-(savedbytes
- filebytes
), NOCRED
, 0);
2101 /* Only set update flag if the logical length changes */
2102 if (old_filesize
!= length
)
2103 cp
->c_touch_modtime
= TRUE
;
2104 fp
->ff_size
= length
;
2106 cp
->c_touch_chgtime
= TRUE
;
2107 retval
= hfs_update(vp
, MNT_WAIT
);
2109 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_NONE
,
2110 -1, -1, -1, retval
, 0);
2115 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_END
,
2116 (int)length
, (int)fp
->ff_size
, (int)filebytes
, retval
, 0);
2124 * Truncate a cnode to at most length size, freeing (or adding) the
2129 hfs_truncate(struct vnode
*vp
, off_t length
, int flags
, int skipsetsize
,
2130 vfs_context_t context
)
2132 struct filefork
*fp
= VTOF(vp
);
2135 int blksize
, error
= 0;
2136 struct cnode
*cp
= VTOC(vp
);
2138 if (vnode_isdir(vp
))
2139 return (EISDIR
); /* cannot truncate an HFS directory! */
2141 blksize
= VTOVCB(vp
)->blockSize
;
2142 fileblocks
= fp
->ff_blocks
;
2143 filebytes
= (off_t
)fileblocks
* (off_t
)blksize
;
2145 // have to loop truncating or growing files that are
2146 // really big because otherwise transactions can get
2147 // enormous and consume too many kernel resources.
2149 if (length
< filebytes
) {
2150 while (filebytes
> length
) {
2151 if ((filebytes
- length
) > HFS_BIGFILE_SIZE
) {
2152 filebytes
-= HFS_BIGFILE_SIZE
;
2156 cp
->c_flag
|= C_FORCEUPDATE
;
2157 error
= do_hfs_truncate(vp
, filebytes
, flags
, skipsetsize
, context
);
2161 } else if (length
> filebytes
) {
2162 while (filebytes
< length
) {
2163 if ((length
- filebytes
) > HFS_BIGFILE_SIZE
) {
2164 filebytes
+= HFS_BIGFILE_SIZE
;
2168 cp
->c_flag
|= C_FORCEUPDATE
;
2169 error
= do_hfs_truncate(vp
, filebytes
, flags
, skipsetsize
, context
);
2173 } else /* Same logical size */ {
2175 error
= do_hfs_truncate(vp
, length
, flags
, skipsetsize
, context
);
2177 /* Files that are changing size are not hot file candidates. */
2178 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) {
2179 fp
->ff_bytesread
= 0;
2188 * Preallocate file storage space.
2191 hfs_vnop_allocate(struct vnop_allocate_args
/* {
2195 off_t *a_bytesallocated;
2197 vfs_context_t a_context;
2200 struct vnode
*vp
= ap
->a_vp
;
2202 struct filefork
*fp
;
2204 off_t length
= ap
->a_length
;
2206 off_t moreBytesRequested
;
2207 off_t actualBytesAdded
;
2210 int retval
, retval2
;
2212 UInt32 extendFlags
; /* For call to ExtendFileC */
2213 struct hfsmount
*hfsmp
;
2214 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
2217 *(ap
->a_bytesallocated
) = 0;
2219 if (!vnode_isreg(vp
))
2221 if (length
< (off_t
)0)
2224 if ((retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2231 fileblocks
= fp
->ff_blocks
;
2232 filebytes
= (off_t
)fileblocks
* (off_t
)vcb
->blockSize
;
2234 if ((ap
->a_flags
& ALLOCATEFROMVOL
) && (length
< filebytes
)) {
2239 /* Fill in the flags word for the call to Extend the file */
2241 extendFlags
= kEFNoClumpMask
;
2242 if (ap
->a_flags
& ALLOCATECONTIG
)
2243 extendFlags
|= kEFContigMask
;
2244 if (ap
->a_flags
& ALLOCATEALL
)
2245 extendFlags
|= kEFAllMask
;
2246 if (cred
&& suser(cred
, NULL
) != 0)
2247 extendFlags
|= kEFReserveMask
;
2251 startingPEOF
= filebytes
;
2253 if (ap
->a_flags
& ALLOCATEFROMPEOF
)
2254 length
+= filebytes
;
2255 else if (ap
->a_flags
& ALLOCATEFROMVOL
)
2256 blockHint
= ap
->a_offset
/ VTOVCB(vp
)->blockSize
;
2258 /* If no changes are necesary, then we're done */
2259 if (filebytes
== length
)
2263 * Lengthen the size of the file. We must ensure that the
2264 * last byte of the file is allocated. Since the smallest
2265 * value of filebytes is 0, length will be at least 1.
2267 if (length
> filebytes
) {
2268 moreBytesRequested
= length
- filebytes
;
2271 retval
= hfs_chkdq(cp
,
2272 (int64_t)(roundup(moreBytesRequested
, vcb
->blockSize
)),
2279 * Metadata zone checks.
2281 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) {
2283 * Allocate Journal and Quota files in metadata zone.
2285 if (hfs_virtualmetafile(cp
)) {
2286 extendFlags
|= kEFMetadataMask
;
2287 blockHint
= hfsmp
->hfs_metazone_start
;
2288 } else if ((blockHint
>= hfsmp
->hfs_metazone_start
) &&
2289 (blockHint
<= hfsmp
->hfs_metazone_end
)) {
2291 * Move blockHint outside metadata zone.
2293 blockHint
= hfsmp
->hfs_metazone_end
+ 1;
2297 if (hfs_start_transaction(hfsmp
) != 0) {
2302 /* Protect extents b-tree and allocation bitmap */
2303 lockflags
= SFL_BITMAP
;
2304 if (overflow_extents(fp
))
2305 lockflags
|= SFL_EXTENTS
;
2306 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2308 retval
= MacToVFSError(ExtendFileC(vcb
,
2313 &actualBytesAdded
));
2315 *(ap
->a_bytesallocated
) = actualBytesAdded
;
2316 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)vcb
->blockSize
;
2318 hfs_systemfile_unlock(hfsmp
, lockflags
);
2321 (void) hfs_update(vp
, TRUE
);
2322 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2325 hfs_end_transaction(hfsmp
);
2328 * if we get an error and no changes were made then exit
2329 * otherwise we must do the hfs_update to reflect the changes
2331 if (retval
&& (startingPEOF
== filebytes
))
2335 * Adjust actualBytesAdded to be allocation block aligned, not
2336 * clump size aligned.
2337 * NOTE: So what we are reporting does not affect reality
2338 * until the file is closed, when we truncate the file to allocation
2341 if ((actualBytesAdded
!= 0) && (moreBytesRequested
< actualBytesAdded
))
2342 *(ap
->a_bytesallocated
) =
2343 roundup(moreBytesRequested
, (off_t
)vcb
->blockSize
);
2345 } else { /* Shorten the size of the file */
2347 if (fp
->ff_size
> length
) {
2349 * Any buffers that are past the truncation point need to be
2350 * invalidated (to maintain buffer cache consistency).
2354 if (hfs_start_transaction(hfsmp
) != 0) {
2359 /* Protect extents b-tree and allocation bitmap */
2360 lockflags
= SFL_BITMAP
;
2361 if (overflow_extents(fp
))
2362 lockflags
|= SFL_EXTENTS
;
2363 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2365 retval
= MacToVFSError(TruncateFileC(vcb
, (FCB
*)fp
, length
, false));
2367 hfs_systemfile_unlock(hfsmp
, lockflags
);
2369 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)vcb
->blockSize
;
2372 (void) hfs_update(vp
, TRUE
);
2373 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2376 hfs_end_transaction(hfsmp
);
2380 * if we get an error and no changes were made then exit
2381 * otherwise we must do the hfs_update to reflect the changes
2383 if (retval
&& (startingPEOF
== filebytes
)) goto Err_Exit
;
2385 /* These are bytesreleased */
2386 (void) hfs_chkdq(cp
, (int64_t)-((startingPEOF
- filebytes
)), NOCRED
,0);
2389 if (fp
->ff_size
> filebytes
) {
2390 fp
->ff_size
= filebytes
;
2393 ubc_setsize(vp
, fp
->ff_size
);
2394 hfs_lock(cp
, HFS_FORCE_LOCK
);
2399 cp
->c_touch_chgtime
= TRUE
;
2400 cp
->c_touch_modtime
= TRUE
;
2401 retval2
= hfs_update(vp
, MNT_WAIT
);
2412 * Pagein for HFS filesystem
2415 hfs_vnop_pagein(struct vnop_pagein_args
*ap
)
2417 struct vnop_pagein_args {
2420 vm_offset_t a_pl_offset,
2424 vfs_context_t a_context;
2428 vnode_t vp
= ap
->a_vp
;
2431 error
= cluster_pagein(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
2432 ap
->a_size
, (off_t
)VTOF(vp
)->ff_size
, ap
->a_flags
);
2434 * Keep track of blocks read.
2436 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
&& error
== 0) {
2438 struct filefork
*fp
;
2440 int took_cnode_lock
= 0;
2445 if (ap
->a_f_offset
== 0 && fp
->ff_size
< PAGE_SIZE
)
2446 bytesread
= fp
->ff_size
;
2448 bytesread
= ap
->a_size
;
2450 /* When ff_bytesread exceeds 32-bits, update it behind the cnode lock. */
2451 if ((fp
->ff_bytesread
+ bytesread
) > 0x00000000ffffffff) {
2452 hfs_lock(cp
, HFS_FORCE_LOCK
);
2453 took_cnode_lock
= 1;
2456 * If this file hasn't been seen since the start of
2457 * the current sampling period then start over.
2459 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
) {
2462 fp
->ff_bytesread
= bytesread
;
2464 cp
->c_atime
= tv
.tv_sec
;
2466 fp
->ff_bytesread
+= bytesread
;
2468 cp
->c_touch_acctime
= TRUE
;
2469 if (took_cnode_lock
)
2476 * Pageout for HFS filesystem.
2479 hfs_vnop_pageout(struct vnop_pageout_args
*ap
)
2481 struct vnop_pageout_args {
2484 vm_offset_t a_pl_offset,
2488 vfs_context_t a_context;
2492 vnode_t vp
= ap
->a_vp
;
2494 struct filefork
*fp
;
2500 if (cp
->c_lockowner
== current_thread()) {
2501 panic("pageout: %s cnode lock already held!\n",
2502 cp
->c_desc
.cd_nameptr
? cp
->c_desc
.cd_nameptr
: "");
2504 if ( (retval
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
2505 if (!(ap
->a_flags
& UPL_NOCOMMIT
)) {
2506 ubc_upl_abort_range(ap
->a_pl
,
2509 UPL_ABORT_FREE_ON_EMPTY
);
2515 filesize
= fp
->ff_size
;
2516 end_of_range
= ap
->a_f_offset
+ ap
->a_size
- 1;
2518 if (end_of_range
>= filesize
) {
2519 end_of_range
= (off_t
)(filesize
- 1);
2521 if (ap
->a_f_offset
< filesize
) {
2522 rl_remove(ap
->a_f_offset
, end_of_range
, &fp
->ff_invalidranges
);
2523 cp
->c_flag
|= C_MODIFIED
; /* leof is dirty */
2527 retval
= cluster_pageout(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
2528 ap
->a_size
, filesize
, ap
->a_flags
);
2531 * If data was written, and setuid or setgid bits are set and
2532 * this process is not the superuser then clear the setuid and
2533 * setgid bits as a precaution against tampering.
2535 if ((retval
== 0) &&
2536 (cp
->c_mode
& (S_ISUID
| S_ISGID
)) &&
2537 (vfs_context_suser(ap
->a_context
) != 0)) {
2538 hfs_lock(cp
, HFS_FORCE_LOCK
);
2539 cp
->c_mode
&= ~(S_ISUID
| S_ISGID
);
2540 cp
->c_touch_chgtime
= TRUE
;
2547 * Intercept B-Tree node writes to unswap them if necessary.
2550 hfs_vnop_bwrite(struct vnop_bwrite_args
*ap
)
2553 register struct buf
*bp
= ap
->a_bp
;
2554 register struct vnode
*vp
= buf_vnode(bp
);
2555 BlockDescriptor block
;
2557 /* Trap B-Tree writes */
2558 if ((VTOC(vp
)->c_fileid
== kHFSExtentsFileID
) ||
2559 (VTOC(vp
)->c_fileid
== kHFSCatalogFileID
) ||
2560 (VTOC(vp
)->c_fileid
== kHFSAttributesFileID
)) {
2563 * Swap and validate the node if it is in native byte order.
2564 * This is always be true on big endian, so we always validate
2565 * before writing here. On little endian, the node typically has
2566 * been swapped and validatated when it was written to the journal,
2567 * so we won't do anything here.
2569 if (((UInt16
*)((char *)buf_dataptr(bp
) + buf_count(bp
) - 2))[0] == 0x000e) {
2570 /* Prepare the block pointer */
2571 block
.blockHeader
= bp
;
2572 block
.buffer
= (char *)buf_dataptr(bp
);
2573 block
.blockNum
= buf_lblkno(bp
);
2574 /* not found in cache ==> came from disk */
2575 block
.blockReadFromDisk
= (buf_fromcache(bp
) == 0);
2576 block
.blockSize
= buf_count(bp
);
2578 /* Endian un-swap B-Tree node */
2579 retval
= hfs_swap_BTNode (&block
, vp
, kSwapBTNodeHostToBig
);
2581 panic("hfs_vnop_bwrite: about to write corrupt node!\n");
2585 /* This buffer shouldn't be locked anymore but if it is clear it */
2586 if ((buf_flags(bp
) & B_LOCKED
)) {
2588 if (VTOHFS(vp
)->jnl
) {
2589 panic("hfs: CLEARING the lock bit on bp 0x%x\n", bp
);
2591 buf_clearflags(bp
, B_LOCKED
);
2593 retval
= vn_bwrite (ap
);
2599 * Relocate a file to a new location on disk
2600 * cnode must be locked on entry
2602 * Relocation occurs by cloning the file's data from its
2603 * current set of blocks to a new set of blocks. During
2604 * the relocation all of the blocks (old and new) are
2605 * owned by the file.
2612 * ----------------- -----------------
2613 * |///////////////| | | STEP 1 (aquire new blocks)
2614 * ----------------- -----------------
2617 * ----------------- -----------------
2618 * |///////////////| |///////////////| STEP 2 (clone data)
2619 * ----------------- -----------------
2623 * |///////////////| STEP 3 (head truncate blocks)
2627 * During steps 2 and 3 page-outs to file offsets less
2628 * than or equal to N are suspended.
2630 * During step 3 page-ins to the file get supended.
2634 hfs_relocate(struct vnode
*vp
, u_int32_t blockHint
, kauth_cred_t cred
,
2638 struct filefork
*fp
;
2639 struct hfsmount
*hfsmp
;
2644 u_int32_t nextallocsave
;
2645 daddr64_t sector_a
, sector_b
;
2646 int disabled_caching
= 0;
2651 int took_trunc_lock
= 0;
2653 enum vtype vnodetype
;
2655 vnodetype
= vnode_vtype(vp
);
2656 if (vnodetype
!= VREG
&& vnodetype
!= VLNK
) {
2661 if (hfsmp
->hfs_flags
& HFS_FRAGMENTED_FREESPACE
) {
2667 if (fp
->ff_unallocblocks
)
2669 blksize
= hfsmp
->blockSize
;
2671 blockHint
= hfsmp
->nextAllocation
;
2673 if ((fp
->ff_size
> (u_int64_t
)0x7fffffff) ||
2674 ((fp
->ff_size
> blksize
) && vnodetype
== VLNK
)) {
2679 // We do not believe that this call to hfs_fsync() is
2680 // necessary and it causes a journal transaction
2681 // deadlock so we are removing it.
2683 //if (vnodetype == VREG && !vnode_issystem(vp)) {
2684 // retval = hfs_fsync(vp, MNT_WAIT, 0, p);
2689 if (!vnode_issystem(vp
) && (vnodetype
!= VLNK
)) {
2691 hfs_lock_truncate(cp
, TRUE
);
2692 if ((retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
2693 hfs_unlock_truncate(cp
);
2696 took_trunc_lock
= 1;
2698 headblks
= fp
->ff_blocks
;
2699 datablks
= howmany(fp
->ff_size
, blksize
);
2700 growsize
= datablks
* blksize
;
2701 eflags
= kEFContigMask
| kEFAllMask
| kEFNoClumpMask
;
2702 if (blockHint
>= hfsmp
->hfs_metazone_start
&&
2703 blockHint
<= hfsmp
->hfs_metazone_end
)
2704 eflags
|= kEFMetadataMask
;
2706 if (hfs_start_transaction(hfsmp
) != 0) {
2707 if (took_trunc_lock
)
2708 hfs_unlock_truncate(cp
);
2713 * Protect the extents b-tree and the allocation bitmap
2714 * during MapFileBlockC and ExtendFileC operations.
2716 lockflags
= SFL_BITMAP
;
2717 if (overflow_extents(fp
))
2718 lockflags
|= SFL_EXTENTS
;
2719 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2721 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, 1, growsize
- 1, §or_a
, NULL
);
2723 retval
= MacToVFSError(retval
);
2728 * STEP 1 - aquire new allocation blocks.
2730 if (!vnode_isnocache(vp
)) {
2731 vnode_setnocache(vp
);
2732 disabled_caching
= 1;
2735 nextallocsave
= hfsmp
->nextAllocation
;
2736 retval
= ExtendFileC(hfsmp
, (FCB
*)fp
, growsize
, blockHint
, eflags
, &newbytes
);
2737 if (eflags
& kEFMetadataMask
) {
2738 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
2739 hfsmp
->nextAllocation
= nextallocsave
;
2740 hfsmp
->vcbFlags
|= 0xFF00;
2741 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2744 retval
= MacToVFSError(retval
);
2746 cp
->c_flag
|= C_MODIFIED
;
2747 if (newbytes
< growsize
) {
2750 } else if (fp
->ff_blocks
< (headblks
+ datablks
)) {
2751 printf("hfs_relocate: allocation failed");
2756 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, 1, growsize
, §or_b
, NULL
);
2758 retval
= MacToVFSError(retval
);
2759 } else if ((sector_a
+ 1) == sector_b
) {
2762 } else if ((eflags
& kEFMetadataMask
) &&
2763 ((((u_int64_t
)sector_b
* hfsmp
->hfs_phys_block_size
) / blksize
) >
2764 hfsmp
->hfs_metazone_end
)) {
2765 printf("hfs_relocate: didn't move into metadata zone\n");
2770 /* Done with system locks and journal for now. */
2771 hfs_systemfile_unlock(hfsmp
, lockflags
);
2773 hfs_end_transaction(hfsmp
);
2778 * Check to see if failure is due to excessive fragmentation.
2780 if ((retval
== ENOSPC
) &&
2781 (hfs_freeblks(hfsmp
, 0) > (datablks
* 2))) {
2782 hfsmp
->hfs_flags
|= HFS_FRAGMENTED_FREESPACE
;
2787 * STEP 2 - clone file data into the new allocation blocks.
2790 if (vnodetype
== VLNK
)
2791 retval
= hfs_clonelink(vp
, blksize
, cred
, p
);
2792 else if (vnode_issystem(vp
))
2793 retval
= hfs_clonesysfile(vp
, headblks
, datablks
, blksize
, cred
, p
);
2795 retval
= hfs_clonefile(vp
, headblks
, datablks
, blksize
);
2797 /* Start transaction for step 3 or for a restore. */
2798 if (hfs_start_transaction(hfsmp
) != 0) {
2807 * STEP 3 - switch to cloned data and remove old blocks.
2809 lockflags
= SFL_BITMAP
;
2810 if (overflow_extents(fp
))
2811 lockflags
|= SFL_EXTENTS
;
2812 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2814 retval
= HeadTruncateFile(hfsmp
, (FCB
*)fp
, headblks
);
2816 hfs_systemfile_unlock(hfsmp
, lockflags
);
2821 if (took_trunc_lock
)
2822 hfs_unlock_truncate(cp
);
2825 hfs_systemfile_unlock(hfsmp
, lockflags
);
2829 // See comment up above about calls to hfs_fsync()
2832 // retval = hfs_fsync(vp, MNT_WAIT, 0, p);
2835 if (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)
2836 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
2838 (void) hfs_flushvolumeheader(hfsmp
, MNT_NOWAIT
, 0);
2841 if (disabled_caching
) {
2842 vnode_clearnocache(vp
);
2845 hfs_end_transaction(hfsmp
);
2850 if (fp
->ff_blocks
== headblks
)
2853 * Give back any newly allocated space.
2855 if (lockflags
== 0) {
2856 lockflags
= SFL_BITMAP
;
2857 if (overflow_extents(fp
))
2858 lockflags
|= SFL_EXTENTS
;
2859 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2862 (void) TruncateFileC(hfsmp
, (FCB
*)fp
, fp
->ff_size
, false);
2864 hfs_systemfile_unlock(hfsmp
, lockflags
);
2867 if (took_trunc_lock
)
2868 hfs_unlock_truncate(cp
);
2878 hfs_clonelink(struct vnode
*vp
, int blksize
, kauth_cred_t cred
, struct proc
*p
)
2880 struct buf
*head_bp
= NULL
;
2881 struct buf
*tail_bp
= NULL
;
2885 error
= (int)buf_meta_bread(vp
, (daddr64_t
)0, blksize
, cred
, &head_bp
);
2889 tail_bp
= buf_getblk(vp
, (daddr64_t
)1, blksize
, 0, 0, BLK_META
);
2890 if (tail_bp
== NULL
) {
2894 bcopy((char *)buf_dataptr(head_bp
), (char *)buf_dataptr(tail_bp
), blksize
);
2895 error
= (int)buf_bwrite(tail_bp
);
2898 buf_markinvalid(head_bp
);
2899 buf_brelse(head_bp
);
2901 (void) buf_invalidateblks(vp
, BUF_WRITE_DATA
, 0, 0);
2907 * Clone a file's data within the file.
2911 hfs_clonefile(struct vnode
*vp
, int blkstart
, int blkcnt
, int blksize
)
2923 filesize
= VTOF(vp
)->ff_blocks
* blksize
; /* virtual file size */
2924 writebase
= blkstart
* blksize
;
2925 copysize
= blkcnt
* blksize
;
2926 iosize
= bufsize
= MIN(copysize
, 4096 * 16);
2929 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufp
, bufsize
)) {
2932 hfs_unlock(VTOC(vp
));
2934 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2936 while (offset
< copysize
) {
2937 iosize
= MIN(copysize
- offset
, iosize
);
2939 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
2940 uio_addiov(auio
, (uintptr_t)bufp
, iosize
);
2942 error
= cluster_read(vp
, auio
, copysize
, 0);
2944 printf("hfs_clonefile: cluster_read failed - %d\n", error
);
2947 if (uio_resid(auio
) != 0) {
2948 printf("clonedata: cluster_read: uio_resid = %lld\n", uio_resid(auio
));
2953 uio_reset(auio
, writebase
+ offset
, UIO_SYSSPACE32
, UIO_WRITE
);
2954 uio_addiov(auio
, (uintptr_t)bufp
, iosize
);
2956 error
= cluster_write(vp
, auio
, filesize
+ offset
,
2957 filesize
+ offset
+ iosize
,
2958 uio_offset(auio
), 0, IO_NOCACHE
| IO_SYNC
);
2960 printf("hfs_clonefile: cluster_write failed - %d\n", error
);
2963 if (uio_resid(auio
) != 0) {
2964 printf("hfs_clonefile: cluster_write failed - uio_resid not zero\n");
2973 * No need to call ubc_sync_range or hfs_invalbuf
2974 * since the file was copied using IO_NOCACHE.
2977 kmem_free(kernel_map
, (vm_offset_t
)bufp
, bufsize
);
2979 hfs_lock(VTOC(vp
), HFS_FORCE_LOCK
);
2984 * Clone a system (metadata) file.
2988 hfs_clonesysfile(struct vnode
*vp
, int blkstart
, int blkcnt
, int blksize
,
2989 kauth_cred_t cred
, struct proc
*p
)
2995 struct buf
*bp
= NULL
;
2998 daddr64_t start_blk
;
3005 iosize
= GetLogicalBlockSize(vp
);
3006 bufsize
= MIN(blkcnt
* blksize
, 1024 * 1024) & ~(iosize
- 1);
3007 breadcnt
= bufsize
/ iosize
;
3009 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufp
, bufsize
)) {
3012 start_blk
= ((daddr64_t
)blkstart
* blksize
) / iosize
;
3013 last_blk
= ((daddr64_t
)blkcnt
* blksize
) / iosize
;
3016 while (blkno
< last_blk
) {
3018 * Read up to a megabyte
3021 for (i
= 0, blk
= blkno
; (i
< breadcnt
) && (blk
< last_blk
); ++i
, ++blk
) {
3022 error
= (int)buf_meta_bread(vp
, blk
, iosize
, cred
, &bp
);
3024 printf("hfs_clonesysfile: meta_bread error %d\n", error
);
3027 if (buf_count(bp
) != iosize
) {
3028 printf("hfs_clonesysfile: b_bcount is only %d\n", buf_count(bp
));
3031 bcopy((char *)buf_dataptr(bp
), offset
, iosize
);
3033 buf_markinvalid(bp
);
3041 * Write up to a megabyte
3044 for (i
= 0; (i
< breadcnt
) && (blkno
< last_blk
); ++i
, ++blkno
) {
3045 bp
= buf_getblk(vp
, start_blk
+ blkno
, iosize
, 0, 0, BLK_META
);
3047 printf("hfs_clonesysfile: getblk failed on blk %qd\n", start_blk
+ blkno
);
3051 bcopy(offset
, (char *)buf_dataptr(bp
), iosize
);
3052 error
= (int)buf_bwrite(bp
);
3064 kmem_free(kernel_map
, (vm_offset_t
)bufp
, bufsize
);
3066 error
= hfs_fsync(vp
, MNT_WAIT
, 0, p
);