diff options
Diffstat (limited to 'fs/freevxfs')
-rw-r--r-- | fs/freevxfs/Kconfig | 27 | ||||
-rw-r--r-- | fs/freevxfs/Makefile | 9 | ||||
-rw-r--r-- | fs/freevxfs/vxfs.h | 257 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_bmap.c | 271 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_dir.h | 68 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_extern.h | 49 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_fshead.c | 166 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_fshead.h | 43 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_immed.c | 53 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_inode.c | 316 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_inode.h | 169 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_lookup.c | 272 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_olt.c | 105 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_olt.h | 120 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_subr.c | 152 | ||||
-rw-r--r-- | fs/freevxfs/vxfs_super.c | 334 |
16 files changed, 2411 insertions, 0 deletions
diff --git a/fs/freevxfs/Kconfig b/fs/freevxfs/Kconfig new file mode 100644 index 0000000000..912107ebea --- /dev/null +++ b/fs/freevxfs/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + depends on BLOCK + select BUFFER_HEAD + help + FreeVxFS is a file system driver that support the VERITAS VxFS(TM) + file system format. VERITAS VxFS(TM) is the standard file system + of SCO UnixWare (and possibly others) and optionally available + for Sunsoft Solaris, HP-UX and many other operating systems. However + these particular OS implementations of vxfs may differ in on-disk + data endianness and/or superblock offset. The vxfs module has been + tested with SCO UnixWare and HP-UX B.10.20 (pa-risc 1.1 arch.) + Currently only readonly access is supported and VxFX versions + 2, 3 and 4. Tests were performed with HP-UX VxFS version 3. + + NOTE: the file system type as used by mount(1), mount(2) and + fstab(5) is 'vxfs' as it describes the file system format, not + the actual driver. + + There is a userspace utility for HP-UX logical volumes which makes + creating HP-UX logical volumes easy from HP-UX disk block device file + or regular file with image of the disk. See: + https://sourceforge.net/projects/linux-vxfs/ + + To compile this as a module, choose M here: the module will be + called freevxfs. If unsure, say N. diff --git a/fs/freevxfs/Makefile b/fs/freevxfs/Makefile new file mode 100644 index 0000000000..e6ee592915 --- /dev/null +++ b/fs/freevxfs/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# VxFS Makefile +# + +obj-$(CONFIG_VXFS_FS) += freevxfs.o + +freevxfs-objs := vxfs_bmap.o vxfs_fshead.o vxfs_immed.o vxfs_inode.o \ + vxfs_lookup.o vxfs_olt.o vxfs_subr.o vxfs_super.o diff --git a/fs/freevxfs/vxfs.h b/fs/freevxfs/vxfs.h new file mode 100644 index 0000000000..bffd156d64 --- /dev/null +++ b/fs/freevxfs/vxfs.h @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ +#ifndef _VXFS_SUPER_H_ +#define _VXFS_SUPER_H_ + +/* + * Veritas filesystem driver - superblock structure. + * + * This file contains the definition of the disk and core + * superblocks of the Veritas Filesystem. + */ +#include <linux/types.h> + +/* + * Superblock magic number (vxfs_super->vs_magic). + */ +#define VXFS_SUPER_MAGIC 0xa501FCF5 + +/* + * The root inode. + */ +#define VXFS_ROOT_INO 2 + +/* + * Num of entries in free extent array + */ +#define VXFS_NEFREE 32 + +enum vxfs_byte_order { + VXFS_BO_LE, + VXFS_BO_BE, +}; + +typedef __u16 __bitwise __fs16; +typedef __u32 __bitwise __fs32; +typedef __u64 __bitwise __fs64; + +/* + * VxFS superblock (disk). + */ +struct vxfs_sb { + /* + * Readonly fields for the version 1 superblock. + * + * Lots of this fields are no more used by version 2 + * and never filesystems. + */ + __fs32 vs_magic; /* Magic number */ + __fs32 vs_version; /* VxFS version */ + __fs32 vs_ctime; /* create time - secs */ + __fs32 vs_cutime; /* create time - usecs */ + __fs32 __unused1; /* unused */ + __fs32 __unused2; /* unused */ + __fs32 vs_old_logstart; /* obsolete */ + __fs32 vs_old_logend; /* obsolete */ + __fs32 vs_bsize; /* block size */ + __fs32 vs_size; /* number of blocks */ + __fs32 vs_dsize; /* number of data blocks */ + __fs32 vs_old_ninode; /* obsolete */ + __fs32 vs_old_nau; /* obsolete */ + __fs32 __unused3; /* unused */ + __fs32 vs_old_defiextsize; /* obsolete */ + __fs32 vs_old_ilbsize; /* obsolete */ + __fs32 vs_immedlen; /* size of immediate data area */ + __fs32 vs_ndaddr; /* number of direct extentes */ + __fs32 vs_firstau; /* address of first AU */ + __fs32 vs_emap; /* offset of extent map in AU */ + __fs32 vs_imap; /* offset of inode map in AU */ + __fs32 vs_iextop; /* offset of ExtOp. map in AU */ + __fs32 vs_istart; /* offset of inode list in AU */ + __fs32 vs_bstart; /* offset of fdblock in AU */ + __fs32 vs_femap; /* aufirst + emap */ + __fs32 vs_fimap; /* aufirst + imap */ + __fs32 vs_fiextop; /* aufirst + iextop */ + __fs32 vs_fistart; /* aufirst + istart */ + __fs32 vs_fbstart; /* aufirst + bstart */ + __fs32 vs_nindir; /* number of entries in indir */ + __fs32 vs_aulen; /* length of AU in blocks */ + __fs32 vs_auimlen; /* length of imap in blocks */ + __fs32 vs_auemlen; /* length of emap in blocks */ + __fs32 vs_auilen; /* length of ilist in blocks */ + __fs32 vs_aupad; /* length of pad in blocks */ + __fs32 vs_aublocks; /* data blocks in AU */ + __fs32 vs_maxtier; /* log base 2 of aublocks */ + __fs32 vs_inopb; /* number of inodes per blk */ + __fs32 vs_old_inopau; /* obsolete */ + __fs32 vs_old_inopilb; /* obsolete */ + __fs32 vs_old_ndiripau; /* obsolete */ + __fs32 vs_iaddrlen; /* size of indirect addr ext. */ + __fs32 vs_bshift; /* log base 2 of bsize */ + __fs32 vs_inoshift; /* log base 2 of inobp */ + __fs32 vs_bmask; /* ~( bsize - 1 ) */ + __fs32 vs_boffmask; /* bsize - 1 */ + __fs32 vs_old_inomask; /* old_inopilb - 1 */ + __fs32 vs_checksum; /* checksum of V1 data */ + + /* + * Version 1, writable + */ + __fs32 vs_free; /* number of free blocks */ + __fs32 vs_ifree; /* number of free inodes */ + __fs32 vs_efree[VXFS_NEFREE]; /* number of free extents by size */ + __fs32 vs_flags; /* flags ?!? */ + __u8 vs_mod; /* filesystem has been changed */ + __u8 vs_clean; /* clean FS */ + __fs16 __unused4; /* unused */ + __fs32 vs_firstlogid; /* mount time log ID */ + __fs32 vs_wtime; /* last time written - sec */ + __fs32 vs_wutime; /* last time written - usec */ + __u8 vs_fname[6]; /* FS name */ + __u8 vs_fpack[6]; /* FS pack name */ + __fs32 vs_logversion; /* log format version */ + __u32 __unused5; /* unused */ + + /* + * Version 2, Read-only + */ + __fs32 vs_oltext[2]; /* OLT extent and replica */ + __fs32 vs_oltsize; /* OLT extent size */ + __fs32 vs_iauimlen; /* size of inode map */ + __fs32 vs_iausize; /* size of IAU in blocks */ + __fs32 vs_dinosize; /* size of inode in bytes */ + __fs32 vs_old_dniaddr; /* indir levels per inode */ + __fs32 vs_checksum2; /* checksum of V2 RO */ + + /* + * Actually much more... + */ +}; + + +/* + * In core superblock filesystem private data for VxFS. + */ +struct vxfs_sb_info { + struct vxfs_sb *vsi_raw; /* raw (on disk) superblock */ + struct buffer_head *vsi_bp; /* buffer for raw superblock*/ + struct inode *vsi_fship; /* fileset header inode */ + struct inode *vsi_ilist; /* inode list inode */ + struct inode *vsi_stilist; /* structural inode list inode */ + u_long vsi_iext; /* initial inode list */ + ino_t vsi_fshino; /* fileset header inode */ + daddr_t vsi_oltext; /* OLT extent */ + daddr_t vsi_oltsize; /* OLT size */ + enum vxfs_byte_order byte_order; +}; + +static inline u16 fs16_to_cpu(struct vxfs_sb_info *sbi, __fs16 a) +{ + if (sbi->byte_order == VXFS_BO_BE) + return be16_to_cpu((__force __be16)a); + else + return le16_to_cpu((__force __le16)a); +} + +static inline u32 fs32_to_cpu(struct vxfs_sb_info *sbi, __fs32 a) +{ + if (sbi->byte_order == VXFS_BO_BE) + return be32_to_cpu((__force __be32)a); + else + return le32_to_cpu((__force __le32)a); +} + +static inline u64 fs64_to_cpu(struct vxfs_sb_info *sbi, __fs64 a) +{ + if (sbi->byte_order == VXFS_BO_BE) + return be64_to_cpu((__force __be64)a); + else + return le64_to_cpu((__force __le64)a); +} + +/* + * File modes. File types above 0xf000 are vxfs internal only, they should + * not be passed back to higher levels of the system. vxfs file types must + * never have one of the regular file type bits set. + */ +enum vxfs_mode { + VXFS_ISUID = 0x00000800, /* setuid */ + VXFS_ISGID = 0x00000400, /* setgid */ + VXFS_ISVTX = 0x00000200, /* sticky bit */ + VXFS_IREAD = 0x00000100, /* read */ + VXFS_IWRITE = 0x00000080, /* write */ + VXFS_IEXEC = 0x00000040, /* exec */ + + VXFS_IFIFO = 0x00001000, /* Named pipe */ + VXFS_IFCHR = 0x00002000, /* Character device */ + VXFS_IFDIR = 0x00004000, /* Directory */ + VXFS_IFNAM = 0x00005000, /* Xenix device ?? */ + VXFS_IFBLK = 0x00006000, /* Block device */ + VXFS_IFREG = 0x00008000, /* Regular file */ + VXFS_IFCMP = 0x00009000, /* Compressed file ?!? */ + VXFS_IFLNK = 0x0000a000, /* Symlink */ + VXFS_IFSOC = 0x0000c000, /* Socket */ + + /* VxFS internal */ + VXFS_IFFSH = 0x10000000, /* Fileset header */ + VXFS_IFILT = 0x20000000, /* Inode list */ + VXFS_IFIAU = 0x30000000, /* Inode allocation unit */ + VXFS_IFCUT = 0x40000000, /* Current usage table */ + VXFS_IFATT = 0x50000000, /* Attr. inode */ + VXFS_IFLCT = 0x60000000, /* Link count table */ + VXFS_IFIAT = 0x70000000, /* Indirect attribute file */ + VXFS_IFEMR = 0x80000000, /* Extent map reorg file */ + VXFS_IFQUO = 0x90000000, /* BSD quota file */ + VXFS_IFPTI = 0xa0000000, /* "Pass through" inode */ + VXFS_IFLAB = 0x11000000, /* Device label file */ + VXFS_IFOLT = 0x12000000, /* OLT file */ + VXFS_IFLOG = 0x13000000, /* Log file */ + VXFS_IFEMP = 0x14000000, /* Extent map file */ + VXFS_IFEAU = 0x15000000, /* Extent AU file */ + VXFS_IFAUS = 0x16000000, /* Extent AU summary file */ + VXFS_IFDEV = 0x17000000, /* Device config file */ + +}; + +#define VXFS_TYPE_MASK 0xfffff000 + +#define VXFS_IS_TYPE(ip,type) (((ip)->vii_mode & VXFS_TYPE_MASK) == (type)) +#define VXFS_ISFIFO(x) VXFS_IS_TYPE((x),VXFS_IFIFO) +#define VXFS_ISCHR(x) VXFS_IS_TYPE((x),VXFS_IFCHR) +#define VXFS_ISDIR(x) VXFS_IS_TYPE((x),VXFS_IFDIR) +#define VXFS_ISNAM(x) VXFS_IS_TYPE((x),VXFS_IFNAM) +#define VXFS_ISBLK(x) VXFS_IS_TYPE((x),VXFS_IFBLK) +#define VXFS_ISLNK(x) VXFS_IS_TYPE((x),VXFS_IFLNK) +#define VXFS_ISREG(x) VXFS_IS_TYPE((x),VXFS_IFREG) +#define VXFS_ISCMP(x) VXFS_IS_TYPE((x),VXFS_IFCMP) +#define VXFS_ISSOC(x) VXFS_IS_TYPE((x),VXFS_IFSOC) + +#define VXFS_ISFSH(x) VXFS_IS_TYPE((x),VXFS_IFFSH) +#define VXFS_ISILT(x) VXFS_IS_TYPE((x),VXFS_IFILT) + +/* + * Inmode organisation types. + */ +enum { + VXFS_ORG_NONE = 0, /* Inode has *no* format ?!? */ + VXFS_ORG_EXT4 = 1, /* Ext4 */ + VXFS_ORG_IMMED = 2, /* All data stored in inode */ + VXFS_ORG_TYPED = 3, /* Typed extents */ +}; + +#define VXFS_IS_ORG(ip,org) ((ip)->vii_orgtype == (org)) +#define VXFS_ISNONE(ip) VXFS_IS_ORG((ip), VXFS_ORG_NONE) +#define VXFS_ISEXT4(ip) VXFS_IS_ORG((ip), VXFS_ORG_EXT4) +#define VXFS_ISIMMED(ip) VXFS_IS_ORG((ip), VXFS_ORG_IMMED) +#define VXFS_ISTYPED(ip) VXFS_IS_ORG((ip), VXFS_ORG_TYPED) + +/* + * Get filesystem private data from VFS superblock. + */ +#define VXFS_SBI(sbp) \ + ((struct vxfs_sb_info *)(sbp)->s_fs_info) + +#endif /* _VXFS_SUPER_H_ */ diff --git a/fs/freevxfs/vxfs_bmap.c b/fs/freevxfs/vxfs_bmap.c new file mode 100644 index 0000000000..de2a5bccb9 --- /dev/null +++ b/fs/freevxfs/vxfs_bmap.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ + +/* + * Veritas filesystem driver - filesystem to disk block mapping. + */ +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/kernel.h> + +#include "vxfs.h" +#include "vxfs_inode.h" +#include "vxfs_extern.h" + + +#ifdef DIAGNOSTIC +static void +vxfs_typdump(struct vxfs_typed *typ) +{ + printk(KERN_DEBUG "type=%Lu ", typ->vt_hdr >> VXFS_TYPED_TYPESHIFT); + printk("offset=%Lx ", typ->vt_hdr & VXFS_TYPED_OFFSETMASK); + printk("block=%x ", typ->vt_block); + printk("size=%x\n", typ->vt_size); +} +#endif + +/** + * vxfs_bmap_ext4 - do bmap for ext4 extents + * @ip: pointer to the inode we do bmap for + * @iblock: logical block. + * + * Description: + * vxfs_bmap_ext4 performs the bmap operation for inodes with + * ext4-style extents (which are much like the traditional UNIX + * inode organisation). + * + * Returns: + * The physical block number on success, else Zero. + */ +static daddr_t +vxfs_bmap_ext4(struct inode *ip, long bn) +{ + struct super_block *sb = ip->i_sb; + struct vxfs_inode_info *vip = VXFS_INO(ip); + struct vxfs_sb_info *sbi = VXFS_SBI(sb); + unsigned long bsize = sb->s_blocksize; + u32 indsize = fs32_to_cpu(sbi, vip->vii_ext4.ve4_indsize); + int i; + + if (indsize > sb->s_blocksize) + goto fail_size; + + for (i = 0; i < VXFS_NDADDR; i++) { + struct direct *d = vip->vii_ext4.ve4_direct + i; + if (bn >= 0 && bn < fs32_to_cpu(sbi, d->size)) + return (bn + fs32_to_cpu(sbi, d->extent)); + bn -= fs32_to_cpu(sbi, d->size); + } + + if ((bn / (indsize * indsize * bsize / 4)) == 0) { + struct buffer_head *buf; + daddr_t bno; + __fs32 *indir; + + buf = sb_bread(sb, + fs32_to_cpu(sbi, vip->vii_ext4.ve4_indir[0])); + if (!buf || !buffer_mapped(buf)) + goto fail_buf; + + indir = (__fs32 *)buf->b_data; + bno = fs32_to_cpu(sbi, indir[(bn / indsize) % (indsize * bn)]) + + (bn % indsize); + + brelse(buf); + return bno; + } else + printk(KERN_WARNING "no matching indir?"); + + return 0; + +fail_size: + printk("vxfs: indirect extent too big!\n"); +fail_buf: + return 0; +} + +/** + * vxfs_bmap_indir - recursion for vxfs_bmap_typed + * @ip: pointer to the inode we do bmap for + * @indir: indirect block we start reading at + * @size: size of the typed area to search + * @block: partially result from further searches + * + * Description: + * vxfs_bmap_indir reads a &struct vxfs_typed at @indir + * and performs the type-defined action. + * + * Return Value: + * The physical block number on success, else Zero. + * + * Note: + * Kernelstack is rare. Unrecurse? + */ +static daddr_t +vxfs_bmap_indir(struct inode *ip, long indir, int size, long block) +{ + struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb); + struct buffer_head *bp = NULL; + daddr_t pblock = 0; + int i; + + for (i = 0; i < size * VXFS_TYPED_PER_BLOCK(ip->i_sb); i++) { + struct vxfs_typed *typ; + int64_t off; + + bp = sb_bread(ip->i_sb, + indir + (i / VXFS_TYPED_PER_BLOCK(ip->i_sb))); + if (!bp || !buffer_mapped(bp)) + return 0; + + typ = ((struct vxfs_typed *)bp->b_data) + + (i % VXFS_TYPED_PER_BLOCK(ip->i_sb)); + off = fs64_to_cpu(sbi, typ->vt_hdr) & VXFS_TYPED_OFFSETMASK; + + if (block < off) { + brelse(bp); + continue; + } + + switch ((u_int32_t)(fs64_to_cpu(sbi, typ->vt_hdr) >> + VXFS_TYPED_TYPESHIFT)) { + case VXFS_TYPED_INDIRECT: + pblock = vxfs_bmap_indir(ip, + fs32_to_cpu(sbi, typ->vt_block), + fs32_to_cpu(sbi, typ->vt_size), + block - off); + if (pblock == -2) + break; + goto out; + case VXFS_TYPED_DATA: + if ((block - off) >= fs32_to_cpu(sbi, typ->vt_size)) + break; + pblock = fs32_to_cpu(sbi, typ->vt_block) + block - off; + goto out; + case VXFS_TYPED_INDIRECT_DEV4: + case VXFS_TYPED_DATA_DEV4: { + struct vxfs_typed_dev4 *typ4 = + (struct vxfs_typed_dev4 *)typ; + + printk(KERN_INFO "\n\nTYPED_DEV4 detected!\n"); + printk(KERN_INFO "block: %llu\tsize: %lld\tdev: %d\n", + fs64_to_cpu(sbi, typ4->vd4_block), + fs64_to_cpu(sbi, typ4->vd4_size), + fs32_to_cpu(sbi, typ4->vd4_dev)); + goto fail; + } + default: + printk(KERN_ERR "%s:%d vt_hdr %llu\n", __func__, + __LINE__, fs64_to_cpu(sbi, typ->vt_hdr)); + BUG(); + } + brelse(bp); + } + +fail: + pblock = 0; +out: + brelse(bp); + return (pblock); +} + +/** + * vxfs_bmap_typed - bmap for typed extents + * @ip: pointer to the inode we do bmap for + * @iblock: logical block + * + * Description: + * Performs the bmap operation for typed extents. + * + * Return Value: + * The physical block number on success, else Zero. + */ +static daddr_t +vxfs_bmap_typed(struct inode *ip, long iblock) +{ + struct vxfs_inode_info *vip = VXFS_INO(ip); + struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb); + daddr_t pblock = 0; + int i; + + for (i = 0; i < VXFS_NTYPED; i++) { + struct vxfs_typed *typ = vip->vii_org.typed + i; + u64 hdr = fs64_to_cpu(sbi, typ->vt_hdr); + int64_t off = (hdr & VXFS_TYPED_OFFSETMASK); + +#ifdef DIAGNOSTIC + vxfs_typdump(typ); +#endif + if (iblock < off) + continue; + switch ((u32)(hdr >> VXFS_TYPED_TYPESHIFT)) { + case VXFS_TYPED_INDIRECT: + pblock = vxfs_bmap_indir(ip, + fs32_to_cpu(sbi, typ->vt_block), + fs32_to_cpu(sbi, typ->vt_size), + iblock - off); + if (pblock == -2) + break; + return (pblock); + case VXFS_TYPED_DATA: + if ((iblock - off) < fs32_to_cpu(sbi, typ->vt_size)) + return (fs32_to_cpu(sbi, typ->vt_block) + + iblock - off); + break; + case VXFS_TYPED_INDIRECT_DEV4: + case VXFS_TYPED_DATA_DEV4: { + struct vxfs_typed_dev4 *typ4 = + (struct vxfs_typed_dev4 *)typ; + + printk(KERN_INFO "\n\nTYPED_DEV4 detected!\n"); + printk(KERN_INFO "block: %llu\tsize: %lld\tdev: %d\n", + fs64_to_cpu(sbi, typ4->vd4_block), + fs64_to_cpu(sbi, typ4->vd4_size), + fs32_to_cpu(sbi, typ4->vd4_dev)); + return 0; + } + default: + BUG(); + } + } + + return 0; +} + +/** + * vxfs_bmap1 - vxfs-internal bmap operation + * @ip: pointer to the inode we do bmap for + * @iblock: logical block + * + * Description: + * vxfs_bmap1 perfoms a logical to physical block mapping + * for vxfs-internal purposes. + * + * Return Value: + * The physical block number on success, else Zero. + */ +daddr_t +vxfs_bmap1(struct inode *ip, long iblock) +{ + struct vxfs_inode_info *vip = VXFS_INO(ip); + + if (VXFS_ISEXT4(vip)) + return vxfs_bmap_ext4(ip, iblock); + if (VXFS_ISTYPED(vip)) + return vxfs_bmap_typed(ip, iblock); + if (VXFS_ISNONE(vip)) + goto unsupp; + if (VXFS_ISIMMED(vip)) + goto unsupp; + + printk(KERN_WARNING "vxfs: inode %ld has no valid orgtype (%x)\n", + ip->i_ino, vip->vii_orgtype); + BUG(); + +unsupp: + printk(KERN_WARNING "vxfs: inode %ld has an unsupported orgtype (%x)\n", + ip->i_ino, vip->vii_orgtype); + return 0; +} diff --git a/fs/freevxfs/vxfs_dir.h b/fs/freevxfs/vxfs_dir.h new file mode 100644 index 0000000000..fbcd603365 --- /dev/null +++ b/fs/freevxfs/vxfs_dir.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ +#ifndef _VXFS_DIR_H_ +#define _VXFS_DIR_H_ + +/* + * Veritas filesystem driver - directory structure. + * + * This file contains the definition of the vxfs directory format. + */ + + +/* + * VxFS directory block header. + * + * This entry is the head of every filesystem block in a directory. + * It is used for free space management and additionally includes + * a hash for speeding up directory search (lookup). + * + * The hash may be empty and in fact we do not use it all in the + * Linux driver for now. + */ +struct vxfs_dirblk { + __fs16 d_free; /* free space in dirblock */ + __fs16 d_nhash; /* no of hash chains */ + __fs16 d_hash[1]; /* hash chain */ +}; + +/* + * VXFS_NAMELEN is the maximum length of the d_name field + * of an VxFS directory entry. + */ +#define VXFS_NAMELEN 256 + +/* + * VxFS directory entry. + */ +struct vxfs_direct { + __fs32 d_ino; /* inode number */ + __fs16 d_reclen; /* record length */ + __fs16 d_namelen; /* d_name length */ + __fs16 d_hashnext; /* next hash entry */ + char d_name[VXFS_NAMELEN]; /* name */ +}; + +/* + * VXFS_DIRPAD defines the directory entry boundaries, is _must_ be + * a multiple of four. + * VXFS_NAMEMIN is the length of a directory entry with a NULL d_name. + * VXFS_DIRROUND is an internal macros that rounds a length to a value + * usable for directory sizes. + * VXFS_DIRLEN calculates the directory entry size for an entry with + * a d_name with size len. + */ +#define VXFS_DIRPAD 4 +#define VXFS_NAMEMIN offsetof(struct vxfs_direct, d_name) +#define VXFS_DIRROUND(len) ((VXFS_DIRPAD + (len) - 1) & ~(VXFS_DIRPAD -1)) +#define VXFS_DIRLEN(len) (VXFS_DIRROUND(VXFS_NAMEMIN + (len))) + +/* + * VXFS_DIRBLKOV is the overhead of a specific dirblock. + */ +#define VXFS_DIRBLKOV(sbi, dbp) \ + ((sizeof(short) * fs16_to_cpu(sbi, dbp->d_nhash)) + 4) + +#endif /* _VXFS_DIR_H_ */ diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h new file mode 100644 index 0000000000..3a2180c5e2 --- /dev/null +++ b/fs/freevxfs/vxfs_extern.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ +#ifndef _VXFS_EXTERN_H_ +#define _VXFS_EXTERN_H_ + +/* + * Veritas filesystem driver - external prototypes. + * + * This file contains prototypes for all vxfs functions used + * outside their respective source files. + */ + + +struct kmem_cache; +struct super_block; +struct vxfs_inode_info; +struct inode; + + +/* vxfs_bmap.c */ +extern daddr_t vxfs_bmap1(struct inode *, long); + +/* vxfs_fshead.c */ +extern int vxfs_read_fshead(struct super_block *); + +/* vxfs_inode.c */ +extern const struct address_space_operations vxfs_immed_aops; +extern void vxfs_dumpi(struct vxfs_inode_info *, ino_t); +extern struct inode *vxfs_blkiget(struct super_block *, u_long, ino_t); +extern struct inode *vxfs_stiget(struct super_block *, ino_t); +extern struct inode *vxfs_iget(struct super_block *, ino_t); +extern void vxfs_evict_inode(struct inode *); + +/* vxfs_lookup.c */ +extern const struct inode_operations vxfs_dir_inode_ops; +extern const struct file_operations vxfs_dir_operations; + +/* vxfs_olt.c */ +extern int vxfs_read_olt(struct super_block *, u_long); + +/* vxfs_subr.c */ +extern const struct address_space_operations vxfs_aops; +extern struct page * vxfs_get_page(struct address_space *, u_long); +extern void vxfs_put_page(struct page *); +extern struct buffer_head * vxfs_bread(struct inode *, int); + +#endif /* _VXFS_EXTERN_H_ */ diff --git a/fs/freevxfs/vxfs_fshead.c b/fs/freevxfs/vxfs_fshead.c new file mode 100644 index 0000000000..c1174a3f89 --- /dev/null +++ b/fs/freevxfs/vxfs_fshead.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ + +/* + * Veritas filesystem driver - fileset header routines. + */ +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include "vxfs.h" +#include "vxfs_inode.h" +#include "vxfs_extern.h" +#include "vxfs_fshead.h" + + +#ifdef DIAGNOSTIC +static void +vxfs_dumpfsh(struct vxfs_fsh *fhp) +{ + printk("\n\ndumping fileset header:\n"); + printk("----------------------------\n"); + printk("version: %u\n", fhp->fsh_version); + printk("fsindex: %u\n", fhp->fsh_fsindex); + printk("iauino: %u\tninodes:%u\n", + fhp->fsh_iauino, fhp->fsh_ninodes); + printk("maxinode: %u\tlctino: %u\n", + fhp->fsh_maxinode, fhp->fsh_lctino); + printk("nau: %u\n", fhp->fsh_nau); + printk("ilistino[0]: %u\tilistino[1]: %u\n", + fhp->fsh_ilistino[0], fhp->fsh_ilistino[1]); +} +#endif + +/** + * vxfs_getfsh - read fileset header into memory + * @ip: the (fake) fileset header inode + * @which: 0 for the structural, 1 for the primary fsh. + * + * Description: + * vxfs_getfsh reads either the structural or primary fileset header + * described by @ip into memory. + * + * Returns: + * The fileset header structure on success, else Zero. + */ +static struct vxfs_fsh * +vxfs_getfsh(struct inode *ip, int which) +{ + struct buffer_head *bp; + + bp = vxfs_bread(ip, which); + if (bp) { + struct vxfs_fsh *fhp; + + if (!(fhp = kmalloc(sizeof(*fhp), GFP_KERNEL))) + goto out; + memcpy(fhp, bp->b_data, sizeof(*fhp)); + + put_bh(bp); + return (fhp); + } +out: + brelse(bp); + return NULL; +} + +/** + * vxfs_read_fshead - read the fileset headers + * @sbp: superblock to which the fileset belongs + * + * Description: + * vxfs_read_fshead will fill the inode and structural inode list in @sb. + * + * Returns: + * Zero on success, else a negative error code (-EINVAL). + */ +int +vxfs_read_fshead(struct super_block *sbp) +{ + struct vxfs_sb_info *infp = VXFS_SBI(sbp); + struct vxfs_fsh *pfp, *sfp; + struct vxfs_inode_info *vip; + + infp->vsi_fship = vxfs_blkiget(sbp, infp->vsi_iext, infp->vsi_fshino); + if (!infp->vsi_fship) { + printk(KERN_ERR "vxfs: unable to read fsh inode\n"); + return -EINVAL; + } + + vip = VXFS_INO(infp->vsi_fship); + if (!VXFS_ISFSH(vip)) { + printk(KERN_ERR "vxfs: fsh list inode is of wrong type (%x)\n", + vip->vii_mode & VXFS_TYPE_MASK); + goto out_iput_fship; + } + +#ifdef DIAGNOSTIC + printk("vxfs: fsh inode dump:\n"); + vxfs_dumpi(vip, infp->vsi_fshino); +#endif + + sfp = vxfs_getfsh(infp->vsi_fship, 0); + if (!sfp) { + printk(KERN_ERR "vxfs: unable to get structural fsh\n"); + goto out_iput_fship; + } + +#ifdef DIAGNOSTIC + vxfs_dumpfsh(sfp); +#endif + + pfp = vxfs_getfsh(infp->vsi_fship, 1); + if (!pfp) { + printk(KERN_ERR "vxfs: unable to get primary fsh\n"); + goto out_free_sfp; + } + +#ifdef DIAGNOSTIC + vxfs_dumpfsh(pfp); +#endif + + infp->vsi_stilist = vxfs_blkiget(sbp, infp->vsi_iext, + fs32_to_cpu(infp, sfp->fsh_ilistino[0])); + if (!infp->vsi_stilist) { + printk(KERN_ERR "vxfs: unable to get structural list inode\n"); + goto out_free_pfp; + } + if (!VXFS_ISILT(VXFS_INO(infp->vsi_stilist))) { + printk(KERN_ERR "vxfs: structural list inode is of wrong type (%x)\n", + VXFS_INO(infp->vsi_stilist)->vii_mode & VXFS_TYPE_MASK); + goto out_iput_stilist; + } + + infp->vsi_ilist = vxfs_stiget(sbp, fs32_to_cpu(infp, pfp->fsh_ilistino[0])); + if (!infp->vsi_ilist) { + printk(KERN_ERR "vxfs: unable to get inode list inode\n"); + goto out_iput_stilist; + } + if (!VXFS_ISILT(VXFS_INO(infp->vsi_ilist))) { + printk(KERN_ERR "vxfs: inode list inode is of wrong type (%x)\n", + VXFS_INO(infp->vsi_ilist)->vii_mode & VXFS_TYPE_MASK); + goto out_iput_ilist; + } + + kfree(pfp); + kfree(sfp); + return 0; + + out_iput_ilist: + iput(infp->vsi_ilist); + out_iput_stilist: + iput(infp->vsi_stilist); + out_free_pfp: + kfree(pfp); + out_free_sfp: + kfree(sfp); + out_iput_fship: + iput(infp->vsi_fship); + return -EINVAL; +} diff --git a/fs/freevxfs/vxfs_fshead.h b/fs/freevxfs/vxfs_fshead.h new file mode 100644 index 0000000000..dfd2147599 --- /dev/null +++ b/fs/freevxfs/vxfs_fshead.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ +#ifndef _VXFS_FSHEAD_H_ +#define _VXFS_FSHEAD_H_ + +/* + * Veritas filesystem driver - fileset header structures. + * + * This file contains the physical structure of the VxFS + * fileset header. + */ + + +/* + * Fileset header + */ +struct vxfs_fsh { + __fs32 fsh_version; /* fileset header version */ + __fs32 fsh_fsindex; /* fileset index */ + __fs32 fsh_time; /* modification time - sec */ + __fs32 fsh_utime; /* modification time - usec */ + __fs32 fsh_extop; /* extop flags */ + __fs32 fsh_ninodes; /* allocated inodes */ + __fs32 fsh_nau; /* number of IAUs */ + __fs32 fsh_old_ilesize; /* old size of ilist */ + __fs32 fsh_dflags; /* flags */ + __fs32 fsh_quota; /* quota limit */ + __fs32 fsh_maxinode; /* maximum inode number */ + __fs32 fsh_iauino; /* IAU inode */ + __fs32 fsh_ilistino[2]; /* ilist inodes */ + __fs32 fsh_lctino; /* link count table inode */ + + /* + * Slightly more fields follow, but they + * a) are not of any interest for us, and + * b) differ a lot in different vxfs versions/ports + */ +}; + +#endif /* _VXFS_FSHEAD_H_ */ diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c new file mode 100644 index 0000000000..9b49ec36e6 --- /dev/null +++ b/fs/freevxfs/vxfs_immed.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ + +/* + * Veritas filesystem driver - support for 'immed' inodes. + */ +#include <linux/fs.h> +#include <linux/pagemap.h> + +#include "vxfs.h" +#include "vxfs_extern.h" +#include "vxfs_inode.h" + +/** + * vxfs_immed_read_folio - read part of an immed inode into pagecache + * @file: file context (unused) + * @folio: folio to fill in. + * + * Description: + * vxfs_immed_read_folio reads a part of the immed area of the + * file that hosts @folio into the pagecache. + * + * Returns: + * Zero on success, else a negative error code. + * + * Locking status: + * @folio is locked and will be unlocked. + */ +static int vxfs_immed_read_folio(struct file *fp, struct folio *folio) +{ + struct vxfs_inode_info *vip = VXFS_INO(folio->mapping->host); + void *src = vip->vii_immed.vi_immed + folio_pos(folio); + unsigned long i; + + for (i = 0; i < folio_nr_pages(folio); i++) { + memcpy_to_page(folio_page(folio, i), 0, src, PAGE_SIZE); + src += PAGE_SIZE; + } + + folio_mark_uptodate(folio); + folio_unlock(folio); + + return 0; +} + +/* + * Address space operations for immed files and directories. + */ +const struct address_space_operations vxfs_immed_aops = { + .read_folio = vxfs_immed_read_folio, +}; diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c new file mode 100644 index 0000000000..ac5d43b164 --- /dev/null +++ b/fs/freevxfs/vxfs_inode.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ + +/* + * Veritas filesystem driver - inode routines. + */ +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/pagemap.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/namei.h> + +#include "vxfs.h" +#include "vxfs_inode.h" +#include "vxfs_extern.h" + + +#ifdef DIAGNOSTIC +/* + * Dump inode contents (partially). + */ +void +vxfs_dumpi(struct vxfs_inode_info *vip, ino_t ino) +{ + printk(KERN_DEBUG "\n\n"); + if (ino) + printk(KERN_DEBUG "dumping vxfs inode %ld\n", ino); + else + printk(KERN_DEBUG "dumping unknown vxfs inode\n"); + + printk(KERN_DEBUG "---------------------------\n"); + printk(KERN_DEBUG "mode is %x\n", vip->vii_mode); + printk(KERN_DEBUG "nlink:%u, uid:%u, gid:%u\n", + vip->vii_nlink, vip->vii_uid, vip->vii_gid); + printk(KERN_DEBUG "size:%Lx, blocks:%u\n", + vip->vii_size, vip->vii_blocks); + printk(KERN_DEBUG "orgtype:%u\n", vip->vii_orgtype); +} +#endif + +/** + * vxfs_transmod - mode for a VxFS inode + * @vip: VxFS inode + * + * Description: + * vxfs_transmod returns a Linux mode_t for a given + * VxFS inode structure. + */ +static __inline__ umode_t +vxfs_transmod(struct vxfs_inode_info *vip) +{ + umode_t ret = vip->vii_mode & ~VXFS_TYPE_MASK; + + if (VXFS_ISFIFO(vip)) + ret |= S_IFIFO; + if (VXFS_ISCHR(vip)) + ret |= S_IFCHR; + if (VXFS_ISDIR(vip)) + ret |= S_IFDIR; + if (VXFS_ISBLK(vip)) + ret |= S_IFBLK; + if (VXFS_ISLNK(vip)) + ret |= S_IFLNK; + if (VXFS_ISREG(vip)) + ret |= S_IFREG; + if (VXFS_ISSOC(vip)) + ret |= S_IFSOCK; + + return (ret); +} + +static inline void dip2vip_cpy(struct vxfs_sb_info *sbi, + struct vxfs_inode_info *vip, struct vxfs_dinode *dip) +{ + struct inode *inode = &vip->vfs_inode; + + vip->vii_mode = fs32_to_cpu(sbi, dip->vdi_mode); + vip->vii_nlink = fs32_to_cpu(sbi, dip->vdi_nlink); + vip->vii_uid = fs32_to_cpu(sbi, dip->vdi_uid); + vip->vii_gid = fs32_to_cpu(sbi, dip->vdi_gid); + vip->vii_size = fs64_to_cpu(sbi, dip->vdi_size); + vip->vii_atime = fs32_to_cpu(sbi, dip->vdi_atime); + vip->vii_autime = fs32_to_cpu(sbi, dip->vdi_autime); + vip->vii_mtime = fs32_to_cpu(sbi, dip->vdi_mtime); + vip->vii_mutime = fs32_to_cpu(sbi, dip->vdi_mutime); + vip->vii_ctime = fs32_to_cpu(sbi, dip->vdi_ctime); + vip->vii_cutime = fs32_to_cpu(sbi, dip->vdi_cutime); + vip->vii_orgtype = dip->vdi_orgtype; + + vip->vii_blocks = fs32_to_cpu(sbi, dip->vdi_blocks); + vip->vii_gen = fs32_to_cpu(sbi, dip->vdi_gen); + + if (VXFS_ISDIR(vip)) + vip->vii_dotdot = fs32_to_cpu(sbi, dip->vdi_dotdot); + else if (!VXFS_ISREG(vip) && !VXFS_ISLNK(vip)) + vip->vii_rdev = fs32_to_cpu(sbi, dip->vdi_rdev); + + /* don't endian swap the fields that differ by orgtype */ + memcpy(&vip->vii_org, &dip->vdi_org, sizeof(vip->vii_org)); + + inode->i_mode = vxfs_transmod(vip); + i_uid_write(inode, (uid_t)vip->vii_uid); + i_gid_write(inode, (gid_t)vip->vii_gid); + + set_nlink(inode, vip->vii_nlink); + inode->i_size = vip->vii_size; + + inode->i_atime.tv_sec = vip->vii_atime; + inode_set_ctime(inode, vip->vii_ctime, 0); + inode->i_mtime.tv_sec = vip->vii_mtime; + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_nsec = 0; + + inode->i_blocks = vip->vii_blocks; + inode->i_generation = vip->vii_gen; +} + +/** + * vxfs_blkiget - find inode based on extent # + * @sbp: superblock of the filesystem we search in + * @extent: number of the extent to search + * @ino: inode number to search + * + * Description: + * vxfs_blkiget searches inode @ino in the filesystem described by + * @sbp in the extent @extent. + * Returns the matching VxFS inode on success, else a NULL pointer. + * + * NOTE: + * While __vxfs_iget uses the pagecache vxfs_blkiget uses the + * buffercache. This function should not be used outside the + * read_super() method, otherwise the data may be incoherent. + */ +struct inode * +vxfs_blkiget(struct super_block *sbp, u_long extent, ino_t ino) +{ + struct buffer_head *bp; + struct inode *inode; + u_long block, offset; + + inode = new_inode(sbp); + if (!inode) + return NULL; + inode->i_ino = get_next_ino(); + + block = extent + ((ino * VXFS_ISIZE) / sbp->s_blocksize); + offset = ((ino % (sbp->s_blocksize / VXFS_ISIZE)) * VXFS_ISIZE); + bp = sb_bread(sbp, block); + + if (bp && buffer_mapped(bp)) { + struct vxfs_inode_info *vip = VXFS_INO(inode); + struct vxfs_dinode *dip; + + dip = (struct vxfs_dinode *)(bp->b_data + offset); + dip2vip_cpy(VXFS_SBI(sbp), vip, dip); + vip->vfs_inode.i_mapping->a_ops = &vxfs_aops; +#ifdef DIAGNOSTIC + vxfs_dumpi(vip, ino); +#endif + brelse(bp); + return inode; + } + + printk(KERN_WARNING "vxfs: unable to read block %ld\n", block); + brelse(bp); + iput(inode); + return NULL; +} + +/** + * __vxfs_iget - generic find inode facility + * @ilistp: inode list + * @vip: VxFS inode to fill in + * @ino: inode number + * + * Description: + * Search the for inode number @ino in the filesystem + * described by @sbp. Use the specified inode table (@ilistp). + * Returns the matching inode on success, else an error code. + */ +static int +__vxfs_iget(struct inode *ilistp, struct vxfs_inode_info *vip, ino_t ino) +{ + struct page *pp; + u_long offset; + + offset = (ino % (PAGE_SIZE / VXFS_ISIZE)) * VXFS_ISIZE; + pp = vxfs_get_page(ilistp->i_mapping, ino * VXFS_ISIZE / PAGE_SIZE); + + if (!IS_ERR(pp)) { + struct vxfs_dinode *dip; + caddr_t kaddr = (char *)page_address(pp); + + dip = (struct vxfs_dinode *)(kaddr + offset); + dip2vip_cpy(VXFS_SBI(ilistp->i_sb), vip, dip); + vip->vfs_inode.i_mapping->a_ops = &vxfs_aops; +#ifdef DIAGNOSTIC + vxfs_dumpi(vip, ino); +#endif + vxfs_put_page(pp); + return 0; + } + + printk(KERN_WARNING "vxfs: error on page 0x%p for inode %ld\n", + pp, (unsigned long)ino); + return PTR_ERR(pp); +} + +/** + * vxfs_stiget - find inode using the structural inode list + * @sbp: VFS superblock + * @ino: inode # + * + * Description: + * Find inode @ino in the filesystem described by @sbp using + * the structural inode list. + * Returns the matching inode on success, else a NULL pointer. + */ +struct inode * +vxfs_stiget(struct super_block *sbp, ino_t ino) +{ + struct inode *inode; + int error; + + inode = new_inode(sbp); + if (!inode) + return NULL; + inode->i_ino = get_next_ino(); + + error = __vxfs_iget(VXFS_SBI(sbp)->vsi_stilist, VXFS_INO(inode), ino); + if (error) { + iput(inode); + return NULL; + } + + return inode; +} + +/** + * vxfs_iget - get an inode + * @sbp: the superblock to get the inode for + * @ino: the number of the inode to get + * + * Description: + * vxfs_read_inode creates an inode, reads the disk inode for @ino and fills + * in all relevant fields in the new inode. + */ +struct inode * +vxfs_iget(struct super_block *sbp, ino_t ino) +{ + struct vxfs_inode_info *vip; + const struct address_space_operations *aops; + struct inode *ip; + int error; + + ip = iget_locked(sbp, ino); + if (!ip) + return ERR_PTR(-ENOMEM); + if (!(ip->i_state & I_NEW)) + return ip; + + vip = VXFS_INO(ip); + error = __vxfs_iget(VXFS_SBI(sbp)->vsi_ilist, vip, ino); + if (error) { + iget_failed(ip); + return ERR_PTR(error); + } + + if (VXFS_ISIMMED(vip)) + aops = &vxfs_immed_aops; + else + aops = &vxfs_aops; + + if (S_ISREG(ip->i_mode)) { + ip->i_fop = &generic_ro_fops; + ip->i_mapping->a_ops = aops; + } else if (S_ISDIR(ip->i_mode)) { + ip->i_op = &vxfs_dir_inode_ops; + ip->i_fop = &vxfs_dir_operations; + ip->i_mapping->a_ops = aops; + } else if (S_ISLNK(ip->i_mode)) { + if (!VXFS_ISIMMED(vip)) { + ip->i_op = &page_symlink_inode_operations; + inode_nohighmem(ip); + ip->i_mapping->a_ops = &vxfs_aops; + } else { + ip->i_op = &simple_symlink_inode_operations; + ip->i_link = vip->vii_immed.vi_immed; + nd_terminate_link(ip->i_link, ip->i_size, + sizeof(vip->vii_immed.vi_immed) - 1); + } + } else + init_special_inode(ip, ip->i_mode, old_decode_dev(vip->vii_rdev)); + + unlock_new_inode(ip); + return ip; +} + +/** + * vxfs_evict_inode - remove inode from main memory + * @ip: inode to discard. + * + * Description: + * vxfs_evict_inode() is called on the final iput and frees the private + * inode area. + */ +void +vxfs_evict_inode(struct inode *ip) +{ + truncate_inode_pages_final(&ip->i_data); + clear_inode(ip); +} diff --git a/fs/freevxfs/vxfs_inode.h b/fs/freevxfs/vxfs_inode.h new file mode 100644 index 0000000000..1e9e138d2b --- /dev/null +++ b/fs/freevxfs/vxfs_inode.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ +#ifndef _VXFS_INODE_H_ +#define _VXFS_INODE_H_ + +/* + * Veritas filesystem driver - inode structure. + * + * This file contains the definition of the disk and core + * inodes of the Veritas Filesystem. + */ + + +#define VXFS_ISIZE 0x100 /* Inode size */ + +#define VXFS_NDADDR 10 /* Number of direct addrs in inode */ +#define VXFS_NIADDR 2 /* Number of indirect addrs in inode */ +#define VXFS_NIMMED 96 /* Size of immediate data in inode */ +#define VXFS_NTYPED 6 /* Num of typed extents */ + +#define VXFS_TYPED_OFFSETMASK (0x00FFFFFFFFFFFFFFULL) +#define VXFS_TYPED_TYPEMASK (0xFF00000000000000ULL) +#define VXFS_TYPED_TYPESHIFT 56 + +#define VXFS_TYPED_PER_BLOCK(sbp) \ + ((sbp)->s_blocksize / sizeof(struct vxfs_typed)) + +/* + * Possible extent descriptor types for %VXFS_ORG_TYPED extents. + */ +enum { + VXFS_TYPED_INDIRECT = 1, + VXFS_TYPED_DATA = 2, + VXFS_TYPED_INDIRECT_DEV4 = 3, + VXFS_TYPED_DATA_DEV4 = 4, +}; + +/* + * Data stored immediately in the inode. + */ +struct vxfs_immed { + __u8 vi_immed[VXFS_NIMMED]; +}; + +struct vxfs_ext4 { + __fs32 ve4_spare; /* ?? */ + __fs32 ve4_indsize; /* Indirect extent size */ + __fs32 ve4_indir[VXFS_NIADDR]; /* Indirect extents */ + struct direct { /* Direct extents */ + __fs32 extent; /* Extent number */ + __fs32 size; /* Size of extent */ + } ve4_direct[VXFS_NDADDR]; +}; + +struct vxfs_typed { + __fs64 vt_hdr; /* Header, 0xTTOOOOOOOOOOOOOO; T=type,O=offs */ + __fs32 vt_block; /* Extent block */ + __fs32 vt_size; /* Size in blocks */ +}; + +struct vxfs_typed_dev4 { + __fs64 vd4_hdr; /* Header, 0xTTOOOOOOOOOOOOOO; T=type,O=offs */ + __fs64 vd4_block; /* Extent block */ + __fs64 vd4_size; /* Size in blocks */ + __fs32 vd4_dev; /* Device ID */ + __u8 __pad1; +}; + +/* + * The inode as contained on the physical device. + */ +struct vxfs_dinode { + __fs32 vdi_mode; + __fs32 vdi_nlink; /* Link count */ + __fs32 vdi_uid; /* UID */ + __fs32 vdi_gid; /* GID */ + __fs64 vdi_size; /* Inode size in bytes */ + __fs32 vdi_atime; /* Last time accessed - sec */ + __fs32 vdi_autime; /* Last time accessed - usec */ + __fs32 vdi_mtime; /* Last modify time - sec */ + __fs32 vdi_mutime; /* Last modify time - usec */ + __fs32 vdi_ctime; /* Create time - sec */ + __fs32 vdi_cutime; /* Create time - usec */ + __u8 vdi_aflags; /* Allocation flags */ + __u8 vdi_orgtype; /* Organisation type */ + __fs16 vdi_eopflags; + __fs32 vdi_eopdata; + union { + __fs32 rdev; + __fs32 dotdot; + struct { + __u32 reserved; + __fs32 fixextsize; + } i_regular; + struct { + __fs32 matchino; + __fs32 fsetindex; + } i_vxspec; + __u64 align; + } vdi_ftarea; + __fs32 vdi_blocks; /* How much blocks does inode occupy */ + __fs32 vdi_gen; /* Inode generation */ + __fs64 vdi_version; /* Version */ + union { + struct vxfs_immed immed; + struct vxfs_ext4 ext4; + struct vxfs_typed typed[VXFS_NTYPED]; + } vdi_org; + __fs32 vdi_iattrino; +}; + +#define vdi_rdev vdi_ftarea.rdev +#define vdi_dotdot vdi_ftarea.dotdot +#define vdi_fixextsize vdi_ftarea.regular.fixextsize +#define vdi_matchino vdi_ftarea.vxspec.matchino +#define vdi_fsetindex vdi_ftarea.vxspec.fsetindex + +#define vdi_immed vdi_org.immed +#define vdi_ext4 vdi_org.ext4 +#define vdi_typed vdi_org.typed + + +/* + * The inode as represented in the main memory. + */ +struct vxfs_inode_info { + struct inode vfs_inode; + + __u32 vii_mode; + __u32 vii_nlink; /* Link count */ + __u32 vii_uid; /* UID */ + __u32 vii_gid; /* GID */ + __u64 vii_size; /* Inode size in bytes */ + __u32 vii_atime; /* Last time accessed - sec */ + __u32 vii_autime; /* Last time accessed - usec */ + __u32 vii_mtime; /* Last modify time - sec */ + __u32 vii_mutime; /* Last modify time - usec */ + __u32 vii_ctime; /* Create time - sec */ + __u32 vii_cutime; /* Create time - usec */ + __u8 vii_orgtype; /* Organisation type */ + union { + __u32 rdev; + __u32 dotdot; + } vii_ftarea; + __u32 vii_blocks; /* How much blocks does inode occupy */ + __u32 vii_gen; /* Inode generation */ + union { + struct vxfs_immed immed; + struct vxfs_ext4 ext4; + struct vxfs_typed typed[VXFS_NTYPED]; + } vii_org; +}; + +#define vii_rdev vii_ftarea.rdev +#define vii_dotdot vii_ftarea.dotdot + +#define vii_immed vii_org.immed +#define vii_ext4 vii_org.ext4 +#define vii_typed vii_org.typed + +static inline struct vxfs_inode_info *VXFS_INO(struct inode *inode) +{ + return container_of(inode, struct vxfs_inode_info, vfs_inode); +} + +#endif /* _VXFS_INODE_H_ */ diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c new file mode 100644 index 0000000000..f04ba2ed1e --- /dev/null +++ b/fs/freevxfs/vxfs_lookup.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ + +/* + * Veritas filesystem driver - lookup and other directory related code. + */ +#include <linux/fs.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/kernel.h> +#include <linux/pagemap.h> + +#include "vxfs.h" +#include "vxfs_dir.h" +#include "vxfs_inode.h" +#include "vxfs_extern.h" + +/* + * Number of VxFS blocks per page. + */ +#define VXFS_BLOCK_PER_PAGE(sbp) ((PAGE_SIZE / (sbp)->s_blocksize)) + + +static struct dentry * vxfs_lookup(struct inode *, struct dentry *, unsigned int); +static int vxfs_readdir(struct file *, struct dir_context *); + +const struct inode_operations vxfs_dir_inode_ops = { + .lookup = vxfs_lookup, +}; + +const struct file_operations vxfs_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate_shared = vxfs_readdir, +}; + + +/** + * vxfs_find_entry - find a mathing directory entry for a dentry + * @ip: directory inode + * @dp: dentry for which we want to find a direct + * @ppp: gets filled with the page the return value sits in + * + * Description: + * vxfs_find_entry finds a &struct vxfs_direct for the VFS directory + * cache entry @dp. @ppp will be filled with the page the return + * value resides in. + * + * Returns: + * The wanted direct on success, else a NULL pointer. + */ +static struct vxfs_direct * +vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp) +{ + u_long bsize = ip->i_sb->s_blocksize; + const char *name = dp->d_name.name; + int namelen = dp->d_name.len; + loff_t limit = VXFS_DIRROUND(ip->i_size); + struct vxfs_direct *de_exit = NULL; + loff_t pos = 0; + struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb); + + while (pos < limit) { + struct page *pp; + char *kaddr; + int pg_ofs = pos & ~PAGE_MASK; + + pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT); + if (IS_ERR(pp)) + return NULL; + kaddr = (char *)page_address(pp); + + while (pg_ofs < PAGE_SIZE && pos < limit) { + struct vxfs_direct *de; + + if ((pos & (bsize - 1)) < 4) { + struct vxfs_dirblk *dbp = + (struct vxfs_dirblk *) + (kaddr + (pos & ~PAGE_MASK)); + int overhead = VXFS_DIRBLKOV(sbi, dbp); + + pos += overhead; + pg_ofs += overhead; + } + de = (struct vxfs_direct *)(kaddr + pg_ofs); + + if (!de->d_reclen) { + pos += bsize - 1; + pos &= ~(bsize - 1); + break; + } + + pg_ofs += fs16_to_cpu(sbi, de->d_reclen); + pos += fs16_to_cpu(sbi, de->d_reclen); + if (!de->d_ino) + continue; + + if (namelen != fs16_to_cpu(sbi, de->d_namelen)) + continue; + if (!memcmp(name, de->d_name, namelen)) { + *ppp = pp; + de_exit = de; + break; + } + } + if (!de_exit) + vxfs_put_page(pp); + else + break; + } + + return de_exit; +} + +/** + * vxfs_inode_by_name - find inode number for dentry + * @dip: directory to search in + * @dp: dentry we search for + * + * Description: + * vxfs_inode_by_name finds out the inode number of + * the path component described by @dp in @dip. + * + * Returns: + * The wanted inode number on success, else Zero. + */ +static ino_t +vxfs_inode_by_name(struct inode *dip, struct dentry *dp) +{ + struct vxfs_direct *de; + struct page *pp; + ino_t ino = 0; + + de = vxfs_find_entry(dip, dp, &pp); + if (de) { + ino = fs32_to_cpu(VXFS_SBI(dip->i_sb), de->d_ino); + kunmap(pp); + put_page(pp); + } + + return (ino); +} + +/** + * vxfs_lookup - lookup pathname component + * @dip: dir in which we lookup + * @dp: dentry we lookup + * @flags: lookup flags + * + * Description: + * vxfs_lookup tries to lookup the pathname component described + * by @dp in @dip. + * + * Returns: + * A NULL-pointer on success, else a negative error code encoded + * in the return pointer. + */ +static struct dentry * +vxfs_lookup(struct inode *dip, struct dentry *dp, unsigned int flags) +{ + struct inode *ip = NULL; + ino_t ino; + + if (dp->d_name.len > VXFS_NAMELEN) + return ERR_PTR(-ENAMETOOLONG); + + ino = vxfs_inode_by_name(dip, dp); + if (ino) + ip = vxfs_iget(dip->i_sb, ino); + return d_splice_alias(ip, dp); +} + +/** + * vxfs_readdir - read a directory + * @fp: the directory to read + * @retp: return buffer + * @filler: filldir callback + * + * Description: + * vxfs_readdir fills @retp with directory entries from @fp + * using the VFS supplied callback @filler. + * + * Returns: + * Zero. + */ +static int +vxfs_readdir(struct file *fp, struct dir_context *ctx) +{ + struct inode *ip = file_inode(fp); + struct super_block *sbp = ip->i_sb; + u_long bsize = sbp->s_blocksize; + loff_t pos, limit; + struct vxfs_sb_info *sbi = VXFS_SBI(sbp); + + if (ctx->pos == 0) { + if (!dir_emit_dot(fp, ctx)) + goto out; + ctx->pos++; + } + if (ctx->pos == 1) { + if (!dir_emit(ctx, "..", 2, VXFS_INO(ip)->vii_dotdot, DT_DIR)) + goto out; + ctx->pos++; + } + + limit = VXFS_DIRROUND(ip->i_size); + if (ctx->pos > limit) + goto out; + + pos = ctx->pos & ~3L; + + while (pos < limit) { + struct page *pp; + char *kaddr; + int pg_ofs = pos & ~PAGE_MASK; + int rc = 0; + + pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT); + if (IS_ERR(pp)) + return -ENOMEM; + + kaddr = (char *)page_address(pp); + + while (pg_ofs < PAGE_SIZE && pos < limit) { + struct vxfs_direct *de; + + if ((pos & (bsize - 1)) < 4) { + struct vxfs_dirblk *dbp = + (struct vxfs_dirblk *) + (kaddr + (pos & ~PAGE_MASK)); + int overhead = VXFS_DIRBLKOV(sbi, dbp); + + pos += overhead; + pg_ofs += overhead; + } + de = (struct vxfs_direct *)(kaddr + pg_ofs); + + if (!de->d_reclen) { + pos += bsize - 1; + pos &= ~(bsize - 1); + break; + } + + pg_ofs += fs16_to_cpu(sbi, de->d_reclen); + pos += fs16_to_cpu(sbi, de->d_reclen); + if (!de->d_ino) + continue; + + rc = dir_emit(ctx, de->d_name, + fs16_to_cpu(sbi, de->d_namelen), + fs32_to_cpu(sbi, de->d_ino), + DT_UNKNOWN); + if (!rc) { + /* the dir entry was not read, fix pos. */ + pos -= fs16_to_cpu(sbi, de->d_reclen); + break; + } + } + vxfs_put_page(pp); + if (!rc) + break; + } + + ctx->pos = pos | 2; + +out: + return 0; +} diff --git a/fs/freevxfs/vxfs_olt.c b/fs/freevxfs/vxfs_olt.c new file mode 100644 index 0000000000..23f35187c2 --- /dev/null +++ b/fs/freevxfs/vxfs_olt.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ + +/* + * Veritas filesystem driver - object location table support. + */ +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/kernel.h> + +#include "vxfs.h" +#include "vxfs_olt.h" +#include "vxfs_extern.h" + + +static inline void +vxfs_get_fshead(struct vxfs_oltfshead *fshp, struct vxfs_sb_info *infp) +{ + BUG_ON(infp->vsi_fshino); + infp->vsi_fshino = fs32_to_cpu(infp, fshp->olt_fsino[0]); +} + +static inline void +vxfs_get_ilist(struct vxfs_oltilist *ilistp, struct vxfs_sb_info *infp) +{ + BUG_ON(infp->vsi_iext); + infp->vsi_iext = fs32_to_cpu(infp, ilistp->olt_iext[0]); +} + +static inline u_long +vxfs_oblock(struct super_block *sbp, daddr_t block, u_long bsize) +{ + BUG_ON(sbp->s_blocksize % bsize); + return (block * (sbp->s_blocksize / bsize)); +} + + +/** + * vxfs_read_olt - read olt + * @sbp: superblock of the filesystem + * @bsize: blocksize of the filesystem + * + * Description: + * vxfs_read_olt reads the olt of the filesystem described by @sbp + * into main memory and does some basic setup. + * + * Returns: + * Zero on success, else a negative error code. + */ +int +vxfs_read_olt(struct super_block *sbp, u_long bsize) +{ + struct vxfs_sb_info *infp = VXFS_SBI(sbp); + struct buffer_head *bp; + struct vxfs_olt *op; + char *oaddr, *eaddr; + + bp = sb_bread(sbp, vxfs_oblock(sbp, infp->vsi_oltext, bsize)); + if (!bp || !bp->b_data) + goto fail; + + op = (struct vxfs_olt *)bp->b_data; + if (fs32_to_cpu(infp, op->olt_magic) != VXFS_OLT_MAGIC) { + printk(KERN_NOTICE "vxfs: ivalid olt magic number\n"); + goto fail; + } + + /* + * It is in theory possible that vsi_oltsize is > 1. + * I've not seen any such filesystem yet and I'm lazy.. --hch + */ + if (infp->vsi_oltsize > 1) { + printk(KERN_NOTICE "vxfs: oltsize > 1 detected.\n"); + printk(KERN_NOTICE "vxfs: please notify hch@infradead.org\n"); + goto fail; + } + + oaddr = bp->b_data + fs32_to_cpu(infp, op->olt_size); + eaddr = bp->b_data + (infp->vsi_oltsize * sbp->s_blocksize); + + while (oaddr < eaddr) { + struct vxfs_oltcommon *ocp = + (struct vxfs_oltcommon *)oaddr; + + switch (fs32_to_cpu(infp, ocp->olt_type)) { + case VXFS_OLT_FSHEAD: + vxfs_get_fshead((struct vxfs_oltfshead *)oaddr, infp); + break; + case VXFS_OLT_ILIST: + vxfs_get_ilist((struct vxfs_oltilist *)oaddr, infp); + break; + } + + oaddr += fs32_to_cpu(infp, ocp->olt_size); + } + + brelse(bp); + return (infp->vsi_fshino && infp->vsi_iext) ? 0 : -EINVAL; + +fail: + brelse(bp); + return -EINVAL; +} diff --git a/fs/freevxfs/vxfs_olt.h b/fs/freevxfs/vxfs_olt.h new file mode 100644 index 0000000000..53afba08d6 --- /dev/null +++ b/fs/freevxfs/vxfs_olt.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ +#ifndef _VXFS_OLT_H_ +#define _VXFS_OLT_H_ + +/* + * Veritas filesystem driver - Object Location Table data structures. + * + * This file contains definitions for the Object Location Table used + * by the Veritas Filesystem version 2 and newer. + */ + + +/* + * OLT magic number (vxfs_olt->olt_magic). + */ +#define VXFS_OLT_MAGIC 0xa504FCF5 + +/* + * VxFS OLT entry types. + */ +enum { + VXFS_OLT_FREE = 1, + VXFS_OLT_FSHEAD = 2, + VXFS_OLT_CUT = 3, + VXFS_OLT_ILIST = 4, + VXFS_OLT_DEV = 5, + VXFS_OLT_SB = 6 +}; + +/* + * VxFS OLT header. + * + * The Object Location Table header is placed at the beginning of each + * OLT extent. It is used to fing certain filesystem-wide metadata, e.g. + * the initial inode list, the fileset header or the device configuration. + */ +struct vxfs_olt { + __fs32 olt_magic; /* magic number */ + __fs32 olt_size; /* size of this entry */ + __fs32 olt_checksum; /* checksum of extent */ + __u32 __unused1; /* ??? */ + __fs32 olt_mtime; /* time of last mod. (sec) */ + __fs32 olt_mutime; /* time of last mod. (usec) */ + __fs32 olt_totfree; /* free space in OLT extent */ + __fs32 olt_extents[2]; /* addr of this extent, replica */ + __fs32 olt_esize; /* size of this extent */ + __fs32 olt_next[2]; /* addr of next extent, replica */ + __fs32 olt_nsize; /* size of next extent */ + __u32 __unused2; /* align to 8 byte boundary */ +}; + +/* + * VxFS common OLT entry (on disk). + */ +struct vxfs_oltcommon { + __fs32 olt_type; /* type of this record */ + __fs32 olt_size; /* size of this record */ +}; + +/* + * VxFS free OLT entry (on disk). + */ +struct vxfs_oltfree { + __fs32 olt_type; /* type of this record */ + __fs32 olt_fsize; /* size of this free record */ +}; + +/* + * VxFS initial-inode list (on disk). + */ +struct vxfs_oltilist { + __fs32 olt_type; /* type of this record */ + __fs32 olt_size; /* size of this record */ + __fs32 olt_iext[2]; /* initial inode list, replica */ +}; + +/* + * Current Usage Table + */ +struct vxfs_oltcut { + __fs32 olt_type; /* type of this record */ + __fs32 olt_size; /* size of this record */ + __fs32 olt_cutino; /* inode of current usage table */ + __u8 __pad; /* unused, 8 byte align */ +}; + +/* + * Inodes containing Superblock, Intent log and OLTs + */ +struct vxfs_oltsb { + __fs32 olt_type; /* type of this record */ + __fs32 olt_size; /* size of this record */ + __fs32 olt_sbino; /* inode of superblock file */ + __u32 __unused1; /* ??? */ + __fs32 olt_logino[2]; /* inode of log file,replica */ + __fs32 olt_oltino[2]; /* inode of OLT, replica */ +}; + +/* + * Inode containing device configuration + it's replica + */ +struct vxfs_oltdev { + __fs32 olt_type; /* type of this record */ + __fs32 olt_size; /* size of this record */ + __fs32 olt_devino[2]; /* inode of device config files */ +}; + +/* + * Fileset header + */ +struct vxfs_oltfshead { + __fs32 olt_type; /* type number */ + __fs32 olt_size; /* size of this record */ + __fs32 olt_fsino[2]; /* inodes of fileset header */ +}; + +#endif /* _VXFS_OLT_H_ */ diff --git a/fs/freevxfs/vxfs_subr.c b/fs/freevxfs/vxfs_subr.c new file mode 100644 index 0000000000..f439877ea6 --- /dev/null +++ b/fs/freevxfs/vxfs_subr.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + */ + +/* + * Veritas filesystem driver - shared subroutines. + */ +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/kernel.h> +#include <linux/pagemap.h> + +#include "vxfs_extern.h" + + +static int vxfs_read_folio(struct file *, struct folio *); +static sector_t vxfs_bmap(struct address_space *, sector_t); + +const struct address_space_operations vxfs_aops = { + .read_folio = vxfs_read_folio, + .bmap = vxfs_bmap, +}; + +inline void +vxfs_put_page(struct page *pp) +{ + kunmap(pp); + put_page(pp); +} + +/** + * vxfs_get_page - read a page into memory. + * @mapping: mapping to read from + * @n: page number + * + * Description: + * vxfs_get_page reads the @n th page of @ip into the pagecache. + * + * Returns: + * The wanted page on success, else a NULL pointer. + */ +struct page * +vxfs_get_page(struct address_space *mapping, u_long n) +{ + struct page * pp; + + pp = read_mapping_page(mapping, n, NULL); + + if (!IS_ERR(pp)) { + kmap(pp); + /** if (!PageChecked(pp)) **/ + /** vxfs_check_page(pp); **/ + } + + return (pp); +} + +/** + * vxfs_bread - read buffer for a give inode,block tuple + * @ip: inode + * @block: logical block + * + * Description: + * The vxfs_bread function reads block no @block of + * @ip into the buffercache. + * + * Returns: + * The resulting &struct buffer_head. + */ +struct buffer_head * +vxfs_bread(struct inode *ip, int block) +{ + struct buffer_head *bp; + daddr_t pblock; + + pblock = vxfs_bmap1(ip, block); + bp = sb_bread(ip->i_sb, pblock); + + return (bp); +} + +/** + * vxfs_getblk - locate buffer for given inode,block tuple + * @ip: inode + * @iblock: logical block + * @bp: buffer skeleton + * @create: %TRUE if blocks may be newly allocated. + * + * Description: + * The vxfs_getblk function fills @bp with the right physical + * block and device number to perform a lowlevel read/write on + * it. + * + * Returns: + * Zero on success, else a negativ error code (-EIO). + */ +static int +vxfs_getblk(struct inode *ip, sector_t iblock, + struct buffer_head *bp, int create) +{ + daddr_t pblock; + + pblock = vxfs_bmap1(ip, iblock); + if (pblock != 0) { + map_bh(bp, ip->i_sb, pblock); + return 0; + } + + return -EIO; +} + +/** + * vxfs_read_folio - read one page synchronously into the pagecache + * @file: file context (unused) + * @folio: folio to fill in. + * + * Description: + * The vxfs_read_folio routine reads @folio synchronously into the + * pagecache. + * + * Returns: + * Zero on success, else a negative error code. + * + * Locking status: + * @folio is locked and will be unlocked. + */ +static int vxfs_read_folio(struct file *file, struct folio *folio) +{ + return block_read_full_folio(folio, vxfs_getblk); +} + +/** + * vxfs_bmap - perform logical to physical block mapping + * @mapping: logical to physical mapping to use + * @block: logical block (relative to @mapping). + * + * Description: + * Vxfs_bmap find out the corresponding phsical block to the + * @mapping, @block pair. + * + * Returns: + * Physical block number on success, else Zero. + * + * Locking status: + * We are under the bkl. + */ +static sector_t +vxfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, vxfs_getblk); +} diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c new file mode 100644 index 0000000000..310d73e254 --- /dev/null +++ b/fs/freevxfs/vxfs_super.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000-2001 Christoph Hellwig. + * Copyright (c) 2016 Krzysztof Blaszkowski + */ + +/* + * Veritas filesystem driver - superblock related routines. + */ +#include <linux/init.h> +#include <linux/module.h> + +#include <linux/blkdev.h> +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/vfs.h> +#include <linux/mount.h> + +#include "vxfs.h" +#include "vxfs_extern.h" +#include "vxfs_dir.h" +#include "vxfs_inode.h" + + +MODULE_AUTHOR("Christoph Hellwig, Krzysztof Blaszkowski"); +MODULE_DESCRIPTION("Veritas Filesystem (VxFS) driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct kmem_cache *vxfs_inode_cachep; + +/** + * vxfs_put_super - free superblock resources + * @sbp: VFS superblock. + * + * Description: + * vxfs_put_super frees all resources allocated for @sbp + * after the last instance of the filesystem is unmounted. + */ + +static void +vxfs_put_super(struct super_block *sbp) +{ + struct vxfs_sb_info *infp = VXFS_SBI(sbp); + + iput(infp->vsi_fship); + iput(infp->vsi_ilist); + iput(infp->vsi_stilist); + + brelse(infp->vsi_bp); + kfree(infp); +} + +/** + * vxfs_statfs - get filesystem information + * @dentry: VFS dentry to locate superblock + * @bufp: output buffer + * + * Description: + * vxfs_statfs fills the statfs buffer @bufp with information + * about the filesystem described by @dentry. + * + * Returns: + * Zero. + * + * Locking: + * No locks held. + * + * Notes: + * This is everything but complete... + */ +static int +vxfs_statfs(struct dentry *dentry, struct kstatfs *bufp) +{ + struct vxfs_sb_info *infp = VXFS_SBI(dentry->d_sb); + struct vxfs_sb *raw_sb = infp->vsi_raw; + + bufp->f_type = VXFS_SUPER_MAGIC; + bufp->f_bsize = dentry->d_sb->s_blocksize; + bufp->f_blocks = fs32_to_cpu(infp, raw_sb->vs_dsize); + bufp->f_bfree = fs32_to_cpu(infp, raw_sb->vs_free); + bufp->f_bavail = 0; + bufp->f_files = 0; + bufp->f_ffree = fs32_to_cpu(infp, raw_sb->vs_ifree); + bufp->f_namelen = VXFS_NAMELEN; + + return 0; +} + +static int vxfs_remount(struct super_block *sb, int *flags, char *data) +{ + sync_filesystem(sb); + *flags |= SB_RDONLY; + return 0; +} + +static struct inode *vxfs_alloc_inode(struct super_block *sb) +{ + struct vxfs_inode_info *vi; + + vi = alloc_inode_sb(sb, vxfs_inode_cachep, GFP_KERNEL); + if (!vi) + return NULL; + inode_init_once(&vi->vfs_inode); + return &vi->vfs_inode; +} + +static void vxfs_free_inode(struct inode *inode) +{ + kmem_cache_free(vxfs_inode_cachep, VXFS_INO(inode)); +} + +static const struct super_operations vxfs_super_ops = { + .alloc_inode = vxfs_alloc_inode, + .free_inode = vxfs_free_inode, + .evict_inode = vxfs_evict_inode, + .put_super = vxfs_put_super, + .statfs = vxfs_statfs, + .remount_fs = vxfs_remount, +}; + +static int vxfs_try_sb_magic(struct super_block *sbp, int silent, + unsigned blk, __fs32 magic) +{ + struct buffer_head *bp; + struct vxfs_sb *rsbp; + struct vxfs_sb_info *infp = VXFS_SBI(sbp); + int rc = -ENOMEM; + + bp = sb_bread(sbp, blk); + do { + if (!bp || !buffer_mapped(bp)) { + if (!silent) { + printk(KERN_WARNING + "vxfs: unable to read disk superblock at %u\n", + blk); + } + break; + } + + rc = -EINVAL; + rsbp = (struct vxfs_sb *)bp->b_data; + if (rsbp->vs_magic != magic) { + if (!silent) + printk(KERN_NOTICE + "vxfs: WRONG superblock magic %08x at %u\n", + rsbp->vs_magic, blk); + break; + } + + rc = 0; + infp->vsi_raw = rsbp; + infp->vsi_bp = bp; + } while (0); + + if (rc) { + infp->vsi_raw = NULL; + infp->vsi_bp = NULL; + brelse(bp); + } + + return rc; +} + +/** + * vxfs_fill_super - read superblock into memory and initialize filesystem + * @sbp: VFS superblock (to fill) + * @dp: fs private mount data + * @silent: do not complain loudly when sth is wrong + * + * Description: + * We are called on the first mount of a filesystem to read the + * superblock into memory and do some basic setup. + * + * Returns: + * The superblock on success, else %NULL. + * + * Locking: + * We are under @sbp->s_lock. + */ +static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) +{ + struct vxfs_sb_info *infp; + struct vxfs_sb *rsbp; + u_long bsize; + struct inode *root; + int ret = -EINVAL; + u32 j; + + sbp->s_flags |= SB_RDONLY; + + infp = kzalloc(sizeof(*infp), GFP_KERNEL); + if (!infp) { + printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n"); + return -ENOMEM; + } + + bsize = sb_min_blocksize(sbp, BLOCK_SIZE); + if (!bsize) { + printk(KERN_WARNING "vxfs: unable to set blocksize\n"); + goto out; + } + + sbp->s_op = &vxfs_super_ops; + sbp->s_fs_info = infp; + sbp->s_time_min = 0; + sbp->s_time_max = U32_MAX; + + if (!vxfs_try_sb_magic(sbp, silent, 1, + (__force __fs32)cpu_to_le32(VXFS_SUPER_MAGIC))) { + /* Unixware, x86 */ + infp->byte_order = VXFS_BO_LE; + } else if (!vxfs_try_sb_magic(sbp, silent, 8, + (__force __fs32)cpu_to_be32(VXFS_SUPER_MAGIC))) { + /* HP-UX, parisc */ + infp->byte_order = VXFS_BO_BE; + } else { + if (!silent) + printk(KERN_NOTICE "vxfs: can't find superblock.\n"); + goto out; + } + + rsbp = infp->vsi_raw; + j = fs32_to_cpu(infp, rsbp->vs_version); + if ((j < 2 || j > 4) && !silent) { + printk(KERN_NOTICE "vxfs: unsupported VxFS version (%d)\n", j); + goto out; + } + +#ifdef DIAGNOSTIC + printk(KERN_DEBUG "vxfs: supported VxFS version (%d)\n", j); + printk(KERN_DEBUG "vxfs: blocksize: %d\n", + fs32_to_cpu(infp, rsbp->vs_bsize)); +#endif + + sbp->s_magic = fs32_to_cpu(infp, rsbp->vs_magic); + + infp->vsi_oltext = fs32_to_cpu(infp, rsbp->vs_oltext[0]); + infp->vsi_oltsize = fs32_to_cpu(infp, rsbp->vs_oltsize); + + j = fs32_to_cpu(infp, rsbp->vs_bsize); + if (!sb_set_blocksize(sbp, j)) { + printk(KERN_WARNING "vxfs: unable to set final block size\n"); + goto out; + } + + if (vxfs_read_olt(sbp, bsize)) { + printk(KERN_WARNING "vxfs: unable to read olt\n"); + goto out; + } + + if (vxfs_read_fshead(sbp)) { + printk(KERN_WARNING "vxfs: unable to read fshead\n"); + goto out; + } + + root = vxfs_iget(sbp, VXFS_ROOT_INO); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto out; + } + sbp->s_root = d_make_root(root); + if (!sbp->s_root) { + printk(KERN_WARNING "vxfs: unable to get root dentry.\n"); + goto out_free_ilist; + } + + return 0; + +out_free_ilist: + iput(infp->vsi_fship); + iput(infp->vsi_ilist); + iput(infp->vsi_stilist); +out: + brelse(infp->vsi_bp); + kfree(infp); + return ret; +} + +/* + * The usual module blurb. + */ +static struct dentry *vxfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_bdev(fs_type, flags, dev_name, data, vxfs_fill_super); +} + +static struct file_system_type vxfs_fs_type = { + .owner = THIS_MODULE, + .name = "vxfs", + .mount = vxfs_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +MODULE_ALIAS_FS("vxfs"); /* makes mount -t vxfs autoload the module */ +MODULE_ALIAS("vxfs"); + +static int __init +vxfs_init(void) +{ + int rv; + + vxfs_inode_cachep = kmem_cache_create_usercopy("vxfs_inode", + sizeof(struct vxfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, + offsetof(struct vxfs_inode_info, vii_immed.vi_immed), + sizeof_field(struct vxfs_inode_info, + vii_immed.vi_immed), + NULL); + if (!vxfs_inode_cachep) + return -ENOMEM; + rv = register_filesystem(&vxfs_fs_type); + if (rv < 0) + kmem_cache_destroy(vxfs_inode_cachep); + return rv; +} + +static void __exit +vxfs_cleanup(void) +{ + unregister_filesystem(&vxfs_fs_type); + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(vxfs_inode_cachep); +} + +module_init(vxfs_init); +module_exit(vxfs_cleanup); |