VirtualBox

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

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

Additions/solaris/SharedFolders: implemented dmode, fmode, dmask and fmask parameters. (untested)

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