Add an implementation of the 9P filesystem

This is derived from swills@ fork of the Juniper virtfs with many
changes by me including bug fixes, style improvements, clearer layering
and more consistent logging. The filesystem is renamed to p9fs to better
reflect its function and to prevent possible future confusion with
virtio-fs.

Several updates and fixes from Juniper have been integrated into this
version by Val Packett and these contributions along with the original
Juniper authors are credited below.

To use this with bhyve, add 'virtio_p9fs_load=YES' to loader.conf. The
bhyve virtio-9p device allows access from the guest to files on the host
by mapping a 'sharename' to a host path. It is possible to use p9fs as a
root filesystem by adding this to /boot/loader.conf:

	vfs.root.mountfrom="p9fs:sharename"

for non-root filesystems add something like this to /etc/fstab:

	sharename /mnt p9fs rw 0 0

In both examples, substitute the share name used on the bhyve command
line.

The 9P filesystem protocol relies on stateful file opens which map
protocol-level FIDs to host file descriptors. The FreeBSD vnode
interface doesn't really support this and we use heuristics to guess the
right FID to use for file operations.  This can be confused by privilege
lowering and does not guarantee that the FID created for a given file
open is always used for file operations, even if the calling process is
using the file descriptor from the original open call. Improving this
would involve changes to the vnode interface which is out-of-scope for
this import.

Differential Revision: https://reviews.freebsd.org/D41844
Reviewed by: kib, emaste, dch
MFC after: 3 months
Co-authored-by: Val Packett <val@packett.cool>
Co-authored-by: Ka Ho Ng <kahon@juniper.net>
Co-authored-by: joyu <joyul@juniper.net>
Co-authored-by: Kumara Babu Narayanaswamy <bkumara@juniper.net>
This commit is contained in:
Doug Rabson 2022-12-06 13:07:46 +00:00
parent 8a607939de
commit e97ad33a89
23 changed files with 6694 additions and 1 deletions

View File

@ -35,6 +35,7 @@ MAN= acct.5 \
nsmb.conf.5 \
nsswitch.conf.5 \
os-release.5 \
p9fs.5 \
passwd.5 \
pbm.5 \
periodic.conf.5 \

127
share/man/man5/p9fs.5 Normal file
View File

@ -0,0 +1,127 @@
.\"
.\" Copyright (c) 2022-present Doug Rabson
.\" 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 DOCUMENTATION 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.
.\"
.Dd December 7, 2022
.Dt P9FS 5
.Os
.Sh NAME
.Nm p9fs
.Nd "9P file system"
.Sh SYNOPSIS
To use this filesystem,
either add the following to the kernel config:
.Bd -ragged -offset indent
.Cd "options P9FS"
.Cd "device virtio_p9fs"
.Ed
.Pp
Alternatively, load the driver as a kernel module,
either at boot time by adding the following to
.Xr loader.conf 5 :
.Bd -literal -offset indent
virtio_p9fs_load="YES"
.Ed
.Pp
or on system startup using the command:
.Pp
.Dl "# sysrc kld_list+=virtio_p9fs"
.Sh DESCRIPTION
The
.Nm
filesystem uses the 9P protocol to mount a host file system directory
into a
.Xr bhyve 8
guest.
Multiple host directories can be accessed using the
.Xr bhyve 8
virtio-9p virtual PCI device.
Each device is configured with a share name and a host directory path.
The share name can be used with
.Xr mount 8
to mount the host directory in the guest:
.Pp
.Dl "# mount -t p9fs mysharename /mnt"
.Pp
Host directories can be mounted on system startup using
.Xr fstab 5
like this:
.Pp
.Bd -literal -offset indent
mysharename /mnt p9fs rw 0 0
.Ed
.Pp
Using
.Nm
as a root file system is supported by adding the following to
.Xr loader.conf 5 :
.Bd -literal -offset indent
vfs.root.mountfrom="p9fs:mysharename"
.Ed
.Sh LIMITATIONS
The 9P protocol relies on stateful file opens
which map protocol-level FIDs to host file descriptors.
The FreeBSD vnode interface doesn't support this and
.Nm
uses heuristics to guess the right FID to use for file operations.
.Pp
This can be confused by privilege lowering and
does not guarantee that the FID created for a
given file open is always used,
even if the calling process is using the file descriptor from
the original open call.
.Pp
In particular, accessing unlinked files using open file descriptor
may not work correctly.
If
.Nm
is the root filesystem,
it is recommented to use with
.Xr tmpfs 5
to ensure that temporary files created in
.Pa /tmp
or
.Pa /var/tmp
have the expected semantics.
.Sh SEE ALSO
.Xr fstab 5
.Sh HISTORY
The 9P protocol first appeared in the Plan 9 operating system.
More recently, the protocol has been widely used with virtual machines
to allow the use of host file resources inside a guest VM.
.Sh AUTHORS
This is derived from software released by Juniper Networks, Inc.
with many improvements and fixes from
.An Steve Wills .
.Pp
This manual page was written by
.An -nosplit
.An Doug Rabson Aq Mt dfr@FreeBSD.org .
.Sh BUGS
A better name for this filesystem would be
.Ar 9pfs
but for technical reasons,
the names of filesystems must be valid C identifiers.
As a compromise,
the filesystem is named
.Nm .

View File

@ -3444,6 +3444,7 @@ dev/virtio/balloon/virtio_balloon.c optional virtio_balloon
dev/virtio/block/virtio_blk.c optional virtio_blk
dev/virtio/console/virtio_console.c optional virtio_console
dev/virtio/gpu/virtio_gpu.c optional virtio_gpu
dev/virtio/p9fs/virtio_p9fs.c optional virtio_p9fs
dev/virtio/random/virtio_random.c optional virtio_random
dev/virtio/scmi/virtio_scmi.c optional virtio_scmi
dev/virtio/scsi/virtio_scsi.c optional virtio_scsi
@ -3593,6 +3594,12 @@ fs/nfsserver/nfs_nfsdcache.c optional nfsd inet
fs/nullfs/null_subr.c optional nullfs
fs/nullfs/null_vfsops.c optional nullfs
fs/nullfs/null_vnops.c optional nullfs
fs/p9fs/p9_client.c optional p9fs
fs/p9fs/p9_protocol.c optional p9fs
fs/p9fs/p9_transport.c optional p9fs
fs/p9fs/p9fs_subr.c optional p9fs
fs/p9fs/p9fs_vfsops.c optional p9fs
fs/p9fs/p9fs_vnops.c optional p9fs
fs/procfs/procfs.c optional procfs
fs/procfs/procfs_dbregs.c optional procfs
fs/procfs/procfs_fpregs.c optional procfs

View File

@ -284,6 +284,7 @@ TMPFS opt_dontuse.h
UDF opt_dontuse.h
UNIONFS opt_dontuse.h
ZFS opt_dontuse.h
P9FS opt_dontuse.h
# Pseudofs debugging
PSEUDOFS_TRACE opt_pseudofs.h

View File

@ -0,0 +1,511 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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 ``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.
*
*/
/*
* The Virtio 9P transport driver. This file contains all functions related to
* the virtqueue infrastructure which include creating the virtqueue, host
* interactions, interrupts etc.
*/
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/module.h>
#include <sys/sglist.h>
#include <sys/queue.h>
#include <sys/bus.h>
#include <sys/kthread.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <fs/p9fs/p9_client.h>
#include <fs/p9fs/p9_debug.h>
#include <fs/p9fs/p9_protocol.h>
#include <fs/p9fs/p9_transport.h>
#include <dev/virtio/virtio.h>
#include <dev/virtio/virtqueue.h>
#include <dev/virtio/virtio_ring.h>
#include <dev/virtio/p9fs/virtio_p9fs.h>
#define VT9P_MTX(_sc) (&(_sc)->vt9p_mtx)
#define VT9P_LOCK(_sc) mtx_lock(VT9P_MTX(_sc))
#define VT9P_UNLOCK(_sc) mtx_unlock(VT9P_MTX(_sc))
#define VT9P_LOCK_INIT(_sc) mtx_init(VT9P_MTX(_sc), \
"VIRTIO 9P CHAN lock", NULL, MTX_DEF)
#define VT9P_LOCK_DESTROY(_sc) mtx_destroy(VT9P_MTX(_sc))
#define MAX_SUPPORTED_SGS 20
static MALLOC_DEFINE(M_P9FS_MNTTAG, "p9fs_mount_tag", "P9fs Mounttag");
struct vt9p_softc {
device_t vt9p_dev;
struct mtx vt9p_mtx;
struct sglist *vt9p_sglist;
struct cv submit_cv;
bool busy;
struct virtqueue *vt9p_vq;
int max_nsegs;
uint16_t mount_tag_len;
char *mount_tag;
STAILQ_ENTRY(vt9p_softc) chan_next;
};
/* Global channel list, Each channel will correspond to a mount point */
static STAILQ_HEAD( ,vt9p_softc) global_chan_list;
struct mtx global_chan_list_mtx;
static struct virtio_feature_desc virtio_9p_feature_desc[] = {
{ VIRTIO_9PNET_F_MOUNT_TAG, "9PMountTag" },
{ 0, NULL }
};
static void
global_chan_list_init(void)
{
mtx_init(&global_chan_list_mtx, "9pglobal",
NULL, MTX_DEF);
STAILQ_INIT(&global_chan_list);
}
SYSINIT(global_chan_list_init, SI_SUB_KLD, SI_ORDER_FIRST,
global_chan_list_init, NULL);
/* We don't currently allow canceling of virtio requests */
static int
vt9p_cancel(void *handle, struct p9_req_t *req)
{
return (1);
}
SYSCTL_NODE(_vfs, OID_AUTO, 9p, CTLFLAG_RW, 0, "9P File System Protocol");
/*
* Maximum number of seconds vt9p_request thread sleep waiting for an
* ack from the host, before exiting
*/
static unsigned int vt9p_ackmaxidle = 120;
SYSCTL_UINT(_vfs_9p, OID_AUTO, ackmaxidle, CTLFLAG_RW, &vt9p_ackmaxidle, 0,
"Maximum time request thread waits for ack from host");
/*
* Wait for completion of a p9 request.
*
* This routine will sleep and release the chan mtx during the period.
* chan mtx will be acquired again upon return.
*/
static int
vt9p_req_wait(struct vt9p_softc *chan, struct p9_req_t *req)
{
if (req->tc->tag != req->rc->tag) {
if (msleep(req, VT9P_MTX(chan), 0, "chan lock",
vt9p_ackmaxidle * hz)) {
/*
* Waited for 120s. No response from host.
* Can't wait for ever..
*/
P9_DEBUG(ERROR, "Timeout after waiting %u seconds"
"for an ack from host\n", vt9p_ackmaxidle);
return (EIO);
}
KASSERT(req->tc->tag == req->rc->tag,
("Spurious event on p9 req"));
}
return (0);
}
/*
* Request handler. This is called for every request submitted to the host
* It basically maps the tc/rc buffers to sg lists and submits the requests
* into the virtqueue. Since we have implemented a synchronous version, the
* submission thread sleeps until the ack in the interrupt wakes it up. Once
* it wakes up, it returns back to the P9fs layer. The rc buffer is then
* processed and completed to its upper layers.
*/
static int
vt9p_request(void *handle, struct p9_req_t *req)
{
int error;
struct vt9p_softc *chan;
int readable, writable;
struct sglist *sg;
struct virtqueue *vq;
chan = handle;
sg = chan->vt9p_sglist;
vq = chan->vt9p_vq;
P9_DEBUG(TRANS, "%s: req=%p\n", __func__, req);
/* Grab the channel lock*/
VT9P_LOCK(chan);
sglist_reset(sg);
/* Handle out VirtIO ring buffers */
error = sglist_append(sg, req->tc->sdata, req->tc->size);
if (error != 0) {
P9_DEBUG(ERROR, "%s: sglist append failed\n", __func__);
VT9P_UNLOCK(chan);
return (error);
}
readable = sg->sg_nseg;
error = sglist_append(sg, req->rc->sdata, req->rc->capacity);
if (error != 0) {
P9_DEBUG(ERROR, "%s: sglist append failed\n", __func__);
VT9P_UNLOCK(chan);
return (error);
}
writable = sg->sg_nseg - readable;
req_retry:
error = virtqueue_enqueue(vq, req, sg, readable, writable);
if (error != 0) {
if (error == ENOSPC) {
/*
* Condvar for the submit queue. Unlock the chan
* since wakeup needs one.
*/
cv_wait(&chan->submit_cv, VT9P_MTX(chan));
P9_DEBUG(TRANS, "%s: retry virtio request\n", __func__);
goto req_retry;
} else {
P9_DEBUG(ERROR, "%s: virtio enuqueue failed \n", __func__);
VT9P_UNLOCK(chan);
return (EIO);
}
}
/* We have to notify */
virtqueue_notify(vq);
error = vt9p_req_wait(chan, req);
if (error != 0) {
VT9P_UNLOCK(chan);
return (error);
}
VT9P_UNLOCK(chan);
P9_DEBUG(TRANS, "%s: virtio request kicked\n", __func__);
return (0);
}
/*
* Completion of the request from the virtqueue. This interrupt handler is
* setup at initialization and is called for every completing request. It
* just wakes up the sleeping submission requests.
*/
static void
vt9p_intr_complete(void *xsc)
{
struct vt9p_softc *chan;
struct virtqueue *vq;
struct p9_req_t *curreq;
chan = (struct vt9p_softc *)xsc;
vq = chan->vt9p_vq;
P9_DEBUG(TRANS, "%s: completing\n", __func__);
VT9P_LOCK(chan);
while ((curreq = virtqueue_dequeue(vq, NULL)) != NULL) {
curreq->rc->tag = curreq->tc->tag;
wakeup_one(curreq);
}
virtqueue_enable_intr(vq);
cv_signal(&chan->submit_cv);
VT9P_UNLOCK(chan);
}
/*
* Allocation of the virtqueue with interrupt complete routines.
*/
static int
vt9p_alloc_virtqueue(struct vt9p_softc *sc)
{
struct vq_alloc_info vq_info;
device_t dev;
dev = sc->vt9p_dev;
VQ_ALLOC_INFO_INIT(&vq_info, sc->max_nsegs,
vt9p_intr_complete, sc, &sc->vt9p_vq,
"%s request", device_get_nameunit(dev));
return (virtio_alloc_virtqueues(dev, 1, &vq_info));
}
/* Probe for existence of 9P virtio channels */
static int
vt9p_probe(device_t dev)
{
/* If the virtio device type is a 9P device, then we claim and attach it */
if (virtio_get_device_type(dev) != VIRTIO_ID_9P)
return (ENXIO);
device_set_desc(dev, "VirtIO 9P Transport");
return (BUS_PROBE_DEFAULT);
}
static void
vt9p_stop(struct vt9p_softc *sc)
{
/* Device specific stops .*/
virtqueue_disable_intr(sc->vt9p_vq);
virtio_stop(sc->vt9p_dev);
}
/* Detach the 9P virtio PCI device */
static int
vt9p_detach(device_t dev)
{
struct vt9p_softc *sc;
sc = device_get_softc(dev);
VT9P_LOCK(sc);
vt9p_stop(sc);
VT9P_UNLOCK(sc);
if (sc->vt9p_sglist) {
sglist_free(sc->vt9p_sglist);
sc->vt9p_sglist = NULL;
}
if (sc->mount_tag) {
free(sc->mount_tag, M_P9FS_MNTTAG);
sc->mount_tag = NULL;
}
mtx_lock(&global_chan_list_mtx);
STAILQ_REMOVE(&global_chan_list, sc, vt9p_softc, chan_next);
mtx_unlock(&global_chan_list_mtx);
VT9P_LOCK_DESTROY(sc);
cv_destroy(&sc->submit_cv);
return (0);
}
/* Attach the 9P virtio PCI device */
static int
vt9p_attach(device_t dev)
{
struct sysctl_ctx_list *ctx;
struct sysctl_oid *tree;
struct vt9p_softc *chan;
char *mount_tag;
int error;
uint16_t mount_tag_len;
chan = device_get_softc(dev);
chan->vt9p_dev = dev;
/* Init the channel lock. */
VT9P_LOCK_INIT(chan);
/* Initialize the condition variable */
cv_init(&chan->submit_cv, "Conditional variable for submit queue" );
chan->max_nsegs = MAX_SUPPORTED_SGS;
chan->vt9p_sglist = sglist_alloc(chan->max_nsegs, M_NOWAIT);
/* Negotiate the features from the host */
virtio_set_feature_desc(dev, virtio_9p_feature_desc);
virtio_negotiate_features(dev, VIRTIO_9PNET_F_MOUNT_TAG);
/*
* If mount tag feature is supported read the mount tag
* from device config
*/
if (virtio_with_feature(dev, VIRTIO_9PNET_F_MOUNT_TAG))
mount_tag_len = virtio_read_dev_config_2(dev,
offsetof(struct virtio_9pnet_config, mount_tag_len));
else {
error = EINVAL;
P9_DEBUG(ERROR, "%s: Mount tag feature not supported by host\n", __func__);
goto out;
}
mount_tag = malloc(mount_tag_len + 1, M_P9FS_MNTTAG,
M_WAITOK | M_ZERO);
virtio_read_device_config(dev,
offsetof(struct virtio_9pnet_config, mount_tag),
mount_tag, mount_tag_len);
device_printf(dev, "Mount tag: %s\n", mount_tag);
mount_tag_len++;
chan->mount_tag_len = mount_tag_len;
chan->mount_tag = mount_tag;
ctx = device_get_sysctl_ctx(dev);
tree = device_get_sysctl_tree(dev);
SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "p9fs_mount_tag",
CTLFLAG_RD, chan->mount_tag, 0, "Mount tag");
if (chan->vt9p_sglist == NULL) {
error = ENOMEM;
P9_DEBUG(ERROR, "%s: Cannot allocate sglist\n", __func__);
goto out;
}
/* We expect one virtqueue, for requests. */
error = vt9p_alloc_virtqueue(chan);
if (error != 0) {
P9_DEBUG(ERROR, "%s: Allocating the virtqueue failed \n", __func__);
goto out;
}
error = virtio_setup_intr(dev, INTR_TYPE_MISC|INTR_MPSAFE);
if (error != 0) {
P9_DEBUG(ERROR, "%s: Cannot setup virtqueue interrupt\n", __func__);
goto out;
}
error = virtqueue_enable_intr(chan->vt9p_vq);
if (error != 0) {
P9_DEBUG(ERROR, "%s: Cannot enable virtqueue interrupt\n", __func__);
goto out;
}
mtx_lock(&global_chan_list_mtx);
/* Insert the channel in global channel list */
STAILQ_INSERT_HEAD(&global_chan_list, chan, chan_next);
mtx_unlock(&global_chan_list_mtx);
return (0);
out:
/* Something went wrong, detach the device */
vt9p_detach(dev);
return (error);
}
/*
* Allocate a new virtio channel. This sets up a transport channel
* for 9P communication
*/
static int
vt9p_create(const char *mount_tag, void **handlep)
{
struct vt9p_softc *sc, *chan;
chan = NULL;
/*
* Find out the corresponding channel for a client from global list
* of channels based on mount tag and attach it to client
*/
mtx_lock(&global_chan_list_mtx);
STAILQ_FOREACH(sc, &global_chan_list, chan_next) {
if (!strcmp(sc->mount_tag, mount_tag)) {
chan = sc;
break;
}
}
mtx_unlock(&global_chan_list_mtx);
/*
* If chan is already attached to a client then it cannot be used for
* another client.
*/
if (chan && chan->busy) {
//p9_debug(TRANS, "Channel busy: used by clnt=%p\n", chan->client);
return (EBUSY);
}
/* If we dont have one, for now bail out.*/
if (chan) {
*handlep = (void *)chan;
chan->busy = TRUE;
} else {
P9_DEBUG(TRANS, "%s: No Global channel with mount_tag=%s\n",
__func__, mount_tag);
return (EINVAL);
}
return (0);
}
static void
vt9p_close(void *handle)
{
struct vt9p_softc *chan = handle;
chan->busy = FALSE;
}
static struct p9_trans_module vt9p_trans = {
.name = "virtio",
.create = vt9p_create,
.close = vt9p_close,
.request = vt9p_request,
.cancel = vt9p_cancel,
};
static device_method_t vt9p_mthds[] = {
/* Device methods. */
DEVMETHOD(device_probe, vt9p_probe),
DEVMETHOD(device_attach, vt9p_attach),
DEVMETHOD(device_detach, vt9p_detach),
DEVMETHOD_END
};
static driver_t vt9p_drv = {
"virtio_p9fs",
vt9p_mthds,
sizeof(struct vt9p_softc)
};
static int
vt9p_modevent(module_t mod, int type, void *unused)
{
int error;
error = 0;
switch (type) {
case MOD_LOAD:
p9_init_zones();
p9_register_trans(&vt9p_trans);
break;
case MOD_UNLOAD:
p9_destroy_zones();
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
DRIVER_MODULE(virtio_p9fs, virtio_pci, vt9p_drv, vt9p_modevent, 0);
MODULE_VERSION(virtio_p9fs, 1);
MODULE_DEPEND(virtio_p9fs, virtio, 1, 1, 1);
MODULE_DEPEND(virtio_p9fs, p9fs, 1, 1, 1);

View File

@ -0,0 +1,39 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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 ``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.
*
*/
#ifndef __VIRTIO_9P_CONFIG__
#define __VIRTIO_9P_CONFIG__
/* Mount point feature specified in config variable */
#define VIRTIO_9PNET_F_MOUNT_TAG 1
struct virtio_9pnet_config {
/* Mount tag length */
uint16_t mount_tag_len;
/* non NULL terminated tag name */
uint8_t mount_tag[0];
};
#endif /* __VIRTIO_9P_CONFIG__ */

1311
sys/fs/p9fs/p9_client.c Normal file

File diff suppressed because it is too large Load Diff

168
sys/fs/p9fs/p9_client.h Normal file
View File

@ -0,0 +1,168 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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.
*/
/* 9P client definitions */
#ifndef FS_P9FS_P9_CLIENT_H
#define FS_P9FS_P9_CLIENT_H
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/_unrhdr.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/dirent.h>
#include <machine/stdarg.h>
#include <fs/p9fs/p9_protocol.h>
/* 9P protocol versions */
enum p9_proto_versions {
p9_proto_legacy, /* legacy version */
p9_proto_2000u, /* Unix version */
p9_proto_2000L, /* Linux version */
};
/* P9 Request exchanged between Host and Guest */
struct p9_req_t {
struct p9_buffer *tc; /* request buffer */
struct p9_buffer *rc; /* response buffer */
};
/* 9P transport status */
enum transport_status {
P9FS_CONNECT, /* transport is connected */
P9FS_BEGIN_DISCONNECT,/* transport has begun to disconnect */
P9FS_DISCONNECT, /* transport has been dosconnected */
};
/* This is set by QEMU so we will oblige */
#define P9FS_MTU 8192
/*
* Even though we have a 8k buffer, Qemu is typically doing 8168
* because of a HDR of 24. Use that amount for transfers so that we dont
* drop anything.
*/
#define P9FS_IOUNIT (P9FS_MTU - 24)
#define P9FS_DIRENT_LEN 256
#define P9_NOTAG 0
/* Client state information */
struct p9_client {
struct p9_trans_module *ops; /* module API instantiated with this client */
void *handle; /* module-specific client handle */
struct mtx clnt_mtx; /* mutex to lock the client */
struct mtx req_mtx; /* mutex to lock the request buffer */
struct cv req_cv; /* condition variable on which to wake up thread */
unsigned int msize; /* maximum data size */
unsigned char proto_version; /* 9P version to use */
struct unrhdr fidpool; /* fid handle accounting for session */
struct unrhdr tagpool; /* transaction id accounting for session */
enum transport_status trans_status; /* tranport instance state */
};
/* The main fid structure which keeps track of the file.*/
struct p9_fid {
struct p9_client *clnt; /* the instatntiating 9P client */
uint32_t fid; /* numeric identifier */
int mode; /* current mode of this fid */
struct p9_qid qid; /* server identifier */
uint32_t mtu; /* max transferrable unit at a time */
uid_t uid; /* numeric uid of the local user who owns this handle */
int v_opens; /* keep count on the number of opens called with this fiel handle */
STAILQ_ENTRY(p9_fid) fid_next; /* points to next fid in the list */
};
/* Directory entry structure */
struct p9_dirent {
struct p9_qid qid; /* 9P server qid for this dirent */
uint64_t d_off; /* offset to the next dirent */
unsigned char d_type; /* file type */
char d_name[P9FS_DIRENT_LEN]; /* file name */
int len;
};
void p9_init_zones(void);
void p9_destroy_zones(void);
/* Session and client Init Ops */
struct p9_client *p9_client_create(struct mount *mp, int *error,
const char *mount_tag);
void p9_client_destroy(struct p9_client *clnt);
struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *fid,
const char *uname, uid_t n_uname, const char *aname, int *error);
/* FILE OPS - These are individually called from the specific vop function */
int p9_client_open(struct p9_fid *fid, int mode);
int p9_client_close(struct p9_fid *fid);
struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwnames,
char **wnames, int clone, int *error);
struct p9_fid *p9_fid_create(struct p9_client *clnt);
void p9_fid_destroy(struct p9_fid *fid);
uint16_t p9_tag_create(struct p9_client *clnt);
void p9_tag_destroy(struct p9_client *clnt, uint16_t tag);
int p9_client_clunk(struct p9_fid *fid);
int p9_client_version(struct p9_client *clnt);
int p9_client_readdir(struct p9_fid *fid, char *data, uint64_t offset, uint32_t count);
int p9_client_read(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data);
int p9_client_write(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data);
int p9_client_file_create(struct p9_fid *fid, char *name, uint32_t perm, int mode,
char *extension);
int p9_client_remove(struct p9_fid *fid);
int p9_dirent_read(struct p9_client *clnt, char *buf, int start, int len,
struct p9_dirent *dirent);
int p9_client_statfs(struct p9_fid *fid, struct p9_statfs *stat);
int p9_client_statread(struct p9_client *clnt, char *data, size_t len, struct p9_wstat *st);
int p9_is_proto_dotu(struct p9_client *clnt);
int p9_is_proto_dotl(struct p9_client *clnt);
void p9_client_cb(struct p9_client *c, struct p9_req_t *req);
int p9stat_read(struct p9_client *clnt, char *data, size_t len, struct p9_wstat *st);
void p9_client_disconnect(struct p9_client *clnt);
void p9_client_begin_disconnect(struct p9_client *clnt);
int p9_create_symlink(struct p9_fid *fid, char *name, char *symtgt, gid_t gid);
int p9_create_hardlink(struct p9_fid *dfid, struct p9_fid *oldfid, char *name);
int p9_readlink(struct p9_fid *fid, char **target);
int p9_client_renameat(struct p9_fid *oldfid, char *oldname, struct p9_fid *newfid, char *newname);
int p9_client_getattr(struct p9_fid *fid, struct p9_stat_dotl *stat_dotl,
uint64_t request_mask);
int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr);
int p9_buf_vwritef(struct p9_buffer *buf, int proto_version, const char *fmt,
va_list ap);
int p9_buf_readf(struct p9_buffer *buf, int proto_version, const char *fmt, ...);
int p9_buf_prepare(struct p9_buffer *buf, int8_t type);
int p9_buf_finalize(struct p9_client *clnt, struct p9_buffer *buf);
void p9_buf_reset(struct p9_buffer *buf);
#endif /* FS_P9FS_P9_CLIENT_H */

