root/sys/vfs/hpfs/hpfs_subr.c
/*-
 * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org)
 * All rights reserved.
 *
 * 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 AUTHOR 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 AUTHOR 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.
 *
 * $FreeBSD: src/sys/fs/hpfs/hpfs_subr.c,v 1.1 1999/12/09 19:09:59 semenu Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/buf.h>

#include <sys/buf2.h>

#include "hpfs.h"
#include "hpfsmount.h"
#include "hpfs_subr.h"

u_long
hpfs_checksum(
        u_int8_t *object,
        int size)
{
        int i;
        u_long csum=0L;
        for (i=0; i < size; i++) {
                csum += (u_long) *object++;
                csum = (csum << 7) + (csum >> (25));
        }
        return (csum);
}

void
hpfs_bmdeinit(
        struct hpfsmount *hpmp)
{
        struct buf *bp;
        int i;

        dprintf(("hpmp_bmdeinit: "));

        if (!(hpmp->hpm_mp->mnt_flag & MNT_RDONLY)) {
                /*
                 * Write down BitMap.
                 */
                for (i=0; i<hpmp->hpm_dbnum; i++) {
                        dprintf(("[%d: 0x%x] ", i, hpmp->hpm_bmind[i]));

                        bp = getblk(hpmp->hpm_devvp,
                                    dbtodoff(hpmp->hpm_bmind[i]),
                                    BMSIZE, 0, 0);
                        clrbuf(bp);

                        bcopy(hpmp->hpm_bitmap + BMSIZE * i, bp->b_data,
                              BMSIZE);

                        bwrite(bp);
                }
        }

        kfree(hpmp->hpm_bitmap, M_HPFSMNT);
        kfree(hpmp->hpm_bmind, M_HPFSMNT);

        dprintf(("\n"));
}

/*
 * Initialize BitMap management, includes calculation of
 * available blocks number.
 */
int
hpfs_bminit(
        struct hpfsmount *hpmp)
{
        struct buf *bp;
        int error, i, k;
        u_long dbavail;

        dprintf(("hpfs_bminit: "));

        hpmp->hpm_dbnum = (hpmp->hpm_su.su_btotal + 0x3FFF) / 0x4000;

        dprintf(("0x%lx data bands, ", hpmp->hpm_dbnum));

        hpmp->hpm_bmind = kmalloc(hpmp->hpm_dbnum * sizeof(lsn_t), M_HPFSMNT,
                                  M_WAITOK);

        hpmp->hpm_bitmap = kmalloc(hpmp->hpm_dbnum * BMSIZE, M_HPFSMNT,
                                   M_WAITOK);

        error = bread(hpmp->hpm_devvp, dbtodoff(hpmp->hpm_su.su_bitmap.lsn1),
                ((hpmp->hpm_dbnum + 0x7F) & ~(0x7F)) << 2, &bp);
        if (error) {
                brelse(bp);
                kfree(hpmp->hpm_bitmap, M_HPFSMNT);
                kfree(hpmp->hpm_bmind, M_HPFSMNT);
                dprintf((" error %d\n", error));
                return (error);
        }
        bcopy(bp->b_data, hpmp->hpm_bmind, hpmp->hpm_dbnum * sizeof(lsn_t));
        
        brelse(bp);

        /*
         * Read in all BitMap
         */
        for (i=0; i<hpmp->hpm_dbnum; i++) {
                dprintf(("[%d: 0x%x] ", i, hpmp->hpm_bmind[i]));

                error = bread(hpmp->hpm_devvp, dbtodoff(hpmp->hpm_bmind[i]),
                                BMSIZE, &bp);
                if (error) {
                        brelse(bp);
                        kfree(hpmp->hpm_bitmap, M_HPFSMNT);
                        kfree(hpmp->hpm_bmind, M_HPFSMNT);
                        dprintf((" error %d\n", error));
                        return (error);
                }
                bcopy(bp->b_data, hpmp->hpm_bitmap + BMSIZE * i, BMSIZE);

                brelse(bp);
        }

