1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2014-2016 Christoph Hellwig. 4 */ 5 #include <linux/sunrpc/svc.h> 6 #include <linux/exportfs.h> 7 #include <linux/iomap.h> 8 #include <linux/nfs4.h> 9 10 #include "nfsd.h" 11 #include "blocklayoutxdr.h" 12 #include "vfs.h" 13 14 #define NFSDDBG_FACILITY NFSDDBG_PNFS 15 16 17 __be32 18 nfsd4_block_encode_layoutget(struct xdr_stream *xdr, 19 struct nfsd4_layoutget *lgp) 20 { 21 struct pnfs_block_extent *b = lgp->lg_content; 22 int len = sizeof(__be32) + 5 * sizeof(__be64) + sizeof(__be32); 23 __be32 *p; 24 25 p = xdr_reserve_space(xdr, sizeof(__be32) + len); 26 if (!p) 27 return nfserr_toosmall; 28 29 *p++ = cpu_to_be32(len); 30 *p++ = cpu_to_be32(1); /* we always return a single extent */ 31 32 p = xdr_encode_opaque_fixed(p, &b->vol_id, 33 sizeof(struct nfsd4_deviceid)); 34 p = xdr_encode_hyper(p, b->foff); 35 p = xdr_encode_hyper(p, b->len); 36 p = xdr_encode_hyper(p, b->soff); 37 *p++ = cpu_to_be32(b->es); 38 return 0; 39 } 40 41 static int 42 nfsd4_block_encode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b) 43 { 44 __be32 *p; 45 int len; 46 47 switch (b->type) { 48 case PNFS_BLOCK_VOLUME_SIMPLE: 49 len = 4 + 4 + 8 + 4 + (XDR_QUADLEN(b->simple.sig_len) << 2); 50 p = xdr_reserve_space(xdr, len); 51 if (!p) 52 return -ETOOSMALL; 53 54 *p++ = cpu_to_be32(b->type); 55 *p++ = cpu_to_be32(1); /* single signature */ 56 p = xdr_encode_hyper(p, b->simple.offset); 57 p = xdr_encode_opaque(p, b->simple.sig, b->simple.sig_len); 58 break; 59 case PNFS_BLOCK_VOLUME_SCSI: 60 len = 4 + 4 + 4 + 4 + (XDR_QUADLEN(b->scsi.designator_len) << 2) + 8; 61 p = xdr_reserve_space(xdr, len); 62 if (!p) 63 return -ETOOSMALL; 64 65 *p++ = cpu_to_be32(b->type); 66 *p++ = cpu_to_be32(b->scsi.code_set); 67 *p++ = cpu_to_be32(b->scsi.designator_type); 68 p = xdr_encode_opaque(p, b->scsi.designator, b->scsi.designator_len); 69 p = xdr_encode_hyper(p, b->scsi.pr_key); 70 break; 71 default: 72 return -ENOTSUPP; 73 } 74 75 return len; 76 } 77 78 __be32 79 nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, 80 struct nfsd4_getdeviceinfo *gdp) 81 { 82 struct pnfs_block_deviceaddr *dev = gdp->gd_device; 83 int len = sizeof(__be32), ret, i; 84 __be32 *p; 85 86 p = xdr_reserve_space(xdr, len + sizeof(__be32)); 87 if (!p) 88 return nfserr_resource; 89 90 for (i = 0; i < dev->nr_volumes; i++) { 91 ret = nfsd4_block_encode_volume(xdr, &dev->volumes[i]); 92 if (ret < 0) 93 return nfserrno(ret); 94 len += ret; 95 } 96 97 /* 98 * Fill in the overall length and number of volumes at the beginning 99 * of the layout. 100 */ 101 *p++ = cpu_to_be32(len); 102 *p++ = cpu_to_be32(dev->nr_volumes); 103 return 0; 104 } 105 106 int 107 nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 108 u32 block_size) 109 { 110 struct iomap *iomaps; 111 u32 nr_iomaps, i; 112 113 if (len < sizeof(u32)) { 114 dprintk("%s: extent array too small: %u\n", __func__, len); 115 return -EINVAL; 116 } 117 len -= sizeof(u32); 118 if (len % PNFS_BLOCK_EXTENT_SIZE) { 119 dprintk("%s: extent array invalid: %u\n", __func__, len); 120 return -EINVAL; 121 } 122 123 nr_iomaps = be32_to_cpup(p++); 124 if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) { 125 dprintk("%s: extent array size mismatch: %u/%u\n", 126 __func__, len, nr_iomaps); 127 return -EINVAL; 128 } 129 130 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 131 if (!iomaps) { 132 dprintk("%s: failed to allocate extent array\n", __func__); 133 return -ENOMEM; 134 } 135 136 for (i = 0; i < nr_iomaps; i++) { 137 struct pnfs_block_extent bex; 138 139 memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid)); 140 p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid)); 141 142 p = xdr_decode_hyper(p, &bex.foff); 143 if (bex.foff & (block_size - 1)) { 144 dprintk("%s: unaligned offset 0x%llx\n", 145 __func__, bex.foff); 146 goto fail; 147 } 148 p = xdr_decode_hyper(p, &bex.len); 149 if (bex.len & (block_size - 1)) { 150 dprintk("%s: unaligned length 0x%llx\n", 151 __func__, bex.foff); 152 goto fail; 153 } 154 p = xdr_decode_hyper(p, &bex.soff); 155 if (bex.soff & (block_size - 1)) { 156 dprintk("%s: unaligned disk offset 0x%llx\n", 157 __func__, bex.soff); 158 goto fail; 159 } 160 bex.es = be32_to_cpup(p++); 161 if (bex.es != PNFS_BLOCK_READWRITE_DATA) { 162 dprintk("%s: incorrect extent state %d\n", 163 __func__, bex.es); 164 goto fail; 165 } 166 167 iomaps[i].offset = bex.foff; 168 iomaps[i].length = bex.len; 169 } 170 171 *iomapp = iomaps; 172 return nr_iomaps; 173 fail: 174 kfree(iomaps); 175 return -EINVAL; 176 } 177 178 int 179 nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 180 u32 block_size) 181 { 182 struct iomap *iomaps; 183 u32 nr_iomaps, expected, i; 184 185 if (len < sizeof(u32)) { 186 dprintk("%s: extent array too small: %u\n", __func__, len); 187 return -EINVAL; 188 } 189 190 nr_iomaps = be32_to_cpup(p++); 191 expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE; 192 if (len != expected) { 193 dprintk("%s: extent array size mismatch: %u/%u\n", 194 __func__, len, expected); 195 return -EINVAL; 196 } 197 198 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 199 if (!iomaps) { 200 dprintk("%s: failed to allocate extent array\n", __func__); 201 return -ENOMEM; 202 } 203 204 for (i = 0; i < nr_iomaps; i++) { 205 u64 val; 206 207 p = xdr_decode_hyper(p, &val); 208 if (val & (block_size - 1)) { 209 dprintk("%s: unaligned offset 0x%llx\n", __func__, val); 210 goto fail; 211 } 212 iomaps[i].offset = val; 213 214 p = xdr_decode_hyper(p, &val); 215 if (val & (block_size - 1)) { 216 dprintk("%s: unaligned length 0x%llx\n", __func__, val); 217 goto fail; 218 } 219 iomaps[i].length = val; 220 } 221 222 *iomapp = iomaps; 223 return nr_iomaps; 224 fail: 225 kfree(iomaps); 226 return -EINVAL; 227 } 228