45
sys/fs/p9fs/p9_debug.h Normal file
View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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.
*/
#ifndef FS_P9FS_P9_DEBUG_H
#define FS_P9FS_P9_DEBUG_H
extern int p9_debug_level; /* All debugs on now */
/* 9P debug flags */
#define P9_DEBUG_TRANS 0x0001 /* Trace transport */
#define P9_DEBUG_SUBR 0x0002 /* Trace driver submissions */
#define P9_DEBUG_LPROTO 0x0004 /* Low level protocol tracing */
#define P9_DEBUG_PROTO 0x0008 /* High level protocol tracing */
#define P9_DEBUG_VOPS 0x0010 /* VOPs tracing */
#define P9_DEBUG_ERROR 0x0020 /* verbose error messages */
#define P9_DEBUG(category, fmt, ...) do { \
if ((p9_debug_level & P9_DEBUG_##category) != 0) \
printf(fmt, ##__VA_ARGS__); \
} while (0)
#endif /* FS_P9FS_P9_DEBUG_H */

632
sys/fs/p9fs/p9_protocol.c Normal file
View File

@ -0,0 +1,632 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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 ``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.
*
*/
/*
* 9P Protocol Support Code
* This file provides the standard for the FS interactions with the server
* interface as it can understand only this protocol. The details of the
* protocol can be found here
* XXX (link to protocol details page on FreeBSD wiki)
*/
#include <sys/types.h>
#include <fs/p9fs/p9_client.h>
#include <fs/p9fs/p9_debug.h>
#include <fs/p9fs/p9_protocol.h>
#define P9FS_MAXLEN 255
static int p9_buf_writef(struct p9_buffer *buf, int proto_version,
const char *fmt, ...);
static void stat_free(struct p9_wstat *sbuf);
static void
stat_free(struct p9_wstat *stbuf)
{
free(stbuf->name, M_TEMP);
free(stbuf->uid, M_TEMP);
free(stbuf->gid, M_TEMP);
free(stbuf->muid, M_TEMP);
free(stbuf->extension, M_TEMP);
}
static size_t
buf_read(struct p9_buffer *buf, void *data, size_t size)
{
size_t len;
len = min(buf->size - buf->offset, size);
memcpy(data, &buf->sdata[buf->offset], len);
buf->offset += len;
return (size - len);
}
static size_t
buf_write(struct p9_buffer *buf, const void *data, size_t size)
{
size_t len;
len = min(buf->capacity - buf->size, size);
memcpy(&buf->sdata[buf->size], data, len);
buf->size += len;
return (size - len);
}
/*
* Main buf_read routine. This copies the data from the buffer into the
* respective values based on the data type.
* Here
* b - int8_t
* w - int16_t
* d - int32_t
* q - int64_t
* s - string
* u - uid
* g - gid
* Q - qid
* S - stat
* A - getattr (9P2000.L)
* D - data blob (int32_t size followed by void *, results are not freed)
* T - array of strings (int16_t count, followed by strings)
* R - array of qids (int16_t count, followed by qids)
* ? - return if version is not .u or .l
*/
static int
p9_buf_vreadf(struct p9_buffer *buf, int proto_version, const char *fmt,
va_list ap)
{
const char *ptr;
int error;
error = 0;
for (ptr = fmt; *ptr; ptr++) {
switch (*ptr) {
case 'b':
{
int8_t *val = va_arg(ap, int8_t *);
if (buf_read(buf, val, sizeof(*val)))
error = EFAULT;
break;
}
case 'w':
{
int16_t *val = va_arg(ap, int16_t *);
if (buf_read(buf, val, sizeof(*val)))
error = EFAULT;
break;
}
case 'd':
{
int32_t *val = va_arg(ap, int32_t *);
if (buf_read(buf, val, sizeof(*val)))
error = EFAULT;
break;
}
case 'q':
{
int64_t *val = va_arg(ap, int64_t *);
if (buf_read(buf, val, sizeof(*val)))
error = EFAULT;
break;
}
case 's':
{
char **sptr_p = va_arg(ap, char **);
uint16_t len;
char *sptr;
error = buf_read(buf, &len, sizeof(uint16_t));
if (error)
break;
sptr = malloc(len + 1, M_TEMP, M_NOWAIT | M_ZERO);
if (buf_read(buf, sptr, len)) {
error = EFAULT;
free(sptr, M_TEMP);
sptr = NULL;
} else {
(sptr)[len] = 0;
*sptr_p = sptr;
}
break;
}
case 'u':
{
uid_t *val = va_arg(ap, uid_t *);
if (buf_read(buf, val, sizeof(*val)))
error = EFAULT;
break;
}
case 'g':
{
gid_t *val = va_arg(ap, gid_t *);
if (buf_read(buf, val, sizeof(*val)))
error = EFAULT;
break;
}
case 'Q':
{
struct p9_qid *qid = va_arg(ap, struct p9_qid *);
error = p9_buf_readf(buf, proto_version, "bdq",
&qid->type, &qid->version, &qid->path);
break;
}
case 'S':
{
struct p9_wstat *stbuf = va_arg(ap, struct p9_wstat *);
error = p9_buf_readf(buf, proto_version, "wwdQdddqssss?sddd",
&stbuf->size, &stbuf->type, &stbuf->dev, &stbuf->qid,
&stbuf->mode, &stbuf->atime, &stbuf->mtime, &stbuf->length,
&stbuf->name, &stbuf->uid, &stbuf->gid, &stbuf->muid,
&stbuf->extension, &stbuf->n_uid, &stbuf->n_gid, &stbuf->n_muid);
if (error != 0)
stat_free(stbuf);
break;
}
case 'A':
{
struct p9_stat_dotl *stbuf = va_arg(ap, struct p9_stat_dotl *);
error = p9_buf_readf(buf, proto_version, "qQdugqqqqqqqqqqqqqqq",
&stbuf->st_result_mask, &stbuf->qid, &stbuf->st_mode,
&stbuf->st_uid,&stbuf->st_gid, &stbuf->st_nlink,
&stbuf->st_rdev, &stbuf->st_size, &stbuf->st_blksize,
&stbuf->st_blocks, &stbuf->st_atime_sec,
&stbuf->st_atime_nsec, &stbuf->st_mtime_sec,
&stbuf->st_mtime_nsec, &stbuf->st_ctime_sec,
&stbuf->st_ctime_nsec, &stbuf->st_btime_sec,
&stbuf->st_btime_nsec, &stbuf->st_gen,
&stbuf->st_data_version);
break;
}
case 'D':
{
uint32_t *count = va_arg(ap, uint32_t *);
void **data = va_arg(ap, void **);
error = buf_read(buf, count, sizeof(uint32_t));
if (error == 0) {
*count = MIN(*count, buf->size - buf->offset);
*data = &buf->sdata[buf->offset];
}
break;
}
case 'T':
{
uint16_t *nwname_p = va_arg(ap, uint16_t *);
char ***wnames_p = va_arg(ap, char ***);
uint16_t nwname;
char **wnames;
int i;
error = buf_read(buf, nwname_p, sizeof(uint16_t));
if (error != 0)
break;
nwname = *nwname_p;
wnames = malloc(sizeof(char *) * nwname, M_TEMP, M_NOWAIT | M_ZERO);
for (i = 0; i < nwname && (error == 0); i++)
error = p9_buf_readf(buf, proto_version, "s", &wnames[i]);
if (error != 0) {
for (i = 0; i < nwname; i++)
free((wnames)[i], M_TEMP);
free(wnames, M_TEMP);
} else
*wnames_p = wnames;
break;
}
case 'R':
{
uint16_t *nwqid_p = va_arg(ap, uint16_t *);
struct p9_qid **wqids_p = va_arg(ap, struct p9_qid **);
uint16_t nwqid;
struct p9_qid *wqids;
int i;
wqids = NULL;
error = buf_read(buf, nwqid_p, sizeof(uint16_t));
if (error != 0)
break;
nwqid = *nwqid_p;
wqids = malloc(nwqid * sizeof(struct p9_qid), M_TEMP, M_NOWAIT | M_ZERO);
if (wqids == NULL) {
error = ENOMEM;
break;
}
for (i = 0; i < nwqid && (error == 0); i++)
error = p9_buf_readf(buf, proto_version, "Q", &(wqids)[i]);
if (error != 0) {
free(wqids, M_TEMP);
} else
*wqids_p = wqids;
break;
}
case '?':
{
if ((proto_version != p9_proto_2000u) && (proto_version != p9_proto_2000L))
return (0);
break;
}
default:
break;
}
if (error != 0)
break;
}
return (error);
}
/*
* Main buf_write routine. This copies the data into the buffer from the
* respective values based on the data type.
* Here
* b - int8_t
* w - int16_t
* d - int32_t
* q - int64_t
* s - string
* u - uid
* g - gid
* Q - qid
* S - stat
* D - data blob (int32_t size followed by void *, results are not freed)
* T - array of strings (int16_t count, followed by strings)
* W - string of a specific length
* R - array of qids (int16_t count, followed by qids)
* A - setattr (9P2000.L)
* ? - return if version is not .u or .l
*/
int
p9_buf_vwritef(struct p9_buffer *buf, int proto_version, const char *fmt,
va_list ap)
{
const char *ptr;
int error;
error = 0;
for (ptr = fmt; *ptr; ptr++) {
switch (*ptr) {
case 'b':
{
int8_t val = va_arg(ap, int);
if (buf_write(buf, &val, sizeof(val)))
error = EFAULT;
break;
}
case 'w':
{
int16_t val = va_arg(ap, int);
if (buf_write(buf, &val, sizeof(val)))
error = EFAULT;
break;
}
case 'd':
{
int32_t val = va_arg(ap, int32_t);
if (buf_write(buf, &val, sizeof(val)))
error = EFAULT;
break;
}
case 'q':
{
int64_t val = va_arg(ap, int64_t);
if (buf_write(buf, &val, sizeof(val)))
error = EFAULT;
break;
}
case 's':
{
const char *sptr = va_arg(ap, const char *);
uint16_t len = 0;
if (sptr)
len = MIN(strlen(sptr), P9FS_MAXLEN);
error = buf_write(buf, &len, sizeof(uint16_t));
if (error == 0 && buf_write(buf, sptr, len))
error = EFAULT;
break;
}
case 'u':
{
uid_t val = va_arg(ap, uid_t);
if (buf_write(buf, &val, sizeof(val)))
error = EFAULT;
break;
}
case 'g':
{
gid_t val = va_arg(ap, gid_t);
if (buf_write(buf, &val, sizeof(val)))
error = EFAULT;
break;
}
case 'Q':
{
const struct p9_qid *qid = va_arg(ap, const struct p9_qid *);
error = p9_buf_writef(buf, proto_version, "bdq",
qid->type, qid->version, qid->path);
break;
}
case 'S':
{
struct p9_wstat *stbuf = va_arg(ap, struct p9_wstat *);
error = p9_buf_writef(buf, proto_version,
"wwdQdddqssss?sddd", stbuf->size, stbuf->type, stbuf->dev, &stbuf->qid,
stbuf->mode, stbuf->atime, stbuf->mtime, stbuf->length, stbuf->name,
stbuf->uid, stbuf->gid, stbuf->muid, stbuf->extension, stbuf->n_uid,
stbuf->n_gid, stbuf->n_muid);
if (error != 0)
stat_free(stbuf);
break;
}
case 'D':
{
uint32_t count = va_arg(ap, uint32_t);
void *data = va_arg(ap, void *);
error = buf_write(buf, &count, sizeof(uint32_t));
if ((error == 0) && buf_write(buf, data, count))
error = EFAULT;
break;
}
case 'T':
{
char **wnames = va_arg(ap, char **);
uint16_t nwnames = va_arg(ap, int);
error = buf_write(buf, &nwnames, sizeof(uint16_t));
if (error == 0) {
int i = 0;
for (i = 0; i < nwnames; i++) {
error = p9_buf_writef(buf, proto_version, "s", wnames[i]);
if (error != 0)
break;
}
}
break;
}
case 'W':
{
const char *sptr = va_arg(ap, const char*);
uint16_t len = va_arg(ap, int);
error = buf_write(buf, &len, sizeof(uint16_t));
if (error == 0 && buf_write(buf, sptr, len))
error = EFAULT;
break;
}
case 'R':
{
uint16_t nwqid = va_arg(ap, int);
struct p9_qid *wqids = va_arg(ap, struct p9_qid *);
int i;
error = buf_write(buf, &nwqid, sizeof(uint16_t));
if (error == 0) {
for (i = 0; i < nwqid; i++) {
error = p9_buf_writef(buf, proto_version, "Q", &wqids[i]);
if (error != 0)
break;
}
}
break;
}
case 'A':
{
struct p9_iattr_dotl *p9attr = va_arg(ap, struct p9_iattr_dotl *);
error = p9_buf_writef(buf, proto_version, "ddugqqqqq",
p9attr->valid, p9attr->mode, p9attr->uid,
p9attr->gid, p9attr->size, p9attr->atime_sec,
p9attr->atime_nsec, p9attr->mtime_sec,
p9attr->mtime_nsec);
break;
}
case '?':
{
if ((proto_version != p9_proto_2000u) && (proto_version != p9_proto_2000L))
return (0);
break;
}
default:
break;
}
if (error != 0)
break;
}
return (error);
}
/* Variadic form of buf_read */
int
p9_buf_readf(struct p9_buffer *buf, int proto_version, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = p9_buf_vreadf(buf, proto_version, fmt, ap);
va_end(ap);
return (ret);
}
/* Variadic form of buf_write */
static int
p9_buf_writef(struct p9_buffer *buf, int proto_version, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = p9_buf_vwritef(buf, proto_version, fmt, ap);
va_end(ap);
return (ret);
}
/* File stats read routine for P9 to get attributes of files */
int
p9stat_read(struct p9_client *clnt, char *buf, size_t len, struct p9_wstat *st)
{
struct p9_buffer msg_buf;
int ret;
msg_buf.size = len;
msg_buf.capacity = len;
msg_buf.sdata = buf;
msg_buf.offset = 0;
ret = p9_buf_readf(&msg_buf, clnt->proto_version, "S", st);
if (ret) {
P9_DEBUG(ERROR, "%s: failed: %d\n", __func__, ret);
}
return (ret);
}
/*
* P9_header preparation routine. All p9 buffers have to have this header(QEMU_HEADER) at the
* front of the buffer.
*/
int
p9_buf_prepare(struct p9_buffer *buf, int8_t type)
{
buf->id = type;
return (p9_buf_writef(buf, 0, "dbw", 0, type, buf->tag));
}
/*
* Final write to the buffer, this is the total size of the buffer. Since the buffer length can
* vary with request, this is computed at the end just before sending the request to the driver
*/
int
p9_buf_finalize(struct p9_client *clnt, struct p9_buffer *buf)
{
int size;
int error;
size = buf->size;
buf->size = 0;
error = p9_buf_writef(buf, 0, "d", size);
buf->size = size;
P9_DEBUG(LPROTO, "%s: size=%d type: %d tag: %d\n",
__func__, buf->size, buf->id, buf->tag);
return (error);
}
/* Reset values of the buffer */
void
p9_buf_reset(struct p9_buffer *buf)
{
buf->offset = 0;
buf->size = 0;
}
/*
* Directory entry read with the buf we have. Call this once we have the buf to parse.
* This buf, obtained from the server, is parsed to make dirent in readdir.
*/
int
p9_dirent_read(struct p9_client *clnt, char *buf, int start, int len,
struct p9_dirent *dent)
{
struct p9_buffer msg_buf;
int ret;
char *nameptr;
uint16_t sle;
msg_buf.size = len;
msg_buf.capacity = len;
msg_buf.sdata = buf;
msg_buf.offset = start;
ret = p9_buf_readf(&msg_buf, clnt->proto_version, "Qqbs", &dent->qid,
&dent->d_off, &dent->d_type, &nameptr);
if (ret) {
P9_DEBUG(ERROR, "%s: failed: %d\n", __func__, ret);
goto out;
}
sle = strlen(nameptr);
strncpy(dent->d_name, nameptr, sle);
dent->len = sle;
free(nameptr, M_TEMP);
out:
return (msg_buf.offset);
}

280
sys/fs/p9fs/p9_protocol.h Normal file
View File

@ -0,0 +1,280 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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.
*/
/* File contains 9P protocol definitions */
#ifndef FS_P9FS_P9_PROTOCOL_H
#define FS_P9FS_P9_PROTOCOL_H
#include <sys/types.h>
/* 9P message types */
enum p9_cmds_t {
P9PROTO_TLERROR = 6, /* not used */
P9PROTO_RLERROR, /* response for any failed request */
P9PROTO_TSTATFS = 8, /* file system status request */
P9PROTO_RSTATFS, /* file system status response */
P9PROTO_TLOPEN = 12, /* open a file (9P2000.L) */
P9PROTO_RLOPEN, /* response to opne request (9P2000.L) */
P9PROTO_TLCREATE = 14, /* prepare for handle for I/O on a new file (9P2000.L) */
P9PROTO_RLCREATE, /* response with file access information (9P2000.L) */
P9PROTO_TSYMLINK = 16, /* symlink creation request */
P9PROTO_RSYMLINK, /* symlink creation response */
P9PROTO_TMKNOD = 18, /* create a special file object request */
P9PROTO_RMKNOD, /* create a special file object response */
P9PROTO_TRENAME = 20, /* rename a file request */
P9PROTO_RRENAME, /* rename a file response */
P9PROTO_TREADLINK = 22, /* request to read value of symbolic link */
P9PROTO_RREADLINK, /* response to read value of symbolic link request */
P9PROTO_TGETATTR = 24, /* get file attributes request */
P9PROTO_RGETATTR, /* get file attributes response */
P9PROTO_TSETATTR = 26, /* set file attributes request */
P9PROTO_RSETATTR, /* set file attributes response */
P9PROTO_TXATTRWALK = 30,/* request to read extended attributes */
P9PROTO_RXATTRWALK, /* response from server with attributes */
P9PROTO_TXATTRCREATE = 32,/* request to set extended attribute */
P9PROTO_RXATTRCREATE, /* response from server for setting extended attribute */
P9PROTO_TREADDIR = 40, /* request to read a directory */
P9PROTO_RREADDIR, /* response from server for read request */
P9PROTO_TFSYNC = 50, /* request to flush an cached data to disk */
P9PROTO_RFSYNC, /* response when cache dat is flushed */
P9PROTO_TLOCK = 52, /* acquire or release a POSIX record lock */
P9PROTO_RLOCK, /* response with the status of the lock */
P9PROTO_TGETLOCK = 54, /* request to check for presence of a POSIX record lock */
P9PROTO_RGETLOCK, /* response with the details of the lock if acquired */
P9PROTO_TLINK = 70, /* request to create hard link */
P9PROTO_RLINK, /* create hard link response */
P9PROTO_TMKDIR = 72, /* create a directory request */
P9PROTO_RMKDIR, /* create a directory response */
P9PROTO_TRENAMEAT = 74, /* request to rename a file or directory */
P9PROTO_RRENAMEAT, /* reponse to rename request */
P9PROTO_TUNLINKAT = 76, /* unlink a file or directory */
P9PROTO_RUNLINKAT, /* reponse to unlink request */
P9PROTO_TVERSION = 100, /* request for version handshake */
P9PROTO_RVERSION, /* response for version handshake */
P9PROTO_TAUTH = 102, /* request to establish authentication channel */
P9PROTO_RAUTH, /* response with authentication information */
P9PROTO_TATTACH = 104, /* establish a user access to a file system*/
P9PROTO_RATTACH, /* response with top level handle to file hierarchy */
P9PROTO_TERROR = 106, /* not used */
P9PROTO_RERROR, /* response for any failed request */
P9PROTO_TFLUSH = 108, /* request to abort a previous request */
P9PROTO_RFLUSH, /* response when previous request has been cancelled */
P9PROTO_TWALK = 110, /* descend a directory hierarchy */
P9PROTO_RWALK, /* response with new handle for position within hierarchy */
P9PROTO_TOPEN = 112, /* prepare file handle for I/O for an existing file */
P9PROTO_ROPEN, /* response with file access information */
P9PROTO_TCREATE = 114, /* prepare for handle for I/O on a new file */
P9PROTO_RCREATE, /* response with file access information */
P9PROTO_TREAD = 116, /* request to transfer data from a file */
P9PROTO_RREAD, /* response with data requested */
P9PROTO_TWRITE = 118, /* request to transfer data to a file */
P9PROTO_RWRITE, /* response with how much data was written to the file */
P9PROTO_TCLUNK = 120, /* forget about a handle to a file within the File System */
P9PROTO_RCLUNK, /* response from the server for forgetting the file handle */
P9PROTO_TREMOVE = 122, /* request to remove a file */
P9PROTO_RREMOVE, /* response when server has removed the file */
P9PROTO_TSTAT = 124, /* request file entity attributes */
P9PROTO_RSTAT, /* response with file entity attributes */
P9PROTO_TWSTAT = 126, /* request to update file entity attributes */
P9PROTO_RWSTAT, /* response when file entity attributes are updated */
};
/* File Open Modes */
enum p9_open_mode_t {
P9PROTO_OREAD = 0x00, /* open file for reading only */
P9PROTO_OWRITE = 0x01, /* open file for writing only */
P9PROTO_ORDWR = 0x02, /* open file for both reading and writing */
P9PROTO_OEXEC = 0x03, /* open file for execution */
P9PROTO_OTRUNC = 0x10, /* truncate file to zero length before opening it */
P9PROTO_OREXEC = 0x20, /* close the file when exec system call is made */
P9PROTO_ORCLOSE = 0x40, /* remove the file when it is closed */
P9PROTO_OAPPEND = 0x80, /* open the file and seek to the end of the file */
P9PROTO_OEXCL = 0x1000, /* only create a file and not open it */
};
/* FIle Permissions */
enum p9_perm_t {
P9PROTO_DMDIR = 0x80000000, /* permission bit for directories */
P9PROTO_DMAPPEND = 0x40000000, /* permission bit for is append-only */
P9PROTO_DMEXCL = 0x20000000, /* permission bit for exclusive use (only one open handle allowed) */
P9PROTO_DMMOUNT = 0x10000000, /* permission bit for mount points */
P9PROTO_DMAUTH = 0x08000000, /* permission bit for authentication file */
P9PROTO_DMTMP = 0x04000000, /* permission bit for non-backed-up files */
P9PROTO_DMSYMLINK = 0x02000000, /* permission bit for symbolic link (9P2000.u) */
P9PROTO_DMLINK = 0x01000000, /* permission bit for hard-link (9P2000.u) */
P9PROTO_DMDEVICE = 0x00800000, /* permission bit for device files (9P2000.u) */
P9PROTO_DMNAMEDPIPE = 0x00200000,/* permission bit for named pipe (9P2000.u) */
P9PROTO_DMSOCKET = 0x00100000, /* permission bit for socket (9P2000.u) */
P9PROTO_DMSETUID = 0x00080000, /* permission bit for setuid (9P2000.u) */
P9PROTO_DMSETGID = 0x00040000, /* permission bit for setgid (9P2000.u) */
P9PROTO_DMSETVTX = 0x00010000, /* permission bit for sticky bit (9P2000.u) */
};
/*
* QID types - they are primarly used to
* differentiate semantics for a file system
*/
enum p9_qid_t {
P9PROTO_QTDIR = 0x80, /* directory */
P9PROTO_QTAPPEND = 0x40, /* append-only */
P9PROTO_QTEXCL = 0x20, /* exclusive use (only one open handle allowed)*/
P9PROTO_QTMOUNT = 0x10, /* mount points */
P9PROTO_QTAUTH = 0x08, /* authentication file */
P9PROTO_QTTMP = 0x04, /* non-backed-up files */
P9PROTO_QTSYMLINK = 0x02, /* symbolic links */
P9PROTO_QTLINK = 0x01, /* hard link */
P9PROTO_QTFILE = 0x00, /* normal files */
};
/* P9 Magic Numbers */
#define P9PROTO_NOFID (uint32_t)(~0)
#define P9_DEFUNAME "nobody"
#define P9_DEFANAME ""
#define P9_NONUNAME (uint32_t)(~0)
#define P9_MAXWELEM 16
/* Exchange unit between Qemu and Client */
struct p9_qid {
uint8_t type; /* the type of the file */
uint32_t version; /* version number for given path */
uint64_t path; /* the file servers unique id for file */
};
/* FS information stat structure */
struct p9_statfs {
uint32_t type; /* type of file system */
uint32_t bsize; /* optimal transfer block size */
uint64_t blocks; /* total data blocks in file system */
uint64_t bfree; /* free blocks in fs */
uint64_t bavail; /* free blocks avail to non-superuser */
uint64_t files; /* total file nodes in file system */
uint64_t ffree; /* free file nodes in fs */
uint64_t fsid; /* file system id */
uint32_t namelen; /* maximum length of filenames */
};
/* File system metadata information */
struct p9_wstat {
uint16_t size; /* total byte count of the following data */
uint16_t type; /* type of file */
uint32_t dev; /* id of device containing file */
struct p9_qid qid; /* identifier used by server for file system entity information */
uint32_t mode; /* protection */
uint32_t atime; /* time of last access */
uint32_t mtime; /* time of last modification */
uint64_t length; /* length of file in bytes */
char *name; /* file name */
char *uid; /* user ID of owner */
char *gid; /* group ID of owner */
char *muid; /* name of the user who last modified the file */
char *extension; /* 9p2000.u extensions */
uid_t n_uid; /* 9p2000.u extensions */
gid_t n_gid; /* 9p2000.u extensions */
uid_t n_muid; /* 9p2000.u extensions */
};
/* The linux version of FS information stat structure*/
struct p9_stat_dotl {
uint64_t st_result_mask;/* indicates fields that are requested */
struct p9_qid qid; /* identifier used by server for file system entity information */
uint32_t st_mode; /* protection */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
uint64_t st_nlink; /* number of hard links */
uint64_t st_rdev; /* device ID (if special file) */
uint64_t st_size; /* total size, in bytes */
uint64_t st_blksize; /* blocksize for file system I/O */
uint64_t st_blocks; /* number of 512B blocks allocated */
uint64_t st_atime_sec; /* time of last access, seconds */
uint64_t st_atime_nsec; /* time of last access, nanoseconds */
uint64_t st_mtime_sec; /* time of last modification, seconds */
uint64_t st_mtime_nsec; /* time of last modifictaion, nanoseconds */
uint64_t st_ctime_sec; /* time of last status change, seconds*/
uint64_t st_ctime_nsec; /* time of last status change, nanoseconds*/
uint64_t st_btime_sec; /* following memebers are reserved for future use */
uint64_t st_btime_nsec;
uint64_t st_gen;
uint64_t st_data_version;
};
/* P9 inode attribute for setattr */
struct p9_iattr_dotl {
uint32_t valid; /* bit fields specifying which fields are valid */
uint32_t mode; /* protection */
uid_t uid; /* user id of owner */
gid_t gid; /* group id */
uint64_t size; /* file size */
uint64_t atime_sec; /* last access time in seconds */
uint64_t atime_nsec; /* last access time in nanoseconds */
uint64_t mtime_sec; /* last modification time in seconds */
uint64_t mtime_nsec; /* last modification time in nanoseconds */
};
#define P9PROTO_STATS_MODE 0x00000001ULL
#define P9PROTO_STATS_NLINK 0x00000002ULL
#define P9PROTO_STATS_UID 0x00000004ULL
#define P9PROTO_STATS_GID 0x00000008ULL
#define P9PROTO_STATS_RDEV 0x00000010ULL
#define P9PROTO_STATS_ATIME 0x00000020ULL
#define P9PROTO_STATS_MTIME 0x00000040ULL
#define P9PROTO_STATS_CTIME 0x00000080ULL
#define P9PROTO_STATS_INO 0x00000100ULL
#define P9PROTO_STATS_SIZE 0x00000200ULL
#define P9PROTO_STATS_BLOCKS 0x00000400ULL
#define P9PROTO_STATS_BTIME 0x00000800ULL
#define P9PROTO_STATS_GEN 0x00001000ULL
#define P9PROTO_STATS_DATA_VERSION 0x00002000ULL
#define P9PROTO_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
#define P9PROTO_STATS_ALL 0x00003fffULL /* Mask for All fields above */
#define P9PROTO_SETATTR_MODE 0x00000001UL
#define P9PROTO_SETATTR_UID 0x00000002UL
#define P9PROTO_SETATTR_GID 0x00000004UL
#define P9PROTO_SETATTR_SIZE 0x00000008UL
#define P9PROTO_SETATTR_ATIME 0x00000010UL
#define P9PROTO_SETATTR_MTIME 0x00000020UL
#define P9PROTO_SETATTR_CTIME 0x00000040UL
#define P9PROTO_SETATTR_ATIME_SET 0x00000080UL
#define P9PROTO_SETATTR_MTIME_SET 0x00000100UL
#define P9PROTO_SETATTR_MASK 0x000001bfUL
#define P9PROTO_TGETATTR_BLK 512
/* PDU buffer used for SG lists. */
struct p9_buffer {
uint32_t size;
uint16_t tag;
uint8_t id;
size_t offset;
size_t capacity;
uint8_t *sdata;
};
#endif /* FS_P9FS_P9_PROTOCOL_H */

View File

@ -0,0 +1,70 @@
/*-
* Copyright (c) 2022-present Doug Rabson
* 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.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/kassert.h>
#include <sys/libkern.h>
#include <fs/p9fs/p9_transport.h>
TAILQ_HEAD(, p9_trans_module) transports;
static void
p9_transport_init(void)
{
TAILQ_INIT(&transports);
}
SYSINIT(p9_transport, SI_SUB_DRIVERS, SI_ORDER_FIRST, p9_transport_init, NULL);
void
p9_register_trans(struct p9_trans_module *m)
{
TAILQ_INSERT_TAIL(&transports, m, link);
}
void
p9_unregister_trans(struct p9_trans_module *m)
{
TAILQ_REMOVE(&transports, m, link);
}
struct p9_trans_module *
p9_get_trans_by_name(char *name)
{
struct p9_trans_module *m;
TAILQ_FOREACH(m, &transports, link) {
if (strcmp(m->name, name) == 0)
return (m);
}
return (NULL);
}

View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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 ``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.
*
*/
/* Transport definitions */
#ifndef FS_P9FS_P9_TRANSPORT_H
#define FS_P9FS_P9_TRANSPORT_H
#include <sys/queue.h>
struct p9_req_t;
/* Tranport module interface */
struct p9_trans_module {
TAILQ_ENTRY(p9_trans_module) link;
char *name; /* name of transport */
/* member function to create a new conection on this transport*/
int (*create)(const char *mount_tag, void **handlep);
/* member function to terminate a connection on this transport */
void (*close) (void *handle);
/* member function to issue a request to the transport*/
int (*request) (void *handle, struct p9_req_t *req);
/* member function to cancel a request if it has been sent */
int (*cancel) (void *handle, struct p9_req_t *req);
};
void p9_register_trans(struct p9_trans_module *m);
void p9_unregister_trans(struct p9_trans_module *m);
struct p9_trans_module *p9_get_trans_by_name(char *s);
#endif /* FS_P9FS_P9_TRANSPORT_H */

203
sys/fs/p9fs/p9fs.h Normal file
View File

@ -0,0 +1,203 @@
/*-
* Copyright (c) 2017-2020 Juniper Networks, 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 ``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.
*
*/
/* This file has prototypes specific to the p9fs file system */
#ifndef FS_P9FS_P9FS_H
#define FS_P9FS_P9FS_H
struct p9fs_session;
/* QID: Unique identification for the file being accessed */
struct p9fs_qid {
uint8_t qid_mode; /* file mode specifiying file type */
uint32_t qid_version; /* version of the file */
uint64_t qid_path; /* unique integer among all files in hierarchy */
};
/*
* The in memory representation of the on disk inode. Save the current
* fields to write it back later.
*/
struct p9fs_inode {
/* Make it simple first, Add more fields later */
uint64_t i_size; /* size of the inode */
uint16_t i_type; /* type of inode */
uint32_t i_dev; /* type of device */
uint32_t i_mode; /* mode of the inode */
uint32_t i_atime; /* time of last access */
uint32_t i_mtime; /* time of last modification */
uint32_t i_ctime; /* time of last status change */
uint32_t i_atime_nsec; /* times of last access in nanoseconds resolution */
uint32_t i_mtime_nsec; /* time of last modification in nanoseconds resolution */
uint32_t i_ctime_nsec; /* time of last status change in nanoseconds resolution */
uint64_t i_length;
char *i_name; /* inode name */
char *i_uid; /* inode user id */
char *i_gid; /* inode group id */
char *i_muid;
char *i_extension; /* 9p2000.u extensions */
uid_t n_uid; /* 9p2000.u extensions */
gid_t n_gid; /* 9p2000.u extensions */
uid_t n_muid; /* 9p2000.u extensions */
/* bookkeeping info on the client. */
uint16_t i_links_count; /*number of references to the inode*/
uint64_t i_qid_path; /* using inode number for reference. */
uint64_t i_flags;
uint64_t blksize; /* block size for file system */
uint64_t blocks; /* number of 512B blocks allocated */
uint64_t gen; /* reserved for future use */
uint64_t data_version; /* reserved for future use */
};
#define P9FS_VFID_MTX(_sc) (&(_sc)->vfid_mtx)
#define P9FS_VFID_LOCK(_sc) mtx_lock(P9FS_VFID_MTX(_sc))
#define P9FS_VFID_UNLOCK(_sc) mtx_unlock(P9FS_VFID_MTX(_sc))
#define P9FS_VFID_LOCK_INIT(_sc) mtx_init(P9FS_VFID_MTX(_sc), \
"VFID List lock", NULL, MTX_DEF)
#define P9FS_VFID_LOCK_DESTROY(_sc) mtx_destroy(P9FS_VFID_MTX(_sc))
#define P9FS_VOFID_MTX(_sc) (&(_sc)->vofid_mtx)
#define P9FS_VOFID_LOCK(_sc) mtx_lock(P9FS_VOFID_MTX(_sc))
#define P9FS_VOFID_UNLOCK(_sc) mtx_unlock(P9FS_VOFID_MTX(_sc))
#define P9FS_VOFID_LOCK_INIT(_sc) mtx_init(P9FS_VOFID_MTX(_sc), \
"VOFID List lock", NULL, MTX_DEF)
#define P9FS_VOFID_LOCK_DESTROY(_sc) mtx_destroy(P9FS_VOFID_MTX(_sc))
#define VFID 0x01
#define VOFID 0x02
/* A Plan9 node. */
struct p9fs_node {
STAILQ_HEAD( ,p9_fid) vfid_list; /* vfid related to uid */
struct mtx vfid_mtx; /* mutex for vfid list */
STAILQ_HEAD( ,p9_fid) vofid_list; /* vofid related to uid */
struct mtx vofid_mtx; /* mutex for vofid list */
struct p9fs_node *parent; /* pointer to parent p9fs node */
struct p9fs_qid vqid; /* the server qid, will be from the host */
struct vnode *v_node; /* vnode for this fs_node. */
struct p9fs_inode inode; /* in memory representation of ondisk information*/
struct p9fs_session *p9fs_ses; /* Session_ptr for this node */
STAILQ_ENTRY(p9fs_node) p9fs_node_next;
uint64_t flags;
};
#define P9FS_VTON(vp) ((struct p9fs_node *)(vp)->v_data)
#define P9FS_NTOV(node) ((node)->v_node)
#define VFSTOP9(mp) ((struct p9fs_mount *)(mp)->mnt_data)
#define QEMU_DIRENTRY_SZ 25
#define P9FS_NODE_MODIFIED 0x1 /* indicating file change */
#define P9FS_ROOT 0x2 /* indicating root p9fs node */
#define P9FS_NODE_DELETED 0x4 /* indicating file or directory delete */
#define P9FS_NODE_IN_SESSION 0x8 /* p9fs_node is in the session - virt_node_list */
#define IS_ROOT(node) (node->flags & P9FS_ROOT)
#define P9FS_SET_LINKS(inode) do { \
(inode)->i_links_count = 1; \
} while (0) \
#define P9FS_INCR_LINKS(inode) do { \
(inode)->i_links_count++; \
} while (0) \
#define P9FS_DECR_LINKS(inode) do { \
(inode)->i_links_count--; \
} while (0) \
#define P9FS_CLR_LINKS(inode) do { \
(inode)->i_links_count = 0; \
} while (0) \
#define P9FS_MTX(_sc) (&(_sc)->p9fs_mtx)
#define P9FS_LOCK(_sc) mtx_lock(P9FS_MTX(_sc))
#define P9FS_UNLOCK(_sc) mtx_unlock(P9FS_MTX(_sc))
#define P9FS_LOCK_INIT(_sc) mtx_init(P9FS_MTX(_sc), \
"P9FS session chain lock", NULL, MTX_DEF)
#define P9FS_LOCK_DESTROY(_sc) mtx_destroy(P9FS_MTX(_sc))
/* Session structure for the FS */
struct p9fs_session {
unsigned char flags; /* these flags for the session */
struct mount *p9fs_mount; /* mount point */
struct p9fs_node rnp; /* root p9fs node for this session */
uid_t uid; /* the uid that has access */
const char *uname; /* user name to mount as */
const char *aname; /* name of remote file tree being mounted */
struct p9_client *clnt; /* 9p client */
struct mtx p9fs_mtx; /* mutex used for guarding the chain.*/
STAILQ_HEAD( ,p9fs_node) virt_node_list; /* list of p9fs nodes in this session*/
struct p9_fid *mnt_fid; /* to save nobody 's fid for unmounting as root user */
};
struct p9fs_mount {
struct p9fs_session p9fs_session; /* per instance session information */
struct mount *p9fs_mountp; /* mount point */
int mount_tag_len; /* length of the mount tag */
char *mount_tag; /* mount tag used */
};
/* All session flags based on 9p versions */
enum virt_session_flags {
P9FS_PROTO_2000U = 0x01,
P9FS_PROTO_2000L = 0x02,
};
/* Session access flags */
#define P9_ACCESS_ANY 0x04 /* single attach for all users */
#define P9_ACCESS_SINGLE 0x08 /* access to only the user who mounts */
#define P9_ACCESS_USER 0x10 /* new attach established for every user */
#define P9_ACCESS_MASK (P9_ACCESS_ANY|P9_ACCESS_SINGLE|P9_ACCESS_USER)
u_quad_t p9fs_round_filesize_to_bytes(uint64_t filesize, uint64_t bsize);
u_quad_t p9fs_pow2_filesize_to_bytes(uint64_t filesize, uint64_t bsize);
/* These are all the P9FS specific vops */
int p9fs_stat_vnode_l(void);
int p9fs_stat_vnode_dotl(struct p9_stat_dotl *st, struct vnode *vp);
int p9fs_reload_stats_dotl(struct vnode *vp, struct ucred *cred);
int p9fs_proto_dotl(struct p9fs_session *vses);
struct p9_fid *p9fs_init_session(struct mount *mp, int *error);
void p9fs_close_session(struct mount *mp);
void p9fs_prepare_to_close(struct mount *mp);
void p9fs_complete_close(struct mount *mp);
int p9fs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp);
int p9fs_vget_common(struct mount *mp, struct p9fs_node *np, int flags,
struct p9fs_node *parent, struct p9_fid *fid, struct vnode **vpp,
char *name);
int p9fs_node_cmp(struct vnode *vp, void *arg);
void p9fs_destroy_node(struct p9fs_node **npp);
void p9fs_dispose_node(struct p9fs_node **npp);
void p9fs_cleanup(struct p9fs_node *vp);
void p9fs_fid_remove_all(struct p9fs_node *np, int leave_ofids);
void p9fs_fid_remove(struct p9fs_node *np, struct p9_fid *vfid,
int fid_type);
void p9fs_fid_add(struct p9fs_node *np, struct p9_fid *fid,
int fid_type);
struct p9_fid *p9fs_get_fid(struct p9_client *clnt,
struct p9fs_node *np, struct ucred *cred, int fid_type, int mode, int *error);
#endif /* FS_P9FS_P9FS_H */

42
sys/fs/p9fs/p9fs_proto.h Normal file
View File

@ -0,0 +1,42 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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 ``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.
*
*/
/*
* Plan9 filesystem (9P2000.u) protocol definitions.
*/
#ifndef FS_P9FS_P9FS_PROTO_H
#define FS_P9FS_P9FS_PROTO_H
//#include <dev/virtio/virtio_fs_9p.h>
/* File permissions */
#define P9FS_OREAD 0
#define P9FS_OWRITE 1
#define P9FS_ORDWR 2
#define P9FS_OEXEC 3
#define P9FS_OTRUNC 0x10
#endif /* FS_P9FS_P9FS_PROTO_H */

411
sys/fs/p9fs/p9fs_subr.c Normal file
View File

@ -0,0 +1,411 @@
/*-
* Copyright (c) 2017 Juniper Networks, 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 ``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.
*
*/
/*-
* 9P filesystem subroutines. This file consists of all the Non VFS subroutines.
* It contains all of the functions related to the driver submission which form
* the upper layer i.e, p9fs driver. This will interact with the client to make
* sure we have correct API calls in the header.
*/
#include <sys/cdefs.h>
#include <sys/systm.h>
#include <sys/limits.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include "p9fs_proto.h"
#include <fs/p9fs/p9_client.h>
#include <fs/p9fs/p9_debug.h>
#include <fs/p9fs/p9_protocol.h>
#include <fs/p9fs/p9fs.h>
int
p9fs_proto_dotl(struct p9fs_session *vses)
{
return (vses->flags & P9FS_PROTO_2000L);
}
/* Initialize a p9fs session */
struct p9_fid *
p9fs_init_session(struct mount *mp, int *error)
{
struct p9fs_session *vses;
struct p9fs_mount *virtmp;
struct p9_fid *fid;
char *access;
virtmp = VFSTOP9(mp);
vses = &virtmp->p9fs_session;
vses->uid = P9_NONUNAME;
vses->uname = P9_DEFUNAME;
vses->aname = P9_DEFANAME;
/*
* Create the client structure. Call into the driver to create
* driver structures for the actual IO transfer.
*/
vses->clnt = p9_client_create(mp, error, virtmp->mount_tag);
if (vses->clnt == NULL) {
P9_DEBUG(ERROR, "%s: p9_client_create failed\n", __func__);
return (NULL);
}
/*
* Find the client version and cache the copy. We will use this copy
* throughout FS layer.
*/
if (p9_is_proto_dotl(vses->clnt))
vses->flags |= P9FS_PROTO_2000L;
else if (p9_is_proto_dotu(vses->clnt))
vses->flags |= P9FS_PROTO_2000U;
/* Set the access mode */
access = vfs_getopts(mp->mnt_optnew, "access", error);
if (access == NULL)
vses->flags |= P9_ACCESS_USER;
else if (!strcmp(access, "any"))
vses->flags |= P9_ACCESS_ANY;
else if (!strcmp(access, "single"))
vses->flags |= P9_ACCESS_SINGLE;
else if (!strcmp(access, "user"))
vses->flags |= P9_ACCESS_USER;
else {
P9_DEBUG(ERROR, "%s: unknown access mode\n", __func__);
*error = EINVAL;
goto out;
}
*error = 0;
/* Attach with the backend host*/
fid = p9_client_attach(vses->clnt, NULL, vses->uname, P9_NONUNAME,
vses->aname, error);
vses->mnt_fid = fid;
if (*error != 0) {
P9_DEBUG(ERROR, "%s: attach failed: %d\n", __func__, *error);
goto out;
}
P9_DEBUG(SUBR, "%s: attach successful fid :%p\n", __func__, fid);
fid->uid = vses->uid;
/* initialize the node list for the session */
STAILQ_INIT(&vses->virt_node_list);
P9FS_LOCK_INIT(vses);
P9_DEBUG(SUBR, "%s: INIT session successful\n", __func__);
return (fid);
out:
p9_client_destroy(vses->clnt);
return (NULL);
}
/* Begin to terminate a session */
void
p9fs_prepare_to_close(struct mount *mp)
{
struct p9fs_session *vses;
struct p9fs_mount *vmp;
struct p9fs_node *np, *pnp, *tmp;
vmp = VFSTOP9(mp);
vses = &vmp->p9fs_session;
/* break the node->parent references */
STAILQ_FOREACH_SAFE(np, &vses->virt_node_list, p9fs_node_next, tmp) {
if (np->parent && np->parent != np) {
pnp = np->parent;
np->parent = NULL;
vrele(P9FS_NTOV(pnp));
}
}
/* We are about to teardown, we dont allow anything other than clunk after this.*/
p9_client_begin_disconnect(vses->clnt);
}
/* Shutdown a session */
void
p9fs_complete_close(struct mount *mp)
{
struct p9fs_session *vses;
struct p9fs_mount *vmp;
vmp = VFSTOP9(mp);
vses = &vmp->p9fs_session;
/* Finish the close*/
p9_client_disconnect(vses->clnt);
}
/* Call from unmount. Close the session. */
void
p9fs_close_session(struct mount *mp)
{
struct p9fs_session *vses;
struct p9fs_mount *vmp;
vmp = VFSTOP9(mp);
vses = &vmp->p9fs_session;
p9fs_complete_close(mp);
/* Clean up the clnt structure. */
p9_client_destroy(vses->clnt);
P9FS_LOCK_DESTROY(vses);
P9_DEBUG(SUBR, "%s: Clean close session .\n", __func__);
}
/*
* Remove all the fids of a particular type from a p9fs node
* as well as destroy/clunk them.
*/
void
p9fs_fid_remove_all(struct p9fs_node *np, int leave_ofids)
{
struct p9_fid *fid, *tfid;
STAILQ_FOREACH_SAFE(fid, &np->vfid_list, fid_next, tfid) {
STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next);
p9_client_clunk(fid);
}
if (!leave_ofids) {
STAILQ_FOREACH_SAFE(fid, &np->vofid_list, fid_next, tfid) {
STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next);
p9_client_clunk(fid);
}
}
}
/* Remove a fid from its corresponding fid list */
void
p9fs_fid_remove(struct p9fs_node *np, struct p9_fid *fid, int fid_type)
{
switch (fid_type) {
case VFID:
P9FS_VFID_LOCK(np);
STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next);
P9FS_VFID_UNLOCK(np);
break;
case VOFID:
P9FS_VOFID_LOCK(np);
STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next);
P9FS_VOFID_UNLOCK(np);
break;
}
}
/* Add a fid to the corresponding fid list */
void
p9fs_fid_add(struct p9fs_node *np, struct p9_fid *fid, int fid_type)
{
switch (fid_type) {
case VFID:
P9FS_VFID_LOCK(np);
STAILQ_INSERT_TAIL(&np->vfid_list, fid, fid_next);
P9FS_VFID_UNLOCK(np);
break;
case VOFID:
P9FS_VOFID_LOCK(np);
STAILQ_INSERT_TAIL(&np->vofid_list, fid, fid_next);
P9FS_VOFID_UNLOCK(np);
break;
}
}
/* Build the path from root to current directory */
static int
p9fs_get_full_path(struct p9fs_node *np, char ***names)
{
int i, n;
struct p9fs_node *node;
char **wnames;
n = 0;
for (node = np ; (node != NULL) && !IS_ROOT(node) ; node = node->parent)
n++;
if (node == NULL)
return (0);
wnames = malloc(n * sizeof(char *), M_TEMP, M_ZERO|M_WAITOK);
for (i = n-1, node = np; i >= 0 ; i--, node = node->parent)
wnames[i] = node->inode.i_name;
*names = wnames;
return (n);
}
/*
* Return TRUE if this fid can be used for the requested mode.
*/
static int
p9fs_compatible_mode(struct p9_fid *fid, int mode)
{
/*
* Return TRUE for an exact match. For OREAD and OWRITE, allow
* existing ORDWR fids to match. Only check the low two bits
* of mode.
*
* TODO: figure out if this is correct for O_APPEND
*/
int fid_mode = fid->mode & 3;
if (fid_mode == mode)
return (TRUE);
if (fid_mode == P9PROTO_ORDWR)
return (mode == P9PROTO_OREAD || mode == P9PROTO_OWRITE);
return (FALSE);
}
/*
* Retrieve fid structure corresponding to a particular
* uid and fid type for a p9fs node
*/
static struct p9_fid *
p9fs_get_fid_from_uid(struct p9fs_node *np, uid_t uid, int fid_type, int mode)
{
struct p9_fid *fid;
switch (fid_type) {
case VFID:
P9FS_VFID_LOCK(np);
STAILQ_FOREACH(fid, &np->vfid_list, fid_next) {
if (fid->uid == uid) {
P9FS_VFID_UNLOCK(np);
return (fid);
}
}
P9FS_VFID_UNLOCK(np);
break;
case VOFID:
P9FS_VOFID_LOCK(np);
STAILQ_FOREACH(fid, &np->vofid_list, fid_next) {
if (fid->uid == uid && p9fs_compatible_mode(fid, mode)) {
P9FS_VOFID_UNLOCK(np);
return (fid);
}
}
P9FS_VOFID_UNLOCK(np);
break;
}
return (NULL);
}
/*
* Function returns the fid sturcture for a file corresponding to current user id.
* First it searches in the fid list of the corresponding p9fs node.
* New fid will be created if not already present and added in the corresponding
* fid list in the p9fs node.
* If the user is not already attached then this will attach the user first
* and then create a new fid for this particular file by doing dir walk.
*/
struct p9_fid *
p9fs_get_fid(struct p9_client *clnt, struct p9fs_node *np, struct ucred *cred,
int fid_type, int mode, int *error)
{
uid_t uid;
struct p9_fid *fid, *oldfid;
struct p9fs_node *root;
struct p9fs_session *vses;
int i, l, clone;
char **wnames = NULL;
uint16_t nwnames;
oldfid = NULL;
vses = np->p9fs_ses;
if (vses->flags & P9_ACCESS_ANY)
uid = vses->uid;
else if (cred)
uid = cred->cr_uid;
else
uid = 0;
/*
* Search for the fid in corresponding fid list.
* We should return NULL for VOFID if it is not present in the list.
* Because VOFID should have been created during the file open.
* If VFID is not present in the list then we should create one.
*/
fid = p9fs_get_fid_from_uid(np, uid, fid_type, mode);
if (fid != NULL || fid_type == VOFID)
return (fid);
/* Check root if the user is attached */
root = &np->p9fs_ses->rnp;
fid = p9fs_get_fid_from_uid(root, uid, fid_type, mode);
if(fid == NULL) {
/* Attach the user */
fid = p9_client_attach(clnt, NULL, NULL, uid,
vses->aname, error);
if (*error != 0)
return (NULL);
p9fs_fid_add(root, fid, fid_type);
}
/* If we are looking for root then return it */
if (IS_ROOT(np))
return (fid);
/* Get full path from root to p9fs node */
nwnames = p9fs_get_full_path(np, &wnames);
/*
* Could not get full path.
* If p9fs node is not deleted, parent should exist.
*/
KASSERT(nwnames != 0, ("%s: Directory of %s doesn't exist", __func__, np->inode.i_name));
clone = 1;
i = 0;
while (i < nwnames) {
l = MIN(nwnames - i, P9_MAXWELEM);
fid = p9_client_walk(fid, l, wnames, clone, error);
if (*error != 0) {
if (oldfid)
p9_client_clunk(oldfid);
fid = NULL;
goto bail_out;
}
oldfid = fid;
clone = 0;
i += l ;
}
p9fs_fid_add(np, fid, fid_type);
bail_out:
free(wnames, M_TEMP);
return (fid);
}

