* Utility code to generate a VMUFAT filesystem
@ 2012-03-21 4:41 Adrian McMenamin
2012-03-25 6:32 ` Mike Frysinger
0 siblings, 1 reply; 3+ messages in thread
From: Adrian McMenamin @ 2012-03-21 4:41 UTC (permalink / raw)
To: viro, linux-fsdevel, LKML; +Cc: Linux-sh, Adrian McMenamin
/*
* mkfs.vmufat.c - make a linux (VMUFAT) filesystem
*
* Copyright (c) 2012 Adrian McMenamin adrianmcmenamin@gmail.com
* 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;
}
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Utility code to generate a VMUFAT filesystem
2012-03-21 4:41 Utility code to generate a VMUFAT filesystem Adrian McMenamin
@ 2012-03-25 6:32 ` Mike Frysinger
2012-04-06 13:40 ` Adrian McMenamin
0 siblings, 1 reply; 3+ messages in thread
From: Mike Frysinger @ 2012-03-25 6:32 UTC (permalink / raw)
To: Adrian McMenamin; +Cc: viro, linux-fsdevel, LKML, Linux-sh, Adrian McMenamin
[-- Attachment #1: Type: Text/Plain, Size: 4189 bytes --]
On Wednesday 21 March 2012 00:41:17 Adrian McMenamin wrote:
> /*
> * mkfs.vmufat.c - make a linux (VMUFAT) filesystem
> *
> * Copyright (c) 2012 Adrian McMenamin adrianmcmenamin@gmail.com
> * Licensed under Version 2 of the GNU General Public Licence
> *
> * Parts shamelessly copied from other mkfs code
> * copyright Linus Torvalds and others
> */
might be useful to sneak into tools/ or Documentation/ or fs/vmufat/
> void usage()
(void)
seems like most (if not just about all) funcs in here should be static
> int checkmount(char* device_name)
> {
> FILE * f;
> struct mntent * mnt;
kernel style wise, this really should be:
char *foo;
FILE *f;
struct mntent *mnt;
etc...
device_name here should be const
> if ((f = setmntent(_PATH_MOUNTED, "r")) == NULL)
> return;
there is libmount now, but that might be overkill for this small tool
> int readforbad(struct badblocklist** root, char* filename, int verbose)
filename should be const
> {
> int error = 0;
> FILE *listfile;
> int badblocks = 0;
> unsigned long blockno;
> ...
>
> while (!feof(listfile)) {
> if (fscanf(listfile, "%ld\n", &blockno) != 1) {
%ld is "signed long" but blockno is "unsigned long". probably want %lu here
and elsewhere in this func.
> void _fill_root_block(char* buf, const struct vmuparam* param)
incoming buf is "char *" ...
> uint16_t* wordbuf;
>
> wordbuf = (uint16_t *)buf;
... which you cast up to "uint16_t *" ...
> 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);
... and is declared on the stack as "char *". there are no alignment
requirements here, and iirc, superh doesn't support unaligned loads. so you
should add gcc aligned attributes, or declare the buffer better:
uint16_t zilches[BLOCKSIZE / 2];
> 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';
memset(zilches, '\0', BLOCKSIZE);
> 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);
if you use fstat() here ...
> 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);
... and pread() here, there's no need for the lseek()'s at all.
> if (lseek(device_numb, fatblock * BLOCKSIZE, SEEK_SET) < 0)
> goto out;
> if (read(device_numb, buffer, BLOCKSIZE) != BLOCKSIZE)
> goto out;
use pread() instead of lseek() && read()
> if (lseek(device_numb, fatblock * BLOCKSIZE, SEEK_SET) < 0)
> goto out;
> if (write(device_numb, buffer, BLOCKSIZE) != BLOCKSIZE)
> goto out;
and pwrite() instead of lseek() && write()
> 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);
technically you've got a race condition here. really should do the open() and
then do fstat() on the fd you get back.
also, what's with the O_EXCL ? that only makes sense with O_CREAT.
should you open+fstat before checking for the mount point ? guess it doesn't
really matter.
it's useful to be able to format regular files as filesystems especially for
debugging. most mkfs tools issue a warning or prompt by default, and using
the -f (force) flag allows you to format regular files.
-mike
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Utility code to generate a VMUFAT filesystem
2012-03-25 6:32 ` Mike Frysinger
@ 2012-04-06 13:40 ` Adrian McMenamin
0 siblings, 0 replies; 3+ messages in thread
From: Adrian McMenamin @ 2012-04-06 13:40 UTC (permalink / raw)
To: Mike Frysinger; +Cc: viro, linux-fsdevel, LKML, Linux-sh, Adrian McMenamin
On 25 March 2012 14:32, Mike Frysinger <vapier@gentoo.org> wrote:
> On Wednesday 21 March 2012 00:41:17 Adrian McMenamin wrote:
>> /*
>> * mkfs.vmufat.c - make a linux (VMUFAT) filesystem
>> *
>> * Copyright (c) 2012 Adrian McMenamin adrianmcmenamin@gmail.com
>> * Licensed under Version 2 of the GNU General Public Licence
>> *
>> * Parts shamelessly copied from other mkfs code
>> * copyright Linus Torvalds and others
With thanks to Mike - a new version of mkfs.vmufat.c is now available
at https://github.com/mcmenaminadrian/mkfs.vmufat
I have tried to incorporate all his points - even the bits where he
was complaining about the code I lifted from Linus's work :)
Any and all feedback welcome.
Adrian
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2012-04-06 13:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-21 4:41 Utility code to generate a VMUFAT filesystem Adrian McMenamin
2012-03-25 6:32 ` Mike Frysinger
2012-04-06 13:40 ` Adrian McMenamin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).