//
// $Header: D:/ext2-os2/RCS/volume.c,v 1.12 1995/08/16 17:38:04 Willm Exp Willm $
//
// Linux ext2 file system driver for OS/2 2.x and WARP - Allows OS/2 to
// access your Linux ext2fs partitions as normal drive letters.
// OS/2 implementation : Copyright (C) 1995 Matthieu WILLM
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Will be eventually moved to ext2/super.c, and openvolume will be split into
// ext2_read_super() and its calling VFS routine
//
extern unsigned long inode_init(unsigned long start, unsigned long end);
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h> // From the "Developer Connection Device Driver Kit" version 2.0
#include <fsd.h>
#include <fsh.h>
#include <os2/errors.h>
#include <os2/types.h>
#include <os2/os2proto.h>
#include <os2/os2misc.h>
#include <linux/fs.h>
#include <linux/e2_fs.h>
#include <linux/fs_proto.h>
#include <linux/e2_proto.h>
#include <linux/sched.h>
#include <os2/log.h> /* Prototypes des fonctions de log.c */
#include <os2/volume.h> /* Prototypes des fonctions de volume.c */
extern int auto_fsck;
static struct super_operations ext2_sops = {
ext2_read_inode,
NULL,
ext2_write_inode,
ext2_put_inode,
NULL, // ext2_put_super,
NULL, // ext2_write_super,
NULL, // ext2_statfs,
NULL // ext2_remount
};
//
// Initializations that can't be done in FS_INIT() because they need some ring 0
// interfaces (FSHelpers ...)
//
static int initialized = 0;
int init_ext2_os2(void) {
if (!initialized) {
initialized = 1;
name_cache_init(0, 0);
inode_init(0, 0);
}
return NO_ERROR;
}
static void ext2_setup_super (struct super_block * sb,
struct ext2_super_block * es)
{
if (es->s_rev_level > EXT2_CURRENT_REV) {
printk ("EXT2-fs warning: revision level too high, "
"forcing read/only mode\n");
sb->s_flags |= MS_RDONLY;
}
if (!(sb->s_flags & MS_RDONLY)) {
if (!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
printk ("EXT2-fs warning: mounting unchecked fs, "
"running e2fsck is recommended\n");
else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
printk ("EXT2-fs warning: mounting fs with errors, "
"running e2fsck is recommended\n");
else if (es->s_max_mnt_count >= 0 &&
es->s_mnt_count >= (unsigned short) es->s_max_mnt_count)
printk ("EXT2-fs warning: maximal mount count reached, "
"running e2fsck is recommended\n");
else if (es->s_checkinterval &&
(es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME))
printk ("EXT2-fs warning: checktime reached, "
"running e2fsck is recommended\n");
es->s_state &= ~EXT2_VALID_FS;
if (!es->s_max_mnt_count)
es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
#ifndef OS2
es->s_mnt_count++;
#else
//
// This is to force Linux to autocheck the ext2fs partition "touched" by OS/2
// this autocheck is enabled unless -no_auto_fsck is specified on the IFS cmd
// line
//
if (auto_fsck) {
kernel_printf("e2fsck will be forced next time Linux will mount this partition");
es->s_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
} else {
es->s_mnt_count++;
}
#endif
es->s_mtime = CURRENT_TIME;
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
#ifndef OS2 // For the moment .....
if (test_opt (sb, DEBUG))
#endif
printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
"bpg=%lu, ipg=%lu, mo=%04lx]\n",
EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,
sb->u.ext2_sb.s_frag_size,
sb->u.ext2_sb.s_groups_count,
EXT2_BLOCKS_PER_GROUP(sb),
EXT2_INODES_PER_GROUP(sb),
sb->u.ext2_sb.s_mount_opt);
#ifndef OS2 // For the moment .....
if (test_opt (sb, CHECK)) {
#endif
ext2_check_blocks_bitmap (sb);
ext2_check_inodes_bitmap (sb);
#ifndef OS2 // For the moment .....
}
#endif
}
}
static int ext2_check_descriptors (struct super_block * sb)
{
#ifndef OS2
int i;
#else
blk_t i;
#endif
int desc_block = 0;
unsigned long block = sb->u.ext2_sb.s_es->s_first_data_block;
struct ext2_group_desc * gdp = NULL;
ext2_debug ("Checking group descriptors");
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++)
{
if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data;
if (gdp->bg_block_bitmap < block ||
gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Block bitmap for group %d"
" not in group (block %lu)!",
i, (unsigned long) gdp->bg_block_bitmap);
return 0;
}
if (gdp->bg_inode_bitmap < block ||
gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Inode bitmap for group %d"
" not in group (block %lu)!",
i, (unsigned long) gdp->bg_inode_bitmap);
return 0;
}
if (gdp->bg_inode_table < block ||
gdp->bg_inode_table + sb->u.ext2_sb.s_itb_per_group >=
block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Inode table for group %d"
" not in group (block %lu)!",
i, (unsigned long) gdp->bg_inode_table);
return 0;
}
block += EXT2_BLOCKS_PER_GROUP(sb);
gdp++;
}
return 1;
}
struct super_block * openvolume(struct vpfsi _FS_PTR pvpfsi, unsigned short hVPB)
{
struct super_block * p_volume;
int rc;
UINT32 time;
pchar Buf, Buf2 = 0;
UINT16 nb_sec;
pext2_super_block psb, psb2;
UINT32 i;
blk_t db_count;
blk_t logic_sb_block = 1, sb_block = 1;
unsigned long offset;
/************************************************************************/
/*** Récupère le temps écoulé en secondes depuis le 1/1/1970 ***/
/*** Valeur mise dans le champ timestamp de chaque bloc du cache pour ***/
/*** l'algorithme de vieillissement ***/
/************************************************************************/
if ((rc = gettime(&time)) != 0) {
return 0;
} /* end if */
/************************************************************************/
/************************************************************************/
/*** Allocation d'un handle de volume ***/
/************************************************************************/
if ((p_volume = (struct super_block *)G_malloc(sizeof(struct super_block))) == 0) {
return 0;
} /* end if */
memset(p_volume, 0, sizeof(struct super_block));
/************************************************************************/
p_volume->sector_size = (UINT32)pvpfsi->vpi_bsize;
p_volume->nb_sectors = (UINT32)pvpfsi->vpi_totsec;
p_volume->drive = (UINT32)pvpfsi->vpi_drive;
p_volume->block_size = BLOCK_SIZE;
p_volume->s_blocksize = p_volume->block_size;
p_volume->sectors_per_block = p_volume->block_size / p_volume->sector_size;
p_volume->s_dev = hVPB;
p_volume->s_op = &ext2_sops;
if (!Read_Write)
p_volume->s_flags = MS_RDONLY;
FSH_SEMSET(&(p_volume->s_semerror));
/************************************************************************/
/*** Lecture et contrôle de superblock ***/
/************************************************************************/
if ((Buf = G_malloc(p_volume->block_size)) == 0) {
G_free((pchar)p_volume);
return 0;
} /* end if */
nb_sec = (UINT16)p_volume->sectors_per_block;
if ((rc = FSH_DOVOLIO(
DVIO_OPREAD,
DVIO_ALLFAIL | DVIO_ALLABORT | DVIO_ALLRETRY,
hVPB,
Buf,
&nb_sec,
1 * p_volume->sectors_per_block
)) != NO_ERROR) {
fs_log("Erreur FSH_DOVOLIO() dans validate_boot_sector()");
G_free(Buf);
G_free((pchar)p_volume);
return 0;
} /* end if */
psb = (pext2_super_block)Buf;
if (psb->s_magic != EXT2_SUPER_MAGIC) {
fs_log("ext2 signature not found in superblock");
if ((rc = G_free(Buf)) != NO_ERROR) {
fs_log("erreur G_free"); /*** FSH_INTERR() ***/
} /* end if */
if ((rc = G_free((pchar)p_volume)) != NO_ERROR) {
fs_log("Erreur G_free");
}
return 0;
} else {
fs_log("ext2 signature found in superblock");
}
p_volume->block_size = EXT2_MIN_BLOCK_SIZE << psb->s_log_block_size;
p_volume->sectors_per_block = p_volume->block_size / p_volume->sector_size;
p_volume->s_blocksize = p_volume->block_size;
if (p_volume->s_blocksize != BLOCK_SIZE &&
(p_volume->s_blocksize == 1024 || p_volume->s_blocksize == 2048 ||
p_volume->s_blocksize == 4096)) {
// unsigned long offset;
if ((Buf2 = G_malloc(p_volume->block_size)) == 0) {
G_free(Buf);
G_free((pchar)p_volume);
return 0;
} /* end if */
// brelse (bh);
// set_blocksize (dev, sb->s_blocksize);
logic_sb_block = (sb_block*BLOCK_SIZE) / p_volume->s_blocksize;
offset = (sb_block*BLOCK_SIZE) % p_volume->s_blocksize;
// bh = bread (dev, logic_sb_block, psb->s_blocksize);
// if(!bh)
// return NULL;
nb_sec = (UINT16)p_volume->sectors_per_block;
if ((rc = FSH_DOVOLIO(
DVIO_OPREAD,
DVIO_ALLFAIL | DVIO_ALLABORT | DVIO_ALLRETRY,
hVPB,
Buf2,
&nb_sec,
logic_sb_block * p_volume->sectors_per_block
)) != NO_ERROR) {
fs_log("Erreur FSH_DOVOLIO() dans validate_boot_sector()");
if ((rc = G_free(Buf)) != NO_ERROR) {
fs_log("erreur G_free"); /*** FSH_INTERR() ***/
} /* end if */
if ((rc = G_free(Buf2)) != NO_ERROR) {
fs_log("erreur G_free"); /*** FSH_INTERR() ***/
} /* end if */
G_free((pchar)p_volume);
return 0;
} /* end if */
psb2 = (pext2_super_block) (Buf2 + offset);
// sb->u.ext2_sb.s_es = es;
if (psb2->s_magic != EXT2_SUPER_MAGIC) {
// sb->s_dev = 0;
// unlock_super (sb);
// brelse (bh);
fs_log ("EXT2-fs: Magic mismatch, very weird !\n");
return 0;
}
}
/************************************************************************/
/************************************************************************/
/*** Allocation des buffers de fichier ***/
/************************************************************************/
if ((rc = init_bufs(p_volume)) != NO_ERROR) {
fs_log("openvolume() Erreur init_bufs()");
return 0;
}
/************************************************************************/
/************************************************************************/
/*** Allocation des file descriptors ***/
/************************************************************************/
if ((rc = init_hfiles(p_volume)) != NO_ERROR) {
fs_log("openvolume() Erreur init_hfiles()");
return 0;
}
/************************************************************************/
init_ext2_os2();
/************************************************************************/
/*** We copy the superblock into a buffer_head structure ***/
/************************************************************************/
if ((p_volume->u.ext2_sb.s_sbh = alloc_filebuf(p_volume)) == 0) {
fs_log("openvolume() Erreur alloc_filebuf()");
return 0;
}
if (Buf2 != 0) {
kernel_printf("Buf2 != 0");
memcpy(p_volume->u.ext2_sb.s_sbh->b_data, Buf2 + offset, sizeof(struct ext2_super_block));
} else {
memcpy(p_volume->u.ext2_sb.s_sbh->b_data, Buf, BLOCK_SIZE);
}
p_volume->u.ext2_sb.s_sbh->b_blocknr = logic_sb_block;
p_volume->u.ext2_sb.s_sbh->b_count = 1;
p_volume->u.ext2_sb.s_sbh->b_dirt = 0;
p_volume->u.ext2_sb.s_sbh->b_size = p_volume->block_size;
p_volume->u.ext2_sb.s_sbh->b_uptodate = 1;
p_volume->u.ext2_sb.s_sbh->b_dev = p_volume->s_dev; // System halt si absent
p_volume->u.ext2_sb.s_sbh->dev = p_volume; // System halt si absent
p_volume->u.ext2_sb.s_es = (pext2_super_block)(p_volume->u.ext2_sb.s_sbh->b_data);
/************************************************************************/
kernel_printf("\tSuperblock block no = %lu - blocksize = %lu", logic_sb_block, p_volume->s_blocksize);
p_volume->u.ext2_sb.s_blocks_per_group = p_volume->u.ext2_sb.s_es->s_blocks_per_group;
p_volume->u.ext2_sb.s_desc_per_block = (EXT2_BLOCK_SIZE(p_volume) / sizeof (struct ext2_group_desc));
p_volume->u.ext2_sb.s_inodes_per_group = p_volume->u.ext2_sb.s_es->s_inodes_per_group;
p_volume->u.ext2_sb.s_groups_count = (p_volume->u.ext2_sb.s_es->s_blocks_count -
p_volume->u.ext2_sb.s_es->s_first_data_block +
EXT2_BLOCKS_PER_GROUP(p_volume) - 1) /
EXT2_BLOCKS_PER_GROUP(p_volume);
p_volume->u.ext2_sb.s_frags_per_group = p_volume->u.ext2_sb.s_es->s_frags_per_group;
p_volume->u.ext2_sb.s_inodes_per_block = p_volume->s_blocksize /
sizeof (struct ext2_inode);
p_volume->u.ext2_sb.s_itb_per_group = p_volume->u.ext2_sb.s_inodes_per_group /
p_volume->u.ext2_sb.s_inodes_per_block;
p_volume->u.ext2_sb.s_mount_state = p_volume->u.ext2_sb.s_es->s_state;
db_count = (p_volume->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(p_volume) - 1) /
EXT2_DESC_PER_BLOCK(p_volume);
for (i = 0; i < db_count; i++) {
p_volume->u.ext2_sb.s_group_desc[i] = bread (p_volume, logic_sb_block + i + 1,
p_volume->block_size);
}
if (!ext2_check_descriptors (p_volume)) {
blk_t j;
p_volume->s_dev = 0;
for (j = 0; j < db_count; j++)
brelse (p_volume->u.ext2_sb.s_group_desc[j]);
brelse (p_volume->u.ext2_sb.s_sbh);
G_free(Buf);
if (Buf2) G_free(Buf2);
G_free((pchar)p_volume);
kernel_printf ("EXT2-fs: group descriptors corrupted !");
return 0;
}
for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
p_volume->u.ext2_sb.s_inode_bitmap_number[i] = 0;
p_volume->u.ext2_sb.s_inode_bitmap[i] = NULL;
p_volume->u.ext2_sb.s_block_bitmap_number[i] = 0;
p_volume->u.ext2_sb.s_block_bitmap[i] = NULL;
}
p_volume->u.ext2_sb.s_loaded_inode_bitmaps = 0;
p_volume->u.ext2_sb.s_loaded_block_bitmaps = 0;
p_volume->u.ext2_sb.s_db_per_group = db_count;
p_volume->s_dev = hVPB;
ext2_setup_super (p_volume, p_volume->u.ext2_sb.s_es);
if ((rc = G_free(Buf)) != NO_ERROR) {
fs_log("erreur G_free"); /*** FSH_INTERR() ***/
} /* end if */
if (Buf2 != 0) {
if ((rc = G_free(Buf2)) != NO_ERROR) {
fs_log("erreur G_free"); /*** FSH_INTERR() ***/
} /* end if */
}
return p_volume;
}
int free_volume (struct super_block **psb) {
struct super_block *sb = *psb;
UINT32 i;
int rc;
for (i = 0 ; i < NB_MAX_SEL ; i++) {
if (sb->GDTsels[i]) {
if ((rc = G_free((char *)(sb->GDTsels[i]))) != NO_ERROR) {
kernel_printf("free_volume - Error G_free");
}
}
}
if ((rc = G_free((char *)sb)) != NO_ERROR) {
kernel_printf("free_volume - Error G_free(sb)");
}
kernel_printf("free_volume() OK");
return NO_ERROR;
}
struct super_block * getvolume(unsigned short hVPB)
{
struct vpfsi _FS_PTR pvpfsi;
struct vpfsd _FS_PTR pvpfsd;
if (!hVPB) {
FSH_INTERR("getvolume() hVPB = 0", sizeof("getvolume() hVPB = 0"));
}
FSH_GETVOLPARM(hVPB, &pvpfsi, &pvpfsd);
return ((hvolume _FS_PTR)pvpfsd)->p_volume;
}
int sync_volume(struct super_block *sb) {
struct buffer_head *bh;
long nb_written_total = 0;
long nb_written = -1;
#if 0
struct inode *inode;
inode = sb->LRU_freevinodes;
while (inode) {
if ((inode->i_dirt) && (inode->i_ino != ~0)) {
kernel_printf("\tsync_volume() - Writing free dirty inode %lu", inode->i_ino);
ext2_write_inode(inode);
}
inode = inode->pnext;
}
inode = sb->usedvinodes;
while (inode) {
if ((inode->i_dirt) && (inode->i_ino != ~0)) {
kernel_printf("\tsync_volume() - Writing used dirty inode %lu", inode->i_ino);
ext2_write_inode(inode);
}
inode = inode->pnext;
}
#endif
//
// We do this loop because while waiting for a write operation to complete, the buffer
// chain could have been modified (for instance by ext2_lw.exe)
//
while (nb_written) {
nb_written = 0;
if (sb->nusedbufs) {
bh = sb->usedbuffers;
while (bh) {
if ((bh->b_dirt) && (bh->b_blocknr != ~0)) {
// kernel_printf("\tsync_volume() - Writing used dirty disk block %lu", bh->b_blocknr);
ll_rw_block(WRITE, 1, &bh);
nb_written ++;
}
bh = bh->pnext;
}
}
if (sb->nfreebufs) {
bh = sb->LRU_freebuffers;
while (bh) {
if ((bh->b_dirt) && (bh->b_blocknr != ~0)) {
// kernel_printf("\tsync_volume() - Writing free dirty disk block %lu", bh->b_blocknr);
ll_rw_block(WRITE, 1, &bh);
nb_written ++;
}
bh = bh->pnext;
}
}
nb_written_total += nb_written;
kernel_printf("sync_volume() - wrote %lu dirty buffers", nb_written);
}
kernel_printf("sync_volume() - total dirty buffers written is %lu", nb_written_total);
return NO_ERROR;
}
void __wait_on_super(struct super_block * sb)
{
cli();
while (sb->s_lock) {
FS_DevHelp_ProcBlock((unsigned long)(&(sb->s_wait)));
cli();
}
sti();
}
int lazy_write(struct super_block *sb) {
struct buffer_head *bh, *tmp;
long nb_written = 0;
long nb_max = (sb->nbufs / 10 > 10 ? sb->nbufs / 10 : 10);
bh = sb->LRU_freebuffers;
while ((bh) && (nb_written < nb_max)) {
if ((bh->b_dirt) && (bh->b_blocknr != ~0)) {
tmp = getblk(sb->s_dev, bh->b_blocknr, sb->s_blocksize);
ll_rw_block(WRITE, 1, &tmp);
FSH_YIELD();
brelse(tmp);
bh = sb->LRU_freebuffers;
nb_written ++;
} else {
bh = bh->pnext;
}
}
kernel_printf("lazy_write() - Wrote %lu buffers", nb_written);
return NO_ERROR;
}
int Check_Ext2fs_magic(struct vpfsi *pvpfsi, unsigned short hVPB) {
int nb_sec;
int rc;
char *Buf;
struct ext2_super_block *es;
int found;
//
// Allocates a temporary buffer
//
if ((Buf = G_malloc(BLOCK_SIZE)) == 0) {
kernel_printf("Check_Ext2fs_magic : G_malloc returned NULL");
return 0;
} /* end if */
//
// Reads disk block 1 (with blocksize = 1024)
//
nb_sec = BLOCK_SIZE / pvpfsi->vpi_bsize;
if ((rc = FSH_DOVOLIO(
DVIO_OPREAD,
DVIO_ALLFAIL | DVIO_ALLABORT | DVIO_ALLRETRY,
hVPB,
Buf,
&nb_sec,
nb_sec // Logical block 1
)) != NO_ERROR) {
kernel_printf("Check_Ext2fs_magic() - FSH_DOVOLIO returned %d", rc);
G_free(Buf);
return 0;
} /* end if */
es = (struct ext2_super_block *)Buf;
if (es->s_magic == EXT2_SUPER_MAGIC) {
kernel_printf("ext2 signature found in superblock (hVPB = 0x%04X)", hVPB);
found = 1;
} else {
kernel_printf("ext2 signature NOT found in superblock (hVPB = 0x%04X)", hVPB);
found = 0;
}
if ((rc = G_free(Buf)) != NO_ERROR) {
kernel_printf("Check_Ext2fs_magic : G_free returned %d", rc);
}
return found;
}