diff --git a/fs/lockd/share.h b/fs/lockd/share.h index 1ec3ccdb2aef..a12b6c454f58 100644 --- a/fs/lockd/share.h +++ b/fs/lockd/share.h @@ -8,9 +8,14 @@ #ifndef _LOCKD_SHARE_H #define _LOCKD_SHARE_H +#include + /* Synthetic svid for lockowner lookup during share operations */ #define LOCKD_SHARE_SVID (~(u32)0) +/* One bit per (access, deny) pair; index = (access << 2) | deny */ +#define LOCKD_FSH_BIT(a, d) BIT(((a) << 2) | (d)) + /* * DOS share for a specific file */ @@ -21,12 +26,13 @@ struct lockd_share { struct xdr_netobj s_owner; /* owner handle */ u32 s_access; /* access mode */ u32 s_mode; /* deny mode */ + u16 s_access_deny_bmap; /* held (access, deny) pairs */ }; __be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, struct xdr_netobj *oh, u32 access, u32 mode); __be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, - struct xdr_netobj *oh); + struct xdr_netobj *oh, u32 access, u32 mode); void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, nlm_host_match_fn_t); diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index b73004a7987e..d104aab0881a 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -1079,7 +1079,9 @@ static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp) if (resp->xdrgen.stat) goto out; - resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh); + resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh, + argp->xdrgen.share.access, + argp->xdrgen.share.mode); nlmsvc_release_lockowner(lock); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index d410b8c69893..a1f9d66c2981 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -1098,7 +1098,9 @@ static __be32 nlmsvc_proc_unshare(struct svc_rqst *rqstp) if (resp->xdrgen.stat) goto out; - resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh); + resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh, + argp->xdrgen.share.access, + argp->xdrgen.share.mode); nlmsvc_release_lockowner(lock); diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 5ac0ec25d62d..a58b7035b58b 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -25,6 +25,25 @@ nlm_cmp_owner(struct lockd_share *share, struct xdr_netobj *oh) && !memcmp(share->s_owner.data, oh->data, oh->len); } +/* + * Recompute s_access / s_mode as the union of every (access, deny) pair + * whose bit is currently set in s_access_deny_bmap. + */ +static void nlm_recompute_share(struct lockd_share *share) +{ + u32 new_access = 0, new_mode = 0; + unsigned int i; + + for (i = 0; i < 16; i++) { + if (share->s_access_deny_bmap & BIT(i)) { + new_access |= i >> 2; + new_mode |= i & 3; + } + } + share->s_access = new_access; + share->s_mode = new_mode; +} + /** * nlmsvc_share_file - create a share * @host: Network client peer @@ -64,12 +83,13 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, share->s_host = host; share->s_owner.data = ohdata; share->s_owner.len = oh->len; + share->s_access_deny_bmap = 0; share->s_next = file->f_shares; file->f_shares = share; update: - share->s_access = access; - share->s_mode = mode; + share->s_access_deny_bmap |= LOCKD_FSH_BIT(access, mode); + nlm_recompute_share(share); return nlm_granted; } @@ -78,12 +98,14 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, * @host: Network client peer * @file: File to be unshared * @oh: Share owner handle + * @access: Access mode of the SHARE being released + * @mode: Deny mode of the SHARE being released * * Returns an NLM status code. */ __be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, - struct xdr_netobj *oh) + struct xdr_netobj *oh, u32 access, u32 mode) { struct lockd_share *share, **shpp; @@ -93,8 +115,12 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, for (shpp = &file->f_shares; (share = *shpp) != NULL; shpp = &share->s_next) { if (share->s_host == host && nlm_cmp_owner(share, oh)) { - *shpp = share->s_next; - kfree(share); + share->s_access_deny_bmap &= ~LOCKD_FSH_BIT(access, mode); + nlm_recompute_share(share); + if (!share->s_access_deny_bmap) { + *shpp = share->s_next; + kfree(share); + } return nlm_granted; } }