aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/p9fs/p9fs_subr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/p9fs/p9fs_subr.c')
-rw-r--r--sys/fs/p9fs/p9fs_subr.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/sys/fs/p9fs/p9fs_subr.c b/sys/fs/p9fs/p9fs_subr.c
new file mode 100644
index 000000000000..d0f04f6c5e97
--- /dev/null
+++ b/sys/fs/p9fs/p9fs_subr.c
@@ -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);
+}