xref: /illumos-gate/usr/src/uts/common/fs/namefs/namevno.c (revision 7c478bd9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  * This file defines the vnode operations for mounted file descriptors.
35  * The routines in this file act as a layer between the NAMEFS file
36  * system and SPECFS/FIFOFS.  With the exception of nm_open(), nm_setattr(),
37  * nm_getattr() and nm_access(), the routines simply apply the VOP operation
38  * to the vnode representing the file descriptor.  This switches control
39  * to the underlying file system to which the file descriptor belongs.
40  */
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/cred.h>
45 #include <sys/errno.h>
46 #include <sys/time.h>
47 #include <sys/file.h>
48 #include <sys/fcntl.h>
49 #include <sys/flock.h>
50 #include <sys/kmem.h>
51 #include <sys/uio.h>
52 #include <sys/vfs.h>
53 #include <sys/vnode.h>
54 #include <sys/pcb.h>
55 #include <sys/signal.h>
56 #include <sys/user.h>
57 #include <sys/proc.h>
58 #include <sys/conf.h>
59 #include <sys/debug.h>
60 #include <vm/seg.h>
61 #include <sys/fs/namenode.h>
62 #include <sys/stream.h>
63 #include <fs/fs_subr.h>
64 #include <sys/policy.h>
65 
66 /*
67  * Create a reference to the vnode representing the file descriptor.
68  * Then, apply the VOP_OPEN operation to that vnode.
69  *
70  * The vnode for the file descriptor may be switched under you.
71  * If it is, search the hash list for an nodep - nodep->nm_filevp
72  * pair. If it exists, return that nodep to the user.
73  * If it does not exist, create a new namenode to attach
74  * to the nodep->nm_filevp then place the pair on the hash list.
75  *
76  * Newly created objects are like children/nodes in the mounted
77  * file system, with the parent being the initial mount.
78  */
79 int
80 nm_open(vnode_t **vpp, int flag, cred_t *crp)
81 {
82 	struct namenode *nodep = VTONM(*vpp);
83 	int error = 0;
84 	struct namenode *newnamep;
85 	struct vnode *newvp;
86 	struct vnode *infilevp;
87 	struct vnode *outfilevp;
88 
89 	/*
90 	 * If the vnode is switched under us, the corresponding
91 	 * VN_RELE for this VN_HOLD will be done by the file system
92 	 * performing the switch. Otherwise, the corresponding
93 	 * VN_RELE will be done by nm_close().
94 	 */
95 	infilevp = outfilevp = nodep->nm_filevp;
96 	VN_HOLD(outfilevp);
97 
98 	if ((error = VOP_OPEN(&outfilevp, flag, crp)) != 0) {
99 		VN_RELE(outfilevp);
100 		return (error);
101 	}
102 	if (infilevp != outfilevp) {
103 		/*
104 		 * See if the new filevp (outfilevp) is already associated
105 		 * with the mount point. If it is, then it already has a
106 		 * namenode associated with it.
107 		 */
108 		mutex_enter(&ntable_lock);
109 		if ((newnamep =
110 		    namefind(outfilevp, nodep->nm_mountpt)) != NULL) {
111 			struct vnode *vp = NMTOV(newnamep);
112 
113 			VN_HOLD(vp);
114 			goto gotit;
115 		}
116 
117 		newnamep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP);
118 		newvp = vn_alloc(KM_SLEEP);
119 		newnamep->nm_vnode = newvp;
120 
121 		mutex_init(&newnamep->nm_lock, NULL, MUTEX_DEFAULT, NULL);
122 
123 		mutex_enter(&nodep->nm_lock);
124 		newvp->v_flag = ((*vpp)->v_flag | VNOMAP | VNOSWAP) & ~VROOT;
125 		vn_setops(newvp, vn_getops(*vpp));
126 		newvp->v_vfsp = &namevfs;
127 		newvp->v_stream = outfilevp->v_stream;
128 		newvp->v_type = outfilevp->v_type;
129 		newvp->v_rdev = outfilevp->v_rdev;
130 		newvp->v_data = (caddr_t)newnamep;
131 		vn_exists(newvp);
132 		bcopy(&nodep->nm_vattr, &newnamep->nm_vattr, sizeof (vattr_t));
133 		newnamep->nm_vattr.va_type = outfilevp->v_type;
134 		newnamep->nm_vattr.va_nodeid = namenodeno_alloc();
135 		newnamep->nm_vattr.va_size = (u_offset_t)0;
136 		newnamep->nm_vattr.va_rdev = outfilevp->v_rdev;
137 		newnamep->nm_flag = NMNMNT;
138 		newnamep->nm_filevp = outfilevp;
139 		newnamep->nm_filep = nodep->nm_filep;
140 		newnamep->nm_mountpt = nodep->nm_mountpt;
141 		mutex_exit(&nodep->nm_lock);
142 
143 		/*
144 		 * Insert the new namenode into the hash list.
145 		 */
146 		nameinsert(newnamep);
147 gotit:
148 		mutex_exit(&ntable_lock);
149 		/*
150 		 * Release the above reference to the infilevp, the reference
151 		 * to the NAMEFS vnode, create a reference to the new vnode
152 		 * and return the new vnode to the user.
153 		 */
154 		VN_RELE(*vpp);
155 		*vpp = NMTOV(newnamep);
156 	}
157 	return (0);
158 }
159 
160 /*
161  * Close a mounted file descriptor.
162  * Remove any locks and apply the VOP_CLOSE operation to the vnode for
163  * the file descriptor.
164  */
165 static int
166 nm_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp)
167 {
168 	struct namenode *nodep = VTONM(vp);
169 	int error = 0;
170 
171 	(void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
172 	cleanshares(vp, ttoproc(curthread)->p_pid);
173 	error = VOP_CLOSE(nodep->nm_filevp, flag, count, offset, crp);
174 	if (count == 1) {
175 		(void) VOP_FSYNC(nodep->nm_filevp, FSYNC, crp);
176 		/*
177 		 * Before VN_RELE() we need to remove the vnode from
178 		 * the hash table.  We should only do so in the  NMNMNT case.
179 		 * In other cases, nodep->nm_filep keeps a reference
180 		 * to nm_filevp and the entry in the hash table doesn't
181 		 * hurt.
182 		 */
183 		if ((nodep->nm_flag & NMNMNT) != 0) {
184 			mutex_enter(&ntable_lock);
185 			nameremove(nodep);
186 			mutex_exit(&ntable_lock);
187 		}
188 		VN_RELE(nodep->nm_filevp);
189 	}
190 	return (error);
191 }
192 
193 static int
194 nm_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp,
195 	caller_context_t *ct)
196 {
197 	return (VOP_READ(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct));
198 }
199 
200 static int
201 nm_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp,
202 	caller_context_t *ct)
203 {
204 	return (VOP_WRITE(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct));
205 }
206 
207 static int
208 nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp)
209 {
210 	return (VOP_IOCTL(VTONM(vp)->nm_filevp, cmd, arg, mode, cr, rvalp));
211 }
212 
213 /*
214  * Return in vap the attributes that are stored in the namenode
215  * structure.  Only the size is taken from the mounted object.
216  */
217 /* ARGSUSED */
218 static int
219 nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp)
220 {
221 	struct namenode *nodep = VTONM(vp);
222 	struct vattr va;
223 	int error;
224 
225 	mutex_enter(&nodep->nm_lock);
226 	bcopy(&nodep->nm_vattr, vap, sizeof (vattr_t));
227 	mutex_exit(&nodep->nm_lock);
228 
229 	if ((va.va_mask = vap->va_mask & AT_SIZE) != 0) {
230 		if (error = VOP_GETATTR(nodep->nm_filevp, &va, flags, crp))
231 			return (error);
232 		vap->va_size = va.va_size;
233 	}
234 
235 	return (0);
236 }
237 
238 /*
239  * Standard access() like check.  Figure out which mode bits apply
240  * to the caller then pass the missing mode bits to the secpolicy function.
241  */
242 static int
243 nm_access_unlocked(void *vnp, int mode, cred_t *crp)
244 {
245 	struct namenode *nodep = vnp;
246 	int shift = 0;
247 
248 	if (crgetuid(crp) != nodep->nm_vattr.va_uid) {
249 		shift += 3;
250 		if (!groupmember(nodep->nm_vattr.va_gid, crp))
251 			shift += 3;
252 	}
253 	mode &= ~(nodep->nm_vattr.va_mode << shift);
254 
255 	if (mode == 0)
256 		return (0);
257 
258 	return (secpolicy_vnode_access(crp, NMTOV(nodep),
259 						nodep->nm_vattr.va_uid, mode));
260 }
261 /*
262  * Set the attributes of the namenode from the attributes in vap.
263  */
264 /* ARGSUSED */
265 static int
266 nm_setattr(
267 	vnode_t *vp,
268 	vattr_t *vap,
269 	int flags,
270 	cred_t *crp,
271 	caller_context_t *ctp)
272 {
273 	struct namenode *nodep = VTONM(vp);
274 	struct vattr *nmvap = &nodep->nm_vattr;
275 	long mask = vap->va_mask;
276 	int error = 0;
277 
278 	/*
279 	 * Cannot set these attributes.
280 	 */
281 	if (mask & (AT_NOSET|AT_SIZE))
282 		return (EINVAL);
283 
284 	(void) VOP_RWLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp);
285 	mutex_enter(&nodep->nm_lock);
286 
287 	/*
288 	 * Change ownership/group/time/access mode of mounted file
289 	 * descriptor.
290 	 */
291 
292 	error = secpolicy_vnode_setattr(crp, vp, vap, nmvap, flags,
293 					    nm_access_unlocked, nodep);
294 	if (error)
295 		goto out;
296 
297 	mask = vap->va_mask;
298 	/*
299 	 * If request to change mode, copy new
300 	 * mode into existing attribute structure.
301 	 */
302 	if (mask & AT_MODE)
303 		nmvap->va_mode = vap->va_mode & ~VSVTX;
304 
305 	/*
306 	 * If request was to change user or group, turn off suid and sgid
307 	 * bits.
308 	 * If the system was configured with the "rstchown" option, the
309 	 * owner is not permitted to give away the file, and can change
310 	 * the group id only to a group of which he or she is a member.
311 	 */
312 	if (mask & AT_UID)
313 		nmvap->va_uid = vap->va_uid;
314 	if (mask & AT_GID)
315 		nmvap->va_gid = vap->va_gid;
316 	/*
317 	 * If request is to modify times, make sure user has write
318 	 * permissions on the file.
319 	 */
320 	if (mask & AT_ATIME)
321 		nmvap->va_atime = vap->va_atime;
322 	if (mask & AT_MTIME) {
323 		nmvap->va_mtime = vap->va_mtime;
324 		gethrestime(&nmvap->va_ctime);
325 	}
326 out:
327 	mutex_exit(&nodep->nm_lock);
328 	VOP_RWUNLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp);
329 	return (error);
330 }
331 
332 /*
333  * Check mode permission on the namenode.  First nm_access_unlocked()
334  * checks the bits on the name node, then an access check is performed
335  * on the underlying file.
336  */
337 /* ARGSUSED */
338 static int
339 nm_access(vnode_t *vp, int mode, int flags, cred_t *crp)
340 {
341 	struct namenode *nodep = VTONM(vp);
342 	int error;
343 
344 	mutex_enter(&nodep->nm_lock);
345 	error = nm_access_unlocked(nodep, mode, crp);
346 	mutex_exit(&nodep->nm_lock);
347 	if (error == 0)
348 		return (VOP_ACCESS(nodep->nm_filevp, mode, flags, crp));
349 	else
350 		return (error);
351 }
352 
353 /*
354  * We can get here if a creat or open with O_CREAT is done on a namefs
355  * mount point, for example, as the object of a shell output redirection to
356  * the mount point.
357  */
358 /*ARGSUSED*/
359 static int
360 nm_create(vnode_t *dvp, char *name, vattr_t *vap, enum vcexcl excl,
361 	int mode, vnode_t **vpp, cred_t *cr, int flag)
362 {
363 	int error;
364 
365 	ASSERT(dvp && *name == '\0');
366 	if (excl == NONEXCL) {
367 		if (mode && (error = nm_access(dvp, mode, 0, cr)) != 0)
368 			return (error);
369 		VN_HOLD(dvp);
370 		return (0);
371 	}
372 	return (EEXIST);
373 }
374 
375 /*
376  * Links are not allowed on mounted file descriptors.
377  */
378 /*ARGSUSED*/
379 static int
380 nm_link(vnode_t *tdvp, vnode_t *vp, char *tnm, cred_t *crp)
381 {
382 	return (EXDEV);
383 }
384 
385 static int
386 nm_fsync(vnode_t *vp, int syncflag, cred_t *crp)
387 {
388 	return (VOP_FSYNC(VTONM(vp)->nm_filevp, syncflag, crp));
389 }
390 
391 /* Free the namenode */
392 /* ARGSUSED */
393 static void
394 nm_inactive(vnode_t *vp, cred_t *crp)
395 {
396 	struct namenode *nodep = VTONM(vp);
397 
398 	mutex_enter(&vp->v_lock);
399 	ASSERT(vp->v_count >= 1);
400 	if (--vp->v_count != 0) {
401 		mutex_exit(&vp->v_lock);
402 		return;
403 	}
404 	mutex_exit(&vp->v_lock);
405 	if (!(nodep->nm_flag & NMNMNT)) {
406 		ASSERT(nodep->nm_filep->f_vnode == nodep->nm_filevp);
407 		(void) closef(nodep->nm_filep);
408 	}
409 	vn_invalid(vp);
410 	vn_free(vp);
411 	namenodeno_free(nodep->nm_vattr.va_nodeid);
412 	kmem_free(nodep, sizeof (struct namenode));
413 }
414 
415 static int
416 nm_fid(vnode_t *vp, struct fid *fidnodep)
417 {
418 	return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep));
419 }
420 
421 static int
422 nm_rwlock(vnode_t *vp, int write, caller_context_t *ctp)
423 {
424 	return (VOP_RWLOCK(VTONM(vp)->nm_filevp, write, ctp));
425 }
426 
427 static void
428 nm_rwunlock(vnode_t *vp, int write, caller_context_t *ctp)
429 {
430 	VOP_RWUNLOCK(VTONM(vp)->nm_filevp, write, ctp);
431 }
432 
433 static int
434 nm_seek(vnode_t *vp, offset_t ooff, offset_t *noffp)
435 {
436 	return (VOP_SEEK(VTONM(vp)->nm_filevp, ooff, noffp));
437 }
438 
439 /*
440  * Return the vnode representing the file descriptor in vpp.
441  */
442 static int
443 nm_realvp(vnode_t *vp, vnode_t **vpp)
444 {
445 	struct vnode *rvp;
446 
447 	vp = VTONM(vp)->nm_filevp;
448 	if (VOP_REALVP(vp, &rvp) == 0)
449 		vp = rvp;
450 	*vpp = vp;
451 	return (0);
452 }
453 
454 static int
455 nm_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
456 	pollhead_t **phpp)
457 {
458 	return (VOP_POLL(VTONM(vp)->nm_filevp, events, anyyet, reventsp, phpp));
459 }
460 
461 struct vnodeops *nm_vnodeops;
462 
463 const fs_operation_def_t nm_vnodeops_template[] = {
464 	VOPNAME_OPEN, nm_open,
465 	VOPNAME_CLOSE, nm_close,
466 	VOPNAME_READ, nm_read,
467 	VOPNAME_WRITE, nm_write,
468 	VOPNAME_IOCTL, nm_ioctl,
469 	VOPNAME_GETATTR, nm_getattr,
470 	VOPNAME_SETATTR, nm_setattr,
471 	VOPNAME_ACCESS, nm_access,
472 	VOPNAME_CREATE, nm_create,
473 	VOPNAME_LINK, nm_link,
474 	VOPNAME_FSYNC, nm_fsync,
475 	VOPNAME_INACTIVE, (fs_generic_func_p) nm_inactive,
476 	VOPNAME_FID, nm_fid,
477 	VOPNAME_RWLOCK, nm_rwlock,
478 	VOPNAME_RWUNLOCK, (fs_generic_func_p) nm_rwunlock,
479 	VOPNAME_SEEK, nm_seek,
480 	VOPNAME_REALVP, nm_realvp,
481 	VOPNAME_POLL, (fs_generic_func_p) nm_poll,
482 	VOPNAME_DISPOSE, fs_error,
483 	NULL, NULL
484 };
485