summaryrefslogtreecommitdiffstats
path: root/fs/freevxfs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /fs/freevxfs
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fs/freevxfs')
-rw-r--r--fs/freevxfs/Kconfig26
-rw-r--r--fs/freevxfs/Makefile9
-rw-r--r--fs/freevxfs/vxfs.h257
-rw-r--r--fs/freevxfs/vxfs_bmap.c271
-rw-r--r--fs/freevxfs/vxfs_dir.h68
-rw-r--r--fs/freevxfs/vxfs_extern.h49
-rw-r--r--fs/freevxfs/vxfs_fshead.c166
-rw-r--r--fs/freevxfs/vxfs_fshead.h43
-rw-r--r--fs/freevxfs/vxfs_immed.c53
-rw-r--r--fs/freevxfs/vxfs_inode.c317
-rw-r--r--fs/freevxfs/vxfs_inode.h169
-rw-r--r--fs/freevxfs/vxfs_lookup.c272
-rw-r--r--fs/freevxfs/vxfs_olt.c105
-rw-r--r--fs/freevxfs/vxfs_olt.h120
-rw-r--r--fs/freevxfs/vxfs_subr.c152
-rw-r--r--fs/freevxfs/vxfs_super.c334
16 files changed, 2411 insertions, 0 deletions
diff --git a/fs/freevxfs/Kconfig b/fs/freevxfs/Kconfig
new file mode 100644
index 000000000..c05c71d57
--- /dev/null
+++ b/fs/freevxfs/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VXFS_FS
+ tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
+ depends on BLOCK
+ 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 endianess 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 000000000..e6ee59291
--- /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 000000000..bffd156d6
--- /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 000000000..de2a5bccb
--- /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 000000000..fbcd60336
--- /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 000000000..3a2180c5e
--- /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 000000000..c1174a3f8
--- /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 000000000..dfd214759
--- /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 000000000..9b49ec36e
--- /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 000000000..ceb6a1264
--- /dev/null
+++ b/fs/freevxfs/vxfs_inode.c
@@ -0,0 +1,317 @@
+// 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->i_ctime.tv_sec = vip->vii_ctime;
+ inode->i_mtime.tv_sec = vip->vii_mtime;
+ inode->i_atime.tv_nsec = 0;
+ inode->i_ctime.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 000000000..1e9e138d2
--- /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 000000000..f04ba2ed1
--- /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 000000000..23f35187c
--- /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 000000000..53afba08d
--- /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 000000000..c99282df7
--- /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.
+ * @ip: inode 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_get_block - 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_get_block 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 000000000..c3b82f716
--- /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_read_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);