aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMi Jinlong <mijinlong@cn.fujitsu.com>2011-08-28 06:18:56 -0400
committerJ. Bruce Fields <bfields@redhat.com>2011-09-16 10:31:01 -0400
commit58e7b33a58d0cd07c9294d5161553b204c75662d (patch)
tree270c778b51d482789aa9b9b982bfa796c1c34992
parent038c01598e728cda5b2996c4bf883e8485b2fe50 (diff)
nfsd41: try to check reply size before operation
For checking the size of reply before calling a operation, we need try to get maxsize of the operation's reply. v3: using new method as Bruce said, "we could handle operations in two different ways: - For operations that actually change something (write, rename, open, close, ...), do it the way we're doing it now: be very careful to estimate the size of the response before even processing the operation. - For operations that don't change anything (read, getattr, ...) just go ahead and do the operation. If you realize after the fact that the response is too large, then return the error at that point. So we'd add another flag to op_flags: say, OP_MODIFIES_SOMETHING. And for operations with OP_MODIFIES_SOMETHING set, we'd do the first thing. For operations without it set, we'd do the second." Signed-off-by: Mi Jinlong <mijinlong@cn.fujitsu.com> [bfields@redhat.com: crash, don't attempt to handle, undefined op_rsize_bop] Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4proc.c247
-rw-r--r--fs/nfsd/nfs4xdr.c37
-rw-r--r--fs/nfsd/xdr4.h1
3 files changed, 248 insertions, 37 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 460eeb329d81..752a367e0e3d 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -35,6 +35,7 @@
35#include <linux/file.h> 35#include <linux/file.h>
36#include <linux/slab.h> 36#include <linux/slab.h>
37 37
38#include "idmap.h"
38#include "cache.h" 39#include "cache.h"
39#include "xdr4.h" 40#include "xdr4.h"
40#include "vfs.h" 41#include "vfs.h"
@@ -1003,6 +1004,8 @@ static inline void nfsd4_increment_op_stats(u32 opnum)
1003 1004
1004typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, 1005typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
1005 void *); 1006 void *);
1007typedef u32(*nfsd4op_rsize)(struct svc_rqst *, struct nfsd4_op *op);
1008
1006enum nfsd4_op_flags { 1009enum nfsd4_op_flags {
1007 ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ 1010 ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */
1008 ALLOWED_ON_ABSENT_FS = 1 << 1, /* ops processed on absent fs */ 1011 ALLOWED_ON_ABSENT_FS = 1 << 1, /* ops processed on absent fs */
@@ -1010,6 +1013,7 @@ enum nfsd4_op_flags {
1010 /* For rfc 5661 section 2.6.3.1.1: */ 1013 /* For rfc 5661 section 2.6.3.1.1: */
1011 OP_HANDLES_WRONGSEC = 1 << 3, 1014 OP_HANDLES_WRONGSEC = 1 << 3,
1012 OP_IS_PUTFH_LIKE = 1 << 4, 1015 OP_IS_PUTFH_LIKE = 1 << 4,
1016 OP_MODIFIES_SOMETHING = 1 << 5, /* op is non-idempotent */
1013}; 1017};
1014 1018
1015struct nfsd4_operation { 1019struct nfsd4_operation {
@@ -1025,6 +1029,8 @@ struct nfsd4_operation {
1025 * the v4.0 case). 1029 * the v4.0 case).
1026 */ 1030 */
1027 bool op_cacheresult; 1031 bool op_cacheresult;
1032 /* Try to get response size before operation */
1033 nfsd4op_rsize op_rsize_bop;
1028}; 1034};
1029 1035
1030static struct nfsd4_operation nfsd4_ops[]; 1036static struct nfsd4_operation nfsd4_ops[];
@@ -1119,6 +1125,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
1119 struct nfsd4_operation *opdesc; 1125 struct nfsd4_operation *opdesc;
1120 struct nfsd4_compound_state *cstate = &resp->cstate; 1126 struct nfsd4_compound_state *cstate = &resp->cstate;
1121 int slack_bytes; 1127 int slack_bytes;
1128 u32 plen = 0;
1122 __be32 status; 1129 __be32 status;
1123 1130
1124 resp->xbuf = &rqstp->rq_res; 1131 resp->xbuf = &rqstp->rq_res;
@@ -1197,6 +1204,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
1197 goto encode_op; 1204 goto encode_op;
1198 } 1205 }
1199 1206
1207 /* If op is non-idempotent */
1208 if (opdesc->op_flags & OP_MODIFIES_SOMETHING) {
1209 plen = opdesc->op_rsize_bop(rqstp, op);
1210 op->status = nfsd4_check_resp_size(resp, plen);
1211 }
1212
1213 if (op->status)
1214 goto encode_op;
1215
1200 if (opdesc->op_func) 1216 if (opdesc->op_func)
1201 op->status = opdesc->op_func(rqstp, cstate, &op->u); 1217 op->status = opdesc->op_func(rqstp, cstate, &op->u);
1202 else 1218 else
@@ -1247,6 +1263,144 @@ out:
1247 return status; 1263 return status;
1248} 1264}
1249 1265
1266#define op_encode_hdr_size (2)
1267#define op_encode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE))
1268#define op_encode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE))
1269#define op_encode_change_info_maxsz (5)
1270#define nfs4_fattr_bitmap_maxsz (4)
1271
1272#define op_encode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
1273#define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz)
1274
1275#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
1276
1277#define op_encode_ace_maxsz (3 + nfs4_owner_maxsz)
1278#define op_encode_delegation_maxsz (1 + op_encode_stateid_maxsz + 1 + \
1279 op_encode_ace_maxsz)
1280
1281#define op_encode_channel_attrs_maxsz (6 + 1 + 1)
1282
1283static inline u32 nfsd4_only_status_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1284{
1285 return (op_encode_hdr_size) * sizeof(__be32);
1286}
1287
1288static inline u32 nfsd4_status_stateid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1289{
1290 return (op_encode_hdr_size + op_encode_stateid_maxsz)* sizeof(__be32);
1291}
1292
1293static inline u32 nfsd4_commit_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1294{
1295 return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32);
1296}
1297
1298static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1299{
1300 return (op_encode_hdr_size + op_encode_change_info_maxsz
1301 + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
1302}
1303
1304static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1305{
1306 return (op_encode_hdr_size + op_encode_change_info_maxsz)
1307 * sizeof(__be32);
1308}
1309
1310static inline u32 nfsd4_lock_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1311{
1312 return (op_encode_hdr_size + op_encode_lock_denied_maxsz)
1313 * sizeof(__be32);
1314}
1315
1316static inline u32 nfsd4_open_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1317{
1318 return (op_encode_hdr_size + op_encode_stateid_maxsz
1319 + op_encode_change_info_maxsz + 1
1320 + nfs4_fattr_bitmap_maxsz
1321 + op_encode_delegation_maxsz) * sizeof(__be32);
1322}
1323
1324static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1325{
1326 u32 maxcount = 0, rlen = 0;
1327
1328 maxcount = svc_max_payload(rqstp);
1329 rlen = op->u.read.rd_length;
1330
1331 if (rlen > maxcount)
1332 rlen = maxcount;
1333
1334 return (op_encode_hdr_size + 2) * sizeof(__be32) + rlen;
1335}
1336
1337static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1338{
1339 u32 rlen = op->u.readdir.rd_maxcount;
1340
1341 if (rlen > PAGE_SIZE)
1342 rlen = PAGE_SIZE;
1343
1344 return (op_encode_hdr_size + op_encode_verifier_maxsz)
1345 * sizeof(__be32) + rlen;
1346}
1347
1348static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1349{
1350 return (op_encode_hdr_size + op_encode_change_info_maxsz)
1351 * sizeof(__be32);
1352}
1353
1354static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1355{
1356 return (op_encode_hdr_size + op_encode_change_info_maxsz
1357 + op_encode_change_info_maxsz) * sizeof(__be32);
1358}
1359
1360static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1361{
1362 return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
1363}
1364
1365static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1366{
1367 return (op_encode_hdr_size + 2 + 1024) * sizeof(__be32);
1368}
1369
1370static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1371{
1372 return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32);
1373}
1374
1375static inline u32 nfsd4_exchange_id_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1376{
1377 return (op_encode_hdr_size + 2 + 1 + /* eir_clientid, eir_sequenceid */\
1378 1 + 1 + 0 + /* eir_flags, spr_how, SP4_NONE (for now) */\
1379 2 + /*eir_server_owner.so_minor_id */\
1380 /* eir_server_owner.so_major_id<> */\
1381 XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\
1382 /* eir_server_scope<> */\
1383 XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\
1384 1 + /* eir_server_impl_id array length */\
1385 0 /* ignored eir_server_impl_id contents */) * sizeof(__be32);
1386}
1387
1388static inline u32 nfsd4_bind_conn_to_session_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1389{
1390 return (op_encode_hdr_size + \
1391 XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* bctsr_sessid */\
1392 2 /* bctsr_dir, use_conn_in_rdma_mode */) * sizeof(__be32);
1393}
1394
1395static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1396{
1397 return (op_encode_hdr_size + \
1398 XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* sessionid */\
1399 2 + /* csr_sequence, csr_flags */\
1400 op_encode_channel_attrs_maxsz + \
1401 op_encode_channel_attrs_maxsz) * sizeof(__be32);
1402}
1403
1250static struct nfsd4_operation nfsd4_ops[] = { 1404static struct nfsd4_operation nfsd4_ops[] = {
1251 [OP_ACCESS] = { 1405 [OP_ACCESS] = {
1252 .op_func = (nfsd4op_func)nfsd4_access, 1406 .op_func = (nfsd4op_func)nfsd4_access,
@@ -1254,20 +1408,28 @@ static struct nfsd4_operation nfsd4_ops[] = {
1254 }, 1408 },
1255 [OP_CLOSE] = { 1409 [OP_CLOSE] = {
1256 .op_func = (nfsd4op_func)nfsd4_close, 1410 .op_func = (nfsd4op_func)nfsd4_close,
1411 .op_flags = OP_MODIFIES_SOMETHING,
1257 .op_name = "OP_CLOSE", 1412 .op_name = "OP_CLOSE",
1413 .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
1258 }, 1414 },
1259 [OP_COMMIT] = { 1415 [OP_COMMIT] = {
1260 .op_func = (nfsd4op_func)nfsd4_commit, 1416 .op_func = (nfsd4op_func)nfsd4_commit,
1417 .op_flags = OP_MODIFIES_SOMETHING,
1261 .op_name = "OP_COMMIT", 1418 .op_name = "OP_COMMIT",
1419 .op_rsize_bop = (nfsd4op_rsize)nfsd4_commit_rsize,
1262 }, 1420 },
1263 [OP_CREATE] = { 1421 [OP_CREATE] = {
1264 .op_func = (nfsd4op_func)nfsd4_create, 1422 .op_func = (nfsd4op_func)nfsd4_create,
1423 .op_flags = OP_MODIFIES_SOMETHING,
1265 .op_name = "OP_CREATE", 1424 .op_name = "OP_CREATE",
1266 .op_cacheresult = true, 1425 .op_cacheresult = true,
1426 .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize,
1267 }, 1427 },
1268 [OP_DELEGRETURN] = { 1428 [OP_DELEGRETURN] = {
1269 .op_func = (nfsd4op_func)nfsd4_delegreturn, 1429 .op_func = (nfsd4op_func)nfsd4_delegreturn,
1430 .op_flags = OP_MODIFIES_SOMETHING,
1270 .op_name = "OP_DELEGRETURN", 1431 .op_name = "OP_DELEGRETURN",
1432 .op_rsize_bop = nfsd4_only_status_rsize,
1271 }, 1433 },
1272 [OP_GETATTR] = { 1434 [OP_GETATTR] = {
1273 .op_func = (nfsd4op_func)nfsd4_getattr, 1435 .op_func = (nfsd4op_func)nfsd4_getattr,
@@ -1280,12 +1442,16 @@ static struct nfsd4_operation nfsd4_ops[] = {
1280 }, 1442 },
1281 [OP_LINK] = { 1443 [OP_LINK] = {
1282 .op_func = (nfsd4op_func)nfsd4_link, 1444 .op_func = (nfsd4op_func)nfsd4_link,
1445 .op_flags = ALLOWED_ON_ABSENT_FS | OP_MODIFIES_SOMETHING,
1283 .op_name = "OP_LINK", 1446 .op_name = "OP_LINK",
1284 .op_cacheresult = true, 1447 .op_cacheresult = true,
1448 .op_rsize_bop = (nfsd4op_rsize)nfsd4_link_rsize,
1285 }, 1449 },
1286 [OP_LOCK] = { 1450 [OP_LOCK] = {
1287 .op_func = (nfsd4op_func)nfsd4_lock, 1451 .op_func = (nfsd4op_func)nfsd4_lock,
1452 .op_flags = OP_MODIFIES_SOMETHING,
1288 .op_name = "OP_LOCK", 1453 .op_name = "OP_LOCK",
1454 .op_rsize_bop = (nfsd4op_rsize)nfsd4_lock_rsize,
1289 }, 1455 },
1290 [OP_LOCKT] = { 1456 [OP_LOCKT] = {
1291 .op_func = (nfsd4op_func)nfsd4_lockt, 1457 .op_func = (nfsd4op_func)nfsd4_lockt,
@@ -1293,7 +1459,9 @@ static struct nfsd4_operation nfsd4_ops[] = {
1293 }, 1459 },
1294 [OP_LOCKU] = { 1460 [OP_LOCKU] = {
1295 .op_func = (nfsd4op_func)nfsd4_locku, 1461 .op_func = (nfsd4op_func)nfsd4_locku,
1462 .op_flags = OP_MODIFIES_SOMETHING,
1296 .op_name = "OP_LOCKU", 1463 .op_name = "OP_LOCKU",
1464 .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
1297 }, 1465 },
1298 [OP_LOOKUP] = { 1466 [OP_LOOKUP] = {
1299 .op_func = (nfsd4op_func)nfsd4_lookup, 1467 .op_func = (nfsd4op_func)nfsd4_lookup,
@@ -1311,42 +1479,54 @@ static struct nfsd4_operation nfsd4_ops[] = {
1311 }, 1479 },
1312 [OP_OPEN] = { 1480 [OP_OPEN] = {
1313 .op_func = (nfsd4op_func)nfsd4_open, 1481 .op_func = (nfsd4op_func)nfsd4_open,
1314 .op_flags = OP_HANDLES_WRONGSEC, 1482 .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
1315 .op_name = "OP_OPEN", 1483 .op_name = "OP_OPEN",
1484 .op_rsize_bop = (nfsd4op_rsize)nfsd4_open_rsize,
1316 }, 1485 },
1317 [OP_OPEN_CONFIRM] = { 1486 [OP_OPEN_CONFIRM] = {
1318 .op_func = (nfsd4op_func)nfsd4_open_confirm, 1487 .op_func = (nfsd4op_func)nfsd4_open_confirm,
1488 .op_flags = OP_MODIFIES_SOMETHING,
1319 .op_name = "OP_OPEN_CONFIRM", 1489 .op_name = "OP_OPEN_CONFIRM",
1490 .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
1320 }, 1491 },
1321 [OP_OPEN_DOWNGRADE] = { 1492 [OP_OPEN_DOWNGRADE] = {
1322 .op_func = (nfsd4op_func)nfsd4_open_downgrade, 1493 .op_func = (nfsd4op_func)nfsd4_open_downgrade,
1494 .op_flags = OP_MODIFIES_SOMETHING,
1323 .op_name = "OP_OPEN_DOWNGRADE", 1495 .op_name = "OP_OPEN_DOWNGRADE",
1496 .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
1324 }, 1497 },
1325 [OP_PUTFH] = { 1498 [OP_PUTFH] = {
1326 .op_func = (nfsd4op_func)nfsd4_putfh, 1499 .op_func = (nfsd4op_func)nfsd4_putfh,
1327 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 1500 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1328 | OP_IS_PUTFH_LIKE, 1501 | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
1329 .op_name = "OP_PUTFH", 1502 .op_name = "OP_PUTFH",
1503 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1330 }, 1504 },
1331 [OP_PUTPUBFH] = { 1505 [OP_PUTPUBFH] = {
1332 .op_func = (nfsd4op_func)nfsd4_putrootfh, 1506 .op_func = (nfsd4op_func)nfsd4_putrootfh,
1333 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 1507 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1334 | OP_IS_PUTFH_LIKE, 1508 | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
1335 .op_name = "OP_PUTPUBFH", 1509 .op_name = "OP_PUTPUBFH",
1510 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1336 }, 1511 },
1337 [OP_PUTROOTFH] = { 1512 [OP_PUTROOTFH] = {
1338 .op_func = (nfsd4op_func)nfsd4_putrootfh, 1513 .op_func = (nfsd4op_func)nfsd4_putrootfh,
1339 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 1514 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1340 | OP_IS_PUTFH_LIKE, 1515 | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
1341 .op_name = "OP_PUTROOTFH", 1516 .op_name = "OP_PUTROOTFH",
1517 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1342 }, 1518 },
1343 [OP_READ] = { 1519 [OP_READ] = {
1344 .op_func = (nfsd4op_func)nfsd4_read, 1520 .op_func = (nfsd4op_func)nfsd4_read,
1521 .op_flags = OP_MODIFIES_SOMETHING,
1345 .op_name = "OP_READ", 1522 .op_name = "OP_READ",
1523 .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize,
1346 }, 1524 },
1347 [OP_READDIR] = { 1525 [OP_READDIR] = {
1348 .op_func = (nfsd4op_func)nfsd4_readdir, 1526 .op_func = (nfsd4op_func)nfsd4_readdir,
1527 .op_flags = OP_MODIFIES_SOMETHING,
1349 .op_name = "OP_READDIR", 1528 .op_name = "OP_READDIR",
1529 .op_rsize_bop = (nfsd4op_rsize)nfsd4_readdir_rsize,
1350 }, 1530 },
1351 [OP_READLINK] = { 1531 [OP_READLINK] = {
1352 .op_func = (nfsd4op_func)nfsd4_readlink, 1532 .op_func = (nfsd4op_func)nfsd4_readlink,
@@ -1354,29 +1534,38 @@ static struct nfsd4_operation nfsd4_ops[] = {
1354 }, 1534 },
1355 [OP_REMOVE] = { 1535 [OP_REMOVE] = {
1356 .op_func = (nfsd4op_func)nfsd4_remove, 1536 .op_func = (nfsd4op_func)nfsd4_remove,
1537 .op_flags = OP_MODIFIES_SOMETHING,
1357 .op_name = "OP_REMOVE", 1538 .op_name = "OP_REMOVE",
1358 .op_cacheresult = true, 1539 .op_cacheresult = true,
1540 .op_rsize_bop = (nfsd4op_rsize)nfsd4_remove_rsize,
1359 }, 1541 },
1360 [OP_RENAME] = { 1542 [OP_RENAME] = {
1361 .op_name = "OP_RENAME",
1362 .op_func = (nfsd4op_func)nfsd4_rename, 1543 .op_func = (nfsd4op_func)nfsd4_rename,
1544 .op_flags = OP_MODIFIES_SOMETHING,
1545 .op_name = "OP_RENAME",
1363 .op_cacheresult = true, 1546 .op_cacheresult = true,
1547 .op_rsize_bop = (nfsd4op_rsize)nfsd4_rename_rsize,
1364 }, 1548 },
1365 [OP_RENEW] = { 1549 [OP_RENEW] = {
1366 .op_func = (nfsd4op_func)nfsd4_renew, 1550 .op_func = (nfsd4op_func)nfsd4_renew,
1367 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, 1551 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1552 | OP_MODIFIES_SOMETHING,
1368 .op_name = "OP_RENEW", 1553 .op_name = "OP_RENEW",
1554 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1555
1369 }, 1556 },
1370 [OP_RESTOREFH] = { 1557 [OP_RESTOREFH] = {
1371 .op_func = (nfsd4op_func)nfsd4_restorefh, 1558 .op_func = (nfsd4op_func)nfsd4_restorefh,
1372 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 1559 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1373 | OP_IS_PUTFH_LIKE, 1560 | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
1374 .op_name = "OP_RESTOREFH", 1561 .op_name = "OP_RESTOREFH",
1562 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1375 }, 1563 },
1376 [OP_SAVEFH] = { 1564 [OP_SAVEFH] = {
1377 .op_func = (nfsd4op_func)nfsd4_savefh, 1565 .op_func = (nfsd4op_func)nfsd4_savefh,
1378 .op_flags = OP_HANDLES_WRONGSEC, 1566 .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
1379 .op_name = "OP_SAVEFH", 1567 .op_name = "OP_SAVEFH",
1568 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1380 }, 1569 },
1381 [OP_SECINFO] = { 1570 [OP_SECINFO] = {
1382 .op_func = (nfsd4op_func)nfsd4_secinfo, 1571 .op_func = (nfsd4op_func)nfsd4_secinfo,
@@ -1386,19 +1575,25 @@ static struct nfsd4_operation nfsd4_ops[] = {
1386 [OP_SETATTR] = { 1575 [OP_SETATTR] = {
1387 .op_func = (nfsd4op_func)nfsd4_setattr, 1576 .op_func = (nfsd4op_func)nfsd4_setattr,
1388 .op_name = "OP_SETATTR", 1577 .op_name = "OP_SETATTR",
1578 .op_flags = OP_MODIFIES_SOMETHING,
1389 .op_cacheresult = true, 1579 .op_cacheresult = true,
1580 .op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize,
1390 }, 1581 },
1391 [OP_SETCLIENTID] = { 1582 [OP_SETCLIENTID] = {
1392 .op_func = (nfsd4op_func)nfsd4_setclientid, 1583 .op_func = (nfsd4op_func)nfsd4_setclientid,
1393 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, 1584 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1585 | OP_MODIFIES_SOMETHING,
1394 .op_name = "OP_SETCLIENTID", 1586 .op_name = "OP_SETCLIENTID",
1395 .op_cacheresult = true, 1587 .op_cacheresult = true,
1588 .op_rsize_bop = (nfsd4op_rsize)nfsd4_setclientid_rsize,
1396 }, 1589 },
1397 [OP_SETCLIENTID_CONFIRM] = { 1590 [OP_SETCLIENTID_CONFIRM] = {
1398 .op_func = (nfsd4op_func)nfsd4_setclientid_confirm, 1591 .op_func = (nfsd4op_func)nfsd4_setclientid_confirm,
1399 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, 1592 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1593 | OP_MODIFIES_SOMETHING,
1400 .op_name = "OP_SETCLIENTID_CONFIRM", 1594 .op_name = "OP_SETCLIENTID_CONFIRM",
1401 .op_cacheresult = true, 1595 .op_cacheresult = true,
1596 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1402 }, 1597 },
1403 [OP_VERIFY] = { 1598 [OP_VERIFY] = {
1404 .op_func = (nfsd4op_func)nfsd4_verify, 1599 .op_func = (nfsd4op_func)nfsd4_verify,
@@ -1406,35 +1601,47 @@ static struct nfsd4_operation nfsd4_ops[] = {
1406 }, 1601 },
1407 [OP_WRITE] = { 1602 [OP_WRITE] = {
1408 .op_func = (nfsd4op_func)nfsd4_write, 1603 .op_func = (nfsd4op_func)nfsd4_write,
1604 .op_flags = OP_MODIFIES_SOMETHING,
1409 .op_name = "OP_WRITE", 1605 .op_name = "OP_WRITE",
1410 .op_cacheresult = true, 1606 .op_cacheresult = true,
1607 .op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize,
1411 }, 1608 },
1412 [OP_RELEASE_LOCKOWNER] = { 1609 [OP_RELEASE_LOCKOWNER] = {
1413 .op_func = (nfsd4op_func)nfsd4_release_lockowner, 1610 .op_func = (nfsd4op_func)nfsd4_release_lockowner,
1414 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, 1611 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
1612 | OP_MODIFIES_SOMETHING,
1415 .op_name = "OP_RELEASE_LOCKOWNER", 1613 .op_name = "OP_RELEASE_LOCKOWNER",
1614 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1416 }, 1615 },
1417 1616
1418 /* NFSv4.1 operations */ 1617 /* NFSv4.1 operations */
1419 [OP_EXCHANGE_ID] = { 1618 [OP_EXCHANGE_ID] = {
1420 .op_func = (nfsd4op_func)nfsd4_exchange_id, 1619 .op_func = (nfsd4op_func)nfsd4_exchange_id,
1421 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, 1620 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
1621 | OP_MODIFIES_SOMETHING,
1422 .op_name = "OP_EXCHANGE_ID", 1622 .op_name = "OP_EXCHANGE_ID",
1623 .op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize,
1423 }, 1624 },
1424 [OP_BIND_CONN_TO_SESSION] = { 1625 [OP_BIND_CONN_TO_SESSION] = {
1425 .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, 1626 .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session,
1426 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, 1627 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
1628 | OP_MODIFIES_SOMETHING,
1427 .op_name = "OP_BIND_CONN_TO_SESSION", 1629 .op_name = "OP_BIND_CONN_TO_SESSION",
1630 .op_rsize_bop = (nfsd4op_rsize)nfsd4_bind_conn_to_session_rsize,
1428 }, 1631 },
1429 [OP_CREATE_SESSION] = { 1632 [OP_CREATE_SESSION] = {
1430 .op_func = (nfsd4op_func)nfsd4_create_session, 1633 .op_func = (nfsd4op_func)nfsd4_create_session,
1431 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, 1634 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
1635 | OP_MODIFIES_SOMETHING,
1432 .op_name = "OP_CREATE_SESSION", 1636 .op_name = "OP_CREATE_SESSION",
1637 .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_session_rsize,
1433 }, 1638 },
1434 [OP_DESTROY_SESSION] = { 1639 [OP_DESTROY_SESSION] = {
1435 .op_func = (nfsd4op_func)nfsd4_destroy_session, 1640 .op_func = (nfsd4op_func)nfsd4_destroy_session,
1436 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, 1641 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
1642 | OP_MODIFIES_SOMETHING,
1437 .op_name = "OP_DESTROY_SESSION", 1643 .op_name = "OP_DESTROY_SESSION",
1644 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1438 }, 1645 },
1439 [OP_SEQUENCE] = { 1646 [OP_SEQUENCE] = {
1440 .op_func = (nfsd4op_func)nfsd4_sequence, 1647 .op_func = (nfsd4op_func)nfsd4_sequence,
@@ -1443,13 +1650,16 @@ static struct nfsd4_operation nfsd4_ops[] = {
1443 }, 1650 },
1444 [OP_DESTROY_CLIENTID] = { 1651 [OP_DESTROY_CLIENTID] = {
1445 .op_func = NULL, 1652 .op_func = NULL,
1446 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, 1653 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
1654 | OP_MODIFIES_SOMETHING,
1447 .op_name = "OP_DESTROY_CLIENTID", 1655 .op_name = "OP_DESTROY_CLIENTID",
1656 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1448 }, 1657 },
1449 [OP_RECLAIM_COMPLETE] = { 1658 [OP_RECLAIM_COMPLETE] = {
1450 .op_func = (nfsd4op_func)nfsd4_reclaim_complete, 1659 .op_func = (nfsd4op_func)nfsd4_reclaim_complete,
1451 .op_flags = ALLOWED_WITHOUT_FH, 1660 .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING,
1452 .op_name = "OP_RECLAIM_COMPLETE", 1661 .op_name = "OP_RECLAIM_COMPLETE",
1662 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1453 }, 1663 },
1454 [OP_SECINFO_NO_NAME] = { 1664 [OP_SECINFO_NO_NAME] = {
1455 .op_func = (nfsd4op_func)nfsd4_secinfo_no_name, 1665 .op_func = (nfsd4op_func)nfsd4_secinfo_no_name,
@@ -1463,8 +1673,9 @@ static struct nfsd4_operation nfsd4_ops[] = {
1463 }, 1673 },
1464 [OP_FREE_STATEID] = { 1674 [OP_FREE_STATEID] = {
1465 .op_func = (nfsd4op_func)nfsd4_free_stateid, 1675 .op_func = (nfsd4op_func)nfsd4_free_stateid,
1466 .op_flags = ALLOWED_WITHOUT_FH, 1676 .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING,
1467 .op_name = "OP_FREE_STATEID", 1677 .op_name = "OP_FREE_STATEID",
1678 .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
1468 }, 1679 },
1469}; 1680};
1470 1681
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5252d6681960..f4116cf16595 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3387,34 +3387,29 @@ static nfsd4_enc nfsd4_enc_ops[] = {
3387 3387
3388/* 3388/*
3389 * Calculate the total amount of memory that the compound response has taken 3389 * Calculate the total amount of memory that the compound response has taken
3390 * after encoding the current operation. 3390 * after encoding the current operation with pad.
3391 * 3391 *
3392 * pad: add on 8 bytes for the next operation's op_code and status so that 3392 * pad: if operation is non-idempotent, pad was calculate by op_rsize_bop()
3393 * there is room to cache a failure on the next operation. 3393 * which was specified at nfsd4_operation, else pad is zero.
3394 * 3394 *
3395 * Compare this length to the session se_fmaxresp_cached. 3395 * Compare this length to the session se_fmaxresp_sz and se_fmaxresp_cached.
3396 * 3396 *
3397 * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so 3397 * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
3398 * will be at least a page and will therefore hold the xdr_buf head. 3398 * will be at least a page and will therefore hold the xdr_buf head.
3399 */ 3399 */
3400static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp) 3400int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
3401{ 3401{
3402 int status = 0;
3403 struct xdr_buf *xb = &resp->rqstp->rq_res; 3402 struct xdr_buf *xb = &resp->rqstp->rq_res;
3404 struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
3405 struct nfsd4_session *session = NULL; 3403 struct nfsd4_session *session = NULL;
3406 struct nfsd4_slot *slot = resp->cstate.slot; 3404 struct nfsd4_slot *slot = resp->cstate.slot;
3407 u32 length, tlen = 0, pad = 8; 3405 u32 length, tlen = 0;
3408 3406
3409 if (!nfsd4_has_session(&resp->cstate)) 3407 if (!nfsd4_has_session(&resp->cstate))
3410 return status; 3408 return 0;
3411 3409
3412 session = resp->cstate.session; 3410 session = resp->cstate.session;
3413 if (session == NULL || slot->sl_cachethis == 0) 3411 if (session == NULL)
3414 return status; 3412 return 0;
3415
3416 if (resp->opcnt >= args->opcnt)
3417 pad = 0; /* this is the last operation */
3418 3413
3419 if (xb->page_len == 0) { 3414 if (xb->page_len == 0) {
3420 length = (char *)resp->p - (char *)xb->head[0].iov_base + pad; 3415 length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
@@ -3427,10 +3422,14 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
3427 dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__, 3422 dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
3428 length, xb->page_len, tlen, pad); 3423 length, xb->page_len, tlen, pad);
3429 3424
3430 if (length <= session->se_fchannel.maxresp_cached) 3425 if (length > session->se_fchannel.maxresp_sz)
3431 return status; 3426 return nfserr_rep_too_big;
3432 else 3427
3428 if (slot->sl_cachethis == 1 &&
3429 length > session->se_fchannel.maxresp_cached)
3433 return nfserr_rep_too_big_to_cache; 3430 return nfserr_rep_too_big_to_cache;
3431
3432 return 0;
3434} 3433}
3435 3434
3436void 3435void
@@ -3450,8 +3449,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
3450 !nfsd4_enc_ops[op->opnum]); 3449 !nfsd4_enc_ops[op->opnum]);
3451 op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u); 3450 op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
3452 /* nfsd4_check_drc_limit guarantees enough room for error status */ 3451 /* nfsd4_check_drc_limit guarantees enough room for error status */
3453 if (!op->status && nfsd4_check_drc_limit(resp)) 3452 if (!op->status)
3454 op->status = nfserr_rep_too_big_to_cache; 3453 op->status = nfsd4_check_resp_size(resp, 0);
3455status: 3454status:
3456 /* 3455 /*
3457 * Note: We write the status directly, instead of using WRITE32(), 3456 * Note: We write the status directly, instead of using WRITE32(),
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index f95a72482064..a767b57b8208 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -524,6 +524,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *,
524 struct nfsd4_compoundargs *); 524 struct nfsd4_compoundargs *);
525int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *, 525int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
526 struct nfsd4_compoundres *); 526 struct nfsd4_compoundres *);
527int nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
527void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *); 528void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
528void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op); 529void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op);
529__be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, 530__be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,