Commit | Line | Data |
---|---|---|
68252eb5 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1dc4bba3 PL |
2 | /* |
3 | * Squashfs - a compressed read only filesystem for Linux | |
4 | * | |
5 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | |
d7f2ff67 | 6 | * Phillip Lougher <phillip@squashfs.org.uk> |
1dc4bba3 | 7 | * |
1dc4bba3 PL |
8 | * symlink.c |
9 | */ | |
10 | ||
11 | /* | |
12 | * This file implements code to handle symbolic links. | |
13 | * | |
14 | * The data contents of symbolic links are stored inside the symbolic | |
15 | * link inode within the inode table. This allows the normally small symbolic | |
16 | * link to be compressed as part of the inode table, achieving much greater | |
17 | * compression than if the symbolic link was compressed individually. | |
18 | */ | |
19 | ||
20 | #include <linux/fs.h> | |
21 | #include <linux/vfs.h> | |
22 | #include <linux/kernel.h> | |
1dc4bba3 PL |
23 | #include <linux/string.h> |
24 | #include <linux/pagemap.h> | |
67f66cc6 | 25 | #include <linux/xattr.h> |
1dc4bba3 PL |
26 | |
27 | #include "squashfs_fs.h" | |
28 | #include "squashfs_fs_sb.h" | |
29 | #include "squashfs_fs_i.h" | |
30 | #include "squashfs.h" | |
01e5b4e4 | 31 | #include "xattr.h" |
1dc4bba3 | 32 | |
124cfc15 | 33 | static int squashfs_symlink_read_folio(struct file *file, struct folio *folio) |
1dc4bba3 | 34 | { |
675f02e5 | 35 | struct inode *inode = folio->mapping->host; |
1dc4bba3 PL |
36 | struct super_block *sb = inode->i_sb; |
37 | struct squashfs_sb_info *msblk = sb->s_fs_info; | |
675f02e5 | 38 | int index = folio_pos(folio); |
1dc4bba3 PL |
39 | u64 block = squashfs_i(inode)->start; |
40 | int offset = squashfs_i(inode)->offset; | |
09cbfeaf | 41 | int length = min_t(int, i_size_read(inode) - index, PAGE_SIZE); |
675f02e5 | 42 | int bytes, copied, error; |
1dc4bba3 PL |
43 | void *pageaddr; |
44 | struct squashfs_cache_entry *entry; | |
45 | ||
46 | TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " | |
675f02e5 | 47 | "%llx, offset %x\n", folio->index, block, offset); |
1dc4bba3 PL |
48 | |
49 | /* | |
50 | * Skip index bytes into symlink metadata. | |
51 | */ | |
52 | if (index) { | |
53 | bytes = squashfs_read_metadata(sb, NULL, &block, &offset, | |
54 | index); | |
55 | if (bytes < 0) { | |
56 | ERROR("Unable to read symlink [%llx:%x]\n", | |
57 | squashfs_i(inode)->start, | |
58 | squashfs_i(inode)->offset); | |
675f02e5 MWO |
59 | error = bytes; |
60 | goto out; | |
1dc4bba3 PL |
61 | } |
62 | } | |
63 | ||
64 | /* | |
65 | * Read length bytes from symlink metadata. Squashfs_read_metadata | |
66 | * is not used here because it can sleep and we want to use | |
675f02e5 | 67 | * kmap_local to map the folio. Instead call the underlying |
1dc4bba3 PL |
68 | * squashfs_cache_get routine. As length bytes may overlap metadata |
69 | * blocks, we may need to call squashfs_cache_get multiple times. | |
70 | */ | |
71 | for (bytes = 0; bytes < length; offset = 0, bytes += copied) { | |
72 | entry = squashfs_cache_get(sb, msblk->block_cache, block, 0); | |
73 | if (entry->error) { | |
74 | ERROR("Unable to read symlink [%llx:%x]\n", | |
75 | squashfs_i(inode)->start, | |
76 | squashfs_i(inode)->offset); | |
77 | squashfs_cache_put(entry); | |
675f02e5 MWO |
78 | error = entry->error; |
79 | goto out; | |
1dc4bba3 PL |
80 | } |
81 | ||
675f02e5 | 82 | pageaddr = kmap_local_folio(folio, 0); |
1dc4bba3 PL |
83 | copied = squashfs_copy_data(pageaddr + bytes, entry, offset, |
84 | length - bytes); | |
85 | if (copied == length - bytes) | |
09cbfeaf | 86 | memset(pageaddr + length, 0, PAGE_SIZE - length); |
1dc4bba3 PL |
87 | else |
88 | block = entry->next_index; | |
675f02e5 | 89 | kunmap_local(pageaddr); |
1dc4bba3 PL |
90 | squashfs_cache_put(entry); |
91 | } | |
92 | ||
675f02e5 MWO |
93 | flush_dcache_folio(folio); |
94 | error = 0; | |
95 | out: | |
96 | folio_end_read(folio, error == 0); | |
97 | return error; | |
1dc4bba3 PL |
98 | } |
99 | ||
100 | ||
101 | const struct address_space_operations squashfs_symlink_aops = { | |
124cfc15 | 102 | .read_folio = squashfs_symlink_read_folio |
1dc4bba3 | 103 | }; |
67f66cc6 PL |
104 | |
105 | const struct inode_operations squashfs_symlink_inode_ops = { | |
6b255391 | 106 | .get_link = page_get_link, |
67f66cc6 PL |
107 | .listxattr = squashfs_listxattr |
108 | }; | |
109 |