/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
* 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 ``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 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.
*
*/
/*
* If running a DEBUG kernel, provide an easy way to set the debug flags when
* running into a problem.
*/
#define UDF_VERBOSE_SYSCTLOPT 1
/*
* XXX the "24" below could be dynamic, thereby eliminating one
* more instance of the "number to vfs" mapping problem, but
* "24" is the order as taken from sys/mount.h
*/
SYSCTL_SETUP(udf_sysctl_setup, "udf sysctl")
{
const struct sysctlnode *node;
/* if the system nodes exist, release them */
static void
udf_release_system_nodes(struct mount *mp)
{
struct udf_mount *ump = VFSTOUDF(mp);
/* if we haven't even got an ump, dont bother */
if (!ump)
return;
/* VAT partition support */
if (ump->vat_node)
vrele(ump->vat_node->vnode);
/* Metadata partition support */
if (ump->metadata_node)
vrele(ump->metadata_node->vnode);
if (ump->metadatamirror_node)
vrele(ump->metadatamirror_node->vnode);
if (ump->metadatabitmap_node)
vrele(ump->metadatabitmap_node->vnode);
}
if (args == NULL)
return EINVAL;
if (*data_len < sizeof *args)
return EINVAL;
if (mp->mnt_flag & MNT_GETARGS) {
/* request for the mount arguments */
ump = VFSTOUDF(mp);
if (ump == NULL)
return EINVAL;
*args = ump->mount_args;
*data_len = sizeof *args;
return 0;
}
/* handle request for updating mount parameters */
/* TODO can't update my mountpoint yet */
if (mp->mnt_flag & MNT_UPDATE) {
return EOPNOTSUPP;
}
/* OK, so we are asked to mount the device */
/* check/translate struct version */
/* TODO sanity checking other mount arguments */
if (args->version != 1) {
printf("mount_udf: unrecognized argument structure version\n");
return EINVAL;
}
/* lookup name to get its vnode */
error = namei_simple_user(args->fspec,
NSM_FOLLOW_NOEMULROOT, &devvp);
if (error)
return error;
#ifdef DEBUG
if (udf_verbose & UDF_DEBUG_VOLUMES)
vprint("UDF mount, trying to mount \n", devvp);
#endif
/* check if its a block device specified */
if (devvp->v_type != VBLK) {
vrele(devvp);
return ENOTBLK;
}
if (bdevsw_lookup(devvp->v_rdev) == NULL) {
vrele(devvp);
return ENXIO;
}
/*
* If mount by non-root, then verify that user has necessary
* permissions on the device.
*/
accessmode = VREAD;
if ((mp->mnt_flag & MNT_RDONLY) == 0)
accessmode |= VWRITE;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode));
VOP_UNLOCK(devvp);
if (error) {
vrele(devvp);
return error;
}
/*
* Open device and try to mount it!
*/
if (mp->mnt_flag & MNT_RDONLY) {
openflags = FREAD;
} else {
openflags = FREAD | FWRITE;
}
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_OPEN(devvp, openflags, FSCRED);
VOP_UNLOCK(devvp);
if (error == 0) {
/* opened ok, try mounting */
error = udf_mountfs(devvp, mp, l, args);
if (error) {
udf_release_system_nodes(mp);
/* cleanup */
udf_discstrat_finish(VFSTOUDF(mp));
free_udf_mountinfo(mp);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
(void) VOP_CLOSE(devvp, openflags, NOCRED);
VOP_UNLOCK(devvp);
}
}
if (error) {
/* devvp is still locked */
vrele(devvp);
return error;
}
/* register our mountpoint being on this device */
spec_node_setmountedfs(devvp, mp);
/* If we're not opened read-only, open its logical volume */
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
if ((error = udf_open_logvol(VFSTOUDF(mp))) != 0) {
printf( "mount_udf: can't open logical volume for "
"writing, downgrading access to read-only\n");
mp->mnt_flag |= MNT_RDONLY;
/* FIXME we can't return error now on open failure */
return 0;
}
}
vprint("", vp);
if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) {
printf(" is locked\n");
}
if (vrefcnt(vp) > 1)
printf(" more than one usecount %d\n", vrefcnt(vp));
return false;
}
static void
udf_unmount_sanity_check(struct mount *mp)
{
struct vnode_iterator *marker;
printf("On unmount, i found the following nodes:\n");
vfs_vnode_iterator_init(mp, &marker);
vfs_vnode_iterator_next(marker, udf_sanity_selector, NULL);
vfs_vnode_iterator_destroy(marker);
}
#endif
int
udf_unmount(struct mount *mp, int mntflags)
{
struct udf_mount *ump;
int error, flags, closeflags;
DPRINTF(CALL, ("udf_umount called\n"));
ump = VFSTOUDF(mp);
if (!ump)
panic("UDF unmount: empty ump\n");
flags = (mntflags & MNT_FORCE) ? FORCECLOSE : 0;
/* TODO remove these paranoid functions */
#ifdef DEBUG
if (udf_verbose & UDF_DEBUG_LOCKING)
udf_unmount_sanity_check(mp);
#endif
/*
* By specifying SKIPSYSTEM we can skip vnodes marked with VV_SYSTEM.
* This hardly documented feature allows us to exempt certain files
* from being flushed.
*/
if ((error = vflush(mp, NULLVP, flags | SKIPSYSTEM)) != 0)
return error;
/* update nodes and wait for completion of writeout of system nodes */
udf_sync(mp, FSYNC_WAIT, NOCRED);
#ifdef DEBUG
if (udf_verbose & UDF_DEBUG_LOCKING)
udf_unmount_sanity_check(mp);
#endif
/* flush again, to check if we are still busy for something else */
if ((error = vflush(ump->vfs_mountp, NULLVP, flags | SKIPSYSTEM)) != 0)
return error;
DPRINTF(VOLUMES, ("flush OK on unmount\n"));
/* close logical volume and close session if requested */
if ((error = udf_close_logvol(ump, mntflags)) != 0)
return error;
/* NOTE release system nodes should NOT write anything */
udf_release_system_nodes(mp);
/* This flush should NOT write anything nor allow any node to remain */
if ((error = vflush(ump->vfs_mountp, NULLVP, 0)) != 0)
panic("Failure to flush UDF system vnodes\n");
/* devvp is still locked by us */
vn_lock(ump->devvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_CLOSE(ump->devvp, closeflags, NOCRED);
if (error)
printf("Error during closure of device! error %d, "
"device might stay locked\n", error);
DPRINTF(VOLUMES, ("device close ok\n"));
/* clear our mount reference and release device node */
spec_node_setmountedfs(ump->devvp, NULL);
vput(ump->devvp);
/*
* Helper function of udf_mount() that actually mounts the disc.
*/
static int
udf_mountfs(struct vnode *devvp, struct mount *mp,
struct lwp *l, struct udf_args *args)
{
struct udf_mount *ump;
uint32_t sector_size, lb_size, bshift;
uint32_t logvol_integrity;
int num_anchors, error;
/* flush out any old buffers remaining from a previous use. */
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0);
VOP_UNLOCK(devvp);
if (error)
return error;
/* temporary check to overcome sectorsize >= 8192 bytes panic */
if (sector_size >= 8192) {
printf("UDF mount: "
"hit implementation limit, sectorsize to big\n");
return EIO;
}
/*
* Inspect if we're asked to mount read-write on a non recordable or
* closed sequential disc.
*/
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
if ((ump->discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) {
printf("UDF mount: disc is not recordable\n");
return EROFS;
}
if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
if (ump->discinfo.disc_state == MMC_STATE_FULL) {
printf("UDF mount: disc is not appendable\n");
return EROFS;
}
/*
* TODO if the last session is closed check if there
* is enough space to open/close new session
*/
}
/* double check if we're not mounting a previous session RW */
if (args->sessionnr != 0) {
printf("UDF mount: updating a previous session "
"not yet allowed\n");
return EROFS;
}
}
/* setup rest of mount information */
mp->mnt_data = ump;
/* bshift is always equal to disc sector size */
mp->mnt_dev_bshift = bshift;
mp->mnt_fs_bshift = bshift;
/* note that the mp info needs to be initialised for reading! */
/* read vds support tables like VAT, sparable etc. */
if ((error = udf_read_vds_tables(ump))) {
printf( "UDF mount: error in format or damaged disc "
"(VDS tables failing)\n");
return error;
}
/* check if volume integrity is closed otherwise its dirty */
logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type);
if (logvol_integrity != UDF_INTEGRITY_CLOSED) {
printf("UDF mount: file system is not clean; ");
printf("please fsck(8)\n");
return EPERM;
}
/* read root directory */
if ((error = udf_read_rootdirs(ump))) {
printf( "UDF mount: "
"disc not properly formatted or damaged disc "
"(rootdirs failing)\n");
return error;
}
/*
* TODO what about writing out free space maps, lvid etc? only on `waitfor'
* i.e. explicit syncing by the user?
*/
static int
udf_sync_writeout_system_files(struct udf_mount *ump, int clearflags)
{
int error;
/* XXX lock for VAT en bitmaps? */
/* metadata nodes are written synchronous */
DPRINTF(CALL, ("udf_sync: syncing metadata\n"));
if (ump->lvclose & UDF_WRITE_VAT)
udf_writeout_vat(ump);
error = 0;
if (ump->lvclose & UDF_WRITE_PART_BITMAPS) {
/* writeout metadata spacetable if existing */
error = udf_write_metadata_partition_spacetable(ump, MNT_WAIT);
if (error)
printf( "udf_writeout_system_files : "
" writeout of metadata space bitmap failed\n");
/* writeout partition spacetables */
error = udf_write_physical_partition_spacetables(ump, MNT_WAIT);
if (error)
printf( "udf_writeout_system_files : "
"writeout of space tables failed\n");
if (!error && clearflags)
ump->lvclose &= ~UDF_WRITE_PART_BITMAPS;
}
return error;
}
int
udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
{
struct udf_mount *ump = VFSTOUDF(mp);
DPRINTF(CALL, ("udf_sync called\n"));
/* if called when mounted readonly, just ignore */
if (mp->mnt_flag & MNT_RDONLY)
return 0;
/*
* Get vnode for the file system type specific file id ino for the fs. Its
* used for reference to files by unique ID and for NFSv3.
* (optional) TODO lookup why some sources state NFSv3
*/
int
udf_vget(struct mount *mp, ino_t ino, int lktype,
struct vnode **vpp)
{
DPRINTF(NOTIMPL, ("udf_vget called\n"));
return EOPNOTSUPP;
}
/*
* Create an unique file handle. Its structure is opaque and won't be used by
* other subsystems. It should uniquely identify the file in the filingsystem
* and enough information to know if a file has been removed and/or resources
* have been recycled.
*/
int
udf_vptofh(struct vnode *vp, struct fid *fid,
size_t *fh_size)
{
DPRINTF(NOTIMPL, ("udf_vptofh called\n"));
return EOPNOTSUPP;
}
/*
* Create a filingsystem snapshot at the specified timestamp. Could be
* implemented by explicitly creating a new session or with spare room in the
* integrity descriptor space
*/
int
udf_snapshot(struct mount *mp, struct vnode *vp,
struct timespec *tm)
{
DPRINTF(NOTIMPL, ("udf_snapshot called\n"));
return EOPNOTSUPP;
}