From: Robert Pengelly Date: Sat, 30 Aug 2025 21:46:47 +0000 (+0100) Subject: Initial commit X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=a70f24484e37afd82ae7622acf82f270d5f050e8;p=e2fsprogs.git Initial commit --- a70f24484e37afd82ae7622acf82f270d5f050e8 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 */