/* $OpenBSD: udf_vfsops.c,v 1.28 2009/06/05 04:35:25 krw Exp $ */ /* * Copyright (c) 2001, 2002 Scott Long * 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/udf/udf_vfsops.c,v 1.25 2005/01/25 15:52:03 phk Exp $ */ /* * Ported to OpenBSD by Pedro Martelletto in February 2005. */ /* * Ok, here's how it goes. The UDF specs are pretty clear on how each data * structure is made up, but not very clear on how they relate to each other. * Here is the skinny... This demostrates a filesystem with one file in the * root directory. Subdirectories are treated just as normal files, but they * have File Id Descriptors of their children as their file data. As for the * Anchor Volume Descriptor Pointer, it can exist in two of the following three * places: sector 256, sector n (the max sector of the disk), or sector * n - 256. It's a pretty good bet that one will exist at sector 256 though. * One caveat is unclosed CD media. For that, sector 256 cannot be written, * so the Anchor Volume Descriptor Pointer can exist at sector 512 until the * media is closed. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct pool udf_trans_pool; struct pool unode_pool; struct pool udf_ds_pool; int udf_find_partmaps(struct umount *, struct logvol_desc *); int udf_get_vpartmap(struct umount *, struct part_map_virt *); int udf_get_spartmap(struct umount *, struct part_map_spare *); int udf_mountfs(struct vnode *, struct mount *, uint32_t, struct proc *); const struct vfsops udf_vfsops = { .vfs_fhtovp = udf_fhtovp, .vfs_init = udf_init, .vfs_mount = udf_mount, .vfs_start = udf_start, .vfs_root = udf_root, .vfs_quotactl = udf_quotactl, .vfs_statfs = udf_statfs, .vfs_sync = udf_sync, .vfs_unmount = udf_unmount, .vfs_vget = udf_vget, .vfs_vptofh = udf_vptofh, .vfs_sysctl = udf_sysctl, .vfs_checkexp = udf_checkexp, }; int udf_init(struct vfsconf *foo) { pool_init(&udf_trans_pool, MAXNAMLEN * sizeof(unicode_t), 0, 0, 0, "udftrpl", &pool_allocator_nointr); pool_init(&unode_pool, sizeof(struct unode), 0, 0, 0, "udfndpl", &pool_allocator_nointr); pool_init(&udf_ds_pool, sizeof(struct udf_dirstream), 0, 0, 0, "udfdspl", &pool_allocator_nointr); return (0); } int udf_start(struct mount *mp, int flags, struct proc *p) { return (0); } int udf_mount(struct mount *mp, const char *path, void *data, struct nameidata *ndp, struct proc *p) { struct vnode *devvp; /* vnode of the mount device */ struct udf_args args; size_t len; int error; if ((mp->mnt_flag & MNT_RDONLY) == 0) { mp->mnt_flag |= MNT_RDONLY; printf("udf_mount: enforcing read-only mode\n"); } /* * No root filesystem support. Probably not a big deal, since the * bootloader doesn't understand UDF. */ if (mp->mnt_flag & MNT_ROOTFS) return (EOPNOTSUPP); error = copyin(data, &args, sizeof(struct udf_args)); if (error) return (error); if (args.fspec == NULL) return (EINVAL); NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if ((error = namei(ndp))) return (error); devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return (ENOTBLK); } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return (ENXIO); } /* Check the access rights on the mount device */ if (p->p_ucred->cr_uid) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_ACCESS(devvp, VREAD, p->p_ucred, p); VOP_UNLOCK(devvp, 0, p); if (error) { vrele(devvp); return (error); } } if ((error = udf_mountfs(devvp, mp, args.lastblock, p))) { vrele(devvp); return (error); } /* * Keep a copy of the mount information. */ copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &len); bzero(mp->mnt_stat.f_mntonname + len, MNAMELEN - len); copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &len); bzero(mp->mnt_stat.f_mntfromname + len, MNAMELEN - len); return (0); }; /* * Check the descriptor tag for both the correct id and correct checksum. * Return zero if all is good, EINVAL if not. */ int udf_checktag(struct desc_tag *tag, uint16_t id) { uint8_t *itag; uint8_t i, cksum = 0; itag = (uint8_t *)tag; if (letoh16(tag->id) != id) return (EINVAL); for (i = 0; i < 15; i++) cksum = cksum + itag[i]; cksum = cksum - itag[4]; if (cksum == tag->cksum) return (0); return (EINVAL); } int udf_mountfs(struct vnode *devvp, struct mount *mp, uint32_t lb, struct proc *p) { struct buf *bp = NULL; struct anchor_vdp avdp; struct umount *ump = NULL; struct part_desc *pd; struct logvol_desc *lvd; struct fileset_desc *fsd; struct file_entry *root_fentry; uint32_t sector, size, mvds_start, mvds_end; uint32_t fsd_offset = 0; uint16_t part_num = 0, fsd_part = 0; int error = EINVAL; int logvol_found = 0, part_found = 0, fsd_found = 0; int bsize; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if ((error = vfs_mountedon(devvp))) return (error); if (vcount(devvp) > 1 && devvp != rootvp) return (EBUSY); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); VOP_UNLOCK(devvp, 0, p); if (error) return (error); error = VOP_OPEN(devvp, FREAD, FSCRED, p); if (error) return (error); ump = malloc(sizeof(*ump), M_UDFMOUNT, M_WAITOK | M_ZERO); mp->mnt_data = (qaddr_t) ump; mp->mnt_stat.f_fsid.val[0] = devvp->v_rdev; mp->mnt_stat.f_fsid.val[1] = makefstype(MOUNT_UDF); mp->mnt_flag |= MNT_LOCAL; ump->um_mountp = mp; ump->um_dev = devvp->v_rdev; ump->um_devvp = devvp; bsize = 2048; /* Should probe the media for its size. */ /* * Get the Anchor Volume Descriptor Pointer from sector 256. * Should also check sector n - 256, n, and 512. */ sector = 256; if ((error = bread(devvp, sector * btodb(bsize), bsize, NOCRED, &bp)) != 0) goto bail; if ((error = udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR))) goto bail; bcopy(bp->b_data, &avdp, sizeof(struct anchor_vdp)); brelse(bp); bp = NULL; /* * Extract the Partition Descriptor and Logical Volume Descriptor * from the Volume Descriptor Sequence. * Should we care about the partition type right now? * What about multiple partitions? */ mvds_start = letoh32(avdp.main_vds_ex.loc); mvds_end = mvds_start + (letoh32(avdp.main_vds_ex.len) - 1) / bsize; for (sector = mvds_start; sector < mvds_end; sector++) { if ((error = bread(devvp, sector * btodb(bsize), bsize, NOCRED, &bp)) != 0) { printf("Can't read sector %d of VDS\n", sector); goto bail; } lvd = (struct logvol_desc *)bp->b_data; if (!udf_checktag(&lvd->tag, TAGID_LOGVOL)) { ump->um_bsize = letoh32(lvd->lb_size); ump->um_bmask = ump->um_bsize - 1; ump->um_bshift = ffs(ump->um_bsize) - 1; fsd_part = letoh16(lvd->_lvd_use.fsd_loc.loc.part_num); fsd_offset = letoh32(lvd->_lvd_use.fsd_loc.loc.lb_num); if (udf_find_partmaps(ump, lvd)) break; logvol_found = 1; } pd = (struct part_desc *)bp->b_data; if (!udf_checktag(&pd->tag, TAGID_PARTITION)) { part_found = 1; part_num = letoh16(pd->part_num); ump->um_len = letoh32(pd->part_len); ump->um_start = letoh32(pd->start_loc); } brelse(bp); bp = NULL; if ((part_found) && (logvol_found)) break; } if (!part_found || !logvol_found) { error = EINVAL; goto bail; } if (fsd_part != part_num) { printf("FSD does not lie within the partition!\n"); error = EINVAL; goto bail; } mtx_init(&ump->um_hashmtx, IPL_NONE); ump->um_hashtbl = hashinit(UDF_HASHTBLSIZE, M_UDFMOUNT, M_WAITOK, &ump->um_hashsz); /* Get the VAT, if needed */ if (ump->um_flags & UDF_MNT_FIND_VAT) { error = udf_vat_get(ump, lb); if (error) goto bail; } /* * Grab the Fileset Descriptor * Thanks to Chuck McCrobie for pointing * me in the right direction here. */ sector = fsd_offset; udf_vat_map(ump, §or); if ((error = RDSECTOR(devvp, sector, ump->um_bsize, &bp)) != 0) { printf("Cannot read sector %d of FSD\n", sector); goto bail; } fsd = (struct fileset_desc *)bp->b_data; if (!udf_checktag(&fsd->tag, TAGID_FSD)) { fsd_found = 1; bcopy(&fsd->rootdir_icb, &ump->um_root_icb, sizeof(struct long_ad)); } brelse(bp); bp = NULL; if (!fsd_found) { printf("Couldn't find the fsd\n"); error = EINVAL; goto bail; } /* * Find the file entry for the root directory. */ sector = letoh32(ump->um_root_icb.loc.lb_num); size = letoh32(ump->um_root_icb.len); udf_vat_map(ump, §or); if ((error = udf_readlblks(ump, sector, size, &bp)) != 0) { printf("Cannot read sector %d\n", sector); goto bail; } root_fentry = (struct file_entry *)bp->b_data; if ((error = udf_checktag(&root_fentry->tag, TAGID_FENTRY))) { printf("Invalid root file entry!\n"); goto bail; } brelse(bp); bp = NULL; devvp->v_specmountpoint = mp; return (0); bail: if (ump->um_hashtbl != NULL) free(ump->um_hashtbl, M_UDFMOUNT); if (ump != NULL) { free(ump, M_UDFMOUNT); mp->mnt_data = NULL; mp->mnt_flag &= ~MNT_LOCAL; } if (bp != NULL) brelse(bp); VOP_CLOSE(devvp, FREAD, FSCRED, p); return (error); } int udf_unmount(struct mount *mp, int mntflags, struct proc *p) { struct umount *ump; struct vnode *devvp; int error, flags = 0; ump = VFSTOUDFFS(mp); devvp = ump->um_devvp; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; if ((error = vflush(mp, NULL, flags))) return (error); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); vinvalbuf(devvp, V_SAVE, NOCRED, p, 0, 0); error = VOP_CLOSE(devvp, FREAD, NOCRED, p); VOP_UNLOCK(devvp, 0, p); if (error) return (error); devvp->v_specmountpoint = NULL; vrele(devvp); if (ump->um_flags & UDF_MNT_USES_VAT) free(ump->um_vat, M_UDFMOUNT); if (ump->um_stbl != NULL) free(ump->um_stbl, M_UDFMOUNT); if (ump->um_hashtbl != NULL) free(ump->um_hashtbl, M_UDFMOUNT); free(ump, M_UDFMOUNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (0); } int udf_root(struct mount *mp, struct vnode **vpp) { struct umount *ump; struct vnode *vp; ino_t id; int error; ump = VFSTOUDFFS(mp); id = udf_getid(&ump->um_root_icb); error = udf_vget(mp, id, vpp); if (error) return (error); vp = *vpp; vp->v_flag |= VROOT; return (0); } int udf_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct proc *p) { return (EOPNOTSUPP); } int udf_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) { struct umount *ump; ump = VFSTOUDFFS(mp); sbp->f_bsize = ump->um_bsize; sbp->f_iosize = ump->um_bsize; sbp->f_blocks = ump->um_len; sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 0; sbp->f_ffree = 0; return (0); } int udf_sync(struct mount *mp, int waitfor, struct ucred *cred, struct proc *p) { return (0); } int udf_vget(struct mount *mp, ino_t ino, struct vnode **vpp) { struct buf *bp; struct vnode *devvp; struct umount *ump; struct proc *p; struct vnode *vp; struct unode *up; struct file_entry *fe; int error, sector, size; p = curproc; bp = NULL; *vpp = NULL; ump = VFSTOUDFFS(mp); /* See if we already have this in the cache */ if ((error = udf_hashlookup(ump, ino, LK_EXCLUSIVE, vpp)) != 0) return (error); if (*vpp != NULL) return (0); /* * Allocate memory and check the tag id's before grabbing a new * vnode, since it's hard to roll back if there is a problem. */ up = pool_get(&unode_pool, PR_WAITOK | PR_ZERO); /* * Copy in the file entry. Per the spec, the size can only be 1 block. */ sector = ino; devvp = ump->um_devvp; udf_vat_map(ump, §or); if ((error = RDSECTOR(devvp, sector, ump->um_bsize, &bp)) != 0) { printf("Cannot read sector %d\n", sector); pool_put(&unode_pool, up); if (bp != NULL) brelse(bp); return (error); } fe = (struct file_entry *)bp->b_data; if (udf_checktag(&fe->tag, TAGID_FENTRY)) { printf("Invalid file entry!\n"); pool_put(&unode_pool, up); brelse(bp); return (ENOMEM); } size = UDF_FENTRY_SIZE + letoh32(fe->l_ea) + letoh32(fe->l_ad); up->u_fentry = malloc(size, M_UDFFENTRY, M_NOWAIT); if (up->u_fentry == NULL) { pool_put(&unode_pool, up); brelse(bp); return (ENOMEM); /* Cannot allocate file entry block */ } bcopy(bp->b_data, up->u_fentry, size); brelse(bp); bp = NULL; if ((error = udf_allocv(mp, &vp, p))) { free(up->u_fentry, M_UDFFENTRY); pool_put(&unode_pool, up); return (error); /* Error from udf_allocv() */ } up->u_vnode = vp; up->u_ino = ino; up->u_devvp = ump->um_devvp; up->u_dev = ump->um_dev; up->u_ump = ump; vp->v_data = up; VREF(ump->um_devvp); lockinit(&up->u_lock, PINOD, "unode", 0, 0); /* * udf_hashins() will lock the vnode for us. */ udf_hashins(up); switch (up->u_fentry->icbtag.file_type) { default: vp->v_type = VBAD; break; case UDF_ICB_FILETYPE_DIRECTORY: vp->v_type = VDIR; break; case UDF_ICB_FILETYPE_RANDOMACCESS: vp->v_type = VREG; break; case UDF_ICB_FILETYPE_BLOCKDEVICE: vp->v_type = VBLK; break; case UDF_ICB_FILETYPE_CHARDEVICE: vp->v_type = VCHR; break; case UDF_ICB_FILETYPE_FIFO: vp->v_type = VFIFO; break; case UDF_ICB_FILETYPE_SOCKET: vp->v_type = VSOCK; break; case UDF_ICB_FILETYPE_SYMLINK: vp->v_type = VLNK; break; case UDF_ICB_FILETYPE_UNKNOWN: vp->v_type = VREG; break; } *vpp = vp; return (0); } struct ifid { u_short ifid_len; u_short ifid_pad; int ifid_ino; long ifid_start; }; int udf_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) { struct ifid *ifhp; struct vnode *nvp; int error; ifhp = (struct ifid *)fhp; if ((error = VFS_VGET(mp, ifhp->ifid_ino, &nvp)) != 0) { *vpp = NULLVP; return (error); } *vpp = nvp; return (0); } int udf_vptofh(struct vnode *vp, struct fid *fhp) { struct unode *up; struct ifid *ifhp; up = VTOU(vp); ifhp = (struct ifid *)fhp; ifhp->ifid_len = sizeof(struct ifid); ifhp->ifid_ino = up->u_ino; return (0); } int udf_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen, struct proc *p) { return (EINVAL); } int udf_checkexp(struct mount *mp, struct mbuf *nam, int *exflagsp, struct ucred **credanonp) { return (EACCES); /* For the time being */ } /* Handle a virtual partition map */ int udf_get_vpartmap(struct umount *ump, struct part_map_virt *pmv) { ump->um_flags |= UDF_MNT_FIND_VAT; /* Should do more than this */ return (0); } /* Handle a sparable partition map */ int udf_get_spartmap(struct umount *ump, struct part_map_spare *pms) { struct buf *bp; int i, error; ump->um_stbl = malloc(letoh32(pms->st_size), M_UDFMOUNT, M_NOWAIT); if (ump->um_stbl == NULL) return (ENOMEM); bzero(ump->um_stbl, letoh32(pms->st_size)); /* Calculate the number of sectors per packet */ ump->um_psecs = letoh16(pms->packet_len) / ump->um_bsize; error = udf_readlblks(ump, letoh32(pms->st_loc[0]), letoh32(pms->st_size), &bp); if (error) { if (bp != NULL) brelse(bp); free(ump->um_stbl, M_UDFMOUNT); return (error); /* Failed to read sparing table */ } bcopy(bp->b_data, ump->um_stbl, letoh32(pms->st_size)); brelse(bp); if (udf_checktag(&ump->um_stbl->tag, 0)) { free(ump->um_stbl, M_UDFMOUNT); return (EINVAL); /* Invalid sparing table found */ } /* * See how many valid entries there are here. The list is * supposed to be sorted, 0xfffffff0 and higher are not valid. */ for (i = 0; i < letoh16(ump->um_stbl->rt_l); i++) { ump->um_stbl_len = i; if (letoh32(ump->um_stbl->entries[i].org) >= 0xfffffff0) break; } return (0); } /* Scan the partition maps */ int udf_find_partmaps(struct umount *ump, struct logvol_desc *lvd) { struct regid *pmap_id; unsigned char regid_id[UDF_REGID_ID_SIZE + 1]; int i, ptype, psize, error; uint8_t *pmap = (uint8_t *) &lvd->maps[0]; for (i = 0; i < letoh32(lvd->n_pm); i++) { ptype = pmap[0]; psize = pmap[1]; if (ptype != 1 && ptype != 2) return (EINVAL); /* Invalid partition map type */ if (psize != sizeof(struct part_map_1) && psize != sizeof(struct part_map_2)) return (EINVAL); /* Invalid partition map size */ if (ptype == 1) { pmap += sizeof(struct part_map_1); continue; } /* Type 2 map. Find out the details */ pmap_id = (struct regid *) &pmap[4]; regid_id[UDF_REGID_ID_SIZE] = '\0'; bcopy(&pmap_id->id[0], ®id_id[0], UDF_REGID_ID_SIZE); if (!bcmp(®id_id[0], "*UDF Virtual Partition", UDF_REGID_ID_SIZE)) error = udf_get_vpartmap(ump, (struct part_map_virt *) pmap); else if (!bcmp(®id_id[0], "*UDF Sparable Partition", UDF_REGID_ID_SIZE)) error = udf_get_spartmap(ump, (struct part_map_spare *) pmap); else return (EINVAL); /* Unsupported partition map */ if (error) return (error); /* Error getting partition */ pmap += sizeof(struct part_map_2); } return (0); }