xref: /kvmtool/virtio/9p.c (revision 9bb99a82ab6939ddeacc27f48018bcd62585ef1b)
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"
8cac9e8fdSSasha Levin #include "kvm/builtin-setup.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>
17c797b6c6SAneesh Kumar K.V #include <sys/vfs.h>
181c7850f9SSasha Levin 
192daa28d4SAneesh Kumar K.V #include <linux/virtio_ring.h>
202daa28d4SAneesh Kumar K.V #include <linux/virtio_9p.h>
21c6cb7c75SAndre Przywara #include <linux/9p.h>
222daa28d4SAneesh Kumar K.V 
23c7838fbdSSasha Levin static LIST_HEAD(devs);
24312c62d1SSasha Levin static int compat_id = -1;
25c7838fbdSSasha Levin 
26e2341580SSasha Levin static int insert_new_fid(struct p9_dev *dev, struct p9_fid *fid);
27e2341580SSasha Levin static struct p9_fid *find_or_create_fid(struct p9_dev *dev, u32 fid)
28e2341580SSasha Levin {
29e2341580SSasha Levin 	struct rb_node *node = dev->fids.rb_node;
30e2341580SSasha Levin 	struct p9_fid *pfid = NULL;
31e2341580SSasha Levin 
32e2341580SSasha Levin 	while (node) {
33e2341580SSasha Levin 		struct p9_fid *cur = rb_entry(node, struct p9_fid, node);
34e2341580SSasha Levin 
35e2341580SSasha Levin 		if (fid < cur->fid) {
36e2341580SSasha Levin 			node = node->rb_left;
37e2341580SSasha Levin 		} else if (fid > cur->fid) {
38e2341580SSasha Levin 			node = node->rb_right;
39e2341580SSasha Levin 		} else {
40e2341580SSasha Levin 			return cur;
41e2341580SSasha Levin 		}
42e2341580SSasha Levin 	}
43e2341580SSasha Levin 
44e2341580SSasha Levin 	pfid = calloc(sizeof(*pfid), 1);
45e2341580SSasha Levin 	if (!pfid)
46e2341580SSasha Levin 		return NULL;
47e2341580SSasha Levin 
48e2341580SSasha Levin 	pfid->fid = fid;
49e2341580SSasha Levin 	strcpy(pfid->abs_path, dev->root_dir);
50e2341580SSasha Levin 	pfid->path = pfid->abs_path + strlen(dev->root_dir);
51e2341580SSasha Levin 
52e2341580SSasha Levin 	insert_new_fid(dev, pfid);
53e2341580SSasha Levin 
54e2341580SSasha Levin 	return pfid;
55e2341580SSasha Levin }
56e2341580SSasha Levin 
57e2341580SSasha Levin static int insert_new_fid(struct p9_dev *dev, struct p9_fid *fid)
58e2341580SSasha Levin {
59e2341580SSasha Levin 	struct rb_node **node = &(dev->fids.rb_node), *parent = NULL;
60e2341580SSasha Levin 
61e2341580SSasha Levin 	while (*node) {
62e2341580SSasha Levin 		int result = fid->fid - rb_entry(*node, struct p9_fid, node)->fid;
63e2341580SSasha Levin 
64e2341580SSasha Levin 		parent = *node;
65e2341580SSasha Levin 		if (result < 0)
66e2341580SSasha Levin 			node    = &((*node)->rb_left);
67e2341580SSasha Levin 		else if (result > 0)
68e2341580SSasha Levin 			node    = &((*node)->rb_right);
69e2341580SSasha Levin 		else
70e2341580SSasha Levin 			return -EEXIST;
71e2341580SSasha Levin 	}
72e2341580SSasha Levin 
73e2341580SSasha Levin 	rb_link_node(&fid->node, parent, node);
74e2341580SSasha Levin 	rb_insert_color(&fid->node, &dev->fids);
75e2341580SSasha Levin 	return 0;
76e2341580SSasha Levin }
77e2341580SSasha Levin 
7831a6fb8dSSasha Levin static struct p9_fid *get_fid(struct p9_dev *p9dev, int fid)
7931a6fb8dSSasha Levin {
80e2341580SSasha Levin 	struct p9_fid *new;
8131a6fb8dSSasha Levin 
82e2341580SSasha Levin 	new = find_or_create_fid(p9dev, fid);
83e2341580SSasha Levin 
84e2341580SSasha Levin 	return new;
8531a6fb8dSSasha Levin }
8631a6fb8dSSasha Levin 
871c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
88b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
89b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
901c7850f9SSasha Levin {
91b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
921c7850f9SSasha Levin 
931c7850f9SSasha Levin 	return abs_path;
941c7850f9SSasha Levin }
951c7850f9SSasha Levin 
96c797b6c6SAneesh Kumar K.V static void stat2qid(struct stat *st, struct p9_qid *qid)
971c7850f9SSasha Levin {
981c7850f9SSasha Levin 	*qid = (struct p9_qid) {
991c7850f9SSasha Levin 		.path		= st->st_ino,
1001c7850f9SSasha Levin 		.version	= st->st_mtime,
1011c7850f9SSasha Levin 	};
1021c7850f9SSasha Levin 
1031c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
1041c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
1051c7850f9SSasha Levin }
1061c7850f9SSasha Levin 
107b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
1081c7850f9SSasha Levin {
109e2341580SSasha Levin 	struct p9_fid *pfid = get_fid(p9dev, fid);
110e2341580SSasha Levin 
111e2341580SSasha Levin 	if (pfid->fd > 0)
112e2341580SSasha Levin 		close(pfid->fd);
113e2341580SSasha Levin 
114e2341580SSasha Levin 	if (pfid->dir)
115e2341580SSasha Levin 		closedir(pfid->dir);
116e2341580SSasha Levin 
117e2341580SSasha Levin 	rb_erase(&pfid->node, &p9dev->fids);
118e2341580SSasha Levin 	free(pfid);
1191c7850f9SSasha Levin }
120e2341580SSasha Levin 
121bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size)
1221c7850f9SSasha Levin {
123bfc15268SAneesh Kumar K.V 	u8 cmd;
124bfc15268SAneesh Kumar K.V 	u16 tag;
125bfc15268SAneesh Kumar K.V 
126bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32);
127bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag);
128bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
129bfc15268SAneesh Kumar K.V 	/* cmd + 1 is the reply message */
130bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag);
1311c7850f9SSasha Levin }
1321c7850f9SSasha Levin 
1336b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
1346b163a87SAneesh Kumar K.V {
1356b163a87SAneesh Kumar K.V 	int i;
1366b163a87SAneesh Kumar K.V 	u32 total = 0;
1376b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
1386b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
1396b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
1406b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
1416b163a87SAneesh Kumar K.V 			i++;
1426b163a87SAneesh Kumar K.V 			break;
1436b163a87SAneesh Kumar K.V 		}
1446b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
1456b163a87SAneesh Kumar K.V 	}
1466b163a87SAneesh Kumar K.V 	return i;
1476b163a87SAneesh Kumar K.V }
1486b163a87SAneesh Kumar K.V 
149eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
150eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
151eee1ba8eSAneesh Kumar K.V {
152bfc15268SAneesh Kumar K.V 	u16 tag;
153eee1ba8eSAneesh Kumar K.V 
1549c2e1d1aSSuzuki K. Poulose 	/* EMFILE at server implies ENFILE for the VM */
1559c2e1d1aSSuzuki K. Poulose 	if (err == EMFILE)
1569c2e1d1aSSuzuki K. Poulose 		err = ENFILE;
1579c2e1d1aSSuzuki K. Poulose 
1585529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
159c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", err);
160bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
161eee1ba8eSAneesh Kumar K.V 
162c797b6c6SAneesh Kumar K.V 	/* read the tag from input */
163bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32) + sizeof(u8);
164bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "w", &tag);
165bfc15268SAneesh Kumar K.V 
166c797b6c6SAneesh Kumar K.V 	/* Update the header */
167bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
168c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RLERROR, tag);
169eee1ba8eSAneesh Kumar K.V }
170eee1ba8eSAneesh Kumar K.V 
171ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
172af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1731c7850f9SSasha Levin {
174c797b6c6SAneesh Kumar K.V 	u32 msize;
175c797b6c6SAneesh Kumar K.V 	char *version;
176c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ds", &msize, &version);
177c797b6c6SAneesh Kumar K.V 	/*
178c797b6c6SAneesh Kumar K.V 	 * reply with the same msize the client sent us
179c797b6c6SAneesh Kumar K.V 	 * Error out if the request is not for 9P2000.L
180c797b6c6SAneesh Kumar K.V 	 */
1815529bcd7SAsias He 	if (!strcmp(version, VIRTIO_9P_VERSION_DOTL))
182c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "ds", msize, version);
183c797b6c6SAneesh Kumar K.V 	else
184c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "ds", msize, "unknown");
1851c7850f9SSasha Levin 
186bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
187bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
188c797b6c6SAneesh Kumar K.V 	free(version);
189ead43b01SAneesh Kumar K.V 	return;
1901c7850f9SSasha Levin }
1911c7850f9SSasha Levin 
192ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
193af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
1941c7850f9SSasha Levin {
195bfc15268SAneesh Kumar K.V 	u32 fid;
1961c7850f9SSasha Levin 
197bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid);
198bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid);
1991c7850f9SSasha Levin 
200bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
201bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
202ead43b01SAneesh Kumar K.V 	return;
2031c7850f9SSasha Levin }
2041c7850f9SSasha Levin 
205c797b6c6SAneesh Kumar K.V /*
206c797b6c6SAneesh Kumar K.V  * FIXME!! Need to map to protocol independent value. Upstream
207c797b6c6SAneesh Kumar K.V  * 9p also have the same BUG
208c797b6c6SAneesh Kumar K.V  */
209c797b6c6SAneesh Kumar K.V static int virtio_p9_openflags(int flags)
210c797b6c6SAneesh Kumar K.V {
211c797b6c6SAneesh Kumar K.V 	flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT | O_DIRECT);
212c797b6c6SAneesh Kumar K.V 	flags |= O_NOFOLLOW;
213c797b6c6SAneesh Kumar K.V 	return flags;
214c797b6c6SAneesh Kumar K.V }
215c797b6c6SAneesh Kumar K.V 
21632585666SSasha Levin static bool is_dir(struct p9_fid *fid)
21732585666SSasha Levin {
21832585666SSasha Levin 	struct stat st;
21932585666SSasha Levin 
22032585666SSasha Levin 	stat(fid->abs_path, &st);
22132585666SSasha Levin 
22232585666SSasha Levin 	return S_ISDIR(st.st_mode);
22332585666SSasha Levin }
22432585666SSasha Levin 
225*9bb99a82SG. Campana /* path is always absolute */
226*9bb99a82SG. Campana static bool path_is_illegal(const char *path)
227*9bb99a82SG. Campana {
228*9bb99a82SG. Campana 	size_t len;
229*9bb99a82SG. Campana 
230*9bb99a82SG. Campana 	if (strstr(path, "/../") != NULL)
231*9bb99a82SG. Campana 		return true;
232*9bb99a82SG. Campana 
233*9bb99a82SG. Campana 	len = strlen(path);
234*9bb99a82SG. Campana 	if (len >= 3 && strcmp(path + len - 3, "/..") == 0)
235*9bb99a82SG. Campana 		return true;
236*9bb99a82SG. Campana 
237*9bb99a82SG. Campana 	return false;
238*9bb99a82SG. Campana }
239*9bb99a82SG. Campana 
240ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
241af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2421c7850f9SSasha Levin {
243c797b6c6SAneesh Kumar K.V 	u32 fid, flags;
2441c7850f9SSasha Levin 	struct stat st;
245bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
246bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
247bfc15268SAneesh Kumar K.V 
248c797b6c6SAneesh Kumar K.V 
249c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dd", &fid, &flags);
25031a6fb8dSSasha Levin 	new_fid = get_fid(p9dev, fid);
2511c7850f9SSasha Levin 
25230204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
253eee1ba8eSAneesh Kumar K.V 		goto err_out;
2541c7850f9SSasha Levin 
255c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
2561c7850f9SSasha Levin 
25732585666SSasha Levin 	if (is_dir(new_fid)) {
2581c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
259eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
260eee1ba8eSAneesh Kumar K.V 			goto err_out;
261eee1ba8eSAneesh Kumar K.V 	} else {
262eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
263c797b6c6SAneesh Kumar K.V 				    virtio_p9_openflags(flags));
264eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
265eee1ba8eSAneesh Kumar K.V 			goto err_out;
266eee1ba8eSAneesh Kumar K.V 	}
267c797b6c6SAneesh Kumar K.V 	/* FIXME!! need ot send proper iounit  */
268bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
269bfc15268SAneesh Kumar K.V 
270bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
271bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
272ead43b01SAneesh Kumar K.V 	return;
273eee1ba8eSAneesh Kumar K.V err_out:
274eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
275ead43b01SAneesh Kumar K.V 	return;
2761c7850f9SSasha Levin }
2771c7850f9SSasha Levin 
278ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
279af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
2801c7850f9SSasha Levin {
281c797b6c6SAneesh Kumar K.V 	int fd, ret;
282bfc15268SAneesh Kumar K.V 	char *name;
283af045e53SAneesh Kumar K.V 	struct stat st;
284bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
285c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
2864bc9734aSAneesh Kumar K.V 	char full_path[PATH_MAX];
287c797b6c6SAneesh Kumar K.V 	u32 dfid_val, flags, mode, gid;
288af045e53SAneesh Kumar K.V 
289c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsddd", &dfid_val,
290c797b6c6SAneesh Kumar K.V 			    &name, &flags, &mode, &gid);
29131a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
2921c7850f9SSasha Levin 
293c797b6c6SAneesh Kumar K.V 	flags = virtio_p9_openflags(flags);
2945f900f6dSSasha Levin 
295c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
296*9bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
297*9bb99a82SG. Campana 		errno = EACCES;
298*9bb99a82SG. Campana 		goto err_out;
299*9bb99a82SG. Campana 	}
300*9bb99a82SG. Campana 
301c797b6c6SAneesh Kumar K.V 	fd = open(full_path, flags | O_CREAT, mode);
3024bc9734aSAneesh Kumar K.V 	if (fd < 0)
3034bc9734aSAneesh Kumar K.V 		goto err_out;
304c797b6c6SAneesh Kumar K.V 	dfid->fd = fd;
305c797b6c6SAneesh Kumar K.V 
3064bc9734aSAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
3076c8ca053SAneesh Kumar K.V 		goto err_out;
3081c7850f9SSasha Levin 
309c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
310c797b6c6SAneesh Kumar K.V 	if (ret < 0)
311c797b6c6SAneesh Kumar K.V 		goto err_out;
312c797b6c6SAneesh Kumar K.V 
313c797b6c6SAneesh Kumar K.V 	sprintf(dfid->path, "%s/%s", dfid->path, name);
314c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
315bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
316bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
317bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3185f900f6dSSasha Levin 	free(name);
3196c8ca053SAneesh Kumar K.V 	return;
3206c8ca053SAneesh Kumar K.V err_out:
3215f900f6dSSasha Levin 	free(name);
322c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
323c797b6c6SAneesh Kumar K.V 	return;
324c797b6c6SAneesh Kumar K.V }
325c797b6c6SAneesh Kumar K.V 
326c797b6c6SAneesh Kumar K.V static void virtio_p9_mkdir(struct p9_dev *p9dev,
327c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
328c797b6c6SAneesh Kumar K.V {
329c797b6c6SAneesh Kumar K.V 	int ret;
330c797b6c6SAneesh Kumar K.V 	char *name;
331c797b6c6SAneesh Kumar K.V 	struct stat st;
332c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
333c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
334c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
335c797b6c6SAneesh Kumar K.V 	u32 dfid_val, mode, gid;
336c797b6c6SAneesh Kumar K.V 
337c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdd", &dfid_val,
338c797b6c6SAneesh Kumar K.V 			    &name, &mode, &gid);
33931a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
340c797b6c6SAneesh Kumar K.V 
341c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
342*9bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
343*9bb99a82SG. Campana 		errno = EACCES;
344*9bb99a82SG. Campana 		goto err_out;
345*9bb99a82SG. Campana 	}
346*9bb99a82SG. Campana 
347c797b6c6SAneesh Kumar K.V 	ret = mkdir(full_path, mode);
348c797b6c6SAneesh Kumar K.V 	if (ret < 0)
349c797b6c6SAneesh Kumar K.V 		goto err_out;
350c797b6c6SAneesh Kumar K.V 
351c797b6c6SAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
352c797b6c6SAneesh Kumar K.V 		goto err_out;
353c797b6c6SAneesh Kumar K.V 
354c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
355c797b6c6SAneesh Kumar K.V 	if (ret < 0)
356c797b6c6SAneesh Kumar K.V 		goto err_out;
357c797b6c6SAneesh Kumar K.V 
358c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
359c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
360c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
361c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
362c797b6c6SAneesh Kumar K.V 	free(name);
363c797b6c6SAneesh Kumar K.V 	return;
364c797b6c6SAneesh Kumar K.V err_out:
365c797b6c6SAneesh Kumar K.V 	free(name);
366c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
367ead43b01SAneesh Kumar K.V 	return;
3681c7850f9SSasha Levin }
3691c7850f9SSasha Levin 
370ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
371af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
3721c7850f9SSasha Levin {
373af045e53SAneesh Kumar K.V 	u8 i;
374bfc15268SAneesh Kumar K.V 	u16 nwqid;
375bfc15268SAneesh Kumar K.V 	u16 nwname;
376bfc15268SAneesh Kumar K.V 	struct p9_qid wqid;
377e2341580SSasha Levin 	struct p9_fid *new_fid, *old_fid;
378c797b6c6SAneesh Kumar K.V 	u32 fid_val, newfid_val;
379c797b6c6SAneesh Kumar K.V 
3801c7850f9SSasha Levin 
381bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname);
38231a6fb8dSSasha Levin 	new_fid	= get_fid(p9dev, newfid_val);
3831c7850f9SSasha Levin 
384bfc15268SAneesh Kumar K.V 	nwqid = 0;
385bfc15268SAneesh Kumar K.V 	if (nwname) {
38631a6fb8dSSasha Levin 		struct p9_fid *fid = get_fid(p9dev, fid_val);
387bfc15268SAneesh Kumar K.V 
388baac79a5SAneesh Kumar K.V 		strcpy(new_fid->path, fid->path);
389bfc15268SAneesh Kumar K.V 		/* skip the space for count */
390bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
391bfc15268SAneesh Kumar K.V 		for (i = 0; i < nwname; i++) {
392bfc15268SAneesh Kumar K.V 			struct stat st;
3931c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
3941c7850f9SSasha Levin 			char full_path[PATH_MAX];
395e55ed135SPekka Enberg 			char *str;
396bfc15268SAneesh Kumar K.V 
397bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_readf(pdu, "s", &str);
3981c7850f9SSasha Levin 
3991c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
400baac79a5SAneesh Kumar K.V 			sprintf(tmp, "%s/%s", new_fid->path, str);
401e55ed135SPekka Enberg 
402e55ed135SPekka Enberg 			free(str);
403e55ed135SPekka Enberg 
404c797b6c6SAneesh Kumar K.V 			if (lstat(rel_to_abs(p9dev, tmp, full_path), &st) < 0)
4056c8ca053SAneesh Kumar K.V 				goto err_out;
4061c7850f9SSasha Levin 
407c797b6c6SAneesh Kumar K.V 			stat2qid(&st, &wqid);
4081c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
409c797b6c6SAneesh Kumar K.V 			new_fid->uid = fid->uid;
410bfc15268SAneesh Kumar K.V 			nwqid++;
411bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "Q", &wqid);
4121c7850f9SSasha Levin 		}
4131c7850f9SSasha Levin 	} else {
414bfc15268SAneesh Kumar K.V 		/*
415bfc15268SAneesh Kumar K.V 		 * update write_offset so our outlen get correct value
416bfc15268SAneesh Kumar K.V 		 */
417bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
418e2341580SSasha Levin 		old_fid = get_fid(p9dev, fid_val);
419e2341580SSasha Levin 		strcpy(new_fid->path, old_fid->path);
420e2341580SSasha Levin 		new_fid->uid    = old_fid->uid;
4211c7850f9SSasha Levin 	}
422bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
4235529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
424bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", nwqid);
425bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4266c8ca053SAneesh Kumar K.V 	return;
4276c8ca053SAneesh Kumar K.V err_out:
4286c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
429ead43b01SAneesh Kumar K.V 	return;
4301c7850f9SSasha Levin }
4311c7850f9SSasha Levin 
432ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
433af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
4341c7850f9SSasha Levin {
435bfc15268SAneesh Kumar K.V 	char *uname;
436bfc15268SAneesh Kumar K.V 	char *aname;
4371c7850f9SSasha Levin 	struct stat st;
438bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
4391c7850f9SSasha Levin 	struct p9_fid *fid;
440c797b6c6SAneesh Kumar K.V 	u32 fid_val, afid, uid;
441bfc15268SAneesh Kumar K.V 
442c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddssd", &fid_val, &afid,
443c797b6c6SAneesh Kumar K.V 			    &uname, &aname, &uid);
4441c7850f9SSasha Levin 
44539257180SPekka Enberg 	free(uname);
44639257180SPekka Enberg 	free(aname);
44739257180SPekka Enberg 
44830204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
4496c8ca053SAneesh Kumar K.V 		goto err_out;
4501c7850f9SSasha Levin 
451c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
4521c7850f9SSasha Levin 
45331a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
454c797b6c6SAneesh Kumar K.V 	fid->uid = uid;
4551c7850f9SSasha Levin 	strcpy(fid->path, "/");
4561c7850f9SSasha Levin 
457bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
458bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
459bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4606c8ca053SAneesh Kumar K.V 	return;
4616c8ca053SAneesh Kumar K.V err_out:
4626c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
463ead43b01SAneesh Kumar K.V 	return;
4641c7850f9SSasha Levin }
4651c7850f9SSasha Levin 
466c797b6c6SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev,
467c797b6c6SAneesh Kumar K.V 				struct stat *st, struct p9_stat_dotl *statl)
4685f900f6dSSasha Levin {
469c797b6c6SAneesh Kumar K.V 	memset(statl, 0, sizeof(*statl));
470c797b6c6SAneesh Kumar K.V 	statl->st_mode		= st->st_mode;
471c797b6c6SAneesh Kumar K.V 	statl->st_nlink		= st->st_nlink;
472506fd90bSSasha Levin 	statl->st_uid		= KUIDT_INIT(st->st_uid);
473506fd90bSSasha Levin 	statl->st_gid		= KGIDT_INIT(st->st_gid);
474c797b6c6SAneesh Kumar K.V 	statl->st_rdev		= st->st_rdev;
475c797b6c6SAneesh Kumar K.V 	statl->st_size		= st->st_size;
476c797b6c6SAneesh Kumar K.V 	statl->st_blksize	= st->st_blksize;
477c797b6c6SAneesh Kumar K.V 	statl->st_blocks	= st->st_blocks;
478c797b6c6SAneesh Kumar K.V 	statl->st_atime_sec	= st->st_atime;
479c797b6c6SAneesh Kumar K.V 	statl->st_atime_nsec	= st->st_atim.tv_nsec;
480c797b6c6SAneesh Kumar K.V 	statl->st_mtime_sec	= st->st_mtime;
481c797b6c6SAneesh Kumar K.V 	statl->st_mtime_nsec	= st->st_mtim.tv_nsec;
482c797b6c6SAneesh Kumar K.V 	statl->st_ctime_sec	= st->st_ctime;
483c797b6c6SAneesh Kumar K.V 	statl->st_ctime_nsec	= st->st_ctim.tv_nsec;
484c797b6c6SAneesh Kumar K.V 	/* Currently we only support BASIC fields in stat */
485c797b6c6SAneesh Kumar K.V 	statl->st_result_mask	= P9_STATS_BASIC;
486c797b6c6SAneesh Kumar K.V 	stat2qid(st, &statl->qid);
4871c7850f9SSasha Levin }
4881c7850f9SSasha Levin 
489ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
490af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4911c7850f9SSasha Levin {
492bfc15268SAneesh Kumar K.V 	u64 offset;
493bfc15268SAneesh Kumar K.V 	u32 fid_val;
494c797b6c6SAneesh Kumar K.V 	u16 iov_cnt;
495c797b6c6SAneesh Kumar K.V 	void *iov_base;
496c797b6c6SAneesh Kumar K.V 	size_t iov_len;
497bfc15268SAneesh Kumar K.V 	u32 count, rcount;
498bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
499c797b6c6SAneesh Kumar K.V 
5001c7850f9SSasha Levin 
501bfc15268SAneesh Kumar K.V 	rcount = 0;
502bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
50331a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
50450c479e0SAneesh Kumar K.V 
50550c479e0SAneesh Kumar K.V 	iov_base = pdu->in_iov[0].iov_base;
50650c479e0SAneesh Kumar K.V 	iov_len  = pdu->in_iov[0].iov_len;
50750c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->in_iov_cnt;
5085529bcd7SAsias He 	pdu->in_iov[0].iov_base += VIRTIO_9P_HDR_LEN + sizeof(u32);
5095529bcd7SAsias He 	pdu->in_iov[0].iov_len -= VIRTIO_9P_HDR_LEN + sizeof(u32);
5106b163a87SAneesh Kumar K.V 	pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
511bfc15268SAneesh Kumar K.V 						   count,
5126b163a87SAneesh Kumar K.V 						   pdu->in_iov_cnt);
513bfc15268SAneesh Kumar K.V 	rcount = preadv(fid->fd, pdu->in_iov,
514bfc15268SAneesh Kumar K.V 			pdu->in_iov_cnt, offset);
515bfc15268SAneesh Kumar K.V 	if (rcount > count)
516bfc15268SAneesh Kumar K.V 		rcount = count;
517bfc15268SAneesh Kumar K.V 	/*
518bfc15268SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
519bfc15268SAneesh Kumar K.V 	 * pdu_writef works correctly.
520bfc15268SAneesh Kumar K.V 	 */
52150c479e0SAneesh Kumar K.V 	pdu->in_iov[0].iov_base = iov_base;
52250c479e0SAneesh Kumar K.V 	pdu->in_iov[0].iov_len  = iov_len;
52350c479e0SAneesh Kumar K.V 	pdu->in_iov_cnt         = iov_cnt;
524c797b6c6SAneesh Kumar K.V 
5255529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
526bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
527bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
528bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
529ead43b01SAneesh Kumar K.V 	return;
5301c7850f9SSasha Levin }
5311c7850f9SSasha Levin 
532c797b6c6SAneesh Kumar K.V static int virtio_p9_dentry_size(struct dirent *dent)
533c797b6c6SAneesh Kumar K.V {
534c797b6c6SAneesh Kumar K.V 	/*
535c797b6c6SAneesh Kumar K.V 	 * Size of each dirent:
536c797b6c6SAneesh Kumar K.V 	 * qid(13) + offset(8) + type(1) + name_len(2) + name
537c797b6c6SAneesh Kumar K.V 	 */
538c797b6c6SAneesh Kumar K.V 	return 24 + strlen(dent->d_name);
539c797b6c6SAneesh Kumar K.V }
540c797b6c6SAneesh Kumar K.V 
541c797b6c6SAneesh Kumar K.V static void virtio_p9_readdir(struct p9_dev *p9dev,
542c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
543c797b6c6SAneesh Kumar K.V {
544c797b6c6SAneesh Kumar K.V 	u32 fid_val;
545c797b6c6SAneesh Kumar K.V 	u32 count, rcount;
546c797b6c6SAneesh Kumar K.V 	struct stat st;
547c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
548c797b6c6SAneesh Kumar K.V 	struct dirent *dent;
549c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
550c797b6c6SAneesh Kumar K.V 	u64 offset, old_offset;
551c797b6c6SAneesh Kumar K.V 
552c797b6c6SAneesh Kumar K.V 	rcount = 0;
553c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
55431a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
555c797b6c6SAneesh Kumar K.V 
55632585666SSasha Levin 	if (!is_dir(fid)) {
55769bb4278SSasha Levin 		errno = EINVAL;
558c797b6c6SAneesh Kumar K.V 		goto err_out;
559c797b6c6SAneesh Kumar K.V 	}
560c797b6c6SAneesh Kumar K.V 
561c797b6c6SAneesh Kumar K.V 	/* Move the offset specified */
562c797b6c6SAneesh Kumar K.V 	seekdir(fid->dir, offset);
563c797b6c6SAneesh Kumar K.V 
564c797b6c6SAneesh Kumar K.V 	old_offset = offset;
565c797b6c6SAneesh Kumar K.V 	/* If reading a dir, fill the buffer with p9_stat entries */
566c797b6c6SAneesh Kumar K.V 	dent = readdir(fid->dir);
567c797b6c6SAneesh Kumar K.V 
568c797b6c6SAneesh Kumar K.V 	/* Skip the space for writing count */
569c797b6c6SAneesh Kumar K.V 	pdu->write_offset += sizeof(u32);
570c797b6c6SAneesh Kumar K.V 	while (dent) {
571c797b6c6SAneesh Kumar K.V 		u32 read;
572c797b6c6SAneesh Kumar K.V 		struct p9_qid qid;
573c797b6c6SAneesh Kumar K.V 
574c797b6c6SAneesh Kumar K.V 		if ((rcount + virtio_p9_dentry_size(dent)) > count) {
575c797b6c6SAneesh Kumar K.V 			/* seek to the previous offset and return */
576c797b6c6SAneesh Kumar K.V 			seekdir(fid->dir, old_offset);
577c797b6c6SAneesh Kumar K.V 			break;
578c797b6c6SAneesh Kumar K.V 		}
579c797b6c6SAneesh Kumar K.V 		old_offset = dent->d_off;
580c797b6c6SAneesh Kumar K.V 		lstat(rel_to_abs(p9dev, dent->d_name, full_path), &st);
581c797b6c6SAneesh Kumar K.V 		stat2qid(&st, &qid);
582c797b6c6SAneesh Kumar K.V 		read = pdu->write_offset;
583c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "Qqbs", &qid, dent->d_off,
584c797b6c6SAneesh Kumar K.V 				     dent->d_type, dent->d_name);
585c797b6c6SAneesh Kumar K.V 		rcount += pdu->write_offset - read;
586c797b6c6SAneesh Kumar K.V 		dent = readdir(fid->dir);
587c797b6c6SAneesh Kumar K.V 	}
588c797b6c6SAneesh Kumar K.V 
5895529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
590c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
591c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
592c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
593c797b6c6SAneesh Kumar K.V 	return;
594c797b6c6SAneesh Kumar K.V err_out:
595c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
596c797b6c6SAneesh Kumar K.V 	return;
597c797b6c6SAneesh Kumar K.V }
598c797b6c6SAneesh Kumar K.V 
599c797b6c6SAneesh Kumar K.V 
600c797b6c6SAneesh Kumar K.V static void virtio_p9_getattr(struct p9_dev *p9dev,
601af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
6021c7850f9SSasha Levin {
603bfc15268SAneesh Kumar K.V 	u32 fid_val;
604af045e53SAneesh Kumar K.V 	struct stat st;
605c797b6c6SAneesh Kumar K.V 	u64 request_mask;
606bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
607c797b6c6SAneesh Kumar K.V 	struct p9_stat_dotl statl;
6081c7850f9SSasha Levin 
609c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dq", &fid_val, &request_mask);
61031a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
61130204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
6126c8ca053SAneesh Kumar K.V 		goto err_out;
6131c7850f9SSasha Levin 
614c797b6c6SAneesh Kumar K.V 	virtio_p9_fill_stat(p9dev, &st, &statl);
615c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "A", &statl);
616bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
617bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
618ead43b01SAneesh Kumar K.V 	return;
6196c8ca053SAneesh Kumar K.V err_out:
6206c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
6216c8ca053SAneesh Kumar K.V 	return;
6221c7850f9SSasha Levin }
6231c7850f9SSasha Levin 
624c797b6c6SAneesh Kumar K.V /* FIXME!! from linux/fs.h */
625c797b6c6SAneesh Kumar K.V /*
626c797b6c6SAneesh Kumar K.V  * Attribute flags.  These should be or-ed together to figure out what
627c797b6c6SAneesh Kumar K.V  * has been changed!
628c797b6c6SAneesh Kumar K.V  */
629c797b6c6SAneesh Kumar K.V #define ATTR_MODE	(1 << 0)
630c797b6c6SAneesh Kumar K.V #define ATTR_UID	(1 << 1)
631c797b6c6SAneesh Kumar K.V #define ATTR_GID	(1 << 2)
632c797b6c6SAneesh Kumar K.V #define ATTR_SIZE	(1 << 3)
633c797b6c6SAneesh Kumar K.V #define ATTR_ATIME	(1 << 4)
634c797b6c6SAneesh Kumar K.V #define ATTR_MTIME	(1 << 5)
635c797b6c6SAneesh Kumar K.V #define ATTR_CTIME	(1 << 6)
636c797b6c6SAneesh Kumar K.V #define ATTR_ATIME_SET	(1 << 7)
637c797b6c6SAneesh Kumar K.V #define ATTR_MTIME_SET	(1 << 8)
638c797b6c6SAneesh Kumar K.V #define ATTR_FORCE	(1 << 9) /* Not a change, but a change it */
639c797b6c6SAneesh Kumar K.V #define ATTR_ATTR_FLAG	(1 << 10)
640c797b6c6SAneesh Kumar K.V #define ATTR_KILL_SUID	(1 << 11)
641c797b6c6SAneesh Kumar K.V #define ATTR_KILL_SGID	(1 << 12)
642c797b6c6SAneesh Kumar K.V #define ATTR_FILE	(1 << 13)
643c797b6c6SAneesh Kumar K.V #define ATTR_KILL_PRIV	(1 << 14)
644c797b6c6SAneesh Kumar K.V #define ATTR_OPEN	(1 << 15) /* Truncating from open(O_TRUNC) */
645c797b6c6SAneesh Kumar K.V #define ATTR_TIMES_SET	(1 << 16)
646c797b6c6SAneesh Kumar K.V 
647c797b6c6SAneesh Kumar K.V #define ATTR_MASK    127
648c797b6c6SAneesh Kumar K.V 
649c797b6c6SAneesh Kumar K.V static void virtio_p9_setattr(struct p9_dev *p9dev,
650af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
6511c7850f9SSasha Levin {
652c797b6c6SAneesh Kumar K.V 	int ret = 0;
653bfc15268SAneesh Kumar K.V 	u32 fid_val;
654bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
655c797b6c6SAneesh Kumar K.V 	struct p9_iattr_dotl p9attr;
6561c7850f9SSasha Levin 
657c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dI", &fid_val, &p9attr);
65831a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
6591c7850f9SSasha Levin 
660c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & ATTR_MODE) {
661c797b6c6SAneesh Kumar K.V 		ret = chmod(fid->abs_path, p9attr.mode);
662c797b6c6SAneesh Kumar K.V 		if (ret < 0)
663c797b6c6SAneesh Kumar K.V 			goto err_out;
664c797b6c6SAneesh Kumar K.V 	}
665c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & (ATTR_ATIME | ATTR_MTIME)) {
666c797b6c6SAneesh Kumar K.V 		struct timespec times[2];
667c797b6c6SAneesh Kumar K.V 		if (p9attr.valid & ATTR_ATIME) {
668c797b6c6SAneesh Kumar K.V 			if (p9attr.valid & ATTR_ATIME_SET) {
669c797b6c6SAneesh Kumar K.V 				times[0].tv_sec = p9attr.atime_sec;
670c797b6c6SAneesh Kumar K.V 				times[0].tv_nsec = p9attr.atime_nsec;
671c797b6c6SAneesh Kumar K.V 			} else {
672c797b6c6SAneesh Kumar K.V 				times[0].tv_nsec = UTIME_NOW;
673c797b6c6SAneesh Kumar K.V 			}
674c797b6c6SAneesh Kumar K.V 		} else {
675c797b6c6SAneesh Kumar K.V 			times[0].tv_nsec = UTIME_OMIT;
676c797b6c6SAneesh Kumar K.V 		}
677c797b6c6SAneesh Kumar K.V 		if (p9attr.valid & ATTR_MTIME) {
678c797b6c6SAneesh Kumar K.V 			if (p9attr.valid & ATTR_MTIME_SET) {
679c797b6c6SAneesh Kumar K.V 				times[1].tv_sec = p9attr.mtime_sec;
680c797b6c6SAneesh Kumar K.V 				times[1].tv_nsec = p9attr.mtime_nsec;
681c797b6c6SAneesh Kumar K.V 			} else {
682c797b6c6SAneesh Kumar K.V 				times[1].tv_nsec = UTIME_NOW;
683c797b6c6SAneesh Kumar K.V 			}
684c797b6c6SAneesh Kumar K.V 		} else
685c797b6c6SAneesh Kumar K.V 			times[1].tv_nsec = UTIME_OMIT;
686c797b6c6SAneesh Kumar K.V 
687c797b6c6SAneesh Kumar K.V 		ret = utimensat(-1, fid->abs_path, times, AT_SYMLINK_NOFOLLOW);
688c797b6c6SAneesh Kumar K.V 		if (ret < 0)
689c797b6c6SAneesh Kumar K.V 			goto err_out;
690c797b6c6SAneesh Kumar K.V 	}
691c797b6c6SAneesh Kumar K.V 	/*
692c797b6c6SAneesh Kumar K.V 	 * If the only valid entry in iattr is ctime we can call
693c797b6c6SAneesh Kumar K.V 	 * chown(-1,-1) to update the ctime of the file
694c797b6c6SAneesh Kumar K.V 	 */
695c797b6c6SAneesh Kumar K.V 	if ((p9attr.valid & (ATTR_UID | ATTR_GID)) ||
696c797b6c6SAneesh Kumar K.V 	    ((p9attr.valid & ATTR_CTIME)
697c797b6c6SAneesh Kumar K.V 	     && !((p9attr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
698c797b6c6SAneesh Kumar K.V 		if (!(p9attr.valid & ATTR_UID))
699506fd90bSSasha Levin 			p9attr.uid = KUIDT_INIT(-1);
700c797b6c6SAneesh Kumar K.V 
701c797b6c6SAneesh Kumar K.V 		if (!(p9attr.valid & ATTR_GID))
702506fd90bSSasha Levin 			p9attr.gid = KGIDT_INIT(-1);
703c797b6c6SAneesh Kumar K.V 
704506fd90bSSasha Levin 		ret = lchown(fid->abs_path, __kuid_val(p9attr.uid),
705506fd90bSSasha Levin 				__kgid_val(p9attr.gid));
706c797b6c6SAneesh Kumar K.V 		if (ret < 0)
707c797b6c6SAneesh Kumar K.V 			goto err_out;
708c797b6c6SAneesh Kumar K.V 	}
709c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & (ATTR_SIZE)) {
710c797b6c6SAneesh Kumar K.V 		ret = truncate(fid->abs_path, p9attr.size);
711c797b6c6SAneesh Kumar K.V 		if (ret < 0)
712c797b6c6SAneesh Kumar K.V 			goto err_out;
713c797b6c6SAneesh Kumar K.V 	}
7145529bcd7SAsias He 	*outlen = VIRTIO_9P_HDR_LEN;
715bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
716ead43b01SAneesh Kumar K.V 	return;
7174bc9734aSAneesh Kumar K.V err_out:
7184bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7194bc9734aSAneesh Kumar K.V 	return;
7201c7850f9SSasha Levin }
7211c7850f9SSasha Levin 
722ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
723af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
7241c7850f9SSasha Levin {
7254bc9734aSAneesh Kumar K.V 
726bfc15268SAneesh Kumar K.V 	u64 offset;
727bfc15268SAneesh Kumar K.V 	u32 fid_val;
7284bc9734aSAneesh Kumar K.V 	u32 count;
7294bc9734aSAneesh Kumar K.V 	ssize_t res;
73050c479e0SAneesh Kumar K.V 	u16 iov_cnt;
73150c479e0SAneesh Kumar K.V 	void *iov_base;
73250c479e0SAneesh Kumar K.V 	size_t iov_len;
733bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
734b064b05aSAneesh Kumar K.V 	/* u32 fid + u64 offset + u32 count */
735b064b05aSAneesh Kumar K.V 	int twrite_size = sizeof(u32) + sizeof(u64) + sizeof(u32);
7361c7850f9SSasha Levin 
737bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
73831a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
739af045e53SAneesh Kumar K.V 
74050c479e0SAneesh Kumar K.V 	iov_base = pdu->out_iov[0].iov_base;
74150c479e0SAneesh Kumar K.V 	iov_len  = pdu->out_iov[0].iov_len;
74250c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->out_iov_cnt;
74350c479e0SAneesh Kumar K.V 
744bfc15268SAneesh Kumar K.V 	/* Adjust the iovec to skip the header and meta data */
745b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) + twrite_size);
746b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_len -=  (sizeof(struct p9_msg) + twrite_size);
747bfc15268SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count,
7486b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
7494bc9734aSAneesh Kumar K.V 	res = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset);
75050c479e0SAneesh Kumar K.V 	/*
75150c479e0SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
75250c479e0SAneesh Kumar K.V 	 * pdu_readf works correctly.
75350c479e0SAneesh Kumar K.V 	 */
75450c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_base = iov_base;
75550c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_len  = iov_len;
75650c479e0SAneesh Kumar K.V 	pdu->out_iov_cnt         = iov_cnt;
757c797b6c6SAneesh Kumar K.V 
7584bc9734aSAneesh Kumar K.V 	if (res < 0)
7594bc9734aSAneesh Kumar K.V 		goto err_out;
7604bc9734aSAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", res);
761bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
762bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
763ead43b01SAneesh Kumar K.V 	return;
7644bc9734aSAneesh Kumar K.V err_out:
7654bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7664bc9734aSAneesh Kumar K.V 	return;
7671c7850f9SSasha Levin }
7681c7850f9SSasha Levin 
7696fc5cd9bSSasha Levin static void virtio_p9_remove(struct p9_dev *p9dev,
7706fc5cd9bSSasha Levin 			     struct p9_pdu *pdu, u32 *outlen)
7716fc5cd9bSSasha Levin {
7726fc5cd9bSSasha Levin 	int ret;
7736fc5cd9bSSasha Levin 	u32 fid_val;
7746fc5cd9bSSasha Levin 	struct p9_fid *fid;
7756fc5cd9bSSasha Levin 
7766fc5cd9bSSasha Levin 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
77731a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
7786fc5cd9bSSasha Levin 
7799b604a9cSSasha Levin 	ret = remove(fid->abs_path);
7806fc5cd9bSSasha Levin 	if (ret < 0)
7816fc5cd9bSSasha Levin 		goto err_out;
7826fc5cd9bSSasha Levin 	*outlen = pdu->write_offset;
7836fc5cd9bSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
7846fc5cd9bSSasha Levin 	return;
7856fc5cd9bSSasha Levin 
7866fc5cd9bSSasha Levin err_out:
7876fc5cd9bSSasha Levin 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7886fc5cd9bSSasha Levin 	return;
7896fc5cd9bSSasha Levin }
7906fc5cd9bSSasha Levin 
791f161f28bSSasha Levin static void virtio_p9_rename(struct p9_dev *p9dev,
792f161f28bSSasha Levin 			     struct p9_pdu *pdu, u32 *outlen)
793f161f28bSSasha Levin {
794f161f28bSSasha Levin 	int ret;
795f161f28bSSasha Levin 	u32 fid_val, new_fid_val;
796f161f28bSSasha Levin 	struct p9_fid *fid, *new_fid;
797f161f28bSSasha Levin 	char full_path[PATH_MAX], *new_name;
798f161f28bSSasha Levin 
799f161f28bSSasha Levin 	virtio_p9_pdu_readf(pdu, "dds", &fid_val, &new_fid_val, &new_name);
80031a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
80131a6fb8dSSasha Levin 	new_fid = get_fid(p9dev, new_fid_val);
802f161f28bSSasha Levin 
803f161f28bSSasha Levin 	sprintf(full_path, "%s/%s", new_fid->abs_path, new_name);
804*9bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
805*9bb99a82SG. Campana 		errno = EACCES;
806*9bb99a82SG. Campana 		goto err_out;
807*9bb99a82SG. Campana 	}
808*9bb99a82SG. Campana 
809f161f28bSSasha Levin 	ret = rename(fid->abs_path, full_path);
810f161f28bSSasha Levin 	if (ret < 0)
811f161f28bSSasha Levin 		goto err_out;
812f161f28bSSasha Levin 	*outlen = pdu->write_offset;
813f161f28bSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
814f161f28bSSasha Levin 	return;
815f161f28bSSasha Levin 
816f161f28bSSasha Levin err_out:
817f161f28bSSasha Levin 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
818f161f28bSSasha Levin 	return;
819f161f28bSSasha Levin }
820f161f28bSSasha Levin 
821c797b6c6SAneesh Kumar K.V static void virtio_p9_readlink(struct p9_dev *p9dev,
822c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
823c797b6c6SAneesh Kumar K.V {
824c797b6c6SAneesh Kumar K.V 	int ret;
825c797b6c6SAneesh Kumar K.V 	u32 fid_val;
826c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
827c797b6c6SAneesh Kumar K.V 	char target_path[PATH_MAX];
828c797b6c6SAneesh Kumar K.V 
829c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
83031a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
831c797b6c6SAneesh Kumar K.V 
832c797b6c6SAneesh Kumar K.V 	memset(target_path, 0, PATH_MAX);
833c797b6c6SAneesh Kumar K.V 	ret = readlink(fid->abs_path, target_path, PATH_MAX - 1);
834c797b6c6SAneesh Kumar K.V 	if (ret < 0)
835c797b6c6SAneesh Kumar K.V 		goto err_out;
836c797b6c6SAneesh Kumar K.V 
837c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "s", target_path);
838c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
839c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
840c797b6c6SAneesh Kumar K.V 	return;
841c797b6c6SAneesh Kumar K.V err_out:
842c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
843c797b6c6SAneesh Kumar K.V 	return;
844c797b6c6SAneesh Kumar K.V }
845c797b6c6SAneesh Kumar K.V 
846c797b6c6SAneesh Kumar K.V static void virtio_p9_statfs(struct p9_dev *p9dev,
847c797b6c6SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
848c797b6c6SAneesh Kumar K.V {
849c797b6c6SAneesh Kumar K.V 	int ret;
850c797b6c6SAneesh Kumar K.V 	u64 fsid;
851c797b6c6SAneesh Kumar K.V 	u32 fid_val;
852c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
853c797b6c6SAneesh Kumar K.V 	struct statfs stat_buf;
854c797b6c6SAneesh Kumar K.V 
855c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
85631a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
857c797b6c6SAneesh Kumar K.V 
858c797b6c6SAneesh Kumar K.V 	ret = statfs(fid->abs_path, &stat_buf);
859c797b6c6SAneesh Kumar K.V 	if (ret < 0)
860c797b6c6SAneesh Kumar K.V 		goto err_out;
861c797b6c6SAneesh Kumar K.V 	/* FIXME!! f_blocks needs update based on client msize */
862c797b6c6SAneesh Kumar K.V 	fsid = (unsigned int) stat_buf.f_fsid.__val[0] |
863c797b6c6SAneesh Kumar K.V 		(unsigned long long)stat_buf.f_fsid.__val[1] << 32;
864c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "ddqqqqqqd", stat_buf.f_type,
865c797b6c6SAneesh Kumar K.V 			     stat_buf.f_bsize, stat_buf.f_blocks,
866c797b6c6SAneesh Kumar K.V 			     stat_buf.f_bfree, stat_buf.f_bavail,
867c797b6c6SAneesh Kumar K.V 			     stat_buf.f_files, stat_buf.f_ffree,
868c797b6c6SAneesh Kumar K.V 			     fsid, stat_buf.f_namelen);
869c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
870c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
871c797b6c6SAneesh Kumar K.V 	return;
872c797b6c6SAneesh Kumar K.V err_out:
873c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
874c797b6c6SAneesh Kumar K.V 	return;
875c797b6c6SAneesh Kumar K.V }
876c797b6c6SAneesh Kumar K.V 
877c797b6c6SAneesh Kumar K.V static void virtio_p9_mknod(struct p9_dev *p9dev,
878c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
879c797b6c6SAneesh Kumar K.V {
880c797b6c6SAneesh Kumar K.V 	int ret;
881c797b6c6SAneesh Kumar K.V 	char *name;
882c797b6c6SAneesh Kumar K.V 	struct stat st;
883c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
884c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
885c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
886c797b6c6SAneesh Kumar K.V 	u32 fid_val, mode, major, minor, gid;
887c797b6c6SAneesh Kumar K.V 
888c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdddd", &fid_val, &name, &mode,
889c797b6c6SAneesh Kumar K.V 			    &major, &minor, &gid);
890c797b6c6SAneesh Kumar K.V 
89131a6fb8dSSasha Levin 	dfid = get_fid(p9dev, fid_val);
892c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
893*9bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
894*9bb99a82SG. Campana 		errno = EACCES;
895*9bb99a82SG. Campana 		goto err_out;
896*9bb99a82SG. Campana 	}
897*9bb99a82SG. Campana 
898c797b6c6SAneesh Kumar K.V 	ret = mknod(full_path, mode, makedev(major, minor));
899c797b6c6SAneesh Kumar K.V 	if (ret < 0)
900c797b6c6SAneesh Kumar K.V 		goto err_out;
901c797b6c6SAneesh Kumar K.V 
902c797b6c6SAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
903c797b6c6SAneesh Kumar K.V 		goto err_out;
904c797b6c6SAneesh Kumar K.V 
905c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
906c797b6c6SAneesh Kumar K.V 	if (ret < 0)
907c797b6c6SAneesh Kumar K.V 		goto err_out;
908c797b6c6SAneesh Kumar K.V 
909c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
910c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
911c797b6c6SAneesh Kumar K.V 	free(name);
912c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
913c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
914c797b6c6SAneesh Kumar K.V 	return;
915c797b6c6SAneesh Kumar K.V err_out:
916c797b6c6SAneesh Kumar K.V 	free(name);
917c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
918c797b6c6SAneesh Kumar K.V 	return;
919c797b6c6SAneesh Kumar K.V }
920c797b6c6SAneesh Kumar K.V 
921c797b6c6SAneesh Kumar K.V static void virtio_p9_fsync(struct p9_dev *p9dev,
922c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
923c797b6c6SAneesh Kumar K.V {
924644140efSRussell King 	int ret, fd;
925c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
926c797b6c6SAneesh Kumar K.V 	u32 fid_val, datasync;
927c797b6c6SAneesh Kumar K.V 
928c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dd", &fid_val, &datasync);
92931a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
930c797b6c6SAneesh Kumar K.V 
931644140efSRussell King 	if (fid->dir)
932644140efSRussell King 		fd = dirfd(fid->dir);
933c797b6c6SAneesh Kumar K.V 	else
934644140efSRussell King 		fd = fid->fd;
935644140efSRussell King 
936644140efSRussell King 	if (datasync)
937644140efSRussell King 		ret = fdatasync(fd);
938644140efSRussell King 	else
939644140efSRussell King 		ret = fsync(fd);
940c797b6c6SAneesh Kumar K.V 	if (ret < 0)
941c797b6c6SAneesh Kumar K.V 		goto err_out;
942c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
943c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
944c797b6c6SAneesh Kumar K.V 	return;
945c797b6c6SAneesh Kumar K.V err_out:
946c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
947c797b6c6SAneesh Kumar K.V 	return;
948c797b6c6SAneesh Kumar K.V }
949c797b6c6SAneesh Kumar K.V 
950c797b6c6SAneesh Kumar K.V static void virtio_p9_symlink(struct p9_dev *p9dev,
951c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
952c797b6c6SAneesh Kumar K.V {
953c797b6c6SAneesh Kumar K.V 	int ret;
954c797b6c6SAneesh Kumar K.V 	struct stat st;
955c797b6c6SAneesh Kumar K.V 	u32 fid_val, gid;
956c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
957c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
958c797b6c6SAneesh Kumar K.V 	char new_name[PATH_MAX];
959c797b6c6SAneesh Kumar K.V 	char *old_path, *name;
960c797b6c6SAneesh Kumar K.V 
961c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dssd", &fid_val, &name, &old_path, &gid);
962c797b6c6SAneesh Kumar K.V 
96331a6fb8dSSasha Levin 	dfid = get_fid(p9dev, fid_val);
964c797b6c6SAneesh Kumar K.V 	sprintf(new_name, "%s/%s", dfid->abs_path, name);
965*9bb99a82SG. Campana 	if (path_is_illegal(new_name)) {
966*9bb99a82SG. Campana 		errno = EACCES;
967*9bb99a82SG. Campana 		goto err_out;
968*9bb99a82SG. Campana 	}
969*9bb99a82SG. Campana 
970c797b6c6SAneesh Kumar K.V 	ret = symlink(old_path, new_name);
971c797b6c6SAneesh Kumar K.V 	if (ret < 0)
972c797b6c6SAneesh Kumar K.V 		goto err_out;
973c797b6c6SAneesh Kumar K.V 
974c797b6c6SAneesh Kumar K.V 	if (lstat(new_name, &st) < 0)
975c797b6c6SAneesh Kumar K.V 		goto err_out;
976c797b6c6SAneesh Kumar K.V 
977c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
978c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
979c797b6c6SAneesh Kumar K.V 	free(name);
980c797b6c6SAneesh Kumar K.V 	free(old_path);
981c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
982c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
983c797b6c6SAneesh Kumar K.V 	return;
984c797b6c6SAneesh Kumar K.V err_out:
985c797b6c6SAneesh Kumar K.V 	free(name);
986c797b6c6SAneesh Kumar K.V 	free(old_path);
987c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
988c797b6c6SAneesh Kumar K.V 	return;
989c797b6c6SAneesh Kumar K.V }
990c797b6c6SAneesh Kumar K.V 
991c797b6c6SAneesh Kumar K.V static void virtio_p9_link(struct p9_dev *p9dev,
992c797b6c6SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
993c797b6c6SAneesh Kumar K.V {
994c797b6c6SAneesh Kumar K.V 	int ret;
995c797b6c6SAneesh Kumar K.V 	char *name;
996c797b6c6SAneesh Kumar K.V 	u32 fid_val, dfid_val;
997c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid, *fid;
998c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
999c797b6c6SAneesh Kumar K.V 
1000c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dds", &dfid_val, &fid_val, &name);
1001c797b6c6SAneesh Kumar K.V 
100231a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
100331a6fb8dSSasha Levin 	fid =  get_fid(p9dev, fid_val);
1004c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", dfid->abs_path, name);
1005*9bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
1006*9bb99a82SG. Campana 		errno = EACCES;
1007*9bb99a82SG. Campana 		goto err_out;
1008*9bb99a82SG. Campana 	}
1009*9bb99a82SG. Campana 
1010c797b6c6SAneesh Kumar K.V 	ret = link(fid->abs_path, full_path);
1011c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1012c797b6c6SAneesh Kumar K.V 		goto err_out;
1013c797b6c6SAneesh Kumar K.V 	free(name);
1014c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1015c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1016c797b6c6SAneesh Kumar K.V 	return;
1017c797b6c6SAneesh Kumar K.V err_out:
1018c797b6c6SAneesh Kumar K.V 	free(name);
1019c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1020c797b6c6SAneesh Kumar K.V 	return;
1021c797b6c6SAneesh Kumar K.V 
1022c797b6c6SAneesh Kumar K.V }
1023c797b6c6SAneesh Kumar K.V 
1024c797b6c6SAneesh Kumar K.V static void virtio_p9_lock(struct p9_dev *p9dev,
1025c797b6c6SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
1026c797b6c6SAneesh Kumar K.V {
1027c797b6c6SAneesh Kumar K.V 	u8 ret;
1028c797b6c6SAneesh Kumar K.V 	u32 fid_val;
1029c797b6c6SAneesh Kumar K.V 	struct p9_flock flock;
1030c797b6c6SAneesh Kumar K.V 
1031c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dbdqqds", &fid_val, &flock.type,
1032c797b6c6SAneesh Kumar K.V 			    &flock.flags, &flock.start, &flock.length,
1033c797b6c6SAneesh Kumar K.V 			    &flock.proc_id, &flock.client_id);
1034c797b6c6SAneesh Kumar K.V 
1035c797b6c6SAneesh Kumar K.V 	/* Just return success */
1036c797b6c6SAneesh Kumar K.V 	ret = P9_LOCK_SUCCESS;
1037c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", ret);
1038c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1039c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1040c797b6c6SAneesh Kumar K.V 	free(flock.client_id);
1041c797b6c6SAneesh Kumar K.V 	return;
1042c797b6c6SAneesh Kumar K.V }
1043c797b6c6SAneesh Kumar K.V 
1044c797b6c6SAneesh Kumar K.V static void virtio_p9_getlock(struct p9_dev *p9dev,
1045c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1046c797b6c6SAneesh Kumar K.V {
1047c797b6c6SAneesh Kumar K.V 	u32 fid_val;
1048c797b6c6SAneesh Kumar K.V 	struct p9_getlock glock;
1049c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dbqqds", &fid_val, &glock.type,
1050c797b6c6SAneesh Kumar K.V 			    &glock.start, &glock.length, &glock.proc_id,
1051c797b6c6SAneesh Kumar K.V 			    &glock.client_id);
1052c797b6c6SAneesh Kumar K.V 
1053c797b6c6SAneesh Kumar K.V 	/* Just return success */
1054c797b6c6SAneesh Kumar K.V 	glock.type = F_UNLCK;
1055c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "bqqds", glock.type,
1056c797b6c6SAneesh Kumar K.V 			     glock.start, glock.length, glock.proc_id,
1057c797b6c6SAneesh Kumar K.V 			     glock.client_id);
1058c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1059c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1060c797b6c6SAneesh Kumar K.V 	free(glock.client_id);
1061c797b6c6SAneesh Kumar K.V 	return;
1062c797b6c6SAneesh Kumar K.V }
1063c797b6c6SAneesh Kumar K.V 
1064c797b6c6SAneesh Kumar K.V static int virtio_p9_ancestor(char *path, char *ancestor)
1065c797b6c6SAneesh Kumar K.V {
1066c797b6c6SAneesh Kumar K.V 	int size = strlen(ancestor);
1067c797b6c6SAneesh Kumar K.V 	if (!strncmp(path, ancestor, size)) {
1068c797b6c6SAneesh Kumar K.V 		/*
1069c797b6c6SAneesh Kumar K.V 		 * Now check whether ancestor is a full name or
1070c797b6c6SAneesh Kumar K.V 		 * or directory component and not just part
1071c797b6c6SAneesh Kumar K.V 		 * of a name.
1072c797b6c6SAneesh Kumar K.V 		 */
1073c797b6c6SAneesh Kumar K.V 		if (path[size] == '\0' || path[size] == '/')
1074c797b6c6SAneesh Kumar K.V 			return 1;
1075c797b6c6SAneesh Kumar K.V 	}
1076c797b6c6SAneesh Kumar K.V 	return 0;
1077c797b6c6SAneesh Kumar K.V }
1078c797b6c6SAneesh Kumar K.V 
1079c797b6c6SAneesh Kumar K.V static void virtio_p9_fix_path(char *fid_path, char *old_name, char *new_name)
1080c797b6c6SAneesh Kumar K.V {
1081c797b6c6SAneesh Kumar K.V 	char tmp_name[PATH_MAX];
1082c797b6c6SAneesh Kumar K.V 	size_t rp_sz = strlen(old_name);
1083c797b6c6SAneesh Kumar K.V 
1084c797b6c6SAneesh Kumar K.V 	if (rp_sz == strlen(fid_path)) {
1085c797b6c6SAneesh Kumar K.V 		/* replace the full name */
1086c797b6c6SAneesh Kumar K.V 		strcpy(fid_path, new_name);
1087c797b6c6SAneesh Kumar K.V 		return;
1088c797b6c6SAneesh Kumar K.V 	}
1089c797b6c6SAneesh Kumar K.V 	/* save the trailing path details */
1090c797b6c6SAneesh Kumar K.V 	strcpy(tmp_name, fid_path + rp_sz);
1091c797b6c6SAneesh Kumar K.V 	sprintf(fid_path, "%s%s", new_name, tmp_name);
1092c797b6c6SAneesh Kumar K.V 	return;
1093c797b6c6SAneesh Kumar K.V }
1094c797b6c6SAneesh Kumar K.V 
1095e2341580SSasha Levin static void rename_fids(struct p9_dev *p9dev, char *old_name, char *new_name)
1096e2341580SSasha Levin {
1097e2341580SSasha Levin 	struct rb_node *node = rb_first(&p9dev->fids);
1098e2341580SSasha Levin 
1099e2341580SSasha Levin 	while (node) {
1100e2341580SSasha Levin 		struct p9_fid *fid = rb_entry(node, struct p9_fid, node);
1101e2341580SSasha Levin 
1102e2341580SSasha Levin 		if (fid->fid != P9_NOFID && virtio_p9_ancestor(fid->path, old_name)) {
1103e2341580SSasha Levin 				virtio_p9_fix_path(fid->path, old_name, new_name);
1104e2341580SSasha Levin 		}
1105e2341580SSasha Levin 		node = rb_next(node);
1106e2341580SSasha Levin 	}
1107e2341580SSasha Levin }
1108e2341580SSasha Levin 
1109c797b6c6SAneesh Kumar K.V static void virtio_p9_renameat(struct p9_dev *p9dev,
1110c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
1111c797b6c6SAneesh Kumar K.V {
1112e2341580SSasha Levin 	int ret;
1113c797b6c6SAneesh Kumar K.V 	char *old_name, *new_name;
1114c797b6c6SAneesh Kumar K.V 	u32 old_dfid_val, new_dfid_val;
1115c797b6c6SAneesh Kumar K.V 	struct p9_fid *old_dfid, *new_dfid;
1116c797b6c6SAneesh Kumar K.V 	char old_full_path[PATH_MAX], new_full_path[PATH_MAX];
1117c797b6c6SAneesh Kumar K.V 
1118c797b6c6SAneesh Kumar K.V 
1119c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsds", &old_dfid_val, &old_name,
1120c797b6c6SAneesh Kumar K.V 			    &new_dfid_val, &new_name);
1121c797b6c6SAneesh Kumar K.V 
112231a6fb8dSSasha Levin 	old_dfid = get_fid(p9dev, old_dfid_val);
112331a6fb8dSSasha Levin 	new_dfid = get_fid(p9dev, new_dfid_val);
1124c797b6c6SAneesh Kumar K.V 
1125c797b6c6SAneesh Kumar K.V 	sprintf(old_full_path, "%s/%s", old_dfid->abs_path, old_name);
1126c797b6c6SAneesh Kumar K.V 	sprintf(new_full_path, "%s/%s", new_dfid->abs_path, new_name);
1127*9bb99a82SG. Campana 	if (path_is_illegal(old_full_path) || path_is_illegal(new_full_path)) {
1128*9bb99a82SG. Campana 		errno = EACCES;
1129*9bb99a82SG. Campana 		goto err_out;
1130*9bb99a82SG. Campana 	}
1131*9bb99a82SG. Campana 
1132c797b6c6SAneesh Kumar K.V 	ret = rename(old_full_path, new_full_path);
1133c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1134c797b6c6SAneesh Kumar K.V 		goto err_out;
1135c797b6c6SAneesh Kumar K.V 	/*
1136c797b6c6SAneesh Kumar K.V 	 * Now fix path in other fids, if the renamed path is part of
1137c797b6c6SAneesh Kumar K.V 	 * that.
1138c797b6c6SAneesh Kumar K.V 	 */
1139e2341580SSasha Levin 	rename_fids(p9dev, old_name, new_name);
1140c797b6c6SAneesh Kumar K.V 	free(old_name);
1141c797b6c6SAneesh Kumar K.V 	free(new_name);
1142c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1143c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1144c797b6c6SAneesh Kumar K.V 	return;
1145c797b6c6SAneesh Kumar K.V err_out:
1146c797b6c6SAneesh Kumar K.V 	free(old_name);
1147c797b6c6SAneesh Kumar K.V 	free(new_name);
1148c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1149c797b6c6SAneesh Kumar K.V 	return;
1150c797b6c6SAneesh Kumar K.V }
1151c797b6c6SAneesh Kumar K.V 
1152c797b6c6SAneesh Kumar K.V static void virtio_p9_unlinkat(struct p9_dev *p9dev,
1153c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
1154c797b6c6SAneesh Kumar K.V {
1155c797b6c6SAneesh Kumar K.V 	int ret;
1156c797b6c6SAneesh Kumar K.V 	char *name;
1157c797b6c6SAneesh Kumar K.V 	u32 fid_val, flags;
1158c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
1159c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
1160c797b6c6SAneesh Kumar K.V 
1161c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsd", &fid_val, &name, &flags);
116231a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
1163c797b6c6SAneesh Kumar K.V 
1164c797b6c6SAneesh Kumar K.V 	sprintf(full_path, "%s/%s", fid->abs_path, name);
1165*9bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
1166*9bb99a82SG. Campana 		errno = EACCES;
1167*9bb99a82SG. Campana 		goto err_out;
1168*9bb99a82SG. Campana 	}
1169*9bb99a82SG. Campana 
1170c797b6c6SAneesh Kumar K.V 	ret = remove(full_path);
1171c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1172c797b6c6SAneesh Kumar K.V 		goto err_out;
1173c797b6c6SAneesh Kumar K.V 	free(name);
1174c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1175c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1176c797b6c6SAneesh Kumar K.V 	return;
1177c797b6c6SAneesh Kumar K.V err_out:
1178c797b6c6SAneesh Kumar K.V 	free(name);
1179c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1180c797b6c6SAneesh Kumar K.V 	return;
1181c797b6c6SAneesh Kumar K.V }
1182c797b6c6SAneesh Kumar K.V 
11835cc808aaSSasha Levin static void virtio_p9_flush(struct p9_dev *p9dev,
11845cc808aaSSasha Levin 				struct p9_pdu *pdu, u32 *outlen)
11855cc808aaSSasha Levin {
11865cc808aaSSasha Levin 	u16 tag, oldtag;
11875cc808aaSSasha Levin 
11885cc808aaSSasha Levin 	virtio_p9_pdu_readf(pdu, "ww", &tag, &oldtag);
11895cc808aaSSasha Levin 	virtio_p9_pdu_writef(pdu, "w", tag);
11905cc808aaSSasha Levin 	*outlen = pdu->write_offset;
11915cc808aaSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
11925cc808aaSSasha Levin 
11935cc808aaSSasha Levin 	return;
11945cc808aaSSasha Levin }
11955cc808aaSSasha Levin 
1196c797b6c6SAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev,
1197c797b6c6SAneesh Kumar K.V 				 struct p9_pdu *pdu, u32 *outlen)
1198c797b6c6SAneesh Kumar K.V {
1199c797b6c6SAneesh Kumar K.V 	return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen);
1200c797b6c6SAneesh Kumar K.V }
1201c797b6c6SAneesh Kumar K.V 
1202ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
1203af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
1204b4422bf3SAneesh Kumar K.V 
1205c797b6c6SAneesh Kumar K.V /* FIXME should be removed when merging with latest linus tree */
1206c797b6c6SAneesh Kumar K.V #define P9_TRENAMEAT 74
1207c797b6c6SAneesh Kumar K.V #define P9_TUNLINKAT 76
1208c797b6c6SAneesh Kumar K.V 
1209c797b6c6SAneesh Kumar K.V static p9_handler *virtio_9p_dotl_handler [] = {
1210c797b6c6SAneesh Kumar K.V 	[P9_TREADDIR]     = virtio_p9_readdir,
1211c797b6c6SAneesh Kumar K.V 	[P9_TSTATFS]      = virtio_p9_statfs,
1212c797b6c6SAneesh Kumar K.V 	[P9_TGETATTR]     = virtio_p9_getattr,
1213c797b6c6SAneesh Kumar K.V 	[P9_TSETATTR]     = virtio_p9_setattr,
1214c797b6c6SAneesh Kumar K.V 	[P9_TXATTRWALK]   = virtio_p9_eopnotsupp,
1215c797b6c6SAneesh Kumar K.V 	[P9_TXATTRCREATE] = virtio_p9_eopnotsupp,
1216c797b6c6SAneesh Kumar K.V 	[P9_TMKNOD]       = virtio_p9_mknod,
1217c797b6c6SAneesh Kumar K.V 	[P9_TLOCK]        = virtio_p9_lock,
1218c797b6c6SAneesh Kumar K.V 	[P9_TGETLOCK]     = virtio_p9_getlock,
1219c797b6c6SAneesh Kumar K.V 	[P9_TRENAMEAT]    = virtio_p9_renameat,
1220c797b6c6SAneesh Kumar K.V 	[P9_TREADLINK]    = virtio_p9_readlink,
1221c797b6c6SAneesh Kumar K.V 	[P9_TUNLINKAT]    = virtio_p9_unlinkat,
1222c797b6c6SAneesh Kumar K.V 	[P9_TMKDIR]       = virtio_p9_mkdir,
1223b4422bf3SAneesh Kumar K.V 	[P9_TVERSION]     = virtio_p9_version,
1224c797b6c6SAneesh Kumar K.V 	[P9_TLOPEN]       = virtio_p9_open,
1225b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]      = virtio_p9_attach,
1226b4422bf3SAneesh Kumar K.V 	[P9_TWALK]        = virtio_p9_walk,
1227c797b6c6SAneesh Kumar K.V 	[P9_TCLUNK]       = virtio_p9_clunk,
1228c797b6c6SAneesh Kumar K.V 	[P9_TFSYNC]       = virtio_p9_fsync,
1229b4422bf3SAneesh Kumar K.V 	[P9_TREAD]        = virtio_p9_read,
12305cc808aaSSasha Levin 	[P9_TFLUSH]       = virtio_p9_flush,
1231c797b6c6SAneesh Kumar K.V 	[P9_TLINK]        = virtio_p9_link,
1232c797b6c6SAneesh Kumar K.V 	[P9_TSYMLINK]     = virtio_p9_symlink,
1233c797b6c6SAneesh Kumar K.V 	[P9_TLCREATE]     = virtio_p9_create,
1234b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]       = virtio_p9_write,
12356fc5cd9bSSasha Levin 	[P9_TREMOVE]      = virtio_p9_remove,
1236f161f28bSSasha Levin 	[P9_TRENAME]      = virtio_p9_rename,
1237b4422bf3SAneesh Kumar K.V };
1238b4422bf3SAneesh Kumar K.V 
1239af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
1240af045e53SAneesh Kumar K.V {
1241af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
1242af045e53SAneesh Kumar K.V 	if (!pdu)
1243af045e53SAneesh Kumar K.V 		return NULL;
1244af045e53SAneesh Kumar K.V 
1245bfc15268SAneesh Kumar K.V 	/* skip the pdu header p9_msg */
12465529bcd7SAsias He 	pdu->read_offset	= VIRTIO_9P_HDR_LEN;
12475529bcd7SAsias He 	pdu->write_offset	= VIRTIO_9P_HDR_LEN;
1248af045e53SAneesh Kumar K.V 	pdu->queue_head		= virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
1249a8a44649SAsias He 					pdu->out_iov, &pdu->in_iov_cnt, &pdu->out_iov_cnt);
1250af045e53SAneesh Kumar K.V 	return pdu;
1251af045e53SAneesh Kumar K.V }
1252af045e53SAneesh Kumar K.V 
1253af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
1254af045e53SAneesh Kumar K.V {
1255af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
1256af045e53SAneesh Kumar K.V 	/*
1257af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
1258af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
1259af045e53SAneesh Kumar K.V 	 */
1260af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
1261af045e53SAneesh Kumar K.V 	return msg->cmd;
1262af045e53SAneesh Kumar K.V }
1263af045e53SAneesh Kumar K.V 
1264b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
12651c7850f9SSasha Levin {
1266af045e53SAneesh Kumar K.V 	u8 cmd;
1267b4422bf3SAneesh Kumar K.V 	u32 len = 0;
1268b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
1269b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
1270af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
1271af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
12721c7850f9SSasha Levin 
1273b4422bf3SAneesh Kumar K.V 	vq = job->vq;
1274b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
12751c7850f9SSasha Levin 
1276af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
1277af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
1278af045e53SAneesh Kumar K.V 
1279c797b6c6SAneesh Kumar K.V 	if ((cmd >= ARRAY_SIZE(virtio_9p_dotl_handler)) ||
1280c797b6c6SAneesh Kumar K.V 	    !virtio_9p_dotl_handler[cmd])
128197b408afSAneesh Kumar K.V 		handler = virtio_p9_eopnotsupp;
1282dd78d9eaSAneesh Kumar K.V 	else
1283c797b6c6SAneesh Kumar K.V 		handler = virtio_9p_dotl_handler[cmd];
1284c797b6c6SAneesh Kumar K.V 
1285af045e53SAneesh Kumar K.V 	handler(p9dev, p9pdu, &len);
1286af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
1287af045e53SAneesh Kumar K.V 	free(p9pdu);
12881c7850f9SSasha Levin 	return true;
12891c7850f9SSasha Levin }
12901c7850f9SSasha Levin 
12911c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
12921c7850f9SSasha Levin {
1293b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
1294b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
1295b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
12961c7850f9SSasha Levin 
12971c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
1298b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
129902eca50cSAsias He 		p9dev->vdev.ops->signal_vq(kvm, &p9dev->vdev, vq - p9dev->vqs);
13001c7850f9SSasha Levin 	}
13011c7850f9SSasha Levin }
13021c7850f9SSasha Levin 
1303c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
13041c7850f9SSasha Levin {
1305c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
13061c7850f9SSasha Levin 
1307c5ae742bSSasha Levin 	return ((u8 *)(p9dev->config));
1308c7838fbdSSasha Levin }
1309c7838fbdSSasha Levin 
1310c7838fbdSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
1311c7838fbdSSasha Levin {
1312c7838fbdSSasha Levin 	return 1 << VIRTIO_9P_MOUNT_TAG;
1313c7838fbdSSasha Levin }
1314c7838fbdSSasha Levin 
1315c7838fbdSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
1316c7838fbdSSasha Levin {
1317c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1318e0ea1859SMarc Zyngier 	struct virtio_9p_config *conf = p9dev->config;
1319c7838fbdSSasha Levin 
1320c7838fbdSSasha Levin 	p9dev->features = features;
1321e0ea1859SMarc Zyngier 	conf->tag_len = virtio_host_to_guest_u16(&p9dev->vdev, conf->tag_len);
1322c7838fbdSSasha Levin }
1323c7838fbdSSasha Levin 
1324c59ba304SWill Deacon static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
1325c59ba304SWill Deacon 		   u32 pfn)
1326c7838fbdSSasha Levin {
1327c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1328b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job;
1329b4422bf3SAneesh Kumar K.V 	struct virt_queue *queue;
1330c7838fbdSSasha Levin 	void *p;
13311c7850f9SSasha Levin 
1332312c62d1SSasha Levin 	compat__remove_message(compat_id);
1333e59662b3SSasha Levin 
1334c7838fbdSSasha Levin 	queue		= &p9dev->vqs[vq];
1335c7838fbdSSasha Levin 	queue->pfn	= pfn;
1336e7e2950aSSasha Levin 	p		= virtio_get_vq(kvm, queue->pfn, page_size);
1337c7838fbdSSasha Levin 	job		= &p9dev->jobs[vq];
13381c7850f9SSasha Levin 
1339c59ba304SWill Deacon 	vring_init(&queue->vring, VIRTQUEUE_NUM, p, align);
1340e0ea1859SMarc Zyngier 	virtio_init_device_vq(&p9dev->vdev, queue);
13411c7850f9SSasha Levin 
1342b4422bf3SAneesh Kumar K.V 	*job		= (struct p9_dev_job) {
1343b4422bf3SAneesh Kumar K.V 		.vq		= queue,
1344b4422bf3SAneesh Kumar K.V 		.p9dev		= p9dev,
1345b4422bf3SAneesh Kumar K.V 	};
1346df0c7f57SSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_p9_do_io, job);
134760eb42d5SSasha Levin 
1348c7838fbdSSasha Levin 	return 0;
13491c7850f9SSasha Levin }
13501c7850f9SSasha Levin 
1351c7838fbdSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
1352c7838fbdSSasha Levin {
1353c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
13541c7850f9SSasha Levin 
1355c7838fbdSSasha Levin 	thread_pool__do_job(&p9dev->jobs[vq].job_id);
1356c7838fbdSSasha Levin 
1357c7838fbdSSasha Levin 	return 0;
1358c7838fbdSSasha Levin }
1359c7838fbdSSasha Levin 
1360c7838fbdSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
1361c7838fbdSSasha Levin {
1362c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1363c7838fbdSSasha Levin 
1364c7838fbdSSasha Levin 	return p9dev->vqs[vq].pfn;
1365c7838fbdSSasha Levin }
1366c7838fbdSSasha Levin 
1367c7838fbdSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
1368c7838fbdSSasha Levin {
1369c7838fbdSSasha Levin 	return VIRTQUEUE_NUM;
1370c7838fbdSSasha Levin }
1371c7838fbdSSasha Levin 
13727aba29c1SWill Deacon static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
13737aba29c1SWill Deacon {
13747aba29c1SWill Deacon 	/* FIXME: dynamic */
13757aba29c1SWill Deacon 	return size;
13767aba29c1SWill Deacon }
13777aba29c1SWill Deacon 
137815542babSAndre Przywara struct virtio_ops p9_dev_virtio_ops = {
1379c7838fbdSSasha Levin 	.get_config		= get_config,
1380c7838fbdSSasha Levin 	.get_host_features	= get_host_features,
1381c7838fbdSSasha Levin 	.set_guest_features	= set_guest_features,
1382c7838fbdSSasha Levin 	.init_vq		= init_vq,
1383c7838fbdSSasha Levin 	.notify_vq		= notify_vq,
1384c7838fbdSSasha Levin 	.get_pfn_vq		= get_pfn_vq,
1385c7838fbdSSasha Levin 	.get_size_vq		= get_size_vq,
13867aba29c1SWill Deacon 	.set_size_vq		= set_size_vq,
1387c7838fbdSSasha Levin };
13881c47ce69SSasha Levin 
1389cac9e8fdSSasha Levin int virtio_9p_rootdir_parser(const struct option *opt, const char *arg, int unset)
1390cac9e8fdSSasha Levin {
1391cac9e8fdSSasha Levin 	char *tag_name;
1392cac9e8fdSSasha Levin 	char tmp[PATH_MAX];
1393cac9e8fdSSasha Levin 	struct kvm *kvm = opt->ptr;
1394cac9e8fdSSasha Levin 
1395cac9e8fdSSasha Levin 	/*
1396cac9e8fdSSasha Levin 	 * 9p dir can be of the form dirname,tag_name or
1397cac9e8fdSSasha Levin 	 * just dirname. In the later case we use the
1398cac9e8fdSSasha Levin 	 * default tag name
1399cac9e8fdSSasha Levin 	 */
1400cac9e8fdSSasha Levin 	tag_name = strstr(arg, ",");
1401cac9e8fdSSasha Levin 	if (tag_name) {
1402cac9e8fdSSasha Levin 		*tag_name = '\0';
1403cac9e8fdSSasha Levin 		tag_name++;
1404cac9e8fdSSasha Levin 	}
1405cac9e8fdSSasha Levin 	if (realpath(arg, tmp)) {
1406cac9e8fdSSasha Levin 		if (virtio_9p__register(kvm, tmp, tag_name) < 0)
1407cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1408cac9e8fdSSasha Levin 	} else
1409cac9e8fdSSasha Levin 		die("Failed resolving 9p path");
1410cac9e8fdSSasha Levin 	return 0;
1411cac9e8fdSSasha Levin }
1412cac9e8fdSSasha Levin 
1413cac9e8fdSSasha Levin int virtio_9p_img_name_parser(const struct option *opt, const char *arg, int unset)
1414cac9e8fdSSasha Levin {
1415cac9e8fdSSasha Levin 	char path[PATH_MAX];
1416cac9e8fdSSasha Levin 	struct stat st;
1417cac9e8fdSSasha Levin 	struct kvm *kvm = opt->ptr;
1418cac9e8fdSSasha Levin 
1419cac9e8fdSSasha Levin 	if (stat(arg, &st) == 0 &&
1420cac9e8fdSSasha Levin 	    S_ISDIR(st.st_mode)) {
1421cac9e8fdSSasha Levin 		char tmp[PATH_MAX];
1422cac9e8fdSSasha Levin 
1423cac9e8fdSSasha Levin 		if (kvm->cfg.using_rootfs)
1424cac9e8fdSSasha Levin 			die("Please use only one rootfs directory atmost");
1425cac9e8fdSSasha Levin 
1426cac9e8fdSSasha Levin 		if (realpath(arg, tmp) == 0 ||
1427cac9e8fdSSasha Levin 		    virtio_9p__register(kvm, tmp, "/dev/root") < 0)
1428cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1429cac9e8fdSSasha Levin 		kvm->cfg.using_rootfs = 1;
1430cac9e8fdSSasha Levin 		return 0;
1431cac9e8fdSSasha Levin 	}
1432cac9e8fdSSasha Levin 
1433cac9e8fdSSasha Levin 	snprintf(path, PATH_MAX, "%s%s", kvm__get_dir(), arg);
1434cac9e8fdSSasha Levin 
1435cac9e8fdSSasha Levin 	if (stat(path, &st) == 0 &&
1436cac9e8fdSSasha Levin 	    S_ISDIR(st.st_mode)) {
1437cac9e8fdSSasha Levin 		char tmp[PATH_MAX];
1438cac9e8fdSSasha Levin 
1439cac9e8fdSSasha Levin 		if (kvm->cfg.using_rootfs)
1440cac9e8fdSSasha Levin 			die("Please use only one rootfs directory atmost");
1441cac9e8fdSSasha Levin 
1442cac9e8fdSSasha Levin 		if (realpath(path, tmp) == 0 ||
1443cac9e8fdSSasha Levin 		    virtio_9p__register(kvm, tmp, "/dev/root") < 0)
1444cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1445cac9e8fdSSasha Levin 		if (virtio_9p__register(kvm, "/", "hostfs") < 0)
1446cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1447cac9e8fdSSasha Levin 		kvm_setup_resolv(arg);
1448cac9e8fdSSasha Levin 		kvm->cfg.using_rootfs = kvm->cfg.custom_rootfs = 1;
1449cac9e8fdSSasha Levin 		kvm->cfg.custom_rootfs_name = arg;
1450cac9e8fdSSasha Levin 		return 0;
1451cac9e8fdSSasha Levin 	}
1452cac9e8fdSSasha Levin 
1453cac9e8fdSSasha Levin 	return -1;
1454cac9e8fdSSasha Levin }
1455cac9e8fdSSasha Levin 
14561c47ce69SSasha Levin int virtio_9p__init(struct kvm *kvm)
14571c47ce69SSasha Levin {
14581c47ce69SSasha Levin 	struct p9_dev *p9dev;
14591c47ce69SSasha Levin 
14601c47ce69SSasha Levin 	list_for_each_entry(p9dev, &devs, list) {
146102eca50cSAsias He 		virtio_init(kvm, p9dev, &p9dev->vdev, &p9_dev_virtio_ops,
1462d97dadecSWill Deacon 			    VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_9P,
1463ae06ce71SWill Deacon 			    VIRTIO_ID_9P, PCI_CLASS_9P);
1464c7838fbdSSasha Levin 	}
1465c7838fbdSSasha Levin 
1466c7838fbdSSasha Levin 	return 0;
1467c7838fbdSSasha Levin }
146849a8afd1SSasha Levin virtio_dev_init(virtio_9p__init);
1469c7838fbdSSasha Levin 
1470c7838fbdSSasha Levin int virtio_9p__register(struct kvm *kvm, const char *root, const char *tag_name)
1471c7838fbdSSasha Levin {
1472c7838fbdSSasha Levin 	struct p9_dev *p9dev;
147354f6802dSPekka Enberg 	int err = 0;
14741c7850f9SSasha Levin 
1475b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
1476b4422bf3SAneesh Kumar K.V 	if (!p9dev)
147754f6802dSPekka Enberg 		return -ENOMEM;
147854f6802dSPekka Enberg 
1479b4422bf3SAneesh Kumar K.V 	if (!tag_name)
14805529bcd7SAsias He 		tag_name = VIRTIO_9P_DEFAULT_TAG;
148154f6802dSPekka Enberg 
1482b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
148354f6802dSPekka Enberg 	if (p9dev->config == NULL) {
148454f6802dSPekka Enberg 		err = -ENOMEM;
1485b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
148654f6802dSPekka Enberg 	}
14871c7850f9SSasha Levin 
1488b4422bf3SAneesh Kumar K.V 	strcpy(p9dev->root_dir, root);
1489b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
149054f6802dSPekka Enberg 	if (p9dev->config->tag_len > MAX_TAG_LEN) {
149154f6802dSPekka Enberg 		err = -EINVAL;
1492b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
149354f6802dSPekka Enberg 	}
14941c7850f9SSasha Levin 
1495c7838fbdSSasha Levin 	memcpy(&p9dev->config->tag, tag_name, strlen(tag_name));
14961c7850f9SSasha Levin 
1497c7838fbdSSasha Levin 	list_add(&p9dev->list, &devs);
1498b4422bf3SAneesh Kumar K.V 
1499d278197dSAsias He 	if (compat_id == -1)
150052f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-9p", "CONFIG_NET_9P_VIRTIO");
1501e59662b3SSasha Levin 
150254f6802dSPekka Enberg 	return err;
150354f6802dSPekka Enberg 
1504b4422bf3SAneesh Kumar K.V free_p9dev_config:
1505b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
1506b4422bf3SAneesh Kumar K.V free_p9dev:
1507b4422bf3SAneesh Kumar K.V 	free(p9dev);
150854f6802dSPekka Enberg 	return err;
15091c7850f9SSasha Levin }
1510