diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h index 2d78758..7e64ac1 100644 --- a/sys/fs/udf/ecma167-udf.h +++ b/sys/fs/udf/ecma167-udf.h @@ -42,7 +42,8 @@ enum { TAGID_LOGVOL_INTEGRITY = 9, TAGID_FSD = 256, TAGID_FID = 257, - TAGID_FENTRY = 261 + TAGID_FENTRY = 261, + TAGID_EXTFENTRY = 266 }; /* Descriptor tag [3/7.2] */ @@ -354,6 +355,54 @@ struct file_entry { #define UDF_FENTRY_PERM_GRP_MASK 0xE0 #define UDF_FENTRY_PERM_OWNER_MASK 0x1C00 +/* Extended File Entry [4/14.17] */ +struct extfile_entry { + struct desc_tag tag; + struct icb_tag icbtag; + uint32_t uid; + uint32_t gid; + uint32_t perm; + uint16_t link_cnt; + uint8_t rec_format; + uint8_t rec_disp_attr; + uint32_t rec_len; + uint64_t inf_len; + uint64_t obj_size; + uint64_t logblks_rec; + struct timestamp atime; + struct timestamp mtime; + struct timestamp ctime; + struct timestamp attrtime; + uint32_t ckpoint; + uint32_t reserved1; + struct long_ad ex_attr_icb; + struct long_ad streamdir_icb; + struct regid imp_id; + uint64_t unique_id; + uint32_t l_ea; /* Length of extended attribute area */ + uint32_t l_ad; /* Length of allocation descriptors */ + uint8_t data[1]; +} __packed; +#define UDF_EXTFENTRY_SIZE 216 +/* Given a pointer to file_entry or extfile_entry object and a field name + * the following macro produces a field of a correctly typed structure + * based on tag id of the entry. + * This is not needed for fields up to and including inf_len, but is a must + * for all following fields. + * This macro, obviosly, can work only for fields that are present in both + * structure types. + */ +#define GET_FENTRY_FIELD(entry, field) \ + ((entry)->tag.id == TAGID_FENTRY ? \ + ((struct file_entry *)(entry))->field \ + : \ + ((struct extfile_entry *)(entry))->field) +#define GET_FENTRY_FIELD_PTR(entry, field) \ + ((entry)->tag.id == TAGID_FENTRY ? \ + &((struct file_entry *)(entry))->field \ + : \ + &((struct extfile_entry *)(entry))->field) + union dscrptr { struct desc_tag tag; struct anchor_vdp avdp; @@ -364,11 +413,12 @@ union dscrptr { struct fileset_desc fsd; struct fileid_desc fid; struct file_entry fe; + struct extfile_entry efe; }; /* Useful defines */ #define GETICB(ad_type, fentry, offset) \ - (struct ad_type *)&fentry->data[offset] + ((struct ad_type *)GET_FENTRY_FIELD_PTR(fentry, data[offset])) #define GETICBLEN(ad_type, icb) le32toh(((struct ad_type *)(icb))->len) diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c index 84d6776..4568031 100644 --- a/sys/fs/udf/udf_vfsops.c +++ b/sys/fs/udf/udf_vfsops.c @@ -308,7 +308,7 @@ udf_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) { struct part_desc *pd; struct logvol_desc *lvd; struct fileset_desc *fsd; - struct file_entry *root_fentry; + struct desc_tag *root_tag; uint32_t sector, size, mvds_start, mvds_end; uint32_t logical_secsize; uint32_t fsd_offset = 0; @@ -330,6 +330,11 @@ udf_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) { bo = &devvp->v_bufobj; + if (devvp->v_rdev->si_iosize_max != 0) + mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; + if (mp->mnt_iosize_max > MAXPHYS) + mp->mnt_iosize_max = MAXPHYS; + /* XXX: should be M_WAITOK */ MALLOC(udfmp, struct udf_mnt *, sizeof(struct udf_mnt), M_UDFMOUNT, M_NOWAIT | M_ZERO); @@ -475,8 +480,9 @@ udf_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) { goto bail; } - root_fentry = (struct file_entry *)bp->b_data; - if ((error = udf_checktag(&root_fentry->tag, TAGID_FENTRY))) { + root_tag = (struct desc_tag *)bp->b_data; + if ((error = udf_checktag(root_tag, TAGID_FENTRY)) + && (error = udf_checktag(root_tag, TAGID_EXTFENTRY))) { printf("Invalid root file entry!\n"); goto bail; } @@ -590,7 +596,7 @@ udf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) struct thread *td; struct vnode *vp; struct udf_node *unode; - struct file_entry *fe; + struct desc_tag *tag; int error, sector, size; error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL); @@ -637,18 +643,27 @@ udf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) return (error); } - fe = (struct file_entry *)bp->b_data; - if (udf_checktag(&fe->tag, TAGID_FENTRY)) { + tag = (struct desc_tag *)bp->b_data; + if (udf_checktag(tag, TAGID_FENTRY) == 0) { + struct file_entry *fe = (struct file_entry *)bp->b_data; + size = UDF_FENTRY_SIZE + le32toh(fe->l_ea) + le32toh(fe->l_ad); + } + else if (udf_checktag(tag, TAGID_EXTFENTRY) == 0) { + struct extfile_entry *efe = (struct extfile_entry *)bp->b_data; + size = UDF_EXTFENTRY_SIZE + le32toh(efe->l_ea) + le32toh(efe->l_ad); + } + else { printf("Invalid file entry!\n"); vgone(vp); vput(vp); brelse(bp); *vpp = NULL; - return (ENOMEM); + return (EINVAL); } - size = UDF_FENTRY_SIZE + le32toh(fe->l_ea) + le32toh(fe->l_ad); + MALLOC(unode->fentry, struct file_entry *, size, M_UDFFENTRY, M_NOWAIT | M_ZERO); + if (unode->fentry == NULL) { printf("Cannot allocate file entry block\n"); vgone(vp); diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c index b77e571..29b5a66 100644 --- a/sys/fs/udf/udf_vnops.c +++ b/sys/fs/udf/udf_vnops.c @@ -168,6 +168,21 @@ udf_open(struct vop_open_args *ap) { struct udf_node *np = VTON(ap->a_vp); off_t fsize; + /* + * We currently do not support any other strategy type, so + * udf_bmap_internal, udf_bmap, udf_strategy would fail. + * I.e. we can not access content of this file anyway. + */ + if (le16toh(np->fentry->icbtag.strat_type) != 4) { + printf("Unsupported strategy type %d\n", le16toh(np->fentry->icbtag.strat_type)); + return EINVAL; + } + + if (np->fentry->tag.id == TAGID_EXTFENTRY && + np->fentry->inf_len != ((struct extfile_entry *)np->fentry)->obj_size) { + printf("Files with multiple streams are not supported\n"); + return EINVAL; + } fsize = le64toh(np->fentry->inf_len); vnode_create_vobject(ap->a_vp, fsize, ap->a_td); return 0; @@ -206,9 +221,13 @@ udf_timetotimespec(struct timestamp *time, struct timespec *t) t->tv_nsec = 0; - /* DirectCD seems to like using bogus year values */ + /* + * DirectCD seems to like using bogus year values. + * Distrust time->month especially, since it will be used for an + * array index. + */ year = le16toh(time->year); - if (year < 1970) { + if (year < 1970 || time->month > 12) { t->tv_sec = 0; return; } @@ -217,21 +236,14 @@ udf_timetotimespec(struct timestamp *time, struct timespec *t) t->tv_sec = time->second; t->tv_sec += time->minute * 60; t->tv_sec += time->hour * 3600; - t->tv_sec += time->day * 3600 * 24; + t->tv_sec += (time->day - 1) * 3600 * 24; /* Calculate the month */ lpyear = udf_isaleapyear(year); for (i = 1; i < time->month; i++) - t->tv_sec += mon_lens[lpyear][i] * 3600 * 24; - - /* Speed up the calculation */ - if (year > 1979) - t->tv_sec += 315532800; - if (year > 1989) - t->tv_sec += 315619200; - if (year > 1999) - t->tv_sec += 315532800; - for (i = 2000; i < year; i++) { + t->tv_sec += mon_lens[lpyear][i - 1] * 3600 * 24; + + for (i = 1970; i < year; i++) { daysinyear = udf_isaleapyear(i) + 365 ; t->tv_sec += daysinyear * 3600 * 24; } @@ -276,9 +288,16 @@ udf_getattr(struct vop_getattr_args *a) */ vap->va_uid = (le32toh(fentry->uid) == -1) ? 0 : le32toh(fentry->uid); vap->va_gid = (le32toh(fentry->gid) == -1) ? 0 : le32toh(fentry->gid); - udf_timetotimespec(&fentry->atime, &vap->va_atime); - udf_timetotimespec(&fentry->mtime, &vap->va_mtime); - vap->va_ctime = vap->va_mtime; /* XXX Stored as an Extended Attribute */ + if (fentry->tag.id == TAGID_FENTRY) { + udf_timetotimespec(&fentry->atime, &vap->va_atime); + udf_timetotimespec(&fentry->mtime, &vap->va_mtime); + vap->va_ctime = vap->va_mtime; /* XXX Stored as an Extended Attribute */ + } + else { /*TAGID_EXTFENTRY*/ + udf_timetotimespec(&((struct extfile_entry *)fentry)->atime, &vap->va_atime); + udf_timetotimespec(&((struct extfile_entry *)fentry)->mtime, &vap->va_atime); + udf_timetotimespec(&((struct extfile_entry *)fentry)->ctime, &vap->va_ctime); + } vap->va_rdev = 0; /* XXX */ if (vp->v_type & VDIR) { /* @@ -287,9 +306,9 @@ udf_getattr(struct vop_getattr_args *a) * that directories consume at least one logical block, * make it appear so. */ - if (fentry->logblks_rec != 0) { + if (GET_FENTRY_FIELD(fentry, logblks_rec) != 0) { vap->va_size = - le64toh(fentry->logblks_rec) * node->udfmp->bsize; + GET_FENTRY_FIELD(fentry, logblks_rec) * node->udfmp->bsize; } else { vap->va_size = node->udfmp->bsize; } @@ -343,7 +362,9 @@ udf_pathconf(struct vop_pathconf_args *a) #define lblkno(udfmp, loc) ((loc) >> (udfmp)->bshift) #define blkoff(udfmp, loc) ((loc) & (udfmp)->bmask) -#define lblktosize(imp, blk) ((blk) << (udfmp)->bshift) +#define lblktosize(udfmp, blk) ((blk) << (udfmp)->bshift) + +#define HAS_EMBEDDED_DATA(node) ((le16toh((node)->fentry->icbtag.flags) & 0x7) == 3) static int udf_read(struct vop_read_args *ap) @@ -362,6 +383,32 @@ udf_read(struct vop_read_args *ap) return (0); if (uio->uio_offset < 0) return (EINVAL); + + if (HAS_EMBEDDED_DATA(node)) { + uint8_t *data; + + if (node->fentry->tag.id == TAGID_FENTRY) { + struct file_entry *fentry = node->fentry; + data = &fentry->data[le32toh(fentry->l_ea)]; + fsize = le32toh(fentry->l_ad); + } + else { + struct extfile_entry *fentry = (struct extfile_entry *)node->fentry; + data = &fentry->data[le32toh(fentry->l_ea)]; + fsize = le32toh(fentry->l_ad); + } + + n = uio->uio_resid; + diff = fsize - uio->uio_offset; + if (diff <= 0) + return (0); + if (diff < n) + n = diff; + + error = uiomove(data + uio->uio_offset, (int)n, uio); + return (error); + } + fsize = le64toh(node->fentry->inf_len); udfmp = node->udfmp; do { @@ -758,7 +805,8 @@ udf_readdir(struct vop_readdir_args *a) ds->this_off); } if (error) { - printf("uiomove returned %d\n", error); + if(error > 0) + printf("uiomove returned %d\n", error); break; } @@ -768,6 +816,8 @@ udf_readdir(struct vop_readdir_args *a) *a->a_eofflag = uiodir.eofflag; uio->uio_offset = ds->offset + ds->off; + if(error < 0) + error = 0; if (!error) error = ds->error; @@ -799,10 +849,10 @@ udf_strategy(struct vop_strategy_args *a) struct buf *bp; struct vnode *vp; struct udf_node *node; + struct bufobj *bo; int maxsize; daddr_t sector; - struct bufobj *bo; - int multiplier; + int error; bp = a->a_bp; vp = a->a_vp; @@ -813,24 +863,23 @@ udf_strategy(struct vop_strategy_args *a) * Files that are embedded in the fentry don't translate well * to a block number. Reject. */ - if (udf_bmap_internal(node, bp->b_lblkno * node->udfmp->bsize, - §or, &maxsize)) { + error = udf_bmap_internal(node, lblktosize(node->udfmp, bp->b_lblkno), §or, &maxsize); + if (error) { + if (error == UDF_INVALID_BMAP) + printf("udf_strategy called for file with data embedded in fentry\n"); clrbuf(bp); bp->b_blkno = -1; + bufdone(bp); + return (0); } - - /* bmap gives sector numbers, bio works with device blocks */ - multiplier = node->udfmp->bsize / DEV_BSIZE; - bp->b_blkno = sector * multiplier; - - } - if ((long)bp->b_blkno == -1) { - bufdone(bp); - return (0); + /* udf_bmap_internal gives sector numbers, bio works with device blocks */ + bp->b_blkno = sector << (node->udfmp->bshift - DEV_BSHIFT); } + bo = node->udfmp->im_bo; bp->b_iooffset = dbtob(bp->b_blkno); BO_STRATEGY(bo, bp); + return (0); } @@ -851,17 +900,43 @@ udf_bmap(struct vop_bmap_args *a) if (a->a_runb) *a->a_runb = 0; - error = udf_bmap_internal(node, a->a_bn * node->udfmp->bsize, &lsector, - &max_size); + /* + * UDF_INVALID_BMAP means data embedded into fentry, this is an internal + * error that should not be propagated to calling code. + * Most obvious mapping for this error is EOPNOTSUPP as we can not truly + * translate block numbers in this case. + * Incidentally, this return code will make vnode pager to use VOP_READ + * to get data for mmap-ed pages and udf_read knows how to do the right + * thing for this kind of files. + */ + error = udf_bmap_internal(node, a->a_bn << node->udfmp->bshift, &lsector, &max_size); + if (error == UDF_INVALID_BMAP) + return EOPNOTSUPP; if (error) return (error); /* Translate logical to physical sector number */ *a->a_bnp = lsector << (node->udfmp->bshift - DEV_BSHIFT); - /* Punt on read-ahead for now */ - if (a->a_runp) - *a->a_runp = 0; + /* + * Determine maximum number of readahead blocks following the + * requested block. + */ + if (a->a_runp) { + int nblk; + + nblk = (max_size >> node->udfmp->bshift) - 1; + if (nblk <= 0) + *a->a_runp = 0; + else if (nblk >= (MAXBSIZE >> node->udfmp->bshift)) + *a->a_runp = (MAXBSIZE >> node->udfmp->bshift) - 1; + else + *a->a_runp = nblk; + } + + if (a->a_runb) { + *a->a_runb = 0; + } return (0); } @@ -1052,7 +1127,6 @@ udf_readatoffset(struct udf_node *node, int *size, off_t 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; @@ -1060,16 +1134,26 @@ udf_readatoffset(struct udf_node *node, int *size, off_t offset, udfmp = node->udfmp; - *bp = NULL; + /* + * This call is made not only to detect UDF_INVALID_BMAP case, + * max_size is used as an ad-hoc read-ahead hint for "normal" case. + */ error = udf_bmap_internal(node, offset, §or, &max_size); if (error == UDF_INVALID_BMAP) { /* * This error means that the file *data* is stored in the * allocation descriptor field of the file entry. */ - fentry = node->fentry; - *data = &fentry->data[le32toh(fentry->l_ea)]; - *size = le32toh(fentry->l_ad); + if (node->fentry->tag.id == TAGID_FENTRY) { + struct file_entry *fentry = node->fentry; + *data = &fentry->data[le32toh(fentry->l_ea)]; + *size = le32toh(fentry->l_ad); + } + else { + struct extfile_entry *fentry = (struct extfile_entry *)node->fentry; + *data = &fentry->data[le32toh(fentry->l_ea)]; + *size = le32toh(fentry->l_ad); + } return (0); } else if (error != 0) { return (error); @@ -1078,9 +1162,10 @@ udf_readatoffset(struct udf_node *node, int *size, off_t offset, /* Adjust the size so that it is within range */ if (*size == 0 || *size > max_size) *size = max_size; - *size = min(*size, MAXBSIZE); + *size = min(*size, MAXBSIZE - blkoff(udfmp, offset)); - if ((error = udf_readlblks(udfmp, sector, *size + (offset & udfmp->bmask), bp))) { + *bp = NULL; + if ((error = bread(node->i_vnode, lblkno(udfmp, offset), (*size + blkoff(udfmp, offset) + udfmp->bmask) & ~udfmp->bmask, NOCRED, bp))) { printf("warning: udf_readlblks returned error %d\n", error); /* note: *bp may be non-NULL */ return (error); @@ -1137,12 +1222,12 @@ udf_bmap_internal(struct udf_node *node, off_t offset, daddr_t *sector, do { offset -= icblen; ad_offset = sizeof(struct short_ad) * ad_num; - if (ad_offset > le32toh(fentry->l_ad)) { + if (ad_offset > le32toh(GET_FENTRY_FIELD(fentry, l_ad))) { printf("File offset out of bounds\n"); return (EINVAL); } icb = GETICB(short_ad, fentry, - le32toh(fentry->l_ea) + ad_offset); + le32toh(GET_FENTRY_FIELD(fentry, l_ea)) + ad_offset); icblen = GETICBLEN(short_ad, icb); ad_num++; } while(offset >= icblen); @@ -1162,12 +1247,12 @@ udf_bmap_internal(struct udf_node *node, off_t offset, daddr_t *sector, do { offset -= icblen; ad_offset = sizeof(struct long_ad) * ad_num; - if (ad_offset > le32toh(fentry->l_ad)) { + if (ad_offset > le32toh(GET_FENTRY_FIELD(fentry, l_ad))) { printf("File offset out of bounds\n"); return (EINVAL); } icb = GETICB(long_ad, fentry, - le32toh(fentry->l_ea) + ad_offset); + le32toh(GET_FENTRY_FIELD(fentry, l_ea)) + ad_offset); icblen = GETICBLEN(long_ad, icb); ad_num++; } while(offset >= icblen);