897 lines
22 KiB
C
897 lines
22 KiB
C
/* $NetBSD: efs_vnops.c,v 1.44 2022/08/06 18:26:41 andvar Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2006 Stephen M. Rumble <rumble@ephemeral.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: efs_vnops.c,v 1.44 2022/08/06 18:26:41 andvar Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/dirent.h>
|
|
#include <sys/lockf.h>
|
|
#include <sys/unistd.h>
|
|
#include <sys/buf.h>
|
|
|
|
#include <miscfs/genfs/genfs.h>
|
|
#include <miscfs/genfs/genfs_node.h>
|
|
#include <miscfs/fifofs/fifo.h>
|
|
#include <miscfs/specfs/specdev.h>
|
|
|
|
#include <fs/efs/efs.h>
|
|
#include <fs/efs/efs_sb.h>
|
|
#include <fs/efs/efs_dir.h>
|
|
#include <fs/efs/efs_genfs.h>
|
|
#include <fs/efs/efs_mount.h>
|
|
#include <fs/efs/efs_extent.h>
|
|
#include <fs/efs/efs_dinode.h>
|
|
#include <fs/efs/efs_inode.h>
|
|
#include <fs/efs/efs_subr.h>
|
|
|
|
MALLOC_DECLARE(M_EFSTMP);
|
|
|
|
/*
|
|
* Lookup a pathname component in the given directory.
|
|
*
|
|
* Returns 0 on success.
|
|
*/
|
|
static int
|
|
efs_lookup(void *v)
|
|
{
|
|
struct vop_lookup_v2_args /* {
|
|
struct vnode *a_dvp;
|
|
struct vnode **a_vpp;
|
|
struct componentname *a_cnp;
|
|
} */ *ap = v;
|
|
struct componentname *cnp = ap->a_cnp;
|
|
struct vnode *vp;
|
|
ino_t ino;
|
|
int err, nameiop = cnp->cn_nameiop;
|
|
|
|
/* ensure that the directory can be accessed first */
|
|
err = VOP_ACCESS(ap->a_dvp, VEXEC, cnp->cn_cred);
|
|
if (err)
|
|
return (err);
|
|
|
|
if (cache_lookup(ap->a_dvp, cnp->cn_nameptr, cnp->cn_namelen,
|
|
cnp->cn_nameiop, cnp->cn_flags, NULL, ap->a_vpp)) {
|
|
return *ap->a_vpp == NULLVP ? ENOENT : 0;
|
|
}
|
|
|
|
/*
|
|
* Handle the lookup types: '.' or everything else.
|
|
*/
|
|
if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
|
|
vref(ap->a_dvp);
|
|
*ap->a_vpp = ap->a_dvp;
|
|
} else {
|
|
err = efs_inode_lookup(VFSTOEFS(ap->a_dvp->v_mount),
|
|
EFS_VTOI(ap->a_dvp), ap->a_cnp, &ino);
|
|
if (err) {
|
|
if (cnp->cn_flags & ISDOTDOT)
|
|
return (err);
|
|
if (err == ENOENT && nameiop != CREATE)
|
|
cache_enter(ap->a_dvp, NULL, cnp->cn_nameptr,
|
|
cnp->cn_namelen, cnp->cn_flags);
|
|
if (err == ENOENT && (nameiop == CREATE ||
|
|
nameiop == RENAME)) {
|
|
err = VOP_ACCESS(ap->a_dvp, VWRITE,
|
|
cnp->cn_cred);
|
|
if (err)
|
|
return (err);
|
|
return (EJUSTRETURN);
|
|
}
|
|
return (err);
|
|
}
|
|
err = vcache_get(ap->a_dvp->v_mount, &ino, sizeof(ino), &vp);
|
|
if (err)
|
|
return (err);
|
|
*ap->a_vpp = vp;
|
|
}
|
|
|
|
cache_enter(ap->a_dvp, *ap->a_vpp, cnp->cn_nameptr, cnp->cn_namelen,
|
|
cnp->cn_flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
efs_check_possible(struct vnode *vp, struct efs_inode *eip, accmode_t accmode)
|
|
{
|
|
|
|
if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY))
|
|
return (EROFS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Determine the accessibility of a file based on the permissions allowed by the
|
|
* specified credentials.
|
|
*
|
|
* Returns 0 on success.
|
|
*/
|
|
static int
|
|
efs_check_permitted(struct vnode *vp, struct efs_inode *eip, accmode_t accmode,
|
|
kauth_cred_t cred)
|
|
{
|
|
|
|
return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
|
|
vp->v_type, eip->ei_mode), vp, NULL, genfs_can_access(vp,
|
|
cred, eip->ei_uid, eip->ei_gid, eip->ei_mode, NULL, accmode));
|
|
}
|
|
|
|
static int
|
|
efs_access(void *v)
|
|
{
|
|
struct vop_access_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
accmode_t a_accmode;
|
|
struct ucred *a_cred;
|
|
} */ *ap = v;
|
|
struct vnode *vp = ap->a_vp;
|
|
struct efs_inode *eip = EFS_VTOI(vp);
|
|
int error;
|
|
|
|
error = efs_check_possible(vp, eip, ap->a_accmode);
|
|
if (error)
|
|
return error;
|
|
|
|
error = efs_check_permitted(vp, eip, ap->a_accmode, ap->a_cred);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Get specific vnode attributes on a file. See vattr(9).
|
|
*
|
|
* Returns 0 on success.
|
|
*/
|
|
static int
|
|
efs_getattr(void *v)
|
|
{
|
|
struct vop_getattr_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
struct vattr *a_vap;
|
|
struct ucred *a_cred;
|
|
} */ *ap = v;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
struct efs_inode *eip = EFS_VTOI(ap->a_vp);
|
|
|
|
vattr_null(ap->a_vap);
|
|
vap->va_type = ap->a_vp->v_type;
|
|
vap->va_mode = eip->ei_mode;
|
|
vap->va_nlink = eip->ei_nlink;
|
|
vap->va_uid = eip->ei_uid;
|
|
vap->va_gid = eip->ei_gid;
|
|
vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid;
|
|
vap->va_fileid = eip->ei_number;
|
|
vap->va_size = eip->ei_size;
|
|
|
|
if (ap->a_vp->v_type == VBLK)
|
|
vap->va_blocksize = BLKDEV_IOSIZE;
|
|
else if (ap->a_vp->v_type == VCHR)
|
|
vap->va_blocksize = MAXBSIZE;
|
|
else
|
|
vap->va_blocksize = EFS_BB_SIZE;
|
|
|
|
vap->va_atime.tv_sec = eip->ei_atime;
|
|
vap->va_mtime.tv_sec = eip->ei_mtime;
|
|
vap->va_ctime.tv_sec = eip->ei_ctime;
|
|
/* vap->va_birthtime = */
|
|
vap->va_gen = eip->ei_gen;
|
|
vap->va_flags = ap->a_vp->v_vflag |
|
|
ap->a_vp->v_iflag | ap->a_vp->v_uflag;
|
|
|
|
if (ap->a_vp->v_type == VBLK || ap->a_vp->v_type == VCHR) {
|
|
uint32_t dmaj, dmin;
|
|
|
|
if (be16toh(eip->ei_di.di_odev) != EFS_DINODE_ODEV_INVALID) {
|
|
dmaj = EFS_DINODE_ODEV_MAJ(be16toh(eip->ei_di.di_odev));
|
|
dmin = EFS_DINODE_ODEV_MIN(be16toh(eip->ei_di.di_odev));
|
|
} else {
|
|
dmaj = EFS_DINODE_NDEV_MAJ(be32toh(eip->ei_di.di_ndev));
|
|
dmin = EFS_DINODE_NDEV_MIN(be32toh(eip->ei_di.di_ndev));
|
|
}
|
|
|
|
vap->va_rdev = makedev(dmaj, dmin);
|
|
}
|
|
|
|
vap->va_bytes = eip->ei_size;
|
|
/* vap->va_filerev = */
|
|
/* vap->va_vaflags = */
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Read a file.
|
|
*
|
|
* Returns 0 on success.
|
|
*/
|
|
static int
|
|
efs_read(void *v)
|
|
{
|
|
struct vop_read_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
struct uio *a_uio;
|
|
int a_ioflag;
|
|
struct ucred *a_cred;
|
|
} */ *ap = v;
|
|
struct efs_extent ex;
|
|
struct efs_extent_iterator exi;
|
|
struct uio *uio = ap->a_uio;
|
|
struct efs_inode *eip = EFS_VTOI(ap->a_vp);
|
|
off_t start;
|
|
vsize_t len;
|
|
int err, ret;
|
|
const int advice = IO_ADV_DECODE(ap->a_ioflag);
|
|
|
|
if (ap->a_vp->v_type == VDIR)
|
|
return (EISDIR);
|
|
|
|
if (ap->a_vp->v_type != VREG)
|
|
return (EINVAL);
|
|
|
|
efs_extent_iterator_init(&exi, eip, uio->uio_offset);
|
|
ret = efs_extent_iterator_next(&exi, &ex);
|
|
while (ret == 0) {
|
|
if (uio->uio_offset < 0 || uio->uio_offset >= eip->ei_size ||
|
|
uio->uio_resid == 0)
|
|
break;
|
|
|
|
start = ex.ex_offset * EFS_BB_SIZE;
|
|
len = ex.ex_length * EFS_BB_SIZE;
|
|
|
|
if (!(uio->uio_offset >= start &&
|
|
uio->uio_offset < (start + len))) {
|
|
ret = efs_extent_iterator_next(&exi, &ex);
|
|
continue;
|
|
}
|
|
|
|
start = uio->uio_offset - start;
|
|
|
|
len = MIN(len - start, uio->uio_resid);
|
|
len = MIN(len, eip->ei_size - uio->uio_offset);
|
|
|
|
err = ubc_uiomove(&ap->a_vp->v_uobj, uio, len, advice,
|
|
UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(ap->a_vp));
|
|
if (err) {
|
|
EFS_DPRINTF(("efs_read: uiomove error %d\n",
|
|
err));
|
|
return (err);
|
|
}
|
|
}
|
|
|
|
return ((ret == -1) ? 0 : ret);
|
|
}
|
|
|
|
static int
|
|
efs_readdir(void *v)
|
|
{
|
|
struct vop_readdir_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
struct uio *a_uio;
|
|
struct ucred *a_cred;
|
|
int *a_eofflag;
|
|
off_t **a_cookies;
|
|
int *a_ncookies;
|
|
} */ *ap = v;
|
|
struct dirent *dp;
|
|
struct efs_dinode edi;
|
|
struct efs_extent ex;
|
|
struct efs_extent_iterator exi;
|
|
struct buf *bp;
|
|
struct efs_dirent *de;
|
|
struct efs_dirblk *db;
|
|
struct uio *uio = ap->a_uio;
|
|
struct efs_inode *ei = EFS_VTOI(ap->a_vp);
|
|
off_t *cookies = NULL;
|
|
off_t offset;
|
|
int i, j, err, ret, s, slot, ncookies, maxcookies = 0;
|
|
|
|
if (ap->a_vp->v_type != VDIR)
|
|
return (ENOTDIR);
|
|
|
|
if (ap->a_eofflag != NULL)
|
|
*ap->a_eofflag = false;
|
|
|
|
if (ap->a_ncookies != NULL) {
|
|
ncookies = 0;
|
|
maxcookies =
|
|
uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
|
|
cookies = malloc(maxcookies * sizeof(off_t), M_TEMP, M_WAITOK);
|
|
}
|
|
|
|
dp = malloc(sizeof(struct dirent), M_EFSTMP, M_WAITOK | M_ZERO);
|
|
|
|
offset = 0;
|
|
efs_extent_iterator_init(&exi, ei, 0);
|
|
while ((ret = efs_extent_iterator_next(&exi, &ex)) == 0) {
|
|
for (i = 0; i < ex.ex_length; i++) {
|
|
err = efs_bread(VFSTOEFS(ap->a_vp->v_mount),
|
|
ex.ex_bn + i, NULL, &bp);
|
|
if (err) {
|
|
goto exit_err;
|
|
}
|
|
|
|
db = (struct efs_dirblk *)bp->b_data;
|
|
|
|
if (be16toh(db->db_magic) != EFS_DIRBLK_MAGIC) {
|
|
printf("efs_readdir: bad dirblk\n");
|
|
brelse(bp, 0);
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < db->db_slots; j++) {
|
|
slot = EFS_DIRENT_OFF_EXPND(db->db_space[j]);
|
|
if (slot == EFS_DIRBLK_SLOT_FREE)
|
|
continue;
|
|
|
|
if (!EFS_DIRENT_OFF_VALID(slot)) {
|
|
printf("efs_readdir: bad dirent\n");
|
|
continue;
|
|
}
|
|
|
|
de = EFS_DIRBLK_TO_DIRENT(db, slot);
|
|
s = _DIRENT_RECLEN(dp, de->de_namelen);
|
|
|
|
if (offset < uio->uio_offset) {
|
|
offset += s;
|
|
continue;
|
|
}
|
|
|
|
/* XXX - shouldn't happen, right? */
|
|
if (offset > uio->uio_offset ||
|
|
s > uio->uio_resid) {
|
|
brelse(bp, 0);
|
|
goto exit_ok;
|
|
}
|
|
|
|
/* de_namelen is uint8_t, d.d_name is 512b */
|
|
KASSERT(sizeof(dp->d_name)-de->de_namelen > 0);
|
|
dp->d_fileno = be32toh(de->de_inumber);
|
|
dp->d_reclen = s;
|
|
dp->d_namlen = de->de_namelen;
|
|
memcpy(dp->d_name, de->de_name,
|
|
de->de_namelen);
|
|
dp->d_name[de->de_namelen] = '\0';
|
|
|
|
/* look up inode to get type */
|
|
err = efs_read_inode(
|
|
VFSTOEFS(ap->a_vp->v_mount),
|
|
dp->d_fileno, NULL, &edi);
|
|
if (err) {
|
|
brelse(bp, 0);
|
|
goto exit_err;
|
|
}
|
|
|
|
switch (be16toh(edi.di_mode) & EFS_IFMT) {
|
|
case EFS_IFIFO:
|
|
dp->d_type = DT_FIFO;
|
|
break;
|
|
case EFS_IFCHR:
|
|
dp->d_type = DT_CHR;
|
|
break;
|
|
case EFS_IFDIR:
|
|
dp->d_type = DT_DIR;
|
|
break;
|
|
case EFS_IFBLK:
|
|
dp->d_type = DT_BLK;
|
|
break;
|
|
case EFS_IFREG:
|
|
dp->d_type = DT_REG;
|
|
break;
|
|
case EFS_IFLNK:
|
|
dp->d_type = DT_LNK;
|
|
break;
|
|
case EFS_IFSOCK:
|
|
dp->d_type = DT_SOCK;
|
|
break;
|
|
default:
|
|
dp->d_type = DT_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
err = uiomove(dp, s, uio);
|
|
if (err) {
|
|
brelse(bp, 0);
|
|
goto exit_err;
|
|
}
|
|
|
|
offset += s;
|
|
|
|
if (cookies != NULL && maxcookies != 0) {
|
|
cookies[ncookies++] = offset;
|
|
if (ncookies == maxcookies) {
|
|
brelse(bp, 0);
|
|
goto exit_ok;
|
|
}
|
|
}
|
|
}
|
|
|
|
brelse(bp, 0);
|
|
}
|
|
}
|
|
|
|
if (ret != -1) {
|
|
err = ret;
|
|
goto exit_err;
|
|
}
|
|
|
|
if (ap->a_eofflag != NULL)
|
|
*ap->a_eofflag = true;
|
|
|
|
exit_ok:
|
|
if (cookies != NULL) {
|
|
*ap->a_cookies = cookies;
|
|
*ap->a_ncookies = ncookies;
|
|
}
|
|
|
|
uio->uio_offset = offset;
|
|
|
|
free(dp, M_EFSTMP);
|
|
|
|
return (0);
|
|
|
|
exit_err:
|
|
if (cookies != NULL)
|
|
free(cookies, M_TEMP);
|
|
|
|
free(dp, M_EFSTMP);
|
|
|
|
return (err);
|
|
}
|
|
|
|
static int
|
|
efs_readlink(void *v)
|
|
{
|
|
struct vop_readlink_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
struct uio *a_uio;
|
|
struct ucred *a_cred;
|
|
} */ *ap = v;
|
|
struct uio *uio = ap->a_uio;
|
|
struct efs_inode *eip = EFS_VTOI(ap->a_vp);
|
|
char *buf;
|
|
size_t len;
|
|
int err, i;
|
|
|
|
if ((eip->ei_mode & EFS_IFMT) != EFS_IFLNK)
|
|
return (EINVAL);
|
|
|
|
if (uio->uio_resid < 1)
|
|
return (EINVAL);
|
|
|
|
buf = malloc(eip->ei_size + 1, M_EFSTMP, M_ZERO | M_WAITOK);
|
|
|
|
/* symlinks are either inlined in the inode, or in extents */
|
|
if (eip->ei_numextents == 0) {
|
|
if (eip->ei_size > sizeof(eip->ei_di.di_symlink)) {
|
|
EFS_DPRINTF(("efs_readlink: too big for inline\n"));
|
|
free(buf, M_EFSTMP);
|
|
return (EBADF);
|
|
}
|
|
|
|
memcpy(buf, eip->ei_di.di_symlink, eip->ei_size);
|
|
len = MIN(uio->uio_resid, eip->ei_size + 1);
|
|
} else {
|
|
struct efs_extent_iterator exi;
|
|
struct efs_extent ex;
|
|
struct buf *bp;
|
|
int resid, off, ret;
|
|
|
|
off = 0;
|
|
resid = eip->ei_size;
|
|
|
|
efs_extent_iterator_init(&exi, eip, 0);
|
|
while ((ret = efs_extent_iterator_next(&exi, &ex)) == 0) {
|
|
for (i = 0; i < ex.ex_length; i++) {
|
|
err = efs_bread(VFSTOEFS(ap->a_vp->v_mount),
|
|
ex.ex_bn + i, NULL, &bp);
|
|
if (err) {
|
|
free(buf, M_EFSTMP);
|
|
return (err);
|
|
}
|
|
|
|
len = MIN(resid, bp->b_bcount);
|
|
memcpy(buf + off, bp->b_data, len);
|
|
brelse(bp, 0);
|
|
|
|
off += len;
|
|
resid -= len;
|
|
|
|
if (resid == 0)
|
|
break;
|
|
}
|
|
|
|
if (resid == 0)
|
|
break;
|
|
}
|
|
|
|
if (ret != 0 && ret != -1) {
|
|
free(buf, M_EFSTMP);
|
|
return (ret);
|
|
}
|
|
|
|
len = off + 1;
|
|
}
|
|
|
|
KASSERT(len >= 1 && len <= (eip->ei_size + 1));
|
|
buf[len - 1] = '\0';
|
|
err = uiomove(buf, len, uio);
|
|
free(buf, M_EFSTMP);
|
|
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* Release an inactive vnode. The vnode _must_ be unlocked on return.
|
|
* It is either nolonger being used by the kernel, or an unmount is being
|
|
* forced.
|
|
*
|
|
* Returns 0 on success.
|
|
*/
|
|
static int
|
|
efs_inactive(void *v)
|
|
{
|
|
struct vop_inactive_v2_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
bool *a_recycle
|
|
} */ *ap = v;
|
|
struct efs_inode *eip = EFS_VTOI(ap->a_vp);
|
|
|
|
*ap->a_recycle = (eip->ei_mode == 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
efs_reclaim(void *v)
|
|
{
|
|
struct vop_reclaim_v2_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
} */ *ap = v;
|
|
struct vnode *vp = ap->a_vp;
|
|
struct efs_inode *eip = EFS_VTOI(vp);
|
|
|
|
VOP_UNLOCK(vp);
|
|
|
|
genfs_node_destroy(vp);
|
|
pool_put(&efs_inode_pool, eip);
|
|
vp->v_data = NULL;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
efs_bmap(void *v)
|
|
{
|
|
struct vop_bmap_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
daddr_t a_bn;
|
|
struct vnode **a_vpp;
|
|
daddr_t *a_bnp;
|
|
int *a_runp;
|
|
} */ *ap = v;
|
|
struct efs_extent ex;
|
|
struct efs_extent_iterator exi;
|
|
struct vnode *vp = ap->a_vp;
|
|
struct efs_inode *eip = EFS_VTOI(vp);
|
|
bool found;
|
|
int ret;
|
|
|
|
if (ap->a_vpp != NULL)
|
|
*ap->a_vpp = VFSTOEFS(vp->v_mount)->em_devvp;
|
|
|
|
found = false;
|
|
efs_extent_iterator_init(&exi, eip, ap->a_bn * EFS_BB_SIZE);
|
|
while ((ret = efs_extent_iterator_next(&exi, &ex)) == 0) {
|
|
if (ap->a_bn >= ex.ex_offset &&
|
|
ap->a_bn < (ex.ex_offset + ex.ex_length)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KASSERT(!found || ret == 0);
|
|
|
|
if (!found) {
|
|
EFS_DPRINTF(("efs_bmap: ap->a_bn not in extents\n"));
|
|
return ((ret == -1) ? EIO : ret);
|
|
}
|
|
|
|
if (ex.ex_magic != EFS_EXTENT_MAGIC) {
|
|
EFS_DPRINTF(("efs_bmap: exn.ex_magic != EFS_EXTENT_MAGIC\n"));
|
|
return (EIO);
|
|
}
|
|
|
|
if (ap->a_bn < ex.ex_offset) {
|
|
EFS_DPRINTF(("efs_bmap: ap->a_bn < exn.ex_offset\n"));
|
|
return (EIO);
|
|
}
|
|
|
|
KASSERT(ap->a_bn >= ex.ex_offset);
|
|
KASSERT(ex.ex_length > ap->a_bn - ex.ex_offset);
|
|
|
|
*ap->a_bnp = ex.ex_bn + (ap->a_bn - ex.ex_offset);
|
|
if (ap->a_runp != NULL)
|
|
*ap->a_runp = ex.ex_length - (ap->a_bn - ex.ex_offset) - 1;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
efs_strategy(void *v)
|
|
{
|
|
struct vop_strategy_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
struct buf *a_bp;
|
|
} */ *ap = v;
|
|
struct vnode *vp = ap->a_vp;
|
|
struct buf *bp = ap->a_bp;
|
|
int error;
|
|
|
|
if (vp == NULL) {
|
|
bp->b_error = EIO;
|
|
biodone(bp);
|
|
return (EIO);
|
|
}
|
|
|
|
if (bp->b_blkno == bp->b_lblkno) {
|
|
error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
|
|
if (error) {
|
|
bp->b_error = error;
|
|
biodone(bp);
|
|
return (error);
|
|
}
|
|
if ((long)bp->b_blkno == -1)
|
|
clrbuf(bp);
|
|
}
|
|
|
|
if ((long)bp->b_blkno == -1) {
|
|
biodone(bp);
|
|
return (0);
|
|
}
|
|
|
|
return (VOP_STRATEGY(VFSTOEFS(vp->v_mount)->em_devvp, bp));
|
|
}
|
|
|
|
static int
|
|
efs_print(void *v)
|
|
{
|
|
struct vop_print_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
} */ *ap = v;
|
|
struct efs_inode *eip = EFS_VTOI(ap->a_vp);
|
|
|
|
printf( "efs_inode (ino %lu):\n"
|
|
" ei_mode: %07o\n"
|
|
" ei_nlink: %d\n"
|
|
" ei_uid: %d\n"
|
|
" ei_gid: %d\n"
|
|
" ei_size: %d\n"
|
|
" ei_atime: %d\n"
|
|
" ei_mtime: %d\n"
|
|
" ei_ctime: %d\n"
|
|
" ei_gen: %d\n"
|
|
" ei_numextents: %d\n"
|
|
" ei_version: %d\n",
|
|
(unsigned long)eip->ei_number,
|
|
(unsigned int)eip->ei_mode,
|
|
eip->ei_nlink,
|
|
eip->ei_uid,
|
|
eip->ei_gid,
|
|
eip->ei_size,
|
|
(int32_t)eip->ei_atime,
|
|
(int32_t)eip->ei_mtime,
|
|
(int32_t)eip->ei_ctime,
|
|
eip->ei_gen,
|
|
eip->ei_numextents,
|
|
eip->ei_version);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
efs_pathconf(void *v)
|
|
{
|
|
struct vop_pathconf_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
int a_name;
|
|
register_t *a_retval;
|
|
} */ *ap = v;
|
|
|
|
/* IRIX 4 values */
|
|
switch (ap->a_name) {
|
|
case _PC_LINK_MAX:
|
|
*ap->a_retval = 30000;
|
|
break;
|
|
case _PC_NAME_MAX:
|
|
*ap->a_retval = 255;
|
|
break;
|
|
case _PC_PATH_MAX:
|
|
*ap->a_retval = 1024;
|
|
break;
|
|
case _PC_NO_TRUNC:
|
|
*ap->a_retval = 1;
|
|
break;
|
|
case _PC_CHOWN_RESTRICTED:
|
|
*ap->a_retval = 1;
|
|
break;
|
|
case _PC_SYNC_IO:
|
|
*ap->a_retval = 1;
|
|
break;
|
|
case _PC_FILESIZEBITS:
|
|
*ap->a_retval = 32;
|
|
break;
|
|
default:
|
|
return genfs_pathconf(ap);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
efs_advlock(void *v)
|
|
{
|
|
struct vop_advlock_args /* {
|
|
const struct vnodeop_desc *a_desc;
|
|
struct vnode *a_vp;
|
|
void *a_id;
|
|
int a_op;
|
|
struct flock *a_fl;
|
|
int a_flags;
|
|
} */ *ap = v;
|
|
struct efs_inode *eip = EFS_VTOI(ap->a_vp);
|
|
|
|
return (lf_advlock(ap, &eip->ei_lockf, eip->ei_size));
|
|
}
|
|
|
|
/* Global vfs data structures for efs */
|
|
int (**efs_vnodeop_p)(void *);
|
|
const struct vnodeopv_entry_desc efs_vnodeop_entries[] = {
|
|
{ &vop_default_desc, vn_default_error}, /* error handler */
|
|
{ &vop_parsepath_desc, genfs_parsepath }, /* parsepath */
|
|
{ &vop_lookup_desc, efs_lookup }, /* lookup */
|
|
{ &vop_create_desc, genfs_eopnotsupp}, /* create */
|
|
{ &vop_mknod_desc, genfs_eopnotsupp}, /* mknod */
|
|
{ &vop_open_desc, genfs_nullop }, /* open */
|
|
{ &vop_close_desc, genfs_nullop }, /* close */
|
|
{ &vop_access_desc, efs_access }, /* access */
|
|
{ &vop_accessx_desc, genfs_accessx }, /* accessx */
|
|
{ &vop_getattr_desc, efs_getattr }, /* getattr */
|
|
{ &vop_setattr_desc, genfs_eopnotsupp}, /* setattr */
|
|
{ &vop_read_desc, efs_read }, /* read */
|
|
{ &vop_write_desc, genfs_eopnotsupp}, /* write */
|
|
{ &vop_fallocate_desc, genfs_eopnotsupp}, /* fallocate */
|
|
{ &vop_fdiscard_desc, genfs_eopnotsupp}, /* fdiscard */
|
|
{ &vop_ioctl_desc, genfs_enoioctl }, /* ioctl */
|
|
{ &vop_fcntl_desc, genfs_fcntl }, /* fcntl */
|
|
{ &vop_poll_desc, genfs_poll }, /* poll */
|
|
{ &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */
|
|
{ &vop_revoke_desc, genfs_revoke }, /* revoke */
|
|
{ &vop_mmap_desc, genfs_mmap }, /* mmap */
|
|
{ &vop_fsync_desc, genfs_eopnotsupp}, /* fsync */
|
|
{ &vop_seek_desc, genfs_seek }, /* seek */
|
|
{ &vop_remove_desc, genfs_eopnotsupp}, /* remove */
|
|
{ &vop_link_desc, genfs_eopnotsupp}, /* link */
|
|
{ &vop_rename_desc, genfs_eopnotsupp}, /* rename */
|
|
{ &vop_mkdir_desc, genfs_eopnotsupp}, /* mkdir */
|
|
{ &vop_rmdir_desc, genfs_eopnotsupp}, /* rmdir */
|
|
{ &vop_symlink_desc, genfs_eopnotsupp}, /* symlink */
|
|
{ &vop_readdir_desc, efs_readdir }, /* readdir */
|
|
{ &vop_readlink_desc, efs_readlink }, /* readlink */
|
|
{ &vop_abortop_desc, genfs_abortop }, /* abortop */
|
|
{ &vop_inactive_desc, efs_inactive }, /* inactive */
|
|
{ &vop_reclaim_desc, efs_reclaim }, /* reclaim */
|
|
{ &vop_lock_desc, genfs_lock, }, /* lock */
|
|
{ &vop_unlock_desc, genfs_unlock, }, /* unlock */
|
|
{ &vop_islocked_desc, genfs_islocked, }, /* islocked */
|
|
{ &vop_bmap_desc, efs_bmap }, /* bmap */
|
|
{ &vop_print_desc, efs_print }, /* print */
|
|
{ &vop_pathconf_desc, efs_pathconf }, /* pathconf */
|
|
{ &vop_advlock_desc, efs_advlock }, /* advlock */
|
|
/* blkatoff */
|
|
/* valloc */
|
|
/* balloc */
|
|
/* vfree */
|
|
/* truncate */
|
|
/* whiteout */
|
|
{ &vop_getpages_desc, genfs_getpages }, /* getpages */
|
|
{ &vop_putpages_desc, genfs_putpages }, /* putpages */
|
|
{ &vop_bwrite_desc, vn_bwrite }, /* bwrite */
|
|
{ &vop_strategy_desc, efs_strategy }, /* strategy */
|
|
{ NULL, NULL }
|
|
};
|
|
const struct vnodeopv_desc efs_vnodeop_opv_desc = {
|
|
&efs_vnodeop_p,
|
|
efs_vnodeop_entries
|
|
};
|
|
|
|
int (**efs_specop_p)(void *);
|
|
const struct vnodeopv_entry_desc efs_specop_entries[] = {
|
|
{ &vop_default_desc, vn_default_error}, /* error handler */
|
|
GENFS_SPECOP_ENTRIES,
|
|
{ &vop_close_desc, spec_close }, /* close */
|
|
{ &vop_access_desc, efs_access }, /* access */
|
|
{ &vop_accessx_desc, genfs_accessx }, /* accessx */
|
|
{ &vop_getattr_desc, efs_getattr }, /* getattr */
|
|
{ &vop_setattr_desc, genfs_eopnotsupp}, /* setattr */
|
|
{ &vop_read_desc, spec_read }, /* read */
|
|
{ &vop_write_desc, spec_write }, /* write */
|
|
{ &vop_fcntl_desc, genfs_fcntl }, /* fcntl */
|
|
{ &vop_fsync_desc, spec_fsync }, /* fsync */
|
|
{ &vop_inactive_desc, efs_inactive }, /* inactive */
|
|
{ &vop_reclaim_desc, efs_reclaim }, /* reclaim */
|
|
{ &vop_lock_desc, genfs_lock, }, /* lock */
|
|
{ &vop_unlock_desc, genfs_unlock, }, /* unlock */
|
|
{ &vop_islocked_desc, genfs_islocked, }, /* islocked */
|
|
{ &vop_print_desc, efs_print }, /* print */
|
|
{ &vop_bwrite_desc, vn_bwrite }, /* bwrite */
|
|
{ NULL, NULL }
|
|
};
|
|
const struct vnodeopv_desc efs_specop_opv_desc = {
|
|
&efs_specop_p,
|
|
efs_specop_entries
|
|
};
|
|
|
|
int (**efs_fifoop_p)(void *);
|
|
const struct vnodeopv_entry_desc efs_fifoop_entries[] = {
|
|
{ &vop_default_desc, vn_default_error}, /* error handler */
|
|
GENFS_FIFOOP_ENTRIES,
|
|
{ &vop_close_desc, vn_fifo_bypass }, /* close */
|
|
{ &vop_access_desc, efs_access }, /* access */
|
|
{ &vop_accessx_desc, genfs_accessx }, /* accessx */
|
|
{ &vop_getattr_desc, efs_getattr }, /* getattr */
|
|
{ &vop_setattr_desc, genfs_eopnotsupp}, /* setattr */
|
|
{ &vop_read_desc, vn_fifo_bypass }, /* read */
|
|
{ &vop_write_desc, vn_fifo_bypass }, /* write */
|
|
{ &vop_fcntl_desc, genfs_fcntl }, /* fcntl */
|
|
{ &vop_fsync_desc, vn_fifo_bypass }, /* fsync */
|
|
{ &vop_inactive_desc, efs_inactive }, /* inactive */
|
|
{ &vop_reclaim_desc, efs_reclaim }, /* reclaim */
|
|
{ &vop_lock_desc, genfs_lock, }, /* lock */
|
|
{ &vop_unlock_desc, genfs_unlock, }, /* unlock */
|
|
{ &vop_islocked_desc, genfs_islocked, }, /* islocked */
|
|
{ &vop_strategy_desc, vn_fifo_bypass }, /* strategy */
|
|
{ &vop_print_desc, efs_print }, /* print */
|
|
{ &vop_bwrite_desc, vn_bwrite }, /* bwrite */
|
|
{ NULL, NULL }
|
|
};
|
|
const struct vnodeopv_desc efs_fifoop_opv_desc = {
|
|
&efs_fifoop_p,
|
|
efs_fifoop_entries
|
|
};
|