Don't allow others to run uuconv
[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 *
de8de1c4 18 * $Id: pcfs_vfsops.c,v 1.5 1993/12/19 02:07:58 ache Exp $
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 */
4c45483e 60 struct pcfsmount *pmp = 0; /* pcfs specific mount control block */
15637ed4
RG
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;
90df75b1 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
d553fb82 299#if defined(PCFSDEBUG)
15637ed4
RG
300 if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
301 printf("mountpcfs(): root directory is not a multiple of the clustersize in length\n");
d553fb82 302#endif
15637ed4
RG
303/*
304 * Compute mask and shift value for isolating cluster relative
305 * byte offsets and cluster numbers from a file offset.
306 */
307 bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
308 pmp->pm_bpcluster = bpc;
309 pmp->pm_depclust = bpc/sizeof(struct direntry);
310 pmp->pm_crbomask = bpc - 1;
311 if (bpc == 0) {
312 error = EINVAL;
313 goto error_exit;
314 }
315 bit = 1;
316 for (i = 0; i < 32; i++) {
317 if (bit & bpc) {
318 if (bit ^ bpc) {
319 error = EINVAL;
320 goto error_exit;
321 }
322 pmp->pm_cnshift = i;
323 break;
324 }
325 bit <<= 1;
326 }
327
328 pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */
329 pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */
330
331/*
332 * Release the bootsector buffer.
333 */
334 brelse(bp0);
335 bp0 = NULL;
336
337/*
338 * Allocate memory for the bitmap of allocated clusters,
339 * and then fill it in.
340 */
341 pmp->pm_inusemap = malloc((pmp->pm_maxcluster >> 3) + 1,
342 M_PCFSFAT, M_WAITOK);
343
344/*
345 * fillinusemap() needs pm_devvp.
346 */
347 pmp->pm_dev = dev;
348 pmp->pm_devvp = devvp;
349
350/*
351 * Have the inuse map filled in.
352 */
353 error = fillinusemap(pmp);
354 if (error)
355 goto error_exit;
356
357/*
358 * If they want fat updates to be synchronous then let
359 * them suffer the performance degradation in exchange
360 * for the on disk copy of the fat being correct just
361 * about all the time. I suppose this would be a good
362 * thing to turn on if the kernel is still flakey.
363 */
364 pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
365
366/*
367 * Finish up.
368 */
369 pmp->pm_ronly = ronly;
de8de1c4 370 pmp->pm_fmod = !ronly;
15637ed4
RG
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{
15637ed4 491 return EOPNOTSUPP;
15637ed4
RG
492}
493
494int
495pcfs_statfs(mp, sbp, p)
496 struct mount *mp;
497 struct statfs *sbp;
498 struct proc *p;
499{
500 struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
501
502/*
503 * Fill in the stat block.
504 */
505 sbp->f_type = MOUNT_MSDOS;
506 sbp->f_fsize = pmp->pm_bpcluster;
507 sbp->f_bsize = pmp->pm_bpcluster;
508 sbp->f_blocks = pmp->pm_nmbrofclusters;
509 sbp->f_bfree = pmp->pm_freeclustercount;
510 sbp->f_bavail = pmp->pm_freeclustercount;
511 sbp->f_files = pmp->pm_RootDirEnts;
512 sbp->f_ffree = 0; /* what to put in here? */
513
514/*
515 * Copy the mounted on and mounted from names into
516 * the passed in stat block, if it is not the one
517 * in the mount structure.
518 */
519 if (sbp != &mp->mnt_stat) {
520 bcopy((caddr_t)mp->mnt_stat.f_mntonname,
521 (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
522 bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
523 (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
524 }
525 return 0;
526}
527
528int
529pcfs_sync(mp, waitfor)
530 struct mount *mp;
531 int waitfor;
532{
533 struct vnode *vp;
534 struct denode *dep;
535 struct pcfsmount *pmp;
536 int error;
537 int allerror = 0;
538
539 pmp = (struct pcfsmount *)mp->mnt_data;
540
541/*
542 * If we ever switch to not updating all of the fats
543 * all the time, this would be the place to update them
544 * from the first one.
545 */
546 if (pmp->pm_fmod) {
547 if (pmp->pm_ronly) {
548 printf("pcfs_sync(): writing to readonly filesystem\n");
549 return EINVAL;
550 } else {
551 /* update fats here */
552 }
553 }
554
555/*
556 * Go thru in memory denodes and write them out along
557 * with unwritten file blocks.
558 */
559loop:
560 for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) {
561 if (vp->v_mount != mp) /* not ours anymore */
562 goto loop;
563 if (VOP_ISLOCKED(vp)) /* file is busy */
564 continue;
565 dep = VTODE(vp);
566 if ((dep->de_flag & DEUPD) == 0 && vp->v_dirtyblkhd == NULL)
567 continue;
568 if (vget(vp)) /* not there anymore? */
569 goto loop;
570 if (vp->v_dirtyblkhd) /* flush dirty file blocks */
571 vflushbuf(vp, 0);
572 if ((dep->de_flag & DEUPD) &&
573 (error = deupdat(dep, &time, 0)))
574 allerror = error;
575 vput(vp); /* done with this one */
576 }
577
578/*
579 * Flush filesystem control info.
580 */
581 vflushbuf(pmp->pm_devvp, waitfor == MNT_WAIT ? B_SYNC : 0);
582 return allerror;
583}
584
585int
586pcfs_fhtovp (mp, fhp, vpp)
587 struct mount *mp;
588 struct fid *fhp;
589 struct vnode **vpp;
590{
591 struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
592 struct defid *defhp = (struct defid *)fhp;
593 struct denode *dep;
594 int error;
595
596 error = deget (pmp, defhp->defid_dirclust, defhp->defid_dirofs,
597 NULL, &dep);
598 if (error)
599 return (error);
600 *vpp = DETOV (dep);
601 return (0);
602}
603
604
605int
606pcfs_vptofh (vp, fhp)
607 struct vnode *vp;
608 struct fid *fhp;
609{
610 struct denode *dep = VTODE(vp);
611 struct defid *defhp = (struct defid *)fhp;
612
613 defhp->defid_len = sizeof(struct defid);
614 defhp->defid_dirclust = dep->de_dirclust;
615 defhp->defid_dirofs = dep->de_diroffset;
616/* defhp->defid_gen = ip->i_gen; */
617 return (0);
618}
619
620struct vfsops pcfs_vfsops = {
621 pcfs_mount,
622 pcfs_start,
623 pcfs_unmount,
624 pcfs_root,
625 pcfs_quotactl,
626 pcfs_statfs,
627 pcfs_sync,
628 pcfs_fhtovp,
629 pcfs_vptofh,
630 pcfs_init
631};