util/os_file: actually return the error read() gave us
[mesa.git] / src / util / os_file.c
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
18 static ssize_t
19 readN(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
43 char *
44 os_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 buf[offset] = '\0';
96
97 return buf;
98 }
99
100 #else
101
102 char *
103 os_read_file(const char *filename)
104 {
105 errno = -ENOSYS;
106 return NULL;
107 }
108
109 #endif