VirtualBox

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

Last change on this file since 104448 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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