1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
14  * Copyright 2024 RackTop Systems, Inc.
15  */
16 
17 /*
18  * The SMB server supports its local file system operations using
19  * kernel-style VOP_... calls.  This layer simulates creating and
20  * finding vnodes for "libfksmbsrv".
21  *
22  * The vnodes manged here are always paired with a private struct
23  * (see fakefs_node_t) to hold the details we need to find them
24  * in our cache and the file descriptor used in simulations.
25  *
26  * The actual VOP_... and VFS_... call simulations are in other
27  * files, generall named after the original kernel ones.
28  * (eg. fake_vfs.c)
29  */
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/cmn_err.h>
35 #include <sys/cred.h>
36 #include <sys/debug.h>
37 #include <sys/errno.h>
38 #include <sys/t_lock.h>
39 #include <sys/user.h>
40 #include <sys/uio.h>
41 #include <sys/file.h>
42 #include <sys/pathname.h>
43 #include <sys/sysmacros.h>
44 #include <sys/vfs.h>
45 #include <sys/vnode.h>
46 #include <sys/avl.h>
47 #include <sys/stat.h>
48 #include <sys/mode.h>
49 
50 #include <fcntl.h>
51 #include <unistd.h>
52 
53 #include "vncache.h"
54 
55 #define	VTOF(vp)	((struct fakefs_node *)(vp)->v_data)
56 #define	FTOV(fnp)	((fnp)->fn_vnode)
57 
58 /* Private to the fake vnode impl. */
59 typedef struct fakefs_node {
60 	avl_node_t	fn_avl_node;
61 	vnode_t		*fn_vnode;
62 	dev_t		fn_st_dev;
63 	ino_t		fn_st_ino;
64 	int		fn_fd;
65 	int		fn_mode;
66 } fakefs_node_t;
67 
68 typedef struct fnode_vnode {
69 	struct fakefs_node fn;
70 	struct vnode vn;
71 } fnode_vnode_t;
72 
73 /*
74  * You can dump this AVL tree with mdb, i.e.
75  * fncache_avl ::walk avl |::print fakefs_node_t
76  * fncache_avl ::walk avl |::print fnode_vnode_t fn vn.v_path
77  */
78 avl_tree_t fncache_avl;
79 kmutex_t fncache_lock;
80 
81 /*
82  * Fake node / vnode cache.
83  */
84 kmem_cache_t *fn_cache;
85 
86 /* ARGSUSED */
87 static int
fn_cache_constructor(void * buf,void * cdrarg,int kmflags)88 fn_cache_constructor(void *buf, void *cdrarg, int kmflags)
89 {
90 	fnode_vnode_t *fvp = buf;
91 
92 	bzero(fvp, sizeof (*fvp));
93 
94 	fvp->fn.fn_vnode = &fvp->vn;
95 	fvp->fn.fn_fd = -1;
96 
97 	fvp->vn.v_data = &fvp->fn;
98 	mutex_init(&fvp->vn.v_lock, NULL, MUTEX_DEFAULT, NULL);
99 
100 	return (0);
101 }
102 
103 /* ARGSUSED */
104 static void
fn_cache_destructor(void * buf,void * cdrarg)105 fn_cache_destructor(void *buf, void *cdrarg)
106 {
107 	fnode_vnode_t *fvp = buf;
108 	mutex_destroy(&fvp->vn.v_lock);
109 }
110 
111 /*
112  * Used by file systems when fs-specific nodes (e.g., ufs inodes) are
113  * cached by the file system and vnodes remain associated.
114  */
115 void
vn_recycle(vnode_t * vp)116 vn_recycle(vnode_t *vp)
117 {
118 	fakefs_node_t *fnp = VTOF(vp);
119 
120 	ASSERT(fnp->fn_fd == -1);
121 
122 	vp->v_rdcnt = 0;
123 	vp->v_wrcnt = 0;
124 
125 	if (vp->v_path) {
126 		strfree(vp->v_path);
127 		vp->v_path = NULL;
128 	}
129 }
130 
131 
132 /*
133  * Used to reset the vnode fields including those that are directly accessible
134  * as well as those which require an accessor function.
135  *
136  * Does not initialize:
137  *	synchronization objects: v_lock, v_vsd_lock, v_nbllock, v_cv
138  *	v_data (since FS-nodes and vnodes point to each other and should
139  *		be updated simultaneously)
140  *	v_op (in case someone needs to make a VOP call on this object)
141  */
142 void
vn_reinit(vnode_t * vp)143 vn_reinit(vnode_t *vp)
144 {
145 	vp->v_count = 1;
146 	vp->v_vfsp = NULL;
147 	vp->v_stream = NULL;
148 	vp->v_flag = 0;
149 	vp->v_type = VNON;
150 	vp->v_rdev = NODEV;
151 
152 	vn_recycle(vp);
153 }
154 
155 vnode_t *
vn_alloc(int kmflag)156 vn_alloc(int kmflag)
157 {
158 	fnode_vnode_t *fvp;
159 	vnode_t *vp = NULL;
160 
161 	fvp = kmem_cache_alloc(fn_cache, kmflag);
162 	if (fvp != NULL) {
163 		vp = &fvp->vn;
164 		vn_reinit(vp);
165 	}
166 
167 	return (vp);
168 }
169 
170 void
vn_free(vnode_t * vp)171 vn_free(vnode_t *vp)
172 {
173 	fakefs_node_t *fnp = VTOF(vp);
174 
175 	/*
176 	 * Some file systems call vn_free() with v_count of zero,
177 	 * some with v_count of 1.  In any case, the value should
178 	 * never be anything else.
179 	 */
180 	ASSERT((vp->v_count == 0) || (vp->v_count == 1));
181 	if (vp->v_path != NULL) {
182 		strfree(vp->v_path);
183 		vp->v_path = NULL;
184 	}
185 	ASSERT(fnp->fn_fd > 2);
186 	(void) close(fnp->fn_fd);
187 	fnp->fn_fd = -1;
188 
189 	/*
190 	 * Make sure fnp points to the beginning of fnode_vnode_t,
191 	 * which is what we must pass to kmem_cache_free.
192 	 */
193 	CTASSERT(offsetof(fnode_vnode_t, fn) == 0);
194 	kmem_cache_free(fn_cache, fnp);
195 }
196 
197 static int
fncache_cmp(const void * v1,const void * v2)198 fncache_cmp(const void *v1, const void *v2)
199 {
200 	const fakefs_node_t *np1 = v1;
201 	const fakefs_node_t *np2 = v2;
202 
203 	/* The args are really fnode_vnode_t */
204 	CTASSERT(offsetof(fnode_vnode_t, fn) == 0);
205 
206 	if (np1->fn_st_dev < np2->fn_st_dev)
207 		return (-1);
208 	if (np1->fn_st_dev > np2->fn_st_dev)
209 		return (+1);
210 	if (np1->fn_st_ino < np2->fn_st_ino)
211 		return (-1);
212 	if (np1->fn_st_ino > np2->fn_st_ino)
213 		return (+1);
214 
215 	return (0);
216 }
217 
218 int
vncache_cmp(const vnode_t * vp1,const vnode_t * vp2)219 vncache_cmp(const vnode_t *vp1, const vnode_t *vp2)
220 {
221 	fakefs_node_t *np1 = VTOF(vp1);
222 	fakefs_node_t *np2 = VTOF(vp2);
223 	return (fncache_cmp(np1, np2));
224 }
225 
226 vnode_t *
vncache_lookup(struct stat * st)227 vncache_lookup(struct stat *st)
228 {
229 	fakefs_node_t tmp_fn;
230 	fakefs_node_t *fnp;
231 	vnode_t *vp = NULL;
232 
233 	tmp_fn.fn_st_dev = st->st_dev;
234 	tmp_fn.fn_st_ino = st->st_ino;
235 
236 	mutex_enter(&fncache_lock);
237 	fnp = avl_find(&fncache_avl, &tmp_fn, NULL);
238 	if (fnp != NULL) {
239 		vp = FTOV(fnp);
240 		VN_HOLD(vp);
241 	}
242 	mutex_exit(&fncache_lock);
243 
244 	return (vp);
245 }
246 
247 vnode_t *
vncache_enter(struct stat * st,vnode_t * dvp,char * name,int fd)248 vncache_enter(struct stat *st, vnode_t *dvp, char *name, int fd)
249 {
250 	vnode_t *old_vp;
251 	vnode_t *new_vp;
252 	fakefs_node_t *old_fnp;
253 	fakefs_node_t *new_fnp;
254 	vfs_t *vfs;
255 	char *vpath;
256 	avl_index_t	where;
257 	int len;
258 
259 	ASSERT(fd > 2);
260 
261 	/*
262 	 * Fill in v_path
263 	 * Note: fsop_root() calls with dvp=NULL
264 	 */
265 	len = strlen(name) + 1;
266 	if (dvp == NULL) {
267 		vpath = kmem_alloc(len, KM_SLEEP);
268 		(void) strlcpy(vpath, name, len);
269 		vfs = rootvfs;
270 	} else {
271 		/* add to length for parent path + "/" */
272 		len += (strlen(dvp->v_path) + 1);
273 		vpath = kmem_alloc(len, KM_SLEEP);
274 		(void) snprintf(vpath, len, "%s/%s", dvp->v_path, name);
275 		vfs = dvp->v_vfsp;
276 	}
277 
278 	/* Note: (vp : fnp) linkage setup in constructor */
279 	new_vp = vn_alloc(KM_SLEEP);
280 	new_vp->v_path = vpath;
281 	new_vp->v_vfsp = vfs;
282 	new_vp->v_type = IFTOVT(st->st_mode);
283 	new_fnp = VTOF(new_vp);
284 	new_fnp->fn_fd = fd;
285 	new_fnp->fn_st_dev = st->st_dev;
286 	new_fnp->fn_st_ino = st->st_ino;
287 
288 	old_vp = NULL;
289 	mutex_enter(&fncache_lock);
290 	old_fnp = avl_find(&fncache_avl, new_fnp, &where);
291 	if (old_fnp != NULL) {
292 		DTRACE_PROBE1(found, fakefs_node_t *, old_fnp);
293 		old_vp = FTOV(old_fnp);
294 		VN_HOLD(old_vp);
295 	} else {
296 		DTRACE_PROBE1(insert, fakefs_node_t *, new_fnp);
297 		avl_insert(&fncache_avl, new_fnp, where);
298 	}
299 	mutex_exit(&fncache_lock);
300 
301 	/* If we lost the race, free new_vp */
302 	if (old_vp != NULL) {
303 		vn_free(new_vp);
304 		return (old_vp);
305 	}
306 
307 	return (new_vp);
308 }
309 
310 /*
311  * Called after a successful rename to update v_path
312  */
313 void
vncache_renamed(vnode_t * vp,vnode_t * to_dvp,char * to_name)314 vncache_renamed(vnode_t *vp, vnode_t *to_dvp, char *to_name)
315 {
316 	char *vpath;
317 	char *ovpath;
318 	int len;
319 
320 	len = strlen(to_name) + 1;
321 	/* add to length for parent path + "/" */
322 	len += (strlen(to_dvp->v_path) + 1);
323 	vpath = kmem_alloc(len, KM_SLEEP);
324 	(void) snprintf(vpath, len, "%s/%s", to_dvp->v_path, to_name);
325 
326 	mutex_enter(&fncache_lock);
327 	ovpath = vp->v_path;
328 	vp->v_path = vpath;
329 	mutex_exit(&fncache_lock);
330 
331 	strfree(ovpath);
332 }
333 
334 /*
335  * Last reference to this vnode is (possibly) going away.
336  * This is normally called by vn_rele() when v_count==1.
337  * Note that due to lock order concerns, we have to take
338  * the fncache_lock (for the avl tree) and then recheck
339  * v_count, which might have gained a ref during the time
340  * we did not hold vp->v_lock.
341  */
342 void
vncache_inactive(vnode_t * vp)343 vncache_inactive(vnode_t *vp)
344 {
345 	fakefs_node_t *fnp = VTOF(vp);
346 	vnode_t *xvp;
347 	uint_t count;
348 
349 	mutex_enter(&fncache_lock);
350 	mutex_enter(&vp->v_lock);
351 
352 	if ((count = vp->v_count) <= 1) {
353 		/* This is (still) the last ref. */
354 		DTRACE_PROBE1(remove, fakefs_node_t *, fnp);
355 		avl_remove(&fncache_avl, fnp);
356 	}
357 
358 	mutex_exit(&vp->v_lock);
359 	mutex_exit(&fncache_lock);
360 
361 	if (count > 1)
362 		return;
363 
364 	/*
365 	 * See fake_lookup_xattrdir()
366 	 */
367 	xvp = vp->v_xattrdir;
368 	vp->v_xattrdir = NULL;
369 	vn_free(vp);
370 
371 	if (xvp != NULL) {
372 		ASSERT((xvp->v_flag & V_XATTRDIR) != 0);
373 		VN_RELE(xvp);
374 	}
375 }
376 
377 int
vncache_getfd(vnode_t * vp)378 vncache_getfd(vnode_t *vp)
379 {
380 	fakefs_node_t *fnp = VTOF(vp);
381 	ASSERT(fnp->fn_fd > 2);
382 	return (fnp->fn_fd);
383 }
384 
385 /*
386  * See fake_lookup_xattrdir()
387  * Special case vnode creation.
388  */
389 void
vncache_setfd(vnode_t * vp,int fd)390 vncache_setfd(vnode_t *vp, int fd)
391 {
392 	fakefs_node_t *fnp = VTOF(vp);
393 	ASSERT(fnp->fn_fd == -1);
394 	ASSERT(fd > 2);
395 	fnp->fn_fd = fd;
396 }
397 
398 
399 int
vncache_init(void)400 vncache_init(void)
401 {
402 	fn_cache = kmem_cache_create("fn_cache", sizeof (fnode_vnode_t),
403 	    VNODE_ALIGN, fn_cache_constructor, fn_cache_destructor,
404 	    NULL, NULL, NULL, 0);
405 	avl_create(&fncache_avl,
406 	    fncache_cmp,
407 	    sizeof (fnode_vnode_t),
408 	    offsetof(fnode_vnode_t, fn.fn_avl_node));
409 	mutex_init(&fncache_lock, NULL, MUTEX_DEFAULT, NULL);
410 	return (0);
411 }
412 
413 void
vncache_fini(void)414 vncache_fini(void)
415 {
416 	mutex_destroy(&fncache_lock);
417 	avl_destroy(&fncache_avl);
418 	kmem_cache_destroy(fn_cache);
419 }
420