- From f9636e8f43cbc064a059d68fa3f1be5611b578aa Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Thu, 13 Nov 2025 01:54:06 +0100
- Subject: [PATCH] daemon,nfs41_build_features.h,sys,tests: Allow atomically
- adding ACLs at file/dir creation time
- Allow atomically adding ACLs at file/dir creation time, as SMB/CIFS
- is able to do.
- Note that this requires that the NFS server supports the |FATTR0_WORD0_ACL|
- attribute at file/dir creation time.
- Reported-by: Aurelien Couderc <aurelien.couderc2002@gmail.com>
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/acl.c | 3 +-
- daemon/open.c | 90 ++++++++++
- daemon/upcall.h | 4 +
- nfs41_build_features.h | 6 +
- sys/nfs41sys_driver.h | 4 +
- sys/nfs41sys_openclose.c | 32 ++++
- .../atomiccreatefilewithacl.ps1 | 159 ++++++++++++++++++
- 7 files changed, 297 insertions(+), 1 deletion(-)
- create mode 100644 tests/atomiccreatefilewithacl/atomiccreatefilewithacl.ps1
- diff --git a/daemon/acl.c b/daemon/acl.c
- index 8675db2..8fece12 100644
- --- a/daemon/acl.c
- +++ b/daemon/acl.c
- @@ -1219,7 +1219,8 @@ out:
- return status;
- }
- -static int map_dacl_2_nfs4acl(PACL acl, PSID sid, PSID gsid, nfsacl41 *nfs4_acl,
- +/* FIXME: Move this into aclutils.c */
- +int map_dacl_2_nfs4acl(PACL acl, PSID sid, PSID gsid, nfsacl41 *nfs4_acl,
- int file_type, bool named_attr_support, char *domain)
- {
- int status;
- diff --git a/daemon/open.c b/daemon/open.c
- index 5532e3c..93f8b3a 100644
- --- a/daemon/open.c
- +++ b/daemon/open.c
- @@ -326,6 +326,9 @@ static int parse_open(
- nfs41_upcall *upcall)
- {
- int status;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + const void *sec_desc_ptr;
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- open_upcall_args *args = &upcall->args.open;
- status = get_name(&buffer, &length, &args->path);
- @@ -344,6 +347,14 @@ static int parse_open(
- if (status) goto out;
- status = safe_read(&buffer, &length, &args->create_opts, sizeof(ULONG));
- if (status) goto out;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + status = safe_read(&buffer, &length, &args->sec_desc_len, sizeof(ULONG));
- + if (status) goto out;
- + status = get_safe_read_bufferpos(&buffer, &length,
- + args->sec_desc_len, (const void **)&sec_desc_ptr);
- + if (status) goto out;
- + args->sec_desc = (PSECURITY_DESCRIPTOR)sec_desc_ptr;
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- status = safe_read(&buffer, &length, &args->disposition, sizeof(ULONG));
- if (status) goto out;
- status = safe_read(&buffer, &length, &args->open_owner_id, sizeof(LONG));
- @@ -751,6 +762,12 @@ out:
- }
- #endif /* NFS41_DRIVER_FEATURE_LOCAL_UIDGID_IN_NFSV3ATTRIBUTES */
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- +/* FIXME: Move this into aclutils.h */
- +int map_dacl_2_nfs4acl(PACL acl, PSID sid, PSID gsid, nfsacl41 *nfs4_acl,
- + int file_type, bool named_attr_support, char *domain);
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- +
- static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- {
- int status = 0;
- @@ -760,6 +777,14 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- nfs41_file_info info = { 0 };
- bool is_caseinsensitive_volume;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + nfsacl41 create_nfs4_acl = {
- + .flag = 0,
- + .aces = NULL,
- + .count = 0
- + };
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- +
- switch (args->is_caseinsensitive_volume) {
- case TRISTATE_BOOL_FALSE:
- is_caseinsensitive_volume = false;
- @@ -819,6 +844,55 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- else
- state->type = NF4REG;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + if (args->sec_desc_len) {
- + PSID sid = NULL, gsid = NULL;
- + BOOL sid_default, gsid_default;
- + BOOL dacl_present, dacl_default;
- + PACL acl;
- + DPRINTF(0,
- + ("handle_open: args->sec_desc=0x%p args->sec_desc_len=%ld\n",
- + args->sec_desc, (long)args->sec_desc_len));
- + status = GetSecurityDescriptorDacl(args->sec_desc, &dacl_present,
- + &acl, &dacl_default);
- + if (!status) {
- + status = GetLastError();
- + eprintf("handle_open: "
- + "GetSecurityDescriptorDacl() failed, lasterr=%d\n",
- + status);
- + goto out;
- + }
- + status = GetSecurityDescriptorOwner(args->sec_desc,
- + &sid, &sid_default);
- + if (!status) {
- + status = GetLastError();
- + eprintf("handle_open: "
- + "GetSecurityDescriptorOwner() failed, lasterr=%d\n",
- + status);
- + goto out;
- + }
- + status = GetSecurityDescriptorGroup(args->sec_desc,
- + &gsid, &gsid_default);
- + if (!status) {
- + status = GetLastError();
- + eprintf("handle_open: "
- + "GetSecurityDescriptorOwner() failed, lasterr=%d\n",
- + status);
- + goto out;
- + }
- + status = map_dacl_2_nfs4acl(acl, sid, gsid, &create_nfs4_acl,
- + state->type,
- + false /* FIXME!! */,
- + nfs41dg->localdomain_name);
- + if (status) {
- + eprintf("handle_open: "
- + "map_dacl_2_nfs4acl() failed, status=%d\n",
- + status);
- + goto out;
- + }
- + }
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- +
- // always do a lookup
- status = nfs41_lookup(upcall->root_ref, nfs41_root_session(upcall->root_ref),
- is_caseinsensitive_volume, &state->path,
- @@ -988,6 +1062,12 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- createattrs.attrmask.arr[0] = 0;
- createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
- createattrs.mode = 0777;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + if (create_nfs4_acl.aces) {
- + createattrs.acl = &create_nfs4_acl;
- + createattrs.attrmask.arr[0] |= FATTR4_WORD0_ACL;
- + }
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- DPRINTF(1, ("creating cygwin symlink '%s' -> '%s'\n",
- state->file.name.name, args->symlink.path));
- @@ -1068,6 +1148,13 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- createattrs.hidden = args->file_attrs & FILE_ATTRIBUTE_HIDDEN ? 1 : 0;
- createattrs.system = args->file_attrs & FILE_ATTRIBUTE_SYSTEM ? 1 : 0;
- createattrs.archive = args->file_attrs & FILE_ATTRIBUTE_ARCHIVE ? 1 : 0;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + if (create_nfs4_acl.aces) {
- + createattrs.acl = &create_nfs4_acl;
- + createattrs.attrmask.arr[0] |= FATTR4_WORD0_ACL;
- + }
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- +
- /* FIXME: What about |FILE_ATTRIBUTE_OFFLINE| ? */
- map_access_2_allowdeny(args->access_mask, args->access_mode,
- @@ -1231,6 +1318,9 @@ create_chgrp_out:
- upcall->state_ref = state;
- nfs41_open_state_ref(upcall->state_ref);
- out:
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + free(create_nfs4_acl.aces);
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- return status;
- out_free_state:
- nfs41_open_state_deref(state);
- diff --git a/daemon/upcall.h b/daemon/upcall.h
- index 4d7afb6..5e78f54 100644
- --- a/daemon/upcall.h
- +++ b/daemon/upcall.h
- @@ -62,6 +62,10 @@ typedef struct __open_upcall_args {
- ULONG file_attrs;
- ULONG disposition;
- ULONG create_opts;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + ULONG sec_desc_len;
- + PSECURITY_DESCRIPTOR sec_desc;
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- LONG open_owner_id;
- DWORD mode;
- #ifdef NFS41_DRIVER_FEATURE_LOCAL_UIDGID_IN_NFSV3ATTRIBUTES
- diff --git a/nfs41_build_features.h b/nfs41_build_features.h
- index 94be0e2..fb744f5 100644
- --- a/nfs41_build_features.h
- +++ b/nfs41_build_features.h
- @@ -267,4 +267,10 @@
- */
- #define NFS41_DRIVER_HACK_ENABLE_PAGEFILE_SUPPORT 1
- +/*
- + * |NFS41_DRIVER_ALLOW_CREATEFILE_ACLS| - allow setting
- + * ACLs at createfile time
- + */
- +#define NFS41_DRIVER_ALLOW_CREATEFILE_ACLS 1
- +
- #endif /* !_NFS41_DRIVER_BUILDFEATURES_ */
- diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
- index ff9b82e..bf5ebc0 100644
- --- a/sys/nfs41sys_driver.h
- +++ b/sys/nfs41sys_driver.h
- @@ -235,6 +235,10 @@ typedef struct _updowncall_entry {
- ULONG access_mode;
- ULONG attrs;
- ULONG copts;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + ULONG SdLength;
- + PSECURITY_DESCRIPTOR SdBuffer;
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- ULONG disp;
- ULONG cattrs;
- LONG open_owner_id;
- diff --git a/sys/nfs41sys_openclose.c b/sys/nfs41sys_openclose.c
- index 9196aad..3247539 100644
- --- a/sys/nfs41sys_openclose.c
- +++ b/sys/nfs41sys_openclose.c
- @@ -124,6 +124,9 @@ NTSTATUS marshal_nfs41_open(
- 7 * sizeof(ULONG) +
- 1 * sizeof(BOOLEAN) +
- 2 * sizeof(HANDLE) +
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + entry->u.Open.SdLength + 1 * sizeof(ULONG) +
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- length_as_utf8(&entry->u.Open.symlink);
- if (header_len > buf_len) {
- status = STATUS_INSUFFICIENT_RESOURCES;
- @@ -147,6 +150,14 @@ NTSTATUS marshal_nfs41_open(
- tmp += sizeof(entry->u.Open.attrs);
- RtlCopyMemory(tmp, &entry->u.Open.copts, sizeof(entry->u.Open.copts));
- tmp += sizeof(entry->u.Open.copts);
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + RtlCopyMemory(tmp, &entry->u.Open.SdLength, sizeof(entry->u.Open.SdLength));
- + tmp += sizeof(entry->u.Open.SdLength);
- + if (entry->u.Open.SdLength) {
- + RtlCopyMemory(tmp, entry->u.Open.SdBuffer, entry->u.Open.SdLength);
- + tmp += entry->u.Open.SdLength;
- + }
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- RtlCopyMemory(tmp, &entry->u.Open.disp, sizeof(entry->u.Open.disp));
- tmp += sizeof(entry->u.Open.disp);
- RtlCopyMemory(tmp, &entry->u.Open.open_owner_id,
- @@ -644,6 +655,23 @@ NTSTATUS nfs41_Create(
- status = check_nfs41_create_args(RxContext);
- if (status) goto out;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + ULONG SdLength = RxContext->Create.SdLength;
- + PSECURITY_DESCRIPTOR SdBuffer;
- + if (SdLength &&
- + params->SecurityContext &&
- + params->SecurityContext->AccessState) {
- + SdBuffer = params->SecurityContext->AccessState->SecurityDescriptor;
- + }
- + else {
- + SdBuffer = NULL;
- + }
- +
- + DbgP("nfs41_Create: "
- + "SecurityDescriptor=0x%p, len=%lu\n",
- + SdBuffer, SdLength);
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- +
- status = nfs41_UpcallCreate(NFS41_SYSOP_OPEN, NULL,
- pVNetRootContext->session, INVALID_HANDLE_VALUE,
- pNetRootContext->nfs41d_version,
- @@ -675,6 +703,10 @@ NTSTATUS nfs41_Create(
- entry->u.Open.attrs |= FILE_ATTRIBUTE_ARCHIVE;
- entry->u.Open.disp = params->Disposition;
- entry->u.Open.copts = params->CreateOptions;
- +#ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- + entry->u.Open.SdLength = SdLength;
- + entry->u.Open.SdBuffer = SdBuffer;
- +#endif /* NFS41_DRIVER_ALLOW_CREATEFILE_ACLS */
- entry->u.Open.srv_open = SrvOpen;
- /* treat the NfsActOnLink ea as FILE_OPEN_REPARSE_POINT */
- if ((ea && AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)) ||
- diff --git a/tests/atomiccreatefilewithacl/atomiccreatefilewithacl.ps1 b/tests/atomiccreatefilewithacl/atomiccreatefilewithacl.ps1
- new file mode 100644
- index 0000000..1fb4e0b
- --- /dev/null
- +++ b/tests/atomiccreatefilewithacl/atomiccreatefilewithacl.ps1
- @@ -0,0 +1,159 @@
- +
- +#
- +# MIT License
- +#
- +# Copyright (c) 2025 Roland Mainz <roland.mainz@nrubsig.org>
- +#
- +# Permission is hereby granted, free of charge, to any person obtaining a copy
- +# of this software and associated documentation files (the "Software"), to deal
- +# in the Software without restriction, including without limitation the rights
- +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- +# copies of the Software, and to permit persons to whom the Software is
- +# furnished to do so, subject to the following conditions:
- +#
- +# The above copyright notice and this permission notice shall be included in all
- +# copies or substantial portions of the Software.
- +#
- +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- +# SOFTWARE.
- +#
- +
- +#
- +# atomiccreatefilewithacl.ps1 - Atomically create file and add ACL at create time
- +#
- +# powershell -Command "$( < /cygdrive/l/createacltest/atomiccreatefilewithacl.ps1 )"
- +#
- +
- +function New-FileWithAcl {
- + [CmdletBinding()]
- + param(
- + [Parameter(Mandatory)]
- + [string]$Path,
- +
- + [Parameter(Mandatory)]
- + [string]$Content,
- +
- + # Zero or more user accounts (local or domain). e.g. "alice", "GLOBAL.LOC\bob"
- + [string[]]$Users = @(),
- +
- + # Zero or more group accounts (local or domain). e.g. "cygwingrp2", "GLOBAL.LOC\DevOps"
- + [string[]]$Groups = @(),
- +
- + # If set, disables inheritance and removes inherited ACEs.
- + [switch]$DisableInheritance,
- +
- + # If set, keeps inherited ACEs but prevents further propagation changes (uses Protect + Preserve)
- + [switch]$ProtectAndPreserveInheritance
- + )
- +
- + begin {
- + # Validate inheritance switches
- + if ($DisableInheritance -and $ProtectAndPreserveInheritance) {
- + throw "Use either -DisableInheritance or -ProtectAndPreserveInheritance, not both."
- + }
- +
- + # Ensure parent directory exists
- + $dir = Split-Path -Path $Path -Parent
- + if ([string]::IsNullOrWhiteSpace($dir)) {
- + $dir = "."
- + }
- + if (-not (Test-Path -LiteralPath $dir)) {
- + New-Item -ItemType Directory -Path $dir -Force | Out-Null
- + }
- +
- + # Helper: resolve an NTAccount (string) to a SID, with clear error if not found
- + function Resolve-ToSid {
- + param([Parameter(Mandatory)][string]$Account)
- + try {
- + $nt = New-Object System.Security.Principal.NTAccount($Account)
- + return $nt.Translate([System.Security.Principal.SecurityIdentifier])
- + }
- + catch {
- + throw "Account not found or not resolvable: '$Account'. Use 'DOMAIN\name' or '.\name' for local."
- + }
- + }
- +
- + # Helper: create a FileSystemAccessRule (FullControl, file-only)
- + function New-FullControlRule {
- + param([Parameter(Mandatory)][System.Security.Principal.SecurityIdentifier]$Sid)
- + return New-Object System.Security.AccessControl.FileSystemAccessRule(
- + $Sid,
- + [System.Security.AccessControl.FileSystemRights]::FullControl,
- + [System.Security.AccessControl.InheritanceFlags]::None, # file only
- + [System.Security.AccessControl.PropagationFlags]::None,
- + [System.Security.AccessControl.AccessControlType]::Allow
- + )
- + }
- + }
- +
- + process {
- + # Build FileSecurity (DACL)
- + $fileSec = New-Object System.Security.AccessControl.FileSecurity
- +
- + # Inheritance behavior
- + if ($DisableInheritance) {
- + # protect: true, preserveInheritedACL: false (remove inherited ACEs)
- + $fileSec.SetAccessRuleProtection($true, $false)
- + }
- + elseif ($ProtectAndPreserveInheritance) {
- + # protect: true, preserveInheritedACL: true (keep existing inherited ACEs)
- + $fileSec.SetAccessRuleProtection($true, $true)
- + }
- + else {
- + # protect: false -> inherit from parent
- + $fileSec.SetAccessRuleProtection($false, $true)
- + }
- +
- + # Add ACEs for users
- + foreach ($u in $Users) {
- + if ([string]::IsNullOrWhiteSpace($u)) { continue }
- + $sid = Resolve-ToSid -Account $u
- + $rule = New-FullControlRule -Sid $sid
- + $fileSec.AddAccessRule($rule) | Out-Null
- + }
- +
- + # Add ACEs for groups
- + foreach ($g in $Groups) {
- + if ([string]::IsNullOrWhiteSpace($g)) { continue }
- + $sid = Resolve-ToSid -Account $g
- + $rule = New-FullControlRule -Sid $sid
- + $fileSec.AddAccessRule($rule) | Out-Null
- + }
- +
- + # Create file atomically with the ACL
- + $fs = $null
- + $writer = $null
- + try {
- + $fs = [System.IO.File]::Create($Path, 4096, [System.IO.FileOptions]::None, $fileSec)
- + $writer = New-Object System.IO.StreamWriter($fs)
- + $writer.Write($Content)
- + $writer.Flush()
- + }
- + finally {
- + if ($writer) { $writer.Dispose() }
- + if ($fs) { $fs.Dispose() }
- + }
- +
- + # Return file info and ACL
- + [PSCustomObject]@{
- + Path = (Resolve-Path -LiteralPath $Path).ProviderPath
- + Length = (Get-Item -LiteralPath $Path).Length
- + Acl = (Get-Acl -LiteralPath $Path)
- + }
- + }
- +}
- +
- +# test 1
- +New-FileWithAcl `
- + -Path "phw1.txt" `
- + -Content "hello world" `
- + -Users @("siegfried_wulsch", "roland_mainz") `
- + -Groups @("cygwingrp2") `
- + -DisableInheritance
- +
- +# EOF.
- --
- 2.51.0
msnfs41client: Patch to allow adding ACLs at file creation time, 2025-11-13
Posted by Anonymous on Thu 13th Nov 2025 01:01
raw | new post
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
rovema.kpaste.net RSS