1189251Ssam/*- 2189251Ssam * SPDX-License-Identifier: BSD-2-Clause 3189251Ssam * 4189251Ssam * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org> 5252726Srpaulo * All rights reserved. 6252726Srpaulo * 7189251Ssam * Redistribution and use in source and binary forms, with or without 8189251Ssam * modification, are permitted provided that the following conditions 9189251Ssam * are met: 10189251Ssam * 1. Redistributions of source code must retain the above copyright 11189251Ssam * notice, this list of conditions and the following disclaimer. 12189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 13189251Ssam * notice, this list of conditions and the following disclaimer in the 14189251Ssam * documentation and/or other materials provided with the distribution. 15189251Ssam * 16189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19189251Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26189251Ssam * SUCH DAMAGE. 27189251Ssam */ 28189251Ssam 29189251Ssam/* udf_vfsops.c */ 30189251Ssam/* Implement the VFS side of things */ 31189251Ssam 32189251Ssam/* 33189251Ssam * Ok, here's how it goes. The UDF specs are pretty clear on how each data 34189251Ssam * structure is made up, but not very clear on how they relate to each other. 35189251Ssam * Here is the skinny... This demostrates a filesystem with one file in the 36189251Ssam * root directory. Subdirectories are treated just as normal files, but they 37189251Ssam * have File Id Descriptors of their children as their file data. As for the 38189251Ssam * Anchor Volume Descriptor Pointer, it can exist in two of the following three 39189251Ssam * places: sector 256, sector n (the max sector of the disk), or sector 40189251Ssam * n - 256. It's a pretty good bet that one will exist at sector 256 though. 41189251Ssam * One caveat is unclosed CD media. For that, sector 256 cannot be written, 42189251Ssam * so the Anchor Volume Descriptor Pointer can exist at sector 512 until the 43189251Ssam * media is closed. 44189251Ssam * 45189251Ssam * Sector: 46189251Ssam * 256: 47189251Ssam * n: Anchor Volume Descriptor Pointer 48189251Ssam * n - 256: | 49189251Ssam * | 50189251Ssam * |-->Main Volume Descriptor Sequence 51189251Ssam * | | 52189251Ssam * | | 53189251Ssam * | |-->Logical Volume Descriptor 54189251Ssam * | | 55189251Ssam * |-->Partition Descriptor | 56189251Ssam * | | 57189251Ssam * | | 58189251Ssam * |-->Fileset Descriptor 59189251Ssam * | 60189251Ssam * | 61189251Ssam * |-->Root Dir File Entry 62189251Ssam * | 63189251Ssam * | 64189251Ssam * |-->File data: 65189251Ssam * File Id Descriptor 66189251Ssam * | 67189251Ssam * | 68189251Ssam * |-->File Entry 69189251Ssam * | 70189251Ssam * | 71189251Ssam * |-->File data 72189251Ssam */ 73189251Ssam#include <sys/types.h> 74189251Ssam#include <sys/param.h> 75189251Ssam#include <sys/systm.h> 76189251Ssam#include <sys/uio.h> 77189251Ssam#include <sys/bio.h> 78189251Ssam#include <sys/buf.h> 79189251Ssam#include <sys/conf.h> 80189251Ssam#include <sys/dirent.h> 81189251Ssam#include <sys/fcntl.h> 82189251Ssam#include <sys/iconv.h> 83189251Ssam#include <sys/kernel.h> 84189251Ssam#include <sys/malloc.h> 85189251Ssam#include <sys/mount.h> 86189251Ssam#include <sys/namei.h> 87189251Ssam#include <sys/priv.h> 88189251Ssam#include <sys/proc.h> 89189251Ssam#include <sys/queue.h> 90189251Ssam#include <sys/vnode.h> 91189251Ssam#include <sys/endian.h> 92189251Ssam 93189251Ssam#include <geom/geom.h> 94189251Ssam#include <geom/geom_vfs.h> 95189251Ssam 96189251Ssam#include <vm/uma.h> 97189251Ssam 98189251Ssam#include <fs/udf/ecma167-udf.h> 99189251Ssam#include <fs/udf/osta.h> 100189251Ssam#include <fs/udf/udf.h> 101189251Ssam#include <fs/udf/udf_mount.h> 102189251Ssam 103189251Ssamstatic MALLOC_DEFINE(M_UDFMOUNT, "udf_mount", "UDF mount structure"); 104189251SsamMALLOC_DEFINE(M_UDFFENTRY, "udf_fentry", "UDF file entry structure"); 105189251Ssam 106189251Ssamstruct iconv_functions *udf_iconv = NULL; 107189251Ssam 108189251Ssam/* Zones */ 109189251Ssamuma_zone_t udf_zone_trans = NULL; 110189251Ssamuma_zone_t udf_zone_node = NULL; 111189251Ssamuma_zone_t udf_zone_ds = NULL; 112189251Ssam 113189251Ssamstatic vfs_init_t udf_init; 114189251Ssamstatic vfs_uninit_t udf_uninit; 115189251Ssamstatic vfs_mount_t udf_mount; 116189251Ssamstatic vfs_root_t udf_root; 117189251Ssamstatic vfs_statfs_t udf_statfs; 118189251Ssamstatic vfs_unmount_t udf_unmount; 119189251Ssamstatic vfs_fhtovp_t udf_fhtovp; 120189251Ssam 121189251Ssamstatic int udf_find_partmaps(struct udf_mnt *, struct logvol_desc *); 122189251Ssam 123189251Ssamstatic struct vfsops udf_vfsops = { 124189251Ssam .vfs_fhtovp = udf_fhtovp, 125189251Ssam .vfs_init = udf_init, 126189251Ssam .vfs_mount = udf_mount, 127189251Ssam .vfs_root = udf_root, 128189251Ssam .vfs_statfs = udf_statfs, 129189251Ssam .vfs_uninit = udf_uninit, 130189251Ssam .vfs_unmount = udf_unmount, 131189251Ssam .vfs_vget = udf_vget, 132189251Ssam}; 133189251SsamVFS_SET(udf_vfsops, udf, VFCF_READONLY); 134189251Ssam 135189251SsamMODULE_VERSION(udf, 1); 136189251Ssam 137189251Ssamstatic int udf_mountfs(struct vnode *, struct mount *); 138189251Ssam 139189251Ssamstatic int 140189251Ssamudf_init(struct vfsconf *foo) 141189251Ssam{ 142189251Ssam 143189251Ssam udf_zone_trans = uma_zcreate("UDF translation buffer, zone", MAXNAMLEN * 144189251Ssam sizeof(unicode_t), NULL, NULL, NULL, NULL, 0, 0); 145189251Ssam 146189251Ssam udf_zone_node = uma_zcreate("UDF Node zone", sizeof(struct udf_node), 147189251Ssam NULL, NULL, NULL, NULL, 0, 0); 148189251Ssam 149189251Ssam udf_zone_ds = uma_zcreate("UDF Dirstream zone", 150189251Ssam sizeof(struct udf_dirstream), NULL, NULL, NULL, NULL, 0, 0); 151189251Ssam 152189251Ssam return 0; 153189251Ssam} 154189251Ssam 155189251Ssamstatic int 156189251Ssamudf_uninit(struct vfsconf *foo) 157189251Ssam{ 158189251Ssam 159189251Ssam if (udf_zone_trans != NULL) { 160189251Ssam uma_zdestroy(udf_zone_trans); 161189251Ssam udf_zone_trans = NULL; 162189251Ssam } 163189251Ssam 164189251Ssam if (udf_zone_node != NULL) { 165189251Ssam uma_zdestroy(udf_zone_node); 166189251Ssam udf_zone_node = NULL; 167189251Ssam } 168189251Ssam 169189251Ssam if (udf_zone_ds != NULL) { 170189251Ssam uma_zdestroy(udf_zone_ds); 171189251Ssam udf_zone_ds = NULL; 172189251Ssam } 173189251Ssam 174189251Ssam return (0); 175189251Ssam} 176189251Ssam 177189251Ssamstatic int 178189251Ssamudf_mount(struct mount *mp) 179189251Ssam{ 180189251Ssam struct vnode *devvp; /* vnode of the mount device */ 181189251Ssam struct thread *td; 182189251Ssam struct udf_mnt *imp = NULL; 183189251Ssam struct vfsoptlist *opts; 184189251Ssam char *fspec, *cs_disk, *cs_local; 185189251Ssam int error, len, *udf_flags; 186189251Ssam struct nameidata nd, *ndp = &nd; 187189251Ssam 188189251Ssam td = curthread; 189189251Ssam opts = mp->mnt_optnew; 190189251Ssam 191189251Ssam /* 192189251Ssam * Unconditionally mount as read-only. 193189251Ssam */ 194189251Ssam MNT_ILOCK(mp); 195189251Ssam mp->mnt_flag |= MNT_RDONLY; 196189251Ssam MNT_IUNLOCK(mp); 197189251Ssam 198189251Ssam /* 199189251Ssam * No root filesystem support. Probably not a big deal, since the 200189251Ssam * bootloader doesn't understand UDF. 201189251Ssam */ 202189251Ssam if (mp->mnt_flag & MNT_ROOTFS) 203189251Ssam return (ENOTSUP); 204189251Ssam 205189251Ssam fspec = NULL; 206189251Ssam error = vfs_getopt(opts, "from", (void **)&fspec, &len); 207189251Ssam if (!error && fspec[len - 1] != '\0') 208189251Ssam return (EINVAL); 209189251Ssam 210189251Ssam if (mp->mnt_flag & MNT_UPDATE) { 211189251Ssam return (0); 212189251Ssam } 213189251Ssam 214189251Ssam /* Check that the mount device exists */ 215189251Ssam if (fspec == NULL) 216189251Ssam return (EINVAL); 217189251Ssam NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec); 218189251Ssam if ((error = namei(ndp))) 219189251Ssam return (error); 220189251Ssam NDFREE_PNBUF(ndp); 221189251Ssam devvp = ndp->ni_vp; 222189251Ssam 223189251Ssam if (!vn_isdisk_error(devvp, &error)) { 224189251Ssam vput(devvp); 225189251Ssam return (error); 226189251Ssam } 227189251Ssam 228189251Ssam /* Check the access rights on the mount device */ 229189251Ssam error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td); 230189251Ssam if (error) 231189251Ssam error = priv_check(td, PRIV_VFS_MOUNT_PERM); 232189251Ssam if (error) { 233189251Ssam vput(devvp); 234189251Ssam return (error); 235189251Ssam } 236189251Ssam 237189251Ssam if ((error = udf_mountfs(devvp, mp))) { 238189251Ssam vrele(devvp); 239189251Ssam return (error); 240189251Ssam } 241189251Ssam 242189251Ssam imp = VFSTOUDFFS(mp); 243189251Ssam 244189251Ssam udf_flags = NULL; 245189251Ssam error = vfs_getopt(opts, "flags", (void **)&udf_flags, &len); 246189251Ssam if (error || len != sizeof(int)) 247189251Ssam return (EINVAL); 248189251Ssam imp->im_flags = *udf_flags; 249189251Ssam 250189251Ssam if (imp->im_flags & UDFMNT_KICONV && udf_iconv) { 251189251Ssam cs_disk = NULL; 252189251Ssam error = vfs_getopt(opts, "cs_disk", (void **)&cs_disk, &len); 253189251Ssam if (!error && cs_disk[len - 1] != '\0') 254189251Ssam return (EINVAL); 255189251Ssam cs_local = NULL; 256189251Ssam error = vfs_getopt(opts, "cs_local", (void **)&cs_local, &len); 257189251Ssam if (!error && cs_local[len - 1] != '\0') 258189251Ssam return (EINVAL); 259189251Ssam udf_iconv->open(cs_local, cs_disk, &imp->im_d2l); 260189251Ssam#if 0 261189251Ssam udf_iconv->open(cs_disk, cs_local, &imp->im_l2d); 262189251Ssam#endif 263189251Ssam } 264189251Ssam 265189251Ssam vfs_mountedfrom(mp, fspec); 266189251Ssam return 0; 267189251Ssam}; 268189251Ssam 269189251Ssam/* 270189251Ssam * Check the descriptor tag for both the correct id and correct checksum. 271189251Ssam * Return zero if all is good, EINVAL if not. 272189251Ssam */ 273189251Ssamint 274189251Ssamudf_checktag(struct desc_tag *tag, uint16_t id) 275189251Ssam{ 276189251Ssam uint8_t *itag; 277189251Ssam uint8_t i, cksum = 0; 278189251Ssam 279189251Ssam itag = (uint8_t *)tag; 280189251Ssam 281189251Ssam if (le16toh(tag->id) != id) 282189251Ssam return (EINVAL); 283189251Ssam 284189251Ssam for (i = 0; i < 16; i++) 285189251Ssam cksum = cksum + itag[i]; 286189251Ssam cksum = cksum - itag[4]; 287189251Ssam 288189251Ssam if (cksum == tag->cksum) 289189251Ssam return (0); 290189251Ssam 291189251Ssam return (EINVAL); 292189251Ssam} 293189251Ssam 294189251Ssamstatic int 295189251Ssamudf_mountfs(struct vnode *devvp, struct mount *mp) 296189251Ssam{ 297189251Ssam struct buf *bp = NULL; 298189251Ssam struct cdev *dev; 299189251Ssam struct anchor_vdp avdp; 300189251Ssam struct udf_mnt *udfmp = NULL; 301189251Ssam struct part_desc *pd; 302189251Ssam struct logvol_desc *lvd; 303189251Ssam struct fileset_desc *fsd; 304189251Ssam struct file_entry *root_fentry; 305189251Ssam uint32_t sector, size, mvds_start, mvds_end; 306189251Ssam uint32_t logical_secsize; 307189251Ssam uint32_t fsd_offset = 0; 308189251Ssam uint16_t part_num = 0, fsd_part = 0; 309189251Ssam int error = EINVAL; 310189251Ssam int logvol_found = 0, part_found = 0, fsd_found = 0; 311189251Ssam int bsize; 312189251Ssam struct g_consumer *cp; 313189251Ssam struct bufobj *bo; 314189251Ssam 315189251Ssam dev = devvp->v_rdev; 316189251Ssam dev_ref(dev); 317189251Ssam g_topology_lock(); 318189251Ssam error = g_vfs_open(devvp, &cp, "udf", 0); 319189251Ssam g_topology_unlock(); 320189251Ssam VOP_UNLOCK(devvp); 321189251Ssam if (error) 322189251Ssam goto bail; 323189251Ssam 324189251Ssam bo = &devvp->v_bufobj; 325189251Ssam 326189251Ssam if (devvp->v_rdev->si_iosize_max != 0) 327189251Ssam mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; 328189251Ssam if (mp->mnt_iosize_max > maxphys) 329189251Ssam mp->mnt_iosize_max = maxphys; 330189251Ssam 331189251Ssam /* XXX: should be M_WAITOK */ 332189251Ssam udfmp = malloc(sizeof(struct udf_mnt), M_UDFMOUNT, 333189251Ssam M_NOWAIT | M_ZERO); 334189251Ssam if (udfmp == NULL) { 335189251Ssam printf("Cannot allocate UDF mount struct\n"); 336189251Ssam error = ENOMEM; 337189251Ssam goto bail; 338189251Ssam } 339189251Ssam 340189251Ssam mp->mnt_data = udfmp; 341189251Ssam mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev); 342189251Ssam mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 343189251Ssam MNT_ILOCK(mp); 344189251Ssam mp->mnt_flag |= MNT_LOCAL; 345189251Ssam mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED; 346189251Ssam MNT_IUNLOCK(mp); 347189251Ssam udfmp->im_mountp = mp; 348189251Ssam udfmp->im_dev = dev; 349189251Ssam udfmp->im_devvp = devvp; 350189251Ssam udfmp->im_d2l = NULL; 351189251Ssam udfmp->im_cp = cp; 352189251Ssam udfmp->im_bo = bo; 353189251Ssam 354189251Ssam#if 0 355189251Ssam udfmp->im_l2d = NULL; 356189251Ssam#endif 357189251Ssam /* 358189251Ssam * The UDF specification defines a logical sectorsize of 2048 359189251Ssam * for DVD media. 360189251Ssam */ 361189251Ssam logical_secsize = 2048; 362189251Ssam 363189251Ssam if (((logical_secsize % cp->provider->sectorsize) != 0) || 364189251Ssam (logical_secsize < cp->provider->sectorsize)) { 365189251Ssam error = EINVAL; 366189251Ssam goto bail; 367189251Ssam } 368189251Ssam 369189251Ssam bsize = cp->provider->sectorsize; 370189251Ssam 371189251Ssam /* 372189251Ssam * Get the Anchor Volume Descriptor Pointer from sector 256. 373189251Ssam * XXX Should also check sector n - 256, n, and 512. 374189251Ssam */ 375189251Ssam sector = 256; 376189251Ssam if ((error = bread(devvp, sector * btodb(logical_secsize), bsize, 377189251Ssam NOCRED, &bp)) != 0) 378189251Ssam goto bail; 379189251Ssam if ((error = udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR))) 380189251Ssam goto bail; 381189251Ssam 382189251Ssam bcopy(bp->b_data, &avdp, sizeof(struct anchor_vdp)); 383189251Ssam brelse(bp); 384189251Ssam bp = NULL; 385189251Ssam 386189251Ssam /* 387189251Ssam * Extract the Partition Descriptor and Logical Volume Descriptor 388189251Ssam * from the Volume Descriptor Sequence. 389189251Ssam * XXX Should we care about the partition type right now? 390189251Ssam * XXX What about multiple partitions? 391189251Ssam */ 392189251Ssam mvds_start = le32toh(avdp.main_vds_ex.loc); 393189251Ssam mvds_end = mvds_start + (le32toh(avdp.main_vds_ex.len) - 1) / bsize; 394189251Ssam for (sector = mvds_start; sector < mvds_end; sector++) { 395189251Ssam if ((error = bread(devvp, sector * btodb(logical_secsize), 396189251Ssam bsize, NOCRED, &bp)) != 0) { 397189251Ssam printf("Can't read sector %d of VDS\n", sector); 398189251Ssam goto bail; 399189251Ssam } 400189251Ssam lvd = (struct logvol_desc *)bp->b_data; 401189251Ssam if (!udf_checktag(&lvd->tag, TAGID_LOGVOL)) { 402189251Ssam udfmp->bsize = le32toh(lvd->lb_size); 403189251Ssam if (udfmp->bsize < 0 || udfmp->bsize > maxbcachebuf) { 404189251Ssam printf("lvd block size %d\n", udfmp->bsize); 405189251Ssam error = EINVAL; 406189251Ssam goto bail; 407189251Ssam } 408189251Ssam udfmp->bmask = udfmp->bsize - 1; 409189251Ssam udfmp->bshift = ffs(udfmp->bsize) - 1; 410189251Ssam fsd_part = le16toh(lvd->_lvd_use.fsd_loc.loc.part_num); 411189251Ssam fsd_offset = le32toh(lvd->_lvd_use.fsd_loc.loc.lb_num); 412189251Ssam if (udf_find_partmaps(udfmp, lvd)) 413189251Ssam break; 414189251Ssam logvol_found = 1; 415189251Ssam } 416189251Ssam pd = (struct part_desc *)bp->b_data; 417189251Ssam if (!udf_checktag(&pd->tag, TAGID_PARTITION)) { 418189251Ssam part_found = 1; 419189251Ssam part_num = le16toh(pd->part_num); 420189251Ssam udfmp->part_len = le32toh(pd->part_len); 421189251Ssam udfmp->part_start = le32toh(pd->start_loc); 422189251Ssam } 423189251Ssam 424189251Ssam brelse(bp); 425252726Srpaulo bp = NULL; 426252726Srpaulo if ((part_found) && (logvol_found)) 427252726Srpaulo break; 428252726Srpaulo } 429252726Srpaulo 430252726Srpaulo if (!part_found || !logvol_found) { 431189251Ssam error = EINVAL; 432189251Ssam goto bail; 433189251Ssam } 434189251Ssam 435189251Ssam if (fsd_part != part_num) { 436189251Ssam printf("FSD does not lie within the partition!\n"); 437189251Ssam error = EINVAL; 438189251Ssam goto bail; 439189251Ssam } 440189251Ssam 441189251Ssam /* 442189251Ssam * Grab the Fileset Descriptor 443189251Ssam * Thanks to Chuck McCrobie <mccrobie@cablespeed.com> for pointing 444189251Ssam * me in the right direction here. 445189251Ssam */ 446189251Ssam sector = udfmp->part_start + fsd_offset; 447189251Ssam if ((error = RDSECTOR(devvp, sector, udfmp->bsize, &bp)) != 0) { 448189251Ssam printf("Cannot read sector %d of FSD\n", sector); 449189251Ssam goto bail; 450189251Ssam } 451189251Ssam fsd = (struct fileset_desc *)bp->b_data; 452189251Ssam if (!udf_checktag(&fsd->tag, TAGID_FSD)) { 453189251Ssam fsd_found = 1; 454189251Ssam bcopy(&fsd->rootdir_icb, &udfmp->root_icb, 455189251Ssam sizeof(struct long_ad)); 456189251Ssam } 457189251Ssam 458189251Ssam brelse(bp); 459189251Ssam bp = NULL; 460189251Ssam 461189251Ssam if (!fsd_found) { 462189251Ssam printf("Couldn't find the fsd\n"); 463189251Ssam error = EINVAL; 464189251Ssam goto bail; 465189251Ssam } 466189251Ssam 467189251Ssam /* 468189251Ssam * Find the file entry for the root directory. 469189251Ssam */ 470189251Ssam sector = le32toh(udfmp->root_icb.loc.lb_num) + udfmp->part_start; 471189251Ssam size = le32toh(udfmp->root_icb.len); 472189251Ssam if (size < UDF_FENTRY_SIZE) { 473189251Ssam printf("Invalid root directory file entry length %u\n", 474189251Ssam size); 475189251Ssam goto bail; 476189251Ssam } 477189251Ssam if ((error = udf_readdevblks(udfmp, sector, size, &bp)) != 0) { 478189251Ssam printf("Cannot read sector %d\n", sector); 479189251Ssam goto bail; 480189251Ssam } 481214734Srpaulo 482189251Ssam root_fentry = (struct file_entry *)bp->b_data; 483214734Srpaulo if ((error = udf_checktag(&root_fentry->tag, TAGID_FENTRY))) { 484214734Srpaulo printf("Invalid root file entry!\n"); 485189251Ssam goto bail; 486189251Ssam } 487189251Ssam 488189251Ssam brelse(bp); 489189251Ssam bp = NULL; 490189251Ssam 491189251Ssam return 0; 492189251Ssam 493189251Ssambail: 494189251Ssam if (udfmp != NULL) 495189251Ssam free(udfmp, M_UDFMOUNT); 496189251Ssam if (bp != NULL) 497189251Ssam brelse(bp); 498252726Srpaulo if (cp != NULL) { 499189251Ssam g_topology_lock(); 500189251Ssam g_vfs_close(cp); 501189251Ssam g_topology_unlock(); 502214734Srpaulo } 503189251Ssam dev_rel(dev); 504214734Srpaulo return error; 505214734Srpaulo}; 506189251Ssam 507189251Ssamstatic int 508214734Srpauloudf_unmount(struct mount *mp, int mntflags) 509214734Srpaulo{ 510214734Srpaulo struct udf_mnt *udfmp; 511189251Ssam int error, flags = 0; 512189251Ssam 513189251Ssam udfmp = VFSTOUDFFS(mp); 514189251Ssam 515214734Srpaulo if (mntflags & MNT_FORCE) 516214734Srpaulo flags |= FORCECLOSE; 517189251Ssam 518189251Ssam if ((error = vflush(mp, 0, flags, curthread))) 519189251Ssam return (error); 520214734Srpaulo 521214734Srpaulo if (udfmp->im_flags & UDFMNT_KICONV && udf_iconv) { 522189251Ssam if (udfmp->im_d2l) 523189251Ssam udf_iconv->close(udfmp->im_d2l); 524189251Ssam#if 0 525214734Srpaulo if (udfmp->im_l2d) 526214734Srpaulo udf_iconv->close(udfmp->im_l2d); 527189251Ssam#endif 528189251Ssam } 529189251Ssam 530189251Ssam g_topology_lock(); 531189251Ssam g_vfs_close(udfmp->im_cp); 532189251Ssam g_topology_unlock(); 533189251Ssam vrele(udfmp->im_devvp); 534189251Ssam dev_rel(udfmp->im_dev); 535189251Ssam 536189251Ssam if (udfmp->s_table != NULL) 537189251Ssam free(udfmp->s_table, M_UDFMOUNT); 538189251Ssam 539189251Ssam free(udfmp, M_UDFMOUNT); 540189251Ssam 541189251Ssam mp->mnt_data = NULL; 542189251Ssam return (0); 543189251Ssam} 544189251Ssam 545189251Ssamstatic int 546189251Ssamudf_root(struct mount *mp, int flags, struct vnode **vpp) 547189251Ssam{ 548189251Ssam struct udf_mnt *udfmp; 549189251Ssam ino_t id; 550189251Ssam 551189251Ssam udfmp = VFSTOUDFFS(mp); 552189251Ssam 553189251Ssam id = udf_getid(&udfmp->root_icb); 554189251Ssam 555189251Ssam return (udf_vget(mp, id, flags, vpp)); 556189251Ssam} 557189251Ssam 558189251Ssamstatic int 559189251Ssamudf_statfs(struct mount *mp, struct statfs *sbp) 560189251Ssam{ 561189251Ssam struct udf_mnt *udfmp; 562189251Ssam 563189251Ssam udfmp = VFSTOUDFFS(mp); 564189251Ssam 565189251Ssam sbp->f_bsize = udfmp->bsize; 566189251Ssam sbp->f_iosize = udfmp->bsize; 567189251Ssam sbp->f_blocks = udfmp->part_len; 568189251Ssam sbp->f_bfree = 0; 569189251Ssam sbp->f_bavail = 0; 570189251Ssam sbp->f_files = 0; 571189251Ssam sbp->f_ffree = 0; 572189251Ssam return 0; 573189251Ssam} 574189251Ssam 575189251Ssamint 576189251Ssamudf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 577189251Ssam{ 578189251Ssam struct buf *bp; 579189251Ssam struct vnode *devvp; 580189251Ssam struct udf_mnt *udfmp; 581189251Ssam struct thread *td; 582189251Ssam struct vnode *vp; 583189251Ssam struct udf_node *unode; 584189251Ssam struct file_entry *fe; 585189251Ssam uint32_t lea, lad; 586189251Ssam int error, sector, size; 587189251Ssam 588189251Ssam error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL); 589189251Ssam if (error || *vpp != NULL) 590189251Ssam return (error); 591189251Ssam 592189251Ssam /* 593189251Ssam * We must promote to an exclusive lock for vnode creation. This 594189251Ssam * can happen if lookup is passed LOCKSHARED. 595189251Ssam */ 596189251Ssam if ((flags & LK_TYPE_MASK) == LK_SHARED) { 597189251Ssam flags &= ~LK_TYPE_MASK; 598189251Ssam flags |= LK_EXCLUSIVE; 599189251Ssam } 600189251Ssam 601189251Ssam /* 602189251Ssam * We do not lock vnode creation as it is believed to be too 603189251Ssam * expensive for such rare case as simultaneous creation of vnode 604189251Ssam * for same ino by different processes. We just allow them to race 605189251Ssam * and check later to decide who wins. Let the race begin! 606189251Ssam */ 607189251Ssam 608189251Ssam td = curthread; 609189251Ssam udfmp = VFSTOUDFFS(mp); 610189251Ssam 611189251Ssam unode = uma_zalloc(udf_zone_node, M_WAITOK | M_ZERO); 612189251Ssam 613189251Ssam if ((error = udf_allocv(mp, &vp, td))) { 614189251Ssam printf("Error from udf_allocv\n"); 615189251Ssam uma_zfree(udf_zone_node, unode); 616189251Ssam return (error); 617189251Ssam } 618189251Ssam 619189251Ssam unode->i_vnode = vp; 620189251Ssam unode->hash_id = ino; 621189251Ssam unode->udfmp = udfmp; 622189251Ssam vp->v_data = unode; 623189251Ssam 624189251Ssam lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); 625189251Ssam error = insmntque(vp, mp); 626189251Ssam if (error != 0) { 627189251Ssam uma_zfree(udf_zone_node, unode); 628189251Ssam return (error); 629189251Ssam } 630189251Ssam error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); 631189251Ssam if (error || *vpp != NULL) 632189251Ssam return (error); 633189251Ssam 634189251Ssam /* 635189251Ssam * Copy in the file entry. Per the spec, the size can only be 1 block. 636189251Ssam */ 637189251Ssam sector = ino + udfmp->part_start; 638189251Ssam devvp = udfmp->im_devvp; 639189251Ssam if ((error = RDSECTOR(devvp, sector, udfmp->bsize, &bp)) != 0) { 640189251Ssam printf("Cannot read sector %d\n", sector); 641189251Ssam goto error; 642189251Ssam } 643189251Ssam 644189251Ssam /* 645189251Ssam * File entry length validation. 646189251Ssam */ 647189251Ssam fe = (struct file_entry *)bp->b_data; 648189251Ssam if (udf_checktag(&fe->tag, TAGID_FENTRY)) { 649189251Ssam printf("Invalid file entry!\n"); 650189251Ssam error = ENOMEM; 651189251Ssam goto error; 652189251Ssam } 653189251Ssam lea = le32toh(fe->l_ea); 654189251Ssam lad = le32toh(fe->l_ad); 655189251Ssam if (lea > udfmp->bsize || lad > udfmp->bsize) { 656189251Ssam printf("Invalid EA and AD lengths %u, %u\n", lea, lad); 657189251Ssam error = EIO; 658189251Ssam goto error; 659189251Ssam } 660189251Ssam size = UDF_FENTRY_SIZE + lea + lad; 661189251Ssam if (size > udfmp->bsize) { 662189251Ssam printf("Invalid file entry size %u\n", size); 663189251Ssam error = EIO; 664189251Ssam goto error; 665189251Ssam } 666189251Ssam 667189251Ssam unode->fentry = malloc(size, M_UDFFENTRY, M_NOWAIT | M_ZERO); 668189251Ssam if (unode->fentry == NULL) { 669189251Ssam printf("Cannot allocate file entry block\n"); 670189251Ssam error = ENOMEM; 671189251Ssam goto error; 672189251Ssam } 673189251Ssam 674189251Ssam bcopy(bp->b_data, unode->fentry, size); 675189251Ssam 676189251Ssam brelse(bp); 677189251Ssam bp = NULL; 678189251Ssam 679189251Ssam switch (unode->fentry->icbtag.file_type) { 680189251Ssam default: 681189251Ssam vp->v_type = VBAD; 682189251Ssam break; 683189251Ssam case 4: 684189251Ssam vp->v_type = VDIR; 685189251Ssam break; 686189251Ssam case 5: 687189251Ssam vp->v_type = VREG; 688189251Ssam break; 689189251Ssam case 6: 690189251Ssam vp->v_type = VBLK; 691189251Ssam break; 692189251Ssam case 7: 693189251Ssam vp->v_type = VCHR; 694189251Ssam break; 695189251Ssam case 9: 696189251Ssam vp->v_type = VFIFO; 697189251Ssam vp->v_op = &udf_fifoops; 698189251Ssam break; 699189251Ssam case 10: 700189251Ssam vp->v_type = VSOCK; 701189251Ssam break; 702189251Ssam case 12: 703189251Ssam vp->v_type = VLNK; 704189251Ssam break; 705189251Ssam } 706189251Ssam 707189251Ssam if (vp->v_type != VFIFO) 708189251Ssam VN_LOCK_ASHARE(vp); 709189251Ssam 710189251Ssam if (ino == udf_getid(&udfmp->root_icb)) 711189251Ssam vp->v_vflag |= VV_ROOT; 712189251Ssam 713189251Ssam vn_set_state(vp, VSTATE_CONSTRUCTED); 714189251Ssam *vpp = vp; 715189251Ssam 716189251Ssam return (0); 717189251Ssam 718189251Ssamerror: 719189251Ssam vgone(vp); 720189251Ssam vput(vp); 721189251Ssam brelse(bp); 722189251Ssam *vpp = NULL; 723189251Ssam return (error); 724189251Ssam} 725189251Ssam 726189251Ssamstatic int 727189251Ssamudf_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) 728189251Ssam{ 729189251Ssam struct ifid *ifhp; 730189251Ssam struct vnode *nvp; 731189251Ssam struct udf_node *np; 732189251Ssam off_t fsize; 733189251Ssam int error; 734189251Ssam 735189251Ssam ifhp = (struct ifid *)fhp; 736189251Ssam 737189251Ssam if ((error = VFS_VGET(mp, ifhp->ifid_ino, LK_EXCLUSIVE, &nvp)) != 0) { 738189251Ssam *vpp = NULLVP; 739189251Ssam return (error); 740189251Ssam } 741189251Ssam 742189251Ssam np = VTON(nvp); 743189251Ssam fsize = le64toh(np->fentry->inf_len); 744189251Ssam 745189251Ssam *vpp = nvp; 746189251Ssam vnode_create_vobject(*vpp, fsize, curthread); 747189251Ssam return (0); 748189251Ssam} 749189251Ssam 750189251Ssamstatic int 751189251Ssamudf_find_partmaps(struct udf_mnt *udfmp, struct logvol_desc *lvd) 752189251Ssam{ 753189251Ssam struct part_map_spare *pms; 754189251Ssam struct regid *pmap_id; 755189251Ssam struct buf *bp; 756189251Ssam unsigned char regid_id[UDF_REGID_ID_SIZE + 1]; 757189251Ssam int i, k, ptype, psize, error; 758189251Ssam uint8_t *pmap = (uint8_t *) &lvd->maps[0]; 759189251Ssam 760189251Ssam for (i = 0; i < le32toh(lvd->n_pm); i++) { 761189251Ssam ptype = pmap[0]; 762189251Ssam psize = pmap[1]; 763189251Ssam if (((ptype != 1) && (ptype != 2)) || 764189251Ssam ((psize != UDF_PMAP_TYPE1_SIZE) && 765189251Ssam (psize != UDF_PMAP_TYPE2_SIZE))) { 766189251Ssam printf("Invalid partition map found\n"); 767189251Ssam return (1); 768189251Ssam } 769189251Ssam 770189251Ssam if (ptype == 1) { 771189251Ssam /* Type 1 map. We don't care */ 772189251Ssam pmap += UDF_PMAP_TYPE1_SIZE; 773189251Ssam continue; 774189251Ssam } 775189251Ssam 776189251Ssam /* Type 2 map. Gotta find out the details */ 777189251Ssam pmap_id = (struct regid *)&pmap[4]; 778189251Ssam bzero(®id_id[0], UDF_REGID_ID_SIZE); 779189251Ssam bcopy(&pmap_id->id[0], ®id_id[0], UDF_REGID_ID_SIZE); 780189251Ssam 781189251Ssam if (bcmp(®id_id[0], "*UDF Sparable Partition", 782189251Ssam UDF_REGID_ID_SIZE)) { 783189251Ssam printf("Unsupported partition map: %s\n", ®id_id[0]); 784189251Ssam return (1); 785189251Ssam } 786189251Ssam 787189251Ssam pms = (struct part_map_spare *)pmap; 788189251Ssam pmap += UDF_PMAP_TYPE2_SIZE; 789189251Ssam udfmp->s_table = malloc(le32toh(pms->st_size), 790189251Ssam M_UDFMOUNT, M_NOWAIT | M_ZERO); 791189251Ssam if (udfmp->s_table == NULL) 792189251Ssam return (ENOMEM); 793189251Ssam 794189251Ssam /* Calculate the number of sectors per packet. */ 795189251Ssam /* XXX Logical or physical? */ 796189251Ssam udfmp->p_sectors = le16toh(pms->packet_len) / udfmp->bsize; 797189251Ssam 798189251Ssam /* 799189251Ssam * XXX If reading the first Sparing Table fails, should look 800189251Ssam * for another table. 801189251Ssam */ 802189251Ssam if ((error = udf_readdevblks(udfmp, le32toh(pms->st_loc[0]), 803189251Ssam le32toh(pms->st_size), &bp)) != 0) { 804189251Ssam if (bp != NULL) 805189251Ssam brelse(bp); 806189251Ssam printf("Failed to read Sparing Table at sector %d\n", 807189251Ssam le32toh(pms->st_loc[0])); 808189251Ssam free(udfmp->s_table, M_UDFMOUNT); 809189251Ssam return (error); 810189251Ssam } 811189251Ssam bcopy(bp->b_data, udfmp->s_table, le32toh(pms->st_size)); 812189251Ssam brelse(bp); 813189251Ssam 814189251Ssam if (udf_checktag(&udfmp->s_table->tag, 0)) { 815189251Ssam printf("Invalid sparing table found\n"); 816189251Ssam free(udfmp->s_table, M_UDFMOUNT); 817189251Ssam return (EINVAL); 818189251Ssam } 819189251Ssam 820189251Ssam /* See how many valid entries there are here. The list is 821189251Ssam * supposed to be sorted. 0xfffffff0 and higher are not valid 822189251Ssam */ 823189251Ssam for (k = 0; k < le16toh(udfmp->s_table->rt_l); k++) { 824189251Ssam udfmp->s_table_entries = k; 825189251Ssam if (le32toh(udfmp->s_table->entries[k].org) >= 826189251Ssam 0xfffffff0) 827189251Ssam break; 828189251Ssam } 829189251Ssam } 830189251Ssam 831189251Ssam return (0); 832189251Ssam} 833189251Ssam