Documentation/filesystems/cmsfs.txt | 64 ++++++ MAINTAINERS | 5 + fs/Kconfig | 8 + fs/Makefile | 1 + fs/cmsfs/Makefile | 3 + fs/cmsfs/bmap.c | 104 ++++++++++ fs/cmsfs/cmsfs.h | 260 ++++++++++++++++++++++++ fs/cmsfs/ebcdic.c | 161 +++++++++++++++ fs/cmsfs/file.c | 204 +++++++++++++++++++ fs/cmsfs/helpers.c | 78 +++++++ fs/cmsfs/inode.c | 269 +++++++++++++++++++++++++ fs/cmsfs/super.c | 379 +++++++++++++++++++++++++++++++++++ fs/cmsfs/xattr.c | 74 +++++++ include/linux/magic.h | 1 + 14 files changed, 1611 insertions(+), 0 deletions(-) create mode 100644 Documentation/filesystems/cmsfs.txt create mode 100644 fs/cmsfs/Makefile create mode 100644 fs/cmsfs/bmap.c create mode 100644 fs/cmsfs/cmsfs.h create mode 100644 fs/cmsfs/ebcdic.c create mode 100644 fs/cmsfs/file.c create mode 100644 fs/cmsfs/helpers.c create mode 100644 fs/cmsfs/inode.c create mode 100644 fs/cmsfs/super.c create mode 100644 fs/cmsfs/xattr.c diff --git a/Documentation/filesystems/cmsfs.txt b/Documentation/filesystems/cmsfs.txt new file mode 100644 index 0000000..0aba00c --- /dev/null +++ b/Documentation/filesystems/cmsfs.txt @@ -0,0 +1,64 @@ +CMS Minidisk Filesystem +======================= + + + + +Mount options +============= + +(none) + + +Extended attributes +=================== + +While the filesystem itself doesn't support extended attributes, the xattr +interface can be used to extract additional information about each file. + + system.items: number of records in this file + system.lrecl: record length + system.recfm: record format + + +Specification +============= + + + + +Credits +======= + +This implementation has been based on a cmsfs patch from 2.4.12-ac6 by Rick +Troth . + +CMS minidisk (EDF) filesystem specifications taken from +http://www.vm.ibm.com/pubs/cmsdacb/FSTD.HTML, +http://www.vm.ibm.com/pubs/cmsdacb/ADTSECT.HTML, +and the Linux kernel patches supplied by IBM (mdisk driver). + + +Credits from the 2.4.12-ac6 patch +================================= + +Thanks to Dave Hilbe for supporting this project. + +Thanks to Arty Ecock for pointing me +back at ADT after I had misunderstood it in my first look. + +Thanks to Rob van der Heij for help with the logic. + +Thanks to Neale Ferguson for "cpint". + +Thanks to Willem Konynenberg +for moral support and championing correct DASD I/O. + +Thanks to Alan Cox for help figuring out Linux VFS. + +Thanks to Reed Mullen for clarifying permission to use doc. + +Thanks to Marilyn Troth for advice about packaging, helping +me stay focused, and for putting up with all the late nights. + +... and many others whom I am sorry to have omitted. diff --git a/MAINTAINERS b/MAINTAINERS index 663485b..90a2fbd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1187,6 +1187,11 @@ P: Nils Faerber (port to kernel 2.4) M: Nils Faerber S: Maintained +CMSFS FILE SYSTEM +P: Josef 'Jeff' Sipek +M: jeffpc@josefsipek.net +S: Maintained + CODA FILE SYSTEM P: Jan Harkes M: jaharkes@cs.cmu.edu diff --git a/fs/Kconfig b/fs/Kconfig index d387358..de25ab0 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1033,6 +1033,14 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. +config CMS_FS + tristate "CMS minidisk filesystem support (EXPERIMENTAL)" + depends on BLOCK && EXPERIMENTAL + help + Support for z/VM CMS minidisk filesystem format. + + If unsure, say N. + config ECRYPT_FS tristate "eCrypt filesystem layer support (EXPERIMENTAL)" depends on EXPERIMENTAL && KEYS && CRYPTO && NET diff --git a/fs/Makefile b/fs/Makefile index a1482a5..3aa1749 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_EXT2_FS) += ext2/ obj-$(CONFIG_CRAMFS) += cramfs/ obj-y += ramfs/ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ +obj-$(CONFIG_CMS_FS) += cmsfs/ obj-$(CONFIG_CODA_FS) += coda/ obj-$(CONFIG_MINIX_FS) += minix/ obj-$(CONFIG_FAT_FS) += fat/ diff --git a/fs/cmsfs/Makefile b/fs/cmsfs/Makefile new file mode 100644 index 0000000..bb6f5ba --- /dev/null +++ b/fs/cmsfs/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_CMS_FS) += cmsfs.o + +cmsfs-y += super.o inode.o file.o bmap.o helpers.o ebcdic.o xattr.o diff --git a/fs/cmsfs/bmap.c b/fs/cmsfs/bmap.c new file mode 100644 index 0000000..ce9eccf --- /dev/null +++ b/fs/cmsfs/bmap.c @@ -0,0 +1,104 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include +#include +#include +#include +#include + +#include "cmsfs.h" + +/** + * cmsfs_get_blocks - map a inode offset to a disk block + * @inode: inode to map + * @iblock: inode offset to map (in fs blocks) + * @bh_result: resulting buffer_head + * @create: ? + */ +static int cmsfs_get_blocks(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct cmsfs_inode_info *cinode = CMSFS_I(inode); + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + + BUG_ON(create); + + /* indirection? */ + if (cinode->level != 0) { + int ptrs_per_blk; + int i, lvl; + void *bp; + u32 b1, b2; + + b2 = ~0; /* to avoid potentially uninit var warning */ + + /* pointers per block */ + if (cinode->psize > 0) + ptrs_per_blk = CMSFS_SB(sb)->blksz / cinode->psize; + else + ptrs_per_blk = CMSFS_SB(sb)->blksz / 4; + + /* read first block for indirection */ + b1 = iblock; + for (i = 0; i < cinode->level; i++) + b1 = b1 / ptrs_per_blk; + bh = sb_bread(sb, cinode->origin - 1 + b1); + if (!bh) + goto fail; + + for (lvl = 1; lvl <= cinode->level; lvl++) { + /* read next block for indirection */ + b1 = iblock; + for (i = lvl; i < cinode->level; i++) + b1 = b1 / ptrs_per_blk; + b1 = b1 % ptrs_per_blk; + bp = bh->b_data; + bp += b1 * cinode->psize; + b2 = be32_to_cpu(*(u32*)bp); + + brelse(bh); + + /* read next block for indirection */ + bh = sb_bread(sb, b2 - 1); + if (!bh) + goto fail; + } + + clear_buffer_new(bh_result); + map_bh(bh_result, inode->i_sb, b2-1); + } else { + clear_buffer_new(bh_result); + map_bh(bh_result, inode->i_sb, + cinode->origin - 1 + iblock); + } + + return 0; + +fail: + return -EIO; +} + +/** + * cmsfs_readpage - read a page for an inode + * @unused: (unused) + * @page: page to read + */ +static int cmsfs_readpage(struct file *unused, struct page *page) +{ + return mpage_readpage(page, cmsfs_get_blocks); +} + +struct address_space_operations cmsfs_aops = { + .readpage = cmsfs_readpage, +}; diff --git a/fs/cmsfs/cmsfs.h b/fs/cmsfs/cmsfs.h new file mode 100644 index 0000000..ab69e32 --- /dev/null +++ b/fs/cmsfs/cmsfs.h @@ -0,0 +1,260 @@ +#ifndef _CMSFS_H +#define _CMSFS_H +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +/******************/ +/* FIXME: remove */ +extern __u8 _ascebc[256]; /* ASCII -> EBCDIC conversion table */ +extern __u8 _ebcasc[256]; /* EBCDIC -> ASCII conversion table */ + +static inline void ebc_trans(__u8 *table, __u8 *addr, int len) +{ + for(; len; len--) + addr[len-1] = table[addr[len-1]]; +} + +#define EBC2ASC(addr,len) ebc_trans(_ebcasc, (addr), (len)) +#define ASC2EBC(addr,len) ebc_trans(_ascebc, (addr), (len)) +/******************/ + +#define CMSFS_FIRST_INO 8 +#define CMSFS_DIRECTOR_INO CMSFS_FIRST_INO +#define CMSFS_ALLOCMAP_INO (CMSFS_DIRECTOR_INO + 1) + +/* filename (up to 8) + period + filetype (up to 8) */ +#define CMSFS_NAME_LEN 17 + +/* Record format */ +enum recfm { + RECFM_FIXED = 0xc6, /* EBCDIC 'F' */ + RECFM_VAR = 0xe5, /* EBCDIC 'V' */ +}; + +/* + * private data structs + */ +struct cmsfs_inode_info { + struct inode vfs_inode; /* Linux VFS inode */ + + enum recfm recfm; + u32 lrecl; + u32 origin; /* "base one" File Origin Pointer (FOP) */ + /* "base zero" offset is derived from FOP */ + u32 blocks; /* blocks in first-level */ + /* If there is only one level, then + this is the total number of blocks */ + u32 items; /* number of records in this file */ + u32 level; /* level of indirection */ + u32 psize; /* size of indir pointers */ + + time_t ctime; /* computed UNIX time from CMS timestamp */ +}; + +struct cmsfs_sb_info { + char volid[7]; /* volume label (volume serial number) */ + long blksz; /* statfs f_bsize block size of this volume */ + int origin; /* "base one" Directory Origin Pointer (DOP) */ + /* "base zero" offset is derived from DOP */ + int ncyls; /* number of cylinders used by filesystem */ + int mcyls; /* number of cylinders on the disk */ + /* CMS has a concept of "recomp" where + cylinders beyond the filesystem may be + used by other things, like a boot loader. */ + long blocks; /* statfs f_blocks: total number of blocks + in the filesystem */ + long bkused; /* blocks used (per ADT) */ + int fstsz; /* size of each directory entry (of each FST) */ + int fstct; /* number of FSTs (FST count) per block */ + time_t ctime; /* time when this filesystem was created */ + int resoff; /* reserved offset */ + /* CMS has a concept of a "reserved disk" + where one file occupies the entire disk. + Additionally, that file is referenced by this + so that the filesystem (directory) mechanism + can be bypassed. */ + long files; /* statfs f_files: extracted from directory + FST "items" */ +}; + +/* + * Function prototypes + */ +extern int cmsfs_read_block(struct super_block *sb, void *buf, int block, + unsigned blocksize); +extern struct inode *cmsfs_iget(struct super_block *sb, unsigned long ino); +extern void munge_name(__u8 *name, char *outname); +extern ssize_t cmsfs_getxattr(struct dentry *dentry, const char *name, + void *value, size_t size); +extern int cmsfs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags); +extern int cmsfs_removexattr(struct dentry *dentry, const char *name); +extern ssize_t cmsfs_listxattr(struct dentry *dentry, char *list, size_t size); + +/* + * Static inlines + */ +static inline struct cmsfs_sb_info *CMSFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct cmsfs_inode_info *CMSFS_I(struct inode *inode) +{ + return container_of(inode, struct cmsfs_inode_info, vfs_inode); +} + +static inline int hex2int(__u8 c) +{ + return ((c >> 4) * 10) + (c & 0xf); +} + +extern struct file_operations cmsfs_file_ops; +extern struct address_space_operations cmsfs_aops; + +/* + * On disk structures + */ + +/* + * File Status Table (FST) + * + * This information is based on VM/ESA 2.4.0. + * Last updated on 12 Aug 1999 at 11:21:05 EDT. + * Copyright IBM Corporation, 1990, 1999 + * http://www.vm.ibm.com/pubs/cmsdacb/FSTD.HTML + * + * + * +-------------------------------------------------------+ + * 0 | FSTFNAME | + * +-------------------------------------------------------+ + * 8 | FSTFTYPE | + * +-------------+-------------+-------------+-------------+ + * 10 | FSTDATEW | FSTTIMEW | FSTWRPNT | FSTRDPNT | + * +-------------+-------------+-------------+------+------+ + * 18 | FSTFMODE | FSTRECCT | FSTFCLPT |:RECFM|:FLAGS| + * +-------------+-------------+-------------+------+------+ + * 20 | FSTLRECL | FSTBLKCT | FSTYEARW | + * +---------------------------+-------------+-------------+ + * 28 | FSTFOP | FSTADBC | + * +---------------------------+------+------+-------------+ + * 30 | FSTAIC |:NLVL |:PTRSZ| (036)- | + * +---------------------------+------+------+-------------+ + * 38 | -FSTADATI |:REALM|:FLAG2|/////////////| + * +---------------------------+------+------+-------------+ + * 40 + * + */ +struct CMSFSFST { + __u8 FSTFNAME[8]; /* filename */ + __u8 FSTFTYPE[8]; /* filetype */ + __u8 FSTDATEW[2]; /* DATE LAST WRITTEN - MMDD */ + __u8 FSTTIMEW[2]; /* TIME LAST WRITTEN - HHMM */ + __be16 FSTWRPNT; /* WRITE POINTER - ITEM NUMBER */ + __be16 FSTRDPNT; /* READ POINTER - ITEM NUMBER */ + __u8 FSTFMODE[2]; /* FILE MODE - LETTER AND NUMBER */ + __be16 FSTRECCT; /* NUMBER OF LOGICAL RECORDS */ + __be16 FSTFCLPT; /* FIRST CHAIN LINK POINTER */ + __u8 FSTRECFM; /* F*1 - RECORD FORMAT - F OR V */ +#define FSTDFIX 0xC6 /* Fixed record format (EBCDIC 'F') */ +#define FSTDVAR 0xE5 /* Variable record format (EBCDIC 'V') */ + __u8 FSTFLAGS; /* F*2 - FST FLAG BYTE */ +#define FSTRWDSK 0x80 /* READ/WRITE DISK */ +#define FSTRODSK 0x00 /* READ/ONLY DISK */ +#define FSTDSFS 0x10 /* Shared File FST */ +#define FSTXRDSK 0x40 /* EXTENSION OF R/O DISK */ +#define FSTXWDSK 0xC0 /* EXTENSION OF R/W DISK */ +#define FSTEPL 0x20 /* EXTENDED PLIST */ +#define FSTDIA 0x40 /* ITEM AVAILABLE */ +#define FSTDRA 0x01 /* PREVIOUS RECORD NULL */ +#define FSTCNTRY 0x08 /* Century for date last written (0=19, 1=20),\\ + corresponds to FSTYEARW, FSTADATI. */ +#define FSTACTRD 0x04 /* ACTIVE FOR READING */ +#define FSTACTWR 0x02 /* ACTIVE FOR WRITING */ +#define FSTACTPT 0x01 /* ACTIVE FROM A POINT */ +#define FSTFILEA 0x07 /* THE FILE IS ACTIVE */ + __be32 FSTLRECL; /* LOGICAL RECORD LENGTH */ + __be16 FSTBLKCT; /* NUMBER OF 800 BYTE BLOCKS */ + __be16 FSTYEARW; /* YEAR LAST WRITTEN */ + __be32 FSTFOP; /* ALT. FILE ORIGIN POINTER */ + __be32 FSTADBC; /* ALT. NUMBER OF DATA BLOCKS */ + __be32 FSTAIC; /* ALT. ITEM COUNT */ + __u8 FSTNLVL; /* NUMBER OF POINTER BLOCK LEVELS */ + __u8 FSTPTRSZ; /* LENGTH OF A POINTER ELEMENT */ + __u8 FSTADATI[6]; /* ALT. DATE/TIME(YY MM DD HH MM SS) */ + __u8 FSTREALM; /* Real filemode */ + __u8 FSTFLAG2; /* F*3 - FST FLAG BYTE 2 FSTFLAG2 */ +#define FSTPIPEU 0x10 /* Reserved for CMS PIPELINES usage */ + __u8 reserved[2]; +}; + +/* + * Active Disk Table (ADT) volume label + * + * This information is based on VM/ESA 2.4.0. + * Last updated on 12 Aug 1999 at 11:17:01 EDT. + * Copyright IBM Corporation, 1990, 1999 + * http://www.vm.ibm.com/pubs/cmsdacb/ADTSECT.HTML + * + * + * The volume portion of ADT beings at offset 0x90 into the actual ADT + * + * +---------------------------+---------------------------+ + * 90 | ADTIDENT | ADTID- | + * +-------------+-------------+---------------------------+ + * 98 | -(094) | ADTVER | ADTDBSIZ | + * +-------------+-------------+---------------------------+ + * A0 | ADTDOP | ADTCYL | + * +---------------------------+---------------------------+ + * A8 | ADTMCYL | ADTNUM | + * +---------------------------+---------------------------+ + * B0 | ADTUSED | ADTFSTSZ | + * +---------------------------+---------------------------+ + * B8 | ADTNFST | ADTDCRED- | + * +-------------+------+------+---------------------------+ + * C0 | -(0BC) |:FLGL |//////| ADTOFFST | + * +-------------+------+------+---------------------------+ + * C8 | ADTAMNB | ADTAMND | + * +---------------------------+---------------------------+ + * D0 | ADTAMUP | ADTOFCNT | + * +---------------------------+---------------------------+ + * D8 | ADTSFNAM | + * +-------------------------------------------------------+ + * E0 + * + */ +struct CMSFSADT { + __be32 ADTIDENT; /* VOL START / LABEL IDENTIFIER */ + __u8 ADTID[6]; /* VOL START / VOL IDENTIFIER */ + __u8 ADTVER[2]; /* VERSION LEVEL */ + __be32 ADTDBSIZ; /* DISK BLOCK SIZE */ + __be32 ADTDOP; /* DISK ORIGIN POINTER */ + __be32 ADTCYL; /* NUM OF FORMATTED CYL ON DISK */ + __be32 ADTMCYL; /* MAX NUM FORMATTED CYL ON DISK */ + __be32 ADTNUM; /* Number of Blocks on disk */ + __be32 ADTUSED; /* Number of Blocks used */ + __be32 ADTFSTSZ; /* SIZE OF FST */ + __be32 ADTNFST; /* NUMBER OF FST'S PER BLOCK */ + __u8 ADTDCRED[6]; /* DISK CREATION DATE (YYMMDDHHMMSS) */ + __u8 ADTFLGL; /* LABEL FLAG BYTE (ADTFLGL) */ +#define ADTCNTRY 0x01 /* Century for disk creation date (0=19, 1=20),\\ + * corresponds to ADTDCRED. */ + __u8 reserved[1]; + __be32 ADTOFFST; /* DISK OFFSET WHEN RESERVED */ + __be32 ADTAMNB; /* ALLOC MAP BLOCK WITH NEXT HOLE */ + __be32 ADTAMND; /* DISP INTO HBLK DATA OF NEXT HOLE */ + __be32 ADTAMUP; /* DISP INTO USER PART OF ALLOC MAP */ + __be32 ADTOFCNT; /* Count of SFS open files for this ADT */ + __u8 ADTSFNAM[8]; /* NAME OF SHARED SEGMENT */ +}; + +#endif diff --git a/fs/cmsfs/ebcdic.c b/fs/cmsfs/ebcdic.c new file mode 100644 index 0000000..6aeed45 --- /dev/null +++ b/fs/cmsfs/ebcdic.c @@ -0,0 +1,161 @@ +/* + * arch/s390/kernel/ebcdic.c + * ECBDIC -> ASCII, ASCII -> ECBDIC, + * upper to lower case (EBCDIC) conversion tables. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky + * Martin Peschke + */ + +#include +#include + +/* + * ASCII (IBM PC 437) -> EBCDIC 037 + */ +__u8 _ascebc[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +/* + * EBCDIC 037 -> ASCII (IBM PC 437) + */ +__u8 _ebcasc[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, + /* 0x60 - / ---- ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ^ ---- ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; diff --git a/fs/cmsfs/file.c b/fs/cmsfs/file.c new file mode 100644 index 0000000..26f23f8 --- /dev/null +++ b/fs/cmsfs/file.c @@ -0,0 +1,204 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include +#include +#include +#include + +#include "cmsfs.h" + +/** + * __read_recfm_fixed - read len bytes from file position pos + * @inode: inode to read from + * @buf: user buffer pointer to copy data to + * @len: length of the buffer + * @pos: position into the inode + */ +static ssize_t __read_recfm_fixed(struct inode *inode, char __user *buf, + size_t len, loff_t pos) +{ + struct cmsfs_inode_info *ci = CMSFS_I(inode); + struct super_block *sb = inode->i_sb; + struct page *page; + unsigned char *kaddr; + u32 blkoff; + size_t err = -EIO; + + BUG_ON(ci->recfm != RECFM_FIXED); + BUG_ON(sb->s_blocksize > PAGE_SIZE); + + page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT, NULL); + if (IS_ERR(page)) + return PTR_ERR(page); + + kaddr = kmap(page); + if (PageError(page)) + goto out; + + blkoff = pos & ~PAGE_MASK; + + /* won't read more than a page */ + len = min(len, PAGE_SIZE - blkoff); + + err = copy_to_user(buf, kaddr + blkoff, len); + if (!err) + err = len; + +out: + kunmap(page); + page_cache_release(page); + return err; +} + +/** + * cmsfs_file_read - read from a file + * @file: file to read from + * @buf: user buffer pointer for data + * @len: length of the buffer + * @ppos: pointer to the file offset to read at + */ +static ssize_t cmsfs_file_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + ssize_t ret; + + if ((*ppos < inode->i_size) && (*ppos + len > inode->i_size)) + len = inode->i_size - *ppos; + else if (*ppos >= inode->i_size) + return 0; + + switch (CMSFS_I(inode)->recfm) { + case RECFM_FIXED: + ret = __read_recfm_fixed(inode, buf, len, *ppos); + break; + case RECFM_VAR: + default: + ret = -EINVAL; + break; + } + + if (ret > 0) + *ppos = *ppos + ret; + + return ret; +} + +/** + * cmsfs_readdir - readdir + * @file: file to readdir + * @dirent: ? + * @filldir: readdir filler function ptr + */ +static int cmsfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct cmsfs_inode_info *cinode = CMSFS_I(inode); + struct CMSFSFST *fst; + struct page *page; + char outname[18]; + int i; + int err = 0; + + if (inode->i_ino != CMSFS_DIRECTOR_INO) + return -ENOTDIR; + + /* FIXME: this should go away, I think */ + if (file->f_pos != 0) + return 0; + + page = NULL; + fst = NULL; + file->f_pos = 0; + + for (i = 0; i < cinode->items; i++, fst++) { + if ((i * cinode->lrecl) % CMSFS_SB(sb)->blksz == 0) { + if (i) { + kunmap(page); + page_cache_release(page); + } + + page = read_mapping_page(inode->i_mapping, + (i * cinode->lrecl) / + CMSFS_SB(sb)->blksz, + NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + + fst = kmap(page); + if (PageError(page)) { + err = -EIO; + goto out_release; + } + } + + /* + * skipping .DIRECTOR and .ALLOCMAP, but using the dirent + * space for the '.' and '..' entries + */ + if (i < 2) { + err = filldir(dirent, i ? ".." : ".", + i ? 2 : 1, file->f_pos, + CMSFS_DIRECTOR_INO, + DT_DIR); + if (err) + break; + continue; + } + + munge_name(fst->FSTFNAME, outname); + + /* No subdirs so all files are regular */ + err = filldir(dirent, outname, strlen(outname), file->f_pos, + i + CMSFS_FIRST_INO, DT_REG); + if (err) + break; + + /* set the file possition */ + file->f_pos += cinode->lrecl; + } + + kunmap(page); +out_release: + page_cache_release(page); +out: + return err; +} + +/** + * cmsfs_file_open - open routine + * @inode: inode to read + * @file: file ptr to be associated with the inode + * + * NOTE: The only reason this exists is because it's a good place to prevent + * RECFM_VAR files being open, and later read. Once that's supported, this + * function should go away. + */ +static int cmsfs_file_open(struct inode *inode, struct file *file) +{ + if (CMSFS_I(inode)->recfm != RECFM_FIXED) { + printk("cmsfs: cannot open files with recfm != fixed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +struct file_operations cmsfs_file_ops = { + .readdir = cmsfs_readdir, + .open = cmsfs_file_open, + .read = cmsfs_file_read, +}; diff --git a/fs/cmsfs/helpers.c b/fs/cmsfs/helpers.c new file mode 100644 index 0000000..93800b3 --- /dev/null +++ b/fs/cmsfs/helpers.c @@ -0,0 +1,78 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include +#include + +#include "cmsfs.h" + +void munge_name(__u8 *name, char *outname) +{ + __u8 *p = name; + char *orig_outname = outname; + + if (!name) + return; + + /* + * NOTE: + * '\x40' is a space in EBCDIC + * '\x4b' is a period in EBCDIC + */ + + /* file name */ + while(*p != '\x40' && (p - name < 8)) + *outname++ = *p++; + + *outname++ = '\x4b'; + + p = name+8; + + while(*p != '\x40' && (p - name < 16)) + *outname++ = *p++; + + *outname = '\0'; + + EBC2ASC(orig_outname, 17); +} + +/** + * cmsfs_read_block - read a single disk block + * @sb: superblock to read from + * @buf: buffer to fill + * @block: block number to read + * @blocksize: blocksize to use + */ +int cmsfs_read_block(struct super_block *sb, void *buf, int block, + unsigned blocksize) +{ + struct buffer_head * bh; + + /* for the moment, we only deal with physical blocks */ + BUG_ON(blocksize != sb->s_blocksize); + + /* We could maybe handle that case by breaking-up this call into + multiple bread() calls, but that'll be a later driver rev. */ + + /* Call the system-level __bread() */ + bh = __bread(sb->s_bdev, block, blocksize); + if (!bh) { + printk(KERN_WARNING "cmsfs: system bread() failed.\n"); + return -EIO; + } + + /* copy the data part, then release the VFS buffer */ + memmove(buf, bh->b_data, blocksize); + brelse(bh); + + return blocksize; +} diff --git a/fs/cmsfs/inode.c b/fs/cmsfs/inode.c new file mode 100644 index 0000000..9c4d51b --- /dev/null +++ b/fs/cmsfs/inode.c @@ -0,0 +1,269 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include +#include +#include + +#include "cmsfs.h" + +/** + * cmsfs_inode_lookup - lookup a file in the directory + * @dir: directory + * @dentry: dentry to lookup + * @nd: (unused) + */ +static struct dentry *cmsfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct cmsfs_inode_info *cdir = CMSFS_I(dir); + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct page *page; + struct CMSFSFST *fst; + char outname[18]; + int i; + int err; + + if (dir->i_ino != CMSFS_DIRECTOR_INO) + return ERR_PTR(-ENOTDIR); + + page = NULL; + inode = NULL; + fst = NULL; + + for (i = 0; i < cdir->items; i++, fst++) { + if ((i * cdir->lrecl) % CMSFS_SB(sb)->blksz == 0) { + if (i) { + kunmap(page); + page_cache_release(page); + } + + page = read_mapping_page(dir->i_mapping, (i * cdir->lrecl) / + CMSFS_SB(sb)->blksz, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + + fst = kmap(page); + if (PageError(page)) { + err = -EIO; + goto out_release; + } + } + + /* skipping .DIRECTOR and .ALLOCMAP, fill VFS dir with files */ + if (i < 2) + continue; + + munge_name(fst->FSTFNAME, outname); + + if (!strncmp(outname, dentry->d_name.name, 17)) { + inode = cmsfs_iget(sb, i + CMSFS_FIRST_INO); + break; + } + } + + kunmap(page); + page_cache_release(page); + return d_splice_alias(inode, dentry); + +out_release: + page_cache_release(page); +out: + return ERR_PTR(err); +} + +static struct inode_operations cmsfs_inode_ops = { + .lookup = cmsfs_lookup, + .setxattr = cmsfs_setxattr, + .getxattr = cmsfs_getxattr, + .removexattr = cmsfs_removexattr, + .listxattr = cmsfs_listxattr, +}; + +/** + * cmsfs_set_inode_size - calculate and set the inode size + * @inode: inode to work with + */ +static int cmsfs_set_inode_size(struct inode *inode) +{ + struct cmsfs_inode_info *cinode = CMSFS_I(inode); + + /* the default - for safety */ + inode->i_size = 0; + + /* if there are no records, then there ain't no bytes */ + if (cinode->items == 0) + return 0; + + /* how big is this file? "it depends" */ + switch (cinode->recfm) { + case RECFM_FIXED: + inode->i_size = cinode->lrecl * cinode->items; + break; + + case RECFM_VAR: + { + static int warned; + if (!warned) + printk(KERN_WARNING "cmsfs: recfm=v is" + " unsuported\n"); + warned=1; + } + break; + + default: + BUG(); + } + + return 0; +} + +/** + * read_inode - read an inode from DASD + * @inode: inode to fill in + */ +static int read_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct page *page; + struct CMSFSFST *fst, *buf; + int err; + + if (inode->i_ino < CMSFS_FIRST_INO) + return -ENOENT; + + page=NULL; + buf=NULL; + + if (inode->i_ino != CMSFS_DIRECTOR_INO) { + /* read file block within the directory */ + unsigned long ino = inode->i_ino - CMSFS_FIRST_INO; + + page = read_mapping_page(sb->s_root->d_inode->i_mapping, + (ino * sizeof(struct CMSFSFST))/ + sb->s_blocksize, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + + fst = kmap(page); + if (PageError(page)) { + err = -EIO; + goto out_release; + } + + fst = &fst[ino % (sb->s_blocksize / sizeof(struct CMSFSFST))]; + } else { + /* + * if we're reading the directory, we can't rely on + * sb->s_root->d_inode to be set up, therefore we are forced + * to do a block read + */ + buf = (struct CMSFSFST *)__get_free_page(GFP_KERNEL); + if(!buf) + return -ENOMEM; + + err = 0; + if (cmsfs_read_block(sb, buf, CMSFS_SB(sb)->origin-1, + sb->s_blocksize) != sb->s_blocksize) { + err = -EIO; + goto out; + } + + fst = buf; + } + + CMSFS_I(inode)->recfm = (enum recfm) fst->FSTRECFM; + CMSFS_I(inode)->lrecl = be32_to_cpu(fst->FSTLRECL); + CMSFS_I(inode)->origin = be32_to_cpu(fst->FSTFOP); + CMSFS_I(inode)->blocks = be32_to_cpu(fst->FSTADBC); + CMSFS_I(inode)->items = be32_to_cpu(fst->FSTAIC); + + /* levels of indirection and pointer size */ + CMSFS_I(inode)->level = fst->FSTNLVL; + CMSFS_I(inode)->psize = fst->FSTPTRSZ; + + CMSFS_I(inode)->ctime = mktime(hex2int(fst->FSTADATI[0]) + + (fst->FSTFLAGS & FSTCNTRY ? 2000 : 1900), + hex2int(fst->FSTADATI[1] - 1), + hex2int(fst->FSTADATI[2]), + hex2int(fst->FSTADATI[3]), + hex2int(fst->FSTADATI[4]), + hex2int(fst->FSTADATI[5])); + + err = 0; + +out: + if (inode->i_ino != CMSFS_DIRECTOR_INO) { + kunmap(page); +out_release: + page_cache_release(page); + } else + free_page((unsigned long)buf); + + return err; +} + +/** + * cmsfs_iget - get a filled-in inode based on inode number + * @sb: superblock to read inode from + * @ino: inode number to look up + */ +struct inode *cmsfs_iget(struct super_block *sb, unsigned long ino) +{ + struct inode *inode; + int err = 0; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + if ((err = read_inode(inode))) + goto bad_inode; + + if (cmsfs_set_inode_size(inode)) + goto bad_inode; + + if (inode->i_ino == CMSFS_DIRECTOR_INO) + inode->i_mode = S_IRWXUGO | S_IFDIR; + else + inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_nlink = 1; + inode->i_blocks = CMSFS_I(inode)->blocks; + inode->i_atime.tv_sec = CMSFS_I(inode)->ctime; + inode->i_ctime.tv_sec = CMSFS_I(inode)->ctime; + inode->i_mtime.tv_sec = CMSFS_I(inode)->ctime; + inode->i_atime.tv_nsec = 0; + inode->i_ctime.tv_nsec = 0; + inode->i_mtime.tv_nsec = 0; + + inode->i_generation = 0; + + inode->i_op = &cmsfs_inode_ops; + inode->i_fop = &cmsfs_file_ops; + inode->i_mapping->a_ops = &cmsfs_aops; + + unlock_new_inode(inode); + return inode; + +bad_inode: + iget_failed(inode); + return ERR_PTR(err); +} diff --git a/fs/cmsfs/super.c b/fs/cmsfs/super.c new file mode 100644 index 0000000..a397b09 --- /dev/null +++ b/fs/cmsfs/super.c @@ -0,0 +1,379 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "cmsfs.h" + +static struct kmem_cache *cmsfs_inode_cachep = NULL; + +/* + * Blocksize test array - possible block sizes for a CMS "EDF" minidisk + * filesystem + */ +static int blksizes[] = { 512, 1024, 2048, 4096, 0, }; + +/** + * cmsfs_find_label - look for the CMS volume label structure (ADT) + * @sb: superblock to operate on + * @adt: ADT structure to fill in + * + * Tries various block sizes to find the CMSFS_SUPER_MAGIC. When found, + * sb->s_blocksize is updated apropriately, and the adt is filled in + */ +static int cmsfs_find_label(struct super_block *sb, struct CMSFSADT *adt) +{ + void *buf; + int i; + int err; + + buf = (void *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = -EIO; + + /* + * FBA DASDs are special. Physical blocksize is always 512 + * and the label is at physical offset 512 (second record), + * though the logical blocksize may be 512, 1K, 2K, or 4K. + */ + if (sb->s_blocksize == 512) { + /* read FBA block #1 (second record) */ + if (cmsfs_read_block(sb, buf, 1, 512) != 512) + goto out; + + memcpy(adt, buf, sizeof(*adt)); + + /* check for the CMS1 magic at the FBA offset */ + if (be32_to_cpu(adt->ADTIDENT) == CMSFS_SUPER_MAGIC) { + u32 blksz; + + blksz = be32_to_cpu(adt->ADTDBSIZ); + if (blksz != 512) { + printk(KERN_WARNING "cmsfs: FS blocksize" + " does not match device blocksize" + " (%u != %d).\n", blksz, 512); + } else { + CMSFS_SB(sb)->blksz = 512; + + err = 0; + } + + goto out; + } + } + + /* not an FBA volume; try CKD blocksizes + * + * CKD DASDs are C/H/S in nature and can have any blocksize + * that the utility or operating system decided to put there. + * OS/390 uses no particular blocksize, referring to tracks + * directly. For CMS CKD volumes, the physical blocksize should + * match logical, unless obscured by the access method. + */ + for (i = 0; blksizes[i] != 0; i++) { + u32 blksz; + + if (sb->s_blocksize != blksizes[i]) + continue; + + /* read CKD block #2 (third record) */ + if (cmsfs_read_block(sb, buf, 2, blksizes[i]) != blksizes[i]) + continue; + + memcpy(adt, buf, sizeof(*adt)); + + /* check for the CMS1 magic */ + if (be32_to_cpu(adt->ADTIDENT) != CMSFS_SUPER_MAGIC) + continue; + + blksz = be32_to_cpu(adt->ADTDBSIZ); + if (blksz == blksizes[i]) { + CMSFS_SB(sb)->blksz = blksz; + + sb_min_blocksize(sb, blksz); + + err = 0; + goto out; + } + + printk(KERN_WARNING "cmsfs: FS blocksize does not match" + " device blocksize (%u != %d).\n", blksz, blksizes[i]); + goto out; + } + + err = -ENOMEDIUM; + +out: + free_page((unsigned long)buf); + return err; +} + +/** + * __fill_super - fill in superblock private data + * @sb: superblock to operate on + * + * Find the ADT struct on the disk, read in the data, byteswap if necessary, + * and then will in CMSFS_SB(sb). + */ +static int __fill_super(struct super_block *sb) +{ + struct CMSFSADT cmsvol; /* partial "ADT" structure */ + struct cmsfs_sb_info *sbi = CMSFS_SB(sb); + struct inode *root; + int err; + + BUG_ON(!sbi); + + /* + * look for the CMS volume label (aka VOLSER) + * + * NOTE: This also effects a load of the ADT struct to &cmsvol and + * sets the blksz member to match the volume found + */ + err = cmsfs_find_label(sb, &cmsvol); + if (err) + return err; + + /* extract volume label */ + memcpy(sbi->volid, cmsvol.ADTID, 6); + sbi->volid[6] = 0x00; + + /* extract "directory origin pointer" */ + sbi->origin = be32_to_cpu(cmsvol.ADTDOP); + + /* extract number of cylinders used */ + sbi->ncyls = be32_to_cpu(cmsvol.ADTCYL); + + /* extract max number of cylinders */ + sbi->mcyls = be32_to_cpu(cmsvol.ADTMCYL); + + /* extract "total blocks on disk" count */ + sbi->blocks = be32_to_cpu(cmsvol.ADTNUM); + + /* compute "blocks used" count */ + sbi->bkused = be32_to_cpu(cmsvol.ADTUSED); + sbi->bkused += 1; /* why??? */ + + /* extract size and number of FSTs */ + sbi->fstsz = be32_to_cpu(cmsvol.ADTFSTSZ); + sbi->fstct = be32_to_cpu(cmsvol.ADTNFST); + + /* extract offset to reserved file, if any */ + sbi->resoff = be32_to_cpu(cmsvol.ADTOFFST); + + sbi->ctime = mktime(hex2int(cmsvol.ADTDCRED[0]) + + (cmsvol.ADTFLGL & ADTCNTRY ? 2000 : 1900), + hex2int(cmsvol.ADTDCRED[1] - 1), + hex2int(cmsvol.ADTDCRED[2]), + hex2int(cmsvol.ADTDCRED[3]), + hex2int(cmsvol.ADTDCRED[4]), + hex2int(cmsvol.ADTDCRED[5])); + + err = -ENOMEM; + root = cmsfs_iget(sb, CMSFS_DIRECTOR_INO); + if (IS_ERR(root)) { + err = PTR_ERR(root); + goto out; + } + + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + printk(KERN_ERR "cmsfs: get root inode failed\n"); + goto out_inode; + } + + err = -EUCLEAN; + + /* sanity check: FOP in dir entry must match DOP in vol */ + if (CMSFS_I(root)->origin != CMSFS_SB(sb)->origin) { + printk("cmsfs: directory FOP != volume DOP\n"); + goto out_inode; + } + + /* sanity check: RECFM of a directory must be fixed */ + if (CMSFS_I(root)->recfm != RECFM_FIXED) { + printk("cmsfs: directory must have recfm fixed.\n"); + goto out_inode; + } + + /* sanity check: LRECL of a directory must be 64 */ + if (CMSFS_I(root)->lrecl != 64) { + printk(KERN_WARNING "cmsfs: directory lrecl %d not 64.\n", + CMSFS_I(root)->lrecl); + goto out_inode; + } + + sbi->files = CMSFS_I(root)->items; + + sb->s_flags |= MS_RDONLY; + sb->s_maxbytes = MAX_NON_LFS; + + err = 0; +out: + return err; + +out_inode: + iput(root); + goto out; +} + +/** + * cmsfs_alloc_inode - allocate a VFS+cmsfs inode structs + * @sb: superblock to work with + */ +static struct inode *cmsfs_alloc_inode(struct super_block *sb) +{ + struct cmsfs_inode_info *cinode; + + cinode = kmem_cache_alloc(cmsfs_inode_cachep, GFP_NOFS); + if (!cinode) + return NULL; + + return &cinode->vfs_inode; +} + +/** + * cmsfs_destroy_inode - free VFS+cmsfs inode structs + * @inode: inode to free + */ +static void cmsfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(cmsfs_inode_cachep, CMSFS_I(inode)); +} + +/** + * cmsfs_statfs - return stat information + * @dentry: destry being stat'd + * @buf: stat buffer to fill + */ +static int cmsfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct cmsfs_sb_info *sbi = CMSFS_SB(dentry->d_sb); + + buf->f_type = CMSFS_SUPER_MAGIC; + buf->f_bsize = sbi->blksz; + buf->f_blocks = sbi->blocks; + buf->f_bfree = sbi->blocks - sbi->bkused; + buf->f_bavail = sbi->blocks - sbi->bkused; + + buf->f_files = sbi->files; + buf->f_ffree = 0; /* no files available on R/O media */ + + buf->f_namelen = CMSFS_NAME_LEN; + + memset(buf->f_fsid.val, 0, 8); + memcpy(buf->f_fsid.val, sbi->volid, 6); + + return 0; +} + +static struct super_operations cmsfs_sops = { + .alloc_inode = cmsfs_alloc_inode, + .destroy_inode = cmsfs_destroy_inode, + .statfs = cmsfs_statfs, +}; + +/** + * cmsfs_fill_super - fill in a superblock + * @sb: superblock to fill + * @data: (unused) + * @silent: don't printk anything + */ +static int cmsfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct cmsfs_sb_info *sbi; + int err; + + sbi = kzalloc(sizeof(struct cmsfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + + sb->s_magic = CMSFS_SUPER_MAGIC; + sb->s_op = &cmsfs_sops; + + sb_min_blocksize(sb, 512); + + if ((err = __fill_super(sb))) { + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; + return err; + } + + return 0; +} + +static int cmsfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, cmsfs_fill_super, + mnt); +} + +static struct file_system_type cmsfs_type = { + .owner = THIS_MODULE, + .name = "cmsfs", + .get_sb = cmsfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static void cmsfs_inode_init_once(void *data) +{ + struct cmsfs_inode_info *cinode = data; + + memset(cinode, 0, sizeof(struct cmsfs_inode_info)); + + inode_init_once(&cinode->vfs_inode); +} + +static int cmsfs_init(void) +{ + int err; + printk(KERN_INFO "CMSFS\n"); + + cmsfs_inode_cachep = kmem_cache_create("cmsfs_inode_cache", + sizeof(struct cmsfs_inode_info), + 0, 0, + cmsfs_inode_init_once); + if (!cmsfs_inode_cachep) + return -ENOMEM; + + err = register_filesystem(&cmsfs_type); + if (err) + kmem_cache_destroy(cmsfs_inode_cachep); + + return err; +} + +static void cmsfs_cleanup(void) +{ + unregister_filesystem(&cmsfs_type); + kmem_cache_destroy(cmsfs_inode_cachep); + return; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CMS Minidisk filesystem Support"); +MODULE_AUTHOR("Josef 'Jeff' Sipek "); + +module_init(cmsfs_init); +module_exit(cmsfs_cleanup); diff --git a/fs/cmsfs/xattr.c b/fs/cmsfs/xattr.c new file mode 100644 index 0000000..df48aed --- /dev/null +++ b/fs/cmsfs/xattr.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#include + +#include "cmsfs.h" + +ssize_t cmsfs_getxattr(struct dentry *dentry, const char *name, void *value, + size_t size) +{ + char tmp[16]; + char *out = NULL; + + if (!strcmp("system.recfm", name)) { + switch (CMSFS_I(dentry->d_inode)->recfm) { + case RECFM_FIXED: + out = "fixed"; + break; + case RECFM_VAR: + out = "variable"; + break; + default: + out = "???"; + break; + } + } else if (!strcmp("system.lrecl", name)) { + snprintf(tmp, 16, "%d", CMSFS_I(dentry->d_inode)->lrecl); + out = tmp; + } else if (!strcmp("system.items", name)) { + snprintf(tmp, 16, "%d", CMSFS_I(dentry->d_inode)->items); + out = tmp; + } + + if (!out) + return -ENODATA; + + if (!value && !size) + return strlen(out); + + if (size < strlen(out)) + return -ERANGE; + + strcpy(value, out); + + return strlen(out); +} + +#define ATTRLIST "system.recfm\0system.lrecl\0system.items\0" + +ssize_t cmsfs_listxattr(struct dentry *dentry, char *list, size_t size) +{ + if (!list || !size) + return sizeof(ATTRLIST); + + if (size < sizeof(ATTRLIST)) + return -ERANGE; + + memcpy(list, ATTRLIST, sizeof(ATTRLIST)); + + return sizeof(ATTRLIST); +} + +int cmsfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags) +{ + return -EOPNOTSUPP; +} + +int cmsfs_removexattr(struct dentry *dentry, const char *name) +{ + return -EOPNOTSUPP; +} diff --git a/include/linux/magic.h b/include/linux/magic.h index 1fa0c2c..e13ec86 100644 --- a/include/linux/magic.h +++ b/include/linux/magic.h @@ -5,6 +5,7 @@ #define AFFS_SUPER_MAGIC 0xadff #define AFS_SUPER_MAGIC 0x5346414F #define AUTOFS_SUPER_MAGIC 0x0187 +#define CMSFS_SUPER_MAGIC 0xC3D4E2F1 #define CODA_SUPER_MAGIC 0x73757245 #define EFS_SUPER_MAGIC 0x414A53 #define EXT2_SUPER_MAGIC 0xEF53