VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c@ 30357

Last change on this file since 30357 was 29142, checked in by vboxsync, 15 years ago

Additions/SharedFolders/Solaris: fix rm -rf on directories.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 36.8 KB
Line 
1/** @file
2 * VirtualBox File System for Solaris Guests, vnode implementation.
3 */
4
5/*
6 * Copyright (C) 2009 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17/*
18 * Shared Folder File System is used from Solaris when run as a guest operating
19 * system on VirtualBox, though is meant to be usable with any hypervisor that
20 * can provide similar functionality. The sffs code handles all the Solaris
21 * specific semantics and relies on a provider module to actually access
22 * directories, files, etc. The provider interfaces are described in
23 * "vboxfs_prov.h" and the module implementing them is shipped as part of the
24 * VirtualBox Guest Additions for Solaris.
25 *
26 * The shared folder file system is similar to a networked file system,
27 * but with some caveats. The sffs code caches minimal information and proxies
28 * out to the provider whenever possible. Here are some things that are
29 * handled in this code and not by the proxy:
30 *
31 * - a way to open ".." from any already open directory
32 * - st_ino numbers
33 * - detecting directory changes that happened on the host.
34 *
35 * The implementation builds a cache of information for every file/directory
36 * ever accessed in all mounted sffs filesystems using sf_node structures.
37 *
38 * This information for both open or closed files can become invalid if
39 * asynchronous changes are made on the host. Solaris should not panic() in
40 * this event, but some file system operations may return unexpected errors.
41 * Information for such directories or files while they have active vnodes
42 * is removed from the regular cache and stored in a "stale" bucket until
43 * the vnode becomes completely inactive.
44 *
45 * No file data is cached in the guest. This means we don't support mmap() yet.
46 * A future version could relatively easily add support for read-only
47 * mmap(MAP_SHARED) and any mmap(MAP_PRIVATE). But a la ZFS, this data caching
48 * would not be coherent with normal simultaneous read()/write() operations,
49 * nor will it be coherent with data access on the host. Writable
50 * mmap(MAP_SHARED) access is possible, but guaranteeing any kind of coherency
51 * with concurrent activity on the host would be near impossible with the
52 * existing interfaces.
53 *
54 * A note about locking. sffs is not a high performance file system.
55 * No fine grained locking is done. The one sffs_lock protects just about
56 * everything.
57 */
58
59#include <VBox/log.h>
60
61#include <sys/types.h>
62#include <sys/stat.h>
63#include <sys/mntent.h>
64#include <sys/param.h>
65#include <sys/modctl.h>
66#include <sys/mount.h>
67#include <sys/policy.h>
68#include <sys/atomic.h>
69#include <sys/sysmacros.h>
70#include <sys/ddi.h>
71#include <sys/sunddi.h>
72#include <sys/vfs.h>
73#if !defined(VBOX_VFS_SOLARIS_10U6)
74#include <sys/vfs_opreg.h>
75#endif
76#include <sys/pathname.h>
77#include <sys/dirent.h>
78#include <sys/fs_subr.h>
79#include "vboxfs_prov.h"
80#include "vboxfs_vnode.h"
81#include "vboxfs_vfs.h"
82
83static struct vnodeops *sffs_ops = NULL;
84
85kmutex_t sffs_lock;
86static avl_tree_t sfnodes;
87static avl_tree_t stale_sfnodes;
88
89/*
90 * For now we'll use an I/O buffer that doesn't page fault for VirtualBox
91 * to transfer data into.
92 */
93char *sffs_buffer;
94
95/*
96 * sfnode_compare() is needed for AVL tree functionality.
97 * The nodes are sorted by mounted filesystem, then path. If the
98 * nodes are stale, the node pointer itself is used to force uniqueness.
99 */
100static int
101sfnode_compare(const void *a, const void *b)
102{
103 sfnode_t *x = (sfnode_t *)a;
104 sfnode_t *y = (sfnode_t *)b;
105 int diff;
106
107 if (x->sf_is_stale) {
108 ASSERT(y->sf_is_stale);
109 diff = strcmp(x->sf_path, y->sf_path);
110 if (diff == 0)
111 diff = (uintptr_t)y - (uintptr_t)x;
112 } else {
113 ASSERT(!y->sf_is_stale);
114 diff = (uintptr_t)y->sf_sffs - (uintptr_t)x->sf_sffs;
115 if (diff == 0)
116 diff = strcmp(x->sf_path, y->sf_path);
117 }
118 if (diff < 0)
119 return (-1);
120 if (diff > 0)
121 return (1);
122 return (0);
123}
124
125/*
126 * Construct a new pathname given an sfnode plus an optional tail component.
127 * This handles ".." and "."
128 */
129static char *
130sfnode_construct_path(sfnode_t *node, char *tail)
131{
132 char *p;
133
134 if (strcmp(tail, ".") == 0 || strcmp(tail, "..") == 0)
135 panic("construct path for %s", tail);
136 p = kmem_alloc(strlen(node->sf_path) + 1 + strlen(tail) + 1, KM_SLEEP);
137 strcpy(p, node->sf_path);
138 strcat(p, "/");
139 strcat(p, tail);
140 return (p);
141}
142
143/*
144 * Open the provider file associated with a vnode. Holding the file open is
145 * the only way we have of trying to have a vnode continue to refer to the
146 * same host file in the host in light of the possibility of host side renames.
147 */
148static void
149sfnode_open(sfnode_t *node)
150{
151 int error;
152 sfp_file_t *fp;
153
154 if (node->sf_file != NULL)
155 return;
156 error = sfprov_open(node->sf_sffs->sf_handle, node->sf_path, &fp);
157 if (error == 0)
158 node->sf_file = fp;
159}
160
161/*
162 * get a new vnode reference for an sfnode
163 */
164vnode_t *
165sfnode_get_vnode(sfnode_t *node)
166{
167 vnode_t *vp;
168
169 if (node->sf_vnode != NULL) {
170 VN_HOLD(node->sf_vnode);
171 } else {
172 vp = vn_alloc(KM_SLEEP);
173 LogFlowFunc((" %s gets vnode 0x%p\n", node->sf_path, vp));
174 vp->v_type = node->sf_type;
175 vp->v_vfsp = node->sf_sffs->sf_vfsp;
176 VFS_HOLD(vp->v_vfsp);
177 vn_setops(vp, sffs_ops);
178 vp->v_flag = VNOMAP | VNOSWAP;
179 vn_exists(vp);
180 vp->v_data = node;
181 node->sf_vnode = vp;
182 }
183 sfnode_open(node);
184 return (node->sf_vnode);
185}
186
187/*
188 * Allocate and initialize a new sfnode and assign it a vnode
189 */
190sfnode_t *
191sfnode_make(
192 sffs_data_t *sffs,
193 char *path,
194 vtype_t type,
195 sfp_file_t *fp,
196 sfnode_t *parent) /* can be NULL for root */
197{
198 sfnode_t *node;
199 avl_index_t where;
200
201 ASSERT(MUTEX_HELD(&sffs_lock));
202 ASSERT(path != NULL);
203
204 /*
205 * build the sfnode
206 */
207 LogFlowFunc(("sffs_make(%s)\n", path));
208 node = kmem_alloc(sizeof (*node), KM_SLEEP);
209 node->sf_sffs = sffs;
210 node->sf_path = path;
211 node->sf_ino = sffs->sf_ino++;
212 node->sf_type = type;
213 node->sf_is_stale = 0; /* never stale at creation */
214 node->sf_file = fp;
215 node->sf_vnode = NULL; /* do this before any sfnode_get_vnode() */
216 node->sf_children = 0;
217 node->sf_parent = parent;
218 if (parent)
219 ++parent->sf_children;
220
221 /*
222 * add the new node to our cache
223 */
224 if (avl_find(&sfnodes, node, &where) != NULL)
225 panic("sffs_create_sfnode(%s): duplicate sfnode_t", path);
226 avl_insert(&sfnodes, node, where);
227 return (node);
228}
229
230/*
231 * destroy an sfnode
232 */
233static void
234sfnode_destroy(sfnode_t *node)
235{
236 avl_index_t where;
237 avl_tree_t *tree;
238 sfnode_t *parent;
239top:
240 parent = node->sf_parent;
241 ASSERT(MUTEX_HELD(&sffs_lock));
242 ASSERT(node->sf_path != NULL);
243 LogFlowFunc(("sffs_destroy(%s)%s\n", node->sf_path, node->sf_is_stale ? " stale": ""));
244 if (node->sf_children != 0)
245 panic("sfnode_destroy(%s) has %d children", node->sf_children);
246 if (node->sf_vnode != NULL)
247 panic("sfnode_destroy(%s) has active vnode", node->sf_path);
248
249 if (node->sf_is_stale)
250 tree = &stale_sfnodes;
251 else
252 tree = &sfnodes;
253 if (avl_find(tree, node, &where) == NULL)
254 panic("sfnode_destroy(%s) not found", node->sf_path);
255 avl_remove(tree, node);
256
257 VFS_RELE(node->sf_sffs->sf_vfsp);
258 kmem_free(node->sf_path, strlen(node->sf_path) + 1);
259 kmem_free(node, sizeof (*node));
260 if (parent != NULL) {
261 if (parent->sf_children == 0)
262 panic("sfnode_destroy(%s) parent has no child");
263 --parent->sf_children;
264 if (parent->sf_children == 0 &&
265 parent->sf_is_stale &&
266 parent->sf_vnode == NULL) {
267 node = parent;
268 goto top;
269 }
270 }
271}
272
273/*
274 * Some sort of host operation on an sfnode has failed or it has been
275 * deleted. Mark this node and any children as stale, deleting knowledge
276 * about any which do not have active vnodes or children
277 * This also handle deleting an inactive node that was already stale.
278 */
279static void
280sfnode_make_stale(sfnode_t *node)
281{
282 sfnode_t *n;
283 int len;
284 ASSERT(MUTEX_HELD(&sffs_lock));
285 avl_index_t where;
286
287 /*
288 * First deal with any children of a directory node.
289 * If a directory becomes stale, anything below it becomes stale too.
290 */
291 if (!node->sf_is_stale && node->sf_type == VDIR) {
292 len = strlen(node->sf_path);
293
294 n = node;
295 while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
296 ASSERT(!n->sf_is_stale);
297
298 /*
299 * quit when no longer seeing children of node
300 */
301 if (n->sf_sffs != node->sf_sffs ||
302 strncmp(node->sf_path, n->sf_path, len) != 0 ||
303 n->sf_path[len] != '/')
304 break;
305
306 /*
307 * Either mark the child as stale or destroy it
308 */
309 if (n->sf_vnode == NULL && n->sf_children == 0) {
310 sfnode_destroy(n);
311 } else {
312 LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path));
313 if (avl_find(&sfnodes, n, &where) == NULL)
314 panic("sfnode_make_stale(%s)"
315 " not in sfnodes", n->sf_path);
316 avl_remove(&sfnodes, n);
317 n->sf_is_stale = 1;
318 if (avl_find(&stale_sfnodes, n, &where) != NULL)
319 panic("sffs_make_stale(%s) duplicates",
320 n->sf_path);
321 avl_insert(&stale_sfnodes, n, where);
322 }
323 }
324 }
325
326 /*
327 * Now deal with the given node.
328 */
329 if (node->sf_vnode == NULL && node->sf_children == 0) {
330 sfnode_destroy(node);
331 } else if (!node->sf_is_stale) {
332 LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path));
333 if (avl_find(&sfnodes, node, &where) == NULL)
334 panic("sfnode_make_stale(%s) not in sfnodes",
335 node->sf_path);
336 avl_remove(&sfnodes, node);
337 node->sf_is_stale = 1;
338 if (avl_find(&stale_sfnodes, node, &where) != NULL)
339 panic("sffs_make_stale(%s) duplicates", node->sf_path);
340 avl_insert(&stale_sfnodes, node, where);
341 }
342}
343
344/*
345 * Rename a file or a directory
346 */
347static void
348sfnode_rename(sfnode_t *node, sfnode_t *newparent, char *path)
349{
350 sfnode_t *n;
351 sfnode_t template;
352 avl_index_t where;
353 int len = strlen(path);
354 int old_len;
355 char *new_path;
356 char *tail;
357 ASSERT(MUTEX_HELD(&sffs_lock));
358
359 ASSERT(!node->sf_is_stale);
360
361 /*
362 * Have to remove anything existing that had the new name.
363 */
364 template.sf_sffs = node->sf_sffs;
365 template.sf_path = path;
366 template.sf_is_stale = 0;
367 n = avl_find(&sfnodes, &template, &where);
368 if (n != NULL)
369 sfnode_make_stale(n);
370
371 /*
372 * Do the renaming, deal with any children of this node first.
373 */
374 if (node->sf_type == VDIR) {
375 old_len = strlen(node->sf_path);
376 while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
377
378 /*
379 * quit when no longer seeing children of node
380 */
381 if (n->sf_sffs != node->sf_sffs ||
382 strncmp(node->sf_path, n->sf_path, old_len) != 0 ||
383 n->sf_path[old_len] != '/')
384 break;
385
386 /*
387 * Rename the child:
388 * - build the new path name
389 * - unlink the AVL node
390 * - assign the new name
391 * - re-insert the AVL name
392 */
393 ASSERT(strlen(n->sf_path) > old_len);
394 tail = n->sf_path + old_len; /* includes intial "/" */
395 new_path = kmem_alloc(len + strlen(tail) + 1,
396 KM_SLEEP);
397 strcpy(new_path, path);
398 strcat(new_path, tail);
399 if (avl_find(&sfnodes, n, &where) == NULL)
400 panic("sfnode_rename(%s) not in sfnodes",
401 n->sf_path);
402 avl_remove(&sfnodes, n);
403 LogFlowFunc(("sfnode_rname(%s to %s) sub\n", n->sf_path, new_path));
404 kmem_free(n->sf_path, strlen(n->sf_path) + 1);
405 n->sf_path = new_path;
406 if (avl_find(&sfnodes, n, &where) != NULL)
407 panic("sfnode_rename(%s) duplicates",
408 n->sf_path);
409 avl_insert(&sfnodes, n, where);
410 }
411 }
412
413 /*
414 * Deal with the given node.
415 */
416 if (avl_find(&sfnodes, node, &where) == NULL)
417 panic("sfnode_rename(%s) not in sfnodes", node->sf_path);
418 avl_remove(&sfnodes, node);
419 LogFlowFunc(("sfnode_rname(%s to %s)\n", node->sf_path, path));
420 kmem_free(node->sf_path, strlen(node->sf_path) + 1);
421 node->sf_path = path;
422 if (avl_find(&sfnodes, node, &where) != NULL)
423 panic("sfnode_rename(%s) duplicates", node->sf_path);
424 avl_insert(&sfnodes, node, where);
425
426 /*
427 * change the parent
428 */
429 if (node->sf_parent == NULL)
430 panic("sfnode_rename(%s) no parent", node->sf_path);
431 if (node->sf_parent->sf_children == 0)
432 panic("sfnode_rename(%s) parent has no child", node->sf_path);
433 --node->sf_parent->sf_children;
434 node->sf_parent = newparent;
435 ++newparent->sf_children;
436}
437
438/*
439 * Look for a cached node, if not found either handle ".." or try looking
440 * via the provider. Create an entry in sfnodes if found but not cached yet.
441 * If the create flag is set, a file or directory is created. If the file
442 * already existed, an error is returned.
443 * Nodes returned from this routine always have a vnode with its ref count
444 * bumped by 1.
445 */
446static sfnode_t *
447sfnode_lookup(sfnode_t *dir, char *name, vtype_t create)
448{
449 avl_index_t where;
450 sfnode_t template;
451 sfnode_t *node;
452 int error;
453 int type;
454 char *fullpath;
455 sfp_file_t *fp;
456
457 ASSERT(MUTEX_HELD(&sffs_lock));
458
459 /*
460 * handle referencing myself
461 */
462 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0)
463 return (dir);
464
465 /*
466 * deal with parent
467 */
468 if (strcmp(name, "..") == 0)
469 return (dir->sf_parent);
470
471 /*
472 * Look for an existing node.
473 */
474 fullpath = sfnode_construct_path(dir, name);
475 template.sf_sffs = dir->sf_sffs;
476 template.sf_path = fullpath;
477 template.sf_is_stale = 0;
478 node = avl_find(&sfnodes, &template, &where);
479 if (node != NULL) {
480 kmem_free(fullpath, strlen(fullpath) + 1);
481 if (create != VNON)
482 return (NULL);
483 return (node);
484 }
485
486 /*
487 * No entry for this path currently.
488 * Check if the file exists with the provider and get the type from
489 * there.
490 */
491 if (create == VREG) {
492 type = VREG;
493 error = sfprov_create(dir->sf_sffs->sf_handle, fullpath, &fp);
494 } else if (create == VDIR) {
495 type = VDIR;
496 error = sfprov_mkdir(dir->sf_sffs->sf_handle, fullpath, &fp);
497 } else {
498 mode_t m;
499 fp = NULL;
500 type = VNON;
501 error =
502 sfprov_get_mode(dir->sf_sffs->sf_handle, fullpath, &m);
503 if (error != 0)
504 error = ENOENT;
505 else if (S_ISDIR(m))
506 type = VDIR;
507 else if (S_ISREG(m))
508 type = VREG;
509 }
510
511 /*
512 * If no errors, make a new node and return it.
513 */
514 if (error) {
515 kmem_free(fullpath, strlen(fullpath) + 1);
516 return (NULL);
517 }
518 node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir);
519 return (node);
520}
521
522
523/*
524 * uid and gid in sffs determine owner and group for all files.
525 */
526static int
527sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr)
528{
529 sffs_data_t *sffs = node->sf_sffs;
530 mode_t m;
531 int shift = 0;
532 int error;
533 vnode_t *vp;
534
535 ASSERT(MUTEX_HELD(&sffs_lock));
536
537 /*
538 * get the mode from the provider
539 */
540 error = sfprov_get_mode(node->sf_sffs->sf_handle, node->sf_path, &m);
541 if (error != 0) {
542 m = 0;
543 if (error == ENOENT)
544 sfnode_make_stale(node);
545 }
546
547 /*
548 * mask off the permissions based on uid/gid
549 */
550 if (crgetuid(cr) != sffs->sf_uid) {
551 shift += 3;
552 if (groupmember(sffs->sf_gid, cr) == 0)
553 shift += 3;
554 }
555 mode &= ~(m << shift);
556
557 if (mode == 0) {
558 error = 0;
559 } else {
560 vp = sfnode_get_vnode(node);
561 error = secpolicy_vnode_access(cr, vp, sffs->sf_uid, mode);
562 VN_RELE(vp);
563 }
564 return (error);
565}
566
567
568/*
569 *
570 * Everything below this point are the vnode operations used by Solaris VFS
571 */
572static int
573sffs_readdir(
574 vnode_t *vp,
575 uio_t *uiop,
576 cred_t *cred,
577 int *eofp,
578 caller_context_t *ct,
579 int flags)
580{
581 sfnode_t *dir = VN2SFN(vp);
582 sfnode_t *node;
583 struct dirent64 *dirent;
584 int dummy_eof;
585 int error = 0;
586 int namelen;
587 void *prov_buff = NULL;
588 size_t prov_buff_size;
589 char **names;
590 uint32_t nents;
591 uint32_t index;
592
593 if (uiop->uio_iovcnt != 1)
594 return (EINVAL);
595
596 if (vp->v_type != VDIR)
597 return (ENOTDIR);
598
599 if (eofp == NULL)
600 eofp = &dummy_eof;
601 *eofp = 0;
602
603 if (uiop->uio_loffset >= MAXOFF_T) {
604 *eofp = 1;
605 return (0);
606 }
607
608 dirent = kmem_zalloc(DIRENT64_RECLEN(MAXNAMELEN), KM_SLEEP);
609
610 /*
611 * Get the directory entry names from the host. This gets all
612 * entries, so add in starting offset. Max the caller can expect
613 * would be the size of the UIO buffer / sizeof of a dirent for
614 * file with name of length 1
615 */
616 mutex_enter(&sffs_lock);
617 index = uiop->uio_loffset;
618 nents = index + (uiop->uio_resid / DIRENT64_RECLEN(1));
619 error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
620 &prov_buff, &prov_buff_size, &nents);
621 if (error != 0)
622 goto done;
623 if (nents <= index) {
624 *eofp = 1;
625 goto done;
626 }
627 names = (void *)prov_buff;
628
629 /*
630 * Lookup each of the names, so that we have ino's.
631 */
632 for (; index < nents; ++index) {
633 if (strcmp(names[index], ".") == 0) {
634 node = dir;
635 } else if (strcmp(names[index], "..") == 0) {
636 node = dir->sf_parent;
637 if (node == NULL)
638 node = dir;
639 } else {
640 node = sfnode_lookup(dir, names[index], VNON);
641 if (node == NULL)
642 panic("sffs_readdir() lookup failed");
643 }
644 namelen = strlen(names[index]);
645 strcpy(&dirent->d_name[0], names[index]);
646 dirent->d_reclen = DIRENT64_RECLEN(namelen);
647 dirent->d_off = index;
648 dirent->d_ino = node->sf_ino;
649 if (dirent->d_reclen > uiop->uio_resid) {
650 error = ENOSPC;
651 break;
652 }
653 error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
654 if (error != 0)
655 break;
656 bzero(&dirent->d_name[0], namelen);
657 }
658 if (error == 0 && index >= nents)
659 *eofp = 1;
660done:
661 mutex_exit(&sffs_lock);
662 if (prov_buff != NULL)
663 kmem_free(prov_buff, prov_buff_size);
664 kmem_free(dirent, DIRENT64_RECLEN(MAXNAMELEN));
665 return (error);
666}
667
668
669#if defined(VBOX_VFS_SOLARIS_10U6)
670/*
671 * HERE JOE.. this may need more logic, need to look at other file systems
672 */
673static int
674sffs_pathconf(
675 vnode_t *vp,
676 int cmd,
677 ulong_t *valp,
678 cred_t *cr)
679{
680 return (fs_pathconf(vp, cmd, valp, cr));
681}
682#else
683/*
684 * HERE JOE.. this may need more logic, need to look at other file systems
685 */
686static int
687sffs_pathconf(
688 vnode_t *vp,
689 int cmd,
690 ulong_t *valp,
691 cred_t *cr,
692 caller_context_t *ct)
693{
694 return (fs_pathconf(vp, cmd, valp, cr, ct));
695}
696#endif
697
698static int
699sffs_getattr(
700 vnode_t *vp,
701 vattr_t *vap,
702 int flags,
703 cred_t *cred,
704 caller_context_t *ct)
705{
706 sfnode_t *node = VN2SFN(vp);
707 sffs_data_t *sffs = node->sf_sffs;
708 mode_t mode;
709 timestruc_t time;
710 uint64_t x;
711 int error;
712
713 mutex_enter(&sffs_lock);
714 vap->va_type = vp->v_type;
715 vap->va_uid = sffs->sf_uid;
716 vap->va_gid = sffs->sf_gid;
717 vap->va_fsid = sffs->sf_vfsp->vfs_dev;
718 vap->va_nodeid = node->sf_ino;
719 vap->va_nlink = 1;
720 vap->va_rdev = sffs->sf_vfsp->vfs_dev;
721 vap->va_seq = 0;
722
723 error = sfprov_get_mode(node->sf_sffs->sf_handle, node->sf_path, &mode);
724 if (error == ENOENT)
725 sfnode_make_stale(node);
726 if (error != 0)
727 goto done;
728 vap->va_mode = mode & MODEMASK;
729
730 error = sfprov_get_size(node->sf_sffs->sf_handle, node->sf_path, &x);
731 if (error == ENOENT)
732 sfnode_make_stale(node);
733 if (error != 0)
734 goto done;
735 vap->va_size = x;
736 vap->va_blksize = 512;
737 vap->va_nblocks = (x + 511) / 512;
738
739 error =
740 sfprov_get_atime(node->sf_sffs->sf_handle, node->sf_path, &time);
741 if (error == ENOENT)
742 sfnode_make_stale(node);
743 if (error != 0)
744 goto done;
745 vap->va_atime = time;
746
747 error =
748 sfprov_get_mtime(node->sf_sffs->sf_handle, node->sf_path, &time);
749 if (error == ENOENT)
750 sfnode_make_stale(node);
751 if (error != 0)
752 goto done;
753 vap->va_mtime = time;
754
755 error =
756 sfprov_get_ctime(node->sf_sffs->sf_handle, node->sf_path, &time);
757 if (error == ENOENT)
758 sfnode_make_stale(node);
759 if (error != 0)
760 goto done;
761 vap->va_ctime = time;
762
763done:
764 mutex_exit(&sffs_lock);
765 return (error);
766}
767
768/*ARGSUSED*/
769static int
770sffs_read(
771 vnode_t *vp,
772 struct uio *uio,
773 int ioflag,
774 cred_t *cred,
775 caller_context_t *ct)
776{
777 sfnode_t *node = VN2SFN(vp);
778 int error = 0;
779 uint32_t bytes;
780 uint32_t done;
781 ulong_t offset;
782 ssize_t total;
783
784 if (vp->v_type == VDIR)
785 return (EISDIR);
786 if (vp->v_type != VREG)
787 return (EINVAL);
788 if (uio->uio_loffset >= MAXOFF_T)
789 return (0);
790 if (uio->uio_loffset < 0)
791 return (EINVAL);
792 total = uio->uio_resid;
793 if (total == 0)
794 return (0);
795
796 mutex_enter(&sffs_lock);
797 sfnode_open(node);
798 if (node->sf_file == NULL) {
799 mutex_exit(&sffs_lock);
800 return (EINVAL);
801 }
802
803 do {
804 offset = uio->uio_offset;
805 done = bytes = MIN(PAGESIZE, uio->uio_resid);
806 error = sfprov_read(node->sf_file, sffs_buffer, offset, &done);
807 if (error == 0 && done > 0)
808 error = uiomove(sffs_buffer, done, UIO_READ, uio);
809 } while (error == 0 && uio->uio_resid > 0 && done > 0);
810
811 mutex_exit(&sffs_lock);
812
813 /*
814 * a partial read is never an error
815 */
816 if (total != uio->uio_resid)
817 error = 0;
818 return (error);
819}
820
821/*ARGSUSED*/
822static int
823sffs_write(
824 vnode_t *vp,
825 struct uio *uiop,
826 int ioflag,
827 cred_t *cred,
828 caller_context_t *ct)
829{
830 sfnode_t *node = VN2SFN(vp);
831 int error = 0;
832 uint32_t bytes;
833 uint32_t done;
834 ulong_t offset;
835 ssize_t total;
836 rlim64_t limit = uiop->uio_llimit;
837
838 if (vp->v_type == VDIR)
839 return (EISDIR);
840 if (vp->v_type != VREG)
841 return (EINVAL);
842
843 /*
844 * We have to hold this lock for a long time to keep
845 * multiple FAPPEND writes from intermixing
846 */
847 mutex_enter(&sffs_lock);
848 sfnode_open(node);
849 if (node->sf_file == NULL) {
850 mutex_exit(&sffs_lock);
851 return (EINVAL);
852 }
853 if (ioflag & FAPPEND) {
854 uint64_t endoffile;
855
856 error = sfprov_get_size(node->sf_sffs->sf_handle,
857 node->sf_path, &endoffile);
858 if (error == ENOENT)
859 sfnode_make_stale(node);
860 if (error != 0) {
861 mutex_exit(&sffs_lock);
862 return (error);
863 }
864 uiop->uio_loffset = endoffile;
865 }
866
867 if (vp->v_type != VREG || uiop->uio_loffset < 0) {
868 mutex_exit(&sffs_lock);
869 return (EINVAL);
870 }
871 if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
872 limit = MAXOFFSET_T;
873 if (limit > MAXOFF_T)
874 limit = MAXOFF_T;
875
876 if (uiop->uio_loffset >= limit) {
877 proc_t *p = ttoproc(curthread);
878 mutex_enter(&p->p_lock);
879 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
880 p, RCA_UNSAFE_SIGINFO);
881 mutex_exit(&p->p_lock);
882 mutex_exit(&sffs_lock);
883 return (EFBIG);
884 }
885
886 if (uiop->uio_loffset >= MAXOFF_T) {
887 mutex_exit(&sffs_lock);
888 return (EFBIG);
889 }
890
891
892 total = uiop->uio_resid;
893 if (total == 0) {
894 mutex_exit(&sffs_lock);
895 return (0);
896 }
897
898 do {
899 offset = uiop->uio_offset;
900 bytes = MIN(PAGESIZE, uiop->uio_resid);
901 if (offset + bytes >= limit) {
902 if (offset >= limit) {
903 error = EFBIG;
904 break;
905 }
906 bytes = limit - offset;
907 }
908 error = uiomove(sffs_buffer, bytes, UIO_WRITE, uiop);
909 if (error != 0)
910 break;
911 done = bytes;
912 if (error == 0)
913 error = sfprov_write(node->sf_file, sffs_buffer,
914 offset, &done);
915 total -= done;
916 if (done != bytes) {
917 uiop->uio_resid += bytes - done;
918 break;
919 }
920 } while (error == 0 && uiop->uio_resid > 0 && done > 0);
921
922 mutex_exit(&sffs_lock);
923
924 /*
925 * A short write is never really an error.
926 */
927 if (total != uiop->uio_resid)
928 error = 0;
929 return (error);
930}
931
932/*ARGSUSED*/
933static int
934sffs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
935{
936 sfnode_t *node = VN2SFN(vp);
937 int error;
938
939 mutex_enter(&sffs_lock);
940 error = sfnode_access(node, mode, cr);
941 mutex_exit(&sffs_lock);
942 return (error);
943}
944
945/*
946 * Lookup an entry in a directory and create a new vnode if found.
947 */
948/* ARGSUSED3 */
949static int
950sffs_lookup(
951 vnode_t *dvp, /* the directory vnode */
952 char *name, /* the name of the file or directory */
953 vnode_t **vpp, /* the vnode we found or NULL */
954 struct pathname *pnp,
955 int flags,
956 vnode_t *rdir,
957 cred_t *cred,
958 caller_context_t *ct,
959 int *direntflags,
960 struct pathname *realpnp)
961{
962 int error;
963 sfnode_t *node;
964
965 /*
966 * dvp must be a directory
967 */
968 if (dvp->v_type != VDIR)
969 return (ENOTDIR);
970
971 /*
972 * An empty component name or just "." means the directory itself.
973 * Don't do any further lookup or checking.
974 */
975 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0) {
976 VN_HOLD(dvp);
977 *vpp = dvp;
978 return (0);
979 }
980
981 /*
982 * Check permission to look at this directory. We always allow "..".
983 */
984 mutex_enter(&sffs_lock);
985 if (strcmp(name, "..") != 0) {
986 error = sfnode_access(VN2SFN(dvp), VEXEC, cred);
987 if (error) {
988 mutex_exit(&sffs_lock);
989 return (error);
990 }
991 }
992
993 /*
994 * Lookup the node.
995 */
996 node = sfnode_lookup(VN2SFN(dvp), name, VNON);
997 if (node != NULL)
998 *vpp = sfnode_get_vnode(node);
999 mutex_exit(&sffs_lock);
1000 return ((node == NULL) ? ENOENT : 0);
1001}
1002
1003/*ARGSUSED*/
1004static int
1005sffs_create(
1006 vnode_t *dvp,
1007 char *name,
1008 struct vattr *vap,
1009 vcexcl_t exclusive,
1010 int mode,
1011 vnode_t **vpp,
1012 cred_t *cr,
1013 int flag,
1014 caller_context_t *ct,
1015 vsecattr_t *vsecp)
1016{
1017 vnode_t *vp;
1018 sfnode_t *node;
1019 int error;
1020
1021 ASSERT(name != NULL);
1022
1023 /*
1024 * this is used for regular files, not mkdir
1025 */
1026 if (vap->va_type == VDIR)
1027 return (EISDIR);
1028 if (vap->va_type != VREG)
1029 return (EINVAL);
1030
1031 /*
1032 * is this a pre-existing file?
1033 */
1034 error = sffs_lookup(dvp, name, &vp,
1035 NULL, 0, NULL, cr, ct, NULL, NULL);
1036 if (error == ENOENT)
1037 vp = NULL;
1038 else if (error != 0)
1039 return (error);
1040
1041 /*
1042 * Operation on a pre-existing file.
1043 */
1044 if (vp != NULL) {
1045 if (exclusive == EXCL) {
1046 VN_RELE(vp);
1047 return (EEXIST);
1048 }
1049 if (vp->v_type == VDIR && (mode & VWRITE) == VWRITE) {
1050 VN_RELE(vp);
1051 return (EISDIR);
1052 }
1053
1054 mutex_enter(&sffs_lock);
1055 node = VN2SFN(vp);
1056 error = sfnode_access(node, mode, cr);
1057 if (error != 0) {
1058 mutex_exit(&sffs_lock);
1059 VN_RELE(vp);
1060 return (error);
1061 }
1062
1063 /*
1064 * handle truncating an existing file
1065 */
1066 if (vp->v_type == VREG && (vap->va_mask & AT_SIZE) &&
1067 vap->va_size == 0) {
1068 sfnode_open(node);
1069 if (node->sf_path == NULL)
1070 error = ENOENT;
1071 else
1072 error = sfprov_trunc(node->sf_sffs->sf_handle,
1073 node->sf_path);
1074 if (error) {
1075 mutex_exit(&sffs_lock);
1076 VN_RELE(vp);
1077 return (error);
1078 }
1079 }
1080 mutex_exit(&sffs_lock);
1081 *vpp = vp;
1082 return (0);
1083 }
1084
1085 /*
1086 * Create a new node. First check for a race creating it.
1087 */
1088 mutex_enter(&sffs_lock);
1089 node = sfnode_lookup(VN2SFN(dvp), name, VNON);
1090 if (node != NULL) {
1091 mutex_exit(&sffs_lock);
1092 return (EEXIST);
1093 }
1094
1095 /*
1096 * Doesn't exist yet and we have the lock, so create it.
1097 */
1098 node = sfnode_lookup(VN2SFN(dvp), name, VREG);
1099 mutex_exit(&sffs_lock);
1100 if (node == NULL)
1101 return (EINVAL);
1102 *vpp = sfnode_get_vnode(node);
1103 return (0);
1104}
1105
1106/*ARGSUSED*/
1107static int
1108sffs_mkdir(
1109 vnode_t *dvp,
1110 char *nm,
1111 vattr_t *va,
1112 vnode_t **vpp,
1113 cred_t *cred,
1114 caller_context_t *ct,
1115 int flags,
1116 vsecattr_t *vsecp)
1117{
1118 sfnode_t *node;
1119 vnode_t *vp;
1120 int error;
1121
1122 /*
1123 * These should never happen
1124 */
1125 ASSERT(nm != NULL);
1126 ASSERT(strcmp(nm, "") != 0);
1127 ASSERT(strcmp(nm, ".") != 0);
1128 ASSERT(strcmp(nm, "..") != 0);
1129
1130 /*
1131 * Do an unlocked look up first
1132 */
1133 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1134 if (error == 0) {
1135 VN_RELE(vp);
1136 return (EEXIST);
1137 }
1138 if (error != ENOENT)
1139 return (error);
1140
1141 /*
1142 * Must be able to write in current directory
1143 */
1144 mutex_enter(&sffs_lock);
1145 error = sfnode_access(VN2SFN(dvp), VWRITE, cred);
1146 if (error) {
1147 mutex_exit(&sffs_lock);
1148 return (error);
1149 }
1150
1151 node = sfnode_lookup(VN2SFN(dvp), nm, VDIR);
1152 mutex_exit(&sffs_lock);
1153 if (node == NULL)
1154 return (EACCES);
1155 *vpp = sfnode_get_vnode(node);
1156 return (0);
1157}
1158
1159/*ARGSUSED*/
1160static int
1161sffs_rmdir(
1162 struct vnode *dvp,
1163 char *nm,
1164 vnode_t *cdir,
1165 cred_t *cred,
1166 caller_context_t *ct,
1167 int flags)
1168{
1169 sfnode_t *node;
1170 vnode_t *vp;
1171 int error;
1172
1173 /*
1174 * Return error when removing . and ..
1175 */
1176 if (strcmp(nm, ".") == 0 || strcmp(nm, "") == 0)
1177 return (EINVAL);
1178 if (strcmp(nm, "..") == 0)
1179 return (EEXIST);
1180
1181 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1182 if (error)
1183 return (error);
1184 if (vp->v_type != VDIR) {
1185 VN_RELE(vp);
1186 return (ENOTDIR);
1187 }
1188
1189#if 0
1190 if (vn_vfswlock(vp)) {
1191 VN_RELE(vp);
1192 return (EBUSY);
1193 }
1194#endif
1195
1196 if (vn_mountedvfs(vp)) {
1197 VN_RELE(vp);
1198 return (EBUSY);
1199 }
1200
1201 node = VN2SFN(vp);
1202
1203 mutex_enter(&sffs_lock);
1204 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1205 if (error)
1206 goto done;
1207
1208 /*
1209 * If anything else is using this vnode, then fail the remove.
1210 * Why? Windows hosts can't remove something that is open,
1211 * so we have to sfprov_close() it first.
1212 * There is no errno for this - since it's not a problem on UNIX,
1213 * but EINVAL is the closest.
1214 */
1215 if (node->sf_file != NULL) {
1216 if (vp->v_count > 1) {
1217 error = EINVAL;
1218 goto done;
1219 }
1220 (void)sfprov_close(node->sf_file);
1221 node->sf_file = NULL;
1222 }
1223
1224 /*
1225 * Remove the directory on the host and mark the node as stale.
1226 */
1227 error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
1228 if (error == ENOENT || error == 0)
1229 sfnode_make_stale(node);
1230done:
1231 mutex_exit(&sffs_lock);
1232 VN_RELE(vp);
1233 return (error);
1234}
1235
1236
1237/*ARGSUSED*/
1238static int
1239sffs_remove(
1240 vnode_t *dvp,
1241 char *name,
1242 cred_t *cred,
1243 caller_context_t *ct,
1244 int flags)
1245{
1246 vnode_t *vp;
1247 sfnode_t *node;
1248 int error;
1249
1250 /*
1251 * These should never happen
1252 */
1253 ASSERT(name != NULL);
1254 ASSERT(strcmp(name, "..") != 0);
1255
1256 error = sffs_lookup(dvp, name, &vp,
1257 NULL, 0, NULL, cred, ct, NULL, NULL);
1258 if (error)
1259 return (error);
1260 node = VN2SFN(vp);
1261
1262 mutex_enter(&sffs_lock);
1263 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1264 if (error)
1265 goto done;
1266
1267 /*
1268 * If anything else is using this vnode, then fail the remove.
1269 * Why? Windows hosts can't sfprov_remove() a file that is open,
1270 * so we have to sfprov_close() it first.
1271 * There is no errno for this - since it's not a problem on UNIX,
1272 * but ETXTBSY is the closest.
1273 */
1274 if (node->sf_file != NULL) {
1275 if (vp->v_count > 1) {
1276 error = ETXTBSY;
1277 goto done;
1278 }
1279 (void)sfprov_close(node->sf_file);
1280 node->sf_file = NULL;
1281 }
1282
1283 /*
1284 * Remove the file on the host and mark the node as stale.
1285 */
1286 error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
1287 if (error == ENOENT || error == 0)
1288 sfnode_make_stale(node);
1289done:
1290 mutex_exit(&sffs_lock);
1291 VN_RELE(vp);
1292 return (error);
1293}
1294
1295/*ARGSUSED*/
1296static int
1297sffs_rename(
1298 vnode_t *old_dir,
1299 char *old_nm,
1300 vnode_t *new_dir,
1301 char *new_nm,
1302 cred_t *cred,
1303 caller_context_t *ct,
1304 int flags)
1305{
1306 char *newpath;
1307 int error;
1308 sfnode_t *node;
1309
1310 if (strcmp(new_nm, "") == 0 ||
1311 strcmp(new_nm, ".") == 0 ||
1312 strcmp(new_nm, "..") == 0 ||
1313 strcmp(old_nm, "") == 0 ||
1314 strcmp(old_nm, ".") == 0 ||
1315 strcmp(old_nm, "..") == 0)
1316 return (EINVAL);
1317
1318 /*
1319 * make sure we have permission to do the rename
1320 */
1321 mutex_enter(&sffs_lock);
1322 error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
1323 if (error == 0 && new_dir != old_dir)
1324 error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
1325 if (error)
1326 goto done;
1327
1328 node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON);
1329 if (node == NULL) {
1330 error = ENOENT;
1331 goto done;
1332 }
1333
1334
1335 /*
1336 * Rename the file on the host and in our caches.
1337 */
1338 newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
1339 error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
1340 node->sf_type == VDIR);
1341 if (error == 0)
1342 sfnode_rename(node, VN2SFN(new_dir), newpath);
1343 else {
1344 kmem_free(newpath, strlen(newpath) + 1);
1345 if (error == ENOENT)
1346 sfnode_make_stale(node);
1347 }
1348done:
1349 mutex_exit(&sffs_lock);
1350 return (error);
1351}
1352
1353
1354/*ARGSUSED*/
1355static int
1356sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
1357{
1358#if 0
1359 sfnode_t *node;
1360
1361 /*
1362 * Ask the host to sync any data it may have cached for open files.
1363 * I don't think we care about errors.
1364 */
1365 mutex_enter(&sffs_lock);
1366 node = VN2SFN(vp);
1367 if (node->sf_file != NULL)
1368 (void) sfprov_fsync(node->sf_file);
1369 mutex_exit(&sffs_lock);
1370#endif
1371 return (0);
1372}
1373
1374/*
1375 * This may be the last reference, possibly time to close the file and
1376 * destroy the vnode. If the sfnode is stale, we'll destroy that too.
1377 */
1378/*ARGSUSED*/
1379static void
1380sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1381{
1382 sfnode_t *node;
1383
1384 /*
1385 * nothing to do if this isn't the last use
1386 */
1387 mutex_enter(&sffs_lock);
1388 node = VN2SFN(vp);
1389 mutex_enter(&vp->v_lock);
1390 if (vp->v_count > 1) {
1391 --vp->v_count;
1392 mutex_exit(&vp->v_lock);
1393 mutex_exit(&sffs_lock);
1394 return;
1395 }
1396
1397 /*
1398 * There should never be cached data, since we don't support mmap().
1399 */
1400 if (vn_has_cached_data(vp))
1401 panic("sffs_inactive() found cached data");
1402
1403 /*
1404 * destroy the vnode
1405 */
1406 node->sf_vnode = NULL;
1407 mutex_exit(&vp->v_lock);
1408 vn_invalid(vp);
1409 vn_free(vp);
1410 LogFlowFunc((" %s vnode cleared\n", node->sf_path));
1411
1412 /*
1413 * Close the sf_file for the node.
1414 */
1415 if (node->sf_file != NULL) {
1416 (void)sfprov_close(node->sf_file);
1417 node->sf_file = NULL;
1418 }
1419
1420 /*
1421 * If the node is stale, we can also destroy it.
1422 */
1423 if (node->sf_is_stale && node->sf_children == 0)
1424 sfnode_destroy(node);
1425
1426 mutex_exit(&sffs_lock);
1427 return;
1428}
1429
1430/*
1431 * All the work for this is really done in lookup.
1432 */
1433/*ARGSUSED*/
1434static int
1435sffs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
1436{
1437 return (0);
1438}
1439
1440/*
1441 * All the work for this is really done in inactive.
1442 */
1443/*ARGSUSED*/
1444static int
1445sffs_close(
1446 vnode_t *vp,
1447 int flag,
1448 int count,
1449 offset_t offset,
1450 cred_t *cr,
1451 caller_context_t *ct)
1452{
1453 return (0);
1454}
1455
1456/* ARGSUSED */
1457static int
1458sffs_seek(vnode_t *v, offset_t o, offset_t *no, caller_context_t *ct)
1459{
1460 if (*no < 0 || *no > MAXOFFSET_T)
1461 return (EINVAL);
1462 return (0);
1463}
1464
1465
1466
1467/*
1468 * By returning an error for this, we prevent anything in sffs from
1469 * being re-exported by NFS
1470 */
1471/* ARGSUSED */
1472static int
1473sffs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
1474{
1475 return (ENOTSUP);
1476}
1477
1478/*
1479 * vnode operations for regular files
1480 */
1481const fs_operation_def_t sffs_ops_template[] = {
1482#if defined(VBOX_VFS_SOLARIS_10U6)
1483 VOPNAME_ACCESS, sffs_access,
1484 VOPNAME_CLOSE, sffs_close,
1485 VOPNAME_CREATE, sffs_create,
1486 VOPNAME_FID, sffs_fid,
1487 VOPNAME_FSYNC, sffs_fsync,
1488 VOPNAME_GETATTR, sffs_getattr,
1489 VOPNAME_INACTIVE, sffs_inactive,
1490 VOPNAME_LOOKUP, sffs_lookup,
1491 VOPNAME_MKDIR, sffs_mkdir,
1492 VOPNAME_OPEN, sffs_open,
1493 VOPNAME_PATHCONF, sffs_pathconf,
1494 VOPNAME_READ, sffs_read,
1495 VOPNAME_READDIR, sffs_readdir,
1496 VOPNAME_REMOVE, sffs_remove,
1497 VOPNAME_RENAME, sffs_rename,
1498 VOPNAME_RMDIR, sffs_rmdir,
1499 VOPNAME_SEEK, sffs_seek,
1500 VOPNAME_WRITE, sffs_write,
1501 NULL, NULL
1502#else
1503 VOPNAME_ACCESS, { .vop_access = sffs_access },
1504 VOPNAME_CLOSE, { .vop_close = sffs_close },
1505 VOPNAME_CREATE, { .vop_create = sffs_create },
1506 VOPNAME_FID, { .vop_fid = sffs_fid },
1507 VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
1508 VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
1509 VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
1510 VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
1511 VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
1512 VOPNAME_OPEN, { .vop_open = sffs_open },
1513 VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
1514 VOPNAME_READ, { .vop_read = sffs_read },
1515 VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
1516 VOPNAME_REMOVE, { .vop_remove = sffs_remove },
1517 VOPNAME_RENAME, { .vop_rename = sffs_rename },
1518 VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
1519 VOPNAME_SEEK, { .vop_seek = sffs_seek },
1520 VOPNAME_WRITE, { .vop_write = sffs_write },
1521 NULL, NULL
1522#endif
1523};
1524
1525/*
1526 * Also, init and fini functions...
1527 */
1528int
1529sffs_vnode_init(void)
1530{
1531 int err;
1532
1533 err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
1534 if (err)
1535 return (err);
1536
1537 avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
1538 offsetof(sfnode_t, sf_linkage));
1539 avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
1540 offsetof(sfnode_t, sf_linkage));
1541
1542 sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
1543
1544 return (0);
1545}
1546
1547void
1548sffs_vnode_fini(void)
1549{
1550 if (sffs_ops)
1551 vn_freevnodeops(sffs_ops);
1552 ASSERT(avl_first(&sfnodes) == NULL);
1553 avl_destroy(&sfnodes);
1554 if (sffs_buffer != NULL) {
1555 kmem_free(sffs_buffer, PAGESIZE);
1556 sffs_buffer = NULL;
1557 }
1558}
1559
1560/*
1561 * Utility at unmount to get all nodes in that mounted filesystem removed.
1562 */
1563int
1564sffs_purge(struct sffs_data *sffs)
1565{
1566 sfnode_t *node;
1567 sfnode_t *prev;
1568
1569 /*
1570 * Check that no vnodes are active.
1571 */
1572 if (sffs->sf_rootnode->v_count > 1)
1573 return (-1);
1574 for (node = avl_first(&sfnodes); node;
1575 node = AVL_NEXT(&sfnodes, node)) {
1576 if (node->sf_sffs == sffs && node->sf_vnode &&
1577 node->sf_vnode != sffs->sf_rootnode)
1578 return (-1);
1579 }
1580 for (node = avl_first(&stale_sfnodes); node;
1581 node = AVL_NEXT(&stale_sfnodes, node)) {
1582 if (node->sf_sffs == sffs && node->sf_vnode &&
1583 node->sf_vnode != sffs->sf_rootnode)
1584 return (-1);
1585 }
1586
1587 /*
1588 * All clear to destroy all node information. Since there are no
1589 * vnodes, the make stale will cause deletion.
1590 */
1591 VN_RELE(sffs->sf_rootnode);
1592 mutex_enter(&sffs_lock);
1593 for (prev = NULL;;) {
1594 if (prev == NULL)
1595 node = avl_first(&sfnodes);
1596 else
1597 node = AVL_NEXT(&sfnodes, prev);
1598
1599 if (node == NULL)
1600 break;
1601
1602 if (node->sf_sffs == sffs) {
1603 if (node->sf_vnode != NULL)
1604 panic("vboxfs: purge hit active vnode");
1605 sfnode_make_stale(node);
1606 } else {
1607 prev = node;
1608 }
1609 }
1610done:
1611 mutex_exit(&sffs_lock);
1612 return (0);
1613}
1614
1615static void
1616sfnode_print(sfnode_t *node)
1617{
1618 Log(("0x%p", node));
1619 Log((" type=%s (%d)",
1620 node->sf_type == VDIR ? "VDIR" :
1621 node->sf_type == VNON ? "VNON" :
1622 node->sf_type == VREG ? "VREG" : "other", node->sf_type));
1623 Log((" ino=%d", (uint_t)node->sf_ino));
1624 Log((" path=%s", node->sf_path));
1625 Log((" parent=0x%p", node->sf_parent));
1626 if (node->sf_children)
1627 Log((" children=%d", node->sf_children));
1628 if (node->sf_vnode)
1629 Log((" vnode=0x%p", node->sf_vnode));
1630 Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
1631}
1632
1633void
1634sfnode_list()
1635{
1636 sfnode_t *n;
1637 for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
1638 sfnode_print(n);
1639 for (n = avl_first(&stale_sfnodes); n != NULL;
1640 n = AVL_NEXT(&stale_sfnodes, n))
1641 sfnode_print(n);
1642}
1643
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette