Commit | Line | Data |
---|---|---|
68252eb5 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
f41d207c PL |
2 | /* |
3 | * Squashfs - a compressed read only filesystem for Linux | |
4 | * | |
5 | * Copyright (c) 2010 | |
d7f2ff67 | 6 | * Phillip Lougher <phillip@squashfs.org.uk> |
f41d207c | 7 | * |
4690148f | 8 | * xattr.c |
f41d207c PL |
9 | */ |
10 | ||
11 | #include <linux/init.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/string.h> | |
14 | #include <linux/fs.h> | |
15 | #include <linux/vfs.h> | |
16 | #include <linux/xattr.h> | |
17 | #include <linux/slab.h> | |
18 | ||
19 | #include "squashfs_fs.h" | |
20 | #include "squashfs_fs_sb.h" | |
21 | #include "squashfs_fs_i.h" | |
22 | #include "squashfs.h" | |
23 | ||
f6db25a8 | 24 | static const struct xattr_handler *squashfs_xattr_handler(int); |
f41d207c PL |
25 | |
26 | ssize_t squashfs_listxattr(struct dentry *d, char *buffer, | |
27 | size_t buffer_size) | |
28 | { | |
2b0143b5 | 29 | struct inode *inode = d_inode(d); |
f41d207c PL |
30 | struct super_block *sb = inode->i_sb; |
31 | struct squashfs_sb_info *msblk = sb->s_fs_info; | |
32 | u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr) | |
33 | + msblk->xattr_table; | |
34 | int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr); | |
35 | int count = squashfs_i(inode)->xattr_count; | |
36 | size_t rest = buffer_size; | |
37 | int err; | |
38 | ||
39 | /* check that the file system has xattrs */ | |
40 | if (msblk->xattr_id_table == NULL) | |
41 | return -EOPNOTSUPP; | |
42 | ||
43 | /* loop reading each xattr name */ | |
44 | while (count--) { | |
45 | struct squashfs_xattr_entry entry; | |
46 | struct squashfs_xattr_val val; | |
f6db25a8 | 47 | const struct xattr_handler *handler; |
764a5c6b | 48 | int name_size; |
f41d207c PL |
49 | |
50 | err = squashfs_read_metadata(sb, &entry, &start, &offset, | |
51 | sizeof(entry)); | |
52 | if (err < 0) | |
53 | goto failed; | |
54 | ||
55 | name_size = le16_to_cpu(entry.size); | |
56 | handler = squashfs_xattr_handler(le16_to_cpu(entry.type)); | |
764a5c6b AG |
57 | if (handler && (!handler->list || handler->list(d))) { |
58 | const char *prefix = handler->prefix ?: handler->name; | |
59 | size_t prefix_size = strlen(prefix); | |
60 | ||
f41d207c PL |
61 | if (buffer) { |
62 | if (prefix_size + name_size + 1 > rest) { | |
63 | err = -ERANGE; | |
64 | goto failed; | |
65 | } | |
764a5c6b | 66 | memcpy(buffer, prefix, prefix_size); |
f41d207c PL |
67 | buffer += prefix_size; |
68 | } | |
69 | err = squashfs_read_metadata(sb, buffer, &start, | |
70 | &offset, name_size); | |
71 | if (err < 0) | |
72 | goto failed; | |
73 | if (buffer) { | |
74 | buffer[name_size] = '\0'; | |
75 | buffer += name_size + 1; | |
76 | } | |
77 | rest -= prefix_size + name_size + 1; | |
78 | } else { | |
79 | /* no handler or insuffficient privileges, so skip */ | |
80 | err = squashfs_read_metadata(sb, NULL, &start, | |
81 | &offset, name_size); | |
82 | if (err < 0) | |
83 | goto failed; | |
84 | } | |
85 | ||
86 | ||
87 | /* skip remaining xattr entry */ | |
88 | err = squashfs_read_metadata(sb, &val, &start, &offset, | |
89 | sizeof(val)); | |
90 | if (err < 0) | |
91 | goto failed; | |
92 | ||
93 | err = squashfs_read_metadata(sb, NULL, &start, &offset, | |
94 | le32_to_cpu(val.vsize)); | |
95 | if (err < 0) | |
96 | goto failed; | |
97 | } | |
98 | err = buffer_size - rest; | |
99 | ||
100 | failed: | |
101 | return err; | |
102 | } | |
103 | ||
104 | ||
105 | static int squashfs_xattr_get(struct inode *inode, int name_index, | |
106 | const char *name, void *buffer, size_t buffer_size) | |
107 | { | |
108 | struct super_block *sb = inode->i_sb; | |
109 | struct squashfs_sb_info *msblk = sb->s_fs_info; | |
110 | u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr) | |
111 | + msblk->xattr_table; | |
112 | int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr); | |
113 | int count = squashfs_i(inode)->xattr_count; | |
114 | int name_len = strlen(name); | |
115 | int err, vsize; | |
116 | char *target = kmalloc(name_len, GFP_KERNEL); | |
117 | ||
118 | if (target == NULL) | |
119 | return -ENOMEM; | |
120 | ||
121 | /* loop reading each xattr name */ | |
122 | for (; count; count--) { | |
123 | struct squashfs_xattr_entry entry; | |
124 | struct squashfs_xattr_val val; | |
125 | int type, prefix, name_size; | |
126 | ||
127 | err = squashfs_read_metadata(sb, &entry, &start, &offset, | |
128 | sizeof(entry)); | |
129 | if (err < 0) | |
130 | goto failed; | |
131 | ||
132 | name_size = le16_to_cpu(entry.size); | |
133 | type = le16_to_cpu(entry.type); | |
134 | prefix = type & SQUASHFS_XATTR_PREFIX_MASK; | |
135 | ||
5c80f5aa PL |
136 | if (prefix == name_index && name_size == name_len) |
137 | err = squashfs_read_metadata(sb, target, &start, | |
138 | &offset, name_size); | |
139 | else | |
140 | err = squashfs_read_metadata(sb, NULL, &start, | |
141 | &offset, name_size); | |
f41d207c PL |
142 | if (err < 0) |
143 | goto failed; | |
144 | ||
145 | if (prefix == name_index && name_size == name_len && | |
146 | strncmp(target, name, name_size) == 0) { | |
147 | /* found xattr */ | |
148 | if (type & SQUASHFS_XATTR_VALUE_OOL) { | |
07724586 PL |
149 | __le64 xattr_val; |
150 | u64 xattr; | |
f41d207c PL |
151 | /* val is a reference to the real location */ |
152 | err = squashfs_read_metadata(sb, &val, &start, | |
153 | &offset, sizeof(val)); | |
154 | if (err < 0) | |
155 | goto failed; | |
07724586 PL |
156 | err = squashfs_read_metadata(sb, &xattr_val, |
157 | &start, &offset, sizeof(xattr_val)); | |
f41d207c PL |
158 | if (err < 0) |
159 | goto failed; | |
07724586 | 160 | xattr = le64_to_cpu(xattr_val); |
f41d207c PL |
161 | start = SQUASHFS_XATTR_BLK(xattr) + |
162 | msblk->xattr_table; | |
163 | offset = SQUASHFS_XATTR_OFFSET(xattr); | |
164 | } | |
165 | /* read xattr value */ | |
166 | err = squashfs_read_metadata(sb, &val, &start, &offset, | |
167 | sizeof(val)); | |
168 | if (err < 0) | |
169 | goto failed; | |
170 | ||
171 | vsize = le32_to_cpu(val.vsize); | |
172 | if (buffer) { | |
173 | if (vsize > buffer_size) { | |
174 | err = -ERANGE; | |
175 | goto failed; | |
176 | } | |
177 | err = squashfs_read_metadata(sb, buffer, &start, | |
178 | &offset, vsize); | |
179 | if (err < 0) | |
180 | goto failed; | |
181 | } | |
182 | break; | |
183 | } | |
184 | ||
185 | /* no match, skip remaining xattr entry */ | |
186 | err = squashfs_read_metadata(sb, &val, &start, &offset, | |
187 | sizeof(val)); | |
188 | if (err < 0) | |
189 | goto failed; | |
190 | err = squashfs_read_metadata(sb, NULL, &start, &offset, | |
191 | le32_to_cpu(val.vsize)); | |
192 | if (err < 0) | |
193 | goto failed; | |
194 | } | |
195 | err = count ? vsize : -ENODATA; | |
196 | ||
197 | failed: | |
198 | kfree(target); | |
199 | return err; | |
200 | } | |
201 | ||
202 | ||
0ddaf72c | 203 | static int squashfs_xattr_handler_get(const struct xattr_handler *handler, |
b296821a AV |
204 | struct dentry *unused, |
205 | struct inode *inode, | |
206 | const char *name, | |
0ddaf72c | 207 | void *buffer, size_t size) |
f41d207c | 208 | { |
b296821a | 209 | return squashfs_xattr_get(inode, handler->flags, name, |
f41d207c PL |
210 | buffer, size); |
211 | } | |
212 | ||
0ddaf72c AG |
213 | /* |
214 | * User namespace support | |
215 | */ | |
f6db25a8 | 216 | static const struct xattr_handler squashfs_xattr_user_handler = { |
f41d207c | 217 | .prefix = XATTR_USER_PREFIX, |
0ddaf72c | 218 | .flags = SQUASHFS_XATTR_USER, |
0ddaf72c | 219 | .get = squashfs_xattr_handler_get |
f41d207c PL |
220 | }; |
221 | ||
222 | /* | |
223 | * Trusted namespace support | |
224 | */ | |
764a5c6b | 225 | static bool squashfs_trusted_xattr_handler_list(struct dentry *d) |
f41d207c | 226 | { |
764a5c6b | 227 | return capable(CAP_SYS_ADMIN); |
f41d207c PL |
228 | } |
229 | ||
f6db25a8 | 230 | static const struct xattr_handler squashfs_xattr_trusted_handler = { |
f41d207c | 231 | .prefix = XATTR_TRUSTED_PREFIX, |
0ddaf72c AG |
232 | .flags = SQUASHFS_XATTR_TRUSTED, |
233 | .list = squashfs_trusted_xattr_handler_list, | |
234 | .get = squashfs_xattr_handler_get | |
f41d207c PL |
235 | }; |
236 | ||
237 | /* | |
238 | * Security namespace support | |
239 | */ | |
f6db25a8 | 240 | static const struct xattr_handler squashfs_xattr_security_handler = { |
f41d207c | 241 | .prefix = XATTR_SECURITY_PREFIX, |
0ddaf72c | 242 | .flags = SQUASHFS_XATTR_SECURITY, |
0ddaf72c | 243 | .get = squashfs_xattr_handler_get |
f41d207c PL |
244 | }; |
245 | ||
a02956e4 | 246 | static const struct xattr_handler *squashfs_xattr_handler(int type) |
f41d207c PL |
247 | { |
248 | if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL)) | |
249 | /* ignore unrecognised type */ | |
250 | return NULL; | |
251 | ||
252 | switch (type & SQUASHFS_XATTR_PREFIX_MASK) { | |
253 | case SQUASHFS_XATTR_USER: | |
254 | return &squashfs_xattr_user_handler; | |
255 | case SQUASHFS_XATTR_TRUSTED: | |
256 | return &squashfs_xattr_trusted_handler; | |
257 | case SQUASHFS_XATTR_SECURITY: | |
258 | return &squashfs_xattr_security_handler; | |
259 | default: | |
260 | /* ignore unrecognised type */ | |
261 | return NULL; | |
262 | } | |
263 | } | |
264 | ||
c08a831c | 265 | const struct xattr_handler * const squashfs_xattr_handlers[] = { |
f41d207c PL |
266 | &squashfs_xattr_user_handler, |
267 | &squashfs_xattr_trusted_handler, | |
268 | &squashfs_xattr_security_handler, | |
269 | NULL | |
270 | }; | |
271 |