[BACK]Return to hfs_subr.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / fs / hfs

File: [cvs.NetBSD.org] / src / sys / fs / hfs / hfs_subr.c (download)

Revision 1.19, Sun Jun 21 13:43:58 2015 UTC (8 years, 10 months ago) by maxv
Branch: MAIN
CVS Tags: tls-maxphys-base-20171202, thorpej-ifq-base, thorpej-ifq, thorpej-i2c-spi-conf2-base, thorpej-i2c-spi-conf2, thorpej-i2c-spi-conf-base, thorpej-i2c-spi-conf, thorpej-futex2-base, thorpej-futex2, thorpej-futex-base, thorpej-futex, thorpej-cfargs2-base, thorpej-cfargs2, thorpej-cfargs-base, thorpej-cfargs, thorpej-altq-separation-base, thorpej-altq-separation, prg-localcount2-base3, prg-localcount2-base2, prg-localcount2-base1, prg-localcount2-base, prg-localcount2, phil-wifi-base, phil-wifi-20200421, phil-wifi-20200411, phil-wifi-20200406, phil-wifi-20191119, phil-wifi-20190609, phil-wifi, pgoyette-localcount-base, pgoyette-localcount-20170426, pgoyette-localcount-20170320, pgoyette-localcount-20170107, pgoyette-localcount-20161104, pgoyette-localcount-20160806, pgoyette-localcount-20160726, pgoyette-localcount, pgoyette-compat-merge-20190127, pgoyette-compat-base, pgoyette-compat-20190127, pgoyette-compat-20190118, pgoyette-compat-1226, pgoyette-compat-1126, pgoyette-compat-1020, pgoyette-compat-0930, pgoyette-compat-0906, pgoyette-compat-0728, pgoyette-compat-0625, pgoyette-compat-0521, pgoyette-compat-0502, pgoyette-compat-0422, pgoyette-compat-0415, pgoyette-compat-0407, pgoyette-compat-0330, pgoyette-compat-0322, pgoyette-compat-0315, pgoyette-compat, perseant-stdc-iso10646-base, perseant-stdc-iso10646, nick-nhusb-base-20170825, nick-nhusb-base-20170204, nick-nhusb-base-20161204, nick-nhusb-base-20161004, nick-nhusb-base-20160907, nick-nhusb-base-20160529, nick-nhusb-base-20160422, nick-nhusb-base-20160319, nick-nhusb-base-20151226, nick-nhusb-base-20150921, netbsd-9-base, netbsd-9-3-RELEASE, netbsd-9-2-RELEASE, netbsd-9-1-RELEASE, netbsd-9-0-RELEASE, netbsd-9-0-RC2, netbsd-9-0-RC1, netbsd-9, netbsd-8-base, netbsd-8-2-RELEASE, netbsd-8-1-RELEASE, netbsd-8-1-RC1, netbsd-8-0-RELEASE, netbsd-8-0-RC2, netbsd-8-0-RC1, netbsd-8, netbsd-10-base, netbsd-10-0-RELEASE, netbsd-10-0-RC6, netbsd-10-0-RC5, netbsd-10-0-RC4, netbsd-10-0-RC3, netbsd-10-0-RC2, netbsd-10-0-RC1, netbsd-10, matt-nb8-mediatek-base, matt-nb8-mediatek, localcount-20160914, jdolecek-ncq-base, jdolecek-ncq, isaki-audio2-base, isaki-audio2, is-mlppp-base, is-mlppp, cjep_sun2x-base1, cjep_sun2x-base, cjep_sun2x, cjep_staticlib_x-base1, cjep_staticlib_x-base, cjep_staticlib_x, bouyer-xenpvh-base2, bouyer-xenpvh-base1, bouyer-xenpvh-base, bouyer-xenpvh, bouyer-sunxi-drm-base, bouyer-sunxi-drm, bouyer-socketcan-base1, bouyer-socketcan-base, bouyer-socketcan, ad-namecache-base3, ad-namecache-base2, ad-namecache-base1, ad-namecache-base, ad-namecache, HEAD
Changes since 1.18: +26 -28 lines

KNF

/*	$NetBSD: hfs_subr.c,v 1.19 2015/06/21 13:43:58 maxv Exp $	*/

/*-
 * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Yevgeny Binder and Dieter Baron.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */                                     

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hfs_subr.c,v 1.19 2015/06/21 13:43:58 maxv Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/mount.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/kauth.h>
#include <sys/buf.h>

#include <fs/hfs/hfs.h>

#include <miscfs/specfs/specdev.h>

/*
 * Initialize the vnode associated with a new hfsnode.
 */
void
hfs_vinit(struct mount *mp, int (**specops)(void *), int (**fifoops)(void *),
	   struct vnode **vpp)
{
	struct hfsnode	*hp;
	struct vnode	*vp;

	vp = *vpp;
	hp = VTOH(vp);

	vp->v_type = hfs_catalog_keyed_record_vtype(
		(hfs_catalog_keyed_record_t *)&hp->h_rec);

