The branch main has been updated by arichardson: URL: https://cgit.FreeBSD.org/src/commit/?id=40d59ee35dc106cda88d66e37527975a32596cd7
commit 40d59ee35dc106cda88d66e37527975a32596cd7 Author: Alex Richardson <[email protected]> AuthorDate: 2026-05-07 04:23:27 +0000 Commit: Alex Richardson <[email protected]> CommitDate: 2026-05-07 04:23:28 +0000 p9fs: Fix creating files with restrictive permissions When a file is created via p9fs with restrictive permissions (like 000), the 9P TCREATE request successfully creates and natively opens the file, returning an open, writable file descriptor. Previously, p9fs would attempt a subsequent TOPEN. That TOPEN would fail with EACCES due to the restrictive mode, leaving a 0-byte file and causing operations like 'mv' to abort. We now preserve the writable descriptor returned by TCREATE so that the subsequent VOP_OPEN can use it directly, avoiding the failing TOPEN. Additionally, p9fs_compatible_mode now appropriately isolates the base access intent when matching fids, preventing extended flags from breaking the match. A test case for this behavior has been submitted to pjdfstest: https://github.com/pjd/pjdfstest/pull/87 Resolves: https://github.com/CTSRD-CHERI/cheribsd/issues/2617 Reviewed by: markj MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D56494 --- sys/fs/p9fs/p9fs_subr.c | 14 +++++++++----- sys/fs/p9fs/p9fs_vnops.c | 24 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/sys/fs/p9fs/p9fs_subr.c b/sys/fs/p9fs/p9fs_subr.c index d0f04f6c5e97..f66e7a171029 100644 --- a/sys/fs/p9fs/p9fs_subr.c +++ b/sys/fs/p9fs/p9fs_subr.c @@ -275,16 +275,20 @@ p9fs_compatible_mode(struct p9_fid *fid, int mode) { /* * Return TRUE for an exact match. For OREAD and OWRITE, allow - * existing ORDWR fids to match. Only check the low two bits - * of mode. + * existing ORDWR fids to match. * - * TODO: figure out if this is correct for O_APPEND + * We mask both the requested mode and the existing fid's mode + * with 3 (0b11) to isolate the base access intent (O_RDONLY, + * O_WRONLY, or O_RDWR). This prevents extended open flags like + * O_EXCL or O_APPEND from causing a mismatch when we are merely + * looking for an appropriately privileged open descriptor. */ int fid_mode = fid->mode & 3; - if (fid_mode == mode) + int req_mode = mode & 3; + if (fid_mode == req_mode) return (TRUE); if (fid_mode == P9PROTO_ORDWR) - return (mode == P9PROTO_OREAD || mode == P9PROTO_OWRITE); + return (req_mode == P9PROTO_OREAD || req_mode == P9PROTO_OWRITE); return (FALSE); } diff --git a/sys/fs/p9fs/p9fs_vnops.c b/sys/fs/p9fs/p9fs_vnops.c index ad739a219acb..2519e5cd050a 100644 --- a/sys/fs/p9fs/p9fs_vnops.c +++ b/sys/fs/p9fs/p9fs_vnops.c @@ -419,7 +419,7 @@ out: * the name and perm specified under the parent dir. If this succeeds (an entry * is created for the new file on the server), we create our metadata for this * file (vnode, p9fs node calling vget). Once we are done, we clunk the open - * fid of the parent directory. + * fid of the parent directory if it was not retained. */ static int create_common(struct p9fs_node *dnp, struct componentname *cnp, @@ -473,6 +473,28 @@ create_common(struct p9fs_node *dnp, struct componentname *cnp, dnp, newfid, vpp, cnp->cn_nameptr); if (error != 0) goto out; + + if (ofid != NULL) { + struct p9fs_node *np = P9FS_VTON(*vpp); + ofid->v_opens = 0; + /* + * The 9P file creation request natively opens + * the file as part of the create operation and + * gives us a writable file handle (ofid). + * We retain this open descriptor by adding it + * to the VOFID list of the new vnode. This + * guarantees that a subsequent VOP_OPEN call + * does not need to send a redundant TOPEN + * request. This is particularly important + * because if a file was requested to be created + * with 000 permissions, the host will reject + * subsequent TOPEN requests due to insufficient + * permissions, which would cause an overall + * open() failure. + */ + p9fs_fid_add(np, ofid, VOFID); + ofid = NULL; /* prevent closing handle below */ + } } else { /* Not found return NOENTRY.*/ goto out;
