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 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 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 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 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 * 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 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 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 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 * 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 * 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 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 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 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 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 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 414 vncache_fini(void) 415 { 416 mutex_destroy(&fncache_lock); 417 avl_destroy(&fncache_avl); 418 kmem_cache_destroy(fn_cache); 419 } 420