	switch(vp->v_type) {
		case VCHR:
		case VBLK:
			vp->v_op = specops;
			spec_node_init(vp,
			    HFS_CONVERT_RDEV(hp->h_rec.file.bsd.special.raw_device));
			break;
		case VFIFO:
			vp->v_op = fifoops;
			break;

		case VNON:
		case VBAD:
		case VSOCK:
		case VDIR:
		case VREG:
		case VLNK:
			break;
	}

	if (hp->h_rec.u.cnid == HFS_CNID_ROOT_FOLDER)
		vp->v_vflag |= VV_ROOT;

	*vpp = vp;
}

/*
 * Callbacks for libhfs
 */

void
hfs_libcb_error(
	const char* format,
	const char* file,
	int line,
	va_list args)
{
#ifdef HFS_DEBUG
	if (file != NULL)
		printf("%s:%i: ", file, line);
	else
		printf("hfs: ");
#else
	printf("hfs: ");
#endif

	/* XXX Should we really display this if debugging is off? */
	vprintf(format, args);
	printf("\n");
}

/* XXX change malloc/realloc/free to use pools */

void*
hfs_libcb_malloc(size_t size, hfs_callback_args* cbargs)
{
	return malloc(size, /*M_HFSMNT*/ M_TEMP, M_WAITOK);
}

void*
hfs_libcb_realloc(void* ptr, size_t size, hfs_callback_args* cbargs)
{
	return realloc(ptr, size, /*M_HFSMNT*/ M_TEMP, M_WAITOK);
}

void
hfs_libcb_free(void* ptr, hfs_callback_args* cbargs)
{
	free(ptr, /*M_HFSMNT*/ M_TEMP);
}

/*
 * hfs_libcb_opendev()
 *
 * hfslib uses this callback to open a volume's device node by name. However,
 * by the time this is called here, the device node has already been opened by
 * VFS. So we are passed the vnode to this volume's block device and use that
 * instead of the device's name.
 */
int
hfs_libcb_opendev(
	hfs_volume* vol,
	const char* devname,
	hfs_callback_args* cbargs)
{
	hfs_libcb_data* cbdata = NULL;
	hfs_libcb_argsopen* args;
	int result, mode;
	uint64_t psize;
	unsigned secsize;

	result = 0;
	args = (hfs_libcb_argsopen*)(cbargs->openvol);

	if (vol == NULL || devname == NULL) {
		result = EINVAL;
		goto error;
	}

	cbdata = malloc(sizeof(hfs_libcb_data), M_HFSMNT, M_WAITOK);
	if (cbdata == NULL) {
		result = ENOMEM;
		goto error;
	}
	vol->cbdata = cbdata;

	cbdata->devvp = NULL;

	/* Open the device node. */
	mode = vol->readonly ? FREAD : FREAD|FWRITE;
	vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY);
	result = VOP_OPEN(args->devvp, mode, FSCRED);
	VOP_UNLOCK(args->devvp);
	if (result != 0)
		goto error;

	/* Flush out any old buffers remaining from a previous use. */
	vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY);
	result = vinvalbuf(args->devvp, V_SAVE, args->cred, args->l, 0, 0);
	VOP_UNLOCK(args->devvp);
	if (result != 0) {
		VOP_CLOSE(args->devvp, mode, FSCRED);
		goto error;
	}

	cbdata->devvp = args->devvp;

	/* Determine the device's block size. Default to DEV_BSIZE if unavailable.*/
	if (getdisksize(args->devvp, &psize, &secsize) != 0)
		cbdata->devblksz = DEV_BSIZE;
	else
		cbdata->devblksz = secsize;

	return 0;

error:
	if (cbdata != NULL) {
		if (cbdata->devvp != NULL) {
			vn_lock(cbdata->devvp, LK_EXCLUSIVE | LK_RETRY);
			(void)VOP_CLOSE(cbdata->devvp, vol->readonly ? FREAD :
				FREAD | FWRITE, NOCRED);
			VOP_UNLOCK(cbdata->devvp);
		}
		free(cbdata, M_HFSMNT);
		vol->cbdata = NULL;
	}

	return result;
}

void
hfs_libcb_closedev(hfs_volume* in_vol, hfs_callback_args* cbargs)
{
	struct vnode *devvp;

	if (in_vol == NULL)
		return;

	if (in_vol->cbdata != NULL) {
		devvp = ((hfs_libcb_data*)in_vol->cbdata)->devvp;
		if (devvp != NULL) {
			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
			(void)VOP_CLOSE(devvp,
			    in_vol->readonly ? FREAD : FREAD | FWRITE, NOCRED);
			VOP_UNLOCK(devvp);
		}

		free(in_vol->cbdata, M_HFSMNT);
		in_vol->cbdata = NULL;
	}
}

