Factor out some ugle code that's shared by udf_readdir and udf_lookup.

Significantly de-obfuscate udf_lookup

Inspired By: tes@sgi.com
This commit is contained in:
scottl 2002-08-15 00:43:43 +00:00
parent 4b64f84a43
commit 826866dc00
3 changed files with 239 additions and 246 deletions

View File

@ -57,6 +57,21 @@ struct udf_mnt {
struct udf_sparing_table *s_table;
};
struct udf_dirstream {
struct udf_node *node;
struct udf_mnt *udfmp;
struct buf *bp;
uint8_t *data;
uint8_t *buf;
int fsize;
int off;
int this_off;
int offset;
int size;
int error;
int fid_fragment;
};
#define VFSTOUDFFS(mp) ((struct udf_mnt *)((mp)->mnt_data))
#define VTON(vp) ((struct udf_node *)((vp)->v_data))
@ -113,3 +128,4 @@ int udf_vget(struct mount *, ino_t, int, struct vnode **);
extern uma_zone_t udf_zone_trans;
extern uma_zone_t udf_zone_node;
extern uma_zone_t udf_zone_ds;

View File

@ -100,6 +100,7 @@ MALLOC_DEFINE(M_UDFSTABLE, "UDF s_table", "UDF sparing table");
/* Zones */
uma_zone_t udf_zone_trans = NULL;
uma_zone_t udf_zone_node = NULL;
uma_zone_t udf_zone_ds = NULL;
static int udf_init(struct vfsconf *);
static int udf_uninit(struct vfsconf *);
@ -147,7 +148,11 @@ udf_init(struct vfsconf *foo)
udf_zone_node = uma_zcreate("UDF Node zone", sizeof(struct udf_node),
NULL, NULL, NULL, NULL, 0, 0);
if ((udf_zone_node == NULL) || (udf_zone_trans == NULL)) {
udf_zone_ds = uma_zcreate("UDF Dirstream zone",
sizeof(struct udf_dirstream), NULL, NULL, NULL, NULL, 0, 0);
if ((udf_zone_node == NULL) || (udf_zone_trans == NULL) ||
(udf_zone_ds == NULL)) {
printf("Cannot create allocation zones.\n");
return (ENOMEM);
}
@ -169,6 +174,11 @@ udf_uninit(struct vfsconf *foo)
udf_zone_node = NULL;
}
if (udf_zone_ds != NULL) {
uma_zdestroy(udf_zone_ds);
udf_zone_ds = NULL;
}
return (0);
}

View File

