VirtualBox

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

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

Additions/solaris/SharedFolders: fixed ref. counting resulting in panic during unmount.

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