        /*
         * Look througth BitMap and count free bits
         */
        dbavail = 0;
        for (i=0; i < hpmp->hpm_su.su_btotal >> 5; i++) {
                u_int32_t mask;
                for (k=0, mask=1; k < 32; k++, mask<<=1)
                        if(((u_int32_t *)hpmp->hpm_bitmap)[i] & mask) 
                                dbavail ++;

        }
        hpmp->hpm_bavail = dbavail;

        return (0);
}

int
hpfs_cmpfname (
        struct hpfsmount *hpmp,
        char * uname,
        int ulen,
        char * dname,
        int dlen,
        u_int16_t cp)
{
        int i, res;

        for (i = 0; i < ulen && i < dlen; i++) {
                res = hpfs_toupper(hpmp, hpfs_u2d(hpmp, uname[i]), cp) - 
                      hpfs_toupper(hpmp, dname[i], cp);
                if (res)
                        return res;
        }
        return (ulen - dlen);
}

int
hpfs_cpstrnnicmp (
        struct hpfsmount *hpmp,
        char * str1,
        int str1len,
        u_int16_t str1cp,
        char * str2,
        int str2len,
        u_int16_t str2cp)
{
        int i, res;

        for (i = 0; i < str1len && i < str2len; i++) {
                res = (int)hpfs_toupper(hpmp, ((u_char *)str1)[i], str1cp) - 
                      (int)hpfs_toupper(hpmp, ((u_char *)str2)[i], str2cp);
                if (res)
                        return res;
        }
        return (str1len - str2len);
}


int
hpfs_cpload (
        struct hpfsmount *hpmp,
        struct cpiblk *cpibp,
        struct cpdblk *cpdbp)
{
        struct buf *bp;
        struct cpdsec * cpdsp;
        int error, i;

        error = bread(hpmp->hpm_devvp, dbtodoff(cpibp->b_cpdsec), DEV_BSIZE, &bp);
        if (error) {
                brelse(bp);
                return (error);
        }

        cpdsp = (struct cpdsec *)bp->b_data;

        for (i=cpdsp->d_cpfirst; i<cpdsp->d_cpcnt; i++) {
                if (cpdsp->d_cpdblk[i].b_cpid == cpibp->b_cpid) {
                        bcopy(cpdsp->d_cpdblk + i, cpdbp, 
                              sizeof(struct cpdblk));

                        brelse(bp);

                        return (0);
                }
        }

        brelse(bp);

        return (ENOENT);
}


/*
 * Initialize Code Page information management.
 * Load all copdepages in memory.
 */
int
hpfs_cpinit (
        struct hpfsmount *hpmp,
        struct hpfs_args *argsp)
{
        struct buf *bp;
        int error, i;
        lsn_t lsn;
        int cpicnt;
        struct cpisec * cpisp;
        struct cpiblk * cpibp;
        struct cpdblk * cpdbp;

        dprintf(("hpfs_cpinit: \n"));

        if (argsp->flags & HPFSMNT_TABLES) {
                bcopy(argsp->d2u, hpmp->hpm_d2u, sizeof(u_char) * 0x80);
                bcopy(argsp->u2d, hpmp->hpm_u2d, sizeof(u_char) * 0x80);
        } else {
                for (i=0x0; i<0x80;i++) {
                        hpmp->hpm_d2u[i] = i + 0x80;
                        hpmp->hpm_u2d[i] = i + 0x80;
                }
        }

        cpicnt = hpmp->hpm_sp.sp_cpinum;

        hpmp->hpm_cpdblk = kmalloc(cpicnt * sizeof(struct cpdblk), M_HPFSMNT,
                                   M_WAITOK);

        cpdbp = hpmp->hpm_cpdblk;
        lsn = hpmp->hpm_sp.sp_cpi;

