#include <vm/uma.h>
#include <sys/systm.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <fs/p9fs/p9_client.h>
#include <fs/p9fs/p9_debug.h>
#include <fs/p9fs/p9_transport.h>
#define QEMU_HEADER 7
#define P9FS_MAX_FID_CNT (1024 * 1024 * 1024)
#define P9FS_ROOT_FID_NO 2
#define P9FS_MIN_TAG 1
#define P9FS_MAX_TAG 65535
#define WSTAT_SIZE 47
#define WSTAT_EXTENSION_SIZE 14
static MALLOC_DEFINE(M_P9CLNT, "p9_client", "p9fs client structure");
static uma_zone_t p9fs_fid_zone;
static uma_zone_t p9fs_req_zone;
static uma_zone_t p9fs_buf_zone;
SYSCTL_DECL(_vfs_p9fs);
int p9_debug_level = 0;
SYSCTL_INT(_vfs_p9fs, OID_AUTO, debug_level, CTLFLAG_RW,
&p9_debug_level, 0, "p9fs debug logging");
static struct p9_req_t *p9_get_request(struct p9_client *c, int *error);
static struct p9_req_t *p9_client_request(
struct p9_client *c, int8_t type, int *error, const char *fmt, ...);
inline int
p9_is_proto_dotl(struct p9_client *clnt)
{
return (clnt->proto_version == p9_proto_2000L);
}
inline int
p9_is_proto_dotu(struct p9_client *clnt)
{
return (clnt->proto_version == p9_proto_2000u);
}
static int
p9_parse_opts(struct mount *mp, struct p9_client *clnt)
{
int error, len;
char *trans;
error = vfs_getopt(mp->mnt_optnew, "trans", (void **)&trans, &len);
if (error == ENOENT)
trans = "virtio";
clnt->proto_version = p9_proto_2000L;
clnt->msize = 8192;
clnt->ops = p9_get_trans_by_name(trans);
return (0);
}
static struct p9_buffer *
p9_buffer_alloc(int alloc_msize)
{
struct p9_buffer *fc;
fc = uma_zalloc(p9fs_buf_zone, M_WAITOK | M_ZERO);
fc->capacity = alloc_msize;
fc->offset = 0;
fc->size = 0;
fc->sdata = (char *)fc + sizeof(struct p9_buffer);
return (fc);
}
static void
p9_buffer_free(struct p9_buffer **buf)
{
uma_zfree(p9fs_buf_zone, *buf);
*buf = NULL;
}
static void
p9_free_req(struct p9_client *clnt, struct p9_req_t *req)
{
if (req->tc != NULL) {
if (req->tc->tag != P9_NOTAG)
p9_tag_destroy(clnt, req->tc->tag);
p9_buffer_free(&req->tc);
}
if (req->rc != NULL)
p9_buffer_free(&req->rc);
uma_zfree(p9fs_req_zone, req);
}
static struct p9_req_t *
p9_get_request(struct p9_client *clnt, int *error)
{
struct p9_req_t *req;
int alloc_msize;
uint16_t tag;
alloc_msize = P9FS_MTU;
req = uma_zalloc(p9fs_req_zone, M_WAITOK | M_ZERO);
req->tc = p9_buffer_alloc(alloc_msize);
req->rc = p9_buffer_alloc(alloc_msize);
tag = p9_tag_create(clnt);
if (tag == P9_NOTAG) {
*error = EAGAIN;
req->tc->tag = P9_NOTAG;
p9_free_req(clnt, req);
return (NULL);
}
req->tc->tag = tag;
return (req);
}
static int
p9_parse_receive(struct p9_buffer *buf, struct p9_client *clnt)
{
int8_t type;
int16_t tag;
int32_t size;
int error;
buf->offset = 0;
if (buf->size == 0)
buf->size = QEMU_HEADER;
error = p9_buf_readf(buf, 0, "dbw", &size, &type, &tag);
if (error != 0)
goto out;
buf->size = size;
buf->id = type;
buf->tag = tag;
P9_DEBUG(TRANS, "%s: size=%d type: %d tag: %d\n",
__func__, buf->size, buf->id, buf->tag);
out:
return (error);
}
static int
p9_client_check_return(struct p9_client *c, struct p9_req_t *req)
{
int error;
int ecode;
char *ename;
error = p9_parse_receive(req->rc, c);
if (error != 0)
goto out;
if (req->rc->id != P9PROTO_RERROR && req->rc->id != P9PROTO_RLERROR)
return (0);
if (req->rc->id == P9PROTO_RERROR) {
error = p9_buf_readf(req->rc, c->proto_version, "s?d", &ename, &ecode);
} else if (req->rc->id == P9PROTO_RLERROR) {
error = p9_buf_readf(req->rc, c->proto_version, "d", &ecode);
} else {
goto out;
}
if (error != 0)
goto out;
error = ecode;
if (error != 0) {
if (req->rc->id == P9PROTO_RERROR) {
P9_DEBUG(PROTO, "RERROR error %d ename %s\n",
error, ename);
} else if (req->rc->id == P9PROTO_RLERROR) {
P9_DEBUG(PROTO, "RLERROR error %d\n", error);
}
}
if (req->rc->id == P9PROTO_RERROR) {
free(ename, M_TEMP);
}
return (error);
out:
P9_DEBUG(ERROR, "couldn't parse receive buffer error%d\n", error);
return (error);
}
void p9_client_disconnect(struct p9_client *clnt)
{
P9_DEBUG(TRANS, "%s: clnt %p\n", __func__, clnt);
clnt->trans_status = P9FS_DISCONNECT;
}
void p9_client_begin_disconnect(struct p9_client *clnt)
{
P9_DEBUG(TRANS, "%s: clnt %p\n", __func__, clnt);
clnt->trans_status = P9FS_BEGIN_DISCONNECT;
}
static struct p9_req_t *
p9_client_prepare_req(struct p9_client *c, int8_t type,
int req_size, int *error, const char *fmt, __va_list ap)
{
struct p9_req_t *req;
P9_DEBUG(TRANS, "%s: client %p op %d\n", __func__, c, type);
if (c->trans_status == P9FS_DISCONNECT) {
*error = EIO;
return (NULL);
}
if ((c->trans_status == P9FS_BEGIN_DISCONNECT) &&
(type != P9PROTO_TCLUNK)) {
*error = EIO;
return (NULL);
}
req = p9_get_request(c, error);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: request allocation failed.\n", __func__);
return (NULL);
}
*error = p9_buf_prepare(req->tc, type);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: p9_buf_prepare failed: %d\n",
__func__, *error);
goto out;
}
*error = p9_buf_vwritef(req->tc, c->proto_version, fmt, ap);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: p9_buf_vwrite failed: %d\n",
__func__, *error);
goto out;
}
*error = p9_buf_finalize(c, req->tc);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: p9_buf_finalize failed: %d \n",
__func__, *error);
goto out;
}
return (req);
out:
p9_free_req(c, req);
return (NULL);
}
static struct p9_req_t *
p9_client_request(struct p9_client *c, int8_t type, int *error,
const char *fmt, ...)
{
va_list ap;
struct p9_req_t *req;
va_start(ap, fmt);
req = p9_client_prepare_req(c, type, c->msize, error, fmt, ap);
va_end(ap);
if (*error != 0)
return (NULL);
*error = c->ops->request(c->handle, req);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: failed: %d\n", __func__, *error);
goto out;
}
*error = p9_client_check_return(c, req);
if (*error != 0)
goto out;
return (req);
out:
p9_free_req(c, req);
return (NULL);
}
uint16_t
p9_tag_create(struct p9_client *clnt)
{
int tag;
tag = alloc_unr(&clnt->tagpool);
P9_DEBUG(LPROTO, "%s: clnt %p: tag %d\n", __func__, clnt, tag);
if (tag == -1) {
return (P9_NOTAG);
}
return (tag);
}
void
p9_tag_destroy(struct p9_client *clnt, uint16_t tag)
{
P9_DEBUG(LPROTO, "%s: clnt %p: tag %d\n", __func__, clnt, tag);
free_unr(&clnt->tagpool, tag);
}
struct p9_fid *
p9_fid_create(struct p9_client *clnt)
{
struct p9_fid *fid;
fid = uma_zalloc(p9fs_fid_zone, M_WAITOK | M_ZERO);
fid->fid = alloc_unr(&clnt->fidpool);
P9_DEBUG(LPROTO, "%s: fid %d\n", __func__, fid->fid);
if (fid->fid == -1) {
uma_zfree(p9fs_fid_zone, fid);
return (NULL);
}
fid->mode = -1;
fid->uid = -1;
fid->clnt = clnt;
return (fid);
}
void
p9_fid_destroy(struct p9_fid *fid)
{
struct p9_client *clnt;
P9_DEBUG(LPROTO, "%s: fid %d\n", __func__, fid->fid);
clnt = fid->clnt;
free_unr(&clnt->fidpool, fid->fid);
uma_zfree(p9fs_fid_zone, fid);
}
int
p9_client_version(struct p9_client *c)
{
int error;
struct p9_req_t *req;
char *version;
int msize;
error = 0;
P9_DEBUG(PROTO, "TVERSION msize %d protocol %d\n",
c->msize, c->proto_version);
switch (c->proto_version) {
case p9_proto_2000L:
req = p9_client_request(c, P9PROTO_TVERSION, &error, "ds",
c->msize, "9P2000.L");
break;
case p9_proto_2000u:
req = p9_client_request(c, P9PROTO_TVERSION, &error, "ds",
c->msize, "9P2000.u");
break;
case p9_proto_legacy:
req = p9_client_request(c, P9PROTO_TVERSION, &error, "ds",
c->msize, "9P2000");
break;
default:
return (EINVAL);
}
if (error != 0)
return (error);
error = p9_buf_readf(req->rc, c->proto_version, "ds", &msize, &version);
if (error != 0) {
P9_DEBUG(ERROR, "%s: version error: %d\n", __func__, error);
goto out;
}
P9_DEBUG(PROTO, "RVERSION msize %d %s\n", msize, version);
if (!strncmp(version, "9P2000.L", 8))
c->proto_version = p9_proto_2000L;
else if (!strncmp(version, "9P2000.u", 8))
c->proto_version = p9_proto_2000u;
else if (!strncmp(version, "9P2000", 6))
c->proto_version = p9_proto_legacy;
else {
error = ENOMEM;
goto out;
}
if (msize < c->msize)
c->msize = msize;
out:
p9_free_req(c, req);
return (error);
}
void
p9_init_zones(void)
{
p9fs_fid_zone = uma_zcreate("p9fs fid zone",
sizeof(struct p9_fid), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
p9fs_req_zone = uma_zcreate("p9fs req zone",
sizeof(struct p9_req_t), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
p9fs_buf_zone = uma_zcreate("p9fs buf zone",
sizeof(struct p9_buffer) + P9FS_MTU, NULL, NULL,
NULL, NULL, UMA_ALIGN_PTR, 0);
}
void
p9_destroy_zones(void)
{
uma_zdestroy(p9fs_fid_zone);
uma_zdestroy(p9fs_req_zone);
uma_zdestroy(p9fs_buf_zone);
}
struct p9_client *
p9_client_create(struct mount *mp, int *error, const char *mount_tag)
{
struct p9_client *clnt;
clnt = malloc(sizeof(struct p9_client), M_P9CLNT, M_WAITOK | M_ZERO);
mtx_init(&clnt->clnt_mtx, "p9clnt", NULL, MTX_DEF);
*error = p9_parse_opts(mp, clnt);
if (*error != 0)
goto out;
if (clnt->ops == NULL) {
*error = EINVAL;
P9_DEBUG(ERROR, "%s: no transport\n", __func__);
goto out;
}
init_unrhdr(&clnt->fidpool, P9FS_ROOT_FID_NO, P9FS_MAX_FID_CNT,
&clnt->clnt_mtx);
init_unrhdr(&clnt->tagpool, P9FS_MIN_TAG, P9FS_MAX_TAG,
&clnt->clnt_mtx);
P9_DEBUG(TRANS, "%s: clnt %p trans %p msize %d protocol %d\n",
__func__, clnt, clnt->ops, clnt->msize, clnt->proto_version);
*error = clnt->ops->create(mount_tag, &clnt->handle);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: transport create failed .%d \n",
__func__, *error);
goto out;
}
clnt->trans_status = P9FS_CONNECT;
*error = p9_client_version(clnt);
if (*error != 0)
goto out;
P9_DEBUG(TRANS, "%s: client creation succeeded.\n", __func__);
return (clnt);
out:
free(clnt, M_P9CLNT);
return (NULL);
}
void
p9_client_destroy(struct p9_client *clnt)
{
P9_DEBUG(TRANS, "%s: client %p\n", __func__, clnt);
clnt->ops->close(clnt->handle);
P9_DEBUG(TRANS, "%s : Destroying fidpool\n", __func__);
clear_unrhdr(&clnt->fidpool);
P9_DEBUG(TRANS, "%s : Destroying tagpool\n", __func__);
clear_unrhdr(&clnt->tagpool);
free(clnt, M_P9CLNT);
}
struct p9_fid *
p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
const char *uname, uid_t n_uname, const char *aname, int *error)
{
struct p9_req_t *req;
struct p9_fid *fid;
struct p9_qid qid;
P9_DEBUG(PROTO, "TATTACH uname=%s aname=%s, n_uname=%d\n",
uname, aname, n_uname);
fid = p9_fid_create(clnt);
if (fid == NULL) {
*error = ENOMEM;
return (NULL);
}
fid->uid = n_uname;
req = p9_client_request(clnt, P9PROTO_TATTACH, error, "ddssd", fid->fid,
P9PROTO_NOFID, uname, aname, n_uname);
if (*error != 0)
goto out;
*error = p9_buf_readf(req->rc, clnt->proto_version, "Q", &qid);
if (*error != 0) {
P9_DEBUG(ERROR, "%s: p9_buf_readf failed: %d \n",
__func__, *error);
goto out;
}
P9_DEBUG(PROTO, "RATTACH qid %x.%llx.%x\n",
qid.type, (unsigned long long)qid.path, qid.version);
memmove(&fid->qid, &qid, sizeof(struct p9_qid));
p9_free_req(clnt, req);
return (fid);
out:
if (req != NULL)
p9_free_req(clnt, req);
if (fid != NULL)
p9_fid_destroy(fid);
return (NULL);
}
int
p9_client_remove(struct p9_fid *fid)
{
int error;
struct p9_client *clnt;
struct p9_req_t *req;
P9_DEBUG(PROTO, "TREMOVE fid %d\n", fid->fid);
error = 0;
clnt = fid->clnt;
req = p9_client_request(clnt, P9PROTO_TREMOVE, &error, "d", fid->fid);
if (error != 0) {
P9_DEBUG(PROTO, "RREMOVE fid %d\n", fid->fid);
return (error);
}
p9_free_req(clnt, req);
return (error);
}
int
p9_client_unlink(struct p9_fid *dfid, const char *name, int32_t flags)
{
int error;
struct p9_client *clnt;
struct p9_req_t *req;
error = 0;
clnt = dfid->clnt;
req = p9_client_request(clnt, P9PROTO_TUNLINKAT, &error, "dsd",
dfid->fid, name, flags);
if (error != 0) {
P9_DEBUG(PROTO, "RUNLINKAT fid %d\n", dfid->fid);
return (error);
}
p9_free_req(clnt, req);
return (error);
}
int
p9_client_clunk(struct p9_fid *fid)
{
int error;
struct p9_client *clnt;
struct p9_req_t *req;
error = 0;
if (fid == NULL) {
P9_DEBUG(ERROR, "%s: clunk with NULL fid is bad\n", __func__);
return (0);
}
P9_DEBUG(PROTO, "TCLUNK fid %d \n", fid->fid);
clnt = fid->clnt;
req = p9_client_request(clnt, P9PROTO_TCLUNK, &error, "d", fid->fid);
if (req != NULL) {
P9_DEBUG(PROTO, "RCLUNK fid %d\n", fid->fid);
p9_free_req(clnt, req);
}
p9_fid_destroy(fid);
return (error);
}
struct p9_fid *
p9_client_walk(struct p9_fid *oldfid, uint16_t nwnames, char **wnames,
int clone, int *error)
{
struct p9_client *clnt;
struct p9_fid *fid;
struct p9_qid *wqids;
struct p9_req_t *req;
uint16_t nwqids, count;
clnt = oldfid->clnt;
wqids = NULL;
nwqids = 0;
if (clnt->trans_status != P9FS_CONNECT) {
*error = EIO;
return (NULL);
}
if (clone) {
fid = p9_fid_create(clnt);
if (fid == NULL) {
*error = ENOMEM;
return (NULL);
}
fid->uid = oldfid->uid;
} else
fid = oldfid;
P9_DEBUG(PROTO, "TWALK fids %d,%d nwnames %u wname %s\n",
oldfid->fid, fid->fid, nwnames,
wnames != NULL ? wnames[nwnames-1] : NULL);
req = p9_client_request(clnt, P9PROTO_TWALK, error, "ddT", oldfid->fid,
fid->fid, wnames, nwnames);
if (*error != 0) {
if (fid != oldfid)
p9_fid_destroy(fid);
return (NULL);
}
*error = p9_buf_readf(req->rc, clnt->proto_version, "R", &nwqids,
&wqids);
if (*error != 0)
goto out;
P9_DEBUG(PROTO, "RWALK nwqid %d:\n", nwqids);
if (nwqids != nwnames) {
*error = ENOENT;
goto out;
}
for (count = 0; count < nwqids; count++)
P9_DEBUG(TRANS, "%s: [%d] %x.%llx.%x\n",
__func__, count, wqids[count].type,
(unsigned long long)wqids[count].path,
wqids[count].version);
if (nwnames)
memmove(&fid->qid, &wqids[nwqids - 1], sizeof(struct p9_qid));
else
fid->qid = oldfid->qid;
p9_free_req(clnt, req);
free(wqids, M_TEMP);
return (fid);
out:
p9_free_req(clnt, req);
if (wqids)
free(wqids, M_TEMP);
if (fid && fid != oldfid)
p9_client_clunk(fid);
return (NULL);
}
int
p9_client_open(struct p9_fid *fid, int mode)
{
int error, mtu;
struct p9_client *clnt;
struct p9_req_t *req;
error = 0;
clnt = fid->clnt;
mtu = 0;
P9_DEBUG(PROTO, "%s fid %d mode %d\n",
p9_is_proto_dotl(clnt) ? "TLOPEN" : "TOPEN",
fid->fid, mode);
if (fid->mode != -1)
return (EINVAL);
if (p9_is_proto_dotl(clnt))
req = p9_client_request(clnt, P9PROTO_TLOPEN, &error, "dd",
fid->fid, mode);
else
req = p9_client_request(clnt, P9PROTO_TOPEN, &error, "db",
fid->fid, mode);
if (error != 0)
return (error);
error = p9_buf_readf(req->rc, clnt->proto_version, "Qd", &fid->qid,
&mtu);
if (error != 0)
goto out;
P9_DEBUG(PROTO, "%s qid %x.%llx.%x mtu %x\n",
p9_is_proto_dotl(clnt) ? "RLOPEN" : "ROPEN",
(fid->qid).type, (unsigned long long)(fid->qid).path,
(fid->qid).version, mtu);
fid->mode = mode;
fid->mtu = mtu;
out:
p9_free_req(clnt, req);
return (error);
}
int
p9_client_readdir(struct p9_fid *fid, char *data, uint64_t offset,
uint32_t count)
{
int error;
uint32_t rsize;
struct p9_client *clnt;
struct p9_req_t *req;
char *dataptr;
P9_DEBUG(PROTO, "TREADDIR fid %d offset %llu count %d\n",
fid->fid, (unsigned long long) offset, count);
error = 0;
rsize = fid->mtu;
clnt = fid->clnt;
if (rsize == 0 || rsize > clnt->msize)
rsize = clnt->msize;
if (count < rsize)
rsize = count;
req = p9_client_request(clnt, P9PROTO_TREADDIR, &error, "dqd",
fid->fid, offset, rsize);
if (error != 0) {
P9_DEBUG(ERROR, "%s: couldn't allocate req in client_readdir\n",
__func__);
return (-error);
}
error = p9_buf_readf(req->rc, clnt->proto_version, "D", &count,
&dataptr);
if (error != 0) {
P9_DEBUG(ERROR, "%s: p0_buf_readf failed: %d\n",
__func__, error);
p9_free_req(clnt, req);
return (-error);
}
P9_DEBUG(PROTO, "RREADDIR count %u\n", count);
memmove(data, dataptr, count);
p9_free_req(clnt, req);
return (count);
}
int
p9_client_read(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data)
{
struct p9_client *clnt;
struct p9_req_t *req;
char *dataptr;
int error, rsize;
clnt = fid->clnt;
rsize = fid->mtu;
error = 0;
P9_DEBUG(PROTO, "TREAD fid %d offset %llu %u\n",
fid->fid, (unsigned long long) offset, count);
if (!rsize || rsize > clnt->msize)
rsize = clnt->msize;
if (count < rsize)
rsize = count;
req = p9_client_request(clnt, P9PROTO_TREAD, &error, "dqd", fid->fid,
offset, rsize);
if (error != 0) {
P9_DEBUG(ERROR, "%s: failed allocate request\n", __func__);
return (-error);
}
error = p9_buf_readf(req->rc, clnt->proto_version, "D", &count,
&dataptr);
if (error != 0) {
P9_DEBUG(ERROR, "%s: p9_buf_readf failed: %d\n",
__func__, error);
goto out;
}
if (rsize < count) {
P9_DEBUG(PROTO, "RREAD count (%d > %d)\n", count, rsize);
count = rsize;
}
P9_DEBUG(PROTO, "RREAD count %d\n", count);
if (count == 0) {
error = -EIO;
P9_DEBUG(ERROR, "%s: EIO error in client_read \n", __func__);
goto out;
}
memmove(data, dataptr, count);
p9_free_req(clnt, req);
return (count);
out:
p9_free_req(clnt, req);
return (-error);
}
int
p9_client_write(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data)
{
struct p9_client *clnt;
struct p9_req_t *req;
int ret, error, rsize;
clnt = fid->clnt;
rsize = fid->mtu;
ret = 0;
error = 0;
P9_DEBUG(PROTO, "TWRITE fid %d offset %llu %u\n",
fid->fid, (unsigned long long) offset, count);
if (!rsize || rsize > clnt->msize)
rsize = clnt->msize;
if (count > rsize) {
count = rsize;
}
req = p9_client_request(clnt, P9PROTO_TWRITE, &error, "dqD", fid->fid,
offset, count, data);
if (error != 0) {
P9_DEBUG(ERROR, "%s: failed allocate request: %d\n",
__func__, error);
return (-error);
}
error = p9_buf_readf(req->rc, clnt->proto_version, "d", &ret);
if (error != 0) {
P9_DEBUG(ERROR, "%s: p9_buf_readf error: %d\n",
__func__, error);
goto out;
}
if (count < ret) {
P9_DEBUG(PROTO, "RWRITE count (%d > %d)\n", count, ret);
ret = count;
}
P9_DEBUG(PROTO, "RWRITE count %d\n", ret);
if (count == 0) {
error = EIO;
P9_DEBUG(ERROR, "%s: EIO error\n", __func__);
goto out;
}
p9_free_req(clnt, req);
return (ret);
out:
p9_free_req(clnt, req);
return (-error);
}
int
p9_client_file_create(struct p9_fid *fid, char *name, uint32_t perm, int mode,
char *extension)
{
int error;
struct p9_client *clnt;
struct p9_req_t *req;
struct p9_qid qid;
int mtu;
P9_DEBUG(PROTO, "TCREATE fid %d name %s perm %d mode %d\n",
fid->fid, name, perm, mode);
clnt = fid->clnt;
error = 0;
if (fid->mode != -1)
return (EINVAL);
req = p9_client_request(clnt, P9PROTO_TCREATE, &error, "dsdb?s",
fid->fid, name, perm, mode, extension);
if (error != 0)
return (error);
error = p9_buf_readf(req->rc, clnt->proto_version, "Qd", &qid, &mtu);
if (error != 0)
goto out;
P9_DEBUG(PROTO, "RCREATE qid %x.%jx.%x mtu %x\n",
qid.type, (uintmax_t)qid.path, qid.version, mtu);
fid->mode = mode;
fid->mtu = mtu;
out:
p9_free_req(clnt, req);
return (error);
}
int
p9_client_statfs(struct p9_fid *fid, struct p9_statfs *stat)
{
int error;
struct p9_req_t *req;
struct p9_client *clnt;
error = 0;
clnt = fid->clnt;
P9_DEBUG(PROTO, "TSTATFS fid %d\n", fid->fid);
req = p9_client_request(clnt, P9PROTO_TSTATFS, &error, "d", fid->fid);
if (error != 0) {
return (error);
}
error = p9_buf_readf(req->rc, clnt->proto_version, "ddqqqqqqd",
&stat->type, &stat->bsize, &stat->blocks, &stat->bfree,
&stat->bavail, &stat->files, &stat->ffree, &stat->fsid,
&stat->namelen);
if (error != 0)
goto out;
P9_DEBUG(PROTO, "RSTATFS fid %d type 0x%jx bsize %ju "
"blocks %ju bfree %ju bavail %ju files %ju ffree %ju "
"fsid %ju namelen %ju\n",
fid->fid, (uintmax_t)stat->type,
(uintmax_t)stat->bsize, (uintmax_t)stat->blocks,
(uintmax_t)stat->bfree, (uintmax_t)stat->bavail,
(uintmax_t)stat->files, (uintmax_t)stat->ffree,
(uintmax_t)stat->fsid, (uintmax_t)stat->namelen);
out:
p9_free_req(clnt, req);
return (error);
}
int
p9_client_renameat(struct p9_fid *oldfid, char *oldname, struct p9_fid *newfid,
char *newname)
{
int error;
struct p9_client *clnt;
struct p9_req_t *req;
P9_DEBUG(PROTO, "TRENAMEAT oldfid %d oldname %s newfid %d newfid %s",
oldfid->fid, oldname, newfid->fid, newname);
error = 0;
clnt = oldfid->clnt;
req = p9_client_request(clnt, P9PROTO_TRENAMEAT, &error, "dsds",
oldfid->fid, oldname, newfid->fid, newname);
if (error != 0)
return (error);
p9_free_req(clnt, req);
return (error);
}
int
p9_create_symlink(struct p9_fid *fid, char *name, char *symtgt, gid_t gid)
{
int error;
struct p9_req_t *req;
struct p9_client *clnt;
struct p9_qid qid;
error = 0;
clnt = fid->clnt;
P9_DEBUG(PROTO, "TSYMLINK fid %d name %s\n", fid->fid, name);
req = p9_client_request(clnt, P9PROTO_TSYMLINK, &error, "dssd",
fid->fid, name, symtgt, gid);
if (error != 0)
return (error);
error = p9_buf_readf(req->rc, clnt->proto_version, "Q", &qid);
if (error != 0) {
P9_DEBUG(ERROR, "%s: buf_readf failed %d\n", __func__, error);
return (error);
}
P9_DEBUG(PROTO, "RSYMLINK qid %x.%jx.%x\n",
qid.type, (uintmax_t)qid.path, qid.version);
p9_free_req(clnt, req);
return (0);
}
int
p9_create_hardlink(struct p9_fid *dfid, struct p9_fid *oldfid, char *name)
{
int error;
struct p9_req_t *req;
struct p9_client *clnt;
error = 0;
clnt = dfid->clnt;
P9_DEBUG(PROTO, "TLINK dfid %d oldfid %d name %s\n",
dfid->fid, oldfid->fid, name);
req = p9_client_request(clnt, P9PROTO_TLINK, &error, "dds", dfid->fid,
oldfid->fid, name);
if (error != 0)
return (error);
p9_free_req(clnt, req);
return (0);
}
int
p9_readlink(struct p9_fid *fid, char **target)
{
int error;
struct p9_client *clnt;
struct p9_req_t *req;
error = 0;
clnt = fid->clnt;
P9_DEBUG(PROTO, "TREADLINK fid %d\n", fid->fid);
req = p9_client_request(clnt, P9PROTO_TREADLINK, &error, "d", fid->fid);
if (error != 0)
return (error);
error = p9_buf_readf(req->rc, clnt->proto_version, "s", target);
if (error != 0) {
P9_DEBUG(ERROR, "%s: buf_readf failed %d\n", __func__, error);
return (error);
}
P9_DEBUG(PROTO, "RREADLINK target %s \n", *target);
p9_free_req(clnt, req);
return (0);
}
int
p9_client_getattr(struct p9_fid *fid, struct p9_stat_dotl *stat_dotl,
uint64_t request_mask)
{
int err;
struct p9_client *clnt;
struct p9_req_t *req;
err = 0;
P9_DEBUG(PROTO, "TGETATTR fid %d mask %ju\n",
fid->fid, (uintmax_t)request_mask);
clnt = fid->clnt;
req = p9_client_request(clnt, P9PROTO_TGETATTR, &err, "dq", fid->fid,
request_mask);
if (req == NULL) {
P9_DEBUG(ERROR, "%s: allocation failed %d", __func__, err);
goto error;
}
err = p9_buf_readf(req->rc, clnt->proto_version, "A", stat_dotl);
if (err != 0) {
P9_DEBUG(ERROR, "%s: buf_readf failed %d\n", __func__, err);
goto error;
}
p9_free_req(clnt, req);
P9_DEBUG(PROTO, "RGETATTR fid %d qid %x.%jx.%x st_mode %8.8x "
"uid %d gid %d nlink %ju rdev %jx st_size %jx blksize %ju "
"blocks %ju st_atime_sec %ju, st_atime_nsec %ju "
"st_mtime_sec %ju, st_mtime_nsec %ju st_ctime_sec %ju "
"st_ctime_nsec %ju st_btime_sec %ju, st_btime_nsec %ju "
"st_stat %ju, st_data_version %ju \n", fid->fid,
stat_dotl->qid.type, (uintmax_t)stat_dotl->qid.path,
stat_dotl->qid.version, stat_dotl->st_mode, stat_dotl->st_uid,
stat_dotl->st_gid, (uintmax_t)stat_dotl->st_nlink,
(uintmax_t)stat_dotl->st_rdev, (uintmax_t)stat_dotl->st_size,
(uintmax_t)stat_dotl->st_blksize,
(uintmax_t)stat_dotl->st_blocks, (uintmax_t)stat_dotl->st_atime_sec,
(uintmax_t)stat_dotl->st_atime_nsec, (uintmax_t)stat_dotl->st_mtime_sec,
(uintmax_t)stat_dotl->st_mtime_nsec, (uintmax_t)stat_dotl->st_ctime_sec,
(uintmax_t)stat_dotl->st_ctime_nsec, (uintmax_t)stat_dotl->st_btime_sec,
(uintmax_t)stat_dotl->st_btime_nsec, (uintmax_t)stat_dotl->st_gen,
(uintmax_t)stat_dotl->st_data_version);
return (err);
error:
if (req != NULL)
p9_free_req(clnt, req);
return (err);
}
int
p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr)
{
int err;
struct p9_req_t *req;
struct p9_client *clnt;
err = 0;
P9_DEBUG(PROTO, "TSETATTR fid %d"
" valid %x mode %x uid %d gid %d size %ju"
" atime_sec %ju atime_nsec %ju"
" mtime_sec %ju mtime_nsec %ju\n",
fid->fid,
p9attr->valid, p9attr->mode, p9attr->uid, p9attr->gid,
(uintmax_t)p9attr->size, (uintmax_t)p9attr->atime_sec,
(uintmax_t)p9attr->atime_nsec, (uintmax_t)p9attr->mtime_sec,
(uintmax_t)p9attr->mtime_nsec);
clnt = fid->clnt;
req = p9_client_request(clnt, P9PROTO_TSETATTR, &err, "dA", fid->fid,
p9attr);
if (req == NULL) {
P9_DEBUG(ERROR, "%s: allocation failed %d\n", __func__, err);
goto error;
}
p9_free_req(clnt, req);
error:
return (err);
}