Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFS avoid expired credential keys for buffered writes

We must avoid buffering a WRITE that is using a credential key (e.g. a GSS
context key) that is about to expire or has expired. We currently will
paint ourselves into a corner by returning success to the applciation
for such a buffered WRITE, only to discover that we do not have permission when
we attempt to flush the WRITE (and potentially associated COMMIT) to disk.

Use the RPC layer credential key timeout and expire routines which use a
a watermark, gss_key_expire_timeo. We test the key in nfs_file_write.

If a WRITE is using a credential with a key that will expire within
watermark seconds, flush the inode in nfs_write_end and send only
NFS_FILE_SYNC WRITEs by adding nfs_ctx_key_to_expire to nfs_need_sync_write.
Note that this results in single page NFS_FILE_SYNC WRITEs.

Signed-off-by: Andy Adamson <andros@netapp.com>
[Trond: removed a pr_warn_ratelimited() for now]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

authored by

Andy Adamson and committed by
Trond Myklebust
dc24826b 4de6caa2

+43 -1
+14 -1
fs/nfs/file.c
··· 411 411 struct page *page, void *fsdata) 412 412 { 413 413 unsigned offset = pos & (PAGE_CACHE_SIZE - 1); 414 + struct nfs_open_context *ctx = nfs_file_open_context(file); 414 415 int status; 415 416 416 417 dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n", ··· 447 446 if (status < 0) 448 447 return status; 449 448 NFS_I(mapping->host)->write_io += copied; 449 + 450 + if (nfs_ctx_key_to_expire(ctx)) { 451 + status = nfs_wb_all(mapping->host); 452 + if (status < 0) 453 + return status; 454 + } 455 + 450 456 return copied; 451 457 } 452 458 ··· 650 642 if (IS_SYNC(inode) || (filp->f_flags & O_DSYNC)) 651 643 return 1; 652 644 ctx = nfs_file_open_context(filp); 653 - if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) 645 + if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags) || 646 + nfs_ctx_key_to_expire(ctx)) 654 647 return 1; 655 648 return 0; 656 649 } ··· 664 655 unsigned long written = 0; 665 656 ssize_t result; 666 657 size_t count = iov_length(iov, nr_segs); 658 + 659 + result = nfs_key_timeout_notify(iocb->ki_filp, inode); 660 + if (result) 661 + return result; 667 662 668 663 if (iocb->ki_filp->f_flags & O_DIRECT) 669 664 return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);
+2
fs/nfs/internal.h
··· 431 431 void nfs_init_cinfo(struct nfs_commit_info *cinfo, 432 432 struct inode *inode, 433 433 struct nfs_direct_req *dreq); 434 + int nfs_key_timeout_notify(struct file *filp, struct inode *inode); 435 + bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx); 434 436 435 437 #ifdef CONFIG_MIGRATION 436 438 extern int nfs_migrate_page(struct address_space *,
+27
fs/nfs/write.c
··· 876 876 } 877 877 878 878 /* 879 + * Avoid buffered writes when a open context credential's key would 880 + * expire soon. 881 + * 882 + * Returns -EACCES if the key will expire within RPC_KEY_EXPIRE_FAIL. 883 + * 884 + * Return 0 and set a credential flag which triggers the inode to flush 885 + * and performs NFS_FILE_SYNC writes if the key will expired within 886 + * RPC_KEY_EXPIRE_TIMEO. 887 + */ 888 + int 889 + nfs_key_timeout_notify(struct file *filp, struct inode *inode) 890 + { 891 + struct nfs_open_context *ctx = nfs_file_open_context(filp); 892 + struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth; 893 + 894 + return rpcauth_key_timeout_notify(auth, ctx->cred); 895 + } 896 + 897 + /* 898 + * Test if the open context credential key is marked to expire soon. 899 + */ 900 + bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx) 901 + { 902 + return rpcauth_cred_key_to_expire(ctx->cred); 903 + } 904 + 905 + /* 879 906 * If the page cache is marked as unsafe or invalid, then we can't rely on 880 907 * the PageUptodate() flag. In this case, we will need to turn off 881 908 * write optimisations that depend on the page contents being correct.