|
| 1 | +#include <fs/tarfs/tar.h> |
| 2 | +#include <stddef.h> |
| 3 | +#include <sys/list.h> |
| 4 | +#include <sys/stdkern.h> |
| 5 | +#include <sys/vfs.h> |
| 6 | + |
| 7 | +/** |
| 8 | + * An internal TAR file node, both as a header block reference for the TAR file |
| 9 | + * and as the VFS node that is exposed as the file system. |
| 10 | + */ |
| 11 | +struct tarfs_node { |
| 12 | + vfs_node_t *node; |
| 13 | + tar_header_block_t *block; |
| 14 | +}; |
| 15 | + |
| 16 | +/** The internals associated with a TARFS volume. */ |
| 17 | +struct tarfs_payload { |
| 18 | + /** A pointer to the memory buffer of the TAR file. */ |
| 19 | + unsigned char *buf; |
| 20 | + |
| 21 | + /** A linked list with all the tarfs_node for all the files. */ |
| 22 | + list_t *nodes; |
| 23 | + |
| 24 | + /** Points back to the mounted VFS volume backing this payload. */ |
| 25 | + vfs_volume_t *volume; |
| 26 | + |
| 27 | + /** Points to the root node of the mounted TAR. */ |
| 28 | + vfs_node_t *root; |
| 29 | +}; |
| 30 | + |
| 31 | +static unsigned int |
| 32 | +octal2int(char *octstring) |
| 33 | +{ |
| 34 | + unsigned int acc = 0; |
| 35 | + while (*octstring) { |
| 36 | + acc = acc * 8 + (*octstring - '0'); |
| 37 | + octstring++; |
| 38 | + } |
| 39 | + return acc; |
| 40 | +} |
| 41 | + |
| 42 | +static int |
| 43 | +decode_block(struct tarfs_payload *tar, tar_header_block_t *block) |
| 44 | +{ |
| 45 | + struct tarfs_node *tnode; |
| 46 | + |
| 47 | + if ((tnode = malloc(sizeof(struct tarfs_node))) != 0) { |
| 48 | + tnode->block = block; |
| 49 | + tnode->node = 0; |
| 50 | + list_append(tar->nodes, tnode); |
| 51 | + return 0; |
| 52 | + } else { |
| 53 | + return -1; |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +static void |
| 58 | +split_basename(char *path, char **dirname, char **basename) |
| 59 | +{ |
| 60 | + char *ptr = path, *slash = 0; |
| 61 | + |
| 62 | + while (*ptr) { |
| 63 | + if (*ptr == '/') { |
| 64 | + slash = ptr; |
| 65 | + } |
| 66 | + ptr++; |
| 67 | + } |
| 68 | + |
| 69 | + if (slash) { |
| 70 | + *slash = 0; |
| 71 | + *basename = slash + 1; |
| 72 | + *dirname = path; |
| 73 | + } else { |
| 74 | + *basename = path; |
| 75 | + *dirname = ""; |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +static void reassemble_nodes(struct tarfs_payload *tarfile, |
| 80 | + struct tarfs_node *node); |
| 81 | + |
| 82 | +static vfs_node_t * |
| 83 | +lookup_vnode(struct tarfs_payload *tarfile, const char *path) |
| 84 | +{ |
| 85 | + listnode_t *listnode; |
| 86 | + struct tarfs_node *tarnode; |
| 87 | + char *kiwi; |
| 88 | + |
| 89 | + list_foreach(tarfile->nodes, listnode) |
| 90 | + { |
| 91 | + unsigned int last_char; |
| 92 | + tarnode = (struct tarfs_node *) listnode->data; |
| 93 | + kiwi = tarnode->block->metadata.name; |
| 94 | + /* If file name is ./, remove trailing dot. */ |
| 95 | + if (*kiwi == '.' && *(kiwi + 1) == '/') { |
| 96 | + kiwi++; |
| 97 | + } |
| 98 | + /* If starts with slash, remove it. */ |
| 99 | + if (*kiwi == '/') { |
| 100 | + kiwi++; |
| 101 | + } |
| 102 | + /* Remove possible trailing slash. */ |
| 103 | + last_char = strlen(kiwi) - 1; |
| 104 | + if (*kiwi && kiwi[last_char] == '/') { |
| 105 | + kiwi[last_char] = 0; |
| 106 | + } |
| 107 | + if (!strncmp(kiwi, path, 256)) { |
| 108 | + reassemble_nodes(tarfile, tarnode); |
| 109 | + return tarnode->node; |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + return NULL; |
| 114 | +} |
| 115 | + |
| 116 | +static void |
| 117 | +reassemble_nodes(struct tarfs_payload *tarfile, struct tarfs_node *node) |
| 118 | +{ |
| 119 | + char *orig_path, *path, *dirname, *basename; |
| 120 | + unsigned int last_char; |
| 121 | + vfs_node_t *vnode; |
| 122 | + |
| 123 | + if (!node->node) { |
| 124 | + orig_path = strdup(node->block->metadata.name); |
| 125 | + path = orig_path; |
| 126 | + |
| 127 | + /* Normalize paths removing absolute dir slashes and dots. */ |
| 128 | + if (*path == '.' && *(path + 1) == '/') { |
| 129 | + path++; |
| 130 | + } |
| 131 | + if (*path == '/') { |
| 132 | + path++; |
| 133 | + } |
| 134 | + |
| 135 | + /* Remove possible trailing slash. */ |
| 136 | + last_char = strlen(path) - 1; |
| 137 | + if (*path && path[last_char] == '/') { |
| 138 | + path[last_char] = 0; |
| 139 | + } |
| 140 | + |
| 141 | + /* Divide my path in dirname and basename. */ |
| 142 | + |
| 143 | + vnode = malloc(sizeof(vfs_node_t)); |
| 144 | + vnode->vn_flags = 0; |
| 145 | + if (*path) { |
| 146 | + split_basename(path, &dirname, &basename); |
| 147 | + strcpy(vnode->vn_name, basename); |
| 148 | + vnode->vn_parent = lookup_vnode(tarfile, dirname); |
| 149 | + } else { |
| 150 | + strcpy(vnode->vn_name, ""); |
| 151 | + vnode->vn_parent = NULL; |
| 152 | + if (!tarfile->root) { |
| 153 | + tarfile->root = vnode; |
| 154 | + } |
| 155 | + } |
| 156 | + vnode->vn_flags = 0; |
| 157 | + switch (node->block->metadata.type) { |
| 158 | + case '0': |
| 159 | + vnode->vn_flags |= VN_FREGFILE; |
| 160 | + break; |
| 161 | + case '5': |
| 162 | + vnode->vn_flags |= VN_FDIR; |
| 163 | + break; |
| 164 | + } |
| 165 | + vnode->vn_volume = tarfile->volume; |
| 166 | + vnode->vn_payload = node; |
| 167 | + node->node = vnode; |
| 168 | + |
| 169 | + free(orig_path); |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +static void |
| 174 | +init_nodes(struct tarfs_payload *tar) |
| 175 | +{ |
| 176 | + unsigned int bx = 0, file_length; |
| 177 | + tar_header_block_t *block = (tar_header_block_t *) tar->buf; |
| 178 | + listnode_t *lnode; |
| 179 | + |
| 180 | + /* First we decode all the files in this TAR. */ |
| 181 | + while (*block[bx].metadata.name) { |
| 182 | + decode_block(tar, &block[bx]); |
| 183 | + file_length = octal2int(block[bx].metadata.size); |
| 184 | + bx += (file_length / 512) + 1; |
| 185 | + if ((file_length % 512)) { |
| 186 | + bx++; |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + /* Then we reassemble the vfs_node_t data structures. */ |
| 191 | + list_foreach(tar->nodes, lnode) |
| 192 | + { |
| 193 | + reassemble_nodes(tar, (struct tarfs_node *) lnode->data); |
| 194 | + } |
| 195 | +} |
| 196 | + |
| 197 | +static int tarfs_mount(vfs_volume_t *volume); |
| 198 | +static int tarfs_open(vfs_node_t *node, unsigned int flags); |
| 199 | +static unsigned int tarfs_read(vfs_node_t *, unsigned, void *, unsigned); |
| 200 | +static unsigned int tarfs_write(vfs_node_t *, unsigned, void *, unsigned); |
| 201 | +static int tarfs_close(vfs_node_t *node); |
| 202 | +static vfs_node_t *tarfs_readdir(vfs_node_t *node, unsigned int index); |
| 203 | +static vfs_node_t *tarfs_finddir(vfs_node_t *node, char *name); |
| 204 | + |
| 205 | +static vfs_ops_t tarfs_ops = { |
| 206 | + .vfs_close = &tarfs_close, |
| 207 | + .vfs_open = &tarfs_open, |
| 208 | + .vfs_read = &tarfs_read, |
| 209 | + .vfs_readdir = &tarfs_readdir, |
| 210 | + .vfs_finddir = &tarfs_finddir, |
| 211 | +}; |
| 212 | + |
| 213 | +static vfs_filesys_t tarfs_driver = { |
| 214 | + .fsd_ident = "tarfs", |
| 215 | + .fsd_name = "TAR File System", |
| 216 | + .fsd_mount = &tarfs_mount, |
| 217 | + .fsd_ops = &tarfs_ops, |
| 218 | +}; |
| 219 | + |
| 220 | +FS_DESCRIPTOR(tarfs, tarfs_driver); |
| 221 | + |
| 222 | +static int |
| 223 | +tarfs_mount(vfs_volume_t *luna) |
| 224 | +{ |
| 225 | + struct tarfs_payload *payload; |
| 226 | + |
| 227 | + if ((payload = malloc(sizeof(struct tarfs_payload)))) { |
| 228 | + payload->buf = luna->vv_payload; |
| 229 | + payload->nodes = list_alloc(); |
| 230 | + payload->volume = luna; |
| 231 | + init_nodes(payload); |
| 232 | + luna->vv_root = payload->root; |
| 233 | + luna->vv_payload = payload; |
| 234 | + return 0; |
| 235 | + } else { |
| 236 | + return -1; |
| 237 | + } |
| 238 | +} |
| 239 | + |
| 240 | +static int |
| 241 | +tarfs_open(vfs_node_t *node, unsigned int flags) |
| 242 | +{ |
| 243 | + /* TODO: Should check the flags, and mark the file as opened. */ |
| 244 | + return 0; |
| 245 | +} |
| 246 | + |
| 247 | +static unsigned int |
| 248 | +tarfs_read(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) |
| 249 | +{ |
| 250 | + unsigned char *data, *dst; |
| 251 | + struct tarfs_node *tar = (struct tarfs_node *) node->vn_payload; |
| 252 | + unsigned int read = 0, size = octal2int(tar->block->metadata.size); |
| 253 | + |
| 254 | + data = (unsigned char *) &tar->block->metadata + 512; |
| 255 | + dst = (unsigned char *) buf; |
| 256 | + while (len > 0 && offt < size) { |
| 257 | + dst[read++] = data[offt++]; |
| 258 | + len--; |
| 259 | + } |
| 260 | + return read; |
| 261 | +} |
| 262 | + |
| 263 | +static unsigned int |
| 264 | +tarfs_write(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) |
| 265 | +{ |
| 266 | + return -1; |
| 267 | +} |
| 268 | + |
| 269 | +static int |
| 270 | +tarfs_close(vfs_node_t *node) |
| 271 | +{ |
| 272 | + return 0; |
| 273 | +} |
| 274 | + |
| 275 | +static vfs_node_t * |
| 276 | +tarfs_readdir(vfs_node_t *klairm_cocayketa_voltereta, |
| 277 | + unsigned int clank_will_not_die) |
| 278 | +{ |
| 279 | + listnode_t *esta_variable_es_del_mejor_mod_de_discord; |
| 280 | + struct tarfs_payload *dia_de_pago; |
| 281 | + struct tarfs_node *kiwi; |
| 282 | + |
| 283 | + dia_de_pago = (struct tarfs_payload *) |
| 284 | + klairm_cocayketa_voltereta->vn_volume->vv_payload; |
| 285 | + |
| 286 | + list_foreach(dia_de_pago->nodes, |
| 287 | + esta_variable_es_del_mejor_mod_de_discord) |
| 288 | + { |
| 289 | + kiwi = (struct tarfs_node *) |
| 290 | + esta_variable_es_del_mejor_mod_de_discord->data; |
| 291 | + if (kiwi->node->vn_parent == klairm_cocayketa_voltereta) { |
| 292 | + if (clank_will_not_die == 0) { |
| 293 | + return kiwi->node; |
| 294 | + } else { |
| 295 | + --clank_will_not_die; |
| 296 | + } |
| 297 | + } |
| 298 | + } |
| 299 | + |
| 300 | + return NULL; |
| 301 | +} |
| 302 | + |
| 303 | +static vfs_node_t * |
| 304 | +tarfs_finddir(vfs_node_t *node, char *name) |
| 305 | +{ |
| 306 | + struct tarfs_payload *payload; |
| 307 | + struct tarfs_node *tarnode; |
| 308 | + listnode_t *listnode; |
| 309 | + |
| 310 | + payload = node->vn_volume->vv_payload; |
| 311 | + list_foreach(payload->nodes, listnode) |
| 312 | + { |
| 313 | + tarnode = (struct tarfs_node *) listnode->data; |
| 314 | + if (tarnode->node->vn_parent == node |
| 315 | + && !strcmp(name, tarnode->node->vn_name)) { |
| 316 | + return tarnode->node; |
| 317 | + } |
| 318 | + } |
| 319 | + |
| 320 | + return NULL; |
| 321 | +} |
0 commit comments