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