xref: /netbsd-src/sys/fs/v7fs/v7fs_vnops.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: v7fs_vnops.c,v 1.6 2011/07/30 03:53:18 uch Exp $	*/
2 
3 /*-
4  * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: v7fs_vnops.c,v 1.6 2011/07/30 03:53:18 uch Exp $");
34 #if defined _KERNEL_OPT
35 #include "opt_v7fs.h"
36 #endif
37 
38 #include <sys/param.h>
39 #include <sys/kernel.h>
40 #include <sys/resource.h>
41 #include <sys/vnode.h>
42 #include <sys/namei.h>
43 #include <sys/dirent.h>
44 #include <sys/malloc.h>
45 #include <sys/lockf.h>
46 #include <sys/unistd.h>
47 #include <sys/fcntl.h>
48 #include <sys/kauth.h>
49 #include <sys/buf.h>
50 #include <sys/stat.h>	/*APPEND */
51 #include <miscfs/genfs/genfs.h>
52 
53 #include <fs/v7fs/v7fs.h>
54 #include <fs/v7fs/v7fs_impl.h>
55 #include <fs/v7fs/v7fs_inode.h>
56 #include <fs/v7fs/v7fs_dirent.h>
57 #include <fs/v7fs/v7fs_file.h>
58 #include <fs/v7fs/v7fs_datablock.h>
59 #include <fs/v7fs/v7fs_extern.h>
60 
61 #ifdef V7FS_VNOPS_DEBUG
62 #define	DPRINTF(fmt, args...)	printf("%s: " fmt, __func__, ##args)
63 #else
64 #define	DPRINTF(arg...)		((void)0)
65 #endif
66 
67 MALLOC_JUSTDEFINE(M_V7FS_VNODE, "v7fs vnode", "v7fs vnode structures");
68 MALLOC_DECLARE(M_V7FS);
69 
70 int v7fs_vnode_reload(struct mount *, struct vnode *);
71 
72 static v7fs_mode_t vtype_to_v7fs_mode(enum vtype);
73 static uint8_t v7fs_mode_to_d_type(v7fs_mode_t);
74 
75 static v7fs_mode_t
76 vtype_to_v7fs_mode(enum vtype type)
77 {
78 	/* Convert Vnode types to V7FS types (sys/vnode.h)*/
79 	v7fs_mode_t table[] = { 0, V7FS_IFREG, V7FS_IFDIR, V7FS_IFBLK,
80 				V7FS_IFCHR, V7FSBSD_IFLNK, V7FSBSD_IFSOCK,
81 				V7FSBSD_IFFIFO };
82 	return table[type];
83 }
84 
85 static uint8_t
86 v7fs_mode_to_d_type(v7fs_mode_t mode)
87 {
88 	/* Convert V7FS types to dirent d_type (sys/dirent.h)*/
89 
90 	return (mode & V7FS_IFMT) >> 12;
91 }
92 
93 int
94 v7fs_lookup(void *v)
95 {
96 	struct vop_lookup_args /* {
97 				  struct vnode *a_dvp;
98 				  struct vnode **a_vpp;
99 				  struct componentname *a_cnp;
100 				  } */ *a = v;
101 	struct vnode *dvp = a->a_dvp;
102 	struct v7fs_node *parent_node = dvp->v_data;
103 	struct v7fs_inode *parent = &parent_node->inode;
104 	struct v7fs_self *fs = parent_node->v7fsmount->core;/* my filesystem */
105 	struct vnode *vpp;
106 	struct componentname *cnp = a->a_cnp;
107 	int nameiop = cnp->cn_nameiop;
108 	const char *name = cnp->cn_nameptr;
109 	int namelen = cnp->cn_namelen;
110 	int flags = cnp->cn_flags;
111 	bool isdotdot = flags & ISDOTDOT;
112 	bool islastcn = flags & ISLASTCN;
113 	v7fs_ino_t ino;
114 	int error;
115 #ifdef V7FS_VNOPS_DEBUG
116 	const char *opname[] = { "LOOKUP", "CREATE", "DELETE", "RENAME" };
117 #endif
118 	DPRINTF("'%s' op=%s flags=%d parent=%d %o %dbyte\n", name,
119 	    opname[nameiop], cnp->cn_flags, parent->inode_number, parent->mode,
120 	    parent->filesize);
121 
122 	*a->a_vpp = 0;
123 
124 	/* Check directory permission for search */
125 	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred))) {
126 		DPRINTF("***perm.\n");
127 		return error;
128 	}
129 
130 	/* Deny last component write operation on a read-only mount */
131 	if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
132 	    (nameiop == DELETE || nameiop == RENAME)) {
133 		DPRINTF("***ROFS.\n");
134 		return EROFS;
135 	}
136 
137 	/* "." */
138 	if (namelen == 1 && name[0] == '.') {
139 		if ((nameiop == RENAME) && islastcn) {
140 			return EISDIR; /* t_vnops rename_dir(3) */
141 		}
142 		vref(dvp); /* v_usecount++ */
143 		*a->a_vpp = dvp;
144 		DPRINTF("done.(.)\n");
145 		return 0;
146 	}
147 
148 	/* ".." and reguler file. */
149 	if ((error = v7fs_file_lookup_by_name(fs, parent, name, &ino))) {
150 		/* Not found. Tell this entry be able to allocate. */
151 		if (((nameiop == CREATE) || (nameiop == RENAME)) && islastcn) {
152 			/* Check directory permission to allocate. */
153 			if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) {
154 				DPRINTF("access denied. (%s)\n", name);
155 				return error;
156 			}
157 			DPRINTF("EJUSTRETURN op=%d (%s)\n", nameiop, name);
158 			return EJUSTRETURN;
159 		}
160 		DPRINTF("lastcn=%d\n", flags & ISLASTCN);
161 		return error;
162 	}
163 
164 	/* Entry found. Allocate v-node */
165 	// Check permissions?
166 	vpp = 0;
167 	if (isdotdot) {
168 		VOP_UNLOCK(dvp); /* preserve reference count. (not vput) */
169 	}
170 	DPRINTF("enter vget\n");
171 	if ((error = v7fs_vget(dvp->v_mount, ino, &vpp))) {
172 		DPRINTF("***can't get vnode.\n");
173 		return error;
174 	}
175 	DPRINTF("exit vget\n");
176 	if (isdotdot) {
177 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
178 	}
179 	*a->a_vpp = vpp;
180 	DPRINTF("done.(%s)\n", name);
181 
182 	return 0;
183 }
184 
185 int
186 v7fs_create(void *v)
187 {
188 	struct vop_create_args /* {
189 				  struct vnode *a_dvp;
190 				  struct vnode **a_vpp;
191 				  struct componentname *a_cnp;
192 				  struct vattr *a_vap;
193 				  } */ *a = v;
194 	struct v7fs_node *parent_node = a->a_dvp->v_data;
195 	struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
196 	struct v7fs_self *fs = v7fsmount->core;
197 	struct mount *mp = v7fsmount->mountp;
198 	struct v7fs_fileattr attr;
199 	struct vattr *va = a->a_vap;
200 	kauth_cred_t cr = a->a_cnp->cn_cred;
201 	v7fs_ino_t ino;
202 	int error = 0;
203 
204 	DPRINTF("%s parent#%d\n", a->a_cnp->cn_nameptr,
205 	    parent_node->inode.inode_number);
206 	KDASSERT((va->va_type == VREG) || (va->va_type == VSOCK));
207 
208 	memset(&attr, 0, sizeof(attr));
209 	attr.uid = kauth_cred_geteuid(cr);
210 	attr.gid = kauth_cred_getegid(cr);
211 	attr.mode = va->va_mode | vtype_to_v7fs_mode (va->va_type);
212 	attr.device = 0;
213 
214 	/* Allocate disk entry. and register its entry to parent directory. */
215 	if ((error = v7fs_file_allocate(fs, &parent_node->inode,
216 		    a->a_cnp->cn_nameptr, &attr, &ino))) {
217 		DPRINTF("v7fs_file_allocate failed.\n");
218 		goto unlock_exit;
219 	}
220 	/* Sync dirent size change. */
221 	uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode));
222 
223 	/* Get myself vnode. */
224 	*a->a_vpp = 0;
225 	if ((error = v7fs_vget(mp, ino, a->a_vpp))) {
226 		DPRINTF("v7fs_vget failed.\n");
227 		goto unlock_exit;
228 	}
229 
230 	/* Scheduling update time. real update by v7fs_update */
231 	struct v7fs_node *newnode = (*a->a_vpp)->v_data;
232 	newnode->update_ctime = true;
233 	newnode->update_mtime = true;
234 	newnode->update_atime = true;
235 	DPRINTF("allocated %s->#%d\n", a->a_cnp->cn_nameptr, ino);
236 
237 unlock_exit:
238 	/* unlock parent directory */
239 	vput(a->a_dvp);	/* locked at v7fs_lookup(); */
240 
241 	return error;
242 }
243 
244 int
245 v7fs_mknod(void *v)
246 {
247 	struct vop_mknod_args /* {
248 				 struct vnode		*a_dvp;
249 				 struct vnode		**a_vpp;
250 				 struct componentname	*a_cnp;
251 				 struct vattr		*a_vap;
252 				 } */ *a = v;
253 	struct componentname *cnp = a->a_cnp;
254 	kauth_cred_t cr = cnp->cn_cred;
255 	struct vnode *dvp = a->a_dvp;
256 	struct vattr *va = a->a_vap;
257 	struct v7fs_node *parent_node = dvp->v_data;
258 	struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
259 	struct v7fs_self *fs = v7fsmount->core;
260 	struct mount *mp = v7fsmount->mountp;
261 	struct v7fs_fileattr attr;
262 
263 	v7fs_ino_t ino;
264 	int error = 0;
265 
266 	DPRINTF("%s %06o %lx %d\n", cnp->cn_nameptr, va->va_mode,
267 	    (long)va->va_rdev, va->va_type);
268 	memset(&attr, 0, sizeof(attr));
269 	attr.uid = kauth_cred_geteuid(cr);
270 	attr.gid = kauth_cred_getegid(cr);
271 	attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type);
272 	attr.device = va->va_rdev;
273 
274 	if ((error = v7fs_file_allocate(fs, &parent_node->inode,
275 	    cnp->cn_nameptr, &attr, &ino)))
276 		goto unlock_exit;
277 	/* Sync dirent size change. */
278 	uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
279 
280 	if ((error = v7fs_vget(mp, ino, a->a_vpp))) {
281 		DPRINTF("can't get vnode.\n");
282 		goto unlock_exit;
283 	}
284 	struct v7fs_node *newnode = (*a->a_vpp)->v_data;
285 	newnode->update_ctime = true;
286 	newnode->update_mtime = true;
287 	newnode->update_atime = true;
288 
289 unlock_exit:
290 	vput(dvp);
291 
292 	return error;
293 }
294 
295 int
296 v7fs_open(void *v)
297 {
298 	struct vop_open_args /* {
299 				struct vnode *a_vp;
300 				int  a_mode;
301 				kauth_cred_t a_cred;
302 				} */ *a = v;
303 
304 	struct vnode *vp = a->a_vp;
305 	struct v7fs_node *v7node = vp->v_data;
306 	struct v7fs_inode *inode = &v7node->inode;
307 
308 	DPRINTF("inode %d\n", inode->inode_number);
309 	/* Append mode file pointer is managed by kernel. */
310 	if (inode->append_mode &&
311 	    ((a->a_mode & (FWRITE | O_APPEND)) == FWRITE)) {
312 		DPRINTF("file is already opened by append mode.\n");
313 		return EPERM;
314 	}
315 
316 	return 0;
317 }
318 
319 int
320 v7fs_close(void *v)
321 {
322 	struct vop_close_args /* {
323 				 struct vnodeop_desc *a_desc;
324 				 struct vnode *a_vp;
325 				 int  a_fflag;
326 				 kauth_cred_t a_cred;
327 				 } */ *a = v;
328 	struct vnode *vp = a->a_vp;
329 #ifdef V7FS_VNOPS_DEBUG
330 	struct v7fs_node *v7node = vp->v_data;
331 	struct v7fs_inode *inode = &v7node->inode;
332 #endif
333 	DPRINTF("#%d (i)%dbyte (v)%zubyte\n", inode->inode_number,
334 	    v7fs_inode_filesize(inode), vp->v_size);
335 
336 	/* Update timestamp */
337 	v7fs_update(vp, 0, 0, UPDATE_WAIT);
338 
339 	return 0;
340 }
341 
342 static int
343 v7fs_check_possible(struct vnode *vp, struct v7fs_node *v7node,
344     mode_t mode)
345 {
346 
347 	if (!(mode & VWRITE))
348 	  return 0;
349 
350 	switch (vp->v_type) {
351 	default:
352 		/*  special file is always writable. */
353 		return 0;
354 	case VDIR:
355 	case VLNK:
356 	case VREG:
357 		break;
358 	}
359 
360 	return vp->v_mount->mnt_flag & MNT_RDONLY ? EROFS : 0;
361 }
362 
363 static int
364 v7fs_check_permitted(struct vnode *vp, struct v7fs_node *v7node,
365     mode_t mode, kauth_cred_t cred)
366 {
367 
368 	struct v7fs_inode *inode = &v7node->inode;
369 
370 	return genfs_can_access(vp->v_type, inode->mode, inode->uid, inode->gid,
371 	    mode, cred);
372 }
373 
374 int
375 v7fs_access(void *v)
376 {
377 	struct vop_access_args /* {
378 				  struct vnode	*a_vp;
379 				  int		a_mode;
380 				  kauth_cred_t	a_cred;
381 				  } */ *ap = v;
382 	struct vnode *vp = ap->a_vp;
383 	struct v7fs_node *v7node = vp->v_data;
384 	int error;
385 
386 	error = v7fs_check_possible(vp, v7node, ap->a_mode);
387 	if (error)
388 		return error;
389 
390 	error = v7fs_check_permitted(vp, v7node, ap->a_mode, ap->a_cred);
391 
392 	return error;
393 }
394 
395 int
396 v7fs_getattr(void *v)
397 {
398 	struct vop_getattr_args /* {
399 				   struct vnode *a_vp;
400 				   struct vattr *a_vap;
401 				   kauth_cred_t a_cred;
402 				   } */ *ap = v;
403 	struct vnode *vp = ap->a_vp;
404 	struct v7fs_node *v7node = vp->v_data;
405 	struct v7fs_inode *inode = &v7node->inode;
406 	struct v7fs_mount *v7fsmount = v7node->v7fsmount;
407 	struct vattr *vap = ap->a_vap;
408 
409 	DPRINTF("\n");
410 	vap->va_type = vp->v_type;
411 	vap->va_mode = inode->mode;
412 	vap->va_nlink = inode->nlink;
413 	vap->va_uid = inode->uid;
414 	vap->va_gid = inode->gid;
415 	vap->va_fsid = v7fsmount->devvp->v_rdev;
416 	vap->va_fileid = inode->inode_number;
417 	vap->va_size = vp->v_size;
418 	vap->va_atime.tv_sec = inode->atime;
419 	vap->va_mtime.tv_sec = inode->mtime;
420 	vap->va_ctime.tv_sec = inode->ctime;
421 	vap->va_birthtime.tv_sec = 0;
422 	vap->va_gen = 1;
423 	vap->va_flags = 0;
424 	vap->va_rdev = inode->device;
425 	vap->va_bytes = vap->va_size; /* No sparse support. */
426 	vap->va_filerev = 0;
427 	vap->va_vaflags = 0;
428 	/* PAGE_SIZE is larger than sizeof(struct dirent). OK.
429 	   getcwd_scandir()@vfs_getcwd.c */
430 	vap->va_blocksize = PAGE_SIZE;
431 
432 	return 0;
433 }
434 
435 int
436 v7fs_setattr(void *v)
437 {
438 	struct vop_setattr_args /* {
439 				   struct vnode *a_vp;
440 				   struct vattr *a_vap;
441 				   kauth_cred_t a_cred;
442 				   struct proc *p;
443 				   } */ *ap = v;
444 	struct vnode *vp = ap->a_vp;
445 	struct vattr *vap = ap->a_vap;
446 	struct v7fs_node *v7node = vp->v_data;
447 	struct v7fs_self *fs = v7node->v7fsmount->core;
448 	struct v7fs_inode *inode = &v7node->inode;
449 	kauth_cred_t cred = ap->a_cred;
450 	struct timespec *acc, *mod;
451 	int error = 0;
452 	acc = mod = NULL;
453 
454 	DPRINTF("\n");
455 
456 	if (vp->v_mount->mnt_flag & MNT_RDONLY) {
457 		switch (vp->v_type) {
458 		default:
459 			/*  special file is always writable. */
460 			break;
461 		case VDIR:
462 		case VLNK:
463 		case VREG:
464 			DPRINTF("read-only mount\n");
465 			return EROFS;
466 		}
467 	}
468 
469 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
470 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
471 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
472 	    ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
473 		DPRINTF("invalid request\n");
474 		return EINVAL;
475 	}
476 	/* File pointer mode. */
477 	if ((vap->va_flags != VNOVAL) && (vap->va_flags & SF_APPEND)) {
478 		DPRINTF("Set append-mode.\n");
479 		inode->append_mode = true;
480 	}
481 
482 	/* File size change. */
483 	if ((vap->va_size != VNOVAL) && (vp->v_type == VREG)) {
484 		error = v7fs_datablock_size_change(fs, vap->va_size, inode);
485 		if (error == 0)
486 			uvm_vnp_setsize(vp, vap->va_size);
487 	}
488 	uid_t uid = inode->uid;
489 	gid_t gid = inode->gid;
490 
491 	if (vap->va_uid != (uid_t)VNOVAL) {
492 		uid = vap->va_uid;
493 		error = kauth_authorize_vnode(cred,
494 		    KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL,
495 		    genfs_can_chown(vp, cred, inode->uid, inode->gid, uid,
496 		    gid));
497 		if (error)
498 			return error;
499 		inode->uid = uid;
500 	}
501 	if (vap->va_gid != (uid_t)VNOVAL) {
502 		gid = vap->va_gid;
503 		error = kauth_authorize_vnode(cred,
504 		    KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL,
505 		    genfs_can_chown(vp, cred, inode->uid, inode->gid, uid,
506 		    gid));
507 		if (error)
508 			return error;
509 		inode->gid = gid;
510 	}
511 	if (vap->va_mode != (mode_t)VNOVAL) {
512 		mode_t mode = vap->va_mode;
513 		error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY,
514 		    vp, NULL, genfs_can_chmod(vp, cred, inode->uid, inode->gid,
515 		    mode));
516 		if (error) {
517 			return error;
518 		}
519 		v7fs_inode_chmod(inode, mode);
520 	}
521 	if (vap->va_atime.tv_sec != VNOVAL) {
522 		acc = &vap->va_atime;
523 	}
524 	if (vap->va_mtime.tv_sec != VNOVAL) {
525 		mod = &vap->va_mtime;
526 		v7node->update_mtime = true;
527 	}
528 	if (vap->va_ctime.tv_sec != VNOVAL) {
529 		v7node->update_ctime = true;
530 	}
531 
532 	v7node->update_atime = true;
533 	v7fs_update(vp, acc, mod, 0);
534 
535 	return error;
536 }
537 
538 int
539 v7fs_read(void *v)
540 {
541 	struct vop_read_args /* {
542 				struct vnode *a_vp;
543 				struct uio *a_uio;
544 				int a_ioflag;
545 				kauth_cred_t a_cred;
546 				} */ *a = v;
547 	struct vnode *vp = a->a_vp;
548 	struct uio *uio = a->a_uio;
549 	struct v7fs_node *v7node = vp->v_data;
550 	struct v7fs_inode *inode = &v7node->inode;
551 	vsize_t sz, filesz = v7fs_inode_filesize(inode);
552 	const int advice = IO_ADV_DECODE(a->a_ioflag);
553 	int error = 0;
554 
555 	DPRINTF("type=%d inode=%d\n", vp->v_type, v7node->inode.inode_number);
556 
557 	while (uio->uio_resid > 0) {
558 		if ((sz = MIN(filesz - uio->uio_offset, uio->uio_resid)) == 0)
559 			break;
560 
561 		error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, UBC_READ |
562 		    UBC_PARTIALOK | UBC_UNMAP_FLAG(v));
563 		if (error) {
564 			break;
565 		}
566 		DPRINTF("read %zubyte\n", sz);
567 	}
568 	v7node->update_atime = true;
569 
570 	return  error;
571 }
572 
573 int
574 v7fs_write(void *v)
575 {
576 	struct vop_write_args /* {
577 				 struct vnode *a_vp;
578 				 struct uio *a_uio;
579 				 int  a_ioflag;
580 				 kauth_cred_t a_cred;
581 				 } */ *a = v;
582 	struct vnode *vp = a->a_vp;
583 	struct uio *uio = a->a_uio;
584 	int advice = IO_ADV_DECODE(a->a_ioflag);
585 	struct v7fs_node *v7node = vp->v_data;
586 	struct v7fs_inode *inode = &v7node->inode;
587 	struct v7fs_self *fs = v7node->v7fsmount->core;
588 	vsize_t sz;
589 	int error = 0;
590 
591 	if (uio->uio_resid == 0)
592 		return 0;
593 
594 	sz = v7fs_inode_filesize(inode);
595 	DPRINTF("(i)%ld (v)%zu ofs=%zu + res=%zu = %zu\n", sz, vp->v_size,
596 	    uio->uio_offset, uio->uio_resid, uio->uio_offset + uio->uio_resid);
597 
598 	/* Append mode file offset is managed by kernel. */
599 	if (a->a_ioflag & IO_APPEND)
600 		uio->uio_offset = sz;
601 
602 	/* If write region is over filesize, expand. */
603 	size_t newsize= uio->uio_offset + uio->uio_resid;
604 	ssize_t expand = newsize - sz;
605  	if (expand > 0) {
606 		if ((error = v7fs_datablock_expand(fs, inode, expand)))
607 			return error;
608 		uvm_vnp_setsize(vp, newsize);
609 	}
610 
611 	while (uio->uio_resid > 0) {
612 		sz = uio->uio_resid;
613 		if ((error = ubc_uiomove(&vp->v_uobj, uio, sz, advice,
614 			    UBC_WRITE | UBC_UNMAP_FLAG(v))))
615 			break;
616 		DPRINTF("write %zubyte\n", sz);
617 	}
618 	v7node->update_mtime = true;
619 
620 	return error;
621 }
622 
623 int
624 v7fs_fsync(void *v)
625 {
626 	struct vop_fsync_args /* {
627 				 struct vnode *a_vp;
628 				 kauth_cred_t a_cred;
629 				 int a_flags;
630 				 off_t offlo;
631 				 off_t offhi;
632 				 } */ *a = v;
633 	struct vnode *vp = a->a_vp;
634 	int error, wait;
635 
636 	DPRINTF("%p\n", a->a_vp);
637 	if (a->a_flags & FSYNC_CACHE) {
638 		return EOPNOTSUPP;
639 	}
640 
641 	wait = (a->a_flags & FSYNC_WAIT);
642 	error = vflushbuf(vp, wait);
643 
644 	if (error == 0 && (a->a_flags & FSYNC_DATAONLY) == 0)
645 		error = v7fs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
646 
647 	return error;
648 }
649 
650 int
651 v7fs_remove(void *v)
652 {
653 	struct vop_remove_args /* {
654 				  struct vnodeop_desc *a_desc;
655 				  struct vnode * a_dvp;
656 				  struct vnode * a_vp;
657 				  struct componentname * a_cnp;
658 				  } */ *a = v;
659 	struct v7fs_node *parent_node = a->a_dvp->v_data;
660 	struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
661 	struct vnode *vp = a->a_vp;
662 	struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode;
663 	struct vnode *dvp = a->a_dvp;
664 	struct v7fs_self *fs = v7fsmount->core;
665 	bool remove;
666 	int error = 0;
667 
668 	DPRINTF("delete %s\n", a->a_cnp->cn_nameptr);
669 
670 	if (vp->v_type == VDIR) {
671 		error = EPERM;
672 		goto out;
673 	}
674 
675 	remove = v7fs_inode_nlink(inode) == 1;
676 	if (remove)
677 		uvm_vnp_setsize(vp, 0);
678 
679 	if ((error = v7fs_file_deallocate(fs, &parent_node->inode,
680 		    a->a_cnp->cn_nameptr))) {
681 		DPRINTF("v7fs_file_delete failed.\n");
682 		goto out;
683 	}
684 	/* Sync dirent size change. */
685 	uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
686 	/* This inode is no longer used. -> v7fs_inactive */
687 	if (remove)
688 		memset(inode, 0, sizeof(*inode));
689 
690 out:
691 	if (dvp == vp)
692 		vrele(vp); /* v_usecount-- of unlocked vp */
693 	else
694 		vput(vp); /* unlock vp and then v_usecount-- */
695 	vput(dvp);
696 
697 	return error;
698 }
699 
700 int
701 v7fs_link(void *v)
702 {
703 	struct vop_link_args /* {
704 				struct vnode *a_dvp;
705 				struct vnode *a_vp;
706 				struct componentname *a_cnp;
707 				} */ *a = v;
708 	struct vnode *dvp = a->a_dvp;
709 	struct vnode *vp = a->a_vp;
710 	struct v7fs_node *parent_node = dvp->v_data;
711 	struct v7fs_node *node = vp->v_data;
712 	struct v7fs_inode *parent = &parent_node->inode;
713 	struct v7fs_inode *p = &node->inode;
714 	struct v7fs_self *fs = node->v7fsmount->core;
715 	struct componentname *cnp = a->a_cnp;
716 	int error = 0;
717 
718 	DPRINTF("%p\n", vp);
719 	/* Lock soruce file */
720 	if ((error = vn_lock(vp, LK_EXCLUSIVE))) {
721 		DPRINTF("lock failed. %p\n", vp);
722 		VOP_ABORTOP(dvp, cnp);
723 		goto unlock;
724 	}
725 	error = v7fs_file_link(fs, parent, p, cnp->cn_nameptr);
726 	/* Sync dirent size change. */
727 	uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
728 
729 	VOP_UNLOCK(vp);
730 unlock:
731 	vput(dvp);
732 
733 	return error;
734 }
735 
736 int
737 v7fs_rename(void *v)
738 {
739 	struct vop_rename_args /* {
740 				  struct vnode *a_fdvp;	from parent-directory
741 				  struct vnode *a_fvp;	from file
742 				  struct componentname *a_fcnp;
743 				  struct vnode *a_tdvp;	to parent-directory
744 				  struct vnode *a_tvp;	to file
745 				  struct componentname *a_tcnp;
746 				  } */ *a = v;
747 	struct vnode *fvp = a->a_fvp;
748 	struct vnode *tvp = a->a_tvp;
749 	struct vnode *fdvp = a->a_fdvp;
750 	struct vnode *tdvp = a->a_tdvp;
751 	struct v7fs_node *parent_from = fdvp->v_data;
752 	struct v7fs_node *parent_to = tdvp->v_data;
753 	struct v7fs_node *v7node = fvp->v_data;
754 	struct v7fs_self *fs = v7node->v7fsmount->core;
755 	const char *from_name = a->a_fcnp->cn_nameptr;
756 	const char *to_name = a->a_tcnp->cn_nameptr;
757 	int error;
758 
759 	DPRINTF("%s->%s %p %p\n", from_name, to_name, fvp, tvp);
760 
761 	if ((fvp->v_mount != tdvp->v_mount) ||
762 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
763 		error = EXDEV;
764 		DPRINTF("cross-device link\n");
765 		goto out;
766 	}
767 	// XXXsource file lock?
768 	error = v7fs_file_rename(fs, &parent_from->inode, from_name,
769 	    &parent_to->inode, to_name);
770 	/* 'to file' inode may be changed. (hard-linked and it is cached.)
771 	   t_vnops rename_reg_nodir */
772 	if (tvp) {
773 		v7fs_vnode_reload(parent_from->v7fsmount->mountp, tvp);
774 	}
775 	/* Sync dirent size change. */
776 	uvm_vnp_setsize(tdvp, v7fs_inode_filesize(&parent_to->inode));
777 	uvm_vnp_setsize(fdvp, v7fs_inode_filesize(&parent_from->inode));
778 out:
779 	if (tvp)
780 		vput(tvp);  /* locked on entry */
781 	if (tdvp == tvp)
782 		vrele(tdvp);
783 	else
784 		vput(tdvp);
785 	vrele(fdvp);
786 	vrele(fvp);
787 
788 	return error;
789 }
790 
791 int
792 v7fs_mkdir(void *v)
793 {
794 	struct vop_mkdir_args /* {
795 				 struct vnode		*a_dvp;
796 				 struct vnode		**a_vpp;
797 				 struct componentname	*a_cnp;
798 				 struct vattr		*a_vap;
799 				 } */ *a = v;
800 	struct componentname *cnp = a->a_cnp;
801 	kauth_cred_t cr = cnp->cn_cred;
802 	struct vnode *dvp = a->a_dvp;
803 	struct vattr *va = a->a_vap;
804 	struct v7fs_node *parent_node = dvp->v_data;
805 	struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
806 	struct v7fs_self *fs = v7fsmount->core;
807 	struct v7fs_fileattr attr;
808 	struct mount *mp = v7fsmount->mountp;
809 	v7fs_ino_t ino;
810 	int error = 0;
811 
812 	DPRINTF("\n");
813 	memset(&attr, 0, sizeof(attr));
814 	attr.uid = kauth_cred_geteuid(cr);
815 	attr.gid = kauth_cred_getegid(cr);
816 	attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type);
817 
818 	if ((error = v7fs_file_allocate(fs, &parent_node->inode,
819 	    cnp->cn_nameptr, &attr, &ino)))
820 		goto unlock_exit;
821 	/* Sync dirent size change. */
822 	uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
823 
824 	if ((error = v7fs_vget(mp, ino, a->a_vpp))) {
825 		DPRINTF("can't get vnode.\n");
826 	}
827 	struct v7fs_node *newnode = (*a->a_vpp)->v_data;
828 	newnode->update_ctime = true;
829 	newnode->update_mtime = true;
830 	newnode->update_atime = true;
831 
832 unlock_exit:
833 	vput(dvp);
834 
835 	return error;
836 }
837 
838 int
839 v7fs_rmdir(void *v)
840 {
841 	struct vop_rmdir_args /* {
842 				 struct vnode		*a_dvp;
843 				 struct vnode		*a_vp;
844 				 struct componentname	*a_cnp;
845 				 } */ *a = v;
846 	struct vnode *vp = a->a_vp;
847 	struct vnode *dvp = a->a_dvp;
848 	struct v7fs_node *parent_node = dvp->v_data;
849 	struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
850 	struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode;
851 	struct v7fs_self *fs = v7fsmount->core;
852 	int error = 0;
853 
854 	DPRINTF("delete %s\n", a->a_cnp->cn_nameptr);
855 
856 	KDASSERT(vp->v_type == VDIR);
857 
858 	if ((error = v7fs_file_deallocate(fs, &parent_node->inode,
859 	    a->a_cnp->cn_nameptr))) {
860 		DPRINTF("v7fs_directory_deallocate failed.\n");
861 		goto out;
862 	}
863 	uvm_vnp_setsize(vp, 0);
864 	/* Sync dirent size change. */
865 	uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
866 	/* This inode is no longer used. -> v7fs_inactive */
867 	memset(inode, 0, sizeof(*inode));
868 out:
869 	vput(vp);
870 	vput(dvp);
871 
872 	return error;
873 }
874 
875 struct v7fs_readdir_arg {
876 	struct dirent *dp;
877 	struct uio *uio;
878 	int start;
879 	int end;
880 	int cnt;
881 };
882 static int readdir_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t);
883 
884 int
885 readdir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
886 {
887 	struct v7fs_readdir_arg *p = (struct v7fs_readdir_arg *)ctx;
888 	struct v7fs_dirent *dir;
889 	struct dirent *dp = p->dp;
890 	struct v7fs_inode inode;
891 	char filename[V7FS_NAME_MAX + 1];
892 	int i, n;
893 	int error = 0;
894 	void *buf;
895 
896 	if (!(buf = scratch_read(fs, blk)))
897 		return EIO;
898 	dir = (struct v7fs_dirent *)buf;
899 
900 	n = sz / sizeof(*dir);
901 
902 	for (i = 0; (i < n) && (p->cnt < p->end); i++, dir++, p->cnt++) {
903 		if (p->cnt < p->start)
904 			continue;
905 
906 		if ((error = v7fs_inode_load(fs, &inode, dir->inode_number)))
907 			break;
908 
909 		v7fs_dirent_filename(filename, dir->name);
910 
911 		DPRINTF("inode=%d name=%s %s\n", dir->inode_number, filename,
912 		    v7fs_inode_isdir(&inode) ? "DIR" : "FILE");
913 		memset(dp, 0, sizeof(*dp));
914 		dp->d_fileno = dir->inode_number;
915 		dp->d_type = v7fs_mode_to_d_type(inode.mode);
916 		dp->d_namlen = strlen(filename);
917 		strcpy(dp->d_name, filename);
918 		dp->d_reclen = sizeof(*dp);
919 		if ((error = uiomove(dp, dp->d_reclen, p->uio))) {
920 			DPRINTF("uiomove failed.\n");
921 			break;
922 		}
923 	}
924 	scratch_free(fs, buf);
925 
926 	if (p->cnt == p->end)
927 		return V7FS_ITERATOR_BREAK;
928 
929 	return error;
930 }
931 
932 int
933 v7fs_readdir(void *v)
934 {
935 	struct vop_readdir_args /* {
936 				   struct vnode *a_vp;
937 				   struct uio *a_uio;
938 				   kauth_cred_t a_cred;
939 				   int *a_eofflag;
940 				   off_t **a_cookies;
941 				   int *a_ncookies;
942 				   } */ *a = v;
943 	struct uio *uio = a->a_uio;
944 	struct vnode *vp = a->a_vp;
945 	struct v7fs_node *v7node = vp->v_data;
946 	struct v7fs_inode *inode = &v7node->inode;
947 	struct v7fs_self *fs = v7node->v7fsmount->core;
948 	struct dirent *dp;
949 	int error;
950 
951 	DPRINTF("offset=%zu residue=%zu\n", uio->uio_offset, uio->uio_resid);
952 
953 	KDASSERT(vp->v_type == VDIR);
954 	KDASSERT(uio->uio_offset >= 0);
955 	KDASSERT(v7fs_inode_isdir(inode));
956 
957 	struct v7fs_readdir_arg arg;
958 	arg.start = uio->uio_offset / sizeof(*dp);
959 	arg.end = arg.start +  uio->uio_resid / sizeof(*dp);
960 	if (arg.start == arg.end) {/* user buffer has not enuf space. */
961 		DPRINTF("uio buffer too small\n");
962 		return ENOMEM;
963 	}
964 	dp = malloc(sizeof(*dp), M_V7FS, M_WAITOK | M_ZERO);
965 	arg.cnt = 0;
966 	arg.dp = dp;
967 	arg.uio = uio;
968 
969 	*a->a_eofflag = false;
970 	error = v7fs_datablock_foreach(fs, inode, readdir_subr, &arg);
971 	if (error == V7FS_ITERATOR_END) {
972 		*a->a_eofflag = true;
973 	}
974 	if (error < 0)
975 		error = 0;
976 
977 	free(dp, M_V7FS);
978 
979 	return error;
980 }
981 
982 int
983 v7fs_inactive(void *v)
984 {
985 	struct vop_inactive_args /* {
986 				    struct vnode *a_vp;
987 				    bool *a_recycle;
988 				    } */ *a = v;
989 	struct vnode *vp = a->a_vp;
990 	struct v7fs_node *v7node = vp->v_data;
991 	struct v7fs_inode *inode = &v7node->inode;
992 
993 	DPRINTF("%p #%d\n", vp, inode->inode_number);
994 	if (v7fs_inode_allocated(inode)) {
995 		v7fs_update(vp, 0, 0, UPDATE_WAIT);
996 		*a->a_recycle = false;
997 	} else {
998 		*a->a_recycle = true;
999 	}
1000 
1001 	VOP_UNLOCK(vp);
1002 
1003 	return 0;
1004 }
1005 
1006 int
1007 v7fs_reclaim(void *v)
1008 {
1009 	/*This vnode is no longer referenced by kernel. */
1010 	extern struct pool v7fs_node_pool;
1011 	struct vop_reclaim_args /* {
1012 				   struct vnode *a_vp;
1013 				   } */ *a = v;
1014 	struct vnode *vp = a->a_vp;
1015 	struct v7fs_node *v7node = vp->v_data;
1016 
1017 	DPRINTF("%p #%d\n", vp, v7node->inode.inode_number);
1018 	mutex_enter(&mntvnode_lock);
1019 	LIST_REMOVE(v7node, link);
1020 	mutex_exit(&mntvnode_lock);
1021 	genfs_node_destroy(vp);
1022 	pool_put(&v7fs_node_pool, v7node);
1023 	vp->v_data = NULL;
1024 
1025 	return 0;
1026 }
1027 
1028 int
1029 v7fs_bmap(void *v)
1030 {
1031 	struct vop_bmap_args /* {
1032 				struct vnode *a_vp;
1033 				daddr_t  a_bn;
1034 				struct vnode **a_vpp;
1035 				daddr_t *a_bnp;
1036 				int *a_runp;
1037 				} */ *a = v;
1038 	struct vnode *vp = a->a_vp;
1039 	struct v7fs_node *v7node = vp->v_data;
1040 	struct v7fs_mount *v7fsmount = v7node->v7fsmount;
1041 	struct v7fs_self *fs = v7node->v7fsmount->core;
1042 	struct v7fs_inode *inode = &v7node->inode;
1043 	int error = 0;
1044 
1045 	DPRINTF("inode=%d offset=%zu %p\n", inode->inode_number, a->a_bn, vp);
1046 	DPRINTF("filesize: %d\n", inode->filesize);
1047 	if (!a->a_bnp)
1048 		return 0;
1049 
1050 	v7fs_daddr_t blk;
1051 	if (!(blk = v7fs_datablock_last(fs, inode,
1052 	    (a->a_bn + 1) << V7FS_BSHIFT))) {
1053 		/* +1 converts block # to file offset. */
1054 		return ENOSPC;
1055 	}
1056 
1057 	*a->a_bnp = blk;
1058 
1059 	if (a->a_vpp)
1060 		*a->a_vpp = v7fsmount->devvp;
1061 	if (a->a_runp)
1062 		*a->a_runp = 0; /*XXX TODO */
1063 
1064 	DPRINTF("%d  %zu->%zu status=%d\n", inode->inode_number, a->a_bn,
1065 	    *a->a_bnp, error);
1066 
1067 	return error;
1068 }
1069 
1070 int
1071 v7fs_strategy(void *v)
1072 {
1073 	struct vop_strategy_args /* {
1074 				    struct vnode *a_vp;
1075 				    struct buf *a_bp;
1076 				    } */ *a = v;
1077 	struct buf *b = a->a_bp;
1078 	struct vnode *vp = a->a_vp;
1079 	struct v7fs_node *v7node = vp->v_data;
1080 	struct v7fs_mount *v7fsmount = v7node->v7fsmount;
1081 	int error;
1082 
1083 	DPRINTF("%p\n", vp);
1084 	KDASSERT(vp->v_type == VREG);
1085 	if (b->b_blkno == b->b_lblkno) {
1086 		error = VOP_BMAP(vp, b->b_lblkno, NULL, &b->b_blkno, NULL);
1087 		if (error) {
1088 			b->b_error = error;
1089 			biodone(b);
1090 			return error;
1091 		}
1092 		if ((long)b->b_blkno == -1)
1093 			clrbuf(b);
1094 	}
1095 	if ((long)b->b_blkno == -1) {
1096 		biodone(b);
1097 		return 0;
1098 	}
1099 
1100 	return VOP_STRATEGY(v7fsmount->devvp, b);
1101 }
1102 
1103 int
1104 v7fs_print(void *v)
1105 {
1106 	struct vop_print_args /* {
1107 				 struct vnode *a_vp;
1108 				 } */ *a = v;
1109 	struct v7fs_node *v7node = a->a_vp->v_data;
1110 
1111 	v7fs_inode_dump(&v7node->inode);
1112 
1113 	return 0;
1114 }
1115 
1116 int
1117 v7fs_advlock(void *v)
1118 {
1119 	struct vop_advlock_args /* {
1120 				   struct vnode *a_vp;
1121 				   void *a_id;
1122 				   int a_op;
1123 				   struct flock *a_fl;
1124 				   int a_flags;
1125 				   } */ *a = v;
1126 	struct v7fs_node *v7node = a->a_vp->v_data;
1127 
1128 	DPRINTF("op=%d\n", a->a_op);
1129 
1130 	return lf_advlock(a, &v7node->lockf,
1131 	    v7fs_inode_filesize(&v7node->inode));
1132 }
1133 
1134 int
1135 v7fs_pathconf(void *v)
1136 {
1137 	struct vop_pathconf_args /* {
1138 				    struct vnode *a_vp;
1139 				    int a_name;
1140 				    register_t *a_retval;
1141 				    } */ *a = v;
1142 	int err = 0;
1143 
1144 	DPRINTF("%p\n", a->a_vp);
1145 
1146 	switch (a->a_name) {
1147 	case _PC_LINK_MAX:
1148 		*a->a_retval = V7FS_LINK_MAX;
1149 		break;
1150 	case _PC_NAME_MAX:
1151 		*a->a_retval = V7FS_NAME_MAX;
1152 		break;
1153 	case _PC_PATH_MAX:
1154 		*a->a_retval = V7FS_PATH_MAX;
1155 		break;
1156 	case _PC_CHOWN_RESTRICTED:
1157 		*a->a_retval = 1;
1158 		break;
1159 	case _PC_NO_TRUNC:
1160 		*a->a_retval = 0;
1161 		break;
1162 	case _PC_SYNC_IO:
1163 		*a->a_retval = 1;
1164 		break;
1165 	case _PC_FILESIZEBITS:
1166 		*a->a_retval = 30; /* ~1G */
1167 		break;
1168 	case _PC_SYMLINK_MAX:
1169 		*a->a_retval = V7FSBSD_MAXSYMLINKLEN;
1170 		break;
1171 	case _PC_2_SYMLINKS:
1172 		*a->a_retval = 1;
1173 		break;
1174 	default:
1175 		err = EINVAL;
1176 		break;
1177 	}
1178 
1179 	return err;
1180 }
1181 
1182 int
1183 v7fs_update(struct vnode *vp, const struct timespec *acc,
1184     const struct timespec *mod, int flags)
1185 {
1186 	struct v7fs_node *v7node = vp->v_data;
1187 	struct v7fs_inode *inode = &v7node->inode;
1188 	struct v7fs_self *fs = v7node->v7fsmount->core;
1189 	bool update = false;
1190 
1191 	DPRINTF("%p %zu %d\n", vp, vp->v_size, v7fs_inode_filesize(inode));
1192 	KDASSERT(vp->v_size == v7fs_inode_filesize(inode));
1193 
1194 	if (v7node->update_atime) {
1195 		inode->atime = acc ? acc->tv_sec : time_second;
1196 		v7node->update_atime = false;
1197 		update = true;
1198 	}
1199 	if (v7node->update_ctime) {
1200 		inode->ctime = time_second;
1201 		v7node->update_ctime = false;
1202 		update = true;
1203 	}
1204 	if (v7node->update_mtime) {
1205 		inode->mtime = mod ? mod->tv_sec : time_second;
1206 		v7node->update_mtime = false;
1207 		update = true;
1208 	}
1209 
1210 	if (update)
1211 		v7fs_inode_writeback(fs, inode);
1212 
1213 	return 0;
1214 }
1215 
1216 int
1217 v7fs_symlink(void *v)
1218 {
1219 	struct vop_symlink_args /* {
1220 				   struct vnode		*a_dvp;
1221 				   struct vnode		**a_vpp;
1222 				   struct componentname	*a_cnp;
1223 				   struct vattr		*a_vap;
1224 				   char			*a_target;
1225 				   } */ *a = v;
1226 	struct v7fs_node *parent_node = a->a_dvp->v_data;
1227 	struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
1228 	struct v7fs_self *fs = v7fsmount->core;
1229 	struct vattr *va = a->a_vap;
1230 	kauth_cred_t cr = a->a_cnp->cn_cred;
1231 	struct componentname *cnp = a->a_cnp;
1232 	struct v7fs_fileattr attr;
1233 	v7fs_ino_t ino;
1234 	const char *from = a->a_target;
1235 	const char *to = cnp->cn_nameptr;
1236 	size_t len = strlen(from) + 1;
1237 	int error = 0;
1238 
1239 	if (len > V7FS_BSIZE) { /* limited to 512byte pathname */
1240 		DPRINTF("too long pathname.");
1241 		return ENAMETOOLONG;
1242 	}
1243 
1244 	memset(&attr, 0, sizeof(attr));
1245 	attr.uid = kauth_cred_geteuid(cr);
1246 	attr.gid = kauth_cred_getegid(cr);
1247 	attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type);
1248 
1249 	if ((error = v7fs_file_allocate
1250 		(fs, &parent_node->inode, to, &attr, &ino))) {
1251 		goto unlock_exit;
1252 	}
1253 	/* Sync dirent size change. */
1254 	uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode));
1255 
1256 	/* Get myself vnode. */
1257 	if ((error = v7fs_vget(v7fsmount->mountp, ino, a->a_vpp))) {
1258 		DPRINTF("can't get vnode.\n");
1259 	}
1260 
1261 	struct v7fs_node *newnode = (*a->a_vpp)->v_data;
1262 	struct v7fs_inode *p = &newnode->inode;
1263 	v7fs_file_symlink(fs, p, from);
1264 	uvm_vnp_setsize(*a->a_vpp, v7fs_inode_filesize(p));
1265 
1266 	newnode->update_ctime = true;
1267 	newnode->update_mtime = true;
1268 	newnode->update_atime = true;
1269 unlock_exit:
1270 	/* unlock parent directory */
1271 	vput(a->a_dvp);
1272 
1273 	return error;
1274 }
1275 
1276 int
1277 v7fs_readlink(void *v)
1278 {
1279 	struct vop_readlink_args /* {
1280 				    struct vnode	*a_vp;
1281 				    struct uio		*a_uio;
1282 				    kauth_cred_t	a_cred;
1283 				    } */ *a = v;
1284 	struct uio *uio = a->a_uio;
1285 	struct vnode *vp = a->a_vp;
1286 	struct v7fs_node *v7node = vp->v_data;
1287 	struct v7fs_inode *inode = &v7node->inode;
1288 	struct v7fs_self *fs = v7node->v7fsmount->core;
1289 	int error = 0;
1290 
1291 	KDASSERT(vp->v_type == VLNK);
1292 	KDASSERT(uio->uio_offset >= 0);
1293 	KDASSERT(v7fs_inode_islnk(inode));
1294 
1295 	v7fs_daddr_t blk = inode->addr[0];
1296 	void *buf;
1297 	if (!(buf = scratch_read(fs, blk))) {
1298 		error = EIO;
1299 		goto error_exit;
1300 	}
1301 
1302 	if ((error = uiomove(buf, strlen(buf), uio))) {
1303 		DPRINTF("uiomove failed.\n");
1304 	}
1305 	scratch_free(fs, buf);
1306 
1307 error_exit:
1308 	return error;
1309 }
1310 
1311