@ -91,6 +91,7 @@ static struct vnodeopv_desc udf_vnodeop_opv_desc =
VNODEOP_SET(udf_vnodeop_opv_desc);
MALLOC_DEFINE(M_UDFFID, "UDF FID", "UDF FileId structure");
MALLOC_DEFINE(M_UDFDS, "UDF DS", "UDF Dirstream structure");
#define INVALID_BMAP -1
@ -528,6 +529,146 @@ udf_uiodir(struct udf_uiodir *uiodir, int de_size, struct uio *uio, long cookie)
return (uiomove((caddr_t)uiodir->dirent, de_size, uio));
}
static struct udf_dirstream *
udf_opendir(struct udf_node *node, int offset, int fsize, struct udf_mnt *udfmp)
{
struct udf_dirstream *ds;
ds = uma_zalloc(udf_zone_ds, 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;
/* End of directory? */
if (ds->offset + ds->off >= ds->fsize) {
ds->error = 0;
return (NULL);
}
/* Grab the first extent of the directory */
if (ds->off == 0) {
ds->size = 0;
error = udf_readatoffset(ds->node, &ds->size, ds->offset,
&ds->bp, &ds->data);
if (error) {
ds->error = error;
return (NULL);
}
}
/* XXX Is this the right place for this? */
if (ds->fid_fragment && ds->buf != NULL) {
ds->fid_fragment = 0;
FREE(ds->buf, M_UDFFID);
}
fid = (struct fileid_desc*)&ds->data[ds->off];
/*
* Check to see if the fid is fragmented. The first test
* ensures that we don't wander off the end of the buffer
* looking for the l_iu and l_fi fields.
*/
if (ds->off + UDF_FID_SIZE > ds->size ||
ds->off + fid->l_iu + fid->l_fi + UDF_FID_SIZE > ds->size) {
/* Copy what we have of the fid into a buffer */
frag_size = ds->size - ds->off;
if (frag_size >= ds->udfmp->bsize) {
printf("udf: invalid FID fragment\n");
ds->error = EINVAL;
return (NULL);
}
/*
* File ID descriptors can only be at most one
* logical sector in size.
*/
MALLOC(ds->buf, uint8_t*, ds->udfmp->bsize, M_UDFFID,
M_WAITOK | M_ZERO);
bcopy(fid, ds->buf, frag_size);
/* Reduce all of the casting magic */
fid = (struct fileid_desc*)ds->buf;
if (ds->bp != NULL)
brelse(ds->bp);
/* Fetch the next allocation */
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 the fragment was so small that we didn't get
* the l_iu and l_fi fields, copy those in.
*/
if (frag_size < UDF_FID_SIZE)
bcopy(ds->data, &ds->buf[frag_size],
UDF_FID_SIZE - frag_size);
/*
* Now that we have enough of the fid to work with,
* copy in the rest of the fid from the new
* allocation.
*/
total_fid_size = UDF_FID_SIZE + fid->l_iu + fid->l_fi;
if (total_fid_size > ds->udfmp->bsize) {
printf("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;
}
/*
* Update the offset. Align on a 4 byte boundary because the
* UDF spec says so. If it was a fragmented entry, clean up.
*/
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)
FREE(ds->buf, M_UDFFID);
uma_zfree(udf_zone_ds, ds);
}
/* Prebuild the . and .. dirents. d_fileno will need to be filled in */
static struct dirent udf_de_dot =
{ 0, sizeof(struct dirent), DT_DIR, 1, "." };
@ -538,26 +679,20 @@ static int
udf_readdir(struct vop_readdir_args *a)
{
struct vnode *vp;
struct buf *bp;
struct uio *uio;
struct dirent dir;
struct udf_node *node;
struct udf_mnt *udfmp;
struct fileid_desc *fid;
struct udf_uiodir uiodir;
struct udf_dirstream *ds;
u_long *cookies = NULL;
uint8_t *data, *buf;
int ncookies;
int error = 0, offset, off, size, de_size, fid_size, fsize;
int total_fid_size = 0, frag_size = 0, fid_fragment = 0;
int error = 0, de_size;
vp = a->a_vp;
uio = a->a_uio;
node = VTON(vp);
udfmp = node->udfmp;
de_size = sizeof(struct dirent);
fid_size = UDF_FID_SIZE;
fsize = node->fentry->inf_len;
uiodir.eofflag = 1;
if (a->a_ncookies != NULL) {
@ -578,109 +713,28 @@ udf_readdir(struct vop_readdir_args *a)
uiodir.cookies = NULL;
}
/*
* offset is the absolute offset into the file data. off is the offset
* into the data, minus the blocks that weren't read because they fell
* before offset.
*/
offset = uio->uio_offset;
off = 0;
/*
* Iterate through the file id descriptors. Give the parent dir
* entry special attention. size will be the size of the extent
* returned in data. If there is more than one extent, things get
* ugly.
*/
size = 0;
error = udf_readatoffset(node, &size, offset, &bp, &data);
if (error) {
if (a->a_ncookies != NULL)
FREE(cookies, M_TEMP);
return (error);
}
ds = udf_opendir(node, uio->uio_offset, node->fentry->inf_len,
node->udfmp);
while (offset + off < fsize) {
fid = (struct fileid_desc*)&data[off];
/*
* Check to see if the fid is fragmented. The first test
* ensures that we don't wander off the end of the buffer
* looking for the l_iu and l_fi fields.
*/
if (off + fid_size > size ||
off + fid->l_iu + fid->l_fi + fid_size > size) {
/* Copy what we have of the fid into a buffer */
frag_size = size - off;
if (frag_size >= udfmp->bsize) {
printf("udf: invalid FID fragment\n");
break;
}
/*
* File ID descriptors can only be at most one
* logical sector in size.
*/
MALLOC(buf, uint8_t*, udfmp->bsize, M_UDFFID,
M_WAITOK | M_ZERO);
bcopy(fid, buf, frag_size);
/* Reduce all of the casting magic */
fid = (struct fileid_desc*)buf;
if (bp != NULL)
brelse(bp);
/* Fetch the next allocation */
offset += size;
size = 0;
error = udf_readatoffset(node, &size, offset, &bp,
&data);
if (error)
break;
/*
* If the fragment was so small that we didn't get
* the l_iu and l_fi fields, copy those in.
*/
if (fid_size > frag_size)
bcopy(data, &buf[frag_size],
fid_size - frag_size);
/*
* Now that we have enough of the fid to work with,
* copy in the rest of the fid from the new
* allocation.
*/
total_fid_size = fid_size + fid->l_iu + fid->l_fi;
if (total_fid_size > udfmp->bsize) {
printf("udf: invalid FID\n");
break;
}
bcopy(data, &buf[frag_size],
total_fid_size - frag_size);
fid_fragment = 1;
} else {
total_fid_size = fid->l_iu + fid->l_fi + fid_size;
}
while ((fid = udf_getfid(ds)) != NULL) {
/* XXX Should we return an error on a bad fid? */
if (udf_checktag(&fid->tag, TAGID_FID)) {
printf("Invalid FID tag\n");
udf_dumpblock(fid, UDF_FID_SIZE);
error = EIO;
break;
}
/* Is this a deleted file? */
if (fid->file_char & UDF_FILE_CHAR_DEL)
goto update_offset;
if (fid->l_iu != 0) {
printf("Possibly invalid fid found.\n");
goto update_offset;
}
continue;
if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
/* Do up the '.' and '..' entries. Dummy values are
@ -705,36 +759,28 @@ udf_readdir(struct vop_readdir_args *a)
DT_DIR : DT_UNKNOWN;
dir.d_reclen = GENERIC_DIRSIZ(&dir);
uiodir.dirent = &dir;
error = udf_uiodir(&uiodir, dir.d_reclen, uio, off);
error = udf_uiodir(&uiodir, dir.d_reclen, uio,
ds->this_off);
}
if (error) {
printf("uiomove returned %d\n", error);
break;
}
update_offset: /*
* Update the offset. Align on a 4 byte boundary because the
* UDF spec says so. If it was a fragmented entry, clean up.
*/
if (fid_fragment) {
off = (total_fid_size - frag_size + 3) & ~0x03;
FREE(fid, M_UDFFID);
fid_fragment = 0;
} else {
off += (total_fid_size + 3) & ~0x03;
}
}
/* tell the calling layer whether we need to be called again */
*a->a_eofflag = uiodir.eofflag;
uio->uio_offset = offset + off;
uio->uio_offset = ds->offset + ds->off;
if (bp != NULL)
brelse(bp);
if (!error)
error = ds->error;
udf_closedir(ds);
if (a->a_ncookies != NULL) {
if (error)
free(cookies, M_TEMP);
FREE(cookies, M_TEMP);
else {
*a->a_ncookies = uiodir.acookies;
*a->a_cookies = cookies;
@ -834,20 +880,18 @@ udf_lookup(struct vop_cachedlookup_args *a)
struct vnode *dvp;
struct vnode *tdp = NULL;
struct vnode **vpp = a->a_vpp;
struct buf *bp = NULL;
struct udf_node *node;
struct udf_mnt *udfmp;
struct fileid_desc *fid = NULL;
struct udf_dirstream *ds;
struct thread *td;
u_long nameiop;
u_long flags;
char *nameptr;
long namelen;
ino_t id = 0;
uint8_t *data, *buf;
int offset, off, error, size;
int numdirpasses, fid_size, fsize, icb_len;
int total_fid_size = 0, fid_fragment = 0, frag_size = 0;
int offset, error = 0;
int numdirpasses, fsize;
dvp = a->a_dvp;
node = VTON(dvp);
@ -856,9 +900,7 @@ udf_lookup(struct vop_cachedlookup_args *a)
flags = a->a_cnp->cn_flags;
nameptr = a->a_cnp->cn_nameptr;
namelen = a->a_cnp->cn_namelen;
fid_size = UDF_FID_SIZE;
fsize = node->fentry->inf_len;
icb_len = sizeof(struct long_ad);
td = a->a_cnp->cn_thread;
/*
@ -867,7 +909,7 @@ udf_lookup(struct vop_cachedlookup_args *a)
* directory may need to be searched twice. For a full description,
* see /sys/isofs/cd9660/cd9660_lookup.c:cd9660_lookup()
*/
if (nameiop != LOOKUP || node->diroff == 0 || node->diroff > size) {
if (nameiop != LOOKUP || node->diroff == 0 || node->diroff > fsize) {
offset = 0;
numdirpasses = 1;
} else {
@ -881,91 +923,20 @@ udf_lookup(struct vop_cachedlookup_args *a)
* Can this be broken out and shared?
*/
lookloop:
size = 0;
off = 0;
error = udf_readatoffset(node, &size, offset, &bp, &data);
if (error)
return (error);
ds = udf_opendir(node, offset, fsize, udfmp);
while (offset + off < fsize) {
fid = (struct fileid_desc*)&data[off];
/*
* Check to see if the fid is fragmented. The first test
* ensures that we don't wander off the end of the buffer
* looking for the l_iu and l_fi fields.
*/
if (off + fid_size > size ||
off + fid_size + fid->l_iu + fid->l_fi > size) {
frag_size = size - off;
if (frag_size >= udfmp->bsize) {
printf("udf: invalid FID fragment\n");
break;
}
/*
* File ID descriptors can only be at most one
* logical sector in size.
* Copy what we have of the fid into a buffer
*/
MALLOC(buf, uint8_t*, udfmp->bsize, M_UDFFID,
M_WAITOK | M_ZERO);
bcopy(fid, buf, frag_size);
/* Reduce all of the casting magic */
fid = (struct fileid_desc*)buf;
if (bp != NULL)
brelse(bp);
/* Fetch the next allocation */
offset += size;
size = 0;
error = udf_readatoffset(node, &size, offset, &bp,
&data);
if (error)
return (error);
/*
* If the fragment was so small that we didn't get
* the l_iu and l_fi fields, copy those in.
*/
if (fid_size > frag_size)
bcopy(data, &buf[frag_size],
fid_size - frag_size);
/*
* Now that we have enough of the fid to work with,
* copy the rest of the fid from the new
* allocation.
*/
total_fid_size = fid_size + fid->l_iu + fid->l_fi;
if (total_fid_size > udfmp->bsize) {
printf("udf: invalid FID\n");
break;
}
bcopy(data, &buf[frag_size],
total_fid_size - frag_size);
off = (total_fid_size - frag_size + 3) & ~0x03;
fid_fragment = 1;
} else {
/*
* Update the offset here to avoid looking at this fid
* again on a subsequent lookup.
*/
total_fid_size = fid->l_iu + fid->l_fi + fid_size;
off += (total_fid_size + 3) & ~0x03;
}
while ((fid = udf_getfid(ds)) != NULL) {
/* XXX Should we return an error on a bad fid? */
if (udf_checktag(&fid->tag, TAGID_FID))
goto continue_lookup;
if (udf_checktag(&fid->tag, TAGID_FID)) {
printf("udf_lookup: Invalid tag\n");
error = EIO;
break;
}
/* Is this a deleted file? */
if (fid->file_char & UDF_FILE_CHAR_DEL)
goto continue_lookup;
continue;
if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
if (flags & ISDOTDOT) {
@ -979,67 +950,63 @@ lookloop:
break;
}
}
}
/*
* If we got this far then this fid isn't what we were
* looking for. It's therefore safe to clean up from a
* fragmented fid.
*/
continue_lookup:
if (fid_fragment) {
FREE(fid, M_UDFFID);
fid_fragment = 0;
}
if (!error)
error = ds->error;
/* XXX Bail out here? */
if (error) {
udf_closedir(ds);
return (error);
}
/* Did we have a match? */
if (id) {
error = udf_vget(udfmp->im_mountp, id, LK_EXCLUSIVE, &tdp);
if (bp != NULL)
brelse(bp);
if (error)
return (error);
if (!error) {
/*
* Remember where this entry was if it's the final
* component.
*/
if ((flags & ISLASTCN) && nameiop == LOOKUP)
node->diroff = ds->offset + ds->off;
if (numdirpasses == 2)
nchstats.ncs_pass2++;
if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
a->a_cnp->cn_flags |= PDIRUNLOCK;
VOP_UNLOCK(dvp, 0, td);
}
/* Remember where this entry was if it's the final component */
if ((flags & ISLASTCN) && nameiop == LOOKUP)
node->diroff = offset + off;
if (numdirpasses == 2)
nchstats.ncs_pass2++;
if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
a->a_cnp->cn_flags |= PDIRUNLOCK;
VOP_UNLOCK(dvp, 0, td);
*vpp = tdp;
/* Put this entry in the cache */
if (flags & MAKEENTRY)
cache_enter(dvp, *vpp, a->a_cnp);
}
} else {
/* Name wasn't found on this pass. Do another pass? */
if (numdirpasses == 2) {
numdirpasses--;
offset = 0;
udf_closedir(ds);
goto lookloop;
}
*vpp = tdp;
/* Put this entry in the cache */
/* Enter name into cache as non-existant */
if (flags & MAKEENTRY)
cache_enter(dvp, *vpp, a->a_cnp);
if (fid_fragment)
FREE(fid, M_UDFFID);
return (0);
if ((flags & ISLASTCN) &&
(nameiop == CREATE || nameiop == RENAME)) {
error = EROFS;
} else {
error = ENOENT;
}
}
/* Name wasn't found on this pass. Do another pass? */
if (numdirpasses == 2) {
numdirpasses--;
offset = 0;
goto lookloop;
}
if (bp != NULL)
brelse(bp);
/* Enter name into cache as non-existant */
if (flags & MAKEENTRY)
cache_enter(dvp, *vpp, a->a_cnp);
if ((flags & ISLASTCN) && (nameiop == CREATE || nameiop == RENAME))
return (EROFS);
return (ENOENT);
udf_closedir(ds);
return (error);
}
static int