331b6cc0ea
Improve support for writing to XFS partitions. Work done by: Russell Cattelan <cattelan at xfs dot org>
473 lines
10 KiB
C
473 lines
10 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_vptofh_t _xfs_vptofh;
|
|
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)
|
|
return error;
|
|
|
|
args->flags |= XFSMNT_32BITINODES;
|
|
|
|
parse_int(mp, "sunit", &args->sunit, &error);
|
|
if (error != 0)
|
|
return error;
|
|
|
|
parse_int(mp, "swidth", &args->swidth, &error);
|
|
if (error != 0)
|
|
return error;
|
|
|
|
parse_int(mp, "logbufs", &args->logbufs, &error);
|
|
if (error != 0)
|
|
return error;
|
|
|
|
parse_int(mp, "logbufsize", &args->logbufsize, &error);
|
|
if (error != 0)
|
|
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;
|
|
struct cdev *ddev;
|
|
int error;
|
|
|
|
if (vfs_filteropt(mp->mnt_optnew, xfs_opts))
|
|
return (EINVAL);
|
|
|
|
if (mp->mnt_flag & MNT_UPDATE)
|
|
return (0);
|
|
|
|
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);
|
|
if (error)
|
|
goto fail_unmount;
|
|
|
|
ddev = XFS_VFSTOM(XFSTOVFS(xmp))->m_ddev_targp->dev;
|
|
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);
|
|
|
|
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;
|
|
{
|
|
int error;
|
|
|
|
XVFS_UNMOUNT(MNTTOVFS(mp), 0, td->td_ucred, error);
|
|
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, curthread);
|
|
}
|
|
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;
|
|
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_vptofh(vp, fhp)
|
|
struct vnode *vp;
|
|
struct fid *fhp;
|
|
{
|
|
printf("xfs_vptofh");
|
|
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_vptofh = _xfs_vptofh,
|
|
.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);
|
|
}
|
|
|
|
struct buf_ops xfs_bo_ops = {
|
|
.bop_name = "XFS",
|
|
.bop_write = xfs_geom_bufwrite,
|
|
.bop_strategy = xfs_geom_strategy,
|
|
.bop_sync = xfs_geom_bufsync,
|
|
};
|