From 573cb448dcd0eebcc6712dc46074aa03ee87ce9b Mon Sep 17 00:00:00 2001 From: ktyang512 Date: Thu, 13 Aug 2026 10:54:02 +0900 Subject: [PATCH] camera_calibration_parsers: allow reading read-only INI files Open calibration INI files with std::ifstream so read access does not require write permission. Add cross-platform regression coverage for read-only files. Generated-by: OpenAI Codex (GPT-5) Signed-off-by: ktyang512 --- camera_calibration_parsers/src/parse_ini.cpp | 2 +- .../test/test_parse_ini.cpp | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/camera_calibration_parsers/src/parse_ini.cpp b/camera_calibration_parsers/src/parse_ini.cpp index fc0350b9..e8c9e0e8 100644 --- a/camera_calibration_parsers/src/parse_ini.cpp +++ b/camera_calibration_parsers/src/parse_ini.cpp @@ -360,7 +360,7 @@ bool readCalibrationIni( const std::string & file_name, std::string & camera_name, CameraInfo & cam_info) { - std::fstream fs(file_name); + std::ifstream fs(file_name); return readCalibrationIni(fs, camera_name, cam_info); } diff --git a/camera_calibration_parsers/test/test_parse_ini.cpp b/camera_calibration_parsers/test/test_parse_ini.cpp index 26e3b28b..0f27913c 100644 --- a/camera_calibration_parsers/test/test_parse_ini.cpp +++ b/camera_calibration_parsers/test/test_parse_ini.cpp @@ -15,7 +15,15 @@ #include #include +#include #include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif #include "camera_calibration_parsers/parse_ini.hpp" #include "sensor_msgs/distortion_models.hpp" @@ -40,6 +48,57 @@ std::string custom_tmpnam() #endif } +class ScopedReadOnlyFile +{ +public: + explicit ScopedReadOnlyFile(std::string path) + : path_(std::move(path)) + {} + + ScopedReadOnlyFile(const ScopedReadOnlyFile &) = delete; + ScopedReadOnlyFile & operator=(const ScopedReadOnlyFile &) = delete; + + ~ScopedReadOnlyFile() + { +#ifdef _WIN32 + if (original_attributes_ != INVALID_FILE_ATTRIBUTES) { + SetFileAttributesA(path_.c_str(), original_attributes_); + } +#else + if (has_original_mode_) { + chmod(path_.c_str(), original_mode_); + } +#endif + std::remove(path_.c_str()); + } + + bool make_read_only() + { +#ifdef _WIN32 + original_attributes_ = GetFileAttributesA(path_.c_str()); + return original_attributes_ != INVALID_FILE_ATTRIBUTES && + SetFileAttributesA(path_.c_str(), original_attributes_ | FILE_ATTRIBUTE_READONLY) != 0; +#else + struct stat file_info; + has_original_mode_ = stat(path_.c_str(), &file_info) == 0; + if (has_original_mode_) { + original_mode_ = file_info.st_mode; + } + return has_original_mode_ && + chmod(path_.c_str(), S_IRUSR | S_IRGRP | S_IROTH) == 0; +#endif + } + +private: + std::string path_; +#ifdef _WIN32 + DWORD original_attributes_{INVALID_FILE_ATTRIBUTES}; +#else + mode_t original_mode_{}; + bool has_original_mode_{false}; +#endif +}; + static const char * kValidCalib5 = R"( # Comment with pound @@ -238,6 +297,34 @@ TEST(ParseIni, roundtrip_calib5) { check_calib(cam_info2); } +TEST(ParseIni, read_only_calib5) { + std::string calib_file = custom_tmpnam(); + ScopedReadOnlyFile read_only_file(calib_file); + + std::string camera_name = "read_only_calib5"; + auto cam_info = make_calib(sensor_msgs::distortion_models::PLUMB_BOB); + ASSERT_TRUE( + camera_calibration_parsers::writeCalibrationIni(calib_file, camera_name, cam_info)); + + ASSERT_TRUE(read_only_file.make_read_only()); + + std::ifstream read_probe(calib_file); + ASSERT_TRUE(read_probe.is_open()); + read_probe.close(); + + std::fstream read_write_probe(calib_file); + if (read_write_probe.is_open()) { + GTEST_SKIP() << "Environment does not enforce read-only file permissions"; + } + + std::string camera_name2; + sensor_msgs::msg::CameraInfo cam_info2; + ASSERT_TRUE( + camera_calibration_parsers::readCalibrationIni(calib_file, camera_name2, cam_info2)); + EXPECT_EQ(camera_name2, camera_name); + check_calib(cam_info2); +} + TEST(ParseIni, cant_write_calib8) { std::string calib_file = custom_tmpnam();