From 6dd2f232edd951fafe1b4480462fa2fc8b8a0754 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 23:48:05 +0000 Subject: [PATCH 1/2] Fix binary file mode: use 'rb' and 'uint8' to prevent data corruption on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fileobj: default permission 'r' → 'rb' for binary-safe file access - fileobj.fread: default precision 'char' → 'uint8' to preserve raw bytes - readonly_fileobj.fopen: hardcoded 'r' → 'rb' - mustBeValidPermission: accept binary mode permissions ('rb', 'wb', etc.) - fileobj write guards: check permission(1) instead of exact 'r' match Text mode ('r') on Linux applies newline translation and encoding conversion, corrupting non-ASCII bytes (e.g., gzip magic 0x8B becomes 0xFF). https://claude.ai/code/session_01THzAXtPm5RNSNDXRMiZXq2 --- src/did/+did/+file/fileobj.m | 12 ++++++------ src/did/+did/+file/mustBeValidPermission.m | 4 ++-- src/did/+did/+file/readonly_fileobj.m | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/did/+did/+file/fileobj.m b/src/did/+did/+file/fileobj.m index f13e405..0158b7b 100644 --- a/src/did/+did/+file/fileobj.m +++ b/src/did/+did/+file/fileobj.m @@ -14,7 +14,7 @@ fid (1,1) double = -1 % permission - The file permission - permission (1,:) char {did.file.mustBeValidPermission} = 'r' + permission (1,:) char {did.file.mustBeValidPermission} = 'rb' % machineformat - big-endian ('b'), little-endian ('l'), or native ('n') machineformat (1,:) char {did.file.mustBeValidMachineFormat} = 'n' @@ -30,7 +30,7 @@ % then the filename is stored. arguments propValues.machineformat (1,1) string {did.file.mustBeValidMachineFormat} = 'n'; % native machine format - propValues.permission (1,1) string {did.file.mustBeValidPermission} = "r" + propValues.permission (1,1) string {did.file.mustBeValidPermission} = "rb" propValues.fid (1,1) int64 = -1 propValues.fullpathfilename = ''; end @@ -207,7 +207,7 @@ function frewind(fileobj_obj) % % See also: FWRITE - if strcmpi(fileobj_obj.permission,'r') + if strcmpi(fileobj_obj.permission(1),'r') error('DID:File:Fileobj','Cannot use fwrite() method with read-only file'); end @@ -233,7 +233,7 @@ function frewind(fileobj_obj) % [DATA,COUNT] = FREAD(FILEOBJ_OBJ, COUNT, [PRECISION], [SKIP], [MACHINEFORMAT]) % % Attempts to read COUNT elements with resolution PRECISION. If PRECISION is not - % provided, then 'char' is assumed. If SKIP is provided, then SKIP is in number of bytes, unless + % provided, then 'uint8' is assumed. If SKIP is provided, then SKIP is in number of bytes, unless % PRECISION is in bits, in which case SKIP is in bits. MACHINEFORMAT is the machine format to use. % % See FREAD for a full description of these input arguments. @@ -243,7 +243,7 @@ function frewind(fileobj_obj) data = []; if nargin<3 - precision = 'char'; + precision = 'uint8'; end if nargin<4 skip = 0; @@ -344,7 +344,7 @@ function frewind(fileobj_obj) varargin end - if strcmpi(fileobj_obj.permission,'r') + if strcmpi(fileobj_obj.permission(1),'r') error('DID:File:Fileobj','Cannot use fprintf() method with read-only file'); end count = 0; diff --git a/src/did/+did/+file/mustBeValidPermission.m b/src/did/+did/+file/mustBeValidPermission.m index 00436b8..70559a9 100644 --- a/src/did/+did/+file/mustBeValidPermission.m +++ b/src/did/+did/+file/mustBeValidPermission.m @@ -9,8 +9,8 @@ function mustBeValidPermission(value) permissionsAsString = strjoin( " " + VALID_PERMISSIONS, newline); - % Add text modes: - VALID_PERMISSIONS = [VALID_PERMISSIONS, insertAfter(VALID_PERMISSIONS, 1, "t")]; + % Add text and binary modes: + VALID_PERMISSIONS = [VALID_PERMISSIONS, insertAfter(VALID_PERMISSIONS, 1, "t"), insertAfter(VALID_PERMISSIONS, 1, "b")]; assert(ismember(value, VALID_PERMISSIONS), ... 'File permission must be one of:\n%s\n', permissionsAsString) diff --git a/src/did/+did/+file/readonly_fileobj.m b/src/did/+did/+file/readonly_fileobj.m index 39266a7..757aed9 100644 --- a/src/did/+did/+file/readonly_fileobj.m +++ b/src/did/+did/+file/readonly_fileobj.m @@ -18,7 +18,7 @@ % then the filename is stored. arguments options.machineformat (1,1) string {did.file.mustBeValidMachineFormat} = 'n'; % native machine format - options.permission (1,1) string {did.file.mustBeValidPermission} = "r" + options.permission (1,1) string {did.file.mustBeValidPermission} = "rb" options.fid (1,1) int64 = -1 options.fullpathfilename = ''; end @@ -29,7 +29,7 @@ % Ensure that the default 'r' permission was not modified if ~strcmpi(fileobj_obj.permission(1),'r') - error('DID:File:ReadOnly_Fileobj','Read-only file must have ''r'' permission'); + error('DID:File:ReadOnly_Fileobj','Read-only file must have ''rb'' permission'); end end % readonly_fileobj() constructor @@ -42,10 +42,10 @@ % % See also: FOPEN, FILEOBJ/FOPEN, FILEOBJ/FCLOSE, FCLOSE - if nargin > 1 && ~strcmpi(permission,'r') - error('DID:File:ReadOnly_Fileobj','Read-only file must be opened with ''r'' permission'); + if nargin > 1 && ~strcmpi(permission,'rb') + error('DID:File:ReadOnly_Fileobj','Read-only file must be opened with ''rb'' permission'); end - fileobj_obj = fopen@did.file.fileobj(fileobj_obj,'r',varargin{:}); + fileobj_obj = fopen@did.file.fileobj(fileobj_obj,'rb',varargin{:}); end %fopen end % methods From a180db3c3616f81d5d44eb64d9ddc0cfd5c9ed33 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 23:53:28 +0000 Subject: [PATCH 2/2] Fix test and binarydoc_matfid to use 'rb' permission - Update test_fileobj to expect 'rb' default permission - Update binarydoc_matfid.fclose to reset permission to 'rb' https://claude.ai/code/session_01THzAXtPm5RNSNDXRMiZXq2 --- src/did/+did/+implementations/binarydoc_matfid.m | 2 +- tests/+did/+unittest/test_fileobj.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/did/+did/+implementations/binarydoc_matfid.m b/src/did/+did/+implementations/binarydoc_matfid.m index 0d341d6..b1a0d47 100644 --- a/src/did/+did/+implementations/binarydoc_matfid.m +++ b/src/did/+did/+implementations/binarydoc_matfid.m @@ -39,7 +39,7 @@ % the database. % binarydoc_matfid_obj.fclose@did.file.fileobj(); - binarydoc_matfid_obj.permission = 'r'; + binarydoc_matfid_obj.permission = 'rb'; end % fclose() end end diff --git a/tests/+did/+unittest/test_fileobj.m b/tests/+did/+unittest/test_fileobj.m index 99305b3..1ed8f25 100644 --- a/tests/+did/+unittest/test_fileobj.m +++ b/tests/+did/+unittest/test_fileobj.m @@ -4,7 +4,7 @@ function test_constructor(testCase) % Test creating a fileobj theFileObj = did.file.fileobj(); testCase.verifyEqual(theFileObj.fid, -1); - testCase.verifyEqual(theFileObj.permission, 'r'); + testCase.verifyEqual(theFileObj.permission, 'rb'); testCase.verifyEqual(theFileObj.machineformat, 'n'); testCase.verifyEqual(theFileObj.fullpathfilename, ''); end