This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / sys / pcfs / pcfs_vfsops.c
CommitLineData
15637ed4
RG
1/*
2 * Written by Paul Popelka (paulp@uts.amdahl.com)
3 *
4 * You can do anything you want with this software,
5 * just don't say you wrote it,
6 * and don't remove this notice.
7 *
8 * This software is provided "as is".
9 *
10 * The author supplies this software to be publicly
11 * redistributed on the understanding that the author
12 * is not responsible for the correct functioning of
13 * this software in any circumstances and is not liable
14 * for any damages caused by this software.
15 *
16 * October 1992
17 *
78ed81a3 18 * $Id$
15637ed4
RG
19 */
20
21#include "param.h"
22#include "systm.h"
23#include "namei.h"
24#include "proc.h"
25#include "kernel.h"
26#include "vnode.h"
27#include "specdev.h" /* defines v_rdev */
28#include "mount.h"
29#include "buf.h"
30#include "file.h"
31#include "malloc.h"
32
33#include "bpb.h"
34#include "bootsect.h"
35#include "direntry.h"
36#include "denode.h"
37#include "pcfsmount.h"
38#include "fat.h"
39
40int pcfsdoforce = 0; /* 1 = force unmount */
41
42/*
43 * mp -
44 * path - addr in user space of mount point (ie /usr or whatever)
45 * data - addr in user space of mount params including the
46 * name of the block special file to treat as a filesystem.
47 * ndp -
48 * p -
49 */
50int
51pcfs_mount(mp, path, data, ndp, p)
52 struct mount *mp;
53 char *path;
54 caddr_t data;
55 struct nameidata *ndp;
56 struct proc *p;
57{
58 struct vnode *devvp; /* vnode for blk device to mount */
59 struct pcfs_args args; /* will hold data from mount request */
60 struct pcfsmount *pmp; /* pcfs specific mount control block */
61 int error;
62 u_int size;
63
64/*
65 * Copy in the args for the mount request.
66 */
67 if (error = copyin(data, (caddr_t)&args, sizeof(struct pcfs_args)))
68 return error;
69
70/*
71 * Check to see if they want it to be an exportable
72 * filesystem via nfs. And, if they do, should it
73 * be read only, and what uid is root to be mapped
74 * to.
75 */
76 if ((args.exflags & MNT_EXPORTED) || (mp->mnt_flag & MNT_EXPORTED)) {
77 if (args.exflags & MNT_EXPORTED)
78 mp->mnt_flag |= MNT_EXPORTED;
79 else
80 mp->mnt_flag &= ~MNT_EXPORTED;
81 if (args.exflags & MNT_EXRDONLY)
82 mp->mnt_flag |= MNT_EXRDONLY;
83 else
84 mp->mnt_flag &= ~MNT_EXRDONLY;
85 mp->mnt_exroot = args.exroot;
86 }
87
88/*
89 * If they just want to update then be sure we can
90 * do what is asked. Can't change a filesystem from
91 * read/write to read only. Why?
92 * And if they've supplied a new device file name then we
93 * continue, otherwise return.
94 */
95 if (mp->mnt_flag & MNT_UPDATE) {
96 pmp = (struct pcfsmount *)mp->mnt_data;
97 if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
98 pmp->pm_ronly = 0;
99 if (args.fspec == 0)
100 return 0;
101 }
102
103/*
104 * Now, lookup the name of the block device this
105 * mount or name update request is to apply to.
106 */
107 ndp->ni_nameiop = LOOKUP | FOLLOW;
108 ndp->ni_segflg = UIO_USERSPACE;
109 ndp->ni_dirp = args.fspec;
110 if (error = namei(ndp, p))
111 return error;
112
113/*
114 * Be sure they've given us a block device to treat
115 * as a filesystem. And, that its major number is
116 * within the bdevsw table.
117 */
118 devvp = ndp->ni_vp;
119 if (devvp->v_type != VBLK) {
120 vrele(devvp); /* namei() acquires this? */
121 return ENOTBLK;
122 }
123 if (major(devvp->v_rdev) >= nblkdev) {
124 vrele(devvp);
125 return ENXIO;
126 }
127
128/*
129 * If this is an update, then make sure the vnode
130 * for the block special device is the same as the
131 * one our filesystem is in.
132 */
133 if (mp->mnt_flag & MNT_UPDATE) {
134 if (devvp != pmp->pm_devvp)
135 error = EINVAL;
136 else
137 vrele(devvp);
138 } else {
139
140/*
141 * Well, it's not an update, it's a real mount request.
142 * Time to get dirty.
143 */
144 error = mountpcfs(devvp, mp, p);
145 }
146 if (error) {
147 vrele(devvp);
148 return error;
149 }
150
151/*
152 * Copy in the name of the directory the filesystem
153 * is to be mounted on.
154 * Then copy in the name of the block special file
155 * representing the filesystem being mounted.
156 * And we clear the remainder of the character strings
157 * to be tidy.
158 * Then, we try to fill in the filesystem stats structure
159 * as best we can with whatever applies from a dos file
160 * system.
161 */
162 pmp = (struct pcfsmount *)mp->mnt_data;
163 copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname,
164 sizeof(mp->mnt_stat.f_mntonname)-1, &size);
165 bzero(mp->mnt_stat.f_mntonname + size,
166 sizeof(mp->mnt_stat.f_mntonname) - size);
167 copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN-1, &size);
168 bzero(mp->mnt_stat.f_mntfromname + size,
169 MNAMELEN - size);
170 (void)pcfs_statfs(mp, &mp->mnt_stat, p);
171#if defined(PCFSDEBUG)
172printf("pcfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap);
173#endif /* defined(PCFSDEBUG) */
174 return 0;
175}
176
177int
178mountpcfs(devvp, mp, p)
179 struct vnode *devvp;
180 struct mount *mp;
181 struct proc *p;
182{
183 int i;
184 int bpc;
185 int bit;
186 int error = 0;
187 int needclose;
188 int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
189 dev_t dev = devvp->v_rdev;
190 union bootsector *bsp;
78ed81a3 191 struct pcfsmount *pmp = NULL;
15637ed4
RG
192 struct buf *bp0 = NULL;
193 struct byte_bpb33 *b33;
194 struct byte_bpb50 *b50;
195
196/*
197 * Multiple mounts of the same block special file
198 * aren't allowed. Make sure no one else has the
199 * special file open. And flush any old buffers
200 * from this filesystem. Presumably this prevents
201 * us from running into buffers that are the wrong
202 * blocksize.
203 * NOTE: mountedon() is a part of the ufs filesystem.
204 * If the ufs filesystem is not gen'ed into the system
205 * we will get an unresolved reference.
206 */
207 if (error = mountedon(devvp)) {
208 return error;
209 }
210 if (vcount(devvp) > 1)
211 return EBUSY;
212 vinvalbuf(devvp, 1);
213
214/*
215 * Now open the block special file.
216 */
217 if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p))
218 return error;
219 needclose = 1;
220#if defined(HDSUPPORT)
221/*
222 * Put this in when we support reading dos filesystems
223 * from partitioned harddisks.
224 */
225 if (VOP_IOCTL(devvp, DIOCGPART, &pcfspart, FREAD, NOCRED, p) == 0) {
226 }
227#endif /* defined(HDSUPPORT) */
228
229/*
230 * Read the boot sector of the filesystem, and then
231 * check the boot signature. If not a dos boot sector
232 * then error out. We could also add some checking on
233 * the bsOemName field. So far I've seen the following
234 * values:
235 * "IBM 3.3"
236 * "MSDOS3.3"
237 * "MSDOS5.0"
238 */
239 if (error = bread(devvp, 0, 512, NOCRED, &bp0))
240 goto error_exit;
241 bp0->b_flags |= B_AGE;
242 bsp = (union bootsector *)bp0->b_un.b_addr;
243 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
244 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
245 if (bsp->bs50.bsBootSectSig != BOOTSIG) {
246 error = EINVAL;
247 goto error_exit;
248 }
249
250 pmp = malloc(sizeof *pmp, M_PCFSMNT, M_WAITOK);
251 pmp->pm_inusemap = NULL;
252 pmp->pm_mountp = mp;
253
254/*
255 * Compute several useful quantities from the bpb in
256 * the bootsector. Copy in the dos 5 variant of the
257 * bpb then fix up the fields that are different between
258 * dos 5 and dos 3.3.
259 */
260 pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
261 pmp->pm_SectPerClust = b50->bpbSecPerClust;
262 pmp->pm_ResSectors = getushort(b50->bpbResSectors);
263 pmp->pm_FATs = b50->bpbFATs;
264 pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
265 pmp->pm_Sectors = getushort(b50->bpbSectors);
266 pmp->pm_Media = b50->bpbMedia;
267 pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
268 pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
269 pmp->pm_Heads = getushort(b50->bpbHeads);
270 if (pmp->pm_Sectors == 0) {
271 pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
272 pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
273 } else {
274 pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
275 pmp->pm_HugeSectors = pmp->pm_Sectors;
276 }
277 pmp->pm_fatblk = pmp->pm_ResSectors;
278 pmp->pm_rootdirblk = pmp->pm_fatblk +
279 (pmp->pm_FATs * pmp->pm_FATsecs);
280 pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
281 /
282 pmp->pm_BytesPerSec; /* in sectors */
283 pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
284 pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
285 pmp->pm_SectPerClust;
286 pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
287 pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
288 if (FAT12(pmp))
289 /* This will usually be a floppy disk.
290 * This size makes sure that one fat entry will not be split
291 * across multiple blocks. */
292 pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
293 else
294 /* This will usually be a hard disk.
295 * Reading or writing one block should be quite fast. */
296 pmp->pm_fatblocksize = MAXBSIZE;
297 pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
298
299 if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
300 printf("mountpcfs(): root directory is not a multiple of the clustersize in length\n");
301
302/*
303 * Compute mask and shift value for isolating cluster relative
304 * byte offsets and cluster numbers from a file offset.
305 */
306 bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
307 pmp->pm_bpcluster = bpc;
308 pmp->pm_depclust = bpc/sizeof(struct direntry);
309 pmp->pm_crbomask = bpc - 1;
310 if (bpc == 0) {
311 error = EINVAL;
312 goto error_exit;
313 }
314 bit = 1;
315 for (i = 0; i < 32; i++) {
316 if (bit & bpc) {
317 if (bit ^ bpc) {
318 error = EINVAL;
319 goto error_exit;
320 }
321 pmp->pm_cnshift = i;
322 break;
323 }
324 bit <<= 1;
325 }
326
327 pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */
328 pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */
329
330/*
331 * Release the bootsector buffer.
332 */
333 brelse(bp0);
334 bp0 = NULL;
335
336/*
337 * Allocate memory for the bitmap of allocated clusters,
338 * and then fill it in.
339 */
340 pmp->pm_inusemap = malloc((pmp->pm_maxcluster >> 3) + 1,
341 M_PCFSFAT, M_WAITOK);
342
343/*
344 * fillinusemap() needs pm_devvp.
345 */
346 pmp->pm_dev = dev;
347 pmp->pm_devvp = devvp;
348
349/*
350 * Have the inuse map filled in.
351 */
352 error = fillinusemap(pmp);
353 if (error)
354 goto error_exit;
355
356/*
357 * If they want fat updates to be synchronous then let
358 * them suffer the performance degradation in exchange
359 * for the on disk copy of the fat being correct just
360 * about all the time. I suppose this would be a good
361 * thing to turn on if the kernel is still flakey.
362 */
363 pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
364
365/*
366 * Finish up.
367 */
368 pmp->pm_ronly = ronly;
369 if (ronly == 0)
370 pmp->pm_fmod = 1;
371 mp->mnt_data = (qaddr_t)pmp;
372 mp->mnt_stat.f_fsid.val[0] = (long)dev;
373 mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS;
374 mp->mnt_flag |= MNT_LOCAL;
375#if defined(QUOTA)
376/*
377 * If we ever do quotas for DOS filesystems this would
378 * be a place to fill in the info in the pcfsmount
379 * structure.
380 * You dolt, quotas on dos filesystems make no sense
381 * because files have no owners on dos filesystems.
382 * of course there is some empty space in the directory
383 * entry where we could put uid's and gid's.
384 */
385#endif /* defined(QUOTA) */
386 devvp->v_specflags |= SI_MOUNTEDON;
387
388 return 0;
389
390error_exit:;
391 if (bp0)
392 brelse(bp0);
393 if (needclose)
394 (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE,
395 NOCRED, p);
396 if (pmp) {
397 if (pmp->pm_inusemap)
398 free((caddr_t)pmp->pm_inusemap, M_PCFSFAT);
399 free((caddr_t)pmp, M_PCFSMNT);
400 mp->mnt_data = (qaddr_t)0;
401 }
402 return error;
403}
404
405int
406pcfs_start(mp, flags, p)
407 struct mount *mp;
408 int flags;
409 struct proc *p;
410{
411 return 0;
412}
413
414/*
415 * Unmount the filesystem described by mp.
416 */
417int
418pcfs_unmount(mp, mntflags, p)
419 struct mount *mp;
420 int mntflags;
421 struct proc *p;
422{
423 int flags = 0;
424 int error;
425 struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
426 struct vnode *vp = pmp->pm_devvp;
427
428 if (mntflags & MNT_FORCE) {
429 if (!pcfsdoforce)
430 return EINVAL;
431 flags |= FORCECLOSE;
432 }
433 mntflushbuf(mp, 0);
434 if (mntinvalbuf(mp))
435 return EBUSY;
436#if defined(QUOTA)
437#endif /* defined(QUOTA) */
438 if (error = vflush(mp, NULLVP, flags))
439 return error;
440 pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
441#if defined(PCFSDEBUG)
442printf("pcfs_umount(): just before calling VOP_CLOSE()\n");
443printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
444 vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
445printf("lastr %d, id %d, mount %08x, op %08x\n",
446 vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op);
447printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n",
448 vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb);
449printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n",
450 vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type);
451printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n",
452 vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]);
453#endif /* defined(PCFSDEBUG) */
454 error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD|FWRITE,
455 NOCRED, p);
456 vrele(pmp->pm_devvp);
457 free((caddr_t)pmp->pm_inusemap, M_PCFSFAT);
458 free((caddr_t)pmp, M_PCFSMNT);
459 mp->mnt_data = (qaddr_t)0;
460 mp->mnt_flag &= ~MNT_LOCAL;
461 return error;
462}
463
464int
465pcfs_root(mp, vpp)
466 struct mount *mp;
467 struct vnode **vpp;
468{
469 struct denode *ndep;
470 struct pcfsmount *pmp = (struct pcfsmount *)(mp->mnt_data);
471 int error;
472
473 error = deget(pmp, PCFSROOT, PCFSROOT_OFS, NULL, &ndep);
474#if defined(PCFSDEBUG)
475printf("pcfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n",
476 mp, pmp, ndep, DETOV(ndep));
477#endif /* defined(PCFSDEBUG) */
478 if (error == 0)
479 *vpp = DETOV(ndep);
480 return error;
481}
482
483int
484pcfs_quotactl(mp, cmds, uid, arg, p)
485 struct mount *mp;
486 int cmds;
487 uid_t uid;
488 caddr_t arg;
489 struct proc *p;
490{
491#if defined(QUOTA)
492#else
493 return EOPNOTSUPP;
494#endif /* defined(QUOTA) */
495}
496
497int
498pcfs_statfs(mp, sbp, p)
499 struct mount *mp;
500 struct statfs *sbp;
501 struct proc *p;
502{
503 struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
504
505/*
506 * Fill in the stat block.
507 */
508 sbp->f_type = MOUNT_MSDOS;
509 sbp->f_fsize = pmp->pm_bpcluster;
510 sbp->f_bsize = pmp->pm_bpcluster;
511 sbp->f_blocks = pmp->pm_nmbrofclusters;
512 sbp->f_bfree = pmp->pm_freeclustercount;
513 sbp->f_bavail = pmp->pm_freeclustercount;
514 sbp->f_files = pmp->pm_RootDirEnts;
515 sbp->f_ffree = 0; /* what to put in here? */
516
517/*
518 * Copy the mounted on and mounted from names into
519 * the passed in stat block, if it is not the one
520 * in the mount structure.
521 */
522 if (sbp != &mp->mnt_stat) {
523 bcopy((caddr_t)mp->mnt_stat.f_mntonname,
524 (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
525 bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
526 (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
527 }
528 return 0;
529}
530
531int
532pcfs_sync(mp, waitfor)
533 struct mount *mp;
534 int waitfor;
535{
536 struct vnode *vp;
537 struct denode *dep;
538 struct pcfsmount *pmp;
539 int error;
540 int allerror = 0;
541
542 pmp = (struct pcfsmount *)mp->mnt_data;
543
544/*
545 * If we ever switch to not updating all of the fats
546 * all the time, this would be the place to update them
547 * from the first one.
548 */
549 if (pmp->pm_fmod) {
550 if (pmp->pm_ronly) {
551 printf("pcfs_sync(): writing to readonly filesystem\n");
552 return EINVAL;
553 } else {
554 /* update fats here */
555 }
556 }
557
558/*
559 * Go thru in memory denodes and write them out along
560 * with unwritten file blocks.
561 */
562loop:
563 for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) {
564 if (vp->v_mount != mp) /* not ours anymore */
565 goto loop;
566 if (VOP_ISLOCKED(vp)) /* file is busy */
567 continue;
568 dep = VTODE(vp);
569 if ((dep->de_flag & DEUPD) == 0 && vp->v_dirtyblkhd == NULL)
570 continue;
571 if (vget(vp)) /* not there anymore? */
572 goto loop;
573 if (vp->v_dirtyblkhd) /* flush dirty file blocks */
574 vflushbuf(vp, 0);
575 if ((dep->de_flag & DEUPD) &&
576 (error = deupdat(dep, &time, 0)))
577 allerror = error;
578 vput(vp); /* done with this one */
579 }
580
581/*
582 * Flush filesystem control info.
583 */
584 vflushbuf(pmp->pm_devvp, waitfor == MNT_WAIT ? B_SYNC : 0);
585 return allerror;
586}
587
588int
589pcfs_fhtovp (mp, fhp, vpp)
590 struct mount *mp;
591 struct fid *fhp;
592 struct vnode **vpp;
593{
594 struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
595 struct defid *defhp = (struct defid *)fhp;
596 struct denode *dep;
597 int error;
598
599 error = deget (pmp, defhp->defid_dirclust, defhp->defid_dirofs,
600 NULL, &dep);
601 if (error)
602 return (error);
603 *vpp = DETOV (dep);
604 return (0);
605}
606
607
608int
609pcfs_vptofh (vp, fhp)
610 struct vnode *vp;
611 struct fid *fhp;
612{
613 struct denode *dep = VTODE(vp);
614 struct defid *defhp = (struct defid *)fhp;
615
616 defhp->defid_len = sizeof(struct defid);
617 defhp->defid_dirclust = dep->de_dirclust;
618 defhp->defid_dirofs = dep->de_diroffset;
619/* defhp->defid_gen = ip->i_gen; */
620 return (0);
621}
622
623struct vfsops pcfs_vfsops = {
624 pcfs_mount,
625 pcfs_start,
626 pcfs_unmount,
627 pcfs_root,
628 pcfs_quotactl,
629 pcfs_statfs,
630 pcfs_sync,
631 pcfs_fhtovp,
632 pcfs_vptofh,
633 pcfs_init
634};