#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/share.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/t_lock.h>
#include <sys/errno.h>
#include <sys/nbmlock.h>
int share_debug = 0;
#ifdef DEBUG
static void print_shares(struct vnode *);
static void print_share(struct shrlock *);
#endif
static int isreadonly(struct vnode *);
static void do_cleanshares(struct vnode *, pid_t, int32_t);
int
add_share(struct vnode *vp, struct shrlock *shr)
{
struct shrlocklist *shrl;
if ((shr->s_access == 0) && (GETSYSID(shr->s_sysid) == 0)) {
return (EINVAL);
}
if (shr->s_access & ~(F_RDACC|F_WRACC|F_RWACC|F_RMACC|F_MDACC)) {
return (EINVAL);
}
if (shr->s_deny & ~(F_NODNY|F_RDDNY|F_WRDNY|F_RWDNY|F_COMPAT|
F_MANDDNY|F_RMDNY)) {
return (EINVAL);
}
mutex_enter(&vp->v_lock);
for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
if ((shrl->shr->s_sysid == shr->s_sysid) &&
(shrl->shr->s_pid == shr->s_pid) &&
(shrl->shr->s_own_len == shr->s_own_len) &&
bcmp(shrl->shr->s_owner, shr->s_owner,
shr->s_own_len) == 0) {
if ((shrl->shr->s_deny & F_COMPAT) &&
(shr->s_deny & F_COMPAT) &&
((shrl->next == NULL) ||
(shrl->shr->s_access & F_WRACC)))
break;
}
if ((shrl->shr->s_deny & F_COMPAT) && (shrl->next == NULL)) {
if (!(shr->s_deny & F_COMPAT)) {
if ((shr->s_access & F_WRACC) ||
(shr->s_deny & F_RDDNY) ||
(shrl->shr->s_access & F_WRACC)) {
mutex_exit(&vp->v_lock);
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
return (EAGAIN);
}
if (isreadonly(vp))
break;
mutex_exit(&vp->v_lock);
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
return (EAGAIN);
}
if ((shr->s_access == F_RDACC) &&
(shrl->shr->s_access == F_RDACC))
break;
mutex_exit(&vp->v_lock);
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
return (EAGAIN);
}
if ((shrl->shr->s_deny & F_COMPAT) && (shr->s_deny & F_COMPAT))
continue;
if (shr->s_deny & F_COMPAT) {
if ((shr->s_access & F_WRACC) ||
(shrl->shr->s_deny & F_RDDNY) ||
(shrl->shr->s_access & F_WRACC)) {
mutex_exit(&vp->v_lock);
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
return (EAGAIN);
}
if (shrl->next == NULL) {
if ((shrl->shr->s_access == F_RDACC) &&
isreadonly(vp)) {
break;
}
mutex_exit(&vp->v_lock);
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
return (EAGAIN);
}
continue;
}
if ((shr->s_access & shrl->shr->s_deny) ||
(shr->s_deny & shrl->shr->s_access)) {
mutex_exit(&vp->v_lock);
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
return (EAGAIN);
}
}
shrl = kmem_alloc(sizeof (struct shrlocklist), KM_SLEEP);
shrl->shr = kmem_alloc(sizeof (struct shrlock), KM_SLEEP);
shrl->shr->s_access = shr->s_access;
shrl->shr->s_deny = shr->s_deny;
if (shrl->shr->s_deny & F_COMPAT)
shrl->shr->s_deny = F_COMPAT;
shrl->shr->s_sysid = shr->s_sysid;
shrl->shr->s_pid = shr->s_pid;
shrl->shr->s_own_len = shr->s_own_len;
shrl->shr->s_owner = kmem_alloc(shr->s_own_len, KM_SLEEP);
bcopy(shr->s_owner, shrl->shr->s_owner, shr->s_own_len);
shrl->next = vp->v_shrlocks;
vp->v_shrlocks = shrl;
#ifdef DEBUG
if (share_debug)
print_shares(vp);
#endif
mutex_exit(&vp->v_lock);
return (0);
}
static int
is_match_for_del(struct shrlock *shr, struct shrlock *element)
{
int nlmid1, nlmid2;
int result = 0;
nlmid1 = GETNLMID(shr->s_sysid);
nlmid2 = GETNLMID(element->s_sysid);
if (nlmid1 != 0) {
if (GETSYSID(shr->s_sysid) != 0 && shr->s_pid == 0) {
result = shr->s_sysid == element->s_sysid;
} else if (GETSYSID(shr->s_sysid) == 0 && shr->s_pid == 0) {
result = (nlmid1 == nlmid2);
} else if (GETSYSID(shr->s_sysid) == 0 && shr->s_pid != 0) {
result = (nlmid1 == nlmid2 &&
shr->s_pid == element->s_pid);
}
} else {
result = ((shr->s_sysid == 0 &&
shr->s_pid == element->s_pid) ||
(shr->s_sysid != 0 &&
shr->s_sysid == element->s_sysid));
}
return (result);
}
int
del_share(struct vnode *vp, struct shrlock *shr)
{
struct shrlocklist *shrl;
struct shrlocklist **shrlp;
int found = 0;
int is_nbmand = 0;
mutex_enter(&vp->v_lock);
shrlp = &vp->v_shrlocks;
while (*shrlp) {
if ((shr->s_own_len == (*shrlp)->shr->s_own_len &&
(bcmp(shr->s_owner, (*shrlp)->shr->s_owner,
shr->s_own_len) == 0)) ||
(shr->s_own_len == 0 &&
is_match_for_del(shr, (*shrlp)->shr))) {
shrl = *shrlp;
*shrlp = shrl->next;
if (shrl->shr->s_deny & F_MANDDNY)
is_nbmand = 1;
kmem_free(shrl->shr->s_owner, shrl->shr->s_own_len);
kmem_free(shrl->shr, sizeof (struct shrlock));
kmem_free(shrl, sizeof (struct shrlocklist));
found++;
continue;
}
shrlp = &(*shrlp)->next;
}
if (is_nbmand)
cv_broadcast(&vp->v_cv);
mutex_exit(&vp->v_lock);
return (found ? 0 : EINVAL);
}
void
cleanshares(struct vnode *vp, pid_t pid)
{
do_cleanshares(vp, pid, 0);
}
void
cleanshares_by_sysid(struct vnode *vp, int32_t sysid)
{
if (sysid == 0)
return;
do_cleanshares(vp, 0, sysid);
}
static void
do_cleanshares(struct vnode *vp, pid_t pid, int32_t sysid)
{
struct shrlock shr;
if (vp->v_shrlocks == NULL)
return;
shr.s_access = 0;
shr.s_deny = 0;
shr.s_pid = pid;
shr.s_sysid = sysid;
shr.s_own_len = 0;
shr.s_owner = NULL;
(void) del_share(vp, &shr);
}
static int
is_match_for_has_remote(int32_t sysid1, int32_t sysid2)
{
int result = 0;
if (GETNLMID(sysid1) != 0) {
if (GETSYSID(sysid1) != 0) {
result = (sysid1 == sysid2);
} else if (GETSYSID(sysid1) == 0) {
result = (GETNLMID(sysid1) == GETNLMID(sysid2));
}
} else {
result = ((sysid1 != 0 && sysid1 == sysid2) ||
(sysid1 == 0 && sysid2 != 0));
}
return (result);
}
int
shr_has_remote_shares(vnode_t *vp, int32_t sysid)
{
struct shrlocklist *shrl;
int result = 0;
mutex_enter(&vp->v_lock);
shrl = vp->v_shrlocks;
while (shrl) {
if (is_match_for_has_remote(sysid, shrl->shr->s_sysid)) {
result = 1;
break;
}
shrl = shrl->next;
}
mutex_exit(&vp->v_lock);
return (result);
}
static int
isreadonly(struct vnode *vp)
{
return (vp->v_type != VCHR && vp->v_type != VBLK &&
vp->v_type != VFIFO && vn_is_readonly(vp));
}
#ifdef DEBUG
static void
print_shares(struct vnode *vp)
{
struct shrlocklist *shrl;
if (vp->v_shrlocks == NULL) {
printf("<NULL>\n");
return;
}
shrl = vp->v_shrlocks;
while (shrl) {
print_share(shrl->shr);
shrl = shrl->next;
}
}
static void
print_share(struct shrlock *shr)
{
int i;
if (shr == NULL) {
printf("<NULL>\n");
return;
}
printf(" access(%d): ", shr->s_access);
if (shr->s_access & F_RDACC)
printf("R");
if (shr->s_access & F_WRACC)
printf("W");
if ((shr->s_access & (F_RDACC|F_WRACC)) == 0)
printf("N");
printf("\n");
printf(" deny: ");
if (shr->s_deny & F_COMPAT)
printf("C");
if (shr->s_deny & F_RDDNY)
printf("R");
if (shr->s_deny & F_WRDNY)
printf("W");
if (shr->s_deny == F_NODNY)
printf("N");
printf("\n");
printf(" sysid: %d\n", shr->s_sysid);
printf(" pid: %d\n", shr->s_pid);
printf(" owner: [%d]", shr->s_own_len);
printf("'");
for (i = 0; i < shr->s_own_len; i++)
printf("%02x", (unsigned)shr->s_owner[i]);
printf("'\n");
}
#endif
int
nbl_share_conflict(vnode_t *vp, nbl_op_t op, caller_context_t *ct)
{
struct shrlocklist *shrl;
int conflict = 0;
pid_t pid;
int sysid;
ASSERT(nbl_in_crit(vp));
if (ct == NULL) {
pid = curproc->p_pid;
sysid = 0;
} else {
pid = ct->cc_pid;
sysid = ct->cc_sysid;
}
mutex_enter(&vp->v_lock);
for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
if (!(shrl->shr->s_deny & F_MANDDNY))
continue;
if (shrl->shr->s_sysid == sysid &&
shrl->shr->s_pid == pid)
continue;
switch (op) {
case NBL_READ:
if (shrl->shr->s_deny & F_RDDNY)
conflict = 1;
break;
case NBL_WRITE:
if (shrl->shr->s_deny & F_WRDNY)
conflict = 1;
break;
case NBL_READWRITE:
if (shrl->shr->s_deny & F_RWDNY)
conflict = 1;
break;
case NBL_REMOVE:
case NBL_RENAME:
if (shrl->shr->s_deny & F_RMDNY)
conflict = 1;
break;
#ifdef DEBUG
default:
cmn_err(CE_PANIC,
"nbl_share_conflict: bogus op (%d)",
op);
break;
#endif
}
if (conflict) {
DTRACE_PROBE1(conflict_shrlock,
struct shrlock *, shrl->shr);
break;
}
}
mutex_exit(&vp->v_lock);
return (conflict);
}
int
proc_has_nbmand_share_on_vp(vnode_t *vp, pid_t pid)
{
struct shrlocklist *shrl;
mutex_enter(&vp->v_lock);
for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
if (shrl->shr->s_sysid == 0 &&
(shrl->shr->s_deny & F_MANDDNY) &&
(shrl->shr->s_pid == pid)) {
mutex_exit(&vp->v_lock);
return (1);
}
}
mutex_exit(&vp->v_lock);
return (0);
}