xref: /minix3/minix/lib/libfsdriver/call.c (revision be4841096dc297191f45f8a3f9a8bcdf7abe0f33)
1 
2 #include "fsdriver.h"
3 #include <minix/ds.h>
4 #include <sys/mman.h>
5 
6 /*
7  * Process a READSUPER request from VFS.
8  */
9 int
10 fsdriver_readsuper(const struct fsdriver * __restrict fdp,
11 	const message * __restrict m_in, message * __restrict m_out)
12 {
13 	struct fsdriver_node root_node;
14 	char label[DS_MAX_KEYLEN];
15 	cp_grant_id_t label_grant;
16 	size_t label_len;
17 	unsigned int flags, res_flags;
18 	dev_t dev;
19 	int r;
20 
21 	dev = m_in->m_vfs_fs_readsuper.device;
22 	label_grant = m_in->m_vfs_fs_readsuper.grant;
23 	label_len = m_in->m_vfs_fs_readsuper.path_len;
24 	flags = m_in->m_vfs_fs_readsuper.flags;
25 
26 	if (fdp->fdr_mount == NULL)
27 		return ENOSYS;
28 
29 	if (fsdriver_mounted) {
30 		printf("fsdriver: attempt to mount multiple times\n");
31 		return EBUSY;
32 	}
33 
34 	if ((r = fsdriver_getname(m_in->m_source, label_grant, label_len,
35 	    label, sizeof(label), FALSE /*not_empty*/)) != OK)
36 		return r;
37 
38 	if (fdp->fdr_driver != NULL)
39 		fdp->fdr_driver(dev, label);
40 
41 	res_flags = RES_NOFLAGS;
42 
43 	r = fdp->fdr_mount(dev, flags, &root_node, &res_flags);
44 
45 	if (r == OK) {
46 		/* This one we can set on the file system's behalf. */
47 		if ((fdp->fdr_peek != NULL && fdp->fdr_bpeek != NULL) ||
48 		    major(dev) == NONE_MAJOR)
49 			res_flags |= RES_HASPEEK;
50 
51 		m_out->m_fs_vfs_readsuper.inode = root_node.fn_ino_nr;
52 		m_out->m_fs_vfs_readsuper.mode = root_node.fn_mode;
53 		m_out->m_fs_vfs_readsuper.file_size = root_node.fn_size;
54 		m_out->m_fs_vfs_readsuper.uid = root_node.fn_uid;
55 		m_out->m_fs_vfs_readsuper.gid = root_node.fn_gid;
56 		m_out->m_fs_vfs_readsuper.flags = res_flags;
57 
58 		/* Update library-local state. */
59 		fsdriver_mounted = TRUE;
60 		fsdriver_device = dev;
61 		fsdriver_root = root_node.fn_ino_nr;
62 	}
63 
64 	return r;
65 }
66 
67 /*
68  * Process an UNMOUNT request from VFS.
69  */
70 int
71 fsdriver_unmount(const struct fsdriver * __restrict fdp,
72 	const message * __restrict __unused m_in,
73 	message * __restrict __unused m_out)
74 {
75 
76 	if (fdp->fdr_unmount != NULL)
77 		fdp->fdr_unmount();
78 
79 	/* If we used mmap emulation, clear any cached blocks from VM. */
80 	if (fdp->fdr_peek == NULL && major(fsdriver_device) == NONE_MAJOR)
81 		vm_clear_cache(fsdriver_device);
82 
83 	/* Update library-local state. */
84 	fsdriver_mounted = FALSE;
85 
86 	return OK;
87 }
88 
89 /*
90  * Process a PUTNODE request from VFS.
91  */
92 int
93 fsdriver_putnode(const struct fsdriver * __restrict fdp,
94 	const message * __restrict m_in, message * __restrict __unused m_out)
95 {
96 	ino_t ino_nr;
97 	unsigned int count;
98 
99 	ino_nr = m_in->m_vfs_fs_putnode.inode;
100 	count = m_in->m_vfs_fs_putnode.count;
101 
102 	if (fdp->fdr_putnode == NULL)
103 		return ENOSYS;
104 
105 	if (count == 0 || count > INT_MAX) {
106 		printf("fsdriver: invalid reference count\n");
107 		return EINVAL;
108 	}
109 
110 	return fdp->fdr_putnode(ino_nr, count);
111 }
112 
113 /*
114  * Process a NEWNODE request from VFS.
115  */
116 int
117 fsdriver_newnode(const struct fsdriver * __restrict fdp,
118 	const message * __restrict m_in, message * __restrict m_out)
119 {
120 	struct fsdriver_node node;
121 	mode_t mode;
122 	uid_t uid;
123 	gid_t gid;
124 	dev_t dev;
125 	int r;
126 
127 	mode = m_in->m_vfs_fs_newnode.mode;
128 	uid = m_in->m_vfs_fs_newnode.uid;
129 	gid = m_in->m_vfs_fs_newnode.gid;
130 	dev = m_in->m_vfs_fs_newnode.device;
131 
132 	if (fdp->fdr_newnode == NULL)
133 		return ENOSYS;
134 
135 	if ((r = fdp->fdr_newnode(mode, uid, gid, dev, &node)) == OK) {
136 		m_out->m_fs_vfs_newnode.inode = node.fn_ino_nr;
137 		m_out->m_fs_vfs_newnode.mode = node.fn_mode;
138 		m_out->m_fs_vfs_newnode.file_size = node.fn_size;
139 		m_out->m_fs_vfs_newnode.uid = node.fn_uid;
140 		m_out->m_fs_vfs_newnode.gid = node.fn_gid;
141 		m_out->m_fs_vfs_newnode.device = node.fn_dev;
142 	}
143 
144 	return r;
145 }
146 
147 /*
148  * Process a read or write request from VFS.
149  */
150 static int
151 read_write(const struct fsdriver * __restrict fdp,
152 	const message * __restrict m_in, message * __restrict m_out, int call)
153 {
154 	struct fsdriver_data data;
155 	ino_t ino_nr;
156 	off_t pos;
157 	size_t nbytes;
158 	ssize_t r;
159 
160 	ino_nr = m_in->m_vfs_fs_readwrite.inode;
161 	pos = m_in->m_vfs_fs_readwrite.seek_pos;
162 	nbytes = m_in->m_vfs_fs_readwrite.nbytes;
163 
164 	if (pos < 0 || nbytes > SSIZE_MAX)
165 		return EINVAL;
166 
167 	data.endpt = m_in->m_source;
168 	data.grant = m_in->m_vfs_fs_readwrite.grant;
169 	data.size = nbytes;
170 
171 	if (call == FSC_WRITE)
172 		r = fdp->fdr_write(ino_nr, &data, nbytes, pos, call);
173 	else
174 		r = fdp->fdr_read(ino_nr, &data, nbytes, pos, call);
175 
176 	if (r >= 0) {
177 		pos += r;
178 
179 		m_out->m_fs_vfs_readwrite.seek_pos = pos;
180 		m_out->m_fs_vfs_readwrite.nbytes = r;
181 		r = OK;
182 	}
183 
184 	return r;
185 }
186 
187 /*
188  * Process a READ request from VFS.
189  */
190 int
191 fsdriver_read(const struct fsdriver * __restrict fdp,
192 	const message * __restrict m_in, message * __restrict m_out)
193 {
194 
195 	if (fdp->fdr_read == NULL)
196 		return ENOSYS;
197 
198 	return read_write(fdp, m_in, m_out, FSC_READ);
199 }
200 
201 /*
202  * Process a WRITE request from VFS.
203  */
204 int
205 fsdriver_write(const struct fsdriver * __restrict fdp,
206 	const message * __restrict m_in, message * __restrict m_out)
207 {
208 
209 	if (fdp->fdr_write == NULL)
210 		return ENOSYS;
211 
212 	return read_write(fdp, m_in, m_out, FSC_WRITE);
213 }
214 
215 /*
216  * A read-based peek implementation.  This allows file systems that do not have
217  * a buffer cache and do not implement peek, to support a limited form of mmap.
218  * We map in a block, fill it by calling the file system's read function, tell
219  * VM about the page, and then unmap the block again.  We tell VM not to cache
220  * the block beyond its immediate use for the mmap request, so as to prevent
221  * potentially stale data from being cached--at the cost of performance.
222  */
223 static ssize_t
224 builtin_peek(const struct fsdriver * __restrict fdp, ino_t ino_nr,
225 	size_t nbytes, off_t pos)
226 {
227 	static u32_t flags = 0;	/* storage for the VMMC_ flags of all blocks */
228 	static off_t dev_off = 0; /* fake device offset, see below */
229 	struct fsdriver_data data;
230 	char *buf;
231 	ssize_t r;
232 
233 	if ((buf = mmap(NULL, nbytes, PROT_READ | PROT_WRITE,
234 	    MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED)
235 		return ENOMEM;
236 
237 	data.endpt = SELF;
238 	data.grant = (cp_grant_id_t)buf;
239 	data.size = nbytes;
240 
241 	r = fdp->fdr_read(ino_nr, &data, nbytes, pos, FSC_READ);
242 
243 	if (r >= 0) {
244 		if ((size_t)r < nbytes)
245 			memset(&buf[r], 0, nbytes - r);
246 
247 		/*
248 		 * VM uses serialized communication to VFS.  Since the page is
249 		 * to be used only once, VM will use and then discard it before
250 		 * sending a new peek request.  Thus, it should be safe to
251 		 * reuse the same device offset all the time.  However, relying
252 		 * on assumptions in protocols elsewhere a bit dangerous, so we
253 		 * use an ever-increasing device offset just to be safe.
254 		 */
255 		r = vm_set_cacheblock(buf, fsdriver_device, dev_off, ino_nr,
256 		    pos, &flags, nbytes, VMSF_ONCE);
257 
258 		if (r == OK) {
259 			dev_off += nbytes;
260 
261 			r = nbytes;
262 		}
263 	}
264 
265 	munmap(buf, nbytes);
266 
267 	return r;
268 }
269 
270 /*
271  * Process a PEEK request from VFS.
272  */
273 int
274 fsdriver_peek(const struct fsdriver * __restrict fdp,
275 	const message * __restrict m_in, message * __restrict __unused m_out)
276 {
277 	ino_t ino_nr;
278 	off_t pos;
279 	size_t nbytes;
280 	ssize_t r;
281 
282 	ino_nr = m_in->m_vfs_fs_readwrite.inode;
283 	pos = m_in->m_vfs_fs_readwrite.seek_pos;
284 	nbytes = m_in->m_vfs_fs_readwrite.nbytes;
285 
286 	if (pos < 0 || nbytes > SSIZE_MAX)
287 		return EINVAL;
288 
289 	if (fdp->fdr_peek == NULL) {
290 		if (major(fsdriver_device) != NONE_MAJOR)
291 			return ENOSYS;
292 
293 		/*
294 		 * For file systems that have no backing device, emulate peek
295 		 * support by reading into temporary buffers and passing these
296 		 * to VM.
297 		 */
298 		r = builtin_peek(fdp, ino_nr, nbytes, pos);
299 	} else
300 		r = fdp->fdr_peek(ino_nr, NULL /*data*/, nbytes, pos,
301 		    FSC_PEEK);
302 
303 	/* Do not return a new position. */
304 	if (r >= 0) {
305 		m_out->m_fs_vfs_readwrite.nbytes = r;
306 		r = OK;
307 	}
308 
309 	return r;
310 }
311 
312 /*
313  * Process a GETDENTS request from VFS.
314  */
315 int
316 fsdriver_getdents(const struct fsdriver * __restrict fdp,
317 	const message * __restrict m_in, message * __restrict m_out)
318 {
319 	struct fsdriver_data data;
320 	ino_t ino_nr;
321 	off_t pos;
322 	size_t nbytes;
323 	ssize_t r;
324 
325 	ino_nr = m_in->m_vfs_fs_getdents.inode;
326 	pos = m_in->m_vfs_fs_getdents.seek_pos;
327 	nbytes = m_in->m_vfs_fs_getdents.mem_size;
328 
329 	if (fdp->fdr_getdents == NULL)
330 		return ENOSYS;
331 
332 	if (pos < 0 || nbytes > SSIZE_MAX)
333 		return EINVAL;
334 
335 	data.endpt = m_in->m_source;
336 	data.grant = m_in->m_vfs_fs_getdents.grant;
337 	data.size = nbytes;
338 
339 	r = fdp->fdr_getdents(ino_nr, &data, nbytes, &pos);
340 
341 	if (r >= 0) {
342 		m_out->m_fs_vfs_getdents.seek_pos = pos;
343 		m_out->m_fs_vfs_getdents.nbytes = r;
344 		r = OK;
345 	}
346 
347 	return r;
348 }
349 
350 /*
351  * Process a FTRUNC request from VFS.
352  */
353 int
354 fsdriver_trunc(const struct fsdriver * __restrict fdp,
355 	const message * __restrict m_in, message * __restrict __unused m_out)
356 {
357 	ino_t ino_nr;
358 	off_t start_pos, end_pos;
359 
360 	ino_nr = m_in->m_vfs_fs_ftrunc.inode;
361 	start_pos = m_in->m_vfs_fs_ftrunc.trc_start;
362 	end_pos = m_in->m_vfs_fs_ftrunc.trc_end;
363 
364 	if (start_pos < 0 || end_pos < 0)
365 		return EINVAL;
366 
367 	if (fdp->fdr_trunc == NULL)
368 		return ENOSYS;
369 
370 	return fdp->fdr_trunc(ino_nr, start_pos, end_pos);
371 }
372 
373 /*
374  * Process a INHIBREAD request from VFS.
375  */
376 int
377 fsdriver_inhibread(const struct fsdriver * __restrict fdp,
378 	const message * __restrict m_in, message * __restrict __unused m_out)
379 {
380 	ino_t ino_nr;
381 
382 	ino_nr = m_in->m_vfs_fs_inhibread.inode;
383 
384 	if (fdp->fdr_seek != NULL)
385 		fdp->fdr_seek(ino_nr);
386 
387 	return OK;
388 }
389 
390 /*
391  * Process a CREATE request from VFS.
392  */
393 int
394 fsdriver_create(const struct fsdriver * __restrict fdp,
395 	const message * __restrict m_in, message * __restrict m_out)
396 {
397 	struct fsdriver_node node;
398 	char name[NAME_MAX+1];
399 	cp_grant_id_t grant;
400 	size_t len;
401 	ino_t dir_nr;
402 	mode_t mode;
403 	uid_t uid;
404 	gid_t gid;
405 	int r;
406 
407 	grant = m_in->m_vfs_fs_create.grant;
408 	len = m_in->m_vfs_fs_create.path_len;
409 	dir_nr = m_in->m_vfs_fs_create.inode;
410 	mode = m_in->m_vfs_fs_create.mode;
411 	uid = m_in->m_vfs_fs_create.uid;
412 	gid = m_in->m_vfs_fs_create.gid;
413 
414 	if (fdp->fdr_create == NULL)
415 		return ENOSYS;
416 
417 	if ((r = fsdriver_getname(m_in->m_source, grant, len, name,
418 	    sizeof(name), TRUE /*not_empty*/)) != OK)
419 		return r;
420 
421 	if (!strcmp(name, ".") || !strcmp(name, ".."))
422 		return EEXIST;
423 
424 	if ((r = fdp->fdr_create(dir_nr, name, mode, uid, gid, &node)) == OK) {
425 		m_out->m_fs_vfs_create.inode = node.fn_ino_nr;
426 		m_out->m_fs_vfs_create.mode = node.fn_mode;
427 		m_out->m_fs_vfs_create.file_size = node.fn_size;
428 		m_out->m_fs_vfs_create.uid = node.fn_uid;
429 		m_out->m_fs_vfs_create.gid = node.fn_gid;
430 	}
431 
432 	return r;
433 }
434 
435 /*
436  * Process a MKDIR request from VFS.
437  */
438 int
439 fsdriver_mkdir(const struct fsdriver * __restrict fdp,
440 	const message * __restrict m_in, message * __restrict __unused m_out)
441 {
442 	char name[NAME_MAX+1];
443 	cp_grant_id_t grant;
444 	size_t path_len;
445 	ino_t dir_nr;
446 	mode_t mode;
447 	uid_t uid;
448 	gid_t gid;
449 	int r;
450 
451 	grant = m_in->m_vfs_fs_mkdir.grant;
452 	path_len = m_in->m_vfs_fs_mkdir.path_len;
453 	dir_nr = m_in->m_vfs_fs_mkdir.inode;
454 	mode = m_in->m_vfs_fs_mkdir.mode;
455 	uid = m_in->m_vfs_fs_mkdir.uid;
456 	gid = m_in->m_vfs_fs_mkdir.gid;
457 
458 	if (fdp->fdr_mkdir == NULL)
459 		return ENOSYS;
460 
461 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
462 	    sizeof(name), TRUE /*not_empty*/)) != OK)
463 		return r;
464 
465 	if (!strcmp(name, ".") || !strcmp(name, ".."))
466 		return EEXIST;
467 
468 	return fdp->fdr_mkdir(dir_nr, name, mode, uid, gid);
469 }
470 
471 /*
472  * Process a MKNOD request from VFS.
473  */
474 int
475 fsdriver_mknod(const struct fsdriver * __restrict fdp,
476 	const message * __restrict m_in, message * __restrict __unused m_out)
477 {
478 	char name[NAME_MAX+1];
479 	cp_grant_id_t grant;
480 	size_t path_len;
481 	ino_t dir_nr;
482 	mode_t mode;
483 	uid_t uid;
484 	gid_t gid;
485 	dev_t dev;
486 	int r;
487 
488 	grant = m_in->m_vfs_fs_mknod.grant;
489 	path_len = m_in->m_vfs_fs_mknod.path_len;
490 	dir_nr = m_in->m_vfs_fs_mknod.inode;
491 	mode = m_in->m_vfs_fs_mknod.mode;
492 	uid = m_in->m_vfs_fs_mknod.uid;
493 	gid = m_in->m_vfs_fs_mknod.gid;
494 	dev = m_in->m_vfs_fs_mknod.device;
495 
496 	if (fdp->fdr_mknod == NULL)
497 		return ENOSYS;
498 
499 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
500 	    sizeof(name), TRUE /*not_empty*/)) != OK)
501 		return r;
502 
503 	if (!strcmp(name, ".") || !strcmp(name, ".."))
504 		return EEXIST;
505 
506 	return fdp->fdr_mknod(dir_nr, name, mode, uid, gid, dev);
507 }
508 
509 /*
510  * Process a LINK request from VFS.
511  */
512 int
513 fsdriver_link(const struct fsdriver * __restrict fdp,
514 	const message * __restrict m_in, message * __restrict __unused m_out)
515 {
516 	char name[NAME_MAX+1];
517 	cp_grant_id_t grant;
518 	size_t path_len;
519 	ino_t dir_nr, ino_nr;
520 	int r;
521 
522 	grant = m_in->m_vfs_fs_link.grant;
523 	path_len = m_in->m_vfs_fs_link.path_len;
524 	dir_nr = m_in->m_vfs_fs_link.dir_ino;
525 	ino_nr = m_in->m_vfs_fs_link.inode;
526 
527 	if (fdp->fdr_link == NULL)
528 		return ENOSYS;
529 
530 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
531 	    sizeof(name), TRUE /*not_empty*/)) != OK)
532 		return r;
533 
534 	if (!strcmp(name, ".") || !strcmp(name, ".."))
535 		return EEXIST;
536 
537 	return fdp->fdr_link(dir_nr, name, ino_nr);
538 }
539 
540 /*
541  * Process an UNLINK request from VFS.
542  */
543 int
544 fsdriver_unlink(const struct fsdriver * __restrict fdp,
545 	const message * __restrict m_in, message * __restrict __unused m_out)
546 {
547 	char name[NAME_MAX+1];
548 	cp_grant_id_t grant;
549 	size_t path_len;
550 	ino_t dir_nr;
551 	int r;
552 
553 	grant = m_in->m_vfs_fs_unlink.grant;
554 	path_len = m_in->m_vfs_fs_unlink.path_len;
555 	dir_nr = m_in->m_vfs_fs_unlink.inode;
556 
557 	if (fdp->fdr_unlink == NULL)
558 		return ENOSYS;
559 
560 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
561 	    sizeof(name), TRUE /*not_empty*/)) != OK)
562 		return r;
563 
564 	if (!strcmp(name, ".") || !strcmp(name, ".."))
565 		return EPERM;
566 
567 	return fdp->fdr_unlink(dir_nr, name, FSC_UNLINK);
568 }
569 
570 /*
571  * Process a RMDIR request from VFS.
572  */
573 int
574 fsdriver_rmdir(const struct fsdriver * __restrict fdp,
575 	const message * __restrict m_in, message * __restrict __unused m_out)
576 {
577 	char name[NAME_MAX+1];
578 	cp_grant_id_t grant;
579 	size_t path_len;
580 	ino_t dir_nr;
581 	int r;
582 
583 	grant = m_in->m_vfs_fs_unlink.grant;
584 	path_len = m_in->m_vfs_fs_unlink.path_len;
585 	dir_nr = m_in->m_vfs_fs_unlink.inode;
586 
587 	if (fdp->fdr_rmdir == NULL)
588 		return ENOSYS;
589 
590 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
591 	    sizeof(name), TRUE /*not_empty*/)) != OK)
592 		return r;
593 
594 	if (!strcmp(name, "."))
595 		return EINVAL;
596 
597 	if (!strcmp(name, ".."))
598 		return ENOTEMPTY;
599 
600 	return fdp->fdr_rmdir(dir_nr, name, FSC_RMDIR);
601 }
602 
603 /*
604  * Process a RENAME request from VFS.
605  */
606 int
607 fsdriver_rename(const struct fsdriver * __restrict fdp,
608 	const message * __restrict m_in, message * __restrict __unused m_out)
609 {
610 	char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
611 	cp_grant_id_t old_grant, new_grant;
612 	size_t old_len, new_len;
613 	ino_t old_dir_nr, new_dir_nr;
614 	int r;
615 
616 	old_grant = m_in->m_vfs_fs_rename.grant_old;
617 	old_len = m_in->m_vfs_fs_rename.len_old;
618 	old_dir_nr = m_in->m_vfs_fs_rename.dir_old;
619 	new_grant = m_in->m_vfs_fs_rename.grant_new;
620 	new_len = m_in->m_vfs_fs_rename.len_new;
621 	new_dir_nr = m_in->m_vfs_fs_rename.dir_new;
622 
623 	if (fdp->fdr_rename == NULL)
624 		return ENOSYS;
625 
626 	if ((r = fsdriver_getname(m_in->m_source, old_grant, old_len, old_name,
627 	    sizeof(old_name), TRUE /*not_empty*/)) != OK)
628 		return r;
629 
630 	if (!strcmp(old_name, ".") || !strcmp(old_name, ".."))
631 		return EINVAL;
632 
633 	if ((r = fsdriver_getname(m_in->m_source, new_grant, new_len, new_name,
634 	    sizeof(new_name), TRUE /*not_empty*/)) != OK)
635 		return r;
636 
637 	if (!strcmp(new_name, ".") || !strcmp(new_name, ".."))
638 		return EINVAL;
639 
640 	return fdp->fdr_rename(old_dir_nr, old_name, new_dir_nr, new_name);
641 }
642 
643 /*
644  * Process a SLINK request from VFS.
645  */
646 int
647 fsdriver_slink(const struct fsdriver * __restrict fdp,
648 	const message * __restrict m_in, message * __restrict __unused m_out)
649 {
650 	struct fsdriver_data data;
651 	char name[NAME_MAX+1];
652 	cp_grant_id_t grant;
653 	size_t path_len;
654 	ino_t dir_nr;
655 	uid_t uid;
656 	gid_t gid;
657 	int r;
658 
659 	grant = m_in->m_vfs_fs_slink.grant_path;
660 	path_len = m_in->m_vfs_fs_slink.path_len;
661 	dir_nr = m_in->m_vfs_fs_slink.inode;
662 	uid = m_in->m_vfs_fs_slink.uid;
663 	gid = m_in->m_vfs_fs_slink.gid;
664 
665 	if (fdp->fdr_slink == NULL)
666 		return ENOSYS;
667 
668 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
669 	    sizeof(name), TRUE /*not_empty*/)) != OK)
670 		return r;
671 
672 	if (!strcmp(name, ".") || !strcmp(name, ".."))
673 		return EEXIST;
674 
675 	data.endpt = m_in->m_source;
676 	data.grant = m_in->m_vfs_fs_slink.grant_target;
677 	data.size = m_in->m_vfs_fs_slink.mem_size;
678 
679 	return fdp->fdr_slink(dir_nr, name, uid, gid, &data, data.size);
680 }
681 
682 /*
683  * Process a RDLINK request from VFS.
684  */
685 int
686 fsdriver_rdlink(const struct fsdriver * __restrict fdp,
687 	const message * __restrict m_in, message * __restrict m_out)
688 {
689 	struct fsdriver_data data;
690 	ssize_t r;
691 
692 	if (fdp->fdr_rdlink == NULL)
693 		return ENOSYS;
694 
695 	data.endpt = m_in->m_source;
696 	data.grant = m_in->m_vfs_fs_rdlink.grant;
697 	data.size = m_in->m_vfs_fs_rdlink.mem_size;
698 
699 	r = fdp->fdr_rdlink(m_in->m_vfs_fs_rdlink.inode, &data, data.size);
700 
701 	if (r >= 0) {
702 		m_out->m_fs_vfs_rdlink.nbytes = r;
703 		r = OK;
704 	}
705 
706 	return r;
707 }
708 
709 /*
710  * Process a STAT request from VFS.
711  */
712 int
713 fsdriver_stat(const struct fsdriver * __restrict fdp,
714 	const message * __restrict m_in, message * __restrict __unused m_out)
715 {
716 	struct stat buf;
717 	cp_grant_id_t grant;
718 	ino_t ino_nr;
719 	int r;
720 
721 	ino_nr = m_in->m_vfs_fs_stat.inode;
722 	grant = m_in->m_vfs_fs_stat.grant;
723 
724 	if (fdp->fdr_stat == NULL)
725 		return ENOSYS;
726 
727 	memset(&buf, 0, sizeof(buf));
728 	buf.st_dev = fsdriver_device;
729 
730 	if ((r = fdp->fdr_stat(ino_nr, &buf)) == OK)
731 		r = sys_safecopyto(m_in->m_source, grant, 0, (vir_bytes)&buf,
732 		    (phys_bytes)sizeof(buf));
733 
734 	return r;
735 }
736 
737 /*
738  * Process a CHOWN request from VFS.
739  */
740 int
741 fsdriver_chown(const struct fsdriver * __restrict fdp,
742 	const message * __restrict m_in, message * __restrict m_out)
743 {
744 	ino_t ino_nr;
745 	uid_t uid;
746 	gid_t gid;
747 	mode_t mode;
748 	int r;
749 
750 	ino_nr = m_in->m_vfs_fs_chown.inode;
751 	uid = m_in->m_vfs_fs_chown.uid;
752 	gid = m_in->m_vfs_fs_chown.gid;
753 
754 	if (fdp->fdr_chown == NULL)
755 		return ENOSYS;
756 
757 	if ((r = fdp->fdr_chown(ino_nr, uid, gid, &mode)) == OK)
758 		m_out->m_fs_vfs_chown.mode = mode;
759 
760 	return r;
761 }
762 
763 /*
764  * Process a CHMOD request from VFS.
765  */
766 int
767 fsdriver_chmod(const struct fsdriver * __restrict fdp,
768 	const message * __restrict m_in, message * __restrict m_out)
769 {
770 	ino_t ino_nr;
771 	mode_t mode;
772 	int r;
773 
774 	ino_nr = m_in->m_vfs_fs_chmod.inode;
775 	mode = m_in->m_vfs_fs_chmod.mode;
776 
777 	if (fdp->fdr_chmod == NULL)
778 		return ENOSYS;
779 
780 	if ((r = fdp->fdr_chmod(ino_nr, &mode)) == OK)
781 		m_out->m_fs_vfs_chmod.mode = mode;
782 
783 	return r;
784 }
785 
786 /*
787  * Process a UTIME request from VFS.
788  */
789 int
790 fsdriver_utime(const struct fsdriver * __restrict fdp,
791 	const message * __restrict m_in, message * __restrict __unused m_out)
792 {
793 	ino_t ino_nr;
794 	struct timespec atime, mtime;
795 
796 	ino_nr = m_in->m_vfs_fs_utime.inode;
797 	atime.tv_sec = m_in->m_vfs_fs_utime.actime;
798 	atime.tv_nsec = m_in->m_vfs_fs_utime.acnsec;
799 	mtime.tv_sec = m_in->m_vfs_fs_utime.modtime;
800 	mtime.tv_nsec = m_in->m_vfs_fs_utime.modnsec;
801 
802 	if (fdp->fdr_utime == NULL)
803 		return ENOSYS;
804 
805 	return fdp->fdr_utime(ino_nr, &atime, &mtime);
806 }
807 
808 /*
809  * Process a MOUNTPOINT request from VFS.
810  */
811 int
812 fsdriver_mountpoint(const struct fsdriver * __restrict fdp,
813 	const message * __restrict m_in, message * __restrict __unused m_out)
814 {
815 	ino_t ino_nr;
816 
817 	ino_nr = m_in->m_vfs_fs_mountpoint.inode;
818 
819 	if (fdp->fdr_mountpt == NULL)
820 		return ENOSYS;
821 
822 	return fdp->fdr_mountpt(ino_nr);
823 }
824 
825 /*
826  * Process a STATVFS request from VFS.
827  */
828 int
829 fsdriver_statvfs(const struct fsdriver * __restrict fdp,
830 	const message * __restrict m_in, message * __restrict __unused m_out)
831 {
832 	struct statvfs buf;
833 	int r;
834 
835 	if (fdp->fdr_statvfs == NULL)
836 		return ENOSYS;
837 
838 	memset(&buf, 0, sizeof(buf));
839 
840 	if ((r = fdp->fdr_statvfs(&buf)) != OK)
841 		return r;
842 
843 	return sys_safecopyto(m_in->m_source, m_in->m_vfs_fs_statvfs.grant, 0,
844 	    (vir_bytes)&buf, (phys_bytes)sizeof(buf));
845 }
846 
847 /*
848  * Process a SYNC request from VFS.
849  */
850 int
851 fsdriver_sync(const struct fsdriver * __restrict fdp,
852 	const message * __restrict __unused m_in,
853 	message * __restrict __unused m_out)
854 {
855 
856 	if (fdp->fdr_sync != NULL)
857 		fdp->fdr_sync();
858 
859 	return OK;
860 }
861 
862 /*
863  * Process a NEW_DRIVER request from VFS.
864  */
865 int
866 fsdriver_newdriver(const struct fsdriver * __restrict fdp,
867 	const message * __restrict m_in, message * __restrict __unused m_out)
868 {
869 	char label[DS_MAX_KEYLEN];
870 	cp_grant_id_t grant;
871 	size_t path_len;
872 	dev_t dev;
873 	int r;
874 
875 	dev = m_in->m_vfs_fs_new_driver.device;
876 	grant = m_in->m_vfs_fs_new_driver.grant;
877 	path_len = m_in->m_vfs_fs_new_driver.path_len;
878 
879 	if (fdp->fdr_driver == NULL)
880 		return OK;
881 
882 	if ((r = fsdriver_getname(m_in->m_source, grant, path_len, label,
883 	    sizeof(label), FALSE /*not_empty*/)) != OK)
884 		return r;
885 
886 	fdp->fdr_driver(dev, label);
887 
888 	return OK;
889 }
890 
891 /*
892  * Process a block read or write request from VFS.
893  */
894 static ssize_t
895 bread_bwrite(const struct fsdriver * __restrict fdp,
896 	const message * __restrict m_in, message * __restrict m_out, int call)
897 {
898 	struct fsdriver_data data;
899 	dev_t dev;
900 	off_t pos;
901 	size_t nbytes;
902 	ssize_t r;
903 
904 	dev = m_in->m_vfs_fs_breadwrite.device;
905 	pos = m_in->m_vfs_fs_breadwrite.seek_pos;
906 	nbytes = m_in->m_vfs_fs_breadwrite.nbytes;
907 
908 	if (pos < 0 || nbytes > SSIZE_MAX)
909 		return EINVAL;
910 
911 	data.endpt = m_in->m_source;
912 	data.grant = m_in->m_vfs_fs_breadwrite.grant;
913 	data.size = nbytes;
914 
915 	if (call == FSC_WRITE)
916 		r = fdp->fdr_bwrite(dev, &data, nbytes, pos, call);
917 	else
918 		r = fdp->fdr_bread(dev, &data, nbytes, pos, call);
919 
920 	if (r >= 0) {
921 		pos += r;
922 
923 		m_out->m_fs_vfs_breadwrite.seek_pos = pos;
924 		m_out->m_fs_vfs_breadwrite.nbytes = r;
925 		r = OK;
926 	}
927 
928 	return r;
929 }
930 
931 /*
932  * Process a BREAD request from VFS.
933  */
934 ssize_t
935 fsdriver_bread(const struct fsdriver * __restrict fdp,
936 	const message * __restrict m_in, message * __restrict m_out)
937 {
938 
939 	if (fdp->fdr_bread == NULL)
940 		return ENOSYS;
941 
942 	return bread_bwrite(fdp, m_in, m_out, FSC_READ);
943 }
944 
945 /*
946  * Process a BWRITE request from VFS.
947  */
948 ssize_t
949 fsdriver_bwrite(const struct fsdriver * __restrict fdp,
950 	const message * __restrict m_in, message * __restrict m_out)
951 {
952 
953 	if (fdp->fdr_bwrite == NULL)
954 		return ENOSYS;
955 
956 	return bread_bwrite(fdp, m_in, m_out, FSC_WRITE);
957 }
958 
959 /*
960  * Process a BPEEK request from VFS.
961  */
962 int
963 fsdriver_bpeek(const struct fsdriver * __restrict fdp,
964 	const message * __restrict m_in, message * __restrict __unused m_out)
965 {
966 	dev_t dev;
967 	off_t pos;
968 	size_t nbytes;
969 	ssize_t r;
970 
971 	dev = m_in->m_vfs_fs_breadwrite.device;
972 	pos = m_in->m_vfs_fs_breadwrite.seek_pos;
973 	nbytes = m_in->m_vfs_fs_breadwrite.nbytes;
974 
975 	if (fdp->fdr_bpeek == NULL)
976 		return ENOSYS;
977 
978 	if (pos < 0 || nbytes > SSIZE_MAX)
979 		return EINVAL;
980 
981 	r = fdp->fdr_bpeek(dev, NULL /*data*/, nbytes, pos, FSC_PEEK);
982 
983 	/* Do not return a new position. */
984 	if (r >= 0) {
985 		m_out->m_fs_vfs_breadwrite.nbytes = r;
986 		r = OK;
987 	}
988 
989 	return r;
990 }
991 
992 /*
993  * Process a FLUSH request from VFS.
994  */
995 int
996 fsdriver_flush(const struct fsdriver * __restrict fdp,
997 	const message * __restrict m_in, message * __restrict __unused m_out)
998 {
999 	dev_t dev;
1000 
1001 	dev = m_in->m_vfs_fs_flush.device;
1002 
1003 	if (fdp->fdr_bflush != NULL)
1004 		fdp->fdr_bflush(dev);
1005 
1006 	return OK;
1007 }
1008