#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>
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;
}
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
vprintf(format, args);
printf("\n");
}
void*
hfs_libcb_malloc(size_t size, hfs_callback_args* cbargs)
{
return malloc(size, M_TEMP, M_WAITOK);
}
void*
hfs_libcb_realloc(void* ptr, size_t size, hfs_callback_args* cbargs)
{
return realloc(ptr, size, M_TEMP, M_WAITOK);
}
void
hfs_libcb_free(void* ptr, hfs_callback_args* cbargs)
{
free(ptr, M_TEMP);
}
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;
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;
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;
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;
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;
physoffset = offset + vol->offset;
return hfs_pread(cbdata->devvp, outbytes, cbdata->devblksz, physoffset,
length, cred);
}
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;
uint64_t start;
int error;
if (vp == NULL || buf == NULL)
return EINVAL;
if (len == 0)
return 0;
curoff = 0;
error = 0;
#define ABSZ(x, bsz) (((x)/(bsz))*(bsz))
#define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1))
start = ABSZ(off, secsz);
while (start + curoff < off + len)
{
bp = NULL;
error = bread(vp, (start + curoff) / DEV_BSIZE,
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;
}
void
hfs_time_to_timespec(uint32_t hfstime, struct timespec *unixtime)
{
if (hfstime < 2082844800)
unixtime->tv_sec = 0;
else
unixtime->tv_sec = hfstime - 2082844800;
unixtime->tv_nsec = 0;
}
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;
}