xref: /dragonfly/sys/vfs/dirfs/dirfs_vnops.c (revision 19b217af)
1 /*
2  * Copyright (c) 2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Antonio Huete Jimenez <tuxillo@quantumachine.net>
6  * by Matthew Dillon <dillon@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 /*
38  * See below a small table with the vnode operation and syscall correspondence
39  * where it applies:
40  *
41  * VNODE OP		SCALL	SCALL_AT  FD	PATH	COMMENTS
42  * dirfs_ncreate	Y	Y	  Y	Y	open(2), openat(2)
43  * dirfs_nresolve	-	-	  -	Y	no syscall needed
44  * dirfs_nlookupdot	-	-	  -	-	-
45  * dirfs_nmknod		Y	Y	  Y	Y	mknod(2), mknodat(2)
46  * dirfs_open		Y	Y	  Y	Y	open(2), openat(2)
47  * dirfs_close		Y	Y	  Y	Y	close(2)
48  * dirfs_access		-	-	  -	-	data from stat(2)
49  * dirfs_getattr	Y	Y	  Y	Y	lstat(2), fstatat(2)
50  * dirfs_setattr	-	-	  -	-	-
51  * dirfs_read		Y	-	  Y	-	read(2).
52  * dirfs_write		Y	-	  Y	-	write(2).
53  * dirfs_fsync		Y	-	  Y	-	fsync(2)
54  * dirfs_mountctl	-	-	  -	-	-
55  * dirfs_nremove	Y	-	  -	Y	unlink(2)
56  * dirfs_nlink		-	-	  -	-	-
57  * dirfs_nrename	Y	Y	  Y	Y	rename(2), renameat(2)
58  * dirfs_nmkdir		Y	Y	  Y	Y	mkdir(2), mkdirat(2)
59  * dirfs_nrmdir		Y	-	  -	Y	rmdir(2)
60  * dirfs_nsymlink	Y	Y	  Y	Y	symlink(2), symlinkat(2)
61  * dirfs_readdir	Y	-	  Y	-	getdirentries(2)
62  * dirfs_readlink	Y	Y	  Y	Y	readlinkat(2)
63  * dirfs_inactive	-	-	  -	-	-
64  * dirfs_reclaim	-	-	  -	-	-
65  * dirfs_print		-	-	  -	-	-
66  * dirfs_pathconf	-	-	  -	-	-
67  * dirfs_bmap		-	-	  -	-	-
68  * dirfs_strategy	Y	-	  Y	-	pwrite(2), pread(2)
69  * dirfs_advlock	-	-	  -	-	-
70  * dirfs_kqfilter	-	-	  -	-	-
71  */
72 
73 #include <stdio.h>
74 #include <errno.h>
75 #include <strings.h>
76 #include <unistd.h>
77 
78 #include <sys/vfsops.h>
79 #include <sys/vnode.h>
80 #include <sys/stat.h>
81 #include <sys/namecache.h>
82 #include <sys/queue.h>
83 #include <sys/systm.h>
84 #include <sys/dirent.h>
85 #include <sys/mount.h>
86 #include <sys/signalvar.h>
87 #include <sys/resource.h>
88 #include <sys/buf2.h>
89 #include <sys/kern_syscall.h>
90 #include <sys/ktr.h>
91 
92 #include "dirfs.h"
93 
94 /*
95  * Kernel tracing facilities
96  */
97 KTR_INFO_MASTER_EXTERN(dirfs);
98 
99 KTR_INFO(KTR_DIRFS, dirfs, unsupported, 0,
100     "DIRFS(func=%s)",
101     const char *func);
102 
103 KTR_INFO(KTR_DIRFS, dirfs, nresolve, 0,
104     "DIRFS(dnp=%p ncp_name=%s parent=%p pfd=%d error=%d)",
105     dirfs_node_t dnp, char *name, dirfs_node_t pdnp, int pfd, int error);
106 
107 KTR_INFO(KTR_DIRFS, dirfs, ncreate, 1,
108     "DIRFS(dnp=%p ncp_name=%s parent=%p pfd=%d error=%d)",
109     dirfs_node_t dnp, char *name, dirfs_node_t pdnp, int pfd, int error);
110 
111 KTR_INFO(KTR_DIRFS, dirfs, open, 2,
112     "DIRFS(dnp=%p dn_name=%s nfd=%d)",
113     dirfs_node_t dnp, char *name, int fd);
114 
115 KTR_INFO(KTR_DIRFS, dirfs, close, 3,
116     "DIRFS(dnp=%p fd=%d opencount=%d writecount=%d vfsync error=%d)",
117     dirfs_node_t dnp, int fd, int oc, int wc, int error);
118 
119 KTR_INFO(KTR_DIRFS, dirfs, readdir, 4,
120     "DIRFS(dnp=%p fd=%d startoff=%jd uio_offset=%jd)",
121     dirfs_node_t dnp, int fd, off_t startoff, off_t uoff);
122 
123 KTR_INFO(KTR_DIRFS, dirfs, access, 5,
124     "DIRFS(dnp=%p error=%d)",
125     dirfs_node_t dnp, int error);
126 
127 KTR_INFO(KTR_DIRFS, dirfs, getattr, 6,
128     "DIRFS(dnp=%p error=%d)",
129     dirfs_node_t dnp, int error);
130 
131 KTR_INFO(KTR_DIRFS, dirfs, setattr, 7,
132     "DIRFS(dnp=%p action=%s error=%d)",
133     dirfs_node_t dnp, const char *action, int error);
134 
135 KTR_INFO(KTR_DIRFS, dirfs, fsync, 8,
136     "DIRFS(dnp=%p error=%d)",
137     dirfs_node_t dnp, int error);
138 
139 KTR_INFO(KTR_DIRFS, dirfs, read, 9,
140     "DIRFS(dnp=%p size=%jd error=%d)",
141     dirfs_node_t dnp, size_t size, int error);
142 
143 KTR_INFO(KTR_DIRFS, dirfs, write, 10,
144     "DIRFS(dnp=%p size=%jd boff=%jd uio_resid=%jd error=%d)",
145     dirfs_node_t dnp, off_t boff, size_t resid, size_t size, int error);
146 
147 KTR_INFO(KTR_DIRFS, dirfs, strategy, 11,
148     "DIRFS(dnp=%p dnp_size=%jd iosize=%jd b_cmd=%d b_error=%d "
149     "b_resid=%d bio_off=%jd error=%d)",
150     dirfs_node_t dnp, size_t size, size_t iosize, int cmd, int berror,
151     int bresid, off_t biooff, int error);
152 
153 KTR_INFO(KTR_DIRFS, dirfs, nremove, 12,
154     "DIRFS(dnp=%p pdnp=%p error=%d)",
155     dirfs_node_t dnp, dirfs_node_t pdnp, int error);
156 
157 KTR_INFO(KTR_DIRFS, dirfs, nmkdir, 13,
158     "DIRFS(pdnp=%p dnp=%p nc_name=%p error=%d)",
159     dirfs_node_t dnp, dirfs_node_t pdnp, char *n, int error);
160 
161 KTR_INFO(KTR_DIRFS, dirfs, nrmdir, 13,
162     "DIRFS(pdnp=%p dnp=%p error=%d)",
163     dirfs_node_t dnp, dirfs_node_t pdnp, int error);
164 
165 KTR_INFO(KTR_DIRFS, dirfs, nsymlink, 14,
166     "DIRFS(dnp=%p target=%s symlink=%s error=%d)",
167     dirfs_node_t dnp, char *tgt, char *lnk, int error);
168 
169 /* Needed prototypes */
170 int dirfs_access(struct vop_access_args *);
171 int dirfs_getattr(struct vop_getattr_args *);
172 int dirfs_setattr(struct vop_setattr_args *);
173 int dirfs_reclaim(struct vop_reclaim_args *);
174 
175 static int
176 dirfs_nresolve(struct vop_nresolve_args *ap)
177 {
178 	dirfs_node_t pdnp, dnp, d1, d2;
179 	dirfs_mount_t dmp;
180 	struct namecache *ncp;
181 	struct nchandle *nch;
182 	struct vnode *dvp;
183 	struct vnode *vp;
184 	struct mount *mp;
185 	int error;
186 
187 	debug_called();
188 
189 	error = 0;
190 	nch = ap->a_nch;
191 	ncp = nch->ncp;
192 	mp = nch->mount;
193 	dvp = ap->a_dvp;
194 	vp = NULL;
195 	dnp = d1 = d2 = NULL;
196 	pdnp = VP_TO_NODE(dvp);
197 	dmp = VFS_TO_DIRFS(mp);
198 
199 	dirfs_node_lock(pdnp);
200 	TAILQ_FOREACH_MUTABLE(d1, &dmp->dm_fdlist, dn_fdentry, d2) {
201 		if (d1->dn_parent == pdnp &&
202 		    (strcmp(d1->dn_name, ncp->nc_name) == 0)) {
203 			dnp = d1;
204 			dirfs_node_ref(dnp);
205 			passive_fd_list_hits++;
206 			break;
207 		}
208 	}
209 	dirfs_node_unlock(pdnp);
210 
211 	if (dnp) {
212 		dirfs_alloc_vp(mp, &vp, LK_CANRECURSE, dnp);
213 		dirfs_node_drop(dmp, dnp);
214 	} else {
215 		passive_fd_list_miss++;
216 		error = dirfs_alloc_file(dmp, &dnp, pdnp, ncp, &vp, NULL, 0);
217 	}
218 
219 	if (vp) {
220 		if (error && error == ENOENT) {
221 			cache_setvp(nch, NULL);
222 		} else {
223 			vn_unlock(vp);
224 			cache_setvp(nch, vp);
225 			vrele(vp);
226 		}
227 	}
228 
229 	KTR_LOG(dirfs_nresolve, dnp, ncp->nc_name, pdnp, pdnp->dn_fd, error);
230 
231 	return error;
232 }
233 
234 static int
235 dirfs_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
236 {
237 	debug_called();
238 
239 	KTR_LOG(dirfs_unsupported, __func__);
240 
241 	return EOPNOTSUPP;
242 }
243 
244 static int
245 dirfs_ncreate(struct vop_ncreate_args *ap)
246 {
247 	dirfs_node_t pdnp;
248 	dirfs_node_t dnp;
249 	dirfs_mount_t dmp;
250 	struct namecache *ncp;
251 	struct vnode *dvp;
252 	struct vnode **vpp;
253 	struct vattr *vap;
254 	int perms = 0;
255 	int error;
256 
257 	debug_called();
258 
259 	error = 0;
260 	dnp = NULL;
261 	dvp = ap->a_dvp;
262 	pdnp = VP_TO_NODE(dvp);
263 	dmp = VFS_TO_DIRFS(dvp->v_mount);
264 	vap = ap->a_vap;
265 	ncp = ap->a_nch->ncp;
266 	vpp = ap->a_vpp;
267 
268 	dirfs_mount_gettoken(dmp);
269 
270 	dirfs_node_getperms(pdnp, &perms);
271 	if ((perms & DIRFS_NODE_WR) == 0)
272 		error = EPERM;
273 
274 	error = dirfs_alloc_file(dmp, &dnp, pdnp, ncp, vpp, vap,
275 	    (O_CREAT | O_RDWR));
276 
277 	if (error == 0) {
278 		cache_setunresolved(ap->a_nch);
279 		cache_setvp(ap->a_nch, *vpp);
280 	}
281 
282 	dirfs_mount_reltoken(dmp);
283 
284 	KTR_LOG(dirfs_ncreate, dnp, ncp->nc_name, pdnp, pdnp->dn_fd, error);
285 
286 	return error;
287 }
288 
289 static int
290 dirfs_nmknod(struct vop_nmknod_args *v)
291 {
292 	debug_called();
293 
294 	return EOPNOTSUPP;
295 }
296 
297 static int
298 dirfs_open(struct vop_open_args *ap)
299 {
300 	dirfs_node_t dnp;
301 	dirfs_mount_t dmp;
302 	struct vnode *vp;
303 	int error;
304 
305 	debug_called();
306 
307 	vp = ap->a_vp;
308 	dnp = VP_TO_NODE(vp);
309 	dmp = VFS_TO_DIRFS(vp->v_mount);
310 	error = 0;
311 
312 	/*
313 	 * Root inode has been allocated and opened in VFS_ROOT() so
314 	 * no reason to attempt to open it again.
315 	 */
316 	if (dmp->dm_root != dnp && dnp->dn_fd == DIRFS_NOFD) {
317 		error = dirfs_open_helper(dmp, dnp, DIRFS_NOFD, NULL);
318 		if (error)
319 			return error;
320 	}
321 
322 	KTR_LOG(dirfs_open, dnp, dnp->dn_name, dnp->dn_fd);
323 
324 	return vop_stdopen(ap);
325 }
326 
327 static int
328 dirfs_close(struct vop_close_args *ap)
329 {
330 	struct vnode *vp;
331 	dirfs_node_t dnp;
332 	int error;
333 
334 	debug_called();
335 
336 	error = 0;
337 	vp = ap->a_vp;
338 	dnp = VP_TO_NODE(vp);
339 
340 	if (vp->v_type == VREG) {
341 		error = vfsync(vp, 0, 1, NULL, NULL);
342 		if (error)
343 			dbg(5, "vfsync error=%d\n", error);
344 	}
345 	vop_stdclose(ap);
346 
347 	/*
348 	 * XXX - Currently VOP_INACTIVE() is not being called unless there is
349 	 * vnode pressure so, by now, call inactive directly on last close.
350 	 */
351 	vn_lock(vp, LK_UPGRADE | LK_RETRY);
352 	if (vp->v_opencount == 0 && vp->v_writecount == 0)
353 		VOP_INACTIVE(vp);
354 
355 	KTR_LOG(dirfs_close, dnp, dnp->dn_fd, vp->v_opencount,
356 	    vp->v_writecount, error);
357 
358 	return 0;
359 }
360 
361 int
362 dirfs_access(struct vop_access_args *ap)
363 {
364 	struct vnode *vp = ap->a_vp;
365 	int error;
366 	dirfs_node_t dnp;
367 
368 	debug_called();
369 
370 	dnp = VP_TO_NODE(vp);
371 
372 	switch (vp->v_type) {
373 	case VDIR:
374 		/* FALLTHROUGH */
375 	case VLNK:
376 		/* FALLTHROUGH */
377 	case VREG:
378 		if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
379 			error = EROFS;
380 			goto out;
381 		}
382 		break;
383 	case VBLK:
384 		/* FALLTHROUGH */
385 	case VCHR:
386 		/* FALLTHROUGH */
387 	case VSOCK:
388 		/* FALLTHROUGH */
389 	case VFIFO:
390 		break;
391 
392 	default:
393 		error = EINVAL;
394 		goto out;
395 	}
396 
397 	error = vop_helper_access(ap, dnp->dn_uid,
398 	    dnp->dn_gid, dnp->dn_mode, 0);
399 
400 out:
401 	KTR_LOG(dirfs_access, dnp, error);
402 
403 	return error;
404 }
405 
406 int
407 dirfs_getattr(struct vop_getattr_args *ap)
408 {
409 	dirfs_mount_t dmp;
410 	dirfs_node_t dnp;
411 	dirfs_node_t pathnp;
412 	struct vnode *vp;
413 	struct vattr *vap;
414 	char *tmp;
415 	char *pathfree;
416 	int error;
417 
418 	debug_called();
419 
420 	vp = ap->a_vp;
421 	vap = ap->a_vap;
422 	dnp = VP_TO_NODE(vp);
423 	dmp = VFS_TO_DIRFS(vp->v_mount);
424 
425 	KKASSERT(dnp);	/* This must not happen */
426 
427 	if (!dirfs_node_isroot(dnp)) {
428 		pathnp = dirfs_findfd(dmp, dnp, &tmp, &pathfree);
429 
430 		KKASSERT(pathnp->dn_fd != DIRFS_NOFD);
431 
432 		error = dirfs_node_stat(pathnp->dn_fd, tmp, dnp);
433 		dirfs_dropfd(dmp, pathnp, pathfree);
434 	} else {
435 		error = dirfs_node_stat(DIRFS_NOFD, dmp->dm_path, dnp);
436 	}
437 
438 	if (error == 0) {
439 		dirfs_node_lock(dnp);
440 		vap->va_nlink = dnp->dn_links;
441 		vap->va_type = dnp->dn_type;
442 		vap->va_mode = dnp->dn_mode;
443 		vap->va_uid = dnp->dn_uid;
444 		vap->va_gid = dnp->dn_gid;
445 		vap->va_fileid = dnp->dn_ino;
446 		vap->va_size = dnp->dn_size;
447 		vap->va_blocksize = dnp->dn_blocksize;
448 		vap->va_atime.tv_sec = dnp->dn_atime;
449 		vap->va_atime.tv_nsec = dnp->dn_atimensec;
450 		vap->va_mtime.tv_sec = dnp->dn_mtime;
451 		vap->va_mtime.tv_nsec = dnp->dn_mtimensec;
452 		vap->va_ctime.tv_sec = dnp->dn_ctime;
453 		vap->va_ctime.tv_nsec = dnp->dn_ctimensec;
454 		vap->va_bytes = dnp->dn_size;
455 		vap->va_gen = dnp->dn_gen;
456 		vap->va_flags = dnp->dn_flags;
457 		vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
458 		dirfs_node_unlock(dnp);
459 	}
460 
461 	KTR_LOG(dirfs_getattr, dnp, error);
462 
463 	return 0;
464 }
465 
466 int
467 dirfs_setattr(struct vop_setattr_args *ap)
468 {
469 	dirfs_mount_t dmp;
470 	dirfs_node_t dnp;
471 	struct vnode *vp;
472 	struct vattr *vap;
473 	struct ucred *cred;
474 	int error;
475 #ifdef KTR
476 	const char *msg[6] = {
477 		"invalid",
478 		"chflags",
479 		"chsize",
480 		"chown",
481 		"chmod",
482 		"chtimes"
483 	};
484 #endif
485 	int msgno;
486 
487 	debug_called();
488 
489 	error = msgno = 0;
490 	vp = ap->a_vp;
491 	vap = ap->a_vap;
492 	cred = ap->a_cred;
493 	dnp = VP_TO_NODE(vp);
494 	dmp = VFS_TO_DIRFS(vp->v_mount);
495 
496 	dirfs_mount_gettoken(dmp);
497 
498 	/*
499 	 * Check for unsettable attributes.
500 	 */
501 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
502 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
503 	    (vap->va_blocksize != VNOVAL) || (vap->va_rmajor != VNOVAL) ||
504 	    ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
505 		msgno = 0;
506 		error = EINVAL;
507 		goto out;
508 	}
509 
510 	/*
511 	 * Change file flags
512 	 */
513 	if (error == 0 && (vap->va_flags != VNOVAL)) {
514 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
515 			error = EROFS;
516 		else
517 			error = dirfs_node_chflags(dnp, vap->va_flags, cred);
518 		msgno = 1;
519 		goto out;
520 	}
521 
522 	/*
523 	 * Extend or truncate a file
524 	 */
525 	if (error == 0 && (vap->va_size != VNOVAL)) {
526 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
527 			error = EROFS;
528 		else
529 			error = dirfs_node_chsize(dnp, vap->va_size);
530 		dbg(2, "dnp size=%jd vap size=%jd\n", dnp->dn_size, vap->va_size);
531 		msgno = 2;
532 		goto out;
533 	}
534 
535 	/*
536 	 * Change file owner or group
537 	 */
538 	if (error == 0 && (vap->va_uid != (uid_t)VNOVAL ||
539 		vap->va_gid != (gid_t)VNOVAL)) {
540 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
541 			error = EROFS;
542 		} else {
543 			mode_t cur_mode = dnp->dn_mode;
544 			uid_t cur_uid = dnp->dn_uid;
545 			gid_t cur_gid = dnp->dn_gid;
546 
547 			error = vop_helper_chown(ap->a_vp, vap->va_uid,
548 						 vap->va_gid, ap->a_cred,
549 						 &cur_uid, &cur_gid, &cur_mode);
550 			if (error == 0 &&
551 			    (cur_mode != dnp->dn_mode ||
552 			     cur_uid != dnp->dn_uid ||
553 			     cur_gid != dnp->dn_gid)) {
554 				error = dirfs_node_chown(dmp, dnp, cur_uid,
555 							 cur_gid, cur_mode);
556 			}
557 		}
558 		msgno = 3;
559 		goto out;
560 	}
561 
562 	/*
563 	 * Change file mode
564 	 */
565 	if (error == 0 && (vap->va_mode != (mode_t)VNOVAL)) {
566 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
567 			error = EROFS;
568 		} else {
569 			mode_t cur_mode = dnp->dn_mode;
570 			uid_t cur_uid = dnp->dn_uid;
571 			gid_t cur_gid = dnp->dn_gid;
572 
573 			error = vop_helper_chmod(ap->a_vp, vap->va_mode,
574 						 ap->a_cred,
575 						 cur_uid, cur_gid, &cur_mode);
576 			if (error == 0 && cur_mode != dnp->dn_mode) {
577 				error = dirfs_node_chmod(dmp, dnp, cur_mode);
578 			}
579 		}
580 		msgno = 4;
581 		goto out;
582 	}
583 
584 	/*
585 	 * Change file times
586 	 */
587 	if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
588 		vap->va_atime.tv_nsec != VNOVAL) ||
589 		(vap->va_mtime.tv_sec != VNOVAL &&
590 		vap->va_mtime.tv_nsec != VNOVAL) )) {
591 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
592 			error = EROFS;
593 		else
594 			error = dirfs_node_chtimes(dnp);
595 		msgno = 5;
596 		goto out;
597 
598 	}
599 out:
600 	dirfs_mount_reltoken(dmp);
601 
602 	KTR_LOG(dirfs_setattr, dnp, msg[msgno], error);
603 
604 	return error;
605 }
606 
607 static int
608 dirfs_fsync(struct vop_fsync_args *ap)
609 {
610 	dirfs_node_t dnp = VP_TO_NODE(ap->a_vp);
611 	int error = 0;
612 
613 	debug_called();
614 
615 	vfsync(ap->a_vp, ap->a_waitfor, 1, NULL, NULL);
616 
617 	if (dnp->dn_fd != DIRFS_NOFD) {
618 		if (fsync(dnp->dn_fd) == -1)
619 			error = fsync(dnp->dn_fd);
620 	}
621 
622 	KTR_LOG(dirfs_fsync, dnp, error);
623 
624 	return 0;
625 }
626 
627 static int
628 dirfs_read(struct vop_read_args *ap)
629 {
630 	struct buf *bp;
631 	struct vnode *vp = ap->a_vp;
632 	struct uio *uio = ap->a_uio;
633 	dirfs_node_t dnp;
634 	off_t base_offset;
635 	size_t offset;
636 	size_t len;
637 	int error;
638 
639 	debug_called();
640 
641 	error = 0;
642 	if (uio->uio_resid == 0) {
643 		dbg(5, "zero len uio->uio_resid\n");
644 		return error;
645 	}
646 
647 	dnp = VP_TO_NODE(vp);
648 
649 	if (uio->uio_offset < 0)
650 		return (EINVAL);
651 	if (vp->v_type != VREG)
652 		return (EINVAL);
653 
654 	while (uio->uio_resid > 0 && uio->uio_offset < dnp->dn_size) {
655 		/*
656 		 * Use buffer cache I/O (via dirfs_strategy)
657 		 */
658 		offset = (size_t)uio->uio_offset & BMASK;
659 		base_offset = (off_t)uio->uio_offset - offset;
660 		bp = getcacheblk(vp, base_offset, BSIZE, 0);
661 		if (bp == NULL) {
662 			lwkt_gettoken(&vp->v_mount->mnt_token);
663 			error = bread(vp, base_offset, BSIZE, &bp);
664 			if (error) {
665 				brelse(bp);
666 				lwkt_reltoken(&vp->v_mount->mnt_token);
667 				dbg(5, "dirfs_read bread error %d\n", error);
668 				break;
669 			}
670 			lwkt_reltoken(&vp->v_mount->mnt_token);
671 		}
672 
673 		/*
674 		 * Figure out how many bytes we can actually copy this loop.
675 		 */
676 		len = BSIZE - offset;
677 		if (len > uio->uio_resid)
678 			len = uio->uio_resid;
679 		if (len > dnp->dn_size - uio->uio_offset)
680 			len = (size_t)(dnp->dn_size - uio->uio_offset);
681 
682 		error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio);
683 		bqrelse(bp);
684 		if (error) {
685 			dbg(5, "dirfs_read uiomove error %d\n", error);
686 			break;
687 		}
688 	}
689 
690 	KTR_LOG(dirfs_read, dnp, dnp->dn_size, error);
691 
692 	return(error);
693 }
694 
695 static int
696 dirfs_write (struct vop_write_args *ap)
697 {
698 	dirfs_node_t dnp;
699 	dirfs_mount_t dmp;
700 	struct buf *bp;
701 	struct vnode *vp = ap->a_vp;
702 	struct uio *uio = ap->a_uio;
703 	struct thread *td = uio->uio_td;
704 	int error;
705 	off_t osize;
706 	off_t nsize;
707 	off_t base_offset;
708 	size_t offset;
709 	size_t len;
710 	struct rlimit limit;
711 
712 	debug_called();
713 
714 	error = 0;
715 	if (uio->uio_resid == 0) {
716 		dbg(5, "zero-length uio->uio_resid\n");
717 		return error;
718 	}
719 
720 	dnp = VP_TO_NODE(vp);
721 	dmp = VFS_TO_DIRFS(vp->v_mount);
722 
723 	if (vp->v_type != VREG)
724 		return (EINVAL);
725 
726 	if (vp->v_type == VREG && td != NULL) {
727 		error = kern_getrlimit(RLIMIT_FSIZE, &limit);
728 		if (error != 0) {
729 			dbg(5, "rlimit failure\n");
730 			return error;
731 		}
732 		if (uio->uio_offset + uio->uio_resid > limit.rlim_cur) {
733 			dbg(5, "file too big\n");
734 			ksignal(td->td_proc, SIGXFSZ);
735 			return (EFBIG);
736 		}
737 	}
738 
739 	if (ap->a_ioflag & IO_APPEND)
740 		uio->uio_offset = dnp->dn_size;
741 
742 	/*
743 	 * buffer cache operations may be deferred, make sure
744 	 * the file is correctly sized right now.
745 	 */
746 	osize = dnp->dn_size;
747 	nsize = uio->uio_offset + uio->uio_resid;
748 	if (nsize > osize && uio->uio_resid) {
749 		KKASSERT(dnp->dn_fd >= 0);
750 		dnp->dn_size = nsize;
751 		ftruncate(dnp->dn_fd, nsize);
752 		nvextendbuf(vp, osize, nsize,
753 			    BSIZE, BSIZE, -1, -1, 0);
754 	} /* else nsize = osize; NOT USED */
755 
756 	while (uio->uio_resid > 0) {
757 		/*
758 		 * Use buffer cache I/O (via dirfs_strategy)
759 		 */
760 		offset = (size_t)uio->uio_offset & BMASK;
761 		base_offset = (off_t)uio->uio_offset - offset;
762 		len = BSIZE - offset;
763 
764 		if (len > uio->uio_resid)
765 			len = uio->uio_resid;
766 
767 		error = bread(vp, base_offset, BSIZE, &bp);
768 		error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio);
769 		if (error) {
770 			brelse(bp);
771 			dbg(2, "WRITE uiomove failed\n");
772 			break;
773 		}
774 
775 //		dbg(2, "WRITE dn_size=%jd uio_offset=%jd uio_resid=%jd base_offset=%jd\n",
776 //		    dnp->dn_size, uio->uio_offset, uio->uio_resid, base_offset);
777 
778 		if (ap->a_ioflag & IO_SYNC)
779 			bwrite(bp);
780 		else
781 			bdwrite(bp);
782 	}
783 
784 	KTR_LOG(dirfs_write, dnp, base_offset, uio->uio_resid,
785 	    dnp->dn_size, error);
786 
787 	return error;
788 }
789 
790 static int
791 dirfs_advlock (struct vop_advlock_args *ap)
792 {
793 	struct vnode *vp = ap->a_vp;
794 	dirfs_node_t dnp = VP_TO_NODE(vp);
795 
796 	debug_called();
797 
798 	return (lf_advlock(ap, &dnp->dn_advlock, dnp->dn_size));
799 }
800 
801 static int
802 dirfs_strategy(struct vop_strategy_args *ap)
803 {
804 	dirfs_node_t dnp;
805 	dirfs_mount_t dmp;
806 	struct bio *bio = ap->a_bio;
807 	struct buf *bp = bio->bio_buf;
808 	struct vnode *vp = ap->a_vp;
809 	int error;
810 	size_t iosize;
811 	char *tmp;
812 	char *pathfree;
813 
814 	debug_called();
815 
816 	dnp = VP_TO_NODE(vp);
817 	dmp = VFS_TO_DIRFS(vp->v_mount);
818 
819 	error = 0;
820 
821 	if (vp->v_type != VREG)  {
822 		dbg(5, "not VREG\n");
823 		bp->b_resid = bp->b_bcount;
824 		bp->b_flags |= B_ERROR | B_INVAL;
825 		bp->b_error = EINVAL;
826 		biodone(bio);
827 		return(0);
828 	}
829 
830 	if (dnp->dn_fd == DIRFS_NOFD) {
831 		print_backtrace(-1);
832 		panic("Meh, no fd to write to. dnp=%p\n", dnp);
833 	}
834 
835 	if (bio->bio_offset + bp->b_bcount > dnp->dn_size)
836 		iosize = dnp->dn_size - bio->bio_offset;
837 	else
838 		iosize = bp->b_bcount;
839 	KKASSERT((ssize_t)iosize >= 0);
840 
841 	switch (bp->b_cmd) {
842 	case BUF_CMD_WRITE:
843 		error = pwrite(dnp->dn_fd, bp->b_data, iosize, bio->bio_offset);
844 		break;
845 	case BUF_CMD_READ:
846 		error = pread(dnp->dn_fd, bp->b_data, iosize, bio->bio_offset);
847 		break;
848 	default:
849 		bp->b_error = error = EINVAL;
850 		bp->b_flags |= B_ERROR;
851 		break;
852 	}
853 
854 	if (error >= 0 && error < bp->b_bcount)
855 		bzero(bp->b_data + error, bp->b_bcount - error);
856 
857 	if (error < 0 && errno != EINTR) {
858 		dbg(5, "error=%d dnp=%p dnp->dn_fd=%d "
859 		    "bio->bio_offset=%ld bcount=%d resid=%d iosize=%zd\n",
860 		    errno, dnp, dnp->dn_fd, bio->bio_offset, bp->b_bcount,
861 		    bp->b_resid, iosize);
862 		bp->b_error = errno;
863 		bp->b_resid = bp->b_bcount;
864 		bp->b_flags |= B_ERROR;
865 	} else {
866 		tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
867 		dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
868 		dirfs_dropfd(dmp, NULL, pathfree);
869 	}
870 
871 	KTR_LOG(dirfs_strategy, dnp, dnp->dn_size, iosize, bp->b_cmd,
872 	    bp->b_error, bp->b_resid, bio->bio_offset, error);
873 
874 	biodone(bio);
875 
876 	return 0;
877 }
878 
879 static int
880 dirfs_bmap(struct vop_bmap_args *ap)
881 {
882 	debug_called();
883 
884 	if (ap->a_doffsetp != NULL)
885 		*ap->a_doffsetp = ap->a_loffset;
886 	if (ap->a_runp != NULL)
887 		*ap->a_runp = 0;
888 	if (ap->a_runb != NULL)
889 		*ap->a_runb = 0;
890 
891 	return 0;
892 }
893 
894 static int
895 dirfs_nremove(struct vop_nremove_args *ap)
896 {
897 	dirfs_node_t dnp, pdnp;
898 	dirfs_node_t pathnp;
899 	dirfs_mount_t dmp;
900 	struct vnode *dvp;
901 	struct nchandle *nch;
902 	struct namecache *ncp;
903 	struct mount *mp;
904 	struct vnode *vp;
905 	int error;
906 	char *tmp;
907 	char *pathfree;
908 	debug_called();
909 
910 	error = 0;
911 	tmp = NULL;
912 	vp = NULL;
913 	dvp = ap->a_dvp;
914 	nch = ap->a_nch;
915 	ncp = nch->ncp;
916 
917 	mp = dvp->v_mount;
918 	dmp = VFS_TO_DIRFS(mp);
919 
920 	lwkt_gettoken(&mp->mnt_token);
921 	cache_vget(nch, ap->a_cred, LK_SHARED, &vp);
922 	vn_unlock(vp);
923 
924 	pdnp = VP_TO_NODE(dvp);
925 	dnp = VP_TO_NODE(vp);
926 
927 	if (vp->v_type == VDIR) {
928 		error = EISDIR;
929 	} else {
930 		pathnp = dirfs_findfd(dmp, dnp, &tmp, &pathfree);
931 		dirfs_node_lock(pdnp);
932 		error = unlinkat(pathnp->dn_fd, tmp, 0);
933 		if (error == 0) {
934 			cache_unlink(nch);
935 			dirfs_node_setpassive(dmp, dnp, 0);
936 			if (dnp->dn_parent) {
937 				dirfs_node_drop(dmp, dnp->dn_parent);
938 				dnp->dn_parent = NULL;
939 			}
940 		} else {
941 			error = errno;
942 		}
943 		dirfs_node_unlock(pdnp);
944 		dirfs_dropfd(dmp, pathnp, pathfree);
945 	}
946 	vrele(vp);
947 	lwkt_reltoken(&mp->mnt_token);
948 
949 	KTR_LOG(dirfs_nremove, dnp, pdnp, error);
950 
951 	return error;
952 }
953 
954 static int
955 dirfs_nlink(struct vop_nlink_args *ap)
956 {
957 	debug_called();
958 
959 	KTR_LOG(dirfs_unsupported, __func__);
960 
961 	return EOPNOTSUPP;
962 }
963 
964 static int
965 dirfs_nrename(struct vop_nrename_args *ap)
966 {
967 	dirfs_node_t dnp, fdnp, tdnp;
968 	dirfs_mount_t dmp;
969 	struct namecache *fncp, *tncp;
970 	struct vnode *fdvp, *tdvp, *vp;
971 	struct mount *mp;
972 	char *fpath, *fpathfree;
973 	char *tpath, *tpathfree;
974 	int error;
975 
976 	debug_called();
977 
978 	error = 0;
979 	fdvp = ap->a_fdvp;
980 	tdvp = ap->a_tdvp;
981 	fncp = ap->a_fnch->ncp;
982 	tncp = ap->a_tnch->ncp;
983 	mp = fdvp->v_mount;
984 	dmp = VFS_TO_DIRFS(mp);
985 	fdnp = VP_TO_NODE(fdvp);
986 	tdnp = VP_TO_NODE(tdvp);
987 
988 	dbg(5, "fdnp=%p tdnp=%p from=%s to=%s\n", fdnp, tdnp, fncp->nc_name,
989 	    tncp->nc_name);
990 
991 	if (fdvp->v_mount != tdvp->v_mount)
992 		return(EXDEV);
993 	if (fdvp->v_mount != fncp->nc_vp->v_mount)
994 		return(EXDEV);
995 	if (fdvp->v_mount->mnt_flag & MNT_RDONLY)
996 		return (EROFS);
997 
998 	tpath = dirfs_node_absolute_path_plus(dmp, tdnp,
999 					      tncp->nc_name, &tpathfree);
1000 	fpath = dirfs_node_absolute_path_plus(dmp, fdnp,
1001 					      fncp->nc_name, &fpathfree);
1002 	error = rename(fpath, tpath);
1003 	if (error < 0)
1004 		error = errno;
1005 	if (error == 0) {
1006 		vp = fncp->nc_vp;	/* file being renamed */
1007 		dnp = VP_TO_NODE(vp);
1008 		dirfs_node_setname(dnp, tncp->nc_name, tncp->nc_nlen);
1009 
1010 		/*
1011 		 * We have to mark the target file that was replaced by
1012 		 * the rename as having been unlinked.
1013 		 */
1014 		vp = tncp->nc_vp;
1015 		if (vp) {
1016 			dbg(5, "RENAME2\n");
1017 			dnp = VP_TO_NODE(vp);
1018 			cache_unlink(ap->a_tnch);
1019 			dirfs_node_setpassive(dmp, dnp, 0);
1020 			if (dnp->dn_parent) {
1021 				dirfs_node_drop(dmp, dnp->dn_parent);
1022 				dnp->dn_parent = NULL;
1023 			}
1024 
1025 			/*
1026 			 * nlinks on directories can be a bit weird.  Zero
1027 			 * it out.
1028 			 */
1029 			dnp->dn_links = 0;
1030 			cache_inval_vp(vp, CINV_DESTROY);
1031 		}
1032 		cache_rename(ap->a_fnch, ap->a_tnch);
1033 	}
1034 	dirfs_dropfd(dmp, NULL, fpathfree);
1035 	dirfs_dropfd(dmp, NULL, tpathfree);
1036 
1037 	return error;
1038 }
1039 
1040 static int
1041 dirfs_nmkdir(struct vop_nmkdir_args *ap)
1042 {
1043 	dirfs_mount_t dmp;
1044 	dirfs_node_t dnp, pdnp, dnp1;
1045 	struct namecache *ncp;
1046 	struct vattr *vap;
1047 	struct vnode *dvp;
1048 	struct vnode **vpp;
1049 	char *tmp, *pathfree;
1050 	char *path;
1051 	int pfd, error;
1052 	int extrapath;
1053 
1054 	debug_called();
1055 
1056 	extrapath = error = 0;
1057 	dvp = ap->a_dvp;
1058 	vpp = ap->a_vpp;
1059 	dmp = VFS_TO_DIRFS(dvp->v_mount);
1060 	pdnp = VP_TO_NODE(dvp);
1061 	ncp = ap->a_nch->ncp;
1062 	vap = ap->a_vap;
1063 	pathfree = tmp = path = NULL;
1064 	dnp = NULL;
1065 
1066 	dirfs_node_lock(pdnp);
1067 	if (pdnp->dn_fd != DIRFS_NOFD) {
1068 		pfd = pdnp->dn_fd;
1069 		path = ncp->nc_name;
1070 	} else {
1071 		dnp1 = dirfs_findfd(dmp, pdnp, &tmp, &pathfree);
1072 		pfd = dnp1->dn_fd;
1073 		/* XXX check there is room to copy the path */
1074 		path = kmalloc(MAXPATHLEN, M_DIRFS_MISC, M_ZERO | M_WAITOK);
1075 		ksnprintf(path, MAXPATHLEN, "%s/%s", tmp, ncp->nc_name);
1076 		extrapath = 1;
1077 		dirfs_dropfd(dmp, dnp1, pathfree);
1078 	}
1079 
1080 	error = mkdirat(pfd, path, vap->va_mode);
1081 	if (error) {
1082 		error = errno;
1083 	} else { /* Directory has been made */
1084 		error = dirfs_alloc_file(dmp, &dnp, pdnp, ncp, vpp,
1085 		    vap, O_DIRECTORY);
1086 		if (error)
1087 			error = errno;
1088 		cache_setunresolved(ap->a_nch);
1089 		cache_setvp(ap->a_nch, *vpp);
1090 	}
1091 	dirfs_node_unlock(pdnp);
1092 
1093 	if (extrapath)
1094 		kfree(path, M_DIRFS_MISC);
1095 
1096 	KTR_LOG(dirfs_nmkdir, pdnp, dnp, ncp->nc_name, error);
1097 
1098 	return error;
1099 }
1100 
1101 static int
1102 dirfs_nrmdir(struct vop_nrmdir_args *ap)
1103 {
1104 	dirfs_node_t dnp, pdnp;
1105 	dirfs_mount_t dmp;
1106 	struct vnode *dvp;
1107 	struct nchandle *nch;
1108 	struct namecache *ncp;
1109 	struct mount *mp;
1110 	struct vnode *vp;
1111 	int error;
1112 	char *tmp;
1113 	char *pathfree;
1114 
1115 	debug_called();
1116 
1117 	error = 0;
1118 	tmp = NULL;
1119 	vp = NULL;
1120 	dvp = ap->a_dvp;
1121 	nch = ap->a_nch;
1122 	ncp = nch->ncp;
1123 
1124 	mp = dvp->v_mount;
1125 	dmp = VFS_TO_DIRFS(mp);
1126 
1127 	lwkt_gettoken(&mp->mnt_token);
1128 	cache_vget(nch, ap->a_cred, LK_SHARED, &vp);
1129 	vn_unlock(vp);
1130 
1131 	pdnp = VP_TO_NODE(dvp);
1132 	dnp = VP_TO_NODE(vp);
1133 
1134 	if (vp->v_type != VDIR) {
1135 		error = ENOTDIR;
1136 	} else {
1137 		tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
1138 		dirfs_node_lock(pdnp);
1139 		error = rmdir(tmp);
1140 		if (error == 0) {
1141 			cache_unlink(nch);
1142 			dirfs_node_setpassive(dmp, dnp, 0);
1143 			if (dnp->dn_parent) {
1144 				dirfs_node_drop(dmp, dnp->dn_parent);
1145 				dnp->dn_parent = NULL;
1146 			}
1147 
1148 			/*
1149 			 * nlinks on directories can be a bit weird.  Zero
1150 			 * it out.
1151 			 */
1152 			dnp->dn_links = 0;
1153 			cache_inval_vp(vp, CINV_DESTROY);
1154 		} else {
1155 			error = errno;
1156 		}
1157 		dirfs_node_unlock(pdnp);
1158 		dirfs_dropfd(dmp, NULL, pathfree);
1159 	}
1160 	vrele(vp);
1161 	lwkt_reltoken(&mp->mnt_token);
1162 
1163 	KTR_LOG(dirfs_nrmdir, dnp, pdnp, error);
1164 
1165 	return error;
1166 }
1167 
1168 static int
1169 dirfs_nsymlink(struct vop_nsymlink_args *ap)
1170 {
1171 	dirfs_mount_t dmp;
1172 	dirfs_node_t dnp, pdnp;
1173 	struct mount *mp;
1174 	struct namecache *ncp;
1175 	struct vattr *vap;
1176 	struct vnode *dvp;
1177 	struct vnode **vpp;
1178 	char *tmp, *pathfree;
1179 	char *path;
1180 	int error;
1181 
1182 	debug_called();
1183 
1184 	error = 0;
1185 	dvp = ap->a_dvp;
1186 	vpp = ap->a_vpp;
1187 	mp = dvp->v_mount;
1188 	dmp = VFS_TO_DIRFS(dvp->v_mount);
1189 	pdnp = VP_TO_NODE(dvp);
1190 	ncp = ap->a_nch->ncp;
1191 	vap = ap->a_vap;
1192 	pathfree = tmp = path = NULL;
1193 	dnp = NULL;
1194 
1195 	lwkt_gettoken(&mp->mnt_token);
1196 	vap->va_type = VLNK;
1197 
1198 	/* Find out the whole path of our new symbolic link */
1199 	tmp = dirfs_node_absolute_path(dmp, pdnp, &pathfree);
1200 	/* XXX check there is room to copy the path */
1201 	path = kmalloc(MAXPATHLEN, M_DIRFS_MISC, M_ZERO | M_WAITOK);
1202 	ksnprintf(path, MAXPATHLEN, "%s/%s", tmp, ncp->nc_name);
1203 	dirfs_dropfd(dmp, NULL, pathfree);
1204 
1205 	error = symlink(ap->a_target, path);
1206 	if (error) {
1207 		error = errno;
1208 	} else { /* Symlink has been made */
1209 		error = dirfs_alloc_file(dmp, &dnp, pdnp, ncp, vpp,
1210 		    NULL, 0);
1211 		if (error)
1212 			error = errno;
1213 		cache_setunresolved(ap->a_nch);
1214 		cache_setvp(ap->a_nch, *vpp);
1215 	}
1216 	dbg(5, "path=%s a_target=%s\n", path, ap->a_target);
1217 
1218 	KTR_LOG(dirfs_nsymlink, dnp, ap->a_target, path, error);
1219 	kfree(path, M_DIRFS_MISC);
1220 	lwkt_reltoken(&mp->mnt_token);
1221 
1222 	return error;
1223 
1224 }
1225 
1226 static int
1227 dirfs_readdir(struct vop_readdir_args *ap)
1228 {
1229 
1230 	struct dirent *dp, *dpn;
1231 	off_t __unused **cookies = ap->a_cookies;
1232 	int *ncookies = ap->a_ncookies;
1233 	int bytes;
1234 	char *buf;
1235 	long base;
1236 	struct vnode *vp = ap->a_vp;
1237 	struct uio *uio;
1238 	dirfs_node_t dnp;
1239 	off_t startoff;
1240 	off_t cnt;
1241 	int error, r;
1242 	size_t bufsiz;
1243 	off_t curoff;
1244 
1245 	debug_called();
1246 
1247 	if (ncookies)
1248 		debug(1, "ncookies=%d\n", *ncookies);
1249 
1250 	dnp = VP_TO_NODE(vp);
1251 	uio = ap->a_uio;
1252 	startoff = uio->uio_offset;
1253 	cnt = 0;
1254 	error = 0;
1255 	base = 0;
1256 	bytes = 0;
1257 
1258 	if (vp->v_type != VDIR)
1259 		return ENOTDIR;
1260 	if (uio->uio_resid < 0)
1261 		return EINVAL;
1262 	if ((bufsiz = uio->uio_resid) > 4096)
1263 		bufsiz = 4096;
1264 	buf = kmalloc(bufsiz, M_DIRFS_MISC, M_WAITOK | M_ZERO);
1265 
1266 	/*
1267 	 * Generally speaking we have to be able to process ALL the
1268 	 * entries returned by getdirentries() in order for the seek
1269 	 * position to be correct.  For now try to size the buffer
1270 	 * to make this happen.  A smaller buffer always works.  For
1271 	 * now just use an appropriate size.
1272 	 */
1273 	dirfs_node_lock(dnp);
1274 	lseek(dnp->dn_fd, startoff, SEEK_SET);
1275 	bytes = getdirentries(dnp->dn_fd, buf, bufsiz, &base);
1276 	dbg(5, "seek %016jx %016jx %016jx\n",
1277 		(intmax_t)startoff, (intmax_t)base,
1278 		(intmax_t)lseek(dnp->dn_fd, 0, SEEK_CUR));
1279 	if (bytes < 0) {
1280 		if (errno == EINVAL)
1281 			panic("EINVAL on readdir\n");
1282 		error = errno;
1283 		curoff = startoff;
1284 		goto out;
1285 	} else if (bytes == 0) {
1286 		*ap->a_eofflag = 1;
1287 		curoff = startoff;
1288 		goto out;
1289 	}
1290 
1291 	for (dp = (struct dirent *)buf; bytes > 0 && uio->uio_resid > 0;
1292 	    bytes -= _DIRENT_DIRSIZ(dp), dp = dpn) {
1293 		r = vop_write_dirent(&error, uio, dp->d_ino, dp->d_type,
1294 		    dp->d_namlen, dp->d_name);
1295 		if (error || r)
1296 			break;
1297 		dpn = _DIRENT_NEXT(dp);
1298 		dp = dpn;
1299 		cnt++;
1300 	}
1301 	curoff = lseek(dnp->dn_fd, 0, SEEK_CUR);
1302 
1303 out:
1304 	kfree(buf, M_DIRFS_MISC);
1305 	uio->uio_offset = curoff;
1306 	dirfs_node_unlock(dnp);
1307 
1308 	KTR_LOG(dirfs_readdir, dnp, dnp->dn_fd, startoff, uio->uio_offset);
1309 
1310 	return error;
1311 }
1312 
1313 static int
1314 dirfs_readlink(struct vop_readlink_args *ap)
1315 {
1316 	dirfs_node_t dnp, pathnp;
1317 	dirfs_mount_t dmp;
1318 	struct vnode *vp;
1319 	struct mount *mp;
1320 	struct uio *uio;
1321 	char *tmp, *pathfree, *buf;
1322 	ssize_t nlen;
1323 	int error;
1324 
1325 	debug_called();
1326 
1327 	vp = ap->a_vp;
1328 
1329 	KKASSERT(vp->v_type == VLNK);
1330 
1331 	error = 0;
1332 	tmp = pathfree = NULL;
1333 	uio = ap->a_uio;
1334 	mp = vp->v_mount;
1335 	dmp = VFS_TO_DIRFS(mp);
1336 	dnp = VP_TO_NODE(vp);
1337 
1338 	lwkt_gettoken(&mp->mnt_token);
1339 
1340 	pathnp = dirfs_findfd(dmp, dnp, &tmp, &pathfree);
1341 
1342 	buf = kmalloc(uio->uio_resid, M_DIRFS_MISC, M_WAITOK | M_ZERO);
1343 	nlen = readlinkat(pathnp->dn_fd, dnp->dn_name, buf, uio->uio_resid);
1344 	if (nlen == -1 ) {
1345 		error = errno;
1346 	} else {
1347 		error = uiomove(buf, nlen + 1, uio);
1348 		buf[nlen] = '\0';
1349 		if (error)
1350 			error = errno;
1351 	}
1352 	dirfs_dropfd(dmp, pathnp, pathfree);
1353 	kfree(buf, M_DIRFS_MISC);
1354 
1355 	lwkt_reltoken(&mp->mnt_token);
1356 
1357 	return error;
1358 }
1359 
1360 /*
1361  * Main tasks to be performed.
1362  * 1) When inode is NULL recycle the vnode
1363  * 2) When the inode has 0 links:
1364  *	- Check if in the TAILQ, if so remove.
1365  *	- Destroy the inode.
1366  *	- Recycle the vnode.
1367  * 3) If none of the above, add the node to the TAILQ
1368  *    when it has a valid fd and there is room on the
1369  *    queue.
1370  *
1371  */
1372 static int
1373 dirfs_inactive(struct vop_inactive_args *ap)
1374 {
1375 	struct vnode *vp;
1376 	dirfs_mount_t dmp;
1377 	dirfs_node_t dnp;
1378 
1379 	debug_called();
1380 
1381 	vp = ap->a_vp;
1382 	dmp = VFS_TO_DIRFS(vp->v_mount);
1383 	dnp = VP_TO_NODE(vp);
1384 
1385 	/* Degenerate case */
1386 	if (dnp == NULL) {
1387 		dbg(5, "dnp was NULL\n");
1388 		vrecycle(vp);
1389 		return 0;
1390 	}
1391 
1392 	dirfs_mount_gettoken(dmp);
1393 
1394 	/*
1395 	 * Deal with the case the inode has 0 links which means it was unlinked.
1396 	 */
1397 	if (dnp->dn_links == 0) {
1398 		vrecycle(vp);
1399 		dbg(5, "recycled a vnode of an unlinked dnp\n");
1400 
1401 		goto out;
1402 	}
1403 
1404 	/*
1405 	 * Try to retain the fd in our fd cache.
1406 	 */
1407 	dirfs_node_setpassive(dmp, dnp, 1);
1408 out:
1409 	dirfs_mount_reltoken(dmp);
1410 
1411 	return 0;
1412 
1413 }
1414 
1415 int
1416 dirfs_reclaim(struct vop_reclaim_args *ap)
1417 {
1418 	struct vnode *vp;
1419 	dirfs_node_t dnp;
1420 	dirfs_mount_t dmp;
1421 
1422 	debug_called();
1423 
1424 	vp = ap->a_vp;
1425 	dnp = VP_TO_NODE(vp);
1426 	dmp = VFS_TO_DIRFS(vp->v_mount);
1427 
1428 	dirfs_free_vp(dmp, dnp);
1429 	/* dnp is now invalid, may have been destroyed */
1430 
1431 	return 0;
1432 }
1433 
1434 static int
1435 dirfs_mountctl(struct vop_mountctl_args *ap)
1436 {
1437 	debug_called();
1438 
1439 	KTR_LOG(dirfs_unsupported, __func__);
1440 
1441 	return EOPNOTSUPP;
1442 }
1443 
1444 static int
1445 dirfs_print(struct vop_print_args *v)
1446 {
1447 	debug_called();
1448 
1449 	KTR_LOG(dirfs_unsupported, __func__);
1450 
1451 	return EOPNOTSUPP;
1452 }
1453 
1454 static int __unused
1455 dirfs_pathconf(struct vop_pathconf_args *v)
1456 {
1457 	debug_called();
1458 
1459 	return EOPNOTSUPP;
1460 }
1461 
1462 static int
1463 dirfs_kqfilter (struct vop_kqfilter_args *ap)
1464 {
1465 	debug_called();
1466 
1467 	KTR_LOG(dirfs_unsupported, __func__);
1468 
1469 	return EOPNOTSUPP;
1470 }
1471 
1472 struct vop_ops dirfs_vnode_vops = {
1473 	.vop_default =			vop_defaultop,
1474 	.vop_nwhiteout =		vop_compat_nwhiteout,
1475 	.vop_ncreate =			dirfs_ncreate,
1476 	.vop_nresolve =			dirfs_nresolve,
1477 	.vop_markatime =		vop_stdmarkatime,
1478 	.vop_nlookupdotdot =		dirfs_nlookupdotdot,
1479 	.vop_nmknod =			dirfs_nmknod,
1480 	.vop_open =			dirfs_open,
1481 	.vop_close =			dirfs_close,
1482 	.vop_access =			dirfs_access,
1483 	.vop_getattr =			dirfs_getattr,
1484 	.vop_setattr =			dirfs_setattr,
1485 	.vop_read =			dirfs_read,
1486 	.vop_write =			dirfs_write,
1487 	.vop_fsync =			dirfs_fsync,
1488 	.vop_mountctl =			dirfs_mountctl,
1489 	.vop_nremove =			dirfs_nremove,
1490 	.vop_nlink =			dirfs_nlink,
1491 	.vop_nrename =			dirfs_nrename,
1492 	.vop_nmkdir =			dirfs_nmkdir,
1493 	.vop_nrmdir =			dirfs_nrmdir,
1494 	.vop_nsymlink =			dirfs_nsymlink,
1495 	.vop_readdir =			dirfs_readdir,
1496 	.vop_readlink =			dirfs_readlink,
1497 	.vop_inactive =			dirfs_inactive,
1498 	.vop_reclaim =			dirfs_reclaim,
1499 	.vop_print =			dirfs_print,
1500 	.vop_pathconf =			vop_stdpathconf,
1501 	.vop_bmap =			dirfs_bmap,
1502 	.vop_strategy =			dirfs_strategy,
1503 	.vop_advlock =			dirfs_advlock,
1504 	.vop_kqfilter =			dirfs_kqfilter,
1505 	.vop_getpages =			vop_stdgetpages,
1506 	.vop_putpages =			vop_stdputpages
1507 };
1508