Skip to content

Commit 1e87702

Browse files
committed
mex: set_permissions()
1 parent 2c951f4 commit 1e87702

File tree

6 files changed

+149
-15
lines changed

6 files changed

+149
-15
lines changed

+stdlib/is_char_device.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
function ok = is_char_device(p)
66
arguments
7-
p (1,1) string
7+
p (1,1) string
88
end
99

1010
if stdlib.isoctave()
1111
ok = S_ISCHR(stat(p).mode);
1212
else
13-
ok = is_char_device_mex(p);
13+
error("need to 'buildtool mex' or 'legacy_mex_build()' first")
1414
end
1515

1616
end

+stdlib/is_symlink.m

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,7 @@
1010
ok = isSymbolicLink(p);
1111
catch e
1212
if strcmp(e.identifier, "MATLAB:UndefinedFunction")
13-
try
14-
ok = is_symlink_mex(p);
15-
catch e
16-
if strcmp(e.identifier, "MATLAB:UndefinedFunction")
17-
ok = java.nio.file.Files.isSymbolicLink(javaPathObject(stdlib.absolute(p, "", false)));
18-
else
19-
rethrow(e)
20-
end
21-
end
13+
ok = java.nio.file.Files.isSymbolicLink(javaPathObject(stdlib.absolute(p, "", false)));
2214
elseif strcmp(e.identifier, "Octave:undefined-function")
2315
ok = S_ISLNK(stat(p).mode);
2416
else

private/get_mex_sources.m

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55

66
%% source C++
77

8-
srcs = {fullfile(top, "src/is_char_device.cpp")};
8+
% Matlab and Java don't have these functions
9+
srcs = {fullfile(top, "src/is_char_device.cpp"), ...
10+
fullfile(top, "src/set_permissions.cpp"), ...
11+
};
12+
13+
% isSymbolicLink() new in R2024b
14+
if ~isMATLABReleaseOlderThan("R2024b")
915

1016
s = fullfile(top, "src/is_symlink.cpp");
1117
if ispc
1218
s(end+1) = fullfile(top, "src/windows.cpp");
1319
end
14-
1520
srcs{end+1} = s;
1621

1722
end
23+
24+
end

