| 1 | |
| 2 | |
| 3 | * Squashfs - a compressed read only filesystem for Linux
|
| 4 | *
|
| 5 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
|
| 6 | * Phillip Lougher <[email protected]>
|
| 7 | *
|
| 8 | * super.c
|
| 9 |
|
| 10 |
|
| 11 | |
| 12 | * This file implements code to read the superblock, read and initialise
|
| 13 | * in-memory structures at mount time, and all the VFS glue code to register
|
| 14 | * the filesystem.
|
| 15 |
|
| 16 |
|
| 17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 18 |
|
| 19 | #include <linux/blkdev.h>
|
| 20 | #include <linux/fs.h>
|
| 21 | #include <linux/fs_context.h>
|
| 22 | #include <linux/fs_parser.h>
|
| 23 | #include <linux/vfs.h>
|
| 24 | #include <linux/slab.h>
|
| 25 | #include <linux/mutex.h>
|
| 26 | #include <linux/seq_file.h>
|
| 27 | #include <linux/pagemap.h>
|
| 28 | #include <linux/init.h>
|
| 29 | #include <linux/module.h>
|
| 30 | #include <linux/magic.h>
|
| 31 | #include <linux/xattr.h>
|
| 32 |
|
| 33 | #include "squashfs_fs.h"
|
| 34 | #include "squashfs_fs_sb.h"
|
| 35 | #include "squashfs_fs_i.h"
|
| 36 | #include "squashfs.h"
|
| 37 | #include "decompressor.h"
|
| 38 | #include "xattr.h"
|
| 39 |
|
| 40 | static struct file_system_type squashfs_fs_type;
|
| 41 | static const struct super_operations squashfs_super_ops;
|
| 42 |
|
| 43 | enum Opt_errors {
|
| 44 | Opt_errors_continue,
|
| 45 | Opt_errors_panic,
|
| 46 | };
|
| 47 |
|
| 48 | enum squashfs_param {
|
| 49 | Opt_errors,
|
| 50 | Opt_threads,
|
| 51 | };
|
| 52 |
|
| 53 | struct squashfs_mount_opts {
|
| 54 | enum Opt_errors errors;
|
| 55 | const struct squashfs_decompressor_thread_ops *thread_ops;
|
| 56 | int thread_num;
|
| 57 | };
|
| 58 |
|
| 59 | static const struct constant_table squashfs_param_errors[] = {
|
| 60 | {"continue", Opt_errors_continue },
|
| 61 | {"panic", Opt_errors_panic },
|
| 62 | {}
|
| 63 | };
|
| 64 |
|
| 65 | static const struct fs_parameter_spec squashfs_fs_parameters[] = {
|
| 66 | fsparam_enum("errors", Opt_errors, squashfs_param_errors),
|
| 67 | fsparam_string("threads", Opt_threads),
|
| 68 | {}
|
| 69 | };
|
| 70 |
|
| 71 |
|
| 72 | static int squashfs_parse_param_threads_str(const char *str, struct squashfs_mount_opts *opts)
|
| 73 | {
|
| 74 | #ifdef CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT
|
| 75 | if (strcmp(str, "single") == 0) {
|
| 76 | opts->thread_ops = &squashfs_decompressor_single;
|
| 77 | return 0;
|
| 78 | }
|
| 79 | if (strcmp(str, "multi") == 0) {
|
| 80 | opts->thread_ops = &squashfs_decompressor_multi;
|
| 81 | return 0;
|
| 82 | }
|
| 83 | if (strcmp(str, "percpu") == 0) {
|
| 84 | opts->thread_ops = &squashfs_decompressor_percpu;
|
| 85 | return 0;
|
| 86 | }
|
| 87 | #endif
|
| 88 | return -EINVAL;
|
| 89 | }
|
| 90 |
|
| 91 | static int squashfs_parse_param_threads_num(const char *str, struct squashfs_mount_opts *opts)
|
| 92 | {
|
| 93 | #ifdef CONFIG_SQUASHFS_MOUNT_DECOMP_THREADS
|
| 94 | int ret;
|
| 95 | unsigned long num;
|
| 96 |
|
| 97 | ret = kstrtoul(str, 0, &num);
|
| 98 | if (ret != 0)
|
| 99 | return -EINVAL;
|
| 100 | if (num > 1) {
|
| 101 | opts->thread_ops = &squashfs_decompressor_multi;
|
| 102 | if (num > opts->thread_ops->max_decompressors())
|
| 103 | return -EINVAL;
|
| 104 | opts->thread_num = (int)num;
|
| 105 | return 0;
|
| 106 | }
|
| 107 | #ifdef CONFIG_SQUASHFS_DECOMP_SINGLE
|
| 108 | if (num == 1) {
|
| 109 | opts->thread_ops = &squashfs_decompressor_single;
|
| 110 | opts->thread_num = 1;
|
| 111 | return 0;
|
| 112 | }
|
| 113 | #endif
|
| 114 | #endif
|
| 115 | return -EINVAL;
|
| 116 | }
|
| 117 |
|
| 118 | static int squashfs_parse_param_threads(const char *str, struct squashfs_mount_opts *opts)
|
| 119 | {
|
| 120 | int ret = squashfs_parse_param_threads_str(str, opts);
|
| 121 |
|
| 122 | if (ret == 0)
|
| 123 | return ret;
|
| 124 | return squashfs_parse_param_threads_num(str, opts);
|
| 125 | }
|
| 126 |
|
| 127 | static int squashfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
| 128 | {
|
| 129 | struct squashfs_mount_opts *opts = fc->fs_private;
|
| 130 | struct fs_parse_result result;
|
| 131 | int opt;
|
| 132 |
|
| 133 | opt = fs_parse(fc, squashfs_fs_parameters, param, &result);
|
| 134 | if (opt < 0)
|
| 135 | return opt;
|
| 136 |
|
| 137 | switch (opt) {
|
| 138 | case Opt_errors:
|
| 139 | opts->errors = result.uint_32;
|
| 140 | break;
|
| 141 | case Opt_threads:
|
| 142 | if (squashfs_parse_param_threads(param->string, opts) != 0)
|
| 143 | return -EINVAL;
|
| 144 | break;
|
| 145 | default:
|
| 146 | return -EINVAL;
|
| 147 | }
|
| 148 |
|
| 149 | return 0;
|
| 150 | }
|
| 151 |
|
| 152 | static const struct squashfs_decompressor *supported_squashfs_filesystem(
|
| 153 | struct fs_context *fc,
|
| 154 | short major, short minor, short id)
|
| 155 | {
|
| 156 | const struct squashfs_decompressor *decompressor;
|
| 157 |
|
| 158 | if (major < SQUASHFS_MAJOR) {
|
| 159 | errorf(fc, "Major/Minor mismatch, older Squashfs %d.%d "
|
| 160 | "filesystems are unsupported", major, minor);
|
| 161 | return NULL;
|
| 162 | } else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
|
| 163 | errorf(fc, "Major/Minor mismatch, trying to mount newer "
|
| 164 | "%d.%d filesystem", major, minor);
|
| 165 | errorf(fc, "Please update your kernel");
|
| 166 | return NULL;
|
| 167 | }
|
| 168 |
|
| 169 | decompressor = squashfs_lookup_decompressor(id);
|
| 170 | if (!decompressor->supported) {
|
| 171 | errorf(fc, "Filesystem uses \"%s\" compression. This is not supported",
|
| 172 | decompressor->name);
|
| 173 | return NULL;
|
| 174 | }
|
| 175 |
|
| 176 | return decompressor;
|
| 177 | }
|
| 178 |
|
| 179 |
|
| 180 | static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
| 181 | {
|
| 182 | struct squashfs_mount_opts *opts = fc->fs_private;
|
| 183 | struct squashfs_sb_info *msblk;
|
| 184 | struct squashfs_super_block *sblk = NULL;
|
| 185 | struct inode *root;
|
| 186 | long long root_inode;
|
| 187 | unsigned short flags;
|
| 188 | unsigned int fragments;
|
| 189 | u64 lookup_table_start, xattr_id_table_start, next_table;
|
| 190 | int err, devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
|
| 191 |
|
| 192 | TRACE("Entered squashfs_fill_superblock\n");
|
| 193 |
|
| 194 | if (!devblksize) {
|
| 195 | errorf(fc, "squashfs: unable to set blocksize\n");
|
| 196 | return -EINVAL;
|
| 197 | }
|
| 198 |
|
| 199 | sb->s_fs_info = kzalloc_obj(*msblk);
|
| 200 | if (sb->s_fs_info == NULL) {
|
| 201 | ERROR("Failed to allocate squashfs_sb_info\n");
|
| 202 | return -ENOMEM;
|
| 203 | }
|
| 204 | msblk = sb->s_fs_info;
|
| 205 | msblk->thread_ops = opts->thread_ops;
|
| 206 |
|
| 207 | msblk->panic_on_errors = (opts->errors == Opt_errors_panic);
|
| 208 |
|
| 209 | msblk->devblksize = devblksize;
|
| 210 | msblk->devblksize_log2 = ffz(~msblk->devblksize);
|
| 211 |
|
| 212 | mutex_init(&msblk->meta_index_mutex);
|
| 213 |
|
| 214 | |
| 215 | * msblk->bytes_used is checked in squashfs_read_table to ensure reads
|
| 216 | * are not beyond filesystem end. But as we're using
|
| 217 | * squashfs_read_table here to read the superblock (including the value
|
| 218 | * of bytes_used) we need to set it to an initial sensible dummy value
|
| 219 |
|
| 220 | msblk->bytes_used = sizeof(*sblk);
|
| 221 | sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk));
|
| 222 |
|
| 223 | if (IS_ERR(sblk)) {
|
| 224 | errorf(fc, "unable to read squashfs_super_block");
|
| 225 | err = PTR_ERR(sblk);
|
| 226 | sblk = NULL;
|
| 227 | goto failed_mount;
|
| 228 | }
|
| 229 |
|
| 230 | err = -EINVAL;
|
| 231 |
|
| 232 |
|
| 233 | sb->s_magic = le32_to_cpu(sblk->s_magic);
|
| 234 | if (sb->s_magic != SQUASHFS_MAGIC) {
|
| 235 | if (!(fc->sb_flags & SB_SILENT))
|
| 236 | errorf(fc, "Can't find a SQUASHFS superblock on %pg",
|
| 237 | sb->s_bdev);
|
| 238 | goto failed_mount;
|
| 239 | }
|
| 240 |
|
| 241 | if (opts->thread_num == 0) {
|
| 242 | msblk->max_thread_num = msblk->thread_ops->max_decompressors();
|
| 243 | } else {
|
| 244 | msblk->max_thread_num = opts->thread_num;
|
| 245 | }
|
| 246 |
|
| 247 |
|
| 248 | msblk->decompressor = supported_squashfs_filesystem(
|
| 249 | fc,
|
| 250 | le16_to_cpu(sblk->s_major),
|
| 251 | le16_to_cpu(sblk->s_minor),
|
| 252 | le16_to_cpu(sblk->compression));
|
| 253 | if (msblk->decompressor == NULL)
|
| 254 | goto failed_mount;
|
| 255 |
|
| 256 | |
| 257 | block device
|
| 258 | msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
|
| 259 | if (msblk->bytes_used < 0 ||
|
| 260 | msblk->bytes_used > bdev_nr_bytes(sb->s_bdev))
|
| 261 | goto failed_mount;
|
| 262 |
|
| 263 |
|
| 264 | msblk->block_size = le32_to_cpu(sblk->block_size);
|
| 265 | if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
|
| 266 | goto insanity;
|
| 267 |
|
| 268 | |
| 269 | * Check the system page size is not larger than the filesystem
|
| 270 | * block size (by default 128K). This is currently not supported.
|
| 271 |
|
| 272 | if (PAGE_SIZE > msblk->block_size) {
|
| 273 | errorf(fc, "Page size > filesystem block size (%d). This is "
|
| 274 | "currently not supported!", msblk->block_size);
|
| 275 | goto failed_mount;
|
| 276 | }
|
| 277 |
|
| 278 |
|
| 279 | msblk->block_log = le16_to_cpu(sblk->block_log);
|
| 280 | if (msblk->block_log > SQUASHFS_FILE_MAX_LOG)
|
| 281 | goto failed_mount;
|
| 282 |
|
| 283 |
|
| 284 | if (msblk->block_size != (1 << msblk->block_log))
|
| 285 | goto insanity;
|
| 286 |
|
| 287 |
|
| 288 | root_inode = le64_to_cpu(sblk->root_inode);
|
| 289 | if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
|
| 290 | goto insanity;
|
| 291 |
|
| 292 | msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
|
| 293 | msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
|
| 294 | msblk->inodes = le32_to_cpu(sblk->inodes);
|
| 295 | msblk->fragments = le32_to_cpu(sblk->fragments);
|
| 296 | msblk->ids = le16_to_cpu(sblk->no_ids);
|
| 297 | flags = le16_to_cpu(sblk->flags);
|
| 298 |
|
| 299 | TRACE("Found valid superblock on %pg\n", sb->s_bdev);
|
| 300 | TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
|
| 301 | ? "un" : "");
|
| 302 | TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
|
| 303 | ? "un" : "");
|
| 304 | TRACE("Filesystem size %lld bytes\n", msblk->bytes_used);
|
| 305 | TRACE("Block size %d\n", msblk->block_size);
|
| 306 | TRACE("Number of inodes %d\n", msblk->inodes);
|
| 307 | TRACE("Number of fragments %d\n", msblk->fragments);
|
| 308 | TRACE("Number of ids %d\n", msblk->ids);
|
| 309 | TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
|
| 310 | TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
|
| 311 | TRACE("sblk->fragment_table_start %llx\n",
|
| 312 | (u64) le64_to_cpu(sblk->fragment_table_start));
|
| 313 | TRACE("sblk->id_table_start %llx\n",
|
| 314 | (u64) le64_to_cpu(sblk->id_table_start));
|
| 315 |
|
| 316 | sb->s_maxbytes = MAX_LFS_FILESIZE;
|
| 317 | sb->s_time_min = 0;
|
| 318 | sb->s_time_max = U32_MAX;
|
| 319 | sb->s_flags |= SB_RDONLY;
|
| 320 | sb->s_op = &squashfs_super_ops;
|
| 321 |
|
| 322 | msblk->block_cache = squashfs_cache_init("metadata",
|
| 323 | SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
|
| 324 | if (IS_ERR(msblk->block_cache)) {
|
| 325 | err = PTR_ERR(msblk->block_cache);
|
| 326 | goto failed_mount;
|
| 327 | }
|
| 328 |
|
| 329 |
|
| 330 | msblk->read_page = squashfs_cache_init("data",
|
| 331 | SQUASHFS_READ_PAGES, msblk->block_size);
|
| 332 | if (IS_ERR(msblk->read_page)) {
|
| 333 | errorf(fc, "Failed to allocate read_page block");
|
| 334 | err = PTR_ERR(msblk->read_page);
|
| 335 | goto failed_mount;
|
| 336 | }
|
| 337 |
|
| 338 | if (msblk->devblksize == PAGE_SIZE) {
|
| 339 | struct inode *cache = new_inode(sb);
|
| 340 |
|
| 341 | if (cache == NULL) {
|
| 342 | err = -ENOMEM;
|
| 343 | goto failed_mount;
|
| 344 | }
|
| 345 |
|
| 346 | set_nlink(cache, 1);
|
| 347 | cache->i_size = OFFSET_MAX;
|
| 348 | mapping_set_gfp_mask(cache->i_mapping, GFP_NOFS);
|
| 349 |
|
| 350 | msblk->cache_mapping = cache->i_mapping;
|
| 351 | }
|
| 352 |
|
| 353 | msblk->stream = squashfs_decompressor_setup(sb, flags);
|
| 354 | if (IS_ERR(msblk->stream)) {
|
| 355 | err = PTR_ERR(msblk->stream);
|
| 356 | msblk->stream = NULL;
|
| 357 | goto insanity;
|
| 358 | }
|
| 359 |
|
| 360 |
|
| 361 | sb->s_xattr = squashfs_xattr_handlers;
|
| 362 | xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start);
|
| 363 | if (xattr_id_table_start == SQUASHFS_INVALID_BLK) {
|
| 364 | next_table = msblk->bytes_used;
|
| 365 | goto allocate_id_index_table;
|
| 366 | }
|
| 367 |
|
| 368 |
|
| 369 | msblk->xattr_id_table = squashfs_read_xattr_id_table(sb,
|
| 370 | xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids);
|
| 371 | if (IS_ERR(msblk->xattr_id_table)) {
|
| 372 | errorf(fc, "unable to read xattr id index table");
|
| 373 | err = PTR_ERR(msblk->xattr_id_table);
|
| 374 | msblk->xattr_id_table = NULL;
|
| 375 | if (err != -ENOTSUPP)
|
| 376 | goto failed_mount;
|
| 377 | }
|
| 378 | next_table = msblk->xattr_table;
|
| 379 |
|
| 380 | allocate_id_index_table:
|
| 381 |
|
| 382 | msblk->id_table = squashfs_read_id_index_table(sb,
|
| 383 | le64_to_cpu(sblk->id_table_start), next_table, msblk->ids);
|
| 384 | if (IS_ERR(msblk->id_table)) {
|
| 385 | errorf(fc, "unable to read id index table");
|
| 386 | err = PTR_ERR(msblk->id_table);
|
| 387 | msblk->id_table = NULL;
|
| 388 | goto failed_mount;
|
| 389 | }
|
| 390 | next_table = le64_to_cpu(msblk->id_table[0]);
|
| 391 |
|
| 392 |
|
| 393 | lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
|
| 394 | if (lookup_table_start == SQUASHFS_INVALID_BLK)
|
| 395 | goto handle_fragments;
|
| 396 |
|
| 397 |
|
| 398 | msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
|
| 399 | lookup_table_start, next_table, msblk->inodes);
|
| 400 | if (IS_ERR(msblk->inode_lookup_table)) {
|
| 401 | errorf(fc, "unable to read inode lookup table");
|
| 402 | err = PTR_ERR(msblk->inode_lookup_table);
|
| 403 | msblk->inode_lookup_table = NULL;
|
| 404 | goto failed_mount;
|
| 405 | }
|
| 406 | next_table = le64_to_cpu(msblk->inode_lookup_table[0]);
|
| 407 |
|
| 408 | sb->s_export_op = &squashfs_export_ops;
|
| 409 |
|
| 410 | handle_fragments:
|
| 411 | fragments = msblk->fragments;
|
| 412 | if (fragments == 0)
|
| 413 | goto check_directory_table;
|
| 414 |
|
| 415 | msblk->fragment_cache = squashfs_cache_init("fragment",
|
| 416 | min(SQUASHFS_CACHED_FRAGMENTS, fragments), msblk->block_size);
|
| 417 | if (IS_ERR(msblk->fragment_cache)) {
|
| 418 | err = PTR_ERR(msblk->fragment_cache);
|
| 419 | goto failed_mount;
|
| 420 | }
|
| 421 |
|
| 422 |
|
| 423 | msblk->fragment_index = squashfs_read_fragment_index_table(sb,
|
| 424 | le64_to_cpu(sblk->fragment_table_start), next_table, fragments);
|
| 425 | if (IS_ERR(msblk->fragment_index)) {
|
| 426 | errorf(fc, "unable to read fragment index table");
|
| 427 | err = PTR_ERR(msblk->fragment_index);
|
| 428 | msblk->fragment_index = NULL;
|
| 429 | goto failed_mount;
|
| 430 | }
|
| 431 | next_table = le64_to_cpu(msblk->fragment_index[0]);
|
| 432 |
|
| 433 | check_directory_table:
|
| 434 |
|
| 435 | if (msblk->directory_table > next_table) {
|
| 436 | err = -EINVAL;
|
| 437 | goto insanity;
|
| 438 | }
|
| 439 |
|
| 440 |
|
| 441 | if (msblk->inode_table >= msblk->directory_table) {
|
| 442 | err = -EINVAL;
|
| 443 | goto insanity;
|
| 444 | }
|
| 445 |
|
| 446 |
|
| 447 | root = new_inode(sb);
|
| 448 | if (!root) {
|
| 449 | err = -ENOMEM;
|
| 450 | goto failed_mount;
|
| 451 | }
|
| 452 |
|
| 453 | err = squashfs_read_inode(root, root_inode);
|
| 454 | if (err) {
|
| 455 | make_bad_inode(root);
|
| 456 | iput(root);
|
| 457 | goto failed_mount;
|
| 458 | }
|
| 459 | insert_inode_hash(root);
|
| 460 |
|
| 461 | sb->s_root = d_make_root(root);
|
| 462 | if (sb->s_root == NULL) {
|
| 463 | ERROR("Root inode create failed\n");
|
| 464 | err = -ENOMEM;
|
| 465 | goto failed_mount;
|
| 466 | }
|
| 467 |
|
| 468 | TRACE("Leaving squashfs_fill_super\n");
|
| 469 | kfree(sblk);
|
| 470 | return 0;
|
| 471 |
|
| 472 | insanity:
|
| 473 | errorf(fc, "squashfs image failed sanity check");
|
| 474 | failed_mount:
|
| 475 | squashfs_cache_delete(msblk->block_cache);
|
| 476 | squashfs_cache_delete(msblk->fragment_cache);
|
| 477 | squashfs_cache_delete(msblk->read_page);
|
| 478 | if (msblk->cache_mapping)
|
| 479 | iput(msblk->cache_mapping->host);
|
| 480 | msblk->thread_ops->destroy(msblk);
|
| 481 | kfree(msblk->inode_lookup_table);
|
| 482 | kfree(msblk->fragment_index);
|
| 483 | kfree(msblk->id_table);
|
| 484 | kfree(msblk->xattr_id_table);
|
| 485 | kfree(sb->s_fs_info);
|
| 486 | sb->s_fs_info = NULL;
|
| 487 | kfree(sblk);
|
| 488 | return err;
|
| 489 | }
|
| 490 |
|
| 491 | static int squashfs_get_tree(struct fs_context *fc)
|
| 492 | {
|
| 493 | return get_tree_bdev(fc, squashfs_fill_super);
|
| 494 | }
|
| 495 |
|
| 496 | static int squashfs_reconfigure(struct fs_context *fc)
|
| 497 | {
|
| 498 | struct super_block *sb = fc->root->d_sb;
|
| 499 | struct squashfs_sb_info *msblk = sb->s_fs_info;
|
| 500 | struct squashfs_mount_opts *opts = fc->fs_private;
|
| 501 |
|
| 502 | sync_filesystem(fc->root->d_sb);
|
| 503 | fc->sb_flags |= SB_RDONLY;
|
| 504 |
|
| 505 | msblk->panic_on_errors = (opts->errors == Opt_errors_panic);
|
| 506 |
|
| 507 | return 0;
|
| 508 | }
|
| 509 |
|
| 510 | static void squashfs_free_fs_context(struct fs_context *fc)
|
| 511 | {
|
| 512 | kfree(fc->fs_private);
|
| 513 | }
|
| 514 |
|
| 515 | static const struct fs_context_operations squashfs_context_ops = {
|
| 516 | .get_tree = squashfs_get_tree,
|
| 517 | .free = squashfs_free_fs_context,
|
| 518 | .parse_param = squashfs_parse_param,
|
| 519 | .reconfigure = squashfs_reconfigure,
|
| 520 | };
|
| 521 |
|
| 522 | static int squashfs_show_options(struct seq_file *s, struct dentry *root)
|
| 523 | {
|
| 524 | struct super_block *sb = root->d_sb;
|
| 525 | struct squashfs_sb_info *msblk = sb->s_fs_info;
|
| 526 |
|
| 527 | if (msblk->panic_on_errors)
|
| 528 | seq_puts(s, ",errors=panic");
|
| 529 | else
|
| 530 | seq_puts(s, ",errors=continue");
|
| 531 |
|
| 532 | #ifdef CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT
|
| 533 | if (msblk->thread_ops == &squashfs_decompressor_single) {
|
| 534 | seq_puts(s, ",threads=single");
|
| 535 | return 0;
|
| 536 | }
|
| 537 | if (msblk->thread_ops == &squashfs_decompressor_percpu) {
|
| 538 | seq_puts(s, ",threads=percpu");
|
| 539 | return 0;
|
| 540 | }
|
| 541 | #endif
|
| 542 | #ifdef CONFIG_SQUASHFS_MOUNT_DECOMP_THREADS
|
| 543 | seq_printf(s, ",threads=%d", msblk->max_thread_num);
|
| 544 | #endif
|
| 545 | return 0;
|
| 546 | }
|
| 547 |
|
| 548 | static int squashfs_init_fs_context(struct fs_context *fc)
|
| 549 | {
|
| 550 | struct squashfs_mount_opts *opts;
|
| 551 |
|
| 552 | opts = kzalloc_obj(*opts);
|
| 553 | if (!opts)
|
| 554 | return -ENOMEM;
|
| 555 |
|
| 556 | #ifdef CONFIG_SQUASHFS_DECOMP_SINGLE
|
| 557 | opts->thread_ops = &squashfs_decompressor_single;
|
| 558 | #elif defined(CONFIG_SQUASHFS_DECOMP_MULTI)
|
| 559 | opts->thread_ops = &squashfs_decompressor_multi;
|
| 560 | #elif defined(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU)
|
| 561 | opts->thread_ops = &squashfs_decompressor_percpu;
|
| 562 | #else
|
| 563 | #error "fail: unknown squashfs decompression thread mode?"
|
| 564 | #endif
|
| 565 | opts->thread_num = 0;
|
| 566 | fc->fs_private = opts;
|
| 567 | fc->ops = &squashfs_context_ops;
|
| 568 | return 0;
|
| 569 | }
|
| 570 |
|
| 571 | static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
| 572 | {
|
| 573 | struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
|
| 574 | u64 id = huge_encode_dev(dentry->d_sb->s_bdev->bd_dev);
|
| 575 |
|
| 576 | TRACE("Entered squashfs_statfs\n");
|
| 577 |
|
| 578 | buf->f_type = SQUASHFS_MAGIC;
|
| 579 | buf->f_bsize = msblk->block_size;
|
| 580 | buf->f_blocks = ((msblk->bytes_used - 1) >> msblk->block_log) + 1;
|
| 581 | buf->f_bfree = buf->f_bavail = 0;
|
| 582 | buf->f_files = msblk->inodes;
|
| 583 | buf->f_ffree = 0;
|
| 584 | buf->f_namelen = SQUASHFS_NAME_LEN;
|
| 585 | buf->f_fsid = u64_to_fsid(id);
|
| 586 |
|
| 587 | return 0;
|
| 588 | }
|
| 589 |
|
| 590 |
|
| 591 | static void squashfs_put_super(struct super_block *sb)
|
| 592 | {
|
| 593 | if (sb->s_fs_info) {
|
| 594 | struct squashfs_sb_info *sbi = sb->s_fs_info;
|
| 595 | squashfs_cache_delete(sbi->block_cache);
|
| 596 | squashfs_cache_delete(sbi->fragment_cache);
|
| 597 | squashfs_cache_delete(sbi->read_page);
|
| 598 | if (sbi->cache_mapping)
|
| 599 | iput(sbi->cache_mapping->host);
|
| 600 | sbi->thread_ops->destroy(sbi);
|
| 601 | kfree(sbi->id_table);
|
| 602 | kfree(sbi->fragment_index);
|
| 603 | kfree(sbi->meta_index);
|
| 604 | kfree(sbi->inode_lookup_table);
|
| 605 | kfree(sbi->xattr_id_table);
|
| 606 | kfree(sb->s_fs_info);
|
| 607 | sb->s_fs_info = NULL;
|
| 608 | }
|
| 609 | }
|
| 610 |
|
| 611 | static struct kmem_cache *squashfs_inode_cachep;
|
| 612 |
|
| 613 |
|
| 614 | static void init_once(void *foo)
|
| 615 | {
|
| 616 | struct squashfs_inode_info *ei = foo;
|
| 617 |
|
| 618 | inode_init_once(&ei->vfs_inode);
|
| 619 | }
|
| 620 |
|
| 621 |
|
| 622 | static int __init init_inodecache(void)
|
| 623 | {
|
| 624 | squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
|
| 625 | sizeof(struct squashfs_inode_info), 0,
|
| 626 | SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
|
| 627 | init_once);
|
| 628 |
|
| 629 | return squashfs_inode_cachep ? 0 : -ENOMEM;
|
| 630 | }
|
| 631 |
|
| 632 |
|
| 633 | static void destroy_inodecache(void)
|
| 634 | {
|
| 635 | |
| 636 | * Make sure all delayed rcu free inodes are flushed before we
|
| 637 | * destroy cache.
|
| 638 |
|
| 639 | rcu_barrier();
|
| 640 | kmem_cache_destroy(squashfs_inode_cachep);
|
| 641 | }
|
| 642 |
|
| 643 |
|
| 644 | static int __init init_squashfs_fs(void)
|
| 645 | {
|
| 646 | int err = init_inodecache();
|
| 647 |
|
| 648 | if (err)
|
| 649 | return err;
|
| 650 |
|
| 651 | err = register_filesystem(&squashfs_fs_type);
|
| 652 | if (err) {
|
| 653 | destroy_inodecache();
|
| 654 | return err;
|
| 655 | }
|
| 656 |
|
| 657 | pr_info("version 4.0 (2009/01/31) Phillip Lougher\n");
|
| 658 |
|
| 659 | return 0;
|
| 660 | }
|
| 661 |
|
| 662 |
|
| 663 | static void __exit exit_squashfs_fs(void)
|
| 664 | {
|
| 665 | unregister_filesystem(&squashfs_fs_type);
|
| 666 | destroy_inodecache();
|
| 667 | }
|
| 668 |
|
| 669 |
|
| 670 | static struct inode *squashfs_alloc_inode(struct super_block *sb)
|
| 671 | {
|
| 672 | struct squashfs_inode_info *ei =
|
| 673 | alloc_inode_sb(sb, squashfs_inode_cachep, GFP_KERNEL);
|
| 674 |
|
| 675 | return ei ? &ei->vfs_inode : NULL;
|
| 676 | }
|
| 677 |
|
| 678 |
|
| 679 | static void squashfs_free_inode(struct inode *inode)
|
| 680 | {
|
| 681 | kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode));
|
| 682 | }
|
| 683 |
|
| 684 | static struct file_system_type squashfs_fs_type = {
|
| 685 | .owner = THIS_MODULE,
|
| 686 | .name = "squashfs",
|
| 687 | .init_fs_context = squashfs_init_fs_context,
|
| 688 | .parameters = squashfs_fs_parameters,
|
| 689 | .kill_sb = kill_block_super,
|
| 690 | .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
|
| 691 | };
|
| 692 | MODULE_ALIAS_FS("squashfs");
|
| 693 |
|
| 694 | static const struct super_operations squashfs_super_ops = {
|
| 695 | .alloc_inode = squashfs_alloc_inode,
|
| 696 | .free_inode = squashfs_free_inode,
|
| 697 | .statfs = squashfs_statfs,
|
| 698 | .put_super = squashfs_put_super,
|
| 699 | .show_options = squashfs_show_options,
|
| 700 | };
|
| 701 |
|
| 702 | module_init(init_squashfs_fs);
|
| 703 | module_exit(exit_squashfs_fs);
|
| 704 | MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem");
|
| 705 | MODULE_AUTHOR("Phillip Lougher <[email protected]>");
|
| 706 | MODULE_LICENSE("GPL");
|