1/*	$NetBSD: hfs_subr.c,v 1.18 2015/03/28 19:24:05 maxv Exp $	*/
2
3/*-
4 * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Yevgeny Binder and Dieter Baron.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
18 *
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.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: hfs_subr.c,v 1.18 2015/03/28 19:24:05 maxv Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/time.h>
38#include <sys/kernel.h>
39#include <sys/proc.h>
40#include <sys/vnode.h>
41#include <sys/malloc.h>
42#include <sys/stat.h>
43#include <sys/file.h>
44#include <sys/filedesc.h>
45#include <sys/mount.h>
46#include <sys/device.h>
47#include <sys/conf.h>
48#include <sys/kauth.h>
49#include <sys/buf.h>
50
51#include <fs/hfs/hfs.h>
52
53#include <miscfs/specfs/specdev.h>
54
55/*
56 * Initialize the vnode associated with a new hfsnode.
57 */
58void
59hfs_vinit(struct mount *mp, int (**specops)(void *), int (**fifoops)(void *),
60	   struct vnode **vpp)
61{
62	struct hfsnode	*hp;
63	struct vnode	*vp;
64
65	vp = *vpp;
66	hp = VTOH(vp);
67
68	vp->v_type = hfs_catalog_keyed_record_vtype(
69		(hfs_catalog_keyed_record_t *)&hp->h_rec);
70
71	switch(vp->v_type) {
72		case VCHR:
73		case VBLK:
74			vp->v_op = specops;
75			spec_node_init(vp,
76			    HFS_CONVERT_RDEV(hp->h_rec.file.bsd.special.raw_device));
77			break;
78		case VFIFO:
79			vp->v_op = fifoops;
80			break;
81
82		case VNON:
83		case VBAD:
84		case VSOCK:
85		case VDIR:
86		case VREG:
87		case VLNK:
88			break;
89	}
90
91	if (hp->h_rec.u.cnid == HFS_CNID_ROOT_FOLDER)
92		vp->v_vflag |= VV_ROOT;
93
94	*vpp = vp;
95}
96
97/*
98 * Callbacks for libhfs
99 */
100
101void
102hfs_libcb_error(
103	const char* format,
104	const char* file,
105	int line,
106	va_list args)
107{
108#ifdef HFS_DEBUG
109	if (file != NULL)
110		printf("%s:%i: ", file, line);
111	else
112		printf("hfs: ");
113#else
114	printf("hfs: ");
115#endif
116
117	/* XXX Should we really display this if debugging is off? */
118	vprintf(format, args);
119	printf("\n");
120}
121
122/* XXX change malloc/realloc/free to use pools */
123
124void*
125hfs_libcb_malloc(size_t size, hfs_callback_args* cbargs)
126{
127	return malloc(size, /*M_HFSMNT*/ M_TEMP, M_WAITOK);
128}
129
130void*
131hfs_libcb_realloc(void* ptr, size_t size, hfs_callback_args* cbargs)
132{
133	return realloc(ptr, size, /*M_HFSMNT*/ M_TEMP, M_WAITOK);
134}
135
136void
137hfs_libcb_free(void* ptr, hfs_callback_args* cbargs)
138{
139	free(ptr, /*M_HFSMNT*/ M_TEMP);
140}
141
142/*
143 * hfs_libcb_opendev()
144 *
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.
149 */
150int
151hfs_libcb_opendev(
152	hfs_volume* vol,
153	const char* devname,
154	hfs_callback_args* cbargs)
155{
156	hfs_libcb_data* cbdata = NULL;
157	hfs_libcb_argsopen* args;
158	int result, mode;
159	uint64_t psize;
160	unsigned secsize;
161
162	result = 0;
163	args = (hfs_libcb_argsopen*)(cbargs->openvol);
164
165	if (vol == NULL || devname == NULL) {
166		result = EINVAL;
167		goto error;
168	}
169
170	cbdata = malloc(sizeof(hfs_libcb_data), M_HFSMNT, M_WAITOK);
171	if (cbdata == NULL) {
172		result = ENOMEM;
173		goto error;
174	}
175	vol->cbdata = cbdata;
176
177	cbdata->devvp = NULL;
178
179	/* Open the device node. */
180	mode = vol->readonly ? FREAD : FREAD|FWRITE;
181	vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY);
182	result = VOP_OPEN(args->devvp, mode, FSCRED);
183	VOP_UNLOCK(args->devvp);
184	if (result != 0)
185		goto error;
186
187	/* Flush out any old buffers remaining from a previous use. */
188	vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY);
189	result = vinvalbuf(args->devvp, V_SAVE, args->cred, args->l, 0, 0);
190	VOP_UNLOCK(args->devvp);
191	if (result != 0) {
192		VOP_CLOSE(args->devvp, mode, FSCRED);
193		goto error;
194	}
195
196	cbdata->devvp = args->devvp;
197
198	/* Determine the device's block size. Default to DEV_BSIZE if unavailable.*/
199	if (getdisksize(args->devvp, &psize, &secsize) != 0)
200		cbdata->devblksz = DEV_BSIZE;
201	else
202		cbdata->devblksz = secsize;
203
204	return 0;
205
206error:
207	if (cbdata != NULL) {
208		if (cbdata->devvp != NULL) {
209			vn_lock(cbdata->devvp, LK_EXCLUSIVE | LK_RETRY);
210			(void)VOP_CLOSE(cbdata->devvp, vol->readonly ? FREAD :
211				FREAD | FWRITE, NOCRED);
212			VOP_UNLOCK(cbdata->devvp);
213		}
214		free(cbdata, M_HFSMNT);
215		vol->cbdata = NULL;
216	}
217
218	return result;
219}
220
221void
222hfs_libcb_closedev(hfs_volume* in_vol, hfs_callback_args* cbargs)
223{
224	struct vnode *devvp;
225
226	if (in_vol == NULL)
227		return;
228
229	if (in_vol->cbdata != NULL) {
230		devvp = ((hfs_libcb_data*)in_vol->cbdata)->devvp;
231		if (devvp != NULL) {
232			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
233			(void)VOP_CLOSE(devvp,
234			    in_vol->readonly ? FREAD : FREAD | FWRITE, NOCRED);
235			VOP_UNLOCK(devvp);
236		}
237
238		free(in_vol->cbdata, M_HFSMNT);
239		in_vol->cbdata = NULL;
240	}
241}
242
243int
244hfs_libcb_read(
245	hfs_volume* vol,
246	void* outbytes,
247	uint64_t length,
248	uint64_t offset,
249	hfs_callback_args* cbargs)
250{
251	hfs_libcb_data *cbdata;
252	hfs_libcb_argsread* argsread;
253	kauth_cred_t cred;
254	uint64_t physoffset; /* physical offset from start of device(?) */
255
256	if (vol == NULL || outbytes == NULL)
257		return -1;
258
259	cbdata = (hfs_libcb_data*)vol->cbdata;
260
261	if (cbargs != NULL
262		&& (argsread = (hfs_libcb_argsread*)cbargs->read) != NULL
263		&& argsread->cred != NULL)
264		cred = argsread->cred;
265	else
266		cred = NOCRED;
267
268	/*
269	 * Since bread() only reads data in terms of integral blocks, it may have
270	 * read some data before and/or after our desired offset & length. So when
271	 * copying that data into the outgoing buffer, start at the actual desired
272	 * offset and only copy the desired length.
273	 */
274	physoffset = offset + vol->offset;
275
276	return hfs_pread(cbdata->devvp, outbytes, cbdata->devblksz, physoffset,
277			length, cred);
278}
279
280/*
281 * So it turns out that bread() is pretty shoddy. It not only requires the size
282 * parameter to be an integral multiple of the device's block size, but also
283 * requires the block number to be on a boundary of that same block size -- and
284 * yet be given as an integral multiple of DEV_BSIZE! So after much toil and
285 * bloodshed, hfs_pread() was written as a convenience (and a model of how sane
286 * people take their bread()). Returns 0 on success.
287 */
288int
289hfs_pread(struct vnode *vp, void *buf, size_t secsz, uint64_t off,
290	uint64_t len, kauth_cred_t cred)
291{
292	struct buf *bp;
293	uint64_t curoff; /* relative to 'start' variable */
294	uint64_t start;
295	int error;
296
297	if (vp == NULL || buf == NULL)
298		return EINVAL;
299
300	if (len == 0)
301		return 0;
302
303	curoff = 0;
304	error = 0;
305
306/* align offset to highest preceding sector boundary */
307#define ABSZ(x, bsz) (((x)/(bsz))*(bsz))
308
309/* round size up to integral # of block sizes */
310#define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1))
311
312	start = ABSZ(off, secsz);
313	while (start + curoff < off + len)
314	{
315		bp = NULL;
316
317		/* XXX  Does the algorithm always do what's intended here when
318		 * XXX  start != off? Need to test this. */
319
320		error = bread(vp, (start + curoff) / DEV_BSIZE,/* no rounding involved*/
321		   RBSZ(min(len - curoff + (off - start), MAXBSIZE), secsz),
322		   0, &bp);
323
324		if (error == 0)
325			memcpy((uint8_t*)buf + curoff, (uint8_t*)bp->b_data +
326				(off - start), min(len - curoff, MAXBSIZE - (off - start)));
327
328		if (bp != NULL)
329			brelse(bp, 0);
330		if (error != 0)
331			return error;
332
333		curoff += MAXBSIZE;
334	}
335#undef ABSZ
336#undef RBSZ
337
338	return 0;
339}
340
341/* XXX Provide a routine to take a catalog record and return its proper BSD file
342 * XXX or directory mode value */
343
344
345/* Convert from HFS+ time representation to UNIX time since epoch. */
346void
347hfs_time_to_timespec(uint32_t hfstime, struct timespec *unixtime)
348{
349	/*
350	 * HFS+ time is calculated in seconds since midnight, Jan 1st, 1904.
351	 * struct timespec counts from midnight, Jan 1st, 1970. Thus, there is
352	 * precisely a 66 year difference between them, which is equal to
353	 * 2,082,844,800 seconds. No, I didn't count them by hand.
354	 */
355
356	if (hfstime < 2082844800)
357		unixtime->tv_sec = 0; /* dates before 1970 are bs anyway, so use epoch*/
358	else
359		unixtime->tv_sec = hfstime - 2082844800;
360
361	unixtime->tv_nsec = 0; /* we don't have nanosecond resolution */
362}
363
364/*
365 * Endian conversion with automatic pointer incrementation.
366 */
367
368uint16_t be16tohp(void** inout_ptr)
369{
370	uint16_t	result;
371
372	if (inout_ptr == NULL)
373		return 0;
374
375	memcpy(&result, *inout_ptr, sizeof(result));
376	*inout_ptr = (char *)*inout_ptr + sizeof(result);
377	return be16toh(result);
378}
379
380uint32_t be32tohp(void** inout_ptr)
381{
382	uint32_t	result;
383
384	if (inout_ptr == NULL)
385		return 0;
386
387	memcpy(&result, *inout_ptr, sizeof(result));
388	*inout_ptr = (char *)*inout_ptr + sizeof(result);
389	return be32toh(result);
390}
391
392uint64_t be64tohp(void** inout_ptr)
393{
394	uint64_t	result;
395
396	if (inout_ptr == NULL)
397		return 0;
398
399	memcpy(&result, *inout_ptr, sizeof(result));
400	*inout_ptr = (char *)*inout_ptr + sizeof(result);
401	return be64toh(result);
402}
403
404enum vtype
405hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t *rec)
406{
407	if (rec->type == HFS_REC_FILE) {
408		uint32_t mode;
409
410		mode = ((const hfs_file_record_t *)rec)->bsd.file_mode;
411		if (mode != 0)
412			return IFTOVT(mode);
413		else
414			return VREG;
415	} else
416		return VDIR;
417}
418