1/** 2 * \file 3 * \brief Trivial RAMFS implementation 4 */ 5 6/* 7 * Copyright (c) 2010, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <stdio.h> 16#include <string.h> 17#include <barrelfish/barrelfish.h> 18#include <if/trivfs_defs.h> 19#include "ramfs.h" 20 21struct dirent { 22 struct dirent *next; ///< next entry in same directory 23 struct dirent **prevp; ///< locn where the preceding child / parent links us 24 struct dirent *parent; ///< parent directory 25 const char *name; ///< malloc'ed name buffer 26 bool isdir; ///< is a directory or a file? 27 bool islive; ///< false if this has been deleted but not yet freed 28 unsigned refcount; ///< outstanding references (handles and/or ongoing IDCs) 29 union { 30 struct { 31 uint8_t *data; ///< on heap 32 size_t size; ///< size of data 33 } file; 34 struct { 35 struct dirent *entries; ///< children of this dir 36 size_t nentries; ///< number of children 37 } dir; 38 } u; 39}; 40 41struct dirent *ramfs_init(void) 42{ 43 struct dirent *root = malloc(sizeof(struct dirent)); 44 assert(root != NULL); 45 46 root->next = NULL; 47 root->prevp = NULL; 48 root->parent = NULL; 49 root->name = ""; 50 root->isdir = true; 51 root->islive = true; 52 root->u.dir.entries = NULL; 53 root->u.dir.nentries = 0; 54 root->refcount = 1; 55 56 return root; 57} 58 59inline void ramfs_incref(struct dirent *e) 60{ 61 assert(e->refcount > 0); 62 assert(e->islive); 63 e->refcount++; 64} 65 66void ramfs_decref(struct dirent *e) 67{ 68 assert(e->refcount > 0); 69 if (--e->refcount == 0) { 70 assert(!e->islive); 71 assert(e->next == NULL && e->prevp == NULL); 72 free((void *)e->name); 73 if (e->isdir) { 74 assert(e->u.dir.nentries == 0); 75 assert(e->u.dir.entries == NULL); 76 } else { 77 free(e->u.file.data); 78 } 79 free(e); 80 } 81} 82 83const char *ramfs_get_name(struct dirent *e) 84{ 85 assert(e->islive && e->refcount > 0); 86 return e->name; 87} 88 89bool ramfs_isdir(struct dirent *e) 90{ 91 assert(e->islive && e->refcount > 0); 92 return e->isdir; 93} 94 95bool ramfs_islive(struct dirent *e) 96{ 97 assert(e->refcount > 0); 98 return e->islive; 99} 100 101size_t ramfs_get_size(struct dirent *e) 102{ 103 assert(e->islive && e->refcount > 0); 104 return e->isdir ? e->u.dir.nentries : e->u.file.size; 105} 106 107errval_t ramfs_readdir(struct dirent *dir, uint32_t idx, struct dirent **ret) 108{ 109 assert(dir->islive && dir->refcount > 0); 110 111 if (!dir->isdir) { 112 return FS_ERR_NOTDIR; 113 } 114 115 struct dirent *e = dir->u.dir.entries; 116 while (e != NULL && idx > 0) { 117 e = e->next; 118 idx--; 119 } 120 121 if (idx > 0 || e == NULL) { 122 return FS_ERR_INDEX_BOUNDS; 123 } 124 125 assert(ret != NULL); 126 *ret = e; 127 return SYS_ERR_OK; 128} 129 130errval_t ramfs_lookup(struct dirent *dir, const char *name, struct dirent **ret) 131{ 132 assert(dir != NULL); 133 assert(dir->islive && dir->refcount > 0); 134 135 if (!dir->isdir) { 136 return FS_ERR_NOTDIR; 137 } 138 139 struct dirent *e; 140 for (e = dir->u.dir.entries; e != NULL; e = e->next) { 141 if (strcmp(e->name, name) == 0) { 142 *ret = e; 143 return SYS_ERR_OK; 144 } 145 } 146 147 return FS_ERR_NOTFOUND; 148} 149 150errval_t ramfs_read(struct dirent *f, off_t offset, uint8_t **retbuf, 151 size_t *maxlen) 152{ 153 assert(f->islive && f->refcount > 0); 154 155 if (f->isdir) { 156 return FS_ERR_NOTFILE; 157 } 158 159 if (offset < 0 || offset >= f->u.file.size) { 160 *retbuf = NULL; 161 *maxlen = 0; 162 } else { 163 assert(f->u.file.data != NULL); 164 *retbuf = &f->u.file.data[offset]; 165 *maxlen = f->u.file.size - offset; 166 } 167 168 return SYS_ERR_OK; 169} 170 171/// Ensure that the file has at least space for a write of the given size at 172/// the given offset, return buffer pointer 173errval_t ramfs_grow(struct dirent *f, off_t offset, size_t len, 174 uint8_t **retbuf) 175{ 176 assert(f->islive && f->refcount > 0); 177 178 if (f->isdir) { 179 return FS_ERR_NOTFILE; 180 } 181 182 assert(offset >= 0); 183 184 // do we need to grow the file? if not, we're done 185 if (offset + len <= f->u.file.size) { 186 *retbuf = &f->u.file.data[offset]; 187 return SYS_ERR_OK; 188 } 189 190 uint8_t *newdata = realloc(f->u.file.data, (size_t)offset + len); 191 if (newdata == NULL) { 192 return LIB_ERR_MALLOC_FAIL; 193 } else { 194 f->u.file.data = newdata; 195 f->u.file.size = (size_t)offset + len; 196 *retbuf = &newdata[offset]; 197 // XXX: new data is not initialised, we rely on the caller to do this! 198 return SYS_ERR_OK; 199 } 200} 201 202errval_t ramfs_resize(struct dirent *f, size_t newlen) 203{ 204 assert(f->islive && f->refcount > 0); 205 206 if (f->isdir) { 207 return FS_ERR_NOTFILE; 208 } 209 210 uint8_t *newdata = realloc(f->u.file.data, newlen); 211 if (newdata == NULL) { 212 return LIB_ERR_MALLOC_FAIL; 213 } 214 215 if (newlen > f->u.file.size) { 216 // zero-fill new data 217 memset(&newdata[f->u.file.size], 0, newlen - f->u.file.size); 218 } 219 220 f->u.file.data = newdata; 221 f->u.file.size = newlen; 222 223 return SYS_ERR_OK; 224} 225 226static errval_t addchild(struct dirent *dir, struct dirent *child) 227{ 228 assert(child->refcount == 1); 229 if (dir->u.dir.entries == NULL) { 230 assert(dir->u.dir.nentries == 0); 231 dir->u.dir.entries = child; 232 child->prevp = &dir->u.dir.entries; 233 } else { 234 // search to end of list 235 struct dirent *e; 236 for (e = dir->u.dir.entries; ; e = e->next) { 237 // check for duplicates 238 if (strcmp(e->name, child->name) == 0) { 239 return FS_ERR_EXISTS; 240 } 241 if (e->next == NULL) { 242 break; 243 } 244 } 245 e->next = child; 246 child->prevp = &e->next; 247 } 248 child->parent = dir; 249 dir->u.dir.nentries++; 250 return SYS_ERR_OK; 251} 252 253errval_t ramfs_create(struct dirent *dir, const char *name, struct dirent **ret) 254{ 255 assert(dir != NULL); 256 assert(dir->islive && dir->refcount > 0); 257 258 if (!dir->isdir) { 259 return FS_ERR_NOTDIR; 260 } 261 262 struct dirent *f = malloc(sizeof(struct dirent)); 263 if (f == NULL) { 264 return LIB_ERR_MALLOC_FAIL; 265 } 266 267 f->next = NULL; 268 f->name = name; /* XXX: takes ownership of name buffer */ 269 f->isdir = false; 270 f->refcount = 1; 271 f->islive = true; 272 f->u.file.data = NULL; 273 f->u.file.size = 0; 274 275 errval_t err = addchild(dir, f); 276 277 if (err_is_ok(err)) { 278 if (ret != NULL) { 279 *ret = f; 280 } 281 } else { 282 free(f); 283 } 284 285 return err; 286} 287 288errval_t ramfs_mkdir(struct dirent *dir, const char *name, struct dirent **ret) 289{ 290 assert(dir != NULL); 291 assert(dir->islive && dir->refcount > 0); 292 293 if (!dir->isdir) { 294 return FS_ERR_NOTDIR; 295 } 296 297 struct dirent *d = malloc(sizeof(struct dirent)); 298 if (d == NULL) { 299 return LIB_ERR_MALLOC_FAIL; 300 } 301 302 d->next = NULL; 303 d->name = name; /* XXX: takes ownership of name buffer */ 304 d->isdir = true; 305 d->refcount = 1; 306 d->islive = true; 307 d->u.dir.entries = NULL; 308 d->u.dir.nentries = 0; 309 310 errval_t err = addchild(dir, d); 311 312 if (err_is_fail(err)) { 313 free(d); 314 } 315 316 if (ret != NULL && err_is_ok(err)) { 317 *ret = d; 318 } 319 320 return err; 321} 322 323errval_t ramfs_delete(struct dirent *e) 324{ 325 assert(e != NULL); 326 assert(e->islive && e->refcount > 0); 327 328 // check if we can delete this 329 if (e->isdir && e->u.dir.entries != NULL) { 330 return FS_ERR_NOTEMPTY; 331 } 332 333 // XXX: prevent deletion of root dir, even if empty 334 if (e->prevp == NULL) { 335 assert(e->isdir); 336 assert(e->next == NULL); 337 return FS_ERR_NOTEMPTY; // XXX 338 } 339 340 // unlink from parent directory 341 assert(e->prevp != NULL); 342 *e->prevp = e->next; 343 if (e->next != NULL) { 344 e->next->prevp = e->prevp; 345 } 346 347 // update parent's child count 348 assert(e->parent != NULL && e->parent->isdir); 349 assert(e->parent->u.dir.nentries > 0); 350 e->parent->u.dir.nentries--; 351 352 // mark dead, deref and we're done 353 e->islive = false; 354 ramfs_decref(e); 355 356 return SYS_ERR_OK; 357} 358