xref: /illumos-gate/usr/src/uts/common/syscall/open.c (revision e67da9d6e15809e01554ff9bb20d0ddf210dbc6c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved	*/
28 
29 /*
30  * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
31  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
32  * Copyright 2026 Oxide Computer Company
33  */
34 /*
35  * Portions of this source code were derived from Berkeley 4.3 BSD
36  * under license from the Regents of the University of California.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/isa_defs.h>
41 #include <sys/types.h>
42 #include <sys/sysmacros.h>
43 #include <sys/user.h>
44 #include <sys/systm.h>
45 #include <sys/errno.h>
46 #include <sys/fcntl.h>
47 #include <sys/stat.h>
48 #include <sys/vnode.h>
49 #include <sys/vfs.h>
50 #include <sys/file.h>
51 #include <sys/mode.h>
52 #include <sys/model.h>
53 #include <sys/uio.h>
54 #include <sys/debug.h>
55 #include <c2/audit.h>
56 
57 /*
58  * Common code for openat().  Check permissions, allocate an open
59  * file structure, and call the device open routine (if any).
60  */
61 
62 static int
copen(int startfd,char * fname,int filemode,int createmode,uio_seg_t seg)63 copen(int startfd, char *fname, int filemode, int createmode, uio_seg_t seg)
64 {
65 	struct pathname pn;
66 	vnode_t *vp, *sdvp;
67 	file_t *fp, *startfp;
68 	enum vtype type;
69 	int error;
70 	int fd, dupfd;
71 	vnode_t *startvp;
72 	proc_t *p = curproc;
73 	char *open_filename = fname;
74 	uint32_t auditing = AU_AUDITING();
75 	char startchar;
76 
77 	if (filemode & (FSEARCH|FEXEC)) {
78 		/*
79 		 * Must be one or the other and neither FREAD nor FWRITE
80 		 * Must not be any of FAPPEND FCREAT FTRUNC FXATTR FXATTRDIROPEN
81 		 * XXX: Should these just be silently ignored?
82 		 */
83 		if ((filemode & (FREAD|FWRITE)) ||
84 		    (filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
85 		    (filemode & (FAPPEND|FCREAT|FTRUNC|FXATTR|FXATTRDIROPEN)))
86 			return (set_errno(EINVAL));
87 	}
88 
89 	if (startfd == AT_FDCWD) {
90 		/*
91 		 * Regular open()
92 		 */
93 		startvp = NULL;
94 	} else {
95 		/*
96 		 * We're here via openat()
97 		 */
98 		if (uio_copyin(fname, &startchar, sizeof (char), seg))
99 			return (set_errno(EFAULT));
100 
101 		/*
102 		 * if startchar is / then startfd is ignored
103 		 */
104 		if (startchar == '/')
105 			startvp = NULL;
106 		else {
107 			if ((startfp = getf(startfd)) == NULL)
108 				return (set_errno(EBADF));
109 			startvp = startfp->f_vnode;
110 			VN_HOLD(startvp);
111 			releasef(startfd);
112 		}
113 	}
114 
115 	/*
116 	 * Handle __openattrdirat() requests
117 	 */
118 	if (filemode & FXATTRDIROPEN) {
119 		if (auditing && startvp != NULL)
120 			audit_setfsat_path(1);
121 		error = lookupnameat(fname, seg, FOLLOW, NULLVPP, &vp, startvp);
122 		if (startvp != NULL)
123 			VN_RELE(startvp);
124 		if (error != 0)
125 			return (set_errno(error));
126 
127 		startvp = vp;
128 	}
129 
130 	/*
131 	 * Do we need to go into extended attribute space?
132 	 */
133 	if (filemode & FXATTR) {
134 		if (startfd == AT_FDCWD) {
135 			if (uio_copyin(fname, &startchar, sizeof (char), seg))
136 				return (set_errno(EFAULT));
137 
138 			/*
139 			 * If startchar == '/' then no extended attributes
140 			 * are looked up.
141 			 */
142 			if (startchar == '/') {
143 				startvp = NULL;
144 			} else {
145 				mutex_enter(&p->p_lock);
146 				startvp = PTOU(p)->u_cdir;
147 				VN_HOLD(startvp);
148 				mutex_exit(&p->p_lock);
149 			}
150 		}
151 
152 		/*
153 		 * Make sure we have a valid extended attribute request.
154 		 * We must either have a real fd or AT_FDCWD and a relative
155 		 * pathname.
156 		 */
157 		if (startvp == NULL) {
158 			goto noxattr;
159 		}
160 	}
161 
162 	if (filemode & (FXATTR|FXATTRDIROPEN)) {
163 		vattr_t vattr;
164 
165 		if (error = pn_get(fname, seg, &pn)) {
166 			goto out;
167 		}
168 
169 		/*
170 		 * In order to access hidden attribute directory the
171 		 * user must be able to stat() the file
172 		 */
173 		vattr.va_mask = AT_ALL;
174 		if (error = VOP_GETATTR(startvp, &vattr, 0, CRED(), NULL)) {
175 			pn_free(&pn);
176 			goto out;
177 		}
178 
179 		if ((startvp->v_vfsp->vfs_flag & VFS_XATTR) != 0 ||
180 		    vfs_has_feature(startvp->v_vfsp, VFSFT_SYSATTR_VIEWS)) {
181 			error = VOP_LOOKUP(startvp, "", &sdvp, &pn,
182 			    (filemode & FXATTRDIROPEN) ? LOOKUP_XATTR :
183 			    LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED(),
184 			    NULL, NULL, NULL);
185 		} else {
186 			error = EINVAL;
187 		}
188 
189 		/*
190 		 * For __openattrdirat() use "." as filename to open
191 		 * as part of vn_openat()
192 		 */
193 		if (error == 0 && (filemode & FXATTRDIROPEN)) {
194 			open_filename = ".";
195 			seg = UIO_SYSSPACE;
196 		}
197 
198 		pn_free(&pn);
199 		if (error != 0)
200 			goto out;
201 
202 		VN_RELE(startvp);
203 		startvp = sdvp;
204 	}
205 
206 noxattr:
207 	if ((filemode & (FREAD|FWRITE|FSEARCH|FEXEC|FXATTRDIROPEN)) != 0) {
208 		if ((filemode & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY))
209 			filemode &= ~FNDELAY;
210 		error = falloc((vnode_t *)NULL, filemode, &fp, &fd);
211 		if (error == 0) {
212 			if (auditing && startvp != NULL)
213 				audit_setfsat_path(1);
214 			/*
215 			 * Last arg is a don't-care term if
216 			 * !(filemode & FCREAT).
217 			 */
218 			error = vn_openat(open_filename, seg, filemode,
219 			    (int)(createmode & MODEMASK),
220 			    &vp, CRCREAT, PTOU(curproc)->u_cmask,
221 			    startvp, fd);
222 
223 			if (startvp != NULL)
224 				VN_RELE(startvp);
225 			if (error == 0) {
226 				if ((vp->v_flag & VDUP) == 0) {
227 					fp->f_vnode = vp;
228 					mutex_exit(&fp->f_tlock);
229 					/*
230 					 * We must now fill in the slot
231 					 * falloc reserved.
232 					 */
233 					setf(fd, fp);
234 					if ((filemode & FCLOEXEC) != 0) {
235 						f_setfd_or(fd, FD_CLOEXEC);
236 					}
237 
238 					if ((filemode & FCLOFORK) != 0) {
239 						f_setfd_or(fd, FD_CLOFORK);
240 					}
241 					return (fd);
242 				} else {
243 					/*
244 					 * Special handling for /dev/fd.
245 					 * Give up the file pointer
246 					 * and dup the indicated file descriptor
247 					 * (in v_rdev). This is ugly, but I've
248 					 * seen worse.
249 					 */
250 					unfalloc(fp);
251 					dupfd = getminor(vp->v_rdev);
252 					type = vp->v_type;
253 					mutex_enter(&vp->v_lock);
254 					vp->v_flag &= ~VDUP;
255 					mutex_exit(&vp->v_lock);
256 					VN_RELE(vp);
257 					if (type != VCHR)
258 						return (set_errno(EINVAL));
259 					if ((fp = getf(dupfd)) == NULL) {
260 						setf(fd, NULL);
261 						return (set_errno(EBADF));
262 					}
263 					mutex_enter(&fp->f_tlock);
264 					fp->f_count++;
265 					mutex_exit(&fp->f_tlock);
266 					setf(fd, fp);
267 					if ((filemode & FCLOEXEC) != 0) {
268 						f_setfd_or(fd, FD_CLOEXEC);
269 					}
270 
271 					if ((filemode & FCLOFORK) != 0) {
272 						f_setfd_or(fd, FD_CLOFORK);
273 					}
274 					releasef(dupfd);
275 				}
276 				return (fd);
277 			} else {
278 				setf(fd, NULL);
279 				unfalloc(fp);
280 				return (set_errno(error));
281 			}
282 		}
283 	} else {
284 		error = EINVAL;
285 	}
286 out:
287 	if (startvp != NULL)
288 		VN_RELE(startvp);
289 	return (set_errno(error));
290 }
291 
292 #define	OPENMODE32(fmode)	(((fmode) & (FSEARCH | FEXEC))? \
293 				    (fmode) : (fmode) - FOPEN)
294 #define	OPENMODE64(fmode)	(OPENMODE32(fmode) | FOFFMAX)
295 #ifdef _LP64
296 #define	OPENMODE(fmode)		OPENMODE64(fmode)
297 #else
298 #define	OPENMODE(fmode)		OPENMODE32(fmode)
299 #endif
300 
301 /*
302  * Kernel-callable open of a kernel-resident path, used to apply spawn(2)
303  * file actions in a spawned child.
304  *
305  * copen() reports failure by returning the error number from set_errno(),
306  * which is indistinguishable by value from a valid file descriptor. We
307  * convert to the convention expected by an in-kernel caller - a return of
308  * -1 with the error left in lwp_errno, and the file descriptor otherwise.
309  */
310 int
kopenat(int startfd,char * path,int fmode,int cmode,model_t model)311 kopenat(int startfd, char *path, int fmode, int cmode, model_t model)
312 {
313 	klwp_t *lwp = ttolwp(curthread);
314 	int fd;
315 
316 	if (model == DATAMODEL_ILP32)
317 		fmode = OPENMODE32(fmode);
318 	else
319 		fmode = OPENMODE64(fmode);
320 
321 	lwp->lwp_errno = 0;
322 	fd = copen(startfd, path, fmode, cmode, UIO_SYSSPACE);
323 	if (lwp->lwp_errno != 0)
324 		return (-1);
325 
326 	return (fd);
327 }
328 
329 /*
330  * Open a file.
331  */
332 int
openat(int fd,char * path,int fmode,int cmode)333 openat(int fd, char *path, int fmode, int cmode)
334 {
335 	return (copen(fd, path, OPENMODE(fmode), cmode, UIO_USERSPACE));
336 }
337 
338 int
open(char * path,int fmode,int cmode)339 open(char *path, int fmode, int cmode)
340 {
341 	return (openat(AT_FDCWD, path, fmode, cmode));
342 }
343 
344 #if defined(_ILP32) || defined(_SYSCALL32_IMPL)
345 /*
346  * Open for large files in 32-bit environment. Sets the FOFFMAX flag.
347  */
348 int
openat64(int fd,char * path,int fmode,int cmode)349 openat64(int fd, char *path, int fmode, int cmode)
350 {
351 	return (copen(fd, path, OPENMODE64(fmode), cmode, UIO_USERSPACE));
352 }
353 
354 int
open64(char * path,int fmode,int cmode)355 open64(char *path, int fmode, int cmode)
356 {
357 	return (openat64(AT_FDCWD, path, fmode, cmode));
358 }
359 
360 #endif	/* _ILP32 || _SYSCALL32_IMPL */
361 
362 #ifdef _SYSCALL32_IMPL
363 /*
364  * Open for 32-bit compatibility on 64-bit kernel
365  */
366 int
openat32(int fd,char * path,int fmode,int cmode)367 openat32(int fd, char *path, int fmode, int cmode)
368 {
369 	return (copen(fd, path, OPENMODE32(fmode), cmode, UIO_USERSPACE));
370 }
371 
372 int
open32(char * path,int fmode,int cmode)373 open32(char *path, int fmode, int cmode)
374 {
375 	return (openat32(AT_FDCWD, path, fmode, cmode));
376 }
377 
378 #endif	/* _SYSCALL32_IMPL */
379