* Copyright (c) 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)ffs_vfsops.c 8.1 (Berkeley) 6/11/93
#include <sys/disklabel.h>
#include <miscfs/specfs/specdev.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ffs/ffs_extern.h>
int ffs_sbupdate
__P((struct ufsmount
*, int));
struct vfsops ufs_vfsops
= {
extern u_long nextgennumber
;
* Called by main() when ufs is going to be mounted as root.
* Name is updated by mount(8) after booting.
#define ROOTNAME "root_device"
extern struct vnode
*rootvp
;
register struct mount
*mp
;
struct proc
*p
= curproc
; /* XXX */
* Get vnodes for swapdev and rootdev.
if (bdevvp(swapdev
, &swapdev_vp
) || bdevvp(rootdev
, &rootvp
))
panic("ffs_mountroot: can't setup bdevvp's");
mp
= malloc((u_long
)sizeof(struct mount
), M_MOUNT
, M_WAITOK
);
bzero((char *)mp
, (u_long
)sizeof(struct mount
));
mp
->mnt_op
= &ufs_vfsops
;
mp
->mnt_flag
= MNT_RDONLY
;
if (error
= ffs_mountfs(rootvp
, mp
, p
)) {
if (error
= vfs_lock(mp
)) {
(void)ffs_unmount(mp
, 0, p
);
mp
->mnt_vnodecovered
= NULLVP
;
bzero(fs
->fs_fsmnt
, sizeof(fs
->fs_fsmnt
));
bcopy((caddr_t
)fs
->fs_fsmnt
, (caddr_t
)mp
->mnt_stat
.f_mntonname
,
(void) copystr(ROOTNAME
, mp
->mnt_stat
.f_mntfromname
, MNAMELEN
- 1,
bzero(mp
->mnt_stat
.f_mntfromname
+ size
, MNAMELEN
- size
);
(void)ffs_statfs(mp
, &mp
->mnt_stat
, p
);
ffs_mount(mp
, path
, data
, ndp
, p
)
register struct mount
*mp
;
if (error
= copyin(data
, (caddr_t
)&args
, sizeof (struct ufs_args
)))
* If updating, check whether changing from read-only to
* read/write; if there is no device name, that's all we do.
if (mp
->mnt_flag
& MNT_UPDATE
) {
if (fs
->fs_ronly
== 0 && (mp
->mnt_flag
& MNT_RDONLY
)) {
if (mp
->mnt_flag
& MNT_FORCE
)
error
= ffs_flushfiles(mp
, flags
, p
);
if (!error
&& (mp
->mnt_flag
& MNT_RELOAD
))
error
= ffs_reload(mp
, ndp
->ni_cnd
.cn_cred
, p
);
if (fs
->fs_ronly
&& (mp
->mnt_flag
& MNT_WANTRDWR
))
* Process export requests.
if (args
.exflags
& MNT_EXPORTED
) {
if (error
= ufs_hang_addrlist(mp
, &args
))
mp
->mnt_flag
|= MNT_EXPORTED
;
if (args
.exflags
& MNT_DELEXPORT
) {
~(MNT_EXPORTED
| MNT_DEFEXPORTED
);
* Not an update, or updating the name: look up the name
* and verify that it refers to a sensible block device.
NDINIT(ndp
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, args
.fspec
, p
);
if (devvp
->v_type
!= VBLK
) {
if (major(devvp
->v_rdev
) >= nblkdev
) {
if ((mp
->mnt_flag
& MNT_UPDATE
) == 0)
error
= ffs_mountfs(devvp
, mp
, p
);
if (devvp
!= ump
->um_devvp
)
error
= EINVAL
; /* needs translation */
(void) copyinstr(path
, fs
->fs_fsmnt
, sizeof(fs
->fs_fsmnt
) - 1, &size
);
bzero(fs
->fs_fsmnt
+ size
, sizeof(fs
->fs_fsmnt
) - size
);
bcopy((caddr_t
)fs
->fs_fsmnt
, (caddr_t
)mp
->mnt_stat
.f_mntonname
,
(void) copyinstr(args
.fspec
, mp
->mnt_stat
.f_mntfromname
, MNAMELEN
- 1,
bzero(mp
->mnt_stat
.f_mntfromname
+ size
, MNAMELEN
- size
);
(void)ffs_statfs(mp
, &mp
->mnt_stat
, p
);
* Reload all incore data for a filesystem (used after running fsck on
* the root filesystem and finding things to fix). The filesystem must
* Things to do to update the mount:
* 1) invalidate all cached meta-data.
* 2) re-read superblock from disk.
* 3) re-read summary information from disk.
* 4) invalidate all inactive vnodes.
* 5) invalidate all cached file data.
* 6) re-read inode data for all active vnodes.
ffs_reload(mountp
, cred
, p
)
register struct mount
*mountp
;
register struct vnode
*vp
, *nvp
, *devvp
;
int i
, blks
, size
, error
;
if ((mountp
->mnt_flag
& MNT_RDONLY
) == 0)
* Step 1: invalidate all cached meta-data.
devvp
= VFSTOUFS(mountp
)->um_devvp
;
if (vinvalbuf(devvp
, 0, cred
, p
, 0, 0))
panic("ffs_reload: dirty1");
* Step 2: re-read superblock from disk.
if (error
= bread(devvp
, SBLOCK
, SBSIZE
, NOCRED
, &bp
))
if (fs
->fs_magic
!= FS_MAGIC
|| fs
->fs_bsize
> MAXBSIZE
||
fs
->fs_bsize
< sizeof(struct fs
)) {
return (EIO
); /* XXX needs translation */
fs
= VFSTOUFS(mountp
)->um_fs
;
bcopy((caddr_t
)&fs
->fs_csp
[0], (caddr_t
)&bp
->b_un
.b_fs
->fs_csp
[0],
bcopy((caddr_t
)bp
->b_un
.b_addr
, (caddr_t
)fs
, (u_int
)fs
->fs_sbsize
);
if (fs
->fs_sbsize
< SBSIZE
)
* Step 3: re-read summary information from disk.
blks
= howmany(fs
->fs_cssize
, fs
->fs_fsize
);
for (i
= 0; i
< blks
; i
+= fs
->fs_frag
) {
if (i
+ fs
->fs_frag
> blks
)
size
= (blks
- i
) * fs
->fs_fsize
;
if (error
= bread(devvp
, fsbtodb(fs
, fs
->fs_csaddr
+ i
), size
,
bcopy((caddr_t
)bp
->b_un
.b_addr
, fs
->fs_csp
[fragstoblks(fs
, i
)],
for (vp
= mountp
->mnt_mounth
; vp
; vp
= nvp
) {
* Step 4: invalidate all inactive vnodes.
if (vp
->v_usecount
== 0) {
* Step 5: invalidate all cached file data.
if (vinvalbuf(vp
, 0, cred
, p
, 0, 0))
panic("ffs_reload: dirty2");
* Step 6: re-read inode data for all active vnodes.
if (error
= bread(devvp
, fsbtodb(fs
, itod(fs
, ip
->i_number
)),
(int)fs
->fs_bsize
, NOCRED
, &bp
)) {
dp
+= itoo(fs
, ip
->i_number
);
if (vp
->v_mount
!= mountp
)
* Common code for mount and mountroot
ffs_mountfs(devvp
, mp
, p
)
register struct vnode
*devvp
;
register struct ufsmount
*ump
;
dev_t dev
= devvp
->v_rdev
;
extern struct vnode
*rootvp
;
* Disallow multiple mounts of the same device.
* Disallow mounting of a device that is currently in use
* (except for root, which might share swap device for miniroot).
* Flush out any old buffers remaining from a previous use.
if (error
= ufs_mountedon(devvp
))
if (vcount(devvp
) > 1 && devvp
!= rootvp
)
if (error
= vinvalbuf(devvp
, V_SAVE
, p
->p_ucred
, p
, 0, 0))
ronly
= (mp
->mnt_flag
& MNT_RDONLY
) != 0;
if (error
= VOP_OPEN(devvp
, ronly
? FREAD
: FREAD
|FWRITE
, FSCRED
, p
))
if (VOP_IOCTL(devvp
, DIOCGPART
, (caddr_t
)&dpart
, FREAD
, NOCRED
, p
) != 0)
size
= dpart
.disklab
->d_secsize
;
if (error
= bread(devvp
, SBLOCK
, SBSIZE
, NOCRED
, &bp
))
if (fs
->fs_magic
!= FS_MAGIC
|| fs
->fs_bsize
> MAXBSIZE
||
fs
->fs_bsize
< sizeof(struct fs
)) {
error
= EINVAL
; /* XXX needs translation */
ump
= malloc(sizeof *ump
, M_UFSMNT
, M_WAITOK
);
bzero((caddr_t
)ump
, sizeof *ump
);
ump
->um_fs
= malloc((u_long
)fs
->fs_sbsize
, M_UFSMNT
,
bcopy((caddr_t
)bp
->b_un
.b_addr
, (caddr_t
)ump
->um_fs
,
if (fs
->fs_sbsize
< SBSIZE
)
dpart
.part
->p_fstype
= FS_BSDFFS
;
dpart
.part
->p_fsize
= fs
->fs_fsize
;
dpart
.part
->p_frag
= fs
->fs_frag
;
dpart
.part
->p_cpg
= fs
->fs_cpg
;
blks
= howmany(fs
->fs_cssize
, fs
->fs_fsize
);
base
= space
= malloc((u_long
)fs
->fs_cssize
, M_UFSMNT
,
for (i
= 0; i
< blks
; i
+= fs
->fs_frag
) {
if (i
+ fs
->fs_frag
> blks
)
size
= (blks
- i
) * fs
->fs_fsize
;
error
= bread(devvp
, fsbtodb(fs
, fs
->fs_csaddr
+ i
), size
,
bcopy((caddr_t
)bp
->b_un
.b_addr
, space
, (u_int
)size
);
fs
->fs_csp
[fragstoblks(fs
, i
)] = (struct csum
*)space
;
mp
->mnt_data
= (qaddr_t
)ump
;
mp
->mnt_stat
.f_fsid
.val
[0] = (long)dev
;
mp
->mnt_stat
.f_fsid
.val
[1] = MOUNT_UFS
;
mp
->mnt_maxsymlinklen
= fs
->fs_maxsymlinklen
;
mp
->mnt_flag
|= MNT_LOCAL
;
ump
->um_nindir
= fs
->fs_nindir
;
ump
->um_bptrtodb
= fs
->fs_fsbtodb
;
ump
->um_seqinc
= fs
->fs_frag
;
for (i
= 0; i
< MAXQUOTAS
; i
++)
ump
->um_quotas
[i
] = NULLVP
;
devvp
->v_specflags
|= SI_MOUNTEDON
;
(void)VOP_CLOSE(devvp
, ronly
? FREAD
: FREAD
|FWRITE
, NOCRED
, p
);
free(ump
->um_fs
, M_UFSMNT
);
mp
->mnt_data
= (qaddr_t
)0;
* Sanity checks for old file systems.
* XXX - goes away some day.
fs
->fs_npsect
= max(fs
->fs_npsect
, fs
->fs_nsect
); /* XXX */
fs
->fs_interleave
= max(fs
->fs_interleave
, 1); /* XXX */
if (fs
->fs_postblformat
== FS_42POSTBLFMT
) /* XXX */
fs
->fs_nrpos
= 8; /* XXX */
if (fs
->fs_inodefmt
< FS_44INODEFMT
) { /* XXX */
quad_t sizepb
= fs
->fs_bsize
; /* XXX */
fs
->fs_maxfilesize
= fs
->fs_bsize
* NDADDR
- 1; /* XXX */
for (i
= 0; i
< NIADDR
; i
++) { /* XXX */
sizepb
*= NINDIR(fs
); /* XXX */
fs
->fs_maxfilesize
+= sizepb
; /* XXX */
fs
->fs_qbmask
= ~fs
->fs_bmask
; /* XXX */
fs
->fs_qfmask
= ~fs
->fs_fmask
; /* XXX */
ffs_unmount(mp
, mntflags
, p
)
register struct ufsmount
*ump
;
if (mntflags
& MNT_FORCE
) {
if (error
= ffs_flushfiles(mp
, flags
, p
))
ump
->um_devvp
->v_specflags
&= ~SI_MOUNTEDON
;
error
= VOP_CLOSE(ump
->um_devvp
, ronly
? FREAD
: FREAD
|FWRITE
,
free(fs
->fs_csp
[0], M_UFSMNT
);
mp
->mnt_data
= (qaddr_t
)0;
mp
->mnt_flag
&= ~MNT_LOCAL
;
* Flush out all the files in a filesystem.
ffs_flushfiles(mp
, flags
, p
)
register struct mount
*mp
;
register struct ufsmount
*ump
;
if (mp
->mnt_flag
& MNT_QUOTA
) {
if (error
= vflush(mp
, NULLVP
, SKIPSYSTEM
|flags
))
for (i
= 0; i
< MAXQUOTAS
; i
++) {
if (ump
->um_quotas
[i
] == NULLVP
)
* Here we fall through to vflush again to ensure
* that we have gotten rid of all the system vnodes.
error
= vflush(mp
, NULLVP
, flags
);
* Return root of a filesystem
if (error
= VFS_VGET(mp
, (ino_t
)ROOTINO
, &nvp
))
* Get file system statistics.
register struct statfs
*sbp
;
register struct ufsmount
*ump
;
if (fs
->fs_magic
!= FS_MAGIC
)
sbp
->f_bsize
= fs
->fs_fsize
;
sbp
->f_iosize
= fs
->fs_bsize
;
sbp
->f_blocks
= fs
->fs_dsize
;
sbp
->f_bfree
= fs
->fs_cstotal
.cs_nbfree
* fs
->fs_frag
+
fs
->fs_cstotal
.cs_nffree
;
sbp
->f_bavail
= (fs
->fs_dsize
* (100 - fs
->fs_minfree
) / 100) -
(fs
->fs_dsize
- sbp
->f_bfree
);
sbp
->f_files
= fs
->fs_ncg
* fs
->fs_ipg
- ROOTINO
;
sbp
->f_ffree
= fs
->fs_cstotal
.cs_nifree
;
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
);
* Go through the disk queues to initiate sandbagged IO;
* go through the inodes to write those that have been modified;
* initiate the writing of the super block if it has been modified.
* Note: we are always called with the filesystem marked `MPBUSY'.
ffs_sync(mp
, waitfor
, cred
, p
)
register struct vnode
*vp
;
register struct inode
*ip
;
register struct ufsmount
*ump
= VFSTOUFS(mp
);
* Write back modified superblock.
* Consistency check that the superblock
* is still in the buffer cache.
if (fs
->fs_ronly
!= 0) { /* XXX */
printf("fs = %s\n", fs
->fs_fsmnt
);
panic("update: rofs mod");
fs
->fs_time
= time
.tv_sec
;
allerror
= ffs_sbupdate(ump
, waitfor
);
* Write back each (modified) inode.
for (vp
= mp
->mnt_mounth
; vp
; vp
= vp
->v_mountf
) {
* If the vnode that we are about to sync is no longer
* associated with this mount point, start over.
if ((ip
->i_flag
& (IMOD
|IACC
|IUPD
|ICHG
)) == 0 &&
vp
->v_dirtyblkhd
.le_next
== NULL
)
if (error
= VOP_FSYNC(vp
, cred
, waitfor
, p
))
* Force stale file system control information to be flushed.
if (error
= VOP_FSYNC(ump
->um_devvp
, cred
, waitfor
, p
))
* Look up a FFS dinode number to find its incore vnode.
* If it is not in core, read it in from the specified device.
* If it is in core, wait for the lock bit to clear, then
* return the inode locked. Detection and handling of mount
* points must be done by the calling routine.
register struct inode
*ip
;
if ((*vpp
= ufs_ihashget(dev
, ino
)) != NULL
)
/* Allocate a new vnode/inode. */
if (error
= getnewvnode(VT_UFS
, mp
, ffs_vnodeop_p
, &vp
)) {
type
= ump
->um_devvp
->v_tag
== VT_MFS
? M_MFSNODE
: M_FFSNODE
; /* XXX */
MALLOC(ip
, struct inode
*, sizeof(struct inode
), type
, M_WAITOK
);
bzero((caddr_t
)ip
, sizeof(struct inode
));
ip
->i_fs
= fs
= ump
->um_fs
;
for (i
= 0; i
< MAXQUOTAS
; i
++)
ip
->i_dquot
[i
] = NODQUOT
;
* Put it onto its hash chain and lock it so that other requests for
* this inode will block if they arrive while we are sleeping waiting
* for old data structures to be purged or for the contents of the
* disk portion of this inode to be read.
/* Read in the disk contents for the inode, copy into the inode. */
if (error
= bread(ump
->um_devvp
, fsbtodb(fs
, itod(fs
, ino
)),
(int)fs
->fs_bsize
, NOCRED
, &bp
)) {
* The inode does not contain anything useful, so it would
* be misleading to leave it on its hash chain. With mode
* still zero, it will be unlinked and returned to the free
* Initialize the vnode from the inode, check for aliases.
* Note that the underlying vnode may have changed.
if (error
= ufs_vinit(mp
, ffs_specop_p
, FFS_FIFOOPS
, &vp
)) {
* Finish inode initialization now that aliasing has been resolved.
ip
->i_devvp
= ump
->um_devvp
;
* Set up a generation number for this inode if it does not
* already have one. This should only happen on old filesystems.
if (++nextgennumber
< (u_long
)time
.tv_sec
)
nextgennumber
= time
.tv_sec
;
ip
->i_gen
= nextgennumber
;
if ((vp
->v_mount
->mnt_flag
& MNT_RDONLY
) == 0)
* Ensure that uid and gid are correct. This is a temporary
* fix until fsck has been changed to do the update.
if (fs
->fs_inodefmt
< FS_44INODEFMT
) { /* XXX */
ip
->i_uid
= ip
->i_din
.di_ouid
; /* XXX */
ip
->i_gid
= ip
->i_din
.di_ogid
; /* XXX */
* Have to be really careful about stale file handles:
* - check that the inode number is valid
* - call ffs_vget() to get the locked inode
* - check for an unallocated inode (i_mode == 0)
* - check that the given client host has export rights and return
* those rights via. exflagsp and credanonp
ffs_fhtovp(mp
, fhp
, nam
, vpp
, exflagsp
, credanonp
)
register struct mount
*mp
;
struct ucred
**credanonp
;
register struct ufid
*ufhp
;
ufhp
= (struct ufid
*)fhp
;
fs
= VFSTOUFS(mp
)->um_fs
;
if (ufhp
->ufid_ino
< ROOTINO
||
ufhp
->ufid_ino
>= fs
->fs_ncg
* fs
->fs_ipg
)
return (ufs_check_export(mp
, ufhp
, nam
, vpp
, exflagsp
, credanonp
));
* Vnode pointer to File handle
register struct inode
*ip
;
register struct ufid
*ufhp
;
ufhp
= (struct ufid
*)fhp
;
ufhp
->ufid_len
= sizeof(struct ufid
);
ufhp
->ufid_ino
= ip
->i_number
;
ufhp
->ufid_gen
= ip
->i_gen
;
* Write a superblock and associated information back to disk.
ffs_sbupdate(mp
, waitfor
)
register struct fs
*fs
= mp
->um_fs
;
bp
= getblk(mp
->um_devvp
, SBLOCK
, (int)fs
->fs_sbsize
, 0, 0);
bcopy((caddr_t
)fs
, bp
->b_un
.b_addr
, (u_int
)fs
->fs_sbsize
);
/* Restore compatibility to old file systems. XXX */
if (fs
->fs_postblformat
== FS_42POSTBLFMT
) /* XXX */
bp
->b_un
.b_fs
->fs_nrpos
= -1; /* XXX */
blks
= howmany(fs
->fs_cssize
, fs
->fs_fsize
);
space
= (caddr_t
)fs
->fs_csp
[0];
for (i
= 0; i
< blks
; i
+= fs
->fs_frag
) {
if (i
+ fs
->fs_frag
> blks
)
size
= (blks
- i
) * fs
->fs_fsize
;
bp
= getblk(mp
->um_devvp
, fsbtodb(fs
, fs
->fs_csaddr
+ i
),
bcopy(space
, bp
->b_un
.b_addr
, (u_int
)size
);