#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/namei.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <sys/module.h>
#include <sys/buf.h>
#include <sys/iconv.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/queue.h>
#include <sys/unistd.h>
#include <machine/inttypes.h>
#include <sys/buf2.h>
#include <vfs/udf/ecma167-udf.h>
#include <vfs/udf/osta.h>
#include <vfs/udf/udf.h>
#include <vfs/udf/udf_mount.h>
static int udf_access(struct vop_access_args *);
static int udf_getattr(struct vop_getattr_args *);
static int udf_ioctl(struct vop_ioctl_args *);
static int udf_pathconf(struct vop_pathconf_args *);
static int udf_read(struct vop_read_args *);
static int udf_readdir(struct vop_readdir_args *);
static int udf_readlink(struct vop_readlink_args *ap);
static int udf_strategy(struct vop_strategy_args *);
static int udf_bmap(struct vop_bmap_args *);
static int udf_lookup(struct vop_old_lookup_args *);
static int udf_reclaim(struct vop_reclaim_args *);
static int udf_readatoffset(struct udf_node *, int *, int, struct buf **, uint8_t **);
static int udf_bmap_internal(struct udf_node *, uint32_t, daddr_t *, uint32_t *);
struct vop_ops udf_vnode_vops = {
.vop_default = vop_defaultop,
.vop_access = udf_access,
.vop_bmap = udf_bmap,
.vop_old_lookup = udf_lookup,
.vop_getattr = udf_getattr,
.vop_ioctl = udf_ioctl,
.vop_pathconf = udf_pathconf,
.vop_read = udf_read,
.vop_readdir = udf_readdir,
.vop_readlink = udf_readlink,
.vop_reclaim = udf_reclaim,
.vop_strategy = udf_strategy
};
MALLOC_DEFINE(M_UDFFID, "UDF FID", "UDF FileId structure");
MALLOC_DEFINE(M_UDFDS, "UDF DS", "UDF Dirstream structure");
#define UDF_INVALID_BMAP -1
int
udf_hashlookup(struct udf_mnt *udfmp, ino_t id, struct vnode **vpp)
{
struct udf_node *node;
struct udf_hash_lh *lh;
struct vnode *vp;
*vpp = NULL;
lwkt_gettoken(&udfmp->hash_token);
loop:
lh = &udfmp->hashtbl[id % udfmp->hashsz];
if (lh == NULL) {
lwkt_reltoken(&udfmp->hash_token);
return(ENOENT);
}
LIST_FOREACH(node, lh, le) {
if (node->hash_id != id)
continue;
vp = node->i_vnode;
if (vget(vp, LK_EXCLUSIVE))
goto loop;
lh = &udfmp->hashtbl[id % udfmp->hashsz];
LIST_FOREACH(node, lh, le) {
if (node->hash_id == id)
break;
}
if (node == NULL || vp != node->i_vnode) {
vput(vp);
goto loop;
}
lwkt_reltoken(&udfmp->hash_token);
*vpp = vp;
return(0);
}
lwkt_reltoken(&udfmp->hash_token);
return(0);
}
int
udf_hashins(struct udf_node *node)
{
struct udf_mnt *udfmp;
struct udf_hash_lh *lh;
udfmp = node->udfmp;
lwkt_gettoken(&udfmp->hash_token);
lh = &udfmp->hashtbl[node->hash_id % udfmp->hashsz];
LIST_INSERT_HEAD(lh, node, le);
lwkt_reltoken(&udfmp->hash_token);
return(0);
}
int
udf_hashrem(struct udf_node *node)
{
struct udf_mnt *udfmp;
struct udf_hash_lh *lh;
udfmp = node->udfmp;
lwkt_gettoken(&udfmp->hash_token);
lh = &udfmp->hashtbl[node->hash_id % udfmp->hashsz];
if (lh == NULL)
panic("hash entry is NULL, node->hash_id= %"PRId64, node->hash_id);
LIST_REMOVE(node, le);
lwkt_reltoken(&udfmp->hash_token);
return(0);
}
int
udf_allocv(struct mount *mp, struct vnode **vpp)
{
int error;
struct vnode *vp;
error = getnewvnode(VT_UDF, mp, &vp, 0, 0);
if (error) {
kprintf("udf_allocv: failed to allocate new vnode\n");
return(error);
}
vx_downgrade(vp);
*vpp = vp;
return(0);
}
static mode_t
udf_permtomode(struct udf_node *node)
{
uint32_t perm;
uint32_t flags;
mode_t mode;
perm = node->fentry->perm;
flags = node->fentry->icbtag.flags;
mode = perm & UDF_FENTRY_PERM_USER_MASK;
mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK) >> 2);
mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
mode |= ((flags & UDF_ICB_TAG_FLAGS_STICKY) << 4);
mode |= ((flags & UDF_ICB_TAG_FLAGS_SETGID) << 6);
mode |= ((flags & UDF_ICB_TAG_FLAGS_SETUID) << 8);
return(mode);
}
static int
udf_access(struct vop_access_args *a)
{
struct vnode *vp;
struct udf_node *node;
vp = a->a_vp;
node = VTON(vp);
KKASSERT(vp->v_mount->mnt_flag & MNT_RDONLY);
return (vop_helper_access(a, node->fentry->uid, node->fentry->gid,
udf_permtomode(node), 0));
}
static int mon_lens[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
static int
udf_isaleapyear(int year)
{
int i;
i = (year % 4) ? 0 : 1;
i &= (year % 100) ? 1 : 0;
i |= (year % 400) ? 0 : 1;
return(i);
}
static void
udf_timetotimespec(struct timestamp *time, struct timespec *t)
{
int i, lpyear, daysinyear;
union {
uint16_t u_tz_offset;
int16_t s_tz_offset;
} tz;
t->tv_nsec = 0;
if (time->year < 1970) {
t->tv_sec = 0;
return;
}
t->tv_sec = time->second;
t->tv_sec += time->minute * 60;
t->tv_sec += time->hour * 3600;
t->tv_sec += time->day * 3600 * 24;
lpyear = udf_isaleapyear(time->year);
for (i = 1; i < time->month; i++)
t->tv_sec += mon_lens[lpyear][i] * 3600 * 24;
if (time->year > 1979)
t->tv_sec += 315532800;
if (time->year > 1989)
t->tv_sec += 315619200;
if (time->year > 1999)
t->tv_sec += 315532800;
for (i = 2000; i < time->year; i++) {
daysinyear = udf_isaleapyear(i) + 365 ;
t->tv_sec += daysinyear * 3600 * 24;
}
tz.u_tz_offset = time->type_tz;
tz.u_tz_offset &= 0x0fff;
if (tz.u_tz_offset & 0x0800)
tz.u_tz_offset |= 0xf000;
if ((time->type_tz & 0x1000) && (tz.s_tz_offset != -2047))
t->tv_sec -= tz.s_tz_offset * 60;
return;
}
static int
udf_getattr(struct vop_getattr_args *a)
{
struct vnode *vp;
struct udf_node *node;
struct vattr *vap;
struct file_entry *fentry;
vp = a->a_vp;
vap = a->a_vap;
node = VTON(vp);
fentry = node->fentry;
vap->va_fsid = devid_from_dev(node->i_dev);
vap->va_fileid = node->hash_id;
vap->va_mode = udf_permtomode(node);
vap->va_nlink = fentry->link_cnt;
vap->va_uid = (fentry->uid == 0xffffffff) ? 0 : fentry->uid;
vap->va_gid = (fentry->gid == 0xffffffff) ? 0 : fentry->gid;
udf_timetotimespec(&fentry->atime, &vap->va_atime);
udf_timetotimespec(&fentry->mtime, &vap->va_mtime);
vap->va_ctime = vap->va_mtime;
vap->va_rmajor = VNOVAL;
vap->va_rminor = VNOVAL;
if (vp->v_type & VDIR) {
if (fentry->logblks_rec != 0)
vap->va_size = fentry->logblks_rec * node->udfmp->bsize;
else
vap->va_size = node->udfmp->bsize;
} else
vap->va_size = fentry->inf_len;
vap->va_flags = 0;
vap->va_gen = 1;
vap->va_blocksize = node->udfmp->bsize;
vap->va_bytes = fentry->inf_len;
vap->va_type = vp->v_type;
vap->va_filerev = 0;
return(0);
}
static int
udf_ioctl(struct vop_ioctl_args *a)
{
kprintf("%s called\n", __func__);
return(ENOTTY);
}
static int
udf_pathconf(struct vop_pathconf_args *a)
{
switch (a->a_name) {
case _PC_LINK_MAX:
*a->a_retval = 65535;
return(0);
case _PC_NAME_MAX:
*a->a_retval = NAME_MAX;
return(0);
case _PC_PATH_MAX:
*a->a_retval = PATH_MAX;
return(0);
case _PC_NO_TRUNC:
*a->a_retval = 1;
return(0);
default:
return(EINVAL);
}
}
static int
udf_read(struct vop_read_args *a)
{
struct vnode *vp = a->a_vp;
struct uio *uio = a->a_uio;
struct udf_node *node = VTON(vp);
struct buf *bp;
uint8_t *data;
int error = 0;
int size, fsize, offset;
if (uio->uio_offset < 0)
return(EINVAL);
fsize = node->fentry->inf_len;
while (uio->uio_offset < fsize && uio->uio_resid > 0) {
offset = uio->uio_offset;
size = uio->uio_resid;
error = udf_readatoffset(node, &size, offset, &bp, &data);
if (error == 0)
error = uiomove(data, size, uio);
if (bp != NULL)
brelse(bp);
if (error)
break;
}
return(error);
}
static int
udf_transname(char *cs0string, char *destname, int len, struct udf_mnt *udfmp)
{
unicode_t *transname;
int i, unilen = 0, destlen;
transname = kmalloc(NAME_MAX * sizeof(unicode_t), M_TEMP, M_WAITOK | M_ZERO);
if ((unilen = udf_UncompressUnicode(len, cs0string, transname)) == -1) {
kprintf("udf: Unicode translation failed\n");
kfree(transname, M_TEMP);
return(0);
}
for (i = 0; i < unilen ; i++)
if (transname[i] & 0xff00)
destname[i] = '.';
else
destname[i] = transname[i] & 0xff;
kfree(transname, M_TEMP);
destname[unilen] = 0;
destlen = unilen;
return(destlen);
}
static int
udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen, struct udf_mnt *udfmp)
{
char *transname;
int error = 0;
transname = kmalloc(NAME_MAX * sizeof(unicode_t), M_TEMP,
M_WAITOK | M_ZERO);
cs0len = udf_transname(cs0string, transname, cs0len, udfmp);
if ((cs0len == 0) || (cs0len != cmplen))
error = -1;
else
error = bcmp(transname, cmpname, cmplen);
kfree(transname, M_TEMP);
return(error);
}
struct udf_uiodir {
struct dirent *dirent;
off_t *cookies;
int ncookies;
int acookies;
int eofflag;
};
static struct udf_dirstream *
udf_opendir(struct udf_node *node, int offset, int fsize, struct udf_mnt *udfmp)
{
struct udf_dirstream *ds;
ds = kmalloc(sizeof(*ds), M_UDFDS, M_WAITOK | M_ZERO);
ds->node = node;
ds->offset = offset;
ds->udfmp = udfmp;
ds->fsize = fsize;
return(ds);
}
static struct fileid_desc *
udf_getfid(struct udf_dirstream *ds)
{
struct fileid_desc *fid;
int error, frag_size = 0, total_fid_size;
if (ds->offset + ds->off >= ds->fsize) {
ds->error = 0;
return(NULL);
}
if (ds->off == 0) {
ds->size = 0;
if (ds->bp != NULL)
brelse(ds->bp);
error = udf_readatoffset(ds->node, &ds->size, ds->offset,
&ds->bp, &ds->data);
if (error) {
ds->error = error;
return(NULL);
}
}
if (ds->fid_fragment && ds->buf != NULL) {
ds->fid_fragment = 0;
kfree(ds->buf, M_UDFFID);
}
fid = (struct fileid_desc*)&ds->data[ds->off];
if (ds->off + UDF_FID_SIZE > ds->size ||
ds->off + fid->l_iu + fid->l_fi + UDF_FID_SIZE > ds->size) {
frag_size = ds->size - ds->off;
if (frag_size >= ds->udfmp->bsize) {
kprintf("udf: invalid FID fragment\n");
ds->error = EINVAL;
return(NULL);
}
ds->buf = kmalloc(ds->udfmp->bsize, M_UDFFID, M_WAITOK | M_ZERO);
bcopy(fid, ds->buf, frag_size);
fid = (struct fileid_desc*)ds->buf;
if (ds->bp != NULL)
brelse(ds->bp);
ds->offset += ds->size;
ds->size = 0;
error = udf_readatoffset(ds->node, &ds->size, ds->offset,
&ds->bp, &ds->data);
if (error) {
ds->error = error;
return(NULL);
}
if (frag_size < UDF_FID_SIZE)
bcopy(ds->data, &ds->buf[frag_size],
UDF_FID_SIZE - frag_size);
total_fid_size = UDF_FID_SIZE + fid->l_iu + fid->l_fi;
if (total_fid_size > ds->udfmp->bsize) {
kprintf("udf: invalid FID\n");
ds->error = EIO;
return(NULL);
}
bcopy(ds->data, &ds->buf[frag_size],
total_fid_size - frag_size);
ds->fid_fragment = 1;
} else
total_fid_size = fid->l_iu + fid->l_fi + UDF_FID_SIZE;
ds->this_off = ds->off;
if (!ds->fid_fragment)
ds->off += (total_fid_size + 3) & ~0x03;
else
ds->off = (total_fid_size - frag_size + 3) & ~0x03;
return(fid);
}
static void
udf_closedir(struct udf_dirstream *ds)
{
if (ds->bp != NULL)
brelse(ds->bp);
if (ds->fid_fragment && ds->buf != NULL)
kfree(ds->buf, M_UDFFID);
kfree(ds, M_UDFDS);
}
static int
udf_readdir(struct vop_readdir_args *a)
{
struct vnode *vp;
struct uio *uio;
struct udf_node *node;
struct udf_mnt *udfmp;
struct fileid_desc *fid;
struct udf_uiodir uiodir;
struct udf_dirstream *ds;
off_t *cookies = NULL;
int ncookies;
int error = 0;
char *name;
vp = a->a_vp;
error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_FAILRECLAIM);
if (error)
return (error);
uio = a->a_uio;
node = VTON(vp);
udfmp = node->udfmp;
uiodir.eofflag = 1;
if (a->a_ncookies != NULL) {
ncookies = uio->uio_resid / 8 + 1;
if (ncookies > 1024)
ncookies = 1024;
cookies = kmalloc(sizeof(off_t) * ncookies, M_TEMP, M_WAITOK);
uiodir.ncookies = ncookies;
uiodir.cookies = cookies;
uiodir.acookies = 0;
} else {
uiodir.cookies = NULL;
uiodir.ncookies = 0;
}
ds = udf_opendir(node, uio->uio_offset, node->fentry->inf_len,
node->udfmp);
name = kmalloc(NAME_MAX, M_TEMP, M_WAITOK);
while ((fid = udf_getfid(ds)) != NULL) {
if (udf_checktag(&fid->tag, TAGID_FID)) {
kprintf("Invalid FID tag\n");
error = EIO;
break;
}
if (fid->file_char & UDF_FILE_CHAR_DEL)
continue;
if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
if (uiodir.cookies != NULL) {
if (++uiodir.acookies > uiodir.ncookies) {
uiodir.eofflag = 0;
break;
}
*uiodir.cookies++ = 1;
}
if (vop_write_dirent(&error, uio, node->hash_id, DT_DIR,
1, ".")) {
uiodir.eofflag = 0;
break;
}
if (error) {
uiodir.eofflag = 0;
break;
}
if (uiodir.cookies != NULL) {
if (++uiodir.acookies > uiodir.ncookies) {
uiodir.eofflag = 0;
break;
}
*uiodir.cookies++ = 2;
}
if (vop_write_dirent(&error, uio, udf_getid(&fid->icb),
DT_DIR, 2, "..")) {
uiodir.eofflag = 0;
break;
}
if (error) {
uiodir.eofflag = 0;
break;
}
} else {
uint8_t d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ?
DT_DIR : DT_UNKNOWN;
uint16_t namelen = udf_transname(&fid->data[fid->l_iu],
name, fid->l_fi, udfmp);
if (uiodir.cookies != NULL) {
if (++uiodir.acookies > uiodir.ncookies) {
uiodir.eofflag = 0;
break;
}
*uiodir.cookies++ = ds->this_off;
}
if (vop_write_dirent(&error, uio, udf_getid(&fid->icb),
d_type, namelen, name)) {
uiodir.eofflag = 0;
break;
}
if (error) {
uiodir.eofflag = 0;
break;
}
}
if (error) {
kprintf("uiomove returned %d\n", error);
break;
}
}
kfree(name, M_TEMP);
*a->a_eofflag = uiodir.eofflag;
uio->uio_offset = ds->offset + ds->off;
if (!error)
error = ds->error;
udf_closedir(ds);
if (a->a_ncookies != NULL) {
if (error)
kfree(cookies, M_TEMP);
else {
*a->a_ncookies = uiodir.acookies;
*a->a_cookies = cookies;
}
}
vn_unlock(vp);
return(error);
}
static int
udf_readlink(struct vop_readlink_args *ap)
{
kprintf("%s called\n", __func__);
return(EOPNOTSUPP);
}
static int
udf_strategy(struct vop_strategy_args *ap)
{
struct bio *bio;
struct bio *nbio;
struct buf *bp;
struct vnode *vp;
struct udf_node *node;
int maxsize;
daddr_t dblkno;
bio = ap->a_bio;
bp = bio->bio_buf;
vp = ap->a_vp;
node = VTON(vp);
nbio = push_bio(bio);
if (nbio->bio_offset == NOOFFSET) {
if (udf_bmap_internal(node,
bio->bio_offset,
&dblkno, &maxsize)) {
clrbuf(bp);
nbio->bio_offset = NOOFFSET;
} else {
nbio->bio_offset = dbtob(dblkno);
}
}
if (nbio->bio_offset == NOOFFSET) {
biodone(bio);
return(0);
}
vn_strategy(node->i_devvp, nbio);
return(0);
}
static int
udf_bmap(struct vop_bmap_args *a)
{
struct udf_node *node;
uint32_t max_size;
daddr_t lsector;
int error;
node = VTON(a->a_vp);
if (a->a_doffsetp == NULL)
return(0);
KKASSERT(a->a_loffset % node->udfmp->bsize == 0);
error = udf_bmap_internal(node, a->a_loffset, &lsector, &max_size);
if (error)
return(error);
*a->a_doffsetp = (off_t)lsector << node->udfmp->bshift;
if (a->a_runp)
*a->a_runp = 0;
if (a->a_runb)
*a->a_runb = 0;
return(0);
}
static int
udf_lookup(struct vop_old_lookup_args *a)
{
struct vnode *dvp;
struct vnode *tdp = NULL;
struct vnode **vpp = a->a_vpp;
struct udf_node *node;
struct udf_mnt *udfmp;
struct fileid_desc *fid = NULL;
struct udf_dirstream *ds;
u_long nameiop;
u_long flags;
char *nameptr;
long namelen;
ino_t id = 0;
int offset, error = 0;
int numdirpasses, fsize;
dvp = a->a_dvp;
node = VTON(dvp);
udfmp = node->udfmp;
nameiop = a->a_cnp->cn_nameiop;
flags = a->a_cnp->cn_flags;
nameptr = a->a_cnp->cn_nameptr;
namelen = a->a_cnp->cn_namelen;
fsize = node->fentry->inf_len;
*vpp = NULL;
if (nameiop != NAMEI_LOOKUP || node->diroff == 0 ||
node->diroff > fsize) {
offset = 0;
numdirpasses = 1;
} else {
offset = node->diroff;
numdirpasses = 2;
}
lookloop:
ds = udf_opendir(node, offset, fsize, udfmp);
while ((fid = udf_getfid(ds)) != NULL) {
if (udf_checktag(&fid->tag, TAGID_FID)) {
kprintf("udf_lookup: Invalid tag\n");
error = EIO;
break;
}
if (fid->file_char & UDF_FILE_CHAR_DEL)
continue;
if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
if (flags & CNP_ISDOTDOT) {
id = udf_getid(&fid->icb);
break;
}
} else {
if (!(udf_cmpname(&fid->data[fid->l_iu],
nameptr, fid->l_fi, namelen, udfmp))) {
id = udf_getid(&fid->icb);
break;
}
}
}
if (!error)
error = ds->error;
if (error) {
udf_closedir(ds);
return (error);
}
if (id) {
error = udf_vget(udfmp->im_mountp, NULL, id, &tdp);
if (!error) {
if (nameiop == NAMEI_LOOKUP)
node->diroff = ds->offset + ds->off;
if ((flags & CNP_LOCKPARENT) == 0) {
a->a_cnp->cn_flags |= CNP_PDIRUNLOCK;
vn_unlock(dvp);
}
*vpp = tdp;
}
} else {
if (numdirpasses == 2) {
numdirpasses--;
offset = 0;
udf_closedir(ds);
goto lookloop;
}
if (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) {
error = EROFS;
} else {
error = ENOENT;
}
}
udf_closedir(ds);
return(error);
}
static int
udf_reclaim(struct vop_reclaim_args *a)
{
struct vnode *vp;
struct udf_node *unode;
vp = a->a_vp;
unode = VTON(vp);
if (unode != NULL) {
udf_hashrem(unode);
if (unode->i_devvp) {
vrele(unode->i_devvp);
unode->i_devvp = 0;
}
if (unode->fentry != NULL)
kfree(unode->fentry, M_UDFFENTRY);
kfree(unode, M_UDFNODE);
vp->v_data = NULL;
}
return(0);
}
static int
udf_readatoffset(struct udf_node *node, int *size, int offset, struct buf **bp,
uint8_t **data)
{
struct udf_mnt *udfmp;
struct file_entry *fentry = NULL;
struct buf *bp1;
uint32_t max_size;
daddr_t sector;
int error;
udfmp = node->udfmp;
*bp = NULL;
error = udf_bmap_internal(node, offset, §or, &max_size);
if (error == UDF_INVALID_BMAP) {
fentry = node->fentry;
*data = &fentry->data[fentry->l_ea];
*size = fentry->l_ad;
return(0);
} else if (error != 0) {
return(error);
}
if (*size == 0 || *size > max_size)
*size = max_size;
*size = min(*size, MAXBSIZE);
if ((error = udf_readlblks(udfmp, sector, *size, bp))) {
kprintf("warning: udf_readlblks returned error %d\n", error);
return(error);
}
bp1 = *bp;
*data = (uint8_t *)&bp1->b_data[offset % udfmp->bsize];
return(0);
}
static int
udf_bmap_internal(struct udf_node *node, uint32_t offset, daddr_t *sector, uint32_t *max_size)
{
struct udf_mnt *udfmp;
struct file_entry *fentry;
void *icb;
struct icb_tag *tag;
uint32_t icblen = 0;
daddr_t lsector;
int ad_offset, ad_num = 0;
int i, p_offset;
udfmp = node->udfmp;
fentry = node->fentry;
tag = &fentry->icbtag;
switch (tag->strat_type) {
case 4:
break;
case 4096:
kprintf("Cannot deal with strategy4096 yet!\n");
return(ENODEV);
default:
kprintf("Unknown strategy type %d\n", tag->strat_type);
return(ENODEV);
}
switch (tag->flags & 0x7) {
case 0:
do {
offset -= icblen;
ad_offset = sizeof(struct short_ad) * ad_num;
if (ad_offset > fentry->l_ad) {
kprintf("File offset out of bounds\n");
return(EINVAL);
}
icb = GETICB(long_ad, fentry, fentry->l_ea + ad_offset);
icblen = GETICBLEN(short_ad, icb);
ad_num++;
} while(offset >= icblen);
lsector = (offset >> udfmp->bshift) +
((struct short_ad *)(icb))->pos;
*max_size = GETICBLEN(short_ad, icb);
break;
case 1:
do {
offset -= icblen;
ad_offset = sizeof(struct long_ad) * ad_num;
if (ad_offset > fentry->l_ad) {
kprintf("File offset out of bounds\n");
return(EINVAL);
}
icb = GETICB(long_ad, fentry, fentry->l_ea + ad_offset);
icblen = GETICBLEN(long_ad, icb);
ad_num++;
} while(offset >= icblen);
lsector = (offset >> udfmp->bshift) +
((struct long_ad *)(icb))->loc.lb_num;
*max_size = GETICBLEN(long_ad, icb);
break;
case 3:
*max_size = 0;
*sector = node->hash_id + udfmp->part_start;
return(UDF_INVALID_BMAP);
case 2:
default:
kprintf("Unsupported allocation descriptor %d\n",
tag->flags & 0x7);
return(ENODEV);
}
*sector = lsector + udfmp->part_start;
if (udfmp->s_table != NULL) {
for (i = 0; i< udfmp->s_table_entries; i++) {
p_offset = lsector - udfmp->s_table->entries[i].org;
if ((p_offset < udfmp->p_sectors) && (p_offset >= 0)) {
*sector = udfmp->s_table->entries[i].map +
p_offset;
break;
}
}
}
return(0);
}