From a70f24484e37afd82ae7622acf82f270d5f050e8 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Sat, 30 Aug 2025 22:46:47 +0100 Subject: [PATCH] Initial commit --- LICENSE | 24 + Makefile.unix | 49 ++ Makefile.w32 | 33 ++ README.md | 23 + cache.c | 140 +++++ cache.h | 39 ++ common.c | 1440 +++++++++++++++++++++++++++++++++++++++++++++++++ common.h | 227 ++++++++ e2cp.c | 580 ++++++++++++++++++++ e2md.c | 410 ++++++++++++++ inode.h | 53 ++ list.c | 49 ++ list.h | 42 ++ mke2fs.c | 775 ++++++++++++++++++++++++++ report.c | 150 ++++++ report.h | 29 + super.h | 67 +++ 17 files changed, 4130 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.unix create mode 100644 Makefile.w32 create mode 100644 README.md create mode 100644 cache.c create mode 100644 cache.h create mode 100644 common.c create mode 100644 common.h create mode 100644 e2cp.c create mode 100644 e2md.c create mode 100644 inode.h create mode 100644 list.c create mode 100644 list.h create mode 100644 mke2fs.c create mode 100644 report.c create mode 100644 report.h create mode 100644 super.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 0000000..538b3a0 --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,49 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90 + +ifeq ($(OS), Windows_NT) +all: mke2fs.exe e2md.exe e2cp.exe + +mke2fs.exe: mke2fs.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ + +e2md.exe: e2md.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ + +e2cp.exe: e2cp.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ +else +all: mke2fs e2md e2cp + +mke2fs: mke2fs.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ + +e2md: e2md.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ + +e2cp: e2cp.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ +endif + +clean: + + if [ -f mke2fs.exe ]; then rm -rf mke2fs.exe; fi + if [ -f mke2fs ]; then rm -rf mke2fs; fi + + if [ -f e2md.exe ]; then rm -rf e2md.exe; fi + if [ -f e2md ]; then rm -rf e2md; fi + + if [ -f e2cp.exe ]; then rm -rf e2cp.exe; fi + if [ -f e2cp ]; then rm -rf e2cp; fi diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100644 index 0000000..7f09f4e --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,33 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90 + +all: mke2fs.exe e2md.exe e2cp.exe + +clean: + + if exist mke2fs.exe ( del /q mke2fs.exe ) + if exist mke2fs ( del /q mke2fs ) + + if exist e2md.exe ( del /q e2md.exe ) + if exist e2md ( del /q e2md ) + + if exist e2cp.exe ( del /q e2cp.exe ) + if exist e2cp ( del /q e2cp ) + +mke2fs.exe: mke2fs.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ + +e2md.exe: e2md.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ + +e2cp.exe: e2cp.c cache.c common.c list.c report.c + + $(CC) $(CFLAGS) -o $@ $^ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5dfd8c --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/e2fsprogs.git + +## Building + + BSD: + + Make sure you have gcc and gmake installed then run gmake -f Makefile.unix. + + Linux: + + Make sure you have gcc and make installed then run make -f Makefile.unix. + + macOS: + + Make sure you have xcode command line tools installed then run make -f Makefile.unix. + + Windows: + + Make sure you have mingw installed and the location within your PATH variable then run mingw32-make.exe -f Makefile.w32. diff --git a/cache.c b/cache.c new file mode 100644 index 0000000..40c3c8d --- /dev/null +++ b/cache.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * @file cache.h + *****************************************************************************/ +#include "cache.h" +#include "list.h" + +struct cache_link *cache_find (struct listcache *c, unsigned long val) { + + unsigned long hash = val % CACHE_LISTS; + struct list_elem *elem; + + list_for_each_elem (&c->lists[hash], elem) { + + struct cache_link *l = container_of (elem, struct cache_link, link); + + if (c->elem_val (l) == val) { + + if (!list_empty (&l->lru_link)) { + + /* It's in the unused list, remove it.*/ + list_del (&l->lru_link); + + list_item_init (&l->lru_link); + c->lru_entries--; + + } + + return l; + + } + + } + + return 0; + +} + +int cache_flush (struct listcache *c) { + + struct list_elem *elem, *next; + struct cache_link *l; + + unsigned long i; + + list_for_each_elem_safe (&c->lru_list, elem, next) { + + l = container_of (elem, struct cache_link, lru_link); + + list_del (elem); + list_del (&l->link); + + c->entries--; + c->lru_entries--; + + c->freed (l); + + } + + for (i = 0; i < CACHE_LISTS; i++) { + + list_for_each_elem_safe (&c->lists[i], elem, next) { + + l = container_of (elem, struct cache_link, lru_link); + list_del (&l->link); + + c->entries--; + c->freed (l); + + } + + } + + return c->entries || c->lru_entries; + +} + +void cache_add (struct listcache *c, struct cache_link *elem) { + + unsigned long hash = c->elem_val (elem) % CACHE_LISTS; + long delcount = c->lru_entries - c->max_free_entries; + + if (delcount > 0) { + + /* Delete some unused items. */ + struct list_elem *lru, *next; + struct cache_link *l; + + list_for_each_elem_safe (&c->lru_list, lru, next) { + + l = container_of (lru, struct cache_link, lru_link); + + list_del (lru); + list_del (&l->link); + + c->entries--; + c->lru_entries--; + + c->freed (l); + delcount--; + + if (delcount <= 0) { + break; + } + + } + + } + + c->entries++; + + list_item_init (&elem->lru_link); /* Mark it not in the LRU list */ + list_add_after (&c->lists[hash], &elem->link); + +} + +void cache_init (struct listcache *c, unsigned long max_free_entries, unsigned long (*elem_val) (struct cache_link *elem), void (*fread) (struct cache_link *elem)) { + + unsigned long i; + + c->entries = 0; + c->lru_entries = 0; + c->max_free_entries = max_free_entries; + + list_init (&c->lru_list); + + for (i = 0; i < CACHE_LISTS; i++) { + list_init (&c->lists[i]); + } + + c->elem_val = elem_val; + c->freed = fread; + +} + +void cache_item_set_unused (struct listcache *c, struct cache_link *elem) { + + list_add_before (&c->lru_list, &elem->lru_link); + c->lru_entries++; + +} diff --git a/cache.h b/cache.h new file mode 100644 index 0000000..e377ffa --- /dev/null +++ b/cache.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * @file cache.h + *****************************************************************************/ +#ifndef _CACHE_H +#define _CACHE_H + +#include "list.h" +#define CACHE_LISTS 256 + +struct cache_link { + + struct list_elem link; + struct list_elem lru_link; + +}; + +struct listcache { + + unsigned long lru_entries; + struct list_elem lru_list; + + unsigned long max_free_entries; + unsigned long entries; + + struct list_elem lists[CACHE_LISTS]; + + unsigned long (*elem_val) (struct cache_link *elem); + void (*freed) (struct cache_link *elem); + +}; + +struct cache_link *cache_find (struct listcache *c, unsigned long val); +int cache_flush (struct listcache *c); + +void cache_add (struct listcache *c, struct cache_link *elem); +void cache_init (struct listcache *c, unsigned long max_free_entries, unsigned long (*elem_val) (struct cache_link *elem), void (*fread) (struct cache_link *elem)); +void cache_item_set_unused (struct listcache *c, struct cache_link *elem); + +#endif /* _CACHE_H */ diff --git a/common.c b/common.c new file mode 100644 index 0000000..aff66f1 --- /dev/null +++ b/common.c @@ -0,0 +1,1440 @@ +/****************************************************************************** + * @file common.c + *****************************************************************************/ +#include +#include +#include +#include + +#include "common.h" +#include "inode.h" +#include "report.h" + +unsigned long offset = 0; + +char *program_name = 0; +char *outfile = 0; + +FILE *ofp = 0; + +int seekto (FILE *fp, unsigned long sector) { + + unsigned long off = offset * SECTOR_SIZE; + off += sector * BLOCKSIZE; + + return fseek (fp, off, SEEK_SET); + +} + + +int mode_con (char *p) { + + unsigned long o1, o2, o3; + + char c1, c2, c3; + int mode; + + c1 = *p++; + c2 = *p++; + c3 = *p++; + + o1 = *p++ - '0'; + o2 = *p++ - '0'; + o3 = *p++ - '0'; + + mode = (o1 << 6) | (o2 << 3) | o3; + + if (c1 == 'c') { + mode |= 0020000; + } else if (c1 == 'd') { + mode |= 0040000; + } else if (c1 == 'b') { + mode |= 0060000; + } else if (c1 == '-') { + mode |= 0100000; + } + + if (c2 == 'u') { + mode |= 0004000; + } + + if (c3 == 'g') { + mode |= 0002000; + } + + return mode; + +} + + +unsigned long byte_array_to_integer (unsigned char *arr, int size) { + + unsigned long val = 0; + int i; + + for (i = 0; i < size; i++) { + val |= arr[i] << (CHAR_BIT * i); + } + + return val; + +} + +void write_to_byte_array (unsigned char *arr, unsigned long value, int size) { + + int i; + + for (i = 0; i < size; i++) { + arr[i] = (value >> (CHAR_BIT * i)) & UCHAR_MAX; + } + +} + + +char *xstrdup (const char *str) { + + char *ptr = xmalloc (strlen (str) + 1); + strcpy (ptr, str); + + return ptr; + +} + +void *xmalloc (unsigned long size) { + + void *ptr = malloc (size); + + if (ptr == NULL && size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (malloc)"); + exit (EXIT_FAILURE); + + } + + memset (ptr, 0, size); + return ptr; + +} + +void *xrealloc (void *ptr, unsigned long size) { + + void *new_ptr = realloc (ptr, size); + + if (new_ptr == NULL && size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (realloc)"); + exit (EXIT_FAILURE); + + } + + return new_ptr; + +} + + +#define MAX_FREE_CACHE_BLOCKS 100 + +static unsigned long blk_elem_val (struct cache_link *elem) { + + struct blk_info *bi = container_of (elem, struct blk_info, link); + return bi->blk; + +} + +static void blk_freed (struct cache_link *elem) { + + struct blk_info *bi = container_of (elem, struct blk_info, link); + + if (seekto (ofp, bi->blk)) { + + report_at (program_name, 0, REPORT_ERROR, "fseek"); + exit (EXIT_FAILURE); + + } + + if (fwrite (bi->b, BLOCKSIZE, 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "blk_freed: write"); + exit (EXIT_FAILURE); + + } + + free (bi->b); + free (bi); + +} + +#define MAX_FREE_CACHE_GDS 100 + +static unsigned long gd_elem_val (struct cache_link *elem) { + + struct gd_info *gi = container_of (elem, struct gd_info, link); + return gi->gds; + +} + +static void gd_freed (struct cache_link *elem) { + + struct gd_info *gi = container_of (elem, struct gd_info, link); + put_blk (gi->bi); + + free (gi); + +} + +struct blkmap_info { + + struct cache_link link; + + struct filesystem *fs; + unsigned long blk; + + unsigned char *b; + struct blk_info *bi; + + unsigned long usecount; + +}; + +#define MAX_FREE_CACHE_BLOCKMAPS 100 + +static unsigned long blkmap_elem_val (struct cache_link *elem) { + + struct blkmap_info *bmi = container_of (elem, struct blkmap_info, link); + return bmi->blk; + +} + +static void blkmap_freed (struct cache_link *elem) { + + struct blkmap_info *bmi = container_of (elem, struct blkmap_info, link); + put_blk (bmi->bi); + + free (bmi); + +} + +#define MAX_FREE_CACHE_INODES 100 + +static unsigned long inode_elem_val (struct cache_link *elem) { + + struct nod_info *ni = container_of (elem, struct nod_info, link); + return ni->nod; + +} + +static void inode_freed (struct cache_link *elem) { + + struct nod_info *ni = container_of (elem, struct nod_info, link); + put_blk (ni->bi); + + free (ni); + +} + +#define GDS_START (EXT2_SUPERBLOCK_OFFSET + sizeof (struct superblock) + BLOCKSIZE - 1) / BLOCKSIZE +#define GDS_PER_BLOCK (BLOCKSIZE / sizeof (struct group_descriptor)) + +struct group_descriptor *get_gd (struct filesystem *fs, unsigned long no, struct gd_info **rgi) { + + unsigned long gdblk, offset; + + struct cache_link *curr; + struct gd_info *gi; + + if ((curr = cache_find (&fs->gds, no))) { + + gi = container_of (curr, struct gd_info, link); + gi->usecount++; + + goto out; + + } + + gi = xmalloc (sizeof (*gi)); + + gi->fs = fs; + gi->gds = no; + gi->usecount = 1; + + gdblk = GDS_START + (no / GDS_PER_BLOCK); + offset = no % GDS_PER_BLOCK; + + gi->gd = ((struct group_descriptor *) get_blk (fs, gdblk, &gi->bi)) + offset; + cache_add (&fs->gds, &gi->link); + +out: + + *rgi = gi; + return gi->gd; + +} + +void put_gd (struct gd_info *gi) { + + if (gi->usecount == 0) { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "put_gd usecount zero"); + exit (EXIT_FAILURE); + + } + + gi->usecount--; + + if (gi->usecount == 0) { + + /* Free happens in the cache code. */ + cache_item_set_unused (&gi->fs->gds, &gi->link); + + } + +} + +#define HDLINK_CNT 16 + +struct filesystem *alloc_fs (void) { + + struct filesystem *fs = xmalloc (sizeof (*fs)); + + cache_init (&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed); + cache_init (&fs->gds, MAX_FREE_CACHE_GDS, gd_elem_val, gd_freed); + cache_init (&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS, blkmap_elem_val, blkmap_freed); + cache_init (&fs->indoes, MAX_FREE_CACHE_INODES, inode_elem_val, inode_freed); + + fs->hdlink_cnt = HDLINK_CNT; + + fs->hdlinks.hdl = xmalloc (sizeof (struct hdlink_s) * fs->hdlink_cnt); + fs->hdlinks.count = 0; + + return fs; + +} + +void finish_fs (struct filesystem *fs) { + + if (cache_flush (&fs->indoes)) { + + report_at (program_name, 0, REPORT_ERROR, "entry mismatch on inode cache flush"); + exit (EXIT_FAILURE); + + } + + if (cache_flush (&fs->blkmaps)) { + + report_at (program_name, 0, REPORT_ERROR, "entry mismatch on blockmap cache flush"); + exit (EXIT_FAILURE); + + } + + if (cache_flush (&fs->gds)) { + + report_at (program_name, 0, REPORT_ERROR, "entry mismatch on gd cache flush"); + exit (EXIT_FAILURE); + + } + + if (cache_flush (&fs->blks)) { + + report_at (program_name, 0, REPORT_ERROR, "entry mismatch on block cache flush"); + exit (EXIT_FAILURE); + + } + + if (seekto (ofp, EXT2_SUPERBLOCK_OFFSET / BLOCKSIZE)) { + + report_at (program_name, 0, REPORT_ERROR, "fseek"); + exit (EXIT_FAILURE); + + } + + if (fwrite (fs->sb, sizeof (*fs->sb), 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "output filesystem superblock"); + exit (EXIT_FAILURE); + + } + +} + +void put_nod (struct nod_info *ni) { + + if (ni->usecount == 0) { + + report_at (program_name, 0, REPORT_ERROR, "put_nod: usecount zero"); + exit (EXIT_FAILURE); + + } + + ni->usecount--; + + if (ni->usecount == 0) { + + /* Free happens in the cache code. */ + cache_item_set_unused (&ni->fs->indoes, &ni->link); + + } + +} + +#define GRP_GROUP_OF_INODE(fs, nod) \ + (((nod) - 1) / byte_array_to_integer ((fs)->sb->s_inodes_per_group, 4)) + +#define GRP_IBM_OFFSET(fs, nod) \ + ((nod) - GRP_GROUP_OF_INODE ((fs), (nod)) * byte_array_to_integer ((fs)->sb->s_inodes_per_group, 4)) + +#define INODES_PER_BLOCK (BLOCKSIZE / sizeof (struct inode)) + +struct inode *get_nod (struct filesystem *fs, unsigned long nod, struct nod_info **rni) { + + unsigned long grp, boffset, offset; + + struct group_descriptor *gd; + struct cache_link *curr; + + struct nod_info *ni; + struct gd_info *gi; + + if ((curr = cache_find (&fs->indoes, nod))) { + + ni = container_of (curr, struct nod_info, link); + ni->usecount++; + + goto out; + + } + + ni = xmalloc (sizeof (*ni)); + + ni->fs = fs; + ni->nod = nod; + ni->usecount = 1; + + cache_add (&fs->indoes, &ni->link); + offset = GRP_IBM_OFFSET (fs, nod) - 1; + + boffset = offset / INODES_PER_BLOCK; + offset %= INODES_PER_BLOCK; + + grp = GRP_GROUP_OF_INODE (fs, nod); + gd = get_gd (fs, grp, &gi); + + ni->b = get_blk (fs, byte_array_to_integer (gd->bg_inode_table, 4) + boffset, &ni->bi); + ni->itab = ((struct inode *) ni->b) + offset; + + put_gd (gi); + +out: + + *rni = ni; + return ni->itab; + +} + +#define GRP_NBGROUPS(fs) \ + ((byte_array_to_integer ((fs)->sb->s_blocks_count, 4) - \ + byte_array_to_integer ((fs)->sb->s_first_data_block, 4) + \ + byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4) - 1) / \ + byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4) \ + ) + +static unsigned long alloc_nod (struct filesystem *fs) { + + unsigned long best_group = 0, nod; + unsigned long grp, nbgroups, avefreei; + + struct group_descriptor *gd, *bestgd; + struct gd_info *gi, *bestgi; + + unsigned long inodes_count = 0; + struct blk_info *bi; + + nbgroups = GRP_NBGROUPS (fs); + + avefreei = byte_array_to_integer (fs->sb->s_free_inodes_count, 4) / nbgroups; + bestgd = get_gd (fs, best_group, &bestgi); + + for (grp = 0; grp < nbgroups; grp++) { + + gd = get_gd (fs, grp, &gi); + + if (byte_array_to_integer (gd->bg_free_inodes_count, 2) < avefreei || byte_array_to_integer (gd->bg_free_inodes_count, 2) == 0) { + + put_gd (gi); + continue; + + } + + if (!best_group || byte_array_to_integer (gd->bg_free_blocks_count, 2) > byte_array_to_integer (bestgd->bg_free_blocks_count, 2)) { + + put_gd (bestgi); + + best_group = grp; + bestgd = gd; + bestgi = gi; + + } else { + put_gd (gi); + } + + } + + if (!(nod = allocate (GRP_GET_GROUP_IBM (fs, bestgd, &bi), 0))) { + + report_at (program_name, 0, REPORT_ERROR, "couldn't allocate an inode (no free inode)"); + exit (EXIT_FAILURE); + + } + + GRP_PUT_GROUP_IBM (bi); + + if ((inodes_count = byte_array_to_integer (bestgd->bg_free_inodes_count, 2)) == 0) { + + report_at (program_name, 0, REPORT_ERROR, "group descr. free blocks count == 0 (corrupted fs?)"); + exit (EXIT_FAILURE); + + } + + write_to_byte_array (bestgd->bg_free_inodes_count, inodes_count - 1, 2); + put_gd (bestgi); + + if ((inodes_count = byte_array_to_integer (fs->sb->s_free_inodes_count, 4)) == 0) { + + report_at (program_name, 0, REPORT_ERROR, "superblock free blocks count == 0 (corrupted fs?)"); + exit (EXIT_FAILURE); + + } + + write_to_byte_array (fs->sb->s_free_inodes_count, inodes_count - 1, 4); + return byte_array_to_integer (fs->sb->s_inodes_per_group, 4) * best_group + nod; + +} + +static void dir_set_name (struct dirwalker *dw, const char *name, unsigned long nlen) { + + write_to_byte_array (dw->d.d_name_len, nlen, 2); + strncpy (((char *) dw->last_d) + sizeof (struct directory), name, nlen); + +} + +static void add2dir (struct filesystem *fs, unsigned long dnod, unsigned long nod, const char *name) { + + struct blockwalker bw, lbw; + struct directory *d; + struct dirwalker dw; + + struct inode *node, *pnode; + struct inode_pos ipos; + + struct nod_info *dni, *ni; + unsigned long bk, reclen, nlen, count, size; + + pnode = get_nod (fs, dnod, &dni); + + if ((byte_array_to_integer (pnode->i_mode, 2) & FM_IFMT) != FM_IFDIR) { + + report_at (program_name, 0, REPORT_ERROR, "can't add '%s' to a non-directory", name); + exit (EXIT_FAILURE); + + } + + if (!*name) { + + report_at (program_name, 0, REPORT_ERROR, "can't create an inode with an empty name"); + exit (EXIT_FAILURE); + + } + + if (strchr (name, '/')) { + + report_at (program_name, 0, REPORT_ERROR, "bad name '%s' (contains a slash)", name); + exit (EXIT_FAILURE); + + } + + nlen = strlen (name); + + if ((reclen = sizeof (struct directory) + rndup (nlen, 4)) > BLOCKSIZE) { + + report_at (program_name, 0, REPORT_ERROR, "bad name '%s' (too long)", name); + exit (EXIT_FAILURE); + + } + + init_bw (&bw); + lbw = bw; + + while ((bk = walk_bw (fs, dnod, &bw, 0, 0)) != WALK_END) { + + for (d = get_dir (fs, bk, &dw); d; d = next_dir (&dw)) { + + if (!byte_array_to_integer (d->d_inode, 4) && byte_array_to_integer (d->d_rec_len, 2) >= reclen) { + + write_to_byte_array (d->d_inode, nod, 4); + + node = get_nod (fs, nod, &ni); + dir_set_name (&dw, name, nlen); + + put_dir (&dw); + + count = byte_array_to_integer (node->i_nlinks, 2); + write_to_byte_array (node->i_nlinks, count + 1, 2); + + put_nod (ni); + goto out; + + } + + if (byte_array_to_integer (d->d_rec_len, 2) >= (sizeof (struct directory) + rndup (byte_array_to_integer (d->d_name_len, 2), 4) + reclen)) { + + d = shrink_dir (&dw, nod, name, nlen); + put_dir (&dw); + + node = get_nod (fs, nod, &ni); + + count = byte_array_to_integer (node->i_nlinks, 2); + write_to_byte_array (node->i_nlinks, count + 1, 2); + + put_nod (ni); + goto out; + + } + + } + + put_dir (&dw); + lbw = bw; + + } + + node = get_nod (fs, nod, &ni); + d = new_dir (fs, nod, name, nlen, &dw); + + count = byte_array_to_integer (node->i_nlinks, 2); + write_to_byte_array (node->i_nlinks, count + 1, 2); + + put_nod (ni); + next_dir (&dw); + + inode_pos_init (fs, &ipos, dnod, INODE_POS_EXTEND, &lbw); + extend_inode_blk (fs, &ipos, dw.b, 1); + inode_pos_finish (&ipos); + + put_dir (&dw); + + size = byte_array_to_integer (pnode->i_size, 4); + write_to_byte_array (pnode->i_size, size + BLOCKSIZE, 4); + +out: + + put_nod (dni); + +} + +#define GRP_GROUP_OF_INODE(fs, nod) \ + (((nod) - 1) / byte_array_to_integer ((fs)->sb->s_inodes_per_group, 4)) + +unsigned long mknod_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime) { + + unsigned long nod, used_dirs_count; + + struct inode *node; + struct nod_info *ni; + + struct gd_info *gi; + nod = alloc_nod (fs); + + node = get_nod (fs, nod, &ni); + write_to_byte_array (node->i_mode, mode, 2); + + add2dir (fs, parent_nod, nod, name); + + switch (mode & FM_IFMT) { + + case FM_IFDIR: + + add2dir (fs, nod, nod, "."); + add2dir (fs, nod, parent_nod, ".."); + + get_gd (fs, GRP_GROUP_OF_INODE (fs, nod), &gi); + + used_dirs_count = byte_array_to_integer (gi->gd->bg_used_dirs_count, 2); + write_to_byte_array (gi->gd->bg_used_dirs_count, used_dirs_count + 1, 2); + + put_gd (gi); + break; + + default: + + break; + + } + + write_to_byte_array (node->i_uid, uid, 2); + write_to_byte_array (node->i_gid, gid, 2); + + write_to_byte_array (node->i_atime, mtime, 4); + write_to_byte_array (node->i_ctime, ctime, 4); + write_to_byte_array (node->i_mtime, mtime, 4); + + put_nod (ni); + return nod; + +} + +unsigned long mkdir_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime) { + return mknod_fs (fs, parent_nod, name, mode | FM_IFDIR, uid, gid, ctime, mtime); +} + +unsigned long rndup (unsigned long qty, unsigned long siz) { + return (qty + (siz - 1)) & ~(siz - 1); +} + +#define GRP_GROUP_OF_BLOCK(fs, blk) \ + (((blk) - 1) / byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4)) + +#define GRP_GET_BLOCK_BITMAP(fs, blk, bi, gi) \ + (GRP_GET_GROUP_BBM ((fs), get_gd (fs, GRP_GROUP_OF_BLOCK ((fs), (blk)), (gi)), (bi))) + +#define GRP_PUT_BLOCK_BITMAP(bi, gi) \ + (GRP_PUT_GROUP_BBM ((bi)), put_gd ((gi))) + +#define GRP_BBM_OFFSET(fs, blk) \ + ((blk) - GRP_GROUP_OF_BLOCK ((fs), (blk)) * byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4)) + +static unsigned long allocated (unsigned char *b, unsigned long item) { + return b[(item - 1) / 8] & (1 << ((item - 1) % 8)); +} + +static void deallocate (unsigned char *block, unsigned long item) { + block[(item - 1) / 8] &= ~(1 << ((item - 1) % 8)); +} + +#define INOBLK (BLOCKSIZE / INODE_BLOCKSIZE) + +static void free_blk (struct filesystem *fs, unsigned long bk) { + + struct group_descriptor *gd; + unsigned long grp, free_blocks; + + struct blk_info *bi; + struct gd_info *gi; + + grp = bk / byte_array_to_integer (fs->sb->s_blocks_per_group, 4); + bk %= byte_array_to_integer (fs->sb->s_blocks_per_group, 4); + + gd = get_gd (fs, grp, &gi); + + deallocate (GRP_GET_GROUP_BBM (fs, gd, &bi), bk); + GRP_PUT_GROUP_BBM (bi); + + free_blocks = byte_array_to_integer (gd->bg_free_blocks_count, 2); + write_to_byte_array (gd->bg_free_blocks_count, free_blocks + 1, 2); + + put_gd (gi); + + free_blocks = byte_array_to_integer (fs->sb->s_free_blocks_count, 4); + write_to_byte_array (fs->sb->s_free_blocks_count, free_blocks + 1, 4); + +} + +#if INT_MAX == 32767 +static unsigned long *get_blkmap (struct filesystem *fs, unsigned long blk, struct blkmap_info **rbmi) { +#else +static unsigned int *get_blkmap (struct filesystem *fs, unsigned long blk, struct blkmap_info **rbmi) { +#endif + + struct blkmap_info *bmi; + struct cache_link *curr; + + if ((curr = cache_find (&fs->blkmaps, blk))) { + + bmi = container_of (curr, struct blkmap_info, link); + bmi->usecount++; + + goto out; + + } + + bmi = xmalloc (sizeof (*bmi)); + bmi->usecount = 1; + + bmi->fs = fs; + bmi->blk = blk; + + bmi->b = get_blk (fs, blk, &bmi->bi); + cache_add (&fs->blkmaps, &bmi->link); + +out: + + *rbmi = bmi; + +#if INT_MAX == 32767 + return (unsigned long *) bmi->b; +#else + return (unsigned int *) bmi->b; +#endif + +} + +static void put_blkmap (struct blkmap_info *bmi) { + + if (bmi->usecount == 0) { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "put_blkmap: usecount zero"); + exit (EXIT_FAILURE); + + } + + bmi->usecount--; + + if (bmi->usecount == 0) { + + /* Free happens in the cache code. */ + cache_item_set_unused (&bmi->fs->blkmaps, &bmi->link); + + } + +} + +static unsigned long alloc_blk (struct filesystem *fs, unsigned long nod) { + + unsigned long bk = 0, grp, nbgroups; + unsigned long free_blocks_count; + + struct blk_info *bi; + struct gd_info *gi; + + struct group_descriptor *gd; + + grp = GRP_GROUP_OF_INODE (fs, nod); + nbgroups = GRP_NBGROUPS (fs); + + gd = get_gd (fs, grp, &gi); + + bk = allocate (GRP_GET_GROUP_BBM (fs, gd, &bi), 0); + GRP_PUT_GROUP_BBM (bi); + + put_gd (gi); + + if (!bk) { + + for (grp = 0; grp < nbgroups && !bk; grp++) { + + gd = get_gd (fs, grp, &gi); + + bk = allocate (GRP_GET_GROUP_BBM (fs, gd, &bi), 0); + GRP_PUT_GROUP_BBM (bi); + + put_gd (gi); + + } + + grp--; + + } + + if (!bk) { + + report_at (program_name, 0, REPORT_ERROR, "couldn't allocate a block (no free space)"); + exit (EXIT_FAILURE); + + } + + gd = get_gd (fs, grp, &gi); + + if (!(free_blocks_count = byte_array_to_integer (gd->bg_free_blocks_count, 2))) { + + report_at (program_name, 0, REPORT_ERROR, "group descr %ld. free blocks count == 0 (corrupted fs?)", grp); + exit (EXIT_FAILURE); + + } + + write_to_byte_array (gd->bg_free_blocks_count, free_blocks_count - 1, 2); + put_gd (gi); + + if (!(free_blocks_count = byte_array_to_integer (fs->sb->s_free_blocks_count, 4))) { + + report_at (program_name, 0, REPORT_ERROR, "superblock free blocks count == 0 (corrupted fs?)"); + exit (EXIT_FAILURE); + + } + + write_to_byte_array (fs->sb->s_free_blocks_count, free_blocks_count - 1, 4); + return byte_array_to_integer (fs->sb->s_first_data_block, 4) + byte_array_to_integer (fs->sb->s_blocks_per_group, 4) * grp + (bk - 1); + +} + +unsigned long walk_bw (struct filesystem *fs, unsigned long nod, struct blockwalker *bw, signed long *create, unsigned int hole) { + + int extend = 0, reduce = 0; + + struct blkmap_info *bmi1 = 0, *bmi2 = 0, *bmi3 = 0; + struct inode *inod; + struct nod_info *ni; + +#if INT_MAX == 32767 + unsigned long *bkref = 0, bk = 0, *iblk, *b; +#else + unsigned int *bkref = 0, bk = 0, *iblk, *b; +#endif + + if (create && (*create) < 0) { + reduce = 1; + } + + inod = get_nod (fs, nod, &ni); + + if (bw->bnum >= byte_array_to_integer (inod->i_blocks, 4) / INOBLK) { + + if (create && (*create) > 0) { + + (*create)--; + extend = 1; + + } else { + + put_nod (ni); + return WALK_END; + + } + + } + +#if INT_MAX == 32767 + iblk = (unsigned long *) inod->i_block; +#else + iblk = (unsigned int *) inod->i_block; +#endif + + if (bw->bpdir == EXT2_INIT_BLOCK) { + + bkref = &iblk[bw->bpdir = 0]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if (bw->bpdir < EXT2_NDIR_BLOCKS) { + + bkref = &iblk[++bw->bpdir]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if (bw->bpdir == EXT2_NDIR_BLOCKS) { + + bw->bnum++; + + bw->bpdir = EXT2_IND_BLOCK; + bw->bpind = 0; + + if (extend) { iblk[bw->bpdir] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, iblk[bw->bpdir]); } + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + bkref = &b[bw->bpind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if ((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE / 4 - 1)) { + + bw->bpind++; + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + bkref = &b[bw->bpind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if (bw->bpdir == EXT2_IND_BLOCK) { + + bw->bpdir = EXT2_DIND_BLOCK; + bw->bnum += 2; + + bw->bpind = 0; + bw->bpdind = 0; + + if (extend) { iblk[bw->bpdir] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, iblk[bw->bpdir]); } + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + + if (extend) { b[bw->bpind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpind], &bmi2); + bkref = &b[bw->bpdind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if ((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE / 4 - 1)) { + + bw->bpdind++; + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + b = get_blkmap (fs, b[bw->bpind], &bmi2); + + bkref = &b[bw->bpdind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if ((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpind < BLOCKSIZE / 4 - 1)) { + + bw->bnum++; + bw->bpind++; + + bw->bpdind = 0; + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + + if (extend) { b[bw->bpind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpind], &bmi2); + bkref = &b[bw->bpdind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if (bw->bpdir == EXT2_DIND_BLOCK) { + + bw->bpdir = EXT2_TIND_BLOCK; + bw->bnum += 3; + + bw->bpind = 0; + bw->bpdind = 0; + bw->bptind = 0; + + if (extend) { iblk[bw->bpdir] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, iblk[bw->bpdir]); } + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + + if (extend) { b[bw->bpind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpind], &bmi2); + + if (extend) { b[bw->bpdind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpdind], &bmi3); + bkref = &b[bw->bptind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if ((bw->bpdir == EXT2_TIND_BLOCK) && (bw->bptind < BLOCKSIZE / 4 - 1)) { + + bw->bptind++; + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + b = get_blkmap (fs, b[bw->bpind], &bmi2); + b = get_blkmap (fs, b[bw->bpdind], &bmi3); + + bkref = &b[bw->bptind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if ((bw->bpdir == EXT2_TIND_BLOCK) && (bw->bpdind < BLOCKSIZE / 4 - 1)) { + + bw->bnum++; + + bw->bptind = 0; + bw->bpdind++; + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + b = get_blkmap (fs, b[bw->bpind], &bmi2); + + if (extend) { b[bw->bpdind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpdind], &bmi3); + bkref = &b[bw->bptind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else if ((bw->bpdir == EXT2_TIND_BLOCK) && (bw->bpind < BLOCKSIZE / 4 - 1)) { + + bw->bnum += 2; + bw->bpind++; + + bw->bpdind = 0; + bw->bptind = 0; + + b = get_blkmap (fs, iblk[bw->bpdir], &bmi1); + + if (extend) { b[bw->bpind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpind], &bmi2); + + if (extend) { b[bw->bpdind] = alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, b[bw->bpind]); } + + b = get_blkmap (fs, b[bw->bpdind], &bmi3); + bkref = &b[bw->bptind]; + + if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); } + if (reduce) { free_blk (fs, *bkref); } + + } else { + + report_at (program_name, 0, REPORT_ERROR, "file too big"); + exit (EXIT_FAILURE); + + } + + bk = *bkref; + + if (bmi3) { put_blkmap (bmi3); } + if (bmi2) { put_blkmap (bmi2); } + if (bmi1) { put_blkmap (bmi1); } + + if (bk) { + + unsigned char *block; + + struct blk_info *bi; + struct gd_info *gi; + + block = GRP_GET_BLOCK_BITMAP (fs, bk, &bi, &gi); + bw->bnum++; + + if (!reduce && !allocated (block, GRP_BBM_OFFSET (fs, bk))) { + + report_at (program_name, 0, REPORT_ERROR, "[block %lu of inode %lu is unallocated !]", bk, nod); + exit (EXIT_FAILURE); + + } + + GRP_PUT_BLOCK_BITMAP (bi, gi); + + } + + if (extend) { + write_to_byte_array (inod->i_blocks, bw->bnum * INOBLK, 4); + } + + put_nod (ni); + return bk; + +} + +void inode_pos_init (struct filesystem *fs, struct inode_pos *ipos, unsigned long nod, int op, struct blockwalker *endbw) { + + struct blockwalker lbw; + + init_bw (&ipos->bw); + ipos->nod = nod; + + ipos->inod = get_nod (fs, nod, &ipos->ni); + + if (op == INODE_POS_TRUNCATE) { + + long create = -1; + + while (walk_bw (fs, nod, &ipos->bw, &create, 0) != WALK_END) { + /* nop */ + } + + write_to_byte_array (ipos->inod->i_blocks, 0, 4); + + } + + if (endbw) { + ipos->bw = *endbw; + } else { + + init_bw (&ipos->bw); + lbw = ipos->bw; + + while (walk_bw (fs, nod, &ipos->bw, 0, 0) != WALK_END) { + lbw = ipos->bw; + } + + ipos->bw = lbw; + + } + +} + +void inode_pos_finish (struct inode_pos *ipos) { + put_nod (ipos->ni); +} + + +unsigned char *get_blk (struct filesystem *fs, unsigned long blk, struct blk_info **rbi) { + + struct cache_link *curr; + struct blk_info *bi; + + if (blk >= byte_array_to_integer (fs->sb->s_blocks_count, 4)) { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "block out of range: %lu, %lu", + blk, byte_array_to_integer (fs->sb->s_blocks_count, 4)); + exit (EXIT_FAILURE); + + } + + if ((curr = cache_find (&fs->blks, blk))) { + + bi = container_of (curr, struct blk_info, link); + bi->usecount++; + + goto out; + + } + + bi = xmalloc (sizeof (*bi)); + + bi->fs = fs; + bi->blk = blk; + bi->usecount = 1; + + bi->b = xmalloc (BLOCKSIZE); + cache_add (&fs->blks, &bi->link); + + if (seekto (ofp, blk)) { + + report_at (program_name, 0, REPORT_ERROR, "fseek"); + exit (EXIT_FAILURE); + + } + + if (fread (bi->b, BLOCKSIZE, 1, ofp) != 1) { + + if (ferror (ofp)) { + + report_at (program_name, 0, REPORT_ERROR, "fread"); + exit (EXIT_FAILURE); + + } + + memset (bi->b, 0, BLOCKSIZE); + + } + +out: + + *rbi = bi; + return bi->b; + +} + +void put_blk (struct blk_info *bi) { + + if (bi->usecount == 0) { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "put_blk: usecount zero"); + exit (EXIT_FAILURE); + + } + + bi->usecount--; + + if (bi->usecount == 0) { + + /* Free happens in the cache code. */ + cache_item_set_unused (&bi->fs->blks, &bi->link); + + } + +} + +unsigned long allocate (unsigned char *b, unsigned long item) { + + if (!item) { + + unsigned char bits; + unsigned long i, j; + + for (i = 0; i < BLOCKSIZE; i++) { + + if ((bits = b[i]) != (unsigned char) -1) { + + for (j = 0; j < 8; j++) { + + if (!(bits & (1 << j))) { + break; + } + + } + + item = i * 8 + j + 1; + break; + + } + + } + + if (i == BLOCKSIZE) { + return 0; + } + + } + + b[(item - 1) / 8] |= (1 << ((item - 1) % 8)); + return item; + +} + +char *dir_name (struct dirwalker *dw) { + return ((char *) dw->last_d) + sizeof (struct directory); +} + +void put_dir (struct dirwalker *dw) { + + if (dw->need_flush) { + + /* Walk ended before reaching the end of block b. */ + memcpy (dw->last_d, &dw->d, sizeof (dw->d)); + + } + + if (dw->nod == 0) { + free (dw->b); + } else { + put_blk (dw->bi); + } + +} + +struct directory *get_dir (struct filesystem *fs, unsigned long nod, struct dirwalker *dw) { + + dw->fs = fs; + dw->nod = nod; + + dw->last_d = dw->b = get_blk (fs, nod, &dw->bi); + dw->need_flush = 1; + + memcpy (&dw->d, dw->last_d, sizeof (struct directory)); + return &dw->d; + +} + +struct directory *next_dir (struct dirwalker *dw) { + + unsigned char *next_d = dw->last_d + byte_array_to_integer (dw->d.d_rec_len, 2); + + if (dw->need_flush) { + memcpy (dw->last_d, &dw->d, sizeof (struct directory)); + } + + if (next_d - dw->b >= BLOCKSIZE) { + + dw->need_flush = 0; + return 0; + + } + + dw->last_d = next_d; + memcpy (&dw->d, next_d, sizeof (struct directory)); + + return &dw->d; + +} + +void init_bw (struct blockwalker *bw) { + + bw->bnum = 0; + bw->bpdir = EXT2_INIT_BLOCK; + +} + +#define EXT2_GOOD_OLD_FIRST_INO EXT2_FIRST_INO +#define EXT2_GOOD_OLD_INODE_SIZE sizeof (struct inode) + +void fs_upgrade_rev1_largefile (struct filesystem *fs) { + + write_to_byte_array (fs->sb->s_rev_level, 1, 4); + + write_to_byte_array (fs->sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO, 4); + write_to_byte_array (fs->sb->s_inode_size, EXT2_GOOD_OLD_INODE_SIZE, 2); + +} + +unsigned char *get_workblk (void) { + return xmalloc (BLOCKSIZE); +} + +struct directory *new_dir (struct filesystem *fs, unsigned long dnod, const char *name, unsigned short nlen, struct dirwalker *dw) { + + struct directory *d; + + dw->fs = fs; + dw->nod = 0; + + dw->b = get_workblk (); + dw->last_d = dw->b; + + dw->need_flush = 1; + d = &dw->d; + + write_to_byte_array (d->d_inode, dnod, 4); + write_to_byte_array (d->d_rec_len, BLOCKSIZE, 2); + write_to_byte_array (d->d_name_len, nlen, 2); + + strncpy (((char *) dw->last_d) + sizeof (struct directory), name, nlen); + return d; + +} + +struct directory *shrink_dir (struct dirwalker *dw, unsigned long nod, const char *name, unsigned short nlen) { + + struct directory *d = &dw->d; + unsigned long reclen, preclen; + + reclen = byte_array_to_integer (d->d_rec_len, 2); + write_to_byte_array (d->d_rec_len, sizeof (*d) + rndup (byte_array_to_integer (d->d_name_len, 2), 4), 2); + + preclen = byte_array_to_integer (d->d_rec_len, 2); + reclen -= preclen; + + memcpy (dw->last_d, &dw->d, sizeof (dw->d)); + + dw->last_d = dw->last_d + preclen; + write_to_byte_array (d->d_rec_len, reclen, 2); + + write_to_byte_array (d->d_inode, nod, 4); + write_to_byte_array (d->d_name_len, nlen, 2); + + strncpy (((char *) dw->last_d) + sizeof (*d), name, nlen); + return d; + +} + +void extend_inode_blk (struct filesystem *fs, struct inode_pos *ipos, unsigned char *b, signed long amount) { + + unsigned long pos, bk; + + struct blk_info *bi; + unsigned char *block; + + if (amount < 0) { + + report_at (program_name, 0, REPORT_ERROR, "got negative amount"); + exit (EXIT_FAILURE); + + } + + for (pos = 0; amount; pos += BLOCKSIZE) { + + if ((bk = walk_bw (fs, ipos->nod, &ipos->bw, &amount, 0)) == WALK_END) { + + report_at (program_name, 0, REPORT_ERROR, "extend_inode_blk: extend failed"); + exit (EXIT_FAILURE); + + } + + block = get_blk (fs, bk, &bi); + memcpy (block, b + pos, BLOCKSIZE); + + put_blk (bi); + + } + +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..e07fbca --- /dev/null +++ b/common.h @@ -0,0 +1,227 @@ +/****************************************************************************** + * @file common.h + *****************************************************************************/ +#ifndef _COMMON_H +#define _COMMON_H + +#include + +#include "cache.h" +#include "inode.h" +#include "super.h" + +extern unsigned long offset; + +extern char *outfile; +extern char *program_name; + +extern FILE *ofp; + +int seekto (FILE *fp, unsigned long sector); +int mode_con (char *p); + +char *xstrdup (const char *str); +void *xmalloc (unsigned long size); +void *xrealloc (void *ptr, unsigned long size); + +unsigned long byte_array_to_integer (unsigned char *arr, int size); +void write_to_byte_array (unsigned char *arr, unsigned long value, int size); + +#define SECTOR_SIZE 512 +#define BLOCKSIZE 1024 + +struct blockwalker { + + unsigned long bnum; + unsigned long bpdir; + unsigned long bpind; + unsigned long bpdind; + unsigned long bptind; + +}; + +struct inode_pos { + + struct blockwalker bw; + unsigned long nod; + + struct nod_info *ni; + struct inode *inod; + +}; + +#define EXT2_FIRST_INO 11 +#define EXT2_ROOT_INO 2 + +struct hdlink_s { + + unsigned long src_index; + unsigned long dst_nod; + +}; + +struct hdlinks_s { + + unsigned long count; + struct hdlink_s *hdl; + +}; + +struct filesystem { + + struct superblock *sb; + int swapit; + + unsigned long hdlink_cnt; + struct hdlinks_s hdlinks; + + unsigned long holes; + + struct listcache blks; + struct listcache gds; + struct listcache indoes; + struct listcache blkmaps; + +}; + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +unsigned long rndup (unsigned long qty, unsigned long siz); + +#define FM_IFMT 0170000 + +#define FM_IFREG 0100000 +#define FM_IFDIR 0040000 +#define FM_IRWXU 0000700 +#define FM_IRGRP 0000040 +#define FM_IXGRP 0000010 +#define FM_IROTH 0000004 +#define FM_IXOTH 0000001 + +struct group_descriptor { + + unsigned char bg_block_bitmap[4]; + unsigned char bg_inode_bitmap[4]; + unsigned char bg_inode_table[4]; + + unsigned char bg_free_blocks_count[2]; + unsigned char bg_free_inodes_count[2]; + unsigned char bg_used_dirs_count[2]; + + unsigned char bg_pad[2]; + unsigned char bg_reserved2[12]; + +}; + +struct blk_info { + + struct cache_link link; + + struct filesystem *fs; + unsigned long blk; + + unsigned char *b; + unsigned long usecount; + +}; + +struct gd_info { + + struct cache_link link; + + struct filesystem *fs; + int gds; + + struct blk_info *bi; + struct group_descriptor *gd; + + unsigned long usecount; + +}; + +struct nod_info { + + struct cache_link link; + + struct filesystem *fs; + unsigned long nod; + + unsigned char *b; + struct blk_info *bi; + + struct inode *itab; + unsigned long usecount; + +}; + +struct filesystem *alloc_fs (void); +unsigned long mknod_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime); + +struct group_descriptor *get_gd (struct filesystem *fs, unsigned long no, struct gd_info **rgi); +void put_gd (struct gd_info *gi); + +unsigned char *get_blk (struct filesystem *fs, unsigned long blk, struct blk_info **rbi); +void put_blk (struct blk_info *bi); + +#define GRP_GET_GROUP_BBM(fs, grp, bi) (get_blk ((fs), byte_array_to_integer ((grp)->bg_block_bitmap, 4), (bi))) +#define GRP_PUT_GROUP_BBM(bi) (put_blk ((bi))) + +#define GRP_GET_GROUP_IBM(fs, grp, bi) (get_blk ((fs), byte_array_to_integer ((grp)->bg_inode_bitmap, 4), (bi))) +#define GRP_PUT_GROUP_IBM(bi) (put_blk ((bi))) + +unsigned long allocate (unsigned char *b, unsigned long item); + +struct directory { + + unsigned char d_inode[4]; + + unsigned char d_rec_len[2]; + unsigned char d_name_len[2]; + +}; + +struct dirwalker { + + struct directory d; + struct filesystem *fs; + + unsigned char *last_d, *b; + unsigned long nod; + + struct blk_info *bi; + int need_flush; + +}; + +#define COPY_BLOCKS 16 + +unsigned long mkdir_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime); +unsigned long walk_bw (struct filesystem *fs, unsigned long nod, struct blockwalker *bw, signed long *create, unsigned int hole); + +#define INODE_POS_TRUNCATE 0 +#define INODE_POS_EXTEND 1 + +#define WALK_END 0xFFFFFFFE + +struct directory *next_dir (struct dirwalker *dw); +void fs_upgrade_rev1_largefile (struct filesystem *fs); + +void inode_pos_init (struct filesystem *fs, struct inode_pos *ipos, unsigned long nod, int op, struct blockwalker *endbw); +void inode_pos_finish (struct inode_pos *ipos); + +struct inode *get_nod (struct filesystem *fs, unsigned long nod, struct nod_info **rni); +void put_nod (struct nod_info *ni); + +unsigned char *get_workblk (void); +void put_dir (struct dirwalker *dw); + +struct directory *get_dir (struct filesystem *fs, unsigned long nod, struct dirwalker *dw); +struct directory *new_dir (struct filesystem *fs, unsigned long dnod, const char *name, unsigned short nlen, struct dirwalker *dw); +struct directory *shrink_dir (struct dirwalker *dw, unsigned long nod, const char *name, unsigned short nlen); + +char *dir_name (struct dirwalker *dw); +void init_bw (struct blockwalker *bw); + +void extend_inode_blk (struct filesystem *fs, struct inode_pos *ipos, unsigned char *b, signed long amount); +void finish_fs (struct filesystem *fs); + +#endif /* _COMMON_H */ diff --git a/e2cp.c b/e2cp.c new file mode 100644 index 0000000..635aadc --- /dev/null +++ b/e2cp.c @@ -0,0 +1,580 @@ +/****************************************************************************** + * @file e2cp.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "inode.h" +#include "report.h" +#include "super.h" + +static char *mode = 0; +static char *root = 0; + +#define OPTION_INPUT 0x0001 +#define OPTION_HELP 0x0002 +#define OPTION_MODE 0x0003 +#define OPTION_OFFSET 0x0004 +#define OPTION_ROOT 0x0005 + +struct option { + + const char *name; + int index, flags; + +}; + +#define OPTION_NO_ARG 0x0001 +#define OPTION_HAS_ARG 0x0002 + +static struct option opts[] = { + + { "-i", OPTION_INPUT, OPTION_HAS_ARG }, + + { "--help", OPTION_HELP, OPTION_NO_ARG }, + { "--mode", OPTION_MODE, OPTION_HAS_ARG }, + { "--offset", OPTION_OFFSET, OPTION_HAS_ARG }, + { "--root", OPTION_ROOT, OPTION_HAS_ARG }, + + { 0, 0, 0 } + +}; + +static char **files = 0; +static long nb_files = 0; + +static int strstart (const char *val, const char **str) { + + const char *p = val; + const char *q = *str; + + while (*p != '\0') { + + if (*p != *q) { + return 0; + } + + ++p; + ++q; + + } + + *str = q; + return 1; + +} + +static void print_help (void) { + + if (program_name) { + + fprintf (stderr, "Usage: %s [options] dirname\n\n", program_name); + fprintf (stderr, "Options:\n\n"); + + fprintf (stderr, " Short options:\n\n"); + fprintf (stderr, " -i Specify the input target.\n"); + + fprintf (stderr, "\n"); + + fprintf (stderr, " Long options:\n\n"); + fprintf (stderr, " --help Show this help information then exit.\n"); + fprintf (stderr, " --offset SECTOR Write the filesystem starting at SECTOR.\n"); + + } + + exit (EXIT_SUCCESS); + +} + +static void dynarray_add (void *ptab, long *nb_ptr, void *data) { + + int nb, nb_alloc; + void **pp; + + nb = *nb_ptr; + pp = *(void ***) ptab; + + if ((nb & (nb - 1)) == 0) { + + if (!nb) { + nb_alloc = 1; + } else { + nb_alloc = nb * 2; + } + + pp = xrealloc (pp, nb_alloc * sizeof (void *)); + *(void ***) ptab = pp; + + } + + pp[nb++] = data; + *nb_ptr = nb; + +} + +static void parse_args (int argc, char **argv, int optind) { + + struct option *popt; + const char *optarg, *r; + + if (argc <= optind) { + print_help (); + } + + while (optind < argc) { + + r = argv[optind++]; + + if (r[0] != '-' || r[1] == '\0') { + + dynarray_add (&files, &nb_files, xstrdup (r)); + continue; + + } + + for (popt = opts; popt; popt++) { + + const char *p1 = popt->name; + const char *r1 = r; + + if (!p1) { + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); + + } + + if (!strstart (p1, &r1)) { + continue; + } + + optarg = r1; + + if (popt->flags & OPTION_HAS_ARG) { + + if (*optarg == '\0') { + + if (optind >= argc) { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r); + exit (EXIT_FAILURE); + + } + + optarg = argv[optind++]; + + } + + } else if (*optarg != '\0') { + continue; + } + + break; + + } + + switch (popt->index) { + + case OPTION_HELP: { + + print_help (); + break; + + } + + case OPTION_INPUT: { + + if (outfile) { + + report_at (program_name, 0, REPORT_ERROR, "multiple output files provided"); + exit (EXIT_FAILURE); + + } + + outfile = xstrdup (optarg); + break; + + } + + case OPTION_MODE: { + + if (strlen (optarg) != 3) { + + report_at (program_name, 0, REPORT_ERROR, "invalid mode provided"); + exit (EXIT_FAILURE); + + } + + memcpy (mode + 3, optarg, 3); + break; + + } + + case OPTION_OFFSET: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "bad number for offset (%s)", optarg); + exit (EXIT_FAILURE); + + } + + if (conversion < 0 || conversion > INT_MAX) { + + report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", INT_MAX); + exit (EXIT_FAILURE); + + } + + offset = conversion; + break; + + } + + case OPTION_ROOT: { + + if (root) { + free (root); + } + + root = xstrdup (optarg); + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); + exit (EXIT_FAILURE); + + } + + } + + } + +} + +static void cleanup (void) { + + if (ofp != NULL) { + fclose (ofp); + } + +} + +static unsigned long find_entry (struct filesystem *fs, unsigned long nod, const char *name) { + + struct blockwalker bw; + unsigned long nlen, bk; + + nlen = strlen (name); + init_bw (&bw); + + while ((bk = walk_bw (fs, nod, &bw, 0, 0)) != WALK_END) { + + struct directory *d; + struct dirwalker dw; + + for (d = get_dir (fs, bk, &dw); d; d = next_dir (&dw)) { + + if (byte_array_to_integer (d->d_inode, 4) && (nlen == byte_array_to_integer (d->d_name_len, 2)) && !strncmp (dir_name (&dw), name, nlen)) { + + unsigned long result = byte_array_to_integer (d->d_inode, 4); + put_dir (&dw); + + return result; + + } + + } + + put_dir (&dw); + + } + + return 0; + +} + +#define CB_SIZE (COPY_BLOCKS * BLOCKSIZE) + +signed long read_cb (struct filesystem *fs, struct inode_pos *ipos, signed long size, void *data) { + + signed long remaining_size = size; + + unsigned long readbytes; + int fullsize; + + unsigned char *b = xmalloc (CB_SIZE); + + while ((readbytes = fread (b, 1, MIN (remaining_size, CB_SIZE), (FILE *) data))) { + + remaining_size -= readbytes; + + fullsize = rndup (readbytes, BLOCKSIZE); + memset (b + readbytes, 0, fullsize - readbytes); + + extend_inode_blk (fs, ipos, b, fullsize / BLOCKSIZE); + + } + + free (b); + return size; + +} + +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 + +unsigned long mkfile_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, void *data, unsigned long size, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime) { + + unsigned long nod; + + struct nod_info *ni; + struct inode *node; + + signed long actual_size; + struct inode_pos ipos; + + nod = mknod_fs (fs, parent_nod, name, mode_con (mode), uid, gid, ctime, mtime); + node = get_nod (fs, nod, &ni); + + inode_pos_init (fs, &ipos, nod, INODE_POS_TRUNCATE, 0); + + if ((actual_size = read_cb (fs, &ipos, size, data)) > 0x7FFFFFFF) { + + unsigned long feature_ro_compat = byte_array_to_integer (fs->sb->s_feature_ro_compat, 4); + + if (byte_array_to_integer (fs->sb->s_rev_level, 4) < 1) { + fs_upgrade_rev1_largefile (fs); + } + + write_to_byte_array (fs->sb->s_feature_ro_compat, feature_ro_compat | EXT2_FEATURE_RO_COMPAT_LARGE_FILE, 4); + + } + + write_to_byte_array (node->i_dir_acl, actual_size >> 32, 4); + write_to_byte_array (node->i_size, actual_size, 4); + + inode_pos_finish (&ipos); + + put_nod (ni); + return nod; + +} + +static int walk_path (struct filesystem *fs, char *dirname) { + + unsigned long nod = EXT2_ROOT_INO; + char *p1 = dirname, *p2; + + while (*p1 && *p1 == '/') { + p1++; + } + + if (*p1) { + + for (; (p2 = strchr (p1, '/')); p1 = p2 + 1) { + + *p2 = '\0'; + + if (strlen (p1) >= 14) { + p1[13] = '\0'; + } + + if (!(nod = find_entry (fs, nod, p1))) { + + report_at (program_name, 0, REPORT_ERROR, "cannot access '%s'", dirname); + + *p2 = '/'; + return 0; + + } + + *p2 = '/'; + + } + + if (!(nod = find_entry (fs, nod, p1))) { + + report_at (program_name, 0, REPORT_ERROR, "cannot access '%s'", dirname); + return 0; + + } + + } + + return nod; + +} + +static void copy_file (struct filesystem *fs, FILE *fp, unsigned long parent, char *fn) { + + unsigned long bytes = 0, timestamp = time (0); + char *basename = fn, *p; + + if ((p = strrchr (fn, '/'))) { + basename = (p + 1); + } + + fseek (fp, 0, SEEK_END); + + bytes = ftell (fp); + fseek (fp, 0, SEEK_SET); + + mkfile_fs (fs, parent, basename, fp, bytes, 0, 0, timestamp, timestamp); + +} + +int main (int argc, char **argv) { + + struct superblock sup = { 0 }; + + unsigned long parent_ino = EXT2_ROOT_INO; + struct filesystem *fs; + + FILE *fp = NULL; + long i; + + if (argc && *argv) { + + char *p; + program_name = *argv; + + if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { + program_name = (p + 1); + } + + } + + atexit (cleanup); + + mode = xstrdup ("---666"); + parse_args (argc, argv, 1); + + if (!outfile) { + + report_at (program_name, 0, REPORT_ERROR, "no input file was provided"); + return EXIT_FAILURE; + + } + + if ((ofp = fopen (outfile, "r+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for writing", outfile); + return EXIT_FAILURE; + + } + + seekto (ofp, EXT2_SUPERBLOCK_OFFSET / BLOCKSIZE); + + if (fread (&sup, sizeof (sup), 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed to whilst reading '%s'", outfile); + exit (EXIT_FAILURE); + + } + + if (sup.s_magic[0] != (EXT2_SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((EXT2_SUPER_MAGIC >> 8) & 0xff)) { + + report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", outfile); + exit (EXIT_FAILURE); + + } + + fs = alloc_fs (); + fs->swapit = 0; + + fs->sb = ⊃ + + if (nb_files >= 2) { + + char *path = files[nb_files - 1]; + char *p; + + if (path[0] == ':' && path[1] == ':') { + + path += 2; + + if (nb_files > 2) { + + report_at (program_name, 0, REPORT_ERROR, "cannot copy multiple files to %s", path); + return EXIT_FAILURE; + + } + + if (root) { + + report_at (program_name, 0, REPORT_WARNING, "root '%s' is ignored, using output path instead", root); + free (root); + + root = 0; + + } + + if ((p = strrchr (path, '/'))) { + + *p = '\0'; + + if (!(parent_ino = walk_path (fs, path))) { + return EXIT_FAILURE; + } + + path = (p + 1); + + } + + if ((fp = fopen (files[0], "rb")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", files[0]); + return EXIT_FAILURE; + + } + + copy_file (fs, fp, parent_ino, path); + + fclose (fp); + return (get_error_count () > 0) ? EXIT_FAILURE : EXIT_SUCCESS; + + } + + } + + if (root) { + + if (!(parent_ino = walk_path (fs, root))) { + return EXIT_FAILURE; + } + + } + + for (i = 0; i < nb_files; i++) { + + if ((fp = fopen (files[i], "rb")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", files[i]); + continue; + + } + + copy_file (fs, fp, parent_ino, files[i]); + fclose (fp); + + } + + finish_fs (fs); + return (get_error_count () > 0) ? EXIT_FAILURE : EXIT_SUCCESS; + +} diff --git a/e2md.c b/e2md.c new file mode 100644 index 0000000..c2347b1 --- /dev/null +++ b/e2md.c @@ -0,0 +1,410 @@ +/****************************************************************************** + * @file e2md.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "inode.h" +#include "report.h" +#include "super.h" + +static char *mode = 0; + +#define OPTION_INPUT 0x0001 +#define OPTION_HELP 0x0002 +#define OPTION_MODE 0x0003 +#define OPTION_OFFSET 0x0004 + +struct option { + + const char *name; + int index, flags; + +}; + +#define OPTION_NO_ARG 0x0001 +#define OPTION_HAS_ARG 0x0002 + +static struct option opts[] = { + + { "-i", OPTION_INPUT, OPTION_HAS_ARG }, + + { "--help", OPTION_HELP, OPTION_NO_ARG }, + { "--mode", OPTION_MODE, OPTION_HAS_ARG }, + { "--offset", OPTION_OFFSET, OPTION_HAS_ARG }, + + { 0, 0, 0 } + +}; + +static char **dirs = 0; +static long nb_dirs = 0; + +static int strstart (const char *val, const char **str) { + + const char *p = val; + const char *q = *str; + + while (*p != '\0') { + + if (*p != *q) { + return 0; + } + + ++p; + ++q; + + } + + *str = q; + return 1; + +} + +static void print_help (void) { + + if (program_name) { + + fprintf (stderr, "Usage: %s [options] dirname\n\n", program_name); + fprintf (stderr, "Options:\n\n"); + + fprintf (stderr, " Short options:\n\n"); + fprintf (stderr, " -i Specify the input target.\n"); + + fprintf (stderr, "\n"); + + fprintf (stderr, " Long options:\n\n"); + fprintf (stderr, " --help Show this help information then exit.\n"); + fprintf (stderr, " --offset SECTOR Write the filesystem starting at SECTOR.\n"); + + } + + exit (EXIT_SUCCESS); + +} + +static void dynarray_add (void *ptab, long *nb_ptr, void *data) { + + int nb, nb_alloc; + void **pp; + + nb = *nb_ptr; + pp = *(void ***) ptab; + + if ((nb & (nb - 1)) == 0) { + + if (!nb) { + nb_alloc = 1; + } else { + nb_alloc = nb * 2; + } + + pp = xrealloc (pp, nb_alloc * sizeof (void *)); + *(void ***) ptab = pp; + + } + + pp[nb++] = data; + *nb_ptr = nb; + +} + +static void parse_args (int argc, char **argv, int optind) { + + struct option *popt; + const char *optarg, *r; + + if (argc <= optind) { + print_help (); + } + + while (optind < argc) { + + r = argv[optind++]; + + if (r[0] != '-' || r[1] == '\0') { + + dynarray_add (&dirs, &nb_dirs, xstrdup (r)); + continue; + + } + + for (popt = opts; popt; popt++) { + + const char *p1 = popt->name; + const char *r1 = r; + + if (!p1) { + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); + + } + + if (!strstart (p1, &r1)) { + continue; + } + + optarg = r1; + + if (popt->flags & OPTION_HAS_ARG) { + + if (*optarg == '\0') { + + if (optind >= argc) { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r); + exit (EXIT_FAILURE); + + } + + optarg = argv[optind++]; + + } + + } else if (*optarg != '\0') { + continue; + } + + break; + + } + + switch (popt->index) { + + case OPTION_HELP: { + + print_help (); + break; + + } + + case OPTION_INPUT: { + + if (outfile) { + + report_at (program_name, 0, REPORT_ERROR, "multiple output files provided"); + exit (EXIT_FAILURE); + + } + + outfile = xstrdup (optarg); + break; + + } + + case OPTION_MODE: { + + if (strlen (optarg) != 3) { + + report_at (program_name, 0, REPORT_ERROR, "invalid mode provided"); + exit (EXIT_FAILURE); + + } + + memcpy (mode + 3, optarg, 3); + break; + + } + + case OPTION_OFFSET: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "bad number for offset (%s)", optarg); + exit (EXIT_FAILURE); + + } + + if (conversion < 0 || conversion > INT_MAX) { + + report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", INT_MAX); + exit (EXIT_FAILURE); + + } + + offset = conversion; + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); + exit (EXIT_FAILURE); + + } + + } + + } + +} + +static void cleanup (void) { + + if (ofp != NULL) { + fclose (ofp); + } + +} + +static unsigned long find_entry (struct filesystem *fs, unsigned long nod, const char *name) { + + struct blockwalker bw; + unsigned long nlen, bk; + + nlen = strlen (name); + init_bw (&bw); + + while ((bk = walk_bw (fs, nod, &bw, 0, 0)) != WALK_END) { + + struct directory *d; + struct dirwalker dw; + + for (d = get_dir (fs, bk, &dw); d; d = next_dir (&dw)) { + + if (byte_array_to_integer (d->d_inode, 4) && (nlen == byte_array_to_integer (d->d_name_len, 2)) && !strncmp (dir_name (&dw), name, nlen)) { + + unsigned long result = byte_array_to_integer (d->d_inode, 4); + put_dir (&dw); + + return result; + + } + + } + + put_dir (&dw); + + } + + return 0; + +} + +static void walk_dir (struct filesystem *fs, char *dirname) { + + unsigned long parent_nod = EXT2_ROOT_INO; + unsigned long timestamp = time (0); + + char *p1 = dirname, *p2; + + while (*p1 && *p1 == '/') { + p1++; + } + + if (*p1) { + + for (; (p2 = strchr (p1, '/')); p1 = p2 + 1) { + + *p2 = '\0'; + + if (strlen (p1) >= 14) { + p1[13] = '\0'; + } + + if (!(parent_nod = find_entry (fs, parent_nod, p1))) { + + report_at (program_name, 0, REPORT_ERROR, "cannot access '%s'", dirname); + + *p2 = '/'; + return; + + } + + *p2 = '/'; + + } + + if (find_entry (fs, parent_nod, p1)) { + + report_at (program_name, 0, REPORT_ERROR, "'%s' already exists", dirname); + return; + + } + + } + + mknod_fs (fs, parent_nod, p1, mode_con (mode), 0, 0, timestamp, timestamp); + +} + +int main (int argc, char **argv) { + + struct superblock sup = { 0 }; + + struct filesystem *fs; + long i; + + if (argc && *argv) { + + char *p; + program_name = *argv; + + if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { + program_name = (p + 1); + } + + } + + atexit (cleanup); + + mode = xstrdup ("d--644"); + parse_args (argc, argv, 1); + + if (!outfile) { + + report_at (program_name, 0, REPORT_ERROR, "no input file was provided"); + return EXIT_FAILURE; + + } + + if ((ofp = fopen (outfile, "r+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for writing", outfile); + return EXIT_FAILURE; + + } + + seekto (ofp, EXT2_SUPERBLOCK_OFFSET / BLOCKSIZE); + + if (fread (&sup, sizeof (sup), 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed to whilst reading '%s'", outfile); + exit (EXIT_FAILURE); + + } + + if (sup.s_magic[0] != (EXT2_SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((EXT2_SUPER_MAGIC >> 8) & 0xff)) { + + report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", outfile); + exit (EXIT_FAILURE); + + } + + fs = alloc_fs (); + fs->swapit = 0; + + fs->sb = ⊃ + + for (i = 0; i < nb_dirs; i++) { + walk_dir (fs, dirs[i]); + } + + finish_fs (fs); + return (get_error_count () > 0) ? EXIT_FAILURE : EXIT_SUCCESS; + +} diff --git a/inode.h b/inode.h new file mode 100644 index 0000000..9599b24 --- /dev/null +++ b/inode.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * @file inode.h + *****************************************************************************/ +#ifndef _INODE_H +#define _INODE_H + +#define EXT2_INIT_BLOCK 0xFFFFFFFF +#define EXT2_NDIR_BLOCKS 11 + +#define EXT2_IND_BLOCK (EXT2_NDIR_BLOCKS + 1) + +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) + +#define EXT2_NBLOCKS (EXT2_TIND_BLOCK + 1) + +struct inode { + + unsigned char i_mode[2]; + unsigned char i_uid[2]; + unsigned char i_size[4]; + + unsigned char i_atime[4]; + unsigned char i_ctime[4]; + unsigned char i_mtime[4]; + unsigned char i_dtime[4]; + + unsigned char i_gid[2]; + unsigned char i_nlinks[2]; + + unsigned char i_blocks[4]; + unsigned char i_flags[4]; + + unsigned char i_reserved1[4]; + unsigned char i_block[EXT2_NBLOCKS * 4]; + + unsigned char i_version[4]; + + unsigned char i_file_acl[4]; + unsigned char i_dir_acl[4]; + + unsigned char i_faddr[4]; + unsigned char i_frag; + unsigned char i_fsize; + unsigned char i_pad1[2]; + + unsigned char i_reserved2[8]; + +}; + +#define INODE_BLOCKSIZE 512 + +#endif /* _INODE_H */ diff --git a/list.c b/list.c new file mode 100644 index 0000000..4f146b1 --- /dev/null +++ b/list.c @@ -0,0 +1,49 @@ +/****************************************************************************** + * @file list.c + *****************************************************************************/ +#include "list.h" + +void list_init (struct list_elem *list) { + + list->next = list; + list->prev = list; + +} + +void list_add_after (struct list_elem *pos, struct list_elem *elem) { + + elem->next = pos->next; + elem->prev = pos; + + pos->next->prev = elem; + pos->next = elem; + +} + +void list_add_before (struct list_elem *pos, struct list_elem *elem) { + + elem->prev = pos->prev; + elem->next = pos; + + pos->prev->next = elem; + pos->prev = elem; + +} + +void list_del (struct list_elem *elem) { + + elem->next->prev = elem->prev; + elem->prev->next = elem->next; + +} + +void list_item_init (struct list_elem *elem) { + + elem->next = elem; + elem->prev = elem; + +} + +int list_empty (struct list_elem *elem) { + return elem->next == elem; +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..452daa9 --- /dev/null +++ b/list.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * @file list.h + *****************************************************************************/ +#ifndef _LIST_H +#define _LIST_H + +#include + +#ifndef offsetof +# define offsetof(st, m) ((size_t) ((char *) &((st *)(0))->m - (char *) 0)) +#endif + +#define container_of(ptr, type, member) \ +({ \ + const __typeof__ (((type *) 0)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof (type, member)); \ +}) + +struct list_elem { + + struct list_elem *next; + struct list_elem *prev; + +}; + +void list_init (struct list_elem *list); + +void list_add_after (struct list_elem *pos, struct list_elem *elem); +void list_add_before (struct list_elem *pos, struct list_elem *elem); + +void list_del (struct list_elem *elem); +void list_item_init (struct list_elem *elem); + +int list_empty (struct list_elem *elem); + +#define list_for_each_elem(list, curr) \ + for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next) + +#define list_for_each_elem_safe(list, curr, next) \ + for ((curr) = (list)->next, (next) = (curr)->next; (curr) != (list); (curr) = (next), (next) = (curr)->next) + +#endif /* _LIST_H */ diff --git a/mke2fs.c b/mke2fs.c new file mode 100644 index 0000000..435904e --- /dev/null +++ b/mke2fs.c @@ -0,0 +1,775 @@ +/****************************************************************************** + * @file mke2fs.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "inode.h" +#include "list.h" +#include "report.h" + +static unsigned long sectors = 0; +static char *boot_sector = 0; + +#define OPTION_BOOT 0x0001 +#define OPTION_HELP 0x0002 +#define OPTION_OFFSET 0x0003 +#define OPTION_SECTORS 0x0004 +#define OPTION_VERBOSE 0x0005 + +struct option { + + const char *name; + int index, flags; + +}; + +#define OPTION_NO_ARG 0x0001 +#define OPTION_HAS_ARG 0x0002 + +static struct option opts[] = { + + { "--boot", OPTION_BOOT, OPTION_HAS_ARG }, + { "--help", OPTION_HELP, OPTION_NO_ARG }, + { "--offset", OPTION_OFFSET, OPTION_HAS_ARG }, + { "--sectors", OPTION_SECTORS, OPTION_HAS_ARG }, + + { 0, 0, 0 } + +}; + +static int strstart (const char *val, const char **str) { + + const char *p = val; + const char *q = *str; + + while (*p != '\0') { + + if (*p != *q) { + return 0; + } + + ++p; + ++q; + + } + + *str = q; + return 1; + +} + +static void print_help (void) { + + if (program_name) { + + fprintf (stderr, "Usage: %s [options] outfile\n\n", program_name); + fprintf (stderr, "Options:\n\n"); + + fprintf (stderr, " --boot FILE Use FILE as the boot sector.\n"); + fprintf (stderr, " --help Show this help information then exit.\n"); + fprintf (stderr, " --offset SECTOR Write the filesystem starting at SECTOR.\n"); + fprintf (stderr, " --sectors COUNT Make the filesystem the size of COUNT * 512.\n"); + + } + + exit (EXIT_SUCCESS); + +} + +static void parse_args (int argc, char **argv, int optind) { + + struct option *popt; + const char *optarg, *r; + + if (argc <= optind) { + print_help (); + } + + while (optind < argc) { + + r = argv[optind++]; + + if (r[0] != '-' || r[1] == '\0') { + + if (outfile) { + + report_at (program_name, 0, REPORT_ERROR, "multiple output files provided"); + exit (EXIT_FAILURE); + + } + + outfile = xstrdup (r); + continue; + + } + + for (popt = opts; popt; popt++) { + + const char *p1 = popt->name; + const char *r1 = r; + + if (!p1) { + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); + + } + + if (!strstart (p1, &r1)) { + continue; + } + + optarg = r1; + + if (popt->flags & OPTION_HAS_ARG) { + + if (*optarg == '\0') { + + if (optind >= argc) { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r); + exit (EXIT_FAILURE); + + } + + optarg = argv[optind++]; + + } + + } else if (*optarg != '\0') { + continue; + } + + break; + + } + + switch (popt->index) { + + case OPTION_BOOT: { + + if (boot_sector) { free (boot_sector); } + + boot_sector = xstrdup (optarg); + break; + + } + + case OPTION_HELP: { + + print_help (); + break; + + } + + case OPTION_OFFSET: { + + char *temp; + long conversion; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || *temp || errno) { + + report_at (program_name, 0, REPORT_ERROR, "bad number for offset"); + exit (EXIT_FAILURE); + + } + + if (conversion < 0 || conversion > INT_MAX) { + + report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", INT_MAX); + exit (EXIT_FAILURE); + + } + + offset = conversion; + break; + + } + + case OPTION_SECTORS: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "bad number for sectors count"); + exit (EXIT_FAILURE); + + } + + if (conversion < 0) { + + report_at (program_name, 0, REPORT_ERROR, "sectors count must be greater than zero"); + exit (EXIT_FAILURE); + + } + + sectors = conversion; + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); + exit (EXIT_FAILURE); + + } + + } + + } + +} + + +static unsigned long reserved_sectors = 4; + +static void wipe_sectors (unsigned long start, unsigned long end) { + + void *zero = xmalloc (BLOCKSIZE); + unsigned long i; + + seekto (ofp, start); + + for (i = start; i < end; i++) { + + if (fwrite (zero, SECTOR_SIZE, 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing blank sector"); + free (zero); + + exit (EXIT_FAILURE); + + } + + } + + seekto (ofp, start); + free (zero); + +} + +static void cleanup (void) { + + if (ofp != NULL) { fclose (ofp); } + + if (get_error_count () > 0) { + remove (outfile); + } + +} + +#define INODES_PER_GROUP 8192 +#define BLOCKS_PER_GROUP 8192 + +struct uuid { + + unsigned long time_low; + unsigned short time_mid; + + unsigned short time_hi_and_version; + unsigned short clock_seq; + + unsigned char node[6]; + +}; + +static void uuid_pack (struct uuid *uu, void *ptr) { + + unsigned char *out = ptr; + unsigned long tmp; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + + tmp >>= 8; + out[2] = (unsigned char) tmp; + + tmp >>= 8; + out[1] = (unsigned char) tmp; + + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy (out + 10, uu->node, 6); + +} + +static void uuid_unpack (void *buf, struct uuid *uu) { + + unsigned char *ptr = buf; + unsigned long tmp; + + tmp = byte_array_to_integer (ptr, 4); + uu->time_low = tmp; + + ptr += 4; + + tmp = byte_array_to_integer (ptr, 2); + uu->time_mid = tmp; + + ptr += 2; + + tmp = byte_array_to_integer (ptr, 2); + uu->time_hi_and_version = tmp; + + ptr += 2; + + tmp = byte_array_to_integer (ptr, 2); + uu->clock_seq = tmp; + + memcpy (uu->node, ptr + 2, 6); + +} + +static void get_random_bytes (void *buf, int nbytes) { + + unsigned char *cp; + int i; + + srand (time (0)); + + for (cp = buf, i = 0; i < nbytes; i++) { + *cp++ ^= (rand () >> 7) & UCHAR_MAX; + } + +} + +static void uuid_generate (unsigned char out[16]) { + + struct uuid uu; + unsigned char buf[16]; + + get_random_bytes (buf, sizeof (buf)); + uuid_unpack (buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; + + uuid_pack (&uu, out); + +} + +static struct filesystem *init_fs (long nbblocks, long nbinodes, long nbresrvd, int holes, unsigned long fs_timestamp, unsigned long creator_os, int swapit) { + + struct group_descriptor *gd; + struct gd_info *gi; + struct blk_info *bi; + + struct nod_info *ni; + struct inode *itab0; + + unsigned char *bbm, *ibm; + struct filesystem *fs; + + struct inode_pos ipos; + struct dirwalker dw; + + unsigned long i, j, bbmpos, ibmpos, itblpos; + unsigned long nod, first_block, gdsz, itblsz; + unsigned long nbgroups, nbinodes_per_group, overhead_per_group; + unsigned long free_blocks, free_blocks_per_group, free_inodes_count; + unsigned long nbblocks_per_group, min_nbgroups; + + first_block = (BLOCKSIZE == 1024); + + if (nbresrvd < 0) { + + report_at (program_name, 0, REPORT_ERROR, "reserved blocks value is invalid"); + exit (EXIT_FAILURE); + + } + + if (nbinodes < EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0)) { + + report_at (program_name, 0, REPORT_ERROR, "too few inodes"); + exit (EXIT_FAILURE); + + } + + if (nbblocks < 0) { + + report_at (program_name, 0, REPORT_ERROR, "too few blocks"); + exit (EXIT_FAILURE); + + } + + min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP; + + if ((nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP) < min_nbgroups) { + nbgroups = min_nbgroups; + } + + nbblocks_per_group = rndup ((nbblocks - first_block + nbgroups - 1) / nbgroups, 8); + nbinodes_per_group = rndup ((nbinodes + nbgroups - 1) / nbgroups, BLOCKSIZE / sizeof (struct inode)); + + if (nbinodes_per_group < 16) { + nbinodes_per_group = 16; + } + + itblsz = nbinodes_per_group * sizeof (struct inode) / BLOCKSIZE; + gdsz = rndup (nbgroups * sizeof (struct group_descriptor), BLOCKSIZE) / BLOCKSIZE; + + overhead_per_group = 3 + gdsz + itblsz; + + free_blocks = nbblocks - overhead_per_group * nbgroups - first_block; + free_blocks_per_group = nbblocks_per_group - overhead_per_group; + + fs = alloc_fs (); + fs->swapit = swapit; + + fs->sb = xmalloc (sizeof (*fs->sb)); + + write_to_byte_array (fs->sb->s_inodes_count, nbinodes_per_group * nbgroups, 4); + write_to_byte_array (fs->sb->s_blocks_count, nbblocks, 4); + + write_to_byte_array (fs->sb->s_r_blocks_count, nbresrvd, 4); + + write_to_byte_array (fs->sb->s_free_blocks_count, free_blocks, 4); + write_to_byte_array (fs->sb->s_free_inodes_count, byte_array_to_integer (fs->sb->s_inodes_count, 4) - EXT2_FIRST_INO + 1, 4); + + write_to_byte_array (fs->sb->s_first_data_block, first_block, 4); + + write_to_byte_array (fs->sb->s_log_block_size, BLOCKSIZE >> 11, 4); + write_to_byte_array (fs->sb->s_log_frag_size, BLOCKSIZE >> 11, 4); + + write_to_byte_array (fs->sb->s_blocks_per_group, nbblocks_per_group, 4); + write_to_byte_array (fs->sb->s_frags_per_group, nbblocks_per_group, 4); + write_to_byte_array (fs->sb->s_inodes_per_group, nbinodes_per_group, 4); + + write_to_byte_array (fs->sb->s_wtime, fs_timestamp, 4); + write_to_byte_array (fs->sb->s_magic, EXT2_SUPER_MAGIC, 2); + + write_to_byte_array (fs->sb->s_lastcheck, fs_timestamp, 4); + write_to_byte_array (fs->sb->s_creator_os, creator_os, 4); + + uuid_generate (fs->sb->s_uuid); + + for (i = 0, bbmpos = first_block + 1 + gdsz, ibmpos = bbmpos + 1, itblpos = ibmpos + 1; i < nbgroups; i++, bbmpos += nbblocks_per_group, ibmpos += nbblocks_per_group, itblpos += nbblocks_per_group) { + + gd = get_gd (fs, i, &gi); + + if (free_blocks > free_blocks_per_group) { + + write_to_byte_array (gd->bg_free_blocks_count, free_blocks_per_group, 2); + free_blocks -= free_blocks_per_group; + + } else { + + write_to_byte_array (gd->bg_free_blocks_count, free_blocks, 2); + free_blocks = 0; + + } + + if (i) { + write_to_byte_array (gd->bg_free_inodes_count, nbinodes_per_group, 2); + } else { + write_to_byte_array (gd->bg_free_inodes_count, nbinodes_per_group - EXT2_FIRST_INO + 2, 2); + } + + write_to_byte_array (gd->bg_used_dirs_count, 0, 2); + + write_to_byte_array (gd->bg_block_bitmap, bbmpos, 4); + write_to_byte_array (gd->bg_inode_bitmap, ibmpos, 4); + write_to_byte_array (gd->bg_inode_table, itblpos, 4); + + put_gd (gi); + + } + + for (i = 0; i < nbgroups; i++) { + + gd = get_gd (fs, i, &gi); + + /* Block bitmap. */ + bbm = GRP_GET_GROUP_BBM (fs, gd, &bi); + + for (j = byte_array_to_integer (gd->bg_free_blocks_count, 2) + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++) { + allocate (bbm, j); + } + + /* System blocks. */ + for (j = 1; j <= overhead_per_group; j++) { + allocate (bbm, j); + } + + GRP_PUT_GROUP_BBM (bi); + + /* Indoe bitmap. */ + ibm = GRP_GET_GROUP_IBM (fs, gd, &bi); + + for (j = byte_array_to_integer (fs->sb->s_inodes_per_group, 4) + 1; j <= BLOCKSIZE * 8; j++) { + allocate (ibm, j); + } + + /* System inodes. */ + if (i == 0) { + + for (j = 1; j < EXT2_FIRST_INO; j++) { + allocate (ibm, j); + } + + } + + GRP_PUT_GROUP_IBM (bi); + put_gd (gi); + + } + + gd = get_gd (fs, 0, &gi); + + free_inodes_count = byte_array_to_integer (gd->bg_free_inodes_count, 2); + write_to_byte_array (gd->bg_free_inodes_count, free_inodes_count - 1, 2); + + write_to_byte_array (gd->bg_used_dirs_count, 1, 2); + put_gd (gi); + + itab0 = get_nod (fs, EXT2_ROOT_INO, &ni); + + write_to_byte_array (itab0->i_mode, FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH, 2); + write_to_byte_array (itab0->i_ctime, fs_timestamp, 4); + write_to_byte_array (itab0->i_mtime, fs_timestamp, 4); + write_to_byte_array (itab0->i_atime, fs_timestamp, 4); + write_to_byte_array (itab0->i_size, BLOCKSIZE, 4); + write_to_byte_array (itab0->i_nlinks, 2, 2); + + put_nod (ni); + + new_dir (fs, EXT2_ROOT_INO, ".", 1, &dw); + shrink_dir (&dw, EXT2_ROOT_INO, "..", 2); + + next_dir (&dw); /* force the data into the buffer. */ + + inode_pos_init (fs, &ipos, EXT2_ROOT_INO, INODE_POS_EXTEND, 0); + extend_inode_blk (fs, &ipos, dw.b, 1); + inode_pos_finish (&ipos); + + put_dir (&dw); + + if (byte_array_to_integer (fs->sb->s_r_blocks_count, 4)) { + + struct inode *node; + unsigned char *b; + + nod = mkdir_fs (fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp); + + b = get_workblk (); + write_to_byte_array (((struct directory *) b)->d_rec_len, BLOCKSIZE, 2); + + inode_pos_init (fs, &ipos, nod, INODE_POS_EXTEND, 0); + + for (i = 1; i < 16; i++) { + extend_inode_blk (fs, &ipos, b, 1); + } + + inode_pos_finish (&ipos); + free (b); + + node = get_nod (fs, nod, &ni); + write_to_byte_array (node->i_size, 16 * BLOCKSIZE, 4); + + put_nod (ni); + + } + + write_to_byte_array (fs->sb->s_state, 1, 2); + write_to_byte_array (fs->sb->s_max_mount_count, 20, 2); + + fs->holes = holes; + return fs; + +} + +int main (int argc, char **argv) { + + unsigned long nbblocks = 0, nbresrvd = 0, nbinodes = 0; + unsigned long image_size = 0, holes = 0; + + struct filesystem *fs; + + if (argc && *argv) { + + char *p; + program_name = *argv; + + if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { + program_name = (p + 1); + } + + } + + atexit (cleanup); + parse_args (argc, argv, 1); + + if (!outfile) { + + report_at (program_name, 0, REPORT_ERROR, "no output file provided"); + return EXIT_FAILURE; + + } + + image_size = sectors * SECTOR_SIZE; + + if ((nbblocks = image_size / BLOCKSIZE) < 5) { + + report_at (program_name, 0, REPORT_ERROR, "block count too small"); + return EXIT_FAILURE; + + } + + nbresrvd = (nbblocks * 5) / 100; + nbinodes = EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0); + + image_size += offset * SECTOR_SIZE; + + if ((ofp = fopen (outfile, "r+b")) == NULL) { + + unsigned long len; + void *zero; + + if ((ofp = fopen (outfile, "w+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", outfile); + return EXIT_FAILURE; + + } + + zero = xmalloc (BLOCKSIZE); + len = image_size; + + while (len > 0) { + + if (fwrite (zero, SECTOR_SIZE, 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing '%s'", outfile); + free (zero); + + return EXIT_FAILURE; + + } + + len -= SECTOR_SIZE; + + } + + free (zero); + + } + + seekto (ofp, 0); + + if (offset * SECTOR_SIZE > image_size) { + + report_at (program_name, 0, REPORT_ERROR, "size (%lu) of %s is less than the requested offset (%lu)", image_size, outfile, offset * SECTOR_SIZE); + return EXIT_FAILURE; + + } + + image_size -= offset * SECTOR_SIZE; + + if (sectors) { + + if (sectors * SECTOR_SIZE > image_size) { + + report_at (program_name, 0, REPORT_ERROR, "size (%lu) of %s is less than the requested size (%lu)", image_size, outfile, sectors * SECTOR_SIZE); + return EXIT_FAILURE; + + } + + } + + wipe_sectors (0, reserved_sectors); + + if (boot_sector) { + + unsigned long bytes; + void *boot_block; + + FILE *ifp; + + if ((ifp = fopen (boot_sector, "rb")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "unable to open %s", boot_sector); + exit (EXIT_FAILURE); + + } + + fseek (ifp, 0, SEEK_END); + + if ((bytes = ftell (ifp)) > BLOCKSIZE) { + + report_at (program_name, 0, REPORT_ERROR, "boot sector must not exceed 1024 bytes in size"); + fclose (ifp); + + exit (EXIT_FAILURE); + + } + + boot_block = xmalloc (BLOCKSIZE); + fseek (ifp, 0, SEEK_SET); + + if (fread (boot_block, bytes, 1, ifp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed to read %s", boot_sector); + free (boot_block); + + fclose (ifp); + return EXIT_FAILURE; + + } + + fclose (ifp); + + if (fwrite (boot_block, BLOCKSIZE, 1, ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "to write boot sector to %s", outfile); + free (boot_block); + + return EXIT_FAILURE; + + } + + free (boot_block); + + } + + fs = init_fs (nbblocks, nbinodes, nbresrvd, holes, time (0), 0, 0); + fs_upgrade_rev1_largefile (fs); + + finish_fs (fs); + return EXIT_SUCCESS; + +} diff --git a/report.c b/report.c new file mode 100644 index 0000000..8e128ef --- /dev/null +++ b/report.c @@ -0,0 +1,150 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include "report.h" + +unsigned long errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +static int OriginalConsoleColor = -1; +#endif + +static void reset_console_color (void) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + + if (OriginalConsoleColor == -1) { return; } + + SetConsoleTextAttribute (hStdError, OriginalConsoleColor); + OriginalConsoleColor = -1; + +#else + + fprintf (stderr, "\033[0m"); + +#endif + +} + +static void set_console_color (int color) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + WORD wColor; + + if (OriginalConsoleColor == -1) { + + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) { + return; + } + + OriginalConsoleColor = csbi.wAttributes; + + } + + wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF); + SetConsoleTextAttribute (hStdError, wColor); + +#else + + fprintf (stderr, "\033[%dm", color); + +#endif + +} +#endif + +static void output_message (const char *filename, unsigned long lineno, unsigned long idx, enum report_type type, const char *fmt, va_list ap) { + + if (filename) { + + if (lineno == 0 && idx == 0) { + fprintf (stderr, "%s: ", filename); + } else { + fprintf (stderr, "%s:", filename); + } + + } + + if (lineno > 0) { + + if (idx == 0) { + fprintf (stderr, "%lu: ", lineno); + } else { + fprintf (stderr, "%lu:", lineno); + } + + } + + if (idx > 0) { + fprintf (stderr, "%lu: ", idx); + } + + if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_ERROR); +#endif + + if (type == REPORT_ERROR) { + fprintf (stderr, "error:"); + } else { + fprintf (stderr, "fatal error:"); + } + + } else if (type == REPORT_INTERNAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_INTERNAL_ERROR); +#endif + + fprintf (stderr, "internal error:"); + + } else if (type == REPORT_WARNING) { + +#ifndef __PDOS__ + set_console_color (COLOR_WARNING); +#endif + + fprintf (stderr, "warning:"); + + } + +#ifndef __PDOS__ + reset_console_color (); +#endif + + fprintf (stderr, " "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + + if (type != REPORT_WARNING) { + ++errors; + } + +} + +unsigned long get_error_count (void) { + return errors; +} + +void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...) { + + va_list ap; + + va_start (ap, fmt); + output_message (filename, lineno, 0, type, fmt, ap); + va_end (ap); + +} diff --git a/report.h b/report.h new file mode 100644 index 0000000..8fc8758 --- /dev/null +++ b/report.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +enum report_type { + + REPORT_ERROR = 0, + REPORT_FATAL_ERROR, + REPORT_INTERNAL_ERROR, + REPORT_WARNING + +}; + +#if defined (_WIN32) +# define COLOR_ERROR 12 +# define COLOR_WARNING 13 +# define COLOR_INTERNAL_ERROR 19 +#else +# define COLOR_ERROR 91 +# define COLOR_INTERNAL_ERROR 94 +# define COLOR_WARNING 95 +#endif + +unsigned long get_error_count (void); +void report_at (const char *filename, unsigned long line_number, enum report_type type, const char *fmt, ...); + +#endif /* _REPORT_H */ diff --git a/super.h b/super.h new file mode 100644 index 0000000..d3470c1 --- /dev/null +++ b/super.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * @file super.h + *****************************************************************************/ +#ifndef _SUPER_H +#define _SUPER_H + +#define EXT2_SUPERBLOCK_OFFSET 1024 + +struct superblock { + + unsigned char s_inodes_count[4]; + unsigned char s_blocks_count[4]; + + unsigned char s_r_blocks_count[4]; + + unsigned char s_free_blocks_count[4]; + unsigned char s_free_inodes_count[4]; + + unsigned char s_first_data_block[4]; + + unsigned char s_log_block_size[4]; + unsigned char s_log_frag_size[4]; + + unsigned char s_blocks_per_group[4]; + unsigned char s_frags_per_group[4]; + unsigned char s_inodes_per_group[4]; + + unsigned char s_mtime[4]; + unsigned char s_wtime[4]; + + unsigned char s_mount_count[2]; + unsigned char s_max_mount_count[2]; + + unsigned char s_magic[2]; + unsigned char s_state[2]; + unsigned char s_errors[2]; + + unsigned char s_minor_rev_level[2]; + + unsigned char s_lastcheck[4]; + unsigned char s_checkinterval[4]; + unsigned char s_creator_os[4]; + unsigned char s_rev_level[4]; + + unsigned char s_def_resuid[2]; + unsigned char s_def_resgid[2]; + + unsigned char s_first_ino[4]; + unsigned char s_inode_size[2]; + unsigned char s_block_group_nr[2]; + + unsigned char s_feature_compat[4]; + unsigned char s_feature_incompat[4]; + unsigned char s_feature_ro_compat[4]; + + unsigned char s_uuid[16]; + unsigned char s_volume_label[16]; + unsigned char s_last_mounted[64]; + + unsigned char s_algorithm_usage_bitmap[4]; + unsigned char s_reserved[820]; + +}; + +#define EXT2_SUPER_MAGIC 0xEF53 + +#endif /* _SUPER_H */ -- 2.34.1