xref: /kvmtool/virtio/9p.c (revision 69bb42789a05d2dbc9a7a80e0153333cc62c382d)
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"
5bfc15268SAneesh Kumar K.V #include "kvm/irq.h"
6bfc15268SAneesh Kumar K.V #include "kvm/virtio-9p.h"
7e59662b3SSasha Levin #include "kvm/guest_compat.h"
81c7850f9SSasha Levin 
9bfc15268SAneesh Kumar K.V #include <stdio.h>
10bfc15268SAneesh Kumar K.V #include <stdlib.h>
111c7850f9SSasha Levin #include <fcntl.h>
121c7850f9SSasha Levin #include <sys/stat.h>
13bfc15268SAneesh Kumar K.V #include <unistd.h>
14bfc15268SAneesh Kumar K.V #include <string.h>
15bfc15268SAneesh Kumar K.V #include <errno.h>
16c797b6c6SAneesh Kumar K.V #include <sys/vfs.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 
22c7838fbdSSasha Levin static LIST_HEAD(devs);
23312c62d1SSasha Levin static int compat_id = -1;
24c7838fbdSSasha Levin 
25e2341580SSasha Levin static int insert_new_fid(struct p9_dev *dev, struct p9_fid *fid);
26e2341580SSasha Levin static struct p9_fid *find_or_create_fid(struct p9_dev *dev, u32 fid)
27e2341580SSasha Levin {
28e2341580SSasha Levin 	struct rb_node *node = dev->fids.rb_node;
29e2341580SSasha Levin 	struct p9_fid *pfid = NULL;
30e2341580SSasha Levin 
31e2341580SSasha Levin 	while (node) {
32e2341580SSasha Levin 		struct p9_fid *cur = rb_entry(node, struct p9_fid, node);
33e2341580SSasha Levin 
34e2341580SSasha Levin 		if (fid < cur->fid) {
35e2341580SSasha Levin 			node = node->rb_left;
36e2341580SSasha Levin 		} else if (fid > cur->fid) {
37e2341580SSasha Levin 			node = node->rb_right;
38e2341580SSasha Levin 		} else {
39e2341580SSasha Levin 			return cur;
40e2341580SSasha Levin 		}
41e2341580SSasha Levin 	}
42e2341580SSasha Levin 
43e2341580SSasha Levin 	pfid = calloc(sizeof(*pfid), 1);
44e2341580SSasha Levin 	if (!pfid)
45e2341580SSasha Levin 		return NULL;
46e2341580SSasha Levin 
47e2341580SSasha Levin 	pfid->fid = fid;
48e2341580SSasha Levin 	strcpy(pfid->abs_path, dev->root_dir);
49e2341580SSasha Levin 	pfid->path = pfid->abs_path + strlen(dev->root_dir);
50e2341580SSasha Levin 
51e2341580SSasha Levin 	insert_new_fid(dev, pfid);
52e2341580SSasha Levin 
53e2341580SSasha Levin 	return pfid;
54e2341580SSasha Levin }
55e2341580SSasha Levin 
56e2341580SSasha Levin static int insert_new_fid(struct p9_dev *dev, struct p9_fid *fid)
57e2341580SSasha Levin {
58e2341580SSasha Levin 	struct rb_node **node = &(dev->fids.rb_node), *parent = NULL;
59e2341580SSasha Levin 
60e2341580SSasha Levin 	while (*node) {
61e2341580SSasha Levin 		int result = fid->fid - rb_entry(*node, struct p9_fid, node)->fid;
62e2341580SSasha Levin 
63e2341580SSasha Levin 		parent = *node;
64e2341580SSasha Levin 		if (result < 0)
65e2341580SSasha Levin 			node    = &((*node)->rb_left);
66e2341580SSasha Levin 		else if (result > 0)
67e2341580SSasha Levin 			node    = &((*node)->rb_right);
68e2341580SSasha Levin 		else
69e2341580SSasha Levin 			return -EEXIST;
70e2341580SSasha Levin 	}
71e2341580SSasha Levin 
72e2341580SSasha Levin 	rb_link_node(&fid->node, parent, node);
73e2341580SSasha Levin 	rb_insert_color(&fid->node, &dev->fids);
74e2341580SSasha Levin 	return 0;
75e2341580SSasha Levin }
76e2341580SSasha Levin 
7731a6fb8dSSasha Levin static struct p9_fid *get_fid(struct p9_dev *p9dev, int fid)
7831a6fb8dSSasha Levin {
79e2341580SSasha Levin 	struct p9_fid *new;
8031a6fb8dSSasha Levin 
81e2341580SSasha Levin 	new = find_or_create_fid(p9dev, fid);
82e2341580SSasha Levin 
83e2341580SSasha Levin 	return new;
8431a6fb8dSSasha Levin }
8531a6fb8dSSasha Levin 
861c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
87b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
88b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
891c7850f9SSasha Levin {
90b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
911c7850f9SSasha Levin 
921c7850f9SSasha Levin 	return abs_path;
931c7850f9SSasha Levin }
941c7850f9SSasha Levin 
95c797b6c6SAneesh Kumar K.V static void stat2qid(struct stat *st, struct p9_qid *qid)
961c7850f9SSasha Levin {
971c7850f9SSasha Levin 	*qid = (struct p9_qid) {
981c7850f9SSasha Levin 		.path		= st->st_ino,
991c7850f9SSasha Levin 		.version	= st->st_mtime,
1001c7850f9SSasha Levin 	};
1011c7850f9SSasha Levin 
1021c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
1031c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
1041c7850f9SSasha Levin }
1051c7850f9SSasha Levin 
106b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
1071c7850f9SSasha Levin {
108e2341580SSasha Levin 	struct p9_fid *pfid = get_fid(p9dev, fid);
109e2341580SSasha Levin 
110e2341580SSasha Levin 	if (pfid->fd > 0)
111e2341580SSasha Levin 		close(pfid->fd);
112e2341580SSasha Levin 
113e2341580SSasha Levin 	if (pfid->dir)
114e2341580SSasha Levin 		closedir(pfid->dir);
115e2341580SSasha Levin 
116e2341580SSasha Levin 	rb_erase(&pfid->node, &p9dev->fids);
117e2341580SSasha Levin 	free(pfid);
1181c7850f9SSasha Levin }
119e2341580SSasha Levin 
120e2341580SSasha Levin static void clear_all_fids(struct p9_dev *p9dev)
121e2341580SSasha Levin {
122e2341580SSasha Levin 	struct rb_node *node = rb_first(&p9dev->fids);
123e2341580SSasha Levin 
124e2341580SSasha Levin 	while (node) {
125e2341580SSasha Levin 		struct p9_fid *fid = rb_entry(node, struct p9_fid, node);
126e2341580SSasha Levin 
127e2341580SSasha Levin 		if (fid->fd > 0)
128e2341580SSasha Levin 			close(fid->fd);
129e2341580SSasha Levin 
130e2341580SSasha Levin 		if (fid->dir)
131e2341580SSasha Levin 			closedir(fid->dir);
132e2341580SSasha Levin 
133e2341580SSasha Levin 		rb_erase(&fid->node, &p9dev->fids);
134e2341580SSasha Levin 		free(fid);
135e2341580SSasha Levin 
136e2341580SSasha Levin 		node = rb_first(&p9dev->fids);
1371c7850f9SSasha Levin 	}
1381c7850f9SSasha Levin }
1391c7850f9SSasha Levin 
140bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size)
1411c7850f9SSasha Levin {
142bfc15268SAneesh Kumar K.V 	u8 cmd;
143bfc15268SAneesh Kumar K.V 	u16 tag;
144bfc15268SAneesh Kumar K.V 
145bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32);
146bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag);
147bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
148bfc15268SAneesh Kumar K.V 	/* cmd + 1 is the reply message */
149bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag);
1501c7850f9SSasha Levin }
1511c7850f9SSasha Levin 
1526b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
1536b163a87SAneesh Kumar K.V {
1546b163a87SAneesh Kumar K.V 	int i;
1556b163a87SAneesh Kumar K.V 	u32 total = 0;
1566b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
1576b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
1586b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
1596b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
1606b163a87SAneesh Kumar K.V 			i++;
1616b163a87SAneesh Kumar K.V 			break;
1626b163a87SAneesh Kumar K.V 		}
1636b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
1646b163a87SAneesh Kumar K.V 	}
1656b163a87SAneesh Kumar K.V 	return i;
1666b163a87SAneesh Kumar K.V }
1676b163a87SAneesh Kumar K.V 
168eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
169eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
170eee1ba8eSAneesh Kumar K.V {
171bfc15268SAneesh Kumar K.V 	u16 tag;
172eee1ba8eSAneesh Kumar K.V 
1735529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
174c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", err);
175bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
176eee1ba8eSAneesh Kumar K.V 
177c797b6c6SAneesh Kumar K.V 	/* read the tag from input */
178bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32) + sizeof(u8);
179bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "w", &tag);
180bfc15268SAneesh Kumar K.V 
181c797b6c6SAneesh Kumar K.V 	/* Update the header */
182bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
183c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RLERROR, tag);
184eee1ba8eSAneesh Kumar K.V }
185eee1ba8eSAneesh Kumar K.V 
186ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
187af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1881c7850f9SSasha Levin {
189c797b6c6SAneesh Kumar K.V 	u32 msize;
190c797b6c6SAneesh Kumar K.V 	char *version;
191c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ds", &msize, &version);
192c797b6c6SAneesh Kumar K.V 	/*
193c797b6c6SAneesh Kumar K.V 	 * reply with the same msize the client sent us
194c797b6c6SAneesh Kumar K.V 	 * Error out if the request is not for 9P2000.L
195c797b6c6SAneesh Kumar K.V 	 */
1965529bcd7SAsias He 	if (!strcmp(version, VIRTIO_9P_VERSION_DOTL))
197c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "ds", msize, version);
198c797b6c6SAneesh Kumar K.V 	else
199c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "ds", msize, "unknown");
2001c7850f9SSasha Levin 
201bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
202bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
203c797b6c6SAneesh Kumar K.V 	free(version);
204ead43b01SAneesh Kumar K.V 	return;
2051c7850f9SSasha Levin }
2061c7850f9SSasha Levin 
207ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
208af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
2091c7850f9SSasha Levin {
210bfc15268SAneesh Kumar K.V 	u32 fid;
2111c7850f9SSasha Levin 
212bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid);
213bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid);
2141c7850f9SSasha Levin 
215bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
216bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
217ead43b01SAneesh Kumar K.V 	return;
2181c7850f9SSasha Levin }
2191c7850f9SSasha Levin 
220c797b6c6SAneesh Kumar K.V /*
221c797b6c6SAneesh Kumar K.V  * FIXME!! Need to map to protocol independent value. Upstream
222c797b6c6SAneesh Kumar K.V  * 9p also have the same BUG
223c797b6c6SAneesh Kumar K.V  */
224c797b6c6SAneesh Kumar K.V static int virtio_p9_openflags(int flags)
225c797b6c6SAneesh Kumar K.V {
226c797b6c6SAneesh Kumar K.V 	flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT | O_DIRECT);
227c797b6c6SAneesh Kumar K.V 	flags |= O_NOFOLLOW;
228c797b6c6SAneesh Kumar K.V 	return flags;
229c797b6c6SAneesh Kumar K.V }
230c797b6c6SAneesh Kumar K.V 
231ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
232af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2331c7850f9SSasha Levin {
234c797b6c6SAneesh Kumar K.V 	u32 fid, flags;
2351c7850f9SSasha Levin 	struct stat st;
236bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
237bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
238bfc15268SAneesh Kumar K.V 
239c797b6c6SAneesh Kumar K.V 
240c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dd", &fid, &flags);
24131a6fb8dSSasha Levin 	new_fid = get_fid(p9dev, fid);
2421c7850f9SSasha Levin 
24330204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
244eee1ba8eSAneesh Kumar K.V 		goto err_out;
2451c7850f9SSasha Levin 
246c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
2471c7850f9SSasha Levin 
248eee1ba8eSAneesh Kumar K.V 	if (new_fid->is_dir) {
2491c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
250eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
251eee1ba8eSAneesh Kumar K.V 			goto err_out;
252eee1ba8eSAneesh Kumar K.V 	} else {
253eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
254c797b6c6SAneesh Kumar K.V 				    virtio_p9_openflags(flags));
255eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
256eee1ba8eSAneesh Kumar K.V 			goto err_out;
257eee1ba8eSAneesh Kumar K.V 	}
258c797b6c6SAneesh Kumar K.V 	/* FIXME!! need ot send proper iounit  */
259bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
260bfc15268SAneesh Kumar K.V 
261bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
262bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
263ead43b01SAneesh Kumar K.V 	return;
264eee1ba8eSAneesh Kumar K.V err_out:
265eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
266ead43b01SAneesh Kumar K.V 	return;
2671c7850f9SSasha Levin }
2681c7850f9SSasha Levin 
269ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
270af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
2711c7850f9SSasha Levin {
272c797b6c6SAneesh Kumar K.V 	int fd, ret;
273bfc15268SAneesh Kumar K.V 	char *name;
274af045e53SAneesh Kumar K.V 	struct stat st;
275bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
276c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
2774bc9734aSAneesh Kumar K.V 	char full_path[PATH_MAX];
278c797b6c6SAneesh Kumar K.V 	u32 dfid_val, flags, mode, gid;
279af045e53SAneesh Kumar K.V 
280c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsddd", &dfid_val,
281c797b6c6SAneesh Kumar K.V 			    &name, &flags, &mode, &gid);
28231a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
2831c7850f9SSasha Levin 
284c797b6c6SAneesh Kumar K.V 	flags = virtio_p9_openflags(flags);
2855f900f6dSSasha Levin 
286c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
287c797b6c6SAneesh Kumar K.V 	fd = open(full_path, flags | O_CREAT, mode);
2884bc9734aSAneesh Kumar K.V 	if (fd < 0)
2894bc9734aSAneesh Kumar K.V 		goto err_out;
290c797b6c6SAneesh Kumar K.V 	dfid->fd = fd;
291c797b6c6SAneesh Kumar K.V 
2924bc9734aSAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
2936c8ca053SAneesh Kumar K.V 		goto err_out;
2941c7850f9SSasha Levin 
295c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
296c797b6c6SAneesh Kumar K.V 	if (ret < 0)
297c797b6c6SAneesh Kumar K.V 		goto err_out;
298c797b6c6SAneesh Kumar K.V 
299c797b6c6SAneesh Kumar K.V 	ret = lchown(full_path, dfid->uid, gid);
300c797b6c6SAneesh Kumar K.V 	if (ret < 0)
301c797b6c6SAneesh Kumar K.V 		goto err_out;
302c797b6c6SAneesh Kumar K.V 
303c797b6c6SAneesh Kumar K.V 	sprintf(dfid->path, "%s/%s", dfid->path, name);
304c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
305bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
306bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
307bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3085f900f6dSSasha Levin 	free(name);
3096c8ca053SAneesh Kumar K.V 	return;
3106c8ca053SAneesh Kumar K.V err_out:
3115f900f6dSSasha Levin 	free(name);
312c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
313c797b6c6SAneesh Kumar K.V 	return;
314c797b6c6SAneesh Kumar K.V }
315c797b6c6SAneesh Kumar K.V 
316c797b6c6SAneesh Kumar K.V static void virtio_p9_mkdir(struct p9_dev *p9dev,
317c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
318c797b6c6SAneesh Kumar K.V {
319c797b6c6SAneesh Kumar K.V 	int ret;
320c797b6c6SAneesh Kumar K.V 	char *name;
321c797b6c6SAneesh Kumar K.V 	struct stat st;
322c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
323c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
324c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
325c797b6c6SAneesh Kumar K.V 	u32 dfid_val, mode, gid;
326c797b6c6SAneesh Kumar K.V 
327c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdd", &dfid_val,
328c797b6c6SAneesh Kumar K.V 			    &name, &mode, &gid);
32931a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
330c797b6c6SAneesh Kumar K.V 
331c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
332c797b6c6SAneesh Kumar K.V 	ret = mkdir(full_path, mode);
333c797b6c6SAneesh Kumar K.V 	if (ret < 0)
334c797b6c6SAneesh Kumar K.V 		goto err_out;
335c797b6c6SAneesh Kumar K.V 
336c797b6c6SAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
337c797b6c6SAneesh Kumar K.V 		goto err_out;
338c797b6c6SAneesh Kumar K.V 
339c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
340c797b6c6SAneesh Kumar K.V 	if (ret < 0)
341c797b6c6SAneesh Kumar K.V 		goto err_out;
342c797b6c6SAneesh Kumar K.V 
343c797b6c6SAneesh Kumar K.V 	ret = lchown(full_path, dfid->uid, gid);
344c797b6c6SAneesh Kumar K.V 	if (ret < 0)
345c797b6c6SAneesh Kumar K.V 		goto err_out;
346c797b6c6SAneesh Kumar K.V 
347c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
348c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
349c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
350c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
351c797b6c6SAneesh Kumar K.V 	free(name);
352c797b6c6SAneesh Kumar K.V 	return;
353c797b6c6SAneesh Kumar K.V err_out:
354c797b6c6SAneesh Kumar K.V 	free(name);
355c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
356ead43b01SAneesh Kumar K.V 	return;
3571c7850f9SSasha Levin }
3581c7850f9SSasha Levin 
359ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
360af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
3611c7850f9SSasha Levin {
362af045e53SAneesh Kumar K.V 	u8 i;
363bfc15268SAneesh Kumar K.V 	u16 nwqid;
364bfc15268SAneesh Kumar K.V 	u16 nwname;
365bfc15268SAneesh Kumar K.V 	struct p9_qid wqid;
366e2341580SSasha Levin 	struct p9_fid *new_fid, *old_fid;
367c797b6c6SAneesh Kumar K.V 	u32 fid_val, newfid_val;
368c797b6c6SAneesh Kumar K.V 
3691c7850f9SSasha Levin 
370bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname);
37131a6fb8dSSasha Levin 	new_fid	= get_fid(p9dev, newfid_val);
3721c7850f9SSasha Levin 
373bfc15268SAneesh Kumar K.V 	nwqid = 0;
374bfc15268SAneesh Kumar K.V 	if (nwname) {
37531a6fb8dSSasha Levin 		struct p9_fid *fid = get_fid(p9dev, fid_val);
376bfc15268SAneesh Kumar K.V 
377baac79a5SAneesh Kumar K.V 		strcpy(new_fid->path, fid->path);
378bfc15268SAneesh Kumar K.V 		/* skip the space for count */
379bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
380bfc15268SAneesh Kumar K.V 		for (i = 0; i < nwname; i++) {
381bfc15268SAneesh Kumar K.V 			struct stat st;
3821c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
3831c7850f9SSasha Levin 			char full_path[PATH_MAX];
384e55ed135SPekka Enberg 			char *str;
385bfc15268SAneesh Kumar K.V 
386bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_readf(pdu, "s", &str);
3871c7850f9SSasha Levin 
3881c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
389baac79a5SAneesh Kumar K.V 			sprintf(tmp, "%s/%s", new_fid->path, str);
390e55ed135SPekka Enberg 
391e55ed135SPekka Enberg 			free(str);
392e55ed135SPekka Enberg 
393c797b6c6SAneesh Kumar K.V 			if (lstat(rel_to_abs(p9dev, tmp, full_path), &st) < 0)
3946c8ca053SAneesh Kumar K.V 				goto err_out;
3951c7850f9SSasha Levin 
396c797b6c6SAneesh Kumar K.V 			stat2qid(&st, &wqid);
3971c7850f9SSasha Levin 			new_fid->is_dir = S_ISDIR(st.st_mode);
3981c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
399c797b6c6SAneesh Kumar K.V 			new_fid->uid = fid->uid;
400bfc15268SAneesh Kumar K.V 			nwqid++;
401bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "Q", &wqid);
4021c7850f9SSasha Levin 		}
4031c7850f9SSasha Levin 	} else {
404bfc15268SAneesh Kumar K.V 		/*
405bfc15268SAneesh Kumar K.V 		 * update write_offset so our outlen get correct value
406bfc15268SAneesh Kumar K.V 		 */
407bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
408e2341580SSasha Levin 		old_fid = get_fid(p9dev, fid_val);
409e2341580SSasha Levin 		new_fid->is_dir = old_fid->is_dir;
410e2341580SSasha Levin 		strcpy(new_fid->path, old_fid->path);
411e2341580SSasha Levin 		new_fid->uid    = old_fid->uid;
4121c7850f9SSasha Levin 	}
413bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
4145529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
415bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", nwqid);
416bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4176c8ca053SAneesh Kumar K.V 	return;
4186c8ca053SAneesh Kumar K.V err_out:
4196c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
420ead43b01SAneesh Kumar K.V 	return;
4211c7850f9SSasha Levin }
4221c7850f9SSasha Levin 
423ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
424af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
4251c7850f9SSasha Levin {
426bfc15268SAneesh Kumar K.V 	char *uname;
427bfc15268SAneesh Kumar K.V 	char *aname;
4281c7850f9SSasha Levin 	struct stat st;
429bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
4301c7850f9SSasha Levin 	struct p9_fid *fid;
431c797b6c6SAneesh Kumar K.V 	u32 fid_val, afid, uid;
432bfc15268SAneesh Kumar K.V 
433c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddssd", &fid_val, &afid,
434c797b6c6SAneesh Kumar K.V 			    &uname, &aname, &uid);
4351c7850f9SSasha Levin 
43639257180SPekka Enberg 	free(uname);
43739257180SPekka Enberg 	free(aname);
43839257180SPekka Enberg 
439e2341580SSasha Levin 	clear_all_fids(p9dev);
4401c7850f9SSasha Levin 
44130204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
4426c8ca053SAneesh Kumar K.V 		goto err_out;
4431c7850f9SSasha Levin 
444c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
4451c7850f9SSasha Levin 
44631a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
447c797b6c6SAneesh Kumar K.V 	fid->uid = uid;
4481c7850f9SSasha Levin 	fid->is_dir = 1;
4491c7850f9SSasha Levin 	strcpy(fid->path, "/");
4501c7850f9SSasha Levin 
451bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
452bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
453bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4546c8ca053SAneesh Kumar K.V 	return;
4556c8ca053SAneesh Kumar K.V err_out:
4566c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
457ead43b01SAneesh Kumar K.V 	return;
4581c7850f9SSasha Levin }
4591c7850f9SSasha Levin 
460c797b6c6SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev,
461c797b6c6SAneesh Kumar K.V 				struct stat *st, struct p9_stat_dotl *statl)
4625f900f6dSSasha Levin {
463c797b6c6SAneesh Kumar K.V 	memset(statl, 0, sizeof(*statl));
464c797b6c6SAneesh Kumar K.V 	statl->st_mode		= st->st_mode;
465c797b6c6SAneesh Kumar K.V 	statl->st_nlink		= st->st_nlink;
466c797b6c6SAneesh Kumar K.V 	statl->st_uid		= st->st_uid;
467c797b6c6SAneesh Kumar K.V 	statl->st_gid		= st->st_gid;
468c797b6c6SAneesh Kumar K.V 	statl->st_rdev		= st->st_rdev;
469c797b6c6SAneesh Kumar K.V 	statl->st_size		= st->st_size;
470c797b6c6SAneesh Kumar K.V 	statl->st_blksize	= st->st_blksize;
471c797b6c6SAneesh Kumar K.V 	statl->st_blocks	= st->st_blocks;
472c797b6c6SAneesh Kumar K.V 	statl->st_atime_sec	= st->st_atime;
473c797b6c6SAneesh Kumar K.V 	statl->st_atime_nsec	= st->st_atim.tv_nsec;
474c797b6c6SAneesh Kumar K.V 	statl->st_mtime_sec	= st->st_mtime;
475c797b6c6SAneesh Kumar K.V 	statl->st_mtime_nsec	= st->st_mtim.tv_nsec;
476c797b6c6SAneesh Kumar K.V 	statl->st_ctime_sec	= st->st_ctime;
477c797b6c6SAneesh Kumar K.V 	statl->st_ctime_nsec	= st->st_ctim.tv_nsec;
478c797b6c6SAneesh Kumar K.V 	/* Currently we only support BASIC fields in stat */
479c797b6c6SAneesh Kumar K.V 	statl->st_result_mask	= P9_STATS_BASIC;
480c797b6c6SAneesh Kumar K.V 	stat2qid(st, &statl->qid);
4811c7850f9SSasha Levin }
4821c7850f9SSasha Levin 
483ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
484af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4851c7850f9SSasha Levin {
486bfc15268SAneesh Kumar K.V 	u64 offset;
487bfc15268SAneesh Kumar K.V 	u32 fid_val;
488c797b6c6SAneesh Kumar K.V 	u16 iov_cnt;
489c797b6c6SAneesh Kumar K.V 	void *iov_base;
490c797b6c6SAneesh Kumar K.V 	size_t iov_len;
491bfc15268SAneesh Kumar K.V 	u32 count, rcount;
492bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
493c797b6c6SAneesh Kumar K.V 
4941c7850f9SSasha Levin 
495bfc15268SAneesh Kumar K.V 	rcount = 0;
496bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
49731a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
49850c479e0SAneesh Kumar K.V 
49950c479e0SAneesh Kumar K.V 	iov_base = pdu->in_iov[0].iov_base;
50050c479e0SAneesh Kumar K.V 	iov_len  = pdu->in_iov[0].iov_len;
50150c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->in_iov_cnt;
5025529bcd7SAsias He 	pdu->in_iov[0].iov_base += VIRTIO_9P_HDR_LEN + sizeof(u32);
5035529bcd7SAsias He 	pdu->in_iov[0].iov_len -= VIRTIO_9P_HDR_LEN + sizeof(u32);
5046b163a87SAneesh Kumar K.V 	pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
505bfc15268SAneesh Kumar K.V 						   count,
5066b163a87SAneesh Kumar K.V 						   pdu->in_iov_cnt);
507bfc15268SAneesh Kumar K.V 	rcount = preadv(fid->fd, pdu->in_iov,
508bfc15268SAneesh Kumar K.V 			pdu->in_iov_cnt, offset);
509bfc15268SAneesh Kumar K.V 	if (rcount > count)
510bfc15268SAneesh Kumar K.V 		rcount = count;
511bfc15268SAneesh Kumar K.V 	/*
512bfc15268SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
513bfc15268SAneesh Kumar K.V 	 * pdu_writef works correctly.
514bfc15268SAneesh Kumar K.V 	 */
51550c479e0SAneesh Kumar K.V 	pdu->in_iov[0].iov_base = iov_base;
51650c479e0SAneesh Kumar K.V 	pdu->in_iov[0].iov_len  = iov_len;
51750c479e0SAneesh Kumar K.V 	pdu->in_iov_cnt         = iov_cnt;
518c797b6c6SAneesh Kumar K.V 
5195529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
520bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
521bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
522bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
523ead43b01SAneesh Kumar K.V 	return;
5241c7850f9SSasha Levin }
5251c7850f9SSasha Levin 
526c797b6c6SAneesh Kumar K.V static int virtio_p9_dentry_size(struct dirent *dent)
527c797b6c6SAneesh Kumar K.V {
528c797b6c6SAneesh Kumar K.V 	/*
529c797b6c6SAneesh Kumar K.V 	 * Size of each dirent:
530c797b6c6SAneesh Kumar K.V 	 * qid(13) + offset(8) + type(1) + name_len(2) + name
531c797b6c6SAneesh Kumar K.V 	 */
532c797b6c6SAneesh Kumar K.V 	return 24 + strlen(dent->d_name);
533c797b6c6SAneesh Kumar K.V }
534c797b6c6SAneesh Kumar K.V 
535c797b6c6SAneesh Kumar K.V static void virtio_p9_readdir(struct p9_dev *p9dev,
536c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
537c797b6c6SAneesh Kumar K.V {
538c797b6c6SAneesh Kumar K.V 	u32 fid_val;
539c797b6c6SAneesh Kumar K.V 	u32 count, rcount;
540c797b6c6SAneesh Kumar K.V 	struct stat st;
541c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
542c797b6c6SAneesh Kumar K.V 	struct dirent *dent;
543c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
544c797b6c6SAneesh Kumar K.V 	u64 offset, old_offset;
545c797b6c6SAneesh Kumar K.V 
546c797b6c6SAneesh Kumar K.V 	rcount = 0;
547c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
54831a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
549c797b6c6SAneesh Kumar K.V 
550c797b6c6SAneesh Kumar K.V 	if (!fid->is_dir) {
551*69bb4278SSasha Levin 		errno = EINVAL;
552c797b6c6SAneesh Kumar K.V 		goto err_out;
553c797b6c6SAneesh Kumar K.V 	}
554c797b6c6SAneesh Kumar K.V 
555c797b6c6SAneesh Kumar K.V 	/* Move the offset specified */
556c797b6c6SAneesh Kumar K.V 	seekdir(fid->dir, offset);
557c797b6c6SAneesh Kumar K.V 
558c797b6c6SAneesh Kumar K.V 	old_offset = offset;
559c797b6c6SAneesh Kumar K.V 	/* If reading a dir, fill the buffer with p9_stat entries */
560c797b6c6SAneesh Kumar K.V 	dent = readdir(fid->dir);
561c797b6c6SAneesh Kumar K.V 
562c797b6c6SAneesh Kumar K.V 	/* Skip the space for writing count */
563c797b6c6SAneesh Kumar K.V 	pdu->write_offset += sizeof(u32);
564c797b6c6SAneesh Kumar K.V 	while (dent) {
565c797b6c6SAneesh Kumar K.V 		u32 read;
566c797b6c6SAneesh Kumar K.V 		struct p9_qid qid;
567c797b6c6SAneesh Kumar K.V 
568c797b6c6SAneesh Kumar K.V 		if ((rcount + virtio_p9_dentry_size(dent)) > count) {
569c797b6c6SAneesh Kumar K.V 			/* seek to the previous offset and return */
570c797b6c6SAneesh Kumar K.V 			seekdir(fid->dir, old_offset);
571c797b6c6SAneesh Kumar K.V 			break;
572c797b6c6SAneesh Kumar K.V 		}
573c797b6c6SAneesh Kumar K.V 		old_offset = dent->d_off;
574c797b6c6SAneesh Kumar K.V 		lstat(rel_to_abs(p9dev, dent->d_name, full_path), &st);
575c797b6c6SAneesh Kumar K.V 		stat2qid(&st, &qid);
576c797b6c6SAneesh Kumar K.V 		read = pdu->write_offset;
577c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "Qqbs", &qid, dent->d_off,
578c797b6c6SAneesh Kumar K.V 				     dent->d_type, dent->d_name);
579c797b6c6SAneesh Kumar K.V 		rcount += pdu->write_offset - read;
580c797b6c6SAneesh Kumar K.V 		dent = readdir(fid->dir);
581c797b6c6SAneesh Kumar K.V 	}
582c797b6c6SAneesh Kumar K.V 
5835529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
584c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
585c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
586c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
587c797b6c6SAneesh Kumar K.V 	return;
588c797b6c6SAneesh Kumar K.V err_out:
589c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
590c797b6c6SAneesh Kumar K.V 	return;
591c797b6c6SAneesh Kumar K.V }
592c797b6c6SAneesh Kumar K.V 
593c797b6c6SAneesh Kumar K.V 
594c797b6c6SAneesh Kumar K.V static void virtio_p9_getattr(struct p9_dev *p9dev,
595af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
5961c7850f9SSasha Levin {
597bfc15268SAneesh Kumar K.V 	u32 fid_val;
598af045e53SAneesh Kumar K.V 	struct stat st;
599c797b6c6SAneesh Kumar K.V 	u64 request_mask;
600bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
601c797b6c6SAneesh Kumar K.V 	struct p9_stat_dotl statl;
6021c7850f9SSasha Levin 
603c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dq", &fid_val, &request_mask);
60431a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
60530204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
6066c8ca053SAneesh Kumar K.V 		goto err_out;
6071c7850f9SSasha Levin 
608c797b6c6SAneesh Kumar K.V 	virtio_p9_fill_stat(p9dev, &st, &statl);
609c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "A", &statl);
610bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
611bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
612ead43b01SAneesh Kumar K.V 	return;
6136c8ca053SAneesh Kumar K.V err_out:
6146c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
6156c8ca053SAneesh Kumar K.V 	return;
6161c7850f9SSasha Levin }
6171c7850f9SSasha Levin 
618c797b6c6SAneesh Kumar K.V /* FIXME!! from linux/fs.h */
619c797b6c6SAneesh Kumar K.V /*
620c797b6c6SAneesh Kumar K.V  * Attribute flags.  These should be or-ed together to figure out what
621c797b6c6SAneesh Kumar K.V  * has been changed!
622c797b6c6SAneesh Kumar K.V  */
623c797b6c6SAneesh Kumar K.V #define ATTR_MODE	(1 << 0)
624c797b6c6SAneesh Kumar K.V #define ATTR_UID	(1 << 1)
625c797b6c6SAneesh Kumar K.V #define ATTR_GID	(1 << 2)
626c797b6c6SAneesh Kumar K.V #define ATTR_SIZE	(1 << 3)
627c797b6c6SAneesh Kumar K.V #define ATTR_ATIME	(1 << 4)
628c797b6c6SAneesh Kumar K.V #define ATTR_MTIME	(1 << 5)
629c797b6c6SAneesh Kumar K.V #define ATTR_CTIME	(1 << 6)
630c797b6c6SAneesh Kumar K.V #define ATTR_ATIME_SET	(1 << 7)
631c797b6c6SAneesh Kumar K.V #define ATTR_MTIME_SET	(1 << 8)
632c797b6c6SAneesh Kumar K.V #define ATTR_FORCE	(1 << 9) /* Not a change, but a change it */
633c797b6c6SAneesh Kumar K.V #define ATTR_ATTR_FLAG	(1 << 10)
634c797b6c6SAneesh Kumar K.V #define ATTR_KILL_SUID	(1 << 11)
635c797b6c6SAneesh Kumar K.V #define ATTR_KILL_SGID	(1 << 12)
636c797b6c6SAneesh Kumar K.V #define ATTR_FILE	(1 << 13)
637c797b6c6SAneesh Kumar K.V #define ATTR_KILL_PRIV	(1 << 14)
638c797b6c6SAneesh Kumar K.V #define ATTR_OPEN	(1 << 15) /* Truncating from open(O_TRUNC) */
639c797b6c6SAneesh Kumar K.V #define ATTR_TIMES_SET	(1 << 16)
640c797b6c6SAneesh Kumar K.V 
641c797b6c6SAneesh Kumar K.V #define ATTR_MASK    127
642c797b6c6SAneesh Kumar K.V 
643c797b6c6SAneesh Kumar K.V static void virtio_p9_setattr(struct p9_dev *p9dev,
644af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
6451c7850f9SSasha Levin {
646c797b6c6SAneesh Kumar K.V 	int ret = 0;
647bfc15268SAneesh Kumar K.V 	u32 fid_val;
648bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
649c797b6c6SAneesh Kumar K.V 	struct p9_iattr_dotl p9attr;
6501c7850f9SSasha Levin 
651c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dI", &fid_val, &p9attr);
65231a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
6531c7850f9SSasha Levin 
654c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & ATTR_MODE) {
655c797b6c6SAneesh Kumar K.V 		ret = chmod(fid->abs_path, p9attr.mode);
656c797b6c6SAneesh Kumar K.V 		if (ret < 0)
657c797b6c6SAneesh Kumar K.V 			goto err_out;
658c797b6c6SAneesh Kumar K.V 	}
659c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & (ATTR_ATIME | ATTR_MTIME)) {
660c797b6c6SAneesh Kumar K.V 		struct timespec times[2];
661c797b6c6SAneesh Kumar K.V 		if (p9attr.valid & ATTR_ATIME) {
662c797b6c6SAneesh Kumar K.V 			if (p9attr.valid & ATTR_ATIME_SET) {
663c797b6c6SAneesh Kumar K.V 				times[0].tv_sec = p9attr.atime_sec;
664c797b6c6SAneesh Kumar K.V 				times[0].tv_nsec = p9attr.atime_nsec;
665c797b6c6SAneesh Kumar K.V 			} else {
666c797b6c6SAneesh Kumar K.V 				times[0].tv_nsec = UTIME_NOW;
667c797b6c6SAneesh Kumar K.V 			}
668c797b6c6SAneesh Kumar K.V 		} else {
669c797b6c6SAneesh Kumar K.V 			times[0].tv_nsec = UTIME_OMIT;
670c797b6c6SAneesh Kumar K.V 		}
671c797b6c6SAneesh Kumar K.V 		if (p9attr.valid & ATTR_MTIME) {
672c797b6c6SAneesh Kumar K.V 			if (p9attr.valid & ATTR_MTIME_SET) {
673c797b6c6SAneesh Kumar K.V 				times[1].tv_sec = p9attr.mtime_sec;
674c797b6c6SAneesh Kumar K.V 				times[1].tv_nsec = p9attr.mtime_nsec;
675c797b6c6SAneesh Kumar K.V 			} else {
676c797b6c6SAneesh Kumar K.V 				times[1].tv_nsec = UTIME_NOW;
677c797b6c6SAneesh Kumar K.V 			}
678c797b6c6SAneesh Kumar K.V 		} else
679c797b6c6SAneesh Kumar K.V 			times[1].tv_nsec = UTIME_OMIT;
680c797b6c6SAneesh Kumar K.V 
681c797b6c6SAneesh Kumar K.V 		ret = utimensat(-1, fid->abs_path, times, AT_SYMLINK_NOFOLLOW);
682c797b6c6SAneesh Kumar K.V 		if (ret < 0)
683c797b6c6SAneesh Kumar K.V 			goto err_out;
684c797b6c6SAneesh Kumar K.V 	}
685c797b6c6SAneesh Kumar K.V 	/*
686c797b6c6SAneesh Kumar K.V 	 * If the only valid entry in iattr is ctime we can call
687c797b6c6SAneesh Kumar K.V 	 * chown(-1,-1) to update the ctime of the file
688c797b6c6SAneesh Kumar K.V 	 */
689c797b6c6SAneesh Kumar K.V 	if ((p9attr.valid & (ATTR_UID | ATTR_GID)) ||
690c797b6c6SAneesh Kumar K.V 	    ((p9attr.valid & ATTR_CTIME)
691c797b6c6SAneesh Kumar K.V 	     && !((p9attr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
692c797b6c6SAneesh Kumar K.V 		if (!(p9attr.valid & ATTR_UID))
693c797b6c6SAneesh Kumar K.V 			p9attr.uid = -1;
694c797b6c6SAneesh Kumar K.V 
695c797b6c6SAneesh Kumar K.V 		if (!(p9attr.valid & ATTR_GID))
696c797b6c6SAneesh Kumar K.V 			p9attr.gid = -1;
697c797b6c6SAneesh Kumar K.V 
698c797b6c6SAneesh Kumar K.V 		ret = lchown(fid->abs_path, p9attr.uid, p9attr.gid);
699c797b6c6SAneesh Kumar K.V 		if (ret < 0)
700c797b6c6SAneesh Kumar K.V 			goto err_out;
701c797b6c6SAneesh Kumar K.V 	}
702c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & (ATTR_SIZE)) {
703c797b6c6SAneesh Kumar K.V 		ret = truncate(fid->abs_path, p9attr.size);
704c797b6c6SAneesh Kumar K.V 		if (ret < 0)
705c797b6c6SAneesh Kumar K.V 			goto err_out;
706c797b6c6SAneesh Kumar K.V 	}
7075529bcd7SAsias He 	*outlen = VIRTIO_9P_HDR_LEN;
708bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
709ead43b01SAneesh Kumar K.V 	return;
7104bc9734aSAneesh Kumar K.V err_out:
7114bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7124bc9734aSAneesh Kumar K.V 	return;
7131c7850f9SSasha Levin }
7141c7850f9SSasha Levin 
715ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
716af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
7171c7850f9SSasha Levin {
7184bc9734aSAneesh Kumar K.V 
719bfc15268SAneesh Kumar K.V 	u64 offset;
720bfc15268SAneesh Kumar K.V 	u32 fid_val;
7214bc9734aSAneesh Kumar K.V 	u32 count;
7224bc9734aSAneesh Kumar K.V 	ssize_t res;
72350c479e0SAneesh Kumar K.V 	u16 iov_cnt;
72450c479e0SAneesh Kumar K.V 	void *iov_base;
72550c479e0SAneesh Kumar K.V 	size_t iov_len;
726bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
727b064b05aSAneesh Kumar K.V 	/* u32 fid + u64 offset + u32 count */
728b064b05aSAneesh Kumar K.V 	int twrite_size = sizeof(u32) + sizeof(u64) + sizeof(u32);
7291c7850f9SSasha Levin 
730bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
73131a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
732af045e53SAneesh Kumar K.V 
73350c479e0SAneesh Kumar K.V 	iov_base = pdu->out_iov[0].iov_base;
73450c479e0SAneesh Kumar K.V 	iov_len  = pdu->out_iov[0].iov_len;
73550c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->out_iov_cnt;
73650c479e0SAneesh Kumar K.V 
737bfc15268SAneesh Kumar K.V 	/* Adjust the iovec to skip the header and meta data */
738b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) + twrite_size);
739b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_len -=  (sizeof(struct p9_msg) + twrite_size);
740bfc15268SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count,
7416b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
7424bc9734aSAneesh Kumar K.V 	res = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset);
74350c479e0SAneesh Kumar K.V 	/*
74450c479e0SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
74550c479e0SAneesh Kumar K.V 	 * pdu_readf works correctly.
74650c479e0SAneesh Kumar K.V 	 */
74750c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_base = iov_base;
74850c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_len  = iov_len;
74950c479e0SAneesh Kumar K.V 	pdu->out_iov_cnt         = iov_cnt;
750c797b6c6SAneesh Kumar K.V 
7514bc9734aSAneesh Kumar K.V 	if (res < 0)
7524bc9734aSAneesh Kumar K.V 		goto err_out;
7534bc9734aSAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", res);
754bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
755bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
756ead43b01SAneesh Kumar K.V 	return;
7574bc9734aSAneesh Kumar K.V err_out:
7584bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7594bc9734aSAneesh Kumar K.V 	return;
7601c7850f9SSasha Levin }
7611c7850f9SSasha Levin 
7626fc5cd9bSSasha Levin static void virtio_p9_remove(struct p9_dev *p9dev,
7636fc5cd9bSSasha Levin 			     struct p9_pdu *pdu, u32 *outlen)
7646fc5cd9bSSasha Levin {
7656fc5cd9bSSasha Levin 	int ret;
7666fc5cd9bSSasha Levin 	u32 fid_val;
7676fc5cd9bSSasha Levin 	struct p9_fid *fid;
7686fc5cd9bSSasha Levin 
7696fc5cd9bSSasha Levin 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
77031a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
7716fc5cd9bSSasha Levin 
7729b604a9cSSasha Levin 	ret = remove(fid->abs_path);
7736fc5cd9bSSasha Levin 	if (ret < 0)
7746fc5cd9bSSasha Levin 		goto err_out;
7756fc5cd9bSSasha Levin 	*outlen = pdu->write_offset;
7766fc5cd9bSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
7776fc5cd9bSSasha Levin 	return;
7786fc5cd9bSSasha Levin 
7796fc5cd9bSSasha Levin err_out:
7806fc5cd9bSSasha Levin 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7816fc5cd9bSSasha Levin 	return;
7826fc5cd9bSSasha Levin }
7836fc5cd9bSSasha Levin 
784f161f28bSSasha Levin static void virtio_p9_rename(struct p9_dev *p9dev,
785f161f28bSSasha Levin 			     struct p9_pdu *pdu, u32 *outlen)
786f161f28bSSasha Levin {
787f161f28bSSasha Levin 	int ret;
788f161f28bSSasha Levin 	u32 fid_val, new_fid_val;
789f161f28bSSasha Levin 	struct p9_fid *fid, *new_fid;
790f161f28bSSasha Levin 	char full_path[PATH_MAX], *new_name;
791f161f28bSSasha Levin 
792f161f28bSSasha Levin 	virtio_p9_pdu_readf(pdu, "dds", &fid_val, &new_fid_val, &new_name);
79331a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
79431a6fb8dSSasha Levin 	new_fid = get_fid(p9dev, new_fid_val);
795f161f28bSSasha Levin 
796f161f28bSSasha Levin 	sprintf(full_path, "%s/%s", new_fid->abs_path, new_name);
797f161f28bSSasha Levin 	ret = rename(fid->abs_path, full_path);
798f161f28bSSasha Levin 	if (ret < 0)
799f161f28bSSasha Levin 		goto err_out;
800f161f28bSSasha Levin 	*outlen = pdu->write_offset;
801f161f28bSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
802f161f28bSSasha Levin 	return;
803f161f28bSSasha Levin 
804f161f28bSSasha Levin err_out:
805f161f28bSSasha Levin 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
806f161f28bSSasha Levin 	return;
807f161f28bSSasha Levin }
808f161f28bSSasha Levin 
809c797b6c6SAneesh Kumar K.V static void virtio_p9_readlink(struct p9_dev *p9dev,
810c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
811c797b6c6SAneesh Kumar K.V {
812c797b6c6SAneesh Kumar K.V 	int ret;
813c797b6c6SAneesh Kumar K.V 	u32 fid_val;
814c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
815c797b6c6SAneesh Kumar K.V 	char target_path[PATH_MAX];
816c797b6c6SAneesh Kumar K.V 
817c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
81831a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
819c797b6c6SAneesh Kumar K.V 
820c797b6c6SAneesh Kumar K.V 	memset(target_path, 0, PATH_MAX);
821c797b6c6SAneesh Kumar K.V 	ret = readlink(fid->abs_path, target_path, PATH_MAX - 1);
822c797b6c6SAneesh Kumar K.V 	if (ret < 0)
823c797b6c6SAneesh Kumar K.V 		goto err_out;
824c797b6c6SAneesh Kumar K.V 
825c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "s", target_path);
826c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
827c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
828c797b6c6SAneesh Kumar K.V 	return;
829c797b6c6SAneesh Kumar K.V err_out:
830c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
831c797b6c6SAneesh Kumar K.V 	return;
832c797b6c6SAneesh Kumar K.V }
833c797b6c6SAneesh Kumar K.V 
834c797b6c6SAneesh Kumar K.V static void virtio_p9_statfs(struct p9_dev *p9dev,
835c797b6c6SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
836c797b6c6SAneesh Kumar K.V {
837c797b6c6SAneesh Kumar K.V 	int ret;
838c797b6c6SAneesh Kumar K.V 	u64 fsid;
839c797b6c6SAneesh Kumar K.V 	u32 fid_val;
840c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
841c797b6c6SAneesh Kumar K.V 	struct statfs stat_buf;
842c797b6c6SAneesh Kumar K.V 
843c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
84431a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
845c797b6c6SAneesh Kumar K.V 
846c797b6c6SAneesh Kumar K.V 	ret = statfs(fid->abs_path, &stat_buf);
847c797b6c6SAneesh Kumar K.V 	if (ret < 0)
848c797b6c6SAneesh Kumar K.V 		goto err_out;
849c797b6c6SAneesh Kumar K.V 	/* FIXME!! f_blocks needs update based on client msize */
850c797b6c6SAneesh Kumar K.V 	fsid = (unsigned int) stat_buf.f_fsid.__val[0] |
851c797b6c6SAneesh Kumar K.V 		(unsigned long long)stat_buf.f_fsid.__val[1] << 32;
852c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "ddqqqqqqd", stat_buf.f_type,
853c797b6c6SAneesh Kumar K.V 			     stat_buf.f_bsize, stat_buf.f_blocks,
854c797b6c6SAneesh Kumar K.V 			     stat_buf.f_bfree, stat_buf.f_bavail,
855c797b6c6SAneesh Kumar K.V 			     stat_buf.f_files, stat_buf.f_ffree,
856c797b6c6SAneesh Kumar K.V 			     fsid, stat_buf.f_namelen);
857c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
858c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
859c797b6c6SAneesh Kumar K.V 	return;
860c797b6c6SAneesh Kumar K.V err_out:
861c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
862c797b6c6SAneesh Kumar K.V 	return;
863c797b6c6SAneesh Kumar K.V }
864c797b6c6SAneesh Kumar K.V 
865c797b6c6SAneesh Kumar K.V static void virtio_p9_mknod(struct p9_dev *p9dev,
866c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
867c797b6c6SAneesh Kumar K.V {
868c797b6c6SAneesh Kumar K.V 	int ret;
869c797b6c6SAneesh Kumar K.V 	char *name;
870c797b6c6SAneesh Kumar K.V 	struct stat st;
871c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
872c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
873c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
874c797b6c6SAneesh Kumar K.V 	u32 fid_val, mode, major, minor, gid;
875c797b6c6SAneesh Kumar K.V 
876c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdddd", &fid_val, &name, &mode,
877c797b6c6SAneesh Kumar K.V 			    &major, &minor, &gid);
878c797b6c6SAneesh Kumar K.V 
87931a6fb8dSSasha Levin 	dfid = get_fid(p9dev, fid_val);
880c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
881c797b6c6SAneesh Kumar K.V 	ret = mknod(full_path, mode, makedev(major, minor));
882c797b6c6SAneesh Kumar K.V 	if (ret < 0)
883c797b6c6SAneesh Kumar K.V 		goto err_out;
884c797b6c6SAneesh Kumar K.V 
885c797b6c6SAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
886c797b6c6SAneesh Kumar K.V 		goto err_out;
887c797b6c6SAneesh Kumar K.V 
888c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
889c797b6c6SAneesh Kumar K.V 	if (ret < 0)
890c797b6c6SAneesh Kumar K.V 		goto err_out;
891c797b6c6SAneesh Kumar K.V 
892c797b6c6SAneesh Kumar K.V 	ret = lchown(full_path, dfid->uid, gid);
893c797b6c6SAneesh Kumar K.V 	if (ret < 0)
894c797b6c6SAneesh Kumar K.V 		goto err_out;
895c797b6c6SAneesh Kumar K.V 
896c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
897c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
898c797b6c6SAneesh Kumar K.V 	free(name);
899c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
900c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
901c797b6c6SAneesh Kumar K.V 	return;
902c797b6c6SAneesh Kumar K.V err_out:
903c797b6c6SAneesh Kumar K.V 	free(name);
904c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
905c797b6c6SAneesh Kumar K.V 	return;
906c797b6c6SAneesh Kumar K.V }
907c797b6c6SAneesh Kumar K.V 
908c797b6c6SAneesh Kumar K.V static void virtio_p9_fsync(struct p9_dev *p9dev,
909c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
910c797b6c6SAneesh Kumar K.V {
911c797b6c6SAneesh Kumar K.V 	int ret;
912c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
913c797b6c6SAneesh Kumar K.V 	u32 fid_val, datasync;
914c797b6c6SAneesh Kumar K.V 
915c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dd", &fid_val, &datasync);
91631a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
917c797b6c6SAneesh Kumar K.V 
918c797b6c6SAneesh Kumar K.V 	if (datasync)
919c797b6c6SAneesh Kumar K.V 		ret = fdatasync(fid->fd);
920c797b6c6SAneesh Kumar K.V 	else
921c797b6c6SAneesh Kumar K.V 		ret = fsync(fid->fd);
922c797b6c6SAneesh Kumar K.V 	if (ret < 0)
923c797b6c6SAneesh Kumar K.V 		goto err_out;
924c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
925c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
926c797b6c6SAneesh Kumar K.V 	return;
927c797b6c6SAneesh Kumar K.V err_out:
928c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
929c797b6c6SAneesh Kumar K.V 	return;
930c797b6c6SAneesh Kumar K.V }
931c797b6c6SAneesh Kumar K.V 
932c797b6c6SAneesh Kumar K.V static void virtio_p9_symlink(struct p9_dev *p9dev,
933c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
934c797b6c6SAneesh Kumar K.V {
935c797b6c6SAneesh Kumar K.V 	int ret;
936c797b6c6SAneesh Kumar K.V 	struct stat st;
937c797b6c6SAneesh Kumar K.V 	u32 fid_val, gid;
938c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
939c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
940c797b6c6SAneesh Kumar K.V 	char new_name[PATH_MAX];
941c797b6c6SAneesh Kumar K.V 	char *old_path, *name;
942c797b6c6SAneesh Kumar K.V 
943c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dssd", &fid_val, &name, &old_path, &gid);
944c797b6c6SAneesh Kumar K.V 
94531a6fb8dSSasha Levin 	dfid = get_fid(p9dev, fid_val);
946c797b6c6SAneesh Kumar K.V 	sprintf(new_name, "%s/%s", dfid->abs_path, name);
947c797b6c6SAneesh Kumar K.V 	ret = symlink(old_path, new_name);
948c797b6c6SAneesh Kumar K.V 	if (ret < 0)
949c797b6c6SAneesh Kumar K.V 		goto err_out;
950c797b6c6SAneesh Kumar K.V 
951c797b6c6SAneesh Kumar K.V 	if (lstat(new_name, &st) < 0)
952c797b6c6SAneesh Kumar K.V 		goto err_out;
953c797b6c6SAneesh Kumar K.V 
954c797b6c6SAneesh Kumar K.V 	ret = lchown(new_name, dfid->uid, gid);
955c797b6c6SAneesh Kumar K.V 	if (ret < 0)
956c797b6c6SAneesh Kumar K.V 		goto err_out;
957c797b6c6SAneesh Kumar K.V 
958c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
959c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
960c797b6c6SAneesh Kumar K.V 	free(name);
961c797b6c6SAneesh Kumar K.V 	free(old_path);
962c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
963c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
964c797b6c6SAneesh Kumar K.V 	return;
965c797b6c6SAneesh Kumar K.V err_out:
966c797b6c6SAneesh Kumar K.V 	free(name);
967c797b6c6SAneesh Kumar K.V 	free(old_path);
968c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
969c797b6c6SAneesh Kumar K.V 	return;
970c797b6c6SAneesh Kumar K.V }
971c797b6c6SAneesh Kumar K.V 
972c797b6c6SAneesh Kumar K.V static void virtio_p9_link(struct p9_dev *p9dev,
973c797b6c6SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
974c797b6c6SAneesh Kumar K.V {
975c797b6c6SAneesh Kumar K.V 	int ret;
976c797b6c6SAneesh Kumar K.V 	char *name;
977c797b6c6SAneesh Kumar K.V 	u32 fid_val, dfid_val;
978c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid, *fid;
979c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
980c797b6c6SAneesh Kumar K.V 
981c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dds", &dfid_val, &fid_val, &name);
982c797b6c6SAneesh Kumar K.V 
98331a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
98431a6fb8dSSasha Levin 	fid =  get_fid(p9dev, fid_val);
985c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
986c797b6c6SAneesh Kumar K.V 	ret = link(fid->abs_path, full_path);
987c797b6c6SAneesh Kumar K.V 	if (ret < 0)
988c797b6c6SAneesh Kumar K.V 		goto err_out;
989c797b6c6SAneesh Kumar K.V 	free(name);
990c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
991c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
992c797b6c6SAneesh Kumar K.V 	return;
993c797b6c6SAneesh Kumar K.V err_out:
994c797b6c6SAneesh Kumar K.V 	free(name);
995c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
996c797b6c6SAneesh Kumar K.V 	return;
997c797b6c6SAneesh Kumar K.V 
998c797b6c6SAneesh Kumar K.V }
999c797b6c6SAneesh Kumar K.V 
1000c797b6c6SAneesh Kumar K.V static void virtio_p9_lock(struct p9_dev *p9dev,
1001c797b6c6SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
1002c797b6c6SAneesh Kumar K.V {
1003c797b6c6SAneesh Kumar K.V 	u8 ret;
1004c797b6c6SAneesh Kumar K.V 	u32 fid_val;
1005c797b6c6SAneesh Kumar K.V 	struct p9_flock flock;
1006c797b6c6SAneesh Kumar K.V 
1007c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dbdqqds", &fid_val, &flock.type,
1008c797b6c6SAneesh Kumar K.V 			    &flock.flags, &flock.start, &flock.length,
1009c797b6c6SAneesh Kumar K.V 			    &flock.proc_id, &flock.client_id);
1010c797b6c6SAneesh Kumar K.V 
1011c797b6c6SAneesh Kumar K.V 	/* Just return success */
1012c797b6c6SAneesh Kumar K.V 	ret = P9_LOCK_SUCCESS;
1013c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", ret);
1014c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1015c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1016c797b6c6SAneesh Kumar K.V 	free(flock.client_id);
1017c797b6c6SAneesh Kumar K.V 	return;
1018c797b6c6SAneesh Kumar K.V }
1019c797b6c6SAneesh Kumar K.V 
1020c797b6c6SAneesh Kumar K.V static void virtio_p9_getlock(struct p9_dev *p9dev,
1021c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1022c797b6c6SAneesh Kumar K.V {
1023c797b6c6SAneesh Kumar K.V 	u32 fid_val;
1024c797b6c6SAneesh Kumar K.V 	struct p9_getlock glock;
1025c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dbqqds", &fid_val, &glock.type,
1026c797b6c6SAneesh Kumar K.V 			    &glock.start, &glock.length, &glock.proc_id,
1027c797b6c6SAneesh Kumar K.V 			    &glock.client_id);
1028c797b6c6SAneesh Kumar K.V 
1029c797b6c6SAneesh Kumar K.V 	/* Just return success */
1030c797b6c6SAneesh Kumar K.V 	glock.type = F_UNLCK;
1031c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "bqqds", glock.type,
1032c797b6c6SAneesh Kumar K.V 			     glock.start, glock.length, glock.proc_id,
1033c797b6c6SAneesh Kumar K.V 			     glock.client_id);
1034c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1035c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1036c797b6c6SAneesh Kumar K.V 	free(glock.client_id);
1037c797b6c6SAneesh Kumar K.V 	return;
1038c797b6c6SAneesh Kumar K.V }
1039c797b6c6SAneesh Kumar K.V 
1040c797b6c6SAneesh Kumar K.V static int virtio_p9_ancestor(char *path, char *ancestor)
1041c797b6c6SAneesh Kumar K.V {
1042c797b6c6SAneesh Kumar K.V 	int size = strlen(ancestor);
1043c797b6c6SAneesh Kumar K.V 	if (!strncmp(path, ancestor, size)) {
1044c797b6c6SAneesh Kumar K.V 		/*
1045c797b6c6SAneesh Kumar K.V 		 * Now check whether ancestor is a full name or
1046c797b6c6SAneesh Kumar K.V 		 * or directory component and not just part
1047c797b6c6SAneesh Kumar K.V 		 * of a name.
1048c797b6c6SAneesh Kumar K.V 		 */
1049c797b6c6SAneesh Kumar K.V 		if (path[size] == '\0' || path[size] == '/')
1050c797b6c6SAneesh Kumar K.V 			return 1;
1051c797b6c6SAneesh Kumar K.V 	}
1052c797b6c6SAneesh Kumar K.V 	return 0;
1053c797b6c6SAneesh Kumar K.V }
1054c797b6c6SAneesh Kumar K.V 
1055c797b6c6SAneesh Kumar K.V static void virtio_p9_fix_path(char *fid_path, char *old_name, char *new_name)
1056c797b6c6SAneesh Kumar K.V {
1057c797b6c6SAneesh Kumar K.V 	char tmp_name[PATH_MAX];
1058c797b6c6SAneesh Kumar K.V 	size_t rp_sz = strlen(old_name);
1059c797b6c6SAneesh Kumar K.V 
1060c797b6c6SAneesh Kumar K.V 	if (rp_sz == strlen(fid_path)) {
1061c797b6c6SAneesh Kumar K.V 		/* replace the full name */
1062c797b6c6SAneesh Kumar K.V 		strcpy(fid_path, new_name);
1063c797b6c6SAneesh Kumar K.V 		return;
1064c797b6c6SAneesh Kumar K.V 	}
1065c797b6c6SAneesh Kumar K.V 	/* save the trailing path details */
1066c797b6c6SAneesh Kumar K.V 	strcpy(tmp_name, fid_path + rp_sz);
1067c797b6c6SAneesh Kumar K.V 	sprintf(fid_path, "%s%s", new_name, tmp_name);
1068c797b6c6SAneesh Kumar K.V 	return;
1069c797b6c6SAneesh Kumar K.V }
1070c797b6c6SAneesh Kumar K.V 
1071e2341580SSasha Levin static void rename_fids(struct p9_dev *p9dev, char *old_name, char *new_name)
1072e2341580SSasha Levin {
1073e2341580SSasha Levin 	struct rb_node *node = rb_first(&p9dev->fids);
1074e2341580SSasha Levin 
1075e2341580SSasha Levin 	while (node) {
1076e2341580SSasha Levin 		struct p9_fid *fid = rb_entry(node, struct p9_fid, node);
1077e2341580SSasha Levin 
1078e2341580SSasha Levin 		if (fid->fid != P9_NOFID && virtio_p9_ancestor(fid->path, old_name)) {
1079e2341580SSasha Levin 				virtio_p9_fix_path(fid->path, old_name, new_name);
1080e2341580SSasha Levin 		}
1081e2341580SSasha Levin 		node = rb_next(node);
1082e2341580SSasha Levin 	}
1083e2341580SSasha Levin }
1084e2341580SSasha Levin 
1085c797b6c6SAneesh Kumar K.V static void virtio_p9_renameat(struct p9_dev *p9dev,
1086c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
1087c797b6c6SAneesh Kumar K.V {
1088e2341580SSasha Levin 	int ret;
1089c797b6c6SAneesh Kumar K.V 	char *old_name, *new_name;
1090c797b6c6SAneesh Kumar K.V 	u32 old_dfid_val, new_dfid_val;
1091c797b6c6SAneesh Kumar K.V 	struct p9_fid *old_dfid, *new_dfid;
1092c797b6c6SAneesh Kumar K.V 	char old_full_path[PATH_MAX], new_full_path[PATH_MAX];
1093c797b6c6SAneesh Kumar K.V 
1094c797b6c6SAneesh Kumar K.V 
1095c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsds", &old_dfid_val, &old_name,
1096c797b6c6SAneesh Kumar K.V 			    &new_dfid_val, &new_name);
1097c797b6c6SAneesh Kumar K.V 
109831a6fb8dSSasha Levin 	old_dfid = get_fid(p9dev, old_dfid_val);
109931a6fb8dSSasha Levin 	new_dfid = get_fid(p9dev, new_dfid_val);
1100c797b6c6SAneesh Kumar K.V 
1101c797b6c6SAneesh Kumar K.V 	sprintf(old_full_path, "%s/%s", old_dfid->abs_path, old_name);
1102c797b6c6SAneesh Kumar K.V 	sprintf(new_full_path, "%s/%s", new_dfid->abs_path, new_name);
1103c797b6c6SAneesh Kumar K.V 	ret = rename(old_full_path, new_full_path);
1104c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1105c797b6c6SAneesh Kumar K.V 		goto err_out;
1106c797b6c6SAneesh Kumar K.V 	/*
1107c797b6c6SAneesh Kumar K.V 	 * Now fix path in other fids, if the renamed path is part of
1108c797b6c6SAneesh Kumar K.V 	 * that.
1109c797b6c6SAneesh Kumar K.V 	 */
1110e2341580SSasha Levin 	rename_fids(p9dev, old_name, new_name);
1111c797b6c6SAneesh Kumar K.V 	free(old_name);
1112c797b6c6SAneesh Kumar K.V 	free(new_name);
1113c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1114c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1115c797b6c6SAneesh Kumar K.V 	return;
1116c797b6c6SAneesh Kumar K.V err_out:
1117c797b6c6SAneesh Kumar K.V 	free(old_name);
1118c797b6c6SAneesh Kumar K.V 	free(new_name);
1119c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1120c797b6c6SAneesh Kumar K.V 	return;
1121c797b6c6SAneesh Kumar K.V }
1122c797b6c6SAneesh Kumar K.V 
1123c797b6c6SAneesh Kumar K.V static void virtio_p9_unlinkat(struct p9_dev *p9dev,
1124c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
1125c797b6c6SAneesh Kumar K.V {
1126c797b6c6SAneesh Kumar K.V 	int ret;
1127c797b6c6SAneesh Kumar K.V 	char *name;
1128c797b6c6SAneesh Kumar K.V 	u32 fid_val, flags;
1129c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
1130c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
1131c797b6c6SAneesh Kumar K.V 
1132c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsd", &fid_val, &name, &flags);
113331a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
1134c797b6c6SAneesh Kumar K.V 
1135c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", fid->abs_path, name);
1136c797b6c6SAneesh Kumar K.V 	ret = remove(full_path);
1137c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1138c797b6c6SAneesh Kumar K.V 		goto err_out;
1139c797b6c6SAneesh Kumar K.V 	free(name);
1140c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1141c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1142c797b6c6SAneesh Kumar K.V 	return;
1143c797b6c6SAneesh Kumar K.V err_out:
1144c797b6c6SAneesh Kumar K.V 	free(name);
1145c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1146c797b6c6SAneesh Kumar K.V 	return;
1147c797b6c6SAneesh Kumar K.V }
1148c797b6c6SAneesh Kumar K.V 
11495cc808aaSSasha Levin static void virtio_p9_flush(struct p9_dev *p9dev,
11505cc808aaSSasha Levin 				struct p9_pdu *pdu, u32 *outlen)
11515cc808aaSSasha Levin {
11525cc808aaSSasha Levin 	u16 tag, oldtag;
11535cc808aaSSasha Levin 
11545cc808aaSSasha Levin 	virtio_p9_pdu_readf(pdu, "ww", &tag, &oldtag);
11555cc808aaSSasha Levin 	virtio_p9_pdu_writef(pdu, "w", tag);
11565cc808aaSSasha Levin 	*outlen = pdu->write_offset;
11575cc808aaSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
11585cc808aaSSasha Levin 
11595cc808aaSSasha Levin 	return;
11605cc808aaSSasha Levin }
11615cc808aaSSasha Levin 
1162c797b6c6SAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev,
1163c797b6c6SAneesh Kumar K.V 				 struct p9_pdu *pdu, u32 *outlen)
1164c797b6c6SAneesh Kumar K.V {
1165c797b6c6SAneesh Kumar K.V 	return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen);
1166c797b6c6SAneesh Kumar K.V }
1167c797b6c6SAneesh Kumar K.V 
1168ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
1169af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
1170b4422bf3SAneesh Kumar K.V 
1171c797b6c6SAneesh Kumar K.V /* FIXME should be removed when merging with latest linus tree */
1172c797b6c6SAneesh Kumar K.V #define P9_TRENAMEAT 74
1173c797b6c6SAneesh Kumar K.V #define P9_TUNLINKAT 76
1174c797b6c6SAneesh Kumar K.V 
1175c797b6c6SAneesh Kumar K.V static p9_handler *virtio_9p_dotl_handler [] = {
1176c797b6c6SAneesh Kumar K.V 	[P9_TREADDIR]     = virtio_p9_readdir,
1177c797b6c6SAneesh Kumar K.V 	[P9_TSTATFS]      = virtio_p9_statfs,
1178c797b6c6SAneesh Kumar K.V 	[P9_TGETATTR]     = virtio_p9_getattr,
1179c797b6c6SAneesh Kumar K.V 	[P9_TSETATTR]     = virtio_p9_setattr,
1180c797b6c6SAneesh Kumar K.V 	[P9_TXATTRWALK]   = virtio_p9_eopnotsupp,
1181c797b6c6SAneesh Kumar K.V 	[P9_TXATTRCREATE] = virtio_p9_eopnotsupp,
1182c797b6c6SAneesh Kumar K.V 	[P9_TMKNOD]       = virtio_p9_mknod,
1183c797b6c6SAneesh Kumar K.V 	[P9_TLOCK]        = virtio_p9_lock,
1184c797b6c6SAneesh Kumar K.V 	[P9_TGETLOCK]     = virtio_p9_getlock,
1185c797b6c6SAneesh Kumar K.V 	[P9_TRENAMEAT]    = virtio_p9_renameat,
1186c797b6c6SAneesh Kumar K.V 	[P9_TREADLINK]    = virtio_p9_readlink,
1187c797b6c6SAneesh Kumar K.V 	[P9_TUNLINKAT]    = virtio_p9_unlinkat,
1188c797b6c6SAneesh Kumar K.V 	[P9_TMKDIR]       = virtio_p9_mkdir,
1189b4422bf3SAneesh Kumar K.V 	[P9_TVERSION]     = virtio_p9_version,
1190c797b6c6SAneesh Kumar K.V 	[P9_TLOPEN]       = virtio_p9_open,
1191b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]      = virtio_p9_attach,
1192b4422bf3SAneesh Kumar K.V 	[P9_TWALK]        = virtio_p9_walk,
1193c797b6c6SAneesh Kumar K.V 	[P9_TCLUNK]       = virtio_p9_clunk,
1194c797b6c6SAneesh Kumar K.V 	[P9_TFSYNC]       = virtio_p9_fsync,
1195b4422bf3SAneesh Kumar K.V 	[P9_TREAD]        = virtio_p9_read,
11965cc808aaSSasha Levin 	[P9_TFLUSH]       = virtio_p9_flush,
1197c797b6c6SAneesh Kumar K.V 	[P9_TLINK]        = virtio_p9_link,
1198c797b6c6SAneesh Kumar K.V 	[P9_TSYMLINK]     = virtio_p9_symlink,
1199c797b6c6SAneesh Kumar K.V 	[P9_TLCREATE]     = virtio_p9_create,
1200b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]       = virtio_p9_write,
12016fc5cd9bSSasha Levin 	[P9_TREMOVE]      = virtio_p9_remove,
1202f161f28bSSasha Levin 	[P9_TRENAME]      = virtio_p9_rename,
1203b4422bf3SAneesh Kumar K.V };
1204b4422bf3SAneesh Kumar K.V 
1205af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
1206af045e53SAneesh Kumar K.V {
1207af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
1208af045e53SAneesh Kumar K.V 	if (!pdu)
1209af045e53SAneesh Kumar K.V 		return NULL;
1210af045e53SAneesh Kumar K.V 
1211bfc15268SAneesh Kumar K.V 	/* skip the pdu header p9_msg */
12125529bcd7SAsias He 	pdu->read_offset	= VIRTIO_9P_HDR_LEN;
12135529bcd7SAsias He 	pdu->write_offset	= VIRTIO_9P_HDR_LEN;
1214af045e53SAneesh Kumar K.V 	pdu->queue_head		= virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
1215a8a44649SAsias He 					pdu->out_iov, &pdu->in_iov_cnt, &pdu->out_iov_cnt);
1216af045e53SAneesh Kumar K.V 	return pdu;
1217af045e53SAneesh Kumar K.V }
1218af045e53SAneesh Kumar K.V 
1219af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
1220af045e53SAneesh Kumar K.V {
1221af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
1222af045e53SAneesh Kumar K.V 	/*
1223af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
1224af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
1225af045e53SAneesh Kumar K.V 	 */
1226af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
1227af045e53SAneesh Kumar K.V 	return msg->cmd;
1228af045e53SAneesh Kumar K.V }
1229af045e53SAneesh Kumar K.V 
1230b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
12311c7850f9SSasha Levin {
1232af045e53SAneesh Kumar K.V 	u8 cmd;
1233b4422bf3SAneesh Kumar K.V 	u32 len = 0;
1234b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
1235b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
1236af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
1237af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
12381c7850f9SSasha Levin 
1239b4422bf3SAneesh Kumar K.V 	vq = job->vq;
1240b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
12411c7850f9SSasha Levin 
1242af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
1243af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
1244af045e53SAneesh Kumar K.V 
1245c797b6c6SAneesh Kumar K.V 	if ((cmd >= ARRAY_SIZE(virtio_9p_dotl_handler)) ||
1246c797b6c6SAneesh Kumar K.V 	    !virtio_9p_dotl_handler[cmd])
124797b408afSAneesh Kumar K.V 		handler = virtio_p9_eopnotsupp;
1248dd78d9eaSAneesh Kumar K.V 	else
1249c797b6c6SAneesh Kumar K.V 		handler = virtio_9p_dotl_handler[cmd];
1250c797b6c6SAneesh Kumar K.V 
1251af045e53SAneesh Kumar K.V 	handler(p9dev, p9pdu, &len);
1252af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
1253af045e53SAneesh Kumar K.V 	free(p9pdu);
12541c7850f9SSasha Levin 	return true;
12551c7850f9SSasha Levin }
12561c7850f9SSasha Levin 
12571c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
12581c7850f9SSasha Levin {
1259b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
1260b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
1261b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
12621c7850f9SSasha Levin 
12631c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
1264b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
126502eca50cSAsias He 		p9dev->vdev.ops->signal_vq(kvm, &p9dev->vdev, vq - p9dev->vqs);
12661c7850f9SSasha Levin 	}
12671c7850f9SSasha Levin }
12681c7850f9SSasha Levin 
1269c7838fbdSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
12701c7850f9SSasha Levin {
1271c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
12721c7850f9SSasha Levin 
1273c7838fbdSSasha Levin 	((u8 *)(p9dev->config))[offset] = data;
1274c7838fbdSSasha Levin }
12751c7850f9SSasha Levin 
1276c7838fbdSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
1277c7838fbdSSasha Levin {
1278c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1279c7838fbdSSasha Levin 
1280c7838fbdSSasha Levin 	return ((u8 *)(p9dev->config))[offset];
1281c7838fbdSSasha Levin }
1282c7838fbdSSasha Levin 
1283c7838fbdSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
1284c7838fbdSSasha Levin {
1285c7838fbdSSasha Levin 	return 1 << VIRTIO_9P_MOUNT_TAG;
1286c7838fbdSSasha Levin }
1287c7838fbdSSasha Levin 
1288c7838fbdSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
1289c7838fbdSSasha Levin {
1290c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1291c7838fbdSSasha Levin 
1292c7838fbdSSasha Levin 	p9dev->features = features;
1293c7838fbdSSasha Levin }
1294c7838fbdSSasha Levin 
1295c7838fbdSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
1296c7838fbdSSasha Levin {
1297c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1298b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job;
1299b4422bf3SAneesh Kumar K.V 	struct virt_queue *queue;
1300c7838fbdSSasha Levin 	void *p;
13011c7850f9SSasha Levin 
1302312c62d1SSasha Levin 	compat__remove_message(compat_id);
1303e59662b3SSasha Levin 
1304c7838fbdSSasha Levin 	queue		= &p9dev->vqs[vq];
1305c7838fbdSSasha Levin 	queue->pfn	= pfn;
13061c7850f9SSasha Levin 	p		= guest_pfn_to_host(kvm, queue->pfn);
1307c7838fbdSSasha Levin 	job		= &p9dev->jobs[vq];
13081c7850f9SSasha Levin 
1309c7838fbdSSasha Levin 	vring_init(&queue->vring, VIRTQUEUE_NUM, p, VIRTIO_PCI_VRING_ALIGN);
13101c7850f9SSasha Levin 
1311b4422bf3SAneesh Kumar K.V 	*job		= (struct p9_dev_job) {
1312b4422bf3SAneesh Kumar K.V 		.vq		= queue,
1313b4422bf3SAneesh Kumar K.V 		.p9dev		= p9dev,
1314b4422bf3SAneesh Kumar K.V 	};
1315df0c7f57SSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_p9_do_io, job);
131660eb42d5SSasha Levin 
1317c7838fbdSSasha Levin 	return 0;
13181c7850f9SSasha Levin }
13191c7850f9SSasha Levin 
1320c7838fbdSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
1321c7838fbdSSasha Levin {
1322c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
13231c7850f9SSasha Levin 
1324c7838fbdSSasha Levin 	thread_pool__do_job(&p9dev->jobs[vq].job_id);
1325c7838fbdSSasha Levin 
1326c7838fbdSSasha Levin 	return 0;
1327c7838fbdSSasha Levin }
1328c7838fbdSSasha Levin 
1329c7838fbdSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
1330c7838fbdSSasha Levin {
1331c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1332c7838fbdSSasha Levin 
1333c7838fbdSSasha Levin 	return p9dev->vqs[vq].pfn;
1334c7838fbdSSasha Levin }
1335c7838fbdSSasha Levin 
1336c7838fbdSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
1337c7838fbdSSasha Levin {
1338c7838fbdSSasha Levin 	return VIRTQUEUE_NUM;
1339c7838fbdSSasha Levin }
1340c7838fbdSSasha Levin 
13411c47ce69SSasha Levin struct virtio_ops p9_dev_virtio_ops = (struct virtio_ops) {
1342c7838fbdSSasha Levin 	.set_config		= set_config,
1343c7838fbdSSasha Levin 	.get_config		= get_config,
1344c7838fbdSSasha Levin 	.get_host_features	= get_host_features,
1345c7838fbdSSasha Levin 	.set_guest_features	= set_guest_features,
1346c7838fbdSSasha Levin 	.init_vq		= init_vq,
1347c7838fbdSSasha Levin 	.notify_vq		= notify_vq,
1348c7838fbdSSasha Levin 	.get_pfn_vq		= get_pfn_vq,
1349c7838fbdSSasha Levin 	.get_size_vq		= get_size_vq,
1350c7838fbdSSasha Levin };
13511c47ce69SSasha Levin 
13521c47ce69SSasha Levin int virtio_9p__init(struct kvm *kvm)
13531c47ce69SSasha Levin {
13541c47ce69SSasha Levin 	struct p9_dev *p9dev;
13551c47ce69SSasha Levin 
13561c47ce69SSasha Levin 	list_for_each_entry(p9dev, &devs, list) {
135702eca50cSAsias He 		virtio_init(kvm, p9dev, &p9dev->vdev, &p9_dev_virtio_ops,
13585529bcd7SAsias He 			    VIRTIO_PCI, PCI_DEVICE_ID_VIRTIO_9P, VIRTIO_ID_9P, PCI_CLASS_9P);
1359c7838fbdSSasha Levin 	}
1360c7838fbdSSasha Levin 
1361c7838fbdSSasha Levin 	return 0;
1362c7838fbdSSasha Levin }
1363c7838fbdSSasha Levin 
1364c7838fbdSSasha Levin int virtio_9p__register(struct kvm *kvm, const char *root, const char *tag_name)
1365c7838fbdSSasha Levin {
1366c7838fbdSSasha Levin 	struct p9_dev *p9dev;
136754f6802dSPekka Enberg 	int err = 0;
13681c7850f9SSasha Levin 
1369b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
1370b4422bf3SAneesh Kumar K.V 	if (!p9dev)
137154f6802dSPekka Enberg 		return -ENOMEM;
137254f6802dSPekka Enberg 
1373b4422bf3SAneesh Kumar K.V 	if (!tag_name)
13745529bcd7SAsias He 		tag_name = VIRTIO_9P_DEFAULT_TAG;
137554f6802dSPekka Enberg 
1376b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
137754f6802dSPekka Enberg 	if (p9dev->config == NULL) {
137854f6802dSPekka Enberg 		err = -ENOMEM;
1379b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
138054f6802dSPekka Enberg 	}
13811c7850f9SSasha Levin 
1382b4422bf3SAneesh Kumar K.V 	strcpy(p9dev->root_dir, root);
1383b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
138454f6802dSPekka Enberg 	if (p9dev->config->tag_len > MAX_TAG_LEN) {
138554f6802dSPekka Enberg 		err = -EINVAL;
1386b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
138754f6802dSPekka Enberg 	}
13881c7850f9SSasha Levin 
1389c7838fbdSSasha Levin 	memcpy(&p9dev->config->tag, tag_name, strlen(tag_name));
13901c7850f9SSasha Levin 
1391c7838fbdSSasha Levin 	list_add(&p9dev->list, &devs);
1392b4422bf3SAneesh Kumar K.V 
1393d278197dSAsias He 	if (compat_id == -1)
139452f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-9p", "CONFIG_NET_9P_VIRTIO");
1395e59662b3SSasha Levin 
139654f6802dSPekka Enberg 	return err;
139754f6802dSPekka Enberg 
1398b4422bf3SAneesh Kumar K.V free_p9dev_config:
1399b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
1400b4422bf3SAneesh Kumar K.V free_p9dev:
1401b4422bf3SAneesh Kumar K.V 	free(p9dev);
140254f6802dSPekka Enberg 	return err;
14031c7850f9SSasha Levin }
1404