Cthulhu  0.2.10
Cthulhu compiler collection
file.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "os/os.h"
4 #include "os_common.h"
5 
6 #include "core/compiler.h"
7 #include "base/panic.h"
8 
9 #include <stdint.h>
10 
11 #if CT_CC_CLANG
12 # pragma clang diagnostic push
13 # pragma clang diagnostic ignored "-Wswitch"
14 #endif
15 
16 CT_LOCAL os_error_t impl_copyfile(const char *dst, const char *src)
17 {
18  CTASSERT(dst != NULL);
19  CTASSERT(src != NULL);
20 
21  BOOL result = CopyFileA(src, dst, FALSE);
22  if (!result)
23  {
24  return GetLastError();
25  }
26 
27  return ERROR_SUCCESS;
28 }
29 
30 static DWORD get_access(os_access_t access)
31 {
32  DWORD result = 0;
33  if (access & eOsAccessRead) { result |= GENERIC_READ; }
34  if (access & eOsAccessWrite) { result |= GENERIC_WRITE; }
35  return result;
36 }
37 
39 os_error_t os_file_exists(const char *path)
40 {
41  CTASSERT(path != NULL);
42 
43  DWORD attr = GetFileAttributesA(path);
44  if (attr == INVALID_FILE_ATTRIBUTES)
45  {
46  DWORD error = GetLastError();
47 
48  // remap file and path not found to not found
49  // TODO: do we want to handle these errors differently?
50  return (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND)
51  ? eOsNotFound
52  : error;
53  }
54 
55  return eOsExists;
56 }
57 
58 static DWORD get_disp(os_access_t access)
59 {
60  switch (access)
61  {
62  case eOsAccessRead:
63  return OPEN_EXISTING;
64 
65  case eOsAccessWrite:
66  case (eOsAccessWrite | eOsAccessRead):
67  return OPEN_ALWAYS;
68 
69  case (eOsAccessWrite | eOsAccessTruncate):
70  case (eOsAccessWrite | eOsAccessRead | eOsAccessTruncate):
71  return CREATE_ALWAYS;
72 
73  default:
74  CT_NEVER("invalid access flags %s", os_access_string(access));
75  }
76 }
77 
79 {
80  DWORD dw_access = get_access(access);
81  DWORD dw_disp = get_disp(access);
82 
83  return CreateFileA(
84  /* lpFileName = */ path,
85  /* dwDesiredAccess = */ dw_access,
86  /* dwShareMode = */ FILE_SHARE_READ,
87  /* lpSecurityAttributes = */ NULL,
88  /* dwCreationDisposition = */ dw_disp,
89  /* dwFlagsAndAttributes = */ FILE_ATTRIBUTE_NORMAL,
90  /* hTemplateFile = */ NULL);
91 }
92 
94 os_error_t os_tmpfile_open(os_file_t *file)
95 {
96  CTASSERT(file != NULL);
97 
98  char path[MAX_PATH];
99  DWORD result = GetTempPathA(MAX_PATH, path);
100  if (result == 0)
101  {
102  return GetLastError();
103  }
104 
105  char name[MAX_PATH];
106  result = GetTempFileNameA(path, "ctu", 0, name);
107  if (result == 0)
108  {
109  return GetLastError();
110  }
111 
112  return os_file_open(name, eOsAccessWrite, file);
113 }
114 
116 {
117  return CloseHandle(impl) != 0;
118 }
119 
120 STA_DECL
121 os_error_t os_file_read(os_file_t *file, void *buffer, size_t size, size_t *actual)
122 {
123  CTASSERT(file != NULL);
124  CTASSERT(buffer != NULL);
125  CTASSERT(actual != NULL);
126  CTASSERTF(size <= UINT32_MAX, "cannot read more than %u bytes at once (%zu is too big)", UINT32_MAX, size);
127 
128  DWORD read_size = 0;
129  BOOL result = ReadFile(file->impl, buffer, (DWORD)size, &read_size, NULL);
130 
131  size_t read = read_size;
132 
133  if (!result)
134  {
135  return GetLastError();
136  }
137 
138  *actual = read;
139  return 0;
140 }
141 
142 STA_DECL
143 os_error_t os_file_write(os_file_t *file, const void *buffer, size_t size, size_t *actual)
144 {
145  CTASSERT(file != NULL);
146  CTASSERT(buffer != NULL);
147  CTASSERT(actual != NULL);
148  CTASSERTF(size <= UINT32_MAX, "cannot write more than %u bytes at once (%zu is too big)", UINT32_MAX, size);
149 
150  DWORD written_size = 0;
151  BOOL result = WriteFile(file->impl, buffer, (DWORD)size, &written_size, NULL);
152 
153  size_t written = written_size;
154 
155  if (!result)
156  {
157  return GetLastError();
158  }
159 
160  *actual = written;
161  return ERROR_SUCCESS;
162 }
163 
164 STA_DECL
165 os_error_t os_file_size(os_file_t *file, size_t *actual)
166 {
167  CTASSERT(file != NULL);
168  CTASSERT(actual != NULL);
169 
170  LARGE_INTEGER size;
171  BOOL result = GetFileSizeEx(file->impl, &size);
172  *actual = size.QuadPart;
173 
174  if (!result)
175  {
176  return GetLastError();
177  }
178 
179  return ERROR_SUCCESS;
180 }
181 
182 STA_DECL
183 os_error_t os_file_seek(os_file_t *file, size_t offset, size_t *actual)
184 {
185  CTASSERT(file != NULL);
186  CTASSERT(actual != NULL);
187 
188  LARGE_INTEGER it = { .QuadPart = (LONGLONG)offset };
189  LARGE_INTEGER out = { 0 };
190  BOOL result = SetFilePointerEx(file->impl, it, &out, FILE_BEGIN);
191  *actual = out.QuadPart;
192 
193  if (!result)
194  {
195  return GetLastError();
196  }
197 
198  return ERROR_SUCCESS;
199 }
200 
201 STA_DECL
202 os_error_t os_file_tell(os_file_t *file, size_t *actual)
203 {
204  CTASSERT(file != NULL);
205 
206  LARGE_INTEGER offset = { 0 };
207  LARGE_INTEGER zero = { 0 };
208  BOOL result = SetFilePointerEx(file->impl, zero, &offset, FILE_CURRENT);
209  *actual = offset.QuadPart;
210 
211  if (!result)
212  {
213  return GetLastError();
214  }
215 
216  return ERROR_SUCCESS;
217 }
218 
219 STA_DECL
220 os_error_t os_file_resize(os_file_t *file, size_t size)
221 {
222  CTASSERT(file != NULL);
223 
224  LARGE_INTEGER it = { .QuadPart = (LONGLONG)size };
225  BOOL result = SetFilePointerEx(file->impl, it, NULL, FILE_BEGIN);
226  if (!result)
227  {
228  return GetLastError();
229  }
230 
231  result = SetEndOfFile(file->impl);
232  if (!result)
233  {
234  return GetLastError();
235  }
236 
237  return ERROR_SUCCESS;
238 }
239 
240 static DWORD get_protect(os_protect_t protect)
241 {
242  // windows PAGE_ macros dont follow a bit pattern
243  // so we end up with a pretty big pile of conditionals
244  switch (protect)
245  {
246  case eOsProtectNone: return PAGE_NOACCESS; // none maps to noaccess
247  case eOsProtectRead: return PAGE_READONLY; // readonly
248  case eOsProtectExecute: return PAGE_EXECUTE; // execute only
249 
250  // we map both write and readwrite to PAGE_READWRITE
251  // i want to avoid copyonwrite
252  case eOsProtectWrite:
253  case (eOsProtectRead | eOsProtectWrite):
254  return PAGE_READWRITE;
255 
256  case (eOsProtectRead | eOsProtectExecute):
257  return PAGE_EXECUTE_READ;
258 
259  // wx and rwx are mapped to PAGE_EXECUTE_READWRITE
260  // again, avoiding copyonwrite
261  case (eOsProtectWrite | eOsProtectExecute):
262  case (eOsProtectRead | eOsProtectWrite | eOsProtectExecute):
263  return PAGE_EXECUTE_READWRITE;
264 
265  default:
266  CT_NEVER("unknown protect %s", os_protect_string(protect));
267  }
268 }
269 
270 static DWORD get_map_access(os_protect_t protect)
271 {
272  // TODO: FILE_MAP_LARGE_PAGES would be fun to play with here for large files
273 
274  DWORD result = 0;
275  if (protect & eOsProtectRead) { result |= FILE_MAP_READ; }
276  if (protect & eOsProtectWrite) { result |= FILE_MAP_WRITE; }
277  if (protect & eOsProtectExecute) { result |= FILE_MAP_EXECUTE; }
278  return result;
279 }
280 
281 CT_LOCAL void *impl_file_map(os_file_t *file, os_protect_t protect, size_t size, os_mapping_t *mapping)
282 {
283  DWORD prot = get_protect(protect);
284  DWORD access = get_map_access(protect);
285 
286  DWORD high = (DWORD)(size >> 32);
287  DWORD low = (DWORD)(size & 0xFFFFFFFF);
288 
289  HANDLE handle = CreateFileMappingA(
290  /* hFile = */ file->impl,
291  /* lpFileMappingAttributes = */ NULL,
292  /* flProtect = */ prot,
293  /* dwMaximumSizeHigh = */ high,
294  /* dwMaximumSizeLow = */ low,
295  /* lpName = */ NULL);
296 
297  if (handle == NULL)
298  {
299  return NULL;
300  }
301 
302  LPVOID view = MapViewOfFile(
303  /* hFileMappingObject = */ handle,
304  /* dwDesiredAccess = */ access,
305  /* dwFileOffsetHigh = */ 0,
306  /* dwFileOffsetLow = */ 0,
307  /* dwNumberOfBytesToMap = */ 0); // view the whole mapping
308 
309  if (view == NULL)
310  {
311  CloseHandle(handle);
312  return NULL;
313  }
314 
315  mapping->handle = handle;
316 
317  return view;
318 }
319 
321 {
322  if (!UnmapViewOfFile(map->view))
323  {
324  return GetLastError();
325  }
326 
327  if (!CloseHandle(map->handle))
328  {
329  return GetLastError();
330  }
331 
332  return ERROR_SUCCESS;
333 }
CT_NODISCARD size_t size
Definition: scan.h:128
CT_NODISCARD CT_CONSTFN STA_RET_STRING CT_OS_API const char * os_protect_string(os_protect_t protect)
get the string representation of a file mapping memory protection
Definition: os_common.c:50
os_protect_t
file mapping memory protection
Definition: core.h:47
CT_NODISCARD CT_CONSTFN STA_RET_STRING CT_OS_API const char * os_access_string(os_access_t access)
get the string representation of a file access mode
Definition: os_common.c:32
os_access_t
file access mode
Definition: core.h:38
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
#define CT_LOCAL
Definition: compiler.h:166
#define CT_NEVER(...)
assert that a code path is never reached
Definition: panic.h:136
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
#define CTASSERTF(expr,...)
assert a condition with a message and optional format arguments
Definition: panic.h:116
STA_DECL os_error_t os_file_resize(os_file_t *file, size_t size)
truncate/expand a file to a specific size
Definition: file.c:143
CT_LOCAL os_error_t impl_unmap(os_mapping_t *map)
Definition: file.c:230
STA_DECL os_error_t os_file_read(os_file_t *file, void *buffer, size_t size, size_t *actual)
Definition: file.c:82
STA_DECL os_error_t os_file_seek(os_file_t *file, size_t offset, size_t *actual)
Definition: file.c:165
STA_DECL os_error_t os_file_write(os_file_t *file, const void *buffer, size_t size, size_t *actual)
Definition: file.c:104
STA_DECL os_error_t os_file_size(os_file_t *file, size_t *actual)
Definition: file.c:126
STA_DECL os_error_t os_tmpfile_open(os_file_t *file)
Definition: file.c:56
STA_DECL os_error_t os_file_exists(const char *path)
check if a file exists
Definition: file.c:38
STA_DECL os_error_t os_file_tell(os_file_t *file, size_t *actual)
Definition: file.c:182
CT_LOCAL bool impl_file_close(os_file_impl_t file)
Definition: file.c:76
CT_LOCAL void * impl_file_map(os_file_t *file, os_protect_t protect, size_t size, os_mapping_t *mapping)
Definition: file.c:220
CT_LOCAL os_file_impl_t impl_file_open(const char *path, os_access_t access)
Definition: file.c:50
CT_LOCAL os_error_t impl_copyfile(const char *dst, const char *src)
Definition: file.c:16
CT_OS_API os_error_t os_file_open(const char *path, os_access_t access, OUT_NOTNULL os_file_t *file)
file api
@ eOsNotFound
Definition: posix.h:25
@ eOsExists
Definition: posix.h:26
FILE * os_file_impl_t
Definition: posix.h:11
a file handle
Definition: os.h:40
os_file_impl_t impl
Definition: os.h:45
memory mapping handle
Definition: posix.h:18
HANDLE handle
Definition: win32.h:16
void * view
Definition: posix.h:19