xref: /kvmtool/virtio/9p.c (revision e277a1b42da83dc36337491e91dbc15f3fd8eee0)
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;
31*e277a1b4SG. Campana 	size_t len;
32e2341580SSasha Levin 
33e2341580SSasha Levin 	while (node) {
34e2341580SSasha Levin 		struct p9_fid *cur = rb_entry(node, struct p9_fid, node);
35e2341580SSasha Levin 
36e2341580SSasha Levin 		if (fid < cur->fid) {
37e2341580SSasha Levin 			node = node->rb_left;
38e2341580SSasha Levin 		} else if (fid > cur->fid) {
39e2341580SSasha Levin 			node = node->rb_right;
40e2341580SSasha Levin 		} else {
41e2341580SSasha Levin 			return cur;
42e2341580SSasha Levin 		}
43e2341580SSasha Levin 	}
44e2341580SSasha Levin 
45e2341580SSasha Levin 	pfid = calloc(sizeof(*pfid), 1);
46e2341580SSasha Levin 	if (!pfid)
47e2341580SSasha Levin 		return NULL;
48e2341580SSasha Levin 
49*e277a1b4SG. Campana 	len = strlen(dev->root_dir);
50*e277a1b4SG. Campana 	if (len >= sizeof(pfid->abs_path)) {
51*e277a1b4SG. Campana 		free(pfid);
52*e277a1b4SG. Campana 		return NULL;
53*e277a1b4SG. Campana 	}
54*e277a1b4SG. Campana 
55e2341580SSasha Levin 	pfid->fid = fid;
56e2341580SSasha Levin 	strcpy(pfid->abs_path, dev->root_dir);
57*e277a1b4SG. Campana 	pfid->path = pfid->abs_path + strlen(pfid->abs_path);
58e2341580SSasha Levin 
59e2341580SSasha Levin 	insert_new_fid(dev, pfid);
60e2341580SSasha Levin 
61e2341580SSasha Levin 	return pfid;
62e2341580SSasha Levin }
63e2341580SSasha Levin 
64e2341580SSasha Levin static int insert_new_fid(struct p9_dev *dev, struct p9_fid *fid)
65e2341580SSasha Levin {
66e2341580SSasha Levin 	struct rb_node **node = &(dev->fids.rb_node), *parent = NULL;
67e2341580SSasha Levin 
68e2341580SSasha Levin 	while (*node) {
69e2341580SSasha Levin 		int result = fid->fid - rb_entry(*node, struct p9_fid, node)->fid;
70e2341580SSasha Levin 
71e2341580SSasha Levin 		parent = *node;
72e2341580SSasha Levin 		if (result < 0)
73e2341580SSasha Levin 			node    = &((*node)->rb_left);
74e2341580SSasha Levin 		else if (result > 0)
75e2341580SSasha Levin 			node    = &((*node)->rb_right);
76e2341580SSasha Levin 		else
77e2341580SSasha Levin 			return -EEXIST;
78e2341580SSasha Levin 	}
79e2341580SSasha Levin 
80e2341580SSasha Levin 	rb_link_node(&fid->node, parent, node);
81e2341580SSasha Levin 	rb_insert_color(&fid->node, &dev->fids);
82e2341580SSasha Levin 	return 0;
83e2341580SSasha Levin }
84e2341580SSasha Levin 
8531a6fb8dSSasha Levin static struct p9_fid *get_fid(struct p9_dev *p9dev, int fid)
8631a6fb8dSSasha Levin {
87e2341580SSasha Levin 	struct p9_fid *new;
8831a6fb8dSSasha Levin 
89e2341580SSasha Levin 	new = find_or_create_fid(p9dev, fid);
90e2341580SSasha Levin 
91e2341580SSasha Levin 	return new;
9231a6fb8dSSasha Levin }
9331a6fb8dSSasha Levin 
941c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
95b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
96b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
971c7850f9SSasha Levin {
98b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
991c7850f9SSasha Levin 
1001c7850f9SSasha Levin 	return abs_path;
1011c7850f9SSasha Levin }
1021c7850f9SSasha Levin 
103c797b6c6SAneesh Kumar K.V static void stat2qid(struct stat *st, struct p9_qid *qid)
1041c7850f9SSasha Levin {
1051c7850f9SSasha Levin 	*qid = (struct p9_qid) {
1061c7850f9SSasha Levin 		.path		= st->st_ino,
1071c7850f9SSasha Levin 		.version	= st->st_mtime,
1081c7850f9SSasha Levin 	};
1091c7850f9SSasha Levin 
1101c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
1111c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
1121c7850f9SSasha Levin }
1131c7850f9SSasha Levin 
114b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
1151c7850f9SSasha Levin {
116e2341580SSasha Levin 	struct p9_fid *pfid = get_fid(p9dev, fid);
117e2341580SSasha Levin 
118e2341580SSasha Levin 	if (pfid->fd > 0)
119e2341580SSasha Levin 		close(pfid->fd);
120e2341580SSasha Levin 
121e2341580SSasha Levin 	if (pfid->dir)
122e2341580SSasha Levin 		closedir(pfid->dir);
123e2341580SSasha Levin 
124e2341580SSasha Levin 	rb_erase(&pfid->node, &p9dev->fids);
125e2341580SSasha Levin 	free(pfid);
1261c7850f9SSasha Levin }
127e2341580SSasha Levin 
128bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size)
1291c7850f9SSasha Levin {
130bfc15268SAneesh Kumar K.V 	u8 cmd;
131bfc15268SAneesh Kumar K.V 	u16 tag;
132bfc15268SAneesh Kumar K.V 
133bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32);
134bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag);
135bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
136bfc15268SAneesh Kumar K.V 	/* cmd + 1 is the reply message */
137bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag);
1381c7850f9SSasha Levin }
1391c7850f9SSasha Levin 
1406b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
1416b163a87SAneesh Kumar K.V {
1426b163a87SAneesh Kumar K.V 	int i;
1436b163a87SAneesh Kumar K.V 	u32 total = 0;
1446b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
1456b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
1466b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
1476b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
1486b163a87SAneesh Kumar K.V 			i++;
1496b163a87SAneesh Kumar K.V 			break;
1506b163a87SAneesh Kumar K.V 		}
1516b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
1526b163a87SAneesh Kumar K.V 	}
1536b163a87SAneesh Kumar K.V 	return i;
1546b163a87SAneesh Kumar K.V }
1556b163a87SAneesh Kumar K.V 
156eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
157eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
158eee1ba8eSAneesh Kumar K.V {
159bfc15268SAneesh Kumar K.V 	u16 tag;
160eee1ba8eSAneesh Kumar K.V 
1619c2e1d1aSSuzuki K. Poulose 	/* EMFILE at server implies ENFILE for the VM */
1629c2e1d1aSSuzuki K. Poulose 	if (err == EMFILE)
1639c2e1d1aSSuzuki K. Poulose 		err = ENFILE;
1649c2e1d1aSSuzuki K. Poulose 
1655529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
166c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", err);
167bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
168eee1ba8eSAneesh Kumar K.V 
169c797b6c6SAneesh Kumar K.V 	/* read the tag from input */
170bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32) + sizeof(u8);
171bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "w", &tag);
172bfc15268SAneesh Kumar K.V 
173c797b6c6SAneesh Kumar K.V 	/* Update the header */
174bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
175c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RLERROR, tag);
176eee1ba8eSAneesh Kumar K.V }
177eee1ba8eSAneesh Kumar K.V 
178ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
179af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1801c7850f9SSasha Levin {
181c797b6c6SAneesh Kumar K.V 	u32 msize;
182c797b6c6SAneesh Kumar K.V 	char *version;
183c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ds", &msize, &version);
184c797b6c6SAneesh Kumar K.V 	/*
185c797b6c6SAneesh Kumar K.V 	 * reply with the same msize the client sent us
186c797b6c6SAneesh Kumar K.V 	 * Error out if the request is not for 9P2000.L
187c797b6c6SAneesh Kumar K.V 	 */
1885529bcd7SAsias He 	if (!strcmp(version, VIRTIO_9P_VERSION_DOTL))
189c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "ds", msize, version);
190c797b6c6SAneesh Kumar K.V 	else
191c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "ds", msize, "unknown");
1921c7850f9SSasha Levin 
193bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
194bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
195c797b6c6SAneesh Kumar K.V 	free(version);
196ead43b01SAneesh Kumar K.V 	return;
1971c7850f9SSasha Levin }
1981c7850f9SSasha Levin 
199ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
200af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
2011c7850f9SSasha Levin {
202bfc15268SAneesh Kumar K.V 	u32 fid;
2031c7850f9SSasha Levin 
204bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid);
205bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid);
2061c7850f9SSasha Levin 
207bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
208bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
209ead43b01SAneesh Kumar K.V 	return;
2101c7850f9SSasha Levin }
2111c7850f9SSasha Levin 
212c797b6c6SAneesh Kumar K.V /*
213c797b6c6SAneesh Kumar K.V  * FIXME!! Need to map to protocol independent value. Upstream
214c797b6c6SAneesh Kumar K.V  * 9p also have the same BUG
215c797b6c6SAneesh Kumar K.V  */
216c797b6c6SAneesh Kumar K.V static int virtio_p9_openflags(int flags)
217c797b6c6SAneesh Kumar K.V {
218c797b6c6SAneesh Kumar K.V 	flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT | O_DIRECT);
219c797b6c6SAneesh Kumar K.V 	flags |= O_NOFOLLOW;
220c797b6c6SAneesh Kumar K.V 	return flags;
221c797b6c6SAneesh Kumar K.V }
222c797b6c6SAneesh Kumar K.V 
22332585666SSasha Levin static bool is_dir(struct p9_fid *fid)
22432585666SSasha Levin {
22532585666SSasha Levin 	struct stat st;
22632585666SSasha Levin 
22732585666SSasha Levin 	stat(fid->abs_path, &st);
22832585666SSasha Levin 
22932585666SSasha Levin 	return S_ISDIR(st.st_mode);
23032585666SSasha Levin }
23132585666SSasha Levin 
2329bb99a82SG. Campana /* path is always absolute */
2339bb99a82SG. Campana static bool path_is_illegal(const char *path)
2349bb99a82SG. Campana {
2359bb99a82SG. Campana 	size_t len;
2369bb99a82SG. Campana 
2379bb99a82SG. Campana 	if (strstr(path, "/../") != NULL)
2389bb99a82SG. Campana 		return true;
2399bb99a82SG. Campana 
2409bb99a82SG. Campana 	len = strlen(path);
2419bb99a82SG. Campana 	if (len >= 3 && strcmp(path + len - 3, "/..") == 0)
2429bb99a82SG. Campana 		return true;
2439bb99a82SG. Campana 
2449bb99a82SG. Campana 	return false;
2459bb99a82SG. Campana }
2469bb99a82SG. Campana 
247ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
248af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2491c7850f9SSasha Levin {
250c797b6c6SAneesh Kumar K.V 	u32 fid, flags;
2511c7850f9SSasha Levin 	struct stat st;
252bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
253bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
254bfc15268SAneesh Kumar K.V 
255c797b6c6SAneesh Kumar K.V 
256c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dd", &fid, &flags);
25731a6fb8dSSasha Levin 	new_fid = get_fid(p9dev, fid);
2581c7850f9SSasha Levin 
25930204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
260eee1ba8eSAneesh Kumar K.V 		goto err_out;
2611c7850f9SSasha Levin 
262c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
2631c7850f9SSasha Levin 
26432585666SSasha Levin 	if (is_dir(new_fid)) {
2651c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
266eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
267eee1ba8eSAneesh Kumar K.V 			goto err_out;
268eee1ba8eSAneesh Kumar K.V 	} else {
269eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
270c797b6c6SAneesh Kumar K.V 				    virtio_p9_openflags(flags));
271eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
272eee1ba8eSAneesh Kumar K.V 			goto err_out;
273eee1ba8eSAneesh Kumar K.V 	}
274c797b6c6SAneesh Kumar K.V 	/* FIXME!! need ot send proper iounit  */
275bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
276bfc15268SAneesh Kumar K.V 
277bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
278bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
279ead43b01SAneesh Kumar K.V 	return;
280eee1ba8eSAneesh Kumar K.V err_out:
281eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
282ead43b01SAneesh Kumar K.V 	return;
2831c7850f9SSasha Levin }
2841c7850f9SSasha Levin 
285ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
286af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
2871c7850f9SSasha Levin {
288c797b6c6SAneesh Kumar K.V 	int fd, ret;
289bfc15268SAneesh Kumar K.V 	char *name;
29032832dd1SG. Campana 	size_t size;
291af045e53SAneesh Kumar K.V 	struct stat st;
292bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
293c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
2944bc9734aSAneesh Kumar K.V 	char full_path[PATH_MAX];
295c797b6c6SAneesh Kumar K.V 	u32 dfid_val, flags, mode, gid;
296af045e53SAneesh Kumar K.V 
297c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsddd", &dfid_val,
298c797b6c6SAneesh Kumar K.V 			    &name, &flags, &mode, &gid);
29931a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
3001c7850f9SSasha Levin 
301c797b6c6SAneesh Kumar K.V 	flags = virtio_p9_openflags(flags);
3025f900f6dSSasha Levin 
30332832dd1SG. Campana 	ret = snprintf(full_path, sizeof(full_path), "%s/%s", dfid->abs_path, name);
30432832dd1SG. Campana 	if (ret >= (int)sizeof(full_path)) {
30532832dd1SG. Campana 		errno = ENAMETOOLONG;
30632832dd1SG. Campana 		goto err_out;
30732832dd1SG. Campana 	}
30832832dd1SG. Campana 
3099bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
3109bb99a82SG. Campana 		errno = EACCES;
3119bb99a82SG. Campana 		goto err_out;
3129bb99a82SG. Campana 	}
3139bb99a82SG. Campana 
31432832dd1SG. Campana 	size = sizeof(dfid->abs_path) - (dfid->path - dfid->abs_path);
31532832dd1SG. Campana 	ret = snprintf(dfid->path, size, "%s/%s", dfid->path, name);
31632832dd1SG. Campana 	if (ret >= (int)size) {
31732832dd1SG. Campana 		errno = ENAMETOOLONG;
31832832dd1SG. Campana 		if (size > 0)
31932832dd1SG. Campana 			dfid->path[size] = '\x00';
32032832dd1SG. Campana 		goto err_out;
32132832dd1SG. Campana 	}
32232832dd1SG. Campana 
323c797b6c6SAneesh Kumar K.V 	fd = open(full_path, flags | O_CREAT, mode);
3244bc9734aSAneesh Kumar K.V 	if (fd < 0)
3254bc9734aSAneesh Kumar K.V 		goto err_out;
326c797b6c6SAneesh Kumar K.V 	dfid->fd = fd;
327c797b6c6SAneesh Kumar K.V 
3284bc9734aSAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
3296c8ca053SAneesh Kumar K.V 		goto err_out;
3301c7850f9SSasha Levin 
331c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
332c797b6c6SAneesh Kumar K.V 	if (ret < 0)
333c797b6c6SAneesh Kumar K.V 		goto err_out;
334c797b6c6SAneesh Kumar K.V 
335c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
336bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
337bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
338bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3395f900f6dSSasha Levin 	free(name);
3406c8ca053SAneesh Kumar K.V 	return;
3416c8ca053SAneesh Kumar K.V err_out:
3425f900f6dSSasha Levin 	free(name);
343c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
344c797b6c6SAneesh Kumar K.V 	return;
345c797b6c6SAneesh Kumar K.V }
346c797b6c6SAneesh Kumar K.V 
347c797b6c6SAneesh Kumar K.V static void virtio_p9_mkdir(struct p9_dev *p9dev,
348c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
349c797b6c6SAneesh Kumar K.V {
350c797b6c6SAneesh Kumar K.V 	int ret;
351c797b6c6SAneesh Kumar K.V 	char *name;
352c797b6c6SAneesh Kumar K.V 	struct stat st;
353c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
354c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
355c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
356c797b6c6SAneesh Kumar K.V 	u32 dfid_val, mode, gid;
357c797b6c6SAneesh Kumar K.V 
358c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdd", &dfid_val,
359c797b6c6SAneesh Kumar K.V 			    &name, &mode, &gid);
36031a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
361c797b6c6SAneesh Kumar K.V 
36232832dd1SG. Campana 	ret = snprintf(full_path, sizeof(full_path), "%s/%s", dfid->abs_path, name);
36332832dd1SG. Campana 	if (ret >= (int)sizeof(full_path)) {
36432832dd1SG. Campana 		errno = ENAMETOOLONG;
36532832dd1SG. Campana 		goto err_out;
36632832dd1SG. Campana 	}
36732832dd1SG. Campana 
3689bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
3699bb99a82SG. Campana 		errno = EACCES;
3709bb99a82SG. Campana 		goto err_out;
3719bb99a82SG. Campana 	}
3729bb99a82SG. Campana 
373c797b6c6SAneesh Kumar K.V 	ret = mkdir(full_path, mode);
374c797b6c6SAneesh Kumar K.V 	if (ret < 0)
375c797b6c6SAneesh Kumar K.V 		goto err_out;
376c797b6c6SAneesh Kumar K.V 
377c797b6c6SAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
378c797b6c6SAneesh Kumar K.V 		goto err_out;
379c797b6c6SAneesh Kumar K.V 
380c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
381c797b6c6SAneesh Kumar K.V 	if (ret < 0)
382c797b6c6SAneesh Kumar K.V 		goto err_out;
383c797b6c6SAneesh Kumar K.V 
384c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
385c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
386c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
387c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
388c797b6c6SAneesh Kumar K.V 	free(name);
389c797b6c6SAneesh Kumar K.V 	return;
390c797b6c6SAneesh Kumar K.V err_out:
391c797b6c6SAneesh Kumar K.V 	free(name);
392c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
393ead43b01SAneesh Kumar K.V 	return;
3941c7850f9SSasha Levin }
3951c7850f9SSasha Levin 
396*e277a1b4SG. Campana static int join_path(struct p9_fid *fid, const char *name)
397*e277a1b4SG. Campana {
398*e277a1b4SG. Campana 	size_t len, size;
399*e277a1b4SG. Campana 
400*e277a1b4SG. Campana 	size = sizeof(fid->abs_path) - (fid->path - fid->abs_path);
401*e277a1b4SG. Campana 	len = strlen(name);
402*e277a1b4SG. Campana 	if (len >= size)
403*e277a1b4SG. Campana 		return -1;
404*e277a1b4SG. Campana 
405*e277a1b4SG. Campana 	strncpy(fid->path, name, size);
406*e277a1b4SG. Campana 	return 0;
407*e277a1b4SG. Campana }
408*e277a1b4SG. Campana 
409ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
410af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4111c7850f9SSasha Levin {
412af045e53SAneesh Kumar K.V 	u8 i;
413bfc15268SAneesh Kumar K.V 	u16 nwqid;
414bfc15268SAneesh Kumar K.V 	u16 nwname;
415bfc15268SAneesh Kumar K.V 	struct p9_qid wqid;
416e2341580SSasha Levin 	struct p9_fid *new_fid, *old_fid;
417c797b6c6SAneesh Kumar K.V 	u32 fid_val, newfid_val;
418c797b6c6SAneesh Kumar K.V 
4191c7850f9SSasha Levin 
420bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname);
42131a6fb8dSSasha Levin 	new_fid	= get_fid(p9dev, newfid_val);
4221c7850f9SSasha Levin 
423bfc15268SAneesh Kumar K.V 	nwqid = 0;
424bfc15268SAneesh Kumar K.V 	if (nwname) {
42531a6fb8dSSasha Levin 		struct p9_fid *fid = get_fid(p9dev, fid_val);
426bfc15268SAneesh Kumar K.V 
427*e277a1b4SG. Campana 		if (join_path(new_fid, fid->path) != 0) {
428*e277a1b4SG. Campana 			errno = ENAMETOOLONG;
429*e277a1b4SG. Campana 			goto err_out;
430*e277a1b4SG. Campana 		}
431*e277a1b4SG. Campana 
432bfc15268SAneesh Kumar K.V 		/* skip the space for count */
433bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
434bfc15268SAneesh Kumar K.V 		for (i = 0; i < nwname; i++) {
435bfc15268SAneesh Kumar K.V 			struct stat st;
4361c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
4371c7850f9SSasha Levin 			char full_path[PATH_MAX];
438e55ed135SPekka Enberg 			char *str;
43932832dd1SG. Campana 			int ret;
440bfc15268SAneesh Kumar K.V 
441bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_readf(pdu, "s", &str);
4421c7850f9SSasha Levin 
4431c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
44432832dd1SG. Campana 			ret = snprintf(tmp, sizeof(tmp), "%s/%s", new_fid->path, str);
44532832dd1SG. Campana 			if (ret >= (int)sizeof(tmp)) {
44632832dd1SG. Campana 				errno = ENAMETOOLONG;
44732832dd1SG. Campana 				goto err_out;
44832832dd1SG. Campana 			}
449e55ed135SPekka Enberg 
450e55ed135SPekka Enberg 			free(str);
451e55ed135SPekka Enberg 
452c797b6c6SAneesh Kumar K.V 			if (lstat(rel_to_abs(p9dev, tmp, full_path), &st) < 0)
4536c8ca053SAneesh Kumar K.V 				goto err_out;
4541c7850f9SSasha Levin 
455c797b6c6SAneesh Kumar K.V 			stat2qid(&st, &wqid);
456*e277a1b4SG. Campana 			if (join_path(new_fid, tmp) != 0) {
457*e277a1b4SG. Campana 				errno = ENAMETOOLONG;
458*e277a1b4SG. Campana 				goto err_out;
459*e277a1b4SG. Campana 			}
460c797b6c6SAneesh Kumar K.V 			new_fid->uid = fid->uid;
461bfc15268SAneesh Kumar K.V 			nwqid++;
462bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "Q", &wqid);
4631c7850f9SSasha Levin 		}
4641c7850f9SSasha Levin 	} else {
465bfc15268SAneesh Kumar K.V 		/*
466bfc15268SAneesh Kumar K.V 		 * update write_offset so our outlen get correct value
467bfc15268SAneesh Kumar K.V 		 */
468bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
469e2341580SSasha Levin 		old_fid = get_fid(p9dev, fid_val);
470*e277a1b4SG. Campana 		if (join_path(new_fid, old_fid->path) != 0) {
471*e277a1b4SG. Campana 			errno = ENAMETOOLONG;
472*e277a1b4SG. Campana 			goto err_out;
473*e277a1b4SG. Campana 		}
474e2341580SSasha Levin 		new_fid->uid    = old_fid->uid;
4751c7850f9SSasha Levin 	}
476bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
4775529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
478bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", nwqid);
479bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4806c8ca053SAneesh Kumar K.V 	return;
4816c8ca053SAneesh Kumar K.V err_out:
4826c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
483ead43b01SAneesh Kumar K.V 	return;
4841c7850f9SSasha Levin }
4851c7850f9SSasha Levin 
486ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
487af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
4881c7850f9SSasha Levin {
489bfc15268SAneesh Kumar K.V 	char *uname;
490bfc15268SAneesh Kumar K.V 	char *aname;
4911c7850f9SSasha Levin 	struct stat st;
492bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
4931c7850f9SSasha Levin 	struct p9_fid *fid;
494c797b6c6SAneesh Kumar K.V 	u32 fid_val, afid, uid;
495bfc15268SAneesh Kumar K.V 
496c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddssd", &fid_val, &afid,
497c797b6c6SAneesh Kumar K.V 			    &uname, &aname, &uid);
4981c7850f9SSasha Levin 
49939257180SPekka Enberg 	free(uname);
50039257180SPekka Enberg 	free(aname);
50139257180SPekka Enberg 
50230204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
5036c8ca053SAneesh Kumar K.V 		goto err_out;
5041c7850f9SSasha Levin 
505c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
5061c7850f9SSasha Levin 
50731a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
508c797b6c6SAneesh Kumar K.V 	fid->uid = uid;
509*e277a1b4SG. Campana 	if (join_path(fid, "/") != 0) {
510*e277a1b4SG. Campana 		errno = ENAMETOOLONG;
511*e277a1b4SG. Campana 		goto err_out;
512*e277a1b4SG. Campana 	}
5131c7850f9SSasha Levin 
514bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
515bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
516bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
5176c8ca053SAneesh Kumar K.V 	return;
5186c8ca053SAneesh Kumar K.V err_out:
5196c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
520ead43b01SAneesh Kumar K.V 	return;
5211c7850f9SSasha Levin }
5221c7850f9SSasha Levin 
523c797b6c6SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev,
524c797b6c6SAneesh Kumar K.V 				struct stat *st, struct p9_stat_dotl *statl)
5255f900f6dSSasha Levin {
526c797b6c6SAneesh Kumar K.V 	memset(statl, 0, sizeof(*statl));
527c797b6c6SAneesh Kumar K.V 	statl->st_mode		= st->st_mode;
528c797b6c6SAneesh Kumar K.V 	statl->st_nlink		= st->st_nlink;
529506fd90bSSasha Levin 	statl->st_uid		= KUIDT_INIT(st->st_uid);
530506fd90bSSasha Levin 	statl->st_gid		= KGIDT_INIT(st->st_gid);
531c797b6c6SAneesh Kumar K.V 	statl->st_rdev		= st->st_rdev;
532c797b6c6SAneesh Kumar K.V 	statl->st_size		= st->st_size;
533c797b6c6SAneesh Kumar K.V 	statl->st_blksize	= st->st_blksize;
534c797b6c6SAneesh Kumar K.V 	statl->st_blocks	= st->st_blocks;
535c797b6c6SAneesh Kumar K.V 	statl->st_atime_sec	= st->st_atime;
536c797b6c6SAneesh Kumar K.V 	statl->st_atime_nsec	= st->st_atim.tv_nsec;
537c797b6c6SAneesh Kumar K.V 	statl->st_mtime_sec	= st->st_mtime;
538c797b6c6SAneesh Kumar K.V 	statl->st_mtime_nsec	= st->st_mtim.tv_nsec;
539c797b6c6SAneesh Kumar K.V 	statl->st_ctime_sec	= st->st_ctime;
540c797b6c6SAneesh Kumar K.V 	statl->st_ctime_nsec	= st->st_ctim.tv_nsec;
541c797b6c6SAneesh Kumar K.V 	/* Currently we only support BASIC fields in stat */
542c797b6c6SAneesh Kumar K.V 	statl->st_result_mask	= P9_STATS_BASIC;
543c797b6c6SAneesh Kumar K.V 	stat2qid(st, &statl->qid);
5441c7850f9SSasha Levin }
5451c7850f9SSasha Levin 
546ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
547af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
5481c7850f9SSasha Levin {
549bfc15268SAneesh Kumar K.V 	u64 offset;
550bfc15268SAneesh Kumar K.V 	u32 fid_val;
551c797b6c6SAneesh Kumar K.V 	u16 iov_cnt;
552c797b6c6SAneesh Kumar K.V 	void *iov_base;
553c797b6c6SAneesh Kumar K.V 	size_t iov_len;
554bfc15268SAneesh Kumar K.V 	u32 count, rcount;
555bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
556c797b6c6SAneesh Kumar K.V 
5571c7850f9SSasha Levin 
558bfc15268SAneesh Kumar K.V 	rcount = 0;
559bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
56031a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
56150c479e0SAneesh Kumar K.V 
56250c479e0SAneesh Kumar K.V 	iov_base = pdu->in_iov[0].iov_base;
56350c479e0SAneesh Kumar K.V 	iov_len  = pdu->in_iov[0].iov_len;
56450c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->in_iov_cnt;
5655529bcd7SAsias He 	pdu->in_iov[0].iov_base += VIRTIO_9P_HDR_LEN + sizeof(u32);
5665529bcd7SAsias He 	pdu->in_iov[0].iov_len -= VIRTIO_9P_HDR_LEN + sizeof(u32);
5676b163a87SAneesh Kumar K.V 	pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
568bfc15268SAneesh Kumar K.V 						   count,
5696b163a87SAneesh Kumar K.V 						   pdu->in_iov_cnt);
570bfc15268SAneesh Kumar K.V 	rcount = preadv(fid->fd, pdu->in_iov,
571bfc15268SAneesh Kumar K.V 			pdu->in_iov_cnt, offset);
572bfc15268SAneesh Kumar K.V 	if (rcount > count)
573bfc15268SAneesh Kumar K.V 		rcount = count;
574bfc15268SAneesh Kumar K.V 	/*
575bfc15268SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
576bfc15268SAneesh Kumar K.V 	 * pdu_writef works correctly.
577bfc15268SAneesh Kumar K.V 	 */
57850c479e0SAneesh Kumar K.V 	pdu->in_iov[0].iov_base = iov_base;
57950c479e0SAneesh Kumar K.V 	pdu->in_iov[0].iov_len  = iov_len;
58050c479e0SAneesh Kumar K.V 	pdu->in_iov_cnt         = iov_cnt;
581c797b6c6SAneesh Kumar K.V 
5825529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
583bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
584bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
585bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
586ead43b01SAneesh Kumar K.V 	return;
5871c7850f9SSasha Levin }
5881c7850f9SSasha Levin 
589c797b6c6SAneesh Kumar K.V static int virtio_p9_dentry_size(struct dirent *dent)
590c797b6c6SAneesh Kumar K.V {
591c797b6c6SAneesh Kumar K.V 	/*
592c797b6c6SAneesh Kumar K.V 	 * Size of each dirent:
593c797b6c6SAneesh Kumar K.V 	 * qid(13) + offset(8) + type(1) + name_len(2) + name
594c797b6c6SAneesh Kumar K.V 	 */
595c797b6c6SAneesh Kumar K.V 	return 24 + strlen(dent->d_name);
596c797b6c6SAneesh Kumar K.V }
597c797b6c6SAneesh Kumar K.V 
598c797b6c6SAneesh Kumar K.V static void virtio_p9_readdir(struct p9_dev *p9dev,
599c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
600c797b6c6SAneesh Kumar K.V {
601c797b6c6SAneesh Kumar K.V 	u32 fid_val;
602c797b6c6SAneesh Kumar K.V 	u32 count, rcount;
603c797b6c6SAneesh Kumar K.V 	struct stat st;
604c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
605c797b6c6SAneesh Kumar K.V 	struct dirent *dent;
606c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
607c797b6c6SAneesh Kumar K.V 	u64 offset, old_offset;
608c797b6c6SAneesh Kumar K.V 
609c797b6c6SAneesh Kumar K.V 	rcount = 0;
610c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
61131a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
612c797b6c6SAneesh Kumar K.V 
61332585666SSasha Levin 	if (!is_dir(fid)) {
61469bb4278SSasha Levin 		errno = EINVAL;
615c797b6c6SAneesh Kumar K.V 		goto err_out;
616c797b6c6SAneesh Kumar K.V 	}
617c797b6c6SAneesh Kumar K.V 
618c797b6c6SAneesh Kumar K.V 	/* Move the offset specified */
619c797b6c6SAneesh Kumar K.V 	seekdir(fid->dir, offset);
620c797b6c6SAneesh Kumar K.V 
621c797b6c6SAneesh Kumar K.V 	old_offset = offset;
622c797b6c6SAneesh Kumar K.V 	/* If reading a dir, fill the buffer with p9_stat entries */
623c797b6c6SAneesh Kumar K.V 	dent = readdir(fid->dir);
624c797b6c6SAneesh Kumar K.V 
625c797b6c6SAneesh Kumar K.V 	/* Skip the space for writing count */
626c797b6c6SAneesh Kumar K.V 	pdu->write_offset += sizeof(u32);
627c797b6c6SAneesh Kumar K.V 	while (dent) {
628c797b6c6SAneesh Kumar K.V 		u32 read;
629c797b6c6SAneesh Kumar K.V 		struct p9_qid qid;
630c797b6c6SAneesh Kumar K.V 
631c797b6c6SAneesh Kumar K.V 		if ((rcount + virtio_p9_dentry_size(dent)) > count) {
632c797b6c6SAneesh Kumar K.V 			/* seek to the previous offset and return */
633c797b6c6SAneesh Kumar K.V 			seekdir(fid->dir, old_offset);
634c797b6c6SAneesh Kumar K.V 			break;
635c797b6c6SAneesh Kumar K.V 		}
636c797b6c6SAneesh Kumar K.V 		old_offset = dent->d_off;
637c797b6c6SAneesh Kumar K.V 		lstat(rel_to_abs(p9dev, dent->d_name, full_path), &st);
638c797b6c6SAneesh Kumar K.V 		stat2qid(&st, &qid);
639c797b6c6SAneesh Kumar K.V 		read = pdu->write_offset;
640c797b6c6SAneesh Kumar K.V 		virtio_p9_pdu_writef(pdu, "Qqbs", &qid, dent->d_off,
641c797b6c6SAneesh Kumar K.V 				     dent->d_type, dent->d_name);
642c797b6c6SAneesh Kumar K.V 		rcount += pdu->write_offset - read;
643c797b6c6SAneesh Kumar K.V 		dent = readdir(fid->dir);
644c797b6c6SAneesh Kumar K.V 	}
645c797b6c6SAneesh Kumar K.V 
6465529bcd7SAsias He 	pdu->write_offset = VIRTIO_9P_HDR_LEN;
647c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
648c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
649c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
650c797b6c6SAneesh Kumar K.V 	return;
651c797b6c6SAneesh Kumar K.V err_out:
652c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
653c797b6c6SAneesh Kumar K.V 	return;
654c797b6c6SAneesh Kumar K.V }
655c797b6c6SAneesh Kumar K.V 
656c797b6c6SAneesh Kumar K.V 
657c797b6c6SAneesh Kumar K.V static void virtio_p9_getattr(struct p9_dev *p9dev,
658af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
6591c7850f9SSasha Levin {
660bfc15268SAneesh Kumar K.V 	u32 fid_val;
661af045e53SAneesh Kumar K.V 	struct stat st;
662c797b6c6SAneesh Kumar K.V 	u64 request_mask;
663bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
664c797b6c6SAneesh Kumar K.V 	struct p9_stat_dotl statl;
6651c7850f9SSasha Levin 
666c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dq", &fid_val, &request_mask);
66731a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
66830204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
6696c8ca053SAneesh Kumar K.V 		goto err_out;
6701c7850f9SSasha Levin 
671c797b6c6SAneesh Kumar K.V 	virtio_p9_fill_stat(p9dev, &st, &statl);
672c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "A", &statl);
673bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
674bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
675ead43b01SAneesh Kumar K.V 	return;
6766c8ca053SAneesh Kumar K.V err_out:
6776c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
6786c8ca053SAneesh Kumar K.V 	return;
6791c7850f9SSasha Levin }
6801c7850f9SSasha Levin 
681c797b6c6SAneesh Kumar K.V /* FIXME!! from linux/fs.h */
682c797b6c6SAneesh Kumar K.V /*
683c797b6c6SAneesh Kumar K.V  * Attribute flags.  These should be or-ed together to figure out what
684c797b6c6SAneesh Kumar K.V  * has been changed!
685c797b6c6SAneesh Kumar K.V  */
686c797b6c6SAneesh Kumar K.V #define ATTR_MODE	(1 << 0)
687c797b6c6SAneesh Kumar K.V #define ATTR_UID	(1 << 1)
688c797b6c6SAneesh Kumar K.V #define ATTR_GID	(1 << 2)
689c797b6c6SAneesh Kumar K.V #define ATTR_SIZE	(1 << 3)
690c797b6c6SAneesh Kumar K.V #define ATTR_ATIME	(1 << 4)
691c797b6c6SAneesh Kumar K.V #define ATTR_MTIME	(1 << 5)
692c797b6c6SAneesh Kumar K.V #define ATTR_CTIME	(1 << 6)
693c797b6c6SAneesh Kumar K.V #define ATTR_ATIME_SET	(1 << 7)
694c797b6c6SAneesh Kumar K.V #define ATTR_MTIME_SET	(1 << 8)
695c797b6c6SAneesh Kumar K.V #define ATTR_FORCE	(1 << 9) /* Not a change, but a change it */
696c797b6c6SAneesh Kumar K.V #define ATTR_ATTR_FLAG	(1 << 10)
697c797b6c6SAneesh Kumar K.V #define ATTR_KILL_SUID	(1 << 11)
698c797b6c6SAneesh Kumar K.V #define ATTR_KILL_SGID	(1 << 12)
699c797b6c6SAneesh Kumar K.V #define ATTR_FILE	(1 << 13)
700c797b6c6SAneesh Kumar K.V #define ATTR_KILL_PRIV	(1 << 14)
701c797b6c6SAneesh Kumar K.V #define ATTR_OPEN	(1 << 15) /* Truncating from open(O_TRUNC) */
702c797b6c6SAneesh Kumar K.V #define ATTR_TIMES_SET	(1 << 16)
703c797b6c6SAneesh Kumar K.V 
704c797b6c6SAneesh Kumar K.V #define ATTR_MASK    127
705c797b6c6SAneesh Kumar K.V 
706c797b6c6SAneesh Kumar K.V static void virtio_p9_setattr(struct p9_dev *p9dev,
707af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
7081c7850f9SSasha Levin {
709c797b6c6SAneesh Kumar K.V 	int ret = 0;
710bfc15268SAneesh Kumar K.V 	u32 fid_val;
711bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
712c797b6c6SAneesh Kumar K.V 	struct p9_iattr_dotl p9attr;
7131c7850f9SSasha Levin 
714c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dI", &fid_val, &p9attr);
71531a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
7161c7850f9SSasha Levin 
717c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & ATTR_MODE) {
718c797b6c6SAneesh Kumar K.V 		ret = chmod(fid->abs_path, p9attr.mode);
719c797b6c6SAneesh Kumar K.V 		if (ret < 0)
720c797b6c6SAneesh Kumar K.V 			goto err_out;
721c797b6c6SAneesh Kumar K.V 	}
722c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & (ATTR_ATIME | ATTR_MTIME)) {
723c797b6c6SAneesh Kumar K.V 		struct timespec times[2];
724c797b6c6SAneesh Kumar K.V 		if (p9attr.valid & ATTR_ATIME) {
725c797b6c6SAneesh Kumar K.V 			if (p9attr.valid & ATTR_ATIME_SET) {
726c797b6c6SAneesh Kumar K.V 				times[0].tv_sec = p9attr.atime_sec;
727c797b6c6SAneesh Kumar K.V 				times[0].tv_nsec = p9attr.atime_nsec;
728c797b6c6SAneesh Kumar K.V 			} else {
729c797b6c6SAneesh Kumar K.V 				times[0].tv_nsec = UTIME_NOW;
730c797b6c6SAneesh Kumar K.V 			}
731c797b6c6SAneesh Kumar K.V 		} else {
732c797b6c6SAneesh Kumar K.V 			times[0].tv_nsec = UTIME_OMIT;
733c797b6c6SAneesh Kumar K.V 		}
734c797b6c6SAneesh Kumar K.V 		if (p9attr.valid & ATTR_MTIME) {
735c797b6c6SAneesh Kumar K.V 			if (p9attr.valid & ATTR_MTIME_SET) {
736c797b6c6SAneesh Kumar K.V 				times[1].tv_sec = p9attr.mtime_sec;
737c797b6c6SAneesh Kumar K.V 				times[1].tv_nsec = p9attr.mtime_nsec;
738c797b6c6SAneesh Kumar K.V 			} else {
739c797b6c6SAneesh Kumar K.V 				times[1].tv_nsec = UTIME_NOW;
740c797b6c6SAneesh Kumar K.V 			}
741c797b6c6SAneesh Kumar K.V 		} else
742c797b6c6SAneesh Kumar K.V 			times[1].tv_nsec = UTIME_OMIT;
743c797b6c6SAneesh Kumar K.V 
744c797b6c6SAneesh Kumar K.V 		ret = utimensat(-1, fid->abs_path, times, AT_SYMLINK_NOFOLLOW);
745c797b6c6SAneesh Kumar K.V 		if (ret < 0)
746c797b6c6SAneesh Kumar K.V 			goto err_out;
747c797b6c6SAneesh Kumar K.V 	}
748c797b6c6SAneesh Kumar K.V 	/*
749c797b6c6SAneesh Kumar K.V 	 * If the only valid entry in iattr is ctime we can call
750c797b6c6SAneesh Kumar K.V 	 * chown(-1,-1) to update the ctime of the file
751c797b6c6SAneesh Kumar K.V 	 */
752c797b6c6SAneesh Kumar K.V 	if ((p9attr.valid & (ATTR_UID | ATTR_GID)) ||
753c797b6c6SAneesh Kumar K.V 	    ((p9attr.valid & ATTR_CTIME)
754c797b6c6SAneesh Kumar K.V 	     && !((p9attr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
755c797b6c6SAneesh Kumar K.V 		if (!(p9attr.valid & ATTR_UID))
756506fd90bSSasha Levin 			p9attr.uid = KUIDT_INIT(-1);
757c797b6c6SAneesh Kumar K.V 
758c797b6c6SAneesh Kumar K.V 		if (!(p9attr.valid & ATTR_GID))
759506fd90bSSasha Levin 			p9attr.gid = KGIDT_INIT(-1);
760c797b6c6SAneesh Kumar K.V 
761506fd90bSSasha Levin 		ret = lchown(fid->abs_path, __kuid_val(p9attr.uid),
762506fd90bSSasha Levin 				__kgid_val(p9attr.gid));
763c797b6c6SAneesh Kumar K.V 		if (ret < 0)
764c797b6c6SAneesh Kumar K.V 			goto err_out;
765c797b6c6SAneesh Kumar K.V 	}
766c797b6c6SAneesh Kumar K.V 	if (p9attr.valid & (ATTR_SIZE)) {
767c797b6c6SAneesh Kumar K.V 		ret = truncate(fid->abs_path, p9attr.size);
768c797b6c6SAneesh Kumar K.V 		if (ret < 0)
769c797b6c6SAneesh Kumar K.V 			goto err_out;
770c797b6c6SAneesh Kumar K.V 	}
7715529bcd7SAsias He 	*outlen = VIRTIO_9P_HDR_LEN;
772bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
773ead43b01SAneesh Kumar K.V 	return;
7744bc9734aSAneesh Kumar K.V err_out:
7754bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
7764bc9734aSAneesh Kumar K.V 	return;
7771c7850f9SSasha Levin }
7781c7850f9SSasha Levin 
779ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
780af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
7811c7850f9SSasha Levin {
7824bc9734aSAneesh Kumar K.V 
783bfc15268SAneesh Kumar K.V 	u64 offset;
784bfc15268SAneesh Kumar K.V 	u32 fid_val;
7854bc9734aSAneesh Kumar K.V 	u32 count;
7864bc9734aSAneesh Kumar K.V 	ssize_t res;
78750c479e0SAneesh Kumar K.V 	u16 iov_cnt;
78850c479e0SAneesh Kumar K.V 	void *iov_base;
78950c479e0SAneesh Kumar K.V 	size_t iov_len;
790bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
791b064b05aSAneesh Kumar K.V 	/* u32 fid + u64 offset + u32 count */
792b064b05aSAneesh Kumar K.V 	int twrite_size = sizeof(u32) + sizeof(u64) + sizeof(u32);
7931c7850f9SSasha Levin 
794bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
79531a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
796af045e53SAneesh Kumar K.V 
79750c479e0SAneesh Kumar K.V 	iov_base = pdu->out_iov[0].iov_base;
79850c479e0SAneesh Kumar K.V 	iov_len  = pdu->out_iov[0].iov_len;
79950c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->out_iov_cnt;
80050c479e0SAneesh Kumar K.V 
801bfc15268SAneesh Kumar K.V 	/* Adjust the iovec to skip the header and meta data */
802b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) + twrite_size);
803b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_len -=  (sizeof(struct p9_msg) + twrite_size);
804bfc15268SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count,
8056b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
8064bc9734aSAneesh Kumar K.V 	res = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset);
80750c479e0SAneesh Kumar K.V 	/*
80850c479e0SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
80950c479e0SAneesh Kumar K.V 	 * pdu_readf works correctly.
81050c479e0SAneesh Kumar K.V 	 */
81150c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_base = iov_base;
81250c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_len  = iov_len;
81350c479e0SAneesh Kumar K.V 	pdu->out_iov_cnt         = iov_cnt;
814c797b6c6SAneesh Kumar K.V 
8154bc9734aSAneesh Kumar K.V 	if (res < 0)
8164bc9734aSAneesh Kumar K.V 		goto err_out;
8174bc9734aSAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", res);
818bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
819bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
820ead43b01SAneesh Kumar K.V 	return;
8214bc9734aSAneesh Kumar K.V err_out:
8224bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
8234bc9734aSAneesh Kumar K.V 	return;
8241c7850f9SSasha Levin }
8251c7850f9SSasha Levin 
8266fc5cd9bSSasha Levin static void virtio_p9_remove(struct p9_dev *p9dev,
8276fc5cd9bSSasha Levin 			     struct p9_pdu *pdu, u32 *outlen)
8286fc5cd9bSSasha Levin {
8296fc5cd9bSSasha Levin 	int ret;
8306fc5cd9bSSasha Levin 	u32 fid_val;
8316fc5cd9bSSasha Levin 	struct p9_fid *fid;
8326fc5cd9bSSasha Levin 
8336fc5cd9bSSasha Levin 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
83431a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
8356fc5cd9bSSasha Levin 
8369b604a9cSSasha Levin 	ret = remove(fid->abs_path);
8376fc5cd9bSSasha Levin 	if (ret < 0)
8386fc5cd9bSSasha Levin 		goto err_out;
8396fc5cd9bSSasha Levin 	*outlen = pdu->write_offset;
8406fc5cd9bSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
8416fc5cd9bSSasha Levin 	return;
8426fc5cd9bSSasha Levin 
8436fc5cd9bSSasha Levin err_out:
8446fc5cd9bSSasha Levin 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
8456fc5cd9bSSasha Levin 	return;
8466fc5cd9bSSasha Levin }
8476fc5cd9bSSasha Levin 
848f161f28bSSasha Levin static void virtio_p9_rename(struct p9_dev *p9dev,
849f161f28bSSasha Levin 			     struct p9_pdu *pdu, u32 *outlen)
850f161f28bSSasha Levin {
851f161f28bSSasha Levin 	int ret;
852f161f28bSSasha Levin 	u32 fid_val, new_fid_val;
853f161f28bSSasha Levin 	struct p9_fid *fid, *new_fid;
854f161f28bSSasha Levin 	char full_path[PATH_MAX], *new_name;
855f161f28bSSasha Levin 
856f161f28bSSasha Levin 	virtio_p9_pdu_readf(pdu, "dds", &fid_val, &new_fid_val, &new_name);
85731a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
85831a6fb8dSSasha Levin 	new_fid = get_fid(p9dev, new_fid_val);
859f161f28bSSasha Levin 
86032832dd1SG. Campana 	ret = snprintf(full_path, sizeof(full_path), "%s/%s", new_fid->abs_path, new_name);
86132832dd1SG. Campana 	if (ret >= (int)sizeof(full_path)) {
86232832dd1SG. Campana 		errno = ENAMETOOLONG;
86332832dd1SG. Campana 		goto err_out;
86432832dd1SG. Campana 	}
86532832dd1SG. Campana 
8669bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
8679bb99a82SG. Campana 		errno = EACCES;
8689bb99a82SG. Campana 		goto err_out;
8699bb99a82SG. Campana 	}
8709bb99a82SG. Campana 
871f161f28bSSasha Levin 	ret = rename(fid->abs_path, full_path);
872f161f28bSSasha Levin 	if (ret < 0)
873f161f28bSSasha Levin 		goto err_out;
874f161f28bSSasha Levin 	*outlen = pdu->write_offset;
875f161f28bSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
876f161f28bSSasha Levin 	return;
877f161f28bSSasha Levin 
878f161f28bSSasha Levin err_out:
879f161f28bSSasha Levin 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
880f161f28bSSasha Levin 	return;
881f161f28bSSasha Levin }
882f161f28bSSasha Levin 
883c797b6c6SAneesh Kumar K.V static void virtio_p9_readlink(struct p9_dev *p9dev,
884c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
885c797b6c6SAneesh Kumar K.V {
886c797b6c6SAneesh Kumar K.V 	int ret;
887c797b6c6SAneesh Kumar K.V 	u32 fid_val;
888c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
889c797b6c6SAneesh Kumar K.V 	char target_path[PATH_MAX];
890c797b6c6SAneesh Kumar K.V 
891c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
89231a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
893c797b6c6SAneesh Kumar K.V 
894c797b6c6SAneesh Kumar K.V 	memset(target_path, 0, PATH_MAX);
895c797b6c6SAneesh Kumar K.V 	ret = readlink(fid->abs_path, target_path, PATH_MAX - 1);
896c797b6c6SAneesh Kumar K.V 	if (ret < 0)
897c797b6c6SAneesh Kumar K.V 		goto err_out;
898c797b6c6SAneesh Kumar K.V 
899c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "s", target_path);
900c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
901c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
902c797b6c6SAneesh Kumar K.V 	return;
903c797b6c6SAneesh Kumar K.V err_out:
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_statfs(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 	u64 fsid;
913c797b6c6SAneesh Kumar K.V 	u32 fid_val;
914c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
915c797b6c6SAneesh Kumar K.V 	struct statfs stat_buf;
916c797b6c6SAneesh Kumar K.V 
917c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
91831a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
919c797b6c6SAneesh Kumar K.V 
920c797b6c6SAneesh Kumar K.V 	ret = statfs(fid->abs_path, &stat_buf);
921c797b6c6SAneesh Kumar K.V 	if (ret < 0)
922c797b6c6SAneesh Kumar K.V 		goto err_out;
923c797b6c6SAneesh Kumar K.V 	/* FIXME!! f_blocks needs update based on client msize */
924c797b6c6SAneesh Kumar K.V 	fsid = (unsigned int) stat_buf.f_fsid.__val[0] |
925c797b6c6SAneesh Kumar K.V 		(unsigned long long)stat_buf.f_fsid.__val[1] << 32;
926c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "ddqqqqqqd", stat_buf.f_type,
927c797b6c6SAneesh Kumar K.V 			     stat_buf.f_bsize, stat_buf.f_blocks,
928c797b6c6SAneesh Kumar K.V 			     stat_buf.f_bfree, stat_buf.f_bavail,
929c797b6c6SAneesh Kumar K.V 			     stat_buf.f_files, stat_buf.f_ffree,
930c797b6c6SAneesh Kumar K.V 			     fsid, stat_buf.f_namelen);
931c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
932c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
933c797b6c6SAneesh Kumar K.V 	return;
934c797b6c6SAneesh Kumar K.V err_out:
935c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
936c797b6c6SAneesh Kumar K.V 	return;
937c797b6c6SAneesh Kumar K.V }
938c797b6c6SAneesh Kumar K.V 
939c797b6c6SAneesh Kumar K.V static void virtio_p9_mknod(struct p9_dev *p9dev,
940c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
941c797b6c6SAneesh Kumar K.V {
942c797b6c6SAneesh Kumar K.V 	int ret;
943c797b6c6SAneesh Kumar K.V 	char *name;
944c797b6c6SAneesh Kumar K.V 	struct stat st;
945c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
946c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
947c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
948c797b6c6SAneesh Kumar K.V 	u32 fid_val, mode, major, minor, gid;
949c797b6c6SAneesh Kumar K.V 
950c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdddd", &fid_val, &name, &mode,
951c797b6c6SAneesh Kumar K.V 			    &major, &minor, &gid);
952c797b6c6SAneesh Kumar K.V 
95331a6fb8dSSasha Levin 	dfid = get_fid(p9dev, fid_val);
95432832dd1SG. Campana 	ret = snprintf(full_path, sizeof(full_path), "%s/%s", dfid->abs_path, name);
95532832dd1SG. Campana 	if (ret >= (int)sizeof(full_path)) {
95632832dd1SG. Campana 		errno = ENAMETOOLONG;
95732832dd1SG. Campana 		goto err_out;
95832832dd1SG. Campana 	}
95932832dd1SG. Campana 
9609bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
9619bb99a82SG. Campana 		errno = EACCES;
9629bb99a82SG. Campana 		goto err_out;
9639bb99a82SG. Campana 	}
9649bb99a82SG. Campana 
965c797b6c6SAneesh Kumar K.V 	ret = mknod(full_path, mode, makedev(major, minor));
966c797b6c6SAneesh Kumar K.V 	if (ret < 0)
967c797b6c6SAneesh Kumar K.V 		goto err_out;
968c797b6c6SAneesh Kumar K.V 
969c797b6c6SAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
970c797b6c6SAneesh Kumar K.V 		goto err_out;
971c797b6c6SAneesh Kumar K.V 
972c797b6c6SAneesh Kumar K.V 	ret = chmod(full_path, mode & 0777);
973c797b6c6SAneesh Kumar K.V 	if (ret < 0)
974c797b6c6SAneesh Kumar K.V 		goto err_out;
975c797b6c6SAneesh Kumar K.V 
976c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
977c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
978c797b6c6SAneesh Kumar K.V 	free(name);
979c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
980c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
981c797b6c6SAneesh Kumar K.V 	return;
982c797b6c6SAneesh Kumar K.V err_out:
983c797b6c6SAneesh Kumar K.V 	free(name);
984c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
985c797b6c6SAneesh Kumar K.V 	return;
986c797b6c6SAneesh Kumar K.V }
987c797b6c6SAneesh Kumar K.V 
988c797b6c6SAneesh Kumar K.V static void virtio_p9_fsync(struct p9_dev *p9dev,
989c797b6c6SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
990c797b6c6SAneesh Kumar K.V {
991644140efSRussell King 	int ret, fd;
992c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
993c797b6c6SAneesh Kumar K.V 	u32 fid_val, datasync;
994c797b6c6SAneesh Kumar K.V 
995c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dd", &fid_val, &datasync);
99631a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
997c797b6c6SAneesh Kumar K.V 
998644140efSRussell King 	if (fid->dir)
999644140efSRussell King 		fd = dirfd(fid->dir);
1000c797b6c6SAneesh Kumar K.V 	else
1001644140efSRussell King 		fd = fid->fd;
1002644140efSRussell King 
1003644140efSRussell King 	if (datasync)
1004644140efSRussell King 		ret = fdatasync(fd);
1005644140efSRussell King 	else
1006644140efSRussell King 		ret = fsync(fd);
1007c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1008c797b6c6SAneesh Kumar K.V 		goto err_out;
1009c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1010c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1011c797b6c6SAneesh Kumar K.V 	return;
1012c797b6c6SAneesh Kumar K.V err_out:
1013c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1014c797b6c6SAneesh Kumar K.V 	return;
1015c797b6c6SAneesh Kumar K.V }
1016c797b6c6SAneesh Kumar K.V 
1017c797b6c6SAneesh Kumar K.V static void virtio_p9_symlink(struct p9_dev *p9dev,
1018c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1019c797b6c6SAneesh Kumar K.V {
1020c797b6c6SAneesh Kumar K.V 	int ret;
1021c797b6c6SAneesh Kumar K.V 	struct stat st;
1022c797b6c6SAneesh Kumar K.V 	u32 fid_val, gid;
1023c797b6c6SAneesh Kumar K.V 	struct p9_qid qid;
1024c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid;
1025c797b6c6SAneesh Kumar K.V 	char new_name[PATH_MAX];
1026c797b6c6SAneesh Kumar K.V 	char *old_path, *name;
1027c797b6c6SAneesh Kumar K.V 
1028c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dssd", &fid_val, &name, &old_path, &gid);
1029c797b6c6SAneesh Kumar K.V 
103031a6fb8dSSasha Levin 	dfid = get_fid(p9dev, fid_val);
103132832dd1SG. Campana 	ret = snprintf(new_name, sizeof(new_name), "%s/%s", dfid->abs_path, name);
103232832dd1SG. Campana 	if (ret >= (int)sizeof(new_name)) {
103332832dd1SG. Campana 		errno = ENAMETOOLONG;
103432832dd1SG. Campana 		goto err_out;
103532832dd1SG. Campana 	}
103632832dd1SG. Campana 
10379bb99a82SG. Campana 	if (path_is_illegal(new_name)) {
10389bb99a82SG. Campana 		errno = EACCES;
10399bb99a82SG. Campana 		goto err_out;
10409bb99a82SG. Campana 	}
10419bb99a82SG. Campana 
1042c797b6c6SAneesh Kumar K.V 	ret = symlink(old_path, new_name);
1043c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1044c797b6c6SAneesh Kumar K.V 		goto err_out;
1045c797b6c6SAneesh Kumar K.V 
1046c797b6c6SAneesh Kumar K.V 	if (lstat(new_name, &st) < 0)
1047c797b6c6SAneesh Kumar K.V 		goto err_out;
1048c797b6c6SAneesh Kumar K.V 
1049c797b6c6SAneesh Kumar K.V 	stat2qid(&st, &qid);
1050c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
1051c797b6c6SAneesh Kumar K.V 	free(name);
1052c797b6c6SAneesh Kumar K.V 	free(old_path);
1053c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1054c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1055c797b6c6SAneesh Kumar K.V 	return;
1056c797b6c6SAneesh Kumar K.V err_out:
1057c797b6c6SAneesh Kumar K.V 	free(name);
1058c797b6c6SAneesh Kumar K.V 	free(old_path);
1059c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1060c797b6c6SAneesh Kumar K.V 	return;
1061c797b6c6SAneesh Kumar K.V }
1062c797b6c6SAneesh Kumar K.V 
1063c797b6c6SAneesh Kumar K.V static void virtio_p9_link(struct p9_dev *p9dev,
1064c797b6c6SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
1065c797b6c6SAneesh Kumar K.V {
1066c797b6c6SAneesh Kumar K.V 	int ret;
1067c797b6c6SAneesh Kumar K.V 	char *name;
1068c797b6c6SAneesh Kumar K.V 	u32 fid_val, dfid_val;
1069c797b6c6SAneesh Kumar K.V 	struct p9_fid *dfid, *fid;
1070c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
1071c797b6c6SAneesh Kumar K.V 
1072c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dds", &dfid_val, &fid_val, &name);
1073c797b6c6SAneesh Kumar K.V 
107431a6fb8dSSasha Levin 	dfid = get_fid(p9dev, dfid_val);
107531a6fb8dSSasha Levin 	fid =  get_fid(p9dev, fid_val);
107632832dd1SG. Campana 	ret = snprintf(full_path, sizeof(full_path), "%s/%s", dfid->abs_path, name);
107732832dd1SG. Campana 	if (ret >= (int)sizeof(full_path)) {
107832832dd1SG. Campana 		errno = ENAMETOOLONG;
107932832dd1SG. Campana 		goto err_out;
108032832dd1SG. Campana 	}
108132832dd1SG. Campana 
10829bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
10839bb99a82SG. Campana 		errno = EACCES;
10849bb99a82SG. Campana 		goto err_out;
10859bb99a82SG. Campana 	}
10869bb99a82SG. Campana 
1087c797b6c6SAneesh Kumar K.V 	ret = link(fid->abs_path, full_path);
1088c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1089c797b6c6SAneesh Kumar K.V 		goto err_out;
1090c797b6c6SAneesh Kumar K.V 	free(name);
1091c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1092c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1093c797b6c6SAneesh Kumar K.V 	return;
1094c797b6c6SAneesh Kumar K.V err_out:
1095c797b6c6SAneesh Kumar K.V 	free(name);
1096c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1097c797b6c6SAneesh Kumar K.V 	return;
1098c797b6c6SAneesh Kumar K.V 
1099c797b6c6SAneesh Kumar K.V }
1100c797b6c6SAneesh Kumar K.V 
1101c797b6c6SAneesh Kumar K.V static void virtio_p9_lock(struct p9_dev *p9dev,
1102c797b6c6SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
1103c797b6c6SAneesh Kumar K.V {
1104c797b6c6SAneesh Kumar K.V 	u8 ret;
1105c797b6c6SAneesh Kumar K.V 	u32 fid_val;
1106c797b6c6SAneesh Kumar K.V 	struct p9_flock flock;
1107c797b6c6SAneesh Kumar K.V 
1108c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dbdqqds", &fid_val, &flock.type,
1109c797b6c6SAneesh Kumar K.V 			    &flock.flags, &flock.start, &flock.length,
1110c797b6c6SAneesh Kumar K.V 			    &flock.proc_id, &flock.client_id);
1111c797b6c6SAneesh Kumar K.V 
1112c797b6c6SAneesh Kumar K.V 	/* Just return success */
1113c797b6c6SAneesh Kumar K.V 	ret = P9_LOCK_SUCCESS;
1114c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", ret);
1115c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1116c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1117c797b6c6SAneesh Kumar K.V 	free(flock.client_id);
1118c797b6c6SAneesh Kumar K.V 	return;
1119c797b6c6SAneesh Kumar K.V }
1120c797b6c6SAneesh Kumar K.V 
1121c797b6c6SAneesh Kumar K.V static void virtio_p9_getlock(struct p9_dev *p9dev,
1122c797b6c6SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1123c797b6c6SAneesh Kumar K.V {
1124c797b6c6SAneesh Kumar K.V 	u32 fid_val;
1125c797b6c6SAneesh Kumar K.V 	struct p9_getlock glock;
1126c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dbqqds", &fid_val, &glock.type,
1127c797b6c6SAneesh Kumar K.V 			    &glock.start, &glock.length, &glock.proc_id,
1128c797b6c6SAneesh Kumar K.V 			    &glock.client_id);
1129c797b6c6SAneesh Kumar K.V 
1130c797b6c6SAneesh Kumar K.V 	/* Just return success */
1131c797b6c6SAneesh Kumar K.V 	glock.type = F_UNLCK;
1132c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "bqqds", glock.type,
1133c797b6c6SAneesh Kumar K.V 			     glock.start, glock.length, glock.proc_id,
1134c797b6c6SAneesh Kumar K.V 			     glock.client_id);
1135c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1136c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1137c797b6c6SAneesh Kumar K.V 	free(glock.client_id);
1138c797b6c6SAneesh Kumar K.V 	return;
1139c797b6c6SAneesh Kumar K.V }
1140c797b6c6SAneesh Kumar K.V 
1141c797b6c6SAneesh Kumar K.V static int virtio_p9_ancestor(char *path, char *ancestor)
1142c797b6c6SAneesh Kumar K.V {
1143c797b6c6SAneesh Kumar K.V 	int size = strlen(ancestor);
1144c797b6c6SAneesh Kumar K.V 	if (!strncmp(path, ancestor, size)) {
1145c797b6c6SAneesh Kumar K.V 		/*
1146c797b6c6SAneesh Kumar K.V 		 * Now check whether ancestor is a full name or
1147c797b6c6SAneesh Kumar K.V 		 * or directory component and not just part
1148c797b6c6SAneesh Kumar K.V 		 * of a name.
1149c797b6c6SAneesh Kumar K.V 		 */
1150c797b6c6SAneesh Kumar K.V 		if (path[size] == '\0' || path[size] == '/')
1151c797b6c6SAneesh Kumar K.V 			return 1;
1152c797b6c6SAneesh Kumar K.V 	}
1153c797b6c6SAneesh Kumar K.V 	return 0;
1154c797b6c6SAneesh Kumar K.V }
1155c797b6c6SAneesh Kumar K.V 
1156*e277a1b4SG. Campana static int virtio_p9_fix_path(struct p9_fid *fid, char *old_name, char *new_name)
1157c797b6c6SAneesh Kumar K.V {
1158*e277a1b4SG. Campana 	int ret;
1159*e277a1b4SG. Campana 	char *p, tmp_name[PATH_MAX];
1160c797b6c6SAneesh Kumar K.V 	size_t rp_sz = strlen(old_name);
1161c797b6c6SAneesh Kumar K.V 
1162*e277a1b4SG. Campana 	if (rp_sz == strlen(fid->path)) {
1163c797b6c6SAneesh Kumar K.V 		/* replace the full name */
1164*e277a1b4SG. Campana 		p = new_name;
1165*e277a1b4SG. Campana 	} else {
1166c797b6c6SAneesh Kumar K.V 		/* save the trailing path details */
1167*e277a1b4SG. Campana 		ret = snprintf(tmp_name, sizeof(tmp_name), "%s%s", new_name, fid->path + rp_sz);
1168*e277a1b4SG. Campana 		if (ret >= (int)sizeof(tmp_name))
1169*e277a1b4SG. Campana 			return -1;
1170*e277a1b4SG. Campana 		p = tmp_name;
1171*e277a1b4SG. Campana 	}
1172*e277a1b4SG. Campana 
1173*e277a1b4SG. Campana 	return join_path(fid, p);
1174c797b6c6SAneesh Kumar K.V }
1175c797b6c6SAneesh Kumar K.V 
1176e2341580SSasha Levin static void rename_fids(struct p9_dev *p9dev, char *old_name, char *new_name)
1177e2341580SSasha Levin {
1178e2341580SSasha Levin 	struct rb_node *node = rb_first(&p9dev->fids);
1179e2341580SSasha Levin 
1180e2341580SSasha Levin 	while (node) {
1181e2341580SSasha Levin 		struct p9_fid *fid = rb_entry(node, struct p9_fid, node);
1182e2341580SSasha Levin 
1183e2341580SSasha Levin 		if (fid->fid != P9_NOFID && virtio_p9_ancestor(fid->path, old_name)) {
1184*e277a1b4SG. Campana 				virtio_p9_fix_path(fid, old_name, new_name);
1185e2341580SSasha Levin 		}
1186e2341580SSasha Levin 		node = rb_next(node);
1187e2341580SSasha Levin 	}
1188e2341580SSasha Levin }
1189e2341580SSasha Levin 
1190c797b6c6SAneesh Kumar K.V static void virtio_p9_renameat(struct p9_dev *p9dev,
1191c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
1192c797b6c6SAneesh Kumar K.V {
1193e2341580SSasha Levin 	int ret;
1194c797b6c6SAneesh Kumar K.V 	char *old_name, *new_name;
1195c797b6c6SAneesh Kumar K.V 	u32 old_dfid_val, new_dfid_val;
1196c797b6c6SAneesh Kumar K.V 	struct p9_fid *old_dfid, *new_dfid;
1197c797b6c6SAneesh Kumar K.V 	char old_full_path[PATH_MAX], new_full_path[PATH_MAX];
1198c797b6c6SAneesh Kumar K.V 
1199c797b6c6SAneesh Kumar K.V 
1200c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsds", &old_dfid_val, &old_name,
1201c797b6c6SAneesh Kumar K.V 			    &new_dfid_val, &new_name);
1202c797b6c6SAneesh Kumar K.V 
120331a6fb8dSSasha Levin 	old_dfid = get_fid(p9dev, old_dfid_val);
120431a6fb8dSSasha Levin 	new_dfid = get_fid(p9dev, new_dfid_val);
1205c797b6c6SAneesh Kumar K.V 
120632832dd1SG. Campana 	ret = snprintf(old_full_path, sizeof(old_full_path), "%s/%s", old_dfid->abs_path, old_name);
120732832dd1SG. Campana 	if (ret >= (int)sizeof(old_full_path)) {
120832832dd1SG. Campana 		errno = ENAMETOOLONG;
120932832dd1SG. Campana 		goto err_out;
121032832dd1SG. Campana 	}
121132832dd1SG. Campana 
121232832dd1SG. Campana 	ret = snprintf(new_full_path, sizeof(new_full_path), "%s/%s", new_dfid->abs_path, new_name);
121332832dd1SG. Campana 	if (ret >= (int)sizeof(new_full_path)) {
121432832dd1SG. Campana 		errno = ENAMETOOLONG;
121532832dd1SG. Campana 		goto err_out;
121632832dd1SG. Campana 	}
121732832dd1SG. Campana 
12189bb99a82SG. Campana 	if (path_is_illegal(old_full_path) || path_is_illegal(new_full_path)) {
12199bb99a82SG. Campana 		errno = EACCES;
12209bb99a82SG. Campana 		goto err_out;
12219bb99a82SG. Campana 	}
12229bb99a82SG. Campana 
1223c797b6c6SAneesh Kumar K.V 	ret = rename(old_full_path, new_full_path);
1224c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1225c797b6c6SAneesh Kumar K.V 		goto err_out;
1226c797b6c6SAneesh Kumar K.V 	/*
1227c797b6c6SAneesh Kumar K.V 	 * Now fix path in other fids, if the renamed path is part of
1228c797b6c6SAneesh Kumar K.V 	 * that.
1229c797b6c6SAneesh Kumar K.V 	 */
1230e2341580SSasha Levin 	rename_fids(p9dev, old_name, new_name);
1231c797b6c6SAneesh Kumar K.V 	free(old_name);
1232c797b6c6SAneesh Kumar K.V 	free(new_name);
1233c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1234c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1235c797b6c6SAneesh Kumar K.V 	return;
1236c797b6c6SAneesh Kumar K.V err_out:
1237c797b6c6SAneesh Kumar K.V 	free(old_name);
1238c797b6c6SAneesh Kumar K.V 	free(new_name);
1239c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1240c797b6c6SAneesh Kumar K.V 	return;
1241c797b6c6SAneesh Kumar K.V }
1242c797b6c6SAneesh Kumar K.V 
1243c797b6c6SAneesh Kumar K.V static void virtio_p9_unlinkat(struct p9_dev *p9dev,
1244c797b6c6SAneesh Kumar K.V 			       struct p9_pdu *pdu, u32 *outlen)
1245c797b6c6SAneesh Kumar K.V {
1246c797b6c6SAneesh Kumar K.V 	int ret;
1247c797b6c6SAneesh Kumar K.V 	char *name;
1248c797b6c6SAneesh Kumar K.V 	u32 fid_val, flags;
1249c797b6c6SAneesh Kumar K.V 	struct p9_fid *fid;
1250c797b6c6SAneesh Kumar K.V 	char full_path[PATH_MAX];
1251c797b6c6SAneesh Kumar K.V 
1252c797b6c6SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsd", &fid_val, &name, &flags);
125331a6fb8dSSasha Levin 	fid = get_fid(p9dev, fid_val);
1254c797b6c6SAneesh Kumar K.V 
125532832dd1SG. Campana 	ret = snprintf(full_path, sizeof(full_path), "%s/%s", fid->abs_path, name);
125632832dd1SG. Campana 	if (ret >= (int)sizeof(full_path)) {
125732832dd1SG. Campana 		errno = ENAMETOOLONG;
125832832dd1SG. Campana 		goto err_out;
125932832dd1SG. Campana 	}
126032832dd1SG. Campana 
12619bb99a82SG. Campana 	if (path_is_illegal(full_path)) {
12629bb99a82SG. Campana 		errno = EACCES;
12639bb99a82SG. Campana 		goto err_out;
12649bb99a82SG. Campana 	}
12659bb99a82SG. Campana 
1266c797b6c6SAneesh Kumar K.V 	ret = remove(full_path);
1267c797b6c6SAneesh Kumar K.V 	if (ret < 0)
1268c797b6c6SAneesh Kumar K.V 		goto err_out;
1269c797b6c6SAneesh Kumar K.V 	free(name);
1270c797b6c6SAneesh Kumar K.V 	*outlen = pdu->write_offset;
1271c797b6c6SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
1272c797b6c6SAneesh Kumar K.V 	return;
1273c797b6c6SAneesh Kumar K.V err_out:
1274c797b6c6SAneesh Kumar K.V 	free(name);
1275c797b6c6SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
1276c797b6c6SAneesh Kumar K.V 	return;
1277c797b6c6SAneesh Kumar K.V }
1278c797b6c6SAneesh Kumar K.V 
12795cc808aaSSasha Levin static void virtio_p9_flush(struct p9_dev *p9dev,
12805cc808aaSSasha Levin 				struct p9_pdu *pdu, u32 *outlen)
12815cc808aaSSasha Levin {
12825cc808aaSSasha Levin 	u16 tag, oldtag;
12835cc808aaSSasha Levin 
12845cc808aaSSasha Levin 	virtio_p9_pdu_readf(pdu, "ww", &tag, &oldtag);
12855cc808aaSSasha Levin 	virtio_p9_pdu_writef(pdu, "w", tag);
12865cc808aaSSasha Levin 	*outlen = pdu->write_offset;
12875cc808aaSSasha Levin 	virtio_p9_set_reply_header(pdu, *outlen);
12885cc808aaSSasha Levin 
12895cc808aaSSasha Levin 	return;
12905cc808aaSSasha Levin }
12915cc808aaSSasha Levin 
1292c797b6c6SAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev,
1293c797b6c6SAneesh Kumar K.V 				 struct p9_pdu *pdu, u32 *outlen)
1294c797b6c6SAneesh Kumar K.V {
1295c797b6c6SAneesh Kumar K.V 	return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen);
1296c797b6c6SAneesh Kumar K.V }
1297c797b6c6SAneesh Kumar K.V 
1298ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
1299af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
1300b4422bf3SAneesh Kumar K.V 
1301c797b6c6SAneesh Kumar K.V /* FIXME should be removed when merging with latest linus tree */
1302c797b6c6SAneesh Kumar K.V #define P9_TRENAMEAT 74
1303c797b6c6SAneesh Kumar K.V #define P9_TUNLINKAT 76
1304c797b6c6SAneesh Kumar K.V 
1305c797b6c6SAneesh Kumar K.V static p9_handler *virtio_9p_dotl_handler [] = {
1306c797b6c6SAneesh Kumar K.V 	[P9_TREADDIR]     = virtio_p9_readdir,
1307c797b6c6SAneesh Kumar K.V 	[P9_TSTATFS]      = virtio_p9_statfs,
1308c797b6c6SAneesh Kumar K.V 	[P9_TGETATTR]     = virtio_p9_getattr,
1309c797b6c6SAneesh Kumar K.V 	[P9_TSETATTR]     = virtio_p9_setattr,
1310c797b6c6SAneesh Kumar K.V 	[P9_TXATTRWALK]   = virtio_p9_eopnotsupp,
1311c797b6c6SAneesh Kumar K.V 	[P9_TXATTRCREATE] = virtio_p9_eopnotsupp,
1312c797b6c6SAneesh Kumar K.V 	[P9_TMKNOD]       = virtio_p9_mknod,
1313c797b6c6SAneesh Kumar K.V 	[P9_TLOCK]        = virtio_p9_lock,
1314c797b6c6SAneesh Kumar K.V 	[P9_TGETLOCK]     = virtio_p9_getlock,
1315c797b6c6SAneesh Kumar K.V 	[P9_TRENAMEAT]    = virtio_p9_renameat,
1316c797b6c6SAneesh Kumar K.V 	[P9_TREADLINK]    = virtio_p9_readlink,
1317c797b6c6SAneesh Kumar K.V 	[P9_TUNLINKAT]    = virtio_p9_unlinkat,
1318c797b6c6SAneesh Kumar K.V 	[P9_TMKDIR]       = virtio_p9_mkdir,
1319b4422bf3SAneesh Kumar K.V 	[P9_TVERSION]     = virtio_p9_version,
1320c797b6c6SAneesh Kumar K.V 	[P9_TLOPEN]       = virtio_p9_open,
1321b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]      = virtio_p9_attach,
1322b4422bf3SAneesh Kumar K.V 	[P9_TWALK]        = virtio_p9_walk,
1323c797b6c6SAneesh Kumar K.V 	[P9_TCLUNK]       = virtio_p9_clunk,
1324c797b6c6SAneesh Kumar K.V 	[P9_TFSYNC]       = virtio_p9_fsync,
1325b4422bf3SAneesh Kumar K.V 	[P9_TREAD]        = virtio_p9_read,
13265cc808aaSSasha Levin 	[P9_TFLUSH]       = virtio_p9_flush,
1327c797b6c6SAneesh Kumar K.V 	[P9_TLINK]        = virtio_p9_link,
1328c797b6c6SAneesh Kumar K.V 	[P9_TSYMLINK]     = virtio_p9_symlink,
1329c797b6c6SAneesh Kumar K.V 	[P9_TLCREATE]     = virtio_p9_create,
1330b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]       = virtio_p9_write,
13316fc5cd9bSSasha Levin 	[P9_TREMOVE]      = virtio_p9_remove,
1332f161f28bSSasha Levin 	[P9_TRENAME]      = virtio_p9_rename,
1333b4422bf3SAneesh Kumar K.V };
1334b4422bf3SAneesh Kumar K.V 
1335af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
1336af045e53SAneesh Kumar K.V {
1337af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
1338af045e53SAneesh Kumar K.V 	if (!pdu)
1339af045e53SAneesh Kumar K.V 		return NULL;
1340af045e53SAneesh Kumar K.V 
1341bfc15268SAneesh Kumar K.V 	/* skip the pdu header p9_msg */
13425529bcd7SAsias He 	pdu->read_offset	= VIRTIO_9P_HDR_LEN;
13435529bcd7SAsias He 	pdu->write_offset	= VIRTIO_9P_HDR_LEN;
1344af045e53SAneesh Kumar K.V 	pdu->queue_head		= virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
1345a8a44649SAsias He 					pdu->out_iov, &pdu->in_iov_cnt, &pdu->out_iov_cnt);
1346af045e53SAneesh Kumar K.V 	return pdu;
1347af045e53SAneesh Kumar K.V }
1348af045e53SAneesh Kumar K.V 
1349af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
1350af045e53SAneesh Kumar K.V {
1351af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
1352af045e53SAneesh Kumar K.V 	/*
1353af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
1354af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
1355af045e53SAneesh Kumar K.V 	 */
1356af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
1357af045e53SAneesh Kumar K.V 	return msg->cmd;
1358af045e53SAneesh Kumar K.V }
1359af045e53SAneesh Kumar K.V 
1360b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
13611c7850f9SSasha Levin {
1362af045e53SAneesh Kumar K.V 	u8 cmd;
1363b4422bf3SAneesh Kumar K.V 	u32 len = 0;
1364b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
1365b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
1366af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
1367af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
13681c7850f9SSasha Levin 
1369b4422bf3SAneesh Kumar K.V 	vq = job->vq;
1370b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
13711c7850f9SSasha Levin 
1372af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
1373af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
1374af045e53SAneesh Kumar K.V 
1375c797b6c6SAneesh Kumar K.V 	if ((cmd >= ARRAY_SIZE(virtio_9p_dotl_handler)) ||
1376c797b6c6SAneesh Kumar K.V 	    !virtio_9p_dotl_handler[cmd])
137797b408afSAneesh Kumar K.V 		handler = virtio_p9_eopnotsupp;
1378dd78d9eaSAneesh Kumar K.V 	else
1379c797b6c6SAneesh Kumar K.V 		handler = virtio_9p_dotl_handler[cmd];
1380c797b6c6SAneesh Kumar K.V 
1381af045e53SAneesh Kumar K.V 	handler(p9dev, p9pdu, &len);
1382af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
1383af045e53SAneesh Kumar K.V 	free(p9pdu);
13841c7850f9SSasha Levin 	return true;
13851c7850f9SSasha Levin }
13861c7850f9SSasha Levin 
13871c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
13881c7850f9SSasha Levin {
1389b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
1390b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
1391b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
13921c7850f9SSasha Levin 
13931c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
1394b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
139502eca50cSAsias He 		p9dev->vdev.ops->signal_vq(kvm, &p9dev->vdev, vq - p9dev->vqs);
13961c7850f9SSasha Levin 	}
13971c7850f9SSasha Levin }
13981c7850f9SSasha Levin 
1399c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
14001c7850f9SSasha Levin {
1401c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
14021c7850f9SSasha Levin 
1403c5ae742bSSasha Levin 	return ((u8 *)(p9dev->config));
1404c7838fbdSSasha Levin }
1405c7838fbdSSasha Levin 
1406c7838fbdSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
1407c7838fbdSSasha Levin {
1408c7838fbdSSasha Levin 	return 1 << VIRTIO_9P_MOUNT_TAG;
1409c7838fbdSSasha Levin }
1410c7838fbdSSasha Levin 
1411c7838fbdSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
1412c7838fbdSSasha Levin {
1413c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1414e0ea1859SMarc Zyngier 	struct virtio_9p_config *conf = p9dev->config;
1415c7838fbdSSasha Levin 
1416c7838fbdSSasha Levin 	p9dev->features = features;
1417e0ea1859SMarc Zyngier 	conf->tag_len = virtio_host_to_guest_u16(&p9dev->vdev, conf->tag_len);
1418c7838fbdSSasha Levin }
1419c7838fbdSSasha Levin 
1420c59ba304SWill Deacon static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
1421c59ba304SWill Deacon 		   u32 pfn)
1422c7838fbdSSasha Levin {
1423c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1424b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job;
1425b4422bf3SAneesh Kumar K.V 	struct virt_queue *queue;
1426c7838fbdSSasha Levin 	void *p;
14271c7850f9SSasha Levin 
1428312c62d1SSasha Levin 	compat__remove_message(compat_id);
1429e59662b3SSasha Levin 
1430c7838fbdSSasha Levin 	queue		= &p9dev->vqs[vq];
1431c7838fbdSSasha Levin 	queue->pfn	= pfn;
1432e7e2950aSSasha Levin 	p		= virtio_get_vq(kvm, queue->pfn, page_size);
1433c7838fbdSSasha Levin 	job		= &p9dev->jobs[vq];
14341c7850f9SSasha Levin 
1435c59ba304SWill Deacon 	vring_init(&queue->vring, VIRTQUEUE_NUM, p, align);
1436e0ea1859SMarc Zyngier 	virtio_init_device_vq(&p9dev->vdev, queue);
14371c7850f9SSasha Levin 
1438b4422bf3SAneesh Kumar K.V 	*job		= (struct p9_dev_job) {
1439b4422bf3SAneesh Kumar K.V 		.vq		= queue,
1440b4422bf3SAneesh Kumar K.V 		.p9dev		= p9dev,
1441b4422bf3SAneesh Kumar K.V 	};
1442df0c7f57SSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_p9_do_io, job);
144360eb42d5SSasha Levin 
1444c7838fbdSSasha Levin 	return 0;
14451c7850f9SSasha Levin }
14461c7850f9SSasha Levin 
1447c7838fbdSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
1448c7838fbdSSasha Levin {
1449c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
14501c7850f9SSasha Levin 
1451c7838fbdSSasha Levin 	thread_pool__do_job(&p9dev->jobs[vq].job_id);
1452c7838fbdSSasha Levin 
1453c7838fbdSSasha Levin 	return 0;
1454c7838fbdSSasha Levin }
1455c7838fbdSSasha Levin 
1456c7838fbdSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
1457c7838fbdSSasha Levin {
1458c7838fbdSSasha Levin 	struct p9_dev *p9dev = dev;
1459c7838fbdSSasha Levin 
1460c7838fbdSSasha Levin 	return p9dev->vqs[vq].pfn;
1461c7838fbdSSasha Levin }
1462c7838fbdSSasha Levin 
1463c7838fbdSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
1464c7838fbdSSasha Levin {
1465c7838fbdSSasha Levin 	return VIRTQUEUE_NUM;
1466c7838fbdSSasha Levin }
1467c7838fbdSSasha Levin 
14687aba29c1SWill Deacon static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
14697aba29c1SWill Deacon {
14707aba29c1SWill Deacon 	/* FIXME: dynamic */
14717aba29c1SWill Deacon 	return size;
14727aba29c1SWill Deacon }
14737aba29c1SWill Deacon 
147415542babSAndre Przywara struct virtio_ops p9_dev_virtio_ops = {
1475c7838fbdSSasha Levin 	.get_config		= get_config,
1476c7838fbdSSasha Levin 	.get_host_features	= get_host_features,
1477c7838fbdSSasha Levin 	.set_guest_features	= set_guest_features,
1478c7838fbdSSasha Levin 	.init_vq		= init_vq,
1479c7838fbdSSasha Levin 	.notify_vq		= notify_vq,
1480c7838fbdSSasha Levin 	.get_pfn_vq		= get_pfn_vq,
1481c7838fbdSSasha Levin 	.get_size_vq		= get_size_vq,
14827aba29c1SWill Deacon 	.set_size_vq		= set_size_vq,
1483c7838fbdSSasha Levin };
14841c47ce69SSasha Levin 
1485cac9e8fdSSasha Levin int virtio_9p_rootdir_parser(const struct option *opt, const char *arg, int unset)
1486cac9e8fdSSasha Levin {
1487cac9e8fdSSasha Levin 	char *tag_name;
1488cac9e8fdSSasha Levin 	char tmp[PATH_MAX];
1489cac9e8fdSSasha Levin 	struct kvm *kvm = opt->ptr;
1490cac9e8fdSSasha Levin 
1491cac9e8fdSSasha Levin 	/*
1492cac9e8fdSSasha Levin 	 * 9p dir can be of the form dirname,tag_name or
1493cac9e8fdSSasha Levin 	 * just dirname. In the later case we use the
1494cac9e8fdSSasha Levin 	 * default tag name
1495cac9e8fdSSasha Levin 	 */
1496cac9e8fdSSasha Levin 	tag_name = strstr(arg, ",");
1497cac9e8fdSSasha Levin 	if (tag_name) {
1498cac9e8fdSSasha Levin 		*tag_name = '\0';
1499cac9e8fdSSasha Levin 		tag_name++;
1500cac9e8fdSSasha Levin 	}
1501cac9e8fdSSasha Levin 	if (realpath(arg, tmp)) {
1502cac9e8fdSSasha Levin 		if (virtio_9p__register(kvm, tmp, tag_name) < 0)
1503cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1504cac9e8fdSSasha Levin 	} else
1505cac9e8fdSSasha Levin 		die("Failed resolving 9p path");
1506cac9e8fdSSasha Levin 	return 0;
1507cac9e8fdSSasha Levin }
1508cac9e8fdSSasha Levin 
1509cac9e8fdSSasha Levin int virtio_9p_img_name_parser(const struct option *opt, const char *arg, int unset)
1510cac9e8fdSSasha Levin {
1511cac9e8fdSSasha Levin 	char path[PATH_MAX];
1512cac9e8fdSSasha Levin 	struct stat st;
1513cac9e8fdSSasha Levin 	struct kvm *kvm = opt->ptr;
1514cac9e8fdSSasha Levin 
1515cac9e8fdSSasha Levin 	if (stat(arg, &st) == 0 &&
1516cac9e8fdSSasha Levin 	    S_ISDIR(st.st_mode)) {
1517cac9e8fdSSasha Levin 		char tmp[PATH_MAX];
1518cac9e8fdSSasha Levin 
1519cac9e8fdSSasha Levin 		if (kvm->cfg.using_rootfs)
1520cac9e8fdSSasha Levin 			die("Please use only one rootfs directory atmost");
1521cac9e8fdSSasha Levin 
1522cac9e8fdSSasha Levin 		if (realpath(arg, tmp) == 0 ||
1523cac9e8fdSSasha Levin 		    virtio_9p__register(kvm, tmp, "/dev/root") < 0)
1524cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1525cac9e8fdSSasha Levin 		kvm->cfg.using_rootfs = 1;
1526cac9e8fdSSasha Levin 		return 0;
1527cac9e8fdSSasha Levin 	}
1528cac9e8fdSSasha Levin 
1529cac9e8fdSSasha Levin 	snprintf(path, PATH_MAX, "%s%s", kvm__get_dir(), arg);
1530cac9e8fdSSasha Levin 
1531cac9e8fdSSasha Levin 	if (stat(path, &st) == 0 &&
1532cac9e8fdSSasha Levin 	    S_ISDIR(st.st_mode)) {
1533cac9e8fdSSasha Levin 		char tmp[PATH_MAX];
1534cac9e8fdSSasha Levin 
1535cac9e8fdSSasha Levin 		if (kvm->cfg.using_rootfs)
1536cac9e8fdSSasha Levin 			die("Please use only one rootfs directory atmost");
1537cac9e8fdSSasha Levin 
1538cac9e8fdSSasha Levin 		if (realpath(path, tmp) == 0 ||
1539cac9e8fdSSasha Levin 		    virtio_9p__register(kvm, tmp, "/dev/root") < 0)
1540cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1541cac9e8fdSSasha Levin 		if (virtio_9p__register(kvm, "/", "hostfs") < 0)
1542cac9e8fdSSasha Levin 			die("Unable to initialize virtio 9p");
1543cac9e8fdSSasha Levin 		kvm_setup_resolv(arg);
1544cac9e8fdSSasha Levin 		kvm->cfg.using_rootfs = kvm->cfg.custom_rootfs = 1;
1545cac9e8fdSSasha Levin 		kvm->cfg.custom_rootfs_name = arg;
1546cac9e8fdSSasha Levin 		return 0;
1547cac9e8fdSSasha Levin 	}
1548cac9e8fdSSasha Levin 
1549cac9e8fdSSasha Levin 	return -1;
1550cac9e8fdSSasha Levin }
1551cac9e8fdSSasha Levin 
15521c47ce69SSasha Levin int virtio_9p__init(struct kvm *kvm)
15531c47ce69SSasha Levin {
15541c47ce69SSasha Levin 	struct p9_dev *p9dev;
15551c47ce69SSasha Levin 
15561c47ce69SSasha Levin 	list_for_each_entry(p9dev, &devs, list) {
155702eca50cSAsias He 		virtio_init(kvm, p9dev, &p9dev->vdev, &p9_dev_virtio_ops,
1558d97dadecSWill Deacon 			    VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_9P,
1559ae06ce71SWill Deacon 			    VIRTIO_ID_9P, PCI_CLASS_9P);
1560c7838fbdSSasha Levin 	}
1561c7838fbdSSasha Levin 
1562c7838fbdSSasha Levin 	return 0;
1563c7838fbdSSasha Levin }
156449a8afd1SSasha Levin virtio_dev_init(virtio_9p__init);
1565c7838fbdSSasha Levin 
1566c7838fbdSSasha Levin int virtio_9p__register(struct kvm *kvm, const char *root, const char *tag_name)
1567c7838fbdSSasha Levin {
1568c7838fbdSSasha Levin 	struct p9_dev *p9dev;
156954f6802dSPekka Enberg 	int err = 0;
15701c7850f9SSasha Levin 
1571b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
1572b4422bf3SAneesh Kumar K.V 	if (!p9dev)
157354f6802dSPekka Enberg 		return -ENOMEM;
157454f6802dSPekka Enberg 
1575b4422bf3SAneesh Kumar K.V 	if (!tag_name)
15765529bcd7SAsias He 		tag_name = VIRTIO_9P_DEFAULT_TAG;
157754f6802dSPekka Enberg 
1578b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
157954f6802dSPekka Enberg 	if (p9dev->config == NULL) {
158054f6802dSPekka Enberg 		err = -ENOMEM;
1581b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
158254f6802dSPekka Enberg 	}
15831c7850f9SSasha Levin 
1584*e277a1b4SG. Campana 	strncpy(p9dev->root_dir, root, sizeof(p9dev->root_dir));
1585*e277a1b4SG. Campana 	p9dev->root_dir[sizeof(p9dev->root_dir)-1] = '\x00';
1586*e277a1b4SG. Campana 
1587b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
158854f6802dSPekka Enberg 	if (p9dev->config->tag_len > MAX_TAG_LEN) {
158954f6802dSPekka Enberg 		err = -EINVAL;
1590b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
159154f6802dSPekka Enberg 	}
15921c7850f9SSasha Levin 
1593c7838fbdSSasha Levin 	memcpy(&p9dev->config->tag, tag_name, strlen(tag_name));
15941c7850f9SSasha Levin 
1595c7838fbdSSasha Levin 	list_add(&p9dev->list, &devs);
1596b4422bf3SAneesh Kumar K.V 
1597d278197dSAsias He 	if (compat_id == -1)
159852f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-9p", "CONFIG_NET_9P_VIRTIO");
1599e59662b3SSasha Levin 
160054f6802dSPekka Enberg 	return err;
160154f6802dSPekka Enberg 
1602b4422bf3SAneesh Kumar K.V free_p9dev_config:
1603b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
1604b4422bf3SAneesh Kumar K.V free_p9dev:
1605b4422bf3SAneesh Kumar K.V 	free(p9dev);
160654f6802dSPekka Enberg 	return err;
16071c7850f9SSasha Levin }
1608