        while (cpicnt > 0) {
                error = bread(hpmp->hpm_devvp, dbtodoff(lsn), DEV_BSIZE, &bp);
                if (error) {
                        brelse(bp);
                        return (error);
                }

                cpisp = (struct cpisec *)bp->b_data;

                cpibp = cpisp->s_cpi;
                for (i=0; i<cpisp->s_cpicnt; i++, cpicnt --, cpdbp++, cpibp++) {
                        dprintf(("hpfs_cpinit: Country: %d, CP: %d (%d)\n",
                                 cpibp->b_country, cpibp->b_cpid, 
                                 cpibp->b_vcpid));

                        error = hpfs_cpload(hpmp, cpibp, cpdbp);
                        if (error) {
                                brelse(bp);
                                return (error);
                        }
                }
                lsn = cpisp->s_next;
                brelse(bp);
        }

        return (0);
}

int
hpfs_cpdeinit (
        struct hpfsmount *hpmp)
{
        dprintf(("hpmp_cpdeinit: "));
        kfree(hpmp->hpm_cpdblk, M_HPFSMNT);
        return (0);
}

/*
 * Lookup for a run of blocks.
 */
int
hpfs_bmlookup (
        struct hpfsmount *hpmp,
        u_long flags,   /* 1 means we want right len blocks in run, not less */
        lsn_t lsn,              /* We want near this one */
        u_long len,             /* We want such long */
        lsn_t *lsnp,    /* We got here */
        u_long *lenp)   /* We got this long */
{
        u_int32_t * bitmap;
        u_int32_t mask;
        int i,k;
        int cband, vcband;
        u_int bandsz;
        int count;

        dprintf(("hpfs_bmlookup: lsn: 0x%x, len 0x%lx | Step1\n", lsn, len));

        if (lsn > hpmp->hpm_su.su_btotal) {
                kprintf("hpfs_bmlookup: OUT OF VOLUME\n");
                return ENOSPC;
        }
        if (len > hpmp->hpm_bavail) {
                kprintf("hpfs_bmlookup: OUT OF SPACE\n");
                return ENOSPC;
        }
        i = lsn >> 5;
        k = lsn & 0x1F;
        mask = 1 << k;
        bitmap = (u_int32_t *)hpmp->hpm_bitmap + i;

        if (*bitmap & mask) {
                *lsnp = lsn;
                *lenp = 0;
                for (; k < 32; k++, mask<<=1) {
                        if (*bitmap & mask)
                                (*lenp) ++;
                        else {
                                if (flags & 1)
                                        goto step2;
                                else 
                                        return (0);
                        }

                        if (*lenp == len)
                                return (0);
                }

                bitmap++;
                i++;
                for (; i < hpmp->hpm_su.su_btotal >> 5; i++, bitmap++) {
                        for (k=0, mask=1; k < 32; k++, mask<<=1) {
                                if (*bitmap & mask)
                                        (*lenp) ++;
                                else {
                                        if (flags & 1)
                                                goto step2;
                                        else 
                                                return (0);
                                }

                                if (*lenp == len)
                                        return (0);
                        }
                }
                return (0);
        }

step2:
        /*
         * Lookup all bands begining from cband, lookup for first block
         */
        cband = (lsn >> 14);
        dprintf(("hpfs_bmlookup: Step2: band 0x%x (0x%lx)\n",
                 cband, hpmp->hpm_dbnum));
        for (vcband = 0; vcband < hpmp->hpm_dbnum; vcband ++, cband++) {
                cband = cband % hpmp->hpm_dbnum;
                bandsz = min (hpmp->hpm_su.su_btotal - (cband << 14), 0x4000);
                dprintf(("hpfs_bmlookup: band: %d, sz: 0x%x\n", cband, bandsz));

                bitmap = (u_int32_t *)hpmp->hpm_bitmap + (cband << 9);
                *lsnp = cband << 14;
                *lenp = 0;
                count = 0;
                for (i=0; i < bandsz >> 5; i++, bitmap++) {
                        for (k=0, mask=1; k < 32; k++, mask<<=1) {
                                if (*bitmap & mask) {
                                        if (count) {
                                                (*lenp) ++;
                                        } else {
                                                count = 1;
                                                *lsnp = (cband << 14) + (i << 5) + k;
                                                *lenp = 1;
                                        }
                                } else {
                                        if ((*lenp) && !(flags & 1)) {
                                                return (0);
                                        } else {
                                                count = 0;
                                        }
                                }

                                if (*lenp == len)
                                        return (0);
                        }
                }
                if (cband == hpmp->hpm_dbnum - 1)  {
                        if ((*lenp) && !(flags & 1)) {
                                return (0);
                        } else {
                                count = 0;
                        }
                }
        }

        return (ENOSPC);
}