int
hfs_libcb_read(
	hfs_volume* vol,
	void* outbytes,
	uint64_t length,
	uint64_t offset,
	hfs_callback_args* cbargs)
{
	hfs_libcb_data *cbdata;
	hfs_libcb_argsread* argsread;
	kauth_cred_t cred;
	uint64_t physoffset; /* physical offset from start of device(?) */

	if (vol == NULL || outbytes == NULL)
		return -1;

	cbdata = (hfs_libcb_data*)vol->cbdata;

	if (cbargs != NULL
		&& (argsread = (hfs_libcb_argsread*)cbargs->read) != NULL
		&& argsread->cred != NULL)
		cred = argsread->cred;
	else
		cred = NOCRED;

	/*
	 * Since bread() only reads data in terms of integral blocks, it may have
	 * read some data before and/or after our desired offset & length. So when
	 * copying that data into the outgoing buffer, start at the actual desired
	 * offset and only copy the desired length.
	 */
	physoffset = offset + vol->offset;

	return hfs_pread(cbdata->devvp, outbytes, cbdata->devblksz, physoffset,
			length, cred);
}

/*
 * So it turns out that bread() is pretty shoddy. It not only requires the size
 * parameter to be an integral multiple of the device's block size, but also
 * requires the block number to be on a boundary of that same block size -- and
 * yet be given as an integral multiple of DEV_BSIZE! So after much toil and
 * bloodshed, hfs_pread() was written as a convenience (and a model of how sane
 * people take their bread()). Returns 0 on success.
 */
int
hfs_pread(struct vnode *vp, void *buf, size_t secsz, uint64_t off,
	uint64_t len, kauth_cred_t cred)
{
	struct buf *bp;
	uint64_t curoff; /* relative to 'start' variable */
	uint64_t start;
	int error;

	if (vp == NULL || buf == NULL)
		return EINVAL;

	if (len == 0)
		return 0;

	curoff = 0;
	error = 0;

/* align offset to highest preceding sector boundary */
#define ABSZ(x, bsz) (((x)/(bsz))*(bsz)) 

/* round size up to integral # of block sizes */
#define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1))

	start = ABSZ(off, secsz);
	while (start + curoff < off + len)
	{
		bp = NULL;

		/* XXX  Does the algorithm always do what's intended here when 
		 * XXX  start != off? Need to test this. */

		error = bread(vp, (start + curoff) / DEV_BSIZE,/* no rounding involved*/
		   RBSZ(min(len - curoff + (off - start), MAXBSIZE), secsz),
		   0, &bp);

		if (error == 0)
			memcpy((uint8_t*)buf + curoff, (uint8_t*)bp->b_data +
				(off - start), min(len - curoff, MAXBSIZE - (off - start)));

		if (bp != NULL)
			brelse(bp, 0);
		if (error != 0)
			return error;

		curoff += MAXBSIZE;
	}
#undef ABSZ
#undef RBSZ

	return 0;
}

/* XXX Provide a routine to take a catalog record and return its proper BSD file
 * XXX or directory mode value */


/* Convert from HFS+ time representation to UNIX time since epoch. */
void
hfs_time_to_timespec(uint32_t hfstime, struct timespec *unixtime)
{
	/*
	 * HFS+ time is calculated in seconds since midnight, Jan 1st, 1904.
	 * struct timespec counts from midnight, Jan 1st, 1970. Thus, there is
	 * precisely a 66 year difference between them, which is equal to
	 * 2,082,844,800 seconds. No, I didn't count them by hand.
	 */

	if (hfstime < 2082844800)
		unixtime->tv_sec = 0; /* dates before 1970 are bs anyway, so use epoch*/
	else
		unixtime->tv_sec = hfstime - 2082844800;

	unixtime->tv_nsec = 0; /* we don't have nanosecond resolution */
}

/*
 * Endian conversion with automatic pointer incrementation.
 */
 
uint16_t be16tohp(void** inout_ptr)
{
	uint16_t	result;

	if (inout_ptr == NULL)
		return 0;

	memcpy(&result, *inout_ptr, sizeof(result));
	*inout_ptr = (char *)*inout_ptr + sizeof(result);
	return be16toh(result);
}

uint32_t be32tohp(void** inout_ptr)
{
	uint32_t	result;

	if (inout_ptr == NULL)
		return 0;

	memcpy(&result, *inout_ptr, sizeof(result));
	*inout_ptr = (char *)*inout_ptr + sizeof(result);
	return be32toh(result);
}

uint64_t be64tohp(void** inout_ptr)
{
	uint64_t	result;

	if (inout_ptr == NULL)
		return 0;

	memcpy(&result, *inout_ptr, sizeof(result));
	*inout_ptr = (char *)*inout_ptr + sizeof(result);
	return be64toh(result);
}

enum vtype
hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t *rec)
{
	if (rec->type == HFS_REC_FILE) {
		uint32_t mode;

		mode = ((const hfs_file_record_t *)rec)->bsd.file_mode;
		if (mode != 0)
			return IFTOVT(mode);
		else
			return VREG;
	} else
		return VDIR;
}