freebsd-dev/sys/gnu/fs/xfs/FreeBSD/xfs_mountops.c
Attilio Rao 09400d5abe - Disallow XFS mounting in write mode. The write support never worked really
and there is no need to maintain it.
- Fix vn_get() in order to let it call vget(9) with a valid locking
  request.  vget(9) returns the vnode locked in order to prevent recycling,
  but in this case internal XFS locks alredy prevent it from happening, so
  it is safe to drop the vnode lock before to return by vn_get().
- Add a VNASSERT() in vget(9) in order to catch malformed locking requests.

Discussed with:	kan, kib
Tested by:	Lothar Braun <lothar at lobraun dot de>
2008-07-21 23:01:09 +00:00

504 lines
11 KiB
C

/*
* Copyright (c) 2001,2006 Alexander Kabaev, Russell Cattelan Digital Elves Inc.
* 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$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <geom/geom.h>
#include <geom/geom_vfs.h>
#include "xfs.h"
#include "xfs_types.h"
#include "xfs_bit.h"
#include "xfs_inum.h"
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_alloc_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_btree.h"
#include "xfs_attr_sf.h"
#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_dinode.h"
#include "xfs_ialloc.h"
#include "xfs_inode.h"
#include "xfs_alloc.h"
#include "xfs_rtalloc.h"
#include "xfs_bmap.h"
#include "xfs_error.h"
#include "xfs_rw.h"
#include "xfs_quota.h"
#include "xfs_fsops.h"
#include "xfs_clnt.h"
#include <xfs_mountops.h>
MALLOC_DEFINE(M_XFSNODE, "XFS node", "XFS vnode private part");
static vfs_mount_t _xfs_mount;
static vfs_unmount_t _xfs_unmount;
static vfs_root_t _xfs_root;
static vfs_quotactl_t _xfs_quotactl;
static vfs_statfs_t _xfs_statfs;
static vfs_sync_t _xfs_sync;
static vfs_vget_t _xfs_vget;
static vfs_fhtovp_t _xfs_fhtovp;
static vfs_init_t _xfs_init;
static vfs_uninit_t _xfs_uninit;
static vfs_extattrctl_t _xfs_extattrctl;
static b_strategy_t xfs_geom_strategy;
static const char *xfs_opts[] =
{ "from", "flags", "logbufs", "logbufsize",
"rtname", "logname", "iosizelog", "sunit",
"swidth", "export",
NULL };
static void
parse_int(struct mount *mp, const char *opt, int *val, int *error)
{
char *tmp, *ep;
tmp = vfs_getopts(mp->mnt_optnew, opt, error);
if (*error != 0) {
return;
}
if (tmp != NULL) {
*val = (int)strtol(tmp, &ep, 10);
if (*ep) {
*error = EINVAL;
return;
}
}
}
static int
_xfs_param_copyin(struct mount *mp, struct thread *td)
{
struct xfsmount *xmp = MNTTOXFS(mp);
struct xfs_mount_args *args = &xmp->m_args;
char *path;
char *fsname;
char *rtname;
char *logname;
int error;
path = vfs_getopts(mp->mnt_optnew, "fspath", &error);
if (error)
return (error);
bzero(args, sizeof(struct xfs_mount_args));
args->logbufs = -1;
args->logbufsize = -1;
parse_int(mp, "flags", &args->flags, &error);
if (error != 0 && error != ENOENT)
return error;
args->flags |= XFSMNT_32BITINODES;
parse_int(mp, "sunit", &args->sunit, &error);
if (error != 0 && error != ENOENT)
return error;
parse_int(mp, "swidth", &args->swidth, &error);
if (error != 0 && error != ENOENT)
return error;
parse_int(mp, "logbufs", &args->logbufs, &error);
if (error != 0 && error != ENOENT)
return error;
parse_int(mp, "logbufsize", &args->logbufsize, &error);
if (error != 0 && error != ENOENT)
return error;
fsname = vfs_getopts(mp->mnt_optnew, "from", &error);
if (error == 0 && fsname != NULL) {
strncpy(args->fsname, fsname, sizeof(args->fsname) - 1);
}
logname = vfs_getopts(mp->mnt_optnew, "logname", &error);
if (error == 0 && logname != NULL) {
strncpy(args->logname, logname, sizeof(args->logname) - 1);
}
rtname = vfs_getopts(mp->mnt_optnew, "rtname", &error);
if (error == 0 && rtname != NULL) {
strncpy(args->rtname, rtname, sizeof(args->rtname) - 1);
}
strncpy(args->mtpt, path, sizeof(args->mtpt));
printf("fsname '%s' logname '%s' rtname '%s'\n"
"flags 0x%x sunit %d swidth %d logbufs %d logbufsize %d\n",
args->fsname, args->logname, args->rtname, args->flags,
args->sunit, args->swidth, args->logbufs, args->logbufsize);
vfs_mountedfrom(mp, args->fsname);
return (0);
}
static int
_xfs_mount(struct mount *mp,
struct thread *td)
{
struct xfsmount *xmp;
struct xfs_vnode *rootvp;
struct ucred *curcred;
struct vnode *rvp, *devvp;
struct cdev *ddev;
struct g_consumer *cp;
int error;
ddev = NULL;
cp = NULL;
if (vfs_filteropt(mp->mnt_optnew, xfs_opts))
return (EINVAL);
if (mp->mnt_flag & MNT_UPDATE)
return (0);
if ((mp->mnt_flag & MNT_RDONLY) == 0)
return (EPERM);
xmp = xfsmount_allocate(mp);
if (xmp == NULL)
return (ENOMEM);
if((error = _xfs_param_copyin(mp, td)) != 0)
goto fail;
curcred = td->td_ucred;
XVFS_MOUNT(XFSTOVFS(xmp), &xmp->m_args, curcred, error);
if (error)
goto fail;
XVFS_ROOT(XFSTOVFS(xmp), &rootvp, error);
ddev = XFS_VFSTOM(XFSTOVFS(xmp))->m_ddev_targp->dev;
devvp = XFS_VFSTOM(XFSTOVFS(xmp))->m_ddev_targp->specvp;
if (error)
goto fail_unmount;
if (ddev->si_iosize_max != 0)
mp->mnt_iosize_max = ddev->si_iosize_max;
if (mp->mnt_iosize_max > MAXPHYS)
mp->mnt_iosize_max = MAXPHYS;
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_stat.f_fsid.val[0] = dev2udev(ddev);
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
if ((error = VFS_STATFS(mp, &mp->mnt_stat, td)) != 0)
goto fail_unmount;
rvp = rootvp->v_vnode;
rvp->v_vflag |= VV_ROOT;
VN_RELE(rootvp);
return (0);
fail_unmount:
XVFS_UNMOUNT(XFSTOVFS(xmp), 0, curcred, error);
if (devvp != NULL) {
cp = devvp->v_bufobj.bo_private;
if (cp != NULL) {
DROP_GIANT();
g_topology_lock();
g_vfs_close(cp, td);
g_topology_unlock();
PICKUP_GIANT();
}
}
fail:
if (xmp != NULL)
xfsmount_deallocate(xmp);
return (error);
}
/*
* Free reference to null layer
*/
static int
_xfs_unmount(mp, mntflags, td)
struct mount *mp;
int mntflags;
struct thread *td;
{
struct vnode *devvp;
struct g_consumer *cp;
int error;
cp = NULL;
devvp = NULL;
devvp = XFS_VFSTOM((MNTTOVFS(mp)))->m_ddev_targp->specvp;
if (devvp != NULL)
cp = devvp->v_bufobj.bo_private;
XVFS_UNMOUNT(MNTTOVFS(mp), 0, td->td_ucred, error);
if (error == 0) {
if (cp != NULL) {
DROP_GIANT();
g_topology_lock();
g_vfs_close(cp, td);
g_topology_unlock();
PICKUP_GIANT();
}
}
return (error);
}
static int
_xfs_root(mp, flags, vpp, td)
struct mount *mp;
int flags;
struct vnode **vpp;
struct thread *td;
{
xfs_vnode_t *vp;
int error;
XVFS_ROOT(MNTTOVFS(mp), &vp, error);
if (error == 0) {
*vpp = vp->v_vnode;
VOP_LOCK(*vpp, flags);
}
return (error);
}
static int
_xfs_quotactl(mp, cmd, uid, arg, td)
struct mount *mp;
int cmd;
uid_t uid;
void *arg;
struct thread *td;
{
printf("xfs_quotactl\n");
return EOPNOTSUPP;
}
static int
_xfs_statfs(mp, sbp, td)
struct mount *mp;
struct statfs *sbp;
struct thread *td;
{
int error;
XVFS_STATVFS(MNTTOVFS(mp), sbp, NULL, error);
if (error)
return error;
/* Fix up the values XFS statvfs calls does not know about. */
sbp->f_iosize = sbp->f_bsize;
return (error);
}
static int
_xfs_sync(mp, waitfor, td)
struct mount *mp;
int waitfor;
struct thread *td;
{
int error;
int flags = SYNC_FSDATA|SYNC_ATTR|SYNC_REFCACHE;
if (waitfor == MNT_WAIT)
flags |= SYNC_WAIT;
else if (waitfor == MNT_LAZY)
flags |= SYNC_BDFLUSH;
XVFS_SYNC(MNTTOVFS(mp), flags, td->td_ucred, error);
return (error);
}
static int
_xfs_vget(mp, ino, flags, vpp)
struct mount *mp;
ino_t ino;
int flags;
struct vnode **vpp;
{
xfs_vnode_t *vp = NULL;
int error;
printf("XVFS_GET_VNODE(MNTTOVFS(mp), &vp, ino, error);\n");
error = ENOSYS;
if (error == 0)
*vpp = vp->v_vnode;
return (error);
}
static int
_xfs_fhtovp(mp, fidp, vpp)
struct mount *mp;
struct fid *fidp;
struct vnode **vpp;
{
printf("xfs_fhtovp\n");
return ENOSYS;
}
static int
_xfs_extattrctl(struct mount *mp, int cm,
struct vnode *filename_v,
int attrnamespace, const char *attrname,
struct thread *td)
{
printf("xfs_extattrctl\n");
return ENOSYS;
}
int
_xfs_init(vfsp)
struct vfsconf *vfsp;
{
int error;
error = init_xfs_fs();
return (error);
}
int
_xfs_uninit(vfsp)
struct vfsconf *vfsp;
{
exit_xfs_fs();
return 0;
}
static struct vfsops xfs_fsops = {
.vfs_mount = _xfs_mount,
.vfs_unmount = _xfs_unmount,
.vfs_root = _xfs_root,
.vfs_quotactl = _xfs_quotactl,
.vfs_statfs = _xfs_statfs,
.vfs_sync = _xfs_sync,
.vfs_vget = _xfs_vget,
.vfs_fhtovp = _xfs_fhtovp,
.vfs_init = _xfs_init,
.vfs_uninit = _xfs_uninit,
.vfs_extattrctl = _xfs_extattrctl,
};
VFS_SET(xfs_fsops, xfs, 0);
/*
* Copy GEOM VFS functions here to provide a conveniet place to
* track all XFS-related IO without being distracted by other
* filesystems which happen to be mounted on the machine at the
* same time.
*/
static void
xfs_geom_biodone(struct bio *bip)
{
struct buf *bp;
if (bip->bio_error) {
printf("g_vfs_done():");
g_print_bio(bip);
printf("error = %d\n", bip->bio_error);
}
bp = bip->bio_caller2;
bp->b_error = bip->bio_error;
bp->b_ioflags = bip->bio_flags;
if (bip->bio_error)
bp->b_ioflags |= BIO_ERROR;
bp->b_resid = bp->b_bcount - bip->bio_completed;
g_destroy_bio(bip);
mtx_lock(&Giant);
bufdone(bp);
mtx_unlock(&Giant);
}
static void
xfs_geom_strategy(struct bufobj *bo, struct buf *bp)
{
struct g_consumer *cp;
struct bio *bip;
cp = bo->bo_private;
G_VALID_CONSUMER(cp);
bip = g_alloc_bio();
bip->bio_cmd = bp->b_iocmd;
bip->bio_offset = bp->b_iooffset;
bip->bio_data = bp->b_data;
bip->bio_done = xfs_geom_biodone;
bip->bio_caller2 = bp;
bip->bio_length = bp->b_bcount;
g_io_request(bip, cp);
}
static int
xfs_geom_bufwrite(struct buf *bp)
{
return bufwrite(bp);
}
static int
xfs_geom_bufsync(struct bufobj *bo, int waitfor, struct thread *td)
{
return bufsync(bo,waitfor,td);
}
static void
xfs_geom_bufbdflush(struct bufobj *bo, struct buf *bp)
{
bufbdflush(bo, bp);
}
struct buf_ops xfs_bo_ops = {
.bop_name = "XFS",
.bop_write = xfs_geom_bufwrite,
.bop_strategy = xfs_geom_strategy,
.bop_sync = xfs_geom_bufsync,
.bop_bdflush = xfs_geom_bufbdflush,
};