kern
diff src/fs.c @ 98:921a264297a4
merged the filesystem stuff
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 17 Apr 2014 17:03:30 +0300 |
parents | 07fe6a614185 |
children |
line diff
1.1 --- a/src/fs.c Thu Apr 17 12:30:02 2014 +0300 1.2 +++ b/src/fs.c Thu Apr 17 17:03:30 2014 +0300 1.3 @@ -1,120 +1,499 @@ 1.4 -/* This code is used by the kernel AND by userspace filesystem-related tools. 1.5 - * The kernel-specific parts are conditionally compiled in #ifdef KERNEL blocks 1.6 - * the rest of the code should be independent. 1.7 +/* This code is used by the kernel AND by userspace filesystem-related tools. */ 1.8 + 1.9 +/* XXX convention: 1.10 + * - functions that accept or return a struct inode, do not read/write it to disk 1.11 + * - functions that accept or return an int ino, do read/write it to disk 1.12 + * other kinds of blocks (data, indirect, etc) always hit the disk directly. 1.13 */ 1.14 + 1.15 #include <stdio.h> 1.16 #include <stdlib.h> 1.17 +#include <string.h> 1.18 +#include <errno.h> 1.19 #include <assert.h> 1.20 -#include <errno.h> 1.21 #include "fs.h" 1.22 -#include "part.h" 1.23 +#include "bdev.h" 1.24 +#include "kdef.h" 1.25 1.26 -#ifdef KERNEL 1.27 -#include "ata.h" 1.28 -#include "panic.h" 1.29 -#endif 1.30 +/* number of inodes in a block */ 1.31 +#define BLK_INODES (BLKSZ / sizeof(struct inode)) 1.32 +/* number of directory entries in a block */ 1.33 +#define BLK_DIRENT (BLKSZ / sizeof(struct dir_entry)) 1.34 1.35 -struct filesys { 1.36 - int dev; 1.37 - struct partition part; 1.38 +#define BLKBITS (BLKSZ * 8) 1.39 1.40 - struct superblock *sb; 1.41 +#define BM_IDX(x) ((x) / 32) 1.42 +#define BM_BIT(x) ((x) & 0x1f) 1.43 1.44 - struct filesys *next; 1.45 -}; 1.46 +#define BM_ISFREE(bm, x) (((bm)[BM_IDX(x)] & (1 << BM_BIT(x))) == 0) 1.47 +#define BM_SET(bm, x) ((bm)[BM_IDX(x)] |= (1 << BM_BIT(x))) 1.48 +#define BM_CLR(bm, x) ((bm)[BM_IDX(x)] &= ~(1 << BM_BIT(x))) 1.49 1.50 -static int find_rootfs(struct filesys *fs); 1.51 -static int readblock(int dev, uint32_t blk, void *buf); 1.52 -static int writeblock(int dev, uint32_t blk, void *buf); 1.53 1.54 -/* root device & partition */ 1.55 -static struct filesys *fslist; 1.56 +static struct inode *newdir(struct filesys *fs, struct inode *parent); 1.57 +static int addlink(struct filesys *fs, struct inode *target, struct inode *node, const char *name); 1.58 +static int read_superblock(struct filesys *fs); 1.59 +static int write_superblock(struct filesys *fs); 1.60 +static int get_inode(struct filesys *fs, int ino, struct inode *inode); 1.61 +static int put_inode(struct filesys *fs, struct inode *inode); 1.62 +static int find_free(uint32_t *bm, int sz); 1.63 +static int alloc_inode(struct filesys *fs); 1.64 +#define free_inode(fs, ino) BM_CLR((fs)->sb->ibm, (ino)) 1.65 +static int alloc_block(struct filesys *fs); 1.66 +#define free_block(fs, bno) BM_CLR((fs)->sb->bm, (bno)) 1.67 +#define zero_block(fs, bno) \ 1.68 + do { \ 1.69 + assert(bno > 0); \ 1.70 + blk_write((fs)->bdev, (bno), 1, (fs)->zeroblock); \ 1.71 + } while(0) 1.72 1.73 -int sys_mount(char *mtpt, char *devname, unsigned int flags) 1.74 +static int file_block(struct filesys *fs, struct inode *node, int boffs, int allocate); 1.75 +#define get_file_block(fs, node, boffs) file_block(fs, node, boffs, 0) 1.76 +#define alloc_file_block(fs, node, boffs) file_block(fs, node, boffs, 1) 1.77 + 1.78 + 1.79 +int openfs(struct filesys *fs, dev_t dev) 1.80 { 1.81 - if(strcmp(mtpt, "/") != 0) { 1.82 - printf("mount: only root can be mounted at the moment\n"); 1.83 - return -EBUG; 1.84 + int res; 1.85 + struct block_device *bdev; 1.86 + 1.87 + assert(BLKSZ % sizeof(struct inode) == 0); 1.88 + 1.89 + if(!(bdev = blk_open(dev))) { 1.90 + return -ENOENT; 1.91 + } 1.92 + fs->bdev = bdev; 1.93 + 1.94 + /* read the superblock */ 1.95 + if(!(fs->sb = malloc(BLKSZ))) { 1.96 + blk_close(bdev); 1.97 + return -ENOMEM; 1.98 + } 1.99 + if((res = read_superblock(fs)) != 0) { 1.100 + blk_close(bdev); 1.101 + return res; 1.102 } 1.103 1.104 - /* mounting root filesystem */ 1.105 - if(fslist) { 1.106 + /* allocate the zero-block buffer written to zero-out blocks */ 1.107 + if(!(fs->zeroblock = malloc(fs->sb->blksize))) { 1.108 + blk_close(bdev); 1.109 + free(fs->sb->ibm); 1.110 + free(fs->sb->bm); 1.111 + free(fs->sb->root); 1.112 + return -ENOMEM; 1.113 + } 1.114 + memset(fs->zeroblock, 0xff, fs->sb->blksize); 1.115 1.116 + return 0; 1.117 } 1.118 1.119 -void init_fs(void) 1.120 +int mkfs(struct filesys *fs, dev_t dev) 1.121 { 1.122 - root.sb = malloc(512); 1.123 - assert(root.sb); 1.124 + struct superblock *sb; 1.125 + struct block_device *bdev; 1.126 + int i, bcount; 1.127 1.128 -#ifdef KERNEL 1.129 - if(find_rootfs(&root) == -1) { 1.130 - panic("can't find root filesystem\n"); 1.131 + if(!(bdev = blk_open(dev))) { 1.132 + return -1; 1.133 } 1.134 -#endif 1.135 + fs->bdev = bdev; 1.136 + 1.137 + if(!(sb = malloc(BLKSZ))) { 1.138 + blk_close(bdev); 1.139 + return -1; 1.140 + } 1.141 + fs->sb = sb; 1.142 + 1.143 + /* populate the superblock */ 1.144 + sb->magic = MAGIC; 1.145 + sb->ver = FS_VER; 1.146 + sb->blksize = BLKSZ; 1.147 + 1.148 + sb->num_blocks = bdev->size; 1.149 + sb->num_inodes = sb->num_blocks / 4; 1.150 + 1.151 + /* inode bitmap just after the superblock */ 1.152 + sb->ibm_start = 2; 1.153 + sb->ibm_count = (sb->num_inodes + BLKBITS - 1) / BLKBITS; 1.154 + /* also allocate and initialize in-memory inode bitmap */ 1.155 + sb->ibm = malloc(sb->ibm_count * BLKSZ); 1.156 + assert(sb->ibm); 1.157 + memset(sb->ibm, 0, sb->ibm_count * BLKSZ); 1.158 + 1.159 + /* XXX mark inode 0 as used always */ 1.160 + BM_SET(sb->ibm, 0); 1.161 + 1.162 + /* block bitmap just after the inode bitmap */ 1.163 + sb->bm_start = sb->ibm_start + sb->ibm_count; 1.164 + sb->bm_count = (sb->num_blocks + BLKBITS - 1) / BLKBITS; 1.165 + /* also allocate and initialize in-memory block bitmap */ 1.166 + sb->bm = malloc(sb->bm_count * BLKSZ); 1.167 + assert(sb->bm); 1.168 + memset(sb->bm, 0, sb->bm_count * BLKSZ); 1.169 + 1.170 + /* inode table, just after the block bitmap */ 1.171 + sb->itbl_start = sb->bm_start + sb->bm_count; 1.172 + sb->itbl_count = (sb->num_inodes * sizeof(struct inode) + BLKSZ - 1) / BLKSZ; 1.173 + 1.174 + /* mark all used blocks as used */ 1.175 + bcount = sb->itbl_start + sb->itbl_count; 1.176 + memset(sb->bm, 0xff, bcount / 8); 1.177 + for(i=0; i<bcount % 8; i++) { 1.178 + int bit = bcount / 8 + i; 1.179 + BM_SET(sb->bm, bit); 1.180 + } 1.181 + 1.182 + /* create the root directory */ 1.183 + sb->root = newdir(fs, 0); 1.184 + sb->root_ino = sb->root->ino; 1.185 + /* and write the inode to disk */ 1.186 + put_inode(fs, sb->root); 1.187 + 1.188 + return 0; 1.189 } 1.190 1.191 +static struct inode *newdir(struct filesys *fs, struct inode *parent) 1.192 +{ 1.193 + struct inode *dirnode; 1.194 1.195 -#ifdef KERNEL 1.196 -#define PART_TYPE 0xcc 1.197 -static int find_rootfs(struct filesys *fs) 1.198 + /* allocate and initialize inode */ 1.199 + if(!(dirnode = malloc(sizeof *dirnode))) { 1.200 + return 0; 1.201 + } 1.202 + memset(dirnode, 0, sizeof *dirnode); 1.203 + 1.204 + if((dirnode->ino = alloc_inode(fs)) == -1) { 1.205 + printf("failed to allocate inode for a new directory\n"); 1.206 + free(dirnode); 1.207 + return 0; 1.208 + } 1.209 + dirnode->mode = S_IFDIR; 1.210 + 1.211 + /* add . and .. links */ 1.212 + addlink(fs, dirnode, dirnode, "."); 1.213 + addlink(fs, dirnode, parent ? parent : dirnode, ".."); 1.214 + 1.215 + return dirnode; 1.216 +} 1.217 + 1.218 +static int addlink(struct filesys *fs, struct inode *target, struct inode *node, const char *name) 1.219 { 1.220 - int i, num_dev, partid; 1.221 - struct partition *plist, *p; 1.222 + struct dir_entry ent, *data; 1.223 + int i, boffs, bidx, len; 1.224 1.225 - num_dev = ata_num_devices(); 1.226 - for(i=0; i<num_dev; i++) { 1.227 - plist = p = get_part_list(i); 1.228 + if(!(target->mode & S_IFDIR)) { 1.229 + return -ENOTDIR; 1.230 + } 1.231 + if(node->mode & S_IFDIR) { 1.232 + return -EPERM; 1.233 + } 1.234 + /* TODO check that the link does not already exist (EEXIST) */ 1.235 1.236 - partid = 0; 1.237 - while(p) { 1.238 - if(get_part_type(p) == PART_TYPE) { 1.239 - /* found the correct partition, now read the superblock 1.240 - * and make sure it's got the correct magic id 1.241 - */ 1.242 - readblock(i, p->start_sect / 2 + 1, fs->sb); 1.243 + if((len = strlen(name)) > NAME_MAX) { 1.244 + return -ENAMETOOLONG; 1.245 + } 1.246 + ent.ino = node->ino; 1.247 + memcpy(ent.name, name, len + 1); 1.248 1.249 - if(fs->sb->magic == MAGIC) { 1.250 - printf("found root ata%dp%d\n", i, partid); 1.251 - fs->dev = i; 1.252 - fs->part = *p; 1.253 - return 0; 1.254 + /* find a place to put it */ 1.255 + if(!(data = malloc(BLKSZ))) { 1.256 + return -ENOMEM; 1.257 + } 1.258 + 1.259 + boffs = 0; 1.260 + while((bidx = get_file_block(fs, target, boffs)) > 0) { 1.261 + /* read the block, and search for an empty entry */ 1.262 + blk_read(fs->bdev, bidx, 1, data); 1.263 + 1.264 + /* for all directory entries in this block... */ 1.265 + for(i=0; i<BLK_DIRENT; i++) { 1.266 + if(data[i].ino == 0) { 1.267 + /* found empty */ 1.268 + memcpy(data + i, &ent, sizeof ent); 1.269 + goto success; 1.270 + } 1.271 + } 1.272 + boffs++; 1.273 + } 1.274 + 1.275 + /* didn't find any free entries amongst our blocks, allocate a new one */ 1.276 + if(!(bidx = alloc_file_block(fs, target, boffs))) { 1.277 + free(data); 1.278 + return -ENOSPC; 1.279 + } 1.280 + /* zero-fill the new block and add the first entry */ 1.281 + memset(data, 0, BLKSZ); 1.282 + *data = ent; 1.283 + 1.284 +success: 1.285 + /* write to disk */ 1.286 + blk_write(fs->bdev, bidx, 1, data); 1.287 + node->nlink++; /* increase reference count */ 1.288 + 1.289 + free(data); 1.290 + return 0; 1.291 +} 1.292 + 1.293 + 1.294 +static int read_superblock(struct filesys *fs) 1.295 +{ 1.296 + struct superblock *sb = fs->sb; 1.297 + 1.298 + /* read superblock and verify */ 1.299 + if(blk_read(fs->bdev, 1, 1, sb) == -1) { 1.300 + printf("failed to read superblock\n"); 1.301 + return -EIO; 1.302 + } 1.303 + if(sb->magic != MAGIC) { 1.304 + printf("invalid magic\n"); 1.305 + return -EINVAL; 1.306 + } 1.307 + if(sb->ver > FS_VER) { 1.308 + printf("invalid version: %d\n", sb->ver); 1.309 + return -EINVAL; 1.310 + } 1.311 + if(sb->blksize != BLKSZ) { 1.312 + printf("invalid block size: %d\n", sb->blksize); 1.313 + return -EINVAL; 1.314 + } 1.315 + 1.316 + /* allocate and populate in-memory bitmaps */ 1.317 + if(!(sb->ibm = malloc(sb->ibm_count * sb->blksize))) { 1.318 + return -ENOMEM; 1.319 + } 1.320 + if(blk_read(fs->bdev, sb->ibm_start, sb->ibm_count, sb->ibm) == -1) { 1.321 + printf("failed to read inode bitmap\n"); 1.322 + free(sb->ibm); 1.323 + return -EIO; 1.324 + } 1.325 + if(!(sb->bm = malloc(sb->bm_count * sb->blksize))) { 1.326 + free(sb->ibm); 1.327 + return -ENOMEM; 1.328 + } 1.329 + if(blk_read(fs->bdev, sb->bm_start, sb->bm_count, sb->bm) == -1) { 1.330 + printf("failed to read block bitmap\n"); 1.331 + free(sb->ibm); 1.332 + free(sb->bm); 1.333 + return -EIO; 1.334 + } 1.335 + 1.336 + /* read the root inode */ 1.337 + if(!(sb->root = malloc(sizeof *sb->root))) { 1.338 + free(sb->ibm); 1.339 + free(sb->bm); 1.340 + return -ENOMEM; 1.341 + } 1.342 + if(get_inode(fs, sb->root_ino, sb->root) == -1) { 1.343 + printf("failed to read root inode\n"); 1.344 + return -1; 1.345 + } 1.346 + 1.347 + return 0; 1.348 +} 1.349 + 1.350 +static int write_superblock(struct filesys *fs) 1.351 +{ 1.352 + struct superblock *sb = fs->sb; 1.353 + 1.354 + /* write back any changes in the root inode */ 1.355 + if(put_inode(fs, sb->root) == -1) { 1.356 + return -1; 1.357 + } 1.358 + /* write back the block bitmap */ 1.359 + if(blk_write(fs->bdev, sb->bm_start, sb->bm_count, sb->bm) == -1) { 1.360 + return -1; 1.361 + } 1.362 + /* write back the inode bitmap */ 1.363 + if(blk_write(fs->bdev, sb->ibm_start, sb->ibm_count, sb->ibm) == -1) { 1.364 + return -1; 1.365 + } 1.366 + /* write the superblock itself */ 1.367 + if(blk_write(fs->bdev, 1, 1, sb) == -1) { 1.368 + return -1; 1.369 + } 1.370 + return 0; 1.371 +} 1.372 + 1.373 +/* copy the requested inode from the disk, into the buffer passed in the last arg */ 1.374 +static int get_inode(struct filesys *fs, int ino, struct inode *inode) 1.375 +{ 1.376 + struct inode *buf = malloc(BLKSZ); 1.377 + assert(buf); 1.378 + 1.379 + if(blk_read(fs->bdev, fs->sb->itbl_start + ino / BLK_INODES, 1, buf) == -1) { 1.380 + free(buf); 1.381 + return -1; 1.382 + } 1.383 + memcpy(inode, buf + ino % BLK_INODES, sizeof *inode); 1.384 + free(buf); 1.385 + return 0; 1.386 +} 1.387 + 1.388 +/* write the inode to the disk */ 1.389 +static int put_inode(struct filesys *fs, struct inode *inode) 1.390 +{ 1.391 + struct inode *buf = malloc(BLKSZ); 1.392 + assert(buf); 1.393 + 1.394 + if(blk_read(fs->bdev, fs->sb->itbl_start + inode->ino / BLK_INODES, 1, buf) == -1) { 1.395 + free(buf); 1.396 + return -1; 1.397 + } 1.398 + memcpy(buf + inode->ino % BLK_INODES, inode, sizeof *inode); 1.399 + 1.400 + if(blk_write(fs->bdev, fs->sb->itbl_start + inode->ino / BLK_INODES, 1, buf) == -1) { 1.401 + free(buf); 1.402 + return -1; 1.403 + } 1.404 + free(buf); 1.405 + return 0; 1.406 +} 1.407 + 1.408 +/* find a free element in the bitmap and return its number */ 1.409 +static int find_free(uint32_t *bm, int nbits) 1.410 +{ 1.411 + int i, j, nwords = nbits / 32; 1.412 + uint32_t ent = 0; 1.413 + 1.414 + for(i=0; i<=nwords; i++) { 1.415 + if(bm[i] != 0xffffffff) { 1.416 + for(j=0; j<32; j++) { 1.417 + if(BM_ISFREE(bm, ent)) { 1.418 + return ent; 1.419 } 1.420 + ent++; 1.421 } 1.422 - p = p->next; 1.423 - partid++; 1.424 + 1.425 + panic("shouldn't happen (in find_free:fs.c)"); 1.426 + } else { 1.427 + ent += 32; 1.428 } 1.429 - free_part_list(plist); 1.430 } 1.431 + 1.432 return -1; 1.433 } 1.434 1.435 -#define NSECT (BLKSZ / 512) 1.436 +static int alloc_inode(struct filesys *fs) 1.437 +{ 1.438 + int ino; 1.439 1.440 -static int readblock(struct block_device *bdev, uint32_t blk, void *buf) 1.441 -{ 1.442 - return blk_read(bdev, blk, buf); 1.443 + if((ino = find_free(fs->sb->ibm, fs->sb->num_inodes)) == -1) { 1.444 + return -1; 1.445 + } 1.446 + BM_SET(fs->sb->ibm, ino); 1.447 + return 0; 1.448 } 1.449 1.450 -static int writeblock(struct block_device *bdev, uint32_t blk, void *buf) 1.451 +static int alloc_block(struct filesys *fs) 1.452 { 1.453 - return blk_write(bdev, blk, buf); 1.454 -} 1.455 -#else 1.456 + int bno; 1.457 1.458 -/* if this is compiled as part of the user-space tools instead of the kernel 1.459 - * forward the call to a user read/write block function supplied by the app. 1.460 - */ 1.461 -int user_readblock(uint32_t, void*); 1.462 -int user_writeblock(uint32_t, void*); 1.463 - 1.464 -static int readblock(struct block_device *bdev, uint32_t blk, void *buf) 1.465 -{ 1.466 - return user_readblock(blk, buf); 1.467 + if((bno = find_free(fs->sb->bm, fs->sb->num_blocks)) == -1) { 1.468 + return -1; 1.469 + } 1.470 + BM_SET(fs->sb->bm, bno); 1.471 + return 0; 1.472 } 1.473 1.474 -static int writeblock(struct block_device *bdev, uint32_t blk, void *buf) 1.475 +#define BLK_BLKID (BLKSZ / sizeof(blkid)) 1.476 +#define MAX_IND (NDIRBLK + BLK_BLKID) 1.477 +#define MAX_DIND (MAX_IND + BLK_BLKID * BLK_BLKID) 1.478 + 1.479 +static int file_block(struct filesys *fs, struct inode *node, int boffs, int allocate) 1.480 { 1.481 - return user_writeblock(blk, buf); 1.482 + int res, idx, node_dirty = 0; 1.483 + blkid *barr; 1.484 + 1.485 + /* out of bounds */ 1.486 + if(boffs < 0 || boffs >= MAX_DIND) { 1.487 + return 0; 1.488 + } 1.489 + 1.490 + /* is it a direct block ? */ 1.491 + if(boffs < NDIRBLK) { 1.492 + if(!(res = node->blk[boffs]) && allocate) { 1.493 + res = node->blk[boffs] = alloc_block(fs); 1.494 + if(res) { 1.495 + zero_block(fs, res); 1.496 + /* also write back the modified inode */ 1.497 + put_inode(fs, node); 1.498 + } 1.499 + } 1.500 + return res; 1.501 + } 1.502 + 1.503 + barr = malloc(fs->sb->blksize); 1.504 + assert(barr); 1.505 + 1.506 + /* is it an indirect block ? */ 1.507 + if(boffs < MAX_IND) { 1.508 + int ind_dirty = 0; 1.509 + 1.510 + if(node->ind) { 1.511 + /* read the indirect block */ 1.512 + blk_read(fs->bdev, node->ind, 1, barr); 1.513 + } else { 1.514 + /* does not exist... try to allocate if requested */ 1.515 + if(!allocate || !(node->ind = alloc_block(fs))) { 1.516 + res = 0; 1.517 + goto end; 1.518 + } 1.519 + 1.520 + /* allocated a block clear the buffer, and invalidate everything */ 1.521 + memset(barr, 0, sizeof fs->sb->blksize); 1.522 + node_dirty = 1; 1.523 + ind_dirty = 1; 1.524 + } 1.525 + 1.526 + idx = boffs - NDIRBLK; 1.527 + 1.528 + if(!(res = barr[idx])) { 1.529 + if(allocate && (res = barr[idx] = alloc_block(fs))) { 1.530 + ind_dirty = 1; 1.531 + } 1.532 + } 1.533 + 1.534 + /* write back the indirect block if needed */ 1.535 + if(ind_dirty) { 1.536 + blk_write(fs->bdev, node->ind, 1, barr); 1.537 + } 1.538 + goto end; 1.539 + } 1.540 + 1.541 + /* TODO check/rewrite this */ 1.542 +#if 0 1.543 + /* is it a double-indirect block ? */ 1.544 + if(boffs < MAX_DIND) { 1.545 + /* first read the dind block and find the index of the ind block */ 1.546 + if(!node->dind) { 1.547 + if(allocate) { 1.548 + /* allocate and zero-out the double indirect block */ 1.549 + res = node->dind = alloc_block(fs); 1.550 + if(res) { 1.551 + zero_block(fs, res); 1.552 + } 1.553 + } else { 1.554 + res = 0; 1.555 + goto end; 1.556 + } 1.557 + } 1.558 + blk_read(fd->bdev, node->dind, 1, barr); 1.559 + idx = (boffs - MAX_IND) / BLK_BLKID; 1.560 + 1.561 + /* then read the ind block and find the index of the block */ 1.562 + if(!barr[idx]) { 1.563 + res = 0; 1.564 + goto end; 1.565 + } 1.566 + blk_read(fd->bdev, barr[idx], 1, barr); 1.567 + res = barr[(boffs - MAX_IND) % BLK_BLKID]; 1.568 + } 1.569 +#endif 1.570 + 1.571 +end: 1.572 + if(node_dirty) { 1.573 + put_inode(fs, node); 1.574 + } 1.575 + free(barr); 1.576 + return res; 1.577 } 1.578 -#endif /* KERNEL */