Home | History | Annotate | Download | only in squashfs
      1 /*
      2  * Squashfs - a compressed read only filesystem for Linux
      3  *
      4  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
      5  * Phillip Lougher <phillip (at) lougher.demon.co.uk>
      6  *
      7  * This program is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU General Public License
      9  * as published by the Free Software Foundation; either version 2,
     10  * or (at your option) any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  * GNU General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU General Public License
     18  * along with this program; if not, write to the Free Software
     19  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20  *
     21  * symlink.c
     22  */
     23 
     24 /*
     25  * This file implements code to handle symbolic links.
     26  *
     27  * The data contents of symbolic links are stored inside the symbolic
     28  * link inode within the inode table.  This allows the normally small symbolic
     29  * link to be compressed as part of the inode table, achieving much greater
     30  * compression than if the symbolic link was compressed individually.
     31  */
     32 
     33 #include <linux/fs.h>
     34 #include <linux/vfs.h>
     35 #include <linux/kernel.h>
     36 #include <linux/slab.h>
     37 #include <linux/string.h>
     38 #include <linux/pagemap.h>
     39 #include <linux/zlib.h>
     40 
     41 #include "squashfs_fs.h"
     42 #include "squashfs_fs_sb.h"
     43 #include "squashfs_fs_i.h"
     44 #include "squashfs.h"
     45 
     46 static int squashfs_symlink_readpage(struct file *file, struct page *page)
     47 {
     48 	struct inode *inode = page->mapping->host;
     49 	struct super_block *sb = inode->i_sb;
     50 	struct squashfs_sb_info *msblk = sb->s_fs_info;
     51 	int index = page->index << PAGE_CACHE_SHIFT;
     52 	u64 block = squashfs_i(inode)->start;
     53 	int offset = squashfs_i(inode)->offset;
     54 	int length = min_t(int, i_size_read(inode) - index, PAGE_CACHE_SIZE);
     55 	int bytes, copied;
     56 	void *pageaddr;
     57 	struct squashfs_cache_entry *entry;
     58 
     59 	TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
     60 			"%llx, offset %x\n", page->index, block, offset);
     61 
     62 	/*
     63 	 * Skip index bytes into symlink metadata.
     64 	 */
     65 	if (index) {
     66 		bytes = squashfs_read_metadata(sb, NULL, &block, &offset,
     67 								index);
     68 		if (bytes < 0) {
     69 			ERROR("Unable to read symlink [%llx:%x]\n",
     70 				squashfs_i(inode)->start,
     71 				squashfs_i(inode)->offset);
     72 			goto error_out;
     73 		}
     74 	}
     75 
     76 	/*
     77 	 * Read length bytes from symlink metadata.  Squashfs_read_metadata
     78 	 * is not used here because it can sleep and we want to use
     79 	 * kmap_atomic to map the page.  Instead call the underlying
     80 	 * squashfs_cache_get routine.  As length bytes may overlap metadata
     81 	 * blocks, we may need to call squashfs_cache_get multiple times.
     82 	 */
     83 	for (bytes = 0; bytes < length; offset = 0, bytes += copied) {
     84 		entry = squashfs_cache_get(sb, msblk->block_cache, block, 0);
     85 		if (entry->error) {
     86 			ERROR("Unable to read symlink [%llx:%x]\n",
     87 				squashfs_i(inode)->start,
     88 				squashfs_i(inode)->offset);
     89 			squashfs_cache_put(entry);
     90 			goto error_out;
     91 		}
     92 
     93 		pageaddr = kmap_atomic(page, KM_USER0);
     94 		copied = squashfs_copy_data(pageaddr + bytes, entry, offset,
     95 								length - bytes);
     96 		if (copied == length - bytes)
     97 			memset(pageaddr + length, 0, PAGE_CACHE_SIZE - length);
     98 		else
     99 			block = entry->next_index;
    100 		kunmap_atomic(pageaddr, KM_USER0);
    101 		squashfs_cache_put(entry);
    102 	}
    103 
    104 	flush_dcache_page(page);
    105 	SetPageUptodate(page);
    106 	unlock_page(page);
    107 	return 0;
    108 
    109 error_out:
    110 	SetPageError(page);
    111 	unlock_page(page);
    112 	return 0;
    113 }
    114 
    115 
    116 const struct address_space_operations squashfs_symlink_aops = {
    117 	.readpage = squashfs_symlink_readpage
    118 };
    119