src/set_permissions.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// C++ Matlab MEX function using C++17 <filesystem>
2+
// https://www.mathworks.com/help/matlab/matlab_external/data-types-for-passing-mex-function-data-1.html
3+
4+
#include "mex.hpp"
5+
#include "mexAdapter.hpp"
6+
7+
#include <string>
8+
// note: <string_view> causes compile failures with MSVC at least
9+
10+
#include <vector>
11+
#include <memory>
12+
13+
#if __has_include(<filesystem>)
14+
# include <filesystem>
15+
# include <system_error>
16+
#endif
17+
18+
19+
static bool fs_set_permissions(std::string path, int readable, int writable, int executable)
20+
{
21+
#ifdef __cpp_lib_filesystem
22+
23+
#if defined(__cpp_using_enum) // C++20
24+
using enum std::filesystem::perms;
25+
#else
26+
constexpr std::filesystem::perms owner_read = std::filesystem::perms::owner_read;
27+
constexpr std::filesystem::perms owner_write = std::filesystem::perms::owner_write;
28+
constexpr std::filesystem::perms owner_exec = std::filesystem::perms::owner_exec;
29+
#endif
30+
31+
std::filesystem::path p(path);
32+
std::error_code ec;
33+
// need to error if path doesn't exist and no operations are requested
34+
if(!std::filesystem::exists(p))
35+
ec = std::make_error_code(std::errc::no_such_file_or_directory);
36+
37+
if (!ec && readable != 0)
38+
std::filesystem::permissions(p, owner_read,
39+
(readable > 0) ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove,
40+
ec);
41+
42+
if (!ec && writable != 0)
43+
std::filesystem::permissions(p, owner_write,
44+
(writable > 0) ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove,
45+
ec);
46+
47+
if (!ec && executable != 0)
48+
std::filesystem::permissions(p, owner_exec,
49+
(executable > 0) ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove,
50+
ec);
51+
52+
if(!ec)
53+
return true;
54+
55+
#endif
56+
return false;
57+
58+
}
59+
60+
61+
62+
class MexFunction : public matlab::mex::Function {
63+
public:
64+
void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {
65+
// boilerplate engine & ArrayFactory setup
66+
std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
67+
68+
matlab::data::ArrayFactory factory;
69+
// boilerplate input checks
70+
if (inputs.size() != 4) {
71+
matlabPtr->feval(u"error", 0,
72+
std::vector<matlab::data::Array>({ factory.createScalar("Four inputs required") }));
73+
}
74+
if (inputs[0].getType() != matlab::data::ArrayType::MATLAB_STRING) {
75+
matlabPtr->feval(u"error", 0,
76+
std::vector<matlab::data::Array>({ factory.createScalar("Input must be a string") }));
77+
}
78+
if (inputs[0].getNumberOfElements() != 1) {
79+
matlabPtr->feval(u"error", 0,
80+
std::vector<matlab::data::Array>({ factory.createScalar("Input must be a scalar string") }));
81+
}
82+
if (inputs[1].getType() != matlab::data::ArrayType::DOUBLE ||
83+
inputs[2].getType() != matlab::data::ArrayType::DOUBLE ||
84+
inputs[3].getType() != matlab::data::ArrayType::DOUBLE) {
85+
matlabPtr->feval(u"error", 0,
86+
std::vector<matlab::data::Array>({ factory.createScalar("Permission inputs must be doubles") }));
87+
}
88+
89+
// Matlab strings are an array, so we use [0][0] to get the first element
90+
std::string inputStr = inputs[0][0];
91+
92+
// actual function algorithm / computation
93+
bool y = fs_set_permissions(inputStr, inputs[1][0], inputs[2][0], inputs[3][0]);
94+
95+
// convert to Matlab output -- even scalars are arrays in Matlab
96+
// https://www.mathworks.com/help/matlab/matlab_external/create-matlab-array-with-matlab-data-cpp-api.html
97+
// https://www.mathworks.com/help/matlab/apiref/matlab.data.arrayfactory.html
98+
99+
outputs[0] = factory.createArray<bool>({1,1}, {y});
100+
}
101+
};

test/TestJava.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,12 @@ function test_is_regular_file(tc)
127127

128128

129129
function test_touch_modtime(tc)
130-
fn = tempname;
130+
import matlab.unittest.fixtures.TemporaryFolderFixture
131+
132+
fixture = tc.applyFixture(TemporaryFolderFixture);
133+
134+
fn = fullfile(fixture.Folder, "modtime.txt");
135+
131136
tc.verifyTrue(stdlib.touch(fn, datetime("yesterday")))
132137
t0 = stdlib.get_modtime(fn);
133138

test/TestPermissions.m

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
methods (Test)
88

99
function test_get_permissions(tc, Ps)
10+
1011
import matlab.unittest.constraints.StartsWithSubstring
1112
import matlab.unittest.constraints.IsOfClass
1213

@@ -21,6 +22,34 @@ function test_get_permissions(tc, Ps)
2122

2223
end
2324

25+
26+
function test_set_permissions(tc)
27+
28+
import matlab.unittest.constraints.StartsWithSubstring
29+
import matlab.unittest.fixtures.TemporaryFolderFixture
30+
31+
fixture = tc.applyFixture(TemporaryFolderFixture);
32+
33+
nr = fullfile(fixture.Folder, "no-read");
34+
35+
tc.verifyTrue(stdlib.touch(nr))
36+
tc.verifyTrue(stdlib.set_permissions(nr, -1, 0, 0))
37+
p = stdlib.get_permissions(nr);
38+
39+
if ~ispc
40+
tc.verifyThat(p, StartsWithSubstring("-"), "non-read permission failed to set")
41+
end
42+
43+
nw = fullfile(fixture.Folder, "no-write");
44+
45+
tc.verifyTrue(stdlib.touch(nw))
46+
tc.verifyTrue(stdlib.set_permissions(nw, 0, -1, 0))
47+
p = stdlib.get_permissions(nw);
48+
49+
tc.verifyThat(p, StartsWithSubstring("r-"), "non-write permission failed to set")
50+
51+
end
52+
2453
end
2554

26-
end
55+
end

0 commit comments

Comments
 (0)