#include <rev_endian_fs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kauth.h>
#include <sys/kernel.h>
#include <sys/vnode_internal.h>
#include <sys/socket.h>
#include <sys/mount_internal.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/file.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/ubc.h>
#include <sys/quota.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/fs.h>
#include <ufs/ffs/ffs_extern.h>
#if REV_ENDIAN_FS
#include <ufs/ufs/ufs_byte_order.h>
#include <libkern/OSByteOrder.h>
#endif
int ffs_sbupdate(struct ufsmount *, int);
struct vfsops ufs_vfsops = {
ffs_mount,
ufs_start,
ffs_unmount,
ufs_root,
ufs_quotactl,
ffs_vfs_getattr,
ffs_sync,
ffs_vget,
ffs_fhtovp,
ffs_vptofh,
ffs_init,
ffs_sysctl,
ffs_vfs_setattr,
{0}
};
extern u_long nextgennumber;
union _qcvt {
int64_t qcvt;
int32_t val[2];
};
#define SETHIGH(q, h) { \
union _qcvt tmp; \
tmp.qcvt = (q); \
tmp.val[_QUAD_HIGHWORD] = (h); \
(q) = tmp.qcvt; \
}
#define SETLOW(q, l) { \
union _qcvt tmp; \
tmp.qcvt = (q); \
tmp.val[_QUAD_LOWWORD] = (l); \
(q) = tmp.qcvt; \
}
int
ffs_mountroot(mount_t mp, vnode_t rvp, vfs_context_t context)
{
struct proc *p = current_proc();
int error;
vfs_setflags(mp, MNT_ASYNC);
if (error = ffs_mountfs(rvp, mp, context))
return (error);
(void)ffs_statfs(mp, vfs_statfs(mp), NULL);
return (0);
}
int
ffs_mount(struct mount *mp, vnode_t devvp, __unused user_addr_t data, vfs_context_t context)
{
struct proc *p = vfs_context_proc(context);
struct ufsmount *ump;
register struct fs *fs;
u_int size;
int error = 0, flags;
mode_t accessmode;
int ronly;
int reload = 0;
if (mp->mnt_flag & MNT_UPDATE) {
ump = VFSTOUFS(mp);
fs = ump->um_fs;
if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
VFS_SYNC(mp, MNT_WAIT, context);
flags = WRITECLOSE;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
if (error = ffs_flushfiles(mp, flags, p))
return (error);
fs->fs_clean = 1;
fs->fs_ronly = 1;
if (error = ffs_sbupdate(ump, MNT_WAIT)) {
fs->fs_clean = 0;
fs->fs_ronly = 0;
return (error);
}
}
ronly = fs->fs_ronly;
if ((mp->mnt_flag & MNT_RELOAD) || ronly)
reload = 1;
if ((reload) &&
(error = ffs_reload(mp, vfs_context_ucred(context), p)))
return (error);
fs->fs_ronly = ronly;
if (!fs->fs_clean && ronly) {
printf("WARNING: trying to mount a dirty file system\n");
if (issingleuser() && (mp->mnt_flag & MNT_ROOTFS)) {
printf("WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n",fs->fs_fsmnt);
fs->fs_ronly = 1;
return(EPERM);
}
}
if (ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
fs->fs_ronly = 0;
fs->fs_clean = 0;
(void) ffs_sbupdate(ump, MNT_WAIT);
}
if (devvp == 0) {
return(0);
}
}
if ((mp->mnt_flag & MNT_UPDATE) == 0)
error = ffs_mountfs(devvp, mp, context);
else {
if (devvp != ump->um_devvp)
error = EINVAL;
}
if (error) {
return (error);
}
ump = VFSTOUFS(mp);
fs = ump->um_fs;
bzero(fs->fs_fsmnt , sizeof(fs->fs_fsmnt));
strncpy(fs->fs_fsmnt, (caddr_t)mp->mnt_vfsstat.f_mntonname, sizeof(fs->fs_fsmnt) - 1);
(void)ffs_statfs(mp, &mp->mnt_vfsstat, p);
return (0);
}
struct ffs_reload_cargs {
struct vnode *devvp;
kauth_cred_t cred;
struct fs *fs;
struct proc *p;
int error;
#if REV_ENDIAN_FS
int rev_endian;
#endif
};
static int
ffs_reload_callback(struct vnode *vp, void *cargs)
{
struct inode *ip;
struct buf *bp;
struct fs *fs;
struct ffs_reload_cargs *args;
args = (struct ffs_reload_cargs *)cargs;
if (buf_invalidateblks(vp, 0, 0, 0))
panic("ffs_reload: dirty2");
ip = VTOI(vp);
fs = args->fs;
if (args->error = (int)buf_bread(args->devvp, (daddr64_t)((unsigned)fsbtodb(fs, ino_to_fsba(fs, ip->i_number))),
(int)fs->fs_bsize, NOCRED, &bp)) {
buf_brelse(bp);
return (VNODE_RETURNED_DONE);
}
#if REV_ENDIAN_FS
if (args->rev_endian) {
byte_swap_inode_in(((struct dinode *)buf_dataptr(bp) +
ino_to_fsbo(fs, ip->i_number)), ip);
} else {
#endif
ip->i_din = *((struct dinode *)buf_dataptr(bp) +
ino_to_fsbo(fs, ip->i_number));
#if REV_ENDIAN_FS
}
#endif
buf_brelse(bp);
return (VNODE_RETURNED);
}
ffs_reload(struct mount *mountp, kauth_cred_t cred, struct proc *p)
{
register struct vnode *devvp;
void *space;
struct buf *bp;
struct fs *fs, *newfs;
int i, blks, size, error;
u_int64_t maxfilesize;
int32_t *lp;
struct ffs_reload_cargs args;
#if REV_ENDIAN_FS
int rev_endian = (mountp->mnt_flag & MNT_REVEND);
#endif
if ((mountp->mnt_flag & MNT_RDONLY) == 0)
return (EINVAL);
devvp = VFSTOUFS(mountp)->um_devvp;
if (buf_invalidateblks(devvp, 0, 0, 0))
panic("ffs_reload: dirty1");
size = vfs_devblocksize(mountp);
if (error = (int)buf_bread(devvp, (daddr64_t)((unsigned)(SBOFF/size)), SBSIZE, NOCRED,&bp)) {
buf_brelse(bp);
return (error);
}
newfs = (struct fs *)buf_dataptr(bp);
#if REV_ENDIAN_FS
if (rev_endian) {
error = byte_swap_sbin(newfs);
if (error) {
buf_brelse(bp);
return (error);
}
}
#endif
if (newfs->fs_magic != FS_MAGIC || newfs->fs_bsize > MAXBSIZE ||
newfs->fs_bsize < sizeof(struct fs)) {
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_sbout(newfs);
#endif
buf_brelse(bp);
return (EIO);
}
fs = VFSTOUFS(mountp)->um_fs;
newfs->fs_csp = fs->fs_csp;
newfs->fs_maxcluster = fs->fs_maxcluster;
newfs->fs_contigdirs = fs->fs_contigdirs;
bcopy(newfs, fs, (u_int)fs->fs_sbsize);
if (fs->fs_sbsize < SBSIZE)
buf_markinvalid(bp);
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_sbout(newfs);
#endif
buf_brelse(bp);
mountp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
ffs_oldfscompat(fs);
maxfilesize = 0x100000000ULL;
if (fs->fs_maxfilesize > maxfilesize)
fs->fs_maxfilesize = maxfilesize;
blks = howmany(fs->fs_cssize, fs->fs_fsize);
space = fs->fs_csp;
for (i = 0; i < blks; i += fs->fs_frag) {
size = fs->fs_bsize;
if (i + fs->fs_frag > blks)
size = (blks - i) * fs->fs_fsize;
if (error = (int)buf_bread(devvp, (daddr64_t)((unsigned)fsbtodb(fs, fs->fs_csaddr + i)), size,
NOCRED, &bp)) {
buf_brelse(bp);
return (error);
}
#if REV_ENDIAN_FS
if (rev_endian) {
byte_swap_ints((int *)buf_dataptr(bp), size / sizeof(int));
}
#endif
bcopy((char *)buf_dataptr(bp), space, (u_int)size);
#if REV_ENDIAN_FS
if (rev_endian) {
byte_swap_ints((int *)buf_dataptr(bp), size / sizeof(int));
}
#endif
space = (char *) space + size;
buf_brelse(bp);
}
if (fs->fs_contigsumsize > 0) {
lp = fs->fs_maxcluster;
for (i = 0; i < fs->fs_ncg; i++)
*lp++ = fs->fs_contigsumsize;
}
#if REV_ENDIAN_FS
args.rev_endian = rev_endian;
#endif
args.devvp = devvp;
args.cred = cred;
args.fs = fs;
args.p = p;
args.error = 0;
vnode_iterate(mountp, VNODE_RELOAD | VNODE_WAIT, ffs_reload_callback, (void *)&args);
return (args.error);
}
int
ffs_mountfs(devvp, mp, context)
struct vnode *devvp;
struct mount *mp;
vfs_context_t context;
{
struct ufsmount *ump;
struct buf *bp;
struct fs *fs;
dev_t dev;
struct buf *cgbp;
struct cg *cgp;
int32_t clustersumoff;
void *space;
int error, i, blks, ronly;
u_int32_t size;
int32_t *lp;
kauth_cred_t cred;
u_int64_t maxfilesize;
u_int dbsize = DEV_BSIZE;
#if REV_ENDIAN_FS
int rev_endian=0;
#endif
dev = devvp->v_rdev;
cred = vfs_context_ucred(context);
ronly = vfs_isrdonly(mp);
bp = NULL;
ump = NULL;
vfs_setlocklocal(mp);
if (VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&size, 0, context)) {
error = ENXIO;
goto out;
}
if (error = (int)buf_bread(devvp, (daddr64_t)((unsigned)(SBOFF/size)),
SBSIZE, cred, &bp))
goto out;
fs = (struct fs *)buf_dataptr(bp);
#if REV_ENDIAN_FS
if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
fs->fs_bsize < sizeof(struct fs)) {
int magic = fs->fs_magic;
byte_swap_ints(&magic, 1);
if (magic != FS_MAGIC) {
error = EINVAL;
goto out;
}
if (error = byte_swap_sbin(fs))
goto out;
if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
fs->fs_bsize < sizeof(struct fs)) {
byte_swap_sbout(fs);
error = EINVAL;
goto out;
}
rev_endian=1;
}
#endif
if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
fs->fs_bsize < sizeof(struct fs)) {
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_sbout(fs);
#endif
error = EINVAL;
goto out;
}
if (fs->fs_sbsize < 0 || fs->fs_sbsize > SBSIZE) {
error = EINVAL;
goto out;
}
if ((fs->fs_bsize > PAGE_SIZE) || (fs->fs_fsize < DIRBLKSIZ) ||
((!(mp->mnt_flag & MNT_ROOTFS)) && (!fs->fs_clean))) {
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_sbout(fs);
#endif
error = ENOTSUP;
goto out;
}
dbsize = fs->fs_fsize / NSPF(fs);
if(dbsize <= 0 ) {
kprintf("device blocksize computaion failed\n");
} else {
if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&dbsize,
FWRITE, context) != 0) {
kprintf("failed to set device blocksize\n");
}
set_fsblocksize(devvp);
}
if (fs->fs_postblformat == FS_42POSTBLFMT && !ronly) {
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_sbout(fs);
#endif
error = EROFS;
goto out;
}
if (ronly == 0){
if (error = (int)buf_bread (devvp, (daddr64_t)((unsigned)fsbtodb(fs, cgtod(fs, 0))),
(int)fs->fs_cgsize, NOCRED, &cgbp)) {
buf_brelse(cgbp);
goto out;
}
cgp = (struct cg *)buf_dataptr(cgbp);
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_cgin(cgp,fs);
#endif
if (!cg_chkmagic(cgp)){
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_cgout(cgp,fs);
#endif
buf_brelse(cgbp);
goto out;
}
if (cgp->cg_clustersumoff != 0) {
clustersumoff = cgp->cg_freeoff +
howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY);
clustersumoff = roundup(clustersumoff, sizeof(long));
if (cgp->cg_clustersumoff < clustersumoff) {
mp->mnt_flag |= MNT_RDONLY;
ronly = 1;
}
}
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_cgout(cgp,fs);
#endif
buf_brelse(cgbp);
}
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,
M_WAITOK);
bcopy((char *)buf_dataptr(bp), ump->um_fs, (u_int)fs->fs_sbsize);
if (fs->fs_sbsize < SBSIZE)
buf_markinvalid(bp);
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_sbout(fs);
#endif
buf_brelse(bp);
bp = NULL;
fs = ump->um_fs;
fs->fs_ronly = ronly;
if (fs->fs_cssize < 1 || fs->fs_fsize < 1 || fs->fs_ncg < 1) {
error = EINVAL;
goto out;
}
if (fs->fs_frag < 1 || fs->fs_frag > MAXFRAG) {
error = EINVAL;
goto out;
}
size = fs->fs_cssize;
blks = howmany(size, fs->fs_fsize);
if (fs->fs_contigsumsize > 0) {
if (fs->fs_ncg > INT_MAX / sizeof(int32_t) || size > INT_MAX - fs->fs_ncg * sizeof(int32_t)) {
error = EINVAL;
goto out;
}
size += fs->fs_ncg * sizeof(int32_t);
}
if (fs->fs_ncg > INT_MAX / sizeof(u_int8_t) || size > INT_MAX - fs->fs_ncg * sizeof(u_int8_t)) {
error = EINVAL;
goto out;
}
size += fs->fs_ncg * sizeof(u_int8_t);
space = _MALLOC((u_long)size, M_UFSMNT, M_WAITOK);
fs->fs_csp = space;
for (i = 0; i < blks; i += fs->fs_frag) {
size = fs->fs_bsize;
if (i + fs->fs_frag > blks)
size = (blks - i) * fs->fs_fsize;
if (error = (int)buf_bread(devvp, (daddr64_t)((unsigned)fsbtodb(fs, fs->fs_csaddr + i)),
size, cred, &bp)) {
_FREE(fs->fs_csp, M_UFSMNT);
goto out;
}
bcopy((char *)buf_dataptr(bp), space, (u_int)size);
#if REV_ENDIAN_FS
if (rev_endian)
byte_swap_ints((int *) space, size / sizeof(int));
#endif
space = (char *)space + size;
buf_brelse(bp);
bp = NULL;
}
if (fs->fs_contigsumsize > 0) {
fs->fs_maxcluster = lp = space;
for (i = 0; i < fs->fs_ncg; i++)
*lp++ = fs->fs_contigsumsize;
space = lp;
}
size = fs->fs_ncg * sizeof(u_int8_t);
fs->fs_contigdirs = (u_int8_t *)space;
space = (u_int8_t *)space + size;
bzero(fs->fs_contigdirs, size);
if (fs->fs_avgfilesize <= 0)
fs->fs_avgfilesize = AVFILESIZ;
if (fs->fs_avgfpdir <= 0)
fs->fs_avgfpdir = AFPDIR;
mp->mnt_data = (qaddr_t)ump;
mp->mnt_vfsstat.f_fsid.val[0] = (long)dev;
mp->mnt_vfsstat.f_fsid.val[1] = vfs_typenum(mp);
mp->mnt_maxsymlinklen = 60;
#if REV_ENDIAN_FS
if (rev_endian)
mp->mnt_flag |= MNT_REVEND;
#endif
ump->um_mountp = mp;
ump->um_dev = dev;
ump->um_devvp = devvp;
ump->um_nindir = fs->fs_nindir;
ump->um_bptrtodb = fs->fs_fsbtodb;
ump->um_seqinc = fs->fs_frag;
for (i = 0; i < MAXQUOTAS; i++)
dqfileinit(&ump->um_qfiles[i]);
ffs_oldfscompat(fs);
ump->um_savedmaxfilesize = fs->fs_maxfilesize;
maxfilesize = 0x100000000ULL;
#if 0
maxfilesize = (u_int64_t)0x40000000 * fs->fs_bsize - 1;
#endif
if (fs->fs_maxfilesize > maxfilesize)
fs->fs_maxfilesize = maxfilesize;
if (ronly == 0) {
fs->fs_clean = 0;
(void) ffs_sbupdate(ump, MNT_WAIT);
}
return (0);
out:
if (bp)
buf_brelse(bp);
if (ump) {
_FREE(ump->um_fs, M_UFSMNT);
_FREE(ump, M_UFSMNT);
}
return (error);
}
ffs_oldfscompat(fs)
struct fs *fs;
{
int i;
fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);
fs->fs_interleave = max(fs->fs_interleave, 1);
if (fs->fs_postblformat == FS_42POSTBLFMT)
fs->fs_nrpos = 8;
if (fs->fs_inodefmt < FS_44INODEFMT) {
u_int64_t sizepb = fs->fs_bsize;
fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;
for (i = 0; i < NIADDR; i++) {
sizepb *= NINDIR(fs);
fs->fs_maxfilesize += sizepb;
}
fs->fs_qbmask = ~fs->fs_bmask;
fs->fs_qfmask = ~fs->fs_fmask;
}
return (0);
}
int
ffs_unmount(mp, mntflags, context)
struct mount *mp;
int mntflags;
vfs_context_t context;
{
struct proc *p = vfs_context_proc(context);
register struct ufsmount *ump;
register struct fs *fs;
int error, flags;
int force;
flags = 0;
force = 0;
if (mntflags & MNT_FORCE) {
flags |= FORCECLOSE;
force = 1;
}
if ( (error = ffs_flushfiles(mp, flags, p)) && !force )
return (error);
ump = VFSTOUFS(mp);
fs = ump->um_fs;
if (fs->fs_ronly == 0) {
fs->fs_clean = 1;
if (error = ffs_sbupdate(ump, MNT_WAIT)) {
fs->fs_clean = 0;
#ifdef notyet
return (error);
#endif
}
}
_FREE(fs->fs_csp, M_UFSMNT);
_FREE(fs, M_UFSMNT);
_FREE(ump, M_UFSMNT);
return (0);
}
ffs_flushfiles(mp, flags, p)
register struct mount *mp;
int flags;
struct proc *p;
{
register struct ufsmount *ump;
int i, error;
ump = VFSTOUFS(mp);
#if QUOTA
if (mp->mnt_flag & MNT_QUOTA) {
struct vnode *rootvp = NULLVP;
int quotafilecnt = 0;
for (i = 0; i < MAXQUOTAS; i++) {
if (ump->um_qfiles[i].qf_vp != NULLVP)
++quotafilecnt;
}
rootvp = ufs_ihashget(ump->um_dev, ROOTINO);
error = vflush(mp, rootvp, SKIPSYSTEM|flags);
if (rootvp) {
if ((error == 0) &&
(rootvp->v_usecount > (1 + quotafilecnt))) {
error = EBUSY;
}
vnode_put(rootvp);
}
if (error && (flags & FORCECLOSE) == 0)
return (error);
for (i = 0; i < MAXQUOTAS; i++) {
if (ump->um_qfiles[i].qf_vp == NULLVP)
continue;
quotaoff(mp, i);
}
}
#endif
error = vflush(mp, NULLVP, SKIPSWAP|flags);
error = vflush(mp, NULLVP, flags);
return (error);
}
int
ffs_statfs(mp, sbp, context)
struct mount *mp;
register struct vfsstatfs *sbp;
vfs_context_t context;
{
register struct ufsmount *ump;
register struct fs *fs;
ump = VFSTOUFS(mp);
fs = ump->um_fs;
if (fs->fs_magic != FS_MAGIC)
panic("ffs_statfs");
sbp->f_bsize = fs->fs_fsize;
sbp->f_iosize = fs->fs_bsize;
sbp->f_blocks = (uint64_t)((unsigned long)fs->fs_dsize);
sbp->f_bfree = (uint64_t) ((unsigned long)(fs->fs_cstotal.cs_nbfree * fs->fs_frag +
fs->fs_cstotal.cs_nffree));
sbp->f_bavail = (uint64_t) ((unsigned long)freespace(fs, fs->fs_minfree));
sbp->f_files = (uint64_t) ((unsigned long)(fs->fs_ncg * fs->fs_ipg - ROOTINO));
sbp->f_ffree = (uint64_t) ((unsigned long)fs->fs_cstotal.cs_nifree);
return (0);
}
int
ffs_vfs_getattr(mp, fsap, context)
struct mount *mp;
struct vfs_attr *fsap;
vfs_context_t context;
{
struct ufsmount *ump;
struct fs *fs;
kauth_cred_t cred;
struct vnode *devvp;
struct buf *bp;
struct ufslabel *ulp;
char *offset;
int bs, error, length;
ump = VFSTOUFS(mp);
fs = ump->um_fs;
cred = vfs_context_ucred(context);
VFSATTR_RETURN(fsap, f_bsize, fs->fs_fsize);
VFSATTR_RETURN(fsap, f_iosize, fs->fs_bsize);
VFSATTR_RETURN(fsap, f_blocks, (uint64_t)((unsigned long)fs->fs_dsize));
VFSATTR_RETURN(fsap, f_bfree, (uint64_t)((unsigned long)
(fs->fs_cstotal.cs_nbfree * fs->fs_frag +
fs->fs_cstotal.cs_nffree)));
VFSATTR_RETURN(fsap, f_bavail, (uint64_t)((unsigned long)freespace(fs,
fs->fs_minfree)));
VFSATTR_RETURN(fsap, f_files, (uint64_t)((unsigned long)
(fs->fs_ncg * fs->fs_ipg - ROOTINO)));
VFSATTR_RETURN(fsap, f_ffree, (uint64_t)((unsigned long)
fs->fs_cstotal.cs_nifree));
if (VFSATTR_IS_ACTIVE(fsap, f_fsid)) {
fsap->f_fsid.val[0] = mp->mnt_vfsstat.f_fsid.val[0];
fsap->f_fsid.val[1] = mp->mnt_vfsstat.f_fsid.val[1];
VFSATTR_SET_SUPPORTED(fsap, f_fsid);
}
if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
devvp = ump->um_devvp;
bs = vfs_devblocksize(mp);
if (error = (int)buf_meta_bread(devvp,
(daddr64_t)(UFS_LABEL_OFFSET / bs),
MAX(bs, UFS_LABEL_SIZE), cred, &bp)) {
if (bp)
buf_brelse(bp);
return (error);
}
buf_setflags(bp, B_NOCACHE);
offset = buf_dataptr(bp) + (UFS_LABEL_OFFSET % bs);
ulp = (struct ufslabel *)offset;
if (ufs_label_check(ulp)) {
length = ulp->ul_namelen;
#if REV_ENDIAN_FS
if (mp->mnt_flag & MNT_REVEND)
length = OSSwapInt16(length);
#endif
if (length > 0 && length <= UFS_MAX_LABEL_NAME) {
bcopy(ulp->ul_name, fsap->f_vol_name, length);
fsap->f_vol_name[UFS_MAX_LABEL_NAME - 1] = '\0';
fsap->f_vol_name[length] = '\0';
}
}
buf_brelse(bp);
VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
}
if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_SYMBOLICLINKS |
VOL_CAP_FMT_HARDLINKS |
VOL_CAP_FMT_SPARSE_FILES |
VOL_CAP_FMT_CASE_SENSITIVE |
VOL_CAP_FMT_CASE_PRESERVING |
VOL_CAP_FMT_FAST_STATFS ;
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES]
= VOL_CAP_INT_NFSEXPORT |
VOL_CAP_INT_VOL_RENAME |
VOL_CAP_INT_ADVLOCK |
VOL_CAP_INT_FLOCK;
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1]
= 0;
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2]
= 0;
fsap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_PERSISTENTOBJECTIDS |
VOL_CAP_FMT_SYMBOLICLINKS |
VOL_CAP_FMT_HARDLINKS |
VOL_CAP_FMT_JOURNAL |
VOL_CAP_FMT_JOURNAL_ACTIVE |
VOL_CAP_FMT_NO_ROOT_TIMES |
VOL_CAP_FMT_SPARSE_FILES |
VOL_CAP_FMT_ZERO_RUNS |
VOL_CAP_FMT_CASE_SENSITIVE |
VOL_CAP_FMT_CASE_PRESERVING |
VOL_CAP_FMT_FAST_STATFS |
VOL_CAP_FMT_2TB_FILESIZE;
fsap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] =
VOL_CAP_INT_SEARCHFS |
VOL_CAP_INT_ATTRLIST |
VOL_CAP_INT_NFSEXPORT |
VOL_CAP_INT_READDIRATTR |
VOL_CAP_INT_EXCHANGEDATA |
VOL_CAP_INT_COPYFILE |
VOL_CAP_INT_ALLOCATE |
VOL_CAP_INT_VOL_RENAME |
VOL_CAP_INT_ADVLOCK |
VOL_CAP_INT_FLOCK ;
fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
}
if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
fsap->f_attributes.validattr.commonattr = 0;
fsap->f_attributes.validattr.volattr =
ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
fsap->f_attributes.validattr.dirattr = 0;
fsap->f_attributes.validattr.fileattr = 0;
fsap->f_attributes.validattr.forkattr = 0;
fsap->f_attributes.nativeattr.commonattr = 0;
fsap->f_attributes.nativeattr.volattr =
ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
fsap->f_attributes.nativeattr.dirattr = 0;
fsap->f_attributes.nativeattr.fileattr = 0;
fsap->f_attributes.nativeattr.forkattr = 0;
VFSATTR_SET_SUPPORTED(fsap, f_attributes);
}
return (0);
}
int
ffs_vfs_setattr(mp, fsap, context)
struct mount *mp;
struct vfs_attr *fsap;
vfs_context_t context;
{
struct ufsmount *ump;
struct vnode *devvp;
struct buf *bp;
struct ufslabel *ulp;
kauth_cred_t cred;
char *offset;
int bs, error;
ump = VFSTOUFS(mp);
cred = vfs_context_ucred(context);
if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
devvp = ump->um_devvp;
bs = vfs_devblocksize(mp);
if (error = buf_meta_bread(devvp,
(daddr64_t)(UFS_LABEL_OFFSET / bs),
MAX(bs, UFS_LABEL_SIZE), cred, &bp)) {
if (bp)
buf_brelse(bp);
return (error);
}
buf_setflags(bp, B_NOCACHE);
offset = buf_dataptr(bp) + (UFS_LABEL_OFFSET % bs);
ulp = (struct ufslabel *)offset;
if (!ufs_label_check(ulp))
ufs_label_init(ulp);
ulp->ul_namelen = strlen(fsap->f_vol_name);
bcopy(fsap->f_vol_name, ulp->ul_name, ulp->ul_namelen);
ulp->ul_name[UFS_MAX_LABEL_NAME - 1] = '\0';
ulp->ul_name[ulp->ul_namelen] = '\0';
#if REV_ENDIAN_FS
if (mp->mnt_flag & MNT_REVEND)
ulp->ul_namelen = OSSwapInt16(ulp->ul_namelen);
#endif
ulp->ul_checksum = 0;
ulp->ul_checksum = ul_cksum(ulp, sizeof(*ulp));
buf_bwrite(bp);
bp = NULL;
VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
}
return (0);
}
struct ffs_sync_cargs {
vfs_context_t context;
int waitfor;
int error;
};
static int
ffs_sync_callback(struct vnode *vp, void *cargs)
{
struct inode *ip;
struct ffs_sync_cargs *args;
int error;
args = (struct ffs_sync_cargs *)cargs;
ip = VTOI(vp);
if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) || vnode_hasdirtyblks(vp)) {
error = VNOP_FSYNC(vp, args->waitfor, args->context);
if (error)
args->error = error;
}
return (VNODE_RETURNED);
}
int
ffs_sync(mp, waitfor, context)
struct mount *mp;
int waitfor;
vfs_context_t context;
{
struct vnode *nvp, *vp;
struct ufsmount *ump = VFSTOUFS(mp);
struct fs *fs;
struct timeval tv;
int error, allerror = 0;
struct ffs_sync_cargs args;
fs = ump->um_fs;
if (fs->fs_fmod != 0 && fs->fs_ronly != 0) {
printf("fs = %s\n", fs->fs_fsmnt);
panic("update: rofs mod");
}
args.context = context;
args.waitfor = waitfor;
args.error = 0;
vnode_iterate(mp, 0, ffs_sync_callback, (void *)&args);
if (args.error)
allerror = args.error;
if (error = VNOP_FSYNC(ump->um_devvp, waitfor, context))
allerror = error;
#if QUOTA
qsync(mp);
#endif
if (fs->fs_fmod != 0) {
fs->fs_fmod = 0;
microtime(&tv);
fs->fs_time = tv.tv_sec;
if (error = ffs_sbupdate(ump, waitfor))
allerror = error;
}
return (allerror);
}
int
ffs_vget(mp, ino, vpp, context)
mount_t mp;
ino64_t ino;
vnode_t *vpp;
vfs_context_t context;
{
return(ffs_vget_internal(mp, (ino_t)ino, vpp, NULL, NULL, 0, 0));
}
int
ffs_vget_internal(mp, ino, vpp, dvp, cnp, mode, fhwanted)
mount_t mp;
ino_t ino;
vnode_t *vpp;
vnode_t dvp;
struct componentname *cnp;
int mode;
int fhwanted;
{
struct proc *p = current_proc();
struct fs *fs;
struct inode *ip;
struct ufsmount *ump;
struct buf *bp;
struct vnode *vp;
struct vnode_fsparam vfsp;
struct timeval tv;
enum vtype vtype;
dev_t dev;
int i, type, error = 0;
*vpp = NULL;
ump = VFSTOUFS(mp);
dev = ump->um_dev;
#if 0
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
return (EPERM);
}
#endif
type = M_FFSNODE;
MALLOC_ZONE(ip, struct inode *, sizeof(struct inode), type, M_WAITOK);
if ((*vpp = ufs_ihashget(dev, ino)) != NULL) {
FREE_ZONE(ip, sizeof(struct inode), type);
vp = *vpp;
return (0);
}
bzero((caddr_t)ip, sizeof(struct inode));
ip->i_fs = fs = ump->um_fs;
ip->i_dev = dev;
ip->i_number = ino;
#if QUOTA
for (i = 0; i < MAXQUOTAS; i++)
ip->i_dquot[i] = NODQUOT;
#endif
SET(ip->i_flag, IN_ALLOC);
ufs_ihashins(ip);
if (error = (int)buf_bread(ump->um_devvp, (daddr64_t)((unsigned)fsbtodb(fs, ino_to_fsba(fs, ino))),
(int)fs->fs_bsize, NOCRED, &bp)) {
buf_brelse(bp);
goto errout;
}
#if REV_ENDIAN_FS
if (mp->mnt_flag & MNT_REVEND) {
byte_swap_inode_in(((struct dinode *)buf_dataptr(bp) + ino_to_fsbo(fs, ino)),ip);
} else {
ip->i_din = *((struct dinode *)buf_dataptr(bp) + ino_to_fsbo(fs, ino));
}
#else
ip->i_din = *((struct dinode *)buf_dataptr(bp) + ino_to_fsbo(fs, ino));
#endif
buf_brelse(bp);
if (mode == 0)
vtype = IFTOVT(ip->i_mode);
else
vtype = IFTOVT(mode);
if (vtype == VNON) {
if (fhwanted) {
error = ESTALE;
goto errout;
} else {
error = ENOENT;
goto errout;
}
}
vfsp.vnfs_mp = mp;
vfsp.vnfs_vtype = vtype;
vfsp.vnfs_str = "ufs";
vfsp.vnfs_dvp = dvp;
vfsp.vnfs_fsnode = ip;
vfsp.vnfs_cnp = cnp;
if (mode == 0)
vfsp.vnfs_filesize = ip->i_din.di_size;
else
vfsp.vnfs_filesize = 0;
if (vtype == VFIFO )
vfsp.vnfs_vops = FFS_FIFOOPS;
else if (vtype == VBLK || vtype == VCHR)
vfsp.vnfs_vops = ffs_specop_p;
else
vfsp.vnfs_vops = ffs_vnodeop_p;
if (vtype == VBLK || vtype == VCHR)
vfsp.vnfs_rdev = ip->i_rdev;
else
vfsp.vnfs_rdev = 0;
if (dvp && cnp && (cnp->cn_flags & MAKEENTRY))
vfsp.vnfs_flags = 0;
else
vfsp.vnfs_flags = VNFS_NOCACHE;
vfsp.vnfs_markroot = (ip->i_number == ROOTINO);
vfsp.vnfs_marksystem = 0;
if ((error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &vp)))
goto errout;
ip->i_devvp = ump->um_devvp;
ip->i_vnode = vp;
vnode_ref(ip->i_devvp);
vnode_addfsref(vp);
vnode_settag(vp, VT_UFS);
microtime(&tv);
SETHIGH(ip->i_modrev, tv.tv_sec);
SETLOW(ip->i_modrev, tv.tv_usec * 4294);
if (ip->i_gen == 0) {
if (++nextgennumber < (u_long)tv.tv_sec)
nextgennumber = tv.tv_sec;
ip->i_gen = nextgennumber;
if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
ip->i_flag |= IN_MODIFIED;
}
if (fs->fs_inodefmt < FS_44INODEFMT) {
ip->i_uid = ip->i_din.di_ouid;
ip->i_gid = ip->i_din.di_ogid;
}
*vpp = vp;
CLR(ip->i_flag, IN_ALLOC);
if (ISSET(ip->i_flag, IN_WALLOC))
wakeup(ip);
return (0);
errout:
ufs_ihashrem(ip);
if (ISSET(ip->i_flag, IN_WALLOC))
wakeup(ip);
FREE_ZONE(ip, sizeof(struct inode), type);
return (error);
}
int
ffs_fhtovp(mp, fhlen, fhp, vpp, context)
register struct mount *mp;
int fhlen;
unsigned char *fhp;
struct vnode **vpp;
vfs_context_t context;
{
register struct ufid *ufhp;
register struct inode *ip;
struct vnode *nvp;
struct fs *fs;
int error;
ino_t ino;
if (fhlen < (int)sizeof(struct ufid))
return (EINVAL);
ufhp = (struct ufid *)fhp;
fs = VFSTOUFS(mp)->um_fs;
ino = ntohl(ufhp->ufid_ino);
if (ino < ROOTINO || ino >= fs->fs_ncg * fs->fs_ipg)
return (ESTALE);
error = ffs_vget_internal(mp, ino, &nvp, NULL, NULL, 0, 1);
if (error) {
*vpp = NULLVP;
return (error);
}
ip = VTOI(nvp);
if (ip->i_mode == 0 || ip->i_gen != ntohl(ufhp->ufid_gen)) {
vnode_put(nvp);
*vpp = NULLVP;
return (ESTALE);
}
*vpp = nvp;
return (0);
}
int
ffs_vptofh(vp, fhlenp, fhp, context)
struct vnode *vp;
int *fhlenp;
unsigned char *fhp;
vfs_context_t context;
{
register struct inode *ip;
register struct ufid *ufhp;
if (*fhlenp < (int)sizeof(struct ufid))
return (EOVERFLOW);
ip = VTOI(vp);
ufhp = (struct ufid *)fhp;
ufhp->ufid_ino = htonl(ip->i_number);
ufhp->ufid_gen = htonl(ip->i_gen);
*fhlenp = sizeof(struct ufid);
return (0);
}
int
ffs_init(vfsp)
struct vfsconf *vfsp;
{
return (ufs_init(vfsp));
}
ffs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
user_addr_t newp, size_t newlen, vfs_context_t context)
{
extern int doclusterread, doclusterwrite, doreallocblks, doasyncfree;
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case FFS_CLUSTERREAD:
return (sysctl_int(oldp, oldlenp, newp, newlen,
&doclusterread));
case FFS_CLUSTERWRITE:
return (sysctl_int(oldp, oldlenp, newp, newlen,
&doclusterwrite));
case FFS_REALLOCBLKS:
return (sysctl_int(oldp, oldlenp, newp, newlen,
&doreallocblks));
case FFS_ASYNCFREE:
return (sysctl_int(oldp, oldlenp, newp, newlen, &doasyncfree));
default:
return (ENOTSUP);
}
}
int
ffs_sbupdate(mp, waitfor)
struct ufsmount *mp;
int waitfor;
{
register struct fs *dfs, *fs = mp->um_fs;
register struct buf *bp;
int blks;
void *space;
int i, size, error, allerror = 0;
int devBlockSize=0;
#if REV_ENDIAN_FS
int rev_endian=(mp->um_mountp->mnt_flag & MNT_REVEND);
#endif
blks = howmany(fs->fs_cssize, fs->fs_fsize);
space = fs->fs_csp;
for (i = 0; i < blks; i += fs->fs_frag) {
size = fs->fs_bsize;
if (i + fs->fs_frag > blks)
size = (blks - i) * fs->fs_fsize;
bp = buf_getblk(mp->um_devvp, (daddr64_t)((unsigned)fsbtodb(fs, fs->fs_csaddr + i)),
size, 0, 0, BLK_META);
bcopy(space, (char *)buf_dataptr(bp), (u_int)size);
#if REV_ENDIAN_FS
if (rev_endian) {
byte_swap_ints((int *)buf_dataptr(bp), size / sizeof(int));
}
#endif
space = (char *)space + size;
if (waitfor != MNT_WAIT)
buf_bawrite(bp);
else if (error = (int)buf_bwrite(bp))
allerror = error;
}
if (allerror)
return (allerror);
devBlockSize = vfs_devblocksize(mp->um_mountp);
bp = buf_getblk(mp->um_devvp, (daddr64_t)((unsigned)(SBOFF/devBlockSize)), (int)fs->fs_sbsize, 0, 0, BLK_META);
bcopy((caddr_t)fs, (char *)buf_dataptr(bp), (u_int)fs->fs_sbsize);
dfs = (struct fs *)buf_dataptr(bp);
if (fs->fs_postblformat == FS_42POSTBLFMT)
dfs->fs_nrpos = -1;
#if REV_ENDIAN_FS
if (rev_endian) {
byte_swap_sbout((struct fs *)buf_dataptr(bp));
}
#endif
if (fs->fs_inodefmt < FS_44INODEFMT) {
int32_t *lp, tmp;
lp = (int32_t *)&dfs->fs_qbmask;
tmp = lp[4];
for (i = 4; i > 0; i--)
lp[i] = lp[i-1];
lp[0] = tmp;
}
#if REV_ENDIAN_FS
if (rev_endian) {
dfs->fs_maxfilesize = OSSwapInt64(mp->um_savedmaxfilesize);
} else {
#endif
dfs->fs_maxfilesize = mp->um_savedmaxfilesize;
#if REV_ENDIAN_FS
}
#endif
if (waitfor != MNT_WAIT)
buf_bawrite(bp);
else if (error = (int)buf_bwrite(bp))
allerror = error;
return (allerror);
}