Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Squashfs - a compressed read only filesystem for Linux
0004  *
0005  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
0006  * Phillip Lougher <phillip@squashfs.org.uk>
0007  *
0008  * export.c
0009  */
0010 
0011 /*
0012  * This file implements code to make Squashfs filesystems exportable (NFS etc.)
0013  *
0014  * The export code uses an inode lookup table to map inode numbers passed in
0015  * filehandles to an inode location on disk.  This table is stored compressed
0016  * into metadata blocks.  A second index table is used to locate these.  This
0017  * second index table for speed of access (and because it is small) is read at
0018  * mount time and cached in memory.
0019  *
0020  * The inode lookup table is used only by the export code, inode disk
0021  * locations are directly encoded in directories, enabling direct access
0022  * without an intermediate lookup for all operations except the export ops.
0023  */
0024 
0025 #include <linux/fs.h>
0026 #include <linux/vfs.h>
0027 #include <linux/dcache.h>
0028 #include <linux/exportfs.h>
0029 #include <linux/slab.h>
0030 
0031 #include "squashfs_fs.h"
0032 #include "squashfs_fs_sb.h"
0033 #include "squashfs_fs_i.h"
0034 #include "squashfs.h"
0035 
0036 /*
0037  * Look-up inode number (ino) in table, returning the inode location.
0038  */
0039 static long long squashfs_inode_lookup(struct super_block *sb, int ino_num)
0040 {
0041     struct squashfs_sb_info *msblk = sb->s_fs_info;
0042     int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1);
0043     int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1);
0044     u64 start;
0045     __le64 ino;
0046     int err;
0047 
0048     TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num);
0049 
0050     if (ino_num == 0 || (ino_num - 1) >= msblk->inodes)
0051         return -EINVAL;
0052 
0053     start = le64_to_cpu(msblk->inode_lookup_table[blk]);
0054 
0055     err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino));
0056     if (err < 0)
0057         return err;
0058 
0059     TRACE("squashfs_inode_lookup, inode = 0x%llx\n",
0060         (u64) le64_to_cpu(ino));
0061 
0062     return le64_to_cpu(ino);
0063 }
0064 
0065 
0066 static struct dentry *squashfs_export_iget(struct super_block *sb,
0067     unsigned int ino_num)
0068 {
0069     long long ino;
0070     struct dentry *dentry = ERR_PTR(-ENOENT);
0071 
0072     TRACE("Entered squashfs_export_iget\n");
0073 
0074     ino = squashfs_inode_lookup(sb, ino_num);
0075     if (ino >= 0)
0076         dentry = d_obtain_alias(squashfs_iget(sb, ino, ino_num));
0077 
0078     return dentry;
0079 }
0080 
0081 
0082 static struct dentry *squashfs_fh_to_dentry(struct super_block *sb,
0083         struct fid *fid, int fh_len, int fh_type)
0084 {
0085     if ((fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT)
0086             || fh_len < 2)
0087         return NULL;
0088 
0089     return squashfs_export_iget(sb, fid->i32.ino);
0090 }
0091 
0092 
0093 static struct dentry *squashfs_fh_to_parent(struct super_block *sb,
0094         struct fid *fid, int fh_len, int fh_type)
0095 {
0096     if (fh_type != FILEID_INO32_GEN_PARENT || fh_len < 4)
0097         return NULL;
0098 
0099     return squashfs_export_iget(sb, fid->i32.parent_ino);
0100 }
0101 
0102 
0103 static struct dentry *squashfs_get_parent(struct dentry *child)
0104 {
0105     struct inode *inode = d_inode(child);
0106     unsigned int parent_ino = squashfs_i(inode)->parent;
0107 
0108     return squashfs_export_iget(inode->i_sb, parent_ino);
0109 }
0110 
0111 
0112 /*
0113  * Read uncompressed inode lookup table indexes off disk into memory
0114  */
0115 __le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
0116         u64 lookup_table_start, u64 next_table, unsigned int inodes)
0117 {
0118     unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
0119     unsigned int indexes = SQUASHFS_LOOKUP_BLOCKS(inodes);
0120     int n;
0121     __le64 *table;
0122     u64 start, end;
0123 
0124     TRACE("In read_inode_lookup_table, length %d\n", length);
0125 
0126     /* Sanity check values */
0127 
0128     /* there should always be at least one inode */
0129     if (inodes == 0)
0130         return ERR_PTR(-EINVAL);
0131 
0132     /*
0133      * The computed size of the lookup table (length bytes) should exactly
0134      * match the table start and end points
0135      */
0136     if (length != (next_table - lookup_table_start))
0137         return ERR_PTR(-EINVAL);
0138 
0139     table = squashfs_read_table(sb, lookup_table_start, length);
0140     if (IS_ERR(table))
0141         return table;
0142 
0143     /*
0144      * table0], table[1], ... table[indexes - 1] store the locations
0145      * of the compressed inode lookup blocks.  Each entry should be
0146      * less than the next (i.e. table[0] < table[1]), and the difference
0147      * between them should be SQUASHFS_METADATA_SIZE or less.
0148      * table[indexes - 1] should  be less than lookup_table_start, and
0149      * again the difference should be SQUASHFS_METADATA_SIZE or less
0150      */
0151     for (n = 0; n < (indexes - 1); n++) {
0152         start = le64_to_cpu(table[n]);
0153         end = le64_to_cpu(table[n + 1]);
0154 
0155         if (start >= end
0156             || (end - start) >
0157             (SQUASHFS_METADATA_SIZE + SQUASHFS_BLOCK_OFFSET)) {
0158             kfree(table);
0159             return ERR_PTR(-EINVAL);
0160         }
0161     }
0162 
0163     start = le64_to_cpu(table[indexes - 1]);
0164     if (start >= lookup_table_start ||
0165         (lookup_table_start - start) >
0166         (SQUASHFS_METADATA_SIZE + SQUASHFS_BLOCK_OFFSET)) {
0167         kfree(table);
0168         return ERR_PTR(-EINVAL);
0169     }
0170 
0171     return table;
0172 }
0173 
0174 
0175 const struct export_operations squashfs_export_ops = {
0176     .fh_to_dentry = squashfs_fh_to_dentry,
0177     .fh_to_parent = squashfs_fh_to_parent,
0178     .get_parent = squashfs_get_parent
0179 };