--- /dev/null
+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 <https://unlicense.org>
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+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.
--- /dev/null
+/******************************************************************************
+ * @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++;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file common.c
+ *****************************************************************************/
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @file common.h
+ *****************************************************************************/
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include <stdio.h>
+
+#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 */
--- /dev/null
+/******************************************************************************
+ * @file e2cp.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file e2md.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @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;
+}
--- /dev/null
+/******************************************************************************
+ * @file list.h
+ *****************************************************************************/
+#ifndef _LIST_H
+#define _LIST_H
+
+#include <stddef.h>
+
+#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 */
--- /dev/null
+/******************************************************************************
+ * @file mke2fs.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file report.c
+ *****************************************************************************/
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "report.h"
+
+unsigned long errors = 0;
+
+#ifndef __PDOS__
+#if defined (_WIN32)
+# include <windows.h>
+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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @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 */