hpfs_vfsops.c revision 233120
1/*-
2 * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/9/sys/fs/hpfs/hpfs_vfsops.c 233120 2012-03-18 15:03:02Z kevlo $
27 */
28
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/namei.h>
33#include <sys/conf.h>
34#include <sys/proc.h>
35#include <sys/kernel.h>
36#include <sys/vnode.h>
37#include <sys/mount.h>
38#include <sys/bio.h>
39#include <sys/buf.h>
40#include <sys/fcntl.h>
41#include <sys/malloc.h>
42
43#include <geom/geom.h>
44#include <geom/geom_vfs.h>
45
46#include <vm/vm.h>
47#include <vm/vm_param.h>
48#include <vm/vm_page.h>
49#include <vm/vm_object.h>
50#include <vm/vm_extern.h>
51
52#include <fs/hpfs/hpfs.h>
53#include <fs/hpfs/hpfsmount.h>
54#include <fs/hpfs/hpfs_subr.h>
55
56MALLOC_DEFINE(M_HPFSMNT, "hpfs_mount", "HPFS mount structure");
57MALLOC_DEFINE(M_HPFSNO, "hpfs_node", "HPFS node structure");
58
59struct sockaddr;
60
61static int	hpfs_mountfs(register struct vnode *, struct mount *,
62				  struct thread *);
63
64static vfs_fhtovp_t     hpfs_fhtovp;
65static vfs_vget_t       hpfs_vget;
66static vfs_cmount_t     hpfs_cmount;
67static vfs_mount_t      hpfs_mount;
68static vfs_root_t       hpfs_root;
69static vfs_statfs_t     hpfs_statfs;
70static vfs_unmount_t    hpfs_unmount;
71
72static int
73hpfs_cmount (
74	struct mntarg *ma,
75	void *data,
76	uint64_t flags)
77{
78	struct hpfs_args args;
79	struct export_args exp;
80	int error;
81
82	error = copyin(data, (caddr_t)&args, sizeof (struct hpfs_args));
83	if (error)
84		return (error);
85	vfs_oexport_conv(&args.export, &exp);
86
87	ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN);
88	ma = mount_arg(ma, "export", &exp, sizeof(exp));
89	ma = mount_argf(ma, "uid", "%d", args.uid);
90	ma = mount_argf(ma, "gid", "%d", args.gid);
91	ma = mount_argf(ma, "mode", "%d", args.mode);
92	if (args.flags & HPFSMNT_TABLES) {
93		ma = mount_arg(ma, "d2u", args.d2u, sizeof args.d2u);
94		ma = mount_arg(ma, "u2d", args.u2d, sizeof args.u2d);
95	}
96
97	error = kernel_mount(ma, flags);
98
99	return (error);
100}
101
102static const char *hpfs_opts[] = {
103	"from", "export", "uid", "gid", "mode", "d2u", "u2d", NULL
104};
105
106static int
107hpfs_mount (struct mount *mp)
108{
109	int		err = 0, error;
110	struct vnode	*devvp;
111	struct thread *td;
112	struct nameidata ndp;
113	struct export_args export;
114	char *from;
115
116	td = curthread;
117	dprintf(("hpfs_omount():\n"));
118	/*
119	 ***
120	 * Mounting non-root filesystem or updating a filesystem
121	 ***
122	 */
123	if (vfs_filteropt(mp->mnt_optnew, hpfs_opts))
124		return (EINVAL);
125
126	from = vfs_getopts(mp->mnt_optnew, "from", &error);
127	if (error)
128		return (error);
129
130	/*
131	 * If updating, check whether changing from read-only to
132	 * read/write; if there is no device name, that's all we do.
133	 */
134	if (mp->mnt_flag & MNT_UPDATE) {
135		dprintf(("hpfs_omount: MNT_UPDATE: "));
136
137		if (from == NULL) {
138			error = vfs_copyopt(mp->mnt_optnew, "export",
139			    &export, sizeof export);
140			if (error)
141				return (error);
142			dprintf(("export 0x%x\n",args.export.ex_flags));
143			err = vfs_export(mp, &export);
144			if (err) {
145				printf("hpfs_omount: vfs_export failed %d\n",
146					err);
147			}
148			goto success;
149		} else {
150			dprintf(("name [FAILED]\n"));
151			err = EINVAL;
152			goto success;
153		}
154		dprintf(("\n"));
155	}
156
157	/*
158	 * Not an update, or updating the name: look up the name
159	 * and verify that it refers to a sensible block device.
160	 */
161	NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td);
162	err = namei(&ndp);
163	if (err) {
164		/* can't get devvp!*/
165		goto error_1;
166	}
167
168	devvp = ndp.ni_vp;
169
170	if (!vn_isdisk(devvp, &err)) {
171		vput(devvp);
172		return (err);
173	}
174
175	/*
176	 ********************
177	 * NEW MOUNT
178	 ********************
179	 */
180
181	/*
182	 * Since this is a new mount, we want the names for
183	 * the device and the mount point copied in.  If an
184	 * error occurs, the mountpoint is discarded by the
185	 * upper level code.  Note that vfs_omount() handles
186	 * copying the mountpoint f_mntonname for us, so we
187	 * don't have to do it here unless we want to set it
188	 * to something other than "path" for some rason.
189	 */
190	/* Save "mounted from" info for mount point (NULL pad)*/
191	vfs_mountedfrom(mp, from);
192
193	err = hpfs_mountfs(devvp, mp, td);
194	if (err) {
195		vrele(devvp);
196		goto error_1;
197	}
198
199	goto success;
200
201error_1:	/* no state to back out*/
202	/* XXX: Missing NDFREE(&ndp, ...) */
203
204success:
205	return( err);
206}
207
208/*
209 * Common code for mount and mountroot
210 */
211int
212hpfs_mountfs(devvp, mp, td)
213	register struct vnode *devvp;
214	struct mount *mp;
215	struct thread *td;
216{
217	int error, ronly, v;
218	struct sublock *sup;
219	struct spblock *spp;
220	struct hpfsmount *hpmp;
221	struct buf *bp = NULL;
222	struct vnode *vp;
223	struct cdev *dev = devvp->v_rdev;
224	struct g_consumer *cp;
225	struct bufobj *bo;
226
227	if (mp->mnt_flag & MNT_ROOTFS)
228		return (EOPNOTSUPP);
229	dprintf(("hpfs_mountfs():\n"));
230	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
231	/* XXX: use VOP_ACCESS to check FS perms */
232	DROP_GIANT();
233	g_topology_lock();
234	error = g_vfs_open(devvp, &cp, "hpfs", ronly ? 0 : 1);
235	g_topology_unlock();
236	PICKUP_GIANT();
237	VOP_UNLOCK(devvp, 0);
238	if (error)
239		return (error);
240
241	bo = &devvp->v_bufobj;
242	bo->bo_private = cp;
243	bo->bo_ops = g_vfs_bufops;
244
245	/*
246	 * Do actual mount
247	 */
248	hpmp = malloc(sizeof(struct hpfsmount), M_HPFSMNT, M_WAITOK | M_ZERO);
249
250	hpmp->hpm_cp = cp;
251	hpmp->hpm_bo = bo;
252
253	/* Read in SuperBlock */
254	error = bread(devvp, SUBLOCK, SUSIZE, NOCRED, &bp);
255	if (error)
256		goto failed;
257	bcopy(bp->b_data, &hpmp->hpm_su, sizeof(struct sublock));
258	brelse(bp); bp = NULL;
259
260	/* Read in SpareBlock */
261	error = bread(devvp, SPBLOCK, SPSIZE, NOCRED, &bp);
262	if (error)
263		goto failed;
264	bcopy(bp->b_data, &hpmp->hpm_sp, sizeof(struct spblock));
265	brelse(bp); bp = NULL;
266
267	sup = &hpmp->hpm_su;
268	spp = &hpmp->hpm_sp;
269
270	/* Check magic */
271	if (sup->su_magic != SU_MAGIC) {
272		printf("hpfs_mountfs: SuperBlock MAGIC DOESN'T MATCH\n");
273		error = EINVAL;
274		goto failed;
275	}
276	if (spp->sp_magic != SP_MAGIC) {
277		printf("hpfs_mountfs: SpareBlock MAGIC DOESN'T MATCH\n");
278		error = EINVAL;
279		goto failed;
280	}
281
282	mp->mnt_data = hpmp;
283	hpmp->hpm_devvp = devvp;
284	hpmp->hpm_dev = devvp->v_rdev;
285	hpmp->hpm_mp = mp;
286	if (vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v) == 1)
287		hpmp->hpm_uid = v;
288	if (vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v) == 1)
289		hpmp->hpm_gid = v;
290	if (vfs_scanopt(mp->mnt_optnew, "mode", "%d", &v) == 1)
291		hpmp->hpm_mode = v;
292
293	error = hpfs_bminit(hpmp);
294	if (error)
295		goto failed;
296
297	error = hpfs_cpinit(mp, hpmp);
298	if (error) {
299		hpfs_bmdeinit(hpmp);
300		goto failed;
301	}
302
303	error = hpfs_root(mp, LK_EXCLUSIVE, &vp);
304	if (error) {
305		hpfs_cpdeinit(hpmp);
306		hpfs_bmdeinit(hpmp);
307		goto failed;
308	}
309
310	vput(vp);
311
312	mp->mnt_stat.f_fsid.val[0] = (long)dev2udev(dev);
313	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
314	mp->mnt_maxsymlinklen = 0;
315	MNT_ILOCK(mp);
316	mp->mnt_flag |= MNT_LOCAL;
317	MNT_IUNLOCK(mp);
318	return (0);
319
320failed:
321	if (bp)
322		brelse (bp);
323	mp->mnt_data = NULL;
324	DROP_GIANT();
325	g_topology_lock();
326	g_vfs_close(cp);
327	g_topology_unlock();
328	PICKUP_GIANT();
329	return (error);
330}
331
332static int
333hpfs_unmount(
334	struct mount *mp,
335	int mntflags)
336{
337	int error, flags;
338	register struct hpfsmount *hpmp = VFSTOHPFS(mp);
339
340	dprintf(("hpfs_unmount():\n"));
341
342	flags = 0;
343	if(mntflags & MNT_FORCE)
344		flags |= FORCECLOSE;
345
346	dprintf(("hpfs_unmount: vflushing...\n"));
347
348	error = vflush(mp, 0, flags, curthread);
349	if (error) {
350		printf("hpfs_unmount: vflush failed: %d\n",error);
351		return (error);
352	}
353
354	vinvalbuf(hpmp->hpm_devvp, V_SAVE, 0, 0);
355	DROP_GIANT();
356	g_topology_lock();
357	g_vfs_close(hpmp->hpm_cp);
358	g_topology_unlock();
359	PICKUP_GIANT();
360	vrele(hpmp->hpm_devvp);
361
362	dprintf(("hpfs_umount: freeing memory...\n"));
363	hpfs_cpdeinit(hpmp);
364	hpfs_bmdeinit(hpmp);
365	mp->mnt_data = NULL;
366	MNT_ILOCK(mp);
367	mp->mnt_flag &= ~MNT_LOCAL;
368	MNT_IUNLOCK(mp);
369	free(hpmp, M_HPFSMNT);
370
371	return (0);
372}
373
374static int
375hpfs_root(
376	struct mount *mp,
377	int flags,
378	struct vnode **vpp)
379{
380	int error = 0;
381	struct hpfsmount *hpmp = VFSTOHPFS(mp);
382
383	dprintf(("hpfs_root():\n"));
384	error = VFS_VGET(mp, (ino_t)hpmp->hpm_su.su_rootfno, LK_EXCLUSIVE, vpp);
385	if(error) {
386		printf("hpfs_root: VFS_VGET failed: %d\n",error);
387		return (error);
388	}
389
390	return (error);
391}
392
393static int
394hpfs_statfs(
395	struct mount *mp,
396	struct statfs *sbp)
397{
398	struct hpfsmount *hpmp = VFSTOHPFS(mp);
399
400	dprintf(("hpfs_statfs(): HPFS%d.%d\n",
401		hpmp->hpm_su.su_hpfsver, hpmp->hpm_su.su_fnctver));
402
403	sbp->f_type = mp->mnt_vfc->vfc_typenum;
404	sbp->f_bsize = DEV_BSIZE;
405	sbp->f_iosize = DEV_BSIZE;
406	sbp->f_blocks = hpmp->hpm_su.su_btotal;
407	sbp->f_bfree = sbp->f_bavail = hpmp->hpm_bavail;
408	sbp->f_ffree = 0;
409	sbp->f_files = 0;
410	sbp->f_flags = mp->mnt_flag;
411
412	return (0);
413}
414
415/*ARGSUSED*/
416static int
417hpfs_fhtovp(
418	struct mount *mp,
419	struct fid *fhp,
420	int flags,
421	struct vnode **vpp)
422{
423	struct vnode *nvp;
424	struct hpfid *hpfhp = (struct hpfid *)fhp;
425	int error;
426
427	if ((error = VFS_VGET(mp, hpfhp->hpfid_ino, LK_EXCLUSIVE, &nvp)) != 0) {
428		*vpp = NULLVP;
429		return (error);
430	}
431	/* XXX as unlink/rmdir/mkdir/creat are not currently possible
432	 * with HPFS, we don't need to check anything else for now */
433	*vpp = nvp;
434
435	return (0);
436}
437
438static int
439hpfs_vget(
440	struct mount *mp,
441	ino_t ino,
442	int flags,
443	struct vnode **vpp)
444{
445	struct hpfsmount *hpmp = VFSTOHPFS(mp);
446	struct vnode *vp;
447	struct hpfsnode *hp;
448	struct buf *bp;
449	int error;
450
451	dprintf(("hpfs_vget(0x%x): ",ino));
452
453	error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL);
454	if (error || *vpp != NULL)
455		return (error);
456
457	*vpp = NULL;
458	hp = NULL;
459	vp = NULL;
460
461	/*
462	 * We have to lock node creation for a while,
463	 * but then we have to call getnewvnode(),
464	 * this may cause hpfs_reclaim() to be called,
465	 * this may need to VOP_VGET() parent dir for
466	 * update reasons, and if parent is not in
467	 * hash, we have to lock node creation...
468	 * To solve this, we MALLOC, getnewvnode and init while
469	 * not locked (probability of node appearence
470	 * at that time is little, and anyway - we'll
471	 * check for it).
472	 */
473	hp = malloc(sizeof(struct hpfsnode),
474		M_HPFSNO, M_WAITOK);
475
476	error = getnewvnode("hpfs", mp, &hpfs_vnodeops, &vp);
477	if (error) {
478		printf("hpfs_vget: can't get new vnode\n");
479		free(hp, M_HPFSNO);
480		return (error);
481	}
482
483	dprintf(("prenew "));
484
485	vp->v_data = hp;
486
487	if (ino == (ino_t)hpmp->hpm_su.su_rootfno)
488		vp->v_vflag |= VV_ROOT;
489
490
491	mtx_init(&hp->h_interlock, "hpfsnode interlock", NULL, MTX_DEF);
492
493	hp->h_flag = H_INVAL;
494	hp->h_vp = vp;
495	hp->h_hpmp = hpmp;
496	hp->h_no = ino;
497	hp->h_dev = hpmp->hpm_dev;
498	hp->h_uid = hpmp->hpm_uid;
499	hp->h_gid = hpmp->hpm_uid;
500	hp->h_mode = hpmp->hpm_mode;
501	hp->h_devvp = hpmp->hpm_devvp;
502
503	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
504	error = insmntque(vp, mp);
505	if (error != 0) {
506		free(hp, M_HPFSNO);
507		return (error);
508	}
509	error = vfs_hash_insert(vp, ino, flags, curthread, vpp, NULL, NULL);
510	if (error || *vpp != NULL)
511		return (error);
512
513	error = bread(hpmp->hpm_devvp, ino, FNODESIZE, NOCRED, &bp);
514	if (error) {
515		printf("hpfs_vget: can't read ino %d\n",ino);
516		vput(vp);
517		return (error);
518	}
519	bcopy(bp->b_data, &hp->h_fn, sizeof(struct fnode));
520	brelse(bp);
521
522	if (hp->h_fn.fn_magic != FN_MAGIC) {
523		printf("hpfs_vget: MAGIC DOESN'T MATCH\n");
524		vput(vp);
525		return (EINVAL);
526	}
527
528	vp->v_type = hp->h_fn.fn_flag ? VDIR:VREG;
529	hp->h_flag &= ~H_INVAL;
530
531	*vpp = vp;
532
533	return (0);
534}
535
536static struct vfsops hpfs_vfsops = {
537	.vfs_fhtovp =		hpfs_fhtovp,
538	.vfs_cmount =		hpfs_cmount,
539	.vfs_mount =		hpfs_mount,
540	.vfs_root =		hpfs_root,
541	.vfs_statfs =		hpfs_statfs,
542	.vfs_unmount =		hpfs_unmount,
543	.vfs_vget =		hpfs_vget,
544};
545VFS_SET(hpfs_vfsops, hpfs, 0);
546