VirtualBox

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

Last change on this file since 38901 was 38901, checked in by vboxsync, 13 years ago

Additions/solaris/SharedFolders: readonly MMAP support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 51.0 KB
Line 
1/** @file
2 * VirtualBox File System for Solaris Guests, vnode implementation.
3 * Portions contributed by: Ronald.
4 */
5
6/*
7 * Copyright (C) 2009-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*
28 * Shared Folder File System is used from Solaris when run as a guest operating
29 * system on VirtualBox, though is meant to be usable with any hypervisor that
30 * can provide similar functionality. The sffs code handles all the Solaris
31 * specific semantics and relies on a provider module to actually access
32 * directories, files, etc. The provider interfaces are described in
33 * "vboxfs_prov.h" and the module implementing them is shipped as part of the
34 * VirtualBox Guest Additions for Solaris.
35 *
36 * The shared folder file system is similar to a networked file system,
37 * but with some caveats. The sffs code caches minimal information and proxies
38 * out to the provider whenever possible. Here are some things that are
39 * handled in this code and not by the proxy:
40 *
41 * - a way to open ".." from any already open directory
42 * - st_ino numbers
43 * - detecting directory changes that happened on the host.
44 *
45 * The implementation builds a cache of information for every file/directory
46 * ever accessed in all mounted sffs filesystems using sf_node structures.
47 *
48 * This information for both open or closed files can become invalid if
49 * asynchronous changes are made on the host. Solaris should not panic() in
50 * this event, but some file system operations may return unexpected errors.
51 * Information for such directories or files while they have active vnodes
52 * is removed from the regular cache and stored in a "stale" bucket until
53 * the vnode becomes completely inactive.
54 *
55 * No file data is cached in the guest. This means we don't support mmap() yet.
56 * A future version could relatively easily add support for read-only
57 * mmap(MAP_SHARED) and any mmap(MAP_PRIVATE). But a la ZFS, this data caching
58 * would not be coherent with normal simultaneous read()/write() operations,
59 * nor will it be coherent with data access on the host. Writable
60 * mmap(MAP_SHARED) access is possible, but guaranteeing any kind of coherency
61 * with concurrent activity on the host would be near impossible with the
62 * existing interfaces.
63 *
64 * A note about locking. sffs is not a high performance file system.
65 * No fine grained locking is done. The one sffs_lock protects just about
66 * everything.
67 */
68
69#include <VBox/log.h>
70#include <iprt/asm.h>
71
72#include <unistd.h>
73#include <sys/types.h>
74#include <sys/stat.h>
75#include <sys/mntent.h>
76#include <sys/param.h>
77#include <sys/modctl.h>
78#include <sys/mount.h>
79#include <sys/policy.h>
80#include <sys/atomic.h>
81#include <sys/sysmacros.h>
82#include <sys/ddi.h>
83#include <sys/sunddi.h>
84#include <sys/vfs.h>
85#include <sys/vmsystm.h>
86#include <vm/seg_kpm.h>
87#include <vm/pvn.h>
88#if !defined(VBOX_VFS_SOLARIS_10U6)
89#include <sys/vfs_opreg.h>
90#endif
91#include <sys/pathname.h>
92#include <sys/dirent.h>
93#include <sys/fs_subr.h>
94#include <sys/time.h>
95#include "vboxfs_prov.h"
96#include "vboxfs_vnode.h"
97#include "vboxfs_vfs.h"
98
99#define VBOXVFS_WITH_MMAP
100
101static struct vnodeops *sffs_ops = NULL;
102
103kmutex_t sffs_lock;
104static avl_tree_t sfnodes;
105static avl_tree_t stale_sfnodes;
106
107/*
108 * For now we'll use an I/O buffer that doesn't page fault for VirtualBox
109 * to transfer data into.
110 */
111char *sffs_buffer;
112
113/*
114 * sfnode_compare() is needed for AVL tree functionality.
115 * The nodes are sorted by mounted filesystem, then path. If the
116 * nodes are stale, the node pointer itself is used to force uniqueness.
117 */
118static int
119sfnode_compare(const void *a, const void *b)
120{
121 sfnode_t *x = (sfnode_t *)a;
122 sfnode_t *y = (sfnode_t *)b;
123 int diff;
124
125 if (x->sf_is_stale) {
126 ASSERT(y->sf_is_stale);
127 diff = strcmp(x->sf_path, y->sf_path);
128 if (diff == 0)
129 diff = (uintptr_t)y - (uintptr_t)x;
130 } else {
131 ASSERT(!y->sf_is_stale);
132 diff = (uintptr_t)y->sf_sffs - (uintptr_t)x->sf_sffs;
133 if (diff == 0)
134 diff = strcmp(x->sf_path, y->sf_path);
135 }
136 if (diff < 0)
137 return (-1);
138 if (diff > 0)
139 return (1);
140 return (0);
141}
142
143/*
144 * Construct a new pathname given an sfnode plus an optional tail component.
145 * This handles ".." and "."
146 */
147static char *
148sfnode_construct_path(sfnode_t *node, char *tail)
149{
150 char *p;
151
152 if (strcmp(tail, ".") == 0 || strcmp(tail, "..") == 0)
153 panic("construct path for %s", tail);
154 p = kmem_alloc(strlen(node->sf_path) + 1 + strlen(tail) + 1, KM_SLEEP);
155 strcpy(p, node->sf_path);
156 strcat(p, "/");
157 strcat(p, tail);
158 return (p);
159}
160
161/*
162 * Clears the (cached) directory listing for the node.
163 */
164static void
165sfnode_clear_dir_list(sfnode_t *node)
166{
167 ASSERT(MUTEX_HELD(&sffs_lock));
168
169 while (node->sf_dir_list != NULL) {
170 sffs_dirents_t *next = node->sf_dir_list->sf_next;
171 kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
172 node->sf_dir_list = next;
173 }
174
175 while (node->sf_dir_stats != NULL) {
176 sffs_stats_t *next = node->sf_dir_stats->sf_next;
177 kmem_free(node->sf_dir_stats, sizeof(*node->sf_dir_stats));
178 node->sf_dir_stats = next;
179 }
180}
181
182/*
183 * Open the provider file associated with a vnode. Holding the file open is
184 * the only way we have of trying to have a vnode continue to refer to the
185 * same host file in the host in light of the possibility of host side renames.
186 */
187static void
188sfnode_open(sfnode_t *node)
189{
190 int error;
191 sfp_file_t *fp;
192
193 if (node->sf_file != NULL)
194 return;
195 error = sfprov_open(node->sf_sffs->sf_handle, node->sf_path, &fp);
196 if (error == 0)
197 node->sf_file = fp;
198}
199
200/*
201 * get a new vnode reference for an sfnode
202 */
203vnode_t *
204sfnode_get_vnode(sfnode_t *node)
205{
206 vnode_t *vp;
207
208 if (node->sf_vnode != NULL) {
209 VN_HOLD(node->sf_vnode);
210 } else {
211 vp = vn_alloc(KM_SLEEP);
212 LogFlowFunc((" %s gets vnode 0x%p\n", node->sf_path, vp));
213 vp->v_type = node->sf_type;
214 vp->v_vfsp = node->sf_sffs->sf_vfsp;
215 vn_setops(vp, sffs_ops);
216 vp->v_flag = VNOSWAP;
217#ifndef VBOXVFS_WITH_MMAP
218 vp->v_flag |= VNOMAP;
219#endif
220 vn_exists(vp);
221 vp->v_data = node;
222 node->sf_vnode = vp;
223 }
224 sfnode_open(node);
225 return (node->sf_vnode);
226}
227
228/*
229 * Allocate and initialize a new sfnode and assign it a vnode
230 */
231sfnode_t *
232sfnode_make(
233 sffs_data_t *sffs,
234 char *path,
235 vtype_t type,
236 sfp_file_t *fp,
237 sfnode_t *parent, /* can be NULL for root */
238 sffs_stat_t *stat,
239 uint64_t stat_time)
240{
241 sfnode_t *node;
242 avl_index_t where;
243
244 ASSERT(MUTEX_HELD(&sffs_lock));
245 ASSERT(path != NULL);
246
247 /*
248 * build the sfnode
249 */
250 LogFlowFunc(("sffs_make(%s)\n", path));
251 node = kmem_alloc(sizeof (*node), KM_SLEEP);
252 node->sf_sffs = sffs;
253 VFS_HOLD(node->sf_sffs->sf_vfsp);
254 node->sf_path = path;
255 node->sf_ino = sffs->sf_ino++;
256 node->sf_type = type;
257 node->sf_is_stale = 0; /* never stale at creation */
258 node->sf_file = fp;
259 node->sf_vnode = NULL; /* do this before any sfnode_get_vnode() */
260 node->sf_children = 0;
261 node->sf_parent = parent;
262 if (parent)
263 ++parent->sf_children;
264 node->sf_dir_list = NULL;
265 node->sf_dir_stats = NULL;
266 if (stat != NULL) {
267 node->sf_stat = *stat;
268 node->sf_stat_time = stat_time;
269 } else {
270 node->sf_stat_time = 0;
271 }
272
273 /*
274 * add the new node to our cache
275 */
276 if (avl_find(&sfnodes, node, &where) != NULL)
277 panic("sffs_create_sfnode(%s): duplicate sfnode_t", path);
278 avl_insert(&sfnodes, node, where);
279 return (node);
280}
281
282/*
283 * destroy an sfnode
284 */
285static void
286sfnode_destroy(sfnode_t *node)
287{
288 avl_index_t where;
289 avl_tree_t *tree;
290 sfnode_t *parent;
291top:
292 parent = node->sf_parent;
293 ASSERT(MUTEX_HELD(&sffs_lock));
294 ASSERT(node->sf_path != NULL);
295 LogFlowFunc(("sffs_destroy(%s)%s\n", node->sf_path, node->sf_is_stale ? " stale": ""));
296 if (node->sf_children != 0)
297 panic("sfnode_destroy(%s) has %d children", node->sf_path, node->sf_children);
298 if (node->sf_vnode != NULL)
299 panic("sfnode_destroy(%s) has active vnode", node->sf_path);
300
301 if (node->sf_is_stale)
302 tree = &stale_sfnodes;
303 else
304 tree = &sfnodes;
305 if (avl_find(tree, node, &where) == NULL)
306 panic("sfnode_destroy(%s) not found", node->sf_path);
307 avl_remove(tree, node);
308
309 VFS_RELE(node->sf_sffs->sf_vfsp);
310 sfnode_clear_dir_list(node);
311 kmem_free(node->sf_path, strlen(node->sf_path) + 1);
312 kmem_free(node, sizeof (*node));
313 if (parent != NULL) {
314 sfnode_clear_dir_list(parent);
315 if (parent->sf_children == 0)
316 panic("sfnode_destroy(%s) parent has no child", node->sf_path);
317 --parent->sf_children;
318 if (parent->sf_children == 0 &&
319 parent->sf_is_stale &&
320 parent->sf_vnode == NULL) {
321 node = parent;
322 goto top;
323 }
324 }
325}
326
327/*
328 * Some sort of host operation on an sfnode has failed or it has been
329 * deleted. Mark this node and any children as stale, deleting knowledge
330 * about any which do not have active vnodes or children
331 * This also handle deleting an inactive node that was already stale.
332 */
333static void
334sfnode_make_stale(sfnode_t *node)
335{
336 sfnode_t *n;
337 int len;
338 ASSERT(MUTEX_HELD(&sffs_lock));
339 avl_index_t where;
340
341 /*
342 * First deal with any children of a directory node.
343 * If a directory becomes stale, anything below it becomes stale too.
344 */
345 if (!node->sf_is_stale && node->sf_type == VDIR) {
346 len = strlen(node->sf_path);
347
348 n = node;
349 while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
350 ASSERT(!n->sf_is_stale);
351
352 /*
353 * quit when no longer seeing children of node
354 */
355 if (n->sf_sffs != node->sf_sffs ||
356 strncmp(node->sf_path, n->sf_path, len) != 0 ||
357 n->sf_path[len] != '/')
358 break;
359
360 /*
361 * Either mark the child as stale or destroy it
362 */
363 if (n->sf_vnode == NULL && n->sf_children == 0) {
364 sfnode_destroy(n);
365 } else {
366 LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path));
367 sfnode_clear_dir_list(n);
368 if (avl_find(&sfnodes, n, &where) == NULL)
369 panic("sfnode_make_stale(%s)"
370 " not in sfnodes", n->sf_path);
371 avl_remove(&sfnodes, n);
372 n->sf_is_stale = 1;
373 if (avl_find(&stale_sfnodes, n, &where) != NULL)
374 panic("sffs_make_stale(%s) duplicates",
375 n->sf_path);
376 avl_insert(&stale_sfnodes, n, where);
377 }
378 }
379 }
380
381 /*
382 * Now deal with the given node.
383 */
384 if (node->sf_vnode == NULL && node->sf_children == 0) {
385 sfnode_destroy(node);
386 } else if (!node->sf_is_stale) {
387 LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path));
388 sfnode_clear_dir_list(node);
389 if (node->sf_parent)
390 sfnode_clear_dir_list(node->sf_parent);
391 if (avl_find(&sfnodes, node, &where) == NULL)
392 panic("sfnode_make_stale(%s) not in sfnodes",
393 node->sf_path);
394 avl_remove(&sfnodes, node);
395 node->sf_is_stale = 1;
396 if (avl_find(&stale_sfnodes, node, &where) != NULL)
397 panic("sffs_make_stale(%s) duplicates", node->sf_path);
398 avl_insert(&stale_sfnodes, node, where);
399 }
400}
401
402static uint64_t
403sfnode_cur_time_usec(void)
404{
405 clock_t now = drv_hztousec(ddi_get_lbolt());
406 return now;
407}
408
409static int
410sfnode_stat_cached(sfnode_t *node)
411{
412 return (sfnode_cur_time_usec() - node->sf_stat_time) <
413 node->sf_sffs->sf_stat_ttl * 1000L;
414}
415
416static int
417sfnode_get_stat(sfp_mount_t *mnt, char *path, sffs_stat_t *stat)
418{
419 return sfprov_get_attr(mnt, path, &stat->sf_mode, &stat->sf_size,
420 &stat->sf_atime, &stat->sf_mtime, &stat->sf_ctime);
421}
422
423static void
424sfnode_invalidate_stat_cache(sfnode_t *node)
425{
426 node->sf_stat_time = 0;
427}
428
429static int
430sfnode_update_stat_cache(sfnode_t *node)
431{
432 int error;
433
434 error = sfnode_get_stat(node->sf_sffs->sf_handle, node->sf_path,
435 &node->sf_stat);
436 if (error == ENOENT)
437 sfnode_make_stale(node);
438 if (error == 0)
439 node->sf_stat_time = sfnode_cur_time_usec();
440
441 return (error);
442}
443
444/*
445 * Rename a file or a directory
446 */
447static void
448sfnode_rename(sfnode_t *node, sfnode_t *newparent, char *path)
449{
450 sfnode_t *n;
451 sfnode_t template;
452 avl_index_t where;
453 int len = strlen(path);
454 int old_len;
455 char *new_path;
456 char *tail;
457 ASSERT(MUTEX_HELD(&sffs_lock));
458
459 ASSERT(!node->sf_is_stale);
460
461 /*
462 * Have to remove anything existing that had the new name.
463 */
464 template.sf_sffs = node->sf_sffs;
465 template.sf_path = path;
466 template.sf_is_stale = 0;
467 n = avl_find(&sfnodes, &template, &where);
468 if (n != NULL)
469 sfnode_make_stale(n);
470
471 /*
472 * Do the renaming, deal with any children of this node first.
473 */
474 if (node->sf_type == VDIR) {
475 old_len = strlen(node->sf_path);
476 while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
477
478 /*
479 * quit when no longer seeing children of node
480 */
481 if (n->sf_sffs != node->sf_sffs ||
482 strncmp(node->sf_path, n->sf_path, old_len) != 0 ||
483 n->sf_path[old_len] != '/')
484 break;
485
486 /*
487 * Rename the child:
488 * - build the new path name
489 * - unlink the AVL node
490 * - assign the new name
491 * - re-insert the AVL name
492 */
493 ASSERT(strlen(n->sf_path) > old_len);
494 tail = n->sf_path + old_len; /* includes initial "/" */
495 new_path = kmem_alloc(len + strlen(tail) + 1,
496 KM_SLEEP);
497 strcpy(new_path, path);
498 strcat(new_path, tail);
499 if (avl_find(&sfnodes, n, &where) == NULL)
500 panic("sfnode_rename(%s) not in sfnodes",
501 n->sf_path);
502 avl_remove(&sfnodes, n);
503 LogFlowFunc(("sfnode_rname(%s to %s) sub\n", n->sf_path, new_path));
504 kmem_free(n->sf_path, strlen(n->sf_path) + 1);
505 n->sf_path = new_path;
506 if (avl_find(&sfnodes, n, &where) != NULL)
507 panic("sfnode_rename(%s) duplicates",
508 n->sf_path);
509 avl_insert(&sfnodes, n, where);
510 }
511 }
512
513 /*
514 * Deal with the given node.
515 */
516 if (avl_find(&sfnodes, node, &where) == NULL)
517 panic("sfnode_rename(%s) not in sfnodes", node->sf_path);
518 avl_remove(&sfnodes, node);
519 LogFlowFunc(("sfnode_rname(%s to %s)\n", node->sf_path, path));
520 kmem_free(node->sf_path, strlen(node->sf_path) + 1);
521 node->sf_path = path;
522 if (avl_find(&sfnodes, node, &where) != NULL)
523 panic("sfnode_rename(%s) duplicates", node->sf_path);
524 avl_insert(&sfnodes, node, where);
525
526 /*
527 * change the parent
528 */
529 if (node->sf_parent == NULL)
530 panic("sfnode_rename(%s) no parent", node->sf_path);
531 if (node->sf_parent->sf_children == 0)
532 panic("sfnode_rename(%s) parent has no child", node->sf_path);
533 sfnode_clear_dir_list(node->sf_parent);
534 sfnode_clear_dir_list(newparent);
535 --node->sf_parent->sf_children;
536 node->sf_parent = newparent;
537 ++newparent->sf_children;
538}
539
540/*
541 * Look for a cached node, if not found either handle ".." or try looking
542 * via the provider. Create an entry in sfnodes if found but not cached yet.
543 * If the create flag is set, a file or directory is created. If the file
544 * already existed, an error is returned.
545 * Nodes returned from this routine always have a vnode with its ref count
546 * bumped by 1.
547 */
548static sfnode_t *
549sfnode_lookup(
550 sfnode_t *dir,
551 char *name,
552 vtype_t create,
553 sffs_stat_t *stat,
554 uint64_t stat_time,
555 int *err)
556{
557 avl_index_t where;
558 sfnode_t template;
559 sfnode_t *node;
560 int error = 0;
561 int type;
562 char *fullpath;
563 sfp_file_t *fp;
564 sffs_stat_t tmp_stat;
565
566 ASSERT(MUTEX_HELD(&sffs_lock));
567
568 if (err)
569 *err = error;
570
571 /*
572 * handle referencing myself
573 */
574 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0)
575 return (dir);
576
577 /*
578 * deal with parent
579 */
580 if (strcmp(name, "..") == 0)
581 return (dir->sf_parent);
582
583 /*
584 * Look for an existing node.
585 */
586 fullpath = sfnode_construct_path(dir, name);
587 template.sf_sffs = dir->sf_sffs;
588 template.sf_path = fullpath;
589 template.sf_is_stale = 0;
590 node = avl_find(&sfnodes, &template, &where);
591 if (node != NULL) {
592 kmem_free(fullpath, strlen(fullpath) + 1);
593 if (create != VNON)
594 return (NULL);
595 return (node);
596 }
597
598 /*
599 * No entry for this path currently.
600 * Check if the file exists with the provider and get the type from
601 * there.
602 */
603 if (create == VREG) {
604 type = VREG;
605 error = sfprov_create(dir->sf_sffs->sf_handle, fullpath, &fp);
606 } else if (create == VDIR) {
607 type = VDIR;
608 error = sfprov_mkdir(dir->sf_sffs->sf_handle, fullpath, &fp);
609 } else {
610 mode_t m;
611 fp = NULL;
612 type = VNON;
613 if (stat == NULL) {
614 stat = &tmp_stat;
615 error = sfnode_get_stat(dir->sf_sffs->sf_handle,
616 fullpath, stat);
617 stat_time = sfnode_cur_time_usec();
618 } else {
619 error = 0;
620 }
621 m = stat->sf_mode;
622 if (error != 0)
623 error = ENOENT;
624 else if (S_ISDIR(m))
625 type = VDIR;
626 else if (S_ISREG(m))
627 type = VREG;
628 }
629
630 if (err)
631 *err = error;
632
633 /*
634 * If no errors, make a new node and return it.
635 */
636 if (error) {
637 kmem_free(fullpath, strlen(fullpath) + 1);
638 return (NULL);
639 }
640 node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir, stat,
641 stat_time);
642 return (node);
643}
644
645
646/*
647 * uid and gid in sffs determine owner and group for all files.
648 */
649static int
650sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr)
651{
652 sffs_data_t *sffs = node->sf_sffs;
653 mode_t m;
654 int shift = 0;
655 int error;
656 vnode_t *vp;
657
658 ASSERT(MUTEX_HELD(&sffs_lock));
659
660 /*
661 * get the mode from the cache or provider
662 */
663 if (sfnode_stat_cached(node))
664 error = 0;
665 else
666 error = sfnode_update_stat_cache(node);
667 m = (error == 0) ? node->sf_stat.sf_mode : 0;
668
669 /*
670 * mask off the permissions based on uid/gid
671 */
672 if (crgetuid(cr) != sffs->sf_uid) {
673 shift += 3;
674 if (groupmember(sffs->sf_gid, cr) == 0)
675 shift += 3;
676 }
677 mode &= ~(m << shift);
678
679 if (mode == 0) {
680 error = 0;
681 } else {
682 vp = sfnode_get_vnode(node);
683 error = secpolicy_vnode_access(cr, vp, sffs->sf_uid, mode);
684 VN_RELE(vp);
685 }
686 return (error);
687}
688
689
690/*
691 *
692 * Everything below this point are the vnode operations used by Solaris VFS
693 */
694static int
695sffs_readdir(
696 vnode_t *vp,
697 uio_t *uiop,
698 cred_t *cred,
699 int *eofp,
700 caller_context_t *ct,
701 int flags)
702{
703 sfnode_t *dir = VN2SFN(vp);
704 sfnode_t *node;
705 struct dirent64 *dirent;
706 sffs_dirents_t *cur_buf;
707 sffs_stats_t *cur_stats;
708 int cur_snum;
709 offset_t offset;
710 int dummy_eof;
711 int error = 0;
712
713 if (uiop->uio_iovcnt != 1)
714 return (EINVAL);
715
716 if (vp->v_type != VDIR)
717 return (ENOTDIR);
718
719 if (eofp == NULL)
720 eofp = &dummy_eof;
721 *eofp = 0;
722
723 if (uiop->uio_loffset >= MAXOFFSET_T) {
724 *eofp = 1;
725 return (0);
726 }
727
728 /*
729 * Get the directory entry names from the host. This gets all
730 * entries. These are stored in a linked list of sffs_dirents_t
731 * buffers, each of which contains a list of dirent64_t's.
732 */
733 mutex_enter(&sffs_lock);
734
735 if (dir->sf_dir_list == NULL) {
736 error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
737 &dir->sf_dir_list, &dir->sf_dir_stats);
738 if (error != 0)
739 goto done;
740 }
741
742 /*
743 * Lookup each of the names, so that we have ino's, and copy to
744 * result buffer.
745 */
746 offset = 0;
747 cur_buf = dir->sf_dir_list;
748 cur_stats = dir->sf_dir_stats;
749 cur_snum = 0;
750 while (cur_buf != NULL) {
751 if (offset + cur_buf->sf_len <= uiop->uio_loffset) {
752 offset += cur_buf->sf_len;
753 cur_buf = cur_buf->sf_next;
754 continue;
755 }
756
757 if (cur_snum >= SFFS_STATS_LEN) {
758 cur_stats = cur_stats->sf_next;
759 cur_snum = 0;
760 }
761
762 dirent = (dirent64_t *)
763 (((char *) &cur_buf->sf_entries[0]) +
764 (uiop->uio_loffset - offset));
765 if (dirent->d_reclen > uiop->uio_resid)
766 break;
767
768 if (strcmp(dirent->d_name, ".") == 0) {
769 node = dir;
770 } else if (strcmp(dirent->d_name, "..") == 0) {
771 node = dir->sf_parent;
772 if (node == NULL)
773 node = dir;
774 } else {
775 node = sfnode_lookup(dir, dirent->d_name, VNON,
776 &cur_stats->sf_stats[cur_snum],
777 sfnode_cur_time_usec(), NULL);
778 if (node == NULL)
779 panic("sffs_readdir() lookup failed");
780 }
781 dirent->d_ino = node->sf_ino;
782
783 error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
784 ++cur_snum;
785 if (error != 0)
786 break;
787 }
788 if (error == 0 && cur_buf == NULL)
789 *eofp = 1;
790done:
791 mutex_exit(&sffs_lock);
792 return (error);
793}
794
795
796#if defined(VBOX_VFS_SOLARIS_10U6)
797/*
798 * HERE JOE.. this may need more logic, need to look at other file systems
799 */
800static int
801sffs_pathconf(
802 vnode_t *vp,
803 int cmd,
804 ulong_t *valp,
805 cred_t *cr)
806{
807 return (fs_pathconf(vp, cmd, valp, cr));
808}
809#else
810/*
811 * HERE JOE.. this may need more logic, need to look at other file systems
812 */
813static int
814sffs_pathconf(
815 vnode_t *vp,
816 int cmd,
817 ulong_t *valp,
818 cred_t *cr,
819 caller_context_t *ct)
820{
821 return (fs_pathconf(vp, cmd, valp, cr, ct));
822}
823#endif
824
825static int
826sffs_getattr(
827 vnode_t *vp,
828 vattr_t *vap,
829 int flags,
830 cred_t *cred,
831 caller_context_t *ct)
832{
833 sfnode_t *node = VN2SFN(vp);
834 sffs_data_t *sffs = node->sf_sffs;
835 mode_t mode;
836 int error = 0;
837
838 mutex_enter(&sffs_lock);
839 vap->va_type = vp->v_type;
840 vap->va_uid = sffs->sf_uid;
841 vap->va_gid = sffs->sf_gid;
842 vap->va_fsid = sffs->sf_vfsp->vfs_dev;
843 vap->va_nodeid = node->sf_ino;
844 vap->va_nlink = 1;
845 vap->va_rdev = sffs->sf_vfsp->vfs_dev;
846 vap->va_seq = 0;
847
848 if (!sfnode_stat_cached(node)) {
849 error = sfnode_update_stat_cache(node);
850 if (error != 0)
851 goto done;
852 }
853
854 vap->va_atime = node->sf_stat.sf_atime;
855 vap->va_mtime = node->sf_stat.sf_mtime;
856 vap->va_ctime = node->sf_stat.sf_ctime;
857
858 mode = node->sf_stat.sf_mode;
859 vap->va_mode = mode & MODEMASK;
860 if (S_ISDIR(mode))
861 vap->va_type = VDIR;
862 else if (S_ISREG(mode))
863 vap->va_type = VREG;
864 else if (S_ISFIFO(mode))
865 vap->va_type = VFIFO;
866 else if (S_ISCHR(mode))
867 vap->va_type = VCHR;
868 else if (S_ISBLK(mode))
869 vap->va_type = VBLK;
870 else if (S_ISLNK(mode))
871 vap->va_type = VLNK;
872 else if (S_ISSOCK(mode))
873 vap->va_type = VSOCK;
874
875 vap->va_size = node->sf_stat.sf_size;
876 vap->va_blksize = 512;
877 vap->va_nblocks = (vap->va_size + 511) / 512;
878
879done:
880 mutex_exit(&sffs_lock);
881 return (error);
882}
883
884static int
885sffs_setattr(
886 vnode_t *vp,
887 vattr_t *vap,
888 int flags,
889 cred_t *cred,
890 caller_context_t *ct)
891{
892 sfnode_t *node = VN2SFN(vp);
893 int error;
894 mode_t mode;
895
896 mode = vap->va_mode;
897 if (vp->v_type == VREG)
898 mode |= S_IFREG;
899 else if (vp->v_type == VDIR)
900 mode |= S_IFDIR;
901 else if (vp->v_type == VBLK)
902 mode |= S_IFBLK;
903 else if (vp->v_type == VCHR)
904 mode |= S_IFCHR;
905 else if (vp->v_type == VLNK)
906 mode |= S_IFLNK;
907 else if (vp->v_type == VFIFO)
908 mode |= S_IFIFO;
909 else if (vp->v_type == VSOCK)
910 mode |= S_IFSOCK;
911
912 mutex_enter(&sffs_lock);
913
914 sfnode_invalidate_stat_cache(node);
915 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
916 vap->va_mask, mode, vap->va_atime, vap->va_mtime, vap->va_ctime);
917 if (error == ENOENT)
918 sfnode_make_stale(node);
919
920 mutex_exit(&sffs_lock);
921 return (error);
922}
923
924static int
925sffs_space(
926 vnode_t *vp,
927 int cmd,
928 struct flock64 *bfp,
929 int flags,
930 offset_t off,
931 cred_t *cred,
932 caller_context_t *ct)
933{
934 sfnode_t *node = VN2SFN(vp);
935 int error;
936
937 /* we only support changing the length of the file */
938 if (bfp->l_whence != SEEK_SET || bfp->l_len != 0)
939 return ENOSYS;
940
941 mutex_enter(&sffs_lock);
942
943 sfnode_invalidate_stat_cache(node);
944
945 error = sfprov_set_size(node->sf_sffs->sf_handle, node->sf_path,
946 bfp->l_start);
947 if (error == ENOENT)
948 sfnode_make_stale(node);
949
950 mutex_exit(&sffs_lock);
951 return (error);
952}
953
954/*ARGSUSED*/
955static int
956sffs_read(
957 vnode_t *vp,
958 struct uio *uio,
959 int ioflag,
960 cred_t *cred,
961 caller_context_t *ct)
962{
963 sfnode_t *node = VN2SFN(vp);
964 int error = 0;
965 uint32_t bytes;
966 uint32_t done;
967 ulong_t offset;
968 ssize_t total;
969
970 if (vp->v_type == VDIR)
971 return (EISDIR);
972 if (vp->v_type != VREG)
973 return (EINVAL);
974 if (uio->uio_loffset >= MAXOFFSET_T)
975 {
976 proc_t *p = ttoproc(curthread);
977 mutex_enter(&p->p_lock);
978 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
979 p, RCA_UNSAFE_SIGINFO);
980 mutex_exit(&p->p_lock);
981 return (EFBIG);
982 }
983 if (uio->uio_loffset < 0)
984 return (EINVAL);
985 total = uio->uio_resid;
986 if (total == 0)
987 return (0);
988
989 mutex_enter(&sffs_lock);
990 sfnode_open(node);
991 if (node->sf_file == NULL) {
992 mutex_exit(&sffs_lock);
993 return (EINVAL);
994 }
995
996 do {
997 offset = uio->uio_offset;
998 done = bytes = MIN(PAGESIZE, uio->uio_resid);
999 error = sfprov_read(node->sf_file, sffs_buffer, offset, &done);
1000 if (error == 0 && done > 0)
1001 error = uiomove(sffs_buffer, done, UIO_READ, uio);
1002 } while (error == 0 && uio->uio_resid > 0 && done > 0);
1003
1004 mutex_exit(&sffs_lock);
1005
1006 /*
1007 * a partial read is never an error
1008 */
1009 if (total != uio->uio_resid)
1010 error = 0;
1011 return (error);
1012}
1013
1014/*ARGSUSED*/
1015static int
1016sffs_write(
1017 vnode_t *vp,
1018 struct uio *uiop,
1019 int ioflag,
1020 cred_t *cred,
1021 caller_context_t *ct)
1022{
1023 sfnode_t *node = VN2SFN(vp);
1024 int error = 0;
1025 uint32_t bytes;
1026 uint32_t done;
1027 ulong_t offset;
1028 ssize_t total;
1029 rlim64_t limit = uiop->uio_llimit;
1030
1031 if (vp->v_type == VDIR)
1032 return (EISDIR);
1033 if (vp->v_type != VREG)
1034 return (EINVAL);
1035
1036 /*
1037 * We have to hold this lock for a long time to keep
1038 * multiple FAPPEND writes from intermixing
1039 */
1040 mutex_enter(&sffs_lock);
1041 sfnode_open(node);
1042 if (node->sf_file == NULL) {
1043 mutex_exit(&sffs_lock);
1044 return (EINVAL);
1045 }
1046
1047 sfnode_invalidate_stat_cache(node);
1048
1049 if (ioflag & FAPPEND) {
1050 uint64_t endoffile;
1051
1052 error = sfprov_get_size(node->sf_sffs->sf_handle,
1053 node->sf_path, &endoffile);
1054 if (error == ENOENT)
1055 sfnode_make_stale(node);
1056 if (error != 0) {
1057 mutex_exit(&sffs_lock);
1058 return (error);
1059 }
1060 uiop->uio_loffset = endoffile;
1061 }
1062
1063 if (vp->v_type != VREG || uiop->uio_loffset < 0) {
1064 mutex_exit(&sffs_lock);
1065 return (EINVAL);
1066 }
1067 if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
1068 limit = MAXOFFSET_T;
1069
1070 if (uiop->uio_loffset >= limit) {
1071 proc_t *p = ttoproc(curthread);
1072 mutex_enter(&p->p_lock);
1073 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
1074 p, RCA_UNSAFE_SIGINFO);
1075 mutex_exit(&p->p_lock);
1076 mutex_exit(&sffs_lock);
1077 return (EFBIG);
1078 }
1079
1080 if (uiop->uio_loffset >= MAXOFFSET_T) {
1081 mutex_exit(&sffs_lock);
1082 return (EFBIG);
1083 }
1084
1085 total = uiop->uio_resid;
1086 if (total == 0) {
1087 mutex_exit(&sffs_lock);
1088 return (0);
1089 }
1090
1091 do {
1092 offset = uiop->uio_offset;
1093 bytes = MIN(PAGESIZE, uiop->uio_resid);
1094 if (offset + bytes >= limit) {
1095 if (offset >= limit) {
1096 error = EFBIG;
1097 break;
1098 }
1099 bytes = limit - offset;
1100 }
1101 error = uiomove(sffs_buffer, bytes, UIO_WRITE, uiop);
1102 if (error != 0)
1103 break;
1104 done = bytes;
1105 if (error == 0)
1106 error = sfprov_write(node->sf_file, sffs_buffer,
1107 offset, &done);
1108 total -= done;
1109 if (done != bytes) {
1110 uiop->uio_resid += bytes - done;
1111 break;
1112 }
1113 } while (error == 0 && uiop->uio_resid > 0 && done > 0);
1114
1115 mutex_exit(&sffs_lock);
1116
1117 /*
1118 * A short write is never really an error.
1119 */
1120 if (total != uiop->uio_resid)
1121 error = 0;
1122 return (error);
1123}
1124
1125/*ARGSUSED*/
1126static int
1127sffs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
1128{
1129 sfnode_t *node = VN2SFN(vp);
1130 int error;
1131
1132 mutex_enter(&sffs_lock);
1133 error = sfnode_access(node, mode, cr);
1134 mutex_exit(&sffs_lock);
1135 return (error);
1136}
1137
1138/*
1139 * Lookup an entry in a directory and create a new vnode if found.
1140 */
1141/* ARGSUSED3 */
1142static int
1143sffs_lookup(
1144 vnode_t *dvp, /* the directory vnode */
1145 char *name, /* the name of the file or directory */
1146 vnode_t **vpp, /* the vnode we found or NULL */
1147 struct pathname *pnp,
1148 int flags,
1149 vnode_t *rdir,
1150 cred_t *cred,
1151 caller_context_t *ct,
1152 int *direntflags,
1153 struct pathname *realpnp)
1154{
1155 int error;
1156 sfnode_t *node;
1157
1158 /*
1159 * dvp must be a directory
1160 */
1161 if (dvp->v_type != VDIR)
1162 return (ENOTDIR);
1163
1164 /*
1165 * An empty component name or just "." means the directory itself.
1166 * Don't do any further lookup or checking.
1167 */
1168 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0) {
1169 VN_HOLD(dvp);
1170 *vpp = dvp;
1171 return (0);
1172 }
1173
1174 /*
1175 * Check permission to look at this directory. We always allow "..".
1176 */
1177 mutex_enter(&sffs_lock);
1178 if (strcmp(name, "..") != 0) {
1179 error = sfnode_access(VN2SFN(dvp), VEXEC, cred);
1180 if (error) {
1181 mutex_exit(&sffs_lock);
1182 return (error);
1183 }
1184 }
1185
1186 /*
1187 * Lookup the node.
1188 */
1189 node = sfnode_lookup(VN2SFN(dvp), name, VNON, NULL, 0, NULL);
1190 if (node != NULL)
1191 *vpp = sfnode_get_vnode(node);
1192 mutex_exit(&sffs_lock);
1193 return ((node == NULL) ? ENOENT : 0);
1194}
1195
1196/*ARGSUSED*/
1197static int
1198sffs_create(
1199 vnode_t *dvp,
1200 char *name,
1201 struct vattr *vap,
1202 vcexcl_t exclusive,
1203 int mode,
1204 vnode_t **vpp,
1205 cred_t *cr,
1206 int flag,
1207 caller_context_t *ct,
1208 vsecattr_t *vsecp)
1209{
1210 vnode_t *vp;
1211 sfnode_t *node;
1212 int error;
1213
1214 ASSERT(name != NULL);
1215
1216 /*
1217 * this is used for regular files, not mkdir
1218 */
1219 if (vap->va_type == VDIR)
1220 return (EISDIR);
1221 if (vap->va_type != VREG)
1222 return (EINVAL);
1223
1224 /*
1225 * is this a pre-existing file?
1226 */
1227 error = sffs_lookup(dvp, name, &vp,
1228 NULL, 0, NULL, cr, ct, NULL, NULL);
1229 if (error == ENOENT)
1230 vp = NULL;
1231 else if (error != 0)
1232 return (error);
1233
1234 /*
1235 * Operation on a pre-existing file.
1236 */
1237 if (vp != NULL) {
1238 if (exclusive == EXCL) {
1239 VN_RELE(vp);
1240 return (EEXIST);
1241 }
1242 if (vp->v_type == VDIR && (mode & VWRITE) == VWRITE) {
1243 VN_RELE(vp);
1244 return (EISDIR);
1245 }
1246
1247 mutex_enter(&sffs_lock);
1248 node = VN2SFN(vp);
1249 error = sfnode_access(node, mode, cr);
1250 if (error != 0) {
1251 mutex_exit(&sffs_lock);
1252 VN_RELE(vp);
1253 return (error);
1254 }
1255
1256 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1257
1258 /*
1259 * handle truncating an existing file
1260 */
1261 if (vp->v_type == VREG && (vap->va_mask & AT_SIZE) &&
1262 vap->va_size == 0) {
1263 sfnode_open(node);
1264 if (node->sf_path == NULL)
1265 error = ENOENT;
1266 else
1267 error = sfprov_trunc(node->sf_sffs->sf_handle,
1268 node->sf_path);
1269 if (error) {
1270 mutex_exit(&sffs_lock);
1271 VN_RELE(vp);
1272 return (error);
1273 }
1274 }
1275 mutex_exit(&sffs_lock);
1276 *vpp = vp;
1277 return (0);
1278 }
1279
1280 /*
1281 * Create a new node. First check for a race creating it.
1282 */
1283 mutex_enter(&sffs_lock);
1284 node = sfnode_lookup(VN2SFN(dvp), name, VNON, NULL, 0, NULL);
1285 if (node != NULL) {
1286 mutex_exit(&sffs_lock);
1287 return (EEXIST);
1288 }
1289
1290 /*
1291 * Doesn't exist yet and we have the lock, so create it.
1292 */
1293 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1294 int lookuperr;
1295 node = sfnode_lookup(VN2SFN(dvp), name, VREG, NULL, 0, &lookuperr);
1296 if (node && (vap->va_mask & AT_MODE)) {
1297 timestruc_t dummy;
1298 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
1299 AT_MODE, vap->va_mode, dummy, dummy, dummy);
1300 if (error)
1301 cmn_err(CE_WARN, "sffs_create: set_mode(%s, %o) failed"
1302 " rc=%d", node->sf_path, vap->va_mode, error);
1303 }
1304
1305 if (node && node->sf_parent)
1306 sfnode_clear_dir_list(node->sf_parent);
1307
1308 mutex_exit(&sffs_lock);
1309 if (node == NULL)
1310 return (lookuperr);
1311 *vpp = sfnode_get_vnode(node);
1312 return (0);
1313}
1314
1315/*ARGSUSED*/
1316static int
1317sffs_mkdir(
1318 vnode_t *dvp,
1319 char *nm,
1320 vattr_t *va,
1321 vnode_t **vpp,
1322 cred_t *cred,
1323 caller_context_t *ct,
1324 int flags,
1325 vsecattr_t *vsecp)
1326{
1327 sfnode_t *node;
1328 vnode_t *vp;
1329 int error;
1330
1331 /*
1332 * These should never happen
1333 */
1334 ASSERT(nm != NULL);
1335 ASSERT(strcmp(nm, "") != 0);
1336 ASSERT(strcmp(nm, ".") != 0);
1337 ASSERT(strcmp(nm, "..") != 0);
1338
1339 /*
1340 * Do an unlocked look up first
1341 */
1342 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1343 if (error == 0) {
1344 VN_RELE(vp);
1345 return (EEXIST);
1346 }
1347 if (error != ENOENT)
1348 return (error);
1349
1350 /*
1351 * Must be able to write in current directory
1352 */
1353 mutex_enter(&sffs_lock);
1354 error = sfnode_access(VN2SFN(dvp), VWRITE, cred);
1355 if (error) {
1356 mutex_exit(&sffs_lock);
1357 return (error);
1358 }
1359
1360 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1361 int lookuperr = EACCES;
1362 node = sfnode_lookup(VN2SFN(dvp), nm, VDIR, NULL, 0, &lookuperr);
1363 if (node && (va->va_mask & AT_MODE)) {
1364 timestruc_t dummy;
1365 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
1366 AT_MODE, va->va_mode, dummy, dummy, dummy);
1367 if (error)
1368 cmn_err(CE_WARN, "sffs_mkdir: set_mode(%s, %o) failed"
1369 " rc=%d", node->sf_path, va->va_mode, error);
1370 }
1371
1372 if (node && node->sf_parent)
1373 sfnode_clear_dir_list(node->sf_parent);
1374
1375 mutex_exit(&sffs_lock);
1376 if (node == NULL)
1377 return (lookuperr);
1378 *vpp = sfnode_get_vnode(node);
1379 return (0);
1380}
1381
1382/*ARGSUSED*/
1383static int
1384sffs_rmdir(
1385 struct vnode *dvp,
1386 char *nm,
1387 vnode_t *cdir,
1388 cred_t *cred,
1389 caller_context_t *ct,
1390 int flags)
1391{
1392 sfnode_t *node;
1393 vnode_t *vp;
1394 int error;
1395
1396 /*
1397 * Return error when removing . and ..
1398 */
1399 if (strcmp(nm, ".") == 0 || strcmp(nm, "") == 0)
1400 return (EINVAL);
1401 if (strcmp(nm, "..") == 0)
1402 return (EEXIST);
1403
1404 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1405 if (error)
1406 return (error);
1407 if (vp->v_type != VDIR) {
1408 VN_RELE(vp);
1409 return (ENOTDIR);
1410 }
1411
1412#ifdef VBOXVFS_WITH_MMAP
1413 if (vn_vfswlock(vp)) {
1414 VN_RELE(vp);
1415 return (EBUSY);
1416 }
1417#endif
1418
1419 if (vn_mountedvfs(vp)) {
1420 VN_RELE(vp);
1421 return (EBUSY);
1422 }
1423
1424 node = VN2SFN(vp);
1425
1426 mutex_enter(&sffs_lock);
1427 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1428 if (error)
1429 goto done;
1430
1431 /*
1432 * If anything else is using this vnode, then fail the remove.
1433 * Why? Windows hosts can't remove something that is open,
1434 * so we have to sfprov_close() it first.
1435 * There is no errno for this - since it's not a problem on UNIX,
1436 * but EINVAL is the closest.
1437 */
1438 if (node->sf_file != NULL) {
1439 if (vp->v_count > 1) {
1440 error = EINVAL;
1441 goto done;
1442 }
1443 (void)sfprov_close(node->sf_file);
1444 node->sf_file = NULL;
1445 }
1446
1447 /*
1448 * Remove the directory on the host and mark the node as stale.
1449 */
1450 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1451 error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
1452 if (error == ENOENT || error == 0)
1453 sfnode_make_stale(node);
1454
1455 if (node->sf_parent)
1456 sfnode_clear_dir_list(node->sf_parent);
1457done:
1458 mutex_exit(&sffs_lock);
1459 VN_RELE(vp);
1460 return (error);
1461}
1462
1463
1464#ifdef VBOXVFS_WITH_MMAP
1465static caddr_t
1466sffs_page_map(
1467 page_t *ppage,
1468 enum seg_rw segaccess)
1469{
1470 /* Use seg_kpm driver if possible (64-bit) */
1471 if (kpm_enable)
1472 return (hat_kpm_mapin(ppage, NULL));
1473 ASSERT(segaccess == S_READ || segaccess == S_WRITE);
1474 return (ppmapin(ppage, PROT_READ | ((segaccess == S_WRITE) ? PROT_WRITE : 0), (caddr_t)-1));
1475}
1476
1477
1478static void
1479sffs_page_unmap(
1480 page_t *ppage,
1481 caddr_t addr)
1482{
1483 if (kpm_enable)
1484 hat_kpm_mapout(ppage, NULL, addr);
1485 else
1486 ppmapout(addr);
1487}
1488
1489
1490/*
1491 * Called when there's no page in the cache. This will create new page(s) and read
1492 * the file data into it.
1493 */
1494static int
1495sffs_readpages(
1496 vnode_t *dvp,
1497 offset_t off,
1498 page_t *pagelist[],
1499 size_t pagelistsize,
1500 struct seg *segp,
1501 caddr_t addr,
1502 enum seg_rw segaccess)
1503{
1504 ASSERT(MUTEX_HELD(&sffs_lock));
1505
1506 int error = 0;
1507 u_offset_t io_off, total;
1508 size_t io_len;
1509 page_t *ppages;
1510 page_t *pcur;
1511
1512 sfnode_t *node = VN2SFN(dvp);
1513 ASSERT(node);
1514 ASSERT(node->sf_file);
1515
1516 if (pagelistsize == PAGESIZE)
1517 {
1518 io_off = off;
1519 io_len = PAGESIZE;
1520 ppages = page_create_va(dvp, io_off, io_len, PG_WAIT | PG_EXCL, segp, addr);
1521 }
1522 else
1523 ppages = pvn_read_kluster(dvp, off, segp, addr, &io_off, &io_len, off, pagelistsize, 0);
1524
1525 /* If page already exists return success */
1526 if (!ppages)
1527 {
1528 *pagelist = NULL;
1529 return (0);
1530 }
1531
1532 /*
1533 * Map & read page-by-page.
1534 */
1535 total = io_off + io_len;
1536 pcur = ppages;
1537 while (io_off < total)
1538 {
1539 ASSERT3U(io_off, ==, pcur->p_offset);
1540
1541 caddr_t virtaddr = sffs_page_map(pcur, segaccess);
1542 uint32_t bytes = PAGESIZE;
1543 error = sfprov_read(node->sf_file, virtaddr, io_off, &bytes);
1544 sffs_page_unmap(pcur, virtaddr);
1545 if (error != 0)
1546 {
1547 cmn_err(CE_WARN, "sffs_readpages: sfprov_read() failed. error=%d bytes=%ld\n", error, bytes);
1548 /* Get rid of all kluster pages read & bail. */
1549 pvn_read_done(ppages, B_ERROR);
1550 return (error);
1551 }
1552 pcur = pcur->p_next;
1553 io_off += PAGESIZE;
1554 }
1555
1556 /*
1557 * Fill in the pagelist from kluster at the requested offset.
1558 */
1559 pvn_plist_init(ppages, pagelist, pagelistsize, off, io_len, segaccess);
1560 ASSERT(pagelist == NULL || (*pagelist)->p_offset == off);
1561 return (0);
1562}
1563
1564
1565/*ARGSUSED*/
1566static int
1567sffs_getpage(
1568 vnode_t *dvp,
1569 offset_t off,
1570 size_t len,
1571 uint_t *protp,
1572 page_t *pagelist[],
1573 size_t pagelistsize,
1574 struct seg *segp,
1575 caddr_t addr,
1576 enum seg_rw segaccess,
1577 cred_t *credp
1578#if !defined(VBOX_VFS_SOLARIS_10U6)
1579 , caller_context_t *ct
1580#endif
1581 )
1582{
1583 int error = 0;
1584 page_t **pageliststart = pagelist;
1585 sfnode_t *node = VN2SFN(dvp);
1586 ASSERT(node);
1587 ASSERT(node->sf_file);
1588
1589 if (segaccess == S_WRITE)
1590 return (ENOSYS); /* Will this ever happen? */
1591
1592 /* Don't bother about faultahead for now. */
1593 if (pagelist == NULL)
1594 return (0);
1595
1596 if (len > pagelistsize)
1597 len = pagelistsize;
1598 else
1599 len = P2ROUNDUP(len, PAGESIZE);
1600 ASSERT(pagelistsize >= len);
1601
1602 if (protp)
1603 *protp = PROT_ALL;
1604
1605 mutex_enter(&sffs_lock);
1606
1607 /* Don't map pages past end of the file. */
1608 if (off + len > node->sf_stat.sf_size + PAGEOFFSET)
1609 {
1610 mutex_exit(&sffs_lock);
1611 return (EFAULT);
1612 }
1613
1614 while (len > 0)
1615 {
1616 /*
1617 * Look for pages in the requested offset range, or create them if we can't find any.
1618 */
1619 if ((*pagelist = page_lookup(dvp, off, SE_SHARED)) != NULL)
1620 *(pagelist + 1) = NULL;
1621 else if ((error = sffs_readpages(dvp, off, pagelist, pagelistsize, segp, addr, segaccess)) != 0)
1622 {
1623 while (pagelist > pageliststart)
1624 page_unlock(*--pagelist);
1625
1626 *pagelist = NULL;
1627 mutex_exit(&sffs_lock);
1628 return (error);
1629 }
1630
1631 while (*pagelist)
1632 {
1633 ASSERT3U((*pagelist)->p_offset, ==, off);
1634 off += PAGESIZE;
1635 addr += PAGESIZE;
1636 if (len > 0)
1637 {
1638 ASSERT3U(len, >=, PAGESIZE);
1639 len -= PAGESIZE;
1640 }
1641
1642 ASSERT3U(pagelistsize, >=, PAGESIZE);
1643 pagelistsize -= PAGESIZE;
1644 pagelist++;
1645 }
1646 }
1647
1648 /*
1649 * Fill the page list array with any pages left in the cache.
1650 */
1651 while ( pagelistsize > 0
1652 && (*pagelist++ = page_lookup_nowait(dvp, off, SE_SHARED)))
1653 {
1654 off += PAGESIZE;
1655 pagelistsize -= PAGESIZE;
1656 }
1657
1658 *pagelist = NULL;
1659 mutex_exit(&sffs_lock);
1660 return (error);
1661}
1662
1663
1664/*ARGSUSED*/
1665static int
1666sffs_putpage(
1667 vnode_t *dvp,
1668 offset_t off,
1669 size_t len,
1670 int flags,
1671 cred_t *credp
1672#if !defined(VBOX_VFS_SOLARIS_10U6)
1673 , caller_context_t *ct
1674#endif
1675 )
1676{
1677 /*
1678 * We don't support PROT_WRITE mmaps. For normal writes we do not map and IO via
1679 * vop_putpage() either, therefore, afaik this shouldn't ever be called.
1680 */
1681 return (ENOSYS);
1682}
1683
1684
1685/*ARGSUSED*/
1686static int
1687sffs_discardpage(
1688 vnode_t *dvp,
1689 page_t *ppage,
1690 u_offset_t *poff,
1691 size_t *plen,
1692 int flags,
1693 cred_t *pcred)
1694{
1695 /*
1696 * We don't support PROT_WRITE mmaps. Just pretend we're done writing back
1697 * to the disk while unmounting/closing the node and discarding the mapped pages.
1698 */
1699 pvn_write_done(ppage, B_INVAL | B_ERROR | B_FORCE);
1700 return (0);
1701}
1702
1703
1704/*ARGSUSED*/
1705static int
1706sffs_map(
1707 vnode_t *dvp,
1708 offset_t off,
1709 struct as *asp,
1710 caddr_t *addrp,
1711 size_t len,
1712 uchar_t prot,
1713 uchar_t maxprot,
1714 uint_t flags,
1715 cred_t *credp
1716#if !defined(VBOX_VFS_SOLARIS_10U6)
1717 , caller_context_t *ct
1718#endif
1719 )
1720{
1721 /*
1722 * Invocation: mmap()->smmap_common()->VOP_MAP()->sffs_map(). Once the
1723 * segment driver creates the new segment via segvn_create(), it'll
1724 * invoke down the line VOP_ADDMAP()->sffs_addmap()
1725 */
1726 int error = 0;
1727 sfnode_t *node = VN2SFN(dvp);
1728 ASSERT(node);
1729 if ((prot & PROT_WRITE))
1730 return (ENOTSUP);
1731
1732 if (off < 0 || len > MAXOFFSET_T - off)
1733 return (ENXIO);
1734
1735 if (dvp->v_type != VREG)
1736 return (ENODEV);
1737
1738 if (dvp->v_flag & VNOMAP)
1739 return (ENOSYS);
1740
1741 if (vn_has_mandatory_locks(dvp, node->sf_stat.sf_mode))
1742 return (EAGAIN);
1743
1744 mutex_enter(&sffs_lock);
1745 as_rangelock(asp);
1746
1747#if defined(VBOX_VFS_SOLARIS_10U6)
1748 if ((flags & MAP_FIXED) == 0)
1749 {
1750 map_addr(addrp, len, off, 1, flags);
1751 if (*addrp == NULL)
1752 error = ENOMEM;
1753 }
1754 else
1755 as_unmap(asp, *addrp, len); /* User specified address, remove any previous mappings */
1756#else
1757 error = choose_addr(asp, addrp, len, off, ADDR_VACALIGN, flags);
1758#endif
1759
1760 if (error)
1761 {
1762 as_rangeunlock(asp);
1763 mutex_exit(&sffs_lock);
1764 return (error);
1765 }
1766
1767 segvn_crargs_t vnodeargs;
1768 memset(&vnodeargs, 0, sizeof(vnodeargs));
1769 vnodeargs.vp = dvp;
1770 vnodeargs.cred = credp;
1771 vnodeargs.offset = off;
1772 vnodeargs.type = flags & MAP_TYPE;
1773 vnodeargs.prot = prot;
1774 vnodeargs.maxprot = maxprot;
1775 vnodeargs.flags = flags & ~MAP_TYPE;
1776 vnodeargs.amp = NULL; /* anon. mapping */
1777 vnodeargs.szc = 0; /* preferred page size code */
1778 vnodeargs.lgrp_mem_policy_flags = 0;
1779
1780 error = as_map(asp, *addrp, len, segvn_create, &vnodeargs);
1781
1782 as_rangeunlock(asp);
1783 mutex_exit(&sffs_lock);
1784 return (error);
1785}
1786
1787
1788/*ARGSUSED*/
1789static int
1790sffs_addmap(
1791 vnode_t *dvp,
1792 offset_t off,
1793 struct as *asp,
1794 caddr_t addr,
1795 size_t len,
1796 uchar_t prot,
1797 uchar_t maxprot,
1798 uint_t flags,
1799 cred_t *credp
1800#if !defined(VBOX_VFS_SOLARIS_10U6)
1801 , caller_context_t *ct
1802#endif
1803 )
1804{
1805 sfnode_t *node = VN2SFN(dvp);
1806 uint64_t npages = btopr(len);
1807
1808 if (dvp->v_flag & VNOMAP)
1809 return (ENOSYS);
1810
1811 ASSERT(node);
1812 return (0);
1813}
1814
1815
1816/*ARGSUSED*/
1817static int
1818sffs_delmap(
1819 vnode_t *dvp,
1820 offset_t off,
1821 struct as *asp,
1822 caddr_t addr,
1823 size_t len,
1824 uint_t prot,
1825 uint_t maxprot,
1826 uint_t flags,
1827 cred_t *credp
1828#if !defined(VBOX_VFS_SOLARIS_10U6)
1829 , caller_context_t *ct
1830#endif
1831 )
1832{
1833 sfnode_t *node = VN2SFN(dvp);
1834 uint64_t npages = btopr(len);
1835
1836 if (dvp->v_flag & VNOMAP)
1837 return (ENOSYS);
1838
1839 return (0);
1840}
1841#endif /* VBOXVFS_WITH_MMAP */
1842
1843
1844/*ARGSUSED*/
1845static int
1846sffs_remove(
1847 vnode_t *dvp,
1848 char *name,
1849 cred_t *cred,
1850 caller_context_t *ct,
1851 int flags)
1852{
1853 vnode_t *vp;
1854 sfnode_t *node;
1855 int error;
1856
1857 /*
1858 * These should never happen
1859 */
1860 ASSERT(name != NULL);
1861 ASSERT(strcmp(name, "..") != 0);
1862
1863 error = sffs_lookup(dvp, name, &vp,
1864 NULL, 0, NULL, cred, ct, NULL, NULL);
1865 if (error)
1866 return (error);
1867 node = VN2SFN(vp);
1868
1869 mutex_enter(&sffs_lock);
1870 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1871 if (error)
1872 goto done;
1873
1874 /*
1875 * If anything else is using this vnode, then fail the remove.
1876 * Why? Windows hosts can't sfprov_remove() a file that is open,
1877 * so we have to sfprov_close() it first.
1878 * There is no errno for this - since it's not a problem on UNIX,
1879 * but ETXTBSY is the closest.
1880 */
1881 if (node->sf_file != NULL) {
1882 if (vp->v_count > 1) {
1883 error = ETXTBSY;
1884 goto done;
1885 }
1886 (void)sfprov_close(node->sf_file);
1887 node->sf_file = NULL;
1888 }
1889
1890 /*
1891 * Remove the file on the host and mark the node as stale.
1892 */
1893 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1894
1895 error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
1896 if (error == ENOENT || error == 0)
1897 sfnode_make_stale(node);
1898
1899 if (node->sf_parent)
1900 sfnode_clear_dir_list(node->sf_parent);
1901done:
1902 mutex_exit(&sffs_lock);
1903 VN_RELE(vp);
1904 return (error);
1905}
1906
1907/*ARGSUSED*/
1908static int
1909sffs_rename(
1910 vnode_t *old_dir,
1911 char *old_nm,
1912 vnode_t *new_dir,
1913 char *new_nm,
1914 cred_t *cred,
1915 caller_context_t *ct,
1916 int flags)
1917{
1918 char *newpath;
1919 int error;
1920 sfnode_t *node;
1921
1922 if (strcmp(new_nm, "") == 0 ||
1923 strcmp(new_nm, ".") == 0 ||
1924 strcmp(new_nm, "..") == 0 ||
1925 strcmp(old_nm, "") == 0 ||
1926 strcmp(old_nm, ".") == 0 ||
1927 strcmp(old_nm, "..") == 0)
1928 return (EINVAL);
1929
1930 /*
1931 * make sure we have permission to do the rename
1932 */
1933 mutex_enter(&sffs_lock);
1934 error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
1935 if (error == 0 && new_dir != old_dir)
1936 error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
1937 if (error)
1938 goto done;
1939
1940 node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON, NULL, 0, NULL);
1941 if (node == NULL) {
1942 error = ENOENT;
1943 goto done;
1944 }
1945
1946 /*
1947 * Rename the file on the host and in our caches.
1948 */
1949 sfnode_invalidate_stat_cache(node);
1950 sfnode_invalidate_stat_cache(VN2SFN(old_dir));
1951 sfnode_invalidate_stat_cache(VN2SFN(new_dir));
1952
1953 newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
1954 error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
1955 node->sf_type == VDIR);
1956 if (error == 0)
1957 sfnode_rename(node, VN2SFN(new_dir), newpath);
1958 else {
1959 kmem_free(newpath, strlen(newpath) + 1);
1960 if (error == ENOENT)
1961 sfnode_make_stale(node);
1962 }
1963done:
1964 mutex_exit(&sffs_lock);
1965 return (error);
1966}
1967
1968
1969/*ARGSUSED*/
1970static int
1971sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
1972{
1973 sfnode_t *node;
1974 int error;
1975
1976 /*
1977 * Ask the host to sync any data it may have cached for open files.
1978 */
1979 mutex_enter(&sffs_lock);
1980 node = VN2SFN(vp);
1981 if (node->sf_file == NULL)
1982 error = EBADF;
1983 else if (node->sf_sffs->sf_fsync)
1984 error = sfprov_fsync(node->sf_file);
1985 else
1986 error = 0;
1987 mutex_exit(&sffs_lock);
1988 return (error);
1989}
1990
1991/*
1992 * This may be the last reference, possibly time to close the file and
1993 * destroy the vnode. If the sfnode is stale, we'll destroy that too.
1994 */
1995/*ARGSUSED*/
1996static void
1997#if defined(VBOX_VFS_SOLARIS_10U6)
1998sffs_inactive(vnode_t *vp, cred_t *cr)
1999#else
2000sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
2001#endif
2002{
2003 sfnode_t *node;
2004
2005 /*
2006 * nothing to do if this isn't the last use
2007 */
2008 mutex_enter(&sffs_lock);
2009 node = VN2SFN(vp);
2010 mutex_enter(&vp->v_lock);
2011 if (vp->v_count > 1) {
2012 --vp->v_count;
2013 mutex_exit(&vp->v_lock);
2014 mutex_exit(&sffs_lock);
2015 return;
2016 }
2017
2018 if (vn_has_cached_data(vp)) {
2019#ifdef VBOXVFS_WITH_MMAP
2020 /* We're fine with releasing the vnode lock here as we should be covered by the sffs_lock */
2021 mutex_exit(&vp->v_lock);
2022 pvn_vplist_dirty(vp, 0 /* offset */, sffs_discardpage, B_INVAL, cr);
2023 mutex_enter(&vp->v_lock);
2024#else
2025 panic("sffs_inactive() found cached data");
2026#endif
2027 }
2028
2029 /*
2030 * destroy the vnode
2031 */
2032 node->sf_vnode = NULL;
2033 mutex_exit(&vp->v_lock);
2034 vn_invalid(vp);
2035 vn_free(vp);
2036 LogFlowFunc((" %s vnode cleared\n", node->sf_path));
2037
2038 /*
2039 * Close the sf_file for the node.
2040 */
2041 if (node->sf_file != NULL) {
2042 (void)sfprov_close(node->sf_file);
2043 node->sf_file = NULL;
2044 }
2045
2046 /*
2047 * Free the directory entries for the node. This should normally
2048 * have been taken care of in sffs_close(), but better safe than
2049 * sorry.
2050 */
2051 sfnode_clear_dir_list(node);
2052
2053 /*
2054 * If the node is stale, we can also destroy it.
2055 */
2056 if (node->sf_is_stale && node->sf_children == 0)
2057 sfnode_destroy(node);
2058
2059 mutex_exit(&sffs_lock);
2060 return;
2061}
2062
2063/*
2064 * All the work for this is really done in lookup.
2065 */
2066/*ARGSUSED*/
2067static int
2068sffs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
2069{
2070 return (0);
2071}
2072
2073/*
2074 * All the work for this is really done in inactive.
2075 */
2076/*ARGSUSED*/
2077static int
2078sffs_close(
2079 vnode_t *vp,
2080 int flag,
2081 int count,
2082 offset_t offset,
2083 cred_t *cr,
2084 caller_context_t *ct)
2085{
2086 sfnode_t *node;
2087
2088 mutex_enter(&sffs_lock);
2089 node = VN2SFN(vp);
2090
2091 /*
2092 * Free the directory entries for the node. We do this on this call
2093 * here because the directory node may not become inactive for a long
2094 * time after the readdir is over. Case in point, if somebody cd's into
2095 * the directory then it won't become inactive until they cd away again.
2096 * In such a case we would end up with the directory listing not getting
2097 * updated (i.e. the result of 'ls' always being the same) until they
2098 * change the working directory.
2099 */
2100 sfnode_clear_dir_list(node);
2101
2102 sfnode_invalidate_stat_cache(node);
2103
2104 mutex_exit(&sffs_lock);
2105 return (0);
2106}
2107
2108/* ARGSUSED */
2109static int
2110sffs_seek(vnode_t *v, offset_t o, offset_t *no, caller_context_t *ct)
2111{
2112 if (*no < 0 || *no > MAXOFFSET_T)
2113 return (EINVAL);
2114 return (0);
2115}
2116
2117
2118
2119/*
2120 * By returning an error for this, we prevent anything in sffs from
2121 * being re-exported by NFS
2122 */
2123/* ARGSUSED */
2124static int
2125sffs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
2126{
2127 return (ENOTSUP);
2128}
2129
2130/*
2131 * vnode operations for regular files
2132 */
2133const fs_operation_def_t sffs_ops_template[] = {
2134#if defined(VBOX_VFS_SOLARIS_10U6)
2135 VOPNAME_ACCESS, sffs_access,
2136 VOPNAME_CLOSE, sffs_close,
2137 VOPNAME_CREATE, sffs_create,
2138 VOPNAME_FID, sffs_fid,
2139 VOPNAME_FSYNC, sffs_fsync,
2140 VOPNAME_GETATTR, sffs_getattr,
2141 VOPNAME_INACTIVE, sffs_inactive,
2142 VOPNAME_LOOKUP, sffs_lookup,
2143 VOPNAME_MKDIR, sffs_mkdir,
2144 VOPNAME_OPEN, sffs_open,
2145 VOPNAME_PATHCONF, sffs_pathconf,
2146 VOPNAME_READ, sffs_read,
2147 VOPNAME_READDIR, sffs_readdir,
2148 VOPNAME_REMOVE, sffs_remove,
2149 VOPNAME_RENAME, sffs_rename,
2150 VOPNAME_RMDIR, sffs_rmdir,
2151 VOPNAME_SEEK, sffs_seek,
2152 VOPNAME_SETATTR, sffs_setattr,
2153 VOPNAME_SPACE, sffs_space,
2154 VOPNAME_WRITE, sffs_write,
2155
2156# ifdef VBOXVFS_WITH_MMAP
2157 VOPNAME_MAP, sffs_map,
2158 VOPNAME_ADDMAP, sffs_addmap,
2159 VOPNAME_DELMAP, sffs_delmap,
2160 VOPNAME_GETPAGE, sffs_getpage,
2161 VOPNAME_PUTPAGE, sffs_putpage,
2162# endif
2163
2164 NULL, NULL
2165#else
2166 VOPNAME_ACCESS, { .vop_access = sffs_access },
2167 VOPNAME_CLOSE, { .vop_close = sffs_close },
2168 VOPNAME_CREATE, { .vop_create = sffs_create },
2169 VOPNAME_FID, { .vop_fid = sffs_fid },
2170 VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
2171 VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
2172 VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
2173 VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
2174 VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
2175 VOPNAME_OPEN, { .vop_open = sffs_open },
2176 VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
2177 VOPNAME_READ, { .vop_read = sffs_read },
2178 VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
2179 VOPNAME_REMOVE, { .vop_remove = sffs_remove },
2180 VOPNAME_RENAME, { .vop_rename = sffs_rename },
2181 VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
2182 VOPNAME_SEEK, { .vop_seek = sffs_seek },
2183 VOPNAME_SETATTR, { .vop_setattr = sffs_setattr },
2184 VOPNAME_SPACE, { .vop_space = sffs_space },
2185 VOPNAME_WRITE, { .vop_write = sffs_write },
2186
2187# ifdef VBOXVFS_WITH_MMAP
2188 VOPNAME_MAP, { .vop_map = sffs_map },
2189 VOPNAME_ADDMAP, { .vop_addmap = sffs_addmap },
2190 VOPNAME_DELMAP, { .vop_delmap = sffs_delmap },
2191 VOPNAME_GETPAGE, { .vop_getpage = sffs_getpage },
2192 VOPNAME_PUTPAGE, { .vop_putpage = sffs_putpage },
2193# endif
2194
2195 NULL, NULL
2196#endif
2197};
2198
2199/*
2200 * Also, init and fini functions...
2201 */
2202int
2203sffs_vnode_init(void)
2204{
2205 int err;
2206
2207 err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
2208 if (err)
2209 return (err);
2210
2211 avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
2212 offsetof(sfnode_t, sf_linkage));
2213 avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
2214 offsetof(sfnode_t, sf_linkage));
2215
2216 sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
2217
2218 return (0);
2219}
2220
2221void
2222sffs_vnode_fini(void)
2223{
2224 if (sffs_ops)
2225 vn_freevnodeops(sffs_ops);
2226 ASSERT(avl_first(&sfnodes) == NULL);
2227 avl_destroy(&sfnodes);
2228 if (sffs_buffer != NULL) {
2229 kmem_free(sffs_buffer, PAGESIZE);
2230 sffs_buffer = NULL;
2231 }
2232}
2233
2234/*
2235 * Utility at unmount to get all nodes in that mounted filesystem removed.
2236 */
2237int
2238sffs_purge(struct sffs_data *sffs)
2239{
2240 sfnode_t *node;
2241 sfnode_t *prev;
2242
2243 /*
2244 * Check that no vnodes are active.
2245 */
2246 if (sffs->sf_rootnode->v_count > 1)
2247 return (-1);
2248 for (node = avl_first(&sfnodes); node;
2249 node = AVL_NEXT(&sfnodes, node)) {
2250 if (node->sf_sffs == sffs && node->sf_vnode &&
2251 node->sf_vnode != sffs->sf_rootnode)
2252 return (-1);
2253 }
2254 for (node = avl_first(&stale_sfnodes); node;
2255 node = AVL_NEXT(&stale_sfnodes, node)) {
2256 if (node->sf_sffs == sffs && node->sf_vnode &&
2257 node->sf_vnode != sffs->sf_rootnode)
2258 return (-1);
2259 }
2260
2261 /*
2262 * All clear to destroy all node information. Since there are no
2263 * vnodes, the make stale will cause deletion.
2264 */
2265 VN_RELE(sffs->sf_rootnode);
2266 mutex_enter(&sffs_lock);
2267 for (prev = NULL;;) {
2268 if (prev == NULL)
2269 node = avl_first(&sfnodes);
2270 else
2271 node = AVL_NEXT(&sfnodes, prev);
2272
2273 if (node == NULL)
2274 break;
2275
2276 if (node->sf_sffs == sffs) {
2277 if (node->sf_vnode != NULL)
2278 panic("vboxfs: purge hit active vnode");
2279 sfnode_make_stale(node);
2280 } else {
2281 prev = node;
2282 }
2283 }
2284 mutex_exit(&sffs_lock);
2285 return (0);
2286}
2287
2288#if 0
2289/* Debug helper functions */
2290static void
2291sfnode_print(sfnode_t *node)
2292{
2293 Log(("0x%p", node));
2294 Log((" type=%s (%d)",
2295 node->sf_type == VDIR ? "VDIR" :
2296 node->sf_type == VNON ? "VNON" :
2297 node->sf_type == VREG ? "VREG" : "other", node->sf_type));
2298 Log((" ino=%d", (uint_t)node->sf_ino));
2299 Log((" path=%s", node->sf_path));
2300 Log((" parent=0x%p", node->sf_parent));
2301 if (node->sf_children)
2302 Log((" children=%d", node->sf_children));
2303 if (node->sf_vnode)
2304 Log((" vnode=0x%p", node->sf_vnode));
2305 Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
2306}
2307
2308static void
2309sfnode_list(void)
2310{
2311 sfnode_t *n;
2312 for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
2313 sfnode_print(n);
2314 for (n = avl_first(&stale_sfnodes); n != NULL;
2315 n = AVL_NEXT(&stale_sfnodes, n))
2316 sfnode_print(n);
2317}
2318#endif
2319
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