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