xref: /kvmtool/virtio/9p.c (revision c7838fbd26dde8a1880231b5e061712d3767ce9c)
11c7850f9SSasha Levin #include "kvm/virtio-pci-dev.h"
21c7850f9SSasha Levin #include "kvm/ioport.h"
31c7850f9SSasha Levin #include "kvm/util.h"
41c7850f9SSasha Levin #include "kvm/threadpool.h"
560eb42d5SSasha Levin #include "kvm/ioeventfd.h"
6bfc15268SAneesh Kumar K.V #include "kvm/irq.h"
7bfc15268SAneesh Kumar K.V #include "kvm/virtio-9p.h"
8e59662b3SSasha Levin #include "kvm/guest_compat.h"
91c7850f9SSasha Levin 
10bfc15268SAneesh Kumar K.V #include <stdio.h>
11bfc15268SAneesh Kumar K.V #include <stdlib.h>
121c7850f9SSasha Levin #include <fcntl.h>
131c7850f9SSasha Levin #include <sys/stat.h>
14bfc15268SAneesh Kumar K.V #include <unistd.h>
15bfc15268SAneesh Kumar K.V #include <string.h>
16bfc15268SAneesh Kumar K.V #include <errno.h>
171c7850f9SSasha Levin 
182daa28d4SAneesh Kumar K.V #include <linux/virtio_ring.h>
192daa28d4SAneesh Kumar K.V #include <linux/virtio_9p.h>
202daa28d4SAneesh Kumar K.V #include <net/9p/9p.h>
212daa28d4SAneesh Kumar K.V 
22*c7838fbdSSasha Levin static LIST_HEAD(devs);
23*c7838fbdSSasha Levin 
241c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
25b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
26b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
271c7850f9SSasha Levin {
28b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
291c7850f9SSasha Levin 
301c7850f9SSasha Levin 	return abs_path;
311c7850f9SSasha Levin }
321c7850f9SSasha Levin 
331c7850f9SSasha Levin static int omode2uflags(u8 mode)
341c7850f9SSasha Levin {
351c7850f9SSasha Levin 	int ret = 0;
361c7850f9SSasha Levin 
371c7850f9SSasha Levin 	/* Basic open modes are same as uflags */
381c7850f9SSasha Levin 	ret = mode & 3;
391c7850f9SSasha Levin 
401c7850f9SSasha Levin 	/* Everything else is different */
411c7850f9SSasha Levin 	if (mode & P9_OTRUNC)
421c7850f9SSasha Levin 		ret |= O_TRUNC;
431c7850f9SSasha Levin 
441c7850f9SSasha Levin 	if (mode & P9_OAPPEND)
451c7850f9SSasha Levin 		ret |= O_APPEND;
461c7850f9SSasha Levin 
471c7850f9SSasha Levin 	if (mode & P9_OEXCL)
481c7850f9SSasha Levin 		ret |= O_EXCL;
491c7850f9SSasha Levin 
501c7850f9SSasha Levin 	return ret;
511c7850f9SSasha Levin }
521c7850f9SSasha Levin 
531c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid)
541c7850f9SSasha Levin {
551c7850f9SSasha Levin 	*qid = (struct p9_qid) {
561c7850f9SSasha Levin 		.path		= st->st_ino,
571c7850f9SSasha Levin 		.version	= st->st_mtime,
581c7850f9SSasha Levin 	};
591c7850f9SSasha Levin 
601c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
611c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
621c7850f9SSasha Levin }
631c7850f9SSasha Levin 
64b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
651c7850f9SSasha Levin {
66b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].fd > 0) {
67b4422bf3SAneesh Kumar K.V 		close(p9dev->fids[fid].fd);
68b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].fd = -1;
691c7850f9SSasha Levin 	}
70b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].dir) {
71b4422bf3SAneesh Kumar K.V 		closedir(p9dev->fids[fid].dir);
72b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].dir = NULL;
731c7850f9SSasha Levin 	}
741c7850f9SSasha Levin }
751c7850f9SSasha Levin 
76bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size)
771c7850f9SSasha Levin {
78bfc15268SAneesh Kumar K.V 	u8 cmd;
79bfc15268SAneesh Kumar K.V 	u16 tag;
80bfc15268SAneesh Kumar K.V 
81bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32);
82bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag);
83bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
84bfc15268SAneesh Kumar K.V 	/* cmd + 1 is the reply message */
85bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag);
861c7850f9SSasha Levin }
871c7850f9SSasha Levin 
886b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
896b163a87SAneesh Kumar K.V {
906b163a87SAneesh Kumar K.V 	int i;
916b163a87SAneesh Kumar K.V 	u32 total = 0;
926b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
936b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
946b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
956b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
966b163a87SAneesh Kumar K.V 			i++;
976b163a87SAneesh Kumar K.V 			break;
986b163a87SAneesh Kumar K.V 		}
996b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
1006b163a87SAneesh Kumar K.V 	}
1016b163a87SAneesh Kumar K.V 	return i;
1026b163a87SAneesh Kumar K.V }
1036b163a87SAneesh Kumar K.V 
104eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
105eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
106eee1ba8eSAneesh Kumar K.V {
107bfc15268SAneesh Kumar K.V 	u16 tag;
108eee1ba8eSAneesh Kumar K.V 	char *err_str;
109eee1ba8eSAneesh Kumar K.V 
110eee1ba8eSAneesh Kumar K.V 	err_str = strerror(err);
111bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
1125f900f6dSSasha Levin 	virtio_p9_pdu_writef(pdu, "sd", err_str, err);
113bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
114eee1ba8eSAneesh Kumar K.V 
115bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32) + sizeof(u8);
116bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "w", &tag);
117bfc15268SAneesh Kumar K.V 
118bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
119bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RERROR, tag);
120eee1ba8eSAneesh Kumar K.V }
121eee1ba8eSAneesh Kumar K.V 
122ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
123af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1241c7850f9SSasha Levin {
125bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "ds", 4096, VIRTIO_P9_VERSION);
1261c7850f9SSasha Levin 
127bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
128bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
129ead43b01SAneesh Kumar K.V 	return;
1301c7850f9SSasha Levin }
1311c7850f9SSasha Levin 
132ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
133af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
1341c7850f9SSasha Levin {
135bfc15268SAneesh Kumar K.V 	u32 fid;
1361c7850f9SSasha Levin 
137bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid);
138bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid);
1391c7850f9SSasha Levin 
140bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
141bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
142ead43b01SAneesh Kumar K.V 	return;
1431c7850f9SSasha Levin }
1441c7850f9SSasha Levin 
145ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
146af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
1471c7850f9SSasha Levin {
148bfc15268SAneesh Kumar K.V 	u8 mode;
149bfc15268SAneesh Kumar K.V 	u32 fid;
1501c7850f9SSasha Levin 	struct stat st;
151bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
152bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
153bfc15268SAneesh Kumar K.V 
154bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "db", &fid, &mode);
155bfc15268SAneesh Kumar K.V 	new_fid = &p9dev->fids[fid];
1561c7850f9SSasha Levin 
15730204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
158eee1ba8eSAneesh Kumar K.V 		goto err_out;
1591c7850f9SSasha Levin 
160bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
1611c7850f9SSasha Levin 
162eee1ba8eSAneesh Kumar K.V 	if (new_fid->is_dir) {
1631c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
164eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
165eee1ba8eSAneesh Kumar K.V 			goto err_out;
166eee1ba8eSAneesh Kumar K.V 	} else {
167eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
168bfc15268SAneesh Kumar K.V 				    omode2uflags(mode) | O_NOFOLLOW);
169eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
170eee1ba8eSAneesh Kumar K.V 			goto err_out;
171eee1ba8eSAneesh Kumar K.V 	}
172bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
173bfc15268SAneesh Kumar K.V 
174bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
175bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
176ead43b01SAneesh Kumar K.V 	return;
177eee1ba8eSAneesh Kumar K.V err_out:
178eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
179ead43b01SAneesh Kumar K.V 	return;
1801c7850f9SSasha Levin }
1811c7850f9SSasha Levin 
182ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
183af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
1841c7850f9SSasha Levin {
1854bc9734aSAneesh Kumar K.V 	DIR *dir;
1864bc9734aSAneesh Kumar K.V 	int fd;
1874bc9734aSAneesh Kumar K.V 	int res;
1881c7850f9SSasha Levin 	u8 mode;
1891c7850f9SSasha Levin 	u32 perm;
190bfc15268SAneesh Kumar K.V 	char *name;
1915f900f6dSSasha Levin 	char *ext = NULL;
192bfc15268SAneesh Kumar K.V 	u32 fid_val;
193af045e53SAneesh Kumar K.V 	struct stat st;
194bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
195bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
1964bc9734aSAneesh Kumar K.V 	char full_path[PATH_MAX];
197af045e53SAneesh Kumar K.V 
1985f900f6dSSasha Levin 	virtio_p9_pdu_readf(pdu, "dsdbs", &fid_val, &name, &perm, &mode, &ext);
199bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
2001c7850f9SSasha Levin 
2014bc9734aSAneesh Kumar K.V 	sprintf(full_path, "%s/%s", fid->abs_path, name);
2021c7850f9SSasha Levin 	if (perm & P9_DMDIR) {
2034bc9734aSAneesh Kumar K.V 		res = mkdir(full_path, perm & 0xFFFF);
2044bc9734aSAneesh Kumar K.V 		if (res < 0)
2054bc9734aSAneesh Kumar K.V 			goto err_out;
2064bc9734aSAneesh Kumar K.V 		dir = opendir(full_path);
2074bc9734aSAneesh Kumar K.V 		if (!dir)
2084bc9734aSAneesh Kumar K.V 			goto err_out;
2094bc9734aSAneesh Kumar K.V 		close_fid(p9dev, fid_val);
2104bc9734aSAneesh Kumar K.V 		fid->dir = dir;
2111c7850f9SSasha Levin 		fid->is_dir = 1;
2125f900f6dSSasha Levin 	} else if (perm & P9_DMSYMLINK) {
2135f900f6dSSasha Levin 		if (symlink(ext, full_path) < 0)
2145f900f6dSSasha Levin 			goto err_out;
2155f900f6dSSasha Levin 	} else if (perm & P9_DMLINK) {
2165f900f6dSSasha Levin 		int ext_fid = atoi(ext);
2175f900f6dSSasha Levin 
2185f900f6dSSasha Levin 		if (link(p9dev->fids[ext_fid].abs_path, full_path) < 0)
2195f900f6dSSasha Levin 			goto err_out;
2201c7850f9SSasha Levin 	} else {
2214bc9734aSAneesh Kumar K.V 		fd = open(full_path, omode2uflags(mode) | O_CREAT, 0777);
2224bc9734aSAneesh Kumar K.V 		if (fd < 0)
2234bc9734aSAneesh Kumar K.V 			goto err_out;
2244bc9734aSAneesh Kumar K.V 		close_fid(p9dev, fid_val);
2254bc9734aSAneesh Kumar K.V 		fid->fd = fd;
2261c7850f9SSasha Levin 	}
2274bc9734aSAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
2286c8ca053SAneesh Kumar K.V 		goto err_out;
2291c7850f9SSasha Levin 
2304bc9734aSAneesh Kumar K.V 	sprintf(fid->path, "%s/%s", fid->path, name);
231bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
232bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
233bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
234bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
2355f900f6dSSasha Levin 
2365f900f6dSSasha Levin 	free(name);
2375f900f6dSSasha Levin 	free(ext);
2386c8ca053SAneesh Kumar K.V 	return;
2396c8ca053SAneesh Kumar K.V err_out:
2406c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
2415f900f6dSSasha Levin 	free(name);
2425f900f6dSSasha Levin 	free(ext);
243ead43b01SAneesh Kumar K.V 	return;
2441c7850f9SSasha Levin }
2451c7850f9SSasha Levin 
246ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
247af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2481c7850f9SSasha Levin {
249af045e53SAneesh Kumar K.V 	u8 i;
250bfc15268SAneesh Kumar K.V 	u16 nwqid;
251bfc15268SAneesh Kumar K.V 	char *str;
252bfc15268SAneesh Kumar K.V 	u16 nwname;
253bfc15268SAneesh Kumar K.V 	u32 fid_val;
254bfc15268SAneesh Kumar K.V 	u32 newfid_val;
255bfc15268SAneesh Kumar K.V 	struct p9_qid wqid;
256bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
2575f900f6dSSasha Levin 	int ret;
2581c7850f9SSasha Levin 
259bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname);
260bfc15268SAneesh Kumar K.V 	new_fid	= &p9dev->fids[newfid_val];
2611c7850f9SSasha Levin 
262bfc15268SAneesh Kumar K.V 	nwqid = 0;
263bfc15268SAneesh Kumar K.V 	if (nwname) {
264bfc15268SAneesh Kumar K.V 		struct p9_fid *fid = &p9dev->fids[fid_val];
265bfc15268SAneesh Kumar K.V 
266bfc15268SAneesh Kumar K.V 		/* skip the space for count */
267bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
268bfc15268SAneesh Kumar K.V 		for (i = 0; i < nwname; i++) {
269bfc15268SAneesh Kumar K.V 			struct stat st;
2701c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
2711c7850f9SSasha Levin 			char full_path[PATH_MAX];
272bfc15268SAneesh Kumar K.V 
273bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_readf(pdu, "s", &str);
2741c7850f9SSasha Levin 
2751c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
276bfc15268SAneesh Kumar K.V 			sprintf(tmp, "%s/%.*s",
277bfc15268SAneesh Kumar K.V 				fid->path, (int)strlen(str), str);
2785f900f6dSSasha Levin 
2795f900f6dSSasha Levin 			ret = lstat(rel_to_abs(p9dev, tmp, full_path), &st);
2805f900f6dSSasha Levin 			if (ret < 0)
2816c8ca053SAneesh Kumar K.V 				goto err_out;
2821c7850f9SSasha Levin 
283bfc15268SAneesh Kumar K.V 			st2qid(&st, &wqid);
2841c7850f9SSasha Levin 			new_fid->is_dir = S_ISDIR(st.st_mode);
2851c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
286bfc15268SAneesh Kumar K.V 			new_fid->fid = newfid_val;
287bfc15268SAneesh Kumar K.V 			nwqid++;
288bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "Q", &wqid);
2891c7850f9SSasha Levin 		}
2901c7850f9SSasha Levin 	} else {
291bfc15268SAneesh Kumar K.V 		/*
292bfc15268SAneesh Kumar K.V 		 * update write_offset so our outlen get correct value
293bfc15268SAneesh Kumar K.V 		 */
294bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
295bfc15268SAneesh Kumar K.V 		new_fid->is_dir = p9dev->fids[fid_val].is_dir;
296bfc15268SAneesh Kumar K.V 		strcpy(new_fid->path, p9dev->fids[fid_val].path);
297bfc15268SAneesh Kumar K.V 		new_fid->fid	= newfid_val;
2981c7850f9SSasha Levin 	}
299bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
300bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
301bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", nwqid);
302bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3036c8ca053SAneesh Kumar K.V 	return;
3046c8ca053SAneesh Kumar K.V err_out:
3056c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
306ead43b01SAneesh Kumar K.V 	return;
3071c7850f9SSasha Levin }
3081c7850f9SSasha Levin 
309ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
310af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
3111c7850f9SSasha Levin {
312af045e53SAneesh Kumar K.V 	u32 i;
313bfc15268SAneesh Kumar K.V 	u32 fid_val;
314bfc15268SAneesh Kumar K.V 	u32 afid;
315bfc15268SAneesh Kumar K.V 	char *uname;
316bfc15268SAneesh Kumar K.V 	char *aname;
3171c7850f9SSasha Levin 	struct stat st;
318bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
3191c7850f9SSasha Levin 	struct p9_fid *fid;
320bfc15268SAneesh Kumar K.V 
321bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddss", &fid_val, &afid, &uname, &aname);
3221c7850f9SSasha Levin 
3231c7850f9SSasha Levin 	/* Reset everything */
3241c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++)
325b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].fid = P9_NOFID;
3261c7850f9SSasha Levin 
32730204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
3286c8ca053SAneesh Kumar K.V 		goto err_out;
3291c7850f9SSasha Levin 
330bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
3311c7850f9SSasha Levin 
332bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
333bfc15268SAneesh Kumar K.V 	fid->fid = fid_val;
3341c7850f9SSasha Levin 	fid->is_dir = 1;
3351c7850f9SSasha Levin 	strcpy(fid->path, "/");
3361c7850f9SSasha Levin 
337bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
338bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
339bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3405f900f6dSSasha Levin 	free(uname);
3415f900f6dSSasha Levin 	free(aname);
3426c8ca053SAneesh Kumar K.V 	return;
3436c8ca053SAneesh Kumar K.V err_out:
3445f900f6dSSasha Levin 	free(uname);
3455f900f6dSSasha Levin 	free(aname);
3466c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
347ead43b01SAneesh Kumar K.V 	return;
3481c7850f9SSasha Levin }
3491c7850f9SSasha Levin 
3505f900f6dSSasha Levin static void virtio_p9_free_stat(struct p9_wstat *wstat)
3515f900f6dSSasha Levin {
3525f900f6dSSasha Levin 	free(wstat->extension);
3535f900f6dSSasha Levin 	free(wstat->name);
3545f900f6dSSasha Levin }
3555f900f6dSSasha Levin 
356bfc15268SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev, const char *name,
357bfc15268SAneesh Kumar K.V 				struct stat *st, struct p9_wstat *wstat)
3581c7850f9SSasha Levin {
359bfc15268SAneesh Kumar K.V 	wstat->type = 0;
360bfc15268SAneesh Kumar K.V 	wstat->dev = 0;
361bfc15268SAneesh Kumar K.V 	st2qid(st, &wstat->qid);
362bfc15268SAneesh Kumar K.V 	wstat->mode = st->st_mode;
363bfc15268SAneesh Kumar K.V 	wstat->length = st->st_size;
3641c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode)) {
365bfc15268SAneesh Kumar K.V 		wstat->length = 0;
366bfc15268SAneesh Kumar K.V 		wstat->mode |= P9_DMDIR;
3671c7850f9SSasha Levin 	}
3685f900f6dSSasha Levin 	if (S_ISLNK(st->st_mode)) {
3695f900f6dSSasha Levin 		char tmp[PATH_MAX] = {0}, full_path[PATH_MAX] = {0};
3705f900f6dSSasha Levin 
3715f900f6dSSasha Levin 		rel_to_abs(p9dev, name, full_path);
3725f900f6dSSasha Levin 
3735f900f6dSSasha Levin 		if (readlink(full_path, tmp, PATH_MAX) > 0)
3745f900f6dSSasha Levin 			wstat->extension = strdup(tmp);
3755f900f6dSSasha Levin 		wstat->mode |= P9_DMSYMLINK;
3765f900f6dSSasha Levin 	} else {
3775f900f6dSSasha Levin 		wstat->extension = NULL;
3785f900f6dSSasha Levin 	}
3791c7850f9SSasha Levin 
380bfc15268SAneesh Kumar K.V 	wstat->atime = st->st_atime;
381bfc15268SAneesh Kumar K.V 	wstat->mtime = st->st_mtime;
3821c7850f9SSasha Levin 
383bfc15268SAneesh Kumar K.V 	wstat->name = strdup(name);
384bfc15268SAneesh Kumar K.V 	wstat->uid = NULL;
385bfc15268SAneesh Kumar K.V 	wstat->gid = NULL;
386bfc15268SAneesh Kumar K.V 	wstat->muid = NULL;
3875f900f6dSSasha Levin 	wstat->n_uid = wstat->n_gid = wstat->n_muid = 0;
3881c7850f9SSasha Levin 
3895f900f6dSSasha Levin 	/*
3905f900f6dSSasha Levin 	 * NOTE: size shouldn't include its own length
3915f900f6dSSasha Levin 	 * size[2] type[2] dev[4] qid[13]
3925f900f6dSSasha Levin 	 * mode[4] atime[4] mtime[4] length[8]
3935f900f6dSSasha Levin 	 * name[s] uid[s] gid[s] muid[s]
3945f900f6dSSasha Levin 	 * ext[s] uid[4] gid[4] muid[4]
3955f900f6dSSasha Levin 	 */
3965f900f6dSSasha Levin 	wstat->size = 2+4+13+4+4+4+8+2+2+2+2+2+4+4+4;
397bfc15268SAneesh Kumar K.V 	if (wstat->name)
398bfc15268SAneesh Kumar K.V 		wstat->size += strlen(wstat->name);
3995f900f6dSSasha Levin 	if (wstat->extension)
4005f900f6dSSasha Levin 		wstat->size += strlen(wstat->extension);
4011c7850f9SSasha Levin }
4021c7850f9SSasha Levin 
403ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
404af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4051c7850f9SSasha Levin {
406bfc15268SAneesh Kumar K.V 	u64 offset;
407bfc15268SAneesh Kumar K.V 	u32 fid_val;
408bfc15268SAneesh Kumar K.V 	u32 count, rcount;
4091c7850f9SSasha Levin 	struct stat st;
410bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
411bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
4121c7850f9SSasha Levin 
413bfc15268SAneesh Kumar K.V 	rcount = 0;
414bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
415bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
4161c7850f9SSasha Levin 	if (fid->is_dir) {
4171c7850f9SSasha Levin 		/* If reading a dir, fill the buffer with p9_stat entries */
4181c7850f9SSasha Levin 		char full_path[PATH_MAX];
419bfc15268SAneesh Kumar K.V 		struct dirent *cur = readdir(fid->dir);
4201c7850f9SSasha Levin 
421bfc15268SAneesh Kumar K.V 		/* Skip the space for writing count */
422bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u32);
4231c7850f9SSasha Levin 		while (cur) {
4241c7850f9SSasha Levin 			u32 read;
4251c7850f9SSasha Levin 
42630204a77SAneesh Kumar K.V 			lstat(rel_to_abs(p9dev, cur->d_name, full_path), &st);
427bfc15268SAneesh Kumar K.V 			virtio_p9_fill_stat(p9dev, cur->d_name, &st, &wstat);
428bfc15268SAneesh Kumar K.V 
429bfc15268SAneesh Kumar K.V 			read = pdu->write_offset;
430bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "S", &wstat);
431bfc15268SAneesh Kumar K.V 			rcount += pdu->write_offset - read;
4325f900f6dSSasha Levin 			virtio_p9_free_stat(&wstat);
433bfc15268SAneesh Kumar K.V 
4341c7850f9SSasha Levin 			cur = readdir(fid->dir);
4351c7850f9SSasha Levin 		}
4361c7850f9SSasha Levin 	} else {
43750c479e0SAneesh Kumar K.V 		u16 iov_cnt;
43850c479e0SAneesh Kumar K.V 		void *iov_base;
43950c479e0SAneesh Kumar K.V 		size_t iov_len;
44050c479e0SAneesh Kumar K.V 
44150c479e0SAneesh Kumar K.V 		iov_base = pdu->in_iov[0].iov_base;
44250c479e0SAneesh Kumar K.V 		iov_len  = pdu->in_iov[0].iov_len;
44350c479e0SAneesh Kumar K.V 		iov_cnt  = pdu->in_iov_cnt;
44450c479e0SAneesh Kumar K.V 
445af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32);
446af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32);
4476b163a87SAneesh Kumar K.V 		pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
448bfc15268SAneesh Kumar K.V 							   count,
4496b163a87SAneesh Kumar K.V 							   pdu->in_iov_cnt);
450bfc15268SAneesh Kumar K.V 		rcount = preadv(fid->fd, pdu->in_iov,
451bfc15268SAneesh Kumar K.V 				pdu->in_iov_cnt, offset);
452bfc15268SAneesh Kumar K.V 		if (rcount > count)
453bfc15268SAneesh Kumar K.V 			rcount = count;
454bfc15268SAneesh Kumar K.V 		/*
455bfc15268SAneesh Kumar K.V 		 * Update the iov_base back, so that rest of
456bfc15268SAneesh Kumar K.V 		 * pdu_writef works correctly.
457bfc15268SAneesh Kumar K.V 		 */
45850c479e0SAneesh Kumar K.V 		pdu->in_iov[0].iov_base = iov_base;
45950c479e0SAneesh Kumar K.V 		pdu->in_iov[0].iov_len  = iov_len;
46050c479e0SAneesh Kumar K.V 		pdu->in_iov_cnt         = iov_cnt;
461bfc15268SAneesh Kumar K.V 	}
462bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
463bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
464bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
465bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4661c7850f9SSasha Levin 
467ead43b01SAneesh Kumar K.V 	return;
4681c7850f9SSasha Levin }
4691c7850f9SSasha Levin 
470ead43b01SAneesh Kumar K.V static void virtio_p9_stat(struct p9_dev *p9dev,
471af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4721c7850f9SSasha Levin {
473bfc15268SAneesh Kumar K.V 	u32 fid_val;
474af045e53SAneesh Kumar K.V 	struct stat st;
475bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
476bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
4771c7850f9SSasha Levin 
478bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
479bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
48030204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
4816c8ca053SAneesh Kumar K.V 		goto err_out;
4821c7850f9SSasha Levin 
483bfc15268SAneesh Kumar K.V 	virtio_p9_fill_stat(p9dev, fid->path, &st, &wstat);
4841c7850f9SSasha Levin 
485bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "wS", 0, &wstat);
486bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
4875f900f6dSSasha Levin 	virtio_p9_free_stat(&wstat);
488bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
489ead43b01SAneesh Kumar K.V 	return;
4906c8ca053SAneesh Kumar K.V err_out:
4916c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
4926c8ca053SAneesh Kumar K.V 	return;
4931c7850f9SSasha Levin }
4941c7850f9SSasha Levin 
495ead43b01SAneesh Kumar K.V static void virtio_p9_wstat(struct p9_dev *p9dev,
496af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
4971c7850f9SSasha Levin {
498aec426f0SCyrill Gorcunov 	int res = 0;
499bfc15268SAneesh Kumar K.V 	u32 fid_val;
500bfc15268SAneesh Kumar K.V 	u16 unused;
501bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
502bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
503af045e53SAneesh Kumar K.V 
504bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dwS", &fid_val, &unused, &wstat);
505bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
5061c7850f9SSasha Levin 
5074bc9734aSAneesh Kumar K.V 	if (wstat.length != -1UL) {
508c6fb59c4SAneesh Kumar K.V 		res = truncate(fid->abs_path, wstat.length);
5094bc9734aSAneesh Kumar K.V 		if (res < 0)
5104bc9734aSAneesh Kumar K.V 			goto err_out;
5114bc9734aSAneesh Kumar K.V 	}
5124bc9734aSAneesh Kumar K.V 	if (wstat.mode != -1U) {
5134bc9734aSAneesh Kumar K.V 		res = chmod(fid->abs_path, wstat.mode & 0xFFFF);
5144bc9734aSAneesh Kumar K.V 		if (res < 0)
5154bc9734aSAneesh Kumar K.V 			goto err_out;
5164bc9734aSAneesh Kumar K.V 	}
517bfc15268SAneesh Kumar K.V 	if (strlen(wstat.name) > 0) {
5181c7850f9SSasha Levin 		char new_name[PATH_MAX] = {0};
5191c7850f9SSasha Levin 		char full_path[PATH_MAX];
5201c7850f9SSasha Levin 		char *last_dir = strrchr(fid->path, '/');
5211c7850f9SSasha Levin 
5221c7850f9SSasha Levin 		/* We need to get the full file name out of twstat->name */
5231c7850f9SSasha Levin 		if (last_dir)
5241c7850f9SSasha Levin 			strncpy(new_name, fid->path, last_dir - fid->path + 1);
5251c7850f9SSasha Levin 
526bfc15268SAneesh Kumar K.V 		memcpy(new_name + strlen(new_name),
527bfc15268SAneesh Kumar K.V 		       wstat.name, strlen(wstat.name));
5281c7850f9SSasha Levin 
5291c7850f9SSasha Levin 		/* fid is reused for the new file */
5304bc9734aSAneesh Kumar K.V 		res = rename(fid->abs_path,
5314bc9734aSAneesh Kumar K.V 			     rel_to_abs(p9dev, new_name, full_path));
5324bc9734aSAneesh Kumar K.V 		if (res < 0)
5334bc9734aSAneesh Kumar K.V 			goto err_out;
5341c7850f9SSasha Levin 		sprintf(fid->path, "%s", new_name);
5351c7850f9SSasha Levin 	}
5361c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
537bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
538ead43b01SAneesh Kumar K.V 	return;
5394bc9734aSAneesh Kumar K.V err_out:
5404bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
5414bc9734aSAneesh Kumar K.V 	return;
5421c7850f9SSasha Levin }
5431c7850f9SSasha Levin 
544ead43b01SAneesh Kumar K.V static void virtio_p9_remove(struct p9_dev *p9dev,
545af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
5461c7850f9SSasha Levin {
5474bc9734aSAneesh Kumar K.V 	int res;
548bfc15268SAneesh Kumar K.V 	u32 fid_val;
549bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
5501c7850f9SSasha Levin 
551bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
552bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
553bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid_val);
5541c7850f9SSasha Levin 	if (fid->is_dir)
5554bc9734aSAneesh Kumar K.V 		res = rmdir(fid->abs_path);
5561c7850f9SSasha Levin 	else
5574bc9734aSAneesh Kumar K.V 		res = unlink(fid->abs_path);
5584bc9734aSAneesh Kumar K.V 	if (res < 0)
5594bc9734aSAneesh Kumar K.V 		goto err_out;
5601c7850f9SSasha Levin 
5611c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
562bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
563ead43b01SAneesh Kumar K.V 	return;
5644bc9734aSAneesh Kumar K.V err_out:
5654bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
5664bc9734aSAneesh Kumar K.V 	return;
5671c7850f9SSasha Levin }
5681c7850f9SSasha Levin 
569ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
570af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
5711c7850f9SSasha Levin {
5724bc9734aSAneesh Kumar K.V 
573bfc15268SAneesh Kumar K.V 	u64 offset;
574bfc15268SAneesh Kumar K.V 	u32 fid_val;
5754bc9734aSAneesh Kumar K.V 	u32 count;
5764bc9734aSAneesh Kumar K.V 	ssize_t res;
57750c479e0SAneesh Kumar K.V 	u16 iov_cnt;
57850c479e0SAneesh Kumar K.V 	void *iov_base;
57950c479e0SAneesh Kumar K.V 	size_t iov_len;
580bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
581b064b05aSAneesh Kumar K.V 	/* u32 fid + u64 offset + u32 count */
582b064b05aSAneesh Kumar K.V 	int twrite_size = sizeof(u32) + sizeof(u64) + sizeof(u32);
5831c7850f9SSasha Levin 
584bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
585bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
586af045e53SAneesh Kumar K.V 
58750c479e0SAneesh Kumar K.V 	iov_base = pdu->out_iov[0].iov_base;
58850c479e0SAneesh Kumar K.V 	iov_len  = pdu->out_iov[0].iov_len;
58950c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->out_iov_cnt;
59050c479e0SAneesh Kumar K.V 
591bfc15268SAneesh Kumar K.V 	/* Adjust the iovec to skip the header and meta data */
592b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) + twrite_size);
593b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_len -=  (sizeof(struct p9_msg) + twrite_size);
594bfc15268SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count,
5956b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
5964bc9734aSAneesh Kumar K.V 	res = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset);
59750c479e0SAneesh Kumar K.V 	/*
59850c479e0SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
59950c479e0SAneesh Kumar K.V 	 * pdu_readf works correctly.
60050c479e0SAneesh Kumar K.V 	 */
60150c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_base = iov_base;
60250c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_len  = iov_len;
60350c479e0SAneesh Kumar K.V 	pdu->out_iov_cnt         = iov_cnt;
6044bc9734aSAneesh Kumar K.V 	if (res < 0)
6054bc9734aSAneesh Kumar K.V 		goto err_out;
6064bc9734aSAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", res);
607bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
608bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
609ead43b01SAneesh Kumar K.V 	return;
6104bc9734aSAneesh Kumar K.V err_out:
6114bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
6124bc9734aSAneesh Kumar K.V 	return;
6131c7850f9SSasha Levin }
6141c7850f9SSasha Levin 
615ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
616af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
617b4422bf3SAneesh Kumar K.V 
618b4422bf3SAneesh Kumar K.V static p9_handler *virtio_9p_handler [] = {
619b4422bf3SAneesh Kumar K.V 	[P9_TVERSION] = virtio_p9_version,
620b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]  = virtio_p9_attach,
621b4422bf3SAneesh Kumar K.V 	[P9_TSTAT]    = virtio_p9_stat,
622b4422bf3SAneesh Kumar K.V 	[P9_TCLUNK]   =	virtio_p9_clunk,
623b4422bf3SAneesh Kumar K.V 	[P9_TWALK]    =	virtio_p9_walk,
624b4422bf3SAneesh Kumar K.V 	[P9_TOPEN]    =	virtio_p9_open,
625b4422bf3SAneesh Kumar K.V 	[P9_TREAD]    = virtio_p9_read,
626b4422bf3SAneesh Kumar K.V 	[P9_TCREATE]  =	virtio_p9_create,
627b4422bf3SAneesh Kumar K.V 	[P9_TWSTAT]   =	virtio_p9_wstat,
628b4422bf3SAneesh Kumar K.V 	[P9_TREMOVE]  =	virtio_p9_remove,
629b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]   =	virtio_p9_write,
630b4422bf3SAneesh Kumar K.V };
631b4422bf3SAneesh Kumar K.V 
632af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
633af045e53SAneesh Kumar K.V {
634af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
635af045e53SAneesh Kumar K.V 	if (!pdu)
636af045e53SAneesh Kumar K.V 		return NULL;
637af045e53SAneesh Kumar K.V 
638bfc15268SAneesh Kumar K.V 	/* skip the pdu header p9_msg */
639bfc15268SAneesh Kumar K.V 	pdu->read_offset  = VIRTIO_P9_HDR_LEN;
640bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
641af045e53SAneesh Kumar K.V 	pdu->queue_head  = virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
642af045e53SAneesh Kumar K.V 						     pdu->out_iov,
643af045e53SAneesh Kumar K.V 						     &pdu->in_iov_cnt,
644af045e53SAneesh Kumar K.V 						     &pdu->out_iov_cnt);
645af045e53SAneesh Kumar K.V 	return pdu;
646af045e53SAneesh Kumar K.V }
647af045e53SAneesh Kumar K.V 
648af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
649af045e53SAneesh Kumar K.V {
650af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
651af045e53SAneesh Kumar K.V 	/*
652af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
653af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
654af045e53SAneesh Kumar K.V 	 */
655af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
656af045e53SAneesh Kumar K.V 	return msg->cmd;
657af045e53SAneesh Kumar K.V }
658af045e53SAneesh Kumar K.V 
65997b408afSAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev,
66097b408afSAneesh Kumar K.V 				 struct p9_pdu *pdu, u32 *outlen)
66197b408afSAneesh Kumar K.V {
66297b408afSAneesh Kumar K.V 	return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen);
66397b408afSAneesh Kumar K.V }
66497b408afSAneesh Kumar K.V 
665b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
6661c7850f9SSasha Levin {
667af045e53SAneesh Kumar K.V 	u8 cmd;
668b4422bf3SAneesh Kumar K.V 	u32 len = 0;
669b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
670b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
671af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
672af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
6731c7850f9SSasha Levin 
674b4422bf3SAneesh Kumar K.V 	vq = job->vq;
675b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
6761c7850f9SSasha Levin 
677af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
678af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
679af045e53SAneesh Kumar K.V 
680af045e53SAneesh Kumar K.V 	if (cmd >= ARRAY_SIZE(virtio_9p_handler) ||
681dd78d9eaSAneesh Kumar K.V 	    !virtio_9p_handler[cmd])
68297b408afSAneesh Kumar K.V 		handler = virtio_p9_eopnotsupp;
683dd78d9eaSAneesh Kumar K.V 	else
684af045e53SAneesh Kumar K.V 		handler = virtio_9p_handler[cmd];
685af045e53SAneesh Kumar K.V 	handler(p9dev, p9pdu, &len);
686af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
687af045e53SAneesh Kumar K.V 	free(p9pdu);
6881c7850f9SSasha Levin 	return true;
6891c7850f9SSasha Levin }
6901c7850f9SSasha Levin 
6911c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
6921c7850f9SSasha Levin {
693b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
694b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
695b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
6961c7850f9SSasha Levin 
6971c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
698b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
699*c7838fbdSSasha Levin 		virtio_pci__signal_vq(kvm, &p9dev->vpci, vq - p9dev->vqs);
7001c7850f9SSasha Levin 	}
7011c7850f9SSasha Levin }
7021c7850f9SSasha Levin 
70360eb42d5SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
70460eb42d5SSasha Levin {
70560eb42d5SSasha Levin 	struct p9_dev_job *job = param;
70660eb42d5SSasha Levin 
707df0c7f57SSasha Levin 	thread_pool__do_job(&job->job_id);
70860eb42d5SSasha Levin }
70960eb42d5SSasha Levin 
710*c7838fbdSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
7111c7850f9SSasha Levin {
712*c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
7131c7850f9SSasha Levin 
714*c7838fbdSSasha Levin 	((u8 *)(p9dev->config))[offset] = data;
715*c7838fbdSSasha Levin }
7161c7850f9SSasha Levin 
717*c7838fbdSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
718*c7838fbdSSasha Levin {
719*c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
720*c7838fbdSSasha Levin 
721*c7838fbdSSasha Levin 	return ((u8 *)(p9dev->config))[offset];
722*c7838fbdSSasha Levin }
723*c7838fbdSSasha Levin 
724*c7838fbdSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
725*c7838fbdSSasha Levin {
726*c7838fbdSSasha Levin 	return 1 << VIRTIO_9P_MOUNT_TAG;
727*c7838fbdSSasha Levin }
728*c7838fbdSSasha Levin 
729*c7838fbdSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
730*c7838fbdSSasha Levin {
731*c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
732*c7838fbdSSasha Levin 
733*c7838fbdSSasha Levin 	p9dev->features = features;
734*c7838fbdSSasha Levin }
735*c7838fbdSSasha Levin 
736*c7838fbdSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
737*c7838fbdSSasha Levin {
738*c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
739b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job;
740b4422bf3SAneesh Kumar K.V 	struct virt_queue *queue;
741*c7838fbdSSasha Levin 	void *p;
742*c7838fbdSSasha Levin 	struct ioevent ioevent;
7431c7850f9SSasha Levin 
744e59662b3SSasha Levin 	compat__remove_message(p9dev->compat_id);
745e59662b3SSasha Levin 
746*c7838fbdSSasha Levin 	queue			= &p9dev->vqs[vq];
747*c7838fbdSSasha Levin 	queue->pfn		= pfn;
7481c7850f9SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
749*c7838fbdSSasha Levin 	job			= &p9dev->jobs[vq];
7501c7850f9SSasha Levin 
751*c7838fbdSSasha Levin 	vring_init(&queue->vring, VIRTQUEUE_NUM, p, VIRTIO_PCI_VRING_ALIGN);
7521c7850f9SSasha Levin 
753b4422bf3SAneesh Kumar K.V 	*job			= (struct p9_dev_job) {
754b4422bf3SAneesh Kumar K.V 		.vq			= queue,
755b4422bf3SAneesh Kumar K.V 		.p9dev			= p9dev,
756b4422bf3SAneesh Kumar K.V 	};
757df0c7f57SSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_p9_do_io, job);
75860eb42d5SSasha Levin 
75960eb42d5SSasha Levin 	ioevent = (struct ioevent) {
760*c7838fbdSSasha Levin 		.io_addr	= p9dev->vpci.base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
76160eb42d5SSasha Levin 		.io_len		= sizeof(u16),
76260eb42d5SSasha Levin 		.fn		= ioevent_callback,
763*c7838fbdSSasha Levin 		.fn_ptr		= &p9dev->jobs[vq],
764*c7838fbdSSasha Levin 		.datamatch	= vq,
76560eb42d5SSasha Levin 		.fn_kvm		= kvm,
76660eb42d5SSasha Levin 		.fd		= eventfd(0, 0),
76760eb42d5SSasha Levin 	};
76860eb42d5SSasha Levin 
76960eb42d5SSasha Levin 	ioeventfd__add_event(&ioevent);
77060eb42d5SSasha Levin 
771*c7838fbdSSasha Levin 	return 0;
7721c7850f9SSasha Levin }
7731c7850f9SSasha Levin 
774*c7838fbdSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
775*c7838fbdSSasha Levin {
776*c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
7771c7850f9SSasha Levin 
778*c7838fbdSSasha Levin 	thread_pool__do_job(&p9dev->jobs[vq].job_id);
779*c7838fbdSSasha Levin 
780*c7838fbdSSasha Levin 	return 0;
781*c7838fbdSSasha Levin }
782*c7838fbdSSasha Levin 
783*c7838fbdSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
784*c7838fbdSSasha Levin {
785*c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
786*c7838fbdSSasha Levin 
787*c7838fbdSSasha Levin 	return p9dev->vqs[vq].pfn;
788*c7838fbdSSasha Levin }
789*c7838fbdSSasha Levin 
790*c7838fbdSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
791*c7838fbdSSasha Levin {
792*c7838fbdSSasha Levin 	return VIRTQUEUE_NUM;
793*c7838fbdSSasha Levin }
794*c7838fbdSSasha Levin 
795*c7838fbdSSasha Levin int virtio_9p__init(struct kvm *kvm)
7961c7850f9SSasha Levin {
797b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
798*c7838fbdSSasha Levin 
799*c7838fbdSSasha Levin 	list_for_each_entry(p9dev, &devs, list) {
800*c7838fbdSSasha Levin 		virtio_pci__init(kvm, &p9dev->vpci, p9dev, PCI_DEVICE_ID_VIRTIO_P9, VIRTIO_ID_9P);
801*c7838fbdSSasha Levin 		p9dev->vpci.ops = (struct virtio_pci_ops) {
802*c7838fbdSSasha Levin 			.set_config		= set_config,
803*c7838fbdSSasha Levin 			.get_config		= get_config,
804*c7838fbdSSasha Levin 			.get_host_features	= get_host_features,
805*c7838fbdSSasha Levin 			.set_guest_features	= set_guest_features,
806*c7838fbdSSasha Levin 			.init_vq		= init_vq,
807*c7838fbdSSasha Levin 			.notify_vq		= notify_vq,
808*c7838fbdSSasha Levin 			.get_pfn_vq		= get_pfn_vq,
809*c7838fbdSSasha Levin 			.get_size_vq		= get_size_vq,
810*c7838fbdSSasha Levin 		};
811*c7838fbdSSasha Levin 	}
812*c7838fbdSSasha Levin 
813*c7838fbdSSasha Levin 	return 0;
814*c7838fbdSSasha Levin }
815*c7838fbdSSasha Levin 
816*c7838fbdSSasha Levin int virtio_9p__register(struct kvm *kvm, const char *root, const char *tag_name)
817*c7838fbdSSasha Levin {
818*c7838fbdSSasha Levin 	struct p9_dev *p9dev;
81954f6802dSPekka Enberg 	u32 i, root_len;
82054f6802dSPekka Enberg 	int err = 0;
8211c7850f9SSasha Levin 
822b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
823b4422bf3SAneesh Kumar K.V 	if (!p9dev)
82454f6802dSPekka Enberg 		return -ENOMEM;
82554f6802dSPekka Enberg 
826b4422bf3SAneesh Kumar K.V 	if (!tag_name)
827b4422bf3SAneesh Kumar K.V 		tag_name = VIRTIO_P9_DEFAULT_TAG;
82854f6802dSPekka Enberg 
829b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
83054f6802dSPekka Enberg 	if (p9dev->config == NULL) {
83154f6802dSPekka Enberg 		err = -ENOMEM;
832b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
83354f6802dSPekka Enberg 	}
8341c7850f9SSasha Levin 
835b4422bf3SAneesh Kumar K.V 	strcpy(p9dev->root_dir, root);
8361c7850f9SSasha Levin 	root_len = strlen(root);
8371c7850f9SSasha Levin 	/*
8381c7850f9SSasha Levin 	 * We prefix the full path in all fids, This allows us to get the
8391c7850f9SSasha Levin 	 * absolute path of an fid without playing with strings.
8401c7850f9SSasha Levin 	 */
8411c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++) {
842b4422bf3SAneesh Kumar K.V 		strcpy(p9dev->fids[i].abs_path, root);
843b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].path = p9dev->fids[i].abs_path + root_len;
8441c7850f9SSasha Levin 	}
845b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
84654f6802dSPekka Enberg 	if (p9dev->config->tag_len > MAX_TAG_LEN) {
84754f6802dSPekka Enberg 		err = -EINVAL;
848b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
84954f6802dSPekka Enberg 	}
8501c7850f9SSasha Levin 
851*c7838fbdSSasha Levin 	memcpy(&p9dev->config->tag, tag_name, strlen(tag_name));
8521c7850f9SSasha Levin 
853*c7838fbdSSasha Levin 	list_add(&p9dev->list, &devs);
854b4422bf3SAneesh Kumar K.V 
855e59662b3SSasha Levin 	p9dev->compat_id = compat__add_message("virtio-9p device was not detected",
856e59662b3SSasha Levin 						"While you have requested a virtio-9p device, "
857e59662b3SSasha Levin 						"the guest kernel didn't seem to detect it.\n"
858e59662b3SSasha Levin 						"Please make sure that the kernel was compiled"
859e59662b3SSasha Levin 						"with CONFIG_NET_9P_VIRTIO.");
860e59662b3SSasha Levin 
86154f6802dSPekka Enberg 	return err;
86254f6802dSPekka Enberg 
863b4422bf3SAneesh Kumar K.V free_p9dev_config:
864b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
865b4422bf3SAneesh Kumar K.V free_p9dev:
866b4422bf3SAneesh Kumar K.V 	free(p9dev);
86754f6802dSPekka Enberg 	return err;
8681c7850f9SSasha Levin }
869