Utility code to generate a VMUFAT filesystem
From: Adrian McMenamin
Date: Wed Mar 21 2012 - 00:41:18 EST
/*
* mkfs.vmufat.c - make a linux (VMUFAT) filesystem
*
* Copyright (c) 2012 Adrian McMenamin adrianmcmenamin@xxxxxxxxx
* Licensed under Version 2 of the GNU General Public Licence
*
* Parts shamelessly copied from other mkfs code
* copyright Linus Torvalds and others
*/
#include <stdio.h>
#include <stdlib.h>
#include <asm/byteorder.h>
#include <fcntl.h>
#include <mntent.h>
#include <paths.h>
#include <stdint.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
static const int BLOCKSIZE = 512;
static const int BLOCKSHIFT = 9;
struct vmuparam {
unsigned int size;
unsigned int rootblock;
unsigned int fatstart;
unsigned int fatsize;
unsigned int dirstart;
unsigned int dirsize;
};
struct badblocklist {
int number;
struct badblocklist *next;
};
struct badblocklist* _add_badblock(struct badblocklist *root, int block)
{
struct badblocklist *nextone;
nextone = malloc(sizeof(struct badblocklist));
if (!nextone)
return NULL;
nextone->next = NULL;
nextone->number = block;
if (root)
root->next = nextone;
return nextone;
}
void clean_blocklist(struct badblocklist *nextblock)
{
if (!nextblock)
return;
clean_blocklist(nextblock->next);
free(nextblock);
}
void usage()
{
printf("Create a VMUFAT filesystem.\n");
printf("Usage: mkfs.vmufat [-c|-l filename] [-N number-of-blocks]\n");
printf("\t[-B log2-number-of-blocks] [-v] device [number-of-blocks]\n");
}
int checkmount(char* device_name)
{
FILE * f;
struct mntent * mnt;
if ((f = setmntent(_PATH_MOUNTED, "r")) == NULL)
return;
while ((mnt = getmntent(f)) != NULL)
if (strcmp(device_name, mnt->mnt_fsname) == 0)
break;
endmntent(f);
if (!mnt)
return 0;
printf("%s already mounted - will not format as VMUFAT\n", device_name);
return -1;
}
int readforbad(struct badblocklist** root, char* filename, int verbose)
{
int error = 0;
FILE *listfile;
int badblocks = 0;
unsigned long blockno;
struct badblocklist *lastbadblock = NULL;
listfile = fopen(filename, "r");
if (listfile == NULL) {
printf("Could not open %s\n", filename);
error = -1;
goto out;
}
while (!feof(listfile)) {
if (fscanf(listfile, "%ld\n", &blockno) != 1) {
printf("Cannot parse %s at line %i\n", filename,
badblocks + 1);
error = -1;
goto close;
}
lastbadblock = _add_badblock(lastbadblock, blockno);
if (!lastbadblock) {
printf("Memory failure\n");
error = -1;
goto close;
}
else if (*root == NULL)
*root = lastbadblock;
if (verbose)
printf("Bad block at %ld noted.\n", blockno);
badblocks++;
}
close:
fclose(listfile);
out:
return error;
}
unsigned int _round_down(unsigned int x)
{
unsigned int y = 0x80000000;
while (y > x)
y = y >> 1;
return y;
}
int calculate_vmuparams(int device_numb, struct vmuparam* param, int blocknum,
int verbose)
{
int error = 0;
off_t size;
size = lseek(device_numb, 0, SEEK_END);
if ((size < BLOCKSIZE * 4) ||(blocknum > 0 && blocknum < 4)) {
printf("Device just %i octets in size. Too small for"
" VMUFAT volume\n", size);
error = -1;
goto out;
}
else if (size < blocknum * BLOCKSIZE) {
printf("Device only %i octets in size. Too small for"
" your request of %i blocks\n", size, blocknum);
error = -1;
goto out;
}
else if (blocknum)
size = blocknum * BLOCKSIZE;
param->size = _round_down(size >> BLOCKSHIFT) << BLOCKSHIFT;
param->rootblock = (param->size >> BLOCKSHIFT) - 1;
param->fatstart = param->rootblock - 1;
param->fatsize = (2 * (param->size >> BLOCKSHIFT)) >> BLOCKSHIFT;
param->dirstart = param->fatstart - param->fatsize;
/* Remainder divided in ratio 16:1 between user blocks and directory */
param->dirsize = ((param->size >> BLOCKSHIFT)
- (1 + param->fatsize)) / 17;
if (verbose) {
printf("VMUFAT file system: Root block at %i\n",
param->rootblock);
printf("\tFAT of length %i begins at %i\n", param->fatsize,
param->fatstart);
printf("\tDirectory of length %i begins at %i\n",
param->dirsize, param->dirstart);
}
out:
return error;
}
char _i2bcd(unsigned int i)
{
char bcd;
unsigned int digit;
digit = i/10;
bcd = digit << 4;
digit = i % 10;
bcd += digit;
return bcd;
}
int mark_fat(int device_numb, const struct vmuparam* param, int verbose)
{
char buffer[BLOCKSIZE];
uint16_t *buf;
int i, j, k, start;
int error = -1;
buf = (uint16_t *)buffer;
for (i = 0; i < BLOCKSIZE / 2; i++)
buf[i] = __cpu_to_le16(0xFFFC);
if (param->fatsize > 1) {
for (i = param->fatstart - 1;
i > param->fatstart - param->fatsize; i--) {
if (lseek(device_numb, i * BLOCKSIZE, SEEK_SET) < 0)
goto badseek;
if (write(device_numb, buffer, BLOCKSIZE) < BLOCKSIZE)
goto badwrite;
}
}
/* Mark for the FAT and Directory */
start = 2 * (param->fatsize + param->dirsize) / BLOCKSIZE + 1;
for (j = param->rootblock - start; j < param->rootblock; j++) {
k = (j - param->dirstart - 1) * BLOCKSIZE;
for (i = 0; i < BLOCKSIZE; i = i + 2) {
/* FAT */
if ((k + i) / 2 > 1 + param->fatstart - param->fatsize)
buf[i / 2] = __cpu_to_le16((k + i) / 2 - 1);
else if ((k + i) / 2 == 1 + param->fatstart
- param->fatsize)
buf[i / 2] = __cpu_to_le16(0xFFFA);
/* Directory */
else if ((k + i) / 2 > 1 + param->dirstart
- param->dirsize)
buf[i / 2] = __cpu_to_le16((k + i) / 2 - 1);
else if ((k + i) / 2 == 1 + param->dirstart
- param->dirsize)
buf[i / 2] = __cpu_to_le16(0xFFFA);
}
if (start > 1) {
if (lseek(device_numb, j * BLOCKSIZE, SEEK_SET) < 0)
goto badseek;
if (write(device_numb, buffer, BLOCKSIZE) < BLOCKSIZE)
goto badwrite;
}
}
buf[BLOCKSIZE / 2 - 1] = __cpu_to_le16(0xFFFA); /*Root block*/
if (lseek(device_numb, (j - 1) * BLOCKSIZE, SEEK_SET) < 0)
goto badseek;
if (write(device_numb, buffer, BLOCKSIZE) < BLOCKSIZE)
goto badwrite;
if (verbose)
printf("FAT written\n");
return 0;
badseek:
printf("Seek failed while writing FAT\n");
return error;
badwrite:
printf("FAT write failed\n");
return error;
}
void _fill_root_block(char* buf, const struct vmuparam* param)
{
int i;
time_t rawtime;
struct tm *ptm;
char century, year, month, day, hour, minute, second, weekday;
uint16_t* wordbuf;
wordbuf = (uint16_t *)buf;
for (i = 0; i < 0x10; i++)
buf[i] = 0x55;
time(&rawtime);
ptm = gmtime(&rawtime);
if (!ptm)
return;
century = _i2bcd(19 + ptm->tm_year / 100);
year = _i2bcd(ptm->tm_year - (ptm->tm_year /100) * 100);
month = _i2bcd(ptm->tm_mon + 1);
day = _i2bcd(ptm->tm_mday);
hour = _i2bcd(ptm->tm_hour);
minute = _i2bcd(ptm->tm_min);
second = _i2bcd(ptm->tm_sec);
weekday = _i2bcd(ptm->tm_wday);
buf[0x30] = century;
buf[0x31] = year;
buf[0x32] = month;
buf[0x33] = day;
buf[0x34] = hour;
buf[0x35] = minute;
buf[0x36] = second;
buf[0x37] = weekday;
wordbuf[0x20] = __cpu_to_le16(param->rootblock);
wordbuf[0x22] = __cpu_to_le16(param->rootblock);
wordbuf[0x23] = __cpu_to_le16(param->fatstart);
wordbuf[0x24] = __cpu_to_le16(param->fatsize);
wordbuf[0x25] = __cpu_to_le16(param->dirstart);
wordbuf[0x26] = __cpu_to_le16(param->dirsize);
/* 32 octets per directory entry */
wordbuf[0x27] = __cpu_to_le16(param->dirsize * 8);
}
int mark_root_block(int device_numb, const struct vmuparam *param, int verbose)
{
char zilches[BLOCKSIZE];
int i, error = 0;
for (i = 0; i < BLOCKSIZE; i++)
zilches[i] = '\0';
_fill_root_block(zilches, param);
if (lseek(device_numb, param->rootblock * BLOCKSIZE, SEEK_SET) < 0) {
printf("Failed to seek root block\n");
error = -1;
goto out;
}
if (write(device_numb, zilches, BLOCKSIZE) < BLOCKSIZE) {
printf("Could not write root block\n");
error = -1;
goto out;
}
if (verbose) {
printf("Root block written to block %i\n", param->rootblock);
printf("BCD string: %c %c %c %c %c %c %c %c\n",
zilches[0x30], zilches[0x31], zilches[0x32],
zilches[0x33], zilches[0x34], zilches[0x35],
zilches[0x36], zilches[0x37]);
}
out:
return error;
}
int zero_blocks(int device_numb, const struct vmuparam *param, int verbose)
{
char zilches[BLOCKSIZE];
int i, error = -1;
for (i = 0; i < BLOCKSIZE; i++)
zilches[i] = '\0';
for (i = 0; i <= param->dirstart; i++) {
if (lseek(device_numb, i * BLOCKSIZE, SEEK_SET) < 0) {
printf("Seek failed on device\n");
goto out;
}
if (write(device_numb, zilches, BLOCKSIZE) < BLOCKSIZE) {
printf("Write failed on device\n");
goto out;
}
}
error = 0;
if (verbose)
printf("Other blocks zeroed\n");
out:
return error;
}
int scanforbad(int device_numb, struct badblocklist** root, int verbose)
{
int error = 0, i;
struct badblocklist *lastbadblock = NULL;
off_t size;
long got;
char buffer[BLOCKSIZE];
size = lseek(device_numb, 0, SEEK_END);
for (i = 0; i < size/BLOCKSIZE; i++)
{
if (verbose > 0)
printf("Testing block %i\n", i);
if (lseek(device_numb, i * BLOCKSIZE, SEEK_SET) !=
i * BLOCKSIZE) {
printf("Seek failed on device\n");
error = -1;
goto out;
}
got = read(device_numb, buffer, BLOCKSIZE);
if (got != BLOCKSIZE) {
printf("Block %i gives bad read\n", i);
lastbadblock = _add_badblock(lastbadblock, i);
if (!lastbadblock) {
printf("Memory failure\n");
error = -1;
goto out;
}
else if (*root == NULL)
*root = lastbadblock;
}
}
out:
return error;
}
int _mark_block_bad(int device_numb, int badblock,
const struct vmuparam *param)
{
int error = -1;
int fatblock;
char buffer[BLOCKSIZE];
uint16_t *buf;
fatblock = (2 * badblock) / BLOCKSIZE + param->dirstart + 1;
if (lseek(device_numb, fatblock * BLOCKSIZE, SEEK_SET) < 0)
goto out;
if (read(device_numb, buffer, BLOCKSIZE) != BLOCKSIZE)
goto out;
buf = (uint16_t *)buffer;
buf[badblock % (BLOCKSIZE / 2)] = __cpu_to_le16(0xFFFA);
if (lseek(device_numb, fatblock * BLOCKSIZE, SEEK_SET) < 0)
goto out;
if (write(device_numb, buffer, BLOCKSIZE) != BLOCKSIZE)
goto out;
error = 0;
out:
return error;
}
int mark_bad_blocks(int device_numb, struct badblocklist *root,
const struct vmuparam *param, int verbose)
{
int error = 0;
int nobadblock;
struct badblocklist *next;
if (!root)
goto out;
next = root;
while (next) {
nobadblock = next->number;
if (nobadblock < 0 || nobadblock > param->rootblock)
goto advance;
if (nobadblock >= param->dirstart
&& nobadblock <= param->rootblock) {
printf("Format fails as system block is bad\n");
error = -1;
goto out;
}
if (_mark_block_bad(device_numb, nobadblock, param)
< 0) {
printf("Format fails as cannot mark FAT"
" for bad block.\n");
error = -1;
goto out;
}
advance:
next = next->next;
}
if (verbose)
printf("Bad blocks now marked off in FAT.\n");
out:
return error;
}
int main(int argc, char* argv[])
{
int blocknum = 0;
int i;
int verbose = 0, scanbadblocks = 0, useblocklist = 0;
int error = 1, device_numb;
char *blocklistfnm = NULL;
char *device_name = NULL;
struct stat statbuf;
struct badblocklist* lstbadblocks = NULL;
struct vmuparam params;
if (argc < 2) {
usage();
goto out;
}
opterr = 0;
while ((i = getopt(argc, argv, "cl:N:B:v")) != -1)
switch (i) {
case 'c':
scanbadblocks = 1;
break;
case 'l':
useblocklist = 1;
blocklistfnm = optarg;
break;
case 'N':
blocknum = atoi(optarg);
break;
case 'B':
blocknum = 1 << atoi(optarg);
break;
case 'v':
verbose = 1;
break;
default:
usage();
goto out;
}
argc -= optind;
argv += optind;
if (!argc > 0) {
usage();
goto out;
}
device_name = argv[0];
argc--;
argv++;
if (argc) {
blocknum = atoi(argv[0]);
if (argc > 1)
usage();
}
if (checkmount(device_name) < 0)
goto out;
if (stat(device_name, &statbuf) < 0) {
printf("Cannot get status of %s\n", device_name);
goto out;
}
if (!S_ISBLK(statbuf.st_mode)) {
printf("%s must be a block device\n", device_name);
goto out;
}
device_numb = open(device_name, O_RDWR|O_EXCL);
if (device_numb < 0) {
printf("Attempting to open %s fails with error %i\n",
device_name, device_numb);
goto out;
}
if (scanbadblocks > 0) {
if (scanforbad(device_numb, &lstbadblocks, verbose) < 0)
goto close;
}
else if (useblocklist > 0) {
if (readforbad(&lstbadblocks, blocklistfnm, verbose) < 0)
goto close;
}
if (calculate_vmuparams(device_numb, ¶ms, blocknum, verbose) < 0)
goto close;
if (mark_root_block(device_numb, ¶ms, verbose) < 0)
goto close;
if (mark_fat(device_numb, ¶ms, verbose) < 0)
goto close;
if (zero_blocks(device_numb, ¶ms, verbose) < 0)
goto close;
if (mark_bad_blocks(device_numb, lstbadblocks, ¶ms, verbose) < 0)
goto close;
if (verbose)
printf("VMUFAT volume created on %s\n", device_name);
error = 0;
close:
close(device_numb);
if (lstbadblocks)
clean_blocklist(lstbadblocks);
out:
return error;
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/