#include <sys/mountctl.h>
#include <sys/namecache.h>
#include <sys/buf2.h>
#include <vfs/fifofs/fifo.h>
#include "hammer.h"
static int hammer_vop_fsync(struct vop_fsync_args *);
static int hammer_vop_read(struct vop_read_args *);
static int hammer_vop_write(struct vop_write_args *);
static int hammer_vop_access(struct vop_access_args *);
static int hammer_vop_advlock(struct vop_advlock_args *);
static int hammer_vop_close(struct vop_close_args *);
static int hammer_vop_ncreate(struct vop_ncreate_args *);
static int hammer_vop_getattr(struct vop_getattr_args *);
static int hammer_vop_nresolve(struct vop_nresolve_args *);
static int hammer_vop_nlookupdotdot(struct vop_nlookupdotdot_args *);
static int hammer_vop_nlink(struct vop_nlink_args *);
static int hammer_vop_nmkdir(struct vop_nmkdir_args *);
static int hammer_vop_nmknod(struct vop_nmknod_args *);
static int hammer_vop_open(struct vop_open_args *);
static int hammer_vop_print(struct vop_print_args *);
static int hammer_vop_readdir(struct vop_readdir_args *);
static int hammer_vop_readlink(struct vop_readlink_args *);
static int hammer_vop_nremove(struct vop_nremove_args *);
static int hammer_vop_nrename(struct vop_nrename_args *);
static int hammer_vop_nrmdir(struct vop_nrmdir_args *);
static int hammer_vop_markatime(struct vop_markatime_args *);
static int hammer_vop_setattr(struct vop_setattr_args *);
static int hammer_vop_strategy(struct vop_strategy_args *);
static int hammer_vop_bmap(struct vop_bmap_args *ap);
static int hammer_vop_nsymlink(struct vop_nsymlink_args *);
static int hammer_vop_nwhiteout(struct vop_nwhiteout_args *);
static int hammer_vop_ioctl(struct vop_ioctl_args *);
static int hammer_vop_mountctl(struct vop_mountctl_args *);
static int hammer_vop_kqfilter (struct vop_kqfilter_args *);
static int hammer_vop_fifoclose (struct vop_close_args *);
static int hammer_vop_fiforead (struct vop_read_args *);
static int hammer_vop_fifowrite (struct vop_write_args *);
static int hammer_vop_fifokqfilter (struct vop_kqfilter_args *);
struct vop_ops hammer_vnode_vops = {
.vop_default = vop_defaultop,
.vop_fsync = hammer_vop_fsync,
.vop_getpages = vop_stdgetpages,
.vop_putpages = vop_stdputpages,
.vop_read = hammer_vop_read,
.vop_write = hammer_vop_write,
.vop_access = hammer_vop_access,
.vop_advlock = hammer_vop_advlock,
.vop_close = hammer_vop_close,
.vop_ncreate = hammer_vop_ncreate,
.vop_getattr = hammer_vop_getattr,
.vop_inactive = hammer_vop_inactive,
.vop_reclaim = hammer_vop_reclaim,
.vop_nresolve = hammer_vop_nresolve,
.vop_nlookupdotdot = hammer_vop_nlookupdotdot,
.vop_nlink = hammer_vop_nlink,
.vop_nmkdir = hammer_vop_nmkdir,
.vop_nmknod = hammer_vop_nmknod,
.vop_open = hammer_vop_open,
.vop_pathconf = vop_stdpathconf,
.vop_print = hammer_vop_print,
.vop_readdir = hammer_vop_readdir,
.vop_readlink = hammer_vop_readlink,
.vop_nremove = hammer_vop_nremove,
.vop_nrename = hammer_vop_nrename,
.vop_nrmdir = hammer_vop_nrmdir,
.vop_markatime = hammer_vop_markatime,
.vop_setattr = hammer_vop_setattr,
.vop_bmap = hammer_vop_bmap,
.vop_strategy = hammer_vop_strategy,
.vop_nsymlink = hammer_vop_nsymlink,
.vop_nwhiteout = hammer_vop_nwhiteout,
.vop_ioctl = hammer_vop_ioctl,
.vop_mountctl = hammer_vop_mountctl,
.vop_kqfilter = hammer_vop_kqfilter
};
struct vop_ops hammer_spec_vops = {
.vop_default = vop_defaultop,
.vop_fsync = hammer_vop_fsync,
.vop_read = vop_stdnoread,
.vop_write = vop_stdnowrite,
.vop_access = hammer_vop_access,
.vop_close = hammer_vop_close,
.vop_markatime = hammer_vop_markatime,
.vop_getattr = hammer_vop_getattr,
.vop_inactive = hammer_vop_inactive,
.vop_reclaim = hammer_vop_reclaim,
.vop_setattr = hammer_vop_setattr
};
struct vop_ops hammer_fifo_vops = {
.vop_default = fifo_vnoperate,
.vop_fsync = hammer_vop_fsync,
.vop_read = hammer_vop_fiforead,
.vop_write = hammer_vop_fifowrite,
.vop_access = hammer_vop_access,
.vop_close = hammer_vop_fifoclose,
.vop_markatime = hammer_vop_markatime,
.vop_getattr = hammer_vop_getattr,
.vop_inactive = hammer_vop_inactive,
.vop_reclaim = hammer_vop_reclaim,
.vop_setattr = hammer_vop_setattr,
.vop_kqfilter = hammer_vop_fifokqfilter
};
static __inline
void
hammer_knote(struct vnode *vp, int flags)
{
if (flags)
KNOTE(&vp->v_pollinfo.vpi_kqinfo.ki_note, flags);
}
static int hammer_dounlink(hammer_transaction_t trans, struct nchandle *nch,
struct vnode *dvp, struct ucred *cred,
int flags, int isdir);
static int hammer_vop_strategy_read(struct vop_strategy_args *ap);
static int hammer_vop_strategy_write(struct vop_strategy_args *ap);
static
int
hammer_vop_fsync(struct vop_fsync_args *ap)
{
hammer_inode_t ip = VTOI(ap->a_vp);
hammer_mount_t hmp = ip->hmp;
int waitfor = ap->a_waitfor;
int mode;
lwkt_gettoken(&hmp->fs_token);
if (ap->a_flags & VOP_FSYNC_SYSCALL) {
switch(hammer_fsync_mode) {
case 0:
mode0:
goto skip;
case 1:
mode1:
if (waitfor == MNT_WAIT)
waitfor = MNT_NOWAIT;
goto skip;
case 2:
if (hmp->version < HAMMER_VOL_VERSION_FOUR)
goto mode0;
mode = HAMMER_FLUSH_UNDOS_AUTO;
break;
case 3:
if (hmp->version < HAMMER_VOL_VERSION_FOUR)
goto mode1;
mode = HAMMER_FLUSH_UNDOS_RELAXED;
if (waitfor == MNT_WAIT)
waitfor = MNT_NOWAIT;
break;
case 4:
lwkt_reltoken(&hmp->fs_token);
return(0);
default:
mode = HAMMER_FLUSH_UNDOS_RELAXED;
if (waitfor == MNT_WAIT)
waitfor = MNT_NOWAIT;
break;
}
if ((ip->flags & HAMMER_INODE_REDO) &&
(ip->flags & HAMMER_INODE_MODMASK_NOREDO) == 0) {
++hammer_count_fsyncs;
hammer_flusher_flush_undos(hmp, mode);
ip->redo_count = 0;
if (ip->vp && (ip->flags & HAMMER_INODE_MODMASK) == 0)
vclrisdirty(ip->vp);
lwkt_reltoken(&hmp->fs_token);
return(0);
}
if (hmp->version >= HAMMER_VOL_VERSION_FOUR &&
(hmp->flags & HAMMER_MOUNT_REDO_RECOVERY_RUN) == 0) {
ip->flags |= HAMMER_INODE_REDO;
ip->redo_count = 0;
}
}
skip:
++hammer_count_fsyncs;
vfsync(ap->a_vp, waitfor, 1, NULL, NULL);
hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
if (waitfor == MNT_WAIT) {
int dorelock;
if ((ap->a_vp->v_flag & VRECLAIMED) == 0) {
vn_unlock(ap->a_vp);
dorelock = 1;
} else {
dorelock = 0;
}
hammer_wait_inode(ip);
if (dorelock)
vn_relock(ap->a_vp, LK_EXCLUSIVE);
}
if (ip->vp && (ip->flags & HAMMER_INODE_MODMASK) == 0)
vclrisdirty(ip->vp);
lwkt_reltoken(&hmp->fs_token);
return (ip->error);
}
static
int
hammer_vop_read(struct vop_read_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t ip;
hammer_mount_t hmp;
off_t offset;
struct buf *bp;
struct uio *uio;
int error;
int n;
int seqcount;
int ioseqcount;
int blksize;
int bigread;
int got_trans;
size_t resid;
if (ap->a_vp->v_type == VDIR)
return (EISDIR);
if (ap->a_vp->v_type != VREG)
return (EINVAL);
ip = VTOI(ap->a_vp);
hmp = ip->hmp;
error = 0;
got_trans = 0;
uio = ap->a_uio;
resid = uio->uio_resid;
error = vop_helper_read_shortcut(ap);
hammer_stats_file_read += resid - uio->uio_resid;
if (error)
return (error);
if (uio->uio_resid == 0)
goto finished;
blksize = hammer_blocksize(uio->uio_offset);
seqcount = howmany(uio->uio_resid, MAXBSIZE);
ioseqcount = ap->a_ioflag >> IO_SEQSHIFT;
if (seqcount < ioseqcount)
seqcount = ioseqcount;
bigread = (uio->uio_resid > 100 * 1024 * 1024);
while (uio->uio_resid > 0 && uio->uio_offset < ip->ino_data.size) {
int64_t base_offset;
int64_t file_limit;
blksize = hammer_blocksize(uio->uio_offset);
offset = (int)uio->uio_offset & (blksize - 1);
base_offset = uio->uio_offset - offset;
if (bigread && (error = hammer_signal_check(ip->hmp)) != 0)
break;
bp = getblk(ap->a_vp, base_offset, blksize, 0, 0);
if ((bp->b_flags & (B_INVAL | B_CACHE | B_RAM)) == B_CACHE) {
bp->b_flags &= ~B_AGE;
error = 0;
goto skip;
}
if (ap->a_ioflag & IO_NRDELAY) {
bqrelse(bp);
return (EWOULDBLOCK);
}
if (got_trans == 0) {
hammer_start_transaction(&trans, ip->hmp);
got_trans = 1;
}
if (hammer_cluster_enable) {
file_limit = ip->ino_data.size;
if (base_offset < HAMMER_XDEMARC &&
file_limit > HAMMER_XDEMARC) {
file_limit = HAMMER_XDEMARC;
}
error = cluster_readx(ap->a_vp,
file_limit, base_offset,
blksize, B_NOTMETA,
uio->uio_resid,
seqcount * MAXBSIZE,
&bp);
} else {
error = breadnx(ap->a_vp, base_offset,
blksize, B_NOTMETA,
NULL, NULL, 0, &bp);
}
if (error) {
brelse(bp);
break;
}
skip:
if ((hammer_debug_io & 0x0001) && (bp->b_flags & B_IOISSUED)) {
hdkprintf("zone2_offset %016jx read file %016jx@%016jx\n",
(intmax_t)bp->b_bio2.bio_offset,
(intmax_t)ip->obj_id,
(intmax_t)bp->b_loffset);
}
bp->b_flags &= ~B_IOISSUED;
if (blksize == HAMMER_XBUFSIZE)
bp->b_flags |= B_CLUSTEROK;
n = blksize - offset;
if (n > uio->uio_resid)
n = uio->uio_resid;
if (n > ip->ino_data.size - uio->uio_offset)
n = (int)(ip->ino_data.size - uio->uio_offset);
bp->b_flags |= B_AGE;
error = uiomovebp(bp, (char *)bp->b_data + offset, n, uio);
bqrelse(bp);
if (error)
break;
hammer_stats_file_read += n;
}
finished:
if (got_trans == 0 && hammer_update_atime_quick(ip) < 0) {
hammer_start_transaction(&trans, ip->hmp);
got_trans = 1;
}
if (got_trans) {
if ((ip->flags & HAMMER_INODE_RO) == 0 &&
(ip->hmp->mp->mnt_flag & MNT_NOATIME) == 0) {
lwkt_gettoken(&hmp->fs_token);
ip->ino_data.atime = trans.time;
hammer_modify_inode(&trans, ip, HAMMER_INODE_ATIME);
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
} else {
hammer_done_transaction(&trans);
}
}
return (error);
}
static
int
hammer_vop_write(struct vop_write_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t ip;
hammer_mount_t hmp;
thread_t td;
struct vnode *vp;
struct uio *uio;
int offset;
off_t base_offset;
int64_t cluster_eof;
struct buf *bp;
int kflags;
int error;
int n;
int flags;
int seqcount;
int bigwrite;
vp = ap->a_vp;
if (vp->v_type != VREG)
return (EINVAL);
ip = VTOI(ap->a_vp);
hmp = ip->hmp;
error = 0;
kflags = 0;
seqcount = ap->a_ioflag >> IO_SEQSHIFT;
if (ip->flags & HAMMER_INODE_RO)
return (EROFS);
hammer_start_transaction(&trans, hmp);
uio = ap->a_uio;
if (uio->uio_segflg == UIO_NOCOPY) {
if (vp->v_flag & VLASTWRITETS) {
trans.time = vp->v_lastwrite_ts.tv_sec * 1000000 +
vp->v_lastwrite_ts.tv_nsec / 1000;
} else {
trans.time = ip->ino_data.mtime;
}
} else {
vclrflags(vp, VLASTWRITETS);
}
if (ap->a_ioflag & IO_APPEND)
uio->uio_offset = ip->ino_data.size;
if (uio->uio_offset < 0) {
hammer_done_transaction(&trans);
return (EFBIG);
}
base_offset = uio->uio_offset + uio->uio_resid;
if (uio->uio_resid > 0 && base_offset <= uio->uio_offset) {
hammer_done_transaction(&trans);
return (EFBIG);
}
if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc &&
base_offset > td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
hammer_done_transaction(&trans);
lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
return (EFBIG);
}
bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
if ((ip->flags & HAMMER_INODE_REDO) &&
ip->redo_count < hammer_limit_redo) {
ip->redo_count += uio->uio_resid;
}
while (uio->uio_resid > 0) {
int fixsize = 0;
int blksize;
int blkmask;
int trivial;
int endofblk;
off_t nsize;
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_WRITE)) != 0)
break;
if (bigwrite && (error = hammer_signal_check(hmp)) != 0)
break;
blksize = hammer_blocksize(uio->uio_offset);
if (ip->rsv_recs >= hammer_limit_inode_recs) {
lwkt_gettoken(&hmp->fs_token);
hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
while (ip->rsv_recs >= hammer_limit_inode_recs * 2) {
ip->flags |= HAMMER_INODE_RECSW;
tsleep(&ip->rsv_recs, 0, "hmrwww", hz);
hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
}
lwkt_reltoken(&hmp->fs_token);
}
if ((ap->a_ioflag & IO_RECURSE) == 0)
bwillwrite(blksize);
blkmask = blksize - 1;
offset = (int)uio->uio_offset & blkmask;
base_offset = uio->uio_offset & ~(int64_t)blkmask;
n = blksize - offset;
if (n > uio->uio_resid) {
n = uio->uio_resid;
endofblk = 0;
} else {
endofblk = 1;
}
nsize = uio->uio_offset + n;
if (nsize > ip->ino_data.size) {
if (uio->uio_offset > ip->ino_data.size)
trivial = 0;
else
trivial = NVEXTF_TRIVIAL;
nvextendbuf(ap->a_vp,
ip->ino_data.size,
nsize,
hammer_blocksize(ip->ino_data.size),
hammer_blocksize(nsize),
hammer_blockoff(ip->ino_data.size),
hammer_blockoff(nsize),
trivial);
fixsize = 1;
kflags |= NOTE_EXTEND;
}
if (uio->uio_segflg == UIO_NOCOPY) {
bp = getblk(ap->a_vp, base_offset,
blksize, GETBLK_BHEAVY, 0);
if ((bp->b_flags & B_CACHE) == 0) {
bqrelse(bp);
error = bread(ap->a_vp, base_offset,
blksize, &bp);
}
} else if (offset == 0 && uio->uio_resid >= blksize) {
bp = getblk(ap->a_vp, base_offset, blksize, GETBLK_BHEAVY, 0);
if ((bp->b_flags & B_CACHE) == 0)
vfs_bio_clrbuf(bp);
} else if (base_offset >= ip->ino_data.size) {
bp = getblk(ap->a_vp, base_offset,
blksize, GETBLK_BHEAVY, 0);
vfs_bio_clrbuf(bp);
} else {
error = bread(ap->a_vp, base_offset, blksize, &bp);
if (error == 0)
bheavy(bp);
}
if (error == 0)
error = uiomovebp(bp, bp->b_data + offset, n, uio);
lwkt_gettoken(&hmp->fs_token);
if ((ip->flags & HAMMER_INODE_REDO) && error == 0) {
if (ip->redo_count < hammer_limit_redo) {
bp->b_flags |= B_VFSFLAG1;
error = hammer_generate_redo(&trans, ip,
base_offset + offset,
HAMMER_REDO_WRITE,
bp->b_data + offset,
(size_t)n);
} else {
ip->flags &= ~HAMMER_INODE_REDO;
}
}
if (error) {
brelse(bp);
if (fixsize) {
nvtruncbuf(ap->a_vp, ip->ino_data.size,
hammer_blocksize(ip->ino_data.size),
hammer_blockoff(ip->ino_data.size),
0);
}
lwkt_reltoken(&hmp->fs_token);
break;
}
kflags |= NOTE_WRITE;
hammer_stats_file_write += n;
if (blksize == HAMMER_XBUFSIZE)
bp->b_flags |= B_CLUSTEROK;
if (ip->ino_data.size < uio->uio_offset) {
ip->ino_data.size = uio->uio_offset;
flags = HAMMER_INODE_SDIRTY;
} else {
flags = 0;
}
ip->ino_data.mtime = trans.time;
flags |= HAMMER_INODE_MTIME | HAMMER_INODE_BUFS;
hammer_modify_inode(&trans, ip, flags);
bp->b_bio2.bio_offset = NOOFFSET;
lwkt_reltoken(&hmp->fs_token);
bp->b_flags |= B_AGE;
if (blksize == HAMMER_XBUFSIZE)
bp->b_flags |= B_CLUSTEROK;
if (ap->a_ioflag & IO_SYNC) {
bwrite(bp);
} else if ((ap->a_ioflag & IO_DIRECT) && endofblk) {
bawrite(bp);
} else if (ap->a_ioflag & IO_ASYNC) {
bawrite(bp);
} else if (hammer_cluster_enable &&
!(ap->a_vp->v_mount->mnt_flag & MNT_NOCLUSTERW)) {
if (base_offset < HAMMER_XDEMARC)
cluster_eof = hammer_blockdemarc(base_offset,
ip->ino_data.size);
else
cluster_eof = ip->ino_data.size;
cluster_write(bp, cluster_eof, blksize, seqcount);
} else {
bdwrite(bp);
}
}
hammer_done_transaction(&trans);
hammer_knote(ap->a_vp, kflags);
return (error);
}
static
int
hammer_vop_access(struct vop_access_args *ap)
{
hammer_inode_t ip = VTOI(ap->a_vp);
uid_t uid;
gid_t gid;
int error;
uid = hammer_to_unix_xid(&ip->ino_data.uid);
gid = hammer_to_unix_xid(&ip->ino_data.gid);
error = vop_helper_access(ap, uid, gid, ip->ino_data.mode,
ip->ino_data.uflags);
return (error);
}
static
int
hammer_vop_advlock(struct vop_advlock_args *ap)
{
hammer_inode_t ip = VTOI(ap->a_vp);
return (lf_advlock(ap, &ip->advlock, ip->ino_data.size));
}
static
int
hammer_vop_close(struct vop_close_args *ap)
{
#if 0
struct vnode *vp = ap->a_vp;
hammer_inode_t ip = VTOI(vp);
int waitfor;
if (ip->flags & (HAMMER_INODE_CLOSESYNC|HAMMER_INODE_CLOSEASYNC)) {
if (vn_islocked(vp) == LK_EXCLUSIVE &&
(vp->v_flag & (VINACTIVE|VRECLAIMED)) == 0) {
if (ip->flags & HAMMER_INODE_CLOSESYNC)
waitfor = MNT_WAIT;
else
waitfor = MNT_NOWAIT;
ip->flags &= ~(HAMMER_INODE_CLOSESYNC |
HAMMER_INODE_CLOSEASYNC);
VOP_FSYNC(vp, MNT_NOWAIT, waitfor);
}
}
#endif
return (vop_stdclose(ap));
}
static
int
hammer_vop_ncreate(struct vop_ncreate_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_inode_t nip;
struct nchandle *nch;
hammer_mount_t hmp;
int error;
nch = ap->a_nch;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (dip->flags & HAMMER_INODE_RO)
return (EROFS);
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0)
return (error);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
dip, nch->ncp->nc_name, nch->ncp->nc_nlen,
NULL, &nip);
if (error) {
hkprintf("hammer_create_inode error %d\n", error);
hammer_done_transaction(&trans);
*ap->a_vpp = NULL;
lwkt_reltoken(&hmp->fs_token);
return (error);
}
error = hammer_ip_add_direntry(&trans, dip,
nch->ncp->nc_name, nch->ncp->nc_nlen,
nip);
if (error)
hkprintf("hammer_ip_add_direntry error %d\n", error);
if (error) {
hammer_rel_inode(nip, 0);
hammer_done_transaction(&trans);
*ap->a_vpp = NULL;
} else {
error = hammer_get_vnode(nip, ap->a_vpp);
hammer_done_transaction(&trans);
hammer_rel_inode(nip, 0);
if (error == 0) {
cache_setunresolved(ap->a_nch);
cache_setvp(ap->a_nch, *ap->a_vpp);
}
hammer_knote(ap->a_dvp, NOTE_WRITE);
}
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_getattr(struct vop_getattr_args *ap)
{
hammer_inode_t ip = VTOI(ap->a_vp);
struct vattr *vap = ap->a_vap;
hammer_lock_sh(&ip->lock);
vap->va_fsid = ip->pfsm->fsid_udev ^ (uint32_t)ip->obj_asof ^
(uint32_t)(ip->obj_asof >> 32);
vap->va_fileid = ip->ino_leaf.base.obj_id;
vap->va_mode = ip->ino_data.mode;
vap->va_nlink = ip->ino_data.nlinks;
vap->va_uid = hammer_to_unix_xid(&ip->ino_data.uid);
vap->va_gid = hammer_to_unix_xid(&ip->ino_data.gid);
vap->va_rmajor = 0;
vap->va_rminor = 0;
vap->va_size = ip->ino_data.size;
if (ip->ino_data.obj_type == HAMMER_OBJTYPE_SOFTLINK &&
ip->ino_data.size == 10 &&
ip->obj_asof == HAMMER_MAX_TID &&
ip->obj_localization == HAMMER_DEF_LOCALIZATION &&
strncmp(ip->ino_data.ext.symlink, "@@PFS", 5) == 0) {
if (hammer_is_pfs_slave(&ip->pfsm->pfsd))
vap->va_size = 26;
else
vap->va_size = 10;
}
if (ip->flags & HAMMER_INODE_RO) {
hammer_time_to_timespec(ip->ino_data.ctime, &vap->va_atime);
hammer_time_to_timespec(ip->ino_data.ctime, &vap->va_mtime);
} else {
hammer_time_to_timespec(ip->ino_data.atime, &vap->va_atime);
hammer_time_to_timespec(ip->ino_data.mtime, &vap->va_mtime);
}
hammer_time_to_timespec(ip->ino_data.ctime, &vap->va_ctime);
vap->va_flags = ip->ino_data.uflags;
vap->va_gen = 1;
vap->va_blocksize = HAMMER_BUFSIZE;
if (ip->ino_data.size >= HAMMER_XDEMARC) {
vap->va_bytes = HAMMER_XBUFSIZE64_DOALIGN(ip->ino_data.size);
} else if (ip->ino_data.size > HAMMER_HBUFSIZE) {
vap->va_bytes = HAMMER_BUFSIZE64_DOALIGN(ip->ino_data.size);
} else {
vap->va_bytes = HAMMER_DATA_DOALIGN(ip->ino_data.size);
}
vap->va_type = hammer_get_vnode_type(ip->ino_data.obj_type);
vap->va_filerev = 0;
vap->va_uid_uuid = ip->ino_data.uid;
vap->va_gid_uuid = ip->ino_data.gid;
vap->va_fsid_uuid = ip->hmp->fsid;
vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
VA_FSID_UUID_VALID;
switch (ip->ino_data.obj_type) {
case HAMMER_OBJTYPE_CDEV:
case HAMMER_OBJTYPE_BDEV:
vap->va_rmajor = ip->ino_data.rmajor;
vap->va_rminor = ip->ino_data.rminor;
break;
default:
break;
}
hammer_unlock(&ip->lock);
return(0);
}
static
int
hammer_vop_nresolve(struct vop_nresolve_args *ap)
{
struct hammer_transaction trans;
struct namecache *ncp;
hammer_mount_t hmp;
hammer_inode_t dip;
hammer_inode_t ip;
hammer_tid_t asof;
struct hammer_cursor cursor;
struct vnode *vp;
int64_t namekey;
int error;
int i;
int nlen;
int flags;
int ispfs;
int64_t obj_id;
uint32_t localization;
uint32_t max_iterations;
dip = VTOI(ap->a_dvp);
ncp = ap->a_nch->ncp;
asof = dip->obj_asof;
localization = dip->obj_localization;
nlen = ncp->nc_nlen;
flags = dip->flags & HAMMER_INODE_RO;
ispfs = 0;
hmp = dip->hmp;
lwkt_gettoken(&hmp->fs_token);
hammer_simple_transaction(&trans, hmp);
for (i = 0; i < nlen; ++i) {
if (ncp->nc_name[i] == '@' && ncp->nc_name[i+1] == '@') {
error = hammer_str_to_tid(ncp->nc_name + i + 2,
&ispfs, &asof, &localization);
if (error != 0) {
i = nlen;
break;
}
if (asof != HAMMER_MAX_TID)
flags |= HAMMER_INODE_RO;
break;
}
}
nlen = i;
if (ispfs && nlen == 0) {
ip = hammer_get_inode(&trans, dip, HAMMER_OBJID_ROOT,
asof, localization,
flags, &error);
if (error == 0) {
error = hammer_get_vnode(ip, &vp);
hammer_rel_inode(ip, 0);
} else {
vp = NULL;
}
if (error == 0) {
vn_unlock(vp);
cache_setvp(ap->a_nch, vp);
vrele(vp);
}
goto done;
}
if (nlen == 0 || (nlen == 1 && ncp->nc_name[0] == '.')) {
ip = hammer_get_inode(&trans, dip, dip->obj_id,
asof, dip->obj_localization,
flags, &error);
if (error == 0) {
error = hammer_get_vnode(ip, &vp);
hammer_rel_inode(ip, 0);
} else {
vp = NULL;
}
if (error == 0) {
vn_unlock(vp);
cache_setvp(ap->a_nch, vp);
vrele(vp);
}
goto done;
}
namekey = hammer_direntry_namekey(dip, ncp->nc_name, nlen,
&max_iterations);
error = hammer_init_cursor(&trans, &cursor, &dip->cache[1], dip);
cursor.key_beg.localization = dip->obj_localization |
hammer_dir_localization(dip);
cursor.key_beg.obj_id = dip->obj_id;
cursor.key_beg.key = namekey;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
cursor.key_beg.obj_type = 0;
cursor.key_end = cursor.key_beg;
cursor.key_end.key += max_iterations;
cursor.asof = asof;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE | HAMMER_CURSOR_ASOF;
obj_id = 0;
localization = HAMMER_DEF_LOCALIZATION;
if (error == 0) {
error = hammer_ip_first(&cursor);
while (error == 0) {
error = hammer_ip_resolve_data(&cursor);
if (error)
break;
if (nlen == cursor.leaf->data_len - HAMMER_ENTRY_NAME_OFF &&
bcmp(ncp->nc_name, cursor.data->entry.name, nlen) == 0) {
obj_id = cursor.data->entry.obj_id;
localization = cursor.data->entry.localization;
break;
}
error = hammer_ip_next(&cursor);
}
}
hammer_done_cursor(&cursor);
if (error == 0) {
ip = hammer_get_inode(&trans, dip, obj_id,
asof, localization,
flags, &error);
if (error == ENOENT) {
hkprintf("WARNING: Missing inode for dirent \"%s\"\n"
"\tobj_id = %016jx, asof=%016jx, lo=%08x\n",
ncp->nc_name,
(intmax_t)obj_id, (intmax_t)asof,
localization);
error = 0;
ip = hammer_get_dummy_inode(&trans, dip, obj_id,
asof, localization,
flags, &error);
}
if (error == 0) {
error = hammer_get_vnode(ip, &vp);
hammer_rel_inode(ip, 0);
} else {
vp = NULL;
}
if (error == 0) {
vn_unlock(vp);
cache_setvp(ap->a_nch, vp);
vrele(vp);
}
} else if (error == ENOENT) {
cache_setvp(ap->a_nch, NULL);
}
done:
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_inode_t ip;
hammer_mount_t hmp;
int64_t parent_obj_id;
uint32_t parent_obj_localization;
hammer_tid_t asof;
int error;
dip = VTOI(ap->a_dvp);
asof = dip->obj_asof;
hmp = dip->hmp;
lwkt_gettoken(&hmp->fs_token);
parent_obj_id = dip->ino_data.parent_obj_id;
if (dip->obj_id == HAMMER_OBJID_ROOT)
parent_obj_localization = HAMMER_DEF_LOCALIZATION;
else
parent_obj_localization = dip->obj_localization;
if (parent_obj_id == 0) {
if (dip->obj_id == HAMMER_OBJID_ROOT &&
asof != hmp->asof) {
parent_obj_id = dip->obj_id;
asof = hmp->asof;
*ap->a_fakename = kmalloc(19, M_TEMP, M_WAITOK);
ksnprintf(*ap->a_fakename, 19, "0x%016jx",
(intmax_t)dip->obj_asof);
} else {
*ap->a_vpp = NULL;
lwkt_reltoken(&hmp->fs_token);
return ENOENT;
}
}
hammer_simple_transaction(&trans, hmp);
ip = hammer_get_inode(&trans, dip, parent_obj_id,
asof, parent_obj_localization,
dip->flags, &error);
if (ip) {
error = hammer_get_vnode(ip, ap->a_vpp);
hammer_rel_inode(ip, 0);
} else {
*ap->a_vpp = NULL;
}
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nlink(struct vop_nlink_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_inode_t ip;
struct nchandle *nch;
hammer_mount_t hmp;
int error;
if (ap->a_dvp->v_mount != ap->a_vp->v_mount)
return(EXDEV);
nch = ap->a_nch;
dip = VTOI(ap->a_dvp);
ip = VTOI(ap->a_vp);
hmp = dip->hmp;
if (dip->obj_localization != ip->obj_localization)
return(EXDEV);
if (dip->flags & HAMMER_INODE_RO)
return (EROFS);
if (ip->flags & HAMMER_INODE_RO)
return (EROFS);
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0)
return (error);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_ip_add_direntry(&trans, dip,
nch->ncp->nc_name, nch->ncp->nc_nlen,
ip);
if (error == 0) {
cache_setunresolved(nch);
cache_setvp(nch, ap->a_vp);
}
hammer_done_transaction(&trans);
hammer_knote(ap->a_vp, NOTE_LINK);
hammer_knote(ap->a_dvp, NOTE_WRITE);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nmkdir(struct vop_nmkdir_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_inode_t nip;
struct nchandle *nch;
hammer_mount_t hmp;
int error;
nch = ap->a_nch;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (dip->flags & HAMMER_INODE_RO)
return (EROFS);
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0)
return (error);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
dip, nch->ncp->nc_name, nch->ncp->nc_nlen,
NULL, &nip);
if (error) {
hammer_done_transaction(&trans);
*ap->a_vpp = NULL;
lwkt_reltoken(&hmp->fs_token);
return (error);
}
error = hammer_ip_add_direntry(&trans, dip,
nch->ncp->nc_name, nch->ncp->nc_nlen,
nip);
if (error)
hkprintf("hammer_mkdir (add) error %d\n", error);
if (error) {
hammer_rel_inode(nip, 0);
*ap->a_vpp = NULL;
} else {
error = hammer_get_vnode(nip, ap->a_vpp);
hammer_rel_inode(nip, 0);
if (error == 0) {
cache_setunresolved(ap->a_nch);
cache_setvp(ap->a_nch, *ap->a_vpp);
}
}
hammer_done_transaction(&trans);
if (error == 0)
hammer_knote(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nmknod(struct vop_nmknod_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_inode_t nip;
struct nchandle *nch;
hammer_mount_t hmp;
int error;
nch = ap->a_nch;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (dip->flags & HAMMER_INODE_RO)
return (EROFS);
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0)
return (error);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
dip, nch->ncp->nc_name, nch->ncp->nc_nlen,
NULL, &nip);
if (error) {
hammer_done_transaction(&trans);
*ap->a_vpp = NULL;
lwkt_reltoken(&hmp->fs_token);
return (error);
}
error = hammer_ip_add_direntry(&trans, dip,
nch->ncp->nc_name, nch->ncp->nc_nlen,
nip);
if (error) {
hammer_rel_inode(nip, 0);
*ap->a_vpp = NULL;
} else {
error = hammer_get_vnode(nip, ap->a_vpp);
hammer_rel_inode(nip, 0);
if (error == 0) {
cache_setunresolved(ap->a_nch);
cache_setvp(ap->a_nch, *ap->a_vpp);
}
}
hammer_done_transaction(&trans);
if (error == 0)
hammer_knote(ap->a_dvp, NOTE_WRITE);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_open(struct vop_open_args *ap)
{
hammer_inode_t ip;
ip = VTOI(ap->a_vp);
if ((ap->a_mode & FWRITE) && (ip->flags & HAMMER_INODE_RO))
return (EROFS);
return(vop_stdopen(ap));
}
static
int
hammer_vop_print(struct vop_print_args *ap)
{
return EOPNOTSUPP;
}
static
int
hammer_vop_readdir(struct vop_readdir_args *ap)
{
struct hammer_transaction trans;
struct hammer_cursor cursor;
hammer_inode_t ip;
hammer_mount_t hmp;
struct uio *uio;
hammer_base_elm_t base;
int error;
int cookie_index;
int ncookies;
off_t *cookies;
off_t saveoff;
int r;
int dtype;
ip = VTOI(ap->a_vp);
uio = ap->a_uio;
saveoff = uio->uio_offset;
hmp = ip->hmp;
if (ap->a_ncookies) {
ncookies = uio->uio_resid / 16 + 1;
if (ncookies > 1024)
ncookies = 1024;
cookies = kmalloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK);
cookie_index = 0;
} else {
ncookies = -1;
cookies = NULL;
cookie_index = 0;
}
lwkt_gettoken(&hmp->fs_token);
hammer_simple_transaction(&trans, hmp);
error = 0;
if (saveoff == 0) {
r = vop_write_dirent(&error, uio, ip->obj_id, DT_DIR, 1, ".");
if (r)
goto done;
if (cookies)
cookies[cookie_index] = saveoff;
++saveoff;
++cookie_index;
if (cookie_index == ncookies)
goto done;
}
if (saveoff == 1) {
if (ip->ino_data.parent_obj_id) {
r = vop_write_dirent(&error, uio,
ip->ino_data.parent_obj_id,
DT_DIR, 2, "..");
} else {
r = vop_write_dirent(&error, uio,
ip->obj_id, DT_DIR, 2, "..");
}
if (r)
goto done;
if (cookies)
cookies[cookie_index] = saveoff;
++saveoff;
++cookie_index;
if (cookie_index == ncookies)
goto done;
}
hammer_init_cursor(&trans, &cursor, &ip->cache[1], ip);
cursor.key_beg.localization = ip->obj_localization |
hammer_dir_localization(ip);
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
cursor.key_beg.obj_type = 0;
cursor.key_beg.key = saveoff;
cursor.key_end = cursor.key_beg;
cursor.key_end.key = HAMMER_MAX_KEY;
cursor.asof = ip->obj_asof;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE | HAMMER_CURSOR_ASOF;
error = hammer_ip_first(&cursor);
while (error == 0) {
error = hammer_ip_resolve_data(&cursor);
if (error)
break;
base = &cursor.leaf->base;
saveoff = base->key;
KKASSERT(cursor.leaf->data_len > HAMMER_ENTRY_NAME_OFF);
if (base->obj_id != ip->obj_id)
hpanic("bad record at %p", cursor.node);
dtype = hammer_get_dtype(cursor.leaf->base.obj_type);
r = vop_write_dirent(
&error, uio, cursor.data->entry.obj_id,
dtype,
cursor.leaf->data_len - HAMMER_ENTRY_NAME_OFF ,
(void *)cursor.data->entry.name);
if (r)
break;
++saveoff;
if (cookies)
cookies[cookie_index] = base->key;
++cookie_index;
if (cookie_index == ncookies)
break;
error = hammer_ip_next(&cursor);
}
hammer_done_cursor(&cursor);
done:
hammer_done_transaction(&trans);
if (ap->a_eofflag)
*ap->a_eofflag = (error == ENOENT);
uio->uio_offset = saveoff;
if (error && cookie_index == 0) {
if (error == ENOENT)
error = 0;
if (cookies) {
kfree(cookies, M_TEMP);
*ap->a_ncookies = 0;
*ap->a_cookies = NULL;
}
} else {
if (error == ENOENT)
error = 0;
if (cookies) {
*ap->a_ncookies = cookie_index;
*ap->a_cookies = cookies;
}
}
lwkt_reltoken(&hmp->fs_token);
return(error);
}
static
int
hammer_vop_readlink(struct vop_readlink_args *ap)
{
struct hammer_transaction trans;
struct hammer_cursor cursor;
hammer_inode_t ip;
hammer_mount_t hmp;
char buf[32];
uint32_t localization;
hammer_pseudofs_inmem_t pfsm;
int error;
ip = VTOI(ap->a_vp);
hmp = ip->hmp;
lwkt_gettoken(&hmp->fs_token);
if (ip->ino_data.size <= HAMMER_INODE_BASESYMLEN) {
char *ptr;
int bytes;
ptr = ip->ino_data.ext.symlink;
bytes = (int)ip->ino_data.size;
if (bytes == 10 &&
ip->obj_asof == HAMMER_MAX_TID &&
ip->obj_localization == HAMMER_DEF_LOCALIZATION &&
strncmp(ptr, "@@PFS", 5) == 0) {
hammer_simple_transaction(&trans, hmp);
bcopy(ptr + 5, buf, 5);
buf[5] = 0;
localization = pfs_to_lo(strtoul(buf, NULL, 10));
pfsm = hammer_load_pseudofs(&trans, localization,
&error);
if (error == 0) {
if (hammer_is_pfs_slave(&pfsm->pfsd)) {
ksnprintf(buf, sizeof(buf),
"@@0x%016jx:%05d",
(intmax_t)pfsm->pfsd.sync_end_tid,
lo_to_pfs(localization));
} else {
ksnprintf(buf, sizeof(buf),
"@@-1:%05d",
lo_to_pfs(localization));
}
ptr = buf;
bytes = strlen(buf);
}
if (pfsm)
hammer_rel_pseudofs(hmp, pfsm);
hammer_done_transaction(&trans);
}
error = uiomove(ptr, bytes, ap->a_uio);
lwkt_reltoken(&hmp->fs_token);
return(error);
}
hammer_simple_transaction(&trans, hmp);
hammer_init_cursor(&trans, &cursor, &ip->cache[1], ip);
cursor.key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_FIX;
cursor.key_beg.obj_type = 0;
cursor.key_beg.key = HAMMER_FIXKEY_SYMLINK;
cursor.asof = ip->obj_asof;
cursor.flags |= HAMMER_CURSOR_ASOF;
error = hammer_ip_lookup(&cursor);
if (error == 0) {
error = hammer_ip_resolve_data(&cursor);
if (error == 0) {
KKASSERT(cursor.leaf->data_len >=
HAMMER_SYMLINK_NAME_OFF);
error = uiomove(cursor.data->symlink.name,
cursor.leaf->data_len -
HAMMER_SYMLINK_NAME_OFF,
ap->a_uio);
}
}
hammer_done_cursor(&cursor);
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return(error);
}
static
int
hammer_vop_nremove(struct vop_nremove_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_mount_t hmp;
int error;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (hammer_nohistory(dip) == 0 &&
(error = hammer_checkspace(hmp, HAMMER_CHKSPC_REMOVE)) != 0) {
return (error);
}
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_dounlink(&trans, ap->a_nch, ap->a_dvp, ap->a_cred, 0, 0);
hammer_done_transaction(&trans);
if (error == 0)
hammer_knote(ap->a_dvp, NOTE_WRITE);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nrename(struct vop_nrename_args *ap)
{
struct hammer_transaction trans;
struct namecache *fncp;
struct namecache *tncp;
hammer_inode_t fdip;
hammer_inode_t tdip;
hammer_inode_t ip;
hammer_mount_t hmp;
struct hammer_cursor cursor;
int64_t namekey;
uint32_t max_iterations;
int nlen, error;
if (ap->a_fdvp->v_mount != ap->a_tdvp->v_mount)
return(EXDEV);
if (ap->a_fdvp->v_mount != ap->a_fnch->ncp->nc_vp->v_mount)
return(EXDEV);
fdip = VTOI(ap->a_fdvp);
tdip = VTOI(ap->a_tdvp);
fncp = ap->a_fnch->ncp;
tncp = ap->a_tnch->ncp;
ip = VTOI(fncp->nc_vp);
KKASSERT(ip != NULL);
hmp = ip->hmp;
if (fdip->obj_localization != tdip->obj_localization)
return(EXDEV);
if (fdip->obj_localization != ip->obj_localization)
return(EXDEV);
if (fdip->flags & HAMMER_INODE_RO)
return (EROFS);
if (tdip->flags & HAMMER_INODE_RO)
return (EROFS);
if (ip->flags & HAMMER_INODE_RO)
return (EROFS);
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0)
return (error);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_dounlink(&trans, ap->a_tnch, ap->a_tdvp,
ap->a_cred, 0, -1);
if (error == 0 || error == ENOENT) {
error = hammer_ip_add_direntry(&trans, tdip,
tncp->nc_name, tncp->nc_nlen,
ip);
if (error == 0) {
ip->ino_data.parent_obj_id = tdip->obj_id;
ip->ino_data.ctime = trans.time;
hammer_modify_inode(&trans, ip, HAMMER_INODE_DDIRTY);
}
}
if (error)
goto failed;
namekey = hammer_direntry_namekey(fdip, fncp->nc_name, fncp->nc_nlen,
&max_iterations);
retry:
hammer_init_cursor(&trans, &cursor, &fdip->cache[1], fdip);
cursor.key_beg.localization = fdip->obj_localization |
hammer_dir_localization(fdip);
cursor.key_beg.obj_id = fdip->obj_id;
cursor.key_beg.key = namekey;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
cursor.key_beg.obj_type = 0;
cursor.key_end = cursor.key_beg;
cursor.key_end.key += max_iterations;
cursor.asof = fdip->obj_asof;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE | HAMMER_CURSOR_ASOF;
error = hammer_ip_first(&cursor);
while (error == 0) {
if (hammer_ip_resolve_data(&cursor) != 0)
break;
nlen = cursor.leaf->data_len - HAMMER_ENTRY_NAME_OFF;
KKASSERT(nlen > 0);
if (fncp->nc_nlen == nlen &&
bcmp(fncp->nc_name, cursor.data->entry.name, nlen) == 0) {
break;
}
error = hammer_ip_next(&cursor);
}
if (error == 0)
error = hammer_ip_del_direntry(&trans, &cursor, fdip, ip);
if (error == EDEADLK) {
hammer_done_cursor(&cursor);
goto retry;
}
hammer_done_cursor(&cursor);
if (error == 0) {
cache_rename(ap->a_fnch, ap->a_tnch);
hammer_knote(ap->a_fdvp, NOTE_WRITE);
hammer_knote(ap->a_tdvp, NOTE_WRITE);
while (ip->vp) {
struct vnode *vp;
error = hammer_get_vnode(ip, &vp);
if (error == 0 && vp) {
vn_unlock(vp);
hammer_knote(ip->vp, NOTE_RENAME);
vrele(vp);
break;
}
hdkprintf("ip/vp race2 avoided\n");
}
}
failed:
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nrmdir(struct vop_nrmdir_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_mount_t hmp;
int error;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (hammer_nohistory(dip) == 0 &&
(error = hammer_checkspace(hmp, HAMMER_CHKSPC_REMOVE)) != 0) {
return (error);
}
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_dounlink(&trans, ap->a_nch, ap->a_dvp, ap->a_cred, 0, 1);
hammer_done_transaction(&trans);
if (error == 0)
hammer_knote(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_markatime(struct vop_markatime_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t ip;
hammer_mount_t hmp;
ip = VTOI(ap->a_vp);
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
if (ip->flags & HAMMER_INODE_RO)
return (EROFS);
hmp = ip->hmp;
if (hmp->mp->mnt_flag & MNT_NOATIME)
return (0);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
ip->ino_data.atime = trans.time;
hammer_modify_inode(&trans, ip, HAMMER_INODE_ATIME);
hammer_done_transaction(&trans);
hammer_knote(ap->a_vp, NOTE_ATTRIB);
lwkt_reltoken(&hmp->fs_token);
return (0);
}
static
int
hammer_vop_setattr(struct vop_setattr_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t ip;
struct vattr *vap;
hammer_mount_t hmp;
int modflags;
int error;
int truncating;
int blksize;
int kflags;
#if 0
int64_t aligned_size;
#endif
uint32_t flags;
vap = ap->a_vap;
ip = ap->a_vp->v_data;
modflags = 0;
kflags = 0;
hmp = ip->hmp;
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
return(EROFS);
if (ip->flags & HAMMER_INODE_RO)
return (EROFS);
if (hammer_nohistory(ip) == 0 &&
(error = hammer_checkspace(hmp, HAMMER_CHKSPC_REMOVE)) != 0) {
return (error);
}
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = 0;
if (vap->va_flags != VNOVAL) {
flags = ip->ino_data.uflags;
error = vop_helper_setattr_flags(&flags, vap->va_flags,
hammer_to_unix_xid(&ip->ino_data.uid),
ap->a_cred);
if (error == 0) {
if (ip->ino_data.uflags != flags) {
ip->ino_data.uflags = flags;
ip->ino_data.ctime = trans.time;
modflags |= HAMMER_INODE_DDIRTY;
kflags |= NOTE_ATTRIB;
}
if (ip->ino_data.uflags & (IMMUTABLE | APPEND)) {
error = 0;
goto done;
}
}
goto done;
}
if (ip->ino_data.uflags & (IMMUTABLE | APPEND)) {
error = EPERM;
goto done;
}
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
mode_t cur_mode = ip->ino_data.mode;
uid_t cur_uid = hammer_to_unix_xid(&ip->ino_data.uid);
gid_t cur_gid = hammer_to_unix_xid(&ip->ino_data.gid);
hammer_uuid_t uuid_uid;
hammer_uuid_t uuid_gid;
error = vop_helper_chown(ap->a_vp, vap->va_uid, vap->va_gid,
ap->a_cred,
&cur_uid, &cur_gid, &cur_mode);
if (error == 0) {
hammer_guid_to_uuid(&uuid_uid, cur_uid);
hammer_guid_to_uuid(&uuid_gid, cur_gid);
if (kuuid_compare(&uuid_uid, &ip->ino_data.uid) ||
kuuid_compare(&uuid_gid, &ip->ino_data.gid) ||
ip->ino_data.mode != cur_mode) {
ip->ino_data.uid = uuid_uid;
ip->ino_data.gid = uuid_gid;
ip->ino_data.mode = cur_mode;
ip->ino_data.ctime = trans.time;
modflags |= HAMMER_INODE_DDIRTY;
}
kflags |= NOTE_ATTRIB;
}
}
while (vap->va_size != VNOVAL && ip->ino_data.size != vap->va_size) {
switch(ap->a_vp->v_type) {
case VREG:
if (vap->va_size == ip->ino_data.size)
break;
if ((ip->flags & HAMMER_INODE_REDO) ||
(ip->flags & HAMMER_INODE_RDIRTY)) {
error = hammer_generate_redo(&trans, ip,
vap->va_size,
HAMMER_REDO_TRUNC,
NULL, 0);
}
blksize = hammer_blocksize(vap->va_size);
if (vap->va_size < ip->ino_data.size) {
nvtruncbuf(ap->a_vp, vap->va_size,
blksize,
hammer_blockoff(vap->va_size),
0);
truncating = 1;
kflags |= NOTE_WRITE;
} else {
nvextendbuf(ap->a_vp,
ip->ino_data.size,
vap->va_size,
hammer_blocksize(ip->ino_data.size),
hammer_blocksize(vap->va_size),
hammer_blockoff(ip->ino_data.size),
hammer_blockoff(vap->va_size),
0);
truncating = 0;
kflags |= NOTE_WRITE | NOTE_EXTEND;
}
ip->ino_data.size = vap->va_size;
ip->ino_data.mtime = trans.time;
modflags |= HAMMER_INODE_MTIME | HAMMER_INODE_DDIRTY;
vclrflags(ap->a_vp, VLASTWRITETS);
if (truncating) {
hammer_ip_frontend_trunc(ip, vap->va_size);
if ((ip->flags & HAMMER_INODE_TRUNCATED) == 0) {
ip->flags |= HAMMER_INODE_TRUNCATED;
ip->trunc_off = vap->va_size;
hammer_inode_dirty(ip);
} else if (ip->trunc_off > vap->va_size) {
ip->trunc_off = vap->va_size;
}
}
#if 0
aligned_size = (vap->va_size + (blksize - 1)) &
~(int64_t)(blksize - 1);
if (truncating && vap->va_size < aligned_size) {
aligned_size -= blksize;
hammer_ip_frontend_trunc(ip, aligned_size);
}
#endif
break;
case VDATABASE:
if ((ip->flags & HAMMER_INODE_TRUNCATED) == 0) {
ip->flags |= HAMMER_INODE_TRUNCATED;
ip->trunc_off = vap->va_size;
hammer_inode_dirty(ip);
} else if (ip->trunc_off > vap->va_size) {
ip->trunc_off = vap->va_size;
}
hammer_ip_frontend_trunc(ip, vap->va_size);
ip->ino_data.size = vap->va_size;
ip->ino_data.mtime = trans.time;
modflags |= HAMMER_INODE_MTIME | HAMMER_INODE_DDIRTY;
vclrflags(ap->a_vp, VLASTWRITETS);
kflags |= NOTE_ATTRIB;
break;
default:
error = EINVAL;
goto done;
}
break;
}
if (vap->va_atime.tv_sec != VNOVAL) {
ip->ino_data.atime = hammer_timespec_to_time(&vap->va_atime);
modflags |= HAMMER_INODE_ATIME;
kflags |= NOTE_ATTRIB;
}
if (vap->va_mtime.tv_sec != VNOVAL) {
ip->ino_data.mtime = hammer_timespec_to_time(&vap->va_mtime);
modflags |= HAMMER_INODE_MTIME;
kflags |= NOTE_ATTRIB;
vclrflags(ap->a_vp, VLASTWRITETS);
}
if (vap->va_mode != (mode_t)VNOVAL) {
mode_t cur_mode = ip->ino_data.mode;
uid_t cur_uid = hammer_to_unix_xid(&ip->ino_data.uid);
gid_t cur_gid = hammer_to_unix_xid(&ip->ino_data.gid);
error = vop_helper_chmod(ap->a_vp, vap->va_mode, ap->a_cred,
cur_uid, cur_gid, &cur_mode);
if (error == 0) {
ip->ino_data.mode = cur_mode;
ip->ino_data.ctime = trans.time;
modflags |= HAMMER_INODE_DDIRTY;
kflags |= NOTE_ATTRIB;
}
}
done:
if (error == 0)
hammer_modify_inode(&trans, ip, modflags);
hammer_done_transaction(&trans);
hammer_knote(ap->a_vp, kflags);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nsymlink(struct vop_nsymlink_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_inode_t nip;
hammer_record_t record;
struct nchandle *nch;
hammer_mount_t hmp;
int error;
int bytes;
ap->a_vap->va_type = VLNK;
nch = ap->a_nch;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (dip->flags & HAMMER_INODE_RO)
return (EROFS);
if ((error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0)
return (error);
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
dip, nch->ncp->nc_name, nch->ncp->nc_nlen,
NULL, &nip);
if (error) {
hammer_done_transaction(&trans);
*ap->a_vpp = NULL;
lwkt_reltoken(&hmp->fs_token);
return (error);
}
if (error == 0) {
bytes = strlen(ap->a_target);
if (bytes <= HAMMER_INODE_BASESYMLEN) {
bcopy(ap->a_target, nip->ino_data.ext.symlink, bytes);
} else {
record = hammer_alloc_mem_record(nip, bytes);
record->type = HAMMER_MEM_RECORD_GENERAL;
record->leaf.base.localization = nip->obj_localization |
HAMMER_LOCALIZE_MISC;
record->leaf.base.key = HAMMER_FIXKEY_SYMLINK;
record->leaf.base.rec_type = HAMMER_RECTYPE_FIX;
record->leaf.data_len = bytes;
KKASSERT(HAMMER_SYMLINK_NAME_OFF == 0);
bcopy(ap->a_target, record->data->symlink.name, bytes);
error = hammer_ip_add_record(&trans, record);
}
if (error == 0) {
nip->ino_data.size = bytes;
hammer_modify_inode(&trans, nip, HAMMER_INODE_DDIRTY);
}
}
if (error == 0)
error = hammer_ip_add_direntry(&trans, dip, nch->ncp->nc_name,
nch->ncp->nc_nlen, nip);
if (error) {
hammer_rel_inode(nip, 0);
*ap->a_vpp = NULL;
} else {
error = hammer_get_vnode(nip, ap->a_vpp);
hammer_rel_inode(nip, 0);
if (error == 0) {
cache_setunresolved(ap->a_nch);
cache_setvp(ap->a_nch, *ap->a_vpp);
hammer_knote(ap->a_dvp, NOTE_WRITE);
}
}
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_nwhiteout(struct vop_nwhiteout_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t dip;
hammer_mount_t hmp;
int error;
dip = VTOI(ap->a_dvp);
hmp = dip->hmp;
if (hammer_nohistory(dip) == 0 &&
(error = hammer_checkspace(hmp, HAMMER_CHKSPC_CREATE)) != 0) {
return (error);
}
lwkt_gettoken(&hmp->fs_token);
hammer_start_transaction(&trans, hmp);
error = hammer_dounlink(&trans, ap->a_nch, ap->a_dvp,
ap->a_cred, ap->a_flags, -1);
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_ioctl(struct vop_ioctl_args *ap)
{
hammer_inode_t ip = ap->a_vp->v_data;
hammer_mount_t hmp = ip->hmp;
int error;
lwkt_gettoken(&hmp->fs_token);
error = hammer_ioctl(ip, ap->a_command, ap->a_data,
ap->a_fflag, ap->a_cred);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
static
int
hammer_vop_mountctl(struct vop_mountctl_args *ap)
{
static const struct mountctl_opt extraopt[] = {
{ HMNT_NOHISTORY, "nohistory" },
{ HMNT_MASTERID, "master" },
{ HMNT_NOMIRROR, "nomirror" },
{ 0, NULL}
};
hammer_mount_t hmp;
struct mount *mp;
int usedbytes;
int error;
error = 0;
usedbytes = 0;
mp = ap->a_head.a_ops->head.vv_mount;
KKASSERT(mp->mnt_data != NULL);
hmp = (hammer_mount_t)mp->mnt_data;
lwkt_gettoken(&hmp->fs_token);
switch(ap->a_op) {
case MOUNTCTL_SET_EXPORT:
if (ap->a_ctllen != sizeof(struct export_args))
error = EINVAL;
else
error = hammer_vfs_export(mp, ap->a_op,
(const struct export_args *)ap->a_ctl);
break;
case MOUNTCTL_MOUNTFLAGS:
error = vop_stdmountctl(ap);
if (error)
break;
usedbytes = *ap->a_res;
if (usedbytes > 0 && usedbytes < ap->a_buflen) {
usedbytes += vfs_flagstostr(hmp->hflags, extraopt,
ap->a_buf,
ap->a_buflen - usedbytes,
&error);
}
*ap->a_res += usedbytes;
break;
default:
error = vop_stdmountctl(ap);
break;
}
lwkt_reltoken(&hmp->fs_token);
return(error);
}
static
int
hammer_vop_strategy(struct vop_strategy_args *ap)
{
struct buf *bp;
int error;
bp = ap->a_bio->bio_buf;
switch(bp->b_cmd) {
case BUF_CMD_READ:
error = hammer_vop_strategy_read(ap);
break;
case BUF_CMD_WRITE:
error = hammer_vop_strategy_write(ap);
break;
default:
bp->b_error = error = EINVAL;
bp->b_flags |= B_ERROR;
biodone(ap->a_bio);
break;
}
return (error);
}
static
int
hammer_vop_strategy_read(struct vop_strategy_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t ip;
hammer_inode_t dip;
hammer_mount_t hmp;
struct hammer_cursor cursor;
hammer_base_elm_t base;
hammer_off_t disk_offset;
struct bio *bio;
struct bio *nbio;
struct buf *bp;
int64_t rec_offset;
int64_t ran_end;
int64_t tmp64;
int error;
int boff;
int roff;
int n;
int isdedupable;
bio = ap->a_bio;
bp = bio->bio_buf;
ip = ap->a_vp->v_data;
hmp = ip->hmp;
nbio = push_bio(bio);
if (hammer_is_zone_large_data(nbio->bio_offset)) {
if (hammer_double_buffer == 0) {
lwkt_gettoken(&hmp->fs_token);
error = hammer_io_direct_read(hmp, nbio, NULL);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
if ((bp->b_flags & B_PAGING) == 0) {
lwkt_gettoken(&hmp->fs_token);
error = hammer_io_indirect_read(hmp, nbio, NULL);
lwkt_reltoken(&hmp->fs_token);
return (error);
}
}
lwkt_gettoken(&hmp->fs_token);
hammer_simple_transaction(&trans, hmp);
hammer_init_cursor(&trans, &cursor, &ip->cache[1], ip);
cursor.key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.obj_type = 0;
cursor.key_beg.key = bio->bio_offset + 1;
cursor.asof = ip->obj_asof;
cursor.flags |= HAMMER_CURSOR_ASOF;
cursor.key_end = cursor.key_beg;
KKASSERT(ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE);
#if 0
if (ip->ino_data.obj_type == HAMMER_OBJTYPE_DBFILE) {
cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
cursor.key_end.rec_type = HAMMER_RECTYPE_DB;
cursor.key_end.key = HAMMER_MAX_KEY;
} else
#endif
{
ran_end = bio->bio_offset + bp->b_bufsize;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
cursor.key_end.rec_type = HAMMER_RECTYPE_DATA;
tmp64 = ran_end + MAXPHYS + 1;
if (tmp64 < ran_end)
cursor.key_end.key = HAMMER_MAX_KEY;
else
cursor.key_end.key = ran_end + MAXPHYS + 1;
}
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
if (hammer_double_buffer == 0 ||
((ap->a_vp->v_flag & VSWAPCACHE) == 0 &&
vm_swapcache_use_chflags)) {
cursor.flags |= HAMMER_CURSOR_NOSWAPCACHE;
}
error = hammer_ip_first(&cursor);
boff = 0;
while (error == 0) {
base = &cursor.leaf->base;
rec_offset = base->key - cursor.leaf->data_len;
n = (int)(rec_offset - (bio->bio_offset + boff));
if (n > 0) {
if (n > bp->b_bufsize - boff)
n = bp->b_bufsize - boff;
bzero((char *)bp->b_data + boff, n);
boff += n;
n = 0;
}
roff = -n;
rec_offset += roff;
n = cursor.leaf->data_len - roff;
if (n <= 0) {
hdkprintf("bad n=%d roff=%d\n", n, roff);
n = 0;
} else if (n > bp->b_bufsize - boff) {
n = bp->b_bufsize - boff;
}
if (ip->flags & HAMMER_INODE_TRUNCATED) {
if (hammer_cursor_ondisk(&cursor)
) {
if (ip->trunc_off <= rec_offset)
n = 0;
else if (ip->trunc_off < rec_offset + n)
n = (int)(ip->trunc_off - rec_offset);
}
}
if (ip->sync_flags & HAMMER_INODE_TRUNCATED) {
if (hammer_cursor_ondisk(&cursor)) {
if (ip->sync_trunc_off <= rec_offset)
n = 0;
else if (ip->sync_trunc_off < rec_offset + n)
n = (int)(ip->sync_trunc_off - rec_offset);
}
}
disk_offset = cursor.leaf->data_offset + roff;
isdedupable = (boff == 0 && n == bp->b_bufsize &&
hammer_cursor_ondisk(&cursor) &&
((int)disk_offset & HAMMER_BUFMASK) == 0);
if (isdedupable && hammer_double_buffer == 0) {
KKASSERT(hammer_is_zone_large_data(disk_offset));
nbio->bio_offset = disk_offset;
error = hammer_io_direct_read(hmp, nbio, cursor.leaf);
goto done;
} else if (isdedupable) {
KKASSERT(hammer_is_zone_large_data(disk_offset));
nbio->bio_offset = disk_offset;
error = hammer_io_indirect_read(hmp, nbio, cursor.leaf);
goto done;
} else if (n) {
error = hammer_ip_resolve_data(&cursor);
if (error == 0) {
bcopy((char *)cursor.data + roff,
(char *)bp->b_data + boff, n);
}
}
if (error)
break;
boff += n;
if (boff == bp->b_bufsize)
break;
error = hammer_ip_next(&cursor);
}
if (error == ENOENT)
error = 0;
if (error == 0 && boff != bp->b_bufsize) {
KKASSERT(boff < bp->b_bufsize);
bzero((char *)bp->b_data + boff, bp->b_bufsize - boff);
}
if (hammer_double_buffer)
bp->b_flags |= B_NOTMETA;
bp->b_resid = 0;
bp->b_error = error;
if (error)
bp->b_flags |= B_ERROR;
biodone(ap->a_bio);
done:
if (cursor.node)
hammer_cache_node(&ip->cache[1], cursor.node);
if (ran_end >= ip->ino_data.size) {
dip = hammer_find_inode(&trans, ip->ino_data.parent_obj_id,
ip->obj_asof, ip->obj_localization);
if (dip) {
hammer_cache_node(&dip->cache[3], cursor.node);
hammer_rel_inode(dip, 0);
}
}
hammer_done_cursor(&cursor);
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
return(error);
}
static
int
hammer_vop_bmap(struct vop_bmap_args *ap)
{
struct hammer_transaction trans;
hammer_inode_t ip;
hammer_mount_t hmp;
struct hammer_cursor cursor;
hammer_base_elm_t base;
int64_t rec_offset;
int64_t ran_end;
int64_t tmp64;
int64_t base_offset;
int64_t base_disk_offset;
int64_t last_offset;
hammer_off_t last_disk_offset;
hammer_off_t disk_offset;
int rec_len;
int error;
int blksize;
ip = ap->a_vp->v_data;
hmp = ip->hmp;
if (ip->ino_data.obj_type != HAMMER_OBJTYPE_REGFILE)
return(EOPNOTSUPP);
if (ap->a_cmd != BUF_CMD_READ)
return(EOPNOTSUPP);
lwkt_gettoken(&hmp->fs_token);
hammer_simple_transaction(&trans, hmp);
hammer_init_cursor(&trans, &cursor, &ip->cache[1], ip);
cursor.key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.obj_type = 0;
if (ap->a_runb)
cursor.key_beg.key = ap->a_loffset - MAXPHYS + 1;
else
cursor.key_beg.key = ap->a_loffset + 1;
if (cursor.key_beg.key < 0)
cursor.key_beg.key = 0;
cursor.asof = ip->obj_asof;
cursor.flags |= HAMMER_CURSOR_ASOF;
cursor.key_end = cursor.key_beg;
KKASSERT(ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE);
ran_end = ap->a_loffset + MAXPHYS;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
cursor.key_end.rec_type = HAMMER_RECTYPE_DATA;
tmp64 = ran_end + MAXPHYS + 1;
if (tmp64 < ran_end)
cursor.key_end.key = HAMMER_MAX_KEY;
else
cursor.key_end.key = ran_end + MAXPHYS + 1;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
error = hammer_ip_first(&cursor);
base_offset = last_offset = 0;
base_disk_offset = last_disk_offset = 0;
while (error == 0) {
base = &cursor.leaf->base;
rec_offset = base->key - cursor.leaf->data_len;
rec_len = cursor.leaf->data_len;
if (ip->flags & HAMMER_INODE_TRUNCATED) {
if (hammer_cursor_ondisk(&cursor) ||
cursor.iprec->flush_state == HAMMER_FST_FLUSH) {
if (ip->trunc_off <= rec_offset)
rec_len = 0;
else if (ip->trunc_off < rec_offset + rec_len)
rec_len = (int)(ip->trunc_off - rec_offset);
}
}
if (ip->sync_flags & HAMMER_INODE_TRUNCATED) {
if (hammer_cursor_ondisk(&cursor)) {
if (ip->sync_trunc_off <= rec_offset)
rec_len = 0;
else if (ip->sync_trunc_off < rec_offset + rec_len)
rec_len = (int)(ip->sync_trunc_off - rec_offset);
}
}
if (error)
break;
if (hammer_cursor_ondisk(&cursor)) {
disk_offset = cursor.leaf->data_offset;
if (rec_offset != last_offset ||
disk_offset != last_disk_offset) {
if (rec_offset > ap->a_loffset)
break;
base_offset = rec_offset;
base_disk_offset = disk_offset;
}
last_offset = rec_offset + rec_len;
last_disk_offset = disk_offset + rec_len;
}
error = hammer_ip_next(&cursor);
}
if (cursor.node)
hammer_cache_node(&ip->cache[1], cursor.node);
hammer_done_cursor(&cursor);
hammer_done_transaction(&trans);
lwkt_reltoken(&hmp->fs_token);
if (last_offset == 0 || last_offset < ap->a_loffset)
return (EOPNOTSUPP);
blksize = hammer_blocksize(ap->a_loffset);
if (hammer_blocksize(base_offset) != blksize) {
base_offset = hammer_blockdemarc(base_offset, ap->a_loffset);
}
if (last_offset != ap->a_loffset &&
hammer_blocksize(last_offset - 1) != blksize) {
last_offset = hammer_blockdemarc(ap->a_loffset,
last_offset - 1);
}
disk_offset = base_disk_offset + (ap->a_loffset - base_offset);
if (!hammer_is_zone_large_data(disk_offset)) {
error = EOPNOTSUPP;
} else if ((disk_offset & HAMMER_BUFMASK) ||
(last_offset - ap->a_loffset) < blksize) {
error = EOPNOTSUPP;
} else {
*ap->a_doffsetp = disk_offset;
if (ap->a_runb) {
*ap->a_runb = ap->a_loffset - base_offset;
KKASSERT(*ap->a_runb >= 0);
}
if (ap->a_runp) {
*ap->a_runp = last_offset - ap->a_loffset;
KKASSERT(*ap->a_runp >= 0);
}
error = 0;
}
return(error);
}
static
int
hammer_vop_strategy_write(struct vop_strategy_args *ap)
{
hammer_record_t record;
hammer_mount_t hmp;
hammer_inode_t ip;
struct bio *bio;
struct buf *bp;
int blksize __debugvar;
int bytes;
int error;
bio = ap->a_bio;
bp = bio->bio_buf;
ip = ap->a_vp->v_data;
hmp = ip->hmp;
blksize = hammer_blocksize(bio->bio_offset);
KKASSERT(bp->b_bufsize == blksize);
if (ip->flags & HAMMER_INODE_RO) {
bp->b_error = EROFS;
bp->b_flags |= B_ERROR;
biodone(ap->a_bio);
return(EROFS);
}
lwkt_gettoken(&hmp->fs_token);
if (hammer_double_buffer)
bp->b_flags |= B_NOTMETA;
if ((ip->flags | ip->sync_flags) &
(HAMMER_INODE_DELETING|HAMMER_INODE_DELETED)) {
bp->b_resid = 0;
biodone(ap->a_bio);
lwkt_reltoken(&hmp->fs_token);
return(0);
}
KKASSERT((bio->bio_offset & HAMMER_BUFMASK) == 0);
KKASSERT(bio->bio_offset < ip->ino_data.size);
if (bio->bio_offset || ip->ino_data.size > HAMMER_HBUFSIZE)
bytes = bp->b_bufsize;
else
bytes = HAMMER_DATA_DOALIGN_WITH(int, ip->ino_data.size);
record = hammer_ip_add_bulk(ip, bio->bio_offset, bp->b_data,
bytes, &error);
if (record) {
if (bp->b_flags & B_VFSFLAG1) {
record->flags |= HAMMER_RECF_REDO;
bp->b_flags &= ~B_VFSFLAG1;
}
hammer_io_direct_write(hmp, bio, record);
if (ip->rsv_recs > 1 && hmp->rsv_recs > hammer_limit_recs)
hammer_flush_inode(ip, 0);
} else {
bp->b_bio2.bio_offset = NOOFFSET;
bp->b_error = error;
bp->b_flags |= B_ERROR;
biodone(ap->a_bio);
}
lwkt_reltoken(&hmp->fs_token);
return(error);
}
static int
hammer_dounlink(hammer_transaction_t trans, struct nchandle *nch,
struct vnode *dvp, struct ucred *cred,
int flags, int isdir)
{
struct namecache *ncp;
hammer_inode_t dip;
hammer_inode_t ip;
hammer_mount_t hmp;
struct hammer_cursor cursor;
int64_t namekey;
uint32_t max_iterations;
int nlen, error;
dip = VTOI(dvp);
ncp = nch->ncp;
hmp = dip->hmp;
if (dip->flags & HAMMER_INODE_RO)
return (EROFS);
namekey = hammer_direntry_namekey(dip, ncp->nc_name, ncp->nc_nlen,
&max_iterations);
retry:
hammer_init_cursor(trans, &cursor, &dip->cache[1], dip);
cursor.key_beg.localization = dip->obj_localization |
hammer_dir_localization(dip);
cursor.key_beg.obj_id = dip->obj_id;
cursor.key_beg.key = namekey;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
cursor.key_beg.obj_type = 0;
cursor.key_end = cursor.key_beg;
cursor.key_end.key += max_iterations;
cursor.asof = dip->obj_asof;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE | HAMMER_CURSOR_ASOF;
error = hammer_ip_first(&cursor);
while (error == 0) {
error = hammer_ip_resolve_data(&cursor);
if (error)
break;
nlen = cursor.leaf->data_len - HAMMER_ENTRY_NAME_OFF;
KKASSERT(nlen > 0);
if (ncp->nc_nlen == nlen &&
bcmp(ncp->nc_name, cursor.data->entry.name, nlen) == 0) {
break;
}
error = hammer_ip_next(&cursor);
}
if (error == 0) {
hammer_unlock(&cursor.ip->lock);
ip = hammer_get_inode(trans, dip, cursor.data->entry.obj_id,
hmp->asof,
cursor.data->entry.localization,
0, &error);
hammer_lock_sh(&cursor.ip->lock);
if (error == ENOENT) {
hkprintf("WARNING: Removing dirent w/missing inode "
"\"%s\"\n"
"\tobj_id = %016jx\n",
ncp->nc_name,
(intmax_t)cursor.data->entry.obj_id);
error = 0;
}
if (error == 0 && isdir >= 0 && ip) {
if (isdir &&
ip->ino_data.obj_type != HAMMER_OBJTYPE_DIRECTORY) {
error = ENOTDIR;
} else if (isdir == 0 &&
ip->ino_data.obj_type == HAMMER_OBJTYPE_DIRECTORY) {
error = EISDIR;
}
}
if (error == 0 && ip && ip->ino_data.obj_type ==
HAMMER_OBJTYPE_DIRECTORY) {
hammer_unlock_cursor(&cursor);
error = hammer_ip_check_directory_empty(trans, ip);
hammer_lock_cursor(&cursor);
if (cursor.flags & HAMMER_CURSOR_RETEST) {
hkprintf("Warning: avoided deadlock "
"on rmdir '%s'\n",
ncp->nc_name);
error = EDEADLK;
}
}
if (error == 0) {
error = hammer_ip_del_direntry(trans, &cursor,
dip, ip);
}
hammer_done_cursor(&cursor);
if (error == 0) {
cache_unlink(nch);
while (ip && ip->vp) {
struct vnode *vp;
error = hammer_get_vnode(ip, &vp);
if (error == 0 && vp) {
vn_unlock(vp);
hammer_knote(ip->vp, NOTE_DELETE);
#if 0
cache_inval_vp(ip->vp, CINV_DESTROY);
#endif
vrele(vp);
break;
}
hdkprintf("ip/vp race1 avoided\n");
}
}
if (ip)
hammer_rel_inode(ip, 0);
} else {
hammer_done_cursor(&cursor);
}
if (error == EDEADLK)
goto retry;
return (error);
}
static int
hammer_vop_fifoclose (struct vop_close_args *ap)
{
return (VOCALL(&fifo_vnode_vops, &ap->a_head));
}
static int
hammer_vop_fiforead (struct vop_read_args *ap)
{
int error;
error = VOCALL(&fifo_vnode_vops, &ap->a_head);
return (error);
}
static int
hammer_vop_fifowrite (struct vop_write_args *ap)
{
int error;
error = VOCALL(&fifo_vnode_vops, &ap->a_head);
return (error);
}
static
int
hammer_vop_fifokqfilter(struct vop_kqfilter_args *ap)
{
int error;
error = VOCALL(&fifo_vnode_vops, &ap->a_head);
if (error)
error = hammer_vop_kqfilter(ap);
return(error);
}
static void filt_hammerdetach(struct knote *kn);
static int filt_hammerread(struct knote *kn, long hint);
static int filt_hammerwrite(struct knote *kn, long hint);
static int filt_hammervnode(struct knote *kn, long hint);
static struct filterops hammerread_filtops =
{ FILTEROP_ISFD | FILTEROP_MPSAFE,
NULL, filt_hammerdetach, filt_hammerread };
static struct filterops hammerwrite_filtops =
{ FILTEROP_ISFD | FILTEROP_MPSAFE,
NULL, filt_hammerdetach, filt_hammerwrite };
static struct filterops hammervnode_filtops =
{ FILTEROP_ISFD | FILTEROP_MPSAFE,
NULL, filt_hammerdetach, filt_hammervnode };
static
int
hammer_vop_kqfilter(struct vop_kqfilter_args *ap)
{
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &hammerread_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &hammerwrite_filtops;
break;
case EVFILT_VNODE:
kn->kn_fop = &hammervnode_filtops;
break;
default:
return (EOPNOTSUPP);
}
kn->kn_hook = (caddr_t)vp;
knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
return(0);
}
static void
filt_hammerdetach(struct knote *kn)
{
struct vnode *vp = (void *)kn->kn_hook;
knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
}
static int
filt_hammerread(struct knote *kn, long hint)
{
struct vnode *vp = (void *)kn->kn_hook;
hammer_inode_t ip = VTOI(vp);
hammer_mount_t hmp = ip->hmp;
off_t off;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
return(1);
}
lwkt_gettoken(&hmp->fs_token);
off = ip->ino_data.size - kn->kn_fp->f_offset;
kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
lwkt_reltoken(&hmp->fs_token);
if (kn->kn_sfflags & NOTE_OLDAPI)
return(1);
return (kn->kn_data != 0);
}
static int
filt_hammerwrite(struct knote *kn, long hint)
{
if (hint == NOTE_REVOKE)
kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
kn->kn_data = 0;
return (1);
}
static int
filt_hammervnode(struct knote *kn, long hint)
{
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
return (1);
}
return (kn->kn_fflags != 0);
}