VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 55.1 KB
Line 
1/* $Id: vboxfs_vnode.c 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox File System for Solaris Guests, vnode implementation.
4 * Portions contributed by: Ronald.
5 */
6
7/*
8 * Copyright (C) 2009-2024 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#if !defined(VBOX_VFS_SOLARIS_10U6)
724 , caller_context_t *ct,
725 int flag
726#endif
727 )
728{
729 sfnode_t *dir = VN2SFN(vp);
730 sfnode_t *node;
731 struct sffs_dirent *dirent = NULL;
732 sffs_dirents_t *cur_buf;
733 offset_t offset = 0;
734 offset_t orig_off = uiop->uio_loffset;
735 int dummy_eof;
736 int error = 0;
737
738 if (uiop->uio_iovcnt != 1)
739 return (EINVAL);
740
741 if (vp->v_type != VDIR)
742 return (ENOTDIR);
743
744 if (eofp == NULL)
745 eofp = &dummy_eof;
746 *eofp = 0;
747
748 if (uiop->uio_loffset >= MAXOFFSET_T) {
749 *eofp = 1;
750 return (0);
751 }
752
753 /*
754 * Get the directory entry names from the host. This gets all
755 * entries. These are stored in a linked list of sffs_dirents_t
756 * buffers, each of which contains a list of dirent64_t's.
757 */
758 mutex_enter(&sffs_lock);
759
760 if (dir->sf_dir_list == NULL) {
761#if defined(VBOX_VFS_SOLARIS_10U6)
762 int flag = 0;
763#endif
764 error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
765 &dir->sf_dir_list, flag);
766 if (error != 0)
767 goto done;
768 }
769
770 /*
771 * Validate and skip to the desired offset.
772 */
773 cur_buf = dir->sf_dir_list;
774 offset = 0;
775
776 while (cur_buf != NULL &&
777 offset + cur_buf->sf_len <= uiop->uio_loffset) {
778 offset += cur_buf->sf_len;
779 cur_buf = cur_buf->sf_next;
780 }
781
782 if (cur_buf == NULL && offset != uiop->uio_loffset) {
783 error = EINVAL;
784 goto done;
785 }
786 if (cur_buf != NULL && offset != uiop->uio_loffset) {
787 offset_t off = offset;
788 int step;
789 dirent = &cur_buf->sf_entries[0];
790
791 while (off < uiop->uio_loffset) {
792 if (dirent->sf_entry.d_off == uiop->uio_loffset)
793 break;
794 step = sizeof(sffs_stat_t) + dirent->sf_entry.d_reclen;
795 dirent = (struct sffs_dirent *) (((char *) dirent) + step);
796 off += step;
797 }
798
799 if (off >= uiop->uio_loffset) {
800 error = EINVAL;
801 goto done;
802 }
803 }
804
805 offset = uiop->uio_loffset - offset;
806
807 /*
808 * Lookup each of the names, so that we have ino's, and copy to
809 * result buffer.
810 */
811 while (cur_buf != NULL) {
812 if (offset >= cur_buf->sf_len) {
813 cur_buf = cur_buf->sf_next;
814 offset = 0;
815 continue;
816 }
817
818 dirent = (struct sffs_dirent *)
819 (((char *) &cur_buf->sf_entries[0]) + offset);
820 if (dirent->sf_entry.d_reclen > uiop->uio_resid)
821 break;
822
823 if (strcmp(dirent->sf_entry.d_name, ".") == 0) {
824 node = dir;
825 } else if (strcmp(dirent->sf_entry.d_name, "..") == 0) {
826 node = dir->sf_parent;
827 if (node == NULL)
828 node = dir;
829 } else {
830 node = sfnode_lookup(dir, dirent->sf_entry.d_name, VNON,
831 0, &dirent->sf_stat, sfnode_cur_time_usec(), NULL);
832 if (node == NULL)
833 panic("sffs_readdir() lookup failed");
834 }
835 dirent->sf_entry.d_ino = node->sf_ino;
836
837 error = uiomove(&dirent->sf_entry, dirent->sf_entry.d_reclen, UIO_READ, uiop);
838 if (error != 0)
839 break;
840
841 uiop->uio_loffset= dirent->sf_entry.d_off;
842 offset += sizeof(sffs_stat_t) + dirent->sf_entry.d_reclen;
843 }
844 if (error == 0 && cur_buf == NULL)
845 *eofp = 1;
846done:
847 mutex_exit(&sffs_lock);
848 if (error != 0)
849 uiop->uio_loffset = orig_off;
850 return (error);
851}
852
853
854/*
855 * HERE JOE.. this may need more logic, need to look at other file systems
856 */
857static int
858sffs_pathconf(
859 vnode_t *vp,
860 int cmd,
861 ulong_t *valp,
862 cred_t *cr
863#if !defined(VBOX_VFS_SOLARIS_10U6)
864 , caller_context_t *ct
865#endif
866 )
867{
868#if !defined(VBOX_VFS_SOLARIS_10U6)
869 return (fs_pathconf(vp, cmd, valp, cr, ct));
870#else
871 return (fs_pathconf(vp, cmd, valp, cr));
872#endif
873}
874
875static int
876sffs_getattr(
877 vnode_t *vp,
878 vattr_t *vap,
879 int flags,
880 cred_t *cred
881#if !defined(VBOX_VFS_SOLARIS_10U6)
882 , caller_context_t *ct
883#endif
884 )
885{
886 sfnode_t *node = VN2SFN(vp);
887 sffs_data_t *sffs = node->sf_sffs;
888 mode_t mode;
889 int error = 0;
890
891 mutex_enter(&sffs_lock);
892 vap->va_type = vp->v_type;
893 vap->va_uid = sffs->sf_handle->sf_uid;
894 vap->va_gid = sffs->sf_handle->sf_gid;
895 vap->va_fsid = sffs->sf_vfsp->vfs_dev;
896 vap->va_nodeid = node->sf_ino;
897 vap->va_nlink = 1;
898 vap->va_rdev = sffs->sf_vfsp->vfs_dev;
899 vap->va_seq = 0;
900
901 if (!sfnode_stat_cached(node)) {
902 error = sfnode_update_stat_cache(node);
903 if (error != 0)
904 goto done;
905 }
906
907 vap->va_atime = node->sf_stat.sf_atime;
908 vap->va_mtime = node->sf_stat.sf_mtime;
909 vap->va_ctime = node->sf_stat.sf_ctime;
910
911 mode = node->sf_stat.sf_mode;
912 vap->va_mode = mode & MODEMASK;
913
914 vap->va_size = node->sf_stat.sf_size;
915 vap->va_blksize = 512;
916 vap->va_nblocks = (node->sf_stat.sf_alloc + 511) / 512;
917
918done:
919 mutex_exit(&sffs_lock);
920 return (error);
921}
922
923static int
924sffs_setattr(
925 vnode_t *vp,
926 vattr_t *vap,
927 int flags,
928 cred_t *cred,
929 caller_context_t *ct)
930{
931 sfnode_t *node = VN2SFN(vp);
932 int error;
933 mode_t mode;
934
935 mode = vap->va_mode;
936 if (vp->v_type == VREG)
937 mode |= S_IFREG;
938 else if (vp->v_type == VDIR)
939 mode |= S_IFDIR;
940 else if (vp->v_type == VBLK)
941 mode |= S_IFBLK;
942 else if (vp->v_type == VCHR)
943 mode |= S_IFCHR;
944 else if (vp->v_type == VLNK)
945 mode |= S_IFLNK;
946 else if (vp->v_type == VFIFO)
947 mode |= S_IFIFO;
948 else if (vp->v_type == VSOCK)
949 mode |= S_IFSOCK;
950
951 mutex_enter(&sffs_lock);
952
953 sfnode_invalidate_stat_cache(node);
954 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
955 vap->va_mask, mode, vap->va_atime, vap->va_mtime, vap->va_ctime);
956 if (error == ENOENT)
957 sfnode_make_stale(node);
958
959 mutex_exit(&sffs_lock);
960 return (error);
961}
962
963static int
964sffs_space(
965 vnode_t *vp,
966 int cmd,
967 struct flock64 *bfp,
968 int flags,
969 offset_t off,
970 cred_t *cred,
971 caller_context_t *ct)
972{
973 sfnode_t *node = VN2SFN(vp);
974 int error;
975
976 /* we only support changing the length of the file */
977 if (bfp->l_whence != SEEK_SET || bfp->l_len != 0)
978 return ENOSYS;
979
980 mutex_enter(&sffs_lock);
981
982 sfnode_invalidate_stat_cache(node);
983
984 error = sfprov_set_size(node->sf_sffs->sf_handle, node->sf_path,
985 bfp->l_start);
986 if (error == ENOENT)
987 sfnode_make_stale(node);
988
989 mutex_exit(&sffs_lock);
990 return (error);
991}
992
993/*ARGSUSED*/
994static int
995sffs_read(
996 vnode_t *vp,
997 struct uio *uio,
998 int ioflag,
999 cred_t *cred,
1000 caller_context_t *ct)
1001{
1002 sfnode_t *node = VN2SFN(vp);
1003 int error = 0;
1004 uint32_t bytes;
1005 uint32_t done;
1006 ulong_t offset;
1007 ssize_t total;
1008
1009 if (vp->v_type == VDIR)
1010 return (EISDIR);
1011 if (vp->v_type != VREG)
1012 return (EINVAL);
1013 if (uio->uio_loffset >= MAXOFFSET_T)
1014 return (0);
1015 if (uio->uio_loffset < 0)
1016 return (EINVAL);
1017 total = uio->uio_resid;
1018 if (total == 0)
1019 return (0);
1020
1021 mutex_enter(&sffs_lock);
1022 if (node->sf_file == NULL) {
1023 ASSERT(node->sf_flag != ~0);
1024 sfnode_open(node, node->sf_flag);
1025 if (node->sf_file == NULL)
1026 return (EBADF);
1027 }
1028
1029 do {
1030 offset = uio->uio_offset;
1031 done = bytes = MIN(PAGESIZE, uio->uio_resid);
1032 error = sfprov_read(node->sf_file, sffs_buffer, offset, &done);
1033 if (error == 0 && done > 0)
1034 error = uiomove(sffs_buffer, done, UIO_READ, uio);
1035 } while (error == 0 && uio->uio_resid > 0 && done > 0);
1036
1037 mutex_exit(&sffs_lock);
1038
1039 /*
1040 * a partial read is never an error
1041 */
1042 if (total != uio->uio_resid)
1043 error = 0;
1044 return (error);
1045}
1046
1047/*ARGSUSED*/
1048static int
1049sffs_write(
1050 vnode_t *vp,
1051 struct uio *uiop,
1052 int ioflag,
1053 cred_t *cred,
1054 caller_context_t *ct)
1055{
1056 sfnode_t *node = VN2SFN(vp);
1057 int error = 0;
1058 uint32_t bytes;
1059 uint32_t done;
1060 ulong_t offset;
1061 ssize_t total;
1062 rlim64_t limit = uiop->uio_llimit;
1063
1064 if (vp->v_type == VDIR)
1065 return (EISDIR);
1066 if (vp->v_type != VREG)
1067 return (EINVAL);
1068
1069 /*
1070 * We have to hold this lock for a long time to keep
1071 * multiple FAPPEND writes from intermixing
1072 */
1073 mutex_enter(&sffs_lock);
1074 if (node->sf_file == NULL) {
1075 ASSERT(node->sf_flag != ~0);
1076 sfnode_open(node, node->sf_flag);
1077 if (node->sf_file == NULL)
1078 return (EBADF);
1079 }
1080
1081 sfnode_invalidate_stat_cache(node);
1082
1083 if (ioflag & FAPPEND) {
1084 uint64_t endoffile;
1085
1086 error = sfprov_get_size(node->sf_sffs->sf_handle,
1087 node->sf_path, &endoffile);
1088 if (error == ENOENT)
1089 sfnode_make_stale(node);
1090 if (error != 0) {
1091 mutex_exit(&sffs_lock);
1092 return (error);
1093 }
1094 uiop->uio_loffset = endoffile;
1095 }
1096
1097 if (vp->v_type != VREG || uiop->uio_loffset < 0) {
1098 mutex_exit(&sffs_lock);
1099 return (EINVAL);
1100 }
1101 if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
1102 limit = MAXOFFSET_T;
1103
1104 if (uiop->uio_loffset >= limit) {
1105 mutex_exit(&sffs_lock);
1106 return (EFBIG);
1107 }
1108
1109 if (uiop->uio_loffset >= MAXOFFSET_T) {
1110 mutex_exit(&sffs_lock);
1111 return (EFBIG);
1112 }
1113
1114 total = uiop->uio_resid;
1115 if (total == 0) {
1116 mutex_exit(&sffs_lock);
1117 return (0);
1118 }
1119
1120 do {
1121 offset = uiop->uio_offset;
1122 bytes = MIN(PAGESIZE, uiop->uio_resid);
1123 if (offset + bytes >= limit) {
1124 if (offset >= limit) {
1125 error = EFBIG;
1126 break;
1127 }
1128 bytes = limit - offset;
1129 }
1130 error = uiomove(sffs_buffer, bytes, UIO_WRITE, uiop);
1131 if (error != 0)
1132 break;
1133 done = bytes;
1134 if (error == 0)
1135 error = sfprov_write(node->sf_file, sffs_buffer,
1136 offset, &done);
1137 total -= done;
1138 if (done != bytes) {
1139 uiop->uio_resid += bytes - done;
1140 break;
1141 }
1142 } while (error == 0 && uiop->uio_resid > 0 && done > 0);
1143
1144 mutex_exit(&sffs_lock);
1145
1146 /*
1147 * A short write is never really an error.
1148 */
1149 if (total != uiop->uio_resid)
1150 error = 0;
1151 return (error);
1152}
1153
1154/*ARGSUSED*/
1155static int
1156sffs_access(
1157 vnode_t *vp,
1158 int mode,
1159 int flags,
1160 cred_t *cr
1161#if !defined(VBOX_VFS_SOLARIS_10U6)
1162 , caller_context_t *ct
1163#endif
1164 )
1165{
1166 sfnode_t *node = VN2SFN(vp);
1167 int error;
1168
1169 mutex_enter(&sffs_lock);
1170 error = sfnode_access(node, mode, cr);
1171 mutex_exit(&sffs_lock);
1172 return (error);
1173}
1174
1175/*
1176 * Lookup an entry in a directory and create a new vnode if found.
1177 */
1178/* ARGSUSED3 */
1179static int
1180sffs_lookup(
1181 vnode_t *dvp, /* the directory vnode */
1182 char *name, /* the name of the file or directory */
1183 vnode_t **vpp, /* the vnode we found or NULL */
1184 struct pathname *pnp,
1185 int flags,
1186 vnode_t *rdir,
1187 cred_t *cred
1188#if !defined(VBOX_VFS_SOLARIS_10U6)
1189 , caller_context_t *ct,
1190 int *direntflags,
1191 struct pathname *realpnp
1192#endif
1193 )
1194{
1195 int error;
1196 sfnode_t *node;
1197
1198 /*
1199 * dvp must be a directory
1200 */
1201 if (dvp->v_type != VDIR)
1202 return (ENOTDIR);
1203
1204 /*
1205 * An empty component name or just "." means the directory itself.
1206 * Don't do any further lookup or checking.
1207 */
1208 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0) {
1209 VN_HOLD(dvp);
1210 *vpp = dvp;
1211 return (0);
1212 }
1213
1214 /*
1215 * Check permission to look at this directory. We always allow "..".
1216 */
1217 mutex_enter(&sffs_lock);
1218 if (strcmp(name, "..") != 0) {
1219 error = sfnode_access(VN2SFN(dvp), VEXEC, cred);
1220 if (error) {
1221 mutex_exit(&sffs_lock);
1222 return (error);
1223 }
1224 }
1225
1226 /*
1227 * Lookup the node.
1228 */
1229 node = sfnode_lookup(VN2SFN(dvp), name, VNON, 0, NULL, 0, NULL);
1230 if (node != NULL)
1231 *vpp = sfnode_get_vnode(node);
1232 mutex_exit(&sffs_lock);
1233 return ((node == NULL) ? ENOENT : 0);
1234}
1235
1236/*ARGSUSED*/
1237static int
1238sffs_create(
1239 vnode_t *dvp,
1240 char *name,
1241 struct vattr *vap,
1242 vcexcl_t exclusive,
1243 int mode,
1244 vnode_t **vpp,
1245 cred_t *cr,
1246 int flag
1247#if !defined(VBOX_VFS_SOLARIS_10U6)
1248 , caller_context_t *ct,
1249 vsecattr_t *vsecp
1250#endif
1251 )
1252{
1253 vnode_t *vp;
1254 sfnode_t *node;
1255 int error;
1256
1257 ASSERT(name != NULL);
1258
1259 /*
1260 * this is used for regular files, not mkdir
1261 */
1262 if (vap->va_type == VDIR)
1263 return (EISDIR);
1264 if (vap->va_type != VREG)
1265 return (EINVAL);
1266
1267 /*
1268 * is this a pre-existing file?
1269 */
1270#if defined(VBOX_VFS_SOLARIS_10U6)
1271 error = sffs_lookup(dvp, name, &vp,
1272 NULL, 0, NULL, cr);
1273#else
1274 error = sffs_lookup(dvp, name, &vp,
1275 NULL, 0, NULL, cr, ct, NULL, NULL);
1276#endif
1277 if (error == ENOENT)
1278 vp = NULL;
1279 else if (error != 0)
1280 return (error);
1281
1282 /*
1283 * Operation on a pre-existing file.
1284 */
1285 if (vp != NULL) {
1286 if (exclusive == EXCL) {
1287 VN_RELE(vp);
1288 return (EEXIST);
1289 }
1290 if (vp->v_type == VDIR && (mode & VWRITE) == VWRITE) {
1291 VN_RELE(vp);
1292 return (EISDIR);
1293 }
1294
1295 mutex_enter(&sffs_lock);
1296 node = VN2SFN(vp);
1297 error = sfnode_access(node, mode, cr);
1298 if (error != 0) {
1299 mutex_exit(&sffs_lock);
1300 VN_RELE(vp);
1301 return (error);
1302 }
1303
1304 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1305
1306 /*
1307 * handle truncating an existing file
1308 */
1309 if (vp->v_type == VREG && (vap->va_mask & AT_SIZE) &&
1310 vap->va_size == 0) {
1311 sfnode_open(node, flag | FTRUNC);
1312 if (node->sf_path == NULL) {
1313 mutex_exit(&sffs_lock);
1314 VN_RELE(vp);
1315 return (ENOENT);
1316 }
1317 }
1318 mutex_exit(&sffs_lock);
1319 *vpp = vp;
1320 return (0);
1321 }
1322
1323 /*
1324 * Create a new node. First check for a race creating it.
1325 */
1326 mutex_enter(&sffs_lock);
1327 node = sfnode_lookup(VN2SFN(dvp), name, VNON, 0, NULL, 0, NULL);
1328 if (node != NULL) {
1329 mutex_exit(&sffs_lock);
1330 return (EEXIST);
1331 }
1332
1333 /*
1334 * Doesn't exist yet and we have the lock, so create it.
1335 */
1336 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1337 int lookuperr;
1338 node = sfnode_lookup(VN2SFN(dvp), name, VREG,
1339 (vap->va_mask & AT_MODE) ? vap->va_mode : 0, NULL, 0, &lookuperr);
1340
1341 if (node && node->sf_parent)
1342 sfnode_clear_dir_list(node->sf_parent);
1343
1344 mutex_exit(&sffs_lock);
1345 if (node == NULL)
1346 return (lookuperr);
1347 *vpp = sfnode_get_vnode(node);
1348 return (0);
1349}
1350
1351/*ARGSUSED*/
1352static int
1353sffs_mkdir(
1354 vnode_t *dvp,
1355 char *nm,
1356 vattr_t *va,
1357 vnode_t **vpp,
1358 cred_t *cred
1359#if !defined(VBOX_VFS_SOLARIS_10U6)
1360 , caller_context_t *ct,
1361 int flags,
1362 vsecattr_t *vsecp
1363#endif
1364 )
1365{
1366 sfnode_t *node;
1367 vnode_t *vp;
1368 int error;
1369
1370 /*
1371 * These should never happen
1372 */
1373 ASSERT(nm != NULL);
1374 ASSERT(strcmp(nm, "") != 0);
1375 ASSERT(strcmp(nm, ".") != 0);
1376 ASSERT(strcmp(nm, "..") != 0);
1377
1378 /*
1379 * Do an unlocked look up first
1380 */
1381#if defined(VBOX_VFS_SOLARIS_10U6)
1382 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred);
1383#else
1384 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1385#endif
1386 if (error == 0) {
1387 VN_RELE(vp);
1388 return (EEXIST);
1389 }
1390 if (error != ENOENT)
1391 return (error);
1392
1393 /*
1394 * Must be able to write in current directory
1395 */
1396 mutex_enter(&sffs_lock);
1397 error = sfnode_access(VN2SFN(dvp), VWRITE, cred);
1398 if (error) {
1399 mutex_exit(&sffs_lock);
1400 return (error);
1401 }
1402
1403 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1404 int lookuperr = EACCES;
1405 node = sfnode_lookup(VN2SFN(dvp), nm, VDIR,
1406 (va->va_mode & AT_MODE) ? va->va_mode : 0, NULL, 0, &lookuperr);
1407
1408 if (node && node->sf_parent)
1409 sfnode_clear_dir_list(node->sf_parent);
1410
1411 mutex_exit(&sffs_lock);
1412 if (node == NULL)
1413 return (lookuperr);
1414 *vpp = sfnode_get_vnode(node);
1415 return (0);
1416}
1417
1418/*ARGSUSED*/
1419static int
1420sffs_rmdir(
1421 struct vnode *dvp,
1422 char *nm,
1423 vnode_t *cdir,
1424 cred_t *cred
1425#if !defined(VBOX_VFS_SOLARIS_10U6)
1426 , caller_context_t *ct,
1427 int flags
1428#endif
1429 )
1430{
1431 sfnode_t *node;
1432 vnode_t *vp;
1433 int error;
1434
1435 /*
1436 * Return error when removing . and ..
1437 */
1438 if (strcmp(nm, ".") == 0 || strcmp(nm, "") == 0)
1439 return (EINVAL);
1440 if (strcmp(nm, "..") == 0)
1441 return (EEXIST);
1442
1443#if defined(VBOX_VFS_SOLARIS_10U6)
1444 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred);
1445#else
1446 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1447#endif
1448 if (error)
1449 return (error);
1450 if (vp->v_type != VDIR) {
1451 VN_RELE(vp);
1452 return (ENOTDIR);
1453 }
1454
1455#ifdef VBOXVFS_WITH_MMAP
1456 if (vn_vfswlock(vp)) {
1457 VN_RELE(vp);
1458 return (EBUSY);
1459 }
1460#endif
1461
1462 if (vn_mountedvfs(vp)) {
1463 VN_RELE(vp);
1464 return (EBUSY);
1465 }
1466
1467 node = VN2SFN(vp);
1468
1469 mutex_enter(&sffs_lock);
1470 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1471 if (error)
1472 goto done;
1473
1474 /*
1475 * If anything else is using this vnode, then fail the remove.
1476 * Why? Windows hosts can't remove something that is open,
1477 * so we have to sfprov_close() it first.
1478 * There is no errno for this - since it's not a problem on UNIX,
1479 * but EINVAL is the closest.
1480 */
1481 if (node->sf_file != NULL) {
1482 if (vp->v_count > 1) {
1483 error = EINVAL;
1484 goto done;
1485 }
1486 (void)sfprov_close(node->sf_file);
1487 node->sf_file = NULL;
1488 }
1489
1490 /*
1491 * Remove the directory on the host and mark the node as stale.
1492 */
1493 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1494 error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
1495 if (error == ENOENT || error == 0)
1496 sfnode_make_stale(node);
1497
1498 if (node->sf_parent)
1499 sfnode_clear_dir_list(node->sf_parent);
1500done:
1501 mutex_exit(&sffs_lock);
1502#ifdef VBOXVFS_WITH_MMAP
1503 vn_vfsunlock(vp);
1504#endif
1505 VN_RELE(vp);
1506 return (error);
1507}
1508
1509
1510#ifdef VBOXVFS_WITH_MMAP
1511static caddr_t
1512sffs_page_map(
1513 page_t *ppage,
1514 enum seg_rw segaccess)
1515{
1516 /* Use seg_kpm driver if possible (64-bit) */
1517 if (kpm_enable)
1518 return (hat_kpm_mapin(ppage, NULL));
1519 ASSERT(segaccess == S_READ || segaccess == S_WRITE);
1520 return (ppmapin(ppage, PROT_READ | ((segaccess == S_WRITE) ? PROT_WRITE : 0), (caddr_t)-1));
1521}
1522
1523
1524static void
1525sffs_page_unmap(
1526 page_t *ppage,
1527 caddr_t addr)
1528{
1529 if (kpm_enable)
1530 hat_kpm_mapout(ppage, NULL, addr);
1531 else
1532 ppmapout(addr);
1533}
1534
1535
1536/*
1537 * Called when there's no page in the cache. This will create new page(s) and read
1538 * the file data into it.
1539 */
1540static int
1541sffs_readpages(
1542 vnode_t *dvp,
1543 offset_t off,
1544 page_t *pagelist[],
1545 size_t pagelistsize,
1546 struct seg *segp,
1547 caddr_t addr,
1548 enum seg_rw segaccess)
1549{
1550 ASSERT(MUTEX_HELD(&sffs_lock));
1551
1552 int error = 0;
1553 u_offset_t io_off, total;
1554 size_t io_len;
1555 page_t *ppages;
1556 page_t *pcur;
1557
1558 sfnode_t *node = VN2SFN(dvp);
1559 ASSERT(node);
1560 ASSERT(node->sf_file);
1561
1562 if (pagelistsize == PAGESIZE)
1563 {
1564 io_off = off;
1565 io_len = PAGESIZE;
1566 ppages = page_create_va(dvp, io_off, io_len, PG_WAIT | PG_EXCL, segp, addr);
1567 }
1568 else
1569 ppages = pvn_read_kluster(dvp, off, segp, addr, &io_off, &io_len, off, pagelistsize, 0);
1570
1571 /* If page already exists return success */
1572 if (!ppages)
1573 {
1574 *pagelist = NULL;
1575 return (0);
1576 }
1577
1578 /*
1579 * Map & read page-by-page.
1580 */
1581 total = io_off + io_len;
1582 pcur = ppages;
1583 while (io_off < total)
1584 {
1585 ASSERT3U(io_off, ==, pcur->p_offset);
1586
1587 caddr_t virtaddr = sffs_page_map(pcur, segaccess);
1588 uint32_t bytes = PAGESIZE;
1589 error = sfprov_read(node->sf_file, virtaddr, io_off, &bytes);
1590 /*
1591 * If we reuse pages without zero'ing them, one process can mmap() and read-past the length
1592 * to read previously mmap'd contents (from possibly other processes).
1593 */
1594 if (error == 0 && bytes < PAGESIZE)
1595 memset(virtaddr + bytes, 0, PAGESIZE - bytes);
1596 sffs_page_unmap(pcur, virtaddr);
1597 if (error != 0)
1598 {
1599 cmn_err(CE_WARN, "sffs_readpages: sfprov_read() failed. error=%d bytes=%u\n", error, bytes);
1600 /* Get rid of all kluster pages read & bail. */
1601 pvn_read_done(ppages, B_ERROR);
1602 return (error);
1603 }
1604 pcur = pcur->p_next;
1605 io_off += PAGESIZE;
1606 }
1607
1608 /*
1609 * Fill in the pagelist from kluster at the requested offset.
1610 */
1611 pvn_plist_init(ppages, pagelist, pagelistsize, off, io_len, segaccess);
1612 ASSERT(pagelist == NULL || (*pagelist)->p_offset == off);
1613 return (0);
1614}
1615
1616
1617/*ARGSUSED*/
1618static int
1619sffs_getpage(
1620 vnode_t *dvp,
1621 offset_t off,
1622 size_t len,
1623 uint_t *protp,
1624 page_t *pagelist[],
1625 size_t pagelistsize,
1626 struct seg *segp,
1627 caddr_t addr,
1628 enum seg_rw segaccess,
1629 cred_t *credp
1630#if !defined(VBOX_VFS_SOLARIS_10U6)
1631 , caller_context_t *ct
1632#endif
1633 )
1634{
1635 int error = 0;
1636 int is_recursive = 0;
1637 page_t **pageliststart = pagelist;
1638 sfnode_t *node = VN2SFN(dvp);
1639 ASSERT(node);
1640 ASSERT(node->sf_file);
1641
1642 if (segaccess == S_WRITE)
1643 return (ENOSYS); /* Will this ever happen? */
1644
1645 /* Don't bother about faultahead for now. */
1646 if (pagelist == NULL)
1647 return (0);
1648
1649 if (len > pagelistsize)
1650 len = pagelistsize;
1651 else
1652 len = P2ROUNDUP(len, PAGESIZE);
1653 ASSERT(pagelistsize >= len);
1654
1655 if (protp)
1656 *protp = PROT_ALL;
1657
1658 /*
1659 * The buffer passed to sffs_write may be mmap'd so we may get a
1660 * pagefault there, in which case we'll end up here with this thread
1661 * already owning the mutex. Mutexes aren't recursive.
1662 */
1663 if (mutex_owner(&sffs_lock) == curthread)
1664 is_recursive = 1;
1665 else
1666 mutex_enter(&sffs_lock);
1667
1668 /* Don't map pages past end of the file. */
1669 if (off + len > node->sf_stat.sf_size + PAGEOFFSET)
1670 {
1671 if (!is_recursive)
1672 mutex_exit(&sffs_lock);
1673 return (EFAULT);
1674 }
1675
1676 while (len > 0)
1677 {
1678 /*
1679 * Look for pages in the requested offset range, or create them if we can't find any.
1680 */
1681 if ((*pagelist = page_lookup(dvp, off, SE_SHARED)) != NULL)
1682 *(pagelist + 1) = NULL;
1683 else if ((error = sffs_readpages(dvp, off, pagelist, pagelistsize, segp, addr, segaccess)) != 0)
1684 {
1685 while (pagelist > pageliststart)
1686 page_unlock(*--pagelist);
1687
1688 *pagelist = NULL;
1689 if (!is_recursive)
1690 mutex_exit(&sffs_lock);
1691 return (error);
1692 }
1693
1694 while (*pagelist)
1695 {
1696 ASSERT3U((*pagelist)->p_offset, ==, off);
1697 off += PAGESIZE;
1698 addr += PAGESIZE;
1699 if (len > 0)
1700 {
1701 ASSERT3U(len, >=, PAGESIZE);
1702 len -= PAGESIZE;
1703 }
1704
1705 ASSERT3U(pagelistsize, >=, PAGESIZE);
1706 pagelistsize -= PAGESIZE;
1707 pagelist++;
1708 }
1709 }
1710
1711 /*
1712 * Fill the page list array with any pages left in the cache.
1713 */
1714 while ( pagelistsize > 0
1715 && (*pagelist++ = page_lookup_nowait(dvp, off, SE_SHARED)))
1716 {
1717 off += PAGESIZE;
1718 pagelistsize -= PAGESIZE;
1719 }
1720
1721 *pagelist = NULL;
1722 if (!is_recursive)
1723 mutex_exit(&sffs_lock);
1724 return (error);
1725}
1726
1727
1728/*ARGSUSED*/
1729static int
1730sffs_putpage(
1731 vnode_t *dvp,
1732 offset_t off,
1733 size_t len,
1734 int flags,
1735 cred_t *credp
1736#if !defined(VBOX_VFS_SOLARIS_10U6)
1737 , caller_context_t *ct
1738#endif
1739 )
1740{
1741 /*
1742 * We don't support PROT_WRITE mmaps.
1743 */
1744 return (ENOSYS);
1745}
1746
1747
1748/*ARGSUSED*/
1749static int
1750sffs_discardpage(
1751 vnode_t *dvp,
1752 page_t *ppage,
1753 u_offset_t *poff,
1754 size_t *plen,
1755 int flags,
1756 cred_t *pcred)
1757{
1758 /*
1759 * This would not get invoked i.e. via pvn_vplist_dirty() since we don't support
1760 * PROT_WRITE mmaps and therefore will not have dirty pages.
1761 */
1762 pvn_write_done(ppage, B_INVAL | B_ERROR | B_FORCE);
1763 return (0);
1764}
1765
1766
1767/*ARGSUSED*/
1768static int
1769sffs_map(
1770 vnode_t *dvp,
1771 offset_t off,
1772 struct as *asp,
1773 caddr_t *addrp,
1774 size_t len,
1775 uchar_t prot,
1776 uchar_t maxprot,
1777 uint_t flags,
1778 cred_t *credp
1779#if !defined(VBOX_VFS_SOLARIS_10U6)
1780 , caller_context_t *ct
1781#endif
1782 )
1783{
1784 /*
1785 * Invocation: mmap()->smmap_common()->VOP_MAP()->sffs_map(). Once the
1786 * segment driver creates the new segment via segvn_create(), it'll
1787 * invoke down the line VOP_ADDMAP()->sffs_addmap()
1788 */
1789 int error = 0;
1790 sfnode_t *node = VN2SFN(dvp);
1791 ASSERT(node);
1792 if ((flags & MAP_SHARED) && (prot & PROT_WRITE))
1793 return (ENOTSUP);
1794
1795 if (off < 0 || len > MAXOFFSET_T - off)
1796 return (ENXIO);
1797
1798 if (dvp->v_type != VREG)
1799 return (ENODEV);
1800
1801 if (dvp->v_flag & VNOMAP)
1802 return (ENOSYS);
1803
1804 if (vn_has_mandatory_locks(dvp, node->sf_stat.sf_mode))
1805 return (EAGAIN);
1806
1807 mutex_enter(&sffs_lock);
1808 as_rangelock(asp);
1809
1810#if defined(VBOX_VFS_SOLARIS_10U6)
1811 if ((flags & MAP_FIXED) == 0)
1812 {
1813 if (g_fVBoxVFS_SolOldAddrMap)
1814 g_VBoxVFS_SolAddrMap.MapAddr.pfnSol_map_addr_old(addrp, len, off, 1, flags);
1815 else
1816 g_VBoxVFS_SolAddrMap.MapAddr.pfnSol_map_addr(addrp, len, off, flags);
1817 if (*addrp == NULL)
1818 error = ENOMEM;
1819 }
1820 else
1821 as_unmap(asp, *addrp, len); /* User specified address, remove any previous mappings */
1822#else
1823 if (g_fVBoxVFS_SolOldAddrMap)
1824 error = g_VBoxVFS_SolAddrMap.ChooseAddr.pfnSol_choose_addr_old(asp, addrp, len, off, 1, flags);
1825 else
1826 error = g_VBoxVFS_SolAddrMap.ChooseAddr.pfnSol_choose_addr(asp, addrp, len, off, flags);
1827#endif
1828
1829 if (error)
1830 {
1831 as_rangeunlock(asp);
1832 mutex_exit(&sffs_lock);
1833 return (error);
1834 }
1835
1836 segvn_crargs_t vnodeargs;
1837 memset(&vnodeargs, 0, sizeof(vnodeargs));
1838 vnodeargs.vp = dvp;
1839 vnodeargs.cred = credp;
1840 vnodeargs.offset = off;
1841 vnodeargs.type = flags & MAP_TYPE;
1842 vnodeargs.prot = prot;
1843 vnodeargs.maxprot = maxprot;
1844 vnodeargs.flags = flags & ~MAP_TYPE;
1845 vnodeargs.amp = NULL; /* anon. mapping */
1846 vnodeargs.szc = 0; /* preferred page size code */
1847 vnodeargs.lgrp_mem_policy_flags = 0;
1848
1849 error = as_map(asp, *addrp, len, segvn_create, &vnodeargs);
1850
1851 as_rangeunlock(asp);
1852 mutex_exit(&sffs_lock);
1853 return (error);
1854}
1855
1856
1857/*ARGSUSED*/
1858static int
1859sffs_addmap(
1860 vnode_t *dvp,
1861 offset_t off,
1862 struct as *asp,
1863 caddr_t addr,
1864 size_t len,
1865 uchar_t prot,
1866 uchar_t maxprot,
1867 uint_t flags,
1868 cred_t *credp
1869#if !defined(VBOX_VFS_SOLARIS_10U6)
1870 , caller_context_t *ct
1871#endif
1872 )
1873{
1874 if (dvp->v_flag & VNOMAP)
1875 return (ENOSYS);
1876 return (0);
1877}
1878
1879
1880/*ARGSUSED*/
1881static int
1882sffs_delmap(
1883 vnode_t *dvp,
1884 offset_t off,
1885 struct as *asp,
1886 caddr_t addr,
1887 size_t len,
1888 uint_t prot,
1889 uint_t maxprot,
1890 uint_t flags,
1891 cred_t *credp
1892#if !defined(VBOX_VFS_SOLARIS_10U6)
1893 , caller_context_t *ct
1894#endif
1895 )
1896{
1897 if (dvp->v_flag & VNOMAP)
1898 return (ENOSYS);
1899
1900 return (0);
1901}
1902#endif /* VBOXVFS_WITH_MMAP */
1903
1904
1905/*ARGSUSED*/
1906static int
1907sffs_readlink(
1908 vnode_t *vp,
1909 uio_t *uiop,
1910 cred_t *cred
1911#if !defined(VBOX_VFS_SOLARIS_10U6)
1912 , caller_context_t *ct
1913#endif
1914 )
1915{
1916 sfnode_t *node;
1917 int error = 0;
1918 char *target = NULL;
1919
1920 if (uiop->uio_iovcnt != 1)
1921 return (EINVAL);
1922
1923 if (vp->v_type != VLNK)
1924 return (EINVAL);
1925
1926 mutex_enter(&sffs_lock);
1927 node = VN2SFN(vp);
1928
1929 target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1930
1931 error = sfprov_readlink(node->sf_sffs->sf_handle, node->sf_path, target,
1932 MAXPATHLEN);
1933 if (error)
1934 goto done;
1935
1936 error = uiomove(target, strlen(target), UIO_READ, uiop);
1937
1938done:
1939 mutex_exit(&sffs_lock);
1940 if (target)
1941 kmem_free(target, MAXPATHLEN);
1942 return (error);
1943}
1944
1945
1946/*ARGSUSED*/
1947static int
1948sffs_symlink(
1949 vnode_t *dvp,
1950 char *linkname,
1951 vattr_t *vap,
1952 char *target,
1953 cred_t *cred
1954#if !defined(VBOX_VFS_SOLARIS_10U6)
1955 , caller_context_t *ct,
1956 int flags
1957#endif
1958 )
1959{
1960 sfnode_t *dir;
1961 sfnode_t *node;
1962 sffs_stat_t stat;
1963 int error = 0;
1964 char *fullpath;
1965
1966 /*
1967 * These should never happen
1968 */
1969 ASSERT(linkname != NULL);
1970 ASSERT(strcmp(linkname, "") != 0);
1971 ASSERT(strcmp(linkname, ".") != 0);
1972 ASSERT(strcmp(linkname, "..") != 0);
1973
1974 /*
1975 * Basic checks.
1976 */
1977 if (vap->va_type != VLNK)
1978 return (EINVAL);
1979
1980 mutex_enter(&sffs_lock);
1981
1982 if (sfnode_lookup(VN2SFN(dvp), linkname, VNON, 0, NULL, 0, NULL) !=
1983 NULL) {
1984 error = EEXIST;
1985 goto done;
1986 }
1987
1988 dir = VN2SFN(dvp);
1989 error = sfnode_access(dir, VWRITE, cred);
1990 if (error)
1991 goto done;
1992
1993 /*
1994 * Create symlink. Note that we ignore vap->va_mode because generally
1995 * we can't change the attributes of the symlink itself.
1996 */
1997 fullpath = sfnode_construct_path(dir, linkname);
1998 error = sfprov_symlink(dir->sf_sffs->sf_handle, fullpath, target,
1999 &stat);
2000 kmem_free(fullpath, strlen(fullpath) + 1);
2001 if (error)
2002 goto done;
2003
2004 node = sfnode_lookup(dir, linkname, VLNK, 0, &stat,
2005 sfnode_cur_time_usec(), NULL);
2006
2007 sfnode_invalidate_stat_cache(dir);
2008 sfnode_clear_dir_list(dir);
2009
2010done:
2011 mutex_exit(&sffs_lock);
2012 return (error);
2013}
2014
2015
2016/*ARGSUSED*/
2017static int
2018sffs_remove(
2019 vnode_t *dvp,
2020 char *name,
2021 cred_t *cred
2022#if !defined(VBOX_VFS_SOLARIS_10U6)
2023 , caller_context_t *ct,
2024 int flags
2025#endif
2026 )
2027{
2028 vnode_t *vp;
2029 sfnode_t *node;
2030 int error;
2031
2032 /*
2033 * These should never happen
2034 */
2035 ASSERT(name != NULL);
2036 ASSERT(strcmp(name, "..") != 0);
2037
2038#if defined(VBOX_VFS_SOLARIS_10U6)
2039 error = sffs_lookup(dvp, name, &vp,
2040 NULL, 0, NULL, cred);
2041#else
2042 error = sffs_lookup(dvp, name, &vp,
2043 NULL, 0, NULL, cred, ct, NULL, NULL);
2044#endif
2045 if (error)
2046 return (error);
2047 node = VN2SFN(vp);
2048
2049 mutex_enter(&sffs_lock);
2050 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
2051 if (error)
2052 goto done;
2053
2054 /*
2055 * If anything else is using this vnode, then fail the remove.
2056 * Why? Windows hosts can't sfprov_remove() a file that is open,
2057 * so we have to sfprov_close() it first.
2058 * There is no errno for this - since it's not a problem on UNIX,
2059 * but ETXTBSY is the closest.
2060 */
2061 if (node->sf_file != NULL) {
2062 if (vp->v_count > 1) {
2063 error = ETXTBSY;
2064 goto done;
2065 }
2066 (void)sfprov_close(node->sf_file);
2067 node->sf_file = NULL;
2068 }
2069
2070 /*
2071 * Remove the file on the host and mark the node as stale.
2072 */
2073 sfnode_invalidate_stat_cache(VN2SFN(dvp));
2074
2075 error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path,
2076 node->sf_type == VLNK);
2077 if (error == ENOENT || error == 0)
2078 sfnode_make_stale(node);
2079
2080 if (node->sf_parent)
2081 sfnode_clear_dir_list(node->sf_parent);
2082done:
2083 mutex_exit(&sffs_lock);
2084 VN_RELE(vp);
2085 return (error);
2086}
2087
2088/*ARGSUSED*/
2089static int
2090sffs_rename(
2091 vnode_t *old_dir,
2092 char *old_nm,
2093 vnode_t *new_dir,
2094 char *new_nm,
2095 cred_t *cred
2096#if !defined(VBOX_VFS_SOLARIS_10U6)
2097 , caller_context_t *ct,
2098 int flags
2099#endif
2100 )
2101{
2102 char *newpath;
2103 int error;
2104 sfnode_t *node;
2105
2106 if (strcmp(new_nm, "") == 0 ||
2107 strcmp(new_nm, ".") == 0 ||
2108 strcmp(new_nm, "..") == 0 ||
2109 strcmp(old_nm, "") == 0 ||
2110 strcmp(old_nm, ".") == 0 ||
2111 strcmp(old_nm, "..") == 0)
2112 return (EINVAL);
2113
2114 /*
2115 * make sure we have permission to do the rename
2116 */
2117 mutex_enter(&sffs_lock);
2118 error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
2119 if (error == 0 && new_dir != old_dir)
2120 error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
2121 if (error)
2122 goto done;
2123
2124 node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON, 0, NULL, 0, NULL);
2125 if (node == NULL) {
2126 error = ENOENT;
2127 goto done;
2128 }
2129
2130 /*
2131 * Rename the file on the host and in our caches.
2132 */
2133 sfnode_invalidate_stat_cache(node);
2134 sfnode_invalidate_stat_cache(VN2SFN(old_dir));
2135 sfnode_invalidate_stat_cache(VN2SFN(new_dir));
2136
2137 newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
2138 error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
2139 node->sf_type == VDIR);
2140 if (error == 0)
2141 sfnode_rename(node, VN2SFN(new_dir), newpath);
2142 else {
2143 kmem_free(newpath, strlen(newpath) + 1);
2144 if (error == ENOENT)
2145 sfnode_make_stale(node);
2146 }
2147done:
2148 mutex_exit(&sffs_lock);
2149 return (error);
2150}
2151
2152
2153/*ARGSUSED*/
2154static int
2155sffs_fsync(
2156 vnode_t *vp,
2157 int flag,
2158 cred_t *cr
2159#if !defined(VBOX_VFS_SOLARIS_10U6)
2160 , caller_context_t *ct
2161#endif
2162 )
2163{
2164 sfnode_t *node;
2165 int error;
2166
2167 /*
2168 * Ask the host to sync any data it may have cached for open files.
2169 */
2170 mutex_enter(&sffs_lock);
2171 node = VN2SFN(vp);
2172 if (node->sf_file == NULL)
2173 error = EBADF;
2174 else if (node->sf_sffs->sf_fsync)
2175 error = sfprov_fsync(node->sf_file);
2176 else
2177 error = 0;
2178 mutex_exit(&sffs_lock);
2179 return (error);
2180}
2181
2182/*
2183 * This may be the last reference, possibly time to close the file and
2184 * destroy the vnode. If the sfnode is stale, we'll destroy that too.
2185 */
2186/*ARGSUSED*/
2187static void
2188sffs_inactive(
2189 vnode_t *vp,
2190 cred_t *cr
2191#if !defined(VBOX_VFS_SOLARIS_10U6)
2192 , caller_context_t *ct
2193#endif
2194 )
2195{
2196 sfnode_t *node;
2197
2198 /*
2199 * nothing to do if this isn't the last use
2200 */
2201 mutex_enter(&sffs_lock);
2202 node = VN2SFN(vp);
2203 mutex_enter(&vp->v_lock);
2204 if (vp->v_count > 1) {
2205 --vp->v_count;
2206 mutex_exit(&vp->v_lock);
2207 mutex_exit(&sffs_lock);
2208 return;
2209 }
2210
2211 if (vn_has_cached_data(vp)) {
2212#ifdef VBOXVFS_WITH_MMAP
2213 /* We're fine with releasing the vnode lock here as we should be covered by the sffs_lock */
2214 mutex_exit(&vp->v_lock);
2215 /* We won't have any dirty pages, this will just invalidate (destroy) the pages and move it to the cachelist. */
2216 pvn_vplist_dirty(vp, 0 /* offset */, sffs_discardpage, B_INVAL, cr);
2217 mutex_enter(&vp->v_lock);
2218#else
2219 panic("sffs_inactive() found cached data");
2220#endif
2221 }
2222
2223 /*
2224 * destroy the vnode
2225 */
2226 node->sf_vnode = NULL;
2227 mutex_exit(&vp->v_lock);
2228 vn_invalid(vp);
2229 vn_free(vp);
2230 LogFlowFunc((" %s vnode cleared\n", node->sf_path));
2231
2232 /*
2233 * Close the sf_file for the node.
2234 */
2235 if (node->sf_file != NULL) {
2236 (void)sfprov_close(node->sf_file);
2237 node->sf_file = NULL;
2238 }
2239
2240 /*
2241 * Free the directory entries for the node. This should normally
2242 * have been taken care of in sffs_close(), but better safe than
2243 * sorry.
2244 */
2245 sfnode_clear_dir_list(node);
2246
2247 /*
2248 * If the node is stale, we can also destroy it.
2249 */
2250 if (node->sf_is_stale && node->sf_children == 0)
2251 sfnode_destroy(node);
2252
2253 mutex_exit(&sffs_lock);
2254 return;
2255}
2256
2257/*
2258 * All the work for this is really done in sffs_lookup().
2259 */
2260/*ARGSUSED*/
2261static int
2262sffs_open(
2263 vnode_t **vpp,
2264 int flag,
2265 cred_t *cr
2266#if !defined(VBOX_VFS_SOLARIS_10U6)
2267 , caller_context_t *ct
2268#endif
2269 )
2270{
2271 sfnode_t *node;
2272 int error = 0;
2273
2274 mutex_enter(&sffs_lock);
2275
2276 node = VN2SFN(*vpp);
2277 sfnode_open(node, flag);
2278 if (node->sf_file == NULL)
2279 error = EINVAL;
2280 mutex_exit(&sffs_lock);
2281
2282 return (error);
2283}
2284
2285/*
2286 * All the work for this is really done in inactive.
2287 */
2288/*ARGSUSED*/
2289static int
2290sffs_close(
2291 vnode_t *vp,
2292 int flag,
2293 int count,
2294 offset_t offset,
2295 cred_t *cr
2296#if !defined(VBOX_VFS_SOLARIS_10U6)
2297 , caller_context_t *ct
2298#endif
2299 )
2300{
2301 sfnode_t *node;
2302
2303 mutex_enter(&sffs_lock);
2304 node = VN2SFN(vp);
2305
2306 /*
2307 * Free the directory entries for the node. We do this on this call
2308 * here because the directory node may not become inactive for a long
2309 * time after the readdir is over. Case in point, if somebody cd's into
2310 * the directory then it won't become inactive until they cd away again.
2311 * In such a case we would end up with the directory listing not getting
2312 * updated (i.e. the result of 'ls' always being the same) until they
2313 * change the working directory.
2314 */
2315 sfnode_clear_dir_list(node);
2316
2317 sfnode_invalidate_stat_cache(node);
2318
2319 if (node->sf_file != NULL && vp->v_count <= 1)
2320 {
2321 (void)sfprov_close(node->sf_file);
2322 node->sf_file = NULL;
2323 }
2324
2325 mutex_exit(&sffs_lock);
2326 return (0);
2327}
2328
2329/* ARGSUSED */
2330static int
2331sffs_seek(
2332 vnode_t *v,
2333 offset_t o,
2334 offset_t *no
2335#if !defined(VBOX_VFS_SOLARIS_10U6)
2336 , caller_context_t *ct
2337#endif
2338 )
2339{
2340 if (*no < 0 || *no > MAXOFFSET_T)
2341 return (EINVAL);
2342
2343 if (v->v_type == VDIR)
2344 {
2345 sffs_dirents_t *cur_buf = VN2SFN(v)->sf_dir_list;
2346 off_t offset = 0;
2347
2348 if (cur_buf == NULL)
2349 return (0);
2350
2351 while (cur_buf != NULL) {
2352 if (*no >= offset && *no <= offset + cur_buf->sf_len)
2353 return (0);
2354 offset += cur_buf->sf_len;
2355 cur_buf = cur_buf->sf_next;
2356 }
2357 return (EINVAL);
2358 }
2359 return (0);
2360}
2361
2362
2363
2364/*
2365 * By returning an error for this, we prevent anything in sffs from
2366 * being re-exported by NFS
2367 */
2368/* ARGSUSED */
2369static int
2370sffs_fid(
2371 vnode_t *vp,
2372 fid_t *fidp
2373#if !defined(VBOX_VFS_SOLARIS_10U6)
2374 , caller_context_t *ct
2375#endif
2376 )
2377{
2378 return (ENOTSUP);
2379}
2380
2381/*
2382 * vnode operations for regular files
2383 */
2384const fs_operation_def_t sffs_ops_template[] = {
2385 VOPNAME_ACCESS, { .vop_access = sffs_access },
2386 VOPNAME_CLOSE, { .vop_close = sffs_close },
2387 VOPNAME_CREATE, { .vop_create = sffs_create },
2388 VOPNAME_FID, { .vop_fid = sffs_fid },
2389 VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
2390 VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
2391 VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
2392 VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
2393 VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
2394 VOPNAME_OPEN, { .vop_open = sffs_open },
2395 VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
2396 VOPNAME_READ, { .vop_read = sffs_read },
2397 VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
2398 VOPNAME_READLINK, { .vop_readlink = sffs_readlink },
2399 VOPNAME_REMOVE, { .vop_remove = sffs_remove },
2400 VOPNAME_RENAME, { .vop_rename = sffs_rename },
2401 VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
2402 VOPNAME_SEEK, { .vop_seek = sffs_seek },
2403 VOPNAME_SETATTR, { .vop_setattr = sffs_setattr },
2404 VOPNAME_SPACE, { .vop_space = sffs_space },
2405 VOPNAME_SYMLINK, { .vop_symlink = sffs_symlink },
2406 VOPNAME_WRITE, { .vop_write = sffs_write },
2407
2408# ifdef VBOXVFS_WITH_MMAP
2409 VOPNAME_MAP, { .vop_map = sffs_map },
2410 VOPNAME_ADDMAP, { .vop_addmap = sffs_addmap },
2411 VOPNAME_DELMAP, { .vop_delmap = sffs_delmap },
2412 VOPNAME_GETPAGE, { .vop_getpage = sffs_getpage },
2413 VOPNAME_PUTPAGE, { .vop_putpage = sffs_putpage },
2414# endif
2415
2416 NULL, NULL
2417};
2418
2419/*
2420 * Also, init and fini functions...
2421 */
2422int
2423sffs_vnode_init(void)
2424{
2425 int err;
2426
2427 err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
2428 if (err)
2429 return (err);
2430
2431 avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
2432 offsetof(sfnode_t, sf_linkage));
2433 avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
2434 offsetof(sfnode_t, sf_linkage));
2435
2436 sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
2437
2438 return (0);
2439}
2440
2441void
2442sffs_vnode_fini(void)
2443{
2444 if (sffs_ops)
2445 vn_freevnodeops(sffs_ops);
2446 ASSERT(avl_first(&sfnodes) == NULL);
2447 avl_destroy(&sfnodes);
2448 if (sffs_buffer != NULL) {
2449 kmem_free(sffs_buffer, PAGESIZE);
2450 sffs_buffer = NULL;
2451 }
2452}
2453
2454/*
2455 * Utility at unmount to get all nodes in that mounted filesystem removed.
2456 */
2457int
2458sffs_purge(struct sffs_data *sffs)
2459{
2460 sfnode_t *node;
2461 sfnode_t *prev;
2462
2463 /*
2464 * Check that no vnodes are active.
2465 */
2466 if (sffs->sf_rootnode->v_count > 1)
2467 return (-1);
2468 for (node = avl_first(&sfnodes); node;
2469 node = AVL_NEXT(&sfnodes, node)) {
2470 if (node->sf_sffs == sffs && node->sf_vnode &&
2471 node->sf_vnode != sffs->sf_rootnode)
2472 return (-1);
2473 }
2474 for (node = avl_first(&stale_sfnodes); node;
2475 node = AVL_NEXT(&stale_sfnodes, node)) {
2476 if (node->sf_sffs == sffs && node->sf_vnode &&
2477 node->sf_vnode != sffs->sf_rootnode)
2478 return (-1);
2479 }
2480
2481 /*
2482 * All clear to destroy all node information. Since there are no
2483 * vnodes, the make stale will cause deletion.
2484 */
2485 VN_RELE(sffs->sf_rootnode);
2486 mutex_enter(&sffs_lock);
2487 for (prev = NULL;;) {
2488 if (prev == NULL)
2489 node = avl_first(&sfnodes);
2490 else
2491 node = AVL_NEXT(&sfnodes, prev);
2492
2493 if (node == NULL)
2494 break;
2495
2496 if (node->sf_sffs == sffs) {
2497 if (node->sf_vnode != NULL)
2498 panic("vboxfs: purge hit active vnode");
2499 sfnode_make_stale(node);
2500 } else {
2501 prev = node;
2502 }
2503 }
2504 mutex_exit(&sffs_lock);
2505 return (0);
2506}
2507
2508#if 0
2509/* Debug helper functions */
2510static void
2511sfnode_print(sfnode_t *node)
2512{
2513 Log(("0x%p", node));
2514 Log((" type=%s (%d)",
2515 node->sf_type == VDIR ? "VDIR" :
2516 node->sf_type == VNON ? "VNON" :
2517 node->sf_type == VLNK ? "VLNK" :
2518 node->sf_type == VREG ? "VREG" : "other", node->sf_type));
2519 Log((" ino=%d", (uint_t)node->sf_ino));
2520 Log((" path=%s", node->sf_path));
2521 Log((" parent=0x%p", node->sf_parent));
2522 if (node->sf_children)
2523 Log((" children=%d", node->sf_children));
2524 if (node->sf_vnode)
2525 Log((" vnode=0x%p", node->sf_vnode));
2526 Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
2527}
2528
2529static void
2530sfnode_list(void)
2531{
2532 sfnode_t *n;
2533 for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
2534 sfnode_print(n);
2535 for (n = avl_first(&stale_sfnodes); n != NULL;
2536 n = AVL_NEXT(&stale_sfnodes, n))
2537 sfnode_print(n);
2538}
2539#endif
2540
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