hpfs_vfsops.c revision 132902
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: head/sys/fs/hpfs/hpfs_vfsops.c 132902 2004-07-30 22:08:52Z phk $
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 <vm/vm.h>
44#include <vm/vm_param.h>
45#include <vm/vm_page.h>
46#include <vm/vm_object.h>
47#include <vm/vm_extern.h>
48
49#include <fs/hpfs/hpfs.h>
50#include <fs/hpfs/hpfsmount.h>
51#include <fs/hpfs/hpfs_subr.h>
52
53MALLOC_DEFINE(M_HPFSMNT, "HPFS mount", "HPFS mount structure");
54MALLOC_DEFINE(M_HPFSNO, "HPFS node", "HPFS node structure");
55
56struct sockaddr;
57
58static int	hpfs_mountfs(register struct vnode *, struct mount *,
59				  struct hpfs_args *, struct thread *);
60
61static vfs_init_t       hpfs_init;
62static vfs_uninit_t     hpfs_uninit;
63static vfs_fhtovp_t     hpfs_fhtovp;
64static vfs_vget_t       hpfs_vget;
65static vfs_omount_t     hpfs_omount;
66static vfs_root_t       hpfs_root;
67static vfs_statfs_t     hpfs_statfs;
68static vfs_unmount_t    hpfs_unmount;
69static vfs_vptofh_t     hpfs_vptofh;
70
71static int
72hpfs_init (
73	struct vfsconf *vcp )
74{
75	dprintf(("hpfs_init():\n"));
76
77	hpfs_hphashinit();
78	return 0;
79}
80
81static int
82hpfs_uninit (vfsp)
83	struct vfsconf *vfsp;
84{
85	hpfs_hphashdestroy();
86	return 0;;
87}
88
89static int
90hpfs_omount (
91	struct mount *mp,
92	char *path,
93	caddr_t data,
94	struct thread *td )
95{
96	size_t		size;
97	int		err = 0;
98	struct vnode	*devvp;
99	struct hpfs_args args;
100	struct hpfsmount *hpmp = 0;
101	struct nameidata ndp;
102
103	dprintf(("hpfs_omount():\n"));
104	/*
105	 ***
106	 * Mounting non-root filesystem or updating a filesystem
107	 ***
108	 */
109
110	/* copy in user arguments*/
111	err = copyin(data, (caddr_t)&args, sizeof (struct hpfs_args));
112	if (err)
113		goto error_1;		/* can't get arguments*/
114
115	/*
116	 * If updating, check whether changing from read-only to
117	 * read/write; if there is no device name, that's all we do.
118	 */
119	if (mp->mnt_flag & MNT_UPDATE) {
120		dprintf(("hpfs_omount: MNT_UPDATE: "));
121
122		hpmp = VFSTOHPFS(mp);
123
124		if (args.fspec == 0) {
125			dprintf(("export 0x%x\n",args.export.ex_flags));
126			err = vfs_export(mp, &args.export);
127			if (err) {
128				printf("hpfs_omount: vfs_export failed %d\n",
129					err);
130			}
131			goto success;
132		} else {
133			dprintf(("name [FAILED]\n"));
134			err = EINVAL;
135			goto success;
136		}
137		dprintf(("\n"));
138	}
139
140	/*
141	 * Not an update, or updating the name: look up the name
142	 * and verify that it refers to a sensible block device.
143	 */
144	NDINIT(&ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, td);
145	err = namei(&ndp);
146	if (err) {
147		/* can't get devvp!*/
148		goto error_1;
149	}
150
151	devvp = ndp.ni_vp;
152
153	if (!vn_isdisk(devvp, &err))
154		goto error_2;
155
156	/*
157	 ********************
158	 * NEW MOUNT
159	 ********************
160	 */
161
162	/*
163	 * Since this is a new mount, we want the names for
164	 * the device and the mount point copied in.  If an
165	 * error occurs, the mountpoint is discarded by the
166	 * upper level code.  Note that vfs_omount() handles
167	 * copying the mountpoint f_mntonname for us, so we
168	 * don't have to do it here unless we want to set it
169	 * to something other than "path" for some rason.
170	 */
171	/* Save "mounted from" info for mount point (NULL pad)*/
172	copyinstr(	args.fspec,			/* device name*/
173			mp->mnt_stat.f_mntfromname,	/* save area*/
174			MNAMELEN - 1,			/* max size*/
175			&size);				/* real size*/
176	bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
177
178	err = hpfs_mountfs(devvp, mp, &args, td);
179	if (err)
180		goto error_2;
181
182	/*
183	 * Initialize FS stat information in mount struct; uses both
184	 * mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname
185	 *
186	 * This code is common to root and non-root mounts
187	 */
188	(void)VFS_STATFS(mp, &mp->mnt_stat, td);
189
190	goto success;
191
192
193error_2:	/* error with devvp held*/
194
195	/* release devvp before failing*/
196	vrele(devvp);
197
198error_1:	/* no state to back out*/
199	/* XXX: Missing NDFREE(&ndp, ...) */
200
201success:
202	return( err);
203}
204
205/*
206 * Common code for mount and mountroot
207 */
208int
209hpfs_mountfs(devvp, mp, argsp, td)
210	register struct vnode *devvp;
211	struct mount *mp;
212	struct hpfs_args *argsp;
213	struct thread *td;
214{
215	int error, ncount, ronly;
216	struct sublock *sup;
217	struct spblock *spp;
218	struct hpfsmount *hpmp;
219	struct buf *bp = NULL;
220	struct vnode *vp;
221	struct cdev *dev = devvp->v_rdev;
222
223	dprintf(("hpfs_mountfs():\n"));
224	/*
225	 * Disallow multiple mounts of the same device.
226	 * Disallow mounting of a device that is currently in use
227	 * (except for root, which might share swap device for miniroot).
228	 * Flush out any old buffers remaining from a previous use.
229	 */
230	error = vfs_mountedon(devvp);
231	if (error)
232		return (error);
233	ncount = vcount(devvp);
234	if (devvp->v_object)
235		ncount -= 1;
236	if (ncount > 1)
237		return (EBUSY);
238
239	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
240	error = vinvalbuf(devvp, V_SAVE, td->td_ucred, td, 0, 0);
241	VOP_UNLOCK(devvp, 0, td);
242	if (error)
243		return (error);
244
245	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
246	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
247	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, td, -1);
248	VOP_UNLOCK(devvp, 0, td);
249	if (error)
250		return (error);
251
252	/*
253	 * Do actual mount
254	 */
255	hpmp = malloc(sizeof(struct hpfsmount), M_HPFSMNT, M_WAITOK | M_ZERO);
256
257	/* Read in SuperBlock */
258	error = bread(devvp, SUBLOCK, SUSIZE, NOCRED, &bp);
259	if (error)
260		goto failed;
261	bcopy(bp->b_data, &hpmp->hpm_su, sizeof(struct sublock));
262	brelse(bp); bp = NULL;
263
264	/* Read in SpareBlock */
265	error = bread(devvp, SPBLOCK, SPSIZE, NOCRED, &bp);
266	if (error)
267		goto failed;
268	bcopy(bp->b_data, &hpmp->hpm_sp, sizeof(struct spblock));
269	brelse(bp); bp = NULL;
270
271	sup = &hpmp->hpm_su;
272	spp = &hpmp->hpm_sp;
273
274	/* Check magic */
275	if (sup->su_magic != SU_MAGIC) {
276		printf("hpfs_mountfs: SuperBlock MAGIC DOESN'T MATCH\n");
277		error = EINVAL;
278		goto failed;
279	}
280	if (spp->sp_magic != SP_MAGIC) {
281		printf("hpfs_mountfs: SpareBlock MAGIC DOESN'T MATCH\n");
282		error = EINVAL;
283		goto failed;
284	}
285
286	mp->mnt_data = (qaddr_t)hpmp;
287	hpmp->hpm_devvp = devvp;
288	hpmp->hpm_dev = devvp->v_rdev;
289	hpmp->hpm_mp = mp;
290	hpmp->hpm_uid = argsp->uid;
291	hpmp->hpm_gid = argsp->gid;
292	hpmp->hpm_mode = argsp->mode;
293
294	error = hpfs_bminit(hpmp);
295	if (error)
296		goto failed;
297
298	error = hpfs_cpinit(hpmp, argsp);
299	if (error) {
300		hpfs_bmdeinit(hpmp);
301		goto failed;
302	}
303
304	error = hpfs_root(mp, &vp, td);
305	if (error) {
306		hpfs_cpdeinit(hpmp);
307		hpfs_bmdeinit(hpmp);
308		goto failed;
309	}
310
311	vput(vp);
312
313	mp->mnt_stat.f_fsid.val[0] = (long)dev2udev(dev);
314	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
315	mp->mnt_maxsymlinklen = 0;
316	mp->mnt_flag |= MNT_LOCAL;
317	devvp->v_rdev->si_mountpoint = mp;
318	return (0);
319
320failed:
321	if (bp)
322		brelse (bp);
323	mp->mnt_data = (qaddr_t)NULL;
324	devvp->v_rdev->si_mountpoint = NULL;
325	(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, td);
326	return (error);
327}
328
329static int
330hpfs_unmount(
331	struct mount *mp,
332	int mntflags,
333	struct thread *td)
334{
335	int error, flags, ronly;
336	register struct hpfsmount *hpmp = VFSTOHPFS(mp);
337
338	dprintf(("hpfs_unmount():\n"));
339
340	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
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, td);
349	if (error) {
350		printf("hpfs_unmount: vflush failed: %d\n",error);
351		return (error);
352	}
353
354	hpmp->hpm_devvp->v_rdev->si_mountpoint = NULL;
355
356	vinvalbuf(hpmp->hpm_devvp, V_SAVE, NOCRED, td, 0, 0);
357	error = VOP_CLOSE(hpmp->hpm_devvp, ronly ? FREAD : FREAD|FWRITE,
358		NOCRED, td);
359
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 = (qaddr_t)0;
366	mp->mnt_flag &= ~MNT_LOCAL;
367	FREE(hpmp, M_HPFSMNT);
368
369	return (0);
370}
371
372static int
373hpfs_root(
374	struct mount *mp,
375	struct vnode **vpp,
376	struct thread *td )
377{
378	int error = 0;
379	struct hpfsmount *hpmp = VFSTOHPFS(mp);
380
381	dprintf(("hpfs_root():\n"));
382	error = VFS_VGET(mp, (ino_t)hpmp->hpm_su.su_rootfno, LK_EXCLUSIVE, vpp);
383	if(error) {
384		printf("hpfs_root: VFS_VGET failed: %d\n",error);
385		return (error);
386	}
387
388	return (error);
389}
390
391static int
392hpfs_statfs(
393	struct mount *mp,
394	struct statfs *sbp,
395	struct thread *td)
396{
397	struct hpfsmount *hpmp = VFSTOHPFS(mp);
398
399	dprintf(("hpfs_statfs(): HPFS%d.%d\n",
400		hpmp->hpm_su.su_hpfsver, hpmp->hpm_su.su_fnctver));
401
402	sbp->f_type = mp->mnt_vfc->vfc_typenum;
403	sbp->f_bsize = DEV_BSIZE;
404	sbp->f_iosize = DEV_BSIZE;
405	sbp->f_blocks = hpmp->hpm_su.su_btotal;
406	sbp->f_bfree = sbp->f_bavail = hpmp->hpm_bavail;
407	sbp->f_ffree = 0;
408	sbp->f_files = 0;
409	if (sbp != &mp->mnt_stat) {
410		bcopy((caddr_t)mp->mnt_stat.f_mntonname,
411			(caddr_t)&sbp->f_mntonname[0], MNAMELEN);
412		bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
413			(caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
414	}
415	sbp->f_flags = mp->mnt_flag;
416
417	return (0);
418}
419
420/*ARGSUSED*/
421static int
422hpfs_fhtovp(
423	struct mount *mp,
424	struct fid *fhp,
425	struct vnode **vpp)
426{
427	struct vnode *nvp;
428	struct hpfid *hpfhp = (struct hpfid *)fhp;
429	int error;
430
431	if ((error = VFS_VGET(mp, hpfhp->hpfid_ino, LK_EXCLUSIVE, &nvp)) != 0) {
432		*vpp = NULLVP;
433		return (error);
434	}
435	/* XXX as unlink/rmdir/mkdir/creat are not currently possible
436	 * with HPFS, we don't need to check anything else for now */
437	*vpp = nvp;
438
439	return (0);
440}
441
442static int
443hpfs_vptofh(
444	struct vnode *vp,
445	struct fid *fhp)
446{
447	register struct hpfsnode *hpp;
448	register struct hpfid *hpfhp;
449
450	hpp = VTOHP(vp);
451	hpfhp = (struct hpfid *)fhp;
452	hpfhp->hpfid_len = sizeof(struct hpfid);
453	hpfhp->hpfid_ino = hpp->h_no;
454	/* hpfhp->hpfid_gen = hpp->h_gen; */
455	return (0);
456}
457
458static int
459hpfs_vget(
460	struct mount *mp,
461	ino_t ino,
462	int flags,
463	struct vnode **vpp)
464{
465	struct hpfsmount *hpmp = VFSTOHPFS(mp);
466	struct vnode *vp;
467	struct hpfsnode *hp;
468	struct buf *bp;
469	struct thread *td = curthread;	/* XXX */
470	int error;
471
472	dprintf(("hpfs_vget(0x%x): ",ino));
473
474	*vpp = NULL;
475	hp = NULL;
476	vp = NULL;
477
478	if ((error = hpfs_hphashvget(hpmp->hpm_dev, ino, flags, vpp, td)) != 0)
479		return (error);
480	if (*vpp != NULL) {
481		dprintf(("hashed\n"));
482		return (0);
483	}
484
485	/*
486	 * We have to lock node creation for a while,
487	 * but then we have to call getnewvnode(),
488	 * this may cause hpfs_reclaim() to be called,
489	 * this may need to VOP_VGET() parent dir for
490	 * update reasons, and if parent is not in
491	 * hash, we have to lock node creation...
492	 * To solve this, we MALLOC, getnewvnode and init while
493	 * not locked (probability of node appearence
494	 * at that time is little, and anyway - we'll
495	 * check for it).
496	 */
497	MALLOC(hp, struct hpfsnode *, sizeof(struct hpfsnode),
498		M_HPFSNO, M_WAITOK);
499
500	error = getnewvnode("hpfs", hpmp->hpm_mp, hpfs_vnodeop_p, &vp);
501	if (error) {
502		printf("hpfs_vget: can't get new vnode\n");
503		FREE(hp, M_HPFSNO);
504		return (error);
505	}
506
507	dprintf(("prenew "));
508
509	vp->v_data = hp;
510
511	if (ino == (ino_t)hpmp->hpm_su.su_rootfno)
512		vp->v_vflag |= VV_ROOT;
513
514
515	mtx_init(&hp->h_interlock, "hpfsnode interlock", NULL, MTX_DEF);
516
517	hp->h_flag = H_INVAL;
518	hp->h_vp = vp;
519	hp->h_hpmp = hpmp;
520	hp->h_no = ino;
521	hp->h_dev = hpmp->hpm_dev;
522	hp->h_uid = hpmp->hpm_uid;
523	hp->h_gid = hpmp->hpm_uid;
524	hp->h_mode = hpmp->hpm_mode;
525	hp->h_devvp = hpmp->hpm_devvp;
526	VREF(hp->h_devvp);
527
528	error = vn_lock(vp, LK_EXCLUSIVE, td);
529	if (error) {
530		vput(vp);
531		return (error);
532	}
533
534	do {
535		if ((error =
536		     hpfs_hphashvget(hpmp->hpm_dev, ino, flags, vpp, td))) {
537			vput(vp);
538			return (error);
539		}
540		if (*vpp != NULL) {
541			dprintf(("hashed2\n"));
542			vput(vp);
543			return (0);
544		}
545	} while(lockmgr(&hpfs_hphash_lock,LK_EXCLUSIVE|LK_SLEEPFAIL,NULL,NULL));
546
547	hpfs_hphashins(hp);
548
549	lockmgr(&hpfs_hphash_lock, LK_RELEASE, NULL, NULL);
550
551	error = bread(hpmp->hpm_devvp, ino, FNODESIZE, NOCRED, &bp);
552	if (error) {
553		printf("hpfs_vget: can't read ino %d\n",ino);
554		vput(vp);
555		return (error);
556	}
557	bcopy(bp->b_data, &hp->h_fn, sizeof(struct fnode));
558	brelse(bp);
559
560	if (hp->h_fn.fn_magic != FN_MAGIC) {
561		printf("hpfs_vget: MAGIC DOESN'T MATCH\n");
562		vput(vp);
563		return (EINVAL);
564	}
565
566	vp->v_type = hp->h_fn.fn_flag ? VDIR:VREG;
567	hp->h_flag &= ~H_INVAL;
568
569	*vpp = vp;
570
571	return (0);
572}
573
574static struct vfsops hpfs_vfsops = {
575	.vfs_fhtovp =		hpfs_fhtovp,
576	.vfs_init =		hpfs_init,
577	.vfs_omount =		hpfs_omount,
578	.vfs_root =		hpfs_root,
579	.vfs_statfs =		hpfs_statfs,
580	.vfs_uninit =		hpfs_uninit,
581	.vfs_unmount =		hpfs_unmount,
582	.vfs_vget =		hpfs_vget,
583	.vfs_vptofh =		hpfs_vptofh,
584};
585VFS_SET(hpfs_vfsops, hpfs, 0);
586