mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
7.7 KiB
362 lines
7.7 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* linux/fs/lockd/xdr4.c |
|
* |
|
* XDR support for lockd and the lock client. |
|
* |
|
* Copyright (C) 1995, 1996 Olaf Kirch <[email protected]> |
|
* Copyright (C) 1999, Trond Myklebust <[email protected]> |
|
*/ |
|
|
|
#include <linux/types.h> |
|
#include <linux/sched.h> |
|
#include <linux/nfs.h> |
|
|
|
#include <linux/sunrpc/xdr.h> |
|
#include <linux/sunrpc/clnt.h> |
|
#include <linux/sunrpc/svc.h> |
|
#include <linux/sunrpc/stats.h> |
|
#include <linux/lockd/lockd.h> |
|
|
|
#include "svcxdr.h" |
|
|
|
static inline loff_t |
|
s64_to_loff_t(__s64 offset) |
|
{ |
|
return (loff_t)offset; |
|
} |
|
|
|
|
|
static inline s64 |
|
loff_t_to_s64(loff_t offset) |
|
{ |
|
s64 res; |
|
if (offset > NLM4_OFFSET_MAX) |
|
res = NLM4_OFFSET_MAX; |
|
else if (offset < -NLM4_OFFSET_MAX) |
|
res = -NLM4_OFFSET_MAX; |
|
else |
|
res = offset; |
|
return res; |
|
} |
|
|
|
/* |
|
* NLM file handles are defined by specification to be a variable-length |
|
* XDR opaque no longer than 1024 bytes. However, this implementation |
|
* limits their length to the size of an NFSv3 file handle. |
|
*/ |
|
static bool |
|
svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) |
|
{ |
|
__be32 *p; |
|
u32 len; |
|
|
|
if (xdr_stream_decode_u32(xdr, &len) < 0) |
|
return false; |
|
if (len > NFS_MAXFHSIZE) |
|
return false; |
|
|
|
p = xdr_inline_decode(xdr, len); |
|
if (!p) |
|
return false; |
|
fh->size = len; |
|
memcpy(fh->data, p, len); |
|
memset(fh->data + len, 0, sizeof(fh->data) - len); |
|
|
|
return true; |
|
} |
|
|
|
static bool |
|
svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) |
|
{ |
|
struct file_lock *fl = &lock->fl; |
|
u64 len, start; |
|
s64 end; |
|
|
|
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) |
|
return false; |
|
if (!svcxdr_decode_fhandle(xdr, &lock->fh)) |
|
return false; |
|
if (!svcxdr_decode_owner(xdr, &lock->oh)) |
|
return false; |
|
if (xdr_stream_decode_u32(xdr, &lock->svid) < 0) |
|
return false; |
|
if (xdr_stream_decode_u64(xdr, &start) < 0) |
|
return false; |
|
if (xdr_stream_decode_u64(xdr, &len) < 0) |
|
return false; |
|
|
|
locks_init_lock(fl); |
|
fl->fl_flags = FL_POSIX; |
|
fl->fl_type = F_RDLCK; |
|
end = start + len - 1; |
|
fl->fl_start = s64_to_loff_t(start); |
|
if (len == 0 || end < 0) |
|
fl->fl_end = OFFSET_MAX; |
|
else |
|
fl->fl_end = s64_to_loff_t(end); |
|
|
|
return true; |
|
} |
|
|
|
static bool |
|
svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock) |
|
{ |
|
const struct file_lock *fl = &lock->fl; |
|
s64 start, len; |
|
|
|
/* exclusive */ |
|
if (xdr_stream_encode_bool(xdr, fl->fl_type != F_RDLCK) < 0) |
|
return false; |
|
if (xdr_stream_encode_u32(xdr, lock->svid) < 0) |
|
return false; |
|
if (!svcxdr_encode_owner(xdr, &lock->oh)) |
|
return false; |
|
start = loff_t_to_s64(fl->fl_start); |
|
if (fl->fl_end == OFFSET_MAX) |
|
len = 0; |
|
else |
|
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); |
|
if (xdr_stream_encode_u64(xdr, start) < 0) |
|
return false; |
|
if (xdr_stream_encode_u64(xdr, len) < 0) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
static bool |
|
svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp) |
|
{ |
|
if (!svcxdr_encode_stats(xdr, resp->status)) |
|
return false; |
|
switch (resp->status) { |
|
case nlm_lck_denied: |
|
if (!svcxdr_encode_holder(xdr, &resp->lock)) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
/* |
|
* Decode Call arguments |
|
*/ |
|
|
|
int |
|
nlm4svc_decode_void(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_testargs(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_args *argp = rqstp->rq_argp; |
|
u32 exclusive; |
|
|
|
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) |
|
return 0; |
|
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) |
|
return 0; |
|
if (!svcxdr_decode_lock(xdr, &argp->lock)) |
|
return 0; |
|
if (exclusive) |
|
argp->lock.fl.fl_type = F_WRLCK; |
|
|
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_args *argp = rqstp->rq_argp; |
|
u32 exclusive; |
|
|
|
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) |
|
return 0; |
|
if (xdr_stream_decode_bool(xdr, &argp->block) < 0) |
|
return 0; |
|
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) |
|
return 0; |
|
if (!svcxdr_decode_lock(xdr, &argp->lock)) |
|
return 0; |
|
if (exclusive) |
|
argp->lock.fl.fl_type = F_WRLCK; |
|
if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0) |
|
return 0; |
|
if (xdr_stream_decode_u32(xdr, &argp->state) < 0) |
|
return 0; |
|
argp->monitor = 1; /* monitor client by default */ |
|
|
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_args *argp = rqstp->rq_argp; |
|
u32 exclusive; |
|
|
|
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) |
|
return 0; |
|
if (xdr_stream_decode_bool(xdr, &argp->block) < 0) |
|
return 0; |
|
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) |
|
return 0; |
|
if (!svcxdr_decode_lock(xdr, &argp->lock)) |
|
return 0; |
|
if (exclusive) |
|
argp->lock.fl.fl_type = F_WRLCK; |
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_args *argp = rqstp->rq_argp; |
|
|
|
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) |
|
return 0; |
|
if (!svcxdr_decode_lock(xdr, &argp->lock)) |
|
return 0; |
|
argp->lock.fl.fl_type = F_UNLCK; |
|
|
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_res(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_res *resp = rqstp->rq_argp; |
|
|
|
if (!svcxdr_decode_cookie(xdr, &resp->cookie)) |
|
return 0; |
|
if (!svcxdr_decode_stats(xdr, &resp->status)) |
|
return 0; |
|
|
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_reboot *argp = rqstp->rq_argp; |
|
u32 len; |
|
|
|
if (xdr_stream_decode_u32(xdr, &len) < 0) |
|
return 0; |
|
if (len > SM_MAXSTRLEN) |
|
return 0; |
|
p = xdr_inline_decode(xdr, len); |
|
if (!p) |
|
return 0; |
|
argp->len = len; |
|
argp->mon = (char *)p; |
|
if (xdr_stream_decode_u32(xdr, &argp->state) < 0) |
|
return 0; |
|
p = xdr_inline_decode(xdr, SM_PRIV_SIZE); |
|
if (!p) |
|
return 0; |
|
memcpy(&argp->priv.data, p, sizeof(argp->priv.data)); |
|
|
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_args *argp = rqstp->rq_argp; |
|
struct nlm_lock *lock = &argp->lock; |
|
|
|
memset(lock, 0, sizeof(*lock)); |
|
locks_init_lock(&lock->fl); |
|
lock->svid = ~(u32)0; |
|
|
|
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) |
|
return 0; |
|
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) |
|
return 0; |
|
if (!svcxdr_decode_fhandle(xdr, &lock->fh)) |
|
return 0; |
|
if (!svcxdr_decode_owner(xdr, &lock->oh)) |
|
return 0; |
|
/* XXX: Range checks are missing in the original code */ |
|
if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0) |
|
return 0; |
|
if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0) |
|
return 0; |
|
|
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_decode_notify(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_arg_stream; |
|
struct nlm_args *argp = rqstp->rq_argp; |
|
struct nlm_lock *lock = &argp->lock; |
|
|
|
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) |
|
return 0; |
|
if (xdr_stream_decode_u32(xdr, &argp->state) < 0) |
|
return 0; |
|
|
|
return 1; |
|
} |
|
|
|
|
|
/* |
|
* Encode Reply results |
|
*/ |
|
|
|
int |
|
nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
return 1; |
|
} |
|
|
|
int |
|
nlm4svc_encode_testres(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_res_stream; |
|
struct nlm_res *resp = rqstp->rq_resp; |
|
|
|
return svcxdr_encode_cookie(xdr, &resp->cookie) && |
|
svcxdr_encode_testrply(xdr, resp); |
|
} |
|
|
|
int |
|
nlm4svc_encode_res(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_res_stream; |
|
struct nlm_res *resp = rqstp->rq_resp; |
|
|
|
return svcxdr_encode_cookie(xdr, &resp->cookie) && |
|
svcxdr_encode_stats(xdr, resp->status); |
|
} |
|
|
|
int |
|
nlm4svc_encode_shareres(struct svc_rqst *rqstp, __be32 *p) |
|
{ |
|
struct xdr_stream *xdr = &rqstp->rq_res_stream; |
|
struct nlm_res *resp = rqstp->rq_resp; |
|
|
|
if (!svcxdr_encode_cookie(xdr, &resp->cookie)) |
|
return 0; |
|
if (!svcxdr_encode_stats(xdr, resp->status)) |
|
return 0; |
|
/* sequence */ |
|
if (xdr_stream_encode_u32(xdr, 0) < 0) |
|
return 0; |
|
|
|
return 1; |
|
}
|
|
|