|
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... [truncated message content] |