xref: /netbsd/sys/fs/hfs/hfs_subr.c (revision 66c55f71)
1*66c55f71Smaxv /*	$NetBSD: hfs_subr.c,v 1.19 2015/06/21 13:43:58 maxv Exp $	*/
225e99827Sdillo 
325e99827Sdillo /*-
425e99827Sdillo  * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc.
525e99827Sdillo  * All rights reserved.
625e99827Sdillo  *
725e99827Sdillo  * This code is derived from software contributed to The NetBSD Foundation
825e99827Sdillo  * by Yevgeny Binder and Dieter Baron.
925e99827Sdillo  *
1025e99827Sdillo  * Redistribution and use in source and binary forms, with or without
1125e99827Sdillo  * modification, are permitted provided that the following conditions
1225e99827Sdillo  * are met:
1325e99827Sdillo  * 1. Redistributions of source code must retain the above copyright
1425e99827Sdillo  *    notice, this list of conditions and the following disclaimer.
1525e99827Sdillo  * 2. Redistributions in binary form must reproduce the above copyright
1625e99827Sdillo  *    notice, this list of conditions and the following disclaimer in the
1725e99827Sdillo  *    documentation and/or other materials provided with the distribution.
1825e99827Sdillo  *
1925e99827Sdillo  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2025e99827Sdillo  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2125e99827Sdillo  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2225e99827Sdillo  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2325e99827Sdillo  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2425e99827Sdillo  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2525e99827Sdillo  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2625e99827Sdillo  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2725e99827Sdillo  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2825e99827Sdillo  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2925e99827Sdillo  * POSSIBILITY OF SUCH DAMAGE.
3025e99827Sdillo  */
3125e99827Sdillo 
3225e99827Sdillo #include <sys/cdefs.h>
33*66c55f71Smaxv __KERNEL_RCSID(0, "$NetBSD: hfs_subr.c,v 1.19 2015/06/21 13:43:58 maxv Exp $");
3425e99827Sdillo 
3525e99827Sdillo #include <sys/param.h>
3625e99827Sdillo #include <sys/systm.h>
3725e99827Sdillo #include <sys/time.h>
3825e99827Sdillo #include <sys/kernel.h>
3925e99827Sdillo #include <sys/proc.h>
4025e99827Sdillo #include <sys/vnode.h>
4125e99827Sdillo #include <sys/malloc.h>
4225e99827Sdillo #include <sys/stat.h>
4325e99827Sdillo #include <sys/file.h>
4425e99827Sdillo #include <sys/filedesc.h>
4525e99827Sdillo #include <sys/mount.h>
46def6d71aSchristos #include <sys/device.h>
4725e99827Sdillo #include <sys/conf.h>
4825e99827Sdillo #include <sys/kauth.h>
49010ce493Spooka #include <sys/buf.h>
5025e99827Sdillo 
5156c3e412Sdillo #include <fs/hfs/hfs.h>
5225e99827Sdillo 
53703069c0Sad #include <miscfs/specfs/specdev.h>
54703069c0Sad 
5525e99827Sdillo /*
5656c3e412Sdillo  * Initialize the vnode associated with a new hfsnode.
5725e99827Sdillo  */
5825e99827Sdillo void
hfs_vinit(struct mount * mp,int (** specops)(void *),int (** fifoops)(void *),struct vnode ** vpp)5956c3e412Sdillo hfs_vinit(struct mount *mp, int (**specops)(void *), int (**fifoops)(void *),
6025e99827Sdillo 	   struct vnode **vpp)
6125e99827Sdillo {
6256c3e412Sdillo 	struct hfsnode	*hp;
6325e99827Sdillo 	struct vnode	*vp;
6425e99827Sdillo 
6525e99827Sdillo 	vp = *vpp;
6625e99827Sdillo 	hp = VTOH(vp);
6725e99827Sdillo 
6856c3e412Sdillo 	vp->v_type = hfs_catalog_keyed_record_vtype(
6956c3e412Sdillo 		(hfs_catalog_keyed_record_t *)&hp->h_rec);
7025e99827Sdillo 
7125e99827Sdillo 	switch(vp->v_type) {
7225e99827Sdillo 		case VCHR:
7325e99827Sdillo 		case VBLK:
7425e99827Sdillo 			vp->v_op = specops;
75703069c0Sad 			spec_node_init(vp,
76703069c0Sad 			    HFS_CONVERT_RDEV(hp->h_rec.file.bsd.special.raw_device));
7725e99827Sdillo 			break;
7825e99827Sdillo 		case VFIFO:
7925e99827Sdillo 			vp->v_op = fifoops;
8025e99827Sdillo 			break;
8125e99827Sdillo 
8225e99827Sdillo 		case VNON:
8325e99827Sdillo 		case VBAD:
8425e99827Sdillo 		case VSOCK:
8525e99827Sdillo 		case VDIR:
8625e99827Sdillo 		case VREG:
8725e99827Sdillo 		case VLNK:
8825e99827Sdillo 			break;
8925e99827Sdillo 	}
9025e99827Sdillo 
9136a81a3bSgmcgarry 	if (hp->h_rec.u.cnid == HFS_CNID_ROOT_FOLDER)
927dad9f73Sad 		vp->v_vflag |= VV_ROOT;
9325e99827Sdillo 
9425e99827Sdillo 	*vpp = vp;
9525e99827Sdillo }
9625e99827Sdillo 
9725e99827Sdillo /*
9856c3e412Sdillo  * Callbacks for libhfs
9925e99827Sdillo  */
10025e99827Sdillo 
10125e99827Sdillo void
hfs_libcb_error(const char * format,const char * file,int line,va_list args)10256c3e412Sdillo hfs_libcb_error(
10325e99827Sdillo 	const char* format,
10425e99827Sdillo 	const char* file,
10525e99827Sdillo 	int line,
10625e99827Sdillo 	va_list args)
10725e99827Sdillo {
10856c3e412Sdillo #ifdef HFS_DEBUG
10925e99827Sdillo 	if (file != NULL)
11025e99827Sdillo 		printf("%s:%i: ", file, line);
11125e99827Sdillo 	else
11256c3e412Sdillo 		printf("hfs: ");
11325e99827Sdillo #else
11456c3e412Sdillo 	printf("hfs: ");
11525e99827Sdillo #endif
11625e99827Sdillo 
11725e99827Sdillo 	/* XXX Should we really display this if debugging is off? */
11825e99827Sdillo 	vprintf(format, args);
11925e99827Sdillo 	printf("\n");
12025e99827Sdillo }
12125e99827Sdillo 
12225e99827Sdillo /* XXX change malloc/realloc/free to use pools */
12325e99827Sdillo 
12425e99827Sdillo void*
hfs_libcb_malloc(size_t size,hfs_callback_args * cbargs)12556c3e412Sdillo hfs_libcb_malloc(size_t size, hfs_callback_args* cbargs)
12625e99827Sdillo {
12756c3e412Sdillo 	return malloc(size, /*M_HFSMNT*/ M_TEMP, M_WAITOK);
12825e99827Sdillo }
12925e99827Sdillo 
13025e99827Sdillo void*
hfs_libcb_realloc(void * ptr,size_t size,hfs_callback_args * cbargs)13156c3e412Sdillo hfs_libcb_realloc(void* ptr, size_t size, hfs_callback_args* cbargs)
13225e99827Sdillo {
13356c3e412Sdillo 	return realloc(ptr, size, /*M_HFSMNT*/ M_TEMP, M_WAITOK);
13425e99827Sdillo }
13525e99827Sdillo 
13625e99827Sdillo void
hfs_libcb_free(void * ptr,hfs_callback_args * cbargs)13756c3e412Sdillo hfs_libcb_free(void* ptr, hfs_callback_args* cbargs)
13825e99827Sdillo {
13956c3e412Sdillo 	free(ptr, /*M_HFSMNT*/ M_TEMP);
14025e99827Sdillo }
14125e99827Sdillo 
14225e99827Sdillo /*
14356c3e412Sdillo  * hfs_libcb_opendev()
14425e99827Sdillo  *
14556c3e412Sdillo  * hfslib uses this callback to open a volume's device node by name. However,
14625e99827Sdillo  * by the time this is called here, the device node has already been opened by
14725e99827Sdillo  * VFS. So we are passed the vnode to this volume's block device and use that
14825e99827Sdillo  * instead of the device's name.
14925e99827Sdillo  */
15025e99827Sdillo int
hfs_libcb_opendev(hfs_volume * vol,const char * devname,hfs_callback_args * cbargs)15156c3e412Sdillo hfs_libcb_opendev(
15256c3e412Sdillo 	hfs_volume* vol,
15325e99827Sdillo 	const char* devname,
15456c3e412Sdillo 	hfs_callback_args* cbargs)
15525e99827Sdillo {
15656c3e412Sdillo 	hfs_libcb_data* cbdata = NULL;
15756c3e412Sdillo 	hfs_libcb_argsopen* args;
158f75bcfdeSpooka 	int result, mode;
159def6d71aSchristos 	uint64_t psize;
160def6d71aSchristos 	unsigned secsize;
16125e99827Sdillo 
16225e99827Sdillo 	result = 0;
16356c3e412Sdillo 	args = (hfs_libcb_argsopen*)(cbargs->openvol);
16425e99827Sdillo 
16525e99827Sdillo 	if (vol == NULL || devname == NULL) {
16625e99827Sdillo 		result = EINVAL;
16725e99827Sdillo 		goto error;
16825e99827Sdillo 	}
16925e99827Sdillo 
17056c3e412Sdillo 	cbdata = malloc(sizeof(hfs_libcb_data), M_HFSMNT, M_WAITOK);
17125e99827Sdillo 	if (cbdata == NULL) {
17225e99827Sdillo 		result = ENOMEM;
17325e99827Sdillo 		goto error;
17425e99827Sdillo 	}
17525e99827Sdillo 	vol->cbdata = cbdata;
17625e99827Sdillo 
17725e99827Sdillo 	cbdata->devvp = NULL;
17825e99827Sdillo 
17925e99827Sdillo 	/* Open the device node. */
180f75bcfdeSpooka 	mode = vol->readonly ? FREAD : FREAD|FWRITE;
181a964277bShannken 	vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY);
182a964277bShannken 	result = VOP_OPEN(args->devvp, mode, FSCRED);
183a964277bShannken 	VOP_UNLOCK(args->devvp);
184a964277bShannken 	if (result != 0)
18525e99827Sdillo 		goto error;
18625e99827Sdillo 
18725e99827Sdillo 	/* Flush out any old buffers remaining from a previous use. */
18825e99827Sdillo 	vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY);
18925e99827Sdillo 	result = vinvalbuf(args->devvp, V_SAVE, args->cred, args->l, 0, 0);
1901423e65bShannken 	VOP_UNLOCK(args->devvp);
191f75bcfdeSpooka 	if (result != 0) {
192f75bcfdeSpooka 		VOP_CLOSE(args->devvp, mode, FSCRED);
19325e99827Sdillo 		goto error;
194f75bcfdeSpooka 	}
19525e99827Sdillo 
19625e99827Sdillo 	cbdata->devvp = args->devvp;
19725e99827Sdillo 
19825e99827Sdillo 	/* Determine the device's block size. Default to DEV_BSIZE if unavailable.*/
199def6d71aSchristos 	if (getdisksize(args->devvp, &psize, &secsize) != 0)
20025e99827Sdillo 		cbdata->devblksz = DEV_BSIZE;
20125e99827Sdillo 	else
202def6d71aSchristos 		cbdata->devblksz = secsize;
20325e99827Sdillo 
20425e99827Sdillo 	return 0;
20525e99827Sdillo 
20625e99827Sdillo error:
20725e99827Sdillo 	if (cbdata != NULL) {
20825e99827Sdillo 		if (cbdata->devvp != NULL) {
20925e99827Sdillo 			vn_lock(cbdata->devvp, LK_EXCLUSIVE | LK_RETRY);
21025e99827Sdillo 			(void)VOP_CLOSE(cbdata->devvp, vol->readonly ? FREAD :
21161e8303eSpooka 				FREAD | FWRITE, NOCRED);
2121423e65bShannken 			VOP_UNLOCK(cbdata->devvp);
21325e99827Sdillo 		}
21456c3e412Sdillo 		free(cbdata, M_HFSMNT);
21525e99827Sdillo 		vol->cbdata = NULL;
21625e99827Sdillo 	}
21725e99827Sdillo 
21825e99827Sdillo 	return result;
21925e99827Sdillo }
22025e99827Sdillo 
22125e99827Sdillo void
hfs_libcb_closedev(hfs_volume * in_vol,hfs_callback_args * cbargs)22256c3e412Sdillo hfs_libcb_closedev(hfs_volume* in_vol, hfs_callback_args* cbargs)
22325e99827Sdillo {
22425e99827Sdillo 	struct vnode *devvp;
22525e99827Sdillo 
22625e99827Sdillo 	if (in_vol == NULL)
22725e99827Sdillo 		return;
22825e99827Sdillo 
22925e99827Sdillo 	if (in_vol->cbdata != NULL) {
23056c3e412Sdillo 		devvp = ((hfs_libcb_data*)in_vol->cbdata)->devvp;
23125e99827Sdillo 		if (devvp != NULL) {
23225e99827Sdillo 			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
23361e8303eSpooka 			(void)VOP_CLOSE(devvp,
23461e8303eSpooka 			    in_vol->readonly ? FREAD : FREAD | FWRITE, NOCRED);
2351423e65bShannken 			VOP_UNLOCK(devvp);
23625e99827Sdillo 		}
23725e99827Sdillo 
23856c3e412Sdillo 		free(in_vol->cbdata, M_HFSMNT);
23925e99827Sdillo 		in_vol->cbdata = NULL;
24025e99827Sdillo 	}
24125e99827Sdillo }
24225e99827Sdillo 
24325e99827Sdillo int
hfs_libcb_read(hfs_volume * vol,void * outbytes,uint64_t length,uint64_t offset,hfs_callback_args * cbargs)24456c3e412Sdillo hfs_libcb_read(
24556c3e412Sdillo 	hfs_volume* vol,
24625e99827Sdillo 	void* outbytes,
24725e99827Sdillo 	uint64_t length,
24825e99827Sdillo 	uint64_t offset,
24956c3e412Sdillo 	hfs_callback_args* cbargs)
25025e99827Sdillo {
25156c3e412Sdillo 	hfs_libcb_data *cbdata;
25256c3e412Sdillo 	hfs_libcb_argsread* argsread;
25325e99827Sdillo 	kauth_cred_t cred;
25425e99827Sdillo 	uint64_t physoffset; /* physical offset from start of device(?) */
25525e99827Sdillo 
25625e99827Sdillo 	if (vol == NULL || outbytes == NULL)
25725e99827Sdillo 		return -1;
25825e99827Sdillo 
25956c3e412Sdillo 	cbdata = (hfs_libcb_data*)vol->cbdata;
26025e99827Sdillo 
26125e99827Sdillo 	if (cbargs != NULL
26256c3e412Sdillo 		&& (argsread = (hfs_libcb_argsread*)cbargs->read) != NULL
26325e99827Sdillo 		&& argsread->cred != NULL)
26425e99827Sdillo 		cred = argsread->cred;
26525e99827Sdillo 	else
26625e99827Sdillo 		cred = NOCRED;
26725e99827Sdillo 
26825e99827Sdillo 	/*
26925e99827Sdillo 	 * Since bread() only reads data in terms of integral blocks, it may have
27025e99827Sdillo 	 * read some data before and/or after our desired offset & length. So when
27125e99827Sdillo 	 * copying that data into the outgoing buffer, start at the actual desired
27225e99827Sdillo 	 * offset and only copy the desired length.
27325e99827Sdillo 	 */
27441627b2cSdillo 	physoffset = offset + vol->offset;
27525e99827Sdillo 
27656c3e412Sdillo 	return hfs_pread(cbdata->devvp, outbytes, cbdata->devblksz, physoffset,
27725e99827Sdillo 			length, cred);
27825e99827Sdillo }
27925e99827Sdillo 
28025e99827Sdillo /*
28125e99827Sdillo  * So it turns out that bread() is pretty shoddy. It not only requires the size
28225e99827Sdillo  * parameter to be an integral multiple of the device's block size, but also
28325e99827Sdillo  * requires the block number to be on a boundary of that same block size -- and
28425e99827Sdillo  * yet be given as an integral multiple of DEV_BSIZE! So after much toil and
28556c3e412Sdillo  * bloodshed, hfs_pread() was written as a convenience (and a model of how sane
28625e99827Sdillo  * people take their bread()). Returns 0 on success.
28725e99827Sdillo  */
28825e99827Sdillo int
hfs_pread(struct vnode * vp,void * buf,size_t secsz,uint64_t off,uint64_t len,kauth_cred_t cred)28956c3e412Sdillo hfs_pread(struct vnode *vp, void *buf, size_t secsz, uint64_t off,
29025e99827Sdillo 	uint64_t len, kauth_cred_t cred)
29125e99827Sdillo {
29225e99827Sdillo 	struct buf *bp;
29325e99827Sdillo 	uint64_t curoff; /* relative to 'start' variable */
29425e99827Sdillo 	uint64_t start;
29525e99827Sdillo 	int error;
29625e99827Sdillo 
29725e99827Sdillo 	if (vp == NULL || buf == NULL)
29825e99827Sdillo 		return EINVAL;
29925e99827Sdillo 
30025e99827Sdillo 	if (len == 0)
30125e99827Sdillo 		return 0;
30225e99827Sdillo 
30325e99827Sdillo 	curoff = 0;
30425e99827Sdillo 	error = 0;
30525e99827Sdillo 
30625e99827Sdillo /* align offset to highest preceding sector boundary */
30725e99827Sdillo #define ABSZ(x, bsz) (((x)/(bsz))*(bsz))
30825e99827Sdillo 
30925e99827Sdillo /* round size up to integral # of block sizes */
31025e99827Sdillo #define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1))
31125e99827Sdillo 
31225e99827Sdillo 	start = ABSZ(off, secsz);
31325e99827Sdillo 	while (start + curoff < off + len)
31425e99827Sdillo 	{
31525e99827Sdillo 		bp = NULL;
31625e99827Sdillo 
31725e99827Sdillo 		/* XXX  Does the algorithm always do what's intended here when
31825e99827Sdillo 		 * XXX  start != off? Need to test this. */
31925e99827Sdillo 
32025e99827Sdillo 		error = bread(vp, (start + curoff) / DEV_BSIZE,/* no rounding involved*/
3215d2bff06Shannken 		   RBSZ(min(len - curoff + (off - start), MAXBSIZE), secsz),
322f61b6169Smaxv 		   0, &bp);
32325e99827Sdillo 
32425e99827Sdillo 		if (error == 0)
32525e99827Sdillo 			memcpy((uint8_t*)buf + curoff, (uint8_t*)bp->b_data +
32625e99827Sdillo 				(off - start), min(len - curoff, MAXBSIZE - (off - start)));
32725e99827Sdillo 
32825e99827Sdillo 		if (bp != NULL)
3299f56dfa5Sad 			brelse(bp, 0);
33025e99827Sdillo 		if (error != 0)
33125e99827Sdillo 			return error;
33225e99827Sdillo 
33325e99827Sdillo 		curoff += MAXBSIZE;
33425e99827Sdillo 	}
33525e99827Sdillo #undef ABSZ
33625e99827Sdillo #undef RBSZ
33725e99827Sdillo 
33825e99827Sdillo 	return 0;
33925e99827Sdillo }
34025e99827Sdillo 
34125e99827Sdillo /* XXX Provide a routine to take a catalog record and return its proper BSD file
34225e99827Sdillo  * XXX or directory mode value */
34325e99827Sdillo 
34425e99827Sdillo 
34525e99827Sdillo /* Convert from HFS+ time representation to UNIX time since epoch. */
34625e99827Sdillo void
hfs_time_to_timespec(uint32_t hfstime,struct timespec * unixtime)34756c3e412Sdillo hfs_time_to_timespec(uint32_t hfstime, struct timespec *unixtime)
34825e99827Sdillo {
34925e99827Sdillo 	/*
35025e99827Sdillo 	 * HFS+ time is calculated in seconds since midnight, Jan 1st, 1904.
35125e99827Sdillo 	 * struct timespec counts from midnight, Jan 1st, 1970. Thus, there is
35225e99827Sdillo 	 * precisely a 66 year difference between them, which is equal to
35325e99827Sdillo 	 * 2,082,844,800 seconds. No, I didn't count them by hand.
35425e99827Sdillo 	 */
35525e99827Sdillo 
35625e99827Sdillo 	if (hfstime < 2082844800)
35725e99827Sdillo 		unixtime->tv_sec = 0; /* dates before 1970 are bs anyway, so use epoch*/
35825e99827Sdillo 	else
35925e99827Sdillo 		unixtime->tv_sec = hfstime - 2082844800;
36025e99827Sdillo 
36125e99827Sdillo 	unixtime->tv_nsec = 0; /* we don't have nanosecond resolution */
36225e99827Sdillo }
36325e99827Sdillo 
36425e99827Sdillo /*
36525e99827Sdillo  * Endian conversion with automatic pointer incrementation.
36625e99827Sdillo  */
36725e99827Sdillo 
be16tohp(void ** inout_ptr)36825e99827Sdillo uint16_t be16tohp(void** inout_ptr)
36925e99827Sdillo {
37025e99827Sdillo 	uint16_t	result;
37125e99827Sdillo 
37225e99827Sdillo 	if (inout_ptr == NULL)
37325e99827Sdillo 		return 0;
37425e99827Sdillo 
37515664883Schristos 	memcpy(&result, *inout_ptr, sizeof(result));
37615664883Schristos 	*inout_ptr = (char *)*inout_ptr + sizeof(result);
37715664883Schristos 	return be16toh(result);
37825e99827Sdillo }
37925e99827Sdillo 
be32tohp(void ** inout_ptr)38025e99827Sdillo uint32_t be32tohp(void** inout_ptr)
38125e99827Sdillo {
38225e99827Sdillo 	uint32_t	result;
38325e99827Sdillo 
38425e99827Sdillo 	if (inout_ptr == NULL)
38525e99827Sdillo 		return 0;
38625e99827Sdillo 
38715664883Schristos 	memcpy(&result, *inout_ptr, sizeof(result));
38815664883Schristos 	*inout_ptr = (char *)*inout_ptr + sizeof(result);
38915664883Schristos 	return be32toh(result);
39025e99827Sdillo }
39125e99827Sdillo 
be64tohp(void ** inout_ptr)39225e99827Sdillo uint64_t be64tohp(void** inout_ptr)
39325e99827Sdillo {
39425e99827Sdillo 	uint64_t	result;
39525e99827Sdillo 
39625e99827Sdillo 	if (inout_ptr == NULL)
39725e99827Sdillo 		return 0;
39825e99827Sdillo 
39915664883Schristos 	memcpy(&result, *inout_ptr, sizeof(result));
40015664883Schristos 	*inout_ptr = (char *)*inout_ptr + sizeof(result);
40115664883Schristos 	return be64toh(result);
40225e99827Sdillo }
40325e99827Sdillo 
40425e99827Sdillo enum vtype
hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t * rec)40556c3e412Sdillo hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t *rec)
40625e99827Sdillo {
40756c3e412Sdillo 	if (rec->type == HFS_REC_FILE) {
40825e99827Sdillo 		uint32_t mode;
40925e99827Sdillo 
41056c3e412Sdillo 		mode = ((const hfs_file_record_t *)rec)->bsd.file_mode;
41125e99827Sdillo 		if (mode != 0)
41225e99827Sdillo 			return IFTOVT(mode);
41325e99827Sdillo 		else
41425e99827Sdillo 			return VREG;
415*66c55f71Smaxv 	} else
41625e99827Sdillo 		return VDIR;
41725e99827Sdillo }
418