Cthulhu  0.2.10
Cthulhu compiler collection
physical.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "arena/arena.h"
4 #include "base/util.h"
5 #include "common.h"
6 
7 #include "std/str.h"
8 #include "std/map.h"
9 
10 #include "os/os.h"
11 #include "io/impl.h"
12 
13 #include "base/panic.h"
14 #include <stdint.h>
15 
16 typedef struct physical_t
17 {
18  const char *root;
19 } physical_t;
20 
21 typedef struct physical_inode_t
22 {
23  const char *path;
25 
26 static const char *get_absolute(fs_t *fs, const fs_inode_t *node, const char *path)
27 {
28  CTASSERT(fs != NULL);
29 
30  const physical_t *self = fs_data(fs);
31  const physical_inode_t *dir = inode_data((fs_inode_t*)node);
32 
33  if (is_path_special(dir->path) && is_path_special(path))
34  {
35  return self->root;
36  }
37 
38  if (is_path_special(dir->path) && !is_path_special(path))
39  {
40  return str_format(fs->arena, "%s" CT_NATIVE_PATH_SEPARATOR "%s", self->root, path);
41  }
42 
43  if (!is_path_special(dir->path) && is_path_special(path))
44  {
45  return str_format(fs->arena, "%s" CT_NATIVE_PATH_SEPARATOR "%s", self->root, dir->path);
46  }
47 
48  return str_format(fs->arena, "%s" CT_NATIVE_PATH_SEPARATOR "%s" CT_NATIVE_PATH_SEPARATOR "%s", self->root, dir->path, path);
49 }
50 
51 static const char *get_relative(const fs_inode_t *node, const char *path, arena_t *arena)
52 {
53  const physical_inode_t *dir = inode_data((fs_inode_t*)node);
54 
55  if (is_path_special(dir->path) && !is_path_special(path))
56  {
57  return path;
58  }
59 
60  if (!is_path_special(dir->path) && is_path_special(path))
61  {
62  return dir->path;
63  }
64 
65  CTASSERT(!is_path_special(dir->path) && !is_path_special(path));
66 
67  return str_format(arena, "%s" CT_NATIVE_PATH_SEPARATOR "%s", dir->path, path);
68 }
69 
70 static fs_inode_t *physical_dir(fs_t *fs, const char *path)
71 {
72  char *id = arena_strdup(path, fs->arena);
73  physical_inode_t inode = {
74  .path = id
75  };
76 
77  const char *name = id;
78 
79  size_t i = str_rfind(id, CT_NATIVE_PATH_SEPARATOR);
80  if (i != SIZE_MAX)
81  {
82  name = id + i + 1;
83  }
84 
85  return inode_dir(fs, name, &inode);
86 }
87 
88 static fs_inode_t *physical_file(fs_t *fs, const char *path)
89 {
90  char *id = arena_strdup(path, fs->arena);
91  physical_inode_t inode = {
92  .path = id
93  };
94 
95  const char *name = id;
96 
97  size_t i = str_rfind(id, CT_NATIVE_PATH_SEPARATOR);
98  if (i != SIZE_MAX)
99  {
100  name = id + i + 1;
101  }
102 
103  return inode_file(fs, name, &inode);
104 }
105 
106 static fs_inode_t *pfs_query_node(fs_t *fs, const fs_inode_t *self, const char *name)
107 {
108  const char *absolute = get_absolute(fs, self, name);
109  os_dirent_t dirent = os_dirent_type(absolute);
110  CTASSERTF(dirent != eOsNodeError, "failed to query node %s", absolute);
111 
112  const char *relative = get_relative(self, name, fs->arena);
113 
114  switch (dirent)
115  {
116  case eOsNodeFile:
117  return physical_file(fs, relative);
118  case eOsNodeDir:
119  return physical_dir(fs, relative);
120  default:
121  return &gInvalidFileNode;
122  }
123 }
124 
125 static io_t *pfs_query_file(fs_t *fs, fs_inode_t *self, os_access_t flags)
126 {
127  const char *absolute = get_absolute(fs, self, NULL);
128  return io_file(absolute, flags, fs->arena);
129 }
130 
131 static inode_result_t pfs_file_create(fs_t *fs, fs_inode_t *self, const char *name)
132 {
133  const char *absolute = get_absolute(fs, self, name);
134  os_error_t err = os_file_create(absolute);
135  if (err != eOsSuccess)
136  {
137  inode_result_t result = { .error = err };
138  return result;
139  }
140 
141  fs_inode_t *inode = physical_file(fs, get_relative(self, name, fs->arena));
142  inode_result_t result = { .node = inode };
143  return result;
144 }
145 
146 static inode_result_t pfs_dir_create(fs_t *fs, fs_inode_t *self, const char *name)
147 {
148  const char *absolute = get_absolute(fs, self, name);
149  os_error_t err = mkdir_recursive(absolute, fs->arena);
150  if (err != eOsSuccess && err != eOsExists)
151  {
152  inode_result_t result = { .error = err };
153  return result;
154  }
155 
156  fs_inode_t *inode = physical_dir(fs, get_relative(self, name, fs->arena));
157  inode_result_t result = { .node = inode };
158  return result;
159 }
160 
161 static os_error_t pfs_dir_delete(fs_t *fs, fs_inode_t *self, const char *name)
162 {
163  const char *absolute = get_absolute(fs, self, name);
164  return os_dir_delete(absolute);
165 }
166 
167 static os_error_t pfs_file_delete(fs_t *fs, fs_inode_t *self, const char *name)
168 {
169  const char *absolute = get_absolute(fs, self, name);
170  return os_file_delete(absolute);
171 }
172 
173 static os_error_t pfs_iter_begin(fs_t *fs, const fs_inode_t *dir, fs_iter_t *iter)
174 {
175  const char *absolute = get_absolute(fs, dir, NULL);
176  os_iter_t *data = iter_data(iter);
177  return os_iter_begin(absolute, data);
178 }
179 
180 static os_error_t pfs_iter_next(fs_iter_t *iter)
181 {
182  os_inode_t cur = { 0 };
183  if (!os_iter_next(iter_data(iter), &cur))
184  return eOsNotFound;
185 
186  iter->current = pfs_query_node(iter->fs, iter->dir, os_inode_name(&cur));
187 
188  return eOsSuccess;
189 }
190 
191 static os_error_t pfs_iter_end(fs_iter_t *iter)
192 {
193  return os_iter_end(iter_data(iter));
194 }
195 
196 static const fs_callbacks_t kPhysicalInterface = {
197  .pfn_query_node = pfs_query_node,
198  .pfn_query_file = pfs_query_file,
199 
200  .pfn_create_dir = pfs_dir_create,
201  .pfn_delete_dir = pfs_dir_delete,
202 
203  .pfn_create_file = pfs_file_create,
204  .pfn_delete_file = pfs_file_delete,
205 
206  .pfn_iter_begin = pfs_iter_begin,
207  .pfn_iter_next = pfs_iter_next,
208  .pfn_iter_end = pfs_iter_end,
209 
210  .iter_size = sizeof(os_iter_t),
211  .inode_size = sizeof(physical_inode_t),
212 };
213 
214 STA_DECL
215 fs_t *fs_physical(const char *root, arena_t *arena)
216 {
217  CTASSERT(root != NULL);
218 
219  if (!os_dir_exists(root))
220  {
221  os_error_t err = mkdir_recursive(root, arena);
222 
223  if (err != eOsSuccess && err != eOsExists)
224  {
225  return NULL;
226  }
227  }
228 
229  physical_t self = {
230  .root = root
231  };
232 
233  physical_inode_t inode = {
234  .path = "."
235  };
236 
237  return fs_new(&inode, &kPhysicalInterface, &self, sizeof(physical_t), arena);
238 }
void * fs_data(fs_t *fs)
Definition: common.c:108
fs_inode_t gInvalidFileNode
Definition: common.c:15
void * inode_data(fs_inode_t *inode)
Definition: common.c:41
os_error_t mkdir_recursive(const char *path, arena_t *arena)
Definition: common.c:72
fs_inode_t * inode_file(fs_t *fs, const char *name, const void *data)
Definition: common.c:31
fs_inode_t * inode_dir(fs_t *fs, const char *name, const void *data)
Definition: common.c:36
fs_t * fs_new(void *root, const fs_callbacks_t *cb, const void *data, size_t size, arena_t *arena)
Definition: common.c:93
void * iter_data(fs_iter_t *iter)
Definition: common.c:48
CT_NODISCARD STA_RET_STRING CT_PUREFN CT_OS_API const char * os_inode_name(const os_inode_t *node)
get the name of an inode
Definition: os.c:457
os_dirent_t
directory entry type
Definition: core.h:56
os_access_t
file access mode
Definition: core.h:38
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
CT_BEGIN_API CT_NODISCARD CT_PUREFN CT_BASE_API bool is_path_special(const char *path)
check if a path is special special paths are paths such as "." and ".." that are not valid for most o...
Definition: util.c:10
#define CT_NATIVE_PATH_SEPARATOR
Definition: compiler.h:88
STA_DECL fs_t * fs_physical(const char *root, arena_t *arena)
create a filesystem interface to a physical location on disk
Definition: physical.c:215
CT_NODISCARD CT_IO_API io_t * io_file(const char *path, os_access_t mode, arena_t *arena)
create an IO object from a file
Definition: file.c:125
CT_NODISCARD CT_ARENA_API char * arena_strdup(const char *str, arena_t *arena)
allocate a copy of a string from a custom allocator
Definition: arena.c:95
#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
CT_PUREFN CT_STD_API size_t str_rfind(const char *str, const char *sub)
find the last instance of a substring in a string
Definition: str.c:884
STA_DECL bool os_iter_next(os_iter_t *iter, os_inode_t *dir)
Definition: os.c:410
CT_OS_API os_error_t os_file_create(const char *path)
create a file
Definition: os.c:289
CT_NODISCARD CT_OS_API os_dirent_t os_dirent_type(const char *path)
get the type of a paths inode entry
Definition: fs.c:65
CT_OS_API os_error_t os_dir_delete(const char *path)
delete a directory
Definition: fs.c:52
CT_OS_API os_error_t os_file_delete(const char *path)
delete a file
Definition: fs.c:16
CT_OS_API os_error_t os_iter_end(STA_RELEASE os_iter_t *iter)
close a directory iterator
CT_NODISCARD OUT_NOTNULL os_inode_t * dir
Definition: os.h:245
CT_OS_API bool os_dir_exists(const char *path)
check if a directory exists
Definition: os.c:353
CT_OS_API os_error_t os_iter_begin(const char *path, OUT_NOTNULL os_iter_t *iter)
directory iteration
@ eOsNotFound
Definition: posix.h:25
@ eOsExists
Definition: posix.h:26
@ eOsSuccess
Definition: posix.h:24
STA_DECL char * str_format(arena_t *arena, const char *fmt,...)
Definition: str.c:97
an allocator object
Definition: arena.h:86
fs_query_node_t pfn_query_node
Definition: common.h:53
fs_inode_t * current
Definition: common.h:23
const fs_inode_t * dir
Definition: common.h:22
fs_t * fs
Definition: common.h:21
Definition: common.h:72
arena_t * arena
Definition: common.h:74
fs_inode_t * node
Definition: common.h:29
os_error_t error
Definition: common.h:30
io object implementation
Definition: impl.h:122
an inode entry
Definition: os.h:52
a directory iterator
Definition: os.h:62
const char * path
path to file or directory relative to root
Definition: physical.c:23
const char * root
absolute path to root directory
Definition: physical.c:18