os_file.c revision 8a1362ad
1/*
2 * Copyright 2019 Intel Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6#include "os_file.h"
7
8#include <errno.h>
9#include <stdlib.h>
10
11#if defined(__linux__)
12
13#include <fcntl.h>
14#include <sys/stat.h>
15#include <unistd.h>
16
17
18static ssize_t
19readN(int fd, char *buf, size_t len)
20{
21   int err = -ENODATA;
22   size_t total = 0;
23   do {
24      ssize_t ret = read(fd, buf + total, len - total);
25
26      if (ret < 0)
27         ret = -errno;
28
29      if (ret == -EINTR || ret == -EAGAIN)
30         continue;
31
32      if (ret <= 0) {
33         err = ret;
34         break;
35      }
36
37      total += ret;
38   } while (total != len);
39
40   return total ? total : err;
41}
42
43char *
44os_read_file(const char *filename)
45{
46   /* Note that this also serves as a slight margin to avoid a 2x grow when
47    * the file is just a few bytes larger when we read it than when we
48    * fstat'ed it.
49    * The string's NULL terminator is also included in here.
50    */
51   size_t len = 64;
52
53   int fd = open(filename, O_RDONLY);
54   if (fd == -1) {
55      /* errno set by open() */
56      return NULL;
57   }
58
59   /* Pre-allocate a buffer at least the size of the file if we can read
60    * that information.
61    */
62   struct stat stat;
63   if (fstat(fd, &stat) == 0)
64      len += stat.st_size;
65
66   char *buf = malloc(len);
67   if (!buf) {
68      close(fd);
69      errno = -ENOMEM;
70      return NULL;
71   }
72
73   ssize_t read;
74   size_t offset = 0, remaining = len - 1;
75   while ((read = readN(fd, buf + offset, remaining)) == remaining) {
76      char *newbuf = realloc(buf, 2 * len);
77      if (!newbuf) {
78         free(buf);
79         close(fd);
80         errno = -ENOMEM;
81         return NULL;
82      }
83
84      buf = newbuf;
85      len *= 2;
86      offset += read;
87      remaining = len - offset - 1;
88   }
89
90   close(fd);
91
92   if (read > 0)
93      offset += read;
94
95   /* Final resize to actual size */
96   len = offset + 1;
97   char *newbuf = realloc(buf, len);
98   if (!newbuf) {
99      free(buf);
100      errno = -ENOMEM;
101      return NULL;
102   }
103   buf = newbuf;
104
105   buf[offset] = '\0';
106
107   return buf;
108}
109
110#else
111
112char *
113os_read_file(const char *filename)
114{
115   errno = -ENOSYS;
116   return NULL;
117}
118
119#endif
120