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@squashfs.org.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/string.h>
37 #include <linux/pagemap.h>
38 #include <linux/xattr.h>
39 
40 #include "squashfs_fs.h"
41 #include "squashfs_fs_sb.h"
42 #include "squashfs_fs_i.h"
43 #include "squashfs.h"
44 #include "xattr.h"
45 
squashfs_symlink_readpage(struct file * file,struct page * page)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);
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);
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 
120 const struct inode_operations squashfs_symlink_inode_ops = {
121 	.readlink = generic_readlink,
122 	.follow_link = page_follow_link_light,
123 	.put_link = page_put_link,
124 	.getxattr = generic_getxattr,
125 	.listxattr = squashfs_listxattr
126 };
127 
128