Commit c0eae66e authored by Benny Halevy's avatar Benny Halevy Committed by Trond Myklebust

nfs: nfs4xdr: get rid of READ_BUF

Use xdr_inline_decode instead.
Open code debug printout and error return.
Signed-off-by: default avatarBenny Halevy <bhalevy@panasas.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 2460ba57
......@@ -2423,24 +2423,6 @@ static int nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, uint32_t *p,
}
#endif /* CONFIG_NFS_V4_1 */
/*
* START OF "GENERIC" DECODE ROUTINES.
* These may look a little ugly since they are imported from a "generic"
* set of XDR encode/decode routines which are intended to be shared by
* all of our NFSv4 implementations (OpenBSD, MacOS X...).
*
* If the pain of reading these is too great, it should be a straightforward
* task to translate them into Linux-specific versions which are more
* consistent with the style used in NFSv2/v3...
*/
#define READ_BUF(nbytes) do { \
p = xdr_inline_decode(xdr, nbytes); \
if (unlikely(!p)) { \
print_overflow_msg(__func__, xdr); \
return -EIO; \
} \
} while (0)
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("nfs: %s: prematurely hit end of receive buffer. "
......@@ -2452,28 +2434,42 @@ static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char
{
__be32 *p;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*len = be32_to_cpup(p++);
READ_BUF(*len);
p = xdr_inline_decode(xdr, *len);
if (unlikely(!p))
goto out_overflow;
*string = (char *)p;
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
__be32 *p;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
hdr->status = be32_to_cpup(p++);
hdr->taglen = be32_to_cpup(p++);
READ_BUF(hdr->taglen + 4);
p = xdr_inline_decode(xdr, hdr->taglen + 4);
if (unlikely(!p))
goto out_overflow;
hdr->tag = (char *)p;
p += XDR_QUADLEN(hdr->taglen);
hdr->nops = be32_to_cpup(p++);
if (unlikely(hdr->nops < 1))
return nfs4_stat_to_errno(hdr->status);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
......@@ -2482,7 +2478,9 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
uint32_t opnum;
int32_t nfserr;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
opnum = be32_to_cpup(p++);
if (opnum != expected) {
dprintk("nfs: Server returned operation"
......@@ -2494,6 +2492,9 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
if (nfserr != NFS_OK)
return nfs4_stat_to_errno(nfserr);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/* Dummy routine */
......@@ -2503,8 +2504,11 @@ static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs_client *clp)
unsigned int strlen;
char *str;
READ_BUF(12);
return decode_opaque_inline(xdr, &strlen, &str);
p = xdr_inline_decode(xdr, 12);
if (likely(p))
return decode_opaque_inline(xdr, &strlen, &str);
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
......@@ -2512,27 +2516,39 @@ static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
uint32_t bmlen;
__be32 *p;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
bmlen = be32_to_cpup(p++);
bitmap[0] = bitmap[1] = 0;
READ_BUF((bmlen << 2));
p = xdr_inline_decode(xdr, (bmlen << 2));
if (unlikely(!p))
goto out_overflow;
if (bmlen > 0) {
bitmap[0] = be32_to_cpup(p++);
if (bmlen > 1)
bitmap[1] = be32_to_cpup(p++);
}
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, __be32 **savep)
{
__be32 *p;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*attrlen = be32_to_cpup(p++);
*savep = xdr->p;
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
......@@ -2555,7 +2571,9 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *
if (unlikely(bitmap[0] & (FATTR4_WORD0_TYPE - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_TYPE)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*type = be32_to_cpup(p++);
if (*type < NF4REG || *type > NF4NAMEDATTR) {
dprintk("%s: bad type %d\n", __func__, *type);
......@@ -2566,6 +2584,9 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *
}
dprintk("%s: type=0%o\n", __func__, nfs_type2fmt[*type]);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change)
......@@ -2577,7 +2598,9 @@ static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t
if (unlikely(bitmap[0] & (FATTR4_WORD0_CHANGE - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, change);
bitmap[0] &= ~FATTR4_WORD0_CHANGE;
ret = NFS_ATTR_FATTR_CHANGE;
......@@ -2585,6 +2608,9 @@ static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t
dprintk("%s: change attribute=%Lu\n", __func__,
(unsigned long long)*change);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *size)
......@@ -2596,13 +2622,18 @@ static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *
if (unlikely(bitmap[0] & (FATTR4_WORD0_SIZE - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_SIZE)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, size);
bitmap[0] &= ~FATTR4_WORD0_SIZE;
ret = NFS_ATTR_FATTR_SIZE;
}
dprintk("%s: file size=%Lu\n", __func__, (unsigned long long)*size);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
......@@ -2613,12 +2644,17 @@ static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, ui
if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*res = be32_to_cpup(p++);
bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT;
}
dprintk("%s: link support=%s\n", __func__, *res == 0 ? "false" : "true");
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
......@@ -2629,12 +2665,17 @@ static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap,
if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*res = be32_to_cpup(p++);
bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT;
}
dprintk("%s: symlink support=%s\n", __func__, *res == 0 ? "false" : "true");
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid)
......@@ -2647,7 +2688,9 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs
if (unlikely(bitmap[0] & (FATTR4_WORD0_FSID - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_FSID)) {
READ_BUF(16);
p = xdr_inline_decode(xdr, 16);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &fsid->major);
p = xdr_decode_hyper(p, &fsid->minor);
bitmap[0] &= ~FATTR4_WORD0_FSID;
......@@ -2657,6 +2700,9 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs
(unsigned long long)fsid->major,
(unsigned long long)fsid->minor);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
......@@ -2667,12 +2713,17 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint
if (unlikely(bitmap[0] & (FATTR4_WORD0_LEASE_TIME - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_LEASE_TIME)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*res = be32_to_cpup(p++);
bitmap[0] &= ~FATTR4_WORD0_LEASE_TIME;
}
dprintk("%s: file size=%u\n", __func__, (unsigned int)*res);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
......@@ -2683,12 +2734,17 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*res = be32_to_cpup(p++);
bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT;
}
dprintk("%s: ACLs supported=%u\n", __func__, (unsigned int)*res);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
......@@ -2700,13 +2756,18 @@ static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t
if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEID - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, fileid);
bitmap[0] &= ~FATTR4_WORD0_FILEID;
ret = NFS_ATTR_FATTR_FILEID;
}
dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
......@@ -2718,13 +2779,18 @@ static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitma
if (unlikely(bitmap[1] & (FATTR4_WORD1_MOUNTED_ON_FILEID - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, fileid);
bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
ret = NFS_ATTR_FATTR_FILEID;
}
dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
......@@ -2736,12 +2802,17 @@ static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uin
if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_AVAIL - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL;
}
dprintk("%s: files avail=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
......@@ -2753,12 +2824,17 @@ static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint
if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_FREE - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[0] &= ~FATTR4_WORD0_FILES_FREE;
}
dprintk("%s: files free=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
......@@ -2770,12 +2846,17 @@ static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uin
if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_TOTAL - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_FILES_TOTAL)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[0] &= ~FATTR4_WORD0_FILES_TOTAL;
}
dprintk("%s: files total=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
......@@ -2784,7 +2865,9 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
__be32 *p;
int status = 0;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
n = be32_to_cpup(p++);
if (n == 0)
goto root_path;
......@@ -2819,6 +2902,9 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
dprintk(" status %d", status);
status = -EIO;
goto out;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res)
......@@ -2836,7 +2922,9 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
status = decode_pathname(xdr, &res->fs_path);
if (unlikely(status != 0))
goto out;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
n = be32_to_cpup(p++);
if (n <= 0)
goto out_eio;
......@@ -2845,7 +2933,9 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
u32 m;
struct nfs4_fs_location *loc = &res->locations[res->nlocations];
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
m = be32_to_cpup(p++);
loc->nservers = 0;
......@@ -2885,6 +2975,8 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
out:
dprintk("%s: fs_locations done, error = %d\n", __func__, status);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
out_eio:
status = -EIO;
goto out;
......@@ -2899,12 +2991,17 @@ static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uin
if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXFILESIZE - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_MAXFILESIZE)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[0] &= ~FATTR4_WORD0_MAXFILESIZE;
}
dprintk("%s: maxfilesize=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink)
......@@ -2916,12 +3013,17 @@ static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXLINK - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*maxlink = be32_to_cpup(p++);
bitmap[0] &= ~FATTR4_WORD0_MAXLINK;
}
dprintk("%s: maxlink=%u\n", __func__, *maxlink);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxname)
......@@ -2933,12 +3035,17 @@ static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXNAME - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_MAXNAME)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*maxname = be32_to_cpup(p++);
bitmap[0] &= ~FATTR4_WORD0_MAXNAME;
}
dprintk("%s: maxname=%u\n", __func__, *maxname);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
......@@ -2951,7 +3058,9 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_MAXREAD)) {
uint64_t maxread;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &maxread);
if (maxread > 0x7FFFFFFF)
maxread = 0x7FFFFFFF;
......@@ -2960,6 +3069,9 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
}
dprintk("%s: maxread=%lu\n", __func__, (unsigned long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
......@@ -2972,7 +3084,9 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_MAXWRITE)) {
uint64_t maxwrite;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &maxwrite);
if (maxwrite > 0x7FFFFFFF)
maxwrite = 0x7FFFFFFF;
......@@ -2981,6 +3095,9 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32
}
dprintk("%s: maxwrite=%lu\n", __func__, (unsigned long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *mode)
......@@ -2993,7 +3110,9 @@ static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *m
if (unlikely(bitmap[1] & (FATTR4_WORD1_MODE - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_MODE)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
tmp = be32_to_cpup(p++);
*mode = tmp & ~S_IFMT;
bitmap[1] &= ~FATTR4_WORD1_MODE;
......@@ -3001,6 +3120,9 @@ static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *m
}
dprintk("%s: file mode=0%o\n", __func__, (unsigned int)*mode);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *nlink)
......@@ -3012,13 +3134,18 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t
if (unlikely(bitmap[1] & (FATTR4_WORD1_NUMLINKS - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_NUMLINKS)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
*nlink = be32_to_cpup(p++);
bitmap[1] &= ~FATTR4_WORD1_NUMLINKS;
ret = NFS_ATTR_FATTR_NLINK;
}
dprintk("%s: nlink=%u\n", __func__, (unsigned int)*nlink);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_client *clp, uint32_t *uid)
......@@ -3031,9 +3158,13 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_OWNER)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
len = be32_to_cpup(p++);
READ_BUF(len);
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
goto out_overflow;
if (len < XDR_MAX_NETOBJ) {
if (nfs_map_name_to_uid(clp, (char *)p, len, uid) == 0)
ret = NFS_ATTR_FATTR_OWNER;
......@@ -3047,6 +3178,9 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
}
dprintk("%s: uid=%d\n", __func__, (int)*uid);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_client *clp, uint32_t *gid)
......@@ -3059,9 +3193,13 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_OWNER_GROUP)) {
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
len = be32_to_cpup(p++);
READ_BUF(len);
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
goto out_overflow;
if (len < XDR_MAX_NETOBJ) {
if (nfs_map_group_to_gid(clp, (char *)p, len, gid) == 0)
ret = NFS_ATTR_FATTR_GROUP;
......@@ -3075,6 +3213,9 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
}
dprintk("%s: gid=%d\n", __func__, (int)*gid);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rdev)
......@@ -3089,7 +3230,9 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde
if (likely(bitmap[1] & FATTR4_WORD1_RAWDEV)) {
dev_t tmp;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
major = be32_to_cpup(p++);
minor = be32_to_cpup(p++);
tmp = MKDEV(major, minor);
......@@ -3100,6 +3243,9 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde
}
dprintk("%s: rdev=(0x%x:0x%x)\n", __func__, major, minor);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
......@@ -3111,12 +3257,17 @@ static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uin
if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_AVAIL - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_SPACE_AVAIL)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[1] &= ~FATTR4_WORD1_SPACE_AVAIL;
}
dprintk("%s: space avail=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
......@@ -3128,12 +3279,17 @@ static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint
if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_FREE - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_SPACE_FREE)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[1] &= ~FATTR4_WORD1_SPACE_FREE;
}
dprintk("%s: space free=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
......@@ -3145,12 +3301,17 @@ static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uin
if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_TOTAL - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_SPACE_TOTAL)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, res);
bitmap[1] &= ~FATTR4_WORD1_SPACE_TOTAL;
}
dprintk("%s: space total=%Lu\n", __func__, (unsigned long long)*res);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *used)
......@@ -3162,7 +3323,9 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint
if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_USED - 1U)))
return -EIO;
if (likely(bitmap[1] & FATTR4_WORD1_SPACE_USED)) {
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, used);
bitmap[1] &= ~FATTR4_WORD1_SPACE_USED;
ret = NFS_ATTR_FATTR_SPACE_USED;
......@@ -3170,6 +3333,9 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint
dprintk("%s: space used=%Lu\n", __func__,
(unsigned long long)*used);
return ret;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
......@@ -3178,12 +3344,17 @@ static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
uint64_t sec;
uint32_t nsec;
READ_BUF(12);
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &sec);
nsec = be32_to_cpup(p++);
time->tv_sec = (time_t)sec;
time->tv_nsec = (long)nsec;
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
......@@ -3261,11 +3432,16 @@ static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *c
{
__be32 *p;
READ_BUF(20);
p = xdr_inline_decode(xdr, 20);
if (unlikely(!p))
goto out_overflow;
cinfo->atomic = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &cinfo->before);
p = xdr_decode_hyper(p, &cinfo->after);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_access(struct xdr_stream *xdr, struct nfs4_accessres *access)
......@@ -3277,12 +3453,17 @@ static int decode_access(struct xdr_stream *xdr, struct nfs4_accessres *access)
status = decode_op_hdr(xdr, OP_ACCESS);
if (status)
return status;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
supp = be32_to_cpup(p++);
acc = be32_to_cpup(p++);
access->supported = supp;
access->access = acc;
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len)
......@@ -3341,10 +3522,16 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
return status;
if ((status = decode_change_info(xdr, cinfo)))
return status;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
bmlen = be32_to_cpup(p++);
READ_BUF(bmlen << 2);
return 0;
p = xdr_inline_decode(xdr, bmlen << 2);
if (likely(p))
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
......@@ -3596,14 +3783,21 @@ static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh)
if (status)
return status;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
len = be32_to_cpup(p++);
if (len > NFS4_FHSIZE)
return -EIO;
fh->size = len;
READ_BUF(len);
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
goto out_overflow;
memcpy(fh->data, p, len);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
......@@ -3625,7 +3819,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
__be32 *p;
uint32_t namelen, type;
READ_BUF(32);
p = xdr_inline_decode(xdr, 32);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &offset);
p = xdr_decode_hyper(p, &length);
type = be32_to_cpup(p++);
......@@ -3641,8 +3837,12 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
}
p = xdr_decode_hyper(p, &clientid);
namelen = be32_to_cpup(p++);
READ_BUF(namelen);
return -NFS4ERR_DENIED;
p = xdr_inline_decode(xdr, namelen);
if (likely(p))
return -NFS4ERR_DENIED;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res)
......@@ -3697,7 +3897,9 @@ static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
__be32 *p;
uint32_t limit_type, nblocks, blocksize;
READ_BUF(12);
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
goto out_overflow;
limit_type = be32_to_cpup(p++);
switch (limit_type) {
case 1:
......@@ -3709,6 +3911,9 @@ static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
*maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
}
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
......@@ -3717,7 +3922,9 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
uint32_t delegation_type;
int status;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
delegation_type = be32_to_cpup(p++);
if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
res->delegation_type = 0;
......@@ -3726,7 +3933,9 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
status = decode_stateid(xdr, &res->delegation);
if (unlikely(status))
return status;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
res->do_recall = be32_to_cpup(p++);
switch (delegation_type) {
......@@ -3739,6 +3948,9 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
return -EIO;
}
return decode_ace(xdr, NULL, res->server->nfs_client);
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
......@@ -3757,13 +3969,17 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
decode_change_info(xdr, &res->cinfo);
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
res->rflags = be32_to_cpup(p++);
bmlen = be32_to_cpup(p++);
if (bmlen > 10)
goto xdr_error;
READ_BUF(bmlen << 2);
p = xdr_inline_decode(xdr, bmlen << 2);
if (unlikely(!p))
goto out_overflow;
savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE);
for (i = 0; i < savewords; ++i)
res->attrset[i] = be32_to_cpup(p++);
......@@ -3774,6 +3990,9 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
xdr_error:
dprintk("%s: Bitmap too large! Length = %u\n", __func__, bmlen);
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
......@@ -3820,7 +4039,9 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
status = decode_op_hdr(xdr, OP_READ);
if (status)
return status;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
eof = be32_to_cpup(p++);
count = be32_to_cpup(p++);
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
......@@ -3835,6 +4056,9 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
res->eof = eof;
res->count = count;
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir)
......@@ -3947,7 +4171,9 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
return status;
/* Convert length of symlink */
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
len = be32_to_cpup(p++);
if (len >= rcvbuf->page_len || len <= 0) {
dprintk("nfs: server returned giant symlink!\n");
......@@ -3972,6 +4198,9 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
kaddr[len+rcvbuf->page_base] = '\0';
kunmap_atomic(kaddr, KM_USER0);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_remove(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
......@@ -4069,10 +4298,16 @@ static int decode_setattr(struct xdr_stream *xdr)
status = decode_op_hdr(xdr, OP_SETATTR);
if (status)
return status;
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
bmlen = be32_to_cpup(p++);
READ_BUF(bmlen << 2);
return 0;
p = xdr_inline_decode(xdr, bmlen << 2);
if (likely(p))
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
......@@ -4081,7 +4316,9 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
uint32_t opnum;
int32_t nfserr;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
opnum = be32_to_cpup(p++);
if (opnum != OP_SETCLIENTID) {
dprintk("nfs: decode_setclientid: Server returned operation"
......@@ -4090,26 +4327,39 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
}
nfserr = be32_to_cpup(p++);
if (nfserr == NFS_OK) {
READ_BUF(8 + NFS4_VERIFIER_SIZE);
p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &clp->cl_clientid);
memcpy(clp->cl_confirm.data, p, NFS4_VERIFIER_SIZE);
} else if (nfserr == NFSERR_CLID_INUSE) {
uint32_t len;
/* skip netid string */
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
len = be32_to_cpup(p++);
READ_BUF(len);
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
goto out_overflow;
/* skip uaddr string */
READ_BUF(4);
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
len = be32_to_cpup(p++);
READ_BUF(len);
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
goto out_overflow;
return -NFSERR_CLID_INUSE;
} else
return nfs4_stat_to_errno(nfserr);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_setclientid_confirm(struct xdr_stream *xdr)
......@@ -4126,11 +4376,16 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
if (status)
return status;
READ_BUF(16);
p = xdr_inline_decode(xdr, 16);
if (unlikely(!p))
goto out_overflow;
res->count = be32_to_cpup(p++);
res->verf->committed = be32_to_cpup(p++);
memcpy(res->verf->verifier, p, 8);
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_delegreturn(struct xdr_stream *xdr)
......@@ -4152,9 +4407,13 @@ static int decode_exchange_id(struct xdr_stream *xdr,
if (status)
return status;
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &clp->cl_ex_clid);
READ_BUF(12);
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
goto out_overflow;
clp->cl_seqid = be32_to_cpup(p++);
clp->cl_exchange_flags = be32_to_cpup(p++);
......@@ -4164,7 +4423,9 @@ static int decode_exchange_id(struct xdr_stream *xdr,
return -EIO;
/* Throw away minor_id */
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
/* Throw away Major id */
status = decode_opaque_inline(xdr, &dummy, &dummy_str);
......@@ -4182,6 +4443,9 @@ static int decode_exchange_id(struct xdr_stream *xdr,
return status;
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_chan_attrs(struct xdr_stream *xdr,
......@@ -4190,7 +4454,9 @@ static int decode_chan_attrs(struct xdr_stream *xdr,
__be32 *p;
u32 nr_attrs;
READ_BUF(28);
p = xdr_inline_decode(xdr, 28);
if (unlikely(!p))
goto out_overflow;
attrs->headerpadsz = be32_to_cpup(p++);
attrs->max_rqst_sz = be32_to_cpup(p++);
attrs->max_resp_sz = be32_to_cpup(p++);
......@@ -4203,9 +4469,15 @@ static int decode_chan_attrs(struct xdr_stream *xdr,
__func__, nr_attrs);
return -EINVAL;
}
if (nr_attrs == 1)
READ_BUF(4); /* skip rdma_attrs */
if (nr_attrs == 1) {
p = xdr_inline_decode(xdr, 4); /* skip rdma_attrs */
if (unlikely(!p))
goto out_overflow;
}
return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid)
......@@ -4228,7 +4500,9 @@ static int decode_create_session(struct xdr_stream *xdr,
return status;
/* seqid, flags */
READ_BUF(8);
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
clp->cl_seqid = be32_to_cpup(p++);
session->flags = be32_to_cpup(p++);
......@@ -4237,6 +4511,9 @@ static int decode_create_session(struct xdr_stream *xdr,
if (!status)
status = decode_chan_attrs(xdr, &session->bc_attrs);
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
static int decode_destroy_session(struct xdr_stream *xdr, void *dummy)
......@@ -4277,7 +4554,9 @@ static int decode_sequence(struct xdr_stream *xdr,
goto out_err;
}
READ_BUF(20);
p = xdr_inline_decode(xdr, 20);
if (unlikely(!p))
goto out_overflow;
/* seqid */
slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid];
......@@ -4302,6 +4581,10 @@ static int decode_sequence(struct xdr_stream *xdr,
out_err:
res->sr_status = status;
return status;
out_overflow:
print_overflow_msg(__func__, xdr);
status = -EIO;
goto out_err;
#else /* CONFIG_NFS_V4_1 */
return 0;
#endif /* CONFIG_NFS_V4_1 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment