You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@nifi.apache.org by GitBox <gi...@apache.org> on 2021/02/03 16:40:56 UTC

[GitHub] [nifi-minifi-cpp] lordgamez commented on a change in pull request #987: MINIFICPP-1455: Fix FileStream error handling and reporting

lordgamez commented on a change in pull request #987:
URL: https://github.com/apache/nifi-minifi-cpp/pull/987#discussion_r569559488



##########
File path: libminifi/src/io/FileStream.cpp
##########
@@ -114,17 +151,18 @@ int FileStream::read(uint8_t *buf, int buflen) {
   }
   if (!IsNullOrEmpty(buf)) {
     std::lock_guard<std::mutex> lock(file_lock_);
-    if (!file_stream_) {
+    if (file_stream_ == nullptr || !file_stream_->is_open()) {
+      logging::LOG_ERROR(logger_) << read_error_msg << invalid_file_stream_error_msg;
       return -1;
     }
     file_stream_->read(reinterpret_cast<char*>(buf), buflen);
-    if ((file_stream_->rdstate() & (file_stream_->eofbit | file_stream_->failbit)) != 0) {
+    if (file_stream_->eof() || file_stream_->fail()) {
       file_stream_->clear();
       file_stream_->seekg(0, file_stream_->end);
       file_stream_->seekp(0, file_stream_->end);

Review comment:
       Maybe these could be checked as well.

##########
File path: libminifi/src/io/FileStream.cpp
##########
@@ -59,15 +79,19 @@ FileStream::FileStream(const std::string &path, uint32_t offset, bool write_enab
   } else {
     file_stream_->open(path.c_str(), std::fstream::in | std::fstream::binary);
   }
-  file_stream_->seekg(0, file_stream_->end);
-  file_stream_->seekp(0, file_stream_->end);
-  std::streamoff len = file_stream_->tellg();
-  if (len > 0) {
-    length_ = gsl::narrow<size_t>(len);
+  if (file_stream_->is_open()) {
+    file_stream_->seekg(0, file_stream_->end);
+    file_stream_->seekp(0, file_stream_->end);
+    std::streamoff len = file_stream_->tellg();
+    if (len > 0) {
+      length_ = gsl::narrow<size_t>(len);
+    } else {
+      length_ = 0;
+    }

Review comment:
       We could use the same ternary operator as above here as well.

##########
File path: libminifi/src/io/FileStream.cpp
##########
@@ -31,21 +31,41 @@ namespace nifi {
 namespace minifi {
 namespace io {
 
+constexpr const char *file_opening_error_msg = "Error opening file: ";
+constexpr const char *read_error_msg = "Error reading from file: ";
+constexpr const char *write_error_msg = "Error writing to file: ";
+constexpr const char *seek_error = "Error seeking in file: ";
+constexpr const char *invalid_file_stream_error_msg = "invalid file stream";
+constexpr const char *tellg_call_error_msg = "tellg call on file stream failed";
+constexpr const char *invalid_buffer_error_msg = "invalid buffer";
+constexpr const char *flush_call_error_msg = "flush call on file stream failed";
+constexpr const char *write_call_error_msg = "write call on file stream failed";
+constexpr const char *empty_message_error_msg = "empty message";
+constexpr const char *seekg_call_error_msg = "seekg call on file stream failed";
+constexpr const char *seekp_call_error_msg = "seekp call on file stream failed";
+
 FileStream::FileStream(const std::string &path, bool append)
     : logger_(logging::LoggerFactory<FileStream>::getLogger()),
       path_(path),
       offset_(0) {
   file_stream_ = std::unique_ptr<std::fstream>(new std::fstream());
   if (append) {
     file_stream_->open(path.c_str(), std::fstream::in | std::fstream::out | std::fstream::app | std::fstream::binary);
-    file_stream_->seekg(0, file_stream_->end);
-    file_stream_->seekp(0, file_stream_->end);
-    std::streamoff len = file_stream_->tellg();
-    length_ = len > 0 ? gsl::narrow<size_t>(len) : 0;
-    seek(offset_);
+    if (file_stream_->is_open()) {
+      file_stream_->seekg(0, file_stream_->end);
+      file_stream_->seekp(0, file_stream_->end);
+      std::streamoff len = file_stream_->tellg();

Review comment:
       Should we maybe check if seekg, seekp and tellg were successful as well?

##########
File path: libminifi/test/unit/FileStreamTests.cpp
##########
@@ -263,3 +267,75 @@ TEST_CASE("Read zero bytes") {
   minifi::io::FileStream stream(utils::file::concat_path(dir, "test.txt"), 0, true);
   REQUIRE(stream.read(nullptr, 0) == 0);
 }
+
+TEST_CASE("Non-existing file read/write test") {
+  TestController test_controller;
+  char format[] = "/tmp/gt.XXXXXX";
+  auto dir = test_controller.createTempDirectory(format);
+  minifi::io::FileStream stream(utils::file::concat_path(dir, "non_existing_file.txt"), 0, true);
+  REQUIRE(test_controller.getLog().getInstance().contains("Error opening file", std::chrono::seconds(0)));
+  REQUIRE(test_controller.getLog().getInstance().contains("No such file or directory", std::chrono::seconds(0)));
+  REQUIRE(stream.write("lorem ipsum", false) == -1);
+  REQUIRE(test_controller.getLog().getInstance().contains("Error writing to file: invalid file stream", std::chrono::seconds(0)));
+  std::vector<uint8_t> readBuffer;
+  stream.seek(0);
+  REQUIRE(stream.read(readBuffer, 1) == -1);
+  REQUIRE(test_controller.getLog().getInstance().contains("Error reading from file: invalid file stream", std::chrono::seconds(0)));
+}
+
+TEST_CASE("Existing file read/write test") {
+  TestController test_controller;
+  char format[] = "/tmp/gt.XXXXXX";
+  auto dir = test_controller.createTempDirectory(format);
+  std::string path_to_existing_file(utils::file::concat_path(dir, "existing_file.txt"));
+  {
+    std::ofstream outfile(path_to_existing_file);
+    outfile << "lorem ipsum" << std::endl;
+    outfile.close();
+  }
+  minifi::io::FileStream stream(path_to_existing_file, 0, true);
+  REQUIRE_FALSE(test_controller.getLog().getInstance().contains("Error opening file", std::chrono::seconds(0)));
+  REQUIRE_FALSE(stream.write("dolor sit amet", false) == -1);
+  REQUIRE_FALSE(test_controller.getLog().getInstance().contains("Error writing to file", std::chrono::seconds(0)));
+  std::vector<uint8_t> readBuffer;
+  stream.seek(0);
+  REQUIRE_FALSE(stream.read(readBuffer, 11) == -1);
+  REQUIRE_FALSE(test_controller.getLog().getInstance().contains("Error reading from file", std::chrono::seconds(0)));
+  stream.seek(0);
+  REQUIRE(stream.read(nullptr, 11) == -1);
+  REQUIRE(test_controller.getLog().getInstance().contains("Error reading from file: invalid buffer", std::chrono::seconds(0)));
+}
+
+#ifdef USE_BOOST
+TEST_CASE("Opening file without permission creates error logs") {
+  TestController test_controller;
+  char format[] = "/tmp/gt.XXXXXX";
+  auto dir = test_controller.createTempDirectory(format);
+  std::string path_to_permissionless_file(utils::file::concat_path(dir, "permissionless_file.txt"));
+  {
+    std::ofstream outfile(path_to_permissionless_file);
+    outfile << "this file has been just created" << std::endl;
+    outfile.close();
+    // This could be done with C++17 std::filesystem
+    boost::filesystem::permissions(path_to_permissionless_file, boost::filesystem::no_perms);

Review comment:
       We could use `set_permissions` functions from `FileUtils.h`, but then the test needs to be undefined for WIN32.

##########
File path: libminifi/src/io/FileStream.cpp
##########
@@ -31,21 +31,41 @@ namespace nifi {
 namespace minifi {
 namespace io {
 
+constexpr const char *file_opening_error_msg = "Error opening file: ";
+constexpr const char *read_error_msg = "Error reading from file: ";
+constexpr const char *write_error_msg = "Error writing to file: ";
+constexpr const char *seek_error = "Error seeking in file: ";
+constexpr const char *invalid_file_stream_error_msg = "invalid file stream";
+constexpr const char *tellg_call_error_msg = "tellg call on file stream failed";
+constexpr const char *invalid_buffer_error_msg = "invalid buffer";
+constexpr const char *flush_call_error_msg = "flush call on file stream failed";
+constexpr const char *write_call_error_msg = "write call on file stream failed";
+constexpr const char *empty_message_error_msg = "empty message";
+constexpr const char *seekg_call_error_msg = "seekg call on file stream failed";
+constexpr const char *seekp_call_error_msg = "seekp call on file stream failed";

Review comment:
       I could not find it in the guideline, but we usually have a FULL_CAPS format for constant variables.

##########
File path: libminifi/src/io/FileStream.cpp
##########
@@ -77,10 +101,16 @@ void FileStream::close() {
 
 void FileStream::seek(uint64_t offset) {
   std::lock_guard<std::mutex> lock(file_lock_);
+  if (file_stream_ == nullptr || !file_stream_->is_open()) {
+    logging::LOG_ERROR(logger_) << seek_error << invalid_file_stream_error_msg;
+    return;
+  }
   offset_ = gsl::narrow<size_t>(offset);
   file_stream_->clear();
-  file_stream_->seekg(offset_);
-  file_stream_->seekp(offset_);
+  if (!file_stream_->seekg(offset_))
+    logging::LOG_ERROR(logger_) << seek_error << seekg_call_error_msg;

Review comment:
       Should we return here if it fails?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org