/*
 * Lookup a single free block.  XXX Need locking on BitMap operations
 * VERY STUPID ROUTINE!!!
 */
int
hpfs_bmfblookup (
        struct hpfsmount *hpmp,
        lsn_t *lp)
{
        u_int32_t * bitmap;
        int i,k;

        dprintf(("hpfs_bmfblookup: "));

        bitmap = (u_int32_t *)hpmp->hpm_bitmap;
        for (i=0; i < hpmp->hpm_su.su_btotal >> 5; i++, bitmap++) {
                k = ffs(*bitmap);
                if (k) {
                        *lp = (i << 5) + k - 1;
                        dprintf((" found: 0x%x\n",*lp));
                        return (0);
                }
        }

        return (ENOSPC);
}

/*
 * Mark contignous block of blocks.
 */
int
hpfs_bmmark (
        struct hpfsmount *hpmp,
        lsn_t bn,
        u_long bl,
        int state)
{
        u_int32_t * bitmap;
        int i, didprint = 0;

        dprintf(("hpfs_bmmark(0x%x, 0x%lx, %d): \n",bn,bl, state));

        if ((bn > hpmp->hpm_su.su_btotal) || (bn+bl > hpmp->hpm_su.su_btotal)) {
                kprintf("hpfs_bmmark: MARKING OUT OF VOLUME\n");
                return 0;
        }
        bitmap = (u_int32_t *)hpmp->hpm_bitmap;
        bitmap += bn >> 5;

        while (bl > 0) {
                for (i = bn & 0x1F; (i < 0x20) && (bl > 0) ; i++, bl--) {
                        if (state) {
                                if ( *bitmap & (1 << i)) {
                                        if (!didprint) {
                                                kprintf("hpfs_bmmark: ALREADY FREE\n");
                                                didprint = 1;
                                        }
                                } else 
                                        hpmp->hpm_bavail++;

                                *bitmap |= (1 << i);
                        } else {
                                if ((~(*bitmap)) & (1 << i)) {
                                        if (!didprint) {
                                                kprintf("hpfs_bmmark: ALREADY BUSY\n");
                                                didprint = 1;
                                        }
                                } else 
                                        hpmp->hpm_bavail--;

                                *bitmap &= ~(1 << i);
                        }
                }
                bn = 0;
                bitmap++;
        }

        return (0);
}


int
hpfs_validateparent (
        struct hpfsnode *hp)
{
        struct hpfsnode *dhp;
        struct vnode *dvp;
        struct hpfsmount *hpmp = hp->h_hpmp;
        struct buf *bp;
        struct dirblk *dp;
        struct hpfsdirent *dep;
        lsn_t lsn, olsn;
        int level, error;

        dprintf(("hpfs_validatetimes(0x%x): [parent: 0x%x] ",
                hp->h_no, hp->h_fn.fn_parent));

        if (hp->h_no == hp->h_fn.fn_parent) {
                dhp = hp;
        } else {
                error = VFS_VGET(hpmp->hpm_mp, NULL, hp->h_fn.fn_parent, &dvp);
                if (error)
                        return (error);
                dhp = VTOHP(dvp);
        }

        lsn = ((alleaf_t *)dhp->h_fn.fn_abd)->al_lsn;

        olsn = 0;
        level = 1;
        bp = NULL;

dive:
        dprintf(("[dive 0x%x] ", lsn));
        if (bp != NULL)
                brelse(bp);
        error = bread(dhp->h_devvp, dbtodoff(lsn), D_BSIZE, &bp);
        if (error)
                goto failed;

        dp = (struct dirblk *) bp->b_data;
        if (dp->d_magic != D_MAGIC) {
                kprintf("hpfs_validatetimes: magic doesn't match\n");
                error = EINVAL;
                goto failed;
        }

        dep = D_DIRENT(dp);

