]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
util: add functions for common file operations
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 22 Oct 2019 16:06:15 +0000 (18:06 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 24 Oct 2019 10:48:45 +0000 (12:48 +0200)
Add a function to open a file for reading, writing, or appending.
In uppercase modes errors are handled as fatal, i.e. the caller doesn't
need to check for NULL. To avoid string manipulations in the callers,
the function accepts an optional directory and suffix. New files are
created with specified permissions, which will be needed for saving
keys. The O_EXCL flag is used in the writing mode to make sure a new
file is created (on filesystems that support it).

Also, add a function to rename a temporary file by changing its suffix,
and a function to remove a file.

All functions log all errors, at least as debug messages.

util.c
util.h

diff --git a/util.c b/util.c
index 5dd1b114f2722ddda9281a5827f86e57eff17e7f..92b4a721f819dc99bce34f1c4e11e57dcdee4265 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1089,6 +1089,152 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
 
 /* ================================================== */
 
+static int
+join_path(const char *basedir, const char *name, const char *suffix,
+          char *buffer, size_t length, LOG_Severity severity)
+{
+  const char *sep;
+
+  if (!basedir) {
+    basedir = "";
+    sep = "";
+  } else {
+    sep = "/";
+  }
+
+  if (!suffix)
+    suffix = "";
+
+  if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
+    LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+FILE *
+UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
+             char mode, mode_t perm)
+{
+  const char *file_mode;
+  char path[PATH_MAX];
+  LOG_Severity severity;
+  int fd, flags;
+  FILE *file;
+
+  severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
+
+  if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
+    return NULL;
+
+  switch (mode) {
+    case 'r':
+    case 'R':
+      flags = O_RDONLY;
+      file_mode = "r";
+      if (severity != LOGS_FATAL)
+        severity = LOGS_DEBUG;
+      break;
+    case 'w':
+    case 'W':
+      flags = O_WRONLY | O_CREAT | O_EXCL;
+      file_mode = "w";
+      break;
+    case 'a':
+    case 'A':
+      flags = O_WRONLY | O_CREAT | O_APPEND;
+      file_mode = "a";
+      break;
+    default:
+      assert(0);
+      return NULL;
+  }
+
+try_again:
+  fd = open(path, flags, perm);
+  if (fd < 0) {
+    if (errno == EEXIST) {
+      if (unlink(path) < 0) {
+        LOG(severity, "Could not remove %s : %s", path, strerror(errno));
+        return NULL;
+      }
+      DEBUG_LOG("Removed %s", path);
+      goto try_again;
+    }
+    LOG(severity, "Could not open %s : %s", path, strerror(errno));
+    return NULL;
+  }
+
+  UTI_FdSetCloexec(fd);
+
+  file = fdopen(fd, file_mode);
+  if (!file) {
+    LOG(severity, "Could not open %s : %s", path, strerror(errno));
+    close(fd);
+    return NULL;
+  }
+
+  DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
+
+  return file;
+}
+
+/* ================================================== */
+
+int
+UTI_RenameTempFile(const char *basedir, const char *name,
+                   const char *old_suffix, const char *new_suffix)
+{
+  char old_path[PATH_MAX], new_path[PATH_MAX];
+
+  if (!join_path(basedir, name, old_suffix, old_path, sizeof (old_path), LOGS_ERR))
+    return 0;
+
+  if (!join_path(basedir, name, new_suffix, new_path, sizeof (new_path), LOGS_ERR))
+    goto error;
+
+  if (rename(old_path, new_path) < 0) {
+    LOG(LOGS_ERR, "Could not replace %s with %s : %s", new_path, old_path, strerror(errno));
+    goto error;
+  }
+
+  DEBUG_LOG("Renamed %s to %s", old_path, new_path);
+
+  return 1;
+
+error:
+  if (unlink(old_path) < 0)
+    LOG(LOGS_ERR, "Could not remove %s : %s", old_path, strerror(errno));
+
+  return 0;
+}
+
+/* ================================================== */
+
+int
+UTI_RemoveFile(const char *basedir, const char *name, const char *suffix)
+{
+  char path[PATH_MAX];
+
+  if (!join_path(basedir, name, suffix, path, sizeof (path), LOGS_ERR))
+    return 0;
+
+  if (unlink(path) < 0) {
+    LOG(errno != ENOENT ? LOGS_ERR : LOGS_DEBUG,
+        "Could not remove %s : %s", path, strerror(errno));
+    return 0;
+  }
+
+  DEBUG_LOG("Removed %s", path);
+
+  return 1;
+}
+
+/* ================================================== */
+
 void
 UTI_DropRoot(uid_t uid, gid_t gid)
 {
diff --git a/util.h b/util.h
index 498fa7944a74550db69c699a76fb9e66c5ff5ba6..39d6cd75cc7aef1f330e76034685aa6ea4f7c0fc 100644 (file)
--- a/util.h
+++ b/util.h
@@ -173,6 +173,25 @@ extern int UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid
    permissions and its uid/gid must match the specified values. */
 extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
 
+/* Open a file.  The full path of the file is constructed from the basedir
+   (may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL).
+   Created files have specified permissions (umasked).  Returns NULL on error.
+   The following modes are supported (if the mode is an uppercase character,
+   errors are fatal):
+   r/R - open an existing file for reading
+   w/W - open a new file for writing (remove existing file)
+   a/A - open an existing file for appending (create if does not exist) */
+extern FILE *UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
+                          char mode, mode_t perm);
+
+/* Rename a temporary file by changing its suffix.  The paths are constructed as
+   in UTI_OpenFile().  If the renaming fails, the file will be removed. */
+extern int UTI_RenameTempFile(const char *basedir, const char *name,
+                              const char *old_suffix, const char *new_suffix);
+
+/* Remove a file.  The path is constructed as in UTI_OpenFile(). */
+extern int UTI_RemoveFile(const char *basedir, const char *name, const char *suffix);
+
 /* Set process user/group IDs and drop supplementary groups */
 extern void UTI_DropRoot(uid_t uid, gid_t gid);