summaryrefslogtreecommitdiffstats
path: root/fs/ufs
diff options
context:
space:
mode:
authorNiklas Cassel <cassel@kernel.org>2026-09-16 15:00:32 +0200
committerNiklas Cassel <cassel@kernel.org>2026-09-16 16:18:00 +0200
commit8d836581f9b1f57ceaa2b47654754ef1260b410b (patch)
tree1dbd4d0dfb99c2e238356cbe947e5b0be3e3a571 /fs/ufs
downloadlinux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.tar.gz
linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.zip
ata: libata-scsi: fix ata_dsm_trim_pages() kernel-docgrafted
make htmldocs fails with: Documentation/driver-api/libata:604: ./drivers/ata/libata-scsi.c:2301: ERROR: Unexpected indentation. [docutils] The Return: section of ata_dsm_trim_pages() ends its first sentence with a colon and continues with an indented bullet list. reStructuredText requires a blank line before an indented block, so docutils chokes on the list. Simply adding the missing blank line does not work either: Return: is a kernel-doc "special section", which is terminated by the first blank line, so the bullet list would end up in the Description section, detached from the sentence introducing it. Spell the two bounds out as prose instead, so that the Return: section stays self-contained. Documentation-only change. Fixes: e64e6b5dc867 ("ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND") Reported-by: Thomas Huth <thuth@redhat.com> Closes: https://lore.kernel.org/linux-ide/b0a0b8e8-cc4f-4c7b-8bc5-fee0712d405a@redhat.com/ Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Link: https://lore.kernel.org/r/20260916130031.29990-2-cassel@kernel.org Signed-off-by: Niklas Cassel <cassel@kernel.org>
Diffstat (limited to 'fs/ufs')
-rw-r--r--fs/ufs/Kconfig45
-rw-r--r--fs/ufs/Makefile10
-rw-r--r--fs/ufs/balloc.c936
-rw-r--r--fs/ufs/cylinder.c207
-rw-r--r--fs/ufs/dir.c658
-rw-r--r--fs/ufs/file.c48
-rw-r--r--fs/ufs/ialloc.c355
-rw-r--r--fs/ufs/inode.c1222
-rw-r--r--fs/ufs/namei.c329
-rw-r--r--fs/ufs/super.c1495
-rw-r--r--fs/ufs/swab.h116
-rw-r--r--fs/ufs/ufs.h164
-rw-r--r--fs/ufs/ufs_fs.h958
-rw-r--r--fs/ufs/util.c223
-rw-r--r--fs/ufs/util.h597
15 files changed, 7363 insertions, 0 deletions
diff --git a/fs/ufs/Kconfig b/fs/ufs/Kconfig
new file mode 100644
index 000000000..9301e7ecd
--- /dev/null
+++ b/fs/ufs/Kconfig
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config UFS_FS
+ tristate "UFS file system support (read only)"
+ depends on BLOCK
+ select BUFFER_HEAD
+ help
+ BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD,
+ OpenBSD and NeXTstep) use a file system called UFS. Some System V
+ Unixes can create and mount hard disk partitions and diskettes using
+ this file system as well. Saying Y here will allow you to read from
+ these partitions; if you also want to write to them, say Y to the
+ experimental "UFS file system write support", below. Please read the
+ file <file:Documentation/admin-guide/ufs.rst> for more information.
+
+ The recently released UFS2 variant (used in FreeBSD 5.x) is
+ READ-ONLY supported.
+
+ Note that this option is generally not needed for floppies, since a
+ good portable way to transport files and directories between unixes
+ (and even other operating systems) is given by the tar program ("man
+ tar" or preferably "info tar").
+
+ When accessing NeXTstep files, you may need to convert them from the
+ NeXT character set to the Latin1 character set; use the program
+ recode ("info recode") for this purpose.
+
+ To compile the UFS file system support as a module, choose M here: the
+ module will be called ufs.
+
+ If you haven't heard about all of this before, it's safe to say N.
+
+config UFS_FS_WRITE
+ bool "UFS file system write support (DANGEROUS)"
+ depends on UFS_FS
+ help
+ Say Y here if you want to try writing to UFS partitions. This is
+ experimental, so you should back up your UFS partitions beforehand.
+
+config UFS_DEBUG
+ bool "UFS debugging"
+ depends on UFS_FS
+ help
+ If you are experiencing any problems with the UFS filesystem, say
+ Y here. This will result in _many_ additional debugging messages to be
+ written to the system log.
diff --git a/fs/ufs/Makefile b/fs/ufs/Makefile
new file mode 100644
index 000000000..042344c85
--- /dev/null
+++ b/fs/ufs/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Linux ufs filesystem routines.
+#
+
+obj-$(CONFIG_UFS_FS) += ufs.o
+
+ufs-objs := balloc.o cylinder.o dir.o file.o ialloc.o inode.o \
+ namei.o super.o util.o
+ccflags-$(CONFIG_UFS_DEBUG) += -DDEBUG
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
new file mode 100644
index 000000000..628edfde3
--- /dev/null
+++ b/fs/ufs/balloc.c
@@ -0,0 +1,936 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/balloc.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * UFS2 write support Evgeniy Dushistov <dushistov@mail.ru>, 2007
+ */
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/buffer_head.h>
+#include <linux/capability.h>
+#include <linux/bitops.h>
+#include <linux/bio.h>
+#include <asm/byteorder.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+#define INVBLOCK ((u64)-1L)
+
+static u64 ufs_add_fragments(struct inode *, u64, unsigned, unsigned);
+static u64 ufs_alloc_fragments(struct inode *, unsigned, u64, unsigned, int *);
+static u64 ufs_alloccg_block(struct inode *, struct ufs_cg_private_info *, u64, int *);
+static u64 ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, u64, unsigned);
+static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[];
+static void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int);
+
+static void adjust_free_blocks(struct super_block *sb,
+ struct ufs_cylinder_group *ucg,
+ struct ufs_cg_private_info *ucpi,
+ unsigned fragment, int delta)
+{
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+
+ if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct(sb, ucpi, fragment, delta);
+
+ fs32_add(sb, &ucg->cg_cs.cs_nbfree, delta);
+ uspi->cs_total.cs_nbfree += delta;
+ fs32_add(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, delta);
+
+ if (uspi->fs_magic != UFS2_MAGIC) {
+ unsigned cylno = ufs_cbtocylno(fragment);
+
+ fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
+ ufs_cbtorpos(fragment)), delta);
+ fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), delta);
+ }
+}
+
+/*
+ * Free 'count' fragments from fragment number 'fragment'
+ */
+void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned cgno, bit, end_bit, bbase, blkmap, i;
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+
+ UFSD("ENTER, fragment %llu, count %u\n",
+ (unsigned long long)fragment, count);
+
+ if (ufs_fragnum(fragment) + count > uspi->s_fpb)
+ ufs_error (sb, "ufs_free_fragments", "internal error");
+
+ mutex_lock(&UFS_SB(sb)->s_lock);
+
+ cgno = ufs_dtog(uspi, fragment);
+ bit = ufs_dtogd(uspi, fragment);
+ if (cgno >= uspi->s_ncg) {
+ ufs_panic (sb, "ufs_free_fragments", "freeing blocks are outside device");
+ goto failed;
+ }
+
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ goto failed;
+ ucg = ubh_get_ucg (UCPI_UBH(ucpi));
+ if (!ufs_cg_chkmagic(sb, ucg)) {
+ ufs_panic (sb, "ufs_free_fragments", "internal error, bad magic number on cg %u", cgno);
+ goto failed;
+ }
+
+ end_bit = bit + count;
+ bbase = ufs_blknum (bit);
+ blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
+ ufs_fragacct (sb, blkmap, ucg->cg_frsum, -1);
+ for (i = bit; i < end_bit; i++) {
+ if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, i))
+ ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, i);
+ else
+ ufs_error (sb, "ufs_free_fragments",
+ "bit already cleared for fragment %u", i);
+ }
+
+ inode_sub_bytes(inode, count << uspi->s_fshift);
+ fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
+ uspi->cs_total.cs_nffree += count;
+ fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
+ blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
+ ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1);
+
+ /*
+ * Trying to reassemble free fragments into block
+ */
+ if (ubh_isblockset(uspi, ucpi, bbase)) {
+ fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb);
+ uspi->cs_total.cs_nffree -= uspi->s_fpb;
+ fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb);
+ adjust_free_blocks(sb, ucg, ucpi, bbase, 1);
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+ ufs_mark_sb_dirty(sb);
+
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT\n");
+ return;
+
+failed:
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT (FAILED)\n");
+ return;
+}
+
+/*
+ * Free 'count' fragments from fragment number 'fragment' (free whole blocks)
+ */
+void ufs_free_blocks(struct inode *inode, u64 fragment, unsigned count)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned overflow, cgno, bit, end_bit, i;
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+
+ UFSD("ENTER, fragment %llu, count %u\n",
+ (unsigned long long)fragment, count);
+
+ if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
+ ufs_error (sb, "ufs_free_blocks", "internal error, "
+ "fragment %llu, count %u\n",
+ (unsigned long long)fragment, count);
+ goto failed;
+ }
+
+ mutex_lock(&UFS_SB(sb)->s_lock);
+
+do_more:
+ overflow = 0;
+ cgno = ufs_dtog(uspi, fragment);
+ bit = ufs_dtogd(uspi, fragment);
+ if (cgno >= uspi->s_ncg) {
+ ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device");
+ goto failed_unlock;
+ }
+ end_bit = bit + count;
+ if (end_bit > uspi->s_fpg) {
+ overflow = bit + count - uspi->s_fpg;
+ count -= overflow;
+ end_bit -= overflow;
+ }
+
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ goto failed_unlock;
+ ucg = ubh_get_ucg (UCPI_UBH(ucpi));
+ if (!ufs_cg_chkmagic(sb, ucg)) {
+ ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno);
+ goto failed_unlock;
+ }
+
+ for (i = bit; i < end_bit; i += uspi->s_fpb) {
+ if (ubh_isblockset(uspi, ucpi, i)) {
+ ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
+ }
+ ubh_setblock(uspi, ucpi, i);
+ inode_sub_bytes(inode, uspi->s_fpb << uspi->s_fshift);
+ adjust_free_blocks(sb, ucg, ucpi, i, 1);
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+
+ if (overflow) {
+ fragment += count;
+ count = overflow;
+ goto do_more;
+ }
+
+ ufs_mark_sb_dirty(sb);
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT\n");
+ return;
+
+failed_unlock:
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+failed:
+ UFSD("EXIT (FAILED)\n");
+ return;
+}
+
+/*
+ * Modify inode page cache in such way:
+ * have - blocks with b_blocknr equal to oldb...oldb+count-1
+ * get - blocks with b_blocknr equal to newb...newb+count-1
+ * also we suppose that oldb...oldb+count-1 blocks
+ * situated at the end of file.
+ *
+ * We can come here from ufs_writepage or ufs_prepare_write,
+ * locked_folio is argument of these functions, so we already lock it.
+ */
+static void ufs_change_blocknr(struct inode *inode, sector_t beg,
+ unsigned int count, sector_t oldb,
+ sector_t newb, struct folio *locked_folio)
+{
+ struct folio *folio;
+ const unsigned blks_per_page =
+ 1 << (PAGE_SHIFT - inode->i_blkbits);
+ const unsigned mask = blks_per_page - 1;
+ struct address_space * const mapping = inode->i_mapping;
+ pgoff_t index, cur_index, last_index;
+ unsigned pos, j, lblock;
+ sector_t end, i;
+ struct buffer_head *head, *bh;
+
+ UFSD("ENTER, ino %llu, count %u, oldb %llu, newb %llu\n",
+ inode->i_ino, count,
+ (unsigned long long)oldb, (unsigned long long)newb);
+
+ BUG_ON(!folio_test_locked(locked_folio));
+
+ cur_index = locked_folio->index;
+ end = count + beg;
+ last_index = end >> (PAGE_SHIFT - inode->i_blkbits);
+ for (i = beg; i < end; i = (i | mask) + 1) {
+ index = i >> (PAGE_SHIFT - inode->i_blkbits);
+
+ if (likely(cur_index != index)) {
+ folio = ufs_get_locked_folio(mapping, index);
+ if (!folio) /* it was truncated */
+ continue;
+ if (IS_ERR(folio)) {/* or EIO */
+ ufs_error(inode->i_sb, __func__,
+ "read of page %llu failed\n",
+ (unsigned long long)index);
+ continue;
+ }
+ } else
+ folio = locked_folio;
+
+ head = folio_buffers(folio);
+ bh = head;
+ pos = i & mask;
+ for (j = 0; j < pos; ++j)
+ bh = bh->b_this_page;
+
+ if (unlikely(index == last_index))
+ lblock = end & mask;
+ else
+ lblock = blks_per_page;
+
+ do {
+ if (j >= lblock)
+ break;
+ pos = (i - beg) + j;
+
+ if (!buffer_mapped(bh))
+ map_bh(bh, inode->i_sb, oldb + pos);
+ if (bh_read(bh, 0) < 0) {
+ ufs_error(inode->i_sb, __func__,
+ "read of block failed\n");
+ break;
+ }
+
+ UFSD(" change from %llu to %llu, pos %u\n",
+ (unsigned long long)(pos + oldb),
+ (unsigned long long)(pos + newb), pos);
+
+ bh->b_blocknr = newb + pos;
+ clean_bdev_bh_alias(bh);
+ mark_buffer_dirty(bh);
+ ++j;
+ bh = bh->b_this_page;
+ } while (bh != head);
+
+ if (likely(cur_index != index))
+ ufs_put_locked_folio(folio);
+ }
+ UFSD("EXIT\n");
+}
+
+static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n,
+ int sync)
+{
+ struct buffer_head *bh;
+ sector_t end = beg + n;
+
+ for (; beg < end; ++beg) {
+ bh = sb_getblk(inode->i_sb, beg);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, inode->i_sb->s_blocksize);
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ if (IS_SYNC(inode) || sync)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ }
+}
+
+u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment,
+ u64 goal, unsigned count, int *err,
+ struct folio *locked_folio)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ unsigned cgno, oldcount, newcount;
+ u64 tmp, request, result;
+
+ UFSD("ENTER, ino %llu, fragment %llu, goal %llu, count %u\n",
+ inode->i_ino, (unsigned long long)fragment,
+ (unsigned long long)goal, count);
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+ usb1 = ubh_get_usb_first(uspi);
+ *err = -ENOSPC;
+
+ mutex_lock(&UFS_SB(sb)->s_lock);
+ tmp = ufs_data_ptr_to_cpu(sb, p);
+
+ if (count + ufs_fragnum(fragment) > uspi->s_fpb) {
+ ufs_warning(sb, "ufs_new_fragments", "internal warning"
+ " fragment %llu, count %u",
+ (unsigned long long)fragment, count);
+ count = uspi->s_fpb - ufs_fragnum(fragment);
+ }
+ oldcount = ufs_fragnum (fragment);
+ newcount = oldcount + count;
+
+ /*
+ * Somebody else has just allocated our fragments
+ */
+ if (oldcount) {
+ if (!tmp) {
+ ufs_error(sb, "ufs_new_fragments", "internal error, "
+ "fragment %llu, tmp %llu\n",
+ (unsigned long long)fragment,
+ (unsigned long long)tmp);
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return INVBLOCK;
+ }
+ if (fragment < UFS_I(inode)->i_lastfrag) {
+ UFSD("EXIT (ALREADY ALLOCATED)\n");
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return 0;
+ }
+ }
+ else {
+ if (tmp) {
+ UFSD("EXIT (ALREADY ALLOCATED)\n");
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return 0;
+ }
+ }
+
+ /*
+ * There is not enough space for user on the device
+ */
+ if (unlikely(ufs_freefrags(uspi) <= uspi->s_root_blocks)) {
+ if (!capable(CAP_SYS_RESOURCE)) {
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT (FAILED)\n");
+ return 0;
+ }
+ }
+
+ if (goal >= uspi->s_size)
+ goal = 0;
+ if (goal == 0)
+ cgno = ufs_inotocg (inode->i_ino);
+ else
+ cgno = ufs_dtog(uspi, goal);
+
+ /*
+ * allocate new fragment
+ */
+ if (oldcount == 0) {
+ result = ufs_alloc_fragments (inode, cgno, goal, count, err);
+ if (result) {
+ ufs_clear_frags(inode, result + oldcount,
+ newcount - oldcount, locked_folio != NULL);
+ *err = 0;
+ write_seqlock(&UFS_I(inode)->meta_lock);
+ ufs_cpu_to_data_ptr(sb, p, result);
+ UFS_I(inode)->i_lastfrag =
+ max(UFS_I(inode)->i_lastfrag, fragment + count);
+ write_sequnlock(&UFS_I(inode)->meta_lock);
+ }
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT, result %llu\n", (unsigned long long)result);
+ return result;
+ }
+
+ /*
+ * resize block
+ */
+ result = ufs_add_fragments(inode, tmp, oldcount, newcount);
+ if (result) {
+ *err = 0;
+ read_seqlock_excl(&UFS_I(inode)->meta_lock);
+ UFS_I(inode)->i_lastfrag = max(UFS_I(inode)->i_lastfrag,
+ fragment + count);
+ read_sequnlock_excl(&UFS_I(inode)->meta_lock);
+ ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
+ locked_folio != NULL);
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT, result %llu\n", (unsigned long long)result);
+ return result;
+ }
+
+ /*
+ * allocate new block and move data
+ */
+ if (fs32_to_cpu(sb, usb1->fs_optim) == UFS_OPTSPACE) {
+ request = newcount;
+ if (uspi->cs_total.cs_nffree < uspi->s_space_to_time)
+ usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
+ } else {
+ request = uspi->s_fpb;
+ if (uspi->cs_total.cs_nffree > uspi->s_time_to_space)
+ usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTSPACE);
+ }
+ result = ufs_alloc_fragments (inode, cgno, goal, request, err);
+ if (result) {
+ ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
+ locked_folio != NULL);
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ ufs_change_blocknr(inode, fragment - oldcount, oldcount,
+ uspi->s_sbbase + tmp,
+ uspi->s_sbbase + result, locked_folio);
+ *err = 0;
+ write_seqlock(&UFS_I(inode)->meta_lock);
+ ufs_cpu_to_data_ptr(sb, p, result);
+ UFS_I(inode)->i_lastfrag = max(UFS_I(inode)->i_lastfrag,
+ fragment + count);
+ write_sequnlock(&UFS_I(inode)->meta_lock);
+ if (newcount < request)
+ ufs_free_fragments (inode, result + newcount, request - newcount);
+ ufs_free_fragments (inode, tmp, oldcount);
+ UFSD("EXIT, result %llu\n", (unsigned long long)result);
+ return result;
+ }
+
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT (FAILED)\n");
+ return 0;
+}
+
+static bool try_add_frags(struct inode *inode, unsigned frags)
+{
+ unsigned size = frags * i_blocksize(inode);
+ spin_lock(&inode->i_lock);
+ __inode_add_bytes(inode, size);
+ if (unlikely((u32)inode->i_blocks != inode->i_blocks)) {
+ __inode_sub_bytes(inode, size);
+ spin_unlock(&inode->i_lock);
+ return false;
+ }
+ spin_unlock(&inode->i_lock);
+ return true;
+}
+
+static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
+ unsigned oldcount, unsigned newcount)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned cgno, fragno, fragoff, count, fragsize, i;
+
+ UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
+ (unsigned long long)fragment, oldcount, newcount);
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+ count = newcount - oldcount;
+
+ cgno = ufs_dtog(uspi, fragment);
+ if (fs32_to_cpu(sb, UFS_SB(sb)->fs_cs(cgno).cs_nffree) < count)
+ return 0;
+ if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb)
+ return 0;
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ return 0;
+ ucg = ubh_get_ucg (UCPI_UBH(ucpi));
+ if (!ufs_cg_chkmagic(sb, ucg)) {
+ ufs_panic (sb, "ufs_add_fragments",
+ "internal error, bad magic number on cg %u", cgno);
+ return 0;
+ }
+
+ fragno = ufs_dtogd(uspi, fragment);
+ fragoff = ufs_fragnum (fragno);
+ for (i = oldcount; i < newcount; i++)
+ if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
+ return 0;
+
+ if (!try_add_frags(inode, count))
+ return 0;
+ /*
+ * Block can be extended
+ */
+ ucg->cg_time = ufs_get_seconds(sb);
+ for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
+ if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
+ break;
+ fragsize = i - oldcount;
+ if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize]))
+ ufs_panic (sb, "ufs_add_fragments",
+ "internal error or corrupted bitmap on cg %u", cgno);
+ fs32_sub(sb, &ucg->cg_frsum[fragsize], 1);
+ if (fragsize != count)
+ fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
+ for (i = oldcount; i < newcount; i++)
+ ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
+ fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
+ uspi->cs_total.cs_nffree -= count;
+
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+ ufs_mark_sb_dirty(sb);
+
+ UFSD("EXIT, fragment %llu\n", (unsigned long long)fragment);
+
+ return fragment;
+}
+
+#define UFS_TEST_FREE_SPACE_CG \
+ ucg = (struct ufs_cylinder_group *) UFS_SB(sb)->s_ucg[cgno]->b_data; \
+ if (fs32_to_cpu(sb, ucg->cg_cs.cs_nbfree)) \
+ goto cg_found; \
+ for (k = count; k < uspi->s_fpb; k++) \
+ if (fs32_to_cpu(sb, ucg->cg_frsum[k])) \
+ goto cg_found;
+
+static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
+ u64 goal, unsigned count, int *err)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned oldcg, i, j, k, allocsize;
+ u64 result;
+
+ UFSD("ENTER, ino %llu, cgno %u, goal %llu, count %u\n",
+ inode->i_ino, cgno, (unsigned long long)goal, count);
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+ oldcg = cgno;
+
+ /*
+ * 1. searching on preferred cylinder group
+ */
+ UFS_TEST_FREE_SPACE_CG
+
+ /*
+ * 2. quadratic rehash
+ */
+ for (j = 1; j < uspi->s_ncg; j *= 2) {
+ cgno += j;
+ if (cgno >= uspi->s_ncg)
+ cgno -= uspi->s_ncg;
+ UFS_TEST_FREE_SPACE_CG
+ }
+
+ /*
+ * 3. brute force search
+ * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
+ */
+ cgno = (oldcg + 1) % uspi->s_ncg;
+ for (j = 2; j < uspi->s_ncg; j++) {
+ cgno++;
+ if (cgno >= uspi->s_ncg)
+ cgno = 0;
+ UFS_TEST_FREE_SPACE_CG
+ }
+
+ UFSD("EXIT (FAILED)\n");
+ return 0;
+
+cg_found:
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ return 0;
+ ucg = ubh_get_ucg (UCPI_UBH(ucpi));
+ if (!ufs_cg_chkmagic(sb, ucg))
+ ufs_panic (sb, "ufs_alloc_fragments",
+ "internal error, bad magic number on cg %u", cgno);
+ ucg->cg_time = ufs_get_seconds(sb);
+
+ if (count == uspi->s_fpb) {
+ result = ufs_alloccg_block (inode, ucpi, goal, err);
+ if (result == INVBLOCK)
+ return 0;
+ goto succed;
+ }
+
+ for (allocsize = count; allocsize < uspi->s_fpb; allocsize++)
+ if (fs32_to_cpu(sb, ucg->cg_frsum[allocsize]) != 0)
+ break;
+
+ if (allocsize == uspi->s_fpb) {
+ result = ufs_alloccg_block (inode, ucpi, goal, err);
+ if (result == INVBLOCK)
+ return 0;
+ goal = ufs_dtogd(uspi, result);
+ for (i = count; i < uspi->s_fpb; i++)
+ ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
+ i = uspi->s_fpb - count;
+
+ inode_sub_bytes(inode, i << uspi->s_fshift);
+ fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
+ uspi->cs_total.cs_nffree += i;
+ fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, i);
+ fs32_add(sb, &ucg->cg_frsum[i], 1);
+ goto succed;
+ }
+
+ result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
+ if (result == INVBLOCK)
+ return 0;
+ if (!try_add_frags(inode, count))
+ return 0;
+ for (i = 0; i < count; i++)
+ ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i);
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
+ uspi->cs_total.cs_nffree -= count;
+ fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
+ fs32_sub(sb, &ucg->cg_frsum[allocsize], 1);
+
+ if (count != allocsize)
+ fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1);
+
+succed:
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+ ufs_mark_sb_dirty(sb);
+
+ result += cgno * uspi->s_fpg;
+ UFSD("EXIT3, result %llu\n", (unsigned long long)result);
+ return result;
+}
+
+static u64 ufs_alloccg_block(struct inode *inode,
+ struct ufs_cg_private_info *ucpi,
+ u64 goal, int *err)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cylinder_group * ucg;
+ u64 result;
+
+ UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+ ucg = ubh_get_ucg(UCPI_UBH(ucpi));
+
+ if (goal == 0) {
+ goal = ucpi->c_rotor;
+ goto norot;
+ }
+ goal = ufs_blknum (goal);
+ goal = ufs_dtogd(uspi, goal);
+
+ /*
+ * If the requested block is available, use it.
+ */
+ if (ubh_isblockset(uspi, ucpi, goal)) {
+ result = goal;
+ goto gotit;
+ }
+
+norot:
+ result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
+ if (result == INVBLOCK)
+ return INVBLOCK;
+ ucpi->c_rotor = result;
+gotit:
+ if (!try_add_frags(inode, uspi->s_fpb))
+ return 0;
+ ubh_clrblock(uspi, ucpi, result);
+ adjust_free_blocks(sb, ucg, ucpi, result, -1);
+
+ UFSD("EXIT, result %llu\n", (unsigned long long)result);
+
+ return result;
+}
+
+static unsigned ubh_scanc(struct ufs_sb_private_info *uspi,
+ struct ufs_buffer_head *ubh,
+ unsigned begin, unsigned size,
+ unsigned char *table, unsigned char mask)
+{
+ unsigned rest, offset;
+ unsigned char *cp;
+
+
+ offset = begin & ~uspi->s_fmask;
+ begin >>= uspi->s_fshift;
+ for (;;) {
+ if ((offset + size) < uspi->s_fsize)
+ rest = size;
+ else
+ rest = uspi->s_fsize - offset;
+ size -= rest;
+ cp = ubh->bh[begin]->b_data + offset;
+ while ((table[*cp++] & mask) == 0 && --rest)
+ ;
+ if (rest || !size)
+ break;
+ begin++;
+ offset = 0;
+ }
+ return (size + rest);
+}
+
+/*
+ * Find a block of the specified size in the specified cylinder group.
+ * @sp: pointer to super block
+ * @ucpi: pointer to cylinder group info
+ * @goal: near which block we want find new one
+ * @count: specified size
+ */
+static u64 ufs_bitmap_search(struct super_block *sb,
+ struct ufs_cg_private_info *ucpi,
+ u64 goal, unsigned count)
+{
+ /*
+ * Bit patterns for identifying fragments in the block map
+ * used as ((map & mask_arr) == want_arr)
+ */
+ static const int mask_arr[9] = {
+ 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
+ };
+ static const int want_arr[9] = {
+ 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
+ };
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ unsigned start, length, loc;
+ unsigned pos, want, blockmap, mask, end;
+ u64 result;
+
+ UFSD("ENTER, cg %u, goal %llu, count %u\n", ucpi->c_cgx,
+ (unsigned long long)goal, count);
+
+ if (goal)
+ start = ufs_dtogd(uspi, goal) >> 3;
+ else
+ start = ucpi->c_frotor >> 3;
+
+ length = ((uspi->s_fpg + 7) >> 3) - start;
+ loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff + start, length,
+ (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
+ 1 << (count - 1 + (uspi->s_fpb & 7)));
+ if (loc == 0) {
+ length = start + 1;
+ loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, length,
+ (uspi->s_fpb == 8) ? ufs_fragtable_8fpb :
+ ufs_fragtable_other,
+ 1 << (count - 1 + (uspi->s_fpb & 7)));
+ if (loc == 0) {
+ ufs_error(sb, "ufs_bitmap_search",
+ "bitmap corrupted on cg %u, start %u,"
+ " length %u, count %u, freeoff %u\n",
+ ucpi->c_cgx, start, length, count,
+ ucpi->c_freeoff);
+ return INVBLOCK;
+ }
+ start = 0;
+ }
+ result = (start + length - loc) << 3;
+ ucpi->c_frotor = result;
+
+ /*
+ * found the byte in the map
+ */
+
+ for (end = result + 8; result < end; result += uspi->s_fpb) {
+ blockmap = ubh_blkmap(UCPI_UBH(ucpi), ucpi->c_freeoff, result);
+ blockmap <<= 1;
+ mask = mask_arr[count];
+ want = want_arr[count];
+ for (pos = 0; pos <= uspi->s_fpb - count; pos++) {
+ if ((blockmap & mask) == want) {
+ UFSD("EXIT, result %llu\n",
+ (unsigned long long)result);
+ return result + pos;
+ }
+ mask <<= 1;
+ want <<= 1;
+ }
+ }
+
+ ufs_error(sb, "ufs_bitmap_search", "block not in map on cg %u\n",
+ ucpi->c_cgx);
+ UFSD("EXIT (FAILED)\n");
+ return INVBLOCK;
+}
+
+static void ufs_clusteracct(struct super_block * sb,
+ struct ufs_cg_private_info * ucpi, unsigned frag, int cnt)
+{
+ struct ufs_sb_private_info * uspi = UFS_SB(sb)->s_uspi;
+ int i, start, end, forw, back;
+ unsigned blkno = ufs_fragstoblks(frag);
+
+ if (uspi->s_contigsumsize <= 0)
+ return;
+
+ if (cnt > 0)
+ ubh_setbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
+ else
+ ubh_clrbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
+
+ /*
+ * Find the size of the cluster going forward.
+ */
+ start = blkno + 1;
+ end = start + uspi->s_contigsumsize;
+ if ( end >= ucpi->c_nclusterblks)
+ end = ucpi->c_nclusterblks;
+ i = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, end, start);
+ if (i > end)
+ i = end;
+ forw = i - start;
+
+ /*
+ * Find the size of the cluster going backward.
+ */
+ start = blkno - 1;
+ end = start - uspi->s_contigsumsize;
+ if (end < 0 )
+ end = -1;
+ i = ubh_find_last_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, start, end);
+ if ( i < end)
+ i = end;
+ back = start - i;
+
+ /*
+ * Account for old cluster and the possibly new forward and
+ * back clusters.
+ */
+ i = back + forw + 1;
+ if (i > uspi->s_contigsumsize)
+ i = uspi->s_contigsumsize;
+ fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (i << 2)), cnt);
+ if (back > 0)
+ fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (back << 2)), cnt);
+ if (forw > 0)
+ fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (forw << 2)), cnt);
+}
+
+
+static unsigned char ufs_fragtable_8fpb[] = {
+ 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C,
+ 0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
+};
+
+static unsigned char ufs_fragtable_other[] = {
+ 0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE,
+ 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
+ 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
+ 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
+ 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
+ 0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A,
+};
diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c
new file mode 100644
index 000000000..a2813270c
--- /dev/null
+++ b/fs/ufs/cylinder.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/cylinder.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * ext2 - inode (block) bitmap caching inspired
+ */
+
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+/*
+ * Read cylinder group into cache. The memory space for ufs_cg_private_info
+ * structure is already allocated during ufs_read_super.
+ */
+static bool ufs_read_cylinder(struct super_block *sb,
+ unsigned cgno, unsigned bitmap_nr)
+{
+ struct ufs_sb_info * sbi = UFS_SB(sb);
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned i, j;
+
+ UFSD("ENTER, cgno %u, bitmap_nr %u\n", cgno, bitmap_nr);
+ uspi = sbi->s_uspi;
+ ucpi = sbi->s_ucpi[bitmap_nr];
+ ucg = (struct ufs_cylinder_group *)sbi->s_ucg[cgno]->b_data;
+
+ UCPI_UBH(ucpi)->fragment = ufs_cgcmin(cgno);
+ UCPI_UBH(ucpi)->count = uspi->s_cgsize >> sb->s_blocksize_bits;
+ /*
+ * We have already the first fragment of cylinder group block in buffer
+ */
+ UCPI_UBH(ucpi)->bh[0] = sbi->s_ucg[cgno];
+ for (i = 1; i < UCPI_UBH(ucpi)->count; i++) {
+ UCPI_UBH(ucpi)->bh[i] = sb_bread(sb, UCPI_UBH(ucpi)->fragment + i);
+ if (!UCPI_UBH(ucpi)->bh[i])
+ goto failed;
+ }
+ sbi->s_cgno[bitmap_nr] = cgno;
+
+ ucpi->c_cgx = fs32_to_cpu(sb, ucg->cg_cgx);
+ ucpi->c_ncyl = fs16_to_cpu(sb, ucg->cg_ncyl);
+ ucpi->c_niblk = fs16_to_cpu(sb, ucg->cg_niblk);
+ ucpi->c_ndblk = fs32_to_cpu(sb, ucg->cg_ndblk);
+ ucpi->c_rotor = fs32_to_cpu(sb, ucg->cg_rotor);
+ ucpi->c_frotor = fs32_to_cpu(sb, ucg->cg_frotor);
+ ucpi->c_irotor = fs32_to_cpu(sb, ucg->cg_irotor);
+ ucpi->c_btotoff = fs32_to_cpu(sb, ucg->cg_btotoff);
+ ucpi->c_boff = fs32_to_cpu(sb, ucg->cg_boff);
+ ucpi->c_iusedoff = fs32_to_cpu(sb, ucg->cg_iusedoff);
+ ucpi->c_freeoff = fs32_to_cpu(sb, ucg->cg_freeoff);
+ ucpi->c_nextfreeoff = fs32_to_cpu(sb, ucg->cg_nextfreeoff);
+ ucpi->c_clustersumoff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clustersumoff);
+ ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff);
+ ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks);
+ UFSD("EXIT\n");
+ return true;
+
+failed:
+ for (j = 1; j < i; j++)
+ brelse(UCPI_UBH(ucpi)->bh[j]);
+ sbi->s_cgno[bitmap_nr] = UFS_CGNO_EMPTY;
+ ufs_error (sb, "ufs_read_cylinder", "can't read cylinder group block %u", cgno);
+ return false;
+}
+
+/*
+ * Remove cylinder group from cache, doesn't release memory
+ * allocated for cylinder group (this is done at ufs_put_super only).
+ */
+void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr)
+{
+ struct ufs_sb_info * sbi = UFS_SB(sb);
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned i;
+
+ UFSD("ENTER, bitmap_nr %u\n", bitmap_nr);
+
+ uspi = sbi->s_uspi;
+ if (sbi->s_cgno[bitmap_nr] == UFS_CGNO_EMPTY) {
+ UFSD("EXIT\n");
+ return;
+ }
+ ucpi = sbi->s_ucpi[bitmap_nr];
+ ucg = ubh_get_ucg(UCPI_UBH(ucpi));
+
+ if (uspi->s_ncg > UFS_MAX_GROUP_LOADED && bitmap_nr >= sbi->s_cg_loaded) {
+ ufs_panic (sb, "ufs_put_cylinder", "internal error");
+ return;
+ }
+ /*
+ * rotor is not so important data, so we put it to disk
+ * at the end of working with cylinder
+ */
+ ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor);
+ ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor);
+ ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor);
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ for (i = 1; i < UCPI_UBH(ucpi)->count; i++) {
+ brelse (UCPI_UBH(ucpi)->bh[i]);
+ }
+
+ sbi->s_cgno[bitmap_nr] = UFS_CGNO_EMPTY;
+ UFSD("EXIT\n");
+}
+
+/*
+ * Find cylinder group in cache and return it as pointer.
+ * If cylinder group is not in cache, we will load it from disk.
+ *
+ * The cache is managed by LRU algorithm.
+ */
+struct ufs_cg_private_info * ufs_load_cylinder (
+ struct super_block * sb, unsigned cgno)
+{
+ struct ufs_sb_info * sbi = UFS_SB(sb);
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ unsigned cg, i, j;
+
+ UFSD("ENTER, cgno %u\n", cgno);
+
+ uspi = sbi->s_uspi;
+ if (cgno >= uspi->s_ncg) {
+ ufs_panic (sb, "ufs_load_cylinder", "internal error, high number of cg");
+ return NULL;
+ }
+ /*
+ * Cylinder group number cg it in cache and it was last used
+ */
+ if (sbi->s_cgno[0] == cgno) {
+ UFSD("EXIT\n");
+ return sbi->s_ucpi[0];
+ }
+ /*
+ * Number of cylinder groups is not higher than UFS_MAX_GROUP_LOADED
+ */
+ if (uspi->s_ncg <= UFS_MAX_GROUP_LOADED) {
+ if (sbi->s_cgno[cgno] != UFS_CGNO_EMPTY) {
+ if (sbi->s_cgno[cgno] != cgno) {
+ ufs_panic (sb, "ufs_load_cylinder", "internal error, wrong number of cg in cache");
+ UFSD("EXIT (FAILED)\n");
+ return NULL;
+ }
+ } else {
+ if (unlikely(!ufs_read_cylinder (sb, cgno, cgno))) {
+ UFSD("EXIT (FAILED)\n");
+ return NULL;
+ }
+ }
+ UFSD("EXIT\n");
+ return sbi->s_ucpi[cgno];
+ }
+ /*
+ * Cylinder group number cg is in cache but it was not last used,
+ * we will move to the first position
+ */
+ for (i = 0; i < sbi->s_cg_loaded && sbi->s_cgno[i] != cgno; i++);
+ if (i < sbi->s_cg_loaded && sbi->s_cgno[i] == cgno) {
+ cg = sbi->s_cgno[i];
+ ucpi = sbi->s_ucpi[i];
+ for (j = i; j > 0; j--) {
+ sbi->s_cgno[j] = sbi->s_cgno[j-1];
+ sbi->s_ucpi[j] = sbi->s_ucpi[j-1];
+ }
+ sbi->s_cgno[0] = cg;
+ sbi->s_ucpi[0] = ucpi;
+ /*
+ * Cylinder group number cg is not in cache, we will read it from disk
+ * and put it to the first position
+ */
+ } else {
+ if (sbi->s_cg_loaded < UFS_MAX_GROUP_LOADED)
+ sbi->s_cg_loaded++;
+ else
+ ufs_put_cylinder (sb, UFS_MAX_GROUP_LOADED-1);
+ ucpi = sbi->s_ucpi[sbi->s_cg_loaded - 1];
+ for (j = sbi->s_cg_loaded - 1; j > 0; j--) {
+ sbi->s_cgno[j] = sbi->s_cgno[j-1];
+ sbi->s_ucpi[j] = sbi->s_ucpi[j-1];
+ }
+ sbi->s_ucpi[0] = ucpi;
+ if (unlikely(!ufs_read_cylinder (sb, cgno, 0))) {
+ UFSD("EXIT (FAILED)\n");
+ return NULL;
+ }
+ }
+ UFSD("EXIT\n");
+ return sbi->s_ucpi[0];
+}
diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
new file mode 100644
index 000000000..e62fe5667
--- /dev/null
+++ b/fs/ufs/dir.c
@@ -0,0 +1,658 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/ufs_dir.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * Migration to usage of "page cache" on May 2006 by
+ * Evgeniy Dushistov <dushistov@mail.ru> based on ext2 code base.
+ */
+
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/filelock.h>
+#include <linux/swap.h>
+#include <linux/iversion.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+/*
+ * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
+ *
+ * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
+ */
+static inline int ufs_match(struct super_block *sb, int len,
+ const unsigned char *name, struct ufs_dir_entry *de)
+{
+ if (len != ufs_get_de_namlen(sb, de))
+ return 0;
+ if (!de->d_ino)
+ return 0;
+ return !memcmp(name, de->d_name, len);
+}
+
+static void ufs_commit_chunk(struct folio *folio, loff_t pos, unsigned len)
+{
+ struct address_space *mapping = folio->mapping;
+ struct inode *dir = mapping->host;
+
+ inode_inc_iversion(dir);
+ block_write_end(pos, len, len, folio);
+ if (pos+len > dir->i_size) {
+ i_size_write(dir, pos+len);
+ mark_inode_dirty(dir);
+ }
+ folio_unlock(folio);
+}
+
+static int ufs_handle_dirsync(struct inode *dir)
+{
+ int err;
+
+ err = filemap_write_and_wait(dir->i_mapping);
+ if (!err)
+ err = sync_inode_metadata(dir, 1);
+ return err;
+}
+
+ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr)
+{
+ ino_t res = 0;
+ struct ufs_dir_entry *de;
+ struct folio *folio;
+
+ de = ufs_find_entry(dir, qstr, &folio);
+ if (de) {
+ res = fs32_to_cpu(dir->i_sb, de->d_ino);
+ folio_release_kmap(folio, de);
+ }
+ return res;
+}
+
+
+int ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
+ struct folio *folio, struct inode *inode,
+ bool update_times)
+{
+ loff_t pos = folio_pos(folio) + offset_in_folio(folio, de);
+ unsigned len = fs16_to_cpu(dir->i_sb, de->d_reclen);
+ int err;
+
+ folio_lock(folio);
+ err = ufs_prepare_chunk(folio, pos, len);
+ if (unlikely(err)) {
+ folio_unlock(folio);
+ return err;
+ }
+
+ de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino);
+ ufs_set_de_type(dir->i_sb, de, inode->i_mode);
+
+ ufs_commit_chunk(folio, pos, len);
+ if (update_times)
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
+ mark_inode_dirty(dir);
+ return ufs_handle_dirsync(dir);
+}
+
+static bool ufs_check_folio(struct folio *folio, char *kaddr)
+{
+ struct inode *dir = folio->mapping->host;
+ struct super_block *sb = dir->i_sb;
+ unsigned offs, rec_len;
+ unsigned limit = folio_size(folio);
+ const unsigned chunk_mask = UFS_SB(sb)->s_uspi->s_dirblksize - 1;
+ struct ufs_dir_entry *p;
+ char *error;
+
+ if (dir->i_size < folio_pos(folio) + limit) {
+ limit = offset_in_folio(folio, dir->i_size);
+ if (limit & chunk_mask)
+ goto Ebadsize;
+ if (!limit)
+ goto out;
+ }
+ for (offs = 0; offs <= limit - UFS_DIR_REC_LEN(1); offs += rec_len) {
+ p = (struct ufs_dir_entry *)(kaddr + offs);
+ rec_len = fs16_to_cpu(sb, p->d_reclen);
+
+ if (rec_len < UFS_DIR_REC_LEN(1))
+ goto Eshort;
+ if (rec_len & 3)
+ goto Ealign;
+ if (rec_len < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, p)))
+ goto Enamelen;
+ if (((offs + rec_len - 1) ^ offs) & ~chunk_mask)
+ goto Espan;
+ if (fs32_to_cpu(sb, p->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg *
+ UFS_SB(sb)->s_uspi->s_ncg))
+ goto Einumber;
+ }
+ if (offs != limit)
+ goto Eend;
+out:
+ folio_set_checked(folio);
+ return true;
+
+ /* Too bad, we had an error */
+
+Ebadsize:
+ ufs_error(sb, __func__,
+ "size of directory #%llu is not a multiple of chunk size",
+ dir->i_ino
+ );
+ goto fail;
+Eshort:
+ error = "rec_len is smaller than minimal";
+ goto bad_entry;
+Ealign:
+ error = "unaligned directory entry";
+ goto bad_entry;
+Enamelen:
+ error = "rec_len is too small for name_len";
+ goto bad_entry;
+Espan:
+ error = "directory entry across blocks";
+ goto bad_entry;
+Einumber:
+ error = "inode out of bounds";
+bad_entry:
+ ufs_error(sb, __func__, "bad entry in directory #%llu: %s - "
+ "offset=%llu, rec_len=%d, name_len=%d",
+ dir->i_ino, error, folio_pos(folio) + offs,
+ rec_len, ufs_get_de_namlen(sb, p));
+ goto fail;
+Eend:
+ p = (struct ufs_dir_entry *)(kaddr + offs);
+ ufs_error(sb, __func__,
+ "entry in directory #%llu spans the page boundary"
+ "offset=%llu",
+ dir->i_ino, folio_pos(folio) + offs);
+fail:
+ return false;
+}
+
+static void *ufs_get_folio(struct inode *dir, unsigned long n,
+ struct folio **foliop)
+{
+ struct address_space *mapping = dir->i_mapping;
+ struct folio *folio = read_mapping_folio(mapping, n, NULL);
+ void *kaddr;
+
+ if (IS_ERR(folio))
+ return ERR_CAST(folio);
+ kaddr = kmap_local_folio(folio, 0);
+ if (unlikely(!folio_test_checked(folio))) {
+ if (!ufs_check_folio(folio, kaddr))
+ goto fail;
+ }
+ *foliop = folio;
+ return kaddr;
+
+fail:
+ folio_release_kmap(folio, kaddr);
+ return ERR_PTR(-EIO);
+}
+
+/*
+ * Return the offset into page `page_nr' of the last valid
+ * byte in that page, plus one.
+ */
+static unsigned
+ufs_last_byte(struct inode *inode, unsigned long page_nr)
+{
+ unsigned last_byte = inode->i_size;
+
+ last_byte -= page_nr << PAGE_SHIFT;
+ if (last_byte > PAGE_SIZE)
+ last_byte = PAGE_SIZE;
+ return last_byte;
+}
+
+static inline struct ufs_dir_entry *
+ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p)
+{
+ return (struct ufs_dir_entry *)((char *)p +
+ fs16_to_cpu(sb, p->d_reclen));
+}
+
+struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct folio **foliop)
+{
+ struct ufs_dir_entry *de = ufs_get_folio(dir, 0, foliop);
+
+ if (!IS_ERR(de))
+ return ufs_next_entry(dir->i_sb, de);
+
+ return NULL;
+}
+
+/*
+ * ufs_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the page in which the entry was found, and the entry itself
+ * (as a parameter - res_dir). Page is returned mapped and unlocked.
+ * Entry is guaranteed to be valid.
+ */
+struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr,
+ struct folio **foliop)
+{
+ struct super_block *sb = dir->i_sb;
+ const unsigned char *name = qstr->name;
+ int namelen = qstr->len;
+ unsigned reclen = UFS_DIR_REC_LEN(namelen);
+ unsigned long start, n;
+ unsigned long npages = dir_pages(dir);
+ struct ufs_inode_info *ui = UFS_I(dir);
+ struct ufs_dir_entry *de;
+
+ UFSD("ENTER, dir_ino %llu, name %s, namlen %u\n", dir->i_ino, name, namelen);
+
+ if (npages == 0 || namelen > UFS_MAXNAMLEN)
+ goto out;
+
+ start = ui->i_dir_start_lookup;
+
+ if (start >= npages)
+ start = 0;
+ n = start;
+ do {
+ char *kaddr = ufs_get_folio(dir, n, foliop);
+
+ if (!IS_ERR(kaddr)) {
+ de = (struct ufs_dir_entry *)kaddr;
+ kaddr += ufs_last_byte(dir, n) - reclen;
+ while ((char *) de <= kaddr) {
+ if (ufs_match(sb, namelen, name, de))
+ goto found;
+ de = ufs_next_entry(sb, de);
+ }
+ folio_release_kmap(*foliop, kaddr);
+ }
+ if (++n >= npages)
+ n = 0;
+ } while (n != start);
+out:
+ return NULL;
+
+found:
+ ui->i_dir_start_lookup = n;
+ return de;
+}
+
+/*
+ * Parent is locked.
+ */
+int ufs_add_link(struct dentry *dentry, struct inode *inode)
+{
+ struct inode *dir = d_inode(dentry->d_parent);
+ const unsigned char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ struct super_block *sb = dir->i_sb;
+ unsigned reclen = UFS_DIR_REC_LEN(namelen);
+ const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize;
+ unsigned short rec_len, name_len;
+ struct folio *folio = NULL;
+ struct ufs_dir_entry *de;
+ unsigned long npages = dir_pages(dir);
+ unsigned long n;
+ loff_t pos;
+ int err;
+
+ UFSD("ENTER, name %s, namelen %u\n", name, namelen);
+
+ /*
+ * We take care of directory expansion in the same loop.
+ * This code plays outside i_size, so it locks the folio
+ * to protect that region.
+ */
+ for (n = 0; n <= npages; n++) {
+ char *kaddr = ufs_get_folio(dir, n, &folio);
+ char *dir_end;
+
+ if (IS_ERR(kaddr))
+ return PTR_ERR(kaddr);
+ folio_lock(folio);
+ dir_end = kaddr + ufs_last_byte(dir, n);
+ de = (struct ufs_dir_entry *)kaddr;
+ kaddr += folio_size(folio) - reclen;
+ while ((char *)de <= kaddr) {
+ if ((char *)de == dir_end) {
+ /* We hit i_size */
+ name_len = 0;
+ rec_len = chunk_size;
+ de->d_reclen = cpu_to_fs16(sb, chunk_size);
+ de->d_ino = 0;
+ goto got_it;
+ }
+ if (de->d_reclen == 0) {
+ ufs_error(dir->i_sb, __func__,
+ "zero-length directory entry");
+ err = -EIO;
+ goto out_unlock;
+ }
+ err = -EEXIST;
+ if (ufs_match(sb, namelen, name, de))
+ goto out_unlock;
+ name_len = UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de));
+ rec_len = fs16_to_cpu(sb, de->d_reclen);
+ if (!de->d_ino && rec_len >= reclen)
+ goto got_it;
+ if (rec_len >= name_len + reclen)
+ goto got_it;
+ de = (struct ufs_dir_entry *) ((char *) de + rec_len);
+ }
+ folio_unlock(folio);
+ folio_release_kmap(folio, kaddr);
+ }
+ BUG();
+ return -EINVAL;
+
+got_it:
+ pos = folio_pos(folio) + offset_in_folio(folio, de);
+ err = ufs_prepare_chunk(folio, pos, rec_len);
+ if (err)
+ goto out_unlock;
+ if (de->d_ino) {
+ struct ufs_dir_entry *de1 =
+ (struct ufs_dir_entry *) ((char *) de + name_len);
+ de1->d_reclen = cpu_to_fs16(sb, rec_len - name_len);
+ de->d_reclen = cpu_to_fs16(sb, name_len);
+
+ de = de1;
+ }
+
+ ufs_set_de_namlen(sb, de, namelen);
+ memcpy(de->d_name, name, namelen + 1);
+ de->d_ino = cpu_to_fs32(sb, inode->i_ino);
+ ufs_set_de_type(sb, de, inode->i_mode);
+
+ ufs_commit_chunk(folio, pos, rec_len);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
+
+ mark_inode_dirty(dir);
+ err = ufs_handle_dirsync(dir);
+ /* OFFSET_CACHE */
+out_put:
+ folio_release_kmap(folio, de);
+ return err;
+out_unlock:
+ folio_unlock(folio);
+ goto out_put;
+}
+
+static inline unsigned
+ufs_validate_entry(struct super_block *sb, char *base,
+ unsigned offset, unsigned mask)
+{
+ struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset);
+ struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask));
+ while ((char*)p < (char*)de)
+ p = ufs_next_entry(sb, p);
+ return (char *)p - base;
+}
+
+
+/*
+ * This is blatantly stolen from ext2fs
+ */
+static int
+ufs_readdir(struct file *file, struct dir_context *ctx)
+{
+ loff_t pos = ctx->pos;
+ struct inode *inode = file_inode(file);
+ struct super_block *sb = inode->i_sb;
+ unsigned int offset = pos & ~PAGE_MASK;
+ unsigned long n = pos >> PAGE_SHIFT;
+ unsigned long npages = dir_pages(inode);
+ unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
+ bool need_revalidate = !inode_eq_iversion(inode, *(u64 *)file->private_data);
+ unsigned flags = UFS_SB(sb)->s_flags;
+
+ UFSD("BEGIN\n");
+
+ if (pos > inode->i_size - UFS_DIR_REC_LEN(1))
+ return 0;
+
+ for ( ; n < npages; n++, offset = 0) {
+ struct ufs_dir_entry *de;
+ struct folio *folio;
+ char *kaddr = ufs_get_folio(inode, n, &folio);
+ char *limit;
+
+ if (IS_ERR(kaddr)) {
+ ufs_error(sb, __func__,
+ "bad page in #%llu",
+ inode->i_ino);
+ ctx->pos += PAGE_SIZE - offset;
+ return PTR_ERR(kaddr);
+ }
+ if (unlikely(need_revalidate)) {
+ if (offset) {
+ offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask);
+ ctx->pos = (n<<PAGE_SHIFT) + offset;
+ }
+ *(u64 *)file->private_data = inode_query_iversion(inode);
+ need_revalidate = false;
+ }
+ de = (struct ufs_dir_entry *)(kaddr+offset);
+ limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1);
+ for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) {
+ if (de->d_ino) {
+ unsigned char d_type = DT_UNKNOWN;
+
+ UFSD("filldir(%s,%u)\n", de->d_name,
+ fs32_to_cpu(sb, de->d_ino));
+ UFSD("namlen %u\n", ufs_get_de_namlen(sb, de));
+
+ if ((flags & UFS_DE_MASK) == UFS_DE_44BSD)
+ d_type = de->d_u.d_44.d_type;
+
+ if (!dir_emit(ctx, de->d_name,
+ ufs_get_de_namlen(sb, de),
+ fs32_to_cpu(sb, de->d_ino),
+ d_type)) {
+ folio_release_kmap(folio, de);
+ return 0;
+ }
+ }
+ ctx->pos += fs16_to_cpu(sb, de->d_reclen);
+ }
+ folio_release_kmap(folio, kaddr);
+ }
+ return 0;
+}
+
+
+/*
+ * ufs_delete_entry deletes a directory entry by merging it with the
+ * previous entry.
+ */
+int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
+ struct folio *folio)
+{
+ struct super_block *sb = inode->i_sb;
+ size_t from, to;
+ char *kaddr;
+ loff_t pos;
+ struct ufs_dir_entry *de, *pde = NULL;
+ int err;
+
+ UFSD("ENTER\n");
+
+ from = offset_in_folio(folio, dir);
+ to = from + fs16_to_cpu(sb, dir->d_reclen);
+ kaddr = (char *)dir - from;
+ from &= ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
+ de = (struct ufs_dir_entry *) (kaddr + from);
+
+ UFSD("ino %u, reclen %u, namlen %u, name %s\n",
+ fs32_to_cpu(sb, de->d_ino),
+ fs16_to_cpu(sb, de->d_reclen),
+ ufs_get_de_namlen(sb, de), de->d_name);
+
+ while ((char*)de < (char*)dir) {
+ if (de->d_reclen == 0) {
+ ufs_error(inode->i_sb, __func__,
+ "zero-length directory entry");
+ return -EIO;
+ }
+ pde = de;
+ de = ufs_next_entry(sb, de);
+ }
+ if (pde)
+ from = offset_in_folio(folio, pde);
+ pos = folio_pos(folio) + from;
+ folio_lock(folio);
+ err = ufs_prepare_chunk(folio, pos, to - from);
+ if (unlikely(err)) {
+ folio_unlock(folio);
+ return err;
+ }
+ if (pde)
+ pde->d_reclen = cpu_to_fs16(sb, to - from);
+ dir->d_ino = 0;
+ ufs_commit_chunk(folio, pos, to - from);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ mark_inode_dirty(inode);
+ return ufs_handle_dirsync(inode);
+}
+
+int ufs_make_empty(struct inode * inode, struct inode *dir)
+{
+ struct super_block * sb = dir->i_sb;
+ struct address_space *mapping = inode->i_mapping;
+ struct folio *folio = filemap_grab_folio(mapping, 0);
+ const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize;
+ struct ufs_dir_entry * de;
+ int err;
+ char *kaddr;
+
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);
+
+ err = ufs_prepare_chunk(folio, 0, chunk_size);
+ if (err) {
+ folio_unlock(folio);
+ goto fail;
+ }
+
+ kaddr = kmap_local_folio(folio, 0);
+ memset(kaddr, 0, folio_size(folio));
+
+ de = (struct ufs_dir_entry *)kaddr;
+
+ de->d_ino = cpu_to_fs32(sb, inode->i_ino);
+ ufs_set_de_type(sb, de, inode->i_mode);
+ ufs_set_de_namlen(sb, de, 1);
+ de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1));
+ strcpy (de->d_name, ".");
+ de = (struct ufs_dir_entry *)
+ ((char *)de + fs16_to_cpu(sb, de->d_reclen));
+ de->d_ino = cpu_to_fs32(sb, dir->i_ino);
+ ufs_set_de_type(sb, de, dir->i_mode);
+ de->d_reclen = cpu_to_fs16(sb, chunk_size - UFS_DIR_REC_LEN(1));
+ ufs_set_de_namlen(sb, de, 2);
+ strcpy (de->d_name, "..");
+ kunmap_local(kaddr);
+
+ ufs_commit_chunk(folio, 0, chunk_size);
+ err = ufs_handle_dirsync(inode);
+fail:
+ folio_put(folio);
+ return err;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int ufs_empty_dir(struct inode * inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct folio *folio;
+ char *kaddr;
+ unsigned long i, npages = dir_pages(inode);
+
+ for (i = 0; i < npages; i++) {
+ struct ufs_dir_entry *de;
+
+ kaddr = ufs_get_folio(inode, i, &folio);
+ if (IS_ERR(kaddr))
+ continue;
+
+ de = (struct ufs_dir_entry *)kaddr;
+ kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1);
+
+ while ((char *)de <= kaddr) {
+ if (de->d_reclen == 0) {
+ ufs_error(inode->i_sb, __func__,
+ "zero-length directory entry: "
+ "kaddr=%p, de=%p\n", kaddr, de);
+ goto not_empty;
+ }
+ if (de->d_ino) {
+ u16 namelen=ufs_get_de_namlen(sb, de);
+ /* check for . and .. */
+ if (de->d_name[0] != '.')
+ goto not_empty;
+ if (namelen > 2)
+ goto not_empty;
+ if (namelen < 2) {
+ if (inode->i_ino !=
+ fs32_to_cpu(sb, de->d_ino))
+ goto not_empty;
+ } else if (de->d_name[1] != '.')
+ goto not_empty;
+ }
+ de = ufs_next_entry(sb, de);
+ }
+ folio_release_kmap(folio, kaddr);
+ }
+ return 1;
+
+not_empty:
+ folio_release_kmap(folio, kaddr);
+ return 0;
+}
+
+static int ufs_dir_open(struct inode *inode, struct file *file)
+{
+ file->private_data = kzalloc(sizeof(u64), GFP_KERNEL);
+ if (!file->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+static int ufs_dir_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static loff_t ufs_dir_llseek(struct file *file, loff_t offset, int whence)
+{
+ return generic_llseek_cookie(file, offset, whence,
+ (u64 *)file->private_data);
+}
+
+const struct file_operations ufs_dir_operations = {
+ .open = ufs_dir_open,
+ .release = ufs_dir_release,
+ .read = generic_read_dir,
+ .iterate_shared = ufs_readdir,
+ .fsync = simple_fsync,
+ .llseek = ufs_dir_llseek,
+ .setlease = generic_setlease,
+};
diff --git a/fs/ufs/file.c b/fs/ufs/file.c
new file mode 100644
index 000000000..85c509ced
--- /dev/null
+++ b/fs/ufs/file.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/file.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/file.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 fs regular file handling primitives
+ */
+
+#include <linux/fs.h>
+#include <linux/filelock.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ufs filesystem.
+ */
+
+const struct file_operations ufs_file_operations = {
+ .llseek = generic_file_llseek,
+ .read_iter = generic_file_read_iter,
+ .write_iter = generic_file_write_iter,
+ .mmap_prepare = generic_file_mmap_prepare,
+ .open = generic_file_open,
+ .fsync = simple_fsync,
+ .splice_read = filemap_splice_read,
+ .splice_write = iter_file_splice_write,
+ .setlease = generic_setlease,
+};
diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c
new file mode 100644
index 000000000..8e51f4630
--- /dev/null
+++ b/fs/ufs/ialloc.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/ialloc.c
+ *
+ * Copyright (c) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/ialloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * BSD ufs-inspired inode and directory allocation by
+ * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ *
+ * UFS2 write support added by
+ * Evgeniy Dushistov <dushistov@mail.ru>, 2007
+ */
+
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/buffer_head.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <asm/byteorder.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ *
+ * HOWEVER: we must make sure that we get no aliases,
+ * which means that we have to call "clear_inode()"
+ * _before_ we mark the inode not in use in the inode
+ * bitmaps. Otherwise a newly created file might use
+ * the same inode number (not actually the same pointer
+ * though), and then we'd have two inodes sharing the
+ * same inode number and space on the harddisk.
+ */
+void ufs_free_inode (struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ int is_directory;
+ unsigned ino, cg, bit;
+
+ UFSD("ENTER, ino %llu\n", inode->i_ino);
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+
+ ino = inode->i_ino;
+
+ mutex_lock(&UFS_SB(sb)->s_lock);
+
+ if (!((ino > 1) && (ino < (uspi->s_ncg * uspi->s_ipg )))) {
+ ufs_warning(sb, "ufs_free_inode", "reserved inode or nonexistent inode %u\n", ino);
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return;
+ }
+
+ cg = ufs_inotocg (ino);
+ bit = ufs_inotocgoff (ino);
+ ucpi = ufs_load_cylinder (sb, cg);
+ if (!ucpi) {
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return;
+ }
+ ucg = ubh_get_ucg(UCPI_UBH(ucpi));
+ if (!ufs_cg_chkmagic(sb, ucg))
+ ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number");
+
+ ucg->cg_time = ufs_get_seconds(sb);
+
+ is_directory = S_ISDIR(inode->i_mode);
+
+ if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit))
+ ufs_error(sb, "ufs_free_inode", "bit already cleared for inode %u", ino);
+ else {
+ ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit);
+ if (ino < ucpi->c_irotor)
+ ucpi->c_irotor = ino;
+ fs32_add(sb, &ucg->cg_cs.cs_nifree, 1);
+ uspi->cs_total.cs_nifree++;
+ fs32_add(sb, &UFS_SB(sb)->fs_cs(cg).cs_nifree, 1);
+
+ if (is_directory) {
+ fs32_sub(sb, &ucg->cg_cs.cs_ndir, 1);
+ uspi->cs_total.cs_ndir--;
+ fs32_sub(sb, &UFS_SB(sb)->fs_cs(cg).cs_ndir, 1);
+ }
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+
+ ufs_mark_sb_dirty(sb);
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ UFSD("EXIT\n");
+}
+
+/*
+ * Nullify new chunk of inodes,
+ * BSD people also set ui_gen field of inode
+ * during nullification, but we not care about
+ * that because of linux ufs do not support NFS
+ */
+static void ufs2_init_inodes_chunk(struct super_block *sb,
+ struct ufs_cg_private_info *ucpi,
+ struct ufs_cylinder_group *ucg)
+{
+ struct buffer_head *bh;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ sector_t beg = uspi->s_sbbase +
+ ufs_inotofsba(ucpi->c_cgx * uspi->s_ipg +
+ fs32_to_cpu(sb, ucg->cg_u.cg_u2.cg_initediblk));
+ sector_t end = beg + uspi->s_fpb;
+
+ UFSD("ENTER cgno %d\n", ucpi->c_cgx);
+
+ for (; beg < end; ++beg) {
+ bh = sb_getblk(sb, beg);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, sb->s_blocksize);
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ }
+
+ fs32_add(sb, &ucg->cg_u.cg_u2.cg_initediblk, uspi->s_inopb);
+ ubh_mark_buffer_dirty(UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+
+ UFSD("EXIT\n");
+}
+
+/*
+ * There are two policies for allocating an inode. If the new inode is
+ * a directory, then a forward search is made for a block group with both
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory's block
+ * group to find a free inode.
+ */
+struct inode *ufs_new_inode(struct inode *dir, umode_t mode)
+{
+ struct super_block * sb;
+ struct ufs_sb_info * sbi;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ struct inode * inode;
+ struct timespec64 ts;
+ unsigned cg, bit, i, j, start;
+ struct ufs_inode_info *ufsi;
+ int err = -ENOSPC;
+
+ UFSD("ENTER\n");
+
+ /* Cannot create files in a deleted directory */
+ if (!dir || !dir->i_nlink)
+ return ERR_PTR(-EPERM);
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ ufsi = UFS_I(inode);
+ sbi = UFS_SB(sb);
+ uspi = sbi->s_uspi;
+
+ mutex_lock(&sbi->s_lock);
+
+ /*
+ * Try to place the inode in its parent directory
+ */
+ i = ufs_inotocg(dir->i_ino);
+ if (sbi->fs_cs(i).cs_nifree) {
+ cg = i;
+ goto cg_found;
+ }
+
+ /*
+ * Use a quadratic hash to find a group with a free inode
+ */
+ for ( j = 1; j < uspi->s_ncg; j <<= 1 ) {
+ i += j;
+ if (i >= uspi->s_ncg)
+ i -= uspi->s_ncg;
+ if (sbi->fs_cs(i).cs_nifree) {
+ cg = i;
+ goto cg_found;
+ }
+ }
+
+ /*
+ * That failed: try linear search for a free inode
+ */
+ i = ufs_inotocg(dir->i_ino) + 1;
+ for (j = 2; j < uspi->s_ncg; j++) {
+ i++;
+ if (i >= uspi->s_ncg)
+ i = 0;
+ if (sbi->fs_cs(i).cs_nifree) {
+ cg = i;
+ goto cg_found;
+ }
+ }
+
+ goto failed;
+
+cg_found:
+ ucpi = ufs_load_cylinder (sb, cg);
+ if (!ucpi) {
+ err = -EIO;
+ goto failed;
+ }
+ ucg = ubh_get_ucg(UCPI_UBH(ucpi));
+ if (!ufs_cg_chkmagic(sb, ucg))
+ ufs_panic (sb, "ufs_new_inode", "internal error, bad cg magic number");
+
+ start = ucpi->c_irotor;
+ bit = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_iusedoff, uspi->s_ipg, start);
+ if (!(bit < uspi->s_ipg)) {
+ bit = ubh_find_first_zero_bit (UCPI_UBH(ucpi), ucpi->c_iusedoff, start);
+ if (!(bit < start)) {
+ ufs_error (sb, "ufs_new_inode",
+ "cylinder group %u corrupted - error in inode bitmap\n", cg);
+ err = -EIO;
+ goto failed;
+ }
+ }
+ UFSD("start = %u, bit = %u, ipg = %u\n", start, bit, uspi->s_ipg);
+ if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit))
+ ubh_setbit (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit);
+ else {
+ ufs_panic (sb, "ufs_new_inode", "internal error");
+ err = -EIO;
+ goto failed;
+ }
+
+ if (uspi->fs_magic == UFS2_MAGIC) {
+ u32 initediblk = fs32_to_cpu(sb, ucg->cg_u.cg_u2.cg_initediblk);
+
+ if (bit + uspi->s_inopb > initediblk &&
+ initediblk < fs32_to_cpu(sb, ucg->cg_u.cg_u2.cg_niblk))
+ ufs2_init_inodes_chunk(sb, ucpi, ucg);
+ }
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nifree, 1);
+ uspi->cs_total.cs_nifree--;
+ fs32_sub(sb, &sbi->fs_cs(cg).cs_nifree, 1);
+
+ if (S_ISDIR(mode)) {
+ fs32_add(sb, &ucg->cg_cs.cs_ndir, 1);
+ uspi->cs_total.cs_ndir++;
+ fs32_add(sb, &sbi->fs_cs(cg).cs_ndir, 1);
+ }
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ ubh_sync_block(UCPI_UBH(ucpi));
+ ufs_mark_sb_dirty(sb);
+
+ inode->i_ino = cg * uspi->s_ipg + bit;
+ inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
+ inode->i_blocks = 0;
+ inode->i_generation = 0;
+ simple_inode_init_ts(inode);
+ ufsi->i_flags = UFS_I(dir)->i_flags;
+ ufsi->i_lastfrag = 0;
+ ufsi->i_shadow = 0;
+ ufsi->i_osync = 0;
+ ufsi->i_oeftflag = 0;
+ ufsi->i_dir_start_lookup = 0;
+ memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1));
+ if (insert_inode_locked(inode) < 0) {
+ err = -EIO;
+ goto failed;
+ }
+ mark_inode_dirty(inode);
+
+ if (uspi->fs_magic == UFS2_MAGIC) {
+ struct buffer_head *bh;
+ struct ufs2_inode *ufs2_inode;
+
+ /*
+ * setup birth date, we do it here because of there is no sense
+ * to hold it in struct ufs_inode_info, and lose 64 bit
+ */
+ bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino));
+ if (!bh) {
+ ufs_warning(sb, "ufs_read_inode",
+ "unable to read inode %llu\n",
+ inode->i_ino);
+ err = -EIO;
+ goto fail_remove_inode;
+ }
+ lock_buffer(bh);
+ ufs2_inode = (struct ufs2_inode *)bh->b_data;
+ ufs2_inode += ufs_inotofsbo(inode->i_ino);
+ ktime_get_real_ts64(&ts);
+ ufs2_inode->ui_birthtime = cpu_to_fs64(sb, ts.tv_sec);
+ ufs2_inode->ui_birthnsec = cpu_to_fs32(sb, ts.tv_nsec);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ if (sb->s_flags & SB_SYNCHRONOUS)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ }
+ mutex_unlock(&sbi->s_lock);
+
+ UFSD("allocating inode %llu\n", inode->i_ino);
+ UFSD("EXIT\n");
+ return inode;
+
+fail_remove_inode:
+ mutex_unlock(&sbi->s_lock);
+ clear_nlink(inode);
+ discard_new_inode(inode);
+ UFSD("EXIT (FAILED): err %d\n", err);
+ return ERR_PTR(err);
+failed:
+ mutex_unlock(&sbi->s_lock);
+ make_bad_inode(inode);
+ iput (inode);
+ UFSD("EXIT (FAILED): err %d\n", err);
+ return ERR_PTR(err);
+}
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
new file mode 100644
index 000000000..440d014cc
--- /dev/null
+++ b/fs/ufs/inode.c
@@ -0,0 +1,1222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/inode.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/buffer_head.h>
+#include <linux/mpage.h>
+#include <linux/writeback.h>
+#include <linux/iversion.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+static int ufs_block_to_path(struct inode *inode, sector_t i_block, unsigned offsets[4])
+{
+ struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi;
+ int ptrs = uspi->s_apb;
+ int ptrs_bits = uspi->s_apbshift;
+ const long direct_blocks = UFS_NDADDR,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ int n = 0;
+
+
+ UFSD("ptrs=uspi->s_apb = %d,double_blocks=%ld \n",ptrs,double_blocks);
+ if (i_block < direct_blocks) {
+ offsets[n++] = i_block;
+ } else if ((i_block -= direct_blocks) < indirect_blocks) {
+ offsets[n++] = UFS_IND_BLOCK;
+ offsets[n++] = i_block;
+ } else if ((i_block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = UFS_DIND_BLOCK;
+ offsets[n++] = i_block >> ptrs_bits;
+ offsets[n++] = i_block & (ptrs - 1);
+ } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+ offsets[n++] = UFS_TIND_BLOCK;
+ offsets[n++] = i_block >> (ptrs_bits * 2);
+ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+ offsets[n++] = i_block & (ptrs - 1);
+ } else {
+ ufs_warning(inode->i_sb, "ufs_block_to_path", "block > big");
+ }
+ return n;
+}
+
+typedef struct {
+ void *p;
+ union {
+ __fs32 key32;
+ __fs64 key64;
+ };
+ struct buffer_head *bh;
+} Indirect;
+
+static inline int grow_chain32(struct ufs_inode_info *ufsi,
+ struct buffer_head *bh, __fs32 *v,
+ Indirect *from, Indirect *to)
+{
+ Indirect *p;
+ unsigned seq;
+ to->bh = bh;
+ do {
+ seq = read_seqbegin(&ufsi->meta_lock);
+ to->key32 = *(__fs32 *)(to->p = v);
+ for (p = from; p <= to && p->key32 == *(__fs32 *)p->p; p++)
+ ;
+ } while (read_seqretry(&ufsi->meta_lock, seq));
+ return (p > to);
+}
+
+static inline int grow_chain64(struct ufs_inode_info *ufsi,
+ struct buffer_head *bh, __fs64 *v,
+ Indirect *from, Indirect *to)
+{
+ Indirect *p;
+ unsigned seq;
+ to->bh = bh;
+ do {
+ seq = read_seqbegin(&ufsi->meta_lock);
+ to->key64 = *(__fs64 *)(to->p = v);
+ for (p = from; p <= to && p->key64 == *(__fs64 *)p->p; p++)
+ ;
+ } while (read_seqretry(&ufsi->meta_lock, seq));
+ return (p > to);
+}
+
+/*
+ * Returns the location of the fragment from
+ * the beginning of the filesystem.
+ */
+
+static u64 ufs_frag_map(struct inode *inode, unsigned offsets[4], int depth)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ u64 mask = (u64) uspi->s_apbmask>>uspi->s_fpbshift;
+ int shift = uspi->s_apbshift-uspi->s_fpbshift;
+ Indirect chain[4], *q = chain;
+ unsigned *p;
+ unsigned flags = UFS_SB(sb)->s_flags;
+ u64 res = 0;
+
+ UFSD(": uspi->s_fpbshift = %d ,uspi->s_apbmask = %x, mask=%llx\n",
+ uspi->s_fpbshift, uspi->s_apbmask,
+ (unsigned long long)mask);
+
+ if (depth == 0)
+ goto no_block;
+
+again:
+ p = offsets;
+
+ if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2)
+ goto ufs2;
+
+ if (!grow_chain32(ufsi, NULL, &ufsi->i_u1.i_data[*p++], chain, q))
+ goto changed;
+ if (!q->key32)
+ goto no_block;
+ while (--depth) {
+ __fs32 *ptr;
+ struct buffer_head *bh;
+ unsigned n = *p++;
+
+ bh = sb_bread(sb, uspi->s_sbbase +
+ fs32_to_cpu(sb, q->key32) + (n>>shift));
+ if (!bh)
+ goto no_block;
+ ptr = (__fs32 *)bh->b_data + (n & mask);
+ if (!grow_chain32(ufsi, bh, ptr, chain, ++q))
+ goto changed;
+ if (!q->key32)
+ goto no_block;
+ }
+ res = fs32_to_cpu(sb, q->key32);
+ goto found;
+
+ufs2:
+ if (!grow_chain64(ufsi, NULL, &ufsi->i_u1.u2_i_data[*p++], chain, q))
+ goto changed;
+ if (!q->key64)
+ goto no_block;
+
+ while (--depth) {
+ __fs64 *ptr;
+ struct buffer_head *bh;
+ unsigned n = *p++;
+
+ bh = sb_bread(sb, uspi->s_sbbase +
+ fs64_to_cpu(sb, q->key64) + (n>>shift));
+ if (!bh)
+ goto no_block;
+ ptr = (__fs64 *)bh->b_data + (n & mask);
+ if (!grow_chain64(ufsi, bh, ptr, chain, ++q))
+ goto changed;
+ if (!q->key64)
+ goto no_block;
+ }
+ res = fs64_to_cpu(sb, q->key64);
+found:
+ res += uspi->s_sbbase;
+no_block:
+ while (q > chain) {
+ brelse(q->bh);
+ q--;
+ }
+ return res;
+
+changed:
+ while (q > chain) {
+ brelse(q->bh);
+ q--;
+ }
+ goto again;
+}
+
+/*
+ * Unpacking tails: we have a file with partial final block and
+ * we had been asked to extend it. If the fragment being written
+ * is within the same block, we need to extend the tail just to cover
+ * that fragment. Otherwise the tail is extended to full block.
+ *
+ * Note that we might need to create a _new_ tail, but that will
+ * be handled elsewhere; this is strictly for resizing old
+ * ones.
+ */
+static bool
+ufs_extend_tail(struct inode *inode, u64 writes_to,
+ int *err, struct folio *locked_folio)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ unsigned lastfrag = ufsi->i_lastfrag; /* it's a short file, so unsigned is enough */
+ unsigned block = ufs_fragstoblks(lastfrag);
+ unsigned new_size;
+ void *p;
+ u64 tmp;
+
+ if (writes_to < (lastfrag | uspi->s_fpbmask))
+ new_size = (writes_to & uspi->s_fpbmask) + 1;
+ else
+ new_size = uspi->s_fpb;
+
+ p = ufs_get_direct_data_ptr(uspi, ufsi, block);
+ tmp = ufs_new_fragments(inode, p, lastfrag, ufs_data_ptr_to_cpu(sb, p),
+ new_size - (lastfrag & uspi->s_fpbmask), err,
+ locked_folio);
+ return tmp != 0;
+}
+
+/**
+ * ufs_inode_getfrag() - allocate new fragment(s)
+ * @inode: pointer to inode
+ * @index: number of block pointer within the inode's array.
+ * @new_fragment: number of new allocated fragment(s)
+ * @err: we set it if something wrong
+ * @new: we set it if we allocate new block
+ * @locked_folio: for ufs_new_fragments()
+ */
+static u64 ufs_inode_getfrag(struct inode *inode, unsigned index,
+ sector_t new_fragment, int *err,
+ int *new, struct folio *locked_folio)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ u64 tmp, goal, lastfrag;
+ unsigned nfrags = uspi->s_fpb;
+ void *p;
+
+ p = ufs_get_direct_data_ptr(uspi, ufsi, index);
+ tmp = ufs_data_ptr_to_cpu(sb, p);
+ if (tmp)
+ goto out;
+
+ lastfrag = ufsi->i_lastfrag;
+
+ /* will that be a new tail? */
+ if (new_fragment < UFS_NDIR_FRAGMENT && new_fragment >= lastfrag)
+ nfrags = (new_fragment & uspi->s_fpbmask) + 1;
+
+ goal = 0;
+ if (index) {
+ goal = ufs_data_ptr_to_cpu(sb,
+ ufs_get_direct_data_ptr(uspi, ufsi, index - 1));
+ if (goal)
+ goal += uspi->s_fpb;
+ }
+ tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment),
+ goal, nfrags, err, locked_folio);
+
+ if (!tmp) {
+ *err = -ENOSPC;
+ return 0;
+ }
+
+ if (new)
+ *new = 1;
+ inode_set_ctime_current(inode);
+ if (IS_SYNC(inode))
+ ufs_sync_inode (inode);
+ mark_inode_dirty(inode);
+out:
+ return tmp + uspi->s_sbbase;
+}
+
+/**
+ * ufs_inode_getblock() - allocate new block
+ * @inode: pointer to inode
+ * @ind_block: block number of the indirect block
+ * @index: number of pointer within the indirect block
+ * @new_fragment: number of new allocated fragment
+ * (block will hold this fragment and also uspi->s_fpb-1)
+ * @err: see ufs_inode_getfrag()
+ * @new: see ufs_inode_getfrag()
+ * @locked_folio: see ufs_inode_getfrag()
+ */
+static u64 ufs_inode_getblock(struct inode *inode, u64 ind_block,
+ unsigned index, sector_t new_fragment, int *err,
+ int *new, struct folio *locked_folio)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ int shift = uspi->s_apbshift - uspi->s_fpbshift;
+ u64 tmp = 0, goal;
+ struct buffer_head *bh;
+ void *p;
+
+ if (!ind_block)
+ return 0;
+
+ bh = sb_bread(sb, ind_block + (index >> shift));
+ if (unlikely(!bh)) {
+ *err = -EIO;
+ return 0;
+ }
+
+ index &= uspi->s_apbmask >> uspi->s_fpbshift;
+ if (uspi->fs_magic == UFS2_MAGIC)
+ p = (__fs64 *)bh->b_data + index;
+ else
+ p = (__fs32 *)bh->b_data + index;
+
+ tmp = ufs_data_ptr_to_cpu(sb, p);
+ if (tmp)
+ goto out;
+
+ if (index && (uspi->fs_magic == UFS2_MAGIC ?
+ (tmp = fs64_to_cpu(sb, ((__fs64 *)bh->b_data)[index-1])) :
+ (tmp = fs32_to_cpu(sb, ((__fs32 *)bh->b_data)[index-1]))))
+ goal = tmp + uspi->s_fpb;
+ else
+ goal = bh->b_blocknr + uspi->s_fpb;
+ tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment), goal,
+ uspi->s_fpb, err, locked_folio);
+ if (!tmp)
+ goto out;
+
+ if (new)
+ *new = 1;
+
+ mark_buffer_dirty(bh);
+ if (IS_SYNC(inode))
+ sync_dirty_buffer(bh);
+ inode_set_ctime_current(inode);
+ mark_inode_dirty(inode);
+out:
+ brelse (bh);
+ UFSD("EXIT\n");
+ if (tmp)
+ tmp += uspi->s_sbbase;
+ return tmp;
+}
+
+/**
+ * ufs_getfrag_block() - `get_block_t' function, interface between UFS and
+ * read_folio, writepages and so on
+ */
+
+static int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ int err = 0, new = 0;
+ unsigned offsets[4];
+ int depth = ufs_block_to_path(inode, fragment >> uspi->s_fpbshift, offsets);
+ u64 phys64 = 0;
+ unsigned frag = fragment & uspi->s_fpbmask;
+
+ phys64 = ufs_frag_map(inode, offsets, depth);
+ if (!create)
+ goto done;
+
+ if (phys64) {
+ if (fragment >= UFS_NDIR_FRAGMENT)
+ goto done;
+ read_seqlock_excl(&UFS_I(inode)->meta_lock);
+ if (fragment < UFS_I(inode)->i_lastfrag) {
+ read_sequnlock_excl(&UFS_I(inode)->meta_lock);
+ goto done;
+ }
+ read_sequnlock_excl(&UFS_I(inode)->meta_lock);
+ }
+ /* This code entered only while writing ....? */
+
+ mutex_lock(&UFS_I(inode)->truncate_mutex);
+
+ UFSD("ENTER, ino %llu, fragment %llu\n", inode->i_ino, (unsigned long long)fragment);
+ if (unlikely(!depth)) {
+ ufs_warning(sb, "ufs_get_block", "block > big");
+ err = -EIO;
+ goto out;
+ }
+
+ if (UFS_I(inode)->i_lastfrag < UFS_NDIR_FRAGMENT) {
+ unsigned lastfrag = UFS_I(inode)->i_lastfrag;
+ unsigned tailfrags = lastfrag & uspi->s_fpbmask;
+ if (tailfrags && fragment >= lastfrag) {
+ if (!ufs_extend_tail(inode, fragment,
+ &err, bh_result->b_folio))
+ goto out;
+ }
+ }
+
+ if (depth == 1) {
+ phys64 = ufs_inode_getfrag(inode, offsets[0], fragment,
+ &err, &new, bh_result->b_folio);
+ } else {
+ int i;
+ phys64 = ufs_inode_getfrag(inode, offsets[0], fragment,
+ &err, NULL, NULL);
+ for (i = 1; i < depth - 1; i++)
+ phys64 = ufs_inode_getblock(inode, phys64, offsets[i],
+ fragment, &err, NULL, NULL);
+ phys64 = ufs_inode_getblock(inode, phys64, offsets[depth - 1],
+ fragment, &err, &new, bh_result->b_folio);
+ }
+out:
+ if (phys64) {
+ phys64 += frag;
+ map_bh(bh_result, sb, phys64);
+ if (new)
+ set_buffer_new(bh_result);
+ }
+ mutex_unlock(&UFS_I(inode)->truncate_mutex);
+ return err;
+
+done:
+ if (phys64)
+ map_bh(bh_result, sb, phys64 + frag);
+ return 0;
+}
+
+static int ufs_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ return mpage_writepages(mapping, wbc, ufs_getfrag_block);
+}
+
+static int ufs_read_folio(struct file *file, struct folio *folio)
+{
+ return block_read_full_folio(folio, ufs_getfrag_block);
+}
+
+int ufs_prepare_chunk(struct folio *folio, loff_t pos, unsigned len)
+{
+ return __block_write_begin(folio, pos, len, ufs_getfrag_block);
+}
+
+static void ufs_truncate_blocks(struct inode *);
+
+static void ufs_write_failed(struct address_space *mapping, loff_t to)
+{
+ struct inode *inode = mapping->host;
+
+ if (to > inode->i_size) {
+ truncate_pagecache(inode, inode->i_size);
+ ufs_truncate_blocks(inode);
+ }
+}
+
+static int ufs_write_begin(const struct kiocb *iocb,
+ struct address_space *mapping,
+ loff_t pos, unsigned len,
+ struct folio **foliop, void **fsdata)
+{
+ int ret;
+
+ ret = block_write_begin(mapping, pos, len, foliop, ufs_getfrag_block);
+ if (unlikely(ret))
+ ufs_write_failed(mapping, pos + len);
+
+ return ret;
+}
+
+static int ufs_write_end(const struct kiocb *iocb,
+ struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct folio *folio, void *fsdata)
+{
+ int ret;
+
+ ret = generic_write_end(iocb, mapping, pos, len, copied, folio, fsdata);
+ if (ret < len)
+ ufs_write_failed(mapping, pos + len);
+ return ret;
+}
+
+static sector_t ufs_bmap(struct address_space *mapping, sector_t block)
+{
+ return generic_block_bmap(mapping,block,ufs_getfrag_block);
+}
+
+const struct address_space_operations ufs_aops = {
+ .dirty_folio = block_dirty_folio,
+ .invalidate_folio = block_invalidate_folio,
+ .read_folio = ufs_read_folio,
+ .writepages = ufs_writepages,
+ .write_begin = ufs_write_begin,
+ .write_end = ufs_write_end,
+ .migrate_folio = buffer_migrate_folio,
+ .bmap = ufs_bmap
+};
+
+static void ufs_set_inode_ops(struct inode *inode)
+{
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ufs_file_inode_operations;
+ inode->i_fop = &ufs_file_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ufs_dir_inode_operations;
+ inode->i_fop = &ufs_dir_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (!inode->i_blocks) {
+ inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink;
+ inode->i_op = &simple_symlink_inode_operations;
+ } else {
+ inode->i_mapping->a_ops = &ufs_aops;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
+ }
+ } else
+ init_special_inode(inode, inode->i_mode,
+ ufs_get_inode_dev(inode->i_sb, UFS_I(inode)));
+}
+
+static int ufs1_read_inode(struct inode *inode, struct ufs_inode *ufs_inode)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ umode_t mode;
+
+ /*
+ * Copy data to the in-core inode.
+ */
+ inode->i_mode = mode = fs16_to_cpu(sb, ufs_inode->ui_mode);
+ set_nlink(inode, fs16_to_cpu(sb, ufs_inode->ui_nlink));
+ if (inode->i_nlink == 0)
+ return -ESTALE;
+
+ /*
+ * Linux now has 32-bit uid and gid, so we can support EFT.
+ */
+ i_uid_write(inode, ufs_get_inode_uid(sb, ufs_inode));
+ i_gid_write(inode, ufs_get_inode_gid(sb, ufs_inode));
+
+ inode->i_size = fs64_to_cpu(sb, ufs_inode->ui_size);
+ inode_set_atime(inode,
+ (signed)fs32_to_cpu(sb, ufs_inode->ui_atime.tv_sec),
+ 0);
+ inode_set_ctime(inode,
+ (signed)fs32_to_cpu(sb, ufs_inode->ui_ctime.tv_sec),
+ 0);
+ inode_set_mtime(inode,
+ (signed)fs32_to_cpu(sb, ufs_inode->ui_mtime.tv_sec),
+ 0);
+ inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks);
+ inode->i_generation = fs32_to_cpu(sb, ufs_inode->ui_gen);
+ ufsi->i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
+ ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
+ ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
+
+
+ if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) {
+ memcpy(ufsi->i_u1.i_data, &ufs_inode->ui_u2.ui_addr,
+ sizeof(ufs_inode->ui_u2.ui_addr));
+ } else {
+ memcpy(ufsi->i_u1.i_symlink, ufs_inode->ui_u2.ui_symlink,
+ sizeof(ufs_inode->ui_u2.ui_symlink) - 1);
+ ufsi->i_u1.i_symlink[sizeof(ufs_inode->ui_u2.ui_symlink) - 1] = 0;
+ }
+ return 0;
+}
+
+static int ufs2_read_inode(struct inode *inode, struct ufs2_inode *ufs2_inode)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ umode_t mode;
+
+ UFSD("Reading ufs2 inode, ino %llu\n", inode->i_ino);
+ /*
+ * Copy data to the in-core inode.
+ */
+ inode->i_mode = mode = fs16_to_cpu(sb, ufs2_inode->ui_mode);
+ set_nlink(inode, fs16_to_cpu(sb, ufs2_inode->ui_nlink));
+ if (inode->i_nlink == 0)
+ return -ESTALE;
+
+ /*
+ * Linux now has 32-bit uid and gid, so we can support EFT.
+ */
+ i_uid_write(inode, fs32_to_cpu(sb, ufs2_inode->ui_uid));
+ i_gid_write(inode, fs32_to_cpu(sb, ufs2_inode->ui_gid));
+
+ inode->i_size = fs64_to_cpu(sb, ufs2_inode->ui_size);
+ inode_set_atime(inode, fs64_to_cpu(sb, ufs2_inode->ui_atime),
+ fs32_to_cpu(sb, ufs2_inode->ui_atimensec));
+ inode_set_ctime(inode, fs64_to_cpu(sb, ufs2_inode->ui_ctime),
+ fs32_to_cpu(sb, ufs2_inode->ui_ctimensec));
+ inode_set_mtime(inode, fs64_to_cpu(sb, ufs2_inode->ui_mtime),
+ fs32_to_cpu(sb, ufs2_inode->ui_mtimensec));
+ inode->i_blocks = fs64_to_cpu(sb, ufs2_inode->ui_blocks);
+ inode->i_generation = fs32_to_cpu(sb, ufs2_inode->ui_gen);
+ ufsi->i_flags = fs32_to_cpu(sb, ufs2_inode->ui_flags);
+ /*
+ ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
+ ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
+ */
+
+ if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) {
+ memcpy(ufsi->i_u1.u2_i_data, &ufs2_inode->ui_u2.ui_addr,
+ sizeof(ufs2_inode->ui_u2.ui_addr));
+ } else {
+ memcpy(ufsi->i_u1.i_symlink, ufs2_inode->ui_u2.ui_symlink,
+ sizeof(ufs2_inode->ui_u2.ui_symlink) - 1);
+ ufsi->i_u1.i_symlink[sizeof(ufs2_inode->ui_u2.ui_symlink) - 1] = 0;
+ }
+ return 0;
+}
+
+struct inode *ufs_iget(struct super_block *sb, unsigned long ino)
+{
+ struct ufs_inode_info *ufsi;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ struct buffer_head * bh;
+ struct inode *inode;
+ int err = -EIO;
+
+ UFSD("ENTER, ino %lu\n", ino);
+
+ if (ino < UFS_ROOTINO || ino > (uspi->s_ncg * uspi->s_ipg)) {
+ ufs_warning(sb, "ufs_read_inode", "bad inode number (%lu)\n",
+ ino);
+ return ERR_PTR(-EIO);
+ }
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode_state_read_once(inode) & I_NEW))
+ return inode;
+
+ ufsi = UFS_I(inode);
+
+ bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino));
+ if (!bh) {
+ ufs_warning(sb, "ufs_read_inode", "unable to read inode %llu\n",
+ inode->i_ino);
+ goto bad_inode;
+ }
+ if ((UFS_SB(sb)->s_flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) {
+ struct ufs2_inode *ufs2_inode = (struct ufs2_inode *)bh->b_data;
+
+ err = ufs2_read_inode(inode,
+ ufs2_inode + ufs_inotofsbo(inode->i_ino));
+ } else {
+ struct ufs_inode *ufs_inode = (struct ufs_inode *)bh->b_data;
+
+ err = ufs1_read_inode(inode,
+ ufs_inode + ufs_inotofsbo(inode->i_ino));
+ }
+ brelse(bh);
+ if (err)
+ goto bad_inode;
+
+ inode_inc_iversion(inode);
+ ufsi->i_lastfrag =
+ (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ ufsi->i_dir_start_lookup = 0;
+ ufsi->i_osync = 0;
+
+ ufs_set_inode_ops(inode);
+
+ UFSD("EXIT\n");
+ unlock_new_inode(inode);
+ return inode;
+
+bad_inode:
+ iget_failed(inode);
+ return ERR_PTR(err);
+}
+
+static void ufs1_update_inode(struct inode *inode, struct ufs_inode *ufs_inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+
+ ufs_inode->ui_mode = cpu_to_fs16(sb, inode->i_mode);
+ ufs_inode->ui_nlink = cpu_to_fs16(sb, inode->i_nlink);
+
+ ufs_set_inode_uid(sb, ufs_inode, i_uid_read(inode));
+ ufs_set_inode_gid(sb, ufs_inode, i_gid_read(inode));
+
+ ufs_inode->ui_size = cpu_to_fs64(sb, inode->i_size);
+ ufs_inode->ui_atime.tv_sec = cpu_to_fs32(sb,
+ inode_get_atime_sec(inode));
+ ufs_inode->ui_atime.tv_usec = 0;
+ ufs_inode->ui_ctime.tv_sec = cpu_to_fs32(sb,
+ inode_get_ctime_sec(inode));
+ ufs_inode->ui_ctime.tv_usec = 0;
+ ufs_inode->ui_mtime.tv_sec = cpu_to_fs32(sb,
+ inode_get_mtime_sec(inode));
+ ufs_inode->ui_mtime.tv_usec = 0;
+ ufs_inode->ui_blocks = cpu_to_fs32(sb, inode->i_blocks);
+ ufs_inode->ui_flags = cpu_to_fs32(sb, ufsi->i_flags);
+ ufs_inode->ui_gen = cpu_to_fs32(sb, inode->i_generation);
+
+ if ((UFS_SB(sb)->s_flags & UFS_UID_MASK) == UFS_UID_EFT) {
+ ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, ufsi->i_shadow);
+ ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, ufsi->i_oeftflag);
+ }
+
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
+ /* ufs_inode->ui_u2.ui_addr.ui_db[0] = cpu_to_fs32(sb, inode->i_rdev); */
+ ufs_inode->ui_u2.ui_addr.ui_db[0] = ufsi->i_u1.i_data[0];
+ } else if (inode->i_blocks) {
+ memcpy(&ufs_inode->ui_u2.ui_addr, ufsi->i_u1.i_data,
+ sizeof(ufs_inode->ui_u2.ui_addr));
+ }
+ else {
+ memcpy(&ufs_inode->ui_u2.ui_symlink, ufsi->i_u1.i_symlink,
+ sizeof(ufs_inode->ui_u2.ui_symlink));
+ }
+
+ if (!inode->i_nlink)
+ memset (ufs_inode, 0, sizeof(struct ufs_inode));
+}
+
+static void ufs2_update_inode(struct inode *inode, struct ufs2_inode *ufs_inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+
+ UFSD("ENTER\n");
+ ufs_inode->ui_mode = cpu_to_fs16(sb, inode->i_mode);
+ ufs_inode->ui_nlink = cpu_to_fs16(sb, inode->i_nlink);
+
+ ufs_inode->ui_uid = cpu_to_fs32(sb, i_uid_read(inode));
+ ufs_inode->ui_gid = cpu_to_fs32(sb, i_gid_read(inode));
+
+ ufs_inode->ui_size = cpu_to_fs64(sb, inode->i_size);
+ ufs_inode->ui_atime = cpu_to_fs64(sb, inode_get_atime_sec(inode));
+ ufs_inode->ui_atimensec = cpu_to_fs32(sb,
+ inode_get_atime_nsec(inode));
+ ufs_inode->ui_ctime = cpu_to_fs64(sb, inode_get_ctime_sec(inode));
+ ufs_inode->ui_ctimensec = cpu_to_fs32(sb,
+ inode_get_ctime_nsec(inode));
+ ufs_inode->ui_mtime = cpu_to_fs64(sb, inode_get_mtime_sec(inode));
+ ufs_inode->ui_mtimensec = cpu_to_fs32(sb,
+ inode_get_mtime_nsec(inode));
+
+ ufs_inode->ui_blocks = cpu_to_fs64(sb, inode->i_blocks);
+ ufs_inode->ui_flags = cpu_to_fs32(sb, ufsi->i_flags);
+ ufs_inode->ui_gen = cpu_to_fs32(sb, inode->i_generation);
+
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
+ /* ufs_inode->ui_u2.ui_addr.ui_db[0] = cpu_to_fs32(sb, inode->i_rdev); */
+ ufs_inode->ui_u2.ui_addr.ui_db[0] = ufsi->i_u1.u2_i_data[0];
+ } else if (inode->i_blocks) {
+ memcpy(&ufs_inode->ui_u2.ui_addr, ufsi->i_u1.u2_i_data,
+ sizeof(ufs_inode->ui_u2.ui_addr));
+ } else {
+ memcpy(&ufs_inode->ui_u2.ui_symlink, ufsi->i_u1.i_symlink,
+ sizeof(ufs_inode->ui_u2.ui_symlink));
+ }
+
+ if (!inode->i_nlink)
+ memset (ufs_inode, 0, sizeof(struct ufs2_inode));
+ UFSD("EXIT\n");
+}
+
+static int ufs_update_inode(struct inode * inode, int do_sync)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ struct buffer_head * bh;
+
+ UFSD("ENTER, ino %llu\n", inode->i_ino);
+
+ if (inode->i_ino < UFS_ROOTINO ||
+ inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
+ ufs_warning (sb, "ufs_read_inode", "bad inode number (%llu)\n", inode->i_ino);
+ return -1;
+ }
+
+ bh = sb_bread(sb, ufs_inotofsba(inode->i_ino));
+ if (!bh) {
+ ufs_warning (sb, "ufs_read_inode", "unable to read inode %llu\n", inode->i_ino);
+ return -1;
+ }
+ if (uspi->fs_magic == UFS2_MAGIC) {
+ struct ufs2_inode *ufs2_inode = (struct ufs2_inode *)bh->b_data;
+
+ ufs2_update_inode(inode,
+ ufs2_inode + ufs_inotofsbo(inode->i_ino));
+ } else {
+ struct ufs_inode *ufs_inode = (struct ufs_inode *) bh->b_data;
+
+ ufs1_update_inode(inode, ufs_inode + ufs_inotofsbo(inode->i_ino));
+ }
+
+ mark_buffer_dirty(bh);
+ if (do_sync)
+ sync_dirty_buffer(bh);
+ brelse (bh);
+
+ UFSD("EXIT\n");
+ return 0;
+}
+
+int ufs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ return ufs_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
+int ufs_sync_inode (struct inode *inode)
+{
+ return ufs_update_inode (inode, 1);
+}
+
+void ufs_evict_inode(struct inode * inode)
+{
+ int want_delete = 0;
+
+ if (!inode->i_nlink && !is_bad_inode(inode))
+ want_delete = 1;
+
+ truncate_inode_pages_final(&inode->i_data);
+ if (want_delete) {
+ inode->i_size = 0;
+ if (inode->i_blocks &&
+ (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ ufs_truncate_blocks(inode);
+ ufs_update_inode(inode, inode_needs_sync(inode));
+ }
+
+ clear_inode(inode);
+
+ if (want_delete)
+ ufs_free_inode(inode);
+}
+
+struct to_free {
+ struct inode *inode;
+ u64 to;
+ unsigned count;
+};
+
+static inline void free_data(struct to_free *ctx, u64 from, unsigned count)
+{
+ if (ctx->count && ctx->to != from) {
+ ufs_free_blocks(ctx->inode, ctx->to - ctx->count, ctx->count);
+ ctx->count = 0;
+ }
+ ctx->count += count;
+ ctx->to = from + count;
+}
+
+#define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift)
+
+/*
+ * used only for truncation down to direct blocks.
+ */
+static void ufs_trunc_direct(struct inode *inode)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ unsigned int new_frags, old_frags;
+ unsigned int old_slot, new_slot;
+ unsigned int old_tail, new_tail;
+ struct to_free ctx = {.inode = inode};
+
+ UFSD("ENTER: ino %llu\n", inode->i_ino);
+
+ new_frags = DIRECT_FRAGMENT;
+ // new_frags = first fragment past the new EOF
+ old_frags = min_t(u64, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag);
+ // old_frags = first fragment past the old EOF or covered by indirects
+
+ if (new_frags >= old_frags) // expanding - nothing to free
+ goto done;
+
+ old_tail = ufs_fragnum(old_frags);
+ old_slot = ufs_fragstoblks(old_frags);
+ new_tail = ufs_fragnum(new_frags);
+ new_slot = ufs_fragstoblks(new_frags);
+
+ if (old_slot == new_slot) { // old_tail > 0
+ void *p = ufs_get_direct_data_ptr(uspi, ufsi, old_slot);
+ u64 tmp = ufs_data_ptr_to_cpu(sb, p);
+ if (!tmp)
+ ufs_panic(sb, __func__, "internal error");
+ if (!new_tail) {
+ write_seqlock(&ufsi->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&ufsi->meta_lock);
+ }
+ ufs_free_fragments(inode, tmp + new_tail, old_tail - new_tail);
+ } else {
+ unsigned int slot = new_slot;
+
+ if (new_tail) {
+ void *p = ufs_get_direct_data_ptr(uspi, ufsi, slot++);
+ u64 tmp = ufs_data_ptr_to_cpu(sb, p);
+ if (!tmp)
+ ufs_panic(sb, __func__, "internal error");
+
+ ufs_free_fragments(inode, tmp + new_tail,
+ uspi->s_fpb - new_tail);
+ }
+ while (slot < old_slot) {
+ void *p = ufs_get_direct_data_ptr(uspi, ufsi, slot++);
+ u64 tmp = ufs_data_ptr_to_cpu(sb, p);
+ if (!tmp)
+ continue;
+ write_seqlock(&ufsi->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&ufsi->meta_lock);
+
+ free_data(&ctx, tmp, uspi->s_fpb);
+ }
+
+ free_data(&ctx, 0, 0);
+
+ if (old_tail) {
+ void *p = ufs_get_direct_data_ptr(uspi, ufsi, slot);
+ u64 tmp = ufs_data_ptr_to_cpu(sb, p);
+ if (!tmp)
+ ufs_panic(sb, __func__, "internal error");
+ write_seqlock(&ufsi->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&ufsi->meta_lock);
+
+ ufs_free_fragments(inode, tmp, old_tail);
+ }
+ }
+done:
+ UFSD("EXIT: ino %llu\n", inode->i_ino);
+}
+
+static void free_full_branch(struct inode *inode, u64 ind_block, int depth)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ struct ufs_buffer_head *ubh = ubh_bread(sb, ind_block, uspi->s_bsize);
+ unsigned i;
+
+ if (!ubh)
+ return;
+
+ if (--depth) {
+ for (i = 0; i < uspi->s_apb; i++) {
+ void *p = ubh_get_data_ptr(uspi, ubh, i);
+ u64 block = ufs_data_ptr_to_cpu(sb, p);
+ if (block)
+ free_full_branch(inode, block, depth);
+ }
+ } else {
+ struct to_free ctx = {.inode = inode};
+
+ for (i = 0; i < uspi->s_apb; i++) {
+ void *p = ubh_get_data_ptr(uspi, ubh, i);
+ u64 block = ufs_data_ptr_to_cpu(sb, p);
+ if (block)
+ free_data(&ctx, block, uspi->s_fpb);
+ }
+ free_data(&ctx, 0, 0);
+ }
+
+ ubh_bforget(ubh);
+ ufs_free_blocks(inode, ind_block, uspi->s_fpb);
+}
+
+static void free_branch_tail(struct inode *inode, unsigned from, struct ufs_buffer_head *ubh, int depth)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ unsigned i;
+
+ if (--depth) {
+ for (i = from; i < uspi->s_apb ; i++) {
+ void *p = ubh_get_data_ptr(uspi, ubh, i);
+ u64 block = ufs_data_ptr_to_cpu(sb, p);
+ if (block) {
+ write_seqlock(&UFS_I(inode)->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&UFS_I(inode)->meta_lock);
+ ubh_mark_buffer_dirty(ubh);
+ free_full_branch(inode, block, depth);
+ }
+ }
+ } else {
+ struct to_free ctx = {.inode = inode};
+
+ for (i = from; i < uspi->s_apb; i++) {
+ void *p = ubh_get_data_ptr(uspi, ubh, i);
+ u64 block = ufs_data_ptr_to_cpu(sb, p);
+ if (block) {
+ write_seqlock(&UFS_I(inode)->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&UFS_I(inode)->meta_lock);
+ ubh_mark_buffer_dirty(ubh);
+ free_data(&ctx, block, uspi->s_fpb);
+ }
+ }
+ free_data(&ctx, 0, 0);
+ }
+ if (IS_SYNC(inode) && ubh_buffer_dirty(ubh))
+ ubh_sync_block(ubh);
+ ubh_brelse(ubh);
+}
+
+static int ufs_alloc_lastblock(struct inode *inode, loff_t size)
+{
+ int err = 0;
+ struct super_block *sb = inode->i_sb;
+ struct address_space *mapping = inode->i_mapping;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ unsigned i, end;
+ sector_t lastfrag;
+ struct folio *folio;
+ struct buffer_head *bh;
+ u64 phys64;
+
+ lastfrag = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
+
+ if (!lastfrag)
+ goto out;
+
+ lastfrag--;
+
+ folio = ufs_get_locked_folio(mapping, lastfrag >>
+ (PAGE_SHIFT - inode->i_blkbits));
+ if (IS_ERR(folio)) {
+ err = -EIO;
+ goto out;
+ }
+
+ end = lastfrag & ((1 << (PAGE_SHIFT - inode->i_blkbits)) - 1);
+ bh = folio_buffers(folio);
+ for (i = 0; i < end; ++i)
+ bh = bh->b_this_page;
+
+ err = ufs_getfrag_block(inode, lastfrag, bh, 1);
+
+ if (unlikely(err))
+ goto out_unlock;
+
+ if (buffer_new(bh)) {
+ clear_buffer_new(bh);
+ clean_bdev_bh_alias(bh);
+ /*
+ * we do not zeroize fragment, because of
+ * if it maped to hole, it already contains zeroes
+ */
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ folio_mark_dirty(folio);
+ }
+
+ if (lastfrag >= UFS_IND_FRAGMENT) {
+ end = uspi->s_fpb - ufs_fragnum(lastfrag) - 1;
+ phys64 = bh->b_blocknr + 1;
+ for (i = 0; i < end; ++i) {
+ bh = sb_getblk(sb, i + phys64);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, sb->s_blocksize);
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ }
+ }
+out_unlock:
+ ufs_put_locked_folio(folio);
+out:
+ return err;
+}
+
+static void ufs_truncate_blocks(struct inode *inode)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ unsigned offsets[4];
+ int depth;
+ int depth2;
+ unsigned i;
+ struct ufs_buffer_head *ubh[3];
+ void *p;
+ u64 block;
+
+ if (inode->i_size) {
+ sector_t last = (inode->i_size - 1) >> uspi->s_bshift;
+ depth = ufs_block_to_path(inode, last, offsets);
+ if (!depth)
+ return;
+ } else {
+ depth = 1;
+ }
+
+ for (depth2 = depth - 1; depth2; depth2--)
+ if (offsets[depth2] != uspi->s_apb - 1)
+ break;
+
+ mutex_lock(&ufsi->truncate_mutex);
+ if (depth == 1) {
+ ufs_trunc_direct(inode);
+ offsets[0] = UFS_IND_BLOCK;
+ } else {
+ /* get the blocks that should be partially emptied */
+ p = ufs_get_direct_data_ptr(uspi, ufsi, offsets[0]++);
+ for (i = 0; i < depth2; i++) {
+ block = ufs_data_ptr_to_cpu(sb, p);
+ if (!block)
+ break;
+ ubh[i] = ubh_bread(sb, block, uspi->s_bsize);
+ if (!ubh[i]) {
+ write_seqlock(&ufsi->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&ufsi->meta_lock);
+ break;
+ }
+ p = ubh_get_data_ptr(uspi, ubh[i], offsets[i + 1]++);
+ }
+ while (i--)
+ free_branch_tail(inode, offsets[i + 1], ubh[i], depth - i - 1);
+ }
+ for (i = offsets[0]; i <= UFS_TIND_BLOCK; i++) {
+ p = ufs_get_direct_data_ptr(uspi, ufsi, i);
+ block = ufs_data_ptr_to_cpu(sb, p);
+ if (block) {
+ write_seqlock(&ufsi->meta_lock);
+ ufs_data_ptr_clear(uspi, p);
+ write_sequnlock(&ufsi->meta_lock);
+ free_full_branch(inode, block, i - UFS_IND_BLOCK + 1);
+ }
+ }
+ read_seqlock_excl(&ufsi->meta_lock);
+ ufsi->i_lastfrag = DIRECT_FRAGMENT;
+ read_sequnlock_excl(&ufsi->meta_lock);
+ mark_inode_dirty(inode);
+ mutex_unlock(&ufsi->truncate_mutex);
+}
+
+static int ufs_truncate(struct inode *inode, loff_t size)
+{
+ int err = 0;
+
+ UFSD("ENTER: ino %llu, i_size: %llu, old_i_size: %llu\n",
+ inode->i_ino, (unsigned long long)size,
+ (unsigned long long)i_size_read(inode));
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return -EPERM;
+
+ err = ufs_alloc_lastblock(inode, size);
+
+ if (err)
+ goto out;
+
+ block_truncate_page(inode->i_mapping, size, ufs_getfrag_block);
+
+ truncate_setsize(inode, size);
+
+ ufs_truncate_blocks(inode);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ mark_inode_dirty(inode);
+out:
+ UFSD("EXIT: err %d\n", err);
+ return err;
+}
+
+int ufs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr)
+{
+ struct inode *inode = d_inode(dentry);
+ unsigned int ia_valid = attr->ia_valid;
+ int error;
+
+ error = setattr_prepare(&nop_mnt_idmap, dentry, attr);
+ if (error)
+ return error;
+
+ if (ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
+ error = ufs_truncate(inode, attr->ia_size);
+ if (error)
+ return error;
+ }
+
+ setattr_copy(&nop_mnt_idmap, inode, attr);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+const struct inode_operations ufs_file_inode_operations = {
+ .setattr = ufs_setattr,
+};
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
new file mode 100644
index 000000000..6703f3bcf
--- /dev/null
+++ b/fs/ufs/namei.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/namei.c
+ *
+ * Migration to usage of "page cache" on May 2006 by
+ * Evgeniy Dushistov <dushistov@mail.ru> based on ext2 code base.
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/namei.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/time.h>
+#include <linux/fs.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "util.h"
+
+static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode)
+{
+ int err = ufs_add_link(dentry, inode);
+ if (!err) {
+ d_instantiate_new(dentry, inode);
+ return 0;
+ }
+ inode_dec_link_count(inode);
+ discard_new_inode(inode);
+ return err;
+}
+
+static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry, unsigned int flags)
+{
+ struct inode * inode = NULL;
+ ino_t ino;
+
+ if (dentry->d_name.len > UFS_MAXNAMLEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ ino = ufs_inode_by_name(dir, &dentry->d_name);
+ if (ino)
+ inode = ufs_iget(dir->i_sb, ino);
+ return d_splice_alias(inode, dentry);
+}
+
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int ufs_create (struct mnt_idmap * idmap,
+ struct inode * dir, struct dentry * dentry, umode_t mode)
+{
+ struct inode *inode;
+
+ inode = ufs_new_inode(dir, mode);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ inode->i_op = &ufs_file_inode_operations;
+ inode->i_fop = &ufs_file_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ mark_inode_dirty(inode);
+ return ufs_add_nondir(dentry, inode);
+}
+
+static int ufs_mknod(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, dev_t rdev)
+{
+ struct inode *inode;
+ int err;
+
+ if (!old_valid_dev(rdev))
+ return -EINVAL;
+
+ inode = ufs_new_inode(dir, mode);
+ err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ init_special_inode(inode, mode, rdev);
+ ufs_set_inode_dev(inode->i_sb, UFS_I(inode), rdev);
+ mark_inode_dirty(inode);
+ err = ufs_add_nondir(dentry, inode);
+ }
+ return err;
+}
+
+static int ufs_symlink (struct mnt_idmap * idmap, struct inode * dir,
+ struct dentry * dentry, const char * symname)
+{
+ struct super_block * sb = dir->i_sb;
+ int err;
+ unsigned l = strlen(symname)+1;
+ struct inode * inode;
+
+ if (l > sb->s_blocksize)
+ return -ENAMETOOLONG;
+
+ inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ return err;
+
+ if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) {
+ /* slow symlink */
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
+ inode->i_mapping->a_ops = &ufs_aops;
+ err = page_symlink(inode, symname, l);
+ if (err)
+ goto out_fail;
+ } else {
+ /* fast symlink */
+ inode->i_op = &simple_symlink_inode_operations;
+ inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink;
+ memcpy(inode->i_link, symname, l);
+ inode->i_size = l-1;
+ }
+ mark_inode_dirty(inode);
+
+ return ufs_add_nondir(dentry, inode);
+
+out_fail:
+ inode_dec_link_count(inode);
+ discard_new_inode(inode);
+ return err;
+}
+
+static int ufs_link (struct dentry * old_dentry, struct inode * dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = d_inode(old_dentry);
+ int error;
+
+ inode_set_ctime_current(inode);
+ inode_inc_link_count(inode);
+ ihold(inode);
+
+ error = ufs_add_link(dentry, inode);
+ if (error) {
+ inode_dec_link_count(inode);
+ iput(inode);
+ } else
+ d_instantiate(dentry, inode);
+ return error;
+}
+
+static struct dentry *ufs_mkdir(struct mnt_idmap * idmap, struct inode * dir,
+ struct dentry * dentry, umode_t mode)
+{
+ struct inode * inode;
+ int err;
+
+ inode_inc_link_count(dir);
+
+ inode = ufs_new_inode(dir, mode);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out_dir;
+
+ inode->i_op = &ufs_dir_inode_operations;
+ inode->i_fop = &ufs_dir_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+
+ inode_inc_link_count(inode);
+
+ err = ufs_make_empty(inode, dir);
+ if (err)
+ goto out_fail;
+
+ err = ufs_add_link(dentry, inode);
+ if (err)
+ goto out_fail;
+
+ d_instantiate_new(dentry, inode);
+ return NULL;
+
+out_fail:
+ inode_dec_link_count(inode);
+ inode_dec_link_count(inode);
+ discard_new_inode(inode);
+out_dir:
+ inode_dec_link_count(dir);
+ return ERR_PTR(err);
+}
+
+static int ufs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode * inode = d_inode(dentry);
+ struct ufs_dir_entry *de;
+ struct folio *folio;
+ int err;
+
+ de = ufs_find_entry(dir, &dentry->d_name, &folio);
+ if (!de)
+ return -ENOENT;
+
+ err = ufs_delete_entry(dir, de, folio);
+ if (!err) {
+ inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
+ inode_dec_link_count(inode);
+ }
+ folio_release_kmap(folio, de);
+ return err;
+}
+
+static int ufs_rmdir (struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = d_inode(dentry);
+ int err= -ENOTEMPTY;
+
+ if (ufs_empty_dir (inode)) {
+ err = ufs_unlink(dir, dentry);
+ if (!err) {
+ inode->i_size = 0;
+ inode_dec_link_count(inode);
+ inode_dec_link_count(dir);
+ }
+ }
+ return err;
+}
+
+static int ufs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
+ struct dentry *old_dentry, struct inode *new_dir,
+ struct dentry *new_dentry, unsigned int flags)
+{
+ struct inode *old_inode = d_inode(old_dentry);
+ struct inode *new_inode = d_inode(new_dentry);
+ struct folio *dir_folio = NULL;
+ struct ufs_dir_entry * dir_de = NULL;
+ struct folio *old_folio;
+ struct ufs_dir_entry *old_de;
+ int err;
+
+ if (flags & ~RENAME_NOREPLACE)
+ return -EINVAL;
+
+ old_de = ufs_find_entry(old_dir, &old_dentry->d_name, &old_folio);
+ if (!old_de)
+ return -ENOENT;
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ err = -EIO;
+ dir_de = ufs_dotdot(old_inode, &dir_folio);
+ if (!dir_de)
+ goto out_old;
+ }
+
+ if (new_inode) {
+ struct folio *new_folio;
+ struct ufs_dir_entry *new_de;
+
+ err = -ENOTEMPTY;
+ if (dir_de && !ufs_empty_dir(new_inode))
+ goto out_dir;
+
+ err = -ENOENT;
+ new_de = ufs_find_entry(new_dir, &new_dentry->d_name, &new_folio);
+ if (!new_de)
+ goto out_dir;
+ err = ufs_set_link(new_dir, new_de, new_folio, old_inode, 1);
+ folio_release_kmap(new_folio, new_de);
+ if (err)
+ goto out_dir;
+ inode_set_ctime_current(new_inode);
+ if (dir_de)
+ drop_nlink(new_inode);
+ inode_dec_link_count(new_inode);
+ } else {
+ err = ufs_add_link(new_dentry, old_inode);
+ if (err)
+ goto out_dir;
+ if (dir_de)
+ inode_inc_link_count(new_dir);
+ }
+
+ /*
+ * Like most other Unix systems, set the ctime for inodes on a
+ * rename.
+ */
+ inode_set_ctime_current(old_inode);
+ mark_inode_dirty(old_inode);
+
+ err = ufs_delete_entry(old_dir, old_de, old_folio);
+ if (!err && dir_de) {
+ if (old_dir != new_dir)
+ err = ufs_set_link(old_inode, dir_de, dir_folio,
+ new_dir, 0);
+ inode_dec_link_count(old_dir);
+ }
+out_dir:
+ if (dir_de)
+ folio_release_kmap(dir_folio, dir_de);
+out_old:
+ folio_release_kmap(old_folio, old_de);
+ return err;
+}
+
+const struct inode_operations ufs_dir_inode_operations = {
+ .create = ufs_create,
+ .lookup = ufs_lookup,
+ .link = ufs_link,
+ .unlink = ufs_unlink,
+ .symlink = ufs_symlink,
+ .mkdir = ufs_mkdir,
+ .rmdir = ufs_rmdir,
+ .mknod = ufs_mknod,
+ .rename = ufs_rename,
+};
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
new file mode 100644
index 000000000..6dcf6d048
--- /dev/null
+++ b/fs/ufs/super.c
@@ -0,0 +1,1495 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/fs/ufs/super.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+/* Derived from
+ *
+ * linux/fs/ext2/super.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+/*
+ * Inspired by
+ *
+ * linux/fs/ufs/super.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Kernel module support added on 96/04/26 by
+ * Stefan Reinauer <stepan@home.culture.mipt.ru>
+ *
+ * Module usage counts added on 96/04/29 by
+ * Gertjan van Wingerde <gwingerde@gmail.com>
+ *
+ * Clean swab support on 19970406 by
+ * Francois-Rene Rideau <fare@tunes.org>
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
+ *
+ * write support Daniel Pirkl <daniel.pirkl@email.cz> 1998
+ *
+ * HP/UX hfs filesystem support added by
+ * Martin K. Petersen <mkp@mkp.net>, August 1999
+ *
+ * UFS2 (of FreeBSD 5.x) support added by
+ * Niraj Kumar <niraj17@iitbombay.org>, Jan 2004
+ *
+ * UFS2 write support added by
+ * Evgeniy Dushistov <dushistov@mail.ru>, 2007
+ */
+
+#include <linux/exportfs.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#include <linux/stdarg.h>
+
+#include <linux/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
+#include <linux/init.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/log2.h>
+#include <linux/seq_file.h>
+#include <linux/iversion.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+static struct inode *ufs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation)
+{
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ struct inode *inode;
+
+ if (ino < UFS_ROOTINO || ino > (u64)uspi->s_ncg * uspi->s_ipg)
+ return ERR_PTR(-ESTALE);
+
+ inode = ufs_iget(sb, ino);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+ if (generation && inode->i_generation != generation) {
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+ return inode;
+}
+
+static struct dentry *ufs_fh_to_dentry(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ufs_nfs_get_inode);
+}
+
+static struct dentry *ufs_fh_to_parent(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ufs_nfs_get_inode);
+}
+
+static struct dentry *ufs_get_parent(struct dentry *child)
+{
+ ino_t ino;
+
+ ino = ufs_inode_by_name(d_inode(child), &dotdot_name);
+ if (!ino)
+ return ERR_PTR(-ENOENT);
+ return d_obtain_alias(ufs_iget(child->d_sb, ino));
+}
+
+static const struct export_operations ufs_export_ops = {
+ .encode_fh = generic_encode_ino32_fh,
+ .fh_to_dentry = ufs_fh_to_dentry,
+ .fh_to_parent = ufs_fh_to_parent,
+ .get_parent = ufs_get_parent,
+};
+
+#ifdef CONFIG_UFS_DEBUG
+/*
+ * Print contents of ufs_super_block, useful for debugging
+ */
+static void ufs_print_super_stuff(struct super_block *sb,
+ struct ufs_super_block_first *usb1,
+ struct ufs_super_block_second *usb2,
+ struct ufs_super_block_third *usb3)
+{
+ u32 magic = fs32_to_cpu(sb, usb3->fs_magic);
+
+ pr_debug("ufs_print_super_stuff\n");
+ pr_debug(" magic: 0x%x\n", magic);
+ if (fs32_to_cpu(sb, usb3->fs_magic) == UFS2_MAGIC) {
+ pr_debug(" fs_size: %llu\n", (unsigned long long)
+ fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_size));
+ pr_debug(" fs_dsize: %llu\n", (unsigned long long)
+ fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_dsize));
+ pr_debug(" bsize: %u\n",
+ fs32_to_cpu(sb, usb1->fs_bsize));
+ pr_debug(" fsize: %u\n",
+ fs32_to_cpu(sb, usb1->fs_fsize));
+ pr_debug(" fs_volname: %s\n", usb2->fs_un.fs_u2.fs_volname);
+ pr_debug(" fs_sblockloc: %llu\n", (unsigned long long)
+ fs64_to_cpu(sb, usb2->fs_un.fs_u2.fs_sblockloc));
+ pr_debug(" cs_ndir(No of dirs): %llu\n", (unsigned long long)
+ fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_ndir));
+ pr_debug(" cs_nbfree(No of free blocks): %llu\n",
+ (unsigned long long)
+ fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_nbfree));
+ pr_info(" cs_nifree(Num of free inodes): %llu\n",
+ (unsigned long long)
+ fs64_to_cpu(sb, usb3->fs_un1.fs_u2.cs_nifree));
+ pr_info(" cs_nffree(Num of free frags): %llu\n",
+ (unsigned long long)
+ fs64_to_cpu(sb, usb3->fs_un1.fs_u2.cs_nffree));
+ pr_info(" fs_maxsymlinklen: %u\n",
+ fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_maxsymlinklen));
+ } else {
+ pr_debug(" sblkno: %u\n", fs32_to_cpu(sb, usb1->fs_sblkno));
+ pr_debug(" cblkno: %u\n", fs32_to_cpu(sb, usb1->fs_cblkno));
+ pr_debug(" iblkno: %u\n", fs32_to_cpu(sb, usb1->fs_iblkno));
+ pr_debug(" dblkno: %u\n", fs32_to_cpu(sb, usb1->fs_dblkno));
+ pr_debug(" cgoffset: %u\n",
+ fs32_to_cpu(sb, usb1->fs_cgoffset));
+ pr_debug(" ~cgmask: 0x%x\n",
+ ~fs32_to_cpu(sb, usb1->fs_cgmask));
+ pr_debug(" size: %u\n", fs32_to_cpu(sb, usb1->fs_size));
+ pr_debug(" dsize: %u\n", fs32_to_cpu(sb, usb1->fs_dsize));
+ pr_debug(" ncg: %u\n", fs32_to_cpu(sb, usb1->fs_ncg));
+ pr_debug(" bsize: %u\n", fs32_to_cpu(sb, usb1->fs_bsize));
+ pr_debug(" fsize: %u\n", fs32_to_cpu(sb, usb1->fs_fsize));
+ pr_debug(" frag: %u\n", fs32_to_cpu(sb, usb1->fs_frag));
+ pr_debug(" fragshift: %u\n",
+ fs32_to_cpu(sb, usb1->fs_fragshift));
+ pr_debug(" ~fmask: %u\n", ~fs32_to_cpu(sb, usb1->fs_fmask));
+ pr_debug(" fshift: %u\n", fs32_to_cpu(sb, usb1->fs_fshift));
+ pr_debug(" sbsize: %u\n", fs32_to_cpu(sb, usb1->fs_sbsize));
+ pr_debug(" spc: %u\n", fs32_to_cpu(sb, usb1->fs_spc));
+ pr_debug(" cpg: %u\n", fs32_to_cpu(sb, usb1->fs_cpg));
+ pr_debug(" ipg: %u\n", fs32_to_cpu(sb, usb1->fs_ipg));
+ pr_debug(" fpg: %u\n", fs32_to_cpu(sb, usb1->fs_fpg));
+ pr_debug(" csaddr: %u\n", fs32_to_cpu(sb, usb1->fs_csaddr));
+ pr_debug(" cssize: %u\n", fs32_to_cpu(sb, usb1->fs_cssize));
+ pr_debug(" cgsize: %u\n", fs32_to_cpu(sb, usb1->fs_cgsize));
+ pr_debug(" fstodb: %u\n",
+ fs32_to_cpu(sb, usb1->fs_fsbtodb));
+ pr_debug(" nrpos: %u\n", fs32_to_cpu(sb, usb3->fs_nrpos));
+ pr_debug(" ndir %u\n",
+ fs32_to_cpu(sb, usb1->fs_cstotal.cs_ndir));
+ pr_debug(" nifree %u\n",
+ fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree));
+ pr_debug(" nbfree %u\n",
+ fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree));
+ pr_debug(" nffree %u\n",
+ fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree));
+ }
+ pr_debug("\n");
+}
+
+/*
+ * Print contents of ufs_cylinder_group, useful for debugging
+ */
+static void ufs_print_cylinder_stuff(struct super_block *sb,
+ struct ufs_cylinder_group *cg)
+{
+ pr_debug("\nufs_print_cylinder_stuff\n");
+ pr_debug("size of ucg: %zu\n", sizeof(struct ufs_cylinder_group));
+ pr_debug(" magic: %x\n", fs32_to_cpu(sb, cg->cg_magic));
+ pr_debug(" time: %u\n", fs32_to_cpu(sb, cg->cg_time));
+ pr_debug(" cgx: %u\n", fs32_to_cpu(sb, cg->cg_cgx));
+ pr_debug(" ncyl: %u\n", fs16_to_cpu(sb, cg->cg_ncyl));
+ pr_debug(" niblk: %u\n", fs16_to_cpu(sb, cg->cg_niblk));
+ pr_debug(" ndblk: %u\n", fs32_to_cpu(sb, cg->cg_ndblk));
+ pr_debug(" cs_ndir: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_ndir));
+ pr_debug(" cs_nbfree: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_nbfree));
+ pr_debug(" cs_nifree: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_nifree));
+ pr_debug(" cs_nffree: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_nffree));
+ pr_debug(" rotor: %u\n", fs32_to_cpu(sb, cg->cg_rotor));
+ pr_debug(" frotor: %u\n", fs32_to_cpu(sb, cg->cg_frotor));
+ pr_debug(" irotor: %u\n", fs32_to_cpu(sb, cg->cg_irotor));
+ pr_debug(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n",
+ fs32_to_cpu(sb, cg->cg_frsum[0]), fs32_to_cpu(sb, cg->cg_frsum[1]),
+ fs32_to_cpu(sb, cg->cg_frsum[2]), fs32_to_cpu(sb, cg->cg_frsum[3]),
+ fs32_to_cpu(sb, cg->cg_frsum[4]), fs32_to_cpu(sb, cg->cg_frsum[5]),
+ fs32_to_cpu(sb, cg->cg_frsum[6]), fs32_to_cpu(sb, cg->cg_frsum[7]));
+ pr_debug(" btotoff: %u\n", fs32_to_cpu(sb, cg->cg_btotoff));
+ pr_debug(" boff: %u\n", fs32_to_cpu(sb, cg->cg_boff));
+ pr_debug(" iuseoff: %u\n", fs32_to_cpu(sb, cg->cg_iusedoff));
+ pr_debug(" freeoff: %u\n", fs32_to_cpu(sb, cg->cg_freeoff));
+ pr_debug(" nextfreeoff: %u\n", fs32_to_cpu(sb, cg->cg_nextfreeoff));
+ pr_debug(" clustersumoff %u\n",
+ fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clustersumoff));
+ pr_debug(" clusteroff %u\n",
+ fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clusteroff));
+ pr_debug(" nclusterblks %u\n",
+ fs32_to_cpu(sb, cg->cg_u.cg_44.cg_nclusterblks));
+ pr_debug("\n");
+}
+#else
+# define ufs_print_super_stuff(sb, usb1, usb2, usb3) /**/
+# define ufs_print_cylinder_stuff(sb, cg) /**/
+#endif /* CONFIG_UFS_DEBUG */
+
+static const struct super_operations ufs_super_ops;
+
+void ufs_error (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct va_format vaf;
+ va_list args;
+
+ uspi = UFS_SB(sb)->s_uspi;
+ usb1 = ubh_get_usb_first(uspi);
+
+ if (!sb_rdonly(sb)) {
+ usb1->fs_clean = UFS_FSBAD;
+ ubh_mark_buffer_dirty(USPI_UBH(uspi));
+ ufs_mark_sb_dirty(sb);
+ sb->s_flags |= SB_RDONLY;
+ }
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ switch (UFS_SB(sb)->s_on_err) {
+ case UFS_MOUNT_ONERROR_PANIC:
+ panic("panic (device %s): %s: %pV\n",
+ sb->s_id, function, &vaf);
+
+ case UFS_MOUNT_ONERROR_LOCK:
+ case UFS_MOUNT_ONERROR_UMOUNT:
+ case UFS_MOUNT_ONERROR_REPAIR:
+ pr_crit("error (device %s): %s: %pV\n",
+ sb->s_id, function, &vaf);
+ }
+ va_end(args);
+}
+
+void ufs_panic (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct va_format vaf;
+ va_list args;
+
+ uspi = UFS_SB(sb)->s_uspi;
+ usb1 = ubh_get_usb_first(uspi);
+
+ if (!sb_rdonly(sb)) {
+ usb1->fs_clean = UFS_FSBAD;
+ ubh_mark_buffer_dirty(USPI_UBH(uspi));
+ ufs_mark_sb_dirty(sb);
+ }
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ sb->s_flags |= SB_RDONLY;
+ pr_crit("panic (device %s): %s: %pV\n",
+ sb->s_id, function, &vaf);
+ va_end(args);
+}
+
+void ufs_warning (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ pr_warn("(device %s): %s: %pV\n",
+ sb->s_id, function, &vaf);
+ va_end(args);
+}
+
+enum { Opt_type, Opt_onerror };
+
+static const struct constant_table ufs_param_ufstype[] = {
+ {"old", UFS_MOUNT_UFSTYPE_OLD},
+ {"sunx86", UFS_MOUNT_UFSTYPE_SUNx86},
+ {"sun", UFS_MOUNT_UFSTYPE_SUN},
+ {"sunos", UFS_MOUNT_UFSTYPE_SUNOS},
+ {"44bsd", UFS_MOUNT_UFSTYPE_44BSD},
+ {"ufs2", UFS_MOUNT_UFSTYPE_UFS2},
+ {"5xbsd", UFS_MOUNT_UFSTYPE_UFS2},
+ {"hp", UFS_MOUNT_UFSTYPE_HP},
+ {"nextstep-cd", UFS_MOUNT_UFSTYPE_NEXTSTEP_CD},
+ {"nextstep", UFS_MOUNT_UFSTYPE_NEXTSTEP},
+ {"openstep", UFS_MOUNT_UFSTYPE_OPENSTEP},
+ {}
+};
+
+static const struct constant_table ufs_param_onerror[] = {
+ {"panic", UFS_MOUNT_ONERROR_PANIC},
+ {"lock", UFS_MOUNT_ONERROR_LOCK},
+ {"umount", UFS_MOUNT_ONERROR_UMOUNT},
+ {"repair", UFS_MOUNT_ONERROR_REPAIR},
+ {}
+};
+
+static const struct fs_parameter_spec ufs_param_spec[] = {
+ fsparam_enum ("ufstype", Opt_type, ufs_param_ufstype),
+ fsparam_enum ("onerror", Opt_onerror, ufs_param_onerror),
+ {}
+};
+
+struct ufs_fs_context {
+ unsigned int flavour, on_err;
+};
+
+static int ufs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ struct ufs_fs_context *ctx = fc->fs_private;
+ struct fs_parse_result result;
+ int opt;
+
+ UFSD("ENTER\n");
+
+ opt = fs_parse(fc, ufs_param_spec, param, &result);
+ if (opt < 0)
+ return opt;
+
+ switch (opt) {
+ case Opt_type:
+ if (ctx->flavour == result.uint_32) /* no-op */
+ return 0;
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+ pr_err("ufstype can't be changed during remount\n");
+ return -EINVAL;
+ }
+ if (ctx->flavour) {
+ pr_err("conflicting ufstype options\n");
+ return -EINVAL;
+ }
+ ctx->flavour = result.uint_32;
+ break;
+ case Opt_onerror:
+ ctx->on_err = result.uint_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Different types of UFS hold fs_cstotal in different
+ * places, and use different data structure for it.
+ * To make things simpler we just copy fs_cstotal to ufs_sb_private_info
+ */
+static void ufs_setup_cstotal(struct super_block *sb)
+{
+ struct ufs_sb_info *sbi = UFS_SB(sb);
+ struct ufs_sb_private_info *uspi = sbi->s_uspi;
+ struct ufs_super_block_first *usb1;
+ struct ufs_super_block_second *usb2;
+ struct ufs_super_block_third *usb3;
+ unsigned mtype = sbi->s_flavour;
+
+ UFSD("ENTER, mtype=%u\n", mtype);
+ usb1 = ubh_get_usb_first(uspi);
+ usb2 = ubh_get_usb_second(uspi);
+ usb3 = ubh_get_usb_third(uspi);
+
+ if ((mtype == UFS_MOUNT_UFSTYPE_44BSD &&
+ (usb2->fs_un.fs_u2.fs_maxbsize == usb1->fs_bsize)) ||
+ mtype == UFS_MOUNT_UFSTYPE_UFS2) {
+ /*we have statistic in different place, then usual*/
+ uspi->cs_total.cs_ndir = fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_ndir);
+ uspi->cs_total.cs_nbfree = fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_nbfree);
+ uspi->cs_total.cs_nifree = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.cs_nifree);
+ uspi->cs_total.cs_nffree = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.cs_nffree);
+ } else {
+ uspi->cs_total.cs_ndir = fs32_to_cpu(sb, usb1->fs_cstotal.cs_ndir);
+ uspi->cs_total.cs_nbfree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree);
+ uspi->cs_total.cs_nifree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree);
+ uspi->cs_total.cs_nffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree);
+ }
+ UFSD("EXIT\n");
+}
+
+/*
+ * Read on-disk structures associated with cylinder groups
+ */
+static int ufs_read_cylinder_structures(struct super_block *sb)
+{
+ struct ufs_sb_info *sbi = UFS_SB(sb);
+ struct ufs_sb_private_info *uspi = sbi->s_uspi;
+ unsigned char * base, * space;
+ unsigned size, blks, i;
+
+ UFSD("ENTER\n");
+
+ /*
+ * Read cs structures from (usually) first data block
+ * on the device.
+ */
+ size = uspi->s_cssize;
+ blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ base = space = kmalloc(size, GFP_NOFS);
+ if (!base)
+ goto failed;
+ sbi->s_csp = (struct ufs_csum *)space;
+ for (i = 0; i < blks; i++) {
+ struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i);
+ if (!bh)
+ goto failed;
+ memcpy(space, bh->b_data, uspi->s_fsize);
+ space += uspi->s_fsize;
+ brelse (bh);
+ }
+
+ /*
+ * Read cylinder group (we read only first fragment from block
+ * at this time) and prepare internal data structures for cg caching.
+ */
+ sbi->s_ucg = kmalloc_objs(struct buffer_head *, uspi->s_ncg, GFP_NOFS);
+ if (!sbi->s_ucg)
+ goto failed;
+ for (i = 0; i < uspi->s_ncg; i++)
+ sbi->s_ucg[i] = NULL;
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
+ sbi->s_ucpi[i] = NULL;
+ sbi->s_cgno[i] = UFS_CGNO_EMPTY;
+ }
+ for (i = 0; i < uspi->s_ncg; i++) {
+ UFSD("read cg %u\n", i);
+ if (!(sbi->s_ucg[i] = sb_bread(sb, ufs_cgcmin(i))))
+ goto failed;
+ if (!ufs_cg_chkmagic (sb, (struct ufs_cylinder_group *) sbi->s_ucg[i]->b_data))
+ goto failed;
+
+ ufs_print_cylinder_stuff(sb, (struct ufs_cylinder_group *) sbi->s_ucg[i]->b_data);
+ }
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
+ if (!(sbi->s_ucpi[i] = kmalloc_obj(struct ufs_cg_private_info, GFP_NOFS)))
+ goto failed;
+ sbi->s_cgno[i] = UFS_CGNO_EMPTY;
+ }
+ sbi->s_cg_loaded = 0;
+ UFSD("EXIT\n");
+ return 1;
+
+failed:
+ kfree (base);
+ if (sbi->s_ucg) {
+ for (i = 0; i < uspi->s_ncg; i++)
+ if (sbi->s_ucg[i])
+ brelse (sbi->s_ucg[i]);
+ kfree (sbi->s_ucg);
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++)
+ kfree (sbi->s_ucpi[i]);
+ }
+ UFSD("EXIT (FAILED)\n");
+ return 0;
+}
+
+/*
+ * Sync our internal copy of fs_cstotal with disk
+ */
+static void ufs_put_cstotal(struct super_block *sb)
+{
+ unsigned mtype = UFS_SB(sb)->s_flavour;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ struct ufs_super_block_first *usb1;
+ struct ufs_super_block_second *usb2;
+ struct ufs_super_block_third *usb3;
+
+ UFSD("ENTER\n");
+ usb1 = ubh_get_usb_first(uspi);
+ usb2 = ubh_get_usb_second(uspi);
+ usb3 = ubh_get_usb_third(uspi);
+
+ if (mtype == UFS_MOUNT_UFSTYPE_UFS2) {
+ /*we have statistic in different place, then usual*/
+ usb2->fs_un.fs_u2.cs_ndir =
+ cpu_to_fs64(sb, uspi->cs_total.cs_ndir);
+ usb2->fs_un.fs_u2.cs_nbfree =
+ cpu_to_fs64(sb, uspi->cs_total.cs_nbfree);
+ usb3->fs_un1.fs_u2.cs_nifree =
+ cpu_to_fs64(sb, uspi->cs_total.cs_nifree);
+ usb3->fs_un1.fs_u2.cs_nffree =
+ cpu_to_fs64(sb, uspi->cs_total.cs_nffree);
+ goto out;
+ }
+
+ if (mtype == UFS_MOUNT_UFSTYPE_44BSD &&
+ (usb2->fs_un.fs_u2.fs_maxbsize == usb1->fs_bsize)) {
+ /* store stats in both old and new places */
+ usb2->fs_un.fs_u2.cs_ndir =
+ cpu_to_fs64(sb, uspi->cs_total.cs_ndir);
+ usb2->fs_un.fs_u2.cs_nbfree =
+ cpu_to_fs64(sb, uspi->cs_total.cs_nbfree);
+ usb3->fs_un1.fs_u2.cs_nifree =
+ cpu_to_fs64(sb, uspi->cs_total.cs_nifree);
+ usb3->fs_un1.fs_u2.cs_nffree =
+ cpu_to_fs64(sb, uspi->cs_total.cs_nffree);
+ }
+ usb1->fs_cstotal.cs_ndir = cpu_to_fs32(sb, uspi->cs_total.cs_ndir);
+ usb1->fs_cstotal.cs_nbfree = cpu_to_fs32(sb, uspi->cs_total.cs_nbfree);
+ usb1->fs_cstotal.cs_nifree = cpu_to_fs32(sb, uspi->cs_total.cs_nifree);
+ usb1->fs_cstotal.cs_nffree = cpu_to_fs32(sb, uspi->cs_total.cs_nffree);
+out:
+ ubh_mark_buffer_dirty(USPI_UBH(uspi));
+ ufs_print_super_stuff(sb, usb1, usb2, usb3);
+ UFSD("EXIT\n");
+}
+
+/**
+ * ufs_put_super_internal() - put on-disk intrenal structures
+ * @sb: pointer to super_block structure
+ * Put on-disk structures associated with cylinder groups
+ * and write them back to disk, also update cs_total on disk
+ */
+static void ufs_put_super_internal(struct super_block *sb)
+{
+ struct ufs_sb_info *sbi = UFS_SB(sb);
+ struct ufs_sb_private_info *uspi = sbi->s_uspi;
+ unsigned char * base, * space;
+ unsigned blks, size, i;
+
+
+ UFSD("ENTER\n");
+
+ ufs_put_cstotal(sb);
+ size = uspi->s_cssize;
+ blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ base = space = (char*) sbi->s_csp;
+ for (i = 0; i < blks; i++, space += uspi->s_fsize) {
+ struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i);
+
+ if (unlikely(!bh)) { // better than an oops...
+ ufs_panic(sb, __func__,
+ "can't write part of cylinder group summary");
+ continue;
+ }
+ memcpy(bh->b_data, space, uspi->s_fsize);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ for (i = 0; i < sbi->s_cg_loaded; i++) {
+ ufs_put_cylinder (sb, i);
+ kfree (sbi->s_ucpi[i]);
+ }
+ for (; i < UFS_MAX_GROUP_LOADED; i++)
+ kfree (sbi->s_ucpi[i]);
+ for (i = 0; i < uspi->s_ncg; i++)
+ brelse (sbi->s_ucg[i]);
+ kfree (sbi->s_ucg);
+ kfree (base);
+
+ UFSD("EXIT\n");
+}
+
+static int ufs_sync_fs(struct super_block *sb, int wait)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_super_block_third * usb3;
+ unsigned flags;
+
+ mutex_lock(&UFS_SB(sb)->s_lock);
+
+ UFSD("ENTER\n");
+
+ flags = UFS_SB(sb)->s_flags;
+ uspi = UFS_SB(sb)->s_uspi;
+ usb1 = ubh_get_usb_first(uspi);
+ usb3 = ubh_get_usb_third(uspi);
+
+ usb1->fs_time = ufs_get_seconds(sb);
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN ||
+ (flags & UFS_ST_MASK) == UFS_ST_SUNOS ||
+ (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ ufs_set_fs_state(sb, usb1, usb3,
+ UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
+ ufs_put_cstotal(sb);
+
+ UFSD("EXIT\n");
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+
+ return 0;
+}
+
+static void delayed_sync_fs(struct work_struct *work)
+{
+ struct ufs_sb_info *sbi;
+
+ sbi = container_of(work, struct ufs_sb_info, sync_work.work);
+
+ spin_lock(&sbi->work_lock);
+ sbi->work_queued = 0;
+ spin_unlock(&sbi->work_lock);
+
+ ufs_sync_fs(sbi->sb, 1);
+}
+
+void ufs_mark_sb_dirty(struct super_block *sb)
+{
+ struct ufs_sb_info *sbi = UFS_SB(sb);
+ unsigned long delay;
+
+ spin_lock(&sbi->work_lock);
+ if (!sbi->work_queued) {
+ delay = msecs_to_jiffies(dirty_writeback_interval * 10);
+ queue_delayed_work(system_dfl_long_wq, &sbi->sync_work, delay);
+ sbi->work_queued = 1;
+ }
+ spin_unlock(&sbi->work_lock);
+}
+
+static void ufs_put_super(struct super_block *sb)
+{
+ struct ufs_sb_info * sbi = UFS_SB(sb);
+
+ UFSD("ENTER\n");
+
+ if (!sb_rdonly(sb))
+ ufs_put_super_internal(sb);
+ cancel_delayed_work_sync(&sbi->sync_work);
+
+ ubh_brelse_uspi (sbi->s_uspi);
+ kfree (sbi->s_uspi);
+ kfree (sbi);
+ sb->s_fs_info = NULL;
+ UFSD("EXIT\n");
+ return;
+}
+
+static u64 ufs_max_bytes(struct super_block *sb)
+{
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ int bits = uspi->s_apbshift;
+ u64 res;
+
+ if (bits > 21)
+ res = ~0ULL;
+ else
+ res = UFS_NDADDR + (1LL << bits) + (1LL << (2*bits)) +
+ (1LL << (3*bits));
+
+ if (res >= (MAX_LFS_FILESIZE >> uspi->s_bshift))
+ return MAX_LFS_FILESIZE;
+ return res << uspi->s_bshift;
+}
+
+static int ufs_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+ struct ufs_fs_context *ctx = fc->fs_private;
+ int silent = fc->sb_flags & SB_SILENT;
+ struct ufs_sb_info * sbi;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_super_block_second * usb2;
+ struct ufs_super_block_third * usb3;
+ struct ufs_buffer_head * ubh;
+ struct inode *inode;
+ unsigned block_size, super_block_size;
+ unsigned flags;
+ unsigned super_block_offset;
+ unsigned maxsymlen;
+ int ret = -EINVAL;
+
+ uspi = NULL;
+ ubh = NULL;
+ flags = 0;
+
+ UFSD("ENTER\n");
+
+#ifndef CONFIG_UFS_FS_WRITE
+ if (!sb_rdonly(sb)) {
+ pr_err("ufs was compiled with read-only support, can't be mounted as read-write\n");
+ return -EROFS;
+ }
+#endif
+
+ sbi = kzalloc_obj(struct ufs_sb_info);
+ if (!sbi)
+ goto failed_nomem;
+ sb->s_fs_info = sbi;
+ sbi->sb = sb;
+
+ UFSD("flag %u\n", (int)(sb_rdonly(sb)));
+
+ mutex_init(&sbi->s_lock);
+ spin_lock_init(&sbi->work_lock);
+ INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs);
+
+ sbi->s_flavour = ctx->flavour;
+ sbi->s_on_err = ctx->on_err;
+
+ if (!sbi->s_flavour) {
+ if (!silent)
+ pr_err("You didn't specify the type of your ufs filesystem\n\n"
+ "mount -t ufs -o ufstype="
+ "sun|sunx86|44bsd|ufs2|5xbsd|old|hp|nextstep|nextstep-cd|openstep ...\n\n"
+ ">>>WARNING<<< Wrong ufstype may corrupt your filesystem, "
+ "default is ufstype=old\n");
+ sbi->s_flavour = UFS_MOUNT_UFSTYPE_OLD;
+ }
+
+ uspi = kzalloc_obj(struct ufs_sb_private_info);
+ sbi->s_uspi = uspi;
+ if (!uspi)
+ goto failed;
+ uspi->s_dirblksize = UFS_SECTOR_SIZE;
+ super_block_offset=UFS_SBLOCK;
+
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+ sb->s_time_gran = NSEC_PER_SEC;
+ sb->s_time_min = S32_MIN;
+ sb->s_time_max = S32_MAX;
+
+ switch (sbi->s_flavour) {
+ case UFS_MOUNT_UFSTYPE_44BSD:
+ UFSD("ufstype=44bsd\n");
+ uspi->s_fsize = block_size = 512;
+ uspi->s_fmask = ~(512 - 1);
+ uspi->s_fshift = 9;
+ uspi->s_sbsize = super_block_size = 1536;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
+ break;
+ case UFS_MOUNT_UFSTYPE_UFS2:
+ UFSD("ufstype=ufs2\n");
+ super_block_offset=SBLOCK_UFS2;
+ uspi->s_fsize = block_size = 512;
+ uspi->s_fmask = ~(512 - 1);
+ uspi->s_fshift = 9;
+ uspi->s_sbsize = super_block_size = 1536;
+ uspi->s_sbbase = 0;
+ sb->s_time_gran = 1;
+ sb->s_time_min = S64_MIN;
+ sb->s_time_max = S64_MAX;
+ flags |= UFS_TYPE_UFS2 | UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_SUN:
+ UFSD("ufstype=sun\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_maxsymlinklen = 0; /* Not supported on disk */
+ flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUN | UFS_CG_SUN;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_SUNOS:
+ UFSD("ufstype=sunos\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = 2048;
+ super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_maxsymlinklen = 0; /* Not supported on disk */
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_SUNOS | UFS_CG_SUN;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_SUNx86:
+ UFSD("ufstype=sunx86\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_maxsymlinklen = 0; /* Not supported on disk */
+ flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUNx86 | UFS_CG_SUN;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_OLD:
+ UFSD("ufstype=old\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!sb_rdonly(sb)) {
+ if (!silent)
+ pr_info("ufstype=old is supported read-only\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_NEXTSTEP:
+ UFSD("ufstype=nextstep\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_dirblksize = 1024;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!sb_rdonly(sb)) {
+ if (!silent)
+ pr_info("ufstype=nextstep is supported read-only\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_NEXTSTEP_CD:
+ UFSD("ufstype=nextstep-cd\n");
+ uspi->s_fsize = block_size = 2048;
+ uspi->s_fmask = ~(2048 - 1);
+ uspi->s_fshift = 11;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_dirblksize = 1024;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!sb_rdonly(sb)) {
+ if (!silent)
+ pr_info("ufstype=nextstep-cd is supported read-only\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_OPENSTEP:
+ UFSD("ufstype=openstep\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_dirblksize = 1024;
+ flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
+ if (!sb_rdonly(sb)) {
+ if (!silent)
+ pr_info("ufstype=openstep is supported read-only\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_HP:
+ UFSD("ufstype=hp\n");
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!sb_rdonly(sb)) {
+ if (!silent)
+ pr_info("ufstype=hp is supported read-only\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+ break;
+ default:
+ if (!silent)
+ pr_err("unknown ufstype\n");
+ goto failed;
+ }
+
+again:
+ if (!sb_set_blocksize(sb, block_size)) {
+ pr_err("failed to set blocksize\n");
+ goto failed;
+ }
+
+ /*
+ * read ufs super block from device
+ */
+
+ ubh = ubh_bread_uspi(uspi, sb, uspi->s_sbbase + super_block_offset/block_size, super_block_size);
+
+ if (!ubh)
+ goto failed;
+
+ usb1 = ubh_get_usb_first(uspi);
+ usb2 = ubh_get_usb_second(uspi);
+ usb3 = ubh_get_usb_third(uspi);
+
+ /* Sort out mod used on SunOS 4.1.3 for fs_state */
+ uspi->s_postblformat = fs32_to_cpu(sb, usb3->fs_postblformat);
+ if (((flags & UFS_ST_MASK) == UFS_ST_SUNOS) &&
+ (uspi->s_postblformat != UFS_42POSTBLFMT)) {
+ flags &= ~UFS_ST_MASK;
+ flags |= UFS_ST_SUN;
+ }
+
+ if ((flags & UFS_ST_MASK) == UFS_ST_44BSD &&
+ uspi->s_postblformat == UFS_42POSTBLFMT) {
+ if (!silent)
+ pr_err("this is not a 44bsd filesystem");
+ goto failed;
+ }
+
+ /*
+ * Check ufs magic number
+ */
+ sbi->s_bytesex = BYTESEX_LE;
+ switch ((uspi->fs_magic = fs32_to_cpu(sb, usb3->fs_magic))) {
+ case UFS_MAGIC:
+ case UFS_MAGIC_BW:
+ case UFS2_MAGIC:
+ case UFS_MAGIC_LFN:
+ case UFS_MAGIC_FEA:
+ case UFS_MAGIC_4GB:
+ goto magic_found;
+ }
+ sbi->s_bytesex = BYTESEX_BE;
+ switch ((uspi->fs_magic = fs32_to_cpu(sb, usb3->fs_magic))) {
+ case UFS_MAGIC:
+ case UFS_MAGIC_BW:
+ case UFS2_MAGIC:
+ case UFS_MAGIC_LFN:
+ case UFS_MAGIC_FEA:
+ case UFS_MAGIC_4GB:
+ goto magic_found;
+ }
+
+ if ((sbi->s_flavour == UFS_MOUNT_UFSTYPE_NEXTSTEP
+ || sbi->s_flavour == UFS_MOUNT_UFSTYPE_NEXTSTEP_CD
+ || sbi->s_flavour == UFS_MOUNT_UFSTYPE_OPENSTEP)
+ && uspi->s_sbbase < 256) {
+ ubh_brelse_uspi(uspi);
+ ubh = NULL;
+ uspi->s_sbbase += 8;
+ goto again;
+ }
+ if (!silent)
+ pr_err("%s(): bad magic number\n", __func__);
+ goto failed;
+
+magic_found:
+ /*
+ * Check block and fragment sizes
+ */
+ uspi->s_bsize = fs32_to_cpu(sb, usb1->fs_bsize);
+ uspi->s_fsize = fs32_to_cpu(sb, usb1->fs_fsize);
+ uspi->s_sbsize = fs32_to_cpu(sb, usb1->fs_sbsize);
+ uspi->s_fmask = fs32_to_cpu(sb, usb1->fs_fmask);
+ uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift);
+
+ if (!is_power_of_2(uspi->s_fsize)) {
+ pr_err("%s(): fragment size %u is not a power of 2\n",
+ __func__, uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_fsize < 512) {
+ pr_err("%s(): fragment size %u is too small\n",
+ __func__, uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_fsize > 4096) {
+ pr_err("%s(): fragment size %u is too large\n",
+ __func__, uspi->s_fsize);
+ goto failed;
+ }
+ if (!is_power_of_2(uspi->s_bsize)) {
+ pr_err("%s(): block size %u is not a power of 2\n",
+ __func__, uspi->s_bsize);
+ goto failed;
+ }
+ if (uspi->s_bsize < 4096) {
+ pr_err("%s(): block size %u is too small\n",
+ __func__, uspi->s_bsize);
+ goto failed;
+ }
+ if (uspi->s_bsize / uspi->s_fsize > 8) {
+ pr_err("%s(): too many fragments per block (%u)\n",
+ __func__, uspi->s_bsize / uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) {
+ ubh_brelse_uspi(uspi);
+ ubh = NULL;
+ block_size = uspi->s_fsize;
+ super_block_size = uspi->s_sbsize;
+ UFSD("another value of block_size or super_block_size %u, %u\n", block_size, super_block_size);
+ goto again;
+ }
+
+ sbi->s_flags = flags;/*after that line some functions use s_flags*/
+ ufs_print_super_stuff(sb, usb1, usb2, usb3);
+
+ /*
+ * Check, if file system was correctly unmounted.
+ * If not, make it read only.
+ */
+ if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) ||
+ ((flags & UFS_ST_MASK) == UFS_ST_OLD) ||
+ (((flags & UFS_ST_MASK) == UFS_ST_SUN ||
+ (flags & UFS_ST_MASK) == UFS_ST_SUNOS ||
+ (flags & UFS_ST_MASK) == UFS_ST_SUNx86) &&
+ (ufs_get_fs_state(sb, usb1, usb3) == (UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time))))) {
+ switch(usb1->fs_clean) {
+ case UFS_FSCLEAN:
+ UFSD("fs is clean\n");
+ break;
+ case UFS_FSSTABLE:
+ UFSD("fs is stable\n");
+ break;
+ case UFS_FSLOG:
+ UFSD("fs is logging fs\n");
+ break;
+ case UFS_FSOSF1:
+ UFSD("fs is DEC OSF/1\n");
+ break;
+ case UFS_FSACTIVE:
+ pr_err("%s(): fs is active\n", __func__);
+ sb->s_flags |= SB_RDONLY;
+ break;
+ case UFS_FSBAD:
+ pr_err("%s(): fs is bad\n", __func__);
+ sb->s_flags |= SB_RDONLY;
+ break;
+ default:
+ pr_err("%s(): can't grok fs_clean 0x%x\n",
+ __func__, usb1->fs_clean);
+ sb->s_flags |= SB_RDONLY;
+ break;
+ }
+ } else {
+ pr_err("%s(): fs needs fsck\n", __func__);
+ sb->s_flags |= SB_RDONLY;
+ }
+
+ /*
+ * Read ufs_super_block into internal data structures
+ */
+ sb->s_op = &ufs_super_ops;
+ sb->s_export_op = &ufs_export_ops;
+
+ sb->s_magic = fs32_to_cpu(sb, usb3->fs_magic);
+
+ uspi->s_sblkno = fs32_to_cpu(sb, usb1->fs_sblkno);
+ uspi->s_cblkno = fs32_to_cpu(sb, usb1->fs_cblkno);
+ uspi->s_iblkno = fs32_to_cpu(sb, usb1->fs_iblkno);
+ uspi->s_dblkno = fs32_to_cpu(sb, usb1->fs_dblkno);
+ uspi->s_cgoffset = fs32_to_cpu(sb, usb1->fs_cgoffset);
+ uspi->s_cgmask = fs32_to_cpu(sb, usb1->fs_cgmask);
+
+ if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) {
+ uspi->s_size = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_size);
+ uspi->s_dsize = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_dsize);
+ } else {
+ uspi->s_size = fs32_to_cpu(sb, usb1->fs_size);
+ uspi->s_dsize = fs32_to_cpu(sb, usb1->fs_dsize);
+ }
+
+ uspi->s_ncg = fs32_to_cpu(sb, usb1->fs_ncg);
+ /* s_bsize already set */
+ /* s_fsize already set */
+ uspi->s_fpb = fs32_to_cpu(sb, usb1->fs_frag);
+ uspi->s_minfree = fs32_to_cpu(sb, usb1->fs_minfree);
+ uspi->s_bmask = fs32_to_cpu(sb, usb1->fs_bmask);
+ uspi->s_fmask = fs32_to_cpu(sb, usb1->fs_fmask);
+ uspi->s_bshift = fs32_to_cpu(sb, usb1->fs_bshift);
+ uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift);
+ UFSD("uspi->s_bshift = %d,uspi->s_fshift = %d", uspi->s_bshift,
+ uspi->s_fshift);
+ uspi->s_fpbshift = fs32_to_cpu(sb, usb1->fs_fragshift);
+ uspi->s_fsbtodb = fs32_to_cpu(sb, usb1->fs_fsbtodb);
+ /* s_sbsize already set */
+ uspi->s_csmask = fs32_to_cpu(sb, usb1->fs_csmask);
+ uspi->s_csshift = fs32_to_cpu(sb, usb1->fs_csshift);
+ uspi->s_nindir = fs32_to_cpu(sb, usb1->fs_nindir);
+ uspi->s_inopb = fs32_to_cpu(sb, usb1->fs_inopb);
+ uspi->s_nspf = fs32_to_cpu(sb, usb1->fs_nspf);
+ uspi->s_npsect = ufs_get_fs_npsect(sb, usb1, usb3);
+ uspi->s_interleave = fs32_to_cpu(sb, usb1->fs_interleave);
+ uspi->s_trackskew = fs32_to_cpu(sb, usb1->fs_trackskew);
+
+ if (uspi->fs_magic == UFS2_MAGIC)
+ uspi->s_csaddr = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_csaddr);
+ else
+ uspi->s_csaddr = fs32_to_cpu(sb, usb1->fs_csaddr);
+
+ uspi->s_cssize = fs32_to_cpu(sb, usb1->fs_cssize);
+ uspi->s_cgsize = fs32_to_cpu(sb, usb1->fs_cgsize);
+ uspi->s_ntrak = fs32_to_cpu(sb, usb1->fs_ntrak);
+ uspi->s_nsect = fs32_to_cpu(sb, usb1->fs_nsect);
+ uspi->s_spc = fs32_to_cpu(sb, usb1->fs_spc);
+ uspi->s_ipg = fs32_to_cpu(sb, usb1->fs_ipg);
+ uspi->s_fpg = fs32_to_cpu(sb, usb1->fs_fpg);
+ uspi->s_cpc = fs32_to_cpu(sb, usb2->fs_un.fs_u1.fs_cpc);
+ uspi->s_contigsumsize = fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_contigsumsize);
+ uspi->s_qbmask = ufs_get_fs_qbmask(sb, usb3);
+ uspi->s_qfmask = ufs_get_fs_qfmask(sb, usb3);
+ uspi->s_nrpos = fs32_to_cpu(sb, usb3->fs_nrpos);
+ uspi->s_postbloff = fs32_to_cpu(sb, usb3->fs_postbloff);
+ uspi->s_rotbloff = fs32_to_cpu(sb, usb3->fs_rotbloff);
+
+ uspi->s_root_blocks = mul_u64_u32_div(uspi->s_dsize,
+ uspi->s_minfree, 100);
+ if (uspi->s_minfree <= 5) {
+ uspi->s_time_to_space = ~0ULL;
+ uspi->s_space_to_time = 0;
+ usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTSPACE);
+ } else {
+ uspi->s_time_to_space = (uspi->s_root_blocks / 2) + 1;
+ uspi->s_space_to_time = mul_u64_u32_div(uspi->s_dsize,
+ uspi->s_minfree - 2, 100) - 1;
+ }
+
+ /*
+ * Compute another frequently used values
+ */
+ uspi->s_fpbmask = uspi->s_fpb - 1;
+ if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2)
+ uspi->s_apbshift = uspi->s_bshift - 3;
+ else
+ uspi->s_apbshift = uspi->s_bshift - 2;
+
+ uspi->s_apb = 1 << uspi->s_apbshift;
+ uspi->s_apbmask = uspi->s_apb - 1;
+ uspi->s_nspfshift = uspi->s_fshift - UFS_SECTOR_BITS;
+ uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift;
+ uspi->s_inopf = uspi->s_inopb >> uspi->s_fpbshift;
+ uspi->s_bpf = uspi->s_fsize << 3;
+ uspi->s_bpfshift = uspi->s_fshift + 3;
+ uspi->s_bpfmask = uspi->s_bpf - 1;
+ if (sbi->s_flavour == UFS_MOUNT_UFSTYPE_44BSD ||
+ sbi->s_flavour == UFS_MOUNT_UFSTYPE_UFS2)
+ uspi->s_maxsymlinklen =
+ fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_maxsymlinklen);
+
+ if (uspi->fs_magic == UFS2_MAGIC)
+ maxsymlen = 2 * 4 * (UFS_NDADDR + UFS_NINDIR);
+ else
+ maxsymlen = 4 * (UFS_NDADDR + UFS_NINDIR);
+ if (uspi->s_maxsymlinklen > maxsymlen) {
+ ufs_warning(sb, __func__, "ufs_read_super: excessive maximum "
+ "fast symlink size (%u)\n", uspi->s_maxsymlinklen);
+ uspi->s_maxsymlinklen = maxsymlen;
+ }
+ sb->s_maxbytes = ufs_max_bytes(sb);
+ sb->s_max_links = UFS_LINK_MAX;
+
+ inode = ufs_iget(sb, UFS_ROOTINO);
+ if (IS_ERR(inode)) {
+ ret = PTR_ERR(inode);
+ goto failed;
+ }
+ sb->s_root = d_make_root(inode);
+ if (!sb->s_root) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ ufs_setup_cstotal(sb);
+ /*
+ * Read cylinder group structures
+ */
+ if (!sb_rdonly(sb))
+ if (!ufs_read_cylinder_structures(sb))
+ goto failed;
+
+ UFSD("EXIT\n");
+ return 0;
+
+failed:
+ if (ubh)
+ ubh_brelse_uspi (uspi);
+ kfree (uspi);
+ kfree(sbi);
+ sb->s_fs_info = NULL;
+ UFSD("EXIT (FAILED)\n");
+ return ret;
+
+failed_nomem:
+ UFSD("EXIT (NOMEM)\n");
+ return -ENOMEM;
+}
+
+static int ufs_reconfigure(struct fs_context *fc)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_super_block_third * usb3;
+ struct ufs_fs_context *ctx = fc->fs_private;
+ struct super_block *sb = fc->root->d_sb;
+ unsigned int ufstype;
+ unsigned int flags;
+
+ sync_filesystem(sb);
+ mutex_lock(&UFS_SB(sb)->s_lock);
+ uspi = UFS_SB(sb)->s_uspi;
+ flags = UFS_SB(sb)->s_flags;
+ usb1 = ubh_get_usb_first(uspi);
+ usb3 = ubh_get_usb_third(uspi);
+
+ ufstype = UFS_SB(sb)->s_flavour;
+
+ if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb)) {
+ UFS_SB(sb)->s_on_err = ctx->on_err;
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return 0;
+ }
+
+ /*
+ * fs was mouted as rw, remounting ro
+ */
+ if (fc->sb_flags & SB_RDONLY) {
+ ufs_put_super_internal(sb);
+ usb1->fs_time = ufs_get_seconds(sb);
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN
+ || (flags & UFS_ST_MASK) == UFS_ST_SUNOS
+ || (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ ufs_set_fs_state(sb, usb1, usb3,
+ UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
+ ubh_mark_buffer_dirty (USPI_UBH(uspi));
+ sb->s_flags |= SB_RDONLY;
+ } else {
+ /*
+ * fs was mounted as ro, remounting rw
+ */
+#ifndef CONFIG_UFS_FS_WRITE
+ pr_err("ufs was compiled with read-only support, can't be mounted as read-write\n");
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return -EINVAL;
+#else
+ if (ufstype != UFS_MOUNT_UFSTYPE_SUN &&
+ ufstype != UFS_MOUNT_UFSTYPE_SUNOS &&
+ ufstype != UFS_MOUNT_UFSTYPE_44BSD &&
+ ufstype != UFS_MOUNT_UFSTYPE_SUNx86 &&
+ ufstype != UFS_MOUNT_UFSTYPE_UFS2) {
+ pr_err("this ufstype is read-only supported\n");
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return -EINVAL;
+ }
+ if (!ufs_read_cylinder_structures(sb)) {
+ pr_err("failed during remounting\n");
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return -EPERM;
+ }
+ sb->s_flags &= ~SB_RDONLY;
+#endif
+ }
+ UFS_SB(sb)->s_on_err = ctx->on_err;
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return 0;
+}
+
+static int ufs_show_options(struct seq_file *seq, struct dentry *root)
+{
+ struct ufs_sb_info *sbi = UFS_SB(root->d_sb);
+ unsigned mval = sbi->s_flavour;
+ const struct constant_table *tp;
+
+ tp = ufs_param_ufstype;
+ while (tp->value && tp->value != mval)
+ ++tp;
+ seq_printf(seq, ",ufstype=%s", tp->name);
+
+ tp = ufs_param_onerror;
+ mval = sbi->s_on_err;
+ while (tp->value && tp->value != mval)
+ ++tp;
+ seq_printf(seq, ",onerror=%s", tp->name);
+
+ return 0;
+}
+
+static int ufs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *sb = dentry->d_sb;
+ struct ufs_sb_private_info *uspi= UFS_SB(sb)->s_uspi;
+ unsigned flags = UFS_SB(sb)->s_flags;
+ u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
+
+ mutex_lock(&UFS_SB(sb)->s_lock);
+
+ if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2)
+ buf->f_type = UFS2_MAGIC;
+ else
+ buf->f_type = UFS_MAGIC;
+
+ buf->f_blocks = uspi->s_dsize;
+ buf->f_bfree = ufs_freefrags(uspi);
+ buf->f_ffree = uspi->cs_total.cs_nifree;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_bavail = (buf->f_bfree > uspi->s_root_blocks)
+ ? (buf->f_bfree - uspi->s_root_blocks) : 0;
+ buf->f_files = uspi->s_ncg * uspi->s_ipg;
+ buf->f_namelen = UFS_MAXNAMLEN;
+ buf->f_fsid = u64_to_fsid(id);
+
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+
+ return 0;
+}
+
+static struct kmem_cache * ufs_inode_cachep;
+
+static struct inode *ufs_alloc_inode(struct super_block *sb)
+{
+ struct ufs_inode_info *ei;
+
+ ei = alloc_inode_sb(sb, ufs_inode_cachep, GFP_NOFS);
+ if (!ei)
+ return NULL;
+
+ inode_set_iversion(&ei->vfs_inode, 1);
+ seqlock_init(&ei->meta_lock);
+ mutex_init(&ei->truncate_mutex);
+ return &ei->vfs_inode;
+}
+
+static void ufs_free_in_core_inode(struct inode *inode)
+{
+ kmem_cache_free(ufs_inode_cachep, UFS_I(inode));
+}
+
+static void init_once(void *foo)
+{
+ struct ufs_inode_info *ei = (struct ufs_inode_info *) foo;
+
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int __init init_inodecache(void)
+{
+ ufs_inode_cachep = kmem_cache_create_usercopy("ufs_inode_cache",
+ sizeof(struct ufs_inode_info), 0,
+ (SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT),
+ offsetof(struct ufs_inode_info, i_u1.i_symlink),
+ sizeof_field(struct ufs_inode_info,
+ i_u1.i_symlink),
+ init_once);
+ if (ufs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ /*
+ * Make sure all delayed rcu free inodes are flushed before we
+ * destroy cache.
+ */
+ rcu_barrier();
+ kmem_cache_destroy(ufs_inode_cachep);
+}
+
+static const struct super_operations ufs_super_ops = {
+ .alloc_inode = ufs_alloc_inode,
+ .free_inode = ufs_free_in_core_inode,
+ .write_inode = ufs_write_inode,
+ .evict_inode = ufs_evict_inode,
+ .put_super = ufs_put_super,
+ .sync_fs = ufs_sync_fs,
+ .statfs = ufs_statfs,
+ .show_options = ufs_show_options,
+};
+
+static int ufs_get_tree(struct fs_context *fc)
+{
+ return get_tree_bdev(fc, ufs_fill_super);
+}
+
+static void ufs_free_fc(struct fs_context *fc)
+{
+ kfree(fc->fs_private);
+}
+
+static const struct fs_context_operations ufs_context_ops = {
+ .parse_param = ufs_parse_param,
+ .get_tree = ufs_get_tree,
+ .reconfigure = ufs_reconfigure,
+ .free = ufs_free_fc,
+};
+
+static int ufs_init_fs_context(struct fs_context *fc)
+{
+ struct ufs_fs_context *ctx;
+
+ ctx = kzalloc_obj(*ctx);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+ struct super_block *sb = fc->root->d_sb;
+ struct ufs_sb_info *sbi = UFS_SB(sb);
+
+ ctx->flavour = sbi->s_flavour;
+ ctx->on_err = sbi->s_on_err;
+ } else {
+ ctx->flavour = 0;
+ ctx->on_err = UFS_MOUNT_ONERROR_LOCK;
+ }
+
+ fc->fs_private = ctx;
+ fc->ops = &ufs_context_ops;
+
+ return 0;
+}
+
+static struct file_system_type ufs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "ufs",
+ .kill_sb = kill_block_super,
+ .init_fs_context = ufs_init_fs_context,
+ .parameters = ufs_param_spec,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+MODULE_ALIAS_FS("ufs");
+
+static int __init init_ufs_fs(void)
+{
+ int err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&ufs_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
+}
+
+static void __exit exit_ufs_fs(void)
+{
+ unregister_filesystem(&ufs_fs_type);
+ destroy_inodecache();
+}
+
+module_init(init_ufs_fs)
+module_exit(exit_ufs_fs)
+MODULE_DESCRIPTION("UFS Filesystem");
+MODULE_LICENSE("GPL");
diff --git a/fs/ufs/swab.h b/fs/ufs/swab.h
new file mode 100644
index 000000000..a0e1d8c82
--- /dev/null
+++ b/fs/ufs/swab.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * linux/fs/ufs/swab.h
+ *
+ * Copyright (C) 1997, 1998 Francois-Rene Rideau <fare@tunes.org>
+ * Copyright (C) 1998 Jakub Jelinek <jj@ultra.linux.cz>
+ * Copyright (C) 2001 Christoph Hellwig <hch@infradead.org>
+ */
+
+#ifndef _UFS_SWAB_H
+#define _UFS_SWAB_H
+
+/*
+ * Notes:
+ * HERE WE ASSUME EITHER BIG OR LITTLE ENDIAN UFSes
+ * in case there are ufs implementations that have strange bytesexes,
+ * you'll need to modify code here as well as in ufs_super.c and ufs_fs.h
+ * to support them.
+ */
+
+enum {
+ BYTESEX_LE,
+ BYTESEX_BE
+};
+
+static inline u64
+fs64_to_cpu(struct super_block *sbp, __fs64 n)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ return le64_to_cpu((__force __le64)n);
+ else
+ return be64_to_cpu((__force __be64)n);
+}
+
+static inline __fs64
+cpu_to_fs64(struct super_block *sbp, u64 n)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ return (__force __fs64)cpu_to_le64(n);
+ else
+ return (__force __fs64)cpu_to_be64(n);
+}
+
+static inline u32
+fs32_to_cpu(struct super_block *sbp, __fs32 n)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ return le32_to_cpu((__force __le32)n);
+ else
+ return be32_to_cpu((__force __be32)n);
+}
+
+static inline __fs32
+cpu_to_fs32(struct super_block *sbp, u32 n)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ return (__force __fs32)cpu_to_le32(n);
+ else
+ return (__force __fs32)cpu_to_be32(n);
+}
+
+static inline void
+fs32_add(struct super_block *sbp, __fs32 *n, int d)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ le32_add_cpu((__le32 *)n, d);
+ else
+ be32_add_cpu((__be32 *)n, d);
+}
+
+static inline void
+fs32_sub(struct super_block *sbp, __fs32 *n, int d)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ le32_add_cpu((__le32 *)n, -d);
+ else
+ be32_add_cpu((__be32 *)n, -d);
+}
+
+static inline u16
+fs16_to_cpu(struct super_block *sbp, __fs16 n)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ return le16_to_cpu((__force __le16)n);
+ else
+ return be16_to_cpu((__force __be16)n);
+}
+
+static inline __fs16
+cpu_to_fs16(struct super_block *sbp, u16 n)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ return (__force __fs16)cpu_to_le16(n);
+ else
+ return (__force __fs16)cpu_to_be16(n);
+}
+
+static inline void
+fs16_add(struct super_block *sbp, __fs16 *n, int d)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ le16_add_cpu((__le16 *)n, d);
+ else
+ be16_add_cpu((__be16 *)n, d);
+}
+
+static inline void
+fs16_sub(struct super_block *sbp, __fs16 *n, int d)
+{
+ if (UFS_SB(sbp)->s_bytesex == BYTESEX_LE)
+ le16_add_cpu((__le16 *)n, -d);
+ else
+ be16_add_cpu((__be16 *)n, -d);
+}
+
+#endif /* _UFS_SWAB_H */
diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h
new file mode 100644
index 000000000..788e02505
--- /dev/null
+++ b/fs/ufs/ufs.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _UFS_UFS_H
+#define _UFS_UFS_H 1
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define UFS_MAX_GROUP_LOADED 8
+#define UFS_CGNO_EMPTY ((unsigned)-1)
+
+struct ufs_sb_private_info;
+struct ufs_cg_private_info;
+struct ufs_csum;
+
+struct ufs_sb_info {
+ struct ufs_sb_private_info * s_uspi;
+ struct ufs_csum * s_csp;
+ unsigned s_bytesex;
+ unsigned s_flags;
+ struct buffer_head ** s_ucg;
+ struct ufs_cg_private_info * s_ucpi[UFS_MAX_GROUP_LOADED];
+ unsigned s_cgno[UFS_MAX_GROUP_LOADED];
+ unsigned short s_cg_loaded;
+ unsigned s_flavour;
+ unsigned s_on_err;
+ struct super_block *sb;
+ int work_queued; /* non-zero if the delayed work is queued */
+ struct delayed_work sync_work; /* FS sync delayed work */
+ spinlock_t work_lock; /* protects sync_work and work_queued */
+ struct mutex s_lock;
+};
+
+struct ufs_inode_info {
+ union {
+ __fs32 i_data[15];
+ __u8 i_symlink[2 * 4 * 15];
+ __fs64 u2_i_data[15];
+ } i_u1;
+ __u32 i_flags;
+ __u32 i_shadow;
+ __u32 i_unused1;
+ __u32 i_unused2;
+ __u32 i_oeftflag;
+ __u16 i_osync;
+ __u64 i_lastfrag;
+ seqlock_t meta_lock;
+ struct mutex truncate_mutex;
+ __u32 i_dir_start_lookup;
+ struct inode vfs_inode;
+};
+
+/* mount options */
+#define UFS_MOUNT_ONERROR_PANIC 0x00000001
+#define UFS_MOUNT_ONERROR_LOCK 0x00000002
+#define UFS_MOUNT_ONERROR_UMOUNT 0x00000004
+#define UFS_MOUNT_ONERROR_REPAIR 0x00000008
+
+#define UFS_MOUNT_UFSTYPE_OLD 0x00000010
+#define UFS_MOUNT_UFSTYPE_44BSD 0x00000020
+#define UFS_MOUNT_UFSTYPE_SUN 0x00000040
+#define UFS_MOUNT_UFSTYPE_NEXTSTEP 0x00000080
+#define UFS_MOUNT_UFSTYPE_NEXTSTEP_CD 0x00000100
+#define UFS_MOUNT_UFSTYPE_OPENSTEP 0x00000200
+#define UFS_MOUNT_UFSTYPE_SUNx86 0x00000400
+#define UFS_MOUNT_UFSTYPE_HP 0x00000800
+#define UFS_MOUNT_UFSTYPE_UFS2 0x00001000
+#define UFS_MOUNT_UFSTYPE_SUNOS 0x00002000
+
+/*
+ * Debug code
+ */
+#ifdef CONFIG_UFS_DEBUG
+# define UFSD(f, a...) { \
+ pr_debug("UFSD (%s, %d): %s:", \
+ __FILE__, __LINE__, __func__); \
+ pr_debug(f, ## a); \
+ }
+#else
+# define UFSD(f, a...) /**/
+#endif
+
+/* balloc.c */
+void ufs_free_fragments (struct inode *, u64 fragment, unsigned count);
+void ufs_free_blocks (struct inode *, u64 fragment, unsigned count);
+u64 ufs_new_fragments(struct inode *, void *, u64 fragment, u64 goal,
+ unsigned count, int *err, struct folio *);
+
+/* cylinder.c */
+extern struct ufs_cg_private_info * ufs_load_cylinder (struct super_block *, unsigned);
+extern void ufs_put_cylinder (struct super_block *, unsigned);
+
+/* dir.c */
+extern const struct inode_operations ufs_dir_inode_operations;
+
+int ufs_add_link(struct dentry *, struct inode *);
+ino_t ufs_inode_by_name(struct inode *, const struct qstr *);
+int ufs_make_empty(struct inode *, struct inode *);
+struct ufs_dir_entry *ufs_find_entry(struct inode *, const struct qstr *,
+ struct folio **);
+int ufs_delete_entry(struct inode *, struct ufs_dir_entry *, struct folio *);
+int ufs_empty_dir(struct inode *);
+struct ufs_dir_entry *ufs_dotdot(struct inode *, struct folio **);
+int ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
+ struct folio *folio, struct inode *inode, bool update_times);
+
+/* file.c */
+extern const struct inode_operations ufs_file_inode_operations;
+extern const struct file_operations ufs_file_operations;
+extern const struct address_space_operations ufs_aops;
+
+/* ialloc.c */
+extern void ufs_free_inode (struct inode *inode);
+extern struct inode * ufs_new_inode (struct inode *, umode_t);
+
+/* inode.c */
+extern struct inode *ufs_iget(struct super_block *, unsigned long);
+extern int ufs_write_inode (struct inode *, struct writeback_control *);
+extern int ufs_sync_inode (struct inode *);
+extern void ufs_evict_inode (struct inode *);
+extern int ufs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr);
+
+/* namei.c */
+extern const struct file_operations ufs_dir_operations;
+
+/* super.c */
+extern __printf(3, 4)
+void ufs_warning(struct super_block *, const char *, const char *, ...);
+extern __printf(3, 4)
+void ufs_error(struct super_block *, const char *, const char *, ...);
+extern __printf(3, 4)
+void ufs_panic(struct super_block *, const char *, const char *, ...);
+void ufs_mark_sb_dirty(struct super_block *sb);
+
+static inline struct ufs_sb_info *UFS_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+static inline struct ufs_inode_info *UFS_I(struct inode *inode)
+{
+ return container_of(inode, struct ufs_inode_info, vfs_inode);
+}
+
+/*
+ * Give cylinder group number for a file system block.
+ * Give cylinder group block number for a file system block.
+ */
+/* #define ufs_dtog(d) ((d) / uspi->s_fpg) */
+static inline u64 ufs_dtog(struct ufs_sb_private_info * uspi, u64 b)
+{
+ do_div(b, uspi->s_fpg);
+ return b;
+}
+/* #define ufs_dtogd(d) ((d) % uspi->s_fpg) */
+static inline u32 ufs_dtogd(struct ufs_sb_private_info * uspi, u64 b)
+{
+ return do_div(b, uspi->s_fpg);
+}
+
+#endif /* _UFS_UFS_H */
diff --git a/fs/ufs/ufs_fs.h b/fs/ufs/ufs_fs.h
new file mode 100644
index 000000000..b8dc354ae
--- /dev/null
+++ b/fs/ufs/ufs_fs.h
@@ -0,0 +1,958 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * linux/include/linux/ufs_fs.h
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * Clean swab support by Fare <fare@tunes.org>
+ * just hope no one is using NNUUXXI on __?64 structure elements
+ * 64-bit clean thanks to Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
+ *
+ * Write support by Daniel Pirkl <daniel.pirkl@email.cz>
+ *
+ * HP/UX hfs filesystem support added by
+ * Martin K. Petersen <mkp@mkp.net>, August 1999
+ *
+ * UFS2 (of FreeBSD 5.x) support added by
+ * Niraj Kumar <niraj17@iitbombay.org> , Jan 2004
+ *
+ */
+
+#ifndef __LINUX_UFS_FS_H
+#define __LINUX_UFS_FS_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/workqueue.h>
+
+#include <asm/div64.h>
+typedef __u64 __bitwise __fs64;
+typedef __u32 __bitwise __fs32;
+typedef __u16 __bitwise __fs16;
+
+#define UFS_BBLOCK 0
+#define UFS_BBSIZE 8192
+#define UFS_SBLOCK 8192
+#define UFS_SBSIZE 8192
+
+#define UFS_SECTOR_SIZE 512
+#define UFS_SECTOR_BITS 9
+#define UFS_MAGIC 0x00011954
+#define UFS_MAGIC_BW 0x0f242697
+#define UFS2_MAGIC 0x19540119
+#define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */
+
+/* Copied from FreeBSD */
+/*
+ * Each disk drive contains some number of filesystems.
+ * A filesystem consists of a number of cylinder groups.
+ * Each cylinder group has inodes and data.
+ *
+ * A filesystem is described by its super-block, which in turn
+ * describes the cylinder groups. The super-block is critical
+ * data and is replicated in each cylinder group to protect against
+ * catastrophic loss. This is done at `newfs' time and the critical
+ * super-block data does not change, so the copies need not be
+ * referenced further unless disaster strikes.
+ *
+ * For filesystem fs, the offsets of the various blocks of interest
+ * are given in the super block as:
+ * [fs->fs_sblkno] Super-block
+ * [fs->fs_cblkno] Cylinder group block
+ * [fs->fs_iblkno] Inode blocks
+ * [fs->fs_dblkno] Data blocks
+ * The beginning of cylinder group cg in fs, is given by
+ * the ``cgbase(fs, cg)'' macro.
+ *
+ * Depending on the architecture and the media, the superblock may
+ * reside in any one of four places. For tiny media where every block
+ * counts, it is placed at the very front of the partition. Historically,
+ * UFS1 placed it 8K from the front to leave room for the disk label and
+ * a small bootstrap. For UFS2 it got moved to 64K from the front to leave
+ * room for the disk label and a bigger bootstrap, and for really piggy
+ * systems we check at 256K from the front if the first three fail. In
+ * all cases the size of the superblock will be SBLOCKSIZE. All values are
+ * given in byte-offset form, so they do not imply a sector size. The
+ * SBLOCKSEARCH specifies the order in which the locations should be searched.
+ */
+#define SBLOCK_FLOPPY 0
+#define SBLOCK_UFS1 8192
+#define SBLOCK_UFS2 65536
+#define SBLOCK_PIGGY 262144
+#define SBLOCKSIZE 8192
+#define SBLOCKSEARCH \
+ { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 }
+
+
+/* HP specific MAGIC values */
+
+#define UFS_MAGIC_LFN 0x00095014 /* fs supports filenames > 14 chars */
+#define UFS_CIGAM_LFN 0x14500900 /* srahc 41 < semanelif stroppus sf */
+
+#define UFS_MAGIC_SEC 0x00612195 /* B1 security fs */
+#define UFS_CIGAM_SEC 0x95216100
+
+#define UFS_MAGIC_FEA 0x00195612 /* fs_featurebits supported */
+#define UFS_CIGAM_FEA 0x12561900
+
+#define UFS_MAGIC_4GB 0x05231994 /* fs > 4 GB && fs_featurebits */
+#define UFS_CIGAM_4GB 0x94192305
+
+/* Seems somebody at HP goofed here. B1 and lfs are both 0x2 !?! */
+#define UFS_FSF_LFN 0x00000001 /* long file names */
+#define UFS_FSF_B1 0x00000002 /* B1 security */
+#define UFS_FSF_LFS 0x00000002 /* large files */
+#define UFS_FSF_LUID 0x00000004 /* large UIDs */
+
+/* End of HP stuff */
+
+
+#define UFS_BSIZE 8192
+#define UFS_MINBSIZE 4096
+#define UFS_FSIZE 1024
+#define UFS_MAXFRAG (UFS_BSIZE / UFS_FSIZE)
+
+#define UFS_NDADDR 12
+#define UFS_NINDIR 3
+
+#define UFS_IND_BLOCK (UFS_NDADDR + 0)
+#define UFS_DIND_BLOCK (UFS_NDADDR + 1)
+#define UFS_TIND_BLOCK (UFS_NDADDR + 2)
+
+#define UFS_NDIR_FRAGMENT (UFS_NDADDR << uspi->s_fpbshift)
+#define UFS_IND_FRAGMENT (UFS_IND_BLOCK << uspi->s_fpbshift)
+#define UFS_DIND_FRAGMENT (UFS_DIND_BLOCK << uspi->s_fpbshift)
+#define UFS_TIND_FRAGMENT (UFS_TIND_BLOCK << uspi->s_fpbshift)
+
+#define UFS_ROOTINO 2
+#define UFS_FIRST_INO (UFS_ROOTINO + 1)
+
+#define UFS_USEEFT ((__u16)65535)
+
+/* fs_clean values */
+#define UFS_FSOK 0x7c269d38
+#define UFS_FSACTIVE ((__s8)0x00)
+#define UFS_FSCLEAN ((__s8)0x01)
+#define UFS_FSSTABLE ((__s8)0x02)
+#define UFS_FSOSF1 ((__s8)0x03) /* is this correct for DEC OSF/1? */
+#define UFS_FSBAD ((__s8)0xff)
+
+/* Solaris-specific fs_clean values */
+#define UFS_FSSUSPEND ((__s8)0xfe) /* temporarily suspended */
+#define UFS_FSLOG ((__s8)0xfd) /* logging fs */
+#define UFS_FSFIX ((__s8)0xfc) /* being repaired while mounted */
+
+/* From here to next blank line, s_flags for ufs_sb_info */
+/* directory entry encoding */
+#define UFS_DE_MASK 0x00000010 /* mask for the following */
+#define UFS_DE_OLD 0x00000000
+#define UFS_DE_44BSD 0x00000010
+/* uid encoding */
+#define UFS_UID_MASK 0x00000060 /* mask for the following */
+#define UFS_UID_OLD 0x00000000
+#define UFS_UID_44BSD 0x00000020
+#define UFS_UID_EFT 0x00000040
+/* superblock state encoding */
+#define UFS_ST_MASK 0x00000700 /* mask for the following */
+#define UFS_ST_OLD 0x00000000
+#define UFS_ST_44BSD 0x00000100
+#define UFS_ST_SUN 0x00000200 /* Solaris */
+#define UFS_ST_SUNOS 0x00000300
+#define UFS_ST_SUNx86 0x00000400 /* Solaris x86 */
+/*cylinder group encoding */
+#define UFS_CG_MASK 0x00003000 /* mask for the following */
+#define UFS_CG_OLD 0x00000000
+#define UFS_CG_44BSD 0x00002000
+#define UFS_CG_SUN 0x00001000
+/* filesystem type encoding */
+#define UFS_TYPE_MASK 0x00010000 /* mask for the following */
+#define UFS_TYPE_UFS1 0x00000000
+#define UFS_TYPE_UFS2 0x00010000
+
+
+/* fs_inodefmt options */
+#define UFS_42INODEFMT -1
+#define UFS_44INODEFMT 2
+
+/*
+ * MINFREE gives the minimum acceptable percentage of file system
+ * blocks which may be free. If the freelist drops below this level
+ * only the superuser may continue to allocate blocks. This may
+ * be set to 0 if no reserve of free blocks is deemed necessary,
+ * however throughput drops by fifty percent if the file system
+ * is run at between 95% and 100% full; thus the minimum default
+ * value of fs_minfree is 5%. However, to get good clustering
+ * performance, 10% is a better choice. hence we use 10% as our
+ * default value. With 10% free space, fragmentation is not a
+ * problem, so we choose to optimize for time.
+ */
+#define UFS_MINFREE 5
+#define UFS_DEFAULTOPT UFS_OPTTIME
+
+/*
+ * Turn file system block numbers into disk block addresses.
+ * This maps file system blocks to device size blocks.
+ */
+#define ufs_fsbtodb(uspi, b) ((b) << (uspi)->s_fsbtodb)
+#define ufs_dbtofsb(uspi, b) ((b) >> (uspi)->s_fsbtodb)
+
+/*
+ * Cylinder group macros to locate things in cylinder groups.
+ * They calc file system addresses of cylinder group data structures.
+ */
+#define ufs_cgbase(c) (uspi->s_fpg * (c))
+#define ufs_cgstart(c) ((uspi)->fs_magic == UFS2_MAGIC ? ufs_cgbase(c) : \
+ (ufs_cgbase(c) + uspi->s_cgoffset * ((c) & ~uspi->s_cgmask)))
+#define ufs_cgsblock(c) (ufs_cgstart(c) + uspi->s_sblkno) /* super blk */
+#define ufs_cgcmin(c) (ufs_cgstart(c) + uspi->s_cblkno) /* cg block */
+#define ufs_cgimin(c) (ufs_cgstart(c) + uspi->s_iblkno) /* inode blk */
+#define ufs_cgdmin(c) (ufs_cgstart(c) + uspi->s_dblkno) /* 1st data */
+
+/*
+ * Macros for handling inode numbers:
+ * inode number to file system block offset.
+ * inode number to cylinder group number.
+ * inode number to file system block address.
+ */
+#define ufs_inotocg(x) ((unsigned int)(x) / uspi->s_ipg)
+#define ufs_inotocgoff(x) ((unsigned int)(x) % uspi->s_ipg)
+#define ufs_inotofsba(x) (((u64)ufs_cgimin(ufs_inotocg(x))) + ufs_inotocgoff(x) / uspi->s_inopf)
+#define ufs_inotofsbo(x) ((unsigned int)(x) % uspi->s_inopf)
+
+/*
+ * Compute the cylinder and rotational position of a cyl block addr.
+ */
+#define ufs_cbtocylno(bno) \
+ ((bno) * uspi->s_nspf / uspi->s_spc)
+#define ufs_cbtorpos(bno) \
+ ((UFS_SB(sb)->s_flags & UFS_CG_SUN) ? \
+ (((((bno) * uspi->s_nspf % uspi->s_spc) % \
+ uspi->s_nsect) * \
+ uspi->s_nrpos) / uspi->s_nsect) \
+ : \
+ ((((bno) * uspi->s_nspf % uspi->s_spc / uspi->s_nsect \
+ * uspi->s_trackskew + (bno) * uspi->s_nspf % uspi->s_spc \
+ % uspi->s_nsect * uspi->s_interleave) % uspi->s_nsect \
+ * uspi->s_nrpos) / uspi->s_npsect))
+
+/*
+ * The following macros optimize certain frequently calculated
+ * quantities by using shifts and masks in place of divisions
+ * modulos and multiplications.
+ */
+#define ufs_blkoff(loc) ((loc) & uspi->s_qbmask)
+#define ufs_fragoff(loc) ((loc) & uspi->s_qfmask)
+#define ufs_lblktosize(blk) ((blk) << uspi->s_bshift)
+#define ufs_lblkno(loc) ((loc) >> uspi->s_bshift)
+#define ufs_numfrags(loc) ((loc) >> uspi->s_fshift)
+#define ufs_blkroundup(size) (((size) + uspi->s_qbmask) & uspi->s_bmask)
+#define ufs_fragroundup(size) (((size) + uspi->s_qfmask) & uspi->s_fmask)
+#define ufs_fragstoblks(frags) ((frags) >> uspi->s_fpbshift)
+#define ufs_blkstofrags(blks) ((blks) << uspi->s_fpbshift)
+#define ufs_fragnum(fsb) ((fsb) & uspi->s_fpbmask)
+#define ufs_blknum(fsb) ((fsb) & ~uspi->s_fpbmask)
+
+#define UFS_MAXNAMLEN 255
+#define UFS_MAXMNTLEN 512
+#define UFS2_MAXMNTLEN 468
+#define UFS2_MAXVOLLEN 32
+#define UFS_MAXCSBUFS 31
+#define UFS_LINK_MAX 32000
+/*
+#define UFS2_NOCSPTRS ((128 / sizeof(void *)) - 4)
+*/
+#define UFS2_NOCSPTRS 28
+
+/*
+ * UFS_DIR_PAD defines the directory entries boundaries
+ * (must be a multiple of 4)
+ */
+#define UFS_DIR_PAD 4
+#define UFS_DIR_ROUND (UFS_DIR_PAD - 1)
+#define UFS_DIR_REC_LEN(name_len) (((name_len) + 1 + 8 + UFS_DIR_ROUND) & ~UFS_DIR_ROUND)
+
+struct ufs_timeval {
+ __fs32 tv_sec;
+ __fs32 tv_usec;
+};
+
+struct ufs_dir_entry {
+ __fs32 d_ino; /* inode number of this entry */
+ __fs16 d_reclen; /* length of this entry */
+ union {
+ __fs16 d_namlen; /* actual length of d_name */
+ struct {
+ __u8 d_type; /* file type */
+ __u8 d_namlen; /* length of string in d_name */
+ } d_44;
+ } d_u;
+ __u8 d_name[UFS_MAXNAMLEN + 1]; /* file name */
+};
+
+struct ufs_csum {
+ __fs32 cs_ndir; /* number of directories */
+ __fs32 cs_nbfree; /* number of free blocks */
+ __fs32 cs_nifree; /* number of free inodes */
+ __fs32 cs_nffree; /* number of free frags */
+};
+struct ufs2_csum_total {
+ __fs64 cs_ndir; /* number of directories */
+ __fs64 cs_nbfree; /* number of free blocks */
+ __fs64 cs_nifree; /* number of free inodes */
+ __fs64 cs_nffree; /* number of free frags */
+ __fs64 cs_numclusters; /* number of free clusters */
+ __fs64 cs_spare[3]; /* future expansion */
+};
+
+struct ufs_csum_core {
+ __u64 cs_ndir; /* number of directories */
+ __u64 cs_nbfree; /* number of free blocks */
+ __u64 cs_nifree; /* number of free inodes */
+ __u64 cs_nffree; /* number of free frags */
+ __u64 cs_numclusters; /* number of free clusters */
+};
+
+/*
+ * File system flags
+ */
+#define UFS_UNCLEAN 0x01 /* file system not clean at mount (unused) */
+#define UFS_DOSOFTDEP 0x02 /* file system using soft dependencies */
+#define UFS_NEEDSFSCK 0x04 /* needs sync fsck (FreeBSD compat, unused) */
+#define UFS_INDEXDIRS 0x08 /* kernel supports indexed directories */
+#define UFS_ACLS 0x10 /* file system has ACLs enabled */
+#define UFS_MULTILABEL 0x20 /* file system is MAC multi-label */
+#define UFS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */
+
+#if 0
+/*
+ * This is the actual superblock, as it is laid out on the disk.
+ * Do NOT use this structure, because of sizeof(ufs_super_block) > 512 and
+ * it may occupy several blocks, use
+ * struct ufs_super_block_(first,second,third) instead.
+ */
+struct ufs_super_block {
+ union {
+ struct {
+ __fs32 fs_link; /* UNUSED */
+ } fs_42;
+ struct {
+ __fs32 fs_state; /* file system state flag */
+ } fs_sun;
+ } fs_u0;
+ __fs32 fs_rlink; /* UNUSED */
+ __fs32 fs_sblkno; /* addr of super-block in filesys */
+ __fs32 fs_cblkno; /* offset of cyl-block in filesys */
+ __fs32 fs_iblkno; /* offset of inode-blocks in filesys */
+ __fs32 fs_dblkno; /* offset of first data after cg */
+ __fs32 fs_cgoffset; /* cylinder group offset in cylinder */
+ __fs32 fs_cgmask; /* used to calc mod fs_ntrak */
+ __fs32 fs_time; /* last time written -- time_t */
+ __fs32 fs_size; /* number of blocks in fs */
+ __fs32 fs_dsize; /* number of data blocks in fs */
+ __fs32 fs_ncg; /* number of cylinder groups */
+ __fs32 fs_bsize; /* size of basic blocks in fs */
+ __fs32 fs_fsize; /* size of frag blocks in fs */
+ __fs32 fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ __fs32 fs_minfree; /* minimum percentage of free blocks */
+ __fs32 fs_rotdelay; /* num of ms for optimal next block */
+ __fs32 fs_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ __fs32 fs_bmask; /* ``blkoff'' calc of blk offsets */
+ __fs32 fs_fmask; /* ``fragoff'' calc of frag offsets */
+ __fs32 fs_bshift; /* ``lblkno'' calc of logical blkno */
+ __fs32 fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ __fs32 fs_maxcontig; /* max number of contiguous blks */
+ __fs32 fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ __fs32 fs_fragshift; /* block to frag shift */
+ __fs32 fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ __fs32 fs_sbsize; /* actual size of super block */
+ __fs32 fs_csmask; /* csum block offset */
+ __fs32 fs_csshift; /* csum block number */
+ __fs32 fs_nindir; /* value of NINDIR */
+ __fs32 fs_inopb; /* value of INOPB */
+ __fs32 fs_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ __fs32 fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ union {
+ struct {
+ __fs32 fs_npsect; /* # sectors/track including spares */
+ } fs_sun;
+ struct {
+ __fs32 fs_state; /* file system state time stamp */
+ } fs_sunx86;
+ } fs_u1;
+ __fs32 fs_interleave; /* hardware sector interleave */
+ __fs32 fs_trackskew; /* sector 0 skew, per track */
+/* a unique id for this filesystem (currently unused and unmaintained) */
+/* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */
+/* Neither of those fields is used in the Tahoe code right now but */
+/* there could be problems if they are. */
+ __fs32 fs_id[2]; /* file system id */
+/* sizes determined by number of cylinder groups and their sizes */
+ __fs32 fs_csaddr; /* blk addr of cyl grp summary area */
+ __fs32 fs_cssize; /* size of cyl grp summary area */
+ __fs32 fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ __fs32 fs_ntrak; /* tracks per cylinder */
+ __fs32 fs_nsect; /* sectors per track */
+ __fs32 fs_spc; /* sectors per cylinder */
+/* this comes from the disk driver partitioning */
+ __fs32 fs_ncyl; /* cylinders in file system */
+/* these fields can be computed from the others */
+ __fs32 fs_cpg; /* cylinders per group */
+ __fs32 fs_ipg; /* inodes per cylinder group */
+ __fs32 fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct ufs_csum fs_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ __s8 fs_fmod; /* super block modified flag */
+ __s8 fs_clean; /* file system is clean flag */
+ __s8 fs_ronly; /* mounted read-only flag */
+ __s8 fs_flags;
+ union {
+ struct {
+ __s8 fs_fsmnt[UFS_MAXMNTLEN];/* name mounted on */
+ __fs32 fs_cgrotor; /* last cg searched */
+ __fs32 fs_csp[UFS_MAXCSBUFS];/*list of fs_cs info buffers */
+ __fs32 fs_maxcluster;
+ __fs32 fs_cpc; /* cyl per cycle in postbl */
+ __fs16 fs_opostbl[16][8]; /* old rotation block list head */
+ } fs_u1;
+ struct {
+ __s8 fs_fsmnt[UFS2_MAXMNTLEN]; /* name mounted on */
+ __u8 fs_volname[UFS2_MAXVOLLEN]; /* volume name */
+ __fs64 fs_swuid; /* system-wide uid */
+ __fs32 fs_pad; /* due to alignment of fs_swuid */
+ __fs32 fs_cgrotor; /* last cg searched */
+ __fs32 fs_ocsp[UFS2_NOCSPTRS]; /*list of fs_cs info buffers */
+ __fs32 fs_contigdirs;/*# of contiguously allocated dirs */
+ __fs32 fs_csp; /* cg summary info buffer for fs_cs */
+ __fs32 fs_maxcluster;
+ __fs32 fs_active;/* used by snapshots to track fs */
+ __fs32 fs_old_cpc; /* cyl per cycle in postbl */
+ __fs32 fs_maxbsize;/*maximum blocking factor permitted */
+ __fs64 fs_sparecon64[17];/*old rotation block list head */
+ __fs64 fs_sblockloc; /* byte offset of standard superblock */
+ struct ufs2_csum_total fs_cstotal;/*cylinder summary information*/
+ struct ufs_timeval fs_time; /* last time written */
+ __fs64 fs_size; /* number of blocks in fs */
+ __fs64 fs_dsize; /* number of data blocks in fs */
+ __fs64 fs_csaddr; /* blk addr of cyl grp summary area */
+ __fs64 fs_pendingblocks;/* blocks in process of being freed */
+ __fs32 fs_pendinginodes;/*inodes in process of being freed */
+ } fs_u2;
+ } fs_u11;
+ union {
+ struct {
+ __fs32 fs_sparecon[53];/* reserved for future constants */
+ __fs32 fs_reclaim;
+ __fs32 fs_sparecon2[1];
+ __fs32 fs_state; /* file system state time stamp */
+ __fs32 fs_qbmask[2]; /* ~usb_bmask */
+ __fs32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sun;
+ struct {
+ __fs32 fs_sparecon[53];/* reserved for future constants */
+ __fs32 fs_reclaim;
+ __fs32 fs_sparecon2[1];
+ __fs32 fs_npsect; /* # sectors/track including spares */
+ __fs32 fs_qbmask[2]; /* ~usb_bmask */
+ __fs32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sunx86;
+ struct {
+ __fs32 fs_sparecon[50];/* reserved for future constants */
+ __fs32 fs_contigsumsize;/* size of cluster summary array */
+ __fs32 fs_maxsymlinklen;/* max length of an internal symlink */
+ __fs32 fs_inodefmt; /* format of on-disk inodes */
+ __fs32 fs_maxfilesize[2]; /* max representable file size */
+ __fs32 fs_qbmask[2]; /* ~usb_bmask */
+ __fs32 fs_qfmask[2]; /* ~usb_fmask */
+ __fs32 fs_state; /* file system state time stamp */
+ } fs_44;
+ } fs_u2;
+ __fs32 fs_postblformat; /* format of positional layout tables */
+ __fs32 fs_nrpos; /* number of rotational positions */
+ __fs32 fs_postbloff; /* (__s16) rotation block list head */
+ __fs32 fs_rotbloff; /* (__u8) blocks for each rotation */
+ __fs32 fs_magic; /* magic number */
+ __u8 fs_space[1]; /* list of blocks for each rotation */
+};
+#endif/*struct ufs_super_block*/
+
+/*
+ * Preference for optimization.
+ */
+#define UFS_OPTTIME 0 /* minimize allocation time */
+#define UFS_OPTSPACE 1 /* minimize disk fragmentation */
+
+/*
+ * Rotational layout table format types
+ */
+#define UFS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define UFS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+
+/*
+ * Convert cylinder group to base address of its global summary info.
+ */
+#define fs_cs(indx) s_csp[(indx)]
+
+/*
+ * Cylinder group block for a file system.
+ *
+ * Writable fields in the cylinder group are protected by the associated
+ * super block lock fs->fs_lock.
+ */
+#define CG_MAGIC 0x090255
+#define ufs_cg_chkmagic(sb, ucg) \
+ (fs32_to_cpu((sb), (ucg)->cg_magic) == CG_MAGIC)
+/*
+ * Macros for access to old cylinder group array structures
+ */
+#define ufs_ocg_blktot(sb, ucg) fs32_to_cpu((sb), ((struct ufs_old_cylinder_group *)(ucg))->cg_btot)
+#define ufs_ocg_blks(sb, ucg, cylno) fs32_to_cpu((sb), ((struct ufs_old_cylinder_group *)(ucg))->cg_b[cylno])
+#define ufs_ocg_inosused(sb, ucg) fs32_to_cpu((sb), ((struct ufs_old_cylinder_group *)(ucg))->cg_iused)
+#define ufs_ocg_blksfree(sb, ucg) fs32_to_cpu((sb), ((struct ufs_old_cylinder_group *)(ucg))->cg_free)
+#define ufs_ocg_chkmagic(sb, ucg) \
+ (fs32_to_cpu((sb), ((struct ufs_old_cylinder_group *)(ucg))->cg_magic) == CG_MAGIC)
+
+/*
+ * size of this structure is 172 B
+ */
+struct ufs_cylinder_group {
+ __fs32 cg_link; /* linked list of cyl groups */
+ __fs32 cg_magic; /* magic number */
+ __fs32 cg_time; /* time last written */
+ __fs32 cg_cgx; /* we are the cgx'th cylinder group */
+ __fs16 cg_ncyl; /* number of cyl's this cg */
+ __fs16 cg_niblk; /* number of inode blocks this cg */
+ __fs32 cg_ndblk; /* number of data blocks this cg */
+ struct ufs_csum cg_cs; /* cylinder summary information */
+ __fs32 cg_rotor; /* position of last used block */
+ __fs32 cg_frotor; /* position of last used frag */
+ __fs32 cg_irotor; /* position of last used inode */
+ __fs32 cg_frsum[UFS_MAXFRAG]; /* counts of available frags */
+ __fs32 cg_btotoff; /* (__u32) block totals per cylinder */
+ __fs32 cg_boff; /* (short) free block positions */
+ __fs32 cg_iusedoff; /* (char) used inode map */
+ __fs32 cg_freeoff; /* (u_char) free block map */
+ __fs32 cg_nextfreeoff; /* (u_char) next available space */
+ union {
+ struct {
+ __fs32 cg_clustersumoff; /* (u_int32) counts of avail clusters */
+ __fs32 cg_clusteroff; /* (u_int8) free cluster map */
+ __fs32 cg_nclusterblks; /* number of clusters this cg */
+ __fs32 cg_sparecon[13]; /* reserved for future use */
+ } cg_44;
+ struct {
+ __fs32 cg_clustersumoff;/* (u_int32) counts of avail clusters */
+ __fs32 cg_clusteroff; /* (u_int8) free cluster map */
+ __fs32 cg_nclusterblks;/* number of clusters this cg */
+ __fs32 cg_niblk; /* number of inode blocks this cg */
+ __fs32 cg_initediblk; /* last initialized inode */
+ __fs32 cg_sparecon32[3];/* reserved for future use */
+ __fs64 cg_time; /* time last written */
+ __fs64 cg_sparecon[3]; /* reserved for future use */
+ } cg_u2;
+ __fs32 cg_sparecon[16]; /* reserved for future use */
+ } cg_u;
+ __u8 cg_space[1]; /* space for cylinder group maps */
+/* actually longer */
+};
+
+/* Historic Cylinder group info */
+struct ufs_old_cylinder_group {
+ __fs32 cg_link; /* linked list of cyl groups */
+ __fs32 cg_rlink; /* for incore cyl groups */
+ __fs32 cg_time; /* time last written */
+ __fs32 cg_cgx; /* we are the cgx'th cylinder group */
+ __fs16 cg_ncyl; /* number of cyl's this cg */
+ __fs16 cg_niblk; /* number of inode blocks this cg */
+ __fs32 cg_ndblk; /* number of data blocks this cg */
+ struct ufs_csum cg_cs; /* cylinder summary information */
+ __fs32 cg_rotor; /* position of last used block */
+ __fs32 cg_frotor; /* position of last used frag */
+ __fs32 cg_irotor; /* position of last used inode */
+ __fs32 cg_frsum[8]; /* counts of available frags */
+ __fs32 cg_btot[32]; /* block totals per cylinder */
+ __fs16 cg_b[32][8]; /* positions of free blocks */
+ __u8 cg_iused[256]; /* used inode map */
+ __fs32 cg_magic; /* magic number */
+ __u8 cg_free[1]; /* free block map */
+/* actually longer */
+};
+
+/*
+ * structure of an on-disk inode
+ */
+struct ufs_inode {
+ __fs16 ui_mode; /* 0x0 */
+ __fs16 ui_nlink; /* 0x2 */
+ union {
+ struct {
+ __fs16 ui_suid; /* 0x4 */
+ __fs16 ui_sgid; /* 0x6 */
+ } oldids;
+ __fs32 ui_inumber; /* 0x4 lsf: inode number */
+ __fs32 ui_author; /* 0x4 GNU HURD: author */
+ } ui_u1;
+ __fs64 ui_size; /* 0x8 */
+ struct ufs_timeval ui_atime; /* 0x10 access */
+ struct ufs_timeval ui_mtime; /* 0x18 modification */
+ struct ufs_timeval ui_ctime; /* 0x20 creation */
+ union {
+ struct {
+ __fs32 ui_db[UFS_NDADDR];/* 0x28 data blocks */
+ __fs32 ui_ib[UFS_NINDIR];/* 0x58 indirect blocks */
+ } ui_addr;
+ __u8 ui_symlink[4*(UFS_NDADDR+UFS_NINDIR)];/* 0x28 fast symlink */
+ } ui_u2;
+ __fs32 ui_flags; /* 0x64 immutable, append-only... */
+ __fs32 ui_blocks; /* 0x68 blocks in use */
+ __fs32 ui_gen; /* 0x6c like ext2 i_version, for NFS support */
+ union {
+ struct {
+ __fs32 ui_shadow; /* 0x70 shadow inode with security data */
+ __fs32 ui_uid; /* 0x74 long EFT version of uid */
+ __fs32 ui_gid; /* 0x78 long EFT version of gid */
+ __fs32 ui_oeftflag; /* 0x7c reserved */
+ } ui_sun;
+ struct {
+ __fs32 ui_uid; /* 0x70 File owner */
+ __fs32 ui_gid; /* 0x74 File group */
+ __fs32 ui_spare[2]; /* 0x78 reserved */
+ } ui_44;
+ struct {
+ __fs32 ui_uid; /* 0x70 */
+ __fs32 ui_gid; /* 0x74 */
+ __fs16 ui_modeh; /* 0x78 mode high bits */
+ __fs16 ui_spare; /* 0x7A unused */
+ __fs32 ui_trans; /* 0x7c filesystem translator */
+ } ui_hurd;
+ } ui_u3;
+};
+
+#define UFS_NXADDR 2 /* External addresses in inode. */
+struct ufs2_inode {
+ __fs16 ui_mode; /* 0: IFMT, permissions; see below. */
+ __fs16 ui_nlink; /* 2: File link count. */
+ __fs32 ui_uid; /* 4: File owner. */
+ __fs32 ui_gid; /* 8: File group. */
+ __fs32 ui_blksize; /* 12: Inode blocksize. */
+ __fs64 ui_size; /* 16: File byte count. */
+ __fs64 ui_blocks; /* 24: Bytes actually held. */
+ __fs64 ui_atime; /* 32: Last access time. */
+ __fs64 ui_mtime; /* 40: Last modified time. */
+ __fs64 ui_ctime; /* 48: Last inode change time. */
+ __fs64 ui_birthtime; /* 56: Inode creation time. */
+ __fs32 ui_mtimensec; /* 64: Last modified time. */
+ __fs32 ui_atimensec; /* 68: Last access time. */
+ __fs32 ui_ctimensec; /* 72: Last inode change time. */
+ __fs32 ui_birthnsec; /* 76: Inode creation time. */
+ __fs32 ui_gen; /* 80: Generation number. */
+ __fs32 ui_kernflags; /* 84: Kernel flags. */
+ __fs32 ui_flags; /* 88: Status flags (chflags). */
+ __fs32 ui_extsize; /* 92: External attributes block. */
+ __fs64 ui_extb[UFS_NXADDR];/* 96: External attributes block. */
+ union {
+ struct {
+ __fs64 ui_db[UFS_NDADDR]; /* 112: Direct disk blocks. */
+ __fs64 ui_ib[UFS_NINDIR];/* 208: Indirect disk blocks.*/
+ } ui_addr;
+ __u8 ui_symlink[2*4*(UFS_NDADDR+UFS_NINDIR)];/* 0x28 fast symlink */
+ } ui_u2;
+ __fs64 ui_spare[3]; /* 232: Reserved; currently unused */
+};
+
+
+/* FreeBSD has these in sys/stat.h */
+/* ui_flags that can be set by a file owner */
+#define UFS_UF_SETTABLE 0x0000ffff
+#define UFS_UF_NODUMP 0x00000001 /* do not dump */
+#define UFS_UF_IMMUTABLE 0x00000002 /* immutable (can't "change") */
+#define UFS_UF_APPEND 0x00000004 /* append-only */
+#define UFS_UF_OPAQUE 0x00000008 /* directory is opaque (unionfs) */
+#define UFS_UF_NOUNLINK 0x00000010 /* can't be removed or renamed */
+/* ui_flags that only root can set */
+#define UFS_SF_SETTABLE 0xffff0000
+#define UFS_SF_ARCHIVED 0x00010000 /* archived */
+#define UFS_SF_IMMUTABLE 0x00020000 /* immutable (can't "change") */
+#define UFS_SF_APPEND 0x00040000 /* append-only */
+#define UFS_SF_NOUNLINK 0x00100000 /* can't be removed or renamed */
+
+/*
+ * This structure is used for reading disk structures larger
+ * than the size of fragment.
+ */
+struct ufs_buffer_head {
+ __u64 fragment; /* first fragment */
+ __u64 count; /* number of fragments */
+ struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */
+};
+
+struct ufs_cg_private_info {
+ struct ufs_buffer_head c_ubh;
+ __u32 c_cgx; /* number of cylidner group */
+ __u16 c_ncyl; /* number of cyl's this cg */
+ __u16 c_niblk; /* number of inode blocks this cg */
+ __u32 c_ndblk; /* number of data blocks this cg */
+ __u32 c_rotor; /* position of last used block */
+ __u32 c_frotor; /* position of last used frag */
+ __u32 c_irotor; /* position of last used inode */
+ __u32 c_btotoff; /* (__u32) block totals per cylinder */
+ __u32 c_boff; /* (short) free block positions */
+ __u32 c_iusedoff; /* (char) used inode map */
+ __u32 c_freeoff; /* (u_char) free block map */
+ __u32 c_nextfreeoff; /* (u_char) next available space */
+ __u32 c_clustersumoff;/* (u_int32) counts of avail clusters */
+ __u32 c_clusteroff; /* (u_int8) free cluster map */
+ __u32 c_nclusterblks; /* number of clusters this cg */
+};
+
+
+struct ufs_sb_private_info {
+ struct ufs_buffer_head s_ubh; /* buffer containing super block */
+ struct ufs_csum_core cs_total;
+ __u32 s_sblkno; /* offset of super-blocks in filesys */
+ __u32 s_cblkno; /* offset of cg-block in filesys */
+ __u32 s_iblkno; /* offset of inode-blocks in filesys */
+ __u32 s_dblkno; /* offset of first data after cg */
+ __u32 s_cgoffset; /* cylinder group offset in cylinder */
+ __u32 s_cgmask; /* used to calc mod fs_ntrak */
+ __u64 s_size; /* number of blocks (fragments) in fs */
+ __u64 s_dsize; /* number of data blocks in fs */
+ __u32 s_ncg; /* number of cylinder groups */
+ __u32 s_bsize; /* size of basic blocks */
+ __u32 s_fsize; /* size of fragments */
+ __u32 s_fpb; /* fragments per block */
+ __u32 s_minfree; /* minimum percentage of free blocks */
+ __u32 s_bmask; /* `blkoff'' calc of blk offsets */
+ __u32 s_fmask; /* s_fsize mask */
+ __u32 s_bshift; /* `lblkno'' calc of logical blkno */
+ __u32 s_fshift; /* s_fsize shift */
+ __u32 s_fpbshift; /* fragments per block shift */
+ __u32 s_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ __u32 s_sbsize; /* actual size of super block */
+ __u32 s_csmask; /* csum block offset */
+ __u32 s_csshift; /* csum block number */
+ __u32 s_nindir; /* value of NINDIR */
+ __u32 s_inopb; /* value of INOPB */
+ __u32 s_nspf; /* value of NSPF */
+ __u32 s_npsect; /* # sectors/track including spares */
+ __u32 s_interleave; /* hardware sector interleave */
+ __u32 s_trackskew; /* sector 0 skew, per track */
+ __u64 s_csaddr; /* blk addr of cyl grp summary area */
+ __u32 s_cssize; /* size of cyl grp summary area */
+ __u32 s_cgsize; /* cylinder group size */
+ __u32 s_ntrak; /* tracks per cylinder */
+ __u32 s_nsect; /* sectors per track */
+ __u32 s_spc; /* sectors per cylinder */
+ __u32 s_ipg; /* inodes per cylinder group */
+ __u32 s_fpg; /* fragments per group */
+ __u32 s_cpc; /* cyl per cycle in postbl */
+ __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */
+ __s64 s_qbmask; /* ~usb_bmask */
+ __s64 s_qfmask; /* ~usb_fmask */
+ __s32 s_postblformat; /* format of positional layout tables */
+ __s32 s_nrpos; /* number of rotational positions */
+ __s32 s_postbloff; /* (__s16) rotation block list head */
+ __s32 s_rotbloff; /* (__u8) blocks for each rotation */
+
+ __u32 s_fpbmask; /* fragments per block mask */
+ __u32 s_apb; /* address per block */
+ __u32 s_apbmask; /* address per block mask */
+ __u32 s_apbshift; /* address per block shift */
+ __u32 s_nspfshift; /* number of sector per fragment shift */
+ __u32 s_nspb; /* number of sector per block */
+ __u32 s_inopf; /* inodes per fragment */
+ __u32 s_sbbase; /* offset of NeXTstep superblock */
+ __u32 s_bpf; /* bits per fragment */
+ __u32 s_bpfshift; /* bits per fragment shift*/
+ __u32 s_bpfmask; /* bits per fragment mask */
+
+ __u32 s_maxsymlinklen;/* upper limit on fast symlinks' size */
+ __s32 fs_magic; /* filesystem magic */
+ unsigned int s_dirblksize;
+ __u64 s_root_blocks;
+ __u64 s_time_to_space;
+ __u64 s_space_to_time;
+};
+
+/*
+ * Sizes of this structures are:
+ * ufs_super_block_first 512
+ * ufs_super_block_second 512
+ * ufs_super_block_third 356
+ */
+struct ufs_super_block_first {
+ union {
+ struct {
+ __fs32 fs_link; /* UNUSED */
+ } fs_42;
+ struct {
+ __fs32 fs_state; /* file system state flag */
+ } fs_sun;
+ } fs_u0;
+ __fs32 fs_rlink;
+ __fs32 fs_sblkno;
+ __fs32 fs_cblkno;
+ __fs32 fs_iblkno;
+ __fs32 fs_dblkno;
+ __fs32 fs_cgoffset;
+ __fs32 fs_cgmask;
+ __fs32 fs_time;
+ __fs32 fs_size;
+ __fs32 fs_dsize;
+ __fs32 fs_ncg;
+ __fs32 fs_bsize;
+ __fs32 fs_fsize;
+ __fs32 fs_frag;
+ __fs32 fs_minfree;
+ __fs32 fs_rotdelay;
+ __fs32 fs_rps;
+ __fs32 fs_bmask;
+ __fs32 fs_fmask;
+ __fs32 fs_bshift;
+ __fs32 fs_fshift;
+ __fs32 fs_maxcontig;
+ __fs32 fs_maxbpg;
+ __fs32 fs_fragshift;
+ __fs32 fs_fsbtodb;
+ __fs32 fs_sbsize;
+ __fs32 fs_csmask;
+ __fs32 fs_csshift;
+ __fs32 fs_nindir;
+ __fs32 fs_inopb;
+ __fs32 fs_nspf;
+ __fs32 fs_optim;
+ union {
+ struct {
+ __fs32 fs_npsect;
+ } fs_sun;
+ struct {
+ __fs32 fs_state;
+ } fs_sunx86;
+ } fs_u1;
+ __fs32 fs_interleave;
+ __fs32 fs_trackskew;
+ __fs32 fs_id[2];
+ __fs32 fs_csaddr;
+ __fs32 fs_cssize;
+ __fs32 fs_cgsize;
+ __fs32 fs_ntrak;
+ __fs32 fs_nsect;
+ __fs32 fs_spc;
+ __fs32 fs_ncyl;
+ __fs32 fs_cpg;
+ __fs32 fs_ipg;
+ __fs32 fs_fpg;
+ struct ufs_csum fs_cstotal;
+ __s8 fs_fmod;
+ __s8 fs_clean;
+ __s8 fs_ronly;
+ __s8 fs_flags;
+ __s8 fs_fsmnt[UFS_MAXMNTLEN - 212];
+
+};
+
+struct ufs_super_block_second {
+ union {
+ struct {
+ __s8 fs_fsmnt[212];
+ __fs32 fs_cgrotor;
+ __fs32 fs_csp[UFS_MAXCSBUFS];
+ __fs32 fs_maxcluster;
+ __fs32 fs_cpc;
+ __fs16 fs_opostbl[82];
+ } fs_u1;
+ struct {
+ __s8 fs_fsmnt[UFS2_MAXMNTLEN - UFS_MAXMNTLEN + 212];
+ __u8 fs_volname[UFS2_MAXVOLLEN];
+ __fs64 fs_swuid;
+ __fs32 fs_pad;
+ __fs32 fs_cgrotor;
+ __fs32 fs_ocsp[UFS2_NOCSPTRS];
+ __fs32 fs_contigdirs;
+ __fs32 fs_csp;
+ __fs32 fs_maxcluster;
+ __fs32 fs_active;
+ __fs32 fs_old_cpc;
+ __fs32 fs_maxbsize;
+ __fs64 fs_sparecon64[17];
+ __fs64 fs_sblockloc;
+ __fs64 cs_ndir;
+ __fs64 cs_nbfree;
+ } fs_u2;
+ } fs_un;
+};
+
+struct ufs_super_block_third {
+ union {
+ struct {
+ __fs16 fs_opostbl[46];
+ } fs_u1;
+ struct {
+ __fs64 cs_nifree; /* number of free inodes */
+ __fs64 cs_nffree; /* number of free frags */
+ __fs64 cs_numclusters; /* number of free clusters */
+ __fs64 cs_spare[3]; /* future expansion */
+ struct ufs_timeval fs_time; /* last time written */
+ __fs64 fs_size; /* number of blocks in fs */
+ __fs64 fs_dsize; /* number of data blocks in fs */
+ __fs64 fs_csaddr; /* blk addr of cyl grp summary area */
+ __fs64 fs_pendingblocks;/* blocks in process of being freed */
+ __fs32 fs_pendinginodes;/*inodes in process of being freed */
+ } __attribute__ ((packed)) fs_u2;
+ } fs_un1;
+ union {
+ struct {
+ __fs32 fs_sparecon[53];/* reserved for future constants */
+ __fs32 fs_reclaim;
+ __fs32 fs_sparecon2[1];
+ __fs32 fs_state; /* file system state time stamp */
+ __fs32 fs_qbmask[2]; /* ~usb_bmask */
+ __fs32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sun;
+ struct {
+ __fs32 fs_sparecon[53];/* reserved for future constants */
+ __fs32 fs_reclaim;
+ __fs32 fs_sparecon2[1];
+ __fs32 fs_npsect; /* # sectors/track including spares */
+ __fs32 fs_qbmask[2]; /* ~usb_bmask */
+ __fs32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sunx86;
+ struct {
+ __fs32 fs_sparecon[50];/* reserved for future constants */
+ __fs32 fs_contigsumsize;/* size of cluster summary array */
+ __fs32 fs_maxsymlinklen;/* max length of an internal symlink */
+ __fs32 fs_inodefmt; /* format of on-disk inodes */
+ __fs32 fs_maxfilesize[2]; /* max representable file size */
+ __fs32 fs_qbmask[2]; /* ~usb_bmask */
+ __fs32 fs_qfmask[2]; /* ~usb_fmask */
+ __fs32 fs_state; /* file system state time stamp */
+ } fs_44;
+ } fs_un2;
+ __fs32 fs_postblformat;
+ __fs32 fs_nrpos;
+ __fs32 fs_postbloff;
+ __fs32 fs_rotbloff;
+ __fs32 fs_magic;
+ __u8 fs_space[1];
+};
+
+#endif /* __LINUX_UFS_FS_H */
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
new file mode 100644
index 000000000..dff6f7461
--- /dev/null
+++ b/fs/ufs/util.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/fs/ufs/util.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+
+#include "ufs_fs.h"
+#include "ufs.h"
+#include "swab.h"
+#include "util.h"
+
+struct ufs_buffer_head * _ubh_bread_ (struct ufs_sb_private_info * uspi,
+ struct super_block *sb, u64 fragment, u64 size)
+{
+ struct ufs_buffer_head * ubh;
+ unsigned i, j ;
+ u64 count = 0;
+ if (size & ~uspi->s_fmask)
+ return NULL;
+ count = size >> uspi->s_fshift;
+ if (count > UFS_MAXFRAG)
+ return NULL;
+ ubh = kmalloc_obj(struct ufs_buffer_head, GFP_NOFS);
+ if (!ubh)
+ return NULL;
+ ubh->fragment = fragment;
+ ubh->count = count;
+ for (i = 0; i < count; i++)
+ if (!(ubh->bh[i] = sb_bread(sb, fragment + i)))
+ goto failed;
+ for (; i < UFS_MAXFRAG; i++)
+ ubh->bh[i] = NULL;
+ return ubh;
+failed:
+ for (j = 0; j < i; j++)
+ brelse (ubh->bh[j]);
+ kfree(ubh);
+ return NULL;
+}
+
+struct ufs_buffer_head * ubh_bread_uspi (struct ufs_sb_private_info * uspi,
+ struct super_block *sb, u64 fragment, u64 size)
+{
+ unsigned i, j;
+ u64 count = 0;
+ if (size & ~uspi->s_fmask)
+ return NULL;
+ count = size >> uspi->s_fshift;
+ if (count <= 0 || count > UFS_MAXFRAG)
+ return NULL;
+ USPI_UBH(uspi)->fragment = fragment;
+ USPI_UBH(uspi)->count = count;
+ for (i = 0; i < count; i++)
+ if (!(USPI_UBH(uspi)->bh[i] = sb_bread(sb, fragment + i)))
+ goto failed;
+ for (; i < UFS_MAXFRAG; i++)
+ USPI_UBH(uspi)->bh[i] = NULL;
+ return USPI_UBH(uspi);
+failed:
+ for (j = 0; j < i; j++)
+ brelse (USPI_UBH(uspi)->bh[j]);
+ return NULL;
+}
+
+void ubh_brelse (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for (i = 0; i < ubh->count; i++)
+ brelse (ubh->bh[i]);
+ kfree (ubh);
+}
+
+void ubh_brelse_uspi (struct ufs_sb_private_info * uspi)
+{
+ unsigned i;
+ if (!USPI_UBH(uspi))
+ return;
+ for ( i = 0; i < USPI_UBH(uspi)->count; i++ ) {
+ brelse (USPI_UBH(uspi)->bh[i]);
+ USPI_UBH(uspi)->bh[i] = NULL;
+ }
+}
+
+void ubh_mark_buffer_dirty (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < ubh->count; i++ )
+ mark_buffer_dirty (ubh->bh[i]);
+}
+
+void ubh_sync_block(struct ufs_buffer_head *ubh)
+{
+ if (ubh) {
+ unsigned i;
+
+ for (i = 0; i < ubh->count; i++)
+ write_dirty_buffer(ubh->bh[i], 0);
+
+ for (i = 0; i < ubh->count; i++)
+ wait_on_buffer(ubh->bh[i]);
+ }
+}
+
+void ubh_bforget (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < ubh->count; i++ ) if ( ubh->bh[i] )
+ bforget (ubh->bh[i]);
+}
+
+int ubh_buffer_dirty (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ unsigned result = 0;
+ if (!ubh)
+ return 0;
+ for ( i = 0; i < ubh->count; i++ )
+ result |= buffer_dirty(ubh->bh[i]);
+ return result;
+}
+
+dev_t
+ufs_get_inode_dev(struct super_block *sb, struct ufs_inode_info *ufsi)
+{
+ __u32 fs32;
+ dev_t dev;
+
+ if ((UFS_SB(sb)->s_flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ fs32 = fs32_to_cpu(sb, ufsi->i_u1.i_data[1]);
+ else
+ fs32 = fs32_to_cpu(sb, ufsi->i_u1.i_data[0]);
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUNx86:
+ case UFS_ST_SUN:
+ if ((fs32 & 0xffff0000) == 0 ||
+ (fs32 & 0xffff0000) == 0xffff0000)
+ dev = old_decode_dev(fs32 & 0x7fff);
+ else
+ dev = MKDEV(sysv_major(fs32), sysv_minor(fs32));
+ break;
+
+ default:
+ dev = old_decode_dev(fs32);
+ break;
+ }
+ return dev;
+}
+
+void
+ufs_set_inode_dev(struct super_block *sb, struct ufs_inode_info *ufsi, dev_t dev)
+{
+ __u32 fs32;
+
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUNx86:
+ case UFS_ST_SUN:
+ fs32 = sysv_encode_dev(dev);
+ if ((fs32 & 0xffff8000) == 0) {
+ fs32 = old_encode_dev(dev);
+ }
+ break;
+
+ default:
+ fs32 = old_encode_dev(dev);
+ break;
+ }
+ if ((UFS_SB(sb)->s_flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ ufsi->i_u1.i_data[1] = cpu_to_fs32(sb, fs32);
+ else
+ ufsi->i_u1.i_data[0] = cpu_to_fs32(sb, fs32);
+}
+
+/**
+ * ufs_get_locked_folio() - locate, pin and lock a pagecache folio, if not exist
+ * read it from disk.
+ * @mapping: the address_space to search
+ * @index: the page index
+ *
+ * Locates the desired pagecache folio, if not exist we'll read it,
+ * locks it, increments its reference
+ * count and returns its address.
+ *
+ */
+struct folio *ufs_get_locked_folio(struct address_space *mapping,
+ pgoff_t index)
+{
+ struct inode *inode = mapping->host;
+ struct folio *folio = filemap_lock_folio(mapping, index);
+ if (IS_ERR(folio)) {
+ folio = read_mapping_folio(mapping, index, NULL);
+
+ if (IS_ERR(folio)) {
+ printk(KERN_ERR "ufs_change_blocknr: read_mapping_folio error: ino %llu, index: %lu\n",
+ mapping->host->i_ino, index);
+ return folio;
+ }
+
+ folio_lock(folio);
+
+ if (unlikely(folio->mapping == NULL)) {
+ /* Truncate got there first */
+ folio_unlock(folio);
+ folio_put(folio);
+ return NULL;
+ }
+ }
+ if (!folio_buffers(folio))
+ create_empty_buffers(folio, 1 << inode->i_blkbits, 0);
+ return folio;
+}
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
new file mode 100644
index 000000000..391bb4f11
--- /dev/null
+++ b/fs/ufs/util.h
@@ -0,0 +1,597 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * linux/fs/ufs/util.h
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/fs.h>
+#include "swab.h"
+
+/*
+ * functions used for retyping
+ */
+static inline struct ufs_buffer_head *UCPI_UBH(struct ufs_cg_private_info *cpi)
+{
+ return &cpi->c_ubh;
+}
+static inline struct ufs_buffer_head *USPI_UBH(struct ufs_sb_private_info *spi)
+{
+ return &spi->s_ubh;
+}
+
+
+
+/*
+ * macros used for accessing structures
+ */
+static inline s32
+ufs_get_fs_state(struct super_block *sb, struct ufs_super_block_first *usb1,
+ struct ufs_super_block_third *usb3)
+{
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUNOS:
+ if (fs32_to_cpu(sb, usb3->fs_postblformat) == UFS_42POSTBLFMT)
+ return fs32_to_cpu(sb, usb1->fs_u0.fs_sun.fs_state);
+ fallthrough; /* to UFS_ST_SUN */
+ case UFS_ST_SUN:
+ return fs32_to_cpu(sb, usb3->fs_un2.fs_sun.fs_state);
+ case UFS_ST_SUNx86:
+ return fs32_to_cpu(sb, usb1->fs_u1.fs_sunx86.fs_state);
+ case UFS_ST_44BSD:
+ default:
+ return fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_state);
+ }
+}
+
+static inline void
+ufs_set_fs_state(struct super_block *sb, struct ufs_super_block_first *usb1,
+ struct ufs_super_block_third *usb3, s32 value)
+{
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUNOS:
+ if (fs32_to_cpu(sb, usb3->fs_postblformat) == UFS_42POSTBLFMT) {
+ usb1->fs_u0.fs_sun.fs_state = cpu_to_fs32(sb, value);
+ break;
+ }
+ fallthrough; /* to UFS_ST_SUN */
+ case UFS_ST_SUN:
+ usb3->fs_un2.fs_sun.fs_state = cpu_to_fs32(sb, value);
+ break;
+ case UFS_ST_SUNx86:
+ usb1->fs_u1.fs_sunx86.fs_state = cpu_to_fs32(sb, value);
+ break;
+ case UFS_ST_44BSD:
+ usb3->fs_un2.fs_44.fs_state = cpu_to_fs32(sb, value);
+ break;
+ }
+}
+
+static inline u32
+ufs_get_fs_npsect(struct super_block *sb, struct ufs_super_block_first *usb1,
+ struct ufs_super_block_third *usb3)
+{
+ if ((UFS_SB(sb)->s_flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ return fs32_to_cpu(sb, usb3->fs_un2.fs_sunx86.fs_npsect);
+ else
+ return fs32_to_cpu(sb, usb1->fs_u1.fs_sun.fs_npsect);
+}
+
+static inline u64
+ufs_get_fs_qbmask(struct super_block *sb, struct ufs_super_block_third *usb3)
+{
+ __fs64 tmp;
+
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUNOS:
+ case UFS_ST_SUN:
+ ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sun.fs_qbmask[0];
+ ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sun.fs_qbmask[1];
+ break;
+ case UFS_ST_SUNx86:
+ ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sunx86.fs_qbmask[0];
+ ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sunx86.fs_qbmask[1];
+ break;
+ case UFS_ST_44BSD:
+ ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_44.fs_qbmask[0];
+ ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_44.fs_qbmask[1];
+ break;
+ }
+
+ return fs64_to_cpu(sb, tmp);
+}
+
+static inline u64
+ufs_get_fs_qfmask(struct super_block *sb, struct ufs_super_block_third *usb3)
+{
+ __fs64 tmp;
+
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUNOS:
+ case UFS_ST_SUN:
+ ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sun.fs_qfmask[0];
+ ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sun.fs_qfmask[1];
+ break;
+ case UFS_ST_SUNx86:
+ ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sunx86.fs_qfmask[0];
+ ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sunx86.fs_qfmask[1];
+ break;
+ case UFS_ST_44BSD:
+ ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_44.fs_qfmask[0];
+ ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_44.fs_qfmask[1];
+ break;
+ }
+
+ return fs64_to_cpu(sb, tmp);
+}
+
+static inline u16
+ufs_get_de_namlen(struct super_block *sb, struct ufs_dir_entry *de)
+{
+ if ((UFS_SB(sb)->s_flags & UFS_DE_MASK) == UFS_DE_OLD)
+ return fs16_to_cpu(sb, de->d_u.d_namlen);
+ else
+ return de->d_u.d_44.d_namlen; /* XXX this seems wrong */
+}
+
+static inline void
+ufs_set_de_namlen(struct super_block *sb, struct ufs_dir_entry *de, u16 value)
+{
+ if ((UFS_SB(sb)->s_flags & UFS_DE_MASK) == UFS_DE_OLD)
+ de->d_u.d_namlen = cpu_to_fs16(sb, value);
+ else
+ de->d_u.d_44.d_namlen = value; /* XXX this seems wrong */
+}
+
+static inline void
+ufs_set_de_type(struct super_block *sb, struct ufs_dir_entry *de, int mode)
+{
+ if ((UFS_SB(sb)->s_flags & UFS_DE_MASK) != UFS_DE_44BSD)
+ return;
+
+ /*
+ * TODO turn this into a table lookup
+ */
+ switch (mode & S_IFMT) {
+ case S_IFSOCK:
+ de->d_u.d_44.d_type = DT_SOCK;
+ break;
+ case S_IFLNK:
+ de->d_u.d_44.d_type = DT_LNK;
+ break;
+ case S_IFREG:
+ de->d_u.d_44.d_type = DT_REG;
+ break;
+ case S_IFBLK:
+ de->d_u.d_44.d_type = DT_BLK;
+ break;
+ case S_IFDIR:
+ de->d_u.d_44.d_type = DT_DIR;
+ break;
+ case S_IFCHR:
+ de->d_u.d_44.d_type = DT_CHR;
+ break;
+ case S_IFIFO:
+ de->d_u.d_44.d_type = DT_FIFO;
+ break;
+ default:
+ de->d_u.d_44.d_type = DT_UNKNOWN;
+ }
+}
+
+static inline u32
+ufs_get_inode_uid(struct super_block *sb, struct ufs_inode *inode)
+{
+ switch (UFS_SB(sb)->s_flags & UFS_UID_MASK) {
+ case UFS_UID_44BSD:
+ return fs32_to_cpu(sb, inode->ui_u3.ui_44.ui_uid);
+ case UFS_UID_EFT:
+ if (inode->ui_u1.oldids.ui_suid == 0xFFFF)
+ return fs32_to_cpu(sb, inode->ui_u3.ui_sun.ui_uid);
+ fallthrough;
+ default:
+ return fs16_to_cpu(sb, inode->ui_u1.oldids.ui_suid);
+ }
+}
+
+static inline void
+ufs_set_inode_uid(struct super_block *sb, struct ufs_inode *inode, u32 value)
+{
+ switch (UFS_SB(sb)->s_flags & UFS_UID_MASK) {
+ case UFS_UID_44BSD:
+ inode->ui_u3.ui_44.ui_uid = cpu_to_fs32(sb, value);
+ inode->ui_u1.oldids.ui_suid = cpu_to_fs16(sb, value);
+ break;
+ case UFS_UID_EFT:
+ inode->ui_u3.ui_sun.ui_uid = cpu_to_fs32(sb, value);
+ if (value > 0xFFFF)
+ value = 0xFFFF;
+ fallthrough;
+ default:
+ inode->ui_u1.oldids.ui_suid = cpu_to_fs16(sb, value);
+ break;
+ }
+}
+
+static inline u32
+ufs_get_inode_gid(struct super_block *sb, struct ufs_inode *inode)
+{
+ switch (UFS_SB(sb)->s_flags & UFS_UID_MASK) {
+ case UFS_UID_44BSD:
+ return fs32_to_cpu(sb, inode->ui_u3.ui_44.ui_gid);
+ case UFS_UID_EFT:
+ if (inode->ui_u1.oldids.ui_sgid == 0xFFFF)
+ return fs32_to_cpu(sb, inode->ui_u3.ui_sun.ui_gid);
+ fallthrough;
+ default:
+ return fs16_to_cpu(sb, inode->ui_u1.oldids.ui_sgid);
+ }
+}
+
+static inline void
+ufs_set_inode_gid(struct super_block *sb, struct ufs_inode *inode, u32 value)
+{
+ switch (UFS_SB(sb)->s_flags & UFS_UID_MASK) {
+ case UFS_UID_44BSD:
+ inode->ui_u3.ui_44.ui_gid = cpu_to_fs32(sb, value);
+ inode->ui_u1.oldids.ui_sgid = cpu_to_fs16(sb, value);
+ break;
+ case UFS_UID_EFT:
+ inode->ui_u3.ui_sun.ui_gid = cpu_to_fs32(sb, value);
+ if (value > 0xFFFF)
+ value = 0xFFFF;
+ fallthrough;
+ default:
+ inode->ui_u1.oldids.ui_sgid = cpu_to_fs16(sb, value);
+ break;
+ }
+}
+
+dev_t ufs_get_inode_dev(struct super_block *, struct ufs_inode_info *);
+void ufs_set_inode_dev(struct super_block *, struct ufs_inode_info *, dev_t);
+int ufs_prepare_chunk(struct folio *folio, loff_t pos, unsigned len);
+
+/*
+ * These functions manipulate ufs buffers
+ */
+#define ubh_bread(sb,fragment,size) _ubh_bread_(uspi,sb,fragment,size)
+extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, struct super_block *, u64 , u64);
+extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, struct super_block *, u64, u64);
+extern void ubh_brelse (struct ufs_buffer_head *);
+extern void ubh_brelse_uspi (struct ufs_sb_private_info *);
+extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *);
+extern void ubh_sync_block(struct ufs_buffer_head *);
+extern void ubh_bforget (struct ufs_buffer_head *);
+extern int ubh_buffer_dirty (struct ufs_buffer_head *);
+
+/* This functions works with cache pages*/
+struct folio *ufs_get_locked_folio(struct address_space *mapping, pgoff_t index);
+static inline void ufs_put_locked_folio(struct folio *folio)
+{
+ folio_unlock(folio);
+ folio_put(folio);
+}
+
+/*
+ * macros and inline function to get important structures from ufs_sb_private_info
+ */
+
+static inline void *get_usb_offset(struct ufs_sb_private_info *uspi,
+ unsigned int offset)
+{
+ unsigned int index;
+
+ index = offset >> uspi->s_fshift;
+ offset &= ~uspi->s_fmask;
+ return uspi->s_ubh.bh[index]->b_data + offset;
+}
+
+#define ubh_get_usb_first(uspi) \
+ ((struct ufs_super_block_first *)get_usb_offset((uspi), 0))
+
+#define ubh_get_usb_second(uspi) \
+ ((struct ufs_super_block_second *)get_usb_offset((uspi), UFS_SECTOR_SIZE))
+
+#define ubh_get_usb_third(uspi) \
+ ((struct ufs_super_block_third *)get_usb_offset((uspi), 2*UFS_SECTOR_SIZE))
+
+
+#define ubh_get_ucg(ubh) \
+ ((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data))
+
+
+/*
+ * Extract byte from ufs_buffer_head
+ * Extract the bits for a block from a map inside ufs_buffer_head
+ */
+#define ubh_get_addr8(ubh,begin) \
+ ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + \
+ ((begin) & ~uspi->s_fmask))
+
+#define ubh_get_addr16(ubh,begin) \
+ (((__fs16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + \
+ ((begin) & ((uspi->fsize>>1) - 1)))
+
+#define ubh_get_addr32(ubh,begin) \
+ (((__fs32*)((ubh)->bh[(begin) >> (uspi->s_fshift-2)]->b_data)) + \
+ ((begin) & ((uspi->s_fsize>>2) - 1)))
+
+#define ubh_get_addr64(ubh,begin) \
+ (((__fs64*)((ubh)->bh[(begin) >> (uspi->s_fshift-3)]->b_data)) + \
+ ((begin) & ((uspi->s_fsize>>3) - 1)))
+
+#define ubh_get_addr ubh_get_addr8
+
+static inline void *ubh_get_data_ptr(struct ufs_sb_private_info *uspi,
+ struct ufs_buffer_head *ubh,
+ u64 blk)
+{
+ if (uspi->fs_magic == UFS2_MAGIC)
+ return ubh_get_addr64(ubh, blk);
+ else
+ return ubh_get_addr32(ubh, blk);
+}
+
+#define ubh_blkmap(ubh,begin,bit) \
+ ((*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) >> ((bit) & 7)) & (0xff >> (UFS_MAXFRAG - uspi->s_fpb)))
+
+static inline u64
+ufs_freefrags(struct ufs_sb_private_info *uspi)
+{
+ return ufs_blkstofrags(uspi->cs_total.cs_nbfree) +
+ uspi->cs_total.cs_nffree;
+}
+
+/*
+ * Macros to access cylinder group array structures
+ */
+#define ubh_cg_blktot(ucpi,cylno) \
+ (*((__fs32*)ubh_get_addr(UCPI_UBH(ucpi), (ucpi)->c_btotoff + ((cylno) << 2))))
+
+#define ubh_cg_blks(ucpi,cylno,rpos) \
+ (*((__fs16*)ubh_get_addr(UCPI_UBH(ucpi), \
+ (ucpi)->c_boff + (((cylno) * uspi->s_nrpos + (rpos)) << 1 ))))
+
+/*
+ * Bitmap operations
+ * These functions work like classical bitmap operations.
+ * The difference is that we don't have the whole bitmap
+ * in one contiguous chunk of memory, but in several buffers.
+ * The parameters of each function are super_block, ufs_buffer_head and
+ * position of the beginning of the bitmap.
+ */
+#define ubh_setbit(ubh,begin,bit) \
+ (*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) |= (1 << ((bit) & 7)))
+
+#define ubh_clrbit(ubh,begin,bit) \
+ (*ubh_get_addr (ubh, (begin) + ((bit) >> 3)) &= ~(1 << ((bit) & 7)))
+
+#define ubh_isset(ubh,begin,bit) \
+ (*ubh_get_addr (ubh, (begin) + ((bit) >> 3)) & (1 << ((bit) & 7)))
+
+#define ubh_isclr(ubh,begin,bit) (!ubh_isset(ubh,begin,bit))
+
+#define ubh_find_first_zero_bit(ubh,begin,size) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0)
+
+#define ubh_find_next_zero_bit(ubh,begin,size,offset) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset)
+static inline unsigned _ubh_find_next_zero_bit_(
+ struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
+ unsigned begin, unsigned size, unsigned offset)
+{
+ unsigned base, count, pos;
+
+ size -= offset;
+ begin <<= 3;
+ offset += begin;
+ base = offset >> uspi->s_bpfshift;
+ offset &= uspi->s_bpfmask;
+ for (;;) {
+ count = min_t(unsigned int, size + offset, uspi->s_bpf);
+ size -= count - offset;
+ pos = find_next_zero_bit_le(ubh->bh[base]->b_data, count, offset);
+ if (pos < count || !size)
+ break;
+ base++;
+ offset = 0;
+ }
+ return (base << uspi->s_bpfshift) + pos - begin;
+}
+
+static inline unsigned find_last_zero_bit (unsigned char * bitmap,
+ unsigned size, unsigned offset)
+{
+ unsigned bit, i;
+ unsigned char * mapp;
+ unsigned char map;
+
+ mapp = bitmap + (size >> 3);
+ map = *mapp--;
+ bit = 1 << (size & 7);
+ for (i = size; i > offset; i--) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & 7) != 0) {
+ bit >>= 1;
+ } else {
+ map = *mapp--;
+ bit = 1 << 7;
+ }
+ }
+ return i;
+}
+
+#define ubh_find_last_zero_bit(ubh,begin,size,offset) _ubh_find_last_zero_bit_(uspi,ubh,begin,size,offset)
+static inline unsigned _ubh_find_last_zero_bit_(
+ struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
+ unsigned begin, unsigned start, unsigned end)
+{
+ unsigned base, count, pos, size;
+
+ size = start - end;
+ begin <<= 3;
+ start += begin;
+ base = start >> uspi->s_bpfshift;
+ start &= uspi->s_bpfmask;
+ for (;;) {
+ count = min_t(unsigned int,
+ size + (uspi->s_bpf - start), uspi->s_bpf)
+ - (uspi->s_bpf - start);
+ size -= count;
+ pos = find_last_zero_bit (ubh->bh[base]->b_data,
+ start, start - count);
+ if (pos > start - count || !size)
+ break;
+ base--;
+ start = uspi->s_bpf;
+ }
+ return (base << uspi->s_bpfshift) + pos - begin;
+}
+
+static inline int ubh_isblockset(struct ufs_sb_private_info *uspi,
+ struct ufs_cg_private_info *ucpi, unsigned int frag)
+{
+ struct ufs_buffer_head *ubh = UCPI_UBH(ucpi);
+ u8 *p = ubh_get_addr(ubh, ucpi->c_freeoff + (frag >> 3));
+ u8 mask;
+
+ switch (uspi->s_fpb) {
+ case 8:
+ return *p == 0xff;
+ case 4:
+ mask = 0x0f << (frag & 4);
+ return (*p & mask) == mask;
+ case 2:
+ mask = 0x03 << (frag & 6);
+ return (*p & mask) == mask;
+ case 1:
+ mask = 0x01 << (frag & 7);
+ return (*p & mask) == mask;
+ }
+ return 0;
+}
+
+static inline void ubh_clrblock(struct ufs_sb_private_info *uspi,
+ struct ufs_cg_private_info *ucpi, unsigned int frag)
+{
+ struct ufs_buffer_head *ubh = UCPI_UBH(ucpi);
+ u8 *p = ubh_get_addr(ubh, ucpi->c_freeoff + (frag >> 3));
+
+ switch (uspi->s_fpb) {
+ case 8:
+ *p = 0x00;
+ return;
+ case 4:
+ *p &= ~(0x0f << (frag & 4));
+ return;
+ case 2:
+ *p &= ~(0x03 << (frag & 6));
+ return;
+ case 1:
+ *p &= ~(0x01 << (frag & 7));
+ return;
+ }
+}
+
+static inline void ubh_setblock(struct ufs_sb_private_info * uspi,
+ struct ufs_cg_private_info *ucpi, unsigned int frag)
+{
+ struct ufs_buffer_head *ubh = UCPI_UBH(ucpi);
+ u8 *p = ubh_get_addr(ubh, ucpi->c_freeoff + (frag >> 3));
+
+ switch (uspi->s_fpb) {
+ case 8:
+ *p = 0xff;
+ return;
+ case 4:
+ *p |= 0x0f << (frag & 4);
+ return;
+ case 2:
+ *p |= 0x03 << (frag & 6);
+ return;
+ case 1:
+ *p |= 0x01 << (frag & 7);
+ return;
+ }
+}
+
+static inline void ufs_fragacct (struct super_block * sb, unsigned blockmap,
+ __fs32 * fraglist, int cnt)
+{
+ struct ufs_sb_private_info * uspi;
+ unsigned fragsize, pos;
+
+ uspi = UFS_SB(sb)->s_uspi;
+
+ fragsize = 0;
+ for (pos = 0; pos < uspi->s_fpb; pos++) {
+ if (blockmap & (1 << pos)) {
+ fragsize++;
+ }
+ else if (fragsize > 0) {
+ fs32_add(sb, &fraglist[fragsize], cnt);
+ fragsize = 0;
+ }
+ }
+ if (fragsize > 0 && fragsize < uspi->s_fpb)
+ fs32_add(sb, &fraglist[fragsize], cnt);
+}
+
+static inline void *ufs_get_direct_data_ptr(struct ufs_sb_private_info *uspi,
+ struct ufs_inode_info *ufsi,
+ unsigned blk)
+{
+ BUG_ON(blk > UFS_TIND_BLOCK);
+ return uspi->fs_magic == UFS2_MAGIC ?
+ (void *)&ufsi->i_u1.u2_i_data[blk] :
+ (void *)&ufsi->i_u1.i_data[blk];
+}
+
+static inline u64 ufs_data_ptr_to_cpu(struct super_block *sb, void *p)
+{
+ return UFS_SB(sb)->s_uspi->fs_magic == UFS2_MAGIC ?
+ fs64_to_cpu(sb, *(__fs64 *)p) :
+ fs32_to_cpu(sb, *(__fs32 *)p);
+}
+
+static inline void ufs_cpu_to_data_ptr(struct super_block *sb, void *p, u64 val)
+{
+ if (UFS_SB(sb)->s_uspi->fs_magic == UFS2_MAGIC)
+ *(__fs64 *)p = cpu_to_fs64(sb, val);
+ else
+ *(__fs32 *)p = cpu_to_fs32(sb, val);
+}
+
+static inline void ufs_data_ptr_clear(struct ufs_sb_private_info *uspi,
+ void *p)
+{
+ if (uspi->fs_magic == UFS2_MAGIC)
+ *(__fs64 *)p = 0;
+ else
+ *(__fs32 *)p = 0;
+}
+
+static inline int ufs_is_data_ptr_zero(struct ufs_sb_private_info *uspi,
+ void *p)
+{
+ if (uspi->fs_magic == UFS2_MAGIC)
+ return *(__fs64 *)p == 0;
+ else
+ return *(__fs32 *)p == 0;
+}
+
+static inline __fs32 ufs_get_seconds(struct super_block *sbp)
+{
+ time64_t now = ktime_get_real_seconds();
+
+ /* Signed 32-bit interpretation wraps around in 2038, which
+ * happens in ufs1 inode stamps but not ufs2 using 64-bits
+ * stamps. For superblock and blockgroup, let's assume
+ * unsigned 32-bit stamps, which are good until y2106.
+ * Wrap around rather than clamp here to make the dirty
+ * file system detection work in the superblock stamp.
+ */
+ return cpu_to_fs32(sbp, lower_32_bits(now));
+}