Commit 1dc4bba39dd29c6d6f77ca7bf63cd3adeb6fc162

Authored by Phillip Lougher
1 parent 1701aecb68

Squashfs: symlink operations

Signed-off-by: Phillip Lougher <phillip@lougher.demon.co.uk>

Showing 1 changed file with 118 additions and 0 deletions Side-by-side Diff

fs/squashfs/symlink.c
  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@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 +};