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