diff options
Diffstat (limited to 'fs/nfsd/nfs4proc.c')
| -rw-r--r-- | fs/nfsd/nfs4proc.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index ac71d13c69ef..d30bea8d0277 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
| @@ -43,6 +43,8 @@ | |||
| 43 | #include "current_stateid.h" | 43 | #include "current_stateid.h" |
| 44 | #include "netns.h" | 44 | #include "netns.h" |
| 45 | #include "acl.h" | 45 | #include "acl.h" |
| 46 | #include "pnfs.h" | ||
| 47 | #include "trace.h" | ||
| 46 | 48 | ||
| 47 | #ifdef CONFIG_NFSD_V4_SECURITY_LABEL | 49 | #ifdef CONFIG_NFSD_V4_SECURITY_LABEL |
| 48 | #include <linux/security.h> | 50 | #include <linux/security.h> |
| @@ -1178,6 +1180,259 @@ nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 1178 | return status == nfserr_same ? nfs_ok : status; | 1180 | return status == nfserr_same ? nfs_ok : status; |
| 1179 | } | 1181 | } |
| 1180 | 1182 | ||
| 1183 | #ifdef CONFIG_NFSD_PNFS | ||
| 1184 | static const struct nfsd4_layout_ops * | ||
| 1185 | nfsd4_layout_verify(struct svc_export *exp, unsigned int layout_type) | ||
| 1186 | { | ||
| 1187 | if (!exp->ex_layout_type) { | ||
| 1188 | dprintk("%s: export does not support pNFS\n", __func__); | ||
| 1189 | return NULL; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | if (exp->ex_layout_type != layout_type) { | ||
| 1193 | dprintk("%s: layout type %d not supported\n", | ||
| 1194 | __func__, layout_type); | ||
| 1195 | return NULL; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | return nfsd4_layout_ops[layout_type]; | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | static __be32 | ||
| 1202 | nfsd4_getdeviceinfo(struct svc_rqst *rqstp, | ||
| 1203 | struct nfsd4_compound_state *cstate, | ||
| 1204 | struct nfsd4_getdeviceinfo *gdp) | ||
| 1205 | { | ||
| 1206 | const struct nfsd4_layout_ops *ops; | ||
| 1207 | struct nfsd4_deviceid_map *map; | ||
| 1208 | struct svc_export *exp; | ||
| 1209 | __be32 nfserr; | ||
| 1210 | |||
| 1211 | dprintk("%s: layout_type %u dev_id [0x%llx:0x%x] maxcnt %u\n", | ||
| 1212 | __func__, | ||
| 1213 | gdp->gd_layout_type, | ||
| 1214 | gdp->gd_devid.fsid_idx, gdp->gd_devid.generation, | ||
| 1215 | gdp->gd_maxcount); | ||
| 1216 | |||
| 1217 | map = nfsd4_find_devid_map(gdp->gd_devid.fsid_idx); | ||
| 1218 | if (!map) { | ||
| 1219 | dprintk("%s: couldn't find device ID to export mapping!\n", | ||
| 1220 | __func__); | ||
| 1221 | return nfserr_noent; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | exp = rqst_exp_find(rqstp, map->fsid_type, map->fsid); | ||
| 1225 | if (IS_ERR(exp)) { | ||
| 1226 | dprintk("%s: could not find device id\n", __func__); | ||
| 1227 | return nfserr_noent; | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | nfserr = nfserr_layoutunavailable; | ||
| 1231 | ops = nfsd4_layout_verify(exp, gdp->gd_layout_type); | ||
| 1232 | if (!ops) | ||
| 1233 | goto out; | ||
| 1234 | |||
| 1235 | nfserr = nfs_ok; | ||
| 1236 | if (gdp->gd_maxcount != 0) | ||
| 1237 | nfserr = ops->proc_getdeviceinfo(exp->ex_path.mnt->mnt_sb, gdp); | ||
| 1238 | |||
| 1239 | gdp->gd_notify_types &= ops->notify_types; | ||
| 1240 | exp_put(exp); | ||
| 1241 | out: | ||
| 1242 | return nfserr; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | static __be32 | ||
| 1246 | nfsd4_layoutget(struct svc_rqst *rqstp, | ||
| 1247 | struct nfsd4_compound_state *cstate, | ||
| 1248 | struct nfsd4_layoutget *lgp) | ||
| 1249 | { | ||
| 1250 | struct svc_fh *current_fh = &cstate->current_fh; | ||
| 1251 | const struct nfsd4_layout_ops *ops; | ||
| 1252 | struct nfs4_layout_stateid *ls; | ||
| 1253 | __be32 nfserr; | ||
| 1254 | int accmode; | ||
| 1255 | |||
| 1256 | switch (lgp->lg_seg.iomode) { | ||
| 1257 | case IOMODE_READ: | ||
| 1258 | accmode = NFSD_MAY_READ; | ||
| 1259 | break; | ||
| 1260 | case IOMODE_RW: | ||
| 1261 | accmode = NFSD_MAY_READ | NFSD_MAY_WRITE; | ||
| 1262 | break; | ||
| 1263 | default: | ||
| 1264 | dprintk("%s: invalid iomode %d\n", | ||
| 1265 | __func__, lgp->lg_seg.iomode); | ||
| 1266 | nfserr = nfserr_badiomode; | ||
| 1267 | goto out; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | nfserr = fh_verify(rqstp, current_fh, 0, accmode); | ||
| 1271 | if (nfserr) | ||
| 1272 | goto out; | ||
| 1273 | |||
| 1274 | nfserr = nfserr_layoutunavailable; | ||
| 1275 | ops = nfsd4_layout_verify(current_fh->fh_export, lgp->lg_layout_type); | ||
| 1276 | if (!ops) | ||
| 1277 | goto out; | ||
| 1278 | |||
| 1279 | /* | ||
| 1280 | * Verify minlength and range as per RFC5661: | ||
| 1281 | * o If loga_length is less than loga_minlength, | ||
| 1282 | * the metadata server MUST return NFS4ERR_INVAL. | ||
| 1283 | * o If the sum of loga_offset and loga_minlength exceeds | ||
| 1284 | * NFS4_UINT64_MAX, and loga_minlength is not | ||
| 1285 | * NFS4_UINT64_MAX, the error NFS4ERR_INVAL MUST result. | ||
| 1286 | * o If the sum of loga_offset and loga_length exceeds | ||
| 1287 | * NFS4_UINT64_MAX, and loga_length is not NFS4_UINT64_MAX, | ||
| 1288 | * the error NFS4ERR_INVAL MUST result. | ||
| 1289 | */ | ||
| 1290 | nfserr = nfserr_inval; | ||
| 1291 | if (lgp->lg_seg.length < lgp->lg_minlength || | ||
| 1292 | (lgp->lg_minlength != NFS4_MAX_UINT64 && | ||
| 1293 | lgp->lg_minlength > NFS4_MAX_UINT64 - lgp->lg_seg.offset) || | ||
| 1294 | (lgp->lg_seg.length != NFS4_MAX_UINT64 && | ||
| 1295 | lgp->lg_seg.length > NFS4_MAX_UINT64 - lgp->lg_seg.offset)) | ||
| 1296 | goto out; | ||
| 1297 | if (lgp->lg_seg.length == 0) | ||
| 1298 | goto out; | ||
| 1299 | |||
| 1300 | nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lgp->lg_sid, | ||
| 1301 | true, lgp->lg_layout_type, &ls); | ||
| 1302 | if (nfserr) { | ||
| 1303 | trace_layout_get_lookup_fail(&lgp->lg_sid); | ||
| 1304 | goto out; | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | nfserr = nfserr_recallconflict; | ||
| 1308 | if (atomic_read(&ls->ls_stid.sc_file->fi_lo_recalls)) | ||
| 1309 | goto out_put_stid; | ||
| 1310 | |||
| 1311 | nfserr = ops->proc_layoutget(current_fh->fh_dentry->d_inode, | ||
| 1312 | current_fh, lgp); | ||
| 1313 | if (nfserr) | ||
| 1314 | goto out_put_stid; | ||
| 1315 | |||
| 1316 | nfserr = nfsd4_insert_layout(lgp, ls); | ||
| 1317 | |||
| 1318 | out_put_stid: | ||
| 1319 | nfs4_put_stid(&ls->ls_stid); | ||
| 1320 | out: | ||
| 1321 | return nfserr; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | static __be32 | ||
| 1325 | nfsd4_layoutcommit(struct svc_rqst *rqstp, | ||
| 1326 | struct nfsd4_compound_state *cstate, | ||
| 1327 | struct nfsd4_layoutcommit *lcp) | ||
| 1328 | { | ||
| 1329 | const struct nfsd4_layout_seg *seg = &lcp->lc_seg; | ||
| 1330 | struct svc_fh *current_fh = &cstate->current_fh; | ||
| 1331 | const struct nfsd4_layout_ops *ops; | ||
| 1332 | loff_t new_size = lcp->lc_last_wr + 1; | ||
| 1333 | struct inode *inode; | ||
| 1334 | struct nfs4_layout_stateid *ls; | ||
| 1335 | __be32 nfserr; | ||
| 1336 | |||
| 1337 | nfserr = fh_verify(rqstp, current_fh, 0, NFSD_MAY_WRITE); | ||
| 1338 | if (nfserr) | ||
| 1339 | goto out; | ||
| 1340 | |||
| 1341 | nfserr = nfserr_layoutunavailable; | ||
| 1342 | ops = nfsd4_layout_verify(current_fh->fh_export, lcp->lc_layout_type); | ||
| 1343 | if (!ops) | ||
| 1344 | goto out; | ||
| 1345 | inode = current_fh->fh_dentry->d_inode; | ||
| 1346 | |||
| 1347 | nfserr = nfserr_inval; | ||
| 1348 | if (new_size <= seg->offset) { | ||
| 1349 | dprintk("pnfsd: last write before layout segment\n"); | ||
| 1350 | goto out; | ||
| 1351 | } | ||
| 1352 | if (new_size > seg->offset + seg->length) { | ||
| 1353 | dprintk("pnfsd: last write beyond layout segment\n"); | ||
| 1354 | goto out; | ||
| 1355 | } | ||
| 1356 | if (!lcp->lc_newoffset && new_size > i_size_read(inode)) { | ||
| 1357 | dprintk("pnfsd: layoutcommit beyond EOF\n"); | ||
| 1358 | goto out; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lcp->lc_sid, | ||
| 1362 | false, lcp->lc_layout_type, | ||
| 1363 | &ls); | ||
| 1364 | if (nfserr) { | ||
| 1365 | trace_layout_commit_lookup_fail(&lcp->lc_sid); | ||
| 1366 | /* fixup error code as per RFC5661 */ | ||
| 1367 | if (nfserr == nfserr_bad_stateid) | ||
| 1368 | nfserr = nfserr_badlayout; | ||
| 1369 | goto out; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | nfserr = ops->proc_layoutcommit(inode, lcp); | ||
| 1373 | if (nfserr) | ||
| 1374 | goto out_put_stid; | ||
| 1375 | |||
| 1376 | if (new_size > i_size_read(inode)) { | ||
| 1377 | lcp->lc_size_chg = 1; | ||
| 1378 | lcp->lc_newsize = new_size; | ||
| 1379 | } else { | ||
| 1380 | lcp->lc_size_chg = 0; | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | out_put_stid: | ||
| 1384 | nfs4_put_stid(&ls->ls_stid); | ||
| 1385 | out: | ||
| 1386 | return nfserr; | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | static __be32 | ||
| 1390 | nfsd4_layoutreturn(struct svc_rqst *rqstp, | ||
| 1391 | struct nfsd4_compound_state *cstate, | ||
| 1392 | struct nfsd4_layoutreturn *lrp) | ||
| 1393 | { | ||
| 1394 | struct svc_fh *current_fh = &cstate->current_fh; | ||
| 1395 | __be32 nfserr; | ||
| 1396 | |||
| 1397 | nfserr = fh_verify(rqstp, current_fh, 0, NFSD_MAY_NOP); | ||
| 1398 | if (nfserr) | ||
| 1399 | goto out; | ||
| 1400 | |||
| 1401 | nfserr = nfserr_layoutunavailable; | ||
| 1402 | if (!nfsd4_layout_verify(current_fh->fh_export, lrp->lr_layout_type)) | ||
| 1403 | goto out; | ||
| 1404 | |||
| 1405 | switch (lrp->lr_seg.iomode) { | ||
| 1406 | case IOMODE_READ: | ||
| 1407 | case IOMODE_RW: | ||
| 1408 | case IOMODE_ANY: | ||
| 1409 | break; | ||
| 1410 | default: | ||
| 1411 | dprintk("%s: invalid iomode %d\n", __func__, | ||
| 1412 | lrp->lr_seg.iomode); | ||
| 1413 | nfserr = nfserr_inval; | ||
| 1414 | goto out; | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | switch (lrp->lr_return_type) { | ||
| 1418 | case RETURN_FILE: | ||
| 1419 | nfserr = nfsd4_return_file_layouts(rqstp, cstate, lrp); | ||
| 1420 | break; | ||
| 1421 | case RETURN_FSID: | ||
| 1422 | case RETURN_ALL: | ||
| 1423 | nfserr = nfsd4_return_client_layouts(rqstp, cstate, lrp); | ||
| 1424 | break; | ||
| 1425 | default: | ||
| 1426 | dprintk("%s: invalid return_type %d\n", __func__, | ||
| 1427 | lrp->lr_return_type); | ||
| 1428 | nfserr = nfserr_inval; | ||
| 1429 | break; | ||
| 1430 | } | ||
| 1431 | out: | ||
| 1432 | return nfserr; | ||
| 1433 | } | ||
| 1434 | #endif /* CONFIG_NFSD_PNFS */ | ||
| 1435 | |||
| 1181 | /* | 1436 | /* |
| 1182 | * NULL call. | 1437 | * NULL call. |
| 1183 | */ | 1438 | */ |
| @@ -1679,6 +1934,36 @@ static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd | |||
| 1679 | op_encode_channel_attrs_maxsz) * sizeof(__be32); | 1934 | op_encode_channel_attrs_maxsz) * sizeof(__be32); |
| 1680 | } | 1935 | } |
| 1681 | 1936 | ||
| 1937 | #ifdef CONFIG_NFSD_PNFS | ||
| 1938 | /* | ||
| 1939 | * At this stage we don't really know what layout driver will handle the request, | ||
| 1940 | * so we need to define an arbitrary upper bound here. | ||
| 1941 | */ | ||
| 1942 | #define MAX_LAYOUT_SIZE 128 | ||
| 1943 | static inline u32 nfsd4_layoutget_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) | ||
| 1944 | { | ||
| 1945 | return (op_encode_hdr_size + | ||
| 1946 | 1 /* logr_return_on_close */ + | ||
| 1947 | op_encode_stateid_maxsz + | ||
| 1948 | 1 /* nr of layouts */ + | ||
| 1949 | MAX_LAYOUT_SIZE) * sizeof(__be32); | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | static inline u32 nfsd4_layoutcommit_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) | ||
| 1953 | { | ||
| 1954 | return (op_encode_hdr_size + | ||
| 1955 | 1 /* locr_newsize */ + | ||
| 1956 | 2 /* ns_size */) * sizeof(__be32); | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | static inline u32 nfsd4_layoutreturn_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) | ||
| 1960 | { | ||
| 1961 | return (op_encode_hdr_size + | ||
| 1962 | 1 /* lrs_stateid */ + | ||
| 1963 | op_encode_stateid_maxsz) * sizeof(__be32); | ||
| 1964 | } | ||
| 1965 | #endif /* CONFIG_NFSD_PNFS */ | ||
| 1966 | |||
| 1682 | static struct nfsd4_operation nfsd4_ops[] = { | 1967 | static struct nfsd4_operation nfsd4_ops[] = { |
| 1683 | [OP_ACCESS] = { | 1968 | [OP_ACCESS] = { |
| 1684 | .op_func = (nfsd4op_func)nfsd4_access, | 1969 | .op_func = (nfsd4op_func)nfsd4_access, |
| @@ -1966,6 +2251,31 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
| 1966 | .op_get_currentstateid = (stateid_getter)nfsd4_get_freestateid, | 2251 | .op_get_currentstateid = (stateid_getter)nfsd4_get_freestateid, |
| 1967 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, | 2252 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, |
| 1968 | }, | 2253 | }, |
| 2254 | #ifdef CONFIG_NFSD_PNFS | ||
| 2255 | [OP_GETDEVICEINFO] = { | ||
| 2256 | .op_func = (nfsd4op_func)nfsd4_getdeviceinfo, | ||
| 2257 | .op_flags = ALLOWED_WITHOUT_FH, | ||
| 2258 | .op_name = "OP_GETDEVICEINFO", | ||
| 2259 | }, | ||
| 2260 | [OP_LAYOUTGET] = { | ||
| 2261 | .op_func = (nfsd4op_func)nfsd4_layoutget, | ||
| 2262 | .op_flags = OP_MODIFIES_SOMETHING, | ||
| 2263 | .op_name = "OP_LAYOUTGET", | ||
| 2264 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_layoutget_rsize, | ||
| 2265 | }, | ||
| 2266 | [OP_LAYOUTCOMMIT] = { | ||
| 2267 | .op_func = (nfsd4op_func)nfsd4_layoutcommit, | ||
| 2268 | .op_flags = OP_MODIFIES_SOMETHING, | ||
| 2269 | .op_name = "OP_LAYOUTCOMMIT", | ||
| 2270 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_layoutcommit_rsize, | ||
| 2271 | }, | ||
| 2272 | [OP_LAYOUTRETURN] = { | ||
| 2273 | .op_func = (nfsd4op_func)nfsd4_layoutreturn, | ||
| 2274 | .op_flags = OP_MODIFIES_SOMETHING, | ||
| 2275 | .op_name = "OP_LAYOUTRETURN", | ||
| 2276 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_layoutreturn_rsize, | ||
| 2277 | }, | ||
| 2278 | #endif /* CONFIG_NFSD_PNFS */ | ||
| 1969 | 2279 | ||
| 1970 | /* NFSv4.2 operations */ | 2280 | /* NFSv4.2 operations */ |
| 1971 | [OP_ALLOCATE] = { | 2281 | [OP_ALLOCATE] = { |
