VirtualBox

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

Last change on this file since 76690 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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