1/*	$NetBSD: v7fs_vfsops.c,v 1.5.6.1 2012/06/24 16:03:40 jdc 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_vfsops.c,v 1.5.6.1 2012/06/24 16:03:40 jdc Exp $");
34#if defined _KERNEL_OPT
35#include "opt_v7fs.h"
36#endif
37
38#include <sys/types.h>
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/pool.h>
42#include <sys/time.h>
43#include <sys/ucred.h>
44#include <sys/mount.h>
45#include <sys/disk.h>
46#include <sys/device.h>
47#include <sys/fcntl.h>
48#include <sys/malloc.h>
49#include <sys/kauth.h>
50#include <sys/proc.h>
51
52/* v-node */
53#include <sys/namei.h>
54#include <sys/vnode.h>
55/* devsw */
56#include <sys/conf.h>
57
58#include "v7fs_extern.h"
59#include "v7fs.h"
60#include "v7fs_impl.h"
61#include "v7fs_inode.h"
62#include "v7fs_superblock.h"
63
64#ifdef V7FS_VFSOPS_DEBUG
65#define	DPRINTF(fmt, args...)	printf("%s: " fmt, __func__, ##args)
66#else
67#define	DPRINTF(arg...)		((void)0)
68#endif
69
70MALLOC_JUSTDEFINE(M_V7FS_VFS, "v7fs vfs", "v7fs vfs structures");
71
72struct pool v7fs_node_pool;
73
74static int v7fs_mountfs(struct vnode *, struct mount *, int);
75static int v7fs_openfs(struct vnode *, struct mount *, struct lwp *);
76static void v7fs_closefs(struct vnode *, struct mount *);
77static int is_v7fs_partition(struct vnode *);
78static enum vtype v7fs_mode_to_vtype(v7fs_mode_t mode);
79int v7fs_vnode_reload(struct mount *, struct vnode *);
80
81int
82v7fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
83{
84	struct lwp *l = curlwp;
85	struct v7fs_args *args = data;
86	struct v7fs_mount *v7fsmount = (void *)mp->mnt_data;
87	struct vnode *devvp = NULL;
88	int error = 0;
89	bool update = mp->mnt_flag & MNT_UPDATE;
90
91	DPRINTF("mnt_flag=%x %s\n", mp->mnt_flag, update ? "update" : "");
92
93	if (args == NULL)
94		return EINVAL;
95	if (*data_len < sizeof(*args))
96		return EINVAL;
97
98	if (mp->mnt_flag & MNT_GETARGS) {
99		if (!v7fsmount)
100			return EIO;
101		args->fspec = NULL;
102		args->endian = v7fsmount->core->endian;
103		*data_len = sizeof(*args);
104		return 0;
105	}
106
107	DPRINTF("args->fspec=%s endian=%d\n", args->fspec, args->endian);
108	if (args->fspec == NULL) {
109		/* nothing to do. */
110		return EINVAL;
111	}
112
113	if (args->fspec != NULL) {
114		/* Look up the name and verify that it's sane. */
115		error = namei_simple_user(args->fspec,
116		    NSM_FOLLOW_NOEMULROOT, &devvp);
117		if (error != 0)
118			return (error);
119		DPRINTF("mount device=%lx\n", (long)devvp->v_rdev);
120
121		if (!update) {
122			/*
123			 * Be sure this is a valid block device
124			 */
125			if (devvp->v_type != VBLK)
126				error = ENOTBLK;
127			else if (bdevsw_lookup(devvp->v_rdev) == NULL)
128				error = ENXIO;
129		} else {
130			KDASSERT(v7fsmount);
131			/*
132			 * Be sure we're still naming the same device
133			 * used for our initial mount
134			 */
135			if (devvp != v7fsmount->devvp) {
136				DPRINTF("devvp %p != %p rootvp=%p\n", devvp,
137				    v7fsmount->devvp, rootvp);
138				if (rootvp == v7fsmount->devvp) {
139					vrele(devvp);
140					devvp = rootvp;
141					vref(devvp);
142				} else {
143					error = EINVAL;
144				}
145			}
146		}
147	}
148
149	/*
150	 * If mount by non-root, then verify that user has necessary
151	 * permissions on the device.
152	 *
153	 * Permission to update a mount is checked higher, so here we presume
154	 * updating the mount is okay (for example, as far as securelevel goes)
155	 * which leaves us with the normal check.
156	 */
157	if (error == 0) {
158		int accessmode = VREAD;
159		if (update ?
160		    (mp->mnt_iflag & IMNT_WANTRDWR) != 0 :
161		    (mp->mnt_flag & MNT_RDONLY) == 0)
162			accessmode |= VWRITE;
163		error = genfs_can_mount(devvp, accessmode, l->l_cred);
164	}
165
166	if (error) {
167		vrele(devvp);
168		return error;
169	}
170
171	if (!update) {
172		if ((error = v7fs_openfs(devvp, mp, l))) {
173			vrele(devvp);
174			return error;
175		}
176
177		if ((error = v7fs_mountfs(devvp, mp, args->endian))) {
178			v7fs_closefs(devvp, mp);
179			VOP_UNLOCK(devvp);
180			vrele(devvp);
181			return error;
182		}
183		VOP_UNLOCK(devvp);
184	} else 	if (mp->mnt_flag & MNT_RDONLY) {
185		/* XXX: r/w -> read only */
186	}
187
188	return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
189	    mp->mnt_op->vfs_name, mp, l);
190}
191
192static int
193is_v7fs_partition(struct vnode *devvp)
194{
195	struct dkwedge_info dkw;
196	int error;
197
198	if ((error = getdiskinfo(devvp, &dkw)) != 0) {
199		DPRINTF("getdiskinfo=%d\n", error);
200		return error;
201	}
202	DPRINTF("ptype=%s size=%" PRIu64 "\n", dkw.dkw_ptype, dkw->dkw_size);
203
204	return strcmp(dkw.dkw_ptype, DKW_PTYPE_V7) == 0 ? 0 : EINVAL;
205}
206
207static int
208v7fs_openfs(struct vnode *devvp, struct mount *mp, struct lwp *l)
209{
210	kauth_cred_t cred = l->l_cred;
211	int oflags;
212	int error;
213
214	/* Flush buffer */
215	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
216	if ((error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0)))
217		goto unlock_exit;
218
219	/* Open block device */
220	oflags = FREAD;
221	if ((mp->mnt_flag & MNT_RDONLY) == 0)
222		oflags |= FWRITE;
223
224	if ((error = VOP_OPEN(devvp, oflags, NOCRED)) != 0) {
225		DPRINTF("VOP_OPEN=%d\n", error);
226		goto unlock_exit;
227	}
228
229	return 0; /* lock held */
230
231unlock_exit:
232	VOP_UNLOCK(devvp);
233
234	return error;
235}
236
237static void
238v7fs_closefs(struct vnode *devvp, struct mount *mp)
239{
240	int oflags = FREAD;
241
242	if ((mp->mnt_flag & MNT_RDONLY) == 0)
243		oflags |= FWRITE;
244
245	VOP_CLOSE(devvp, oflags, NOCRED);
246}
247
248static int
249v7fs_mountfs(struct vnode *devvp, struct mount *mp, int endian)
250{
251	struct v7fs_mount *v7fsmount;
252	int error;
253	struct v7fs_mount_device mount;
254
255	DPRINTF("%d\n",endian);
256
257	v7fsmount = malloc(sizeof(*v7fsmount), M_V7FS_VFS, M_WAITOK);
258	if (v7fsmount == NULL) {
259		return ENOMEM;
260	}
261	v7fsmount->devvp = devvp;
262	v7fsmount->mountp = mp;
263
264	mount.device.vnode = devvp;
265	mount.endian = endian;
266
267	if ((error = v7fs_io_init(&v7fsmount->core, &mount, V7FS_BSIZE))) {
268		goto err_exit;
269	}
270	struct v7fs_self *fs = v7fsmount->core;
271
272	if ((error = v7fs_superblock_load(fs))) {
273		v7fs_io_fini(fs);
274		goto err_exit;
275	}
276
277	LIST_INIT(&v7fsmount->v7fs_node_head);
278
279	mp->mnt_data = v7fsmount;
280	mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev;
281	mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_V7FS);
282	mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
283	mp->mnt_stat.f_namemax = V7FS_NAME_MAX;
284	mp->mnt_flag |= MNT_LOCAL;
285	mp->mnt_dev_bshift = V7FS_BSHIFT;
286	mp->mnt_fs_bshift = V7FS_BSHIFT;
287
288	return 0;
289
290err_exit:
291	free(v7fsmount, M_V7FS_VFS);
292	return error;
293}
294
295int
296v7fs_start(struct mount *mp, int flags)
297{
298
299	DPRINTF("\n");
300	/* Nothing to do. */
301	return 0;
302}
303
304int
305v7fs_unmount(struct mount *mp, int mntflags)
306{
307	struct v7fs_mount *v7fsmount = (void *)mp->mnt_data;
308	int error;
309
310	DPRINTF("%p\n", v7fsmount);
311
312	if ((error = vflush(mp, NULLVP,
313		    mntflags & MNT_FORCE ? FORCECLOSE : 0)) != 0)
314		return error;
315
316	vn_lock(v7fsmount->devvp, LK_EXCLUSIVE | LK_RETRY);
317	error = VOP_CLOSE(v7fsmount->devvp, FREAD, NOCRED);
318	vput(v7fsmount->devvp);
319
320	v7fs_io_fini(v7fsmount->core);
321
322	free(v7fsmount, M_V7FS_VFS);
323	mp->mnt_data = NULL;
324	mp->mnt_flag &= ~MNT_LOCAL;
325
326	return 0;
327}
328
329int
330v7fs_root(struct mount *mp, struct vnode **vpp)
331{
332	struct vnode *vp;
333	int error;
334
335	DPRINTF("\n");
336	if ((error = VFS_VGET(mp, V7FS_ROOT_INODE, &vp)) != 0) {
337		DPRINTF("error=%d\n", error);
338		return error;
339	}
340	*vpp = vp;
341	DPRINTF("done.\n");
342
343	return 0;
344}
345
346int
347v7fs_statvfs(struct mount *mp, struct statvfs *f)
348{
349	struct v7fs_mount *v7fsmount = mp->mnt_data;
350	struct v7fs_self *fs = v7fsmount->core;
351
352	DPRINTF("scratch remain=%d\n", fs->scratch_remain);
353
354	v7fs_superblock_status(fs);
355
356	f->f_bsize = V7FS_BSIZE;
357	f->f_frsize = V7FS_BSIZE;
358	f->f_iosize = V7FS_BSIZE;
359	f->f_blocks = fs->stat.total_blocks;
360	f->f_bfree = fs->stat.free_blocks;
361	f->f_bavail = fs->stat.free_blocks;
362	f->f_bresvd = 0;
363	f->f_files = fs->stat.total_files;
364	f->f_ffree = fs->stat.free_inode;
365	f->f_favail = f->f_ffree;
366	f->f_fresvd = 0;
367	copy_statvfs_info(f, mp);
368
369	return 0;
370}
371
372int
373v7fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
374{
375	struct v7fs_mount *v7fsmount = mp->mnt_data;
376	struct v7fs_self *fs = v7fsmount->core;
377	struct v7fs_node *v7fs_node;
378	struct v7fs_inode *inode;
379	struct vnode *v;
380	int err, error;
381	int retry_cnt;
382
383	DPRINTF("\n");
384
385	v7fs_superblock_writeback(fs);
386	for (retry_cnt = 0; retry_cnt < 2; retry_cnt++) {
387		error = 0;
388
389		mutex_enter(&mntvnode_lock);
390		for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head);
391		    v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) {
392			inode = &v7fs_node->inode;
393			if (!v7fs_inode_allocated(inode)) {
394				continue;
395			}
396			v = v7fs_node->vnode;
397			mutex_enter(v->v_interlock);
398			mutex_exit(&mntvnode_lock);
399			err = vget(v, LK_EXCLUSIVE | LK_NOWAIT);
400			if (err == 0) {
401				err = VOP_FSYNC(v, cred, FSYNC_WAIT, 0, 0);
402				vput(v);
403			}
404			if (err != 0)
405				error = err;
406			mutex_enter(&mntvnode_lock);
407		}
408		mutex_exit(&mntvnode_lock);
409
410		if (error == 0)
411			break;
412	}
413
414	return error;
415}
416
417static enum vtype
418v7fs_mode_to_vtype (v7fs_mode_t mode)
419{
420	enum vtype table[] = { VCHR, VDIR, VBLK, VREG, VLNK, VSOCK };
421
422	if ((mode & V7FS_IFMT) == V7FSBSD_IFFIFO)
423		return VFIFO;
424
425	return table[((mode >> 13) & 7) - 1];
426}
427
428int
429v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
430{
431	struct v7fs_mount *v7fsmount = mp->mnt_data;
432	struct v7fs_self *fs = v7fsmount->core;
433	struct vnode *vp;
434	struct v7fs_node *v7fs_node;
435	struct v7fs_inode inode;
436	int error;
437
438	/* Lookup requested i-node */
439	if ((error = v7fs_inode_load(fs, &inode, ino))) {
440		DPRINTF("v7fs_inode_load failed.\n");
441		return error;
442	}
443
444retry:
445	mutex_enter(&mntvnode_lock);
446	for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head);
447	    v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) {
448		if (v7fs_node->inode.inode_number == ino) {
449			vp = v7fs_node->vnode;
450			mutex_enter(vp->v_interlock);
451			mutex_exit(&mntvnode_lock);
452			if (vget(vp, LK_EXCLUSIVE) == 0) {
453				*vpp = vp;
454				return 0;
455			} else {
456				DPRINTF("retry!\n");
457				goto retry;
458			}
459		}
460	}
461	mutex_exit(&mntvnode_lock);
462
463	/* Allocate v-node. */
464	if ((error = getnewvnode(VT_V7FS, mp, v7fs_vnodeop_p, NULL, &vp))) {
465		DPRINTF("getnewvnode error.\n");
466		return error;
467	}
468	/* Lock vnode here */
469	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
470
471	/* Allocate i-node */
472	vp->v_data = pool_get(&v7fs_node_pool, PR_WAITOK);
473	memset(vp->v_data, 0, sizeof(*v7fs_node));
474	v7fs_node = vp->v_data;
475	mutex_enter(&mntvnode_lock);
476	LIST_INSERT_HEAD(&v7fsmount->v7fs_node_head, v7fs_node, link);
477	mutex_exit(&mntvnode_lock);
478	v7fs_node->vnode = vp;
479	v7fs_node->v7fsmount = v7fsmount;
480	v7fs_node->inode = inode;/*structure copy */
481	v7fs_node->lockf = NULL; /* advlock */
482
483	genfs_node_init(vp, &v7fs_genfsops);
484	uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode));
485
486	if (ino == V7FS_ROOT_INODE) {
487		vp->v_type = VDIR;
488		vp->v_vflag |= VV_ROOT;
489	} else {
490		vp->v_type = v7fs_mode_to_vtype(inode.mode);
491
492		if (vp->v_type == VBLK || vp->v_type == VCHR) {
493			dev_t rdev = inode.device;
494			vp->v_op = v7fs_specop_p;
495			spec_node_init(vp, rdev);
496		} else if (vp->v_type == VFIFO) {
497			vp->v_op = v7fs_fifoop_p;
498		}
499	}
500
501	*vpp = vp;
502
503	return 0;
504}
505
506
507int
508v7fs_fhtovp(struct mount *mp, struct fid *fid, struct vnode **vpp)
509{
510
511	DPRINTF("\n");
512	/* notyet */
513	return EOPNOTSUPP;
514}
515
516int
517v7fs_vptofh(struct vnode *vpp, struct fid *fid, size_t *fh_size)
518{
519
520	DPRINTF("\n");
521	/* notyet */
522	return EOPNOTSUPP;
523}
524
525MALLOC_DECLARE(M_V7FS);
526MALLOC_DECLARE(M_V7FS_VNODE);
527
528void
529v7fs_init(void)
530{
531
532	DPRINTF("\n");
533	malloc_type_attach(M_V7FS_VFS);
534	malloc_type_attach(M_V7FS);
535	malloc_type_attach(M_V7FS_VNODE);
536	pool_init(&v7fs_node_pool, sizeof(struct v7fs_node), 0, 0, 0,
537	    "v7fs_node_pool", &pool_allocator_nointr, IPL_NONE);
538}
539
540void
541v7fs_reinit(void)
542{
543
544	/* Nothing to do. */
545	DPRINTF("\n");
546}
547
548void
549v7fs_done(void)
550{
551
552	DPRINTF("\n");
553	pool_destroy(&v7fs_node_pool);
554	malloc_type_detach(M_V7FS);
555	malloc_type_detach(M_V7FS_VFS);
556	malloc_type_detach(M_V7FS_VNODE);
557}
558
559int
560v7fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags,
561    kauth_cred_t cred)
562{
563
564	DPRINTF("\n");
565	return 0;
566}
567
568int
569v7fs_mountroot(void)
570{
571	struct mount *mp;
572	int error;
573
574	DPRINTF("");
575	/* On mountroot, devvp (rootdev) is opened by vfs_mountroot */
576	if ((error = is_v7fs_partition (rootvp)))
577		return error;
578
579	if ((error = vfs_rootmountalloc(MOUNT_V7FS, "root_device", &mp))) {
580		DPRINTF("mountalloc error=%d\n", error);
581		vrele(rootvp);
582		return error;
583	}
584
585	if ((error = v7fs_mountfs(rootvp, mp, _BYTE_ORDER))) {
586		DPRINTF("mountfs error=%d\n", error);
587		vfs_unbusy(mp, false, NULL);
588		vfs_destroy(mp);
589		return error;
590	}
591
592	mutex_enter(&mountlist_lock);
593	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
594	mutex_exit(&mountlist_lock);
595
596	vfs_unbusy(mp, false, NULL);
597
598	return 0;
599}
600
601/* Reload disk inode information */
602int
603v7fs_vnode_reload(struct mount *mp, struct vnode *vp)
604{
605	struct v7fs_mount *v7fsmount = mp->mnt_data;
606	struct v7fs_self *fs = v7fsmount->core;
607	struct v7fs_node *v7fs_node;
608	struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode;
609	int target_ino = inode->inode_number;
610	int error = 0;
611
612	DPRINTF("#%d\n", target_ino);
613	mutex_enter(&mntvnode_lock);
614	for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head);
615	     v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) {
616		inode = &v7fs_node->inode;
617		if (!v7fs_inode_allocated(inode)) {
618			continue;
619		}
620		if (inode->inode_number == target_ino) {
621			error = v7fs_inode_load(fs, &v7fs_node->inode,
622			    target_ino);
623			DPRINTF("sync #%d error=%d\n", target_ino, error);
624			break;
625		}
626	}
627	mutex_exit(&mntvnode_lock);
628
629	return error;
630}
631