/***************************************************************************** * super.c * Superblock operations and basic interface to the Linux VFS. * * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x * Copyright (c) 2001 Vince Weaver * Copyright (c) 2001 Matt Jensen. * This program is free software distributed under the terms of the GPL. *****************************************************************************/ /*= Kernel Includes =========================================================*/ #include #include #include #include #include /*= DOS 3.3 Includes =======================================================*/ #include "dos33.h" /*= Forward Declarations ====================================================*/ /* For the Linux module interface. */ int __init init_dos33_fs(void); void __exit exit_dos33_fs(void); /* For dos33_fs_type. */ struct super_block *dos33_read_super(struct super_block *,void *,int); /* For dos33_super_operations. */ void dos33_put_super(struct super_block *); void dos33_write_super(struct super_block *); int dos33_statfs(struct super_block *,struct statfs *); /*= VFS Interface Structures ================================================*/ /* Linux module operations. */ EXPORT_NO_SYMBOLS; module_init(init_dos33_fs) module_exit(exit_dos33_fs) /* DOS 3.3 driver metainformation. */ DECLARE_FSTYPE_DEV(dos33_fs_type,"dos33",dos33_read_super); /* DOS 3.3 superblock operations. */ struct super_operations dos33_super_operations = { read_inode: dos33_read_inode, write_inode: dos33_write_inode, put_inode: dos33_put_inode, put_super: dos33_put_super, // write_super: dos33_write_super, statfs: dos33_statfs }; /* Number of one bits in a given 4-bit value. */ static int onebits[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; /*= Private Functions =======================================================*/ /***************************************************************************** * dos33_count_ones() * Counts the one (set) bits in a block of @data, @size bytes long. This is * basically the nifty algorithm that is also employed by (and was stolen * from) the minix fs driver. *****************************************************************************/ int dos33_count_ones(void *data,size_t size) { const u32 *bytes = (u32*)data; u32 ones = 0; u32 tmp = 0; DOS33_DEBUG("Count Ones\n"); /* Count one bits, one nibble at a time. */ for(size /= 4;size > 0;size--) { tmp = *bytes++; while (tmp) { ones += onebits[tmp & 0x0f]; tmp >>= 4; } } /* Return the number of ones found. */ return ones; } /***************************************************************************** * dos33_find_first_one() * Return the position of the first one bit in a block of @data which is @size * bytes in length. Returns -1 if no one bits were found. *****************************************************************************/ int dos33_find_first_one(void *data,size_t size) { // const u32 *bytes = (u32*)data; // int pos = 0; // u32 tmp = 0; DOS33_DEBUG("find_first_one\n"); #if 0 /* Search for a one bit; if found, return its position. */ for (size /= 4;size > 0;size--,bytes++,pos += 32) { if (*bytes) { for (tmp = be32_to_cpu(*bytes);!(tmp & 0x80000000);tmp <<= 1,pos++); return pos; } } /* No one bits were found. */ #endif return -1; } /***************************************************************************** * prodos_mark_block() * Modify an entry in the volume bitmap. Note that the bitmap must be locked * before calling this function. *****************************************************************************/ void dos33_mark_block(struct super_block *sb,int block,int free) { // SHORTCUT struct prodos_sb_info *psb = PRODOS_SB(sb); // SHORTCUT struct buffer_head *bh = psb->s_bm_bh[block / 4096]; // u32 * const data = ((u32*)bh->b_data) + block % 4096 / 32; // const int bit = cpu_to_be32(0x80000000 >> block % 4096 % 32); DOS33_DEBUG("mark_block\n"); #if 0 /* Check whether we are to mark the block used or free. */ if (free) { /* We should only be freeing used blocks. */ if (*data & bit) { PRODOS_ERROR_1(sb,"block %i is already marked free",block); return; } /* Mark it free. */ *data |= bit; if (psb->s_bm_free >= 0) psb->s_bm_free++; } else { /* We should only be allocating free blocks. */ if (!(*data & bit)) { PRODOS_ERROR_1(sb,"block %i is already marked used",block); return; } /* Mark it clear. */ *data &= ~bit; if (psb->s_bm_free >= 0) psb->s_bm_free--; } #endif /* The bitmap block is now dirty. */ // mark_buffer_dirty(bh); } /***************************************************************************** * prodos_parse_options() * Parse the mount options string and fill prodos_sb_info fields accordingly. *****************************************************************************/ int dos33_parse_options(struct super_block *sb,char *opts) { // SHORTCUT struct prodos_sb_info * const psb = PRODOS_SB(sb); // char *arg_key = NULL; // char *arg_value = NULL; DOS33_DEBUG("parse_options\n"); #if 0 /* Step through "key[=value]" options, modifying members of psb */ if (!opts) return 1; for (arg_key = strtok(opts,",");arg_key;arg_key = strtok(NULL,",")) { if ((arg_value = strchr(arg_key,'='))) *arg_value++ = 0; if (!strcmp(arg_key,"verbose")) psb->s_flags |= PRODOS_FLAG_VERBOSE; else if (!strcmp(arg_key,"crconv")) psb->s_flags |= PRODOS_FLAG_CONVERT_CR; else if (!strcmp(arg_key,"case")) { if (arg_value) { if (!strcmp(arg_value,"lower")) psb->s_flags |= PRODOS_FLAG_LOWERCASE; else if (strcmp(arg_value,"asis")) goto out_invalid_value; } else goto out_omitted_value; } else if (!strcmp(arg_key,"forks")) { if (arg_value) { if (!strcmp(arg_value,"show")) psb->s_flags |= PRODOS_FLAG_SHOW_FORKS; else if (strcmp(arg_value,"hide")) goto out_invalid_value; } else goto out_omitted_value; } else if (!strcmp(arg_key,"partition") || !strcmp(arg_key,"part")) { if (arg_value) strncpy(psb->s_part,arg_value,32); else goto out_omitted_value; } else goto out_invalid_key; } /* Success. */ return ~0; out_omitted_value: PRODOS_ERROR_1(sb,"option \"%s\" requires a value",arg_key); goto out_error; out_invalid_value: PRODOS_ERROR_2(sb,"option \"%s\" given unrecognized value \"%s\"",arg_key,arg_value); goto out_error; out_invalid_key: PRODOS_ERROR_1(sb,"unrecognized option \"%s\"",arg_key); goto out_error; out_error: #endif return 0; } /*= Exported Functions ======================================================*/ /***************************************************************************** * dos33_count_free_blocks() * Count free blocks on the volume by counting the one bits in the volume * bitmap. *****************************************************************************/ int dos33_count_free_blocks(struct super_block *sb) { SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); DOS33_DEBUG("Count_free_blocks\n"); /* Grab the bitmap lock. */ down(&psb->s_bm_lock); /* Count free blocks if they have not already been counted. */ if (psb->s_bm_free < 0) { psb->s_bm_free = 0; psb->s_bm_free += (dos33_count_ones(&(psb->bitmaps[0]),0x23))/2; } /* Release the bitmap lock and return free block count. */ up(&psb->s_bm_lock); return psb->s_bm_free; } /***************************************************************************** * prodos_alloc_block() * Find the first free block on the volume, mark it used, and return its * index. *****************************************************************************/ int dos33_alloc_block(struct super_block *sb) { // SHORTCUT struct prodos_sb_info * const psb = PRODOS_SB(sb); int result = -ENOSPC; // int pos = -1; // int i = 0; DOS33_DEBUG("alloc_block\n"); #if 0 /* Grab the bitmap lock. */ down(&psb->s_bm_lock); /* Fail early if we already know that all blocks are full. */ if (!psb->s_bm_free) goto out_cleanup; /* Find first one bit in the bitmap, which signifies a free block. */ for (i = 0;psb->s_bm_bh[i];i++) { pos = prodos_find_first_one(psb->s_bm_bh[i]->b_data,PRODOS_BLOCK_SIZE); if (pos != -1) { pos += (i * 4096); break; } } /* XXX: should probably cache first free block index. */ /* If a bit was found, clear it. */ if (pos >= 0 && pos < psb->s_part_size) { prodos_mark_block(sb,pos,0); result = pos; } out_cleanup: up(&psb->s_bm_lock); #endif return result; } /***************************************************************************** * prodos_free_block() * Mark a block as free by setting its bit in the volume bitmap. *****************************************************************************/ int dos33_free_block(struct super_block *sb,u16 block) { DOS33_DEBUG("free_blocks\n"); #if 0 /* Grab the bitmap lock. */ down(&PRODOS_SB(sb)->s_bm_lock); /* Set the bit. */ prodos_mark_block(sb,block,1); /* Release the lock and return. */ up(&PRODOS_SB(sb)->s_bm_lock); #endif return 0; } /***************************************************************************** * prodos_free_tree_blocks() * Mark all blocks in a tree (file or fork; seedling, sapling, or full tree) * as free. *****************************************************************************/ int prodos_free_tree_blocks(struct super_block *sb,u8 stype,u16 key_block,u16 blocks_used) { int result = 0; // struct buffer_head *dind_bh = NULL; // u16 ind_block = 0; // struct buffer_head *ind_bh = NULL; DOS33_DEBUG("free_tree_blocks\n"); #if 0 /* Grab the bitmap lock. */ down(&PRODOS_SB(sb)->s_bm_lock); /* Free blocks in sapling and tree files. */ if (stype != PRODOS_STYPE_SEEDLING) { int i = 0; u16 block = 0; /* Load the double indirect block for tree files. */ if (stype == PRODOS_STYPE_TREE) { dind_bh = prodos_bread(sb,key_block); if (!dind_bh) { result = -EIO; goto out_cleanup; } /* Allow for double indirect to be freed outside the loop. */ blocks_used--; } /* Release all data blocks and indirect blocks. */ while (blocks_used) { /* Get next indirect block when necessary. */ if (!(i % 256)) { ind_block = dind_bh ? PRODOS_GET_INDIRECT_ENTRY(dind_bh->b_data,i / 256) : key_block; ind_bh = prodos_bread(sb,ind_block); if (!ind_bh) { result = -EIO; goto out_cleanup; } } /* Free next data block (if it isn't sparse.) */ block = PRODOS_GET_INDIRECT_ENTRY(ind_bh->b_data,i % 256); if (block) { prodos_mark_block(sb,block,1); blocks_used--; } /* Free indirect block when necessary. */ if (!(++i % 256) || (blocks_used == 1)) { prodos_brelse(ind_bh); ind_bh = NULL; prodos_mark_block(sb,ind_block,1); blocks_used--; } } /* Free the double indirect block for tree files. */ if (stype == PRODOS_STYPE_TREE) { prodos_brelse(dind_bh); dind_bh = NULL; prodos_mark_block(sb,key_block,1); } } /* Free blocks in seedling files. */ else prodos_mark_block(sb,key_block,1); out_cleanup: if (ind_bh) prodos_brelse(ind_bh); if (dind_bh) prodos_brelse(dind_bh); up(&PRODOS_SB(sb)->s_bm_lock); #endif return result; } /*= Interface Functions =====================================================*/ /***************************************************************************** * dos33_read_super() *****************************************************************************/ struct super_block *dos33_read_super(struct super_block *sb,void *opts,int silent) { SHORTCUT kdev_t const dev = sb->s_dev; struct dos33_sb_info *psb = NULL; struct buffer_head *bh = NULL; struct dos33_vtol *pdbf = NULL; struct inode *root_inode = NULL; u16 i = 0; DOS33_DEBUG("read_super\n"); /* Initialize the device and the super_block struct. */ set_blocksize(dev,DOS33_BLOCK_SIZE); sb->s_magic = DOS33_SUPER_MAGIC; sb->s_op = &dos33_super_operations; sb->s_blocksize = DOS33_BLOCK_SIZE; sb->s_blocksize_bits = DOS33_BLOCK_SIZE_BITS; sb->s_maxbytes = DOS33_MAX_FILE_SIZE; sb->s_flags = MS_RDONLY | MS_NOSUID; /* Allocate the DOS 3.3 superblock metainformation struct. */ psb = kmalloc(sizeof(struct dos33_sb_info),GFP_KERNEL); if (!psb) { DOS33_ERROR("failed to allocate memory for prodos_sb_info"); goto out_error; } DOS33_SB(sb) = psb; /* Fill the superblock metainformation struct with default values. */ memset(DOS33_SB(sb),0,sizeof(struct dos33_sb_info)); psb->s_part_size = blk_size[MAJOR(dev)] ? blk_size[MAJOR(dev)][MINOR(dev)] * 2 : 0; init_MUTEX(&psb->s_bm_lock); init_MUTEX(&psb->s_dir_lock); /* Parse mount options and, if necessary, find desired partition. */ // if (!prodos_parse_options(sb,(char*)opts)) goto out_error; /* Load DOS 3.3 volume directory block. */ bh = dos33_bread(sb,DOS33_VOLUME_DIR_BLOCK); if (!bh) { DOS33_ERROR("failed to read volume directory block"); goto out_error; } pdbf = (struct dos33_vtol*)bh->b_data; /* Check for DOS 3.3 footprint in the block. */ /* this is a possibly-bogus check, replace */ /* if (pdbf->four != 0x4) { DOS33_ERROR("failed to find a DOS 3.3 filesystem"); goto out_error; } */ /* Use size from the volume directory as partition size. */ psb->s_part_size = ((pdbf->tracks_per_disk*pdbf->sectors_per_track*le16_to_cpu(pdbf->bytes_per_sector))/512); /* Initialize the volume bitmap stuff. */ psb->s_bm_free = -1; for(i=0;itracks_per_disk;i++) { psb->bitmaps[i]=cpu_to_le32(pdbf->bitmaps[i]); } printk("dos33.o: found %ik DOS 3.%i filesystem, Volume %i\n", psb->s_part_size/2,pdbf->dos_version,pdbf->volume); /* Get root inode. */ root_inode = iget(sb,DOS33_MAKE_INO(1,0,DOS33_VOLUME_DIR_TRACK,0xf)); sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) { DOS33_ERROR("failed to get root inode"); goto out_error; } sb->s_root->d_op = &dos33_dentry_operations; /* Return a copy of the 'sb' pointer on success. */ return sb; out_error: if (sb->s_root) { iput(root_inode); root_inode = NULL; } if (bh) { dos33_brelse(bh); bh = NULL; } if (psb) { kfree(DOS33_SB(sb)); DOS33_SB(sb) = NULL; } return NULL; } /***************************************************************************** * prodos_put_super() *****************************************************************************/ void dos33_put_super(struct super_block *sb) { SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); // int i = 0; DOS33_DEBUG("put_super\n"); #if 0 /* Release volume bitmap blocks. */ for (i = 0;psb->s_bm_bh[i];i++) { prodos_brelse(psb->s_bm_bh[i]); psb->s_bm_bh[i] = NULL; } #endif /* Release the ProDOS super block metainformation structure. */ kfree(psb); } /***************************************************************************** * prodos_write_super() ***************************************************************************** void prodos_write_super(struct super_block *sb) { }*/ /***************************************************************************** * prodos_statfs() *****************************************************************************/ int dos33_statfs(struct super_block *sb,struct statfs *statfs) { SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); DOS33_DEBUG("statfs\n"); /* Copy applicable information. */ statfs->f_type = DOS33_SUPER_MAGIC; statfs->f_bsize = sb->s_blocksize; statfs->f_blocks = psb->s_part_size; statfs->f_bfree = dos33_count_free_blocks(sb); statfs->f_bavail = statfs->f_bfree; return 0; } /*= Module Functions ========================================================*/ /***************************************************************************** * init_dos33_filesystem() *****************************************************************************/ int __init init_dos33_fs(void) { /* Since we are overlaying our prodos_inode_info structure over a minix_inode_info structure, we should verify that the former is no larger than the latter. */ DOS33_DEBUG("Insmodding\n"); if (sizeof(struct dos33_inode_info) > sizeof(struct minix_inode_info)) { DOS33_ERROR("aborting; struct prodos_inode_info too big!"); return -EFAULT; } /* Register the filesystem. */ return register_filesystem(&dos33_fs_type); } /***************************************************************************** * exit_dos33_filesystem() *****************************************************************************/ void __exit exit_dos33_fs(void) { /* Unregister the filesystem. */ DOS33_DEBUG("Rmmodding\n"); unregister_filesystem(&dos33_fs_type); }