1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/extattr.h> 30 #include <sys/fcntl.h> 31 #include <sys/namei.h> 32 #include <sys/proc.h> 33 #include <sys/syscallsubr.h> 34 35 #ifdef COMPAT_LINUX32 36 #include <machine/../linux32/linux.h> 37 #include <machine/../linux32/linux32_proto.h> 38 #else 39 #include <machine/../linux/linux.h> 40 #include <machine/../linux/linux_proto.h> 41 #endif 42 43 #include <compat/linux/linux_util.h> 44 45 #define LINUX_XATTR_SIZE_MAX 65536 46 #define LINUX_XATTR_LIST_MAX 65536 47 #define LINUX_XATTR_NAME_MAX 255 48 49 #define LINUX_XATTR_CREATE 0x1 50 #define LINUX_XATTR_REPLACE 0x2 51 #define LINUX_XATTR_FLAGS LINUX_XATTR_CREATE|LINUX_XATTR_REPLACE 52 53 struct listxattr_args { 54 int fd; 55 const char *path; 56 char *list; 57 l_size_t size; 58 int follow; 59 }; 60 61 struct setxattr_args { 62 int fd; 63 const char *path; 64 const char *name; 65 void *value; 66 l_size_t size; 67 l_int flags; 68 int follow; 69 }; 70 71 static char *extattr_namespace_names[] = EXTATTR_NAMESPACE_NAMES; 72 73 74 static int 75 xatrr_to_extattr(const char *uattrname, int *attrnamespace, char *attrname) 76 { 77 char uname[LINUX_XATTR_NAME_MAX + 1], *dot; 78 size_t len, cplen; 79 int error; 80 81 error = copyinstr(uattrname, uname, sizeof(uname), &cplen); 82 if (error != 0) 83 return (error); 84 if (cplen == sizeof(uname)) 85 return (ERANGE); 86 dot = strchr(uname, '.'); 87 if (dot == NULL) 88 return (ENOTSUP); 89 *dot = '\0'; 90 for (*attrnamespace = EXTATTR_NAMESPACE_USER; 91 *attrnamespace < nitems(extattr_namespace_names); 92 (*attrnamespace)++) { 93 if (bcmp(uname, extattr_namespace_names[*attrnamespace], 94 dot - uname + 1) == 0) { 95 dot++; 96 len = strlen(dot) + 1; 97 bcopy(dot, attrname, len); 98 return (0); 99 } 100 } 101 return (ENOTSUP); 102 } 103 104 static int 105 listxattr(struct thread *td, struct listxattr_args *args) 106 { 107 char attrname[LINUX_XATTR_NAME_MAX + 1]; 108 char *data, *prefix, *key; 109 struct uio auio; 110 struct iovec aiov; 111 unsigned char keylen; 112 size_t sz, cnt, rs, prefixlen, pairlen; 113 int attrnamespace, error; 114 115 if (args->size != 0) 116 sz = min(LINUX_XATTR_LIST_MAX, args->size); 117 else 118 sz = LINUX_XATTR_LIST_MAX; 119 120 data = malloc(sz, M_LINUX, M_WAITOK); 121 auio.uio_iov = &aiov; 122 auio.uio_iovcnt = 1; 123 auio.uio_rw = UIO_READ; 124 auio.uio_segflg = UIO_SYSSPACE; 125 auio.uio_td = td; 126 cnt = 0; 127 for (attrnamespace = EXTATTR_NAMESPACE_USER; 128 attrnamespace < nitems(extattr_namespace_names); 129 attrnamespace++) { 130 aiov.iov_base = data; 131 aiov.iov_len = sz; 132 auio.uio_resid = sz; 133 auio.uio_offset = 0; 134 135 if (args->path != NULL) 136 error = kern_extattr_list_path(td, args->path, 137 attrnamespace, &auio, args->follow, UIO_USERSPACE); 138 else 139 error = kern_extattr_list_fd(td, args->fd, 140 attrnamespace, &auio); 141 rs = sz - auio.uio_resid; 142 if (error != 0 || rs == 0) 143 continue; 144 prefix = extattr_namespace_names[attrnamespace]; 145 prefixlen = strlen(prefix); 146 key = data; 147 while (rs > 0) { 148 keylen = (unsigned char)key[0]; 149 pairlen = prefixlen + 1 + keylen + 1; 150 if (cnt + pairlen > LINUX_XATTR_LIST_MAX) { 151 error = E2BIG; 152 break; 153 } 154 if ((args->list != NULL && cnt > args->size) || 155 pairlen >= sizeof(attrname)) { 156 error = ERANGE; 157 break; 158 } 159 ++key; 160 if (args->list != NULL) { 161 sprintf(attrname, "%s.%.*s", prefix, keylen, key); 162 error = copyout(attrname, args->list, pairlen); 163 if (error != 0) 164 break; 165 args->list += pairlen; 166 } 167 cnt += pairlen; 168 key += keylen; 169 rs -= (keylen + 1); 170 } 171 } 172 if (error == 0) 173 td->td_retval[0] = cnt; 174 free(data, M_LINUX); 175 return (error); 176 } 177 178 int 179 linux_listxattr(struct thread *td, struct linux_listxattr_args *args) 180 { 181 struct listxattr_args eargs = { 182 .fd = -1, 183 .path = args->path, 184 .list = args->list, 185 .size = args->size, 186 .follow = FOLLOW, 187 }; 188 189 return (listxattr(td, &eargs)); 190 } 191 192 int 193 linux_llistxattr(struct thread *td, struct linux_llistxattr_args *args) 194 { 195 struct listxattr_args eargs = { 196 .fd = -1, 197 .path = args->path, 198 .list = args->list, 199 .size = args->size, 200 .follow = NOFOLLOW, 201 }; 202 203 return (listxattr(td, &eargs)); 204 } 205 206 int 207 linux_flistxattr(struct thread *td, struct linux_flistxattr_args *args) 208 { 209 struct listxattr_args eargs = { 210 .fd = args->fd, 211 .path = NULL, 212 .list = args->list, 213 .size = args->size, 214 .follow = 0, 215 }; 216 217 return (listxattr(td, &eargs)); 218 } 219 220 static int 221 linux_path_removexattr(struct thread *td, const char *upath, const char *uname, 222 int follow) 223 { 224 char attrname[LINUX_XATTR_NAME_MAX + 1]; 225 int attrnamespace, error; 226 227 error = xatrr_to_extattr(uname, &attrnamespace, attrname); 228 if (error != 0) 229 return (error); 230 231 return (kern_extattr_delete_path(td, upath, attrnamespace, 232 attrname, follow, UIO_USERSPACE)); 233 } 234 235 int 236 linux_removexattr(struct thread *td, struct linux_removexattr_args *args) 237 { 238 239 return (linux_path_removexattr(td, args->path, args->name, 240 FOLLOW)); 241 } 242 243 int 244 linux_lremovexattr(struct thread *td, struct linux_lremovexattr_args *args) 245 { 246 247 return (linux_path_removexattr(td, args->path, args->name, 248 NOFOLLOW)); 249 } 250 251 int 252 linux_fremovexattr(struct thread *td, struct linux_fremovexattr_args *args) 253 { 254 char attrname[LINUX_XATTR_NAME_MAX + 1]; 255 int attrnamespace, error; 256 257 error = xatrr_to_extattr(args->name, &attrnamespace, attrname); 258 if (error != 0) 259 return (error); 260 return (kern_extattr_delete_fd(td, args->fd, attrnamespace, 261 attrname)); 262 } 263 264 static int 265 linux_path_getxattr(struct thread *td, const char *upath, const char *uname, 266 void *value, l_size_t size, int follow) 267 { 268 char attrname[LINUX_XATTR_NAME_MAX + 1]; 269 int attrnamespace, error; 270 271 error = xatrr_to_extattr(uname, &attrnamespace, attrname); 272 if (error != 0) 273 return (error); 274 275 return (kern_extattr_get_path(td, upath, attrnamespace, 276 attrname, value, size, follow, UIO_USERSPACE)); 277 } 278 279 int 280 linux_getxattr(struct thread *td, struct linux_getxattr_args *args) 281 { 282 283 return (linux_path_getxattr(td, args->path, args->name, 284 args->value, args->size, FOLLOW)); 285 } 286 287 int 288 linux_lgetxattr(struct thread *td, struct linux_lgetxattr_args *args) 289 { 290 291 return (linux_path_getxattr(td, args->path, args->name, 292 args->value, args->size, NOFOLLOW)); 293 } 294 295 int 296 linux_fgetxattr(struct thread *td, struct linux_fgetxattr_args *args) 297 { 298 char attrname[LINUX_XATTR_NAME_MAX + 1]; 299 int attrnamespace, error; 300 301 error = xatrr_to_extattr(args->name, &attrnamespace, attrname); 302 if (error != 0) 303 return (error); 304 return (kern_extattr_get_fd(td, args->fd, attrnamespace, 305 attrname, args->value, args->size)); 306 } 307 308 static int 309 setxattr(struct thread *td, struct setxattr_args *args) 310 { 311 char attrname[LINUX_XATTR_NAME_MAX + 1]; 312 int attrnamespace, error; 313 314 if ((args->flags & ~(LINUX_XATTR_FLAGS)) != 0 || 315 args->flags == (LINUX_XATTR_FLAGS)) 316 return (EINVAL); 317 error = xatrr_to_extattr(args->name, &attrnamespace, attrname); 318 if (error != 0) 319 return (error); 320 321 if ((args->flags & (LINUX_XATTR_FLAGS)) != 0 ) { 322 if (args->path != NULL) 323 error = kern_extattr_get_path(td, args->path, 324 attrnamespace, attrname, NULL, args->size, 325 args->follow, UIO_USERSPACE); 326 else 327 error = kern_extattr_get_fd(td, args->fd, 328 attrnamespace, attrname, NULL, args->size); 329 if ((args->flags & LINUX_XATTR_CREATE) != 0) { 330 if (error == 0) 331 error = EEXIST; 332 else if (error == ENOATTR) 333 error = 0; 334 } 335 if (error != 0) 336 goto out; 337 } 338 if (args->path != NULL) 339 error = kern_extattr_set_path(td, args->path, attrnamespace, 340 attrname, args->value, args->size, args->follow, 341 UIO_USERSPACE); 342 else 343 error = kern_extattr_set_fd(td, args->fd, attrnamespace, 344 attrname, args->value, args->size); 345 out: 346 td->td_retval[0] = 0; 347 return (error); 348 } 349 350 int 351 linux_setxattr(struct thread *td, struct linux_setxattr_args *args) 352 { 353 struct setxattr_args eargs = { 354 .fd = -1, 355 .path = args->path, 356 .name = args->name, 357 .value = args->value, 358 .size = args->size, 359 .flags = args->flags, 360 .follow = FOLLOW, 361 }; 362 363 return (setxattr(td, &eargs)); 364 } 365 366 int 367 linux_lsetxattr(struct thread *td, struct linux_lsetxattr_args *args) 368 { 369 struct setxattr_args eargs = { 370 .fd = -1, 371 .path = args->path, 372 .name = args->name, 373 .value = args->value, 374 .size = args->size, 375 .flags = args->flags, 376 .follow = NOFOLLOW, 377 }; 378 379 return (setxattr(td, &eargs)); 380 } 381 382 int 383 linux_fsetxattr(struct thread *td, struct linux_fsetxattr_args *args) 384 { 385 struct setxattr_args eargs = { 386 .fd = args->fd, 387 .path = NULL, 388 .name = args->name, 389 .value = args->value, 390 .size = args->size, 391 .flags = args->flags, 392 .follow = 0, 393 }; 394 395 return (setxattr(td, &eargs)); 396 } 397