1 #include "file.h"
2
3 #include <errno.h>
4 #include <stdlib.h>
5 #include "log.h"
6
7 static int
file_get(char const * file_name,FILE ** result,struct stat * stat,char const * mode)8 file_get(char const *file_name, FILE **result, struct stat *stat,
9 char const *mode)
10 {
11 FILE *file;
12 int error;
13
14 file = fopen(file_name, mode);
15 if (file == NULL)
16 return pr_val_errno(errno, "Could not open file '%s'", file_name);
17
18 if (fstat(fileno(file), stat) == -1) {
19 error = pr_val_errno(errno, "fstat(%s) failed", file_name);
20 goto fail;
21 }
22 if (!S_ISREG(stat->st_mode)) {
23 error = pr_op_err("%s does not seem to be a file", file_name);
24 goto fail;
25 }
26
27 *result = file;
28 return 0;
29
30 fail:
31 file_close(file);
32 return error;
33 }
34
35 int
file_open(char const * file_name,FILE ** result,struct stat * stat)36 file_open(char const *file_name, FILE **result, struct stat *stat)
37 {
38 return file_get(file_name, result, stat, "rb");
39 }
40
41 int
file_write(char const * file_name,FILE ** result)42 file_write(char const *file_name, FILE **result)
43 {
44 struct stat stat;
45 return file_get(file_name, result, &stat, "wb");
46 }
47
48 void
file_close(FILE * file)49 file_close(FILE *file)
50 {
51 if (fclose(file) == -1)
52 pr_val_errno(errno, "fclose() failed");
53 }
54
55 int
file_load(char const * file_name,struct file_contents * fc)56 file_load(char const *file_name, struct file_contents *fc)
57 {
58 FILE *file;
59 struct stat stat;
60 size_t fread_result;
61 int error;
62
63 error = file_open(file_name, &file, &stat);
64 if (error)
65 return error;
66
67 fc->buffer_size = stat.st_size;
68 fc->buffer = malloc(fc->buffer_size);
69 if (fc->buffer == NULL) {
70 error = pr_enomem();
71 goto end;
72 }
73
74 fread_result = fread(fc->buffer, 1, fc->buffer_size, file);
75 if (fread_result < fc->buffer_size) {
76 error = ferror(file);
77 if (error) {
78 /*
79 * The manpage doesn't say that the result is an error
80 * code. It literally doesn't say how to get an error
81 * code.
82 */
83 pr_val_errno(error,
84 "File reading error. Error message (apparently)");
85 free(fc->buffer);
86 goto end;
87 }
88
89 /*
90 * As far as I can tell from the man page, fread() cannot return
91 * less bytes than requested like read() does. It's either
92 * "consumed everything", "EOF reached" or error.
93 */
94 pr_op_err("Likely programming error: fread() < file size");
95 pr_op_err("fr:%zu bs:%zu EOF:%d", fread_result, fc->buffer_size,
96 feof(file));
97 free(fc->buffer);
98 error = -EINVAL;
99 goto end;
100 }
101
102 error = 0;
103
104 end:
105 file_close(file);
106 return error;
107 }
108
109 void
file_free(struct file_contents * fc)110 file_free(struct file_contents *fc)
111 {
112 free(fc->buffer);
113 }
114
115 /*
116 * Validate @file_name, if it doesn't exist, this function will create it and
117 * close it.
118 */
119 bool
file_valid(char const * file_name)120 file_valid(char const *file_name)
121 {
122 FILE *tmp;
123 int error;
124
125 if (file_name == NULL)
126 return false;
127
128 error = file_write(file_name, &tmp);
129 if (error)
130 return false;
131
132 file_close(tmp);
133 return true;
134 }
135