xref: /kernel/linux/linux-5.10/fs/squashfs/super.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Squashfs - a compressed read only filesystem for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
68c2ecf20Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * super.c
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * This file implements code to read the superblock, read and initialise
138c2ecf20Sopenharmony_ci * in-memory structures at mount time, and all the VFS glue code to register
148c2ecf20Sopenharmony_ci * the filesystem.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/fs.h>
208c2ecf20Sopenharmony_ci#include <linux/fs_context.h>
218c2ecf20Sopenharmony_ci#include <linux/vfs.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/mutex.h>
248c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
258c2ecf20Sopenharmony_ci#include <linux/init.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/magic.h>
288c2ecf20Sopenharmony_ci#include <linux/xattr.h>
298c2ecf20Sopenharmony_ci#include <linux/backing-dev.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "squashfs_fs.h"
328c2ecf20Sopenharmony_ci#include "squashfs_fs_sb.h"
338c2ecf20Sopenharmony_ci#include "squashfs_fs_i.h"
348c2ecf20Sopenharmony_ci#include "squashfs.h"
358c2ecf20Sopenharmony_ci#include "decompressor.h"
368c2ecf20Sopenharmony_ci#include "xattr.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic struct file_system_type squashfs_fs_type;
398c2ecf20Sopenharmony_cistatic const struct super_operations squashfs_super_ops;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor *supported_squashfs_filesystem(
428c2ecf20Sopenharmony_ci	struct fs_context *fc,
438c2ecf20Sopenharmony_ci	short major, short minor, short id)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	const struct squashfs_decompressor *decompressor;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (major < SQUASHFS_MAJOR) {
488c2ecf20Sopenharmony_ci		errorf(fc, "Major/Minor mismatch, older Squashfs %d.%d "
498c2ecf20Sopenharmony_ci		       "filesystems are unsupported", major, minor);
508c2ecf20Sopenharmony_ci		return NULL;
518c2ecf20Sopenharmony_ci	} else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
528c2ecf20Sopenharmony_ci		errorf(fc, "Major/Minor mismatch, trying to mount newer "
538c2ecf20Sopenharmony_ci		       "%d.%d filesystem", major, minor);
548c2ecf20Sopenharmony_ci		errorf(fc, "Please update your kernel");
558c2ecf20Sopenharmony_ci		return NULL;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	decompressor = squashfs_lookup_decompressor(id);
598c2ecf20Sopenharmony_ci	if (!decompressor->supported) {
608c2ecf20Sopenharmony_ci		errorf(fc, "Filesystem uses \"%s\" compression. This is not supported",
618c2ecf20Sopenharmony_ci		       decompressor->name);
628c2ecf20Sopenharmony_ci		return NULL;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return decompressor;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int squashfs_bdi_init(struct super_block *sb)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int err;
718c2ecf20Sopenharmony_ci	unsigned int major = MAJOR(sb->s_dev);
728c2ecf20Sopenharmony_ci	unsigned int minor = MINOR(sb->s_dev);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	bdi_put(sb->s_bdi);
758c2ecf20Sopenharmony_ci	sb->s_bdi = &noop_backing_dev_info;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	err = super_setup_bdi_name(sb, "squashfs_%u_%u", major, minor);
788c2ecf20Sopenharmony_ci	if (err)
798c2ecf20Sopenharmony_ci		return err;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	sb->s_bdi->ra_pages = 0;
828c2ecf20Sopenharmony_ci	sb->s_bdi->io_pages = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk;
908c2ecf20Sopenharmony_ci	struct squashfs_super_block *sblk = NULL;
918c2ecf20Sopenharmony_ci	struct inode *root;
928c2ecf20Sopenharmony_ci	long long root_inode;
938c2ecf20Sopenharmony_ci	unsigned short flags;
948c2ecf20Sopenharmony_ci	unsigned int fragments;
958c2ecf20Sopenharmony_ci	u64 lookup_table_start, xattr_id_table_start, next_table;
968c2ecf20Sopenharmony_ci	int err;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	TRACE("Entered squashfs_fill_superblock\n");
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/*
1018c2ecf20Sopenharmony_ci	 * squashfs provides 'backing_dev_info' in order to disable read-ahead. For
1028c2ecf20Sopenharmony_ci	 * squashfs, I/O is not deferred, it is done immediately in readpage,
1038c2ecf20Sopenharmony_ci	 * which means the user would have to wait not just for their own I/O
1048c2ecf20Sopenharmony_ci	 * but the read-ahead I/O as well i.e. completely pointless.squashfs_bdi_init
1058c2ecf20Sopenharmony_ci	 * will set sb->s_bdi->ra_pages and sb->s_bdi->io_pages to 0.
1068c2ecf20Sopenharmony_ci	 */
1078c2ecf20Sopenharmony_ci	err = squashfs_bdi_init(sb);
1088c2ecf20Sopenharmony_ci	if (err) {
1098c2ecf20Sopenharmony_ci		errorf(fc, "squashfs init bdi failed");
1108c2ecf20Sopenharmony_ci		return err;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
1148c2ecf20Sopenharmony_ci	if (sb->s_fs_info == NULL) {
1158c2ecf20Sopenharmony_ci		ERROR("Failed to allocate squashfs_sb_info\n");
1168c2ecf20Sopenharmony_ci		return -ENOMEM;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	msblk = sb->s_fs_info;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
1218c2ecf20Sopenharmony_ci	msblk->devblksize_log2 = ffz(~msblk->devblksize);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	mutex_init(&msblk->meta_index_mutex);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * msblk->bytes_used is checked in squashfs_read_table to ensure reads
1278c2ecf20Sopenharmony_ci	 * are not beyond filesystem end.  But as we're using
1288c2ecf20Sopenharmony_ci	 * squashfs_read_table here to read the superblock (including the value
1298c2ecf20Sopenharmony_ci	 * of bytes_used) we need to set it to an initial sensible dummy value
1308c2ecf20Sopenharmony_ci	 */
1318c2ecf20Sopenharmony_ci	msblk->bytes_used = sizeof(*sblk);
1328c2ecf20Sopenharmony_ci	sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk));
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (IS_ERR(sblk)) {
1358c2ecf20Sopenharmony_ci		errorf(fc, "unable to read squashfs_super_block");
1368c2ecf20Sopenharmony_ci		err = PTR_ERR(sblk);
1378c2ecf20Sopenharmony_ci		sblk = NULL;
1388c2ecf20Sopenharmony_ci		goto failed_mount;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	err = -EINVAL;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Check it is a SQUASHFS superblock */
1448c2ecf20Sopenharmony_ci	sb->s_magic = le32_to_cpu(sblk->s_magic);
1458c2ecf20Sopenharmony_ci	if (sb->s_magic != SQUASHFS_MAGIC) {
1468c2ecf20Sopenharmony_ci		if (!(fc->sb_flags & SB_SILENT))
1478c2ecf20Sopenharmony_ci			errorf(fc, "Can't find a SQUASHFS superblock on %pg",
1488c2ecf20Sopenharmony_ci			       sb->s_bdev);
1498c2ecf20Sopenharmony_ci		goto failed_mount;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* Check the MAJOR & MINOR versions and lookup compression type */
1538c2ecf20Sopenharmony_ci	msblk->decompressor = supported_squashfs_filesystem(
1548c2ecf20Sopenharmony_ci			fc,
1558c2ecf20Sopenharmony_ci			le16_to_cpu(sblk->s_major),
1568c2ecf20Sopenharmony_ci			le16_to_cpu(sblk->s_minor),
1578c2ecf20Sopenharmony_ci			le16_to_cpu(sblk->compression));
1588c2ecf20Sopenharmony_ci	if (msblk->decompressor == NULL)
1598c2ecf20Sopenharmony_ci		goto failed_mount;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Check the filesystem does not extend beyond the end of the
1628c2ecf20Sopenharmony_ci	   block device */
1638c2ecf20Sopenharmony_ci	msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
1648c2ecf20Sopenharmony_ci	if (msblk->bytes_used < 0 || msblk->bytes_used >
1658c2ecf20Sopenharmony_ci			i_size_read(sb->s_bdev->bd_inode))
1668c2ecf20Sopenharmony_ci		goto failed_mount;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* Check block size for sanity */
1698c2ecf20Sopenharmony_ci	msblk->block_size = le32_to_cpu(sblk->block_size);
1708c2ecf20Sopenharmony_ci	if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
1718c2ecf20Sopenharmony_ci		goto insanity;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/*
1748c2ecf20Sopenharmony_ci	 * Check the system page size is not larger than the filesystem
1758c2ecf20Sopenharmony_ci	 * block size (by default 128K).  This is currently not supported.
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci	if (PAGE_SIZE > msblk->block_size) {
1788c2ecf20Sopenharmony_ci		errorf(fc, "Page size > filesystem block size (%d).  This is "
1798c2ecf20Sopenharmony_ci		       "currently not supported!", msblk->block_size);
1808c2ecf20Sopenharmony_ci		goto failed_mount;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* Check block log for sanity */
1848c2ecf20Sopenharmony_ci	msblk->block_log = le16_to_cpu(sblk->block_log);
1858c2ecf20Sopenharmony_ci	if (msblk->block_log > SQUASHFS_FILE_MAX_LOG)
1868c2ecf20Sopenharmony_ci		goto failed_mount;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Check that block_size and block_log match */
1898c2ecf20Sopenharmony_ci	if (msblk->block_size != (1 << msblk->block_log))
1908c2ecf20Sopenharmony_ci		goto insanity;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Check the root inode for sanity */
1938c2ecf20Sopenharmony_ci	root_inode = le64_to_cpu(sblk->root_inode);
1948c2ecf20Sopenharmony_ci	if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
1958c2ecf20Sopenharmony_ci		goto insanity;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
1988c2ecf20Sopenharmony_ci	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
1998c2ecf20Sopenharmony_ci	msblk->inodes = le32_to_cpu(sblk->inodes);
2008c2ecf20Sopenharmony_ci	msblk->fragments = le32_to_cpu(sblk->fragments);
2018c2ecf20Sopenharmony_ci	msblk->ids = le16_to_cpu(sblk->no_ids);
2028c2ecf20Sopenharmony_ci	flags = le16_to_cpu(sblk->flags);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	TRACE("Found valid superblock on %pg\n", sb->s_bdev);
2058c2ecf20Sopenharmony_ci	TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
2068c2ecf20Sopenharmony_ci				? "un" : "");
2078c2ecf20Sopenharmony_ci	TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
2088c2ecf20Sopenharmony_ci				? "un" : "");
2098c2ecf20Sopenharmony_ci	TRACE("Filesystem size %lld bytes\n", msblk->bytes_used);
2108c2ecf20Sopenharmony_ci	TRACE("Block size %d\n", msblk->block_size);
2118c2ecf20Sopenharmony_ci	TRACE("Number of inodes %d\n", msblk->inodes);
2128c2ecf20Sopenharmony_ci	TRACE("Number of fragments %d\n", msblk->fragments);
2138c2ecf20Sopenharmony_ci	TRACE("Number of ids %d\n", msblk->ids);
2148c2ecf20Sopenharmony_ci	TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
2158c2ecf20Sopenharmony_ci	TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
2168c2ecf20Sopenharmony_ci	TRACE("sblk->fragment_table_start %llx\n",
2178c2ecf20Sopenharmony_ci		(u64) le64_to_cpu(sblk->fragment_table_start));
2188c2ecf20Sopenharmony_ci	TRACE("sblk->id_table_start %llx\n",
2198c2ecf20Sopenharmony_ci		(u64) le64_to_cpu(sblk->id_table_start));
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	sb->s_maxbytes = MAX_LFS_FILESIZE;
2228c2ecf20Sopenharmony_ci	sb->s_time_min = 0;
2238c2ecf20Sopenharmony_ci	sb->s_time_max = U32_MAX;
2248c2ecf20Sopenharmony_ci	sb->s_flags |= SB_RDONLY;
2258c2ecf20Sopenharmony_ci	sb->s_op = &squashfs_super_ops;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	err = -ENOMEM;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	msblk->block_cache = squashfs_cache_init("metadata",
2308c2ecf20Sopenharmony_ci			SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
2318c2ecf20Sopenharmony_ci	if (msblk->block_cache == NULL)
2328c2ecf20Sopenharmony_ci		goto failed_mount;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Allocate read_page block */
2358c2ecf20Sopenharmony_ci	msblk->read_page = squashfs_cache_init("data",
2368c2ecf20Sopenharmony_ci		squashfs_max_decompressors(), msblk->block_size);
2378c2ecf20Sopenharmony_ci	if (msblk->read_page == NULL) {
2388c2ecf20Sopenharmony_ci		errorf(fc, "Failed to allocate read_page block");
2398c2ecf20Sopenharmony_ci		goto failed_mount;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	msblk->stream = squashfs_decompressor_setup(sb, flags);
2438c2ecf20Sopenharmony_ci	if (IS_ERR(msblk->stream)) {
2448c2ecf20Sopenharmony_ci		err = PTR_ERR(msblk->stream);
2458c2ecf20Sopenharmony_ci		msblk->stream = NULL;
2468c2ecf20Sopenharmony_ci		goto insanity;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Handle xattrs */
2508c2ecf20Sopenharmony_ci	sb->s_xattr = squashfs_xattr_handlers;
2518c2ecf20Sopenharmony_ci	xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start);
2528c2ecf20Sopenharmony_ci	if (xattr_id_table_start == SQUASHFS_INVALID_BLK) {
2538c2ecf20Sopenharmony_ci		next_table = msblk->bytes_used;
2548c2ecf20Sopenharmony_ci		goto allocate_id_index_table;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Allocate and read xattr id lookup table */
2588c2ecf20Sopenharmony_ci	msblk->xattr_id_table = squashfs_read_xattr_id_table(sb,
2598c2ecf20Sopenharmony_ci		xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids);
2608c2ecf20Sopenharmony_ci	if (IS_ERR(msblk->xattr_id_table)) {
2618c2ecf20Sopenharmony_ci		errorf(fc, "unable to read xattr id index table");
2628c2ecf20Sopenharmony_ci		err = PTR_ERR(msblk->xattr_id_table);
2638c2ecf20Sopenharmony_ci		msblk->xattr_id_table = NULL;
2648c2ecf20Sopenharmony_ci		if (err != -ENOTSUPP)
2658c2ecf20Sopenharmony_ci			goto failed_mount;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	next_table = msblk->xattr_table;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ciallocate_id_index_table:
2708c2ecf20Sopenharmony_ci	/* Allocate and read id index table */
2718c2ecf20Sopenharmony_ci	msblk->id_table = squashfs_read_id_index_table(sb,
2728c2ecf20Sopenharmony_ci		le64_to_cpu(sblk->id_table_start), next_table, msblk->ids);
2738c2ecf20Sopenharmony_ci	if (IS_ERR(msblk->id_table)) {
2748c2ecf20Sopenharmony_ci		errorf(fc, "unable to read id index table");
2758c2ecf20Sopenharmony_ci		err = PTR_ERR(msblk->id_table);
2768c2ecf20Sopenharmony_ci		msblk->id_table = NULL;
2778c2ecf20Sopenharmony_ci		goto failed_mount;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	next_table = le64_to_cpu(msblk->id_table[0]);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Handle inode lookup table */
2828c2ecf20Sopenharmony_ci	lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
2838c2ecf20Sopenharmony_ci	if (lookup_table_start == SQUASHFS_INVALID_BLK)
2848c2ecf20Sopenharmony_ci		goto handle_fragments;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* Allocate and read inode lookup table */
2878c2ecf20Sopenharmony_ci	msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
2888c2ecf20Sopenharmony_ci		lookup_table_start, next_table, msblk->inodes);
2898c2ecf20Sopenharmony_ci	if (IS_ERR(msblk->inode_lookup_table)) {
2908c2ecf20Sopenharmony_ci		errorf(fc, "unable to read inode lookup table");
2918c2ecf20Sopenharmony_ci		err = PTR_ERR(msblk->inode_lookup_table);
2928c2ecf20Sopenharmony_ci		msblk->inode_lookup_table = NULL;
2938c2ecf20Sopenharmony_ci		goto failed_mount;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci	next_table = le64_to_cpu(msblk->inode_lookup_table[0]);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	sb->s_export_op = &squashfs_export_ops;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cihandle_fragments:
3008c2ecf20Sopenharmony_ci	fragments = msblk->fragments;
3018c2ecf20Sopenharmony_ci	if (fragments == 0)
3028c2ecf20Sopenharmony_ci		goto check_directory_table;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	msblk->fragment_cache = squashfs_cache_init("fragment",
3058c2ecf20Sopenharmony_ci		SQUASHFS_CACHED_FRAGMENTS, msblk->block_size);
3068c2ecf20Sopenharmony_ci	if (msblk->fragment_cache == NULL) {
3078c2ecf20Sopenharmony_ci		err = -ENOMEM;
3088c2ecf20Sopenharmony_ci		goto failed_mount;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* Allocate and read fragment index table */
3128c2ecf20Sopenharmony_ci	msblk->fragment_index = squashfs_read_fragment_index_table(sb,
3138c2ecf20Sopenharmony_ci		le64_to_cpu(sblk->fragment_table_start), next_table, fragments);
3148c2ecf20Sopenharmony_ci	if (IS_ERR(msblk->fragment_index)) {
3158c2ecf20Sopenharmony_ci		errorf(fc, "unable to read fragment index table");
3168c2ecf20Sopenharmony_ci		err = PTR_ERR(msblk->fragment_index);
3178c2ecf20Sopenharmony_ci		msblk->fragment_index = NULL;
3188c2ecf20Sopenharmony_ci		goto failed_mount;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	next_table = le64_to_cpu(msblk->fragment_index[0]);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cicheck_directory_table:
3238c2ecf20Sopenharmony_ci	/* Sanity check directory_table */
3248c2ecf20Sopenharmony_ci	if (msblk->directory_table > next_table) {
3258c2ecf20Sopenharmony_ci		err = -EINVAL;
3268c2ecf20Sopenharmony_ci		goto insanity;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* Sanity check inode_table */
3308c2ecf20Sopenharmony_ci	if (msblk->inode_table >= msblk->directory_table) {
3318c2ecf20Sopenharmony_ci		err = -EINVAL;
3328c2ecf20Sopenharmony_ci		goto insanity;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* allocate root */
3368c2ecf20Sopenharmony_ci	root = new_inode(sb);
3378c2ecf20Sopenharmony_ci	if (!root) {
3388c2ecf20Sopenharmony_ci		err = -ENOMEM;
3398c2ecf20Sopenharmony_ci		goto failed_mount;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	err = squashfs_read_inode(root, root_inode);
3438c2ecf20Sopenharmony_ci	if (err) {
3448c2ecf20Sopenharmony_ci		make_bad_inode(root);
3458c2ecf20Sopenharmony_ci		iput(root);
3468c2ecf20Sopenharmony_ci		goto failed_mount;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	insert_inode_hash(root);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	sb->s_root = d_make_root(root);
3518c2ecf20Sopenharmony_ci	if (sb->s_root == NULL) {
3528c2ecf20Sopenharmony_ci		ERROR("Root inode create failed\n");
3538c2ecf20Sopenharmony_ci		err = -ENOMEM;
3548c2ecf20Sopenharmony_ci		goto failed_mount;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	TRACE("Leaving squashfs_fill_super\n");
3588c2ecf20Sopenharmony_ci	kfree(sblk);
3598c2ecf20Sopenharmony_ci	return 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ciinsanity:
3628c2ecf20Sopenharmony_ci	errorf(fc, "squashfs image failed sanity check");
3638c2ecf20Sopenharmony_cifailed_mount:
3648c2ecf20Sopenharmony_ci	squashfs_cache_delete(msblk->block_cache);
3658c2ecf20Sopenharmony_ci	squashfs_cache_delete(msblk->fragment_cache);
3668c2ecf20Sopenharmony_ci	squashfs_cache_delete(msblk->read_page);
3678c2ecf20Sopenharmony_ci	squashfs_decompressor_destroy(msblk);
3688c2ecf20Sopenharmony_ci	kfree(msblk->inode_lookup_table);
3698c2ecf20Sopenharmony_ci	kfree(msblk->fragment_index);
3708c2ecf20Sopenharmony_ci	kfree(msblk->id_table);
3718c2ecf20Sopenharmony_ci	kfree(msblk->xattr_id_table);
3728c2ecf20Sopenharmony_ci	kfree(sb->s_fs_info);
3738c2ecf20Sopenharmony_ci	sb->s_fs_info = NULL;
3748c2ecf20Sopenharmony_ci	kfree(sblk);
3758c2ecf20Sopenharmony_ci	return err;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic int squashfs_get_tree(struct fs_context *fc)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	return get_tree_bdev(fc, squashfs_fill_super);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int squashfs_reconfigure(struct fs_context *fc)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	sync_filesystem(fc->root->d_sb);
3868c2ecf20Sopenharmony_ci	fc->sb_flags |= SB_RDONLY;
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic const struct fs_context_operations squashfs_context_ops = {
3918c2ecf20Sopenharmony_ci	.get_tree	= squashfs_get_tree,
3928c2ecf20Sopenharmony_ci	.reconfigure	= squashfs_reconfigure,
3938c2ecf20Sopenharmony_ci};
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int squashfs_init_fs_context(struct fs_context *fc)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	fc->ops = &squashfs_context_ops;
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
4048c2ecf20Sopenharmony_ci	u64 id = huge_encode_dev(dentry->d_sb->s_bdev->bd_dev);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	TRACE("Entered squashfs_statfs\n");
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	buf->f_type = SQUASHFS_MAGIC;
4098c2ecf20Sopenharmony_ci	buf->f_bsize = msblk->block_size;
4108c2ecf20Sopenharmony_ci	buf->f_blocks = ((msblk->bytes_used - 1) >> msblk->block_log) + 1;
4118c2ecf20Sopenharmony_ci	buf->f_bfree = buf->f_bavail = 0;
4128c2ecf20Sopenharmony_ci	buf->f_files = msblk->inodes;
4138c2ecf20Sopenharmony_ci	buf->f_ffree = 0;
4148c2ecf20Sopenharmony_ci	buf->f_namelen = SQUASHFS_NAME_LEN;
4158c2ecf20Sopenharmony_ci	buf->f_fsid = u64_to_fsid(id);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic void squashfs_put_super(struct super_block *sb)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	if (sb->s_fs_info) {
4248c2ecf20Sopenharmony_ci		struct squashfs_sb_info *sbi = sb->s_fs_info;
4258c2ecf20Sopenharmony_ci		squashfs_cache_delete(sbi->block_cache);
4268c2ecf20Sopenharmony_ci		squashfs_cache_delete(sbi->fragment_cache);
4278c2ecf20Sopenharmony_ci		squashfs_cache_delete(sbi->read_page);
4288c2ecf20Sopenharmony_ci		squashfs_decompressor_destroy(sbi);
4298c2ecf20Sopenharmony_ci		kfree(sbi->id_table);
4308c2ecf20Sopenharmony_ci		kfree(sbi->fragment_index);
4318c2ecf20Sopenharmony_ci		kfree(sbi->meta_index);
4328c2ecf20Sopenharmony_ci		kfree(sbi->inode_lookup_table);
4338c2ecf20Sopenharmony_ci		kfree(sbi->xattr_id_table);
4348c2ecf20Sopenharmony_ci		kfree(sb->s_fs_info);
4358c2ecf20Sopenharmony_ci		sb->s_fs_info = NULL;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic struct kmem_cache *squashfs_inode_cachep;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic void init_once(void *foo)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct squashfs_inode_info *ei = foo;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	inode_init_once(&ei->vfs_inode);
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int __init init_inodecache(void)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
4538c2ecf20Sopenharmony_ci		sizeof(struct squashfs_inode_info), 0,
4548c2ecf20Sopenharmony_ci		SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
4558c2ecf20Sopenharmony_ci		init_once);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return squashfs_inode_cachep ? 0 : -ENOMEM;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic void destroy_inodecache(void)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	/*
4648c2ecf20Sopenharmony_ci	 * Make sure all delayed rcu free inodes are flushed before we
4658c2ecf20Sopenharmony_ci	 * destroy cache.
4668c2ecf20Sopenharmony_ci	 */
4678c2ecf20Sopenharmony_ci	rcu_barrier();
4688c2ecf20Sopenharmony_ci	kmem_cache_destroy(squashfs_inode_cachep);
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int __init init_squashfs_fs(void)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	int err = init_inodecache();
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (err)
4778c2ecf20Sopenharmony_ci		return err;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	err = register_filesystem(&squashfs_fs_type);
4808c2ecf20Sopenharmony_ci	if (err) {
4818c2ecf20Sopenharmony_ci		destroy_inodecache();
4828c2ecf20Sopenharmony_ci		return err;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	pr_info("version 4.0 (2009/01/31) Phillip Lougher\n");
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	return 0;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic void __exit exit_squashfs_fs(void)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	unregister_filesystem(&squashfs_fs_type);
4948c2ecf20Sopenharmony_ci	destroy_inodecache();
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic struct inode *squashfs_alloc_inode(struct super_block *sb)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct squashfs_inode_info *ei =
5018c2ecf20Sopenharmony_ci		kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return ei ? &ei->vfs_inode : NULL;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void squashfs_free_inode(struct inode *inode)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode));
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic struct file_system_type squashfs_fs_type = {
5138c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
5148c2ecf20Sopenharmony_ci	.name = "squashfs",
5158c2ecf20Sopenharmony_ci	.init_fs_context = squashfs_init_fs_context,
5168c2ecf20Sopenharmony_ci	.kill_sb = kill_block_super,
5178c2ecf20Sopenharmony_ci	.fs_flags = FS_REQUIRES_DEV
5188c2ecf20Sopenharmony_ci};
5198c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("squashfs");
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic const struct super_operations squashfs_super_ops = {
5228c2ecf20Sopenharmony_ci	.alloc_inode = squashfs_alloc_inode,
5238c2ecf20Sopenharmony_ci	.free_inode = squashfs_free_inode,
5248c2ecf20Sopenharmony_ci	.statfs = squashfs_statfs,
5258c2ecf20Sopenharmony_ci	.put_super = squashfs_put_super,
5268c2ecf20Sopenharmony_ci};
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cimodule_init(init_squashfs_fs);
5298c2ecf20Sopenharmony_cimodule_exit(exit_squashfs_fs);
5308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem");
5318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Phillip Lougher <phillip@squashfs.org.uk>");
5328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
533