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  * symlink.c
0009  */
0010 
0011 /*
0012  * This file implements code to handle symbolic links.
0013  *
0014  * The data contents of symbolic links are stored inside the symbolic
0015  * link inode within the inode table.  This allows the normally small symbolic
0016  * link to be compressed as part of the inode table, achieving much greater
0017  * compression than if the symbolic link was compressed individually.
0018  */
0019 
0020 #include <linux/fs.h>
0021 #include <linux/vfs.h>
0022 #include <linux/kernel.h>
0023 #include <linux/string.h>
0024 #include <linux/pagemap.h>
0025 #include <linux/xattr.h>
0026 
0027 #include "squashfs_fs.h"
0028 #include "squashfs_fs_sb.h"
0029 #include "squashfs_fs_i.h"
0030 #include "squashfs.h"
0031 #include "xattr.h"
0032 
0033 static int squashfs_symlink_read_folio(struct file *file, struct folio *folio)
0034 {
0035     struct page *page = &folio->page;
0036     struct inode *inode = page->mapping->host;
0037     struct super_block *sb = inode->i_sb;
0038     struct squashfs_sb_info *msblk = sb->s_fs_info;
0039     int index = page->index << PAGE_SHIFT;
0040     u64 block = squashfs_i(inode)->start;
0041     int offset = squashfs_i(inode)->offset;
0042     int length = min_t(int, i_size_read(inode) - index, PAGE_SIZE);
0043     int bytes, copied;
0044     void *pageaddr;
0045     struct squashfs_cache_entry *entry;
0046 
0047     TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
0048             "%llx, offset %x\n", page->index, block, offset);
0049 
0050     /*
0051      * Skip index bytes into symlink metadata.
0052      */
0053     if (index) {
0054         bytes = squashfs_read_metadata(sb, NULL, &block, &offset,
0055                                 index);
0056         if (bytes < 0) {
0057             ERROR("Unable to read symlink [%llx:%x]\n",
0058                 squashfs_i(inode)->start,
0059                 squashfs_i(inode)->offset);
0060             goto error_out;
0061         }
0062     }
0063 
0064     /*
0065      * Read length bytes from symlink metadata.  Squashfs_read_metadata
0066      * is not used here because it can sleep and we want to use
0067      * kmap_atomic to map the page.  Instead call the underlying
0068      * squashfs_cache_get routine.  As length bytes may overlap metadata
0069      * blocks, we may need to call squashfs_cache_get multiple times.
0070      */
0071     for (bytes = 0; bytes < length; offset = 0, bytes += copied) {
0072         entry = squashfs_cache_get(sb, msblk->block_cache, block, 0);
0073         if (entry->error) {
0074             ERROR("Unable to read symlink [%llx:%x]\n",
0075                 squashfs_i(inode)->start,
0076                 squashfs_i(inode)->offset);
0077             squashfs_cache_put(entry);
0078             goto error_out;
0079         }
0080 
0081         pageaddr = kmap_atomic(page);
0082         copied = squashfs_copy_data(pageaddr + bytes, entry, offset,
0083                                 length - bytes);
0084         if (copied == length - bytes)
0085             memset(pageaddr + length, 0, PAGE_SIZE - length);
0086         else
0087             block = entry->next_index;
0088         kunmap_atomic(pageaddr);
0089         squashfs_cache_put(entry);
0090     }
0091 
0092     flush_dcache_page(page);
0093     SetPageUptodate(page);
0094     unlock_page(page);
0095     return 0;
0096 
0097 error_out:
0098     SetPageError(page);
0099     unlock_page(page);
0100     return 0;
0101 }
0102 
0103 
0104 const struct address_space_operations squashfs_symlink_aops = {
0105     .read_folio = squashfs_symlink_read_folio
0106 };
0107 
0108 const struct inode_operations squashfs_symlink_inode_ops = {
0109     .get_link = page_get_link,
0110     .listxattr = squashfs_listxattr
0111 };
0112