1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Squashfs - a compressed read only filesystem for Linux |
4 | * |
5 | * Copyright (c) 2010 |
6 | * Phillip Lougher <phillip@squashfs.org.uk> |
7 | * |
8 | * xattr.c |
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 | |
24 | static const struct xattr_handler *squashfs_xattr_handler(int); |
25 | |
26 | ssize_t squashfs_listxattr(struct dentry *d, char *buffer, |
27 | size_t buffer_size) |
28 | { |
29 | struct inode *inode = d_inode(dentry: d); |
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; |
47 | const struct xattr_handler *handler; |
48 | int name_size; |
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)); |
57 | if (handler && (!handler->list || handler->list(d))) { |
58 | const char *prefix = handler->prefix ?: handler->name; |
59 | size_t prefix_size = strlen(prefix); |
60 | |
61 | if (buffer) { |
62 | if (prefix_size + name_size + 1 > rest) { |
63 | err = -ERANGE; |
64 | goto failed; |
65 | } |
66 | memcpy(buffer, prefix, prefix_size); |
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(size: 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 | |
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); |
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) { |
149 | __le64 xattr_val; |
150 | u64 xattr; |
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; |
156 | err = squashfs_read_metadata(sb, &xattr_val, |
157 | &start, &offset, sizeof(xattr_val)); |
158 | if (err < 0) |
159 | goto failed; |
160 | xattr = le64_to_cpu(xattr_val); |
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(objp: target); |
199 | return err; |
200 | } |
201 | |
202 | |
203 | static int squashfs_xattr_handler_get(const struct xattr_handler *handler, |
204 | struct dentry *unused, |
205 | struct inode *inode, |
206 | const char *name, |
207 | void *buffer, size_t size) |
208 | { |
209 | return squashfs_xattr_get(inode, name_index: handler->flags, name, |
210 | buffer, buffer_size: size); |
211 | } |
212 | |
213 | /* |
214 | * User namespace support |
215 | */ |
216 | static const struct xattr_handler squashfs_xattr_user_handler = { |
217 | .prefix = XATTR_USER_PREFIX, |
218 | .flags = SQUASHFS_XATTR_USER, |
219 | .get = squashfs_xattr_handler_get |
220 | }; |
221 | |
222 | /* |
223 | * Trusted namespace support |
224 | */ |
225 | static bool squashfs_trusted_xattr_handler_list(struct dentry *d) |
226 | { |
227 | return capable(CAP_SYS_ADMIN); |
228 | } |
229 | |
230 | static const struct xattr_handler squashfs_xattr_trusted_handler = { |
231 | .prefix = XATTR_TRUSTED_PREFIX, |
232 | .flags = SQUASHFS_XATTR_TRUSTED, |
233 | .list = squashfs_trusted_xattr_handler_list, |
234 | .get = squashfs_xattr_handler_get |
235 | }; |
236 | |
237 | /* |
238 | * Security namespace support |
239 | */ |
240 | static const struct xattr_handler squashfs_xattr_security_handler = { |
241 | .prefix = XATTR_SECURITY_PREFIX, |
242 | .flags = SQUASHFS_XATTR_SECURITY, |
243 | .get = squashfs_xattr_handler_get |
244 | }; |
245 | |
246 | static const struct xattr_handler *squashfs_xattr_handler(int type) |
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 | |
265 | const struct xattr_handler * const squashfs_xattr_handlers[] = { |
266 | &squashfs_xattr_user_handler, |
267 | &squashfs_xattr_trusted_handler, |
268 | &squashfs_xattr_security_handler, |
269 | NULL |
270 | }; |
271 | |
272 | |