VirtualBox

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

Last change on this file since 29074 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 36.7 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 if (vn_vfswlock(vp) || vn_mountedvfs(vp)) {
1189 VN_RELE(vp);
1190 return (EBUSY);
1191 }
1192
1193 node = VN2SFN(vp);
1194
1195 mutex_enter(&sffs_lock);
1196 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1197 if (error)
1198 goto done;
1199
1200 /*
1201 * If anything else is using this vnode, then fail the remove.
1202 * Why? Windows hosts can't remove something that is open,
1203 * so we have to sfprov_close() it first.
1204 * There is no errno for this - since it's not a problem on UNIX,
1205 * but EINVAL is the closest.
1206 */
1207 if (node->sf_file != NULL) {
1208 if (vp->v_count > 1) {
1209 error = EINVAL;
1210 goto done;
1211 }
1212 (void)sfprov_close(node->sf_file);
1213 node->sf_file = NULL;
1214 }
1215
1216 /*
1217 * Remove the directory on the host and mark the node as stale.
1218 */
1219 error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
1220 if (error == ENOENT || error == 0)
1221 sfnode_make_stale(node);
1222done:
1223 mutex_exit(&sffs_lock);
1224 VN_RELE(vp);
1225 return (error);
1226}
1227
1228
1229/*ARGSUSED*/
1230static int
1231sffs_remove(
1232 vnode_t *dvp,
1233 char *name,
1234 cred_t *cred,
1235 caller_context_t *ct,
1236 int flags)
1237{
1238 vnode_t *vp;
1239 sfnode_t *node;
1240 int error;
1241
1242 /*
1243 * These should never happen
1244 */
1245 ASSERT(name != NULL);
1246 ASSERT(strcmp(name, "..") != 0);
1247
1248 error = sffs_lookup(dvp, name, &vp,
1249 NULL, 0, NULL, cred, ct, NULL, NULL);
1250 if (error)
1251 return (error);
1252 node = VN2SFN(vp);
1253
1254 mutex_enter(&sffs_lock);
1255 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1256 if (error)
1257 goto done;
1258
1259 /*
1260 * If anything else is using this vnode, then fail the remove.
1261 * Why? Windows hosts can't sfprov_remove() a file that is open,
1262 * so we have to sfprov_close() it first.
1263 * There is no errno for this - since it's not a problem on UNIX,
1264 * but ETXTBSY is the closest.
1265 */
1266 if (node->sf_file != NULL) {
1267 if (vp->v_count > 1) {
1268 error = ETXTBSY;
1269 goto done;
1270 }
1271 (void)sfprov_close(node->sf_file);
1272 node->sf_file = NULL;
1273 }
1274
1275 /*
1276 * Remove the file on the host and mark the node as stale.
1277 */
1278 error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
1279 if (error == ENOENT || error == 0)
1280 sfnode_make_stale(node);
1281done:
1282 mutex_exit(&sffs_lock);
1283 VN_RELE(vp);
1284 return (error);
1285}
1286
1287/*ARGSUSED*/
1288static int
1289sffs_rename(
1290 vnode_t *old_dir,
1291 char *old_nm,
1292 vnode_t *new_dir,
1293 char *new_nm,
1294 cred_t *cred,
1295 caller_context_t *ct,
1296 int flags)
1297{
1298 char *newpath;
1299 int error;
1300 sfnode_t *node;
1301
1302 if (strcmp(new_nm, "") == 0 ||
1303 strcmp(new_nm, ".") == 0 ||
1304 strcmp(new_nm, "..") == 0 ||
1305 strcmp(old_nm, "") == 0 ||
1306 strcmp(old_nm, ".") == 0 ||
1307 strcmp(old_nm, "..") == 0)
1308 return (EINVAL);
1309
1310 /*
1311 * make sure we have permission to do the rename
1312 */
1313 mutex_enter(&sffs_lock);
1314 error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
1315 if (error == 0 && new_dir != old_dir)
1316 error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
1317 if (error)
1318 goto done;
1319
1320 node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON);
1321 if (node == NULL) {
1322 error = ENOENT;
1323 goto done;
1324 }
1325
1326
1327 /*
1328 * Rename the file on the host and in our caches.
1329 */
1330 newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
1331 error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
1332 node->sf_type == VDIR);
1333 if (error == 0)
1334 sfnode_rename(node, VN2SFN(new_dir), newpath);
1335 else {
1336 kmem_free(newpath, strlen(newpath) + 1);
1337 if (error == ENOENT)
1338 sfnode_make_stale(node);
1339 }
1340done:
1341 mutex_exit(&sffs_lock);
1342 return (error);
1343}
1344
1345
1346/*ARGSUSED*/
1347static int
1348sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
1349{
1350#if 0
1351 sfnode_t *node;
1352
1353 /*
1354 * Ask the host to sync any data it may have cached for open files.
1355 * I don't think we care about errors.
1356 */
1357 mutex_enter(&sffs_lock);
1358 node = VN2SFN(vp);
1359 if (node->sf_file != NULL)
1360 (void) sfprov_fsync(node->sf_file);
1361 mutex_exit(&sffs_lock);
1362#endif
1363 return (0);
1364}
1365
1366/*
1367 * This may be the last reference, possibly time to close the file and
1368 * destroy the vnode. If the sfnode is stale, we'll destroy that too.
1369 */
1370/*ARGSUSED*/
1371static void
1372sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1373{
1374 sfnode_t *node;
1375
1376 /*
1377 * nothing to do if this isn't the last use
1378 */
1379 mutex_enter(&sffs_lock);
1380 node = VN2SFN(vp);
1381 mutex_enter(&vp->v_lock);
1382 if (vp->v_count > 1) {
1383 --vp->v_count;
1384 mutex_exit(&vp->v_lock);
1385 mutex_exit(&sffs_lock);
1386 return;
1387 }
1388
1389 /*
1390 * There should never be cached data, since we don't support mmap().
1391 */
1392 if (vn_has_cached_data(vp))
1393 panic("sffs_inactive() found cached data");
1394
1395 /*
1396 * destroy the vnode
1397 */
1398 node->sf_vnode = NULL;
1399 mutex_exit(&vp->v_lock);
1400 vn_invalid(vp);
1401 vn_free(vp);
1402 LogFlowFunc((" %s vnode cleared\n", node->sf_path));
1403
1404 /*
1405 * Close the sf_file for the node.
1406 */
1407 if (node->sf_file != NULL) {
1408 (void)sfprov_close(node->sf_file);
1409 node->sf_file = NULL;
1410 }
1411
1412 /*
1413 * If the node is stale, we can also destroy it.
1414 */
1415 if (node->sf_is_stale && node->sf_children == 0)
1416 sfnode_destroy(node);
1417
1418 mutex_exit(&sffs_lock);
1419 return;
1420}
1421
1422/*
1423 * All the work for this is really done in lookup.
1424 */
1425/*ARGSUSED*/
1426static int
1427sffs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
1428{
1429 return (0);
1430}
1431
1432/*
1433 * All the work for this is really done in inactive.
1434 */
1435/*ARGSUSED*/
1436static int
1437sffs_close(
1438 vnode_t *vp,
1439 int flag,
1440 int count,
1441 offset_t offset,
1442 cred_t *cr,
1443 caller_context_t *ct)
1444{
1445 return (0);
1446}
1447
1448/* ARGSUSED */
1449static int
1450sffs_seek(vnode_t *v, offset_t o, offset_t *no, caller_context_t *ct)
1451{
1452 if (*no < 0 || *no > MAXOFFSET_T)
1453 return (EINVAL);
1454 return (0);
1455}
1456
1457
1458
1459/*
1460 * By returning an error for this, we prevent anything in sffs from
1461 * being re-exported by NFS
1462 */
1463/* ARGSUSED */
1464static int
1465sffs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
1466{
1467 return (ENOTSUP);
1468}
1469
1470/*
1471 * vnode operations for regular files
1472 */
1473const fs_operation_def_t sffs_ops_template[] = {
1474#if defined(VBOX_VFS_SOLARIS_10U6)
1475 VOPNAME_ACCESS, sffs_access,
1476 VOPNAME_CLOSE, sffs_close,
1477 VOPNAME_CREATE, sffs_create,
1478 VOPNAME_FID, sffs_fid,
1479 VOPNAME_FSYNC, sffs_fsync,
1480 VOPNAME_GETATTR, sffs_getattr,
1481 VOPNAME_INACTIVE, sffs_inactive,
1482 VOPNAME_LOOKUP, sffs_lookup,
1483 VOPNAME_MKDIR, sffs_mkdir,
1484 VOPNAME_OPEN, sffs_open,
1485 VOPNAME_PATHCONF, sffs_pathconf,
1486 VOPNAME_READ, sffs_read,
1487 VOPNAME_READDIR, sffs_readdir,
1488 VOPNAME_REMOVE, sffs_remove,
1489 VOPNAME_RENAME, sffs_rename,
1490 VOPNAME_RMDIR, sffs_rmdir,
1491 VOPNAME_SEEK, sffs_seek,
1492 VOPNAME_WRITE, sffs_write,
1493 NULL, NULL
1494#else
1495 VOPNAME_ACCESS, { .vop_access = sffs_access },
1496 VOPNAME_CLOSE, { .vop_close = sffs_close },
1497 VOPNAME_CREATE, { .vop_create = sffs_create },
1498 VOPNAME_FID, { .vop_fid = sffs_fid },
1499 VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
1500 VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
1501 VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
1502 VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
1503 VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
1504 VOPNAME_OPEN, { .vop_open = sffs_open },
1505 VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
1506 VOPNAME_READ, { .vop_read = sffs_read },
1507 VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
1508 VOPNAME_REMOVE, { .vop_remove = sffs_remove },
1509 VOPNAME_RENAME, { .vop_rename = sffs_rename },
1510 VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
1511 VOPNAME_SEEK, { .vop_seek = sffs_seek },
1512 VOPNAME_WRITE, { .vop_write = sffs_write },
1513 NULL, NULL
1514#endif
1515};
1516
1517/*
1518 * Also, init and fini functions...
1519 */
1520int
1521sffs_vnode_init(void)
1522{
1523 int err;
1524
1525 err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
1526 if (err)
1527 return (err);
1528
1529 avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
1530 offsetof(sfnode_t, sf_linkage));
1531 avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
1532 offsetof(sfnode_t, sf_linkage));
1533
1534 sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
1535
1536 return (0);
1537}
1538
1539void
1540sffs_vnode_fini(void)
1541{
1542 if (sffs_ops)
1543 vn_freevnodeops(sffs_ops);
1544 ASSERT(avl_first(&sfnodes) == NULL);
1545 avl_destroy(&sfnodes);
1546 if (sffs_buffer != NULL) {
1547 kmem_free(sffs_buffer, PAGESIZE);
1548 sffs_buffer = NULL;
1549 }
1550}
1551
1552/*
1553 * Utility at unmount to get all nodes in that mounted filesystem removed.
1554 */
1555int
1556sffs_purge(struct sffs_data *sffs)
1557{
1558 sfnode_t *node;
1559 sfnode_t *prev;
1560
1561 /*
1562 * Check that no vnodes are active.
1563 */
1564 if (sffs->sf_rootnode->v_count > 1)
1565 return (-1);
1566 for (node = avl_first(&sfnodes); node;
1567 node = AVL_NEXT(&sfnodes, node)) {
1568 if (node->sf_sffs == sffs && node->sf_vnode &&
1569 node->sf_vnode != sffs->sf_rootnode)
1570 return (-1);
1571 }
1572 for (node = avl_first(&stale_sfnodes); node;
1573 node = AVL_NEXT(&stale_sfnodes, node)) {
1574 if (node->sf_sffs == sffs && node->sf_vnode &&
1575 node->sf_vnode != sffs->sf_rootnode)
1576 return (-1);
1577 }
1578
1579 /*
1580 * All clear to destroy all node information. Since there are no
1581 * vnodes, the make stale will cause deletion.
1582 */
1583 VN_RELE(sffs->sf_rootnode);
1584 mutex_enter(&sffs_lock);
1585 for (prev = NULL;;) {
1586 if (prev == NULL)
1587 node = avl_first(&sfnodes);
1588 else
1589 node = AVL_NEXT(&sfnodes, prev);
1590
1591 if (node == NULL)
1592 break;
1593
1594 if (node->sf_sffs == sffs) {
1595 if (node->sf_vnode != NULL)
1596 panic("vboxfs: purge hit active vnode");
1597 sfnode_make_stale(node);
1598 } else {
1599 prev = node;
1600 }
1601 }
1602done:
1603 mutex_exit(&sffs_lock);
1604 return (0);
1605}
1606
1607static void
1608sfnode_print(sfnode_t *node)
1609{
1610 Log(("0x%p", node));
1611 Log((" type=%s (%d)",
1612 node->sf_type == VDIR ? "VDIR" :
1613 node->sf_type == VNON ? "VNON" :
1614 node->sf_type == VREG ? "VREG" : "other", node->sf_type));
1615 Log((" ino=%d", (uint_t)node->sf_ino));
1616 Log((" path=%s", node->sf_path));
1617 Log((" parent=0x%p", node->sf_parent));
1618 if (node->sf_children)
1619 Log((" children=%d", node->sf_children));
1620 if (node->sf_vnode)
1621 Log((" vnode=0x%p", node->sf_vnode));
1622 Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
1623}
1624
1625void
1626sfnode_list()
1627{
1628 sfnode_t *n;
1629 for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
1630 sfnode_print(n);
1631 for (n = avl_first(&stale_sfnodes); n != NULL;
1632 n = AVL_NEXT(&stale_sfnodes, n))
1633 sfnode_print(n);
1634}
1635
Note: See TracBrowser for help on using the repository browser.

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