602
sys/fs/p9fs/p9fs_vfsops.c Normal file
View File

@ -0,0 +1,602 @@
/*-
* Copyright (c) 2017-2020 Juniper Networks, 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 ``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.
*
*/
/*
* This file consists of all the VFS interactions of VFS ops which include
* mount, unmount, initilaize etc. for p9fs.
*/
#include <sys/cdefs.h>
#include <sys/systm.h>
#include <sys/fnv_hash.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <vm/uma.h>
#include <fs/p9fs/p9fs_proto.h>
#include <fs/p9fs/p9_client.h>
#include <fs/p9fs/p9_debug.h>
#include <fs/p9fs/p9fs.h>
SYSCTL_NODE(_vfs, OID_AUTO, p9fs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Plan 9 filesystem");
/* This count is static now. Can be made tunable later */
#define P9FS_FLUSH_RETRIES 10
static MALLOC_DEFINE(M_P9MNT, "p9fs_mount", "Mount structures for p9fs");
static uma_zone_t p9fs_node_zone;
uma_zone_t p9fs_io_buffer_zone;
uma_zone_t p9fs_getattr_zone;
uma_zone_t p9fs_setattr_zone;
extern struct vop_vector p9fs_vnops;
/* option parsing */
static const char *p9fs_opts[] = {
"from", "trans", "access", NULL
};
/* Dispose p9fs node, freeing it to the UMA zone */
void
p9fs_dispose_node(struct p9fs_node **npp)
{
struct p9fs_node *node;
struct vnode *vp;
node = *npp;
if (node == NULL)
return;
if (node->parent && node->parent != node) {
vrele(P9FS_NTOV(node->parent));
}
P9_DEBUG(VOPS, "%s: node: %p\n", __func__, *npp);
vp = P9FS_NTOV(node);
vp->v_data = NULL;
/* Free our associated memory */
if (!(vp->v_vflag & VV_ROOT)) {
free(node->inode.i_name, M_TEMP);
uma_zfree(p9fs_node_zone, node);
}
*npp = NULL;
}
/* Initialize memory allocation */
static int
p9fs_init(struct vfsconf *vfsp)
{
p9fs_node_zone = uma_zcreate("p9fs node zone",
sizeof(struct p9fs_node), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
/* Create the getattr_dotl zone */
p9fs_getattr_zone = uma_zcreate("p9fs getattr zone",
sizeof(struct p9_stat_dotl), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
/* Create the setattr_dotl zone */
p9fs_setattr_zone = uma_zcreate("p9fs setattr zone",
sizeof(struct p9_iattr_dotl), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
/*
* Create the io_buffer zone pool to keep things simpler in case of
* multiple threads. Each thread works with its own so there is no
* contention.
*/
p9fs_io_buffer_zone = uma_zcreate("p9fs io_buffer zone",
P9FS_MTU, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
return (0);
}
/* Destroy all the allocated memory */
static int
p9fs_uninit(struct vfsconf *vfsp)
{
uma_zdestroy(p9fs_node_zone);
uma_zdestroy(p9fs_io_buffer_zone);
uma_zdestroy(p9fs_getattr_zone);
uma_zdestroy(p9fs_setattr_zone);
return (0);
}
/* Function to umount p9fs */
static int
p9fs_unmount(struct mount *mp, int mntflags)
{
struct p9fs_mount *vmp;
struct p9fs_session *vses;
int error, flags, i;
error = 0;
flags = 0;
vmp = VFSTOP9(mp);
if (vmp == NULL)
return (0);
vses = &vmp->p9fs_session;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
p9fs_prepare_to_close(mp);
for (i = 0; i < P9FS_FLUSH_RETRIES; i++) {
/* Flush everything on this mount point.*/
error = vflush(mp, 1, flags, curthread);
if (error == 0 || (mntflags & MNT_FORCE) == 0)
break;
/* Sleep until interrupted or 1 tick expires. */
error = tsleep(&error, PSOCK, "p9unmnt", 1);
if (error == EINTR)
break;
error = EBUSY;
}
if (error != 0)
goto out;
p9fs_close_session(mp);
/* Cleanup the mount structure. */
free(vmp, M_P9MNT);
mp->mnt_data = NULL;
return (error);
out:
/* Restore the flag in case of error */
vses->clnt->trans_status = P9FS_CONNECT;
return (error);
}
/*
* Compare qid stored in p9fs node
* Return 1 if does not match otherwise return 0
*/
int
p9fs_node_cmp(struct vnode *vp, void *arg)
{
struct p9fs_node *np;
struct p9_qid *qid;
np = vp->v_data;
qid = (struct p9_qid *)arg;
if (np == NULL)
return (1);
if (np->vqid.qid_path == qid->path) {
if (vp->v_vflag & VV_ROOT)
return (0);
else if (np->vqid.qid_mode == qid->type &&
np->vqid.qid_version == qid->version)
return (0);
}
return (1);
}
/*
* Cleanup p9fs node
* - Destroy the FID LIST locks
* - Dispose all node knowledge
*/
void
p9fs_destroy_node(struct p9fs_node **npp)
{
struct p9fs_node *np;
np = *npp;
if (np == NULL)
return;
/* Destroy the FID LIST locks */
P9FS_VFID_LOCK_DESTROY(np);
P9FS_VOFID_LOCK_DESTROY(np);
/* Dispose all node knowledge.*/
p9fs_dispose_node(&np);
}
/*
* Common code used across p9fs to return vnode for the file represented
* by the fid.
* Lookup for the vnode in hash_list. This lookup is based on the qid path
* which is unique to a file. p9fs_node_cmp is called in this lookup process.
* I. If the vnode we are looking for is found in the hash list
* 1. Check if the vnode is a valid vnode by reloading its stats
* a. if the reloading of the vnode stats returns error then remove the
* vnode from hash list and return
* b. If reloading of vnode stats returns without any error then, clunk the
* new fid which was created for the vnode as we know that the vnode
* already has a fid associated with it and return the vnode.
* This is to avoid fid leaks
* II. If vnode is not found in the hash list then, create new vnode, p9fs
* node and return the vnode
*/
int
p9fs_vget_common(struct mount *mp, struct p9fs_node *np, int flags,
struct p9fs_node *parent, struct p9_fid *fid, struct vnode **vpp,
char *name)
{
struct p9fs_mount *vmp;
struct p9fs_session *vses;
struct vnode *vp;
struct p9fs_node *node;
struct thread *td;
uint32_t hash;
int error, error_reload = 0;
struct p9fs_inode *inode;
td = curthread;
vmp = VFSTOP9(mp);
vses = &vmp->p9fs_session;
/* Look for vp in the hash_list */
hash = fnv_32_buf(&fid->qid.path, sizeof(uint64_t), FNV1_32_INIT);
error = vfs_hash_get(mp, hash, flags, td, &vp, p9fs_node_cmp,
&fid->qid);
if (error != 0)
return (error);
else if (vp != NULL) {
if (vp->v_vflag & VV_ROOT) {
if (np == NULL)
p9_client_clunk(fid);
*vpp = vp;
return (0);
}
error = p9fs_reload_stats_dotl(vp, curthread->td_ucred);
if (error != 0) {
node = vp->v_data;
/* Remove stale vnode from hash list */
vfs_hash_remove(vp);
node->flags |= P9FS_NODE_DELETED;
vput(vp);
*vpp = NULLVP;
vp = NULL;
} else {
*vpp = vp;
/* Clunk the new fid if not root */
p9_client_clunk(fid);
return (0);
}
}
/*
* We must promote to an exclusive lock for vnode creation. This
* can happen if lookup is passed LOCKSHARED.
*/
if ((flags & LK_TYPE_MASK) == LK_SHARED) {
flags &= ~LK_TYPE_MASK;
flags |= LK_EXCLUSIVE;
}
/* Allocate a new vnode. */
if ((error = getnewvnode("p9fs", mp, &p9fs_vnops, &vp)) != 0) {
*vpp = NULLVP;
P9_DEBUG(ERROR, "%s: getnewvnode failed: %d\n", __func__, error);
return (error);
}
/* If we dont have it, create one. */
if (np == NULL) {
np = uma_zalloc(p9fs_node_zone, M_WAITOK | M_ZERO);
/* Initialize the VFID list */
P9FS_VFID_LOCK_INIT(np);
STAILQ_INIT(&np->vfid_list);
p9fs_fid_add(np, fid, VFID);
/* Initialize the VOFID list */
P9FS_VOFID_LOCK_INIT(np);
STAILQ_INIT(&np->vofid_list);
vref(P9FS_NTOV(parent));
np->parent = parent;
np->p9fs_ses = vses; /* Map the current session */
inode = &np->inode;
/*Fill the name of the file in inode */
inode->i_name = malloc(strlen(name)+1, M_TEMP, M_NOWAIT | M_ZERO);
strlcpy(inode->i_name, name, strlen(name)+1);
} else {
vp->v_type = VDIR; /* root vp is a directory */
vp->v_vflag |= VV_ROOT;
vref(vp); /* Increment a reference on root vnode during mount */
}
vp->v_data = np;
np->v_node = vp;
inode = &np->inode;
inode->i_qid_path = fid->qid.path;
P9FS_SET_LINKS(inode);
lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
error = insmntque(vp, mp);
if (error != 0) {
/*
* vput(vp) is already called from insmntque_stddtr().
* Just goto 'out' to dispose the node.
*/
goto out;
}
/* Init the vnode with the disk info*/
error = p9fs_reload_stats_dotl(vp, curthread->td_ucred);
if (error != 0) {
error_reload = 1;
goto out;
}
error = vfs_hash_insert(vp, hash, flags, td, vpp,
p9fs_node_cmp, &fid->qid);
if (error != 0) {
goto out;
}
if (*vpp == NULL) {
P9FS_LOCK(vses);
STAILQ_INSERT_TAIL(&vses->virt_node_list, np, p9fs_node_next);
np->flags |= P9FS_NODE_IN_SESSION;
P9FS_UNLOCK(vses);
*vpp = vp;
} else {
/*
* Returning matching vp found in hashlist.
* So cleanup the np allocated above in this context.
*/
if (!IS_ROOT(np)) {
p9fs_destroy_node(&np);
}
}
return (0);
out:
/* Something went wrong, dispose the node */
if (!IS_ROOT(np)) {
p9fs_destroy_node(&np);
}
if (error_reload) {
vput(vp);
}
*vpp = NULLVP;
return (error);
}
/* Main mount function for 9pfs */
static int
p9_mount(struct mount *mp)
{
struct p9_fid *fid;
struct p9fs_mount *vmp;
struct p9fs_session *vses;
struct p9fs_node *p9fs_root;
int error;
char *from;
int len;
/* Verify the validity of mount options */
if (vfs_filteropt(mp->mnt_optnew, p9fs_opts))
return (EINVAL);
/* Extract NULL terminated mount tag from mount options */
error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len);
if (error != 0 || from[len - 1] != '\0')
return (EINVAL);
/* Allocate and initialize the private mount structure. */
vmp = malloc(sizeof (struct p9fs_mount), M_P9MNT, M_WAITOK | M_ZERO);
mp->mnt_data = vmp;
vmp->p9fs_mountp = mp;
vmp->mount_tag = from;
vmp->mount_tag_len = len;
vses = &vmp->p9fs_session;
vses->p9fs_mount = mp;
p9fs_root = &vses->rnp;
/* Hardware iosize from the Qemu */
mp->mnt_iosize_max = PAGE_SIZE;
/*
* Init the session for the p9fs root. This creates a new root fid and
* attaches the client and server.
*/
fid = p9fs_init_session(mp, &error);
if (fid == NULL) {
goto out;
}
P9FS_VFID_LOCK_INIT(p9fs_root);
STAILQ_INIT(&p9fs_root->vfid_list);
p9fs_fid_add(p9fs_root, fid, VFID);
P9FS_VOFID_LOCK_INIT(p9fs_root);
STAILQ_INIT(&p9fs_root->vofid_list);
p9fs_root->parent = p9fs_root;
p9fs_root->flags |= P9FS_ROOT;
p9fs_root->p9fs_ses = vses;
vfs_getnewfsid(mp);
strlcpy(mp->mnt_stat.f_mntfromname, from,
sizeof(mp->mnt_stat.f_mntfromname));
MNT_ILOCK(mp);
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED;
MNT_IUNLOCK(mp);
P9_DEBUG(VOPS, "%s: Mount successful\n", __func__);
/* Mount structures created. */
return (0);
out:
P9_DEBUG(ERROR, "%s: Mount Failed \n", __func__);
if (vmp != NULL) {
free(vmp, M_P9MNT);
mp->mnt_data = NULL;
}
return (error);
}
/* Mount entry point */
static int
p9fs_mount(struct mount *mp)
{
int error;
/*
* Minimal support for MNT_UPDATE - allow changing from
* readonly.
*/
if (mp->mnt_flag & MNT_UPDATE) {
if ((mp->mnt_flag & MNT_RDONLY) && !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
mp->mnt_flag &= ~MNT_RDONLY;
}
return (0);
}
error = p9_mount(mp);
if (error != 0)
(void) p9fs_unmount(mp, MNT_FORCE);
return (error);
}
/*
* Retrieve the root vnode of this mount. After filesystem is mounted, the root
* vnode is created for the first time. Subsequent calls to p9fs root will
* return the same vnode created during mount.
*/
static int
p9fs_root(struct mount *mp, int lkflags, struct vnode **vpp)
{
struct p9fs_mount *vmp;
struct p9fs_node *np;
struct p9_client *clnt;
struct p9_fid *vfid;
int error;
vmp = VFSTOP9(mp);
np = &vmp->p9fs_session.rnp;
clnt = vmp->p9fs_session.clnt;
error = 0;
P9_DEBUG(VOPS, "%s: node=%p name=%s\n",__func__, np, np->inode.i_name);
vfid = p9fs_get_fid(clnt, np, curthread->td_ucred, VFID, -1, &error);
if (error != 0) {
/* for root use the nobody user's fid as vfid.
* This is used while unmounting as root when non-root
* user has mounted p9fs
*/
if (vfid == NULL && clnt->trans_status == P9FS_BEGIN_DISCONNECT)
vfid = vmp->p9fs_session.mnt_fid;
else {
*vpp = NULLVP;
return (error);
}
}
error = p9fs_vget_common(mp, np, lkflags, np, vfid, vpp, NULL);
if (error != 0) {
*vpp = NULLVP;
return (error);
}
np->v_node = *vpp;
return (error);
}
/* Retrieve the file system statistics */
static int
p9fs_statfs(struct mount *mp __unused, struct statfs *buf)
{
struct p9fs_mount *vmp;
struct p9fs_node *np;
struct p9_client *clnt;
struct p9_fid *vfid;
struct p9_statfs statfs;
int res, error;
vmp = VFSTOP9(mp);
np = &vmp->p9fs_session.rnp;
clnt = vmp->p9fs_session.clnt;
error = 0;
vfid = p9fs_get_fid(clnt, np, curthread->td_ucred, VFID, -1, &error);
if (error != 0) {
return (error);
}
res = p9_client_statfs(vfid, &statfs);
if (res == 0) {
buf->f_type = statfs.type;
/*
* We have a limit of 4k irrespective of what the
* Qemu server can do.
*/
if (statfs.bsize > PAGE_SIZE)
buf->f_bsize = PAGE_SIZE;
else
buf->f_bsize = statfs.bsize;
buf->f_iosize = buf->f_bsize;
buf->f_blocks = statfs.blocks;
buf->f_bfree = statfs.bfree;
buf->f_bavail = statfs.bavail;
buf->f_files = statfs.files;
buf->f_ffree = statfs.ffree;
}
else {
/* Atleast set these if stat fail */
buf->f_bsize = PAGE_SIZE;
buf->f_iosize = buf->f_bsize; /* XXX */
}
return (0);
}
static int
p9fs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
{
return (EINVAL);
}
struct vfsops p9fs_vfsops = {
.vfs_init = p9fs_init,
.vfs_uninit = p9fs_uninit,
.vfs_mount = p9fs_mount,
.vfs_unmount = p9fs_unmount,
.vfs_root = p9fs_root,
.vfs_statfs = p9fs_statfs,
.vfs_fhtovp = p9fs_fhtovp,
};
VFS_SET(p9fs_vfsops, p9fs, VFCF_JAIL);
MODULE_VERSION(p9fs, 1);

2148
sys/fs/p9fs/p9fs_vnops.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1019,6 +1019,7 @@ vfs_mountroot_wait_if_neccessary(const char *fs, const char *dev)
* behaviour by setting vfs.root_mount_always_wait=1.
*/
if (strcmp(fs, "zfs") == 0 || strstr(fs, "nfs") != NULL ||
strcmp(fs, "p9fs") == 0 ||
dev[0] == '\0' || root_mount_always_wait != 0) {
vfs_mountroot_wait();
return (0);

View File

@ -305,6 +305,7 @@ SUBDIR= \
otus \
${_otusfw} \
ow \
p9fs \
${_padlock} \
${_padlock_rng} \
${_pchtherm} \

View File

@ -0,0 +1,8 @@
.PATH: ${SRCTOP}/sys/fs/p9fs
KMOD= p9fs
SRCS= vnode_if.h \
p9_client.c p9_protocol.c p9_transport.c \
p9fs_subr.c p9fs_vfsops.c p9fs_vnops.c
.include <bsd.kmod.mk>

View File

@ -22,6 +22,6 @@
# SUCH DAMAGE.
#
SUBDIR= virtio pci network block balloon scsi random console
SUBDIR= virtio pci network block balloon scsi random console p9fs
.include <bsd.subdir.mk>

View File

@ -0,0 +1,32 @@
#
#
# 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.
#
.PATH: ${SRCTOP}/sys/dev/virtio/p9fs
KMOD= virtio_p9fs
SRCS= virtio_p9fs.c
SRCS+= virtio_bus_if.h virtio_if.h
SRCS+= bus_if.h device_if.h
.include <bsd.kmod.mk>