        if (olsn) {
                dprintf(("[restore 0x%x] ", olsn));

                while(!(dep->de_flag & DE_END) ) {
                        if((dep->de_flag & DE_DOWN) &&
                           (olsn == DE_DOWNLSN(dep)))
                                         break;
                        dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen);
                }

                if((dep->de_flag & DE_DOWN) && (olsn == DE_DOWNLSN(dep))) {
                        if (dep->de_flag & DE_END)
                                goto blockdone;

                        if (hp->h_no == dep->de_fnode) {
                                dprintf(("[found] "));
                                goto readdone;
                        }

                        dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen);
                } else {
                        kprintf("hpfs_validatetimes: ERROR! oLSN not found\n");
                        error = EINVAL;
                        goto failed;
                }
        }

        olsn = 0;

        while(!(dep->de_flag & DE_END)) {
                if(dep->de_flag & DE_DOWN) {
                        lsn = DE_DOWNLSN(dep);
                        level++;
                        goto dive;
                }

                if (hp->h_no == dep->de_fnode) {
                        dprintf(("[found] "));
                        goto readdone;
                }

                dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen);
        }

        if(dep->de_flag & DE_DOWN) {
                dprintf(("[enddive] "));
                lsn = DE_DOWNLSN(dep);
                level++;
                goto dive;
        }

blockdone:
        dprintf(("[EOB] "));
        olsn = lsn;
        lsn = dp->d_parent;
        level--;
        dprintf(("[level %d] ", level));
        if (level > 0)
                goto dive;      /* undive really */

        goto failed;

readdone:
        bcopy(dep->de_name,hp->h_name,dep->de_namelen);
        hp->h_name[dep->de_namelen] = '\0';
        hp->h_namelen = dep->de_namelen;
        hp->h_ctime = dep->de_ctime;
        hp->h_atime = dep->de_atime;
        hp->h_mtime = dep->de_mtime;
        hp->h_flag |= H_PARVALID;

        dprintf(("[readdone]"));

failed:
        dprintf(("\n"));
        if (bp != NULL)
                brelse(bp);
        if (hp != dhp)
                vput(dvp);

        return (error);
}

struct timespec
hpfstimetounix (
        u_long hptime)
{
        struct timespec t;

        t.tv_nsec = 0;
        t.tv_sec = hptime;

        return t;
}

/*
 * Write down changes done to parent dir, these are only times for now. 
 * hpfsnode have to be locked.
 */
int
hpfs_updateparent (
        struct hpfsnode *hp)
{
        struct hpfsnode *dhp;
        struct vnode *dvp;
        struct hpfsdirent *dep;
        struct buf * bp;
        int error;

        dprintf(("hpfs_updateparent(0x%x): \n", hp->h_no));

        if (!(hp->h_flag & H_PARCHANGE))
                return (0);

        if (!(hp->h_flag & H_PARVALID)) {
                error = hpfs_validateparent (hp);
                if (error)
                        return (error);
        }

        if (hp->h_no == hp->h_fn.fn_parent) {
                dhp = hp;
        } else {
                error = VFS_VGET(hp->h_hpmp->hpm_mp, NULL,
                                 hp->h_fn.fn_parent, &dvp);
                if (error)
                        return (error);
                dhp = VTOHP(dvp);
        }

        error = hpfs_genlookupbyname (dhp, hp->h_name, hp->h_namelen,
                                        &bp, &dep);
        if (error) {
                goto failed;
        }

        dep->de_atime = hp->h_atime;
        dep->de_mtime = hp->h_mtime;
        dep->de_size = hp->h_fn.fn_size;

        bdwrite (bp);

        hp->h_flag &= ~H_PARCHANGE;

        error = 0;
failed:
        if (hp != dhp)
                vput(dvp);

        return (0);
}

/*
 * Write down on disk changes done to fnode. hpfsnode have to be locked.
 */
int
hpfs_update (
        struct hpfsnode *hp)
{
        struct buf * bp;

        dprintf(("hpfs_update(0x%x): \n", hp->h_no));

        if (!(hp->h_flag & H_CHANGE))
                return (0);

