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