xref: /freebsd/sys/kern/vfs_extattr.c (revision 2c905456312b2e5986afe3402a9c87d49eb9cf86)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1999-2001 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * This software was developed by Robert Watson for the TrustedBSD Project.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/capsicum.h>
34 #include <sys/lock.h>
35 #include <sys/mount.h>
36 #include <sys/mutex.h>
37 #include <sys/sysproto.h>
38 #include <sys/fcntl.h>
39 #include <sys/namei.h>
40 #include <sys/filedesc.h>
41 #include <sys/limits.h>
42 #include <sys/vnode.h>
43 #include <sys/proc.h>
44 #include <sys/extattr.h>
45 #include <sys/syscallsubr.h>
46 
47 #include <security/audit/audit.h>
48 #include <security/mac/mac_framework.h>
49 
50 static int	user_extattr_set_path(struct thread *td, const char *path,
51 		    int attrnamespace, const char *attrname, void *data,
52 		    size_t nbytes, int follow);
53 static int	user_extattr_get_path(struct thread *td, const char *path,
54 		    int attrnamespace, const char *attrname, void *data,
55 		    size_t nbytes, int follow);
56 static int	user_extattr_delete_path(struct thread *td, const char *path,
57 		    int attrnamespace, const char *attrname, int follow);
58 static int	user_extattr_list_path(struct thread *td, const char *path,
59 		    int attrnamespace, void *data, size_t nbytes, int follow);
60 
61 /*
62  * Syscall to push extended attribute configuration information into the VFS.
63  * Accepts a path, which it converts to a mountpoint, as well as a command
64  * (int cmd), and attribute name and misc data.
65  *
66  * Currently this is used only by UFS1 extended attributes.
67  */
68 #ifndef _SYS_SYSPROTO_H_
69 struct extattrctl_args {
70 	const char *path;
71 	int cmd;
72 	const char *filename;
73 	int attrnamespace;
74 	const char *attrname;
75 };
76 #endif
77 int
sys_extattrctl(struct thread * td,struct extattrctl_args * uap)78 sys_extattrctl(struct thread *td, struct extattrctl_args *uap)
79 {
80 	struct vnode *filename_vp;
81 	struct nameidata nd;
82 	struct mount *mp, *mp_writable;
83 	char attrname[EXTATTR_MAXNAMELEN + 1];
84 	int error;
85 
86 	AUDIT_ARG_CMD(uap->cmd);
87 	AUDIT_ARG_VALUE(uap->attrnamespace);
88 	/*
89 	 * uap->attrname is not always defined.  We check again later when we
90 	 * invoke the VFS call so as to pass in NULL there if needed.
91 	 */
92 	if (uap->attrname != NULL) {
93 		error = copyinstr(uap->attrname, attrname, sizeof(attrname),
94 		    NULL);
95 		if (error)
96 			return (error);
97 	}
98 	AUDIT_ARG_TEXT(attrname);
99 
100 	mp = NULL;
101 	filename_vp = NULL;
102 	if (uap->filename != NULL) {
103 		NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2, UIO_USERSPACE,
104 		    uap->filename);
105 		error = namei(&nd);
106 		if (error)
107 			return (error);
108 		filename_vp = nd.ni_vp;
109 		NDFREE_PNBUF(&nd);
110 	}
111 
112 	/* uap->path is always defined. */
113 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE,
114 	    uap->path);
115 	error = namei(&nd);
116 	if (error)
117 		goto out;
118 	mp = nd.ni_vp->v_mount;
119 	error = vfs_busy(mp, 0);
120 	if (error) {
121 		vput(nd.ni_vp);
122 		NDFREE_PNBUF(&nd);
123 		mp = NULL;
124 		goto out;
125 	}
126 	VOP_UNLOCK(nd.ni_vp);
127 	error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | V_PCATCH);
128 	vrele(nd.ni_vp);
129 	NDFREE_PNBUF(&nd);
130 	if (error)
131 		goto out;
132 	if (filename_vp != NULL) {
133 		/*
134 		 * uap->filename is not always defined.  If it is,
135 		 * grab a vnode lock, which VFS_EXTATTRCTL() will
136 		 * later release.
137 		 */
138 		error = vn_lock(filename_vp, LK_EXCLUSIVE);
139 		if (error) {
140 			vn_finished_write(mp_writable);
141 			goto out;
142 		}
143 	}
144 
145 	error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
146 	    uap->attrname != NULL ? attrname : NULL);
147 
148 	vn_finished_write(mp_writable);
149 out:
150 	if (mp != NULL)
151 		vfs_unbusy(mp);
152 
153 	/*
154 	 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
155 	 * so vrele it if it is defined.
156 	 */
157 	if (filename_vp != NULL)
158 		vrele(filename_vp);
159 	return (error);
160 }
161 
162 /*-
163  * Set a named extended attribute on a file or directory
164  *
165  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
166  *            kernelspace string pointer "attrname", userspace buffer
167  *            pointer "data", buffer length "nbytes", thread "td".
168  * Returns: 0 on success, an error number otherwise
169  * Locks: none
170  * References: vp must be a valid reference for the duration of the call
171  */
172 static int
extattr_set_vp(struct vnode * vp,int attrnamespace,const char * attrname,void * data,size_t nbytes,struct thread * td)173 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
174     void *data, size_t nbytes, struct thread *td)
175 {
176 	struct mount *mp;
177 	struct uio auio;
178 	struct iovec aiov;
179 	ssize_t cnt;
180 	int error;
181 
182 	if (nbytes > IOSIZE_MAX)
183 		return (EINVAL);
184 
185 	error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
186 	if (error)
187 		return (error);
188 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
189 
190 	aiov.iov_base = data;
191 	aiov.iov_len = nbytes;
192 	auio.uio_iov = &aiov;
193 	auio.uio_iovcnt = 1;
194 	auio.uio_offset = 0;
195 	auio.uio_resid = nbytes;
196 	auio.uio_rw = UIO_WRITE;
197 	auio.uio_segflg = UIO_USERSPACE;
198 	auio.uio_td = td;
199 	cnt = nbytes;
200 
201 #ifdef MAC
202 	error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
203 	    attrname);
204 	if (error)
205 		goto done;
206 #endif
207 
208 	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
209 	    td->td_ucred, td);
210 	cnt -= auio.uio_resid;
211 	td->td_retval[0] = cnt;
212 
213 #ifdef MAC
214 done:
215 #endif
216 	VOP_UNLOCK(vp);
217 	vn_finished_write(mp);
218 	return (error);
219 }
220 
221 #ifndef _SYS_SYSPROTO_H_
222 struct extattr_set_fd_args {
223 	int fd;
224 	int attrnamespace;
225 	const char *attrname;
226 	void *data;
227 	size_t nbytes;
228 };
229 #endif
230 int
sys_extattr_set_fd(struct thread * td,struct extattr_set_fd_args * uap)231 sys_extattr_set_fd(struct thread *td, struct extattr_set_fd_args *uap)
232 {
233 	char attrname[EXTATTR_MAXNAMELEN + 1];
234 	int error;
235 
236 	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
237 	if (error)
238 		return (error);
239 	return (kern_extattr_set_fd(td, uap->fd, uap->attrnamespace,
240 	    attrname, uap->data, uap->nbytes));
241 }
242 
243 int
kern_extattr_set_fp(struct thread * td,struct file * fp,int attrnamespace,const char * attrname,void * data,size_t nbytes)244 kern_extattr_set_fp(struct thread *td, struct file *fp, int attrnamespace,
245     const char *attrname, void *data, size_t nbytes)
246 {
247 
248 	return (extattr_set_vp(fp->f_vnode, attrnamespace, attrname, data,
249 	    nbytes, td));
250 }
251 
252 int
kern_extattr_set_fd(struct thread * td,int fd,int attrnamespace,const char * attrname,void * data,size_t nbytes)253 kern_extattr_set_fd(struct thread *td, int fd, int attrnamespace,
254     const char *attrname, void *data, size_t nbytes)
255 {
256 	struct file *fp;
257 	cap_rights_t rights;
258 	int error;
259 
260 	AUDIT_ARG_FD(fd);
261 	AUDIT_ARG_VALUE(attrnamespace);
262 	AUDIT_ARG_TEXT(attrname);
263 
264 	error = getvnode_path(td, fd,
265 	    cap_rights_init_one(&rights, CAP_EXTATTR_SET), NULL, &fp);
266 	if (error)
267 		return (error);
268 
269 	error = kern_extattr_set_fp(td, fp, attrnamespace, attrname, data,
270 	    nbytes);
271 	fdrop(fp, td);
272 
273 	return (error);
274 }
275 
276 #ifndef _SYS_SYSPROTO_H_
277 struct extattr_set_file_args {
278 	const char *path;
279 	int attrnamespace;
280 	const char *attrname;
281 	void *data;
282 	size_t nbytes;
283 };
284 #endif
285 int
sys_extattr_set_file(struct thread * td,struct extattr_set_file_args * uap)286 sys_extattr_set_file(struct thread *td, struct extattr_set_file_args *uap)
287 {
288 
289 	return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
290 	    uap->attrname, uap->data, uap->nbytes, FOLLOW));
291 }
292 
293 #ifndef _SYS_SYSPROTO_H_
294 struct extattr_set_link_args {
295 	const char *path;
296 	int attrnamespace;
297 	const char *attrname;
298 	void *data;
299 	size_t nbytes;
300 };
301 #endif
302 int
sys_extattr_set_link(struct thread * td,struct extattr_set_link_args * uap)303 sys_extattr_set_link(struct thread *td, struct extattr_set_link_args *uap)
304 {
305 
306 	return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
307 	    uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
308 }
309 
310 static int
user_extattr_set_path(struct thread * td,const char * path,int attrnamespace,const char * uattrname,void * data,size_t nbytes,int follow)311 user_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
312     const char *uattrname, void *data, size_t nbytes, int follow)
313 {
314 	char attrname[EXTATTR_MAXNAMELEN + 1];
315 	int error;
316 
317 	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
318 	if (error)
319 		return (error);
320 	return (kern_extattr_set_path(td, path, attrnamespace,
321 	    attrname, data, nbytes, follow, UIO_USERSPACE));
322 }
323 
324 int
kern_extattr_set_path(struct thread * td,const char * path,int attrnamespace,const char * attrname,void * data,size_t nbytes,int follow,enum uio_seg pathseg)325 kern_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
326     const char *attrname, void *data, size_t nbytes, int follow,
327     enum uio_seg pathseg)
328 {
329 	struct nameidata nd;
330 	int error;
331 
332 	AUDIT_ARG_VALUE(attrnamespace);
333 	AUDIT_ARG_TEXT(attrname);
334 
335 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
336 	error = namei(&nd);
337 	if (error)
338 		return (error);
339 	NDFREE_PNBUF(&nd);
340 
341 	error = extattr_set_vp(nd.ni_vp, attrnamespace, attrname, data,
342 	    nbytes, td);
343 
344 	vrele(nd.ni_vp);
345 	return (error);
346 }
347 
348 /*-
349  * Get a named extended attribute on a file or directory
350  *
351  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
352  *            kernelspace string pointer "attrname", userspace buffer
353  *            pointer "data", buffer length "nbytes", thread "td".
354  * Returns: 0 on success, an error number otherwise
355  * Locks: none
356  * References: vp must be a valid reference for the duration of the call
357  */
358 static int
extattr_get_vp(struct vnode * vp,int attrnamespace,const char * attrname,void * data,size_t nbytes,struct thread * td)359 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
360     void *data, size_t nbytes, struct thread *td)
361 {
362 	struct uio auio, *auiop;
363 	struct iovec aiov;
364 	ssize_t cnt;
365 	size_t size, *sizep;
366 	int error;
367 
368 	if (nbytes > IOSIZE_MAX)
369 		return (EINVAL);
370 
371 	vn_lock(vp, LK_SHARED | LK_RETRY);
372 
373 	/*
374 	 * Slightly unusual semantics: if the user provides a NULL data
375 	 * pointer, they don't want to receive the data, just the maximum
376 	 * read length.
377 	 */
378 	auiop = NULL;
379 	sizep = NULL;
380 	cnt = 0;
381 	if (data != NULL) {
382 		aiov.iov_base = data;
383 		aiov.iov_len = nbytes;
384 		auio.uio_iov = &aiov;
385 		auio.uio_iovcnt = 1;
386 		auio.uio_offset = 0;
387 		auio.uio_resid = nbytes;
388 		auio.uio_rw = UIO_READ;
389 		auio.uio_segflg = UIO_USERSPACE;
390 		auio.uio_td = td;
391 		auiop = &auio;
392 		cnt = nbytes;
393 	} else
394 		sizep = &size;
395 
396 #ifdef MAC
397 	error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
398 	    attrname);
399 	if (error)
400 		goto done;
401 #endif
402 
403 	error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
404 	    td->td_ucred, td);
405 
406 	if (auiop != NULL) {
407 		cnt -= auio.uio_resid;
408 		td->td_retval[0] = cnt;
409 	} else
410 		td->td_retval[0] = size;
411 #ifdef MAC
412 done:
413 #endif
414 	VOP_UNLOCK(vp);
415 	return (error);
416 }
417 
418 #ifndef _SYS_SYSPROTO_H_
419 struct extattr_get_fd_args {
420 	int fd;
421 	int attrnamespace;
422 	const char *attrname;
423 	void *data;
424 	size_t nbytes;
425 };
426 #endif
427 int
sys_extattr_get_fd(struct thread * td,struct extattr_get_fd_args * uap)428 sys_extattr_get_fd(struct thread *td, struct extattr_get_fd_args *uap)
429 {
430 	char attrname[EXTATTR_MAXNAMELEN + 1];
431 	int error;
432 
433 	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
434 	if (error)
435 		return (error);
436 	return (kern_extattr_get_fd(td, uap->fd, uap->attrnamespace,
437 	    attrname, uap->data, uap->nbytes));
438 }
439 
440 int
kern_extattr_get_fp(struct thread * td,struct file * fp,int attrnamespace,const char * attrname,void * data,size_t nbytes)441 kern_extattr_get_fp(struct thread *td, struct file *fp, int attrnamespace,
442     const char *attrname, void *data, size_t nbytes)
443 {
444 
445 	return (extattr_get_vp(fp->f_vnode, attrnamespace, attrname, data,
446 	    nbytes, td));
447 }
448 
449 int
kern_extattr_get_fd(struct thread * td,int fd,int attrnamespace,const char * attrname,void * data,size_t nbytes)450 kern_extattr_get_fd(struct thread *td, int fd, int attrnamespace,
451     const char *attrname, void *data, size_t nbytes)
452 {
453 	struct file *fp;
454 	cap_rights_t rights;
455 	int error;
456 
457 	AUDIT_ARG_FD(fd);
458 	AUDIT_ARG_VALUE(attrnamespace);
459 	AUDIT_ARG_TEXT(attrname);
460 
461 	error = getvnode_path(td, fd,
462 	    cap_rights_init_one(&rights, CAP_EXTATTR_GET), NULL, &fp);
463 	if (error)
464 		return (error);
465 
466 	error = kern_extattr_get_fp(td, fp, attrnamespace, attrname, data,
467 	    nbytes);
468 
469 	fdrop(fp, td);
470 	return (error);
471 }
472 
473 #ifndef _SYS_SYSPROTO_H_
474 struct extattr_get_file_args {
475 	const char *path;
476 	int attrnamespace;
477 	const char *attrname;
478 	void *data;
479 	size_t nbytes;
480 };
481 #endif
482 int
sys_extattr_get_file(struct thread * td,struct extattr_get_file_args * uap)483 sys_extattr_get_file(struct thread *td, struct extattr_get_file_args *uap)
484 {
485 	return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
486 	    uap->attrname, uap->data, uap->nbytes, FOLLOW));
487 }
488 
489 #ifndef _SYS_SYSPROTO_H_
490 struct extattr_get_link_args {
491 	const char *path;
492 	int attrnamespace;
493 	const char *attrname;
494 	void *data;
495 	size_t nbytes;
496 };
497 #endif
498 int
sys_extattr_get_link(struct thread * td,struct extattr_get_link_args * uap)499 sys_extattr_get_link(struct thread *td, struct extattr_get_link_args *uap)
500 {
501 	return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
502 	    uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
503 }
504 
505 static int
user_extattr_get_path(struct thread * td,const char * path,int attrnamespace,const char * uattrname,void * data,size_t nbytes,int follow)506 user_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
507     const char *uattrname, void *data, size_t nbytes, int follow)
508 {
509 	char attrname[EXTATTR_MAXNAMELEN + 1];
510 	int error;
511 
512 	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
513 	if (error)
514 		return (error);
515 	return (kern_extattr_get_path(td, path, attrnamespace,
516 	    attrname, data, nbytes, follow, UIO_USERSPACE));
517 }
518 
519 int
kern_extattr_get_path(struct thread * td,const char * path,int attrnamespace,const char * attrname,void * data,size_t nbytes,int follow,enum uio_seg pathseg)520 kern_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
521     const char *attrname, void *data, size_t nbytes, int follow,
522     enum uio_seg pathseg)
523 {
524 	struct nameidata nd;
525 	int error;
526 
527 	AUDIT_ARG_VALUE(attrnamespace);
528 	AUDIT_ARG_TEXT(attrname);
529 
530 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
531 	error = namei(&nd);
532 	if (error)
533 		return (error);
534 	NDFREE_PNBUF(&nd);
535 
536 	error = extattr_get_vp(nd.ni_vp, attrnamespace, attrname, data,
537 	    nbytes, td);
538 
539 	vrele(nd.ni_vp);
540 	return (error);
541 }
542 
543 /*
544  * extattr_delete_vp(): Delete a named extended attribute on a file or
545  *                      directory
546  *
547  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
548  *            kernelspace string pointer "attrname", proc "p"
549  * Returns: 0 on success, an error number otherwise
550  * Locks: none
551  * References: vp must be a valid reference for the duration of the call
552  */
553 static int
extattr_delete_vp(struct vnode * vp,int attrnamespace,const char * attrname,struct thread * td)554 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
555     struct thread *td)
556 {
557 	struct mount *mp;
558 	int error;
559 
560 	error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
561 	if (error)
562 		return (error);
563 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
564 
565 #ifdef MAC
566 	error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
567 	    attrname);
568 	if (error)
569 		goto done;
570 #endif
571 
572 	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
573 	    td);
574 	if (error == EOPNOTSUPP)
575 		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
576 		    td->td_ucred, td);
577 #ifdef MAC
578 done:
579 #endif
580 	VOP_UNLOCK(vp);
581 	vn_finished_write(mp);
582 	return (error);
583 }
584 
585 #ifndef _SYS_SYSPROTO_H_
586 struct extattr_delete_fd_args {
587 	int fd;
588 	int attrnamespace;
589 	const char *attrname;
590 };
591 #endif
592 int
sys_extattr_delete_fd(struct thread * td,struct extattr_delete_fd_args * uap)593 sys_extattr_delete_fd(struct thread *td, struct extattr_delete_fd_args *uap)
594 {
595 	char attrname[EXTATTR_MAXNAMELEN + 1];
596 	int error;
597 
598 	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
599 	if (error)
600 		return (error);
601 	return (kern_extattr_delete_fd(td, uap->fd, uap->attrnamespace,
602 	    attrname));
603 }
604 
605 int
kern_extattr_delete_fp(struct thread * td,struct file * fp,int attrnamespace,const char * attrname)606 kern_extattr_delete_fp(struct thread *td, struct file *fp, int attrnamespace,
607     const char *attrname)
608 {
609 
610 	return (extattr_delete_vp(fp->f_vnode, attrnamespace, attrname, td));
611 }
612 
613 int
kern_extattr_delete_fd(struct thread * td,int fd,int attrnamespace,const char * attrname)614 kern_extattr_delete_fd(struct thread *td, int fd, int attrnamespace,
615     const char *attrname)
616 {
617 	struct file *fp;
618 	cap_rights_t rights;
619 	int error;
620 
621 	AUDIT_ARG_FD(fd);
622 	AUDIT_ARG_VALUE(attrnamespace);
623 	AUDIT_ARG_TEXT(attrname);
624 
625 	error = getvnode_path(td, fd,
626 	    cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), NULL, &fp);
627 	if (error)
628 		return (error);
629 
630 	error = kern_extattr_delete_fp(td, fp, attrnamespace, attrname);
631 	fdrop(fp, td);
632 	return (error);
633 }
634 
635 #ifndef _SYS_SYSPROTO_H_
636 struct extattr_delete_file_args {
637 	const char *path;
638 	int attrnamespace;
639 	const char *attrname;
640 };
641 #endif
642 int
sys_extattr_delete_file(struct thread * td,struct extattr_delete_file_args * uap)643 sys_extattr_delete_file(struct thread *td, struct extattr_delete_file_args *uap)
644 {
645 
646 	return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
647 	    uap->attrname, FOLLOW));
648 }
649 
650 #ifndef _SYS_SYSPROTO_H_
651 struct extattr_delete_link_args {
652 	const char *path;
653 	int attrnamespace;
654 	const char *attrname;
655 };
656 #endif
657 int
sys_extattr_delete_link(struct thread * td,struct extattr_delete_link_args * uap)658 sys_extattr_delete_link(struct thread *td, struct extattr_delete_link_args *uap)
659 {
660 
661 	return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
662 	    uap->attrname, NOFOLLOW));
663 }
664 
665 int
user_extattr_delete_path(struct thread * td,const char * path,int attrnamespace,const char * uattrname,int follow)666 user_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
667     const char *uattrname, int follow)
668 {
669 	char attrname[EXTATTR_MAXNAMELEN + 1];
670 	int error;
671 
672 	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
673 	if (error)
674 		return(error);
675 	return (kern_extattr_delete_path(td, path, attrnamespace,
676 	    attrname, follow, UIO_USERSPACE));
677 }
678 
679 int
kern_extattr_delete_path(struct thread * td,const char * path,int attrnamespace,const char * attrname,int follow,enum uio_seg pathseg)680 kern_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
681     const char *attrname, int follow, enum uio_seg pathseg)
682 {
683 	struct nameidata nd;
684 	int error;
685 
686 	AUDIT_ARG_VALUE(attrnamespace);
687 	AUDIT_ARG_TEXT(attrname);
688 
689 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
690 	error = namei(&nd);
691 	if (error)
692 		return(error);
693 	NDFREE_PNBUF(&nd);
694 
695 	error = extattr_delete_vp(nd.ni_vp, attrnamespace, attrname, td);
696 	vrele(nd.ni_vp);
697 	return(error);
698 }
699 
700 /*-
701  * Retrieve a list of extended attributes on a file or directory.
702  *
703  * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
704  *            userspace buffer pointer "data", buffer length "nbytes",
705  *            thread "td".
706  * Returns: 0 on success, an error number otherwise
707  * Locks: none
708  * References: vp must be a valid reference for the duration of the call
709  */
710 static int
extattr_list_vp(struct vnode * vp,int attrnamespace,struct uio * auiop,struct thread * td)711 extattr_list_vp(struct vnode *vp, int attrnamespace, struct uio *auiop,
712     struct thread *td)
713 {
714 	size_t size, *sizep;
715 	ssize_t cnt;
716 	int error;
717 
718 	sizep = NULL;
719 	cnt = 0;
720 	if (auiop != NULL) {
721 		if (auiop->uio_resid > IOSIZE_MAX)
722 			return (EINVAL);
723 		cnt = auiop->uio_resid;
724 	} else
725 		sizep = &size;
726 
727 	vn_lock(vp, LK_SHARED | LK_RETRY);
728 
729 #ifdef MAC
730 	error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
731 	if (error) {
732 		VOP_UNLOCK(vp);
733 		return (error);
734 	}
735 #endif
736 
737 	error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
738 	    td->td_ucred, td);
739 	VOP_UNLOCK(vp);
740 
741 	if (auiop != NULL) {
742 		cnt -= auiop->uio_resid;
743 		td->td_retval[0] = cnt;
744 	} else
745 		td->td_retval[0] = size;
746 	return (error);
747 }
748 
749 #ifndef _SYS_SYSPROTO_H_
750 struct extattr_list_fd_args {
751 	int fd;
752 	int attrnamespace;
753 	void *data;
754 	size_t nbytes;
755 };
756 #endif
757 int
sys_extattr_list_fd(struct thread * td,struct extattr_list_fd_args * uap)758 sys_extattr_list_fd(struct thread *td, struct extattr_list_fd_args *uap)
759 {
760 	struct uio auio, *auiop;
761 	struct iovec aiov;
762 
763 	if (uap->data != NULL) {
764 		aiov.iov_base = uap->data;
765 		aiov.iov_len = uap->nbytes;
766 		auio.uio_iov = &aiov;
767 		auio.uio_iovcnt = 1;
768 		auio.uio_offset = 0;
769 		auio.uio_resid = uap->nbytes;
770 		auio.uio_rw = UIO_READ;
771 		auio.uio_segflg = UIO_USERSPACE;
772 		auio.uio_td = td;
773 		auiop = &auio;
774 	} else
775 		auiop = NULL;
776 
777 	return (kern_extattr_list_fd(td, uap->fd, uap->attrnamespace,
778 	    auiop));
779 }
780 
781 int
kern_extattr_list_fp(struct thread * td,struct file * fp,int attrnamespace,struct uio * auiop)782 kern_extattr_list_fp(struct thread *td, struct file *fp, int attrnamespace,
783     struct uio *auiop)
784 {
785 
786 	return (extattr_list_vp(fp->f_vnode, attrnamespace, auiop, td));
787 }
788 
789 int
kern_extattr_list_fd(struct thread * td,int fd,int attrnamespace,struct uio * auiop)790 kern_extattr_list_fd(struct thread *td, int fd, int attrnamespace,
791     struct uio *auiop)
792 {
793 	struct file *fp;
794 	cap_rights_t rights;
795 	int error;
796 
797 	AUDIT_ARG_FD(fd);
798 	AUDIT_ARG_VALUE(attrnamespace);
799 	error = getvnode_path(td, fd,
800 	    cap_rights_init_one(&rights, CAP_EXTATTR_LIST), NULL, &fp);
801 	if (error)
802 		return (error);
803 
804 	error = kern_extattr_list_fp(td, fp, attrnamespace, auiop);
805 
806 	fdrop(fp, td);
807 	return (error);
808 }
809 
810 #ifndef _SYS_SYSPROTO_H_
811 struct extattr_list_file_args {
812 	const char *path;
813 	int attrnamespace;
814 	void *data;
815 	size_t nbytes;
816 }
817 #endif
818 int
sys_extattr_list_file(struct thread * td,struct extattr_list_file_args * uap)819 sys_extattr_list_file(struct thread *td, struct extattr_list_file_args *uap)
820 {
821 
822 	return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
823 	    uap->data, uap->nbytes, FOLLOW));
824 }
825 
826 #ifndef _SYS_SYSPROTO_H_
827 struct extattr_list_link_args {
828 	const char *path;
829 	int attrnamespace;
830 	void *data;
831 	size_t nbytes;
832 };
833 #endif
834 int
sys_extattr_list_link(struct thread * td,struct extattr_list_link_args * uap)835 sys_extattr_list_link(struct thread *td, struct extattr_list_link_args *uap)
836 {
837 
838 	return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
839 	    uap->data, uap->nbytes, NOFOLLOW));
840 }
841 
842 static int
user_extattr_list_path(struct thread * td,const char * path,int attrnamespace,void * data,size_t nbytes,int follow)843 user_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
844     void *data, size_t nbytes, int follow)
845 {
846 	struct uio auio, *auiop;
847 	struct iovec aiov;
848 
849 	if (data != NULL) {
850 		aiov.iov_base = data;
851 		aiov.iov_len = nbytes;
852 		auio.uio_iov = &aiov;
853 		auio.uio_iovcnt = 1;
854 		auio.uio_offset = 0;
855 		auio.uio_resid = nbytes;
856 		auio.uio_rw = UIO_READ;
857 		auio.uio_segflg = UIO_USERSPACE;
858 		auio.uio_td = td;
859 		auiop = &auio;
860 	} else
861 		auiop = NULL;
862 
863 	return (kern_extattr_list_path(td, path, attrnamespace,
864 	    auiop, follow, UIO_USERSPACE));
865 }
866 
867 int
kern_extattr_list_path(struct thread * td,const char * path,int attrnamespace,struct uio * auiop,int follow,enum uio_seg pathseg)868 kern_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
869     struct uio *auiop, int follow, enum uio_seg pathseg)
870 {
871 	struct nameidata nd;
872 	int error;
873 
874 	AUDIT_ARG_VALUE(attrnamespace);
875 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
876 	error = namei(&nd);
877 	if (error)
878 		return (error);
879 	NDFREE_PNBUF(&nd);
880 
881 	error = extattr_list_vp(nd.ni_vp, attrnamespace, auiop, td);
882 
883 	vrele(nd.ni_vp);
884 	return (error);
885 }
886