From 98f45194b1a7cd07c506f62895cbbd4327c3cc4b Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Sat, 30 May 2026 00:43:38 +0200 Subject: [PATCH] nwconn: scan volume user disk restrictions Wire NCP 0x16/0x20 Scan Volume's User Disk Restrictions to the existing quota backend when quota support is available. The WebSDK documents NCP 0x2222/22/32 as taking a Volume Number and a Sequence value, and returning a Number Of Entries byte followed by up to twelve Object ID / Restriction pairs. The Sequence starts at zero and is incremented by the number of entries returned by previous calls. Restrictions are reported in 4K blocks. The SDK headers expose the same call as NWScanVolDiskRestrictions() and NWScanVolDiskRestrictions2(). The Rust nwserver implementation and lwared both validate the volume and return an empty list, which is compatible with an unrestricted server but does not expose configured quotas. Keep that empty-list behavior for non-quota builds. When QUOTA_SUPPORT is enabled, scan bindery user objects, map each object through its UNIX_USER property to a Unix uid, query nw_get_vol_restrictions(), and return entries for users with an actual finite restriction. Use the request Sequence as an index into the restricted-entry stream and return at most the SDK maximum of twelve entries per reply. Add the SDK request/reply semantics to the inline endpoint comment. This enables the documented endpoint path while preserving the existing no-restrictions result for builds without quota support. --- src/nwconn.c | 111 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 14 deletions(-) diff --git a/src/nwconn.c b/src/nwconn.c index 56c5f34..93cf99b 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -43,6 +43,7 @@ #include "nwqconn.h" #include "namspace.h" #include "nwshare.h" +#include "nwdbm.h" #include "nwconn.h" #include "trustee.h" @@ -77,6 +78,77 @@ static char *prog_title; static int req_printed=0; + +static int scan_volume_user_disk_restrictions(uint8 volnr, uint32 sequence, + uint8 *response) +{ + struct XDATA { + uint8 entries; + struct { + uint8 id[4]; + uint8 restriction[4]; + } entry[12]; + } *xdata = (struct XDATA *)response; + int result = nw_get_volume_name(volnr, NULL, 0); + + if (result < 0) + return result; + + xdata->entries = 0; + +#if QUOTA_SUPPORT + { + NETOBJ obj; + uint32 last_obj_id = MAX_U32; + uint32 restricted_index = 0; + uint8 wild[] = "*"; + int scan_result; + + memset(&obj, 0, sizeof(obj)); + obj.type = 1; /* user */ + xstrcpy(obj.name, wild); + + scan_result = scan_for_obj(&obj, last_obj_id, 1); + while (!scan_result && xdata->entries < 12) { + uint8 more_segments = 0; + uint8 property_flags = 0; + char unix_user[128]; + uint32 restriction = 0x40000000; + uint32 inuse = 0; + + memset(unix_user, 0, sizeof(unix_user)); + if (!nw_get_prop_val_by_obj_id(obj.id, 1, + (uint8 *)"UNIX_USER", 9, + (uint8 *)unix_user, &more_segments, &property_flags)) { + struct passwd *pw; + + unix_user[sizeof(unix_user)-1] = '\0'; + pw = getpwnam(unix_user); + + if (pw && !nw_get_vol_restrictions(volnr, pw->pw_uid, + &restriction, &inuse) && + restriction < 0x40000000) { + if (restricted_index >= sequence) { + U32_TO_BE32(obj.id, xdata->entry[xdata->entries].id); + U32_TO_BE32(restriction, xdata->entry[xdata->entries].restriction); + xdata->entries++; + } + restricted_index++; + } + } + + last_obj_id = obj.id; + memset(&obj, 0, sizeof(obj)); + obj.type = 1; + xstrcpy(obj.name, wild); + scan_result = scan_for_obj(&obj, last_obj_id, 1); + } + } +#endif + + return 1 + (8 * xdata->entries); +} + static int trustee_v3_to_ncp22_rights(int rights) { int ncp22 = 0; @@ -1215,21 +1287,32 @@ static int handle_ncp_serv(void) } break; - case 0x20 : { /* scan volume user disk restrictions */ + case 0x20 : { /* Scan Volume's User Disk Restrictions */ + /* + * NCP 0x2222/22/32 Scan Volume's User Disk Restrictions. + * + * WebSDK / headers: + * Request: Volume Number, Sequence. Sequence starts at + * zero and is incremented by the number of entries returned + * by previous calls. + * Reply: Number Of Entries followed by up to twelve + * Object ID / Restriction pairs. Restrictions are reported + * in 4K blocks. + * + * The Rust nwserver and lwared implementations both return + * an empty list after validating the volume. MARS-NWE keeps + * that SDK-compatible unrestricted result for non-quota + * builds, and fills entries from the existing Linux quota + * backend when QUOTA_SUPPORT is enabled and a restricted + * bindery user maps to a Unix uid. + */ uint8 volnr = *(p+1); - /* uint32 sequence = GET_BE32(p+2); */ - struct XDATA { - uint8 entries; /* 0x0 */ - /*--- per entry (max.entries = 12) ----*/ - uint8 id[4]; - uint8 restriction[4]; - } *xdata = (struct XDATA*) responsedata; - int result = nw_get_volume_name(volnr, NULL, 0); - if (result > -1) { - xdata->entries = 0x0; - data_len = (8 * xdata->entries) + 1; - } else completition = (uint8) (-result); - } + uint32 sequence = GET_BE32(p+2); + int result = scan_volume_user_disk_restrictions( + volnr, sequence, responsedata); + if (result > -1) data_len = result; + else completition = (uint8) (-result); + } break; case 0x21 : { /* change Vol restrictions for Obj */