|
From: Jan-Benedict G. <jb...@us...> - 2004-09-19 09:48:01
|
Update of /cvsroot/linux-vax/kernel-2.5/fs/ods2 In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20217 Added Files: CHANGES Makefile.i386 README dir.c file.c inode.c ods2.h super.c tparse.c tparse.h util.c Log Message: - Initial import of latest ods2 sources (version 0.9.3). This won't compile right now, don't try to build it until you want to hack it! --- NEW FILE: super.c --- /* * linux/fs/ods2/super.c * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * Written 2003 by Jonas Lindholm <jl...@us...> * */ #include <linux/config.h> #include <linux/module.h> #include <linux/string.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <asm/uaccess.h> #include "ods2.h" /* This routine is executed when the ODS2 file system is unmounted. The only thing we need to do is to release file INDEXF.SYS;1 and deallocate memory used for index file header bitmap. */ static void ods2_put_super(struct super_block *sb) { ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; if (ods2p != NULL) { iput(ods2p->indexf); /* release INDEXF.SYS;1 */ kfree(ods2p->ibitmap); kfree(sb->u.generic_sbp); } } /* This routine is executed when the user want to get information about the ODS2 file system. As we are read only we can just copy the information we were gathering during the mount into the buffer. */ int ods2_statfs(struct super_block *sb, struct statfs *buf) { ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; memcpy(buf, &ods2p->statfs, sizeof(struct statfs)); return 0; } static struct super_operations ods2_sops = { read_inode: ods2_read_inode, write_inode: NULL, put_inode: ods2_put_inode, delete_inode: ods2_delete_inode, clear_inode: ods2_clear_inode, put_super: ods2_put_super, write_super: NULL, statfs: ods2_statfs, remount_fs: NULL, }; /* This array is used to get the number of bits set for a nibble value. */ static char unsigned nibble2bits[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; /* This routine open and read the BITMAP.SYS;1 file. */ int ods2_read_bitmap(struct super_block *sb) { ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; struct buffer_head *bh; struct inode *inode; if ((inode = iget(sb, 2)) != NULL) { /* this is BITMAP.SYS */ ODS2FH *ods2fhp = (ODS2FH *)(inode->u.generic_ip); u32 lbn; if ((lbn = vbn2lbn(sb, ods2fhp->map, 1)) > 0 && (bh = sb_bread(sb, GETBLKNO(sb, lbn))) != NULL && bh->b_data != NULL) { struct scbdef *scb = (SCBDEF *)(GETBLKP(sb, lbn, bh->b_data)); short unsigned *p; short unsigned chksum = 0; for (p = (short unsigned *)scb ; p < (short unsigned *)&(scb->scb_w_checksum) ; chksum += *p++); if (scb->u1.s1.scb_b_structlevl == 2 && scb->u1.s1.scb_b_structlevv >= 1 && scb->scb_w_cluster == ods2p->hm2.hm2_w_cluster && scb->scb_w_checksum == chksum) { struct buffer_head *bh2; u32 vbn = 1; u32 bitset = 0; /* We need to loop through all bytes that make up the bitmap. The fastest way to count the number of bits set in the byte is to have a nibble table that has the number of bits for the values of 0 to 15. By adding the number of bits for the low and high nibble we can get the total amount of bits set. */ while (vbn * 512 < inode->i_size && (lbn = vbn2lbn(sb, ods2fhp->map, vbn + 1)) > 0 && (bh2 = sb_bread(sb, GETBLKNO(sb, lbn))) != NULL && bh->b_data != NULL) { u8 *bp = (char unsigned *)(GETBLKP(sb, lbn, bh2->b_data)); int cnt; for (cnt = 0; cnt < 512; cnt++, bp++) { bitset += (nibble2bits[*bp & 0x0f] + nibble2bits[*bp >> 4]); } brelse(bh2); vbn++; } bitset *= scb->scb_w_cluster; /* each bit represent 1 or more blocks (cluster factor) */ ods2p->statfs.f_blocks = scb->scb_l_volsize; ods2p->statfs.f_bfree = bitset; ods2p->statfs.f_bavail = bitset; brelse(bh); iput(inode); return 1; /* everything went ok */ } brelse(bh); /* invalid data in VBN 1 */ } iput(inode); /* could not read VBN 1 */ } return 0; /* unable to get inode 2 OR some other problem */ } /* This routine allocate memory for the index file header bitmap and copy data from the INDEXF.SYS file. At the same time the number of free file headers are counted. */ int ods2_read_ibitmap(struct super_block *sb) { ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; struct buffer_head *bh; int idx; ods2p->statfs.f_ffree = 0; if ((ods2p->ibitmap = kmalloc(ods2p->hm2.hm2_w_ibmapsize << 9, GFP_KERNEL)) != NULL) { memset(ods2p->ibitmap, 0, (ods2p->hm2.hm2_w_ibmapsize << 9)); for (idx = 0 ; idx < ods2p->hm2.hm2_w_ibmapsize ; idx++) { if ((bh = sb_bread(sb, GETBLKNO(sb, ods2p->hm2.hm2_l_ibmaplbn + idx))) != NULL && bh->b_data != NULL) { u8 *bp = (GETBLKP(sb, ods2p->hm2.hm2_l_ibmaplbn + idx, bh->b_data)); int cnt; memcpy((ods2p->ibitmap + (idx << 9)), GETBLKP(sb, ods2p->hm2.hm2_l_ibmaplbn + idx, bh->b_data), 512); for (cnt = 0; cnt < 512; cnt++, bp++) { ods2p->statfs.f_ffree += (nibble2bits[(*bp & 0x0f) ^ 0xf] + nibble2bits[(*bp >> 4) ^ 0xf]); } bforget(bh); } } return 1; } printk("ODS2-fs error when allocating memory for index file header bitmap\n"); return 0; } /* This is the routine that is invoked when an ODS2 file system is mounted. */ static struct super_block * ods2_read_super(struct super_block *sb, void *data, int silent) { struct buffer_head *bh; ODS2SB *ods2p; sb_set_blocksize(sb, get_hardsect_size(sb->s_dev)); if ((bh = sb_bread(sb, GETBLKNO(sb, 1))) != NULL && bh->b_data != NULL) { u16 *p; u16 chksum1 = 0; u16 chksum2 = 0; if ((sb->u.generic_sbp = kmalloc(sizeof(ODS2SB), GFP_KERNEL)) == NULL) { printk("ODS2-fs kmalloc failed for sb generic\n"); return NULL; } ods2p = (ODS2SB *)sb->u.generic_sbp; memcpy(&ods2p->hm2, GETBLKP(sb, 1, bh->b_data), sizeof(HM2DEF)); brelse(bh); for (p = (u16 *)&(ods2p->hm2) ; p < (u16 *)&(ods2p->hm2.hm2_w_checksum1) ; chksum1 += *p++); for (p = (u16 *)&(ods2p->hm2) ; p < (u16 *)&(ods2p->hm2.hm2_w_checksum2) ; chksum2 += *p++); /* This is the way to check for a valid home block. */ if (ods2p->hm2.hm2_l_homelbn != 0 && ods2p->hm2.hm2_l_alhomelbn != 0 && ods2p->hm2.hm2_l_altidxlbn != 0 && ods2p->hm2.hm2_w_cluster != 0 && ods2p->hm2.u1.s1.hm2_b_structlevl == 2 && ods2p->hm2.u1.s1.hm2_b_structlevv >= 1 && ods2p->hm2.hm2_w_homevbn != 0 && ods2p->hm2.hm2_l_ibmaplbn != 0 && ods2p->hm2.hm2_l_maxfiles > ods2p->hm2.hm2_w_resfiles && ods2p->hm2.hm2_w_resfiles >= 5 && chksum1 == ods2p->hm2.hm2_w_checksum1 && chksum2 == ods2p->hm2.hm2_w_checksum2) { ods2p->flags.v_raw = 0; ods2p->flags.v_lowercase = 0; ods2p->flags.v_version = SB_M_VERSALL; ods2p->dollar = '_'; ods2p->semicolon = '.'; if (data != NULL) { parse_options(sb, data); } sb->s_op = &ods2_sops; ods2p->indexf = iget(sb, 1); /* read INDEXF.SYS. */ sb->s_root = d_alloc_root(iget(sb, 4)); /* this is 000000.DIR;1 */ /* We need to be able to read the index file header bitmap. */ if (ods2_read_ibitmap(sb)) { /* We need to be able to read BITMAP.SYS as it contains the bitmap for allocated blocks. Without this file we need to rebuild it by reading ALL file mapping pointers for ALL files and create the file. That will be in a later release... */ if (ods2_read_bitmap(sb)) { char format[13]; char volname[13]; char volowner[13]; /* We need to fill in statfs structure used when any user want to get information about the mounted ODS2 file system. Some of the information is static and other is found in BITMAP.SYS. */ ods2p->statfs.f_type = 0x3253444f; /* 2SDO */ ods2p->statfs.f_bsize = 512; ods2p->statfs.f_files = ods2p->hm2.hm2_l_maxfiles; ods2p->statfs.f_namelen = 80; memcpy(format, ods2p->hm2.hm2_t_format, 12); format[12] = 0; memcpy(volname, ods2p->hm2.hm2_t_volname, 12); volname[12] = 0; memcpy(volowner, ods2p->hm2.hm2_t_ownername, 12); volowner[12] = 0; printk("ODS2-fs This is a valid ODS2 file system with format /%s/ and volume name /%s/ and owner /%s/\n", format, volname, volowner); return sb; } kfree(ods2p->ibitmap); } } kfree(sb->u.generic_sbp); } return NULL; } static DECLARE_FSTYPE_DEV(ods2_fs_type, "ods2", ods2_read_super); static int __init init_ods2_fs(void) { return register_filesystem(&ods2_fs_type); } static void __exit exit_ods2_fs(void) { unregister_filesystem(&ods2_fs_type); } EXPORT_NO_SYMBOLS; module_init(init_ods2_fs); module_exit(exit_ods2_fs); MODULE_AUTHOR("Jonas Lindholm - <jl...@us...>"); MODULE_DESCRIPTION("ODS2 Filesystem"); MODULE_LICENSE("GPL"); /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ --- NEW FILE: inode.c --- /* * linux/fs/ods2/inode.c * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * Written 2003 by Jonas Lindholm <jl...@us...> * */ #include <linux/config.h> /* #include <linux/module.h> */ #include <linux/string.h> #include <linux/ctype.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <asm/uaccess.h> #include "ods2.h" struct file_operations ods2_dir_operations = { read: NULL, readdir: ods2_readdir, open: ods2_open_release, release: ods2_open_release, ioctl: NULL, fsync: NULL, }; struct file_operations ods2_file_operations = { read: ods2_read, readdir: NULL, llseek: ods2_llseek, open: ods2_open_release, release: ods2_open_release, ioctl: ods2_file_ioctl, fsync: NULL, }; struct inode_operations ods2_dir_inode_operations = { create: NULL, lookup: ods2_lookup, link: NULL, unlink: NULL, symlink: NULL, mkdir: NULL, rmdir: NULL, mknod: NULL, rename: NULL, }; struct dentry *ods2_lookup(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; struct buffer_head *bh = NULL; char *vp; u16 *rec; ODS2FH *ods2fhp = (ODS2FH *)dir->u.generic_ip; u32 vbn = 1; u32 lbn; int vers = 0; char name[dentry->d_name.len + 1]; memcpy(name, dentry->d_name.name, dentry->d_name.len); name[dentry->d_name.len] = 0; /* We need to extract any version number and terminate the file name with file type at the ; character because in the directory file only the file name and type is stored as text without the ; character. The version number for the file is stored together with each FID. */ if (( vp = strrchr(name, ods2p->semicolon)) != NULL) { *vp++ = 0; if (sscanf(vp, "%d", &vers) != 1) { *--vp = ods2p->semicolon; } else if (vers > 32767) { printk("ODS2-fs error with version number for %s (%s)\n", name, vp); return ERR_PTR(-EBADF); } } while ((lbn = vbn2lbn(sb, ods2fhp->map, vbn)) > 0 && (bh = sb_bread(sb, GETBLKNO(sb, lbn))) != NULL && bh->b_data != NULL) { rec = (u16 *)(GETBLKP(sb, lbn, bh->b_data)); while (*rec != 65535 && *rec != 0) { DIRDEF *dire = (DIRDEF *)rec; if (dire->u1.s1.dir_b_namecount == strlen(name)) { char dirname[dire->u1.s1.dir_b_namecount + 1]; memcpy(dirname, &dire->u1.s1.dir_t_name, dire->u1.s1.dir_b_namecount); dirname[dire->u1.s1.dir_b_namecount] = 0; if (ods2p->dollar != '$' || ods2p->flags.v_lowercase) { char *p = dirname; char cnt = dire->u1.s1.dir_b_namecount; while (*p && cnt-- > 0) { if (*p == '$') { *p = ods2p->dollar; } if (ods2p->flags.v_lowercase) { *p = tolower(*p); } p++; } } if (strcmp(dirname, name) == 0) { int curbyte = 0; while (curbyte < dire->u1.s1.dir_w_size) { u32 ino; DIRDEF *dirv = (DIRDEF *)((char *)dire + ((dire->u1.s1.dir_b_namecount + 1) & ~1) + 6 + curbyte); if (dirv->u1.s2.dir_w_version == vers || vers == 0) { struct inode *inode; ino = (dirv->u1.s2.u2.s3.fid_b_nmx << 16) | le16_to_cpu(dirv->u1.s2.u2.s3.fid_w_num); brelse(bh); if ((inode = iget(dir->i_sb, ino)) != NULL) { d_add(dentry, inode); return NULL; } printk("ODS2-fs error when iget for file %s\n", name); return ERR_PTR(-EACCES); } curbyte += 8; } } } rec = (u16 *)((char *)rec + le16_to_cpu(dire->u1.s1.dir_w_size) + 2); } brelse(bh); vbn++; } d_add(dentry, NULL); return NULL; } /* The array is used to map ODS2 protection bits to Unix protection bits. There are two problems when doing the mapping. The first one is that ODS2 have four types of classes, system, owner, group and world. As you know Unix has only three, owner, group and other. We solve that by mapping owner to owner, group to group and world to other. The system class is ignored. The other problem is that ODS2 have four different protection bits, read, write, execute and delete. The read, write and execute can be mapped directly to Unix bits but the delete bit must be mapped to something else. As write access give the user delete access on Unix we map the delete bit to write access. Please note that on an ODS2 disk a set bit mean deny access where on Unix a set bit mean granted access. */ char unsigned vms2unixprot[] = { /* ODS2 prot */ S_IROTH | S_IWOTH | S_IXOTH , /* D E W R */ 0 | S_IWOTH | S_IXOTH , /* D E W */ S_IROTH | S_IWOTH | S_IXOTH , /* D E R */ 0 | S_IWOTH | S_IXOTH , /* D E */ S_IROTH | S_IWOTH | 0 , /* D W R */ 0 | S_IWOTH | 0 , /* D W */ S_IROTH | S_IWOTH | 0 , /* D R */ 0 | S_IWOTH | 0 , /* D */ S_IROTH | S_IWOTH | S_IXOTH , /* E W R */ 0 | S_IWOTH | S_IXOTH , /* E W */ S_IROTH | 0 | S_IXOTH , /* E R */ 0 | 0 | S_IXOTH , /* E */ S_IROTH | S_IWOTH | 0 , /* W R */ 0 | S_IWOTH | 0 , /* W */ S_IROTH | 0 | 0 , /* R */ 0 | 0 | 0 , /* */ }; void ods2_read_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; u32 fhlbn; if ((fhlbn = ino2fhlbn(sb, inode->i_ino)) > 0 && (bh = sb_bread(sb, GETBLKNO(sb, fhlbn))) != NULL && bh->b_data != NULL) { FH2DEF *fh2p = (FH2DEF *)(GETBLKP(sb, fhlbn, bh->b_data)); if ((inode->u.generic_ip = kmalloc(sizeof(ODS2FH), GFP_KERNEL)) != NULL) { ODS2FH *ods2fhp; FI2DEF *fi2p; FATDEF *fatp; ods2fhp = (ODS2FH *)inode->u.generic_ip; ods2fhp->map = NULL; ods2fhp->ods2vari = NULL; fi2p = (FI2DEF *)((short unsigned *)fh2p + fh2p->fh2_b_idoffset); fatp = (FATDEF *)&(fh2p->fh2_w_recattr); if (verify_fh(fh2p, inode->i_ino)) { memcpy(&ods2fhp->fat, fatp, sizeof(FATDEF)); ods2fhp->map = getmap(sb, fh2p); if (fh2p->u4.s1.fch_v_directory) { inode->i_mode = S_IFDIR; inode->i_op = &ods2_dir_inode_operations; inode->i_fop = &ods2_dir_operations; } else { inode->i_mode = S_IFREG; inode->i_fop = &ods2_file_operations; } inode->i_uid = le16_to_cpu(fh2p->u5.s1.fh2_w_mem); inode->i_gid = le16_to_cpu(fh2p->u5.s1.fh2_w_grp); inode->i_ctime = vms2unixtime(fi2p->fi2_q_credate); inode->i_mtime = vms2unixtime(fi2p->fi2_q_revdate); inode->i_atime = vms2unixtime(fi2p->fi2_q_revdate); /* Note that we don't use the system protection bits for ODS2. */ inode->i_mode |= vms2unixprot[(le16_to_cpu(fh2p->fh2_w_fileprot) >> 4) & 0x0f] << 6; /* owner */ inode->i_mode |= vms2unixprot[(le16_to_cpu(fh2p->fh2_w_fileprot) >> 8) & 0x0f] << 3; /* group */ inode->i_mode |= vms2unixprot[(le16_to_cpu(fh2p->fh2_w_fileprot) >> 12) & 0x0f]; /* world => other */ inode->i_blksize = 512; inode->i_blocks = ((le16_to_cpu(fatp->u1.s1.fat_w_hiblkh) << 16) | le16_to_cpu(fatp->u1.s1.fat_w_hiblkl)); inode->i_size = ((le16_to_cpu(fatp->u2.s1.fat_w_efblkh) << 16) | le16_to_cpu(fatp->u2.s1.fat_w_efblkl)) << 9; if (inode->i_size > 0) { inode->i_size -= 512; } inode->i_size += le16_to_cpu(fatp->fat_w_ffbyte); if ((fatp->u0.s0.fat_v_rtype == FAT_C_VFC || fatp->u0.s0.fat_v_rtype == FAT_C_VARIABLE) && !ods2p->flags.v_raw) { if ((ods2fhp->ods2vari = (ODS2VARI *)kmalloc(sizeof(ODS2VARI), GFP_KERNEL)) != NULL) { memset(ods2fhp->ods2vari, 0 , sizeof(ODS2VARI)); sema_init(&(ods2fhp->ods2vari->sem), 1); } else { printk("ODS2-fs kmalloc failed for vari data\n"); } } ods2fhp->parent = (fh2p->u6.s1.fid_b_nmx << 16) | le16_to_cpu(fh2p->u6.s1.fid_w_num); inode->i_version = ++event; bforget(bh); return; } printk("ODS2-fs not a valid file header\n"); } else { bforget(bh); printk("ODS2-fs kmalloc failed for extension inode\n"); kfree(inode->u.generic_ip); } } printk("ODS2-fs error reading inode\n"); make_bad_inode(inode); } /* For a read only file system there is nothing to do for put_inode. */ void ods2_put_inode(struct inode *inode) { } void ods2_clear_inode(struct inode *inode) { ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; if (ods2fhp != NULL) { ODS2MAP *map = ods2fhp->map; while (map != NULL) { ODS2MAP *nxt = map->nxt; kfree(map); map = nxt; } ods2fhp->map = NULL; if (ods2fhp->ods2vari != NULL) { /* in case the file was of variable record type */ int idx; for (idx = 0; idx < 128; idx++) { ODS2VAR *ods2varp = ods2fhp->ods2vari->ods2varp[idx]; while (ods2varp != NULL) { ODS2VAR *nxt = ods2varp->nxt; kfree(ods2varp); ods2varp = nxt; } } kfree(ods2fhp->ods2vari); ods2fhp->ods2vari = NULL; } kfree(inode->u.generic_ip); inode->u.generic_ip = NULL; } } /* This routine doesn't need to be defined for a read only filesystem but we do it for fun so remember to call clear_inode otherwise you will run out of memory... */ void ods2_delete_inode(struct inode *inode) { clear_inode(inode); } /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ --- NEW FILE: dir.c --- /* * linux/fs/ods2/dir.c * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * Written 2003 by Jonas Lindholm <jl...@us...> * */ #include <linux/config.h> /* #include <linux/module.h> */ #include <linux/string.h> #include <linux/ctype.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <asm/uaccess.h> #include "ods2.h" /* This routine return one or more file names for a directory file. For an ODS2 file structure each file name can have one or more versions of a file, each file must be treated as a unique file. */ int ods2_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; struct buffer_head *bh = NULL; ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; loff_t pos = filp->f_pos; u32 vbn = (ods2filep->currec >> 9); /* get current VBN-1 to use */ u32 lbn; char cdirname[256]; memset(cdirname, ' ', sizeof(cdirname)); /* When there are no more files to return the file position in file is set to -1. */ if (pos == -1) return 0; /* When we get called the first time for a directory file the file position is set to 0. We must then return two fake entries, . for the current directory and .. for the parent directory. */ if (pos == 0) { filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR); filldir(dirent, "..", 2, 1, ods2fhp->parent, DT_DIR); ods2filep->currec = 0; ods2filep->curbyte = 0; vbn = 0; } /* As long we can translate the virtual block number, VBN, to a logical block number, LBN, and read the block we continue to loop. */ while (vbn * 512 < inode->i_size && (lbn = vbn2lbn(sb, ods2fhp->map, vbn + 1)) > 0 && (bh = sb_bread(sb, GETBLKNO(sb, lbn))) != NULL && bh->b_data != NULL) { u16 *recp = (short unsigned *)((char *)(GETBLKP(sb, lbn, bh->b_data)) + (ods2filep->currec & 511)); /* For a ODS2 directory each block contains 1 to 62 directory entries. Note that a directory entry can not span between two or more blocks. We should be able to use the routine to read variable block size but because directory file is so specific we do our own block decoding here. When there are no more directory entries in the current block the record length -1 is inserted as the last record. */ while (*recp != 65535 && *recp <= 512 && ods2filep->currec < inode->i_size) { DIRDEF *dire = (DIRDEF *)recp; char dirname[dire->u1.s1.dir_b_namecount + 1]; memcpy(dirname, &dire->u1.s1.dir_t_name, dire->u1.s1.dir_b_namecount); dirname[dire->u1.s1.dir_b_namecount] = 0; if (ods2p->dollar != '$' || ods2p->flags.v_lowercase) { char *p = dirname; char cnt = dire->u1.s1.dir_b_namecount; while (*p && cnt-- > 0) { if (*p == '$') { *p = ods2p->dollar; } if (ods2p->flags.v_lowercase) { *p = tolower(*p); } p++; } } if (ods2filep->curbyte == 0) { ods2filep->curbyte = ((dire->u1.s1.dir_b_namecount + 1) & ~1) + 6; } filp->f_pos = ods2filep->currec + ods2filep->curbyte; while (ods2filep->curbyte < dire->u1.s1.dir_w_size && !(ods2p->flags.v_version != SB_M_VERSALL && strlen(dirname) == strlen(cdirname) && strncmp(dirname, cdirname, strlen(dirname)) == 0)) { DIRDEF *dirv = (DIRDEF *)((char *)dire + ods2filep->curbyte); u32 ino = (dirv->u1.s2.u2.s3.fid_b_nmx << 16) | le16_to_cpu(dirv->u1.s2.u2.s3.fid_w_num); char dirnamev[dire->u1.s1.dir_b_namecount + 1 + 5 + 1]; if (ino != 4) { /* we must ignore 000000.DIR as it is the same as . */ if (ods2p->flags.v_version == SB_M_VERSNONE) { sprintf(dirnamev, "%s", dirname); } else { sprintf(dirnamev, "%s%c%d", dirname, ods2p->semicolon, dirv->u1.s2.dir_w_version); } /* We don't really know if the file is a directory by just checking the file extension but it is the best we can do. Should the file have extension .DIR but be a regular file the mistake will be detected later on when the user try to walk down into the false directory. */ if (filldir(dirent, dirnamev, strlen(dirnamev), filp->f_pos, ino, (strstr(dirnamev, (ods2p->flags.v_lowercase ? ".dir." : ".DIR")) == NULL ? DT_REG : DT_DIR))) { /* We come here when filldir is unable to handle more entries. */ brelse(bh); return 0; } if (ods2p->flags.v_version != SB_M_VERSALL) { strcpy(cdirname, dirname); } } if (ods2p->flags.v_version == SB_M_VERSALL) { ods2filep->curbyte += 8; filp->f_pos += 8; } else { ods2filep->curbyte = le16_to_cpu(dire->u1.s1.dir_w_size); filp->f_pos += dire->u1.s1.dir_w_size; } } /* When we come here there are no more versions for the file name. We then reset our current byte offset and set current record offset to the next directory entry. */ ods2filep->curbyte = 0; ods2filep->currec += le16_to_cpu(dire->u1.s1.dir_w_size) + 2; recp = (u16 *)((char *)recp + le16_to_cpu(dire->u1.s1.dir_w_size) + 2); } /* When we come here there are no more directory entries in the current block and we just release the buffer and increase the VBN counter. */ brelse(bh); vbn++; ods2filep->currec = vbn * 512; } filp->f_pos = -1; /* this mark that we have no more files to return */ return 0; } /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ --- NEW FILE: file.c --- /* * linux/fs/ods2/file.c * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * Written 2003 by Jonas Lindholm <jl...@us...> * * Changes: 0.9.2 - A lot of bug fixes for keeping track of * virtual position for variable record files. * */ #include <linux/config.h> /* #include <linux/module.h> */ #include <linux/string.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <asm/uaccess.h> #include "ods2.h" /* FUNCTION: This routine take care of ioctl command for an open file. It is possible to put a file into raw mode independing if raw mode was selected or not during the file system mount. This is used by the rms library. INPUT: *inode pointer to inode structure for the open file. *filp pointer to file structure for the open file. cmd ODS2 specific command. arg argument for the command. OUTPUT: 0 if everything went ok. -ENOTTY for invalid cmmand. Other negativ values for different errors. IMPLICIT: None. */ int ods2_file_ioctl(struct inode *inode, struct file *filp, int unsigned cmd, long unsigned arg) { struct super_block *sb = inode->i_sb; ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; int error = -ENOTTY; int onoff; switch (cmd) { case ODS2_IOC_FISETRAW: if ((error = get_user(onoff, (int *)arg)) == 0) { ods2filep->u1.s1.v_raw = (onoff == 1); } break; case ODS2_IOC_FIGETRAW: onoff = ods2filep->u1.s1.v_raw; error = put_user(onoff, (int *)arg); break; case ODS2_IOC_SBGETRAW: onoff = ods2p->flags.v_raw; error = put_user(onoff, (int *)arg); break; } return error; } /* FUNCTION: This routine update the memory structure used to keep track of the virtual position in a variable record file. INPUT: loff virtual position. *ods2vari pointer to memory structure used to keep tracj of position. currec current record position in file. This is the offset in bytes from the start of the file. OUTPUT: 1 if the update was successful. 0 if something went wrong such as memory allocaion. IMPLICIT: The only requirement is that a linked list of varp structures are at least the number of entries allocated by macro IDXBLOCK. */ int update_virtual_file_pos(loff_t loff, ODS2VARI *ods2vari, u64 currec) { ODS2VAR *ods2varp = NULL; int idxvar = IDXVAR(loff); int idxvari = IDXVARI(loff); int idxblock = IDXBLOCK(loff); if (ods2vari->ods2varp[idxvari] == NULL) { if ((ods2vari->ods2varp[idxvari] = (ODS2VAR *)kmalloc(sizeof(ODS2VAR), GFP_KERNEL)) != NULL) { memset(ods2vari->ods2varp[idxvari], 0, sizeof(ODS2VAR)); } else { printk("ODS2-fs kmalloc failed for new varp (1)\n"); return 0; } } ods2varp = ods2vari->ods2varp[idxvari]; for (; idxblock > 0; idxblock--) { if (ods2varp->nxt == NULL) { if ((ods2varp->nxt = (ODS2VAR *)kmalloc(sizeof(ODS2VAR), GFP_KERNEL)) != NULL) { memset(ods2varp->nxt, 0, sizeof(ODS2VAR)); } else { printk("ODS2-fs kmalloc failed for new varp (2)\n"); return 0; } } ods2varp = ods2varp->nxt; } if (ods2varp != NULL && ods2varp->s1[idxvar].loff == 0) { ods2varp->s1[idxvar].recoffs = currec; ods2varp->s1[idxvar].loff = loff; ods2vari->highidx = loff; } return 1; } /* FUNCTION: This routine take care of reading of variable record files. This routine will add a LF after each record if one of the following record attributes are set: FAT_M_FORTRANCC, FAT_M_IMPLIEDCC, FAT_M_PRINTCC. Note that a correct handling of all record structures should be able to handle form feed and insertion of more than one LF after each record. All this extra functionality must be handled outside of this driver. It will also handle the FAT_M_NOSPAN. This attributes indicates that no record must span between blocks. In each block a record with length 65535 (-1) is inserted to indicate that there are no more records in the current block. INPUT: *filp pointer to the file. *buf buffer where to return data. buflen size of buf area. *loff virtual position in file where to read from. OUTPUT: The number of bytes read. 0 if no bytes was read. IMPLICIT: The ODS2 specific part of the file header must have a ODS2VARI structure attached to it. */ ssize_t ods2_read_variable(struct file *filp, char *buf, size_t buflen, loff_t *loff) { struct inode *inode = filp->f_dentry->d_inode; char *buforg = buf; ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; ODS2VARI *ods2vari = ods2fhp->ods2vari; FATDEF *fatp = (FATDEF *)&(ods2fhp->fat); u32 vbn = 0; u16 cpylen; if (*loff == 0) { ods2filep->currec = 0; ods2filep->curbyte = 0; ods2filep->reclen = 0; } if (ods2filep->reclen == 65535) { brelse(ods2filep->bhp); ods2filep->bhp = NULL; return 0; } while (1) { /* We need to loop until the calculated value of currec offset plus currect byte offset from currec give the same VBN as the last one we fetched. There is one case when we will loop. That case is when a record start is at the last two bytes of the block. In that case the length will be fetched from current block but all data will start on next block. */ do { vbn = (ods2filep->currec + ods2filep->curbyte) >> 9; if (!(getfilebh(filp, vbn + 1))) { ods2filep->reclen = 65535; return (buf - buforg); } /* If curbyte is zero we will start on a new record. */ if (ods2filep->curbyte == 0) { ods2filep->reclen = le16_to_cpu(*((u16 *)((char *)ods2filep->data + (ods2filep->currec & 511)))); if ((*loff >> 16) != 0) { down(&(ods2vari->sem)); update_virtual_file_pos(*loff, ods2vari, ods2filep->currec); up(&(ods2vari->sem)); } if ((ods2filep->reclen == 65535 && !(fatp->fat_b_rattrib & FAT_M_NOSPAN)) || (ods2filep->currec >= inode->i_size)) { /* end of records */ ods2filep->reclen = 65535; return (buf - buforg); } if (ods2filep->reclen == 65535 && (fatp->fat_b_rattrib & FAT_M_NOSPAN)) { ods2filep->currec = (vbn + 1) * 512; /* could be a new record at next block */ } else { ods2filep->curbyte = 2; ods2filep->curbyte += (fatp->u0.s0.fat_v_rtype == FAT_C_VFC ? fatp->fat_b_vfcsize : 0); } } } while (((ods2filep->currec + ods2filep->curbyte) >> 9) != vbn); cpylen = MIN(MIN((ods2filep->reclen - ods2filep->curbyte + 2), buflen), (512 - ((ods2filep->currec + ods2filep->curbyte) & 511))); if (cpylen > 0) { u8 *recp = (u8 *)((char *)ods2filep->data + ((ods2filep->currec + ods2filep->curbyte) & 511)); memcpy(buf, recp, cpylen); *loff += cpylen; /* loff will always be a virtual offset for a variable record file */ buf += cpylen; buflen -= cpylen; ods2filep->curbyte += cpylen; } if (ods2filep->curbyte - 2 == ods2filep->reclen) { if (buflen > 0) { if (fatp->fat_b_rattrib & FAT_M_FORTRANCC || fatp->fat_b_rattrib & FAT_M_IMPLIEDCC || fatp->fat_b_rattrib & FAT_M_PRINTCC) { buflen--; *buf++ = '\n'; *loff += 1; } ods2filep->currec = ((ods2filep->currec + ods2filep->reclen + 1) & ~1) + 2; /* each record is always even aligned */ ods2filep->curbyte = 0; } } if (buflen == 0) { return (buf - buforg); } } } /* FUNCTION: This routine is invoked when the file type is one of STREAM, STREAMLF or STREAMCR. For a non-RMS machine that doesn't know anything about records these three formats are the same. For RMS the different between these formats is the following: STREAM: Records are delimited by FF, VT, LF, or CRLF. STREAMLF: Records are delimited by LF. STREAMCR: Records are delimited by CR. Note that we can not use generic read routines even if we treat the data as just a stream of bytes because the way we need to translate from VBN to LBN. INPUT: *filp pointer to the file. *buf buffer where to return data. buflen size of buf area. *loff virtual position in file where to read from. OUTPUT: The number of bytes read. 0 if no bytes was read. IMPLICIT: None. */ ssize_t ods2_read_stream(struct file *filp, char *buf, size_t buflen, loff_t *loff) { struct inode *inode = filp->f_dentry->d_inode; char *buforg = buf; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; u32 vbn = 0; u16 cpylen; while (*loff < inode->i_size) { vbn = *loff >> 9; if (!(getfilebh(filp, vbn + 1))) { *loff = inode->i_size; return (buf - buforg); } if ((cpylen = MIN(MIN(inode->i_size - *loff, buflen), 512 - (*loff & 511))) > 0) { u8 *recp = (u8 *)((char *)ods2filep->data + (*loff & 511)); memcpy(buf, recp, cpylen); *loff += cpylen; buf += cpylen; buflen -= cpylen; if (buflen == 0) { return (buf - buforg); } } } brelse(ods2filep->bhp); ods2filep->bhp = NULL; return (buf - buforg); } /* FUNCTION: This routine is called when a read request is done for any file. The routine will invoke one of two functions. One function is for files of STREAM types. The other routine is for VARIABLE record files. File of type RELATIVE or INDEXED are not supported by this module. Should the file system be mounted by option raw or if the file has been set to raw mode the routine to hamdle STREAM format is invoked for ALL file types including RELATIVE and INDEXED files. *filp pointer to the file. *buf buffer where to return data. buflen size of buf area. *loff virtual position in file where to read from. OUTPUT: The number of bytes read. 0 if no bytes was read. IMPLICIT: None. */ ssize_t ods2_read(struct file *filp, char *buf, size_t buflen, loff_t *loff) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; FATDEF *fatp = (FATDEF *)&(ods2fhp->fat); if (ods2p->flags.v_raw || ods2filep->u1.s1.v_raw) { return ods2_read_stream(filp, buf, buflen, loff); } else { switch (fatp->u0.s0.fat_v_fileorg) { case FAT_C_SEQUANTIAL: { switch (fatp->u0.s0.fat_v_rtype) { case FAT_C_VFC: case FAT_C_VARIABLE: return ods2_read_variable(filp, buf, buflen, loff); case FAT_C_FIXED: case FAT_C_STREAMLF: case FAT_C_STREAMCR: case FAT_C_STREAM: return ods2_read_stream(filp, buf, buflen, loff); default: return 0; } } default: return 0; } } } /* FUNCTION: This routine return a valid file offset for STREAM files. Note that the current ODS2 driver does not support an offset that is larger then file size. INPUT: *filp pointer to the file. loff virtual position in file where to read from. seek how loff should be calculated for the file. 0 = absolute position. 1 = offset from current file position. 2 = offset from end of file. OUTPUT: The new position in the file is returned. IMPLICIT: This routine will not allow the current position to be beyond the end of file position. */ loff_t ods2_llseek_stream(struct file *filp, loff_t loff, int seek) { struct inode *inode = filp->f_dentry->d_inode; loff_t offs; if (seek == 0) { /* SEEK_SET */ offs = MIN(loff, inode->i_size); } else { if (seek == 1) { /* SEEK_CUR */ if (loff > 0) { offs = MIN(filp->f_pos + loff, inode->i_size); } else { offs = MAX(filp->f_pos + loff, 0); } } else { offs = MIN(inode->i_size + loff, inode->i_size); } } filp->f_pos = offs; filp->f_reada = 0; filp->f_version++; return offs; } /* FUNCTION: This routine return a valid file offset for VARIABLE files. Note that the current ODS2 driver does not support an offset that is larger then file size. This routine will take care of the fact that Linux doesn't know anything about records in a file so all routines and utilities believe the file offset is the exact position in the file. For a variable record file each record consists not only of data but also of the record length (2 bytes). An additional fix part of the record can contain meta data for the record such as print control information. All this make it complicated to calculate the record offset into the file from a given offset. To avoid to be forced to read from the start of the file to find the correct position for a given offset checkpoints are stored together with the inode for each 64K blocks of data. By using these checkpoints this routine can calculate the record position for a given offset by starting reading records from the closest checkpoint. If the requested position is within a part of the file already read no more than 128 blocks of data must be read to find the position. On the other hand if no reading has been done for the requested position before we could ending up to read all records for the remaining of the file but that is no other good solution to the problem. INPUT: *filp pointer to the file. loff virtual position in file where to read from. seek how loff should be calculated for the file. 0 = absolute position. 1 = offset from current file position. 2 = offset from end of file. OUTPUT: The new position in the file is returned. IMPLICIT: This routine will not allow the current position to be beyond the end of file position. */ loff_t ods2_llseek_variable(struct file *filp, loff_t loff, int seek) { struct inode *inode = filp->f_dentry->d_inode; ODS2VAR *ods2varp = NULL; ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; ODS2VARI *ods2vari = ods2fhp->ods2vari; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; FATDEF *fatp = (FATDEF *)&(ods2fhp->fat); int idxblock = 0; loff_t offs = 0; loff_t coffs = 0; loff_t currec = 0; u32 vbn = 0; u16 reclen = 0; offs = loff; if (seek == 0) { /* SEEK_SET */ offs = MIN(offs, inode->i_size); } else { if (seek == 1) { /* SEEK_CUR */ if (offs > 0) { offs = MIN(filp->f_pos + offs, inode->i_size); } else { offs = MAX(filp->f_pos + offs, 0); } } else { offs = MIN(inode->i_size + offs, inode->i_size); } } /* offs - the absolute virtual offset into the file we want to find. coffs - offset counter. */ down(&(ods2vari->sem)); if (offs > 65535) { coffs = offs; if ((coffs >> 16) > (ods2vari->highidx >> 16)) { coffs = ods2vari->highidx; } coffs += 65536; do { coffs -= 65536; idxblock = IDXBLOCK(coffs); ods2varp = ods2vari->ods2varp[IDXVARI(coffs)]; for (; idxblock > 0; idxblock--) { ods2varp = ods2varp->nxt; } } while (coffs > 65535 && ods2varp->s1[IDXVAR(coffs)].loff > offs); if (coffs > 65535) { currec = ods2varp->s1[IDXVAR(coffs)].recoffs; coffs = ods2varp->s1[IDXVAR(coffs)].loff; } else { coffs = 0; } } while (1) { do { vbn = currec >> 9; if (!(getfilebh(filp, vbn + 1))) { ods2filep->reclen = 65535; up(&(ods2vari->sem)); filp->f_pos = coffs; filp->f_reada = 0; filp->f_version++; return offs; } reclen = le16_to_cpu(*((u16 *)((char *)ods2filep->data + (currec & 511)))); if ((coffs >> 16) != 0) { update_virtual_file_pos(coffs, ods2vari, currec); } if ((reclen == 65535 && !(fatp->fat_b_rattrib & FAT_M_NOSPAN)) || currec > inode->i_size) { /* end of records */ ods2filep->reclen = 65535; up(&(ods2vari->sem)); filp->f_pos = coffs; filp->f_reada = 0; filp->f_version++; return offs; } if (reclen == 65535 && (fatp->fat_b_rattrib & FAT_M_NOSPAN)) { currec = (vbn + 1) * 512; /* next block... */ } } while (reclen == 65535); if (coffs <= offs && (coffs + reclen - (fatp->u0.s0.fat_v_rtype == FAT_C_VFC ? fatp->fat_b_vfcsize : 0)) >= offs) { /* we have found our location */ ods2filep->currec = currec; ods2filep->curbyte = (offs - coffs) + 2 + (fatp->u0.s0.fat_v_rtype == FAT_C_VFC ? fatp->fat_b_vfcsize : 0); ods2filep->reclen = reclen; up(&(ods2vari->sem)); filp->f_pos = coffs; filp->f_reada = 0; filp->f_version++; return offs; } coffs += (reclen - (fatp->u0.s0.fat_v_rtype == FAT_C_VFC ? fatp->fat_b_vfcsize : 0)); if (fatp->fat_b_rattrib & FAT_M_FORTRANCC || fatp->fat_b_rattrib & FAT_M_IMPLIEDCC || fatp->fat_b_rattrib & FAT_M_PRINTCC) { coffs++; /* need to add one byte for LF */ } currec = ((currec + reclen + 1) & ~1) + 2; /* all records are even aligned */ } } loff_t ods2_llseek(struct file *filp, loff_t loff, int seek) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; FATDEF *fatp = (FATDEF *)&(ods2fhp->fat); if (ods2p->flags.v_raw || ods2filep->u1.s1.v_raw) { return ods2_llseek_stream(filp, loff, seek); } else { switch (fatp->u0.s0.fat_v_fileorg) { case FAT_C_SEQUANTIAL: { switch (fatp->u0.s0.fat_v_rtype) { case FAT_C_VFC: case FAT_C_VARIABLE: return ods2_llseek_variable(filp, loff, seek); case FAT_C_FIXED: case FAT_C_STREAMLF: case FAT_C_STREAMCR: case FAT_C_STREAM: return ods2_llseek_stream(filp, loff, seek); default: return loff; } } default: return loff; } } } int ods2_open_release(struct inode *inode, struct file *filp) { if (filp->private_data == NULL) { if ((filp->private_data = kmalloc(sizeof(ODS2FILE), GFP_KERNEL)) != NULL) { memset(filp->private_data, 0, sizeof(ODS2FILE)); } else { printk("ODS2-fs kmalloc failed for open_release\n"); return 0; } } else { ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; if (ods2filep != NULL) { brelse(ods2filep->bhp); kfree(filp->private_data); } filp->private_data = NULL; } return 0; } /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ --- NEW FILE: util.c --- /* * linux/fs/ods2/util.c * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * Written 2003 by Jonas Lindholm <jl...@us...> * */ #include <linux/config.h> /* #include <linux/module.h> */ #include <linux/string.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/locks.h> #include <linux/blkdev.h> #include <asm/uaccess.h> #include "ods2.h" #include "tparse.h" u64 div64(u64 a, u32 b0) { u32 a1, a2; u32 long res; a1 = ((u32 *)&a)[0]; a2 = ((u32 *)&a)[1]; res = a1/b0 + (u64)a2 * (u64)(0xffffffff/b0) + a2 / b0 + (a2 * (0xffffffff % b0)) / b0; return res; } u32 vbn2lbn(struct super_block *sb, ODS2MAP *map, u32 vbn) { int idx = 0; u32 curvbn = 1; /* VBN is 1 based - not 0 */ while (map && map->s1[idx].cnt > 0 && curvbn < vbn && curvbn + map->s1[idx].cnt <= vbn) { curvbn += map->s1[idx].cnt; if (++idx > 15) { map = map->nxt; idx = 0; } } if (map && map->s1[idx].cnt > 0) { return map->s1[idx].lbn + (vbn - curvbn); } return 0; } u32 ino2fhlbn(struct super_block *sb, u32 ino) { ODS2SB *ods2p = (ODS2SB *)sb->u.generic_sbp; if (ino < 17) { /* the first 16 file headers are located at known locations in INDEXF.SYS */ return le16_to_cpu(ods2p->hm2.hm2_w_ibmapsize) + le32_to_cpu(ods2p->hm2.hm2_l_ibmaplbn) + ino - 1; } else { ODS2FH *ods2fhp = (ODS2FH *)ods2p->indexf->u.generic_ip; return vbn2lbn(sb, ods2fhp->map, le16_to_cpu(ods2p->hm2.hm2_w_cluster) * 4 + le16_to_cpu(ods2p->hm2.hm2_w_ibmapsize) + ino); } return 0; } /* This function retreives all file mapping pointers and create a linked list so VBN's can be translated to LBN's. Note that this routine read ALL mapping pointers thus creating a catedral window for the file. Should there be extension headers they are all read directly not using iget to fetch them. */ ODS2MAP *getmap(struct super_block *sb, FH2DEF *fh2p) { FM2DEF *fm2p = (FM2DEF *)((short unsigned *)fh2p + fh2p->fh2_b_mpoffset); ODS2MAP *map = kmalloc(sizeof(ODS2MAP), GFP_KERNEL); ODS2MAP *mapfst = map; struct buffer_head *bh = NULL; int idx = 0; u8 mapinuse = 0; if (map == NULL) { printk("ODS2-fs kmalloc failed for getmap (1)\n"); return NULL; } memset(map, 0, sizeof(ODS2MAP)); do { mapinuse = fh2p->fh2_b_map_inuse; while (fm2p < (FM2DEF *)((short unsigned *)fh2p + fh2p->fh2_b_acoffset) && mapinuse > 0) { u32 cnt = 0; u32 lbn = 0; u16 size = 0; switch (fm2p->u1.fm1.fm2_v_format) { case 0: size = 1; break; case 1: cnt = fm2p->u1.fm1.fm2_b_count1; lbn = (fm2p->u1.fm1.fm2_v_highlbn << 16) | fm2p->u1.fm1.fm2_w_lowlbn; size = 2; break; case 2: cnt = fm2p->u1.fm2.fm2_v_count2; lbn = (le16_to_cpu(fm2p->u1.fm2.fm2_l_lbn2[1]) << 16) | le16_to_cpu(fm2p->u1.fm2.fm2_l_lbn2[0]); size = 3; break; case 3: cnt = (fm2p->u1.fm3.fm2_v_count2 << 16) | le16_to_cpu(fm2p->u1.fm3.fm2_w_lowcount); lbn = le32_to_cpu(fm2p->u1.fm3.fm2_l_lbn3); size = 4; break; } if (fm2p->u1.fm1.fm2_v_format > 0) { if (idx > 15) { if ((map->nxt = kmalloc(sizeof(ODS2MAP), GFP_KERNEL)) != NULL) { map = map->nxt; memset(map, 0, sizeof(ODS2MAP)); idx = 0; } else { printk("ODS2-fs kmalloc failed for getmap (2)\n"); return map; } } map->s1[idx].cnt = cnt + 1; /* the count is always N + 1 mapped blocks */ map->s1[idx].lbn = lbn; idx++; } mapinuse -= size; fm2p = (FM2DEF *)((short unsigned *)(fm2p) + size); } /* If there is an extension header we need to read all of them because they could have additional mapping information. Note that we can not use iget to fetch the extension header because it is not a valid inode for an ODS2 file. Only primary file header can be used as an inode. */ if (fh2p->u3.s1.fid_w_ex_fidnum != 0) { u32 lbn; if ((lbn = ino2fhlbn(sb, le16_to_cpu(fh2p->u3.s1.fid_w_ex_fidnum) | (fh2p->u3.s1.fid_b_ex_fidnmx << 16))) != 0) { fh2p = NULL; brelse(bh); if ((bh = sb_bread(sb, GETBLKNO(sb, lbn))) != NULL && bh->b_data != NULL) { fh2p = (FH2DEF *)(GETBLKP(sb, lbn, bh->b_data)); fm2p = (FM2DEF *)((short unsigned *)fh2p + fh2p->fh2_b_mpoffset); } } } else { fh2p = NULL; } } while (fh2p != NULL); brelse(bh); return mapfst; } struct buffer_head *getfilebh(struct file *filp, u32 vbn) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; ODS2FH *ods2fhp = (ODS2FH *)inode->u.generic_ip; ODS2FILE *ods2filep = (ODS2FILE *)filp->private_data; if ((vbn - 1) * 512 < inode->i_size) { u32 lbn; if ((lbn = vbn2lbn(sb, ods2fhp->map, vbn)) > 0) { if (ods2filep->bhp == NULL || GETBLKNO(sb, lbn) != ods2filep->bhp->b_blocknr) { brelse(ods2filep->bhp); ods2filep->bhp = NULL; if ((ods2filep->bhp = sb_bread(sb, GETBLKNO(sb, lbn))) != NULL) { if (ods2filep->bhp->b_data != NULL) { ods2filep->data = GETBLKP(sb, lbn, ods2filep->bhp->b_data); return ods2filep->bhp; } } } else { ods2filep->data = GETBLKP(sb, lbn, ods2filep->bhp->b_data); return ods2filep->bhp; } } } return NULL; } int verify_fh(FH2DEF *fh2p, u32 ino) { u16 *p = (short unsigned *)fh2p; u16 chksum = 0; for (; p < (short unsigned *)&(fh2p->fh2_w_checksum) ; chksum += le16_to_cpu(*p++)); if (fh2p->fh2_b_idoffset <= fh2p->fh2_b_mpoffset && fh2p->fh2_b_mpoffset <= fh2p->fh2_b_acoffset && fh2p->fh2_b_acoffset <= fh2p->fh2_b_rsoffset && fh2p->u1.s1.fh2_b_structlevl == 2 && fh2p->u1.s1.fh2_b_structlevv >= 1 && fh2p->u2.s1.fh2_w_fid_num != 0 && ((fh2p->u2.s1.fh2_b_fid_nmx << 16) | le16_to_cpu(fh2p->u2.s1.fh2_w_fid_num)) == ino && fh2p->fh2_b_map_inuse <= (fh2p->fh2_b_acoffset - fh2p->fh2_b_mpoffset) && le16_to_cpu(fh2p->fh2_w_checksum) == chksum) { return 1; /* it is a valid file header */ } return 0; } int save_raw(ARGBLK *argblk) { struct super_block *sb = (void *)argblk->arg; ODS2SB *ods2p = (void *)sb->u.generic_sbp; ods2p->flags.v_raw = 1; return 1; } int save_lowercase(ARGBLK *argblk) { struct super_block *sb = (void *)argblk->arg; ODS2SB *ods2p = (void *)sb->u.generic_sbp; ods2p->flags.v_lowercase = 1; return 1; } int save_dollar(ARGBLK *argblk) { struct super_block *sb = (void *)argblk->arg; ODS2SB *ods2p = (void *)sb->u.generic_sbp; ods2p->dollar = argblk->token[0]; return 1; } int save_semicolon(ARGBLK *argblk) { struct super_block *sb = (void *)argblk->arg; ODS2SB *ods2p = (void *)sb->u.generic_sbp; ods2p->semicolon = argblk->token[0]; return 1; } int save_version(ARGBLK *argblk) { struct super_block *sb = (void *)argblk->arg; ODS2SB *ods2p = (void *)sb->u.generic_sbp; ods2p->flags.v_version = argblk->mask; return 1; } TPARSE tpa1[]; TPARSE tpa10[]; TPARSE tpa11[]; TPARSE tpa20[]; TPARSE tpa21[]; TPARSE tpa30[]; TPARSE tpa31[]; TPARSE tpa1[] = { { "dollar", tpa10, NULL, 0, NULL, 0 }, { "version", tpa20, NULL, 0, NULL, 0 }, { "semicolon", tpa30, NULL, 0, NULL, 0 }, { "lowercase", tpa1, save_lowercase, 0, NULL, 0 }, { "raw", tpa1, save_raw, 0, NULL, 0 }, { TPA_EOS, TPA_EXIT, NULL, 0, NULL, 0 }, TPA_END }; TPARSE tpa10[] = { { "=", tpa11, NULL, 0, NULL, 0 }, TPA_END }; TPARSE tpa11[] = { { TPA_ANY, tpa1, save_dollar, 0, NULL, 0 }, TPA_END }; TPARSE tpa20[] = { { "=", tpa21, NULL, 0, NULL, 0 }, TPA_END }; TPARSE tpa21[] = { { "all", tpa1, save_version, SB_M_VERSALL, NULL, 0 }, { "highest", tpa1, save_version, SB_M_VERSHIGH, NULL, 0 }, { "none", tpa1, save_version, SB_M_VERSNONE, NULL, 0 }, TPA_END }; TPARSE tpa30[] = { { "=", tpa31, NULL, 0, NULL, 0 }, TPA_END }; TPARSE tpa31[] = { { TPA_ANY, tpa1, save_semicolon, 0, NULL, 0 }, TPA_END }; int parse_options(struct super_block *sb, char *options) { ARGBLK argblk; argblk.str = options; argblk.arg = (long unsigned)sb; return tparse(&argblk, tpa1); } /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ --- NEW FILE: tparse.c --- /* * linux/fs/ods2/file.c * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * Written 2003 by Jonas Lindholm <jl...@us...> * */ #include <linux/string.h> #include <linux/ctype.h> #include "tparse.h" int tparse(ARGBLK *argblk, TPARSE *tpa) { int idx; int found; while (1) { char *str = argblk->str; while (*str && (*str == ' ' || *str == '\t')) { str++; }; found = 0; idx = -1; argblk->number = 0; argblk->str = str; argblk->token = str; do { str = argblk->str; idx++; switch ((long unsigned)tpa[idx].type) { case lu(TPA_ANY): { if (*str) { str++; found = 1; } break; } case lu(TPA_ALPHA): { if (*str && ((*str >= 'A' && *str <= 'Z') || (*str >= 'a' && *str <= 'z'))) { str++; found = 1; } break; } case lu(TPA_DIGIT): { if (*str && (*str >= '0' && *str <= '9')) { argblk->number = *str++ - '0'; found = 1; } break; } case lu(TPA_HEX): { while (*str && ((*str >= '0' && *str <= '9') || (tolower(*str) >= 'a' && tolower(*str) <= 'f'))) { argblk->number = (argblk->number * 16) + (*str <= '9' ? *str - '0' : tolower(*str) - 'a' + 10); str++; } found = 1; break; } case lu(TPA_OCTAL): { while (*str && (*str >= '0' && *str <= '7')) { argblk->number = argblk->number * 8 + (*str++ - '0'); } found = 1; break; } case lu(TPA_DECIMAL): { while (*str && (*str >= '0' && *str <= '9')) { argblk->number = argblk->number * 10 + (*str++ - '0'); } found = 1; break; } case lu(TPA_STRING): { while (*str && ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'Z') || (*str >= 'a' && *str <= 'z'))) { str++; } found = 1; break; } case lu(TPA_SYMBOL): { while (*str && ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'Z') || (*str >= 'a' && *str <= 'z') || *str == '_' || *str == '$')) { str++; } found = 1; break; } case lu(TPA_EOS): { found = !*str; break; } case lu(TPA_LAMBDA): { found = 1; } case 0: /* no more entries in table */ break; default: { if (((void **)tpa[idx].type)[0] == (void *)244) { TPARSE *tmptpa = ((TPARSE **)tpa[idx].type)[1]; ARGBLK tmpargblk; tmpargblk.options = argblk->options; tmpargblk.arg = argblk->arg; tmpargblk.str = str; tmpargblk.token = NULL; tmpargblk.number = 0; tmpargblk.param = tpa[idx].param; if ((found = tparse(&tmpargblk, tmptpa))) { str = tmpargblk.str; } break; } else { if (strlen(tpa[idx].type) == 1) { if (*str++ == *tpa[idx].type) { found = 1; } } else { while (*str && ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'Z') || (*str >= 'a' && *str <= 'z') || *str == '_' || *str == '$')) { str++; } if (strlen(tpa[idx].type) == (str - argblk->token) && strncmp(argblk->token, tpa[idx].type, (str - argblk->token)) == 0) { found = 1; } } } } } if (found) { if (tpa[idx].action != NULL) { char tmp = *str; *str = 0; argblk->param = tpa[idx].param; argblk->mask = tpa[idx].mask; argblk->mskadr = tpa[idx].mskadr; found = tpa[idx].action(argblk); *str = tmp; } argblk->str = str; argblk->token = str; } } while (!found && tpa[idx].type != NULL); if (found) { if (tpa[idx].mskadr != NULL) { *tpa[idx].mskadr |= tpa[idx].mask; } if ((void *)tpa[idx].label == (void *)0) { return 0; } if ((void *)tpa[idx].label == (void *)1) { return 1; } tpa = tpa[idx].label; } else { return 0; } } } --- NEW FILE: README --- Overview ======== The ODS2 driver for Linux version 0.9.2 support read of ODS2 formated file systems such as SCSI disks, CDROM's, container disks that have been formatted using OpenVMS. Currently only stream and variable record files are supported. Limitations and workarounds =========================== The nature of variable record files make it hard for utilities like less to find the correct end. The less utility assume that the file size is the true end of the last byte of data in the file but for a variable record file the file size also include bytes not to be displayed. The major problem is that if you use less and then hit the End key less will try to go beyond the end of the virtual position. A simple way around this is to use cat on the file and pipe that to less. By this method you will go to end of file when you hit the End key for less. Utilities like less use llseek to find its positions in the file. Without any workaround less would ending up in the wrong position in the file. To solve the problem the ODS2 driver keep track of the virtual position in the file (the position less knows about) and create checkpoints every 64K position. By these checkpoints function llseek can find its position by searching from the closest position instead reading all records from start. License ======= The ODS2 source code is released under GNU General Public License (GPL). Author ====== The ODS2 driver is written by me, Jonas Lindholm, and I was doing it for fun and to get deeper knowledge of file systems on Linux. It is also a challange to get it to work and it seems that people are looking for a ODS filoe system module for Linux. Later versions will support write as well. --- NEW FILE: CHANGES --- Changes from version 0.9.2 to version 0.9.3 =========================================== - Added code to handle hard sector size of 1024 and 2048 bytes. - Fix bug when checking RMS file type and record type. - Files of type FIXED will now be treated as STREAM files. Changes from version 0.9.1 to version 0.9.2 =========================================== - Added check of hard sector size before trying to mount. Current version of this driver only support 512 byte sectors. - Fix bug in ods2_llseek_variable. The fixed record size was not removed from variable coffs giving wrong offsets. Another bug was that the VFC value was not taken into account when testing if the location was found. Yet another bug was the check for the end of file for variable record files. - Updated all definitions of vbn and lbn to use u32. - Updated all definitions of currec to u64. - Updated all structures to use u8, u16 and u32 so it will work on 64 bits architectures such as Alpha. Changes from version 0.9.0 to version 0.9.1 =========================================== - This is a special version for kernel 2.2.X - Changed all long unsigned in structures to u32. --- NEW FILE: Makefile.i386 --- TARGET = ods2.o OBJECTS = super.o inode.o file.o dir.o util.o tparse.o INCLUDE = ods2.h tparse.h DEBUG = -g OPTIMIZE= CFLAGS = -D__KERNEL__ -I/usr/src/linux-2.4.20/include -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer -pipe -mpreferred-stack-boundary=2 -march=i686 -DMODULE -DMODVERSIONS -include /usr/src/linux-2.4.20/include/linux/modversions.h -nostdinc -iwithprefix include $(TARGET):... [truncated message content] |