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