1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657 |
- /* vim: tabstop=4 shiftwidth=4 noexpandtab
- * This file is part of ToaruOS and is released under the terms
- * of the NCSA / University of Illinois License - see LICENSE.md
- * Copyright (C) 2014-2018 K. Lange
- */
- #include <kernel/system.h>
- #include <kernel/types.h>
- #include <kernel/fs.h>
- #include <kernel/ext2.h>
- #include <kernel/logging.h>
- #include <kernel/module.h>
- #include <kernel/args.h>
- #include <kernel/printf.h>
- #include <kernel/tokenize.h>
- #define EXT2_BGD_BLOCK 2
- #define E_SUCCESS 0
- #define E_BADBLOCK 1
- #define E_NOSPACE 2
- #define E_BADPARENT 3
- #undef _symlink
- #define _symlink(inode) ((char *)(inode)->block)
- /*
- * EXT2 filesystem object
- */
- typedef struct {
- ext2_superblock_t * superblock; /* Device superblock, contains important information */
- ext2_bgdescriptor_t * block_groups; /* Block Group Descriptor / Block groups */
- fs_node_t * root_node; /* Root FS node (attached to mountpoint) */
- fs_node_t * block_device; /* Block device node XXX unused */
- unsigned int block_size; /* Size of one block */
- unsigned int pointers_per_block; /* Number of pointers that fit in a block */
- unsigned int inodes_per_group; /* Number of inodes in a "group" */
- unsigned int block_group_count; /* Number of blocks groups */
- ext2_disk_cache_entry_t * disk_cache; /* Dynamically allocated array of cache entries */
- unsigned int cache_entries; /* Size of ->disk_cache */
- unsigned int cache_time; /* "timer" that increments with each cache read/write */
- spin_lock_t lock; /* Synchronization lock point */
- uint8_t bgd_block_span;
- uint8_t bgd_offset;
- unsigned int inode_size;
- uint8_t * cache_data;
- int flags;
- } ext2_fs_t;
- #define EXT2_FLAG_NOCACHE 0x0001
- /*
- * These macros were used in the original toaru ext2 driver.
- * They make referring to some of the core parts of the drive a bit easier.
- */
- #define BGDS (this->block_group_count)
- #define SB (this->superblock)
- #define BGD (this->block_groups)
- #define RN (this->root_node)
- #define DC (this->disk_cache)
- /*
- * These macros deal with the block group descriptor bitmap
- */
- #define BLOCKBIT(n) (bg_buffer[((n) >> 3)] & (1 << (((n) % 8))))
- #define BLOCKBYTE(n) (bg_buffer[((n) >> 3)])
- #define SETBIT(n) (1 << (((n) % 8)))
- static uint32_t node_from_file(ext2_fs_t * this, ext2_inodetable_t *inode, ext2_dir_t *direntry, fs_node_t *fnode);
- static uint32_t ext2_root(ext2_fs_t * this, ext2_inodetable_t *inode, fs_node_t *fnode);
- static ext2_inodetable_t * read_inode(ext2_fs_t * this, uint32_t inode);
- static void refresh_inode(ext2_fs_t * this, ext2_inodetable_t * inodet, uint32_t inode);
- static int write_inode(ext2_fs_t * this, ext2_inodetable_t *inode, uint32_t index);
- static fs_node_t * finddir_ext2(fs_node_t *node, char *name);
- static unsigned int allocate_block(ext2_fs_t * this);
- /**
- * ext2->get_cache_time Increment and return the current cache time
- *
- * @returns Current cache time
- */
- static unsigned int get_cache_time(ext2_fs_t * this) {
- return this->cache_time++;
- }
- /**
- * ext2->cache_flush_dirty Flush dirty cache entry to the disk.
- *
- * @param ent_no Cache entry to dump
- * @returns Error code or E_SUCCESS
- */
- static int cache_flush_dirty(ext2_fs_t * this, unsigned int ent_no) {
- write_fs(this->block_device, (DC[ent_no].block_no) * this->block_size, this->block_size, (uint8_t *)(DC[ent_no].block));
- DC[ent_no].dirty = 0;
- return E_SUCCESS;
- }
- /**
- * ext2->rewrite_superblock Rewrite the superblock.
- *
- * Superblocks are a bit different from other blocks, as they are always in the same place,
- * regardless of what the filesystem block size is. This doesn't work well with our setup,
- * so we need to special-case it.
- */
- static int rewrite_superblock(ext2_fs_t * this) {
- write_fs(this->block_device, 1024, sizeof(ext2_superblock_t), (uint8_t *)SB);
- return E_SUCCESS;
- }
- /**
- * ext2->read_block Read a block from the block device associated with this filesystem.
- *
- * The read block will be copied into the buffer pointed to by `buf`.
- *
- * @param block_no Number of block to read.
- * @param buf Where to put the data read.
- * @returns Error code or E_SUCCESS
- */
- static int read_block(ext2_fs_t * this, unsigned int block_no, uint8_t * buf) {
- /* 0 is an invalid block number. So is anything beyond the total block count, but we can't check that. */
- if (!block_no) {
- return E_BADBLOCK;
- }
- /* This operation requires the filesystem lock to be obtained */
- spin_lock(this->lock);
- /* We can make reads without a cache in place. */
- if (!DC) {
- /* In such cases, we read directly from the block device */
- read_fs(this->block_device, block_no * this->block_size, this->block_size, (uint8_t *)buf);
- /* We are done, release the lock */
- spin_unlock(this->lock);
- /* And return SUCCESS */
- return E_SUCCESS;
- }
- /*
- * Search the cache for this entry
- * We'll look for the oldest entry, too.
- */
- int oldest = -1;
- unsigned int oldest_age = UINT32_MAX;
- for (unsigned int i = 0; i < this->cache_entries; ++i) {
- if (DC[i].block_no == block_no) {
- /* We found it! Update usage times */
- DC[i].last_use = get_cache_time(this);
- /* Read the block */
- memcpy(buf, DC[i].block, this->block_size);
- /* Release the lock */
- spin_unlock(this->lock);
- /* Success! */
- return E_SUCCESS;
- }
- if (DC[i].last_use < oldest_age) {
- /* We found an older block, remember this. */
- oldest = i;
- oldest_age = DC[i].last_use;
- }
- }
- /*
- * At this point, we did not find this block in the cache.
- * We are going to replace the oldest entry with this new one.
- */
- /* We'll start by flushing the block if it was dirty. */
- if (DC[oldest].dirty) {
- cache_flush_dirty(this, oldest);
- }
- /* Then we'll read the new one */
- read_fs(this->block_device, block_no * this->block_size, this->block_size, (uint8_t *)DC[oldest].block);
- /* And copy the results to the output buffer */
- memcpy(buf, DC[oldest].block, this->block_size);
- /* And update the cache entry to point to the new block */
- DC[oldest].block_no = block_no;
- DC[oldest].last_use = get_cache_time(this);
- DC[oldest].dirty = 0;
- /* Release the lock */
- spin_unlock(this->lock);
- /* And return success */
- return E_SUCCESS;
- }
- /**
- * ext2->write_block Write a block to the block device.
- *
- * @param block_no Block to write
- * @param buf Data in the block
- * @returns Error code or E_SUCCESSS
- */
- static int write_block(ext2_fs_t * this, unsigned int block_no, uint8_t *buf) {
- if (!block_no) {
- debug_print(ERROR, "Attempted to write to block #0. Enable tracing and retry this operation.");
- debug_print(ERROR, "Your file system is most likely corrupted now.");
- return E_BADBLOCK;
- }
- /* This operation requires the filesystem lock */
- spin_lock(this->lock);
- if (!DC) {
- write_fs(this->block_device, block_no * this->block_size, this->block_size, buf);
- spin_unlock(this->lock);
- return E_SUCCESS;
- }
- /* Find the entry in the cache */
- int oldest = -1;
- unsigned int oldest_age = UINT32_MAX;
- for (unsigned int i = 0; i < this->cache_entries; ++i) {
- if (DC[i].block_no == block_no) {
- /* We found it. Update the cache entry */
- DC[i].last_use = get_cache_time(this);
- DC[i].dirty = 1;
- memcpy(DC[i].block, buf, this->block_size);
- spin_unlock(this->lock);
- return E_SUCCESS;
- }
- if (DC[i].last_use < oldest_age) {
- /* Keep track of the oldest entry */
- oldest = i;
- oldest_age = DC[i].last_use;
- }
- }
- /* We did not find this element in the cache, so make room. */
- if (DC[oldest].dirty) {
- /* Flush the oldest entry */
- cache_flush_dirty(this, oldest);
- }
- /* Update the entry */
- memcpy(DC[oldest].block, buf, this->block_size);
- DC[oldest].block_no = block_no;
- DC[oldest].last_use = get_cache_time(this);
- DC[oldest].dirty = 1;
- /* Release the lock */
- spin_unlock(this->lock);
- /* We're done. */
- return E_SUCCESS;
- }
- static unsigned int ext2_sync(ext2_fs_t * this) {
- if (!this->disk_cache) return 0;
- /* This operation requires the filesystem lock */
- spin_lock(this->lock);
- /* Flush each cache entry. */
- for (unsigned int i = 0; i < this->cache_entries; ++i) {
- if (DC[i].dirty) {
- cache_flush_dirty(this, i);
- }
- }
- /* Release the lock */
- spin_unlock(this->lock);
- return 0;
- }
- /**
- * ext2->set_block_number Set the "real" block number for a given "inode" block number.
- *
- * @param inode Inode to operate on
- * @param iblock Block offset within the inode
- * @param rblock Real block number
- * @returns Error code or E_SUCCESS
- */
- static unsigned int set_block_number(ext2_fs_t * this, ext2_inodetable_t * inode, unsigned int inode_no, unsigned int iblock, unsigned int rblock) {
- unsigned int p = this->pointers_per_block;
- /* We're going to do some crazy math in a bit... */
- unsigned int a, b, c, d, e, f, g;
- uint8_t * tmp;
- if (iblock < EXT2_DIRECT_BLOCKS) {
- inode->block[iblock] = rblock;
- return E_SUCCESS;
- } else if (iblock < EXT2_DIRECT_BLOCKS + p) {
- /* XXX what if inode->block[EXT2_DIRECT_BLOCKS] isn't set? */
- if (!inode->block[EXT2_DIRECT_BLOCKS]) {
- unsigned int block_no = allocate_block(this);
- if (!block_no) return E_NOSPACE;
- inode->block[EXT2_DIRECT_BLOCKS] = block_no;
- write_inode(this, inode, inode_no);
- }
- tmp = malloc(this->block_size);
- read_block(this, inode->block[EXT2_DIRECT_BLOCKS], (uint8_t *)tmp);
- ((uint32_t *)tmp)[iblock - EXT2_DIRECT_BLOCKS] = rblock;
- write_block(this, inode->block[EXT2_DIRECT_BLOCKS], (uint8_t *)tmp);
- free(tmp);
- return E_SUCCESS;
- } else if (iblock < EXT2_DIRECT_BLOCKS + p + p * p) {
- a = iblock - EXT2_DIRECT_BLOCKS;
- b = a - p;
- c = b / p;
- d = b - c * p;
- if (!inode->block[EXT2_DIRECT_BLOCKS+1]) {
- unsigned int block_no = allocate_block(this);
- if (!block_no) return E_NOSPACE;
- inode->block[EXT2_DIRECT_BLOCKS+1] = block_no;
- write_inode(this, inode, inode_no);
- }
- tmp = malloc(this->block_size);
- read_block(this, inode->block[EXT2_DIRECT_BLOCKS + 1], (uint8_t *)tmp);
- if (!((uint32_t *)tmp)[c]) {
- unsigned int block_no = allocate_block(this);
- if (!block_no) goto no_space_free;
- ((uint32_t *)tmp)[c] = block_no;
- write_block(this, inode->block[EXT2_DIRECT_BLOCKS + 1], (uint8_t *)tmp);
- }
- uint32_t nblock = ((uint32_t *)tmp)[c];
- read_block(this, nblock, (uint8_t *)tmp);
- ((uint32_t *)tmp)[d] = rblock;
- write_block(this, nblock, (uint8_t *)tmp);
- free(tmp);
- return E_SUCCESS;
- } else if (iblock < EXT2_DIRECT_BLOCKS + p + p * p + p) {
- a = iblock - EXT2_DIRECT_BLOCKS;
- b = a - p;
- c = b - p * p;
- d = c / (p * p);
- e = c - d * p * p;
- f = e / p;
- g = e - f * p;
- if (!inode->block[EXT2_DIRECT_BLOCKS+2]) {
- unsigned int block_no = allocate_block(this);
- if (!block_no) return E_NOSPACE;
- inode->block[EXT2_DIRECT_BLOCKS+2] = block_no;
- write_inode(this, inode, inode_no);
- }
- tmp = malloc(this->block_size);
- read_block(this, inode->block[EXT2_DIRECT_BLOCKS + 2], (uint8_t *)tmp);
- if (!((uint32_t *)tmp)[d]) {
- unsigned int block_no = allocate_block(this);
- if (!block_no) goto no_space_free;
- ((uint32_t *)tmp)[d] = block_no;
- write_block(this, inode->block[EXT2_DIRECT_BLOCKS + 2], (uint8_t *)tmp);
- }
- uint32_t nblock = ((uint32_t *)tmp)[d];
- read_block(this, nblock, (uint8_t *)tmp);
- if (!((uint32_t *)tmp)[f]) {
- unsigned int block_no = allocate_block(this);
- if (!block_no) goto no_space_free;
- ((uint32_t *)tmp)[f] = block_no;
- write_block(this, nblock, (uint8_t *)tmp);
- }
- nblock = ((uint32_t *)tmp)[f];
- read_block(this, nblock, (uint8_t *)tmp);
- ((uint32_t *)tmp)[g] = nblock;
- write_block(this, nblock, (uint8_t *)tmp);
- free(tmp);
- return E_SUCCESS;
- }
- debug_print(CRITICAL, "EXT2 driver tried to write to a block number that was too high (%d)", rblock);
- return E_BADBLOCK;
- no_space_free:
- free(tmp);
- return E_NOSPACE;
- }
- /**
- * ext2->get_block_number Given an inode block number, get the real block number.
- *
- * @param inode Inode to operate on
- * @param iblock Block offset within the inode
- * @returns Real block number
- */
- static unsigned int get_block_number(ext2_fs_t * this, ext2_inodetable_t * inode, unsigned int iblock) {
- unsigned int p = this->pointers_per_block;
- /* We're going to do some crazy math in a bit... */
- unsigned int a, b, c, d, e, f, g;
- uint8_t * tmp;
- if (iblock < EXT2_DIRECT_BLOCKS) {
- return inode->block[iblock];
- } else if (iblock < EXT2_DIRECT_BLOCKS + p) {
- /* XXX what if inode->block[EXT2_DIRECT_BLOCKS] isn't set? */
- tmp = malloc(this->block_size);
- read_block(this, inode->block[EXT2_DIRECT_BLOCKS], (uint8_t *)tmp);
- unsigned int out = ((uint32_t *)tmp)[iblock - EXT2_DIRECT_BLOCKS];
- free(tmp);
- return out;
- } else if (iblock < EXT2_DIRECT_BLOCKS + p + p * p) {
- a = iblock - EXT2_DIRECT_BLOCKS;
- b = a - p;
- c = b / p;
- d = b - c * p;
- tmp = malloc(this->block_size);
- read_block(this, inode->block[EXT2_DIRECT_BLOCKS + 1], (uint8_t *)tmp);
- uint32_t nblock = ((uint32_t *)tmp)[c];
- read_block(this, nblock, (uint8_t *)tmp);
- unsigned int out = ((uint32_t *)tmp)[d];
- free(tmp);
- return out;
- } else if (iblock < EXT2_DIRECT_BLOCKS + p + p * p + p) {
- a = iblock - EXT2_DIRECT_BLOCKS;
- b = a - p;
- c = b - p * p;
- d = c / (p * p);
- e = c - d * p * p;
- f = e / p;
- g = e - f * p;
- tmp = malloc(this->block_size);
- read_block(this, inode->block[EXT2_DIRECT_BLOCKS + 2], (uint8_t *)tmp);
- uint32_t nblock = ((uint32_t *)tmp)[d];
- read_block(this, nblock, (uint8_t *)tmp);
- nblock = ((uint32_t *)tmp)[f];
- read_block(this, nblock, (uint8_t *)tmp);
- unsigned int out = ((uint32_t *)tmp)[g];
- free(tmp);
- return out;
- }
- debug_print(CRITICAL, "EXT2 driver tried to read to a block number that was too high (%d)", iblock);
- return 0;
- }
- static int write_inode(ext2_fs_t * this, ext2_inodetable_t *inode, uint32_t index) {
- uint32_t group = index / this->inodes_per_group;
- if (group > BGDS) {
- return E_BADBLOCK;
- }
- uint32_t inode_table_block = BGD[group].inode_table;
- index -= group * this->inodes_per_group;
- uint32_t block_offset = ((index - 1) * this->inode_size) / this->block_size;
- uint32_t offset_in_block = (index - 1) - block_offset * (this->block_size / this->inode_size);
- ext2_inodetable_t *inodet = malloc(this->block_size);
- /* Read the current table block */
- read_block(this, inode_table_block + block_offset, (uint8_t *)inodet);
- memcpy((uint8_t *)((uint32_t)inodet + offset_in_block * this->inode_size), inode, this->inode_size);
- write_block(this, inode_table_block + block_offset, (uint8_t *)inodet);
- free(inodet);
- return E_SUCCESS;
- }
- static unsigned int allocate_block(ext2_fs_t * this) {
- unsigned int block_no = 0;
- unsigned int block_offset = 0;
- unsigned int group = 0;
- uint8_t * bg_buffer = malloc(this->block_size);
- for (unsigned int i = 0; i < BGDS; ++i) {
- if (BGD[i].free_blocks_count > 0) {
- read_block(this, BGD[i].block_bitmap, (uint8_t *)bg_buffer);
- while (BLOCKBIT(block_offset)) {
- ++block_offset;
- }
- block_no = block_offset + SB->blocks_per_group * i;
- group = i;
- break;
- }
- }
- if (!block_no) {
- debug_print(CRITICAL, "No available blocks, disk is out of space!");
- free(bg_buffer);
- return 0;
- }
- debug_print(WARNING, "allocating block #%d (group %d)", block_no, group);
- BLOCKBYTE(block_offset) |= SETBIT(block_offset);
- write_block(this, BGD[group].block_bitmap, (uint8_t *)bg_buffer);
- BGD[group].free_blocks_count--;
- for (int i = 0; i < this->bgd_block_span; ++i) {
- write_block(this, this->bgd_offset + i, (uint8_t *)((uint32_t)BGD + this->block_size * i));
- }
- SB->free_blocks_count--;
- rewrite_superblock(this);
- memset(bg_buffer, 0x00, this->block_size);
- write_block(this, block_no, bg_buffer);
- free(bg_buffer);
- return block_no;
- }
- /**
- * ext2->allocate_inode_block Allocate a block in an inode.
- *
- * @param inode Inode to operate on
- * @param inode_no Number of the inode (this is not part of the struct)
- * @param block Block within inode to allocate
- * @returns Error code or E_SUCCESS
- */
- static int allocate_inode_block(ext2_fs_t * this, ext2_inodetable_t * inode, unsigned int inode_no, unsigned int block) {
- debug_print(NOTICE, "Allocating block #%d for inode #%d", block, inode_no);
- unsigned int block_no = allocate_block(this);
- if (!block_no) return E_NOSPACE;
- set_block_number(this, inode, inode_no, block, block_no);
- unsigned int t = (block + 1) * (this->block_size / 512);
- if (inode->blocks < t) {
- debug_print(NOTICE, "Setting inode->blocks to %d = (%d fs blocks)", t, t / (this->block_size / 512));
- inode->blocks = t;
- }
- write_inode(this, inode, inode_no);
- return E_SUCCESS;
- }
- /**
- * ext2->inode_read_block
- *
- * @param inode
- * @param no
- * @param block
- * @parma buf
- * @returns Real block number for reference.
- */
- static unsigned int inode_read_block(ext2_fs_t * this, ext2_inodetable_t * inode, unsigned int block, uint8_t * buf) {
- if (block >= inode->blocks / (this->block_size / 512)) {
- memset(buf, 0x00, this->block_size);
- debug_print(WARNING, "Tried to read an invalid block. Asked for %d (0-indexed), but inode only has %d!", block, inode->blocks / (this->block_size / 512));
- return 0;
- }
- unsigned int real_block = get_block_number(this, inode, block);
- read_block(this, real_block, buf);
- return real_block;
- }
- /**
- * ext2->inode_write_block
- */
- static unsigned int inode_write_block(ext2_fs_t * this, ext2_inodetable_t * inode, unsigned int inode_no, unsigned int block, uint8_t * buf) {
- if (block >= inode->blocks / (this->block_size / 512)) {
- debug_print(WARNING, "Attempting to write beyond the existing allocated blocks for this inode.");
- debug_print(WARNING, "Inode %d, Block %d", inode_no, block);
- }
- debug_print(WARNING, "clearing and allocating up to required blocks (block=%d, %d)", block, inode->blocks);
- char * empty = NULL;
- while (block >= inode->blocks / (this->block_size / 512)) {
- allocate_inode_block(this, inode, inode_no, inode->blocks / (this->block_size / 512));
- refresh_inode(this, inode, inode_no);
- }
- if (empty) free(empty);
- debug_print(WARNING, "... done");
- unsigned int real_block = get_block_number(this, inode, block);
- debug_print(WARNING, "Writing virtual block %d for inode %d maps to real block %d", block, inode_no, real_block);
- write_block(this, real_block, buf);
- return real_block;
- }
- /**
- * ext2->create_entry
- *
- * @returns Error code or E_SUCCESS
- */
- static int create_entry(fs_node_t * parent, char * name, uint32_t inode) {
- ext2_fs_t * this = (ext2_fs_t *)parent->device;
- ext2_inodetable_t * pinode = read_inode(this,parent->inode);
- if (((pinode->mode & EXT2_S_IFDIR) == 0) || (name == NULL)) {
- debug_print(WARNING, "Attempted to allocate an inode in a parent that was not a directory.");
- return E_BADPARENT;
- }
- debug_print(WARNING, "Creating a directory entry for %s pointing to inode %d.", name, inode);
- /* okay, how big is it... */
- debug_print(WARNING, "We need to append %d bytes to the direcotry.", sizeof(ext2_dir_t) + strlen(name));
- unsigned int rec_len = sizeof(ext2_dir_t) + strlen(name);
- rec_len += (rec_len % 4) ? (4 - (rec_len % 4)) : 0;
- debug_print(WARNING, "Our directory entry looks like this:");
- debug_print(WARNING, " inode = %d", inode);
- debug_print(WARNING, " rec_len = %d", rec_len);
- debug_print(WARNING, " name_len = %d", strlen(name));
- debug_print(WARNING, " file_type = %d", 0);
- debug_print(WARNING, " name = %s", name);
- debug_print(WARNING, "The inode size is marked as: %d", pinode->size);
- debug_print(WARNING, "Block size is %d", this->block_size);
- uint8_t * block = malloc(this->block_size);
- uint8_t block_nr = 0;
- uint32_t dir_offset = 0;
- uint32_t total_offset = 0;
- int modify_or_replace = 0;
- ext2_dir_t *previous;
- inode_read_block(this, pinode, block_nr, block);
- while (total_offset < pinode->size) {
- if (dir_offset >= this->block_size) {
- block_nr++;
- dir_offset -= this->block_size;
- inode_read_block(this, pinode, block_nr, block);
- }
- ext2_dir_t *d_ent = (ext2_dir_t *)((uintptr_t)block + dir_offset);
- unsigned int sreclen = d_ent->name_len + sizeof(ext2_dir_t);
- sreclen += (sreclen % 4) ? (4 - (sreclen % 4)) : 0;
- {
- char f[d_ent->name_len+1];
- memcpy(f, d_ent->name, d_ent->name_len);
- f[d_ent->name_len] = 0;
- debug_print(WARNING, " * file: %s", f);
- }
- debug_print(WARNING, " rec_len: %d", d_ent->rec_len);
- debug_print(WARNING, " type: %d", d_ent->file_type);
- debug_print(WARNING, " namel: %d", d_ent->name_len);
- debug_print(WARNING, " inode: %d", d_ent->inode);
- if (d_ent->rec_len != sreclen && total_offset + d_ent->rec_len == pinode->size) {
- debug_print(WARNING, " - should be %d, but instead points to end of block", sreclen);
- debug_print(WARNING, " - we've hit the end, should change this pointer");
- dir_offset += sreclen;
- total_offset += sreclen;
- modify_or_replace = 1; /* Modify */
- previous = d_ent;
- break;
- }
- if (d_ent->inode == 0) {
- modify_or_replace = 2; /* Replace */
- }
- dir_offset += d_ent->rec_len;
- total_offset += d_ent->rec_len;
- }
- if (!modify_or_replace) {
- debug_print(WARNING, "That's odd, this shouldn't have happened, we made it all the way here without hitting our two end conditions?");
- }
- if (modify_or_replace == 1) {
- debug_print(WARNING, "The last node in the list is a real node, we need to modify it.");
- if (dir_offset + rec_len >= this->block_size) {
- debug_print(WARNING, "Need to allocate more space, bail!");
- free(block);
- return E_NOSPACE;
- } else {
- unsigned int sreclen = previous->name_len + sizeof(ext2_dir_t);
- sreclen += (sreclen % 4) ? (4 - (sreclen % 4)) : 0;
- previous->rec_len = sreclen;
- debug_print(WARNING, "Set previous node rec_len to %d", sreclen);
- }
- } else if (modify_or_replace == 2) {
- debug_print(WARNING, "The last node in the list is a fake node, we'll replace it.");
- }
- debug_print(WARNING, " total_offset = 0x%x", total_offset);
- debug_print(WARNING, " dir_offset = 0x%x", dir_offset);
- ext2_dir_t *d_ent = (ext2_dir_t *)((uintptr_t)block + dir_offset);
- d_ent->inode = inode;
- d_ent->rec_len = this->block_size - dir_offset;
- d_ent->name_len = strlen(name);
- d_ent->file_type = 0; /* This is unused */
- memcpy(d_ent->name, name, strlen(name));
- inode_write_block(this, pinode, parent->inode, block_nr, block);
- free(block);
- free(pinode);
- return E_NOSPACE;
- }
- static unsigned int allocate_inode(ext2_fs_t * this) {
- uint32_t node_no = 0;
- uint32_t node_offset = 0;
- uint32_t group = 0;
- uint8_t * bg_buffer = malloc(this->block_size);
- for (unsigned int i = 0; i < BGDS; ++i) {
- if (BGD[i].free_inodes_count > 0) {
- debug_print(NOTICE, "Group %d has %d free inodes.", i, BGD[i].free_inodes_count);
- read_block(this, BGD[i].inode_bitmap, (uint8_t *)bg_buffer);
- while (BLOCKBIT(node_offset)) {
- node_offset++;
- }
- node_no = node_offset + i * this->inodes_per_group + 1;
- group = i;
- break;
- }
- }
- if (!node_no) {
- debug_print(ERROR, "Ran out of inodes!");
- return 0;
- }
- BLOCKBYTE(node_offset) |= SETBIT(node_offset);
- write_block(this, BGD[group].inode_bitmap, (uint8_t *)bg_buffer);
- free(bg_buffer);
- BGD[group].free_inodes_count--;
- for (int i = 0; i < this->bgd_block_span; ++i) {
- write_block(this, this->bgd_offset + i, (uint8_t *)((uint32_t)BGD + this->block_size * i));
- }
- SB->free_inodes_count--;
- rewrite_superblock(this);
- return node_no;
- }
- static int mkdir_ext2(fs_node_t * parent, char * name, uint16_t permission) {
- if (!name) return -EINVAL;
- ext2_fs_t * this = parent->device;
- /* first off, check if it exists */
- fs_node_t * check = finddir_ext2(parent, name);
- if (check) {
- debug_print(WARNING, "A file by this name already exists: %s", name);
- free(check);
- return -EEXIST;
- }
- /* Allocate an inode for it */
- unsigned int inode_no = allocate_inode(this);
- ext2_inodetable_t * inode = read_inode(this,inode_no);
- /* Set the access and creation times to now */
- inode->atime = now();
- inode->ctime = inode->atime;
- inode->mtime = inode->atime;
- inode->dtime = 0; /* This inode was never deleted */
- /* Empty the file */
- memset(inode->block, 0x00, sizeof(inode->block));
- inode->blocks = 0;
- inode->size = 0; /* empty */
- /* Assign it to root */
- inode->uid = current_process->user; /* user */
- inode->gid = current_process->user;
- /* misc */
- inode->faddr = 0;
- inode->links_count = 2; /* There's the parent's pointer to us, and our pointer to us. */
- inode->flags = 0;
- inode->osd1 = 0;
- inode->generation = 0;
- inode->file_acl = 0;
- inode->dir_acl = 0;
- /* File mode */
- inode->mode = EXT2_S_IFDIR;
- inode->mode |= 0xFFF & permission;
- /* Write the osd blocks to 0 */
- memset(inode->osd2, 0x00, sizeof(inode->osd2));
- /* Write out inode changes */
- write_inode(this, inode, inode_no);
- /* Now append the entry to the parent */
- create_entry(parent, name, inode_no);
- inode->size = this->block_size;
- write_inode(this, inode, inode_no);
- uint8_t * tmp = malloc(this->block_size);
- ext2_dir_t * t = calloc(12,1);
- t->inode = inode_no;
- t->rec_len = 12;
- t->name_len = 1;
- t->name[0] = '.';
- memcpy(&tmp[0], t, 12);
- t->inode = parent->inode;
- t->name_len = 2;
- t->name[1] = '.';
- t->rec_len = this->block_size - 12;
- memcpy(&tmp[12], t, 12);
- free(t);
- inode_write_block(this, inode, inode_no, 0, tmp);
- free(inode);
- free(tmp);
- /* Update parent link count */
- ext2_inodetable_t * pinode = read_inode(this, parent->inode);
- pinode->links_count++;
- write_inode(this, pinode, parent->inode);
- free(pinode);
- /* Update directory count in block group descriptor */
- uint32_t group = inode_no / this->inodes_per_group;
- BGD[group].used_dirs_count++;
- for (int i = 0; i < this->bgd_block_span; ++i) {
- write_block(this, this->bgd_offset + i, (uint8_t *)((uint32_t)BGD + this->block_size * i));
- }
- ext2_sync(this);
- return 0;
- }
- static int create_ext2(fs_node_t * parent, char * name, uint16_t permission) {
- if (!name) return -EINVAL;
- ext2_fs_t * this = parent->device;
- /* first off, check if it exists */
- fs_node_t * check = finddir_ext2(parent, name);
- if (check) {
- debug_print(WARNING, "A file by this name already exists: %s", name);
- free(check);
- return -EEXIST;
- }
- /* Allocate an inode for it */
- unsigned int inode_no = allocate_inode(this);
- ext2_inodetable_t * inode = read_inode(this,inode_no);
- /* Set the access and creation times to now */
- inode->atime = now();
- inode->ctime = inode->atime;
- inode->mtime = inode->atime;
- inode->dtime = 0; /* This inode was never deleted */
- /* Empty the file */
- memset(inode->block, 0x00, sizeof(inode->block));
- inode->blocks = 0;
- inode->size = 0; /* empty */
- /* Assign it to root */
- inode->uid = current_process->user; /* user */
- inode->gid = current_process->user;
- /* misc */
- inode->faddr = 0;
- inode->links_count = 1; /* The one we're about to create. */
- inode->flags = 0;
- inode->osd1 = 0;
- inode->generation = 0;
- inode->file_acl = 0;
- inode->dir_acl = 0;
- /* File mode */
- /* TODO: Use the mask from `permission` */
- inode->mode = EXT2_S_IFREG;
- inode->mode |= 0xFFF & permission;
- /* Write the osd blocks to 0 */
- memset(inode->osd2, 0x00, sizeof(inode->osd2));
- /* Write out inode changes */
- write_inode(this, inode, inode_no);
- /* Now append the entry to the parent */
- create_entry(parent, name, inode_no);
- free(inode);
- ext2_sync(this);
- return 0;
- }
- static int chmod_ext2(fs_node_t * node, int mode) {
- ext2_fs_t * this = node->device;
- ext2_inodetable_t * inode = read_inode(this,node->inode);
- inode->mode = (inode->mode & 0xFFFFF000) | mode;
- write_inode(this, inode, node->inode);
- ext2_sync(this);
- return 0;
- }
- /**
- * direntry_ext2
- */
- static ext2_dir_t * direntry_ext2(ext2_fs_t * this, ext2_inodetable_t * inode, uint32_t no, uint32_t index) {
- uint8_t *block = malloc(this->block_size);
- uint8_t block_nr = 0;
- inode_read_block(this, inode, block_nr, block);
- uint32_t dir_offset = 0;
- uint32_t total_offset = 0;
- uint32_t dir_index = 0;
- while (total_offset < inode->size && dir_index <= index) {
- ext2_dir_t *d_ent = (ext2_dir_t *)((uintptr_t)block + dir_offset);
- if (d_ent->inode != 0 && dir_index == index) {
- ext2_dir_t *out = malloc(d_ent->rec_len);
- memcpy(out, d_ent, d_ent->rec_len);
- free(block);
- return out;
- }
- dir_offset += d_ent->rec_len;
- total_offset += d_ent->rec_len;
- if (d_ent->inode) {
- dir_index++;
- }
- if (dir_offset >= this->block_size) {
- block_nr++;
- dir_offset -= this->block_size;
- inode_read_block(this, inode, block_nr, block);
- }
- }
- free(block);
- return NULL;
- }
- /**
- * finddir_ext2
- */
- static fs_node_t * finddir_ext2(fs_node_t *node, char *name) {
- ext2_fs_t * this = (ext2_fs_t *)node->device;
- ext2_inodetable_t *inode = read_inode(this,node->inode);
- assert(inode->mode & EXT2_S_IFDIR);
- uint8_t * block = malloc(this->block_size);
- ext2_dir_t *direntry = NULL;
- uint8_t block_nr = 0;
- inode_read_block(this, inode, block_nr, block);
- uint32_t dir_offset = 0;
- uint32_t total_offset = 0;
- while (total_offset < inode->size) {
- if (dir_offset >= this->block_size) {
- block_nr++;
- dir_offset -= this->block_size;
- inode_read_block(this, inode, block_nr, block);
- }
- ext2_dir_t *d_ent = (ext2_dir_t *)((uintptr_t)block + dir_offset);
- if (d_ent->inode == 0 || strlen(name) != d_ent->name_len) {
- dir_offset += d_ent->rec_len;
- total_offset += d_ent->rec_len;
- continue;
- }
- char *dname = malloc(sizeof(char) * (d_ent->name_len + 1));
- memcpy(dname, &(d_ent->name), d_ent->name_len);
- dname[d_ent->name_len] = '\0';
- if (!strcmp(dname, name)) {
- free(dname);
- direntry = malloc(d_ent->rec_len);
- memcpy(direntry, d_ent, d_ent->rec_len);
- break;
- }
- free(dname);
- dir_offset += d_ent->rec_len;
- total_offset += d_ent->rec_len;
- }
- free(inode);
- if (!direntry) {
- free(block);
- return NULL;
- }
- fs_node_t *outnode = malloc(sizeof(fs_node_t));
- memset(outnode, 0, sizeof(fs_node_t));
- inode = read_inode(this, direntry->inode);
- if (!node_from_file(this, inode, direntry, outnode)) {
- debug_print(CRITICAL, "Oh dear. Couldn't allocate the outnode?");
- }
- free(direntry);
- free(inode);
- free(block);
- return outnode;
- }
- static int unlink_ext2(fs_node_t * node, char * name) {
- /* XXX this is a very bad implementation */
- ext2_fs_t * this = (ext2_fs_t *)node->device;
- ext2_inodetable_t *inode = read_inode(this,node->inode);
- assert(inode->mode & EXT2_S_IFDIR);
- uint8_t * block = malloc(this->block_size);
- ext2_dir_t *direntry = NULL;
- uint8_t block_nr = 0;
- inode_read_block(this, inode, block_nr, block);
- uint32_t dir_offset = 0;
- uint32_t total_offset = 0;
- while (total_offset < inode->size) {
- if (dir_offset >= this->block_size) {
- block_nr++;
- dir_offset -= this->block_size;
- inode_read_block(this, inode, block_nr, block);
- }
- ext2_dir_t *d_ent = (ext2_dir_t *)((uintptr_t)block + dir_offset);
- if (d_ent->inode == 0 || strlen(name) != d_ent->name_len) {
- dir_offset += d_ent->rec_len;
- total_offset += d_ent->rec_len;
- continue;
- }
- char *dname = malloc(sizeof(char) * (d_ent->name_len + 1));
- memcpy(dname, &(d_ent->name), d_ent->name_len);
- dname[d_ent->name_len] = '\0';
- if (!strcmp(dname, name)) {
- free(dname);
- direntry = d_ent;
- break;
- }
- free(dname);
- dir_offset += d_ent->rec_len;
- total_offset += d_ent->rec_len;
- }
- free(inode);
- if (!direntry) {
- free(block);
- return -ENOENT;
- }
- direntry->inode = 0;
- inode_write_block(this, inode, node->inode, block_nr, block);
- free(block);
- ext2_sync(this);
- return 0;
- }
- static void refresh_inode(ext2_fs_t * this, ext2_inodetable_t * inodet, uint32_t inode) {
- uint32_t group = inode / this->inodes_per_group;
- if (group > BGDS) {
- return;
- }
- uint32_t inode_table_block = BGD[group].inode_table;
- inode -= group * this->inodes_per_group; // adjust index within group
- uint32_t block_offset = ((inode - 1) * this->inode_size) / this->block_size;
- uint32_t offset_in_block = (inode - 1) - block_offset * (this->block_size / this->inode_size);
- uint8_t * buf = malloc(this->block_size);
- read_block(this, inode_table_block + block_offset, buf);
- ext2_inodetable_t *inodes = (ext2_inodetable_t *)buf;
- memcpy(inodet, (uint8_t *)((uint32_t)inodes + offset_in_block * this->inode_size), this->inode_size);
- free(buf);
- }
- /**
- * read_inode
- */
- static ext2_inodetable_t * read_inode(ext2_fs_t * this, uint32_t inode) {
- ext2_inodetable_t *inodet = malloc(this->inode_size);
- refresh_inode(this, inodet, inode);
- return inodet;
- }
- static uint32_t read_ext2(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
- ext2_fs_t * this = (ext2_fs_t *)node->device;
- ext2_inodetable_t * inode = read_inode(this, node->inode);
- uint32_t end;
- if (inode->size == 0) return 0;
- if (offset + size > inode->size) {
- end = inode->size;
- } else {
- end = offset + size;
- }
- uint32_t start_block = offset / this->block_size;
- uint32_t end_block = end / this->block_size;
- uint32_t end_size = end - end_block * this->block_size;
- uint32_t size_to_read = end - offset;
- uint8_t * buf = malloc(this->block_size);
- if (start_block == end_block) {
- inode_read_block(this, inode, start_block, buf);
- memcpy(buffer, (uint8_t *)(((uint32_t)buf) + (offset % this->block_size)), size_to_read);
- } else {
- uint32_t block_offset;
- uint32_t blocks_read = 0;
- for (block_offset = start_block; block_offset < end_block; block_offset++, blocks_read++) {
- if (block_offset == start_block) {
- inode_read_block(this, inode, block_offset, buf);
- memcpy(buffer, (uint8_t *)(((uint32_t)buf) + (offset % this->block_size)), this->block_size - (offset % this->block_size));
- } else {
- inode_read_block(this, inode, block_offset, buf);
- memcpy(buffer + this->block_size * blocks_read - (offset % this->block_size), buf, this->block_size);
- }
- }
- if (end_size) {
- inode_read_block(this, inode, end_block, buf);
- memcpy(buffer + this->block_size * blocks_read - (offset % this->block_size), buf, end_size);
- }
- }
- free(inode);
- free(buf);
- return size_to_read;
- }
- static uint32_t write_inode_buffer(ext2_fs_t * this, ext2_inodetable_t * inode, uint32_t inode_number, uint32_t offset, uint32_t size, uint8_t *buffer) {
- uint32_t end = offset + size;
- if (end > inode->size) {
- inode->size = end;
- write_inode(this, inode, inode_number);
- }
- uint32_t start_block = offset / this->block_size;
- uint32_t end_block = end / this->block_size;
- uint32_t end_size = end - end_block * this->block_size;
- uint32_t size_to_read = end - offset;
- uint8_t * buf = malloc(this->block_size);
- if (start_block == end_block) {
- inode_read_block(this, inode, start_block, buf);
- memcpy((uint8_t *)(((uint32_t)buf) + (offset % this->block_size)), buffer, size_to_read);
- inode_write_block(this, inode, inode_number, start_block, buf);
- } else {
- uint32_t block_offset;
- uint32_t blocks_read = 0;
- for (block_offset = start_block; block_offset < end_block; block_offset++, blocks_read++) {
- if (block_offset == start_block) {
- int b = inode_read_block(this, inode, block_offset, buf);
- memcpy((uint8_t *)(((uint32_t)buf) + (offset % this->block_size)), buffer, this->block_size - (offset % this->block_size));
- inode_write_block(this, inode, inode_number, block_offset, buf);
- if (!b) {
- refresh_inode(this, inode, inode_number);
- }
- } else {
- int b = inode_read_block(this, inode, block_offset, buf);
- memcpy(buf, buffer + this->block_size * blocks_read - (offset % this->block_size), this->block_size);
- inode_write_block(this, inode, inode_number, block_offset, buf);
- if (!b) {
- refresh_inode(this, inode, inode_number);
- }
- }
- }
- if (end_size) {
- inode_read_block(this, inode, end_block, buf);
- memcpy(buf, buffer + this->block_size * blocks_read - (offset % this->block_size), end_size);
- inode_write_block(this, inode, inode_number, end_block, buf);
- }
- }
- free(buf);
- return size_to_read;
- }
- static uint32_t write_ext2(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
- ext2_fs_t * this = (ext2_fs_t *)node->device;
- ext2_inodetable_t * inode = read_inode(this, node->inode);
- uint32_t rv = write_inode_buffer(this, inode, node->inode, offset, size, buffer);
- free(inode);
- return rv;
- }
- static void open_ext2(fs_node_t *node, unsigned int flags) {
- ext2_fs_t * this = node->device;
- if (flags & O_TRUNC) {
- /* Uh, herp */
- ext2_inodetable_t * inode = read_inode(this,node->inode);
- inode->size = 0;
- write_inode(this, inode, node->inode);
- }
- }
- static void close_ext2(fs_node_t *node) {
- /* Nothing to do here */
- }
- /**
- * readdir_ext2
- */
- static struct dirent * readdir_ext2(fs_node_t *node, uint32_t index) {
- ext2_fs_t * this = (ext2_fs_t *)node->device;
- ext2_inodetable_t *inode = read_inode(this, node->inode);
- assert(inode->mode & EXT2_S_IFDIR);
- ext2_dir_t *direntry = direntry_ext2(this, inode, node->inode, index);
- if (!direntry) {
- free(inode);
- return NULL;
- }
- struct dirent *dirent = malloc(sizeof(struct dirent));
- memcpy(&dirent->name, &direntry->name, direntry->name_len);
- dirent->name[direntry->name_len] = '\0';
- dirent->ino = direntry->inode;
- free(direntry);
- free(inode);
- return dirent;
- }
- static int symlink_ext2(fs_node_t * parent, char * target, char * name) {
- if (!name) return -EINVAL;
- ext2_fs_t * this = parent->device;
- /* first off, check if it exists */
- fs_node_t * check = finddir_ext2(parent, name);
- if (check) {
- debug_print(WARNING, "A file by this name already exists: %s", name);
- free(check);
- return -EEXIST; /* this should probably have a return value... */
- }
- /* Allocate an inode for it */
- unsigned int inode_no = allocate_inode(this);
- ext2_inodetable_t * inode = read_inode(this,inode_no);
- /* Set the access and creation times to now */
- inode->atime = now();
- inode->ctime = inode->atime;
- inode->mtime = inode->atime;
- inode->dtime = 0; /* This inode was never deleted */
- /* Empty the file */
- memset(inode->block, 0x00, sizeof(inode->block));
- inode->blocks = 0;
- inode->size = 0; /* empty */
- /* Assign it to current user */
- inode->uid = current_process->user;
- inode->gid = current_process->user;
- /* misc */
- inode->faddr = 0;
- inode->links_count = 1; /* The one we're about to create. */
- inode->flags = 0;
- inode->osd1 = 0;
- inode->generation = 0;
- inode->file_acl = 0;
- inode->dir_acl = 0;
- inode->mode = EXT2_S_IFLNK;
- /* I *think* this is what you're supposed to do with symlinks */
- inode->mode |= 0777;
- /* Write the osd blocks to 0 */
- memset(inode->osd2, 0x00, sizeof(inode->osd2));
- size_t target_len = strlen(target);
- int embedded = target_len <= 60; // sizeof(_symlink(inode));
- if (embedded) {
- memcpy(_symlink(inode), target, target_len);
- inode->size = target_len;
- }
- /* Write out inode changes */
- write_inode(this, inode, inode_no);
- /* Now append the entry to the parent */
- create_entry(parent, name, inode_no);
- /* If we didn't embed it in the inode just use write_inode_buffer to finish the job */
- if (!embedded) {
- write_inode_buffer(parent->device, inode, inode_no, 0, target_len, (uint8_t *)target);
- }
- free(inode);
- ext2_sync(this);
- return 0;
- }
- static int readlink_ext2(fs_node_t * node, char * buf, size_t size) {
- ext2_fs_t * this = (ext2_fs_t *)node->device;
- ext2_inodetable_t * inode = read_inode(this, node->inode);
- size_t read_size = inode->size < size ? inode->size : size;
- if (inode->size > 60) { //sizeof(_symlink(inode))) {
- read_ext2(node, 0, read_size, (uint8_t *)buf);
- } else {
- memcpy(buf, _symlink(inode), read_size);
- }
- /* Believe it or not, we actually aren't supposed to include the nul in the length. */
- if (read_size < size) {
- buf[read_size] = '\0';
- }
- free(inode);
- return read_size;
- }
- static uint32_t node_from_file(ext2_fs_t * this, ext2_inodetable_t *inode, ext2_dir_t *direntry, fs_node_t *fnode) {
- if (!fnode) {
- /* You didn't give me a node to write into, go **** yourself */
- return 0;
- }
- /* Information from the direntry */
- fnode->device = (void *)this;
- fnode->inode = direntry->inode;
- memcpy(&fnode->name, &direntry->name, direntry->name_len);
- fnode->name[direntry->name_len] = '\0';
- /* Information from the inode */
- fnode->uid = inode->uid;
- fnode->gid = inode->gid;
- fnode->length = inode->size;
- fnode->mask = inode->mode & 0xFFF;
- fnode->nlink = inode->links_count;
- /* File Flags */
- fnode->flags = 0;
- if ((inode->mode & EXT2_S_IFREG) == EXT2_S_IFREG) {
- fnode->flags |= FS_FILE;
- fnode->read = read_ext2;
- fnode->write = write_ext2;
- fnode->create = NULL;
- fnode->mkdir = NULL;
- fnode->readdir = NULL;
- fnode->finddir = NULL;
- fnode->symlink = NULL;
- fnode->readlink = NULL;
- }
- if ((inode->mode & EXT2_S_IFDIR) == EXT2_S_IFDIR) {
- fnode->flags |= FS_DIRECTORY;
- fnode->create = create_ext2;
- fnode->mkdir = mkdir_ext2;
- fnode->readdir = readdir_ext2;
- fnode->finddir = finddir_ext2;
- fnode->unlink = unlink_ext2;
- fnode->write = NULL;
- fnode->symlink = symlink_ext2;
- fnode->readlink = NULL;
- }
- if ((inode->mode & EXT2_S_IFBLK) == EXT2_S_IFBLK) {
- fnode->flags |= FS_BLOCKDEVICE;
- }
- if ((inode->mode & EXT2_S_IFCHR) == EXT2_S_IFCHR) {
- fnode->flags |= FS_CHARDEVICE;
- }
- if ((inode->mode & EXT2_S_IFIFO) == EXT2_S_IFIFO) {
- fnode->flags |= FS_PIPE;
- }
- if ((inode->mode & EXT2_S_IFLNK) == EXT2_S_IFLNK) {
- fnode->flags |= FS_SYMLINK;
- fnode->read = NULL;
- fnode->write = NULL;
- fnode->create = NULL;
- fnode->mkdir = NULL;
- fnode->readdir = NULL;
- fnode->finddir = NULL;
- fnode->readlink = readlink_ext2;
- }
- fnode->atime = inode->atime;
- fnode->mtime = inode->mtime;
- fnode->ctime = inode->ctime;
- debug_print(INFO, "file a/m/c times are %d/%d/%d", fnode->atime, fnode->mtime, fnode->ctime);
- fnode->chmod = chmod_ext2;
- fnode->open = open_ext2;
- fnode->close = close_ext2;
- fnode->ioctl = NULL;
- return 1;
- }
- static uint32_t ext2_root(ext2_fs_t * this, ext2_inodetable_t *inode, fs_node_t *fnode) {
- if (!fnode) {
- return 0;
- }
- /* Information for root dir */
- fnode->device = (void *)this;
- fnode->inode = 2;
- fnode->name[0] = '/';
- fnode->name[1] = '\0';
- /* Information from the inode */
- fnode->uid = inode->uid;
- fnode->gid = inode->gid;
- fnode->length = inode->size;
- fnode->mask = inode->mode & 0xFFF;
- fnode->nlink = inode->links_count;
- /* File Flags */
- fnode->flags = 0;
- if ((inode->mode & EXT2_S_IFREG) == EXT2_S_IFREG) {
- debug_print(CRITICAL, "Root appears to be a regular file.");
- debug_print(CRITICAL, "This is probably very, very wrong.");
- return 0;
- }
- if ((inode->mode & EXT2_S_IFDIR) == EXT2_S_IFDIR) {
- } else {
- debug_print(CRITICAL, "Root doesn't appear to be a directory.");
- debug_print(CRITICAL, "This is probably very, very wrong.");
- debug_print(ERROR, "Other useful information:");
- debug_print(ERROR, "%d", inode->uid);
- debug_print(ERROR, "%d", inode->gid);
- debug_print(ERROR, "%d", inode->size);
- debug_print(ERROR, "%d", inode->mode);
- debug_print(ERROR, "%d", inode->links_count);
- return 0;
- }
- if ((inode->mode & EXT2_S_IFBLK) == EXT2_S_IFBLK) {
- fnode->flags |= FS_BLOCKDEVICE;
- }
- if ((inode->mode & EXT2_S_IFCHR) == EXT2_S_IFCHR) {
- fnode->flags |= FS_CHARDEVICE;
- }
- if ((inode->mode & EXT2_S_IFIFO) == EXT2_S_IFIFO) {
- fnode->flags |= FS_PIPE;
- }
- if ((inode->mode & EXT2_S_IFLNK) == EXT2_S_IFLNK) {
- fnode->flags |= FS_SYMLINK;
- }
- fnode->atime = inode->atime;
- fnode->mtime = inode->mtime;
- fnode->ctime = inode->ctime;
- fnode->flags |= FS_DIRECTORY;
- fnode->read = NULL;
- fnode->write = NULL;
- fnode->chmod = chmod_ext2;
- fnode->open = open_ext2;
- fnode->close = close_ext2;
- fnode->readdir = readdir_ext2;
- fnode->finddir = finddir_ext2;
- fnode->ioctl = NULL;
- fnode->create = create_ext2;
- fnode->mkdir = mkdir_ext2;
- fnode->unlink = unlink_ext2;
- return 1;
- }
- static fs_node_t * mount_ext2(fs_node_t * block_device, int flags) {
- debug_print(NOTICE, "Mounting ext2 file system...");
- ext2_fs_t * this = malloc(sizeof(ext2_fs_t));
- memset(this, 0x00, sizeof(ext2_fs_t));
- this->flags = flags;
- this->block_device = block_device;
- this->block_size = 1024;
- vfs_lock(this->block_device);
- SB = malloc(this->block_size);
- debug_print(INFO, "Reading superblock...");
- read_block(this, 1, (uint8_t *)SB);
- if (SB->magic != EXT2_SUPER_MAGIC) {
- debug_print(ERROR, "... not an EXT2 filesystem? (magic didn't match, got 0x%x)", SB->magic);
- return NULL;
- }
- this->inode_size = SB->inode_size;
- if (SB->inode_size == 0) {
- this->inode_size = 128;
- }
- this->block_size = 1024 << SB->log_block_size;
- this->cache_entries = 10240;
- if (this->block_size > 2048) {
- this->cache_entries /= 4;
- }
- debug_print(INFO, "bs=%d, cache entries=%d", this->block_size, this->cache_entries);
- this->pointers_per_block = this->block_size / 4;
- debug_print(INFO, "Log block size = %d -> %d", SB->log_block_size, this->block_size);
- BGDS = SB->blocks_count / SB->blocks_per_group;
- if (SB->blocks_per_group * BGDS < SB->blocks_count) {
- BGDS += 1;
- }
- this->inodes_per_group = SB->inodes_count / BGDS;
- if (!(this->flags & EXT2_FLAG_NOCACHE)) {
- debug_print(INFO, "Allocating cache...");
- DC = malloc(sizeof(ext2_disk_cache_entry_t) * this->cache_entries);
- this->cache_data = calloc(this->block_size, this->cache_entries);
- for (uint32_t i = 0; i < this->cache_entries; ++i) {
- DC[i].block_no = 0;
- DC[i].dirty = 0;
- DC[i].last_use = 0;
- DC[i].block = this->cache_data + i * this->block_size;
- if (i % 128 == 0) {
- debug_print(INFO, "Allocated cache block #%d", i+1);
- }
- }
- debug_print(INFO, "Allocated cache.");
- } else {
- DC = NULL;
- debug_print(NOTICE, "ext2 cache is disabled (nocache)");
- }
- // load the block group descriptors
- this->bgd_block_span = sizeof(ext2_bgdescriptor_t) * BGDS / this->block_size + 1;
- BGD = malloc(this->block_size * this->bgd_block_span);
- debug_print(INFO, "bgd_block_span = %d", this->bgd_block_span);
- this->bgd_offset = 2;
- if (this->block_size > 1024) {
- this->bgd_offset = 1;
- }
- for (int i = 0; i < this->bgd_block_span; ++i) {
- read_block(this, this->bgd_offset + i, (uint8_t *)((uint32_t)BGD + this->block_size * i));
- }
- #ifdef DEBUG_BLOCK_DESCRIPTORS
- char * bg_buffer = malloc(this->block_size * sizeof(char));
- for (uint32_t i = 0; i < BGDS; ++i) {
- debug_print(INFO, "Block Group Descriptor #%d @ %d", i, this->bgd_offset + i * SB->blocks_per_group);
- debug_print(INFO, "\tBlock Bitmap @ %d", BGD[i].block_bitmap); {
- debug_print(INFO, "\t\tExamining block bitmap at %d", BGD[i].block_bitmap);
- read_block(this, BGD[i].block_bitmap, (uint8_t *)bg_buffer);
- uint32_t j = 0;
- while (BLOCKBIT(j)) {
- ++j;
- }
- debug_print(INFO, "\t\tFirst free block in group is %d", j + BGD[i].block_bitmap - 2);
- }
- debug_print(INFO, "\tInode Bitmap @ %d", BGD[i].inode_bitmap); {
- debug_print(INFO, "\t\tExamining inode bitmap at %d", BGD[i].inode_bitmap);
- read_block(this, BGD[i].inode_bitmap, (uint8_t *)bg_buffer);
- uint32_t j = 0;
- while (BLOCKBIT(j)) {
- ++j;
- }
- debug_print(INFO, "\t\tFirst free inode in group is %d", j + this->inodes_per_group * i + 1);
- }
- debug_print(INFO, "\tInode Table @ %d", BGD[i].inode_table);
- debug_print(INFO, "\tFree Blocks = %d", BGD[i].free_blocks_count);
- debug_print(INFO, "\tFree Inodes = %d", BGD[i].free_inodes_count);
- }
- free(bg_buffer);
- #endif
- ext2_inodetable_t *root_inode = read_inode(this, 2);
- RN = (fs_node_t *)malloc(sizeof(fs_node_t));
- if (!ext2_root(this, root_inode, RN)) {
- return NULL;
- }
- debug_print(NOTICE, "Mounted EXT2 disk, root VFS node is at 0x%x", RN);
- return RN;
- }
- fs_node_t * ext2_fs_mount(char * device, char * mount_path) {
- char * arg = strdup(device);
- char * argv[10];
- int argc = tokenize(arg, ",", argv);
- fs_node_t * dev = kopen(argv[0], 0);
- if (!dev) {
- debug_print(ERROR, "failed to open %s", device);
- return NULL;
- }
- int flags = 0;
- for (int i = 1; i < argc; ++i) {
- if (!strcmp(argv[i],"nocache")) {
- flags |= EXT2_FLAG_NOCACHE;
- } else {
- debug_print(WARNING, "Unrecognized option to ext2 driver: %s", argv[i]);
- }
- }
- fs_node_t * fs = mount_ext2(dev, flags);
- free(arg);
- return fs;
- }
- int ext2_initialize(void) {
- vfs_register("ext2", ext2_fs_mount);
- return 0;
- }
- int ext2_finalize(void) {
- return 0;
- }
- MODULE_DEF(ext2, ext2_initialize, ext2_finalize);
|