Add classes and functions for accessing files in artd.

This change includes:
- An AIDL representation of Linux filesystem permission and a function
  to convert it to Linux access mode.
- A class that creates a new file for writing and cleans it up unless
  the file is committed.
- A function that opens a file for reading.

Bug: 229268202
Test: m test-art-host-gtest-art_artd_tests
Ignore-AOSP-First: ART Services
Change-Id: I0e24e8fc31eee5e7004a35649df610b7da4d3178
diff --git a/artd/file_utils.h b/artd/file_utils.h
new file mode 100644
index 0000000..9dc41f4
--- /dev/null
+++ b/artd/file_utils.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_ARTD_FILE_UTILS_H_
+#define ART_ARTD_FILE_UTILS_H_
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "aidl/com/android/server/art/FsPermission.h"
+#include "android-base/result.h"
+#include "base/os.h"
+
+namespace art {
+namespace artd {
+
+// A class that creates a new file that will eventually be committed to the given path. The new file
+// is created at a temporary location. It will not overwrite the file at the given path until
+// `CommitOrAbandon` has been called and will be automatically cleaned up on object destruction
+// unless `CommitOrAbandon` has been called.
+// The new file is opened without O_CLOEXEC so that it can be passed to subprocesses.
+class NewFile {
+ public:
+  // Creates a new file at the given path with the given permission.
+  static android::base::Result<std::unique_ptr<NewFile>> Create(
+      const std::string& path, const aidl::com::android::server::art::FsPermission& fs_permission);
+
+  NewFile(const NewFile&) = delete;
+  NewFile& operator=(const NewFile&) = delete;
+  NewFile(NewFile&& other) noexcept
+      : fd_(std::exchange(other.fd_, -1)),
+        final_path_(std::move(other.final_path_)),
+        temp_path_(std::move(other.temp_path_)),
+        temp_id_(std::move(other.temp_id_)),
+        fs_permission_(other.fs_permission_) {}
+
+  // Deletes the file if it is not committed.
+  virtual ~NewFile();
+
+  int Fd() const { return fd_; }
+
+  // The path that the file will eventually be committed to.
+  const std::string& FinalPath() const { return final_path_; }
+
+  // The path to the new file.
+  const std::string& TempPath() const { return temp_path_; }
+
+  // The unique ID of the new file. Can be used by `BuildTempPath` for reconstructing the path to
+  // the file.
+  const std::string& TempId() const { return temp_id_; }
+
+  // Closes the new file, keeps it, moves the file to the final path, and overwrites any existing
+  // file at that path, or abandons the file on failure. The fd will be invalid after this function
+  // is called.
+  android::base::Result<void> CommitOrAbandon();
+
+  // Closes the new file and keeps it at the temporary location. The file will not be automatically
+  // cleaned up on object destruction. The file can be found at `TempPath()` (i.e.,
+  // `BuildTempPath(FinalPath(), TempId())`). The fd will be invalid after this function is called.
+  virtual android::base::Result<void> Keep();
+
+  // Unlinks and closes the new file if it is not committed. The fd will be invalid after this
+  // function is called.
+  void Cleanup();
+
+  // Commits all new files, replacing old files, and removes given files in addition. Or abandons
+  // new files and restores old files at best effort if any error occurs. The fds will be invalid
+  // after this function is called.
+  //
+  // Note: This function is NOT thread-safe. It is intended to be used in single-threaded code or in
+  // cases where some race condition is acceptable.
+  //
+  // Usage:
+  //
+  // Commit `file_1` and `file_2`, and remove the file at "path_3":
+  //   CommitAllOrAbandon({file_1, file_2}, {"path_3"});
+  static android::base::Result<void> CommitAllOrAbandon(
+      const std::vector<NewFile*>& files_to_commit,
+      const std::vector<std::string_view>& files_to_remove = {});
+
+  // Returns the path to a temporary file. See `Keep`.
+  static std::string BuildTempPath(std::string_view final_path, const std::string& id);
+
+ private:
+  NewFile(const std::string& path,
+          const aidl::com::android::server::art::FsPermission& fs_permission)
+      : final_path_(path), fs_permission_(fs_permission) {}
+
+  android::base::Result<void> Init();
+
+  // Unlinks the new file. The fd will still be valid after this function is called.
+  void Unlink();
+
+  int fd_ = -1;
+  std::string final_path_;
+  std::string temp_path_;
+  std::string temp_id_;
+  aidl::com::android::server::art::FsPermission fs_permission_;
+  bool committed_ = false;
+};
+
+// Opens a file for reading.
+android::base::Result<std::unique_ptr<File>> OpenFileForReading(const std::string& path);
+
+// Converts FsPermission to Linux access mode.
+mode_t FsPermissionToMode(const aidl::com::android::server::art::FsPermission& fs_permission);
+
+}  // namespace artd
+}  // namespace art
+
+#endif  // ART_ARTD_FILE_UTILS_H_