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