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 const struct nfsd4_layoutget *lgp) 20 { 21 const 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 const 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 /* 87 * See paragraph 5 of RFC 8881 S18.40.3. 88 */ 89 if (!gdp->gd_maxcount) { 90 if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) 91 return nfserr_resource; 92 return nfs_ok; 93 } 94 95 p = xdr_reserve_space(xdr, len + sizeof(__be32)); 96 if (!p) 97 return nfserr_resource; 98 99 for (i = 0; i < dev->nr_volumes; i++) { 100 ret = nfsd4_block_encode_volume(xdr, &dev->volumes[i]); 101 if (ret < 0) 102 return nfserrno(ret); 103 len += ret; 104 } 105 106 /* 107 * Fill in the overall length and number of volumes at the beginning 108 * of the layout. 109 */ 110 *p++ = cpu_to_be32(len); 111 *p++ = cpu_to_be32(dev->nr_volumes); 112 return 0; 113 } 114 115 int 116 nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 117 u32 block_size) 118 { 119 struct iomap *iomaps; 120 u32 nr_iomaps, i; 121 122 if (len < sizeof(u32)) { 123 dprintk("%s: extent array too small: %u\n", __func__, len); 124 return -EINVAL; 125 } 126 len -= sizeof(u32); 127 if (len % PNFS_BLOCK_EXTENT_SIZE) { 128 dprintk("%s: extent array invalid: %u\n", __func__, len); 129 return -EINVAL; 130 } 131 132 nr_iomaps = be32_to_cpup(p++); 133 if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) { 134 dprintk("%s: extent array size mismatch: %u/%u\n", 135 __func__, len, nr_iomaps); 136 return -EINVAL; 137 } 138 139 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 140 if (!iomaps) { 141 dprintk("%s: failed to allocate extent array\n", __func__); 142 return -ENOMEM; 143 } 144 145 for (i = 0; i < nr_iomaps; i++) { 146 struct pnfs_block_extent bex; 147 148 memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid)); 149 p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid)); 150 151 p = xdr_decode_hyper(p, &bex.foff); 152 if (bex.foff & (block_size - 1)) { 153 dprintk("%s: unaligned offset 0x%llx\n", 154 __func__, bex.foff); 155 goto fail; 156 } 157 p = xdr_decode_hyper(p, &bex.len); 158 if (bex.len & (block_size - 1)) { 159 dprintk("%s: unaligned length 0x%llx\n", 160 __func__, bex.foff); 161 goto fail; 162 } 163 p = xdr_decode_hyper(p, &bex.soff); 164 if (bex.soff & (block_size - 1)) { 165 dprintk("%s: unaligned disk offset 0x%llx\n", 166 __func__, bex.soff); 167 goto fail; 168 } 169 bex.es = be32_to_cpup(p++); 170 if (bex.es != PNFS_BLOCK_READWRITE_DATA) { 171 dprintk("%s: incorrect extent state %d\n", 172 __func__, bex.es); 173 goto fail; 174 } 175 176 iomaps[i].offset = bex.foff; 177 iomaps[i].length = bex.len; 178 } 179 180 *iomapp = iomaps; 181 return nr_iomaps; 182 fail: 183 kfree(iomaps); 184 return -EINVAL; 185 } 186 187 int 188 nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 189 u32 block_size) 190 { 191 struct iomap *iomaps; 192 u32 nr_iomaps, expected, i; 193 194 if (len < sizeof(u32)) { 195 dprintk("%s: extent array too small: %u\n", __func__, len); 196 return -EINVAL; 197 } 198 199 nr_iomaps = be32_to_cpup(p++); 200 expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE; 201 if (len != expected) { 202 dprintk("%s: extent array size mismatch: %u/%u\n", 203 __func__, len, expected); 204 return -EINVAL; 205 } 206 207 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 208 if (!iomaps) { 209 dprintk("%s: failed to allocate extent array\n", __func__); 210 return -ENOMEM; 211 } 212 213 for (i = 0; i < nr_iomaps; i++) { 214 u64 val; 215 216 p = xdr_decode_hyper(p, &val); 217 if (val & (block_size - 1)) { 218 dprintk("%s: unaligned offset 0x%llx\n", __func__, val); 219 goto fail; 220 } 221 iomaps[i].offset = val; 222 223 p = xdr_decode_hyper(p, &val); 224 if (val & (block_size - 1)) { 225 dprintk("%s: unaligned length 0x%llx\n", __func__, val); 226 goto fail; 227 } 228 iomaps[i].length = val; 229 } 230 231 *iomapp = iomaps; 232 return nr_iomaps; 233 fail: 234 kfree(iomaps); 235 return -EINVAL; 236 } 237
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.