summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2002-08-15 00:43:43 +0000
committerscottl <scottl@FreeBSD.org>2002-08-15 00:43:43 +0000
commit826866dc000d408cd2808a21b199ab003baa37bd (patch)
treedc82fa8b219d28814bf4e880509335606f2f943b /sys/fs
parent4b64f84a432f94425359802d506816872f43c5d7 (diff)
downloadFreeBSD-src-826866dc000d408cd2808a21b199ab003baa37bd.zip
FreeBSD-src-826866dc000d408cd2808a21b199ab003baa37bd.tar.gz
Factor out some ugle code that's shared by udf_readdir and udf_lookup.
Significantly de-obfuscate udf_lookup Inspired By: tes@sgi.com
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/udf/udf.h16
-rw-r--r--sys/fs/udf/udf_vfsops.c12
-rw-r--r--sys/fs/udf/udf_vnops.c457
3 files changed, 239 insertions, 246 deletions
diff --git a/sys/fs/udf/udf.h b/sys/fs/udf/udf.h
index 060332a..654c8fd 100644
--- a/sys/fs/udf/udf.h
+++ b/sys/fs/udf/udf.h
@@ -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;
diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c
index adc8c0b..b23e30f 100644
--- a/sys/fs/udf/udf_vfsops.c
+++ b/sys/fs/udf/udf_vfsops.c
@@ -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);
}
diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c
index 9141d3b..6f364c2 100644
--- a/sys/fs/udf/udf_vnops.c
+++ b/sys/fs/udf/udf_vnops.c
@@ -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) {
@@ -579,108 +714,27 @@ udf_readdir(struct vop_readdir_args *a)
}
/*
- * 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 (!error)
+ error = ds->error;
- if (bp != NULL)
- brelse(bp);
+ 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);
-
- 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);
+ ds = udf_opendir(node, offset, fsize, udfmp);
- /*
- * 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;
- *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;
+ }
- /* 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);
- }
-
- /* Name wasn't found on this pass. Do another pass? */
- if (numdirpasses == 2) {
- numdirpasses--;
- offset = 0;
- goto lookloop;
+ if ((flags & ISLASTCN) &&
+ (nameiop == CREATE || nameiop == RENAME)) {
+ error = EROFS;
+ } else {
+ error = ENOENT;
+ }
}
- 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
OpenPOWER on IntegriCloud