162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014-2016 Christoph Hellwig.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/sunrpc/svc.h>
662306a36Sopenharmony_ci#include <linux/exportfs.h>
762306a36Sopenharmony_ci#include <linux/iomap.h>
862306a36Sopenharmony_ci#include <linux/nfs4.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "nfsd.h"
1162306a36Sopenharmony_ci#include "blocklayoutxdr.h"
1262306a36Sopenharmony_ci#include "vfs.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define NFSDDBG_FACILITY	NFSDDBG_PNFS
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci__be32
1862306a36Sopenharmony_cinfsd4_block_encode_layoutget(struct xdr_stream *xdr,
1962306a36Sopenharmony_ci		struct nfsd4_layoutget *lgp)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct pnfs_block_extent *b = lgp->lg_content;
2262306a36Sopenharmony_ci	int len = sizeof(__be32) + 5 * sizeof(__be64) + sizeof(__be32);
2362306a36Sopenharmony_ci	__be32 *p;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, sizeof(__be32) + len);
2662306a36Sopenharmony_ci	if (!p)
2762306a36Sopenharmony_ci		return nfserr_toosmall;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	*p++ = cpu_to_be32(len);
3062306a36Sopenharmony_ci	*p++ = cpu_to_be32(1);		/* we always return a single extent */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	p = xdr_encode_opaque_fixed(p, &b->vol_id,
3362306a36Sopenharmony_ci			sizeof(struct nfsd4_deviceid));
3462306a36Sopenharmony_ci	p = xdr_encode_hyper(p, b->foff);
3562306a36Sopenharmony_ci	p = xdr_encode_hyper(p, b->len);
3662306a36Sopenharmony_ci	p = xdr_encode_hyper(p, b->soff);
3762306a36Sopenharmony_ci	*p++ = cpu_to_be32(b->es);
3862306a36Sopenharmony_ci	return 0;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int
4262306a36Sopenharmony_cinfsd4_block_encode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	__be32 *p;
4562306a36Sopenharmony_ci	int len;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	switch (b->type) {
4862306a36Sopenharmony_ci	case PNFS_BLOCK_VOLUME_SIMPLE:
4962306a36Sopenharmony_ci		len = 4 + 4 + 8 + 4 + (XDR_QUADLEN(b->simple.sig_len) << 2);
5062306a36Sopenharmony_ci		p = xdr_reserve_space(xdr, len);
5162306a36Sopenharmony_ci		if (!p)
5262306a36Sopenharmony_ci			return -ETOOSMALL;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		*p++ = cpu_to_be32(b->type);
5562306a36Sopenharmony_ci		*p++ = cpu_to_be32(1);	/* single signature */
5662306a36Sopenharmony_ci		p = xdr_encode_hyper(p, b->simple.offset);
5762306a36Sopenharmony_ci		p = xdr_encode_opaque(p, b->simple.sig, b->simple.sig_len);
5862306a36Sopenharmony_ci		break;
5962306a36Sopenharmony_ci	case PNFS_BLOCK_VOLUME_SCSI:
6062306a36Sopenharmony_ci		len = 4 + 4 + 4 + 4 + (XDR_QUADLEN(b->scsi.designator_len) << 2) + 8;
6162306a36Sopenharmony_ci		p = xdr_reserve_space(xdr, len);
6262306a36Sopenharmony_ci		if (!p)
6362306a36Sopenharmony_ci			return -ETOOSMALL;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		*p++ = cpu_to_be32(b->type);
6662306a36Sopenharmony_ci		*p++ = cpu_to_be32(b->scsi.code_set);
6762306a36Sopenharmony_ci		*p++ = cpu_to_be32(b->scsi.designator_type);
6862306a36Sopenharmony_ci		p = xdr_encode_opaque(p, b->scsi.designator, b->scsi.designator_len);
6962306a36Sopenharmony_ci		p = xdr_encode_hyper(p, b->scsi.pr_key);
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	default:
7262306a36Sopenharmony_ci		return -ENOTSUPP;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return len;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci__be32
7962306a36Sopenharmony_cinfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr,
8062306a36Sopenharmony_ci		struct nfsd4_getdeviceinfo *gdp)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct pnfs_block_deviceaddr *dev = gdp->gd_device;
8362306a36Sopenharmony_ci	int len = sizeof(__be32), ret, i;
8462306a36Sopenharmony_ci	__be32 *p;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/*
8762306a36Sopenharmony_ci	 * See paragraph 5 of RFC 8881 S18.40.3.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	if (!gdp->gd_maxcount) {
9062306a36Sopenharmony_ci		if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
9162306a36Sopenharmony_ci			return nfserr_resource;
9262306a36Sopenharmony_ci		return nfs_ok;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, len + sizeof(__be32));
9662306a36Sopenharmony_ci	if (!p)
9762306a36Sopenharmony_ci		return nfserr_resource;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	for (i = 0; i < dev->nr_volumes; i++) {
10062306a36Sopenharmony_ci		ret = nfsd4_block_encode_volume(xdr, &dev->volumes[i]);
10162306a36Sopenharmony_ci		if (ret < 0)
10262306a36Sopenharmony_ci			return nfserrno(ret);
10362306a36Sopenharmony_ci		len += ret;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/*
10762306a36Sopenharmony_ci	 * Fill in the overall length and number of volumes at the beginning
10862306a36Sopenharmony_ci	 * of the layout.
10962306a36Sopenharmony_ci	 */
11062306a36Sopenharmony_ci	*p++ = cpu_to_be32(len);
11162306a36Sopenharmony_ci	*p++ = cpu_to_be32(dev->nr_volumes);
11262306a36Sopenharmony_ci	return 0;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciint
11662306a36Sopenharmony_cinfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp,
11762306a36Sopenharmony_ci		u32 block_size)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct iomap *iomaps;
12062306a36Sopenharmony_ci	u32 nr_iomaps, i;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (len < sizeof(u32)) {
12362306a36Sopenharmony_ci		dprintk("%s: extent array too small: %u\n", __func__, len);
12462306a36Sopenharmony_ci		return -EINVAL;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	len -= sizeof(u32);
12762306a36Sopenharmony_ci	if (len % PNFS_BLOCK_EXTENT_SIZE) {
12862306a36Sopenharmony_ci		dprintk("%s: extent array invalid: %u\n", __func__, len);
12962306a36Sopenharmony_ci		return -EINVAL;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	nr_iomaps = be32_to_cpup(p++);
13362306a36Sopenharmony_ci	if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) {
13462306a36Sopenharmony_ci		dprintk("%s: extent array size mismatch: %u/%u\n",
13562306a36Sopenharmony_ci			__func__, len, nr_iomaps);
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
14062306a36Sopenharmony_ci	if (!iomaps) {
14162306a36Sopenharmony_ci		dprintk("%s: failed to allocate extent array\n", __func__);
14262306a36Sopenharmony_ci		return -ENOMEM;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	for (i = 0; i < nr_iomaps; i++) {
14662306a36Sopenharmony_ci		struct pnfs_block_extent bex;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid));
14962306a36Sopenharmony_ci		p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		p = xdr_decode_hyper(p, &bex.foff);
15262306a36Sopenharmony_ci		if (bex.foff & (block_size - 1)) {
15362306a36Sopenharmony_ci			dprintk("%s: unaligned offset 0x%llx\n",
15462306a36Sopenharmony_ci				__func__, bex.foff);
15562306a36Sopenharmony_ci			goto fail;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci		p = xdr_decode_hyper(p, &bex.len);
15862306a36Sopenharmony_ci		if (bex.len & (block_size - 1)) {
15962306a36Sopenharmony_ci			dprintk("%s: unaligned length 0x%llx\n",
16062306a36Sopenharmony_ci				__func__, bex.foff);
16162306a36Sopenharmony_ci			goto fail;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci		p = xdr_decode_hyper(p, &bex.soff);
16462306a36Sopenharmony_ci		if (bex.soff & (block_size - 1)) {
16562306a36Sopenharmony_ci			dprintk("%s: unaligned disk offset 0x%llx\n",
16662306a36Sopenharmony_ci				__func__, bex.soff);
16762306a36Sopenharmony_ci			goto fail;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci		bex.es = be32_to_cpup(p++);
17062306a36Sopenharmony_ci		if (bex.es != PNFS_BLOCK_READWRITE_DATA) {
17162306a36Sopenharmony_ci			dprintk("%s: incorrect extent state %d\n",
17262306a36Sopenharmony_ci				__func__, bex.es);
17362306a36Sopenharmony_ci			goto fail;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		iomaps[i].offset = bex.foff;
17762306a36Sopenharmony_ci		iomaps[i].length = bex.len;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	*iomapp = iomaps;
18162306a36Sopenharmony_ci	return nr_iomaps;
18262306a36Sopenharmony_cifail:
18362306a36Sopenharmony_ci	kfree(iomaps);
18462306a36Sopenharmony_ci	return -EINVAL;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciint
18862306a36Sopenharmony_cinfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp,
18962306a36Sopenharmony_ci		u32 block_size)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct iomap *iomaps;
19262306a36Sopenharmony_ci	u32 nr_iomaps, expected, i;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (len < sizeof(u32)) {
19562306a36Sopenharmony_ci		dprintk("%s: extent array too small: %u\n", __func__, len);
19662306a36Sopenharmony_ci		return -EINVAL;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	nr_iomaps = be32_to_cpup(p++);
20062306a36Sopenharmony_ci	expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE;
20162306a36Sopenharmony_ci	if (len != expected) {
20262306a36Sopenharmony_ci		dprintk("%s: extent array size mismatch: %u/%u\n",
20362306a36Sopenharmony_ci			__func__, len, expected);
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
20862306a36Sopenharmony_ci	if (!iomaps) {
20962306a36Sopenharmony_ci		dprintk("%s: failed to allocate extent array\n", __func__);
21062306a36Sopenharmony_ci		return -ENOMEM;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (i = 0; i < nr_iomaps; i++) {
21462306a36Sopenharmony_ci		u64 val;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		p = xdr_decode_hyper(p, &val);
21762306a36Sopenharmony_ci		if (val & (block_size - 1)) {
21862306a36Sopenharmony_ci			dprintk("%s: unaligned offset 0x%llx\n", __func__, val);
21962306a36Sopenharmony_ci			goto fail;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		iomaps[i].offset = val;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		p = xdr_decode_hyper(p, &val);
22462306a36Sopenharmony_ci		if (val & (block_size - 1)) {
22562306a36Sopenharmony_ci			dprintk("%s: unaligned length 0x%llx\n", __func__, val);
22662306a36Sopenharmony_ci			goto fail;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci		iomaps[i].length = val;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	*iomapp = iomaps;
23262306a36Sopenharmony_ci	return nr_iomaps;
23362306a36Sopenharmony_cifail:
23462306a36Sopenharmony_ci	kfree(iomaps);
23562306a36Sopenharmony_ci	return -EINVAL;
23662306a36Sopenharmony_ci}
237