1 /* $NetBSD: hfs_subr.c,v 1.12 2009/03/26 20:05:07 pooka Exp $ */
4 * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Yevgeny Binder and Dieter Baron.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: hfs_subr.c,v 1.12 2009/03/26 20:05:07 pooka Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/kernel.h>
40 #include <sys/vnode.h>
41 #include <sys/malloc.h>
44 #include <sys/filedesc.h>
45 #include <sys/mount.h>
46 #include <sys/disklabel.h>
48 #include <sys/kauth.h>
51 #include <fs/hfs/hfs.h>
53 #include <miscfs/specfs/specdev.h>
56 * Initialize the vnode associated with a new hfsnode.
59 hfs_vinit(struct mount
*mp
, int (**specops
)(void *), int (**fifoops
)(void *),
68 vp
->v_type
= hfs_catalog_keyed_record_vtype(
69 (hfs_catalog_keyed_record_t
*)&hp
->h_rec
);
76 HFS_CONVERT_RDEV(hp
->h_rec
.file
.bsd
.special
.raw_device
));
91 if (hp
->h_rec
.u
.cnid
== HFS_CNID_ROOT_FOLDER
)
92 vp
->v_vflag
|= VV_ROOT
;
98 * Callbacks for libhfs
110 printf("%s:%i: ", file
, line
);
117 /* XXX Should we really display this if debugging is off? */
118 vprintf(format
, args
);
122 /* XXX change malloc/realloc/free to use pools */
125 hfs_libcb_malloc(size_t size
, hfs_callback_args
* cbargs
)
127 return malloc(size
, /*M_HFSMNT*/ M_TEMP
, M_WAITOK
);
131 hfs_libcb_realloc(void* ptr
, size_t size
, hfs_callback_args
* cbargs
)
133 return realloc(ptr
, size
, /*M_HFSMNT*/ M_TEMP
, M_WAITOK
);
137 hfs_libcb_free(void* ptr
, hfs_callback_args
* cbargs
)
139 free(ptr
, /*M_HFSMNT*/ M_TEMP
);
143 * hfs_libcb_opendev()
145 * hfslib uses this callback to open a volume's device node by name. However,
146 * by the time this is called here, the device node has already been opened by
147 * VFS. So we are passed the vnode to this volume's block device and use that
148 * instead of the device's name.
154 hfs_callback_args
* cbargs
)
156 hfs_libcb_data
* cbdata
= NULL
;
157 hfs_libcb_argsopen
* args
;
158 struct partinfo dpart
;
162 args
= (hfs_libcb_argsopen
*)(cbargs
->openvol
);
164 if (vol
== NULL
|| devname
== NULL
) {
169 cbdata
= malloc(sizeof(hfs_libcb_data
), M_HFSMNT
, M_WAITOK
);
170 if (cbdata
== NULL
) {
174 vol
->cbdata
= cbdata
;
176 cbdata
->devvp
= NULL
;
178 /* Open the device node. */
179 mode
= vol
->readonly
? FREAD
: FREAD
|FWRITE
;
180 if ((result
= VOP_OPEN(args
->devvp
, mode
,
184 /* Flush out any old buffers remaining from a previous use. */
185 vn_lock(args
->devvp
, LK_EXCLUSIVE
| LK_RETRY
);
186 result
= vinvalbuf(args
->devvp
, V_SAVE
, args
->cred
, args
->l
, 0, 0);
187 VOP_UNLOCK(args
->devvp
, 0);
189 VOP_CLOSE(args
->devvp
, mode
, FSCRED
);
193 cbdata
->devvp
= args
->devvp
;
195 /* Determine the device's block size. Default to DEV_BSIZE if unavailable.*/
196 if (VOP_IOCTL(args
->devvp
, DIOCGPART
, &dpart
, FREAD
, args
->cred
)
198 cbdata
->devblksz
= DEV_BSIZE
;
200 cbdata
->devblksz
= dpart
.disklab
->d_secsize
;
205 if (cbdata
!= NULL
) {
206 if (cbdata
->devvp
!= NULL
) {
207 vn_lock(cbdata
->devvp
, LK_EXCLUSIVE
| LK_RETRY
);
208 (void)VOP_CLOSE(cbdata
->devvp
, vol
->readonly
? FREAD
:
209 FREAD
| FWRITE
, NOCRED
);
210 VOP_UNLOCK(cbdata
->devvp
, 0);
212 free(cbdata
, M_HFSMNT
);
220 hfs_libcb_closedev(hfs_volume
* in_vol
, hfs_callback_args
* cbargs
)
227 if (in_vol
->cbdata
!= NULL
) {
228 devvp
= ((hfs_libcb_data
*)in_vol
->cbdata
)->devvp
;
230 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
231 (void)VOP_CLOSE(devvp
,
232 in_vol
->readonly
? FREAD
: FREAD
| FWRITE
, NOCRED
);
233 VOP_UNLOCK(devvp
, 0);
236 free(in_vol
->cbdata
, M_HFSMNT
);
237 in_vol
->cbdata
= NULL
;
247 hfs_callback_args
* cbargs
)
249 hfs_libcb_data
*cbdata
;
250 hfs_libcb_argsread
* argsread
;
252 uint64_t physoffset
; /* physical offset from start of device(?) */
254 if (vol
== NULL
|| outbytes
== NULL
)
257 cbdata
= (hfs_libcb_data
*)vol
->cbdata
;
260 && (argsread
= (hfs_libcb_argsread
*)cbargs
->read
) != NULL
261 && argsread
->cred
!= NULL
)
262 cred
= argsread
->cred
;
267 * Since bread() only reads data in terms of integral blocks, it may have
268 * read some data before and/or after our desired offset & length. So when
269 * copying that data into the outgoing buffer, start at the actual desired
270 * offset and only copy the desired length.
272 physoffset
= offset
+ vol
->offset
;
274 return hfs_pread(cbdata
->devvp
, outbytes
, cbdata
->devblksz
, physoffset
,
279 * So it turns out that bread() is pretty shoddy. It not only requires the size
280 * parameter to be an integral multiple of the device's block size, but also
281 * requires the block number to be on a boundary of that same block size -- and
282 * yet be given as an integral multiple of DEV_BSIZE! So after much toil and
283 * bloodshed, hfs_pread() was written as a convenience (and a model of how sane
284 * people take their bread()). Returns 0 on success.
287 hfs_pread(struct vnode
*vp
, void *buf
, size_t secsz
, uint64_t off
,
288 uint64_t len
, kauth_cred_t cred
)
291 uint64_t curoff
; /* relative to 'start' variable */
295 if (vp
== NULL
|| buf
== NULL
)
304 /* align offset to highest preceding sector boundary */
305 #define ABSZ(x, bsz) (((x)/(bsz))*(bsz))
307 /* round size up to integral # of block sizes */
308 #define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1))
310 start
= ABSZ(off
, secsz
);
311 while (start
+ curoff
< off
+ len
)
315 /* XXX Does the algorithm always do what's intended here when
316 * XXX start != off? Need to test this. */
318 error
= bread(vp
, (start
+ curoff
) / DEV_BSIZE
,/* no rounding involved*/
319 RBSZ(min(len
- curoff
+ (off
- start
), MAXBSIZE
), secsz
),
323 memcpy((uint8_t*)buf
+ curoff
, (uint8_t*)bp
->b_data
+
324 (off
- start
), min(len
- curoff
, MAXBSIZE
- (off
- start
)));
339 /* XXX Provide a routine to take a catalog record and return its proper BSD file
340 * XXX or directory mode value */
343 /* Convert from HFS+ time representation to UNIX time since epoch. */
345 hfs_time_to_timespec(uint32_t hfstime
, struct timespec
*unixtime
)
348 * HFS+ time is calculated in seconds since midnight, Jan 1st, 1904.
349 * struct timespec counts from midnight, Jan 1st, 1970. Thus, there is
350 * precisely a 66 year difference between them, which is equal to
351 * 2,082,844,800 seconds. No, I didn't count them by hand.
354 if (hfstime
< 2082844800)
355 unixtime
->tv_sec
= 0; /* dates before 1970 are bs anyway, so use epoch*/
357 unixtime
->tv_sec
= hfstime
- 2082844800;
359 unixtime
->tv_nsec
= 0; /* we don't have nanosecond resolution */
363 * Endian conversion with automatic pointer incrementation.
366 uint16_t be16tohp(void** inout_ptr
)
376 result
= be16toh(*ptr
);
384 uint32_t be32tohp(void** inout_ptr
)
394 result
= be32toh(*ptr
);
402 uint64_t be64tohp(void** inout_ptr
)
412 result
= be64toh(*ptr
);
421 hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t
*rec
)
423 if (rec
->type
== HFS_REC_FILE
) {
426 mode
= ((const hfs_file_record_t
*)rec
)->bsd
.file_mode
;