💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › 00b27da2981ae09dab55da606d4ba… captured on 2023-12-28 at 15:45:21. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * ISC License
2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
3 */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <stdint.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <pwd.h>
12 #include <fcntl.h>
13 #ifdef __linux__
14 #include <linux/limits.h>
15 #endif
16 #include <errno.h>
17 #include "macro.h"
18 #include "strlcpy.h"
19 #include "storage.h"
20 #include "error.h"
21 #include "utf8.h"
22
23 #ifndef PATH_MAX
24 #define PATH_MAX 1024
25 #endif
26
27 #define CONFIG_FOLDER "vgmi"
28 #define DOWNLOAD_PATH "Downloads"
29 #define CONFIG_PATH ".config/"CONFIG_FOLDER
30
31 int storage_fd = -1, download_fd = -1;
32
33 static int storage_mkdir(const char *path) {
34
35 struct stat st;
36 const char *ptr;
37 char path_copy[PATH_MAX];
38 int i;
39
40 if (stat(path, &st) == 0) return 0;
41
42 i = STRLCPY(path_copy, path);
43 path_copy[i] = '/';
44 path_copy[i + 1] = 0;
45 for (ptr = path_copy; ptr && *ptr; ptr = strchr(ptr, '/')) {
46 char buf[PATH_MAX];
47 if (ptr++ == path_copy) continue;
48 if ((size_t)(ptr - path_copy) >= sizeof(buf)) return -1;
49 strlcpy(buf, path_copy, ptr - path_copy + 1);
50 if (stat(buf, &st) == 0) continue;
51 if (mkdir(buf, 0700)) return -1;
52 }
53
54 return 0;
55 }
56
57 static int storage_from_home(char *out, size_t length, const char *dir) {
58
59 struct passwd *pw;
60 const char *path = getenv("HOME");
61
62 if (path) {
63 snprintf(out, length, "%s/%s", path, dir);
64 return 0;
65 }
66
67 pw = getpwuid(getuid());
68 if (pw) {
69 snprintf(out, length, "%s/%s", pw->pw_dir, dir);
70 return 0;
71 }
72
73 return -1;
74 }
75
76 int storage_path(char *out, size_t length) {
77
78 const char *path;
79
80 if (length > PATH_MAX) length = PATH_MAX;
81
82 path = getenv("XDG_CONFIG_HOME");
83 if (path) {
84 int i = strlcpy(out, path, length);
85 strlcpy(&out[i], CONFIG_FOLDER, length - i);
86 return 0;
87 }
88
89 return storage_from_home(out, length, CONFIG_PATH);
90 }
91
92 int storage_download_path(char *out, size_t length) {
93
94 FILE *f;
95
96 if (length > PATH_MAX) length = PATH_MAX;
97
98 f = popen("xdg-user-dir DOWNLOAD 2>&1", "r");
99 if (f) {
100 fread(out, 1, length, f);
101 pclose(f);
102 if (out[0] == '/') { /* success if the output is a path */
103 size_t i = 0;
104 while (i < length) {
105 uint32_t ch;
106 int len = utf8_char_to_unicode(&ch, &out[i]);
107 if (ch < ' ') {
108 out[i] = 0;
109 break;
110 }
111 i += len;
112 }
113 return 0;
114 }
115 }
116
117 return storage_from_home(out, length, DOWNLOAD_PATH);
118 }
119
120 static FILE* _fopen(const char *name, const char *mode, int dir) {
121
122 int fd;
123 int flags;
124
125 if (strchr(mode, 'w')) flags = O_WRONLY | O_TRUNC | O_CREAT;
126 else if (strchr(mode, 'r')) flags = O_RDONLY;
127 else if (strchr(mode, 'a')) flags = O_WRONLY | O_APPEND | O_CREAT;
128 else return NULL;
129
130 fd = openat(dir, name, flags, 0600);
131 if (fd < 0) return NULL;
132
133 return fdopen(fd, mode);
134 }
135
136 FILE* storage_fopen(const char *name, const char *mode) {
137 return _fopen(name, mode, storage_fd);
138 }
139
140 FILE* storage_download_fopen(const char *name, const char *mode) {
141 return _fopen(name, mode, download_fd);
142 }
143
144 int storage_open(const char *name, int flags, int mode) {
145 return openat(storage_fd, name, flags, mode);
146 }
147
148 int storage_download_open(const char *name, int flags, int mode) {
149 return openat(download_fd, name, flags, mode);
150 }
151
152 int storage_download_exist(const char *name) {
153 struct stat stat;
154 return !fstatat(download_fd, name, &stat, 0);
155 }
156
157 int storage_read(const char *name, char *out, size_t length,
158 size_t *length_out) {
159 int fd;
160 size_t file_length;
161
162 fd = openat(storage_fd, name, O_RDONLY, 0);
163 if (fd < 0) return ERROR_STORAGE_ACCESS;
164
165 file_length = lseek(fd, 0, SEEK_END);
166 lseek(fd, 0, SEEK_SET);
167
168 if (length < file_length) return ERROR_BUFFER_OVERFLOW;
169
170 if (read(fd, out, file_length) != (ssize_t)file_length)
171 return ERROR_STORAGE_ACCESS;
172 *length_out = file_length;
173
174 close(fd);
175 return 0;
176 }
177
178 int storage_init() {
179
180 char path[PATH_MAX], download[PATH_MAX];
181 int ret;
182
183 if (storage_path(V(path))) return ERROR_STORAGE_ACCESS;
184 if (storage_download_path(V(download))) return ERROR_STORAGE_ACCESS;
185 if (storage_mkdir(path)) return ERROR_STORAGE_ACCESS;
186 if (storage_mkdir(download)) return ERROR_STORAGE_ACCESS;
187 if ((ret = open(path, O_DIRECTORY)) < 0) return ERROR_STORAGE_ACCESS;
188 storage_fd = ret;
189 if ((ret = open(download, O_DIRECTORY)) < 0)
190 return ERROR_STORAGE_ACCESS;
191 download_fd = ret;
192 return 0;
193 }
194
195 int storage_close() {
196 close(storage_fd);
197 close(download_fd);
198 return 0;
199 }
200