libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
file.c
1#include <assert.h>
2
3#include "../platform.h"
4#include "../../error.h"
5#include <fcntl.h>
6#include <stdlib.h>
7#include <errno.h>
8#include <string.h>
9#include <unistd.h>
10#include <sys/stat.h>
11#include <sys/mman.h>
12
13struct file {
14 void *map;
15 size_t size;
16 int fd;
17 bool writeable;
18};
19
20enum clod_region_result file_open(file *f, const dir d, const char *name, const bool create, const struct clod_region_opts *opts) {
21 int o_flags = 0;
22 if (opts->mode == CLOD_REGION_MODE_RDWR) o_flags |= O_RDWR;
23 if (opts->mode == CLOD_REGION_MODE_RDONLY) o_flags |= O_RDONLY;
24 if (create) o_flags |= O_CREAT;
25
26 mode_t o_mode = 0664;
27 if (opts->unix_file_perms) o_mode = opts->unix_file_perms;
28
29 const int fd = openat((int)(intptr_t)d, name, o_flags, o_mode);
30 if (fd < 0) {
31 if (!create && errno == ENOENT) {
33 }
34
35 region_error(CLOD_REGION_INVALID_USAGE, "Opening \"%s\": %s", name, strerror(errno));
37 }
38
39#if HAVE_STATX
40 struct statx st;
41 if (statx(fd, "", AT_EMPTY_PATH, STATX_SIZE, &st)) {
42 region_error(CLOD_REGION_INVALID_USAGE, "Failed to stat \"%s\": %s", name, strerror(errno));
43 close(fd);
45 }
46 const size_t size = st.stx_size;
47#else
48 struct stat st;
49 if (fstat(fd, &st)) {
50 region_error(CLOD_REGION_INVALID_USAGE, "Failed to stat \"%s\": %s", name, strerror(errno));
51 close(fd);
53 }
54 const size_t size = (size_t)st.st_size;
55#endif
56
57 int prot = PROT_READ;
58 if (opts->mode == CLOD_REGION_MODE_RDWR) prot |= PROT_WRITE;
59 void *map = nullptr;
60 if (size > 0) {
61 map = mmap(nullptr, size, prot, MAP_SHARED, fd, 0);
62 if (map == MAP_FAILED) {
63 region_error(CLOD_REGION_INVALID_USAGE, "Failed to mmap \"%s\": %s", name, strerror(errno));
64 close(fd);
66 }
67 }
68
69 struct file *file_struct = malloc(sizeof(struct file));
70 if (!file_struct) {
71 region_error(CLOD_REGION_INVALID_USAGE, "Failed to allocate memory for file.");
72 if (size > 0) munmap(map, size);
73 close(fd);
75 }
76
77 file_struct->map = map;
78 file_struct->size = size;
79 file_struct->fd = fd;
80 file_struct->writeable = opts->mode == CLOD_REGION_MODE_RDWR;
81 *f = (uintptr_t)file_struct;
82 return CLOD_REGION_OK;
83}
84enum clod_region_result file_get(const file f, void **data, size_t *size) {
85 *data = ((struct file *)f)->map;
86 *size = ((struct file *)f)->size;
87 return CLOD_REGION_OK;
88}
89enum clod_region_result file_truncate(const file f, const size_t new_size) {
90 auto const file_struct = (struct file *)f;
91 if (file_struct->size == new_size) return CLOD_REGION_OK;
92
93 if (ftruncate(file_struct->fd, (off_t)new_size)) {
94 return region_error(CLOD_REGION_INVALID_USAGE, "Failed to truncate file: %s", strerror(errno));
95 }
96
97 const size_t old_size = file_struct->size;
98 file_struct->size = new_size;
99
100#if HAVE_MREMAP
101 if (file_struct->map != nullptr && new_size > 0) {
102 assert(old_size > 0);
103 file_struct->map = mremap(file_struct->map, old_size, new_size, MREMAP_MAYMOVE);
104 if (file_struct->map == MAP_FAILED) {
105 file_struct->map = nullptr;
106 return region_error(CLOD_REGION_INVALID_USAGE, "Failed to remap file: %s", strerror(errno));
107 }
108 return CLOD_REGION_OK;
109 }
110#endif
111
112 if (file_struct->map != nullptr) {
113 assert(old_size > 0);
114 munmap(file_struct->map, old_size);
115 file_struct->map = nullptr;
116 }
117 if (new_size > 0) {
118 int mmap_flag = PROT_READ;
119 if (file_struct->writeable) mmap_flag |= PROT_WRITE;
120
121 file_struct->map = mmap(nullptr, new_size, mmap_flag, MAP_SHARED, file_struct->fd, 0);
122 if (file_struct->map == MAP_FAILED) {
123 file_struct->map = nullptr;
124 return region_error(CLOD_REGION_INVALID_USAGE, "Failed to map file: %s", strerror(errno));
125 }
126 }
127 return true;
128}
129enum clod_region_result file_close(const file f) {
130 auto const file_struct = (struct file *)f;
132 if (file_struct->map && munmap(file_struct->map, file_struct->size)) {
133 res = region_error(CLOD_REGION_INVALID_USAGE, "Failed to unmap file: %s", strerror(errno));
134 }
135 if (close(file_struct->fd)) {
136 res = region_error(CLOD_REGION_INVALID_USAGE, "Failed to close file: %s", strerror(errno));
137 }
138 free(file_struct);
139 return res;
140}
clod_region_result
Definition region.h:43
@ CLOD_REGION_NOT_FOUND
Definition region.h:56
@ CLOD_REGION_INVALID_USAGE
Definition region.h:49
@ CLOD_REGION_OK
Definition region.h:45
uint8_t mode
Definition region.h:176
uint32_t unix_file_perms
Definition region.h:195
Definition file.c:13