* Written by Paul Popelka (paulp@uts.amdahl.com)
* You can do anything you want with this software,
* just don't say you wrote it,
* and don't remove this notice.
* This software is provided "as is".
* The author supplies this software to be publicly
* redistributed on the understanding that the author
* is not responsible for the correct functioning of
* this software in any circumstances and is not liable
* for any damages caused by this software.
#include "specdev.h" /* defines v_rdev */
int pcfsdoforce
= 0; /* 1 = force unmount */
* path - addr in user space of mount point (ie /usr or whatever)
* data - addr in user space of mount params including the
* name of the block special file to treat as a filesystem.
pcfs_mount(mp
, path
, data
, ndp
, p
)
struct vnode
*devvp
; /* vnode for blk device to mount */
struct pcfs_args args
; /* will hold data from mount request */
struct pcfsmount
*pmp
; /* pcfs specific mount control block */
* Copy in the args for the mount request.
if (error
= copyin(data
, (caddr_t
)&args
, sizeof(struct pcfs_args
)))
* Check to see if they want it to be an exportable
* filesystem via nfs. And, if they do, should it
* be read only, and what uid is root to be mapped
if ((args
.exflags
& MNT_EXPORTED
) || (mp
->mnt_flag
& MNT_EXPORTED
)) {
if (args
.exflags
& MNT_EXPORTED
)
mp
->mnt_flag
|= MNT_EXPORTED
;
mp
->mnt_flag
&= ~MNT_EXPORTED
;
if (args
.exflags
& MNT_EXRDONLY
)
mp
->mnt_flag
|= MNT_EXRDONLY
;
mp
->mnt_flag
&= ~MNT_EXRDONLY
;
mp
->mnt_exroot
= args
.exroot
;
* If they just want to update then be sure we can
* do what is asked. Can't change a filesystem from
* read/write to read only. Why?
* And if they've supplied a new device file name then we
* continue, otherwise return.
if (mp
->mnt_flag
& MNT_UPDATE
) {
pmp
= (struct pcfsmount
*)mp
->mnt_data
;
if (pmp
->pm_ronly
&& (mp
->mnt_flag
& MNT_RDONLY
) == 0)
* Now, lookup the name of the block device this
* mount or name update request is to apply to.
ndp
->ni_nameiop
= LOOKUP
| FOLLOW
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= args
.fspec
;
if (error
= namei(ndp
, p
))
* Be sure they've given us a block device to treat
* as a filesystem. And, that its major number is
* within the bdevsw table.
if (devvp
->v_type
!= VBLK
) {
vrele(devvp
); /* namei() acquires this? */
if (major(devvp
->v_rdev
) >= nblkdev
) {
* If this is an update, then make sure the vnode
* for the block special device is the same as the
* one our filesystem is in.
if (mp
->mnt_flag
& MNT_UPDATE
) {
if (devvp
!= pmp
->pm_devvp
)
* Well, it's not an update, it's a real mount request.
error
= mountpcfs(devvp
, mp
, p
);
* Copy in the name of the directory the filesystem
* Then copy in the name of the block special file
* representing the filesystem being mounted.
* And we clear the remainder of the character strings
* Then, we try to fill in the filesystem stats structure
* as best we can with whatever applies from a dos file
pmp
= (struct pcfsmount
*)mp
->mnt_data
;
copyinstr(path
, (caddr_t
)mp
->mnt_stat
.f_mntonname
,
sizeof(mp
->mnt_stat
.f_mntonname
)-1, &size
);
bzero(mp
->mnt_stat
.f_mntonname
+ size
,
sizeof(mp
->mnt_stat
.f_mntonname
) - size
);
copyinstr(args
.fspec
, mp
->mnt_stat
.f_mntfromname
, MNAMELEN
-1, &size
);
bzero(mp
->mnt_stat
.f_mntfromname
+ size
,
(void)pcfs_statfs(mp
, &mp
->mnt_stat
, p
);
printf("pcfs_mount(): mp %x, pmp %x, inusemap %x\n", mp
, pmp
, pmp
->pm_inusemap
);
#endif /* defined(PCFSDEBUG) */
int ronly
= (mp
->mnt_flag
& MNT_RDONLY
) != 0;
dev_t dev
= devvp
->v_rdev
;
struct pcfsmount
*pmp
= NULL
;
* Multiple mounts of the same block special file
* aren't allowed. Make sure no one else has the
* special file open. And flush any old buffers
* from this filesystem. Presumably this prevents
* us from running into buffers that are the wrong
* NOTE: mountedon() is a part of the ufs filesystem.
* If the ufs filesystem is not gen'ed into the system
* we will get an unresolved reference.
if (error
= mountedon(devvp
)) {
* Now open the block special file.
if (error
= VOP_OPEN(devvp
, ronly
? FREAD
: FREAD
|FWRITE
, NOCRED
, p
))
* Put this in when we support reading dos filesystems
* from partitioned harddisks.
if (VOP_IOCTL(devvp
, DIOCGPART
, &pcfspart
, FREAD
, NOCRED
, p
) == 0) {
#endif /* defined(HDSUPPORT) */
* Read the boot sector of the filesystem, and then
* check the boot signature. If not a dos boot sector
* then error out. We could also add some checking on
* the bsOemName field. So far I've seen the following
if (error
= bread(devvp
, 0, 512, NOCRED
, &bp0
))
bsp
= (union bootsector
*)bp0
->b_un
.b_addr
;
b33
= (struct byte_bpb33
*)bsp
->bs33
.bsBPB
;
b50
= (struct byte_bpb50
*)bsp
->bs50
.bsBPB
;
if (bsp
->bs50
.bsBootSectSig
!= BOOTSIG
) {
pmp
= malloc(sizeof *pmp
, M_PCFSMNT
, M_WAITOK
);
* Compute several useful quantities from the bpb in
* the bootsector. Copy in the dos 5 variant of the
* bpb then fix up the fields that are different between
pmp
->pm_BytesPerSec
= getushort(b50
->bpbBytesPerSec
);
pmp
->pm_SectPerClust
= b50
->bpbSecPerClust
;
pmp
->pm_ResSectors
= getushort(b50
->bpbResSectors
);
pmp
->pm_FATs
= b50
->bpbFATs
;
pmp
->pm_RootDirEnts
= getushort(b50
->bpbRootDirEnts
);
pmp
->pm_Sectors
= getushort(b50
->bpbSectors
);
pmp
->pm_Media
= b50
->bpbMedia
;
pmp
->pm_FATsecs
= getushort(b50
->bpbFATsecs
);
pmp
->pm_SecPerTrack
= getushort(b50
->bpbSecPerTrack
);
pmp
->pm_Heads
= getushort(b50
->bpbHeads
);
if (pmp
->pm_Sectors
== 0) {
pmp
->pm_HiddenSects
= getulong(b50
->bpbHiddenSecs
);
pmp
->pm_HugeSectors
= getulong(b50
->bpbHugeSectors
);
pmp
->pm_HiddenSects
= getushort(b33
->bpbHiddenSecs
);
pmp
->pm_HugeSectors
= pmp
->pm_Sectors
;
pmp
->pm_fatblk
= pmp
->pm_ResSectors
;
pmp
->pm_rootdirblk
= pmp
->pm_fatblk
+
(pmp
->pm_FATs
* pmp
->pm_FATsecs
);
pmp
->pm_rootdirsize
= (pmp
->pm_RootDirEnts
* sizeof(struct direntry
))
pmp
->pm_BytesPerSec
; /* in sectors */
pmp
->pm_firstcluster
= pmp
->pm_rootdirblk
+ pmp
->pm_rootdirsize
;
pmp
->pm_nmbrofclusters
= (pmp
->pm_HugeSectors
- pmp
->pm_firstcluster
) /
pmp
->pm_maxcluster
= pmp
->pm_nmbrofclusters
+ 1;
pmp
->pm_fatsize
= pmp
->pm_FATsecs
* pmp
->pm_BytesPerSec
;
/* This will usually be a floppy disk.
* This size makes sure that one fat entry will not be split
* across multiple blocks. */
pmp
->pm_fatblocksize
= 3 * pmp
->pm_BytesPerSec
;
/* This will usually be a hard disk.
* Reading or writing one block should be quite fast. */
pmp
->pm_fatblocksize
= MAXBSIZE
;
pmp
->pm_fatblocksec
= pmp
->pm_fatblocksize
/ pmp
->pm_BytesPerSec
;
if ((pmp
->pm_rootdirsize
% pmp
->pm_SectPerClust
) != 0)
printf("mountpcfs(): root directory is not a multiple of the clustersize in length\n");
* Compute mask and shift value for isolating cluster relative
* byte offsets and cluster numbers from a file offset.
bpc
= pmp
->pm_SectPerClust
* pmp
->pm_BytesPerSec
;
pmp
->pm_depclust
= bpc
/sizeof(struct direntry
);
pmp
->pm_crbomask
= bpc
- 1;
for (i
= 0; i
< 32; i
++) {
pmp
->pm_brbomask
= 0x01ff; /* 512 byte blocks only (so far) */
pmp
->pm_bnshift
= 9; /* shift right 9 bits to get bn */
* Release the bootsector buffer.
* Allocate memory for the bitmap of allocated clusters,
pmp
->pm_inusemap
= malloc((pmp
->pm_maxcluster
>> 3) + 1,
* fillinusemap() needs pm_devvp.
* Have the inuse map filled in.
error
= fillinusemap(pmp
);
* If they want fat updates to be synchronous then let
* them suffer the performance degradation in exchange
* for the on disk copy of the fat being correct just
* about all the time. I suppose this would be a good
* thing to turn on if the kernel is still flakey.
pmp
->pm_waitonfat
= mp
->mnt_flag
& MNT_SYNCHRONOUS
;
mp
->mnt_data
= (qaddr_t
)pmp
;
mp
->mnt_stat
.f_fsid
.val
[0] = (long)dev
;
mp
->mnt_stat
.f_fsid
.val
[1] = MOUNT_MSDOS
;
mp
->mnt_flag
|= MNT_LOCAL
;
* If we ever do quotas for DOS filesystems this would
* be a place to fill in the info in the pcfsmount
* You dolt, quotas on dos filesystems make no sense
* because files have no owners on dos filesystems.
* of course there is some empty space in the directory
* entry where we could put uid's and gid's.
#endif /* defined(QUOTA) */
devvp
->v_specflags
|= SI_MOUNTEDON
;
(void)VOP_CLOSE(devvp
, ronly
? FREAD
: FREAD
|FWRITE
,
free((caddr_t
)pmp
->pm_inusemap
, M_PCFSFAT
);
free((caddr_t
)pmp
, M_PCFSMNT
);
mp
->mnt_data
= (qaddr_t
)0;
* Unmount the filesystem described by mp.
pcfs_unmount(mp
, mntflags
, p
)
struct pcfsmount
*pmp
= (struct pcfsmount
*)mp
->mnt_data
;
struct vnode
*vp
= pmp
->pm_devvp
;
if (mntflags
& MNT_FORCE
) {
#endif /* defined(QUOTA) */
if (error
= vflush(mp
, NULLVP
, flags
))
pmp
->pm_devvp
->v_specflags
&= ~SI_MOUNTEDON
;
printf("pcfs_umount(): just before calling VOP_CLOSE()\n");
printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
vp
->v_flag
, vp
->v_usecount
, vp
->v_writecount
, vp
->v_holdcnt
);
printf("lastr %d, id %d, mount %08x, op %08x\n",
vp
->v_lastr
, vp
->v_id
, vp
->v_mount
, vp
->v_op
);
printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n",
vp
->v_freef
, vp
->v_freeb
, vp
->v_mountf
, vp
->v_mountb
);
printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n",
vp
->v_cleanblkhd
, vp
->v_dirtyblkhd
, vp
->v_numoutput
, vp
->v_type
);
printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n",
vp
->v_socket
, vp
->v_tag
, vp
->v_data
[0], vp
->v_data
[1]);
#endif /* defined(PCFSDEBUG) */
error
= VOP_CLOSE(pmp
->pm_devvp
, pmp
->pm_ronly
? FREAD
: FREAD
|FWRITE
,
free((caddr_t
)pmp
->pm_inusemap
, M_PCFSFAT
);
free((caddr_t
)pmp
, M_PCFSMNT
);
mp
->mnt_data
= (qaddr_t
)0;
mp
->mnt_flag
&= ~MNT_LOCAL
;
struct pcfsmount
*pmp
= (struct pcfsmount
*)(mp
->mnt_data
);
error
= deget(pmp
, PCFSROOT
, PCFSROOT_OFS
, NULL
, &ndep
);
printf("pcfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n",
mp
, pmp
, ndep
, DETOV(ndep
));
#endif /* defined(PCFSDEBUG) */
pcfs_quotactl(mp
, cmds
, uid
, arg
, p
)
#endif /* defined(QUOTA) */
struct pcfsmount
*pmp
= (struct pcfsmount
*)mp
->mnt_data
;
* Fill in the stat block.
sbp
->f_type
= MOUNT_MSDOS
;
sbp
->f_fsize
= pmp
->pm_bpcluster
;
sbp
->f_bsize
= pmp
->pm_bpcluster
;
sbp
->f_blocks
= pmp
->pm_nmbrofclusters
;
sbp
->f_bfree
= pmp
->pm_freeclustercount
;
sbp
->f_bavail
= pmp
->pm_freeclustercount
;
sbp
->f_files
= pmp
->pm_RootDirEnts
;
sbp
->f_ffree
= 0; /* what to put in here? */
* Copy the mounted on and mounted from names into
* the passed in stat block, if it is not the one
* in the mount structure.
if (sbp
!= &mp
->mnt_stat
) {
bcopy((caddr_t
)mp
->mnt_stat
.f_mntonname
,
(caddr_t
)&sbp
->f_mntonname
[0], MNAMELEN
);
bcopy((caddr_t
)mp
->mnt_stat
.f_mntfromname
,
(caddr_t
)&sbp
->f_mntfromname
[0], MNAMELEN
);
pmp
= (struct pcfsmount
*)mp
->mnt_data
;
* If we ever switch to not updating all of the fats
* all the time, this would be the place to update them
printf("pcfs_sync(): writing to readonly filesystem\n");
* Go thru in memory denodes and write them out along
* with unwritten file blocks.
for (vp
= mp
->mnt_mounth
; vp
; vp
= vp
->v_mountf
) {
if (vp
->v_mount
!= mp
) /* not ours anymore */
if (VOP_ISLOCKED(vp
)) /* file is busy */
if ((dep
->de_flag
& DEUPD
) == 0 && vp
->v_dirtyblkhd
== NULL
)
if (vget(vp
)) /* not there anymore? */
if (vp
->v_dirtyblkhd
) /* flush dirty file blocks */
if ((dep
->de_flag
& DEUPD
) &&
(error
= deupdat(dep
, &time
, 0)))
vput(vp
); /* done with this one */
* Flush filesystem control info.
vflushbuf(pmp
->pm_devvp
, waitfor
== MNT_WAIT
? B_SYNC
: 0);
pcfs_fhtovp (mp
, fhp
, vpp
)
struct pcfsmount
*pmp
= (struct pcfsmount
*)mp
->mnt_data
;
struct defid
*defhp
= (struct defid
*)fhp
;
error
= deget (pmp
, defhp
->defid_dirclust
, defhp
->defid_dirofs
,
struct denode
*dep
= VTODE(vp
);
struct defid
*defhp
= (struct defid
*)fhp
;
defhp
->defid_len
= sizeof(struct defid
);
defhp
->defid_dirclust
= dep
->de_dirclust
;
defhp
->defid_dirofs
= dep
->de_diroffset
;
/* defhp->defid_gen = ip->i_gen; */
struct vfsops pcfs_vfsops
= {