#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);
}
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;
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);
}
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;
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;
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;
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);
}
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;
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));
}
}
p9_client_begin_disconnect(vses->clnt);
}
void
p9fs_complete_close(struct mount *mp)
{
struct p9fs_session *vses;
struct p9fs_mount *vmp;
vmp = VFSTOP9(mp);
vses = &vmp->p9fs_session;
p9_client_disconnect(vses->clnt);
}
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);
p9_client_destroy(vses->clnt);
P9FS_LOCK_DESTROY(vses);
P9_DEBUG(SUBR, "%s: Clean close session .\n", __func__);
}
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);
}
}
}
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;
}
}
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;
}
}
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);
}
static int
p9fs_compatible_mode(struct p9_fid *fid, int mode)
{
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);
}
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);
}
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;
fid = p9fs_get_fid_from_uid(np, uid, fid_type, mode);
if (fid != NULL || fid_type == VOFID)
return (fid);
root = &np->p9fs_ses->rnp;
fid = p9fs_get_fid_from_uid(root, uid, fid_type, mode);
if(fid == NULL) {
fid = p9_client_attach(clnt, NULL, NULL, uid,
vses->aname, error);
if (*error != 0)
return (NULL);
p9fs_fid_add(root, fid, fid_type);
}
if (IS_ROOT(np))
return (fid);
nwnames = p9fs_get_full_path(np, &wnames);
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);
}