2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* @(#)hfs_readwrite.c 1.0
24 * (c) 1998-2001 Apple Computer, Inc. All Rights Reserved
26 * hfs_readwrite.c -- vnode operations to deal with reading and writing files.
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/resourcevar.h>
33 #include <sys/kernel.h>
34 #include <sys/fcntl.h>
35 #include <sys/filedesc.h>
39 #include <sys/kauth.h>
40 #include <sys/vnode.h>
42 #include <sys/vfs_context.h>
44 #include <miscfs/specfs/specdev.h>
47 #include <vm/vm_pageout.h>
48 #include <vm/vm_kern.h>
50 #include <sys/kdebug.h>
53 #include "hfs_endian.h"
54 #include "hfs_fsctl.h"
55 #include "hfs_quota.h"
56 #include "hfscommon/headers/FileMgrInternal.h"
57 #include "hfscommon/headers/BTreesInternal.h"
58 #include "hfs_cnode.h"
61 extern int overflow_extents(struct filefork
*fp
);
63 #define can_cluster(size) ((((size & (4096-1))) == 0) && (size <= (MAXPHYSIO/2)))
66 MAXHFSFILESIZE
= 0x7FFFFFFF /* this needs to go in the mount structure */
69 extern u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
71 extern int hfs_setextendedsecurity(struct hfsmount
*, int);
74 static int hfs_clonelink(struct vnode
*, int, kauth_cred_t
, struct proc
*);
75 static int hfs_clonefile(struct vnode
*, int, int, int);
76 static int hfs_clonesysfile(struct vnode
*, int, int, int, kauth_cred_t
, struct proc
*);
79 /*****************************************************************************
81 * I/O Operations on vnodes
83 *****************************************************************************/
84 int hfs_vnop_read(struct vnop_read_args
*);
85 int hfs_vnop_write(struct vnop_write_args
*);
86 int hfs_vnop_ioctl(struct vnop_ioctl_args
*);
87 int hfs_vnop_select(struct vnop_select_args
*);
88 int hfs_vnop_blktooff(struct vnop_blktooff_args
*);
89 int hfs_vnop_offtoblk(struct vnop_offtoblk_args
*);
90 int hfs_vnop_blockmap(struct vnop_blockmap_args
*);
91 int hfs_vnop_strategy(struct vnop_strategy_args
*);
92 int hfs_vnop_allocate(struct vnop_allocate_args
*);
93 int hfs_vnop_pagein(struct vnop_pagein_args
*);
94 int hfs_vnop_pageout(struct vnop_pageout_args
*);
95 int hfs_vnop_bwrite(struct vnop_bwrite_args
*);
99 * Read data from a file.
102 hfs_vnop_read(struct vnop_read_args
*ap
)
104 uio_t uio
= ap
->a_uio
;
105 struct vnode
*vp
= ap
->a_vp
;
108 struct hfsmount
*hfsmp
;
111 off_t start_resid
= uio_resid(uio
);
112 off_t offset
= uio_offset(uio
);
116 /* Preflight checks */
117 if (!vnode_isreg(vp
)) {
118 /* can only read regular files */
124 if (start_resid
== 0)
125 return (0); /* Nothing left to do */
127 return (EINVAL
); /* cant read from a negative offset */
133 /* Protect against a size change. */
134 hfs_lock_truncate(cp
, 0);
136 filesize
= fp
->ff_size
;
137 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
138 if (offset
> filesize
) {
139 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) &&
140 (offset
> (off_t
)MAXHFSFILESIZE
)) {
146 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 12)) | DBG_FUNC_START
,
147 (int)uio_offset(uio
), uio_resid(uio
), (int)filesize
, (int)filebytes
, 0);
149 retval
= cluster_read(vp
, uio
, filesize
, 0);
151 cp
->c_touch_acctime
= TRUE
;
153 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 12)) | DBG_FUNC_END
,
154 (int)uio_offset(uio
), uio_resid(uio
), (int)filesize
, (int)filebytes
, 0);
157 * Keep track blocks read
159 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
&& retval
== 0) {
160 int took_cnode_lock
= 0;
163 bytesread
= start_resid
- uio_resid(uio
);
165 /* When ff_bytesread exceeds 32-bits, update it behind the cnode lock. */
166 if ((fp
->ff_bytesread
+ bytesread
) > 0x00000000ffffffff) {
167 hfs_lock(cp
, HFS_FORCE_LOCK
);
171 * If this file hasn't been seen since the start of
172 * the current sampling period then start over.
174 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
) {
177 fp
->ff_bytesread
= bytesread
;
179 cp
->c_atime
= tv
.tv_sec
;
181 fp
->ff_bytesread
+= bytesread
;
187 hfs_unlock_truncate(cp
);
192 * Write data to a file.
195 hfs_vnop_write(struct vnop_write_args
*ap
)
197 uio_t uio
= ap
->a_uio
;
198 struct vnode
*vp
= ap
->a_vp
;
201 struct hfsmount
*hfsmp
;
202 kauth_cred_t cred
= NULL
;
206 off_t actualBytesAdded
;
211 int ioflag
= ap
->a_ioflag
;
214 int cnode_locked
= 0;
216 // LP64todo - fix this! uio_resid may be 64-bit value
217 resid
= uio_resid(uio
);
218 offset
= uio_offset(uio
);
224 if (!vnode_isreg(vp
))
225 return (EPERM
); /* Can only write regular files */
227 /* Protect against a size change. */
228 hfs_lock_truncate(VTOC(vp
), TRUE
);
230 if ( (retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
231 hfs_unlock_truncate(VTOC(vp
));
238 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
240 if (ioflag
& IO_APPEND
) {
241 uio_setoffset(uio
, fp
->ff_size
);
242 offset
= fp
->ff_size
;
244 if ((cp
->c_flags
& APPEND
) && offset
!= fp
->ff_size
) {
249 origFileSize
= fp
->ff_size
;
250 eflags
= kEFDeferMask
; /* defer file block allocations */
252 #ifdef HFS_SPARSE_DEV
254 * When the underlying device is sparse and space
255 * is low (< 8MB), stop doing delayed allocations
256 * and begin doing synchronous I/O.
258 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) &&
259 (hfs_freeblks(hfsmp
, 0) < 2048)) {
260 eflags
&= ~kEFDeferMask
;
263 #endif /* HFS_SPARSE_DEV */
265 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_START
,
266 (int)offset
, uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
268 /* Now test if we need to extend the file */
269 /* Doing so will adjust the filebytes for us */
271 writelimit
= offset
+ resid
;
272 if (writelimit
<= filebytes
)
275 cred
= vfs_context_ucred(ap
->a_context
);
277 bytesToAdd
= writelimit
- filebytes
;
278 retval
= hfs_chkdq(cp
, (int64_t)(roundup(bytesToAdd
, hfsmp
->blockSize
)),
284 if (hfs_start_transaction(hfsmp
) != 0) {
289 while (writelimit
> filebytes
) {
290 bytesToAdd
= writelimit
- filebytes
;
291 if (cred
&& suser(cred
, NULL
) != 0)
292 eflags
|= kEFReserveMask
;
294 /* Protect extents b-tree and allocation bitmap */
295 lockflags
= SFL_BITMAP
;
296 if (overflow_extents(fp
))
297 lockflags
|= SFL_EXTENTS
;
298 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
300 /* Files that are changing size are not hot file candidates. */
301 if (hfsmp
->hfc_stage
== HFC_RECORDING
) {
302 fp
->ff_bytesread
= 0;
304 retval
= MacToVFSError(ExtendFileC (hfsmp
, (FCB
*)fp
, bytesToAdd
,
305 0, eflags
, &actualBytesAdded
));
307 hfs_systemfile_unlock(hfsmp
, lockflags
);
309 if ((actualBytesAdded
== 0) && (retval
== E_NONE
))
311 if (retval
!= E_NONE
)
313 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
314 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_NONE
,
315 (int)offset
, uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
317 (void) hfs_update(vp
, TRUE
);
318 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
319 (void) hfs_end_transaction(hfsmp
);
322 if (retval
== E_NONE
) {
330 struct rl_entry
*invalid_range
;
332 if (writelimit
> fp
->ff_size
)
333 filesize
= writelimit
;
335 filesize
= fp
->ff_size
;
337 lflag
= (ioflag
& IO_SYNC
);
339 if (offset
<= fp
->ff_size
) {
340 zero_off
= offset
& ~PAGE_MASK_64
;
342 /* Check to see whether the area between the zero_offset and the start
343 of the transfer to see whether is invalid and should be zero-filled
344 as part of the transfer:
346 if (offset
> zero_off
) {
347 if (rl_scan(&fp
->ff_invalidranges
, zero_off
, offset
- 1, &invalid_range
) != RL_NOOVERLAP
)
348 lflag
|= IO_HEADZEROFILL
;
351 off_t eof_page_base
= fp
->ff_size
& ~PAGE_MASK_64
;
353 /* The bytes between fp->ff_size and uio->uio_offset must never be
354 read without being zeroed. The current last block is filled with zeroes
355 if it holds valid data but in all cases merely do a little bookkeeping
356 to track the area from the end of the current last page to the start of
357 the area actually written. For the same reason only the bytes up to the
358 start of the page where this write will start is invalidated; any remainder
359 before uio->uio_offset is explicitly zeroed as part of the cluster_write.
361 Note that inval_start, the start of the page after the current EOF,
362 may be past the start of the write, in which case the zeroing
363 will be handled by the cluser_write of the actual data.
365 inval_start
= (fp
->ff_size
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
366 inval_end
= offset
& ~PAGE_MASK_64
;
367 zero_off
= fp
->ff_size
;
369 if ((fp
->ff_size
& PAGE_MASK_64
) &&
370 (rl_scan(&fp
->ff_invalidranges
,
373 &invalid_range
) != RL_NOOVERLAP
)) {
374 /* The page containing the EOF is not valid, so the
375 entire page must be made inaccessible now. If the write
376 starts on a page beyond the page containing the eof
377 (inval_end > eof_page_base), add the
378 whole page to the range to be invalidated. Otherwise
379 (i.e. if the write starts on the same page), zero-fill
380 the entire page explicitly now:
382 if (inval_end
> eof_page_base
) {
383 inval_start
= eof_page_base
;
385 zero_off
= eof_page_base
;
389 if (inval_start
< inval_end
) {
391 /* There's some range of data that's going to be marked invalid */
393 if (zero_off
< inval_start
) {
394 /* The pages between inval_start and inval_end are going to be invalidated,
395 and the actual write will start on a page past inval_end. Now's the last
396 chance to zero-fill the page containing the EOF:
400 retval
= cluster_write(vp
, (uio_t
) 0,
401 fp
->ff_size
, inval_start
,
403 lflag
| IO_HEADZEROFILL
| IO_NOZERODIRTY
);
404 hfs_lock(cp
, HFS_FORCE_LOCK
);
406 if (retval
) goto ioerr_exit
;
407 offset
= uio_offset(uio
);
410 /* Mark the remaining area of the newly allocated space as invalid: */
411 rl_add(inval_start
, inval_end
- 1 , &fp
->ff_invalidranges
);
413 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
414 zero_off
= fp
->ff_size
= inval_end
;
417 if (offset
> zero_off
) lflag
|= IO_HEADZEROFILL
;
420 /* Check to see whether the area between the end of the write and the end of
421 the page it falls in is invalid and should be zero-filled as part of the transfer:
423 tail_off
= (writelimit
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
424 if (tail_off
> filesize
) tail_off
= filesize
;
425 if (tail_off
> writelimit
) {
426 if (rl_scan(&fp
->ff_invalidranges
, writelimit
, tail_off
- 1, &invalid_range
) != RL_NOOVERLAP
) {
427 lflag
|= IO_TAILZEROFILL
;
432 * if the write starts beyond the current EOF (possibly advanced in the
433 * zeroing of the last block, above), then we'll zero fill from the current EOF
434 * to where the write begins:
436 * NOTE: If (and ONLY if) the portion of the file about to be written is
437 * before the current EOF it might be marked as invalid now and must be
438 * made readable (removed from the invalid ranges) before cluster_write
441 io_start
= (lflag
& IO_HEADZEROFILL
) ? zero_off
: offset
;
442 if (io_start
< fp
->ff_size
) {
445 io_end
= (lflag
& IO_TAILZEROFILL
) ? tail_off
: writelimit
;
446 rl_remove(io_start
, io_end
- 1, &fp
->ff_invalidranges
);
451 retval
= cluster_write(vp
, uio
, fp
->ff_size
, filesize
, zero_off
,
452 tail_off
, lflag
| IO_NOZERODIRTY
);
453 offset
= uio_offset(uio
);
454 if (offset
> fp
->ff_size
) {
455 fp
->ff_size
= offset
;
457 ubc_setsize(vp
, fp
->ff_size
); /* XXX check errors */
458 /* Files that are changing size are not hot file candidates. */
459 if (hfsmp
->hfc_stage
== HFC_RECORDING
)
460 fp
->ff_bytesread
= 0;
462 if (resid
> uio_resid(uio
)) {
463 cp
->c_touch_chgtime
= TRUE
;
464 cp
->c_touch_modtime
= TRUE
;
467 HFS_KNOTE(vp
, NOTE_WRITE
);
471 * If we successfully wrote any data, and we are not the superuser
472 * we clear the setuid and setgid bits as a precaution against
475 if (cp
->c_mode
& (S_ISUID
| S_ISGID
)) {
476 cred
= vfs_context_ucred(ap
->a_context
);
477 if (resid
> uio_resid(uio
) && cred
&& suser(cred
, NULL
)) {
479 hfs_lock(cp
, HFS_FORCE_LOCK
);
482 cp
->c_mode
&= ~(S_ISUID
| S_ISGID
);
486 if (ioflag
& IO_UNIT
) {
488 hfs_lock(cp
, HFS_FORCE_LOCK
);
491 (void)hfs_truncate(vp
, origFileSize
, ioflag
& IO_SYNC
,
493 // LP64todo - fix this! resid needs to by user_ssize_t
494 uio_setoffset(uio
, (uio_offset(uio
) - (resid
- uio_resid(uio
))));
495 uio_setresid(uio
, resid
);
496 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)hfsmp
->blockSize
;
498 } else if ((ioflag
& IO_SYNC
) && (resid
> uio_resid(uio
))) {
500 hfs_lock(cp
, HFS_FORCE_LOCK
);
503 retval
= hfs_update(vp
, TRUE
);
505 /* Updating vcbWrCnt doesn't need to be atomic. */
508 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 0)) | DBG_FUNC_END
,
509 (int)uio_offset(uio
), uio_resid(uio
), (int)fp
->ff_size
, (int)filebytes
, 0);
513 hfs_unlock_truncate(cp
);
517 /* support for the "bulk-access" fcntl */
519 #define CACHE_ELEMS 64
520 #define CACHE_LEVELS 16
521 #define PARENT_IDS_FLAG 0x100
523 /* from hfs_attrlist.c */
524 extern unsigned long DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
,
525 mode_t obj_mode
, struct mount
*mp
,
526 kauth_cred_t cred
, struct proc
*p
);
528 /* from vfs/vfs_fsevents.c */
529 extern char *get_pathbuff(void);
530 extern void release_pathbuff(char *buff
);
532 struct access_cache
{
534 int cachehits
; /* these two for statistics gathering */
536 unsigned int *acache
;
541 uid_t uid
; /* IN: effective user id */
542 short flags
; /* IN: access requested (i.e. R_OK) */
543 short num_groups
; /* IN: number of groups user belongs to */
544 int num_files
; /* IN: number of files to process */
545 int *file_ids
; /* IN: array of file ids */
546 gid_t
*groups
; /* IN: array of groups */
547 short *access
; /* OUT: access info for each file (0 for 'has access') */
550 struct user_access_t
{
551 uid_t uid
; /* IN: effective user id */
552 short flags
; /* IN: access requested (i.e. R_OK) */
553 short num_groups
; /* IN: number of groups user belongs to */
554 int num_files
; /* IN: number of files to process */
555 user_addr_t file_ids
; /* IN: array of file ids */
556 user_addr_t groups
; /* IN: array of groups */
557 user_addr_t access
; /* OUT: access info for each file (0 for 'has access') */
561 * Perform a binary search for the given parent_id. Return value is
562 * found/not found boolean, and indexp will be the index of the item
563 * or the index at which to insert the item if it's not found.
566 lookup_bucket(struct access_cache
*cache
, int *indexp
, cnid_t parent_id
)
569 int index
, matches
= 0;
571 if (cache
->numcached
== 0) {
573 return 0; // table is empty, so insert at index=0 and report no match
576 if (cache
->numcached
> CACHE_ELEMS
) {
577 /*printf("EGAD! numcached is %d... cut our losses and trim to %d\n",
578 cache->numcached, CACHE_ELEMS);*/
579 cache
->numcached
= CACHE_ELEMS
;
583 hi
= cache
->numcached
- 1;
586 /* perform binary search for parent_id */
588 unsigned int mid
= (hi
- lo
)/2 + lo
;
589 unsigned int this_id
= cache
->acache
[mid
];
591 if (parent_id
== this_id
) {
596 if (parent_id
< this_id
) {
601 if (parent_id
> this_id
) {
607 /* check if lo and hi converged on the match */
608 if (parent_id
== cache
->acache
[hi
]) {
612 /* if no existing entry found, find index for new one */
614 index
= (parent_id
< cache
->acache
[hi
]) ? hi
: hi
+ 1;
625 * Add a node to the access_cache at the given index (or do a lookup first
626 * to find the index if -1 is passed in). We currently do a replace rather
627 * than an insert if the cache is full.
630 add_node(struct access_cache
*cache
, int index
, cnid_t nodeID
, int access
)
632 int lookup_index
= -1;
634 /* need to do a lookup first if -1 passed for index */
636 if (lookup_bucket(cache
, &lookup_index
, nodeID
)) {
637 if (cache
->haveaccess
[lookup_index
] != access
) {
638 /* change access info for existing entry... should never happen */
639 cache
->haveaccess
[lookup_index
] = access
;
642 /* mission accomplished */
645 index
= lookup_index
;
650 /* if the cache is full, do a replace rather than an insert */
651 if (cache
->numcached
>= CACHE_ELEMS
) {
652 //printf("cache is full (%d). replace at index %d\n", cache->numcached, index);
653 cache
->numcached
= CACHE_ELEMS
-1;
655 if (index
> cache
->numcached
) {
656 // printf("index %d pinned to %d\n", index, cache->numcached);
657 index
= cache
->numcached
;
659 } else if (index
>= 0 && index
< cache
->numcached
) {
660 /* only do bcopy if we're inserting */
661 bcopy( cache
->acache
+index
, cache
->acache
+(index
+1), (cache
->numcached
- index
)*sizeof(int) );
662 bcopy( cache
->haveaccess
+index
, cache
->haveaccess
+(index
+1), (cache
->numcached
- index
)*sizeof(Boolean
) );
665 cache
->acache
[index
] = nodeID
;
666 cache
->haveaccess
[index
] = access
;
679 snoop_callback(const struct cat_desc
*descp
, const struct cat_attr
*attrp
, void * arg
)
681 struct cinfo
*cip
= (struct cinfo
*)arg
;
683 cip
->uid
= attrp
->ca_uid
;
684 cip
->gid
= attrp
->ca_gid
;
685 cip
->mode
= attrp
->ca_mode
;
686 cip
->parentcnid
= descp
->cd_parentcnid
;
692 * Lookup the cnid's attr info (uid, gid, and mode) as well as its parent id. If the item
693 * isn't incore, then go to the catalog.
696 do_attr_lookup(struct hfsmount
*hfsmp
, struct access_cache
*cache
, dev_t dev
, cnid_t cnid
,
697 struct cnode
*skip_cp
, CatalogKey
*keyp
, struct cat_attr
*cnattrp
, struct proc
*p
)
701 /* if this id matches the one the fsctl was called with, skip the lookup */
702 if (cnid
== skip_cp
->c_cnid
) {
703 cnattrp
->ca_uid
= skip_cp
->c_uid
;
704 cnattrp
->ca_gid
= skip_cp
->c_gid
;
705 cnattrp
->ca_mode
= skip_cp
->c_mode
;
706 keyp
->hfsPlus
.parentID
= skip_cp
->c_parentcnid
;
710 /* otherwise, check the cnode hash incase the file/dir is incore */
711 if (hfs_chash_snoop(dev
, cnid
, snoop_callback
, &c_info
) == 0) {
712 cnattrp
->ca_uid
= c_info
.uid
;
713 cnattrp
->ca_gid
= c_info
.gid
;
714 cnattrp
->ca_mode
= c_info
.mode
;
715 keyp
->hfsPlus
.parentID
= c_info
.parentcnid
;
719 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
721 /* lookup this cnid in the catalog */
722 error
= cat_getkeyplusattr(hfsmp
, cnid
, keyp
, cnattrp
);
724 hfs_systemfile_unlock(hfsmp
, lockflags
);
734 * Compute whether we have access to the given directory (nodeID) and all its parents. Cache
735 * up to CACHE_LEVELS as we progress towards the root.
738 do_access_check(struct hfsmount
*hfsmp
, int *err
, struct access_cache
*cache
, HFSCatalogNodeID nodeID
,
739 struct cnode
*skip_cp
, struct proc
*theProcPtr
, kauth_cred_t myp_ucred
, dev_t dev
)
743 HFSCatalogNodeID thisNodeID
;
744 unsigned long myPerms
;
745 struct cat_attr cnattr
;
746 int cache_index
= -1;
749 int i
= 0, ids_to_cache
= 0;
750 int parent_ids
[CACHE_LEVELS
];
752 /* root always has access */
753 if (!suser(myp_ucred
, NULL
)) {
758 while (thisNodeID
>= kRootDirID
) {
759 myResult
= 0; /* default to "no access" */
761 /* check the cache before resorting to hitting the catalog */
763 /* ASSUMPTION: access info of cached entries is "final"... i.e. no need
764 * to look any further after hitting cached dir */
766 if (lookup_bucket(cache
, &cache_index
, thisNodeID
)) {
768 myResult
= cache
->haveaccess
[cache_index
];
769 goto ExitThisRoutine
;
772 /* remember which parents we want to cache */
773 if (ids_to_cache
< CACHE_LEVELS
) {
774 parent_ids
[ids_to_cache
] = thisNodeID
;
778 /* do the lookup (checks the cnode hash, then the catalog) */
779 myErr
= do_attr_lookup(hfsmp
, cache
, dev
, thisNodeID
, skip_cp
, &catkey
, &cnattr
, theProcPtr
);
781 goto ExitThisRoutine
; /* no access */
784 myPerms
= DerivePermissionSummary(cnattr
.ca_uid
, cnattr
.ca_gid
,
785 cnattr
.ca_mode
, hfsmp
->hfs_mp
,
786 myp_ucred
, theProcPtr
);
788 if ( (myPerms
& X_OK
) == 0 ) {
790 goto ExitThisRoutine
; /* no access */
793 /* up the hierarchy we go */
794 thisNodeID
= catkey
.hfsPlus
.parentID
;
797 /* if here, we have access to this node */
802 //printf("*** error %d from catalog looking up parent %d/%d!\n", myErr, dev, thisNodeID);
807 /* cache the parent directory(ies) */
808 for (i
= 0; i
< ids_to_cache
; i
++) {
809 /* small optimization: get rid of double-lookup for all these */
810 // printf("adding %d to cache with result: %d\n", parent_ids[i], myResult);
811 add_node(cache
, -1, parent_ids
[i
], myResult
);
816 /* end "bulk-access" support */
821 * Callback for use with freeze ioctl.
824 hfs_freezewrite_callback(struct vnode
*vp
, void *cargs
)
826 vnode_waitforwrites(vp
, 0, 0, 0, "hfs freeze");
832 * Control filesystem operating characteristics.
835 hfs_vnop_ioctl( struct vnop_ioctl_args
/* {
840 vfs_context_t a_context;
843 struct vnode
* vp
= ap
->a_vp
;
844 struct hfsmount
*hfsmp
= VTOHFS(vp
);
845 vfs_context_t context
= ap
->a_context
;
846 kauth_cred_t cred
= vfs_context_ucred(context
);
847 proc_t p
= vfs_context_proc(context
);
848 struct vfsstatfs
*vfsp
;
851 is64bit
= proc_is64bit(p
);
853 switch (ap
->a_command
) {
855 case HFS_RESIZE_VOLUME
: {
859 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
860 if (suser(cred
, NULL
) &&
861 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
862 return (EACCES
); /* must be owner of file system */
864 if (!vnode_isvroot(vp
)) {
867 newsize
= *(u_int64_t
*)ap
->a_data
;
868 cursize
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
870 if (newsize
> cursize
) {
871 return hfs_extendfs(hfsmp
, *(u_int64_t
*)ap
->a_data
, context
);
872 } else if (newsize
< cursize
) {
873 return hfs_truncatefs(hfsmp
, *(u_int64_t
*)ap
->a_data
, context
);
878 case HFS_CHANGE_NEXT_ALLOCATION
: {
881 if (vnode_vfsisrdonly(vp
)) {
884 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
885 if (suser(cred
, NULL
) &&
886 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
887 return (EACCES
); /* must be owner of file system */
889 if (!vnode_isvroot(vp
)) {
892 location
= *(u_int32_t
*)ap
->a_data
;
893 if (location
> hfsmp
->totalBlocks
- 1) {
896 /* Return previous value. */
897 *(u_int32_t
*)ap
->a_data
= hfsmp
->nextAllocation
;
898 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
899 hfsmp
->nextAllocation
= location
;
900 hfsmp
->vcbFlags
|= 0xFF00;
901 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
905 #ifdef HFS_SPARSE_DEV
906 case HFS_SETBACKINGSTOREINFO
: {
907 struct vnode
* bsfs_rootvp
;
908 struct vnode
* di_vp
;
909 struct hfs_backingstoreinfo
*bsdata
;
912 if (hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) {
915 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
916 if (suser(cred
, NULL
) &&
917 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
918 return (EACCES
); /* must be owner of file system */
920 bsdata
= (struct hfs_backingstoreinfo
*)ap
->a_data
;
921 if (bsdata
== NULL
) {
924 if ((error
= file_vnode(bsdata
->backingfd
, &di_vp
))) {
927 if ((error
= vnode_getwithref(di_vp
))) {
928 file_drop(bsdata
->backingfd
);
932 if (vnode_mount(vp
) == vnode_mount(di_vp
)) {
933 (void)vnode_put(di_vp
);
934 file_drop(bsdata
->backingfd
);
939 * Obtain the backing fs root vnode and keep a reference
940 * on it. This reference will be dropped in hfs_unmount.
942 error
= VFS_ROOT(vnode_mount(di_vp
), &bsfs_rootvp
, NULL
); /* XXX use context! */
944 (void)vnode_put(di_vp
);
945 file_drop(bsdata
->backingfd
);
948 vnode_ref(bsfs_rootvp
);
949 vnode_put(bsfs_rootvp
);
951 hfsmp
->hfs_backingfs_rootvp
= bsfs_rootvp
;
952 hfsmp
->hfs_flags
|= HFS_HAS_SPARSE_DEVICE
;
953 hfsmp
->hfs_sparsebandblks
= bsdata
->bandsize
/ HFSTOVCB(hfsmp
)->blockSize
;
954 hfsmp
->hfs_sparsebandblks
*= 4;
956 (void)vnode_put(di_vp
);
957 file_drop(bsdata
->backingfd
);
960 case HFS_CLRBACKINGSTOREINFO
: {
961 struct vnode
* tmpvp
;
963 vfsp
= vfs_statfs(HFSTOVFS(hfsmp
));
964 if (suser(cred
, NULL
) &&
965 kauth_cred_getuid(cred
) != vfsp
->f_owner
) {
966 return (EACCES
); /* must be owner of file system */
968 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) &&
969 hfsmp
->hfs_backingfs_rootvp
) {
971 hfsmp
->hfs_flags
&= ~HFS_HAS_SPARSE_DEVICE
;
972 tmpvp
= hfsmp
->hfs_backingfs_rootvp
;
973 hfsmp
->hfs_backingfs_rootvp
= NULLVP
;
974 hfsmp
->hfs_sparsebandblks
= 0;
979 #endif /* HFS_SPARSE_DEV */
988 mp
= vnode_mount(vp
);
989 hfsmp
= VFSTOHFS(mp
);
994 task
= current_task();
995 task_working_set_disable(task
);
997 // flush things before we get started to try and prevent
998 // dirty data from being paged out while we're frozen.
999 // note: can't do this after taking the lock as it will
1000 // deadlock against ourselves.
1001 vnode_iterate(mp
, 0, hfs_freezewrite_callback
, NULL
);
1002 hfs_global_exclusive_lock_acquire(hfsmp
);
1003 journal_flush(hfsmp
->jnl
);
1004 // don't need to iterate on all vnodes, we just need to
1005 // wait for writes to the system files and the device vnode
1006 // vnode_iterate(mp, 0, hfs_freezewrite_callback, NULL);
1007 if (HFSTOVCB(hfsmp
)->extentsRefNum
)
1008 vnode_waitforwrites(HFSTOVCB(hfsmp
)->extentsRefNum
, 0, 0, 0, "hfs freeze");
1009 if (HFSTOVCB(hfsmp
)->catalogRefNum
)
1010 vnode_waitforwrites(HFSTOVCB(hfsmp
)->catalogRefNum
, 0, 0, 0, "hfs freeze");
1011 if (HFSTOVCB(hfsmp
)->allocationsRefNum
)
1012 vnode_waitforwrites(HFSTOVCB(hfsmp
)->allocationsRefNum
, 0, 0, 0, "hfs freeze");
1013 if (hfsmp
->hfs_attribute_vp
)
1014 vnode_waitforwrites(hfsmp
->hfs_attribute_vp
, 0, 0, 0, "hfs freeze");
1015 vnode_waitforwrites(hfsmp
->hfs_devvp
, 0, 0, 0, "hfs freeze");
1017 hfsmp
->hfs_freezing_proc
= current_proc();
1026 // if we're not the one who froze the fs then we
1028 if (hfsmp
->hfs_freezing_proc
!= current_proc()) {
1032 // NOTE: if you add code here, also go check the
1033 // code that "thaws" the fs in hfs_vnop_close()
1035 hfsmp
->hfs_freezing_proc
= NULL
;
1036 hfs_global_exclusive_lock_release(hfsmp
);
1041 #define HFSIOC_BULKACCESS _IOW('h', 9, struct access_t)
1042 #define HFS_BULKACCESS_FSCTL IOCBASECMD(HFSIOC_BULKACCESS)
1044 case HFS_BULKACCESS_FSCTL
:
1045 case HFS_BULKACCESS
: {
1047 * NOTE: on entry, the vnode is locked. Incase this vnode
1048 * happens to be in our list of file_ids, we'll note it
1049 * avoid calling hfs_chashget_nowait() on that id as that
1050 * will cause a "locking against myself" panic.
1052 Boolean check_leaf
= true;
1054 struct user_access_t
*user_access_structp
;
1055 struct user_access_t tmp_user_access_t
;
1056 struct access_cache cache
;
1060 dev_t dev
= VTOC(vp
)->c_dev
;
1063 struct ucred myucred
; /* XXX ILLEGAL */
1065 int *file_ids
= NULL
;
1066 short *access
= NULL
;
1069 cnid_t prevParent_cnid
= 0;
1070 unsigned long myPerms
;
1072 struct cat_attr cnattr
;
1074 struct cnode
*skip_cp
= VTOC(vp
);
1075 struct vfs_context my_context
;
1077 /* first, return error if not run as root */
1078 if (cred
->cr_ruid
!= 0) {
1082 /* initialize the local cache and buffers */
1083 cache
.numcached
= 0;
1084 cache
.cachehits
= 0;
1087 file_ids
= (int *) get_pathbuff();
1088 access
= (short *) get_pathbuff();
1089 cache
.acache
= (int *) get_pathbuff();
1090 cache
.haveaccess
= (Boolean
*) get_pathbuff();
1092 if (file_ids
== NULL
|| access
== NULL
|| cache
.acache
== NULL
|| cache
.haveaccess
== NULL
) {
1093 release_pathbuff((char *) file_ids
);
1094 release_pathbuff((char *) access
);
1095 release_pathbuff((char *) cache
.acache
);
1096 release_pathbuff((char *) cache
.haveaccess
);
1101 /* struct copyin done during dispatch... need to copy file_id array separately */
1102 if (ap
->a_data
== NULL
) {
1104 goto err_exit_bulk_access
;
1108 user_access_structp
= (struct user_access_t
*)ap
->a_data
;
1111 struct access_t
* accessp
= (struct access_t
*)ap
->a_data
;
1112 tmp_user_access_t
.uid
= accessp
->uid
;
1113 tmp_user_access_t
.flags
= accessp
->flags
;
1114 tmp_user_access_t
.num_groups
= accessp
->num_groups
;
1115 tmp_user_access_t
.num_files
= accessp
->num_files
;
1116 tmp_user_access_t
.file_ids
= CAST_USER_ADDR_T(accessp
->file_ids
);
1117 tmp_user_access_t
.groups
= CAST_USER_ADDR_T(accessp
->groups
);
1118 tmp_user_access_t
.access
= CAST_USER_ADDR_T(accessp
->access
);
1119 user_access_structp
= &tmp_user_access_t
;
1122 num_files
= user_access_structp
->num_files
;
1123 if (num_files
< 1) {
1124 goto err_exit_bulk_access
;
1126 if (num_files
> 256) {
1128 goto err_exit_bulk_access
;
1131 if ((error
= copyin(user_access_structp
->file_ids
, (caddr_t
)file_ids
,
1132 num_files
* sizeof(int)))) {
1133 goto err_exit_bulk_access
;
1136 /* fill in the ucred structure */
1137 flags
= user_access_structp
->flags
;
1138 if ((flags
& (F_OK
| R_OK
| W_OK
| X_OK
)) == 0) {
1142 /* check if we've been passed leaf node ids or parent ids */
1143 if (flags
& PARENT_IDS_FLAG
) {
1147 memset(&myucred
, 0, sizeof(myucred
));
1149 myucred
.cr_uid
= myucred
.cr_ruid
= myucred
.cr_svuid
= user_access_structp
->uid
;
1150 myucred
.cr_ngroups
= user_access_structp
->num_groups
;
1151 if (myucred
.cr_ngroups
< 1 || myucred
.cr_ngroups
> 16) {
1152 myucred
.cr_ngroups
= 0;
1153 } else if ((error
= copyin(user_access_structp
->groups
, (caddr_t
)myucred
.cr_groups
,
1154 myucred
.cr_ngroups
* sizeof(gid_t
)))) {
1155 goto err_exit_bulk_access
;
1157 myucred
.cr_rgid
= myucred
.cr_svgid
= myucred
.cr_groups
[0];
1159 my_context
.vc_proc
= p
;
1160 my_context
.vc_ucred
= &myucred
;
1162 /* Check access to each file_id passed in */
1163 for (i
= 0; i
< num_files
; i
++) {
1165 cnid
= (cnid_t
) file_ids
[i
];
1167 /* root always has access */
1168 if (!suser(&myucred
, NULL
)) {
1175 /* do the lookup (checks the cnode hash, then the catalog) */
1176 error
= do_attr_lookup(hfsmp
, &cache
, dev
, cnid
, skip_cp
, &catkey
, &cnattr
, p
);
1178 access
[i
] = (short) error
;
1182 /* before calling CheckAccess(), check the target file for read access */
1183 myPerms
= DerivePermissionSummary(cnattr
.ca_uid
, cnattr
.ca_gid
,
1184 cnattr
.ca_mode
, hfsmp
->hfs_mp
, &myucred
, p
);
1187 /* fail fast if no access */
1188 if ((myPerms
& flags
) == 0) {
1193 /* we were passed an array of parent ids */
1194 catkey
.hfsPlus
.parentID
= cnid
;
1197 /* if the last guy had the same parent and had access, we're done */
1198 if (i
> 0 && catkey
.hfsPlus
.parentID
== prevParent_cnid
&& access
[i
-1] == 0) {
1204 myaccess
= do_access_check(hfsmp
, &error
, &cache
, catkey
.hfsPlus
.parentID
,
1205 skip_cp
, p
, &myucred
, dev
);
1208 access
[i
] = 0; // have access.. no errors to report
1210 access
[i
] = (error
!= 0 ? (short) error
: EACCES
);
1213 prevParent_cnid
= catkey
.hfsPlus
.parentID
;
1217 cnid
= (cnid_t
)file_ids
[i
];
1219 while (cnid
>= kRootDirID
) {
1220 /* get the vnode for this cnid */
1221 myErr
= hfs_vget(hfsmp
, cnid
, &vp
, 0);
1227 cnid
= VTOC(vp
)->c_parentcnid
;
1229 hfs_unlock(VTOC(vp
));
1230 if (vnode_vtype(vp
) == VDIR
) {
1231 myErr
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, &my_context
);
1233 // try again with just read-access
1234 myErr
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, &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
: {
1272 if (ap
->a_data
== NULL
) {
1275 state
= *(int *)ap
->a_data
;
1276 if (state
== 0 || state
== 1)
1277 return hfs_setextendedsecurity(hfsmp
, state
);
1285 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1287 error
= hfs_fsync(vp
, MNT_NOWAIT
, TRUE
, p
);
1288 hfs_unlock(VTOC(vp
));
1295 register struct cnode
*cp
;
1298 if (!vnode_isreg(vp
))
1301 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1305 * used by regression test to determine if
1306 * all the dirty pages (via write) have been cleaned
1307 * after a call to 'fsysnc'.
1309 error
= is_file_clean(vp
, VTOF(vp
)->ff_size
);
1316 register struct radvisory
*ra
;
1317 struct filefork
*fp
;
1320 if (!vnode_isreg(vp
))
1323 ra
= (struct radvisory
*)(ap
->a_data
);
1326 /* Protect against a size change. */
1327 hfs_lock_truncate(VTOC(vp
), TRUE
);
1329 if (ra
->ra_offset
>= fp
->ff_size
) {
1332 error
= advisory_read(vp
, fp
->ff_size
, ra
->ra_offset
, ra
->ra_count
);
1335 hfs_unlock_truncate(VTOC(vp
));
1339 case F_READBOOTSTRAP
:
1340 case F_WRITEBOOTSTRAP
:
1342 struct vnode
*devvp
= NULL
;
1343 user_fbootstraptransfer_t
*user_bootstrapp
;
1347 daddr64_t blockNumber
;
1351 user_fbootstraptransfer_t user_bootstrap
;
1353 if (!vnode_isvroot(vp
))
1355 /* LP64 - when caller is a 64 bit process then we are passed a pointer
1356 * to a user_fbootstraptransfer_t else we get a pointer to a
1357 * fbootstraptransfer_t which we munge into a user_fbootstraptransfer_t
1360 user_bootstrapp
= (user_fbootstraptransfer_t
*)ap
->a_data
;
1363 fbootstraptransfer_t
*bootstrapp
= (fbootstraptransfer_t
*)ap
->a_data
;
1364 user_bootstrapp
= &user_bootstrap
;
1365 user_bootstrap
.fbt_offset
= bootstrapp
->fbt_offset
;
1366 user_bootstrap
.fbt_length
= bootstrapp
->fbt_length
;
1367 user_bootstrap
.fbt_buffer
= CAST_USER_ADDR_T(bootstrapp
->fbt_buffer
);
1369 if (user_bootstrapp
->fbt_offset
+ user_bootstrapp
->fbt_length
> 1024)
1372 devvp
= VTOHFS(vp
)->hfs_devvp
;
1373 auio
= uio_create(1, user_bootstrapp
->fbt_offset
,
1374 is64bit
? UIO_USERSPACE64
: UIO_USERSPACE32
,
1375 (ap
->a_command
== F_WRITEBOOTSTRAP
) ? UIO_WRITE
: UIO_READ
);
1376 uio_addiov(auio
, user_bootstrapp
->fbt_buffer
, user_bootstrapp
->fbt_length
);
1378 devBlockSize
= vfs_devblocksize(vnode_mount(vp
));
1380 while (uio_resid(auio
) > 0) {
1381 blockNumber
= uio_offset(auio
) / devBlockSize
;
1382 error
= (int)buf_bread(devvp
, blockNumber
, devBlockSize
, cred
, &bp
);
1384 if (bp
) buf_brelse(bp
);
1389 blockOffset
= uio_offset(auio
) % devBlockSize
;
1390 xfersize
= devBlockSize
- blockOffset
;
1391 error
= uiomove((caddr_t
)buf_dataptr(bp
) + blockOffset
, (int)xfersize
, auio
);
1397 if (uio_rw(auio
) == UIO_WRITE
) {
1398 error
= VNOP_BWRITE(bp
);
1411 case _IOC(IOC_OUT
,'h', 4, 0): /* Create date in local time */
1414 *(user_time_t
*)(ap
->a_data
) = (user_time_t
) (to_bsd_time(VTOVCB(vp
)->localCreateDate
));
1417 *(time_t *)(ap
->a_data
) = to_bsd_time(VTOVCB(vp
)->localCreateDate
);
1422 case HFS_GET_MOUNT_TIME
:
1423 return copyout(&hfsmp
->hfs_mount_time
, CAST_USER_ADDR_T(ap
->a_data
), sizeof(hfsmp
->hfs_mount_time
));
1426 case HFS_GET_LAST_MTIME
:
1427 return copyout(&hfsmp
->hfs_last_mounted_mtime
, CAST_USER_ADDR_T(ap
->a_data
), sizeof(hfsmp
->hfs_last_mounted_mtime
));
1430 case HFS_SET_BOOT_INFO
:
1431 if (!vnode_isvroot(vp
))
1433 if (!kauth_cred_issuser(cred
) && (kauth_cred_getuid(cred
) != vfs_statfs(HFSTOVFS(hfsmp
))->f_owner
))
1434 return(EACCES
); /* must be superuser or owner of filesystem */
1435 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1436 bcopy(ap
->a_data
, &hfsmp
->vcbFndrInfo
, sizeof(hfsmp
->vcbFndrInfo
));
1437 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1438 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
1441 case HFS_GET_BOOT_INFO
:
1442 if (!vnode_isvroot(vp
))
1444 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1445 bcopy(&hfsmp
->vcbFndrInfo
, ap
->a_data
, sizeof(hfsmp
->vcbFndrInfo
));
1446 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1453 /* Should never get here */
1461 hfs_vnop_select(__unused
struct vnop_select_args
*ap
)
1463 struct vnop_select_args {
1468 vfs_context_t a_context;
1473 * We should really check to see if I/O is possible.
1479 * Converts a logical block number to a physical block, and optionally returns
1480 * the amount of remaining blocks in a run. The logical block is based on hfsNode.logBlockSize.
1481 * The physical block number is based on the device block size, currently its 512.
1482 * The block run is returned in logical blocks, and is the REMAINING amount of blocks
1485 hfs_bmap(struct vnode
*vp
, daddr_t bn
, struct vnode
**vpp
, daddr64_t
*bnp
, int *runp
)
1487 struct cnode
*cp
= VTOC(vp
);
1488 struct filefork
*fp
= VTOF(vp
);
1489 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1490 int retval
= E_NONE
;
1491 daddr_t logBlockSize
;
1492 size_t bytesContAvail
= 0;
1493 off_t blockposition
;
1498 * Check for underlying vnode requests and ensure that logical
1499 * to physical mapping is requested.
1506 logBlockSize
= GetLogicalBlockSize(vp
);
1507 blockposition
= (off_t
)bn
* (off_t
)logBlockSize
;
1509 lockExtBtree
= overflow_extents(fp
);
1512 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_SHARED_LOCK
);
1514 retval
= MacToVFSError(
1515 MapFileBlockC (HFSTOVCB(hfsmp
),
1523 hfs_systemfile_unlock(hfsmp
, lockflags
);
1525 if (retval
== E_NONE
) {
1526 /* Figure out how many read ahead blocks there are */
1528 if (can_cluster(logBlockSize
)) {
1529 /* Make sure this result never goes negative: */
1530 *runp
= (bytesContAvail
< logBlockSize
) ? 0 : (bytesContAvail
/ logBlockSize
) - 1;
1540 * Convert logical block number to file offset.
1543 hfs_vnop_blktooff(struct vnop_blktooff_args
*ap
)
1545 struct vnop_blktooff_args {
1552 if (ap
->a_vp
== NULL
)
1554 *ap
->a_offset
= (off_t
)ap
->a_lblkno
* (off_t
)GetLogicalBlockSize(ap
->a_vp
);
1560 * Convert file offset to logical block number.
1563 hfs_vnop_offtoblk(struct vnop_offtoblk_args
*ap
)
1565 struct vnop_offtoblk_args {
1568 daddr64_t *a_lblkno;
1572 if (ap
->a_vp
== NULL
)
1574 *ap
->a_lblkno
= (daddr64_t
)(ap
->a_offset
/ (off_t
)GetLogicalBlockSize(ap
->a_vp
));
1580 * Map file offset to physical block number.
1582 * System file cnodes are expected to be locked (shared or exclusive).
1585 hfs_vnop_blockmap(struct vnop_blockmap_args
*ap
)
1587 struct vnop_blockmap_args {
1595 vfs_context_t a_context;
1599 struct vnode
*vp
= ap
->a_vp
;
1601 struct filefork
*fp
;
1602 struct hfsmount
*hfsmp
;
1603 size_t bytesContAvail
= 0;
1604 int retval
= E_NONE
;
1607 struct rl_entry
*invalid_range
;
1608 enum rl_overlaptype overlaptype
;
1613 * Check for underlying vnode requests and ensure that logical
1614 * to physical mapping is requested.
1616 if (ap
->a_bpn
== NULL
)
1619 if ( !vnode_issystem(vp
) && !vnode_islnk(vp
)) {
1620 if (VTOC(vp
)->c_lockowner
!= current_thread()) {
1621 hfs_lock(VTOC(vp
), HFS_FORCE_LOCK
);
1625 panic("blockmap: %s cnode lock already held!\n",
1626 cp
->c_desc
.cd_nameptr
? cp
->c_desc
.cd_nameptr
: "");
1634 if (fp
->ff_unallocblocks
) {
1635 if (hfs_start_transaction(hfsmp
) != 0) {
1641 syslocks
= SFL_EXTENTS
| SFL_BITMAP
;
1643 } else if (overflow_extents(fp
)) {
1644 syslocks
= SFL_EXTENTS
;
1648 lockflags
= hfs_systemfile_lock(hfsmp
, syslocks
, HFS_EXCLUSIVE_LOCK
);
1651 * Check for any delayed allocations.
1653 if (fp
->ff_unallocblocks
) {
1655 u_int32_t loanedBlocks
;
1658 // Make sure we have a transaction. It's possible
1659 // that we came in and fp->ff_unallocblocks was zero
1660 // but during the time we blocked acquiring the extents
1661 // btree, ff_unallocblocks became non-zero and so we
1662 // will need to start a transaction.
1664 if (started_tr
== 0) {
1666 hfs_systemfile_unlock(hfsmp
, lockflags
);
1673 * Note: ExtendFileC will Release any blocks on loan and
1674 * aquire real blocks. So we ask to extend by zero bytes
1675 * since ExtendFileC will account for the virtual blocks.
1678 loanedBlocks
= fp
->ff_unallocblocks
;
1679 retval
= ExtendFileC(hfsmp
, (FCB
*)fp
, 0, 0,
1680 kEFAllMask
| kEFNoClumpMask
, &actbytes
);
1683 fp
->ff_unallocblocks
= loanedBlocks
;
1684 cp
->c_blocks
+= loanedBlocks
;
1685 fp
->ff_blocks
+= loanedBlocks
;
1687 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1688 hfsmp
->loanedBlocks
+= loanedBlocks
;
1689 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1693 hfs_systemfile_unlock(hfsmp
, lockflags
);
1694 cp
->c_flag
|= C_MODIFIED
;
1696 (void) hfs_update(vp
, TRUE
);
1697 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1699 hfs_end_transaction(hfsmp
);
1705 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, ap
->a_size
, ap
->a_foffset
,
1706 ap
->a_bpn
, &bytesContAvail
);
1708 hfs_systemfile_unlock(hfsmp
, lockflags
);
1713 (void) hfs_update(vp
, TRUE
);
1714 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1715 hfs_end_transaction(hfsmp
);
1722 /* Adjust the mapping information for invalid file ranges: */
1723 overlaptype
= rl_scan(&fp
->ff_invalidranges
, ap
->a_foffset
,
1724 ap
->a_foffset
+ (off_t
)bytesContAvail
- 1,
1726 if (overlaptype
!= RL_NOOVERLAP
) {
1727 switch(overlaptype
) {
1728 case RL_MATCHINGOVERLAP
:
1729 case RL_OVERLAPCONTAINSRANGE
:
1730 case RL_OVERLAPSTARTSBEFORE
:
1731 /* There's no valid block for this byte offset: */
1732 *ap
->a_bpn
= (daddr64_t
)-1;
1733 /* There's no point limiting the amount to be returned
1734 * if the invalid range that was hit extends all the way
1735 * to the EOF (i.e. there's no valid bytes between the
1736 * end of this range and the file's EOF):
1738 if (((off_t
)fp
->ff_size
> (invalid_range
->rl_end
+ 1)) &&
1739 (invalid_range
->rl_end
+ 1 - ap
->a_foffset
< bytesContAvail
)) {
1740 bytesContAvail
= invalid_range
->rl_end
+ 1 - ap
->a_foffset
;
1744 case RL_OVERLAPISCONTAINED
:
1745 case RL_OVERLAPENDSAFTER
:
1746 /* The range of interest hits an invalid block before the end: */
1747 if (invalid_range
->rl_start
== ap
->a_foffset
) {
1748 /* There's actually no valid information to be had starting here: */
1749 *ap
->a_bpn
= (daddr64_t
)-1;
1750 if (((off_t
)fp
->ff_size
> (invalid_range
->rl_end
+ 1)) &&
1751 (invalid_range
->rl_end
+ 1 - ap
->a_foffset
< bytesContAvail
)) {
1752 bytesContAvail
= invalid_range
->rl_end
+ 1 - ap
->a_foffset
;
1755 bytesContAvail
= invalid_range
->rl_start
- ap
->a_foffset
;
1762 if (bytesContAvail
> ap
->a_size
)
1763 bytesContAvail
= ap
->a_size
;
1766 *ap
->a_run
= bytesContAvail
;
1769 *(int *)ap
->a_poff
= 0;
1774 return (MacToVFSError(retval
));
1779 * prepare and issue the I/O
1780 * buf_strategy knows how to deal
1781 * with requests that require
1785 hfs_vnop_strategy(struct vnop_strategy_args
*ap
)
1787 buf_t bp
= ap
->a_bp
;
1788 vnode_t vp
= buf_vnode(bp
);
1789 struct cnode
*cp
= VTOC(vp
);
1791 return (buf_strategy(cp
->c_devvp
, ap
));
1796 do_hfs_truncate(struct vnode
*vp
, off_t length
, int flags
, int skipsetsize
, vfs_context_t context
)
1798 register struct cnode
*cp
= VTOC(vp
);
1799 struct filefork
*fp
= VTOF(vp
);
1800 struct proc
*p
= vfs_context_proc(context
);;
1801 kauth_cred_t cred
= vfs_context_ucred(context
);
1804 off_t actualBytesAdded
;
1808 struct hfsmount
*hfsmp
;
1811 blksize
= VTOVCB(vp
)->blockSize
;
1812 fileblocks
= fp
->ff_blocks
;
1813 filebytes
= (off_t
)fileblocks
* (off_t
)blksize
;
1815 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_START
,
1816 (int)length
, (int)fp
->ff_size
, (int)filebytes
, 0, 0);
1821 if ((!ISHFSPLUS(VTOVCB(vp
))) && (length
> (off_t
)MAXHFSFILESIZE
))
1828 /* Files that are changing size are not hot file candidates. */
1829 if (hfsmp
->hfc_stage
== HFC_RECORDING
) {
1830 fp
->ff_bytesread
= 0;
1834 * We cannot just check if fp->ff_size == length (as an optimization)
1835 * since there may be extra physical blocks that also need truncation.
1838 if ((retval
= hfs_getinoquota(cp
)))
1843 * Lengthen the size of the file. We must ensure that the
1844 * last byte of the file is allocated. Since the smallest
1845 * value of ff_size is 0, length will be at least 1.
1847 if (length
> (off_t
)fp
->ff_size
) {
1849 retval
= hfs_chkdq(cp
, (int64_t)(roundup(length
- filebytes
, blksize
)),
1855 * If we don't have enough physical space then
1856 * we need to extend the physical size.
1858 if (length
> filebytes
) {
1860 u_long blockHint
= 0;
1862 /* All or nothing and don't round up to clumpsize. */
1863 eflags
= kEFAllMask
| kEFNoClumpMask
;
1865 if (cred
&& suser(cred
, NULL
) != 0)
1866 eflags
|= kEFReserveMask
; /* keep a reserve */
1869 * Allocate Journal and Quota files in metadata zone.
1871 if (filebytes
== 0 &&
1872 hfsmp
->hfs_flags
& HFS_METADATA_ZONE
&&
1873 hfs_virtualmetafile(cp
)) {
1874 eflags
|= kEFMetadataMask
;
1875 blockHint
= hfsmp
->hfs_metazone_start
;
1877 if (hfs_start_transaction(hfsmp
) != 0) {
1882 /* Protect extents b-tree and allocation bitmap */
1883 lockflags
= SFL_BITMAP
;
1884 if (overflow_extents(fp
))
1885 lockflags
|= SFL_EXTENTS
;
1886 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
1888 while ((length
> filebytes
) && (retval
== E_NONE
)) {
1889 bytesToAdd
= length
- filebytes
;
1890 retval
= MacToVFSError(ExtendFileC(VTOVCB(vp
),
1895 &actualBytesAdded
));
1897 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)blksize
;
1898 if (actualBytesAdded
== 0 && retval
== E_NONE
) {
1899 if (length
> filebytes
)
1905 hfs_systemfile_unlock(hfsmp
, lockflags
);
1908 (void) hfs_update(vp
, TRUE
);
1909 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1912 hfs_end_transaction(hfsmp
);
1917 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_NONE
,
1918 (int)length
, (int)fp
->ff_size
, (int)filebytes
, 0, 0);
1921 if (!(flags
& IO_NOZEROFILL
)) {
1922 if (UBCINFOEXISTS(vp
) && retval
== E_NONE
) {
1923 struct rl_entry
*invalid_range
;
1926 zero_limit
= (fp
->ff_size
+ (PAGE_SIZE_64
- 1)) & ~PAGE_MASK_64
;
1927 if (length
< zero_limit
) zero_limit
= length
;
1929 if (length
> (off_t
)fp
->ff_size
) {
1932 /* Extending the file: time to fill out the current last page w. zeroes? */
1933 if ((fp
->ff_size
& PAGE_MASK_64
) &&
1934 (rl_scan(&fp
->ff_invalidranges
, fp
->ff_size
& ~PAGE_MASK_64
,
1935 fp
->ff_size
- 1, &invalid_range
) == RL_NOOVERLAP
)) {
1937 /* There's some valid data at the start of the (current) last page
1938 of the file, so zero out the remainder of that page to ensure the
1939 entire page contains valid data. Since there is no invalid range
1940 possible past the (current) eof, there's no need to remove anything
1941 from the invalid range list before calling cluster_write(): */
1943 retval
= cluster_write(vp
, (struct uio
*) 0, fp
->ff_size
, zero_limit
,
1944 fp
->ff_size
, (off_t
)0,
1945 (flags
& IO_SYNC
) | IO_HEADZEROFILL
| IO_NOZERODIRTY
);
1946 hfs_lock(cp
, HFS_FORCE_LOCK
);
1947 if (retval
) goto Err_Exit
;
1949 /* Merely invalidate the remaining area, if necessary: */
1950 if (length
> zero_limit
) {
1952 rl_add(zero_limit
, length
- 1, &fp
->ff_invalidranges
);
1953 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
1956 /* The page containing the (current) eof is invalid: just add the
1957 remainder of the page to the invalid list, along with the area
1958 being newly allocated:
1961 rl_add(fp
->ff_size
, length
- 1, &fp
->ff_invalidranges
);
1962 cp
->c_zftimeout
= tv
.tv_sec
+ ZFTIMELIMIT
;
1966 panic("hfs_truncate: invoked on non-UBC object?!");
1969 cp
->c_touch_modtime
= TRUE
;
1970 fp
->ff_size
= length
;
1972 /* Nested transactions will do their own ubc_setsize. */
1975 * ubc_setsize can cause a pagein here
1976 * so we need to drop cnode lock.
1979 ubc_setsize(vp
, length
);
1980 hfs_lock(cp
, HFS_FORCE_LOCK
);
1983 } else { /* Shorten the size of the file */
1985 if ((off_t
)fp
->ff_size
> length
) {
1987 * Any buffers that are past the truncation point need to be
1988 * invalidated (to maintain buffer cache consistency).
1991 /* Nested transactions will do their own ubc_setsize. */
1994 * ubc_setsize can cause a pageout here
1995 * so we need to drop cnode lock.
1998 ubc_setsize(vp
, length
);
1999 hfs_lock(cp
, HFS_FORCE_LOCK
);
2002 /* Any space previously marked as invalid is now irrelevant: */
2003 rl_remove(length
, fp
->ff_size
- 1, &fp
->ff_invalidranges
);
2007 * Account for any unmapped blocks. Note that the new
2008 * file length can still end up with unmapped blocks.
2010 if (fp
->ff_unallocblocks
> 0) {
2011 u_int32_t finalblks
;
2012 u_int32_t loanedBlocks
;
2014 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
2016 loanedBlocks
= fp
->ff_unallocblocks
;
2017 cp
->c_blocks
-= loanedBlocks
;
2018 fp
->ff_blocks
-= loanedBlocks
;
2019 fp
->ff_unallocblocks
= 0;
2021 hfsmp
->loanedBlocks
-= loanedBlocks
;
2023 finalblks
= (length
+ blksize
- 1) / blksize
;
2024 if (finalblks
> fp
->ff_blocks
) {
2025 /* calculate required unmapped blocks */
2026 loanedBlocks
= finalblks
- fp
->ff_blocks
;
2027 hfsmp
->loanedBlocks
+= loanedBlocks
;
2029 fp
->ff_unallocblocks
= loanedBlocks
;
2030 cp
->c_blocks
+= loanedBlocks
;
2031 fp
->ff_blocks
+= loanedBlocks
;
2033 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2037 * For a TBE process the deallocation of the file blocks is
2038 * delayed until the file is closed. And hfs_close calls
2039 * truncate with the IO_NDELAY flag set. So when IO_NDELAY
2040 * isn't set, we make sure this isn't a TBE process.
2042 if ((flags
& IO_NDELAY
) || (proc_tbe(p
) == 0)) {
2044 off_t savedbytes
= ((off_t
)fp
->ff_blocks
* (off_t
)blksize
);
2046 if (hfs_start_transaction(hfsmp
) != 0) {
2051 if (fp
->ff_unallocblocks
== 0) {
2052 /* Protect extents b-tree and allocation bitmap */
2053 lockflags
= SFL_BITMAP
;
2054 if (overflow_extents(fp
))
2055 lockflags
|= SFL_EXTENTS
;
2056 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2058 retval
= MacToVFSError(TruncateFileC(VTOVCB(vp
),
2059 (FCB
*)fp
, length
, false));
2061 hfs_systemfile_unlock(hfsmp
, lockflags
);
2064 (void) hfs_update(vp
, TRUE
);
2065 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2068 hfs_end_transaction(hfsmp
);
2070 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)blksize
;
2074 /* These are bytesreleased */
2075 (void) hfs_chkdq(cp
, (int64_t)-(savedbytes
- filebytes
), NOCRED
, 0);
2078 /* Only set update flag if the logical length changes */
2079 if ((off_t
)fp
->ff_size
!= length
)
2080 cp
->c_touch_modtime
= TRUE
;
2081 fp
->ff_size
= length
;
2083 cp
->c_touch_chgtime
= TRUE
;
2084 retval
= hfs_update(vp
, MNT_WAIT
);
2086 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_NONE
,
2087 -1, -1, -1, retval
, 0);
2092 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW
, 7)) | DBG_FUNC_END
,
2093 (int)length
, (int)fp
->ff_size
, (int)filebytes
, retval
, 0);
2101 * Truncate a cnode to at most length size, freeing (or adding) the
2106 hfs_truncate(struct vnode
*vp
, off_t length
, int flags
, int skipsetsize
,
2107 vfs_context_t context
)
2109 struct filefork
*fp
= VTOF(vp
);
2112 int blksize
, error
= 0;
2114 if (vnode_isdir(vp
))
2115 return (EISDIR
); /* cannot truncate an HFS directory! */
2117 blksize
= VTOVCB(vp
)->blockSize
;
2118 fileblocks
= fp
->ff_blocks
;
2119 filebytes
= (off_t
)fileblocks
* (off_t
)blksize
;
2121 // have to loop truncating or growing files that are
2122 // really big because otherwise transactions can get
2123 // enormous and consume too many kernel resources.
2125 if (length
< filebytes
) {
2126 while (filebytes
> length
) {
2127 if ((filebytes
- length
) > HFS_BIGFILE_SIZE
) {
2128 filebytes
-= HFS_BIGFILE_SIZE
;
2132 error
= do_hfs_truncate(vp
, filebytes
, flags
, skipsetsize
, context
);
2136 } else if (length
> filebytes
) {
2137 while (filebytes
< length
) {
2138 if ((length
- filebytes
) > HFS_BIGFILE_SIZE
) {
2139 filebytes
+= HFS_BIGFILE_SIZE
;
2143 error
= do_hfs_truncate(vp
, filebytes
, flags
, skipsetsize
, context
);
2147 } else /* Same logical size */ {
2149 error
= do_hfs_truncate(vp
, length
, flags
, skipsetsize
, context
);
2151 /* Files that are changing size are not hot file candidates. */
2152 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) {
2153 fp
->ff_bytesread
= 0;
2162 * Preallocate file storage space.
2165 hfs_vnop_allocate(struct vnop_allocate_args
/* {
2169 off_t *a_bytesallocated;
2171 vfs_context_t a_context;
2174 struct vnode
*vp
= ap
->a_vp
;
2176 struct filefork
*fp
;
2178 off_t length
= ap
->a_length
;
2180 off_t moreBytesRequested
;
2181 off_t actualBytesAdded
;
2184 int retval
, retval2
;
2186 UInt32 extendFlags
; /* For call to ExtendFileC */
2187 struct hfsmount
*hfsmp
;
2188 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
2191 *(ap
->a_bytesallocated
) = 0;
2193 if (!vnode_isreg(vp
))
2195 if (length
< (off_t
)0)
2198 if ((retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2205 fileblocks
= fp
->ff_blocks
;
2206 filebytes
= (off_t
)fileblocks
* (off_t
)vcb
->blockSize
;
2208 if ((ap
->a_flags
& ALLOCATEFROMVOL
) && (length
< filebytes
)) {
2213 /* Fill in the flags word for the call to Extend the file */
2215 extendFlags
= kEFNoClumpMask
;
2216 if (ap
->a_flags
& ALLOCATECONTIG
)
2217 extendFlags
|= kEFContigMask
;
2218 if (ap
->a_flags
& ALLOCATEALL
)
2219 extendFlags
|= kEFAllMask
;
2220 if (cred
&& suser(cred
, NULL
) != 0)
2221 extendFlags
|= kEFReserveMask
;
2225 startingPEOF
= filebytes
;
2227 if (ap
->a_flags
& ALLOCATEFROMPEOF
)
2228 length
+= filebytes
;
2229 else if (ap
->a_flags
& ALLOCATEFROMVOL
)
2230 blockHint
= ap
->a_offset
/ VTOVCB(vp
)->blockSize
;
2232 /* If no changes are necesary, then we're done */
2233 if (filebytes
== length
)
2237 * Lengthen the size of the file. We must ensure that the
2238 * last byte of the file is allocated. Since the smallest
2239 * value of filebytes is 0, length will be at least 1.
2241 if (length
> filebytes
) {
2242 moreBytesRequested
= length
- filebytes
;
2245 retval
= hfs_chkdq(cp
,
2246 (int64_t)(roundup(moreBytesRequested
, vcb
->blockSize
)),
2253 * Metadata zone checks.
2255 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) {
2257 * Allocate Journal and Quota files in metadata zone.
2259 if (hfs_virtualmetafile(cp
)) {
2260 extendFlags
|= kEFMetadataMask
;
2261 blockHint
= hfsmp
->hfs_metazone_start
;
2262 } else if ((blockHint
>= hfsmp
->hfs_metazone_start
) &&
2263 (blockHint
<= hfsmp
->hfs_metazone_end
)) {
2265 * Move blockHint outside metadata zone.
2267 blockHint
= hfsmp
->hfs_metazone_end
+ 1;
2271 if (hfs_start_transaction(hfsmp
) != 0) {
2276 /* Protect extents b-tree and allocation bitmap */
2277 lockflags
= SFL_BITMAP
;
2278 if (overflow_extents(fp
))
2279 lockflags
|= SFL_EXTENTS
;
2280 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2282 retval
= MacToVFSError(ExtendFileC(vcb
,
2287 &actualBytesAdded
));
2289 *(ap
->a_bytesallocated
) = actualBytesAdded
;
2290 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)vcb
->blockSize
;
2292 hfs_systemfile_unlock(hfsmp
, lockflags
);
2295 (void) hfs_update(vp
, TRUE
);
2296 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2299 hfs_end_transaction(hfsmp
);
2302 * if we get an error and no changes were made then exit
2303 * otherwise we must do the hfs_update to reflect the changes
2305 if (retval
&& (startingPEOF
== filebytes
))
2309 * Adjust actualBytesAdded to be allocation block aligned, not
2310 * clump size aligned.
2311 * NOTE: So what we are reporting does not affect reality
2312 * until the file is closed, when we truncate the file to allocation
2315 if ((actualBytesAdded
!= 0) && (moreBytesRequested
< actualBytesAdded
))
2316 *(ap
->a_bytesallocated
) =
2317 roundup(moreBytesRequested
, (off_t
)vcb
->blockSize
);
2319 } else { /* Shorten the size of the file */
2321 if (fp
->ff_size
> length
) {
2323 * Any buffers that are past the truncation point need to be
2324 * invalidated (to maintain buffer cache consistency).
2328 if (hfs_start_transaction(hfsmp
) != 0) {
2333 /* Protect extents b-tree and allocation bitmap */
2334 lockflags
= SFL_BITMAP
;
2335 if (overflow_extents(fp
))
2336 lockflags
|= SFL_EXTENTS
;
2337 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2339 retval
= MacToVFSError(TruncateFileC(vcb
, (FCB
*)fp
, length
, false));
2341 hfs_systemfile_unlock(hfsmp
, lockflags
);
2343 filebytes
= (off_t
)fp
->ff_blocks
* (off_t
)vcb
->blockSize
;
2346 (void) hfs_update(vp
, TRUE
);
2347 (void) hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
2350 hfs_end_transaction(hfsmp
);
2354 * if we get an error and no changes were made then exit
2355 * otherwise we must do the hfs_update to reflect the changes
2357 if (retval
&& (startingPEOF
== filebytes
)) goto Err_Exit
;
2359 /* These are bytesreleased */
2360 (void) hfs_chkdq(cp
, (int64_t)-((startingPEOF
- filebytes
)), NOCRED
,0);
2363 if (fp
->ff_size
> filebytes
) {
2364 fp
->ff_size
= filebytes
;
2367 ubc_setsize(vp
, fp
->ff_size
);
2368 hfs_lock(cp
, HFS_FORCE_LOCK
);
2373 cp
->c_touch_chgtime
= TRUE
;
2374 cp
->c_touch_modtime
= TRUE
;
2375 retval2
= hfs_update(vp
, MNT_WAIT
);
2386 * Pagein for HFS filesystem
2389 hfs_vnop_pagein(struct vnop_pagein_args
*ap
)
2391 struct vnop_pagein_args {
2394 vm_offset_t a_pl_offset,
2398 vfs_context_t a_context;
2402 vnode_t vp
= ap
->a_vp
;
2405 error
= cluster_pagein(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
2406 ap
->a_size
, (off_t
)VTOF(vp
)->ff_size
, ap
->a_flags
);
2408 * Keep track of blocks read.
2410 if (VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
&& error
== 0) {
2412 struct filefork
*fp
;
2414 int took_cnode_lock
= 0;
2419 if (ap
->a_f_offset
== 0 && fp
->ff_size
< PAGE_SIZE
)
2420 bytesread
= fp
->ff_size
;
2422 bytesread
= ap
->a_size
;
2424 /* When ff_bytesread exceeds 32-bits, update it behind the cnode lock. */
2425 if ((fp
->ff_bytesread
+ bytesread
) > 0x00000000ffffffff) {
2426 hfs_lock(cp
, HFS_FORCE_LOCK
);
2427 took_cnode_lock
= 1;
2430 * If this file hasn't been seen since the start of
2431 * the current sampling period then start over.
2433 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
) {
2436 fp
->ff_bytesread
= bytesread
;
2438 cp
->c_atime
= tv
.tv_sec
;
2440 fp
->ff_bytesread
+= bytesread
;
2442 cp
->c_touch_acctime
= TRUE
;
2443 if (took_cnode_lock
)
2450 * Pageout for HFS filesystem.
2453 hfs_vnop_pageout(struct vnop_pageout_args
*ap
)
2455 struct vnop_pageout_args {
2458 vm_offset_t a_pl_offset,
2462 vfs_context_t a_context;
2466 vnode_t vp
= ap
->a_vp
;
2468 struct filefork
*fp
;
2474 if (cp
->c_lockowner
== current_thread()) {
2475 panic("pageout: %s cnode lock already held!\n",
2476 cp
->c_desc
.cd_nameptr
? cp
->c_desc
.cd_nameptr
: "");
2478 if ( (retval
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
2483 filesize
= fp
->ff_size
;
2484 end_of_range
= ap
->a_f_offset
+ ap
->a_size
- 1;
2486 if (end_of_range
>= filesize
) {
2487 end_of_range
= (off_t
)(filesize
- 1);
2489 if (ap
->a_f_offset
< filesize
) {
2490 rl_remove(ap
->a_f_offset
, end_of_range
, &fp
->ff_invalidranges
);
2491 cp
->c_flag
|= C_MODIFIED
; /* leof is dirty */
2495 retval
= cluster_pageout(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
2496 ap
->a_size
, filesize
, ap
->a_flags
);
2499 * If data was written, and setuid or setgid bits are set and
2500 * this process is not the superuser then clear the setuid and
2501 * setgid bits as a precaution against tampering.
2503 if ((retval
== 0) &&
2504 (cp
->c_mode
& (S_ISUID
| S_ISGID
)) &&
2505 (vfs_context_suser(ap
->a_context
) != 0)) {
2506 hfs_lock(cp
, HFS_FORCE_LOCK
);
2507 cp
->c_mode
&= ~(S_ISUID
| S_ISGID
);
2508 cp
->c_touch_chgtime
= TRUE
;
2515 * Intercept B-Tree node writes to unswap them if necessary.
2518 hfs_vnop_bwrite(struct vnop_bwrite_args
*ap
)
2521 register struct buf
*bp
= ap
->a_bp
;
2522 register struct vnode
*vp
= buf_vnode(bp
);
2523 #if BYTE_ORDER == LITTLE_ENDIAN
2524 BlockDescriptor block
;
2526 /* Trap B-Tree writes */
2527 if ((VTOC(vp
)->c_fileid
== kHFSExtentsFileID
) ||
2528 (VTOC(vp
)->c_fileid
== kHFSCatalogFileID
) ||
2529 (VTOC(vp
)->c_fileid
== kHFSAttributesFileID
)) {
2531 /* Swap if the B-Tree node is in native byte order */
2532 if (((UInt16
*)((char *)buf_dataptr(bp
) + buf_count(bp
) - 2))[0] == 0x000e) {
2533 /* Prepare the block pointer */
2534 block
.blockHeader
= bp
;
2535 block
.buffer
= (char *)buf_dataptr(bp
);
2536 /* not found in cache ==> came from disk */
2537 block
.blockReadFromDisk
= (buf_fromcache(bp
) == 0);
2538 block
.blockSize
= buf_count(bp
);
2540 /* Endian un-swap B-Tree node */
2541 SWAP_BT_NODE (&block
, ISHFSPLUS (VTOVCB(vp
)), VTOC(vp
)->c_fileid
, 1);
2544 /* We don't check to make sure that it's 0x0e00 because it could be all zeros */
2547 /* This buffer shouldn't be locked anymore but if it is clear it */
2548 if ((buf_flags(bp
) & B_LOCKED
)) {
2550 if (VTOHFS(vp
)->jnl
) {
2551 panic("hfs: CLEARING the lock bit on bp 0x%x\n", bp
);
2553 buf_clearflags(bp
, B_LOCKED
);
2555 retval
= vn_bwrite (ap
);
2561 * Relocate a file to a new location on disk
2562 * cnode must be locked on entry
2564 * Relocation occurs by cloning the file's data from its
2565 * current set of blocks to a new set of blocks. During
2566 * the relocation all of the blocks (old and new) are
2567 * owned by the file.
2574 * ----------------- -----------------
2575 * |///////////////| | | STEP 1 (aquire new blocks)
2576 * ----------------- -----------------
2579 * ----------------- -----------------
2580 * |///////////////| |///////////////| STEP 2 (clone data)
2581 * ----------------- -----------------
2585 * |///////////////| STEP 3 (head truncate blocks)
2589 * During steps 2 and 3 page-outs to file offsets less
2590 * than or equal to N are suspended.
2592 * During step 3 page-ins to the file get supended.
2596 hfs_relocate(struct vnode
*vp
, u_int32_t blockHint
, kauth_cred_t cred
,
2600 struct filefork
*fp
;
2601 struct hfsmount
*hfsmp
;
2606 u_int32_t nextallocsave
;
2607 daddr64_t sector_a
, sector_b
;
2608 int disabled_caching
= 0;
2613 int took_trunc_lock
= 0;
2615 enum vtype vnodetype
;
2617 vnodetype
= vnode_vtype(vp
);
2618 if (vnodetype
!= VREG
&& vnodetype
!= VLNK
) {
2623 if (hfsmp
->hfs_flags
& HFS_FRAGMENTED_FREESPACE
) {
2629 if (fp
->ff_unallocblocks
)
2631 blksize
= hfsmp
->blockSize
;
2633 blockHint
= hfsmp
->nextAllocation
;
2635 if ((fp
->ff_size
> (u_int64_t
)0x7fffffff) ||
2636 ((fp
->ff_size
> blksize
) && vnodetype
== VLNK
)) {
2641 // We do not believe that this call to hfs_fsync() is
2642 // necessary and it causes a journal transaction
2643 // deadlock so we are removing it.
2645 //if (vnodetype == VREG && !vnode_issystem(vp)) {
2646 // retval = hfs_fsync(vp, MNT_WAIT, 0, p);
2651 if (!vnode_issystem(vp
) && (vnodetype
!= VLNK
)) {
2653 hfs_lock_truncate(cp
, TRUE
);
2654 if ((retval
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
2655 hfs_unlock_truncate(cp
);
2658 took_trunc_lock
= 1;
2660 headblks
= fp
->ff_blocks
;
2661 datablks
= howmany(fp
->ff_size
, blksize
);
2662 growsize
= datablks
* blksize
;
2663 eflags
= kEFContigMask
| kEFAllMask
| kEFNoClumpMask
;
2664 if (blockHint
>= hfsmp
->hfs_metazone_start
&&
2665 blockHint
<= hfsmp
->hfs_metazone_end
)
2666 eflags
|= kEFMetadataMask
;
2668 if (hfs_start_transaction(hfsmp
) != 0) {
2669 if (took_trunc_lock
)
2670 hfs_unlock_truncate(cp
);
2675 * Protect the extents b-tree and the allocation bitmap
2676 * during MapFileBlockC and ExtendFileC operations.
2678 lockflags
= SFL_BITMAP
;
2679 if (overflow_extents(fp
))
2680 lockflags
|= SFL_EXTENTS
;
2681 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2683 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, 1, growsize
- 1, §or_a
, NULL
);
2685 retval
= MacToVFSError(retval
);
2690 * STEP 1 - aquire new allocation blocks.
2692 if (!vnode_isnocache(vp
)) {
2693 vnode_setnocache(vp
);
2694 disabled_caching
= 1;
2697 nextallocsave
= hfsmp
->nextAllocation
;
2698 retval
= ExtendFileC(hfsmp
, (FCB
*)fp
, growsize
, blockHint
, eflags
, &newbytes
);
2699 if (eflags
& kEFMetadataMask
) {
2700 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
2701 hfsmp
->nextAllocation
= nextallocsave
;
2702 hfsmp
->vcbFlags
|= 0xFF00;
2703 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2706 retval
= MacToVFSError(retval
);
2708 cp
->c_flag
|= C_MODIFIED
;
2709 if (newbytes
< growsize
) {
2712 } else if (fp
->ff_blocks
< (headblks
+ datablks
)) {
2713 printf("hfs_relocate: allocation failed");
2718 retval
= MapFileBlockC(hfsmp
, (FCB
*)fp
, 1, growsize
, §or_b
, NULL
);
2720 retval
= MacToVFSError(retval
);
2721 } else if ((sector_a
+ 1) == sector_b
) {
2724 } else if ((eflags
& kEFMetadataMask
) &&
2725 ((((u_int64_t
)sector_b
* hfsmp
->hfs_phys_block_size
) / blksize
) >
2726 hfsmp
->hfs_metazone_end
)) {
2727 printf("hfs_relocate: didn't move into metadata zone\n");
2732 /* Done with system locks and journal for now. */
2733 hfs_systemfile_unlock(hfsmp
, lockflags
);
2735 hfs_end_transaction(hfsmp
);
2740 * Check to see if failure is due to excessive fragmentation.
2742 if ((retval
== ENOSPC
) &&
2743 (hfs_freeblks(hfsmp
, 0) > (datablks
* 2))) {
2744 hfsmp
->hfs_flags
|= HFS_FRAGMENTED_FREESPACE
;
2749 * STEP 2 - clone file data into the new allocation blocks.
2752 if (vnodetype
== VLNK
)
2753 retval
= hfs_clonelink(vp
, blksize
, cred
, p
);
2754 else if (vnode_issystem(vp
))
2755 retval
= hfs_clonesysfile(vp
, headblks
, datablks
, blksize
, cred
, p
);
2757 retval
= hfs_clonefile(vp
, headblks
, datablks
, blksize
);
2759 /* Start transaction for step 3 or for a restore. */
2760 if (hfs_start_transaction(hfsmp
) != 0) {
2769 * STEP 3 - switch to cloned data and remove old blocks.
2771 lockflags
= SFL_BITMAP
;
2772 if (overflow_extents(fp
))
2773 lockflags
|= SFL_EXTENTS
;
2774 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2776 retval
= HeadTruncateFile(hfsmp
, (FCB
*)fp
, headblks
);
2778 hfs_systemfile_unlock(hfsmp
, lockflags
);
2783 if (took_trunc_lock
)
2784 hfs_unlock_truncate(cp
);
2787 hfs_systemfile_unlock(hfsmp
, lockflags
);
2791 // See comment up above about calls to hfs_fsync()
2794 // retval = hfs_fsync(vp, MNT_WAIT, 0, p);
2797 if (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)
2798 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
2800 (void) hfs_flushvolumeheader(hfsmp
, MNT_NOWAIT
, 0);
2803 if (disabled_caching
) {
2804 vnode_clearnocache(vp
);
2807 hfs_end_transaction(hfsmp
);
2812 if (fp
->ff_blocks
== headblks
)
2815 * Give back any newly allocated space.
2817 if (lockflags
== 0) {
2818 lockflags
= SFL_BITMAP
;
2819 if (overflow_extents(fp
))
2820 lockflags
|= SFL_EXTENTS
;
2821 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
2824 (void) TruncateFileC(hfsmp
, (FCB
*)fp
, fp
->ff_size
, false);
2826 hfs_systemfile_unlock(hfsmp
, lockflags
);
2829 if (took_trunc_lock
)
2830 hfs_unlock_truncate(cp
);
2840 hfs_clonelink(struct vnode
*vp
, int blksize
, kauth_cred_t cred
, struct proc
*p
)
2842 struct buf
*head_bp
= NULL
;
2843 struct buf
*tail_bp
= NULL
;
2847 error
= (int)buf_meta_bread(vp
, (daddr64_t
)0, blksize
, cred
, &head_bp
);
2851 tail_bp
= buf_getblk(vp
, (daddr64_t
)1, blksize
, 0, 0, BLK_META
);
2852 if (tail_bp
== NULL
) {
2856 bcopy((char *)buf_dataptr(head_bp
), (char *)buf_dataptr(tail_bp
), blksize
);
2857 error
= (int)buf_bwrite(tail_bp
);
2860 buf_markinvalid(head_bp
);
2861 buf_brelse(head_bp
);
2863 (void) buf_invalidateblks(vp
, BUF_WRITE_DATA
, 0, 0);
2869 * Clone a file's data within the file.
2873 hfs_clonefile(struct vnode
*vp
, int blkstart
, int blkcnt
, int blksize
)
2885 filesize
= VTOF(vp
)->ff_blocks
* blksize
; /* virtual file size */
2886 writebase
= blkstart
* blksize
;
2887 copysize
= blkcnt
* blksize
;
2888 iosize
= bufsize
= MIN(copysize
, 4096 * 16);
2891 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufp
, bufsize
)) {
2894 hfs_unlock(VTOC(vp
));
2896 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2898 while (offset
< copysize
) {
2899 iosize
= MIN(copysize
- offset
, iosize
);
2901 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
2902 uio_addiov(auio
, (uintptr_t)bufp
, iosize
);
2904 error
= cluster_read(vp
, auio
, copysize
, 0);
2906 printf("hfs_clonefile: cluster_read failed - %d\n", error
);
2909 if (uio_resid(auio
) != 0) {
2910 printf("clonedata: cluster_read: uio_resid = %lld\n", uio_resid(auio
));
2915 uio_reset(auio
, writebase
+ offset
, UIO_SYSSPACE32
, UIO_WRITE
);
2916 uio_addiov(auio
, (uintptr_t)bufp
, iosize
);
2918 error
= cluster_write(vp
, auio
, filesize
+ offset
,
2919 filesize
+ offset
+ iosize
,
2920 uio_offset(auio
), 0, IO_NOCACHE
| IO_SYNC
);
2922 printf("hfs_clonefile: cluster_write failed - %d\n", error
);
2925 if (uio_resid(auio
) != 0) {
2926 printf("hfs_clonefile: cluster_write failed - uio_resid not zero\n");
2935 * No need to call ubc_sync_range or hfs_invalbuf
2936 * since the file was copied using IO_NOCACHE.
2939 kmem_free(kernel_map
, (vm_offset_t
)bufp
, bufsize
);
2941 hfs_lock(VTOC(vp
), HFS_FORCE_LOCK
);
2946 * Clone a system (metadata) file.
2950 hfs_clonesysfile(struct vnode
*vp
, int blkstart
, int blkcnt
, int blksize
,
2951 kauth_cred_t cred
, struct proc
*p
)
2957 struct buf
*bp
= NULL
;
2960 daddr64_t start_blk
;
2967 iosize
= GetLogicalBlockSize(vp
);
2968 bufsize
= MIN(blkcnt
* blksize
, 1024 * 1024) & ~(iosize
- 1);
2969 breadcnt
= bufsize
/ iosize
;
2971 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufp
, bufsize
)) {
2974 start_blk
= ((daddr64_t
)blkstart
* blksize
) / iosize
;
2975 last_blk
= ((daddr64_t
)blkcnt
* blksize
) / iosize
;
2978 while (blkno
< last_blk
) {
2980 * Read up to a megabyte
2983 for (i
= 0, blk
= blkno
; (i
< breadcnt
) && (blk
< last_blk
); ++i
, ++blk
) {
2984 error
= (int)buf_meta_bread(vp
, blk
, iosize
, cred
, &bp
);
2986 printf("hfs_clonesysfile: meta_bread error %d\n", error
);
2989 if (buf_count(bp
) != iosize
) {
2990 printf("hfs_clonesysfile: b_bcount is only %d\n", buf_count(bp
));
2993 bcopy((char *)buf_dataptr(bp
), offset
, iosize
);
2995 buf_markinvalid(bp
);
3003 * Write up to a megabyte
3006 for (i
= 0; (i
< breadcnt
) && (blkno
< last_blk
); ++i
, ++blkno
) {
3007 bp
= buf_getblk(vp
, start_blk
+ blkno
, iosize
, 0, 0, BLK_META
);
3009 printf("hfs_clonesysfile: getblk failed on blk %qd\n", start_blk
+ blkno
);
3013 bcopy(offset
, (char *)buf_dataptr(bp
), iosize
);
3014 error
= (int)buf_bwrite(bp
);
3026 kmem_free(kernel_map
, (vm_offset_t
)bufp
, bufsize
);
3028 error
= hfs_fsync(vp
, MNT_WAIT
, 0, p
);