        bp = getblk(hp->h_devvp, dbtodoff(hp->h_no), FNODESIZE, 0, 0);
        clrbuf(bp);

        bcopy (&hp->h_fn, bp->b_data, sizeof(struct fnode));
        bdwrite (bp);

        hp->h_flag &= ~H_CHANGE;

        if (hp->h_flag & H_PARCHANGE)
                return (hpfs_updateparent(hp));

        return (0);
}

/*
 * Truncate file to specifed size. hpfsnode have to be locked.
 */
int
hpfs_truncate (
        struct hpfsnode *hp,
        u_long size)
{
        struct hpfsmount *hpmp = hp->h_hpmp;
        lsn_t newblen;
#ifdef HPFS_DEBUG
        lsn_t oldblen;
#endif
        int error, pf;

        dprintf(("hpfs_truncate(0x%x, 0x%x -> 0x%lx): ",
                hp->h_no, hp->h_fn.fn_size, size));

        newblen = (size + DEV_BSIZE - 1) >> DEV_BSHIFT;
#ifdef HPFS_DEBUG
        oldblen = (hp->h_fn.fn_size + DEV_BSIZE - 1) >> DEV_BSHIFT;
#endif

        dprintf(("blen: 0x%x -> 0x%x\n", oldblen, newblen));

        error = hpfs_truncatealblk (hpmp, &hp->h_fn.fn_ab, newblen, &pf);
        if (error)
                return (error);
        if (pf) {
                hp->h_fn.fn_ab.ab_flag = 0;
                hp->h_fn.fn_ab.ab_freecnt = 0x8;
                hp->h_fn.fn_ab.ab_busycnt = 0x0;
                hp->h_fn.fn_ab.ab_freeoff = sizeof(alblk_t);
        }

        hp->h_fn.fn_size = size;

        hp->h_flag |= (H_CHANGE | H_PARCHANGE);

        dprintf(("hpfs_truncate: successful\n"));

        return (0);
}

/*
 * Enlarge file to specifed size. hpfsnode have to be locked.
 */
int
hpfs_extend (
        struct hpfsnode *hp,
        u_long size)
{
        struct hpfsmount *hpmp = hp->h_hpmp;
        lsn_t newblen, oldblen;
        int error;

        dprintf(("hpfs_extend(0x%x, 0x%x -> 0x%lx): ",
                hp->h_no, hp->h_fn.fn_size, size));

        if (hpmp->hpm_bavail < 0x10) 
                return (ENOSPC);

        newblen = (size + DEV_BSIZE - 1) >> DEV_BSHIFT;
        oldblen = (hp->h_fn.fn_size + DEV_BSIZE - 1) >> DEV_BSHIFT;

        dprintf(("blen: 0x%x -> 0x%x\n", oldblen, newblen));

        error = hpfs_addextent(hpmp, hp, newblen - oldblen);
        if (error) {
                kprintf("hpfs_extend: FAILED TO ADD EXTENT %d\n", error);
                return (error);
        }

        hp->h_fn.fn_size = size;

        hp->h_flag |= (H_CHANGE | H_PARCHANGE);

        dprintf(("hpfs_extend: successful\n"));

        return (0);
}

/*
 * Read AlSec structure, and check if magic is valid.
 * You don't need to brelse buf on error.
 */
int
hpfs_breadstruct (
        struct hpfsmount *hpmp,
        lsn_t lsn,
        u_int len,
        u_int32_t magic,
        struct buf **bpp)
{
        struct buf *bp;
        u_int32_t *mp;
        int error;

        dprintf(("hpfs_breadstruct: reading at 0x%x\n", lsn));

        *bpp = NULL;

        error = bread(hpmp->hpm_devvp, dbtodoff(lsn), len, &bp);
        if (error) {
                brelse(bp);
                return (error);
        }
        mp = (u_int32_t *) bp->b_data;
        if (*mp != magic) {
                brelse(bp);
                kprintf("hpfs_breadstruct: MAGIC DOESN'T MATCH (0x%08x != 0x%08x)\n",
                        *mp, magic);
                return (EINVAL);
        }

        *bpp = bp;

        return (0);
}