From 7bdff7f55c801d09834f0487b377c8408fd162b9 Mon Sep 17 00:00:00 2001
From: Jakub Filak <jfilak@redhat.com>
Date: Fri, 29 May 2015 15:08:56 +0200
Subject: [PATCH] dd: add a function parsing number from fd
We often need to parse a number from a plain text file.
Related: #354
Signed-off-by: Jakub Filak <jfilak@redhat.com>
---
src/lib/dump_dir.c | 120 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 72 insertions(+), 48 deletions(-)
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
index aed2de0..9ba2352 100644
--- a/src/lib/dump_dir.c
+++ b/src/lib/dump_dir.c
@@ -151,15 +151,14 @@ int secure_openat_read(int dir_fd, const char *filename)
if (strchr(filename, '/'))
{
error_msg("Path must be file name without directory: '%s'", filename);
- errno = EFAULT;
- return -1;
+ return -EFAULT;
}
static char reopen_buf[sizeof("/proc/self/fd/") + 3*sizeof(int) + 1];
int path_fd = openat(dir_fd, filename, O_PATH | O_NOFOLLOW);
if (path_fd < 0)
- return -1;
+ return -errno;
struct stat path_sb;
int r = fstat(path_fd, &path_sb);
@@ -167,15 +166,14 @@ int secure_openat_read(int dir_fd, const char *filename)
{
perror_msg("stat");
close(path_fd);
- return -1;
+ return -EINVAL;
}
if (!S_ISREG(path_sb.st_mode) || path_sb.st_nlink > 1)
{
log_notice("Path isn't a regular file or has more links (%lu)", (unsigned long)path_sb.st_nlink);
- errno = EINVAL;
close(path_fd);
- return -1;
+ return -EINVAL;
}
if (snprintf(reopen_buf, sizeof(reopen_buf), "/proc/self/fd/%d", path_fd) >= sizeof(reopen_buf)) {
@@ -189,71 +187,97 @@ int secure_openat_read(int dir_fd, const char *filename)
return fd;
}
-/* Returns value less than 0 if the file is not readable or
- * if the file doesn't contain valid unixt time stamp.
- *
- * Any possible failure will be logged.
- */
-static time_t parse_time_file_at(int dir_fd, const char *filename)
+static int read_number_from_file_at(int dir_fd, const char *filename, const char *typename,
+ size_t typesz, long long max, long long min, long long *value)
{
- /* Open input file, and parse it. */
- int fd = secure_openat_read(dir_fd, filename);
+ const int fd = secure_openat_read(dir_fd, filename);
if (fd < 0)
{
- VERB2 pwarn_msg("Can't open '%s'", filename);
- return -1;
+ log_warning("Can't open '%s'", filename);
+ return fd;
}
- /* ~ maximal number of digits for positive time stamp string */
- char time_buf[sizeof(time_t) * 3 + 1];
- ssize_t rdsz = read(fd, time_buf, sizeof(time_buf));
-
+ int ret = 0;
+ /* - xmalloc_read() does not count '\0' Byte
+ * - count on sign
+ * - count on '\n'
+ */
+ const size_t max_size = typesz * 3 + 2;
+ /* allow to read one extra Byte to be able to identify longer text */
+ size_t total_read = max_size + 1;
+ char *const value_buf = xmalloc_read(fd, &total_read);
/* Just reading, so no need to check the returned value. */
- close(fd);
- if (rdsz == -1)
+ if (value_buf == NULL)
{
- VERB2 pwarn_msg("Can't read from '%s'", filename);
- return -1;
+ log_warning("Can't read from '%s'", filename);
+ ret = -EBADFD;
+ goto finito;
}
- /* approximate maximal number of digits in timestamp is sizeof(time_t)*3 */
- /* buffer has this size + 1 byte for trailing '\0' */
- /* if whole size of buffer was read then file is bigger */
- /* than string representing maximal time stamp */
- if (rdsz == sizeof(time_buf))
+
+ if (total_read >= max_size)
{
- VERB2 warn_msg("File '%s' is too long to be valid unix "
- "time stamp (max size %u)", filename, (int)sizeof(time_buf));
- return -1;
+ log_warning("File '%s' is too long to be valid %s "
+ "(max size %u)", filename, typename, (int)sizeof(value_buf));
+ ret = -EMSGSIZE;
+ goto finito;
}
- /* Our tools don't put trailing newline into time file,
+ /* Our tools don't put trailing newline into one line files,
* but we allow such format too:
*/
- if (rdsz > 0 && time_buf[rdsz - 1] == '\n')
- rdsz--;
- time_buf[rdsz] = '\0';
-
- /* Note that on some architectures (x32) time_t is "long long" */
+ if (value_buf[total_read - 1] == '\n')
+ --total_read;
+ value_buf[total_read] = '\0';
errno = 0; /* To distinguish success/failure after call */
char *endptr;
- long long val = strtoll(time_buf, &endptr, /* base */ 10);
- const long long MAX_TIME_T = (1ULL << (sizeof(time_t)*8 - 1)) - 1;
+ long long res = strtoll(value_buf, &endptr, /* base */ 10);
/* Check for various possible errors */
- if (errno
- || (*endptr != '\0')
- || val >= MAX_TIME_T
- || !isdigit_str(time_buf) /* this filters out "-num", " num", "" */
+ if ( (errno == ERANGE && (res == LONG_LONG_MAX || res == LONG_LONG_MIN))
+ || (errno != 0 && res == 0)
+ || (*endptr != '\0')
+ || endptr == value_buf
) {
- VERB2 pwarn_msg("File '%s' doesn't contain valid unix "
- "time stamp ('%s')", filename, time_buf);
- return -1;
+ log_warning("File '%s' doesn't contain valid %s"
+ "('%s')", filename, typename, value_buf);
+ ret = -EINVAL;
+ goto finito;
}
+ /* If the stored number is long long, this condition falsely indetify
+ * LONG_LONG_(MAX|MIN) as an value which is out of range.
+ */
+ if (res <= min || res >= max)
+ {
+ log_warning("File '%s' contains a number out-of-range of %s"
+ "('%s')", filename, typename, value_buf);
+ ret = -ERANGE;
+ goto finito;
+ }
+
+ *value = res;
+
+finito:
+ close(fd);
+ free(value_buf);
/* If we got here, strtoll() successfully parsed a number */
- return val;
+ return ret;
+}
+
+/* Returns value less than 0 if the file is not readable or
+ * if the file doesn't contain valid unixt time stamp.
+ *
+ * Any possible failure will be logged.
+ */
+static time_t parse_time_file_at(int dir_fd, const char *filename)
+{
+ /* Note that on some architectures (x32) time_t is "long long" */
+ const long long MAX_TIME_T = (1ULL << (sizeof(time_t)*8 - 1)) - 1;
+ long long value = -1;
+ read_number_from_file_at(dir_fd, filename, "unix time stamp", sizeof(time_t), MAX_TIME_T, 0, &value);
+ return (time_t)value;
}
/* Return